# COBOL

«bold»两种书写格式都收«/»（经典固定格式与自由格式，«bold»自动判别«/»）。执行模型是 COBOL 的段落与«bold»落穿«/»：主流程把每段按顺序 `CALL` 一遍，`PERFORM` 就是 `CALL`。

「完整运算 + 控制流 + 段落 / `PERFORM` + 库调用」齐备，`CALL "ui_2d_clear" USING …` 能够到 `ui_*`。

## 在手机上怎么跑

```
vml run examples/cobol/demo/fizzbuzz.cob
```

编译要等一会儿（C 那种要一两分钟，脚本类语言几秒）。程序跑起来后**屏幕底部就是手柄**，
方向键 + 四个动作键都在；点右上角返回可以回到命令行。

## 写法要点

- 写固定格式记住列位：«bold»第 1–6 列是序号区、第 7 列是指示符、第 8–72 列才是代码«/»，第 73–80 列会被«bold»丢掉«/»
- 两种格式«bold»自动判别«/»（判据是「第 1–6 列有没有字母」）；判错了就在文件里写一行 `>>SOURCE FORMAT IS FREE`（或 `FIXED`）强制
- 注释：行内 `*>`（«bold»引号内的不算«/»）；固定格式的整行注释写在«bold»第 7 列«/»（`*` 或 `/`）
- «bold»句号 `.` 就是一条语句的结束«/»，COBOL 里它不可省
- 调 UI 用 `CALL "ui_2d_clear" USING …`（实参逆序压栈）

## 示例

| 文件 | 演示什么 |
|---|---|
| `demo/fizzbuzz.cob` | FizzBuzz（`PERFORM VARYING` + `COMPUTE` 取余 + `IF`） |
| `demo/hello.cob` | **固定格式**的最小程序 |
| `demo/report.cob` | 小报表（`PIC X(n)` 定长补空格 + 数值排版 + 累加） |

## 实测踩过的坑

- ⚠ «bold»定标小数（`PIC 9(2)V99`）不做换算«/» —— `V` 只记账，本子集的算术是«bold»整数«/»算术：`COMPUTE P = 10 / 4` 得 `2`（标准 COBOL 得 `2.50`）。
- ⚠ «bold»`OCCURS` 表没有数组语义«/» —— `T(1)` 与 `T(2)` 打到«bold»同一个格子«/»（有警告，但结果就是错的）。
- `MOVE CORRESPONDING` «bold»编译期报错«/»；`COPY` 认得出库名但«bold»不展开«/»。
- `STRING … DELIMITED BY SPACE/字面量` 一律按 `SIZE`（整段）处理。
- «bold»文件 I/O 与排序 / 检查未做«/»：`OPEN`/`READ`/`WRITE`/`CLOSE`/`SORT`/`SEARCH`/`INSPECT`/`UNSTRING` 都在«bold»编译期报「还不支持」«/»（不静默）。
- `COMP-3` 打包十进制认得出子句，但«bold»按普通整数存«/»。
- `SECTION` 头当段落头处理，`PERFORM 段名` 只跑段内«bold»第一段«/»；`ACCEPT … FROM 环境名` 里 `FROM` 后面«bold»整段被忽略«/»（只从标准输入读）。
- `/` 是«bold»整除«/»（`7 / 3` = `2`）；字面量里写小数会被«bold»截断«/»并给一条警告。

---

下面的内容是**从 VML 源码里直接带的**（`third_party/vml/VMLPrepares/CobolCompiler/`）：
`README` 讲这个前端支持什么、怎么编；`语言规范` 讲语法本身。
上游一改，这里重新生成就是最新的。

## 语言规范

### COBOL 子集语言规格

> 面向«bold»写程序的人«/»：本前端收什么写法、那些写法是什么语义、哪些地方会跟标准 COBOL 不一样。
> 实现细节见 `README.md`，接线见 `INTEGRATION.md`。

版本：v1（2026-10-13）· 扩展名 `.cob`（也收 `.cbl`）· 探针 `scripts/vml-cobol-probe/run.sh`

---

> «bold»版本«/»：v1.0 | «bold»日期«/»：2026-10-13 | «bold»修订者«/»：深圳市探索智能科技有限公司

#### 规范标准

| 字段 | 值 |
|:-----|:----|
| «bold»目标标准«/» | COBOL-85 子集 |
| «bold»发布年份«/» | 1985 |
| «bold»完成度«/» | 子集（未逐项统计） |
| «bold»测试«/» | 28 通过 / 0 失败 / 4 已知红 |

