COBOL
Both source layouts are accepted (classic fixed format and free format, detected automatically). The execution model is COBOL's paragraphs and fall-through: the main flow CALLs each paragraph in order, and PERFORM is just a CALL.
Arithmetic, control flow, paragraphs / PERFORM and library calls are all there; CALL "ui_2d_clear" USING ... reaches the ui_* interfaces.
How to run it on your phone
vml run examples/cobol/demo/fizzbuzz.cob
Compiling takes a while (one to two minutes for C, a few seconds for scripting languages). Once the program is running, the gamepad is at the bottom of the screen - the D-pad and the four action buttons are all there. Tap the back arrow in the top-right corner to return to the Shell.
What to know before you write
- In fixed format, mind the columns: 1-6 are the sequence area, column 7 is the indicator, and only columns 8-72 are code; columns 73-80 are discarded
- The two layouts are detected automatically (the rule is whether columns 1-6 contain a letter); to force one, write
>>SOURCE FORMAT IS FREE(orFIXED) in the file - Comments: inline
*>(not inside quotes); a whole-line comment in fixed format goes in column 7 (*or/) - A full stop
.ends a sentence - COBOL does not let you leave it out - Call the UI with
CALL "ui_2d_clear" USING ...(arguments pushed in reverse)
Examples
| File | What it shows |
|---|---|
demo/fizzbuzz.cob | FizzBuzz (PERFORM VARYING plus COMPUTE remainders plus IF) |
demo/hello.cob | The smallest program in fixed format |
demo/report.cob | A small report (PIC X(n) space padding plus numeric layout plus accumulation) |
Pitfalls we hit on real devices
- ⚠ The
Vscaling decimal point is not converted (PIC 9(2)V99):Vis only recorded and this subset's arithmetic is integer arithmetic, soCOMPUTE P = 10 / 4gives2where standard COBOL gives2.50. - ⚠ An
OCCURStable has no array semantics:T(1)andT(2)are the same cell (there is a warning, but the result is still wrong). MOVE CORRESPONDINGis a compile-time error;COPYrecognises the library name but does not expand it.STRING ... DELIMITED BY SPACE/literalis always treated asSIZE(the whole field).- File I/O and sorting / inspection are not done:
OPEN/READ/WRITE/CLOSE/SORT/SEARCH/INSPECT/UNSTRINGall report "not supported yet" at compile time (never silently). COMP-3packed decimal is recognised as a clause but stored as a plain integer.- A
SECTIONheader is treated as a paragraph header, soPERFORM section-nameruns only the first paragraph in it; inACCEPT ... FROM env-nameeverything afterFROMis ignored (it always reads standard input). /is integer division (7 / 3is2); a decimal in a literal is truncated with a warning.
The rest of this page is taken straight from the VML source (third_party/vml/VMLPrepares/CobolCompiler/): the README covers what this front end supports and how to compile it, and the language reference covers the syntax itself. When upstream changes, regenerating this page brings it up to date.
Language reference
COBOL subset — language spec
Written for people writing programs: what this frontend accepts, what those forms mean, and where it differs from standard COBOL. Implementation notes live in
README.md; wiring lives inINTEGRATION.md.
Version: v1 (2026-10-13) · extensions .cob (also .cbl) · probe scripts/vml-cobol-probe/run.sh
Version: v1.0 | Date: 2026-10-13 | Revised by: Shenzhen Tanso Intelligent Technology Co., Ltd.
Specification standard
| Field | Value |
|---|---|
| Target standard | COBOL-85 subset |
| Publication year | 1985 |
| Completeness | Subset (not itemised) |
| Tests | 28 passing / 0 failing / 4 known-red |
Keywords
ACCEPT ADD ADVANCING AFTER ALL ALTER AND ANY ASCENDING AT BEFORE BINARY BLANK BY CALL CLOSE COMMUNICATION COMP COMP-1 COMP-2 COMP-3 COMP-4 COMPUTE CONTENT CONTINUE CORR CORRESPONDING DATA DELETE DELIMITED DESCENDING DISPLAY DIVIDE DIVISION ELSE END END-ACCEPT END-ADD END-CALL END-COMPUTE END-DISPLAY END-DIVIDE END-EVALUATE END-IF END-MULTIPLY END-PERFORM END-STRING END-SUBTRACT ENTER ENVIRONMENT EQUAL ERROR EVALUATE EXIT EXTERNAL FILE FILLER FROM GIVING GLOBAL GO GOBACK GREATER HIGH-VALUE HIGH-VALUES IDENTIFICATION IF INITIAL INITIALIZE INSPECT INTO INVALID IS JUSTIFIED KEY LESS LINKAGE LOCAL-STORAGE LOW-VALUE LOW-VALUES MERGE MOVE MULTIPLY NEXT NO NOT NULL NULLS OCCURS OF ON OPEN OR OTHER PACKED-DECIMAL PERFORM PIC PICTURE PROCEDURE PROGRAM PROGRAM-ID QUOTE QUOTES READ REDEFINES REFERENCE RELEASE REMAINDER REPORT RETURN RETURNING REWRITE ROUNDED RUN SCREEN SEARCH SECTION SENTENCE SET SIGN SIZE SORT SPACE SPACES START STOP STRING SUBTRACT TEST THAN THEN THROUGH THRU TIMES TO TRUE UNSTRING UNTIL UPON USAGE USING VALUE VALUES VARYING WHEN WITH WORKING-STORAGE WRITE ZERO ZEROES ZEROS
Overview
A subset of COBOL-85 is supported, compiled to VML assembly. The source first goes through CobolSourceFormat, which folds physical lines into logical lines (fixed format: columns 1–6 sequence area, column 7 indicator, A and B areas, columns 73–80 discarded per the standard, - for continuation; plus free format — the two are detected automatically and can be overridden with >>SOURCE FORMAT IS FREE|FIXED). COBOL is not tokenised by punctuation (MOVE 1 TO X ADD 1 TO Y. is a single sentence), so the parser locates statement boundaries by asking whether the next word is a verb or clause word; unimplemented verbs report loudly rather than being skipped silently. All numbers are 32-bit integers, and libraries are pulled in by the .linked entries in Lib/cobol/builtin.vml.
Supported language features
The features this frontend accepts are organised by the sections below; each one states what is accepted and what it means:
- The two source layouts: fixed (sequence area / column-7 indicator / A and B areas / columns 73–80 discarded / continuation) and free; the detection rule is falsifiable, and
>>SOURCE FORMAT IS …overrides it — §1 - Program structure: the
IDENTIFICATION/ENVIRONMENT/DATA/PROCEDUREdivisions,SECTIONs and paragraphs (including fall-through),PROGRAM-ID— §2 - Data items and PIC: level numbers,
PIC/PICTURE,USAGE,REDEFINES,OCCURS,VALUE,FILLER— §3 - Statements: assignment and arithmetic (
MOVE/COMPUTE/ADD/SUBTRACT/MULTIPLY/DIVIDE), output (DISPLAY), conditionals and branching (IF,EVALUATE), loops and paragraph calls (PERFORM), string building (STRING), library calls (CALL), input (ACCEPT), termination (STOP RUN/GOBACK/EXIT/CONTINUE) — §4 - Comments: fixed format column 7
*///D; inline*>in both formats (not inside quotes) — §5 - Numbers and rounding: 32-bit integers,
/is integer division,**is exponentiation — §6 - Known limits: no scaling for the
Vdecimal point,OCCURShas no array semantics,COPYis not expanded, file I/O / sorting / inspection not done — §7
See the sections below for details.
1. The two source layouts
COBOL sources come in two layouts. This frontend detects them automatically and accepts both.
Fixed format (classic)
column 123456|7|8...........11|12.....................72|73..80
meaning seq |ind| area A | area B |dropped
- 1–6: sequence area (old-style line numbers, may be blank)
- 7: indicator — blank = normal code;
*or/= whole-line comment;D(ord) = debug line (treated as a comment);-= continuation (this line's column 8 onward is appended to the previous line) - 8–11 (area A): paragraph names,
SECTION,01/77data items, … - 12–72 (area B): ordinary statements
- 73–80: dropped per the standard (this frontend does exactly that;
23-col72-limit.cobpins it)
Free format
Code starts at column 1 (or a few columns of indent); tokens are separated by whitespace; comments are *>.
The detection rule (falsifiable)
Scan every non-blank line; if any line has a letter in columns 1–6, it is free format; otherwise fixed format.
Because fixed-format code always starts at column 8 (columns 1–6 never hold letters), while free-format code starts at column 1. Fixed format with sequence numbers (000100 IDENTIFICATION …) has digits in columns 1–6 ⇒ still fixed.
Force it (escape hatch when auto-detection guesses wrong; the first occurrence wins):
>>SOURCE FORMAT IS FREE
>>SOURCE FORMAT IS FIXED
2. Program structure
IDENTIFICATION DIVISION.
PROGRAM-ID. NAME.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-N PIC 9(3). *> data item
77 WS-X PIC X(10). *> level-77 standalone item
PROCEDURE DIVISION.
MAIN-PARA. *> a paragraph is "NAME." on its own line
MOVE 1 TO WS-N.
STOP RUN.
3. Data items and PIC
01 WS-N PIC 9(3). *> 3 digits
01 WS-NAME PIC X(10). *> 10 characters (fixed length)
01 WS-AMT PIC S9(5)V99. *> signed + implied decimals (see section 7)
01 WS-CNT PIC 9(3) VALUE 0. *> with an initial value
Supported picture symbols: 9, X/A, S, V, Z/*, and repetition (n). Other editing symbols (B / , + - $) are recognised but do not change the length semantics.
A numeric item is a 4-byte integer; a character item is fixed length and its initial value is padded with spaces to that length (COBOL convention). VALUE "AB" in PIC X(5) prints as AB .
4. Statements
Assignment and arithmetic
MOVE 5 TO WS-N.
MOVE "AB" TO WS-NAME. *> short string padded to the fixed length
MOVE WS-N TO WS-NAME. *> numeric -> character (converted to text)
MOVE WS-NAME TO WS-N. *> character -> numeric (parsed back)
COMPUTE WS-N = (A + B) * 2. *> + - * / ** with parentheses and precedence
ADD 5 TO WS-N. *> WS-N = WS-N + 5
ADD 5 TO WS-N GIVING WS-M. *> WS-M = WS-N + 5 (WS-N unchanged)
SUBTRACT 3 FROM WS-N. *> WS-N = WS-N - 3
MULTIPLY 2 BY WS-N. *> WS-N = WS-N * 2
DIVIDE 2 INTO WS-N. *> WS-N = WS-N / 2 (integer division)
DIVIDE 7 BY 3 GIVING WS-M. *> WS-M = 7 / 3 = 2
With GIVING the target is only written; without GIVING the target takes part in the operation — that is COBOL's own rule, don't mix it up.
Output
DISPLAY "TOTAL=" WS-N. *> operands are printed back to back, newline at the end
DISPLAY "X" WITH NO ADVANCING. *> no trailing newline
DISPLAY inserts no separator between operands (that is COBOL; write the space into the literal yourself).
Conditions
IF A > B THEN DISPLAY "BIG" ELSE DISPLAY "SMALL" END-IF.
IF A = 1 THEN DISPLAY "ONE". *> period-terminated (old style) works too
Both operator spellings are accepted: = < > <= >= <>, and IS [NOT] EQUAL TO / IS GREATER THAN / IS LESS THAN. Conditions combine with AND / OR / NOT and parentheses. A bare operand as a condition (IF FLAG) means "non-zero is true".
Branching
EVALUATE K
WHEN 1 DISPLAY "ONE"
WHEN 3 DISPLAY "THREE"
WHEN OTHER DISPLAY "OTHER"
END-EVALUATE.
Loops and paragraph calls
PERFORM PARA. *> call once, returns at the paragraph's end
PERFORM A THRU B. *> runs every paragraph from A through B
PERFORM PARA UNTIL WS-I > 10. *> test before (COBOL default)
PERFORM PARA VARYING WS-I FROM 1 BY 1 UNTIL WS-I > 10.
PERFORM 3 TIMES … END-PERFORM. *> inline block, 3 times
⚠ The control variable ends one step past the last executed value (UNTIL WS-I > 5 running 1..5 leaves WS-I = 6) — that is COBOL's definition (increment, then test), not a defect.
Paragraphs and fall-through
COBOL executes paragraphs sequentially: from the first paragraph onward, each one following the previous, unless STOP RUN / GOBACK / PERFORM moves control elsewhere.
P1.
ADD 1 TO N.
P2.
ADD 10 TO N. *> no PERFORM: the main flow falls through from P1 into here
STOP RUN.
PERFORM P runs only P and returns (no fall-through past P).
String concatenation
STRING "HELLO" DELIMITED BY SIZE "WORLD" DELIMITED BY SIZE INTO WS-S.
Whatever follows DELIMITED BY is currently treated as SIZE (the whole piece); "stop at a space" (BY SPACE) is not implemented yet.
Library calls (CALL)
CALL "abs" USING -9 GIVING WS-R. *> math library
CALL "strlen" USING WS-NAME GIVING WS-R. *> string library
CALL "ui_2d_clear" USING WS-COLOR. *> host drawing API
CALL "name" USING a b passes the arguments in order (VML __stdcall convention); the return value, if any, lands in the GIVING item. This is the door from COBOL to ui_* / tty_* and every shared library. The auto-linked set is in Lib/cobol/builtin.vml (includes vmlui / tty / vml3d).
Input
ACCEPT WS-N. *> numeric item: reads one integer
ACCEPT WS-NAME. *> character item: reads one line
Termination
STOP RUN. / GOBACK. / EXIT. / CONTINUE. (no-op). STOP RUN inside a paragraph really ends the process — including when reached through PERFORM.
5. Comments
| Where | How |
|---|---|
| fixed format, whole line | * or / in column 7 |
| fixed format, debug line | D/d in column 7 (treated as a comment) |
| inline (both formats) | from *> to end of line |
| free format, whole line | *> at the start (any indent) |
⚠ *> inside a string literal is text, not a comment (DISPLAY "A*>B" prints A*>B).
6. Numbers and rounding
- All numerics are 32-bit integers;
/is integer division (7 / 3 = 2) - A literal with a fraction (
1.5) is truncated to an integer and produces a warning (never silent) «bold»is exponentiation (2 «/» 8 = 256, via the library functionipow)
7. Known limitations (differences from standard COBOL)
| Limitation | Effect | Criterion |
|---|---|---|
scaled decimals (V) are not scaled | PIC 9(2)V99 is just an integer; COMPUTE P = 10 / 4 gives 2 (standard COBOL gives 2.50) | bugs/vml_cobol_bugs/cb01 |
OCCURS tables unsupported | T(1) and T(2) hit the same cell (with a warning); no array semantics at all | cb02 |
MOVE CORRESPONDING unsupported | compile error (it does not guess) | cb03 |
STRING … DELIMITED BY SPACE/literal | everything is treated as SIZE (whole piece) | cb04 |
COPY is not expanded | the copybook name is recognised, but its text is not inserted | — |
| file I/O / sorting / inspection | OPEN/READ/WRITE/CLOSE/SORT/SEARCH/INSPECT/UNSTRING report "not supported yet" at compile time | — |
COMP-3 packed decimal | the clause is recognised, the value is stored as a plain integer | — |
| section-level PERFORM | a SECTION header is treated as a paragraph header; PERFORM SECTION-NAME runs only its first paragraph | — |
ACCEPT … FROM env-name | everything after FROM is ignored (input always comes from stdin) | — |
All of these are registered one by one, with reproductions and measured numbers, in bugs/vml_cobol_bugs/COBOL问题清单.md.
8. Complete runnable examples
third_party/vml/Examples/cobol/demo/:
| File | Shows |
|---|---|
hello.cob | smallest fixed-format program |
fizzbuzz.cob | PERFORM VARYING + nested IF + remainder |
report.cob | fixed-length character padding + numeric layout + accumulation |
Compiler README
COBOL compiler (subset)
Path: third_party/vml/VMLPrepares/CobolCompiler/ Extensions: .cob (also accepts .cbl) Language library: Lib/cobol/ (builtin.vml) Maturity: enough to write ordinary programs (full arithmetic + control flow + calls + paragraphs/PERFORM + library calls) Probe: scripts/vml-cobol-probe/run.sh — pass 28 / fail 0 / known-red 4 (measured 2026-10-13; 28 = 27 language criteria + 1 carrier criterion)
1. What this frontend accepts
| Feature | Status |
|---|---|
| Fixed format (cols 1–6 sequence area / col 7 indicator / cols 8–72 code) | ✅ auto-detected, see below |
| Free format (code from column 1) | ✅ auto-detected; >>SOURCE FORMAT IS FREE/FIXED overrides |
Column-7 * / whole-line comment, D debug line | ✅ |
Column-7 - continuation | ✅ (a string literal may be split across it) |
Inline comment *> | ✅ (*> inside quotes is not a comment) |
| Columns 73–80 dropped per the standard | ✅ |
IDENTIFICATION / DATA / PROCEDURE DIVISION | ✅ |
WORKING-STORAGE SECTION / level-77 items | ✅ |
PIC: 9 X A S V Z * plus repetition (n) | ✅ parsed; V scale is recorded only (see limitations) |
VALUE initial value (numeric / string / figurative constant) | ✅ |
MOVE (numeric↔numeric / char→numeric / numeric→char, char fields padded with spaces) | ✅ |
COMPUTE dst = expr (+ - * / **, parentheses, precedence) | ✅ |
ADD / SUBTRACT / MULTIPLY / DIVIDE (with GIVING) | ✅ |
DISPLAY (multiple operands, WITH NO ADVANCING, UPON) | ✅ |
IF … THEN … ELSE … END-IF and the period-terminated form | ✅ |
EVALUATE … WHEN … WHEN OTHER … END-EVALUATE (incl. EVALUATE TRUE) | ✅ |
PERFORM paragraph / PERFORM A THRU B | ✅ |
PERFORM para UNTIL cond (test before, COBOL default) | ✅ |
PERFORM para VARYING i FROM a BY b UNTIL c (nestable) | ✅ |
PERFORM n TIMES … END-PERFORM (inline block) | ✅ |
STRING … DELIMITED BY SIZE … INTO … | ✅ |
CALL "name" USING … GIVING … (reaches ui_* / tty_* / shared libraries) | ✅ |
ACCEPT (numeric item via input_int, char item via gets) | ✅ |
STOP RUN / GOBACK / EXIT / CONTINUE / NEXT SENTENCE | ✅ |
| Sequential paragraph execution (fall-through) — COBOL's execution model | ✅ |
STOP RUN inside a paragraph (ends the process even when PERFORM'd) | ✅ |
Not supported yet (each has a criterion in bugs/vml_cobol_bugs/, see COBOL问题清单.md): scaled decimals (V) in arithmetic, OCCURS tables, MOVE CORRESPONDING, COPY expansion, STRING … DELIMITED BY SPACE/literal, file I/O (OPEN/READ/WRITE), COMP-3 packed decimal, SORT/SEARCH/INSPECT/UNSTRING, section-level PERFORM.
2. How the two formats are decided
The rule lives in SourceFormat.cs (a pure function, pinned directly by the probe):
Scan every non-blank physical line; if any line has a letter in columns 1–6, it is free format; otherwise fixed format.
- Fixed-format code always starts at column 8 (columns 1–7 hold sequence/indicator/blank) ⇒ never triggers
- Free-format code starts at column 1 (a few columns of indent at most) ⇒ always triggers
- Fixed format with sequence numbers (
000100 IDENTIFICATION …) has digits in columns 1–6 ⇒ still fixed
Escape hatch: >>SOURCE FORMAT IS FREE / >>SOURCE FORMAT IS FIXED (the first one wins).
3. Execution model (COBOL paragraph semantics, one-to-one)
main: CALL para1 ; CALL para2 ; … ; exit ← this is COBOL's sequential / fall-through execution
paraN: prologue ; statements… ; epilogue(RET) ← every paragraph is a subroutine that returns
PERFORM P ⇒ CALL P
PERFORM A THRU B ⇒ CALL A ; CALL B ← every paragraph in the range runs, none outside it
The mapping is exact, not approximate: in COBOL "fall-through" and "PERFORM returns" look contradictory, and "main calls each paragraph once + each paragraph returns" expresses both at the same time (criteria: 20-fallthrough.cob pins fall-through, 17-perform-thru.cob pins the THRU boundary).
4. Data and calling convention
- All data items live in the data section (COBOL data is static; there is no recursion): a numeric item is one 4-byte word, a character item is
Length+1bytes (NUL included, pre-filled with spaces). - Assigning to a character item goes through
str_padend(dst, src, totalLen, space)— that is how "COBOL character items are fixed length and pad with spaces" is implemented (MOVE "AB" TO X(5)thenDISPLAYprintsAB). - Cross-type
MOVE(numeric ↔ character) usesint_to_str/str_to_int. CALL "x" USING a bis just VMLCALL(arguments pushed in reverse,__stdcallconvention).
5. Build and run
The csproj is Exe during development (switch back to Library for release, see INTEGRATION.md):
# end-to-end on one case (inlines the standard library; vmlcli can then assemble+run it)
dotnet run --project third_party/vml/VMLPrepares/CobolCompiler -- prog.cob -o prog.vml
# see what the lexer actually read (handy for fixed-format column trimming / PIC pictures)
dotnet run --project third_party/vml/VMLPrepares/CobolCompiler -- --tokens prog.cob
# inline the standard library into the output (used by the dev-time probe; see run.sh)
dotnet run --project third_party/vml/VMLPrepares/CobolCompiler -- --inline prog.cob -o prog.vml
# criteria
scripts/vml-cobol-probe/run.sh
6. Files
| File | Role |
|---|---|
SourceFormat.cs | physical line → logical line (format detection, column trimming, continuation, inline comments) — pure functions |
Lexer.cs | tokenizer (reads a PIC picture string as one token) |
Parser.cs | syntax → AST (two word tables decide where a statement ends) |
CodeGenerator.cs | AST → VML |
CobolCompiler.cs / CobolPlugin.cs / Program.cs | the three entry points / plugin / CLI |
COBOL_LANGUAGE_SPEC.md | language subset spec (what you can write, and what it means) |
INTEGRATION.md | wiring checklist (which file, which line) |