#### 关键字

`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`

#### 概述
支持 COBOL-85 子集，编译为 VML 汇编代码。源码先经 `CobolSourceFormat` 把«bold»物理行折叠成逻辑行«/»（固定格式的 1–6 列序号区、第 7 列指示符、A 区 / B 区、第 73–80 列按标准丢弃、`-` 续行，与自由格式，两种«bold»自动判别«/»、也可被 `>>SOURCE FORMAT IS FREE|FIXED` 显式覆盖），再交给词法器；由于 COBOL 的分词«bold»不靠标点«/»（`MOVE 1 TO X ADD 1 TO Y.` 可以连着写），解析器靠「下一个词是不是动词 / 子句词」来判定语句边界，«bold»未实现的动词响亮报错«/»、不静默跳过。数值一律 32 位整数，库靠 `Lib/cobol/builtin.vml` 里的 `.linked` 自动挂上。

#### 支持的语言特性

本前端接受的语言特性按下方原有章节组织，每节都写明「收什么写法、是什么语义」：

- «bold»两种书写格式«/»：固定格式（序号区 / 第 7 列指示符 / A 区 / B 区 / 第 73–80 列丢弃 / 续行）与自由格式；判别规则可反证，`>>SOURCE FORMAT IS …` 可显式覆盖 —— §1
- «bold»程序结构«/»：`IDENTIFICATION` / `ENVIRONMENT` / `DATA` / `PROCEDURE` 四个 DIVISION、`SECTION` 与段落（含「落穿」）、`PROGRAM-ID` —— §2
- «bold»数据项与 PIC«/»：层号、`PIC` / `PICTURE`、`USAGE`、`REDEFINES`、`OCCURS`、`VALUE`、`FILLER` —— §3
- «bold»语句«/»：赋值与算术（`MOVE` / `COMPUTE` / `ADD` / `SUBTRACT` / `MULTIPLY` / `DIVIDE`）、输出（`DISPLAY`）、条件与分支（`IF`、`EVALUATE`）、循环与段落调用（`PERFORM`）、字符串拼接（`STRING`）、调库（`CALL`）、输入（`ACCEPT`）、结束（`STOP RUN` / `GOBACK` / `EXIT` / `CONTINUE`） —— §4
- «bold»注释«/»：固定格式第 7 列 `*` / `/` / `D`，两种格式都认行内 `*>`（引号内的不算） —— §5
- «bold»数字与取整«/»：32 位整数、`/` 是整除、`**` 是幂 —— §6
- «bold»已知限制«/»：定标小数（`V`）不换算、`OCCURS` 无数组语义、`COPY` 不展开、文件 I/O / 排序 / 检查未做 —— §7

详见下方各节。

#### 1. 两种书写格式

COBOL 有两种源码版式，«bold»本前端自动判别、两种都收«/»。

##### 固定格式（经典）

```
列号  123456|7|8...........11|12.....................72|73..80
含义  序号区 |指示符|  A 区      |        B 区            | 丢弃
```

- «bold»1–6 列«/»：序号区（老式行号，可空）
- «bold»7 列«/»：指示符 —— 空白 = 正常代码行；`*` 或 `/` = 整行注释；`D`（或 `d`）= 调试行（当注释）；`-` = «bold»续行«/»（本行第 8 列起接到上一行尾巴上）
- «bold»8–11 列（A 区）«/»：段落名、`SECTION`、`01`/`77` 级数据项、`FD` 等
- «bold»12–72 列（B 区）«/»：普通语句
- «bold»73–80 列«/»：按标准«bold»丢弃«/»（本前端照办：`23-col72-limit.cob` 压的就是它）

##### 自由格式

代码从第 1 列（或缩进几格）开始，按空白分词。注释是 `*>`。

##### 判别规则（可反证）

> 扫所有非空行，«bold»任意一行的第 1–6 列出现字母«/» ⇒ 自由格式；否则 ⇒ 固定格式。

理由：固定格式的代码必然在第 8 列以后，1–6 列不会有字母；自由格式必然从第 1 列附近开始。
带序号的固定格式（`000100 IDENTIFICATION …`）1–6 列是数字 ⇒ 仍是固定格式。

«bold»强制指定«/»（自动判别猜错时的出口，取文件里第一个出现的）：

```
>>SOURCE FORMAT IS FREE
>>SOURCE FORMAT IS FIXED
```

---

#### 2. 程序结构

```cobol
       IDENTIFICATION DIVISION.
       PROGRAM-ID. 名字.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01 数据名 PIC 图象.      *> 数据项
       77 独立项 PIC 图象.
       PROCEDURE DIVISION.
       段落名.                    *> 段落（"名字." 单独一行）
           语句…
           STOP RUN.
```

- `IDENTIFICATION / DATA / PROCEDURE DIVISION` 必须有（缺了也能编，但建议写全）
- `PROGRAM-ID` 后面那个名字只是标识，不影响编译
- `WORKING-STORAGE SECTION` 下用 `01`/`77` 级声明数据；`88` 级（条件名）认得出但«bold»不建存储«/»
- `PROCEDURE DIVISION` 里，`名字.` 单独一行就是一个«bold»段落«/»

#### 3. 数据项与 PIC

```
       01 WS-N    PIC 9(3).            *> 3 位整数
       01 WS-NAME PIC X(10).           *> 10 个字符（定长）
       01 WS-AMT  PIC S9(5)V99.        *> 带符号 + 隐含小数（见第 7 节）
       01 WS-CNT  PIC 9(3) VALUE 0.    *> 带初值
```

图象串支持：`9`（数字）、`X`/`A`（字符）、`S`（符号）、`V`（隐含小数位）、`Z`/`*`（编辑位）
以及重复次数 `(n)`；其余编辑符号（`B` `/` `,` `+` `-` `$`）认得出但不改变长度语义。

«bold»数值项«/»是 4 字节整数；«bold»字符项«/»是定长缓冲，初值按 COBOL 惯例«bold»补空格到 n«/»。
`VALUE` 的初值也照此处理：`01 S PIC X(5) VALUE "AB".` 打印出来是 `AB   `（5 格）。

#### 4. 语句

##### 赋值与算术

```cobol
           MOVE 5 TO WS-N.
           MOVE "AB" TO WS-NAME.          *> 短串补空格到定长
           MOVE WS-N TO WS-NAME.          *> 数值 → 字符（转成文字）
           MOVE WS-NAME TO WS-N.          *> 字符 → 数值（解析回整数）
           COMPUTE WS-N = (A + B) * 2.    *> 支持 + - * / ** 与括号、优先级
           ADD 5 TO WS-N.                 *> WS-N = WS-N + 5
           ADD 3 5 TO WS-N.               *> WS-N = WS-N + 3 + 5
           ADD 5 TO WS-N GIVING WS-M.     *> WS-M = WS-N + 5（WS-N 不变）
           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（整除）
           DIVIDE 7 BY 3 GIVING WS-M.     *> WS-M = 7 / 3 = 2
```

«bold»有 `GIVING` 时目标只被写入；没有 `GIVING` 时目标自身参与运算«/» —— 这条是 COBOL 的原义，别记反。

##### 输出

```cobol
           DISPLAY "TOTAL=" WS-N.        *> 逐项连着打，末尾换行
           DISPLAY "X" WITH NO ADVANCING.  *> 不换行
```

`DISPLAY` 的各个操作数«bold»之间不加空格«/»（COBOL 就是这样，要空格就自己写进字面量）。

##### 条件

```cobol
           IF A > B THEN DISPLAY "BIG" ELSE DISPLAY "SMALL" END-IF.
           IF A = 1 THEN DISPLAY "ONE".        *> 句点收尾（老式写法）也认
```
关系运算符两种写法都收：`=` `<` `>` `<=` `>=` `<>`，以及 `IS [NOT] EQUAL TO` /
`IS GREATER THAN` / `IS LESS THAN`。条件可以 `AND` / `OR` / `NOT` 组合、可加括号。
裸操作数当条件（`IF FLAG`）按"非 0 即真"处理。

##### 分支

```cobol
           EVALUATE K
               WHEN 1   DISPLAY "ONE"
               WHEN 3   DISPLAY "THREE"
               WHEN OTHER DISPLAY "OTHER"
           END-EVALUATE.
```

##### 循环与段落调用

```cobol
           PERFORM 段落.                                   *> 调用一次，到段落末尾返回
           PERFORM A THRU B.                               *> 跑 A…B 之间每一段
           PERFORM 段落 UNTIL WS-I > 10.                    *> 先测后做（COBOL 默认）
           PERFORM 段落 VARYING WS-I FROM 1 BY 1 UNTIL WS-I > 10.
           PERFORM 3 TIMES … END-PERFORM.                   *> 行内块，跑 3 次
```

⚠ «bold»循环变量结束时是"最后一次的值 + 一步"«/»（`UNTIL WS-I > 5` 跑完 1..5 之后 `WS-I = 6`）——
这是 COBOL 的定义（先加步长再判条件），不是缺陷。

##### 段落与"落穿"

COBOL 的段落是«bold»顺次执行«/»的：从第一段开始，一段完了接着下一段，
除非 `STOP RUN` / `GOBACK` / `PERFORM` 让控制流离开。

```cobol
       PROCEDURE DIVISION.
       P1.
           ADD 1 TO N.
       P2.
           ADD 10 TO N.          *> 不写 PERFORM，主流程也会从 P1 落到这里
           STOP RUN.
```

`PERFORM P` 则«bold»只跑 P«/»、跑完就返回（不会落穿到 P 的下一段）。

##### 字符串拼接

```cobol
           STRING "HELLO" DELIMITED BY SIZE "WORLD" DELIMITED BY SIZE INTO WS-S.
```

`DELIMITED BY` 后面写什么目前都按 `SIZE`（整段）处理 —— `BY SPACE` 那种"遇空格截断"还没做。

##### 调库（`CALL`）

```cobol
           CALL "abs" USING -9 GIVING WS-R.        *> math 库
           CALL "strlen" USING WS-NAME GIVING WS-R.  *> string 库
           CALL "ui_2d_clear" USING WS-COLOR.       *> 宿主绘图接口
```

`CALL "名字" USING a b` 就是把实参按顺序递给那个库函数（VML 的 `__stdcall` 约定），
返回值（若有）在 `GIVING` 那一项里。«bold»这是 COBOL 够到 `ui_*` / `tty_*` / 全部共享库的门。«/»
默认自动挂上的库见 `Lib/cobol/builtin.vml`（含 `vmlui` / `tty` / `vml3d`）。

##### 输入

```cobol
           ACCEPT WS-N.        *> 数值项：读一个整数
           ACCEPT WS-NAME.     *> 字符项：读一行
```

##### 结束

`STOP RUN.`（结束程序）/ `GOBACK.` / `EXIT.` / `CONTINUE.`（空语句）。
段落里也能用 `STOP RUN` —— 从 `PERFORM` 里进来的一样会真正结束进程。

#### 5. 注释

| 位置 | 写法 |
|---|---|
| 固定格式整行 | 第 7 列写 `*` 或 `/` |
| 固定格式调试行 | 第 7 列写 `D`/`d`（当注释） |
| 行内（两种格式） | `*>` 起，到行尾为止 |
| 自由格式整行 | 行首（任意缩进）写 `*>` |

⚠ «bold»字符串字面量里的 `*>` 是正文，不是注释«/»（`DISPLAY "A*>B"` 打的是 `A*>B`）。

#### 6. 数字与取整

- 全部数值是 «bold»32 位整数«/»；`/` 是«bold»整除«/»（`7 / 3 = 2`）
- 字面量里写小数（`1.5`）会被«bold»截断成整数«/»并给一条«bold»警告«/»（不静默）
- `«bold»` 是幂运算（`2 «/» 8 = 256`，走库函数 `ipow`）

#### 7. 已知限制（与标准 COBOL 的差异）

| 限制 | 表现 | 判据 |
|---|---|---|
| «bold»定标小数（`V`）不做换算«/» | `PIC 9(2)V99` 只当整数用；`COMPUTE P = 10 / 4` 得 `2`（标准 COBOL 得 `2.50`） | `bugs/vml_cobol_bugs/cb01` |
| «bold»`OCCURS` 表不支持«/» | `T(1)` 与 `T(2)` 打到同一个格子（«bold»有警告«/»），数组语义全无 | `cb02` |
| «bold»`MOVE CORRESPONDING` 不支持«/» | 编译期«bold»报错«/»（不猜） | `cb03` |
| «bold»`STRING … DELIMITED BY SPACE/字面量`«/» | 一律按 `SIZE`（整段）处理 | `cb04` |
| «bold»`COPY` 不展开«/» | 认得出库名，但不真把副本插进来 | — |
| «bold»文件 I/O / 排序 / 检查«/» | `OPEN/READ/WRITE/CLOSE/SORT/SEARCH/INSPECT/UNSTRING` 编译期报"还不支持" | — |
| «bold»`COMP-3` 打包十进制«/» | 认得出子句，但按普通整数存 | — |
| «bold»`SECTION` 的段级 PERFORM«/» | `SECTION` 头当段落头处理；`PERFORM 段名` 只跑段内«bold»第一段«/» | — |
| «bold»`ACCEPT … FROM 环境名`«/» | `FROM` 后面整段被忽略（只从标准输入读） | — |

这些都在 `bugs/vml_cobol_bugs/COBOL问题清单.md` 里逐条登记（含复现与实测数字）。

#### 8. 完整的可运行例子

`third_party/vml/Examples/cobol/demo/`：

| 文件 | 演示 |
|---|---|
| `hello.cob` | 固定格式的最小程序 |
| `fizzbuzz.cob` | `PERFORM VARYING` + 嵌套 `IF` + 取余 |
| `report.cob` | 定长字符项补空格 + 数值排版 + 累加 |

## 编译器 README

### COBOL 编译器（子集）

«bold»路径«/»：`third_party/vml/VMLPrepares/CobolCompiler/`
«bold»扩展名«/»：`.cob`（也收 `.cbl`）
«bold»语言库«/»：`Lib/cobol/`（`builtin.vml`）
«bold»完成度«/»：能写一般程序（完整运算 + 控制流 + 函数调用 + 段落/PERFORM + 库调用）
«bold»探针«/»：`scripts/vml-cobol-probe/run.sh` —— «bold»通过 28 / 失败 0 / 已知红 4«/»（2026-10-13 实测；28 = 27 条语言判据 + 1 条载体判据）

---

#### 一、这个前端收什么

| 能力 | 支持情况 |
|---|---|
| «bold»固定格式«/»（1–6 列序号区 / 7 列指示符 / 8–72 列代码） | ✅ 自动判别，见下 |
| «bold»自由格式«/»（第 1 列起写代码） | ✅ 自动判别；`>>SOURCE FORMAT IS FREE/FIXED` 可强制 |
| 第 7 列 `*` `/` 整行注释、`D` 调试行 | ✅ |
| 第 7 列 `-` «bold»续行«/» | ✅（字符串字面量可以断在续行上） |
| 行内注释 `*>` | ✅（«bold»引号内的 `*>` 不算«/»） |
| 第 73–80 列按标准丢弃 | ✅ |
| `IDENTIFICATION / DATA / PROCEDURE DIVISION` | ✅ |
| `WORKING-STORAGE SECTION` / `77` 级独立项 | ✅ |
| `PIC`：`9` `X` `A` `S` `V` `Z` `*` + 重复 `(n)` | ✅ 解析；`V` 的定标只记账（见「已知限制」） |
| `VALUE` 初值（数值/字符串/表意常量） | ✅ |
| `MOVE`（数值↔数值 / 字符→数值 / 数值→字符，«bold»字符项按 COBOL 语义补空格«/»） | ✅ |
| `COMPUTE dst = expr`（`+ - * / **` 与括号、优先级） | ✅ |
| `ADD / SUBTRACT / MULTIPLY / DIVIDE`（含 `GIVING`） | ✅ |
| `DISPLAY`（多项、`WITH NO ADVANCING`、`UPON`） | ✅ |
| `IF … THEN … ELSE … END-IF` / «bold»句点收尾«/»两种写法 | ✅ |
| `EVALUATE … WHEN … WHEN OTHER … END-EVALUATE`（含 `EVALUATE TRUE`） | ✅ |
| `PERFORM 段` / `PERFORM A THRU B` | ✅ |
| `PERFORM 段 UNTIL 条件`（«bold»先测后做«/»，COBOL 默认） | ✅ |
| `PERFORM 段 VARYING i FROM a BY b UNTIL c`（可嵌套） | ✅ |
| `PERFORM n TIMES … END-PERFORM`（行内块） | ✅ |
| `STRING … DELIMITED BY SIZE … INTO …` | ✅ |
| `CALL "名字" USING … GIVING …`（«bold»够到 `ui_*` / `tty_*` / 共享库«/»） | ✅ |
| `ACCEPT`（数值项走 `input_int`、字符项走 `gets`） | ✅ |
| `STOP RUN` / `GOBACK` / `EXIT` / `CONTINUE` / `NEXT SENTENCE` | ✅ |
| «bold»段落顺次执行（落穿）«/» —— COBOL 的执行模型 | ✅ |
| 段落里直接 `STOP RUN`（从 PERFORM 里也能真正结束进程） | ✅ |

«bold»尚未支持«/»（每条都在 `bugs/vml_cobol_bugs/` 有对应判据，见 `COBOL问题清单.md`）：
定标小数（`V`）的算术、`OCCURS` 表、`MOVE CORRESPONDING`、`COPY` 展开、
`STRING … DELIMITED BY SPACE/字面量`、文件 I/O（`OPEN/READ/WRITE`）、
`COMP-3` 打包十进制、`SORT`/`SEARCH`/`INSPECT`/`UNSTRING`、`SECTION` 的段级 PERFORM 语义。

---

#### 二、两个格式怎么判

`SourceFormat.cs` 里的规则（«bold»纯函数，探针直接压«/»）：

> 扫一遍非空物理行，«bold»只要有一行的第 1–6 列出现了字母«/» ⇒ 自由格式；否则 ⇒ 固定格式。

- 固定格式的代码必然落在第 8 列及以后（1–7 列是序号/指示符/空白）⇒ «bold»不会«/»触发
- 自由格式的代码从第 1 列开始（最多缩进几格）⇒ «bold»必然«/»触发
- 带序号的固定格式（`000100 IDENTIFICATION …`）1–6 列是«bold»数字«/» ⇒ 仍是固定格式

判错的出口：文件里写 `>>SOURCE FORMAT IS FREE` / `>>SOURCE FORMAT IS FIXED`（取第一个出现的）。

#### 三、执行模型（COBOL 段落语义，逐条对应）

```
main:  CALL 段1 ; CALL 段2 ; … ; 退出       ← 这就是 COBOL 的"顺次执行/落穿"
段i:   序言 ; 语句… ; 尾声(RET)              ← 每个段落都是一段带返回的子程序
PERFORM P          ⇒ CALL P
PERFORM A THRU B   ⇒ CALL A ; CALL B         ← 区间内每一段都跑，区间外的不跑
```

这套映射是«bold»精确«/»的，不是近似：COBOL 里"落穿"与"PERFORM 会返回"本来是一对矛盾，
用「主流程把每段 CALL 一遍 + 每段各自 RET」正好同时表达出来
（判据：`20-fallthrough.cob` 压落穿、`17-perform-thru.cob` 压 THRU 的区间边界）。

#### 四、数据与调用约定

- 数据项一律放«bold»数据段«/»（COBOL 的数据是静态的、没有递归）：数值项一个 4 字节字，
  字符项 `Length+1` 字节（含 NUL，«bold»初值整段填空格«/»）。
- 字符项赋值走 `str_padend(dst, src, 总长, 空格)` —— 这是"COBOL 的定长字符项
  要补空格"那一条的实现（`MOVE "AB" TO X(5)` 之后 `DISPLAY` 出来是 `AB   `）。
- 数值 ↔ 字符的跨类型 `MOVE` 走 `int_to_str` / `str_to_int`。
- `CALL "x" USING a b` 就是 VML 的 `CALL`（实参逆序压栈、`__stdcall` 约定）。

#### 五、构建与运行

开发期 csproj 是 `Exe`（发行时改回 `Library`，见 `INTEGRATION.md`）：

```bash
# 端到端跑一个用例（内联标准库，产物可被 vmlcli 直接汇编运行）
dotnet run --project third_party/vml/VMLPrepares/CobolCompiler -- prog.cob -o prog.vml

# 看词法器真正读到了什么（固定格式列裁剪 / PIC 图象串现形用）
dotnet run --project third_party/vml/VMLPrepares/CobolCompiler -- --tokens prog.cob

# 把标准库**内联**进产物（开发期探针用；见 run.sh 的说明）
dotnet run --project third_party/vml/VMLPrepares/CobolCompiler -- --inline prog.cob -o prog.vml

# 判据
scripts/vml-cobol-probe/run.sh
```

#### 六、文件

| 文件 | 作用 |
|---|---|
| `SourceFormat.cs` | «bold»物理行 → 逻辑行«/»（固定/自由判别、列裁剪、续行、行内注释）—— 纯函数 |
| `Lexer.cs` | 记号化（含 `PIC` 图象串的整体读取） |
| `Parser.cs` | 语法 → AST（子句词/动词两张表管"一句话到哪儿结束"） |
| `CodeGenerator.cs` | AST → VML |
| `CobolCompiler.cs` / `CobolPlugin.cs` / `Program.cs` | 三条对外入口 / 插件 / CLI |
| `COBOL_LANGUAGE_SPEC.md` | 语言子集规格（给用户看的：能写什么、写出来是什么语义） |
| `INTEGRATION.md` | «bold»接线清单«/»（哪个文件加哪一行） |
