# dBase

xBase 系（dBASE III/IV、FoxPro、Clipper 风格）的 `.prg`：`IF` / `DO WHILE` / `FOR` / `DO CASE`、`PROCEDURE` / `FUNCTION` / `RETURN` / `DO … WITH`，以及一组字符串 / 数值内置函数。

«bold»大小写不敏感«/»；任何未定义的函数名一律按库函数发 `CALL`，所以 `ui_*` / `tty_*` / `dbf_*` 直接就能写。

## 在手机上怎么跑

```
vml run examples/dbase/fizzbuzz.prg
```

编译要等一会儿（C 那种要一两分钟，脚本类语言几秒）。程序跑起来后**屏幕底部就是手柄**，
方向键 + 四个动作键都在；点右上角返回可以回到命令行。

## 写法要点

- 输出用 `?`（带换行）与 `??`（不带换行）
- `ui_*` / `tty_*` / `dbf_*` «bold»直接写«/»就行（未定义的名字一律编译成 `CALL <原名>`，由链接器解析）
- 字符串拼接用 `+`，数值转字符串用 `STR()`
- 注释：整行 `*`（行首）、行内 `&&`
- 读写 DBF 数据文件用现成的 `dbf_*` 库函数（`dbf_open` / `dbf_records` / `dbf_close`）

## 示例

| 文件 | 演示什么 |
|---|---|
| `fizzbuzz.prg` | FizzBuzz（`FOR` + `MOD` + `?`） |
| `primes.prg` | 2..40 的素数（`FUNCTION` + 循环 + 逻辑返回） |
| `strings.prg` | 内置字符串函数 + `DO … WITH` 调过程 |

## 实测踩过的坑

- ⚠ «bold»`#` 是不等号，所以本前端有意不跑 C 预处理器«/» —— 别指望 `#include` 那一套；`a # b` 就是「a 不等于 b」。
- ⚠ «bold»类型是编译期推的，而且流不敏感«/»：同一个变量在不同分支被赋成不同类型时，«bold»全程序按最后那个类型«/»算 —— 要么避免，要么显式 `INT()` 转换。
- ⚠ «bold»没有数组 / 下标«/»（`[` 是«bold»字符串引号«/»）；没有日期类型；`&var` 宏替换不做。
- ⚠ 数据库命令（`USE` / `SELECT` / `INDEX` / `APPEND` / `REPLACE` / `LOCATE` / `SKIP`…）、屏幕布局 `@ 行,列 SAY`、行输入 `ACCEPT` / `INPUT` / `WAIT` 都是«bold»识别到就整行跳过«/»（不报错、也不生成代码）—— 要判断某一行有没有真的生效，«bold»看编译产物里有没有对应指令«/»。
- 库函数名«bold»保留原始拼写«/»（不跟着大小写归并），要一字不差地交给链接器。

---

下面的内容是**从 VML 源码里直接带的**（`third_party/vml/VMLPrepares/DBaseCompiler/`）：
`README` 讲这个前端支持什么、怎么编；`语言规范` 讲语法本身。
上游一改，这里重新生成就是最新的。

## 语言规范

### dBase（xBase）语言子集规范

> 本文档描述 «bold»都来码 VML 平台«/»的 dBase 前端«bold»支持的那一部分语言«/»。
> 它是"做子集"的规范：«bold»这里没写的东西不一定不支持，但这里写的都支持并已跑通«/»。
> 英文版见 `DBASE_LANGUAGE_SPEC.en.md`。

> «bold»版本«/»：v1.0 | «bold»日期«/»：2026-10-13 | «bold»修订者«/»：深圳市探索智能科技有限公司

#### 规范标准

| 字段 | 值 |
|:-----|:----|
| «bold»目标标准«/» | dBase/xBase 子集 |
| «bold»发布年份«/» | — |
| «bold»完成度«/» | 子集（未逐项统计） |
| «bold»测试«/» | 19 通过 / 0 失败 / 1 已知红 |

#### 关键字

`if` `else` `elseif` `endif` `endi` `do` `while` `enddo` `endd` `for` `to`
`step` `next` `endfor` `case` `otherwise` `endcase` `exit` `loop` `return`
`procedure` `proc` `function` `with` `store` `public` `private` `local`

#### 概述
支持 dBase/xBase 子集，编译为 VML 汇编代码。dBase/xBase 是«bold»事实标准«/» —— 它没有正式的标准编号，各家实现（dBASE III/IV、FoxPro、Clipper…）互有出入，所以本文只描述本前端«bold»实际接受«/»的那一部分。前端«bold»大小写不敏感«/»（词法器按大写归并；«bold»唯一例外«/»是库函数名，它们要一字不差地交给链接器，故保留原始拼写）；«bold»有意不跑 C 预处理器«/»（dBase 里 `#` 是«bold»不等号«/»，一见 `#` 就跑预处理会把每个不等号当成指令、静默改语义）；数据库命令字（`USE` / `SELECT` / `COUNT` / `TOTAL`…）«bold»不进关键字表«/»（它们同时是极常见的变量名，进去会让 `total = 0` 被当成命令整行跳掉），改在语句层按标识符文本识别并«bold»整行跳过«/»。内置函数之外的名字一律发 `CALL <原名>`，由链接器解析到共享库。

#### 支持的语言特性

本前端接受的语言特性按下方原有章节组织，每节都写明「收什么写法、是什么语义」：

- «bold»词法«/»：注释（行首 `*`、行内 `&&`、行首 `NOTE`）、大小写规则、字面量（整数 / 浮点 / 三种引号的字符串 / 逻辑 `.T.`）、运算符与优先级、类型提升 —— §1
- «bold»语句«/»：输出、赋值、声明（= 登记变量）、条件、循环、多分支、跳出 / 继续、子程序 —— §2
- «bold»内置函数«/»：`LEN` / `UPPER` / `LOWER` / `SUBSTR` / `STR` / `VAL` / `INT` / `MOD` / `ABS` / `ROUND` —— §3
- «bold»库函数直呼«/»：`ui_*` / `tty_*` / `dbf_*` 直接写，编译成 `CALL <原名>` —— §4
- «bold»调用约定«/»：实参从右到左压栈、调用方清栈，返回值在 `R0`（Int/Str/Bool）或 `D0`（Float） —— §5
- «bold»明确不支持«/»：数组与下标、日期类型、宏替换 `&var`、数据库命令形式、`@ 行,列 SAY`、行输入 `ACCEPT` / `INPUT` / `WAIT` —— §6

详见下方各节。

#### 1. 词法

##### 1.1 注释
- «bold»整行注释«/»：`*` 在«bold»行首«/»（前导空白之后）到行尾。
- «bold»行内注释«/»：`&&` 到行尾。
- `NOTE …` 在«bold»行首«/»也是注释。

> `*` 在别处是«bold»乘号«/»（`x = 5 * 2`）——判据是"是不是本行第一个非空白字符"。

##### 1.2 大小写
dBase «bold»大小写不敏感«/»：关键字（`IF`/`Do`/`endif`）、内置函数名、用户变量名/过程名
都按大写归并。«bold»唯一的例外«/»是«bold»库函数名«/»（`ui_2d_clear` / `dbf_open` 这种）——
它们要一字不差地交给链接器，所以«bold»保留原始拼写«/»。

##### 1.3 字面量
| 形态 | 例子 | 说明 |
|:--|:--|:--|
| 整数 | `42`、`-7` | 走 «bold»Int«/» 轨（4 字节） |
| 浮点 | `3.14`、`1.5e3`、`.5` | 走 «bold»Float«/» 轨（8 字节双精度） |
| 字符串 | `"abc"`、`'abc'`、`[abc]` | «bold»三种引号都收«/»（xBase 惯例：`[` 是引号，不是下标） |
| 逻辑 | `.T.` / `.F.`（也收 `.Y.` / `.N.`） | 值 `1` / `0` |

字符串里的双写引号表示一个引号字面量：`"say ""hi"""` → `say "hi"`。

##### 1.4 运算符（按优先级从低到高）
| 优先级 | 运算符 |
|:--|:--|
| 1（最低） | `.OR.` |
| 2 | `.AND.` |
| 3 | `=` `==` `<>` `#` `!=` `<` `<=` `>` `>=` |
| 4 | `+` `-` |
| 5 | `*` `/` `%` |
| 6 | `**` `^`（右结合） |
| 7（最高） | 一元 `-` `+` `.NOT.` |

- `/` «bold»恒为实数除法«/»（`10 / 3` = `3.3333333`）；整除用 `INT(a / b)` 或 `MOD`。
- `%` 是整数取模（`MOD(17,5)` = `2`）。
- `**` / `^` 是幂：两边都整数走 `ipow`，任一侧浮点走 `pow`。
- `.NOT.` 的优先级高于比较（与 C 的 `!` 同族）。

##### 1.5 类型提升
- 字符串参与 `+` ⇒ «bold»字符串拼接«/»（任一侧是串即可）。其余算术里出现字符串按整数（指针）算。
- 算术：任一侧是 Float ⇒ 结果 Float；否则 Int。
- 比较、逻辑运算结果一律 «bold»Bool«/»（0/1）。

#### 2. 语句

语句以«bold»换行«/»终结（`;` 也当换行）。支持的语句：

##### 2.1 输出
```
? 表达式 [, 表达式 …]      * 打印后换行
?? 表达式 [, 表达式 …]     * 打印不换行（`:` 是 ?? 的同义写法）
?                          * 空 ? 只输出一个换行
```
表达式之间«bold»直接拼接«/»（不加分隔符）。字符串原样打印，整数按十进制，浮点按小数。

##### 2.2 赋值
```
x = 42
x := 42
STORE 42 TO x
STORE 42 TO a, b, c        * 一个值赋给多个变量
m.x = 42                   * `m.` 前缀（内存变量）—— 与本文件里对应
```
赋值按«bold»目标变量的声明类型«/»转换右值（见 README 的"类型模型"）。

##### 2.3 声明（本子集 = 登记变量）
```
PUBLIC a, b
PRIVATE c
LOCAL d
```

##### 2.4 条件
```
IF 条件
  …
ELSEIF 条件
  …
ELSE
  …
ENDIF
```
`ENDIF` 也写作 `ENDI`。

##### 2.5 循环
```
DO WHILE 条件          * 前置判断：条件为假则循环体一次都不跑
  …
ENDDO                  * 也写作 ENDD

FOR i = 起 TO 止 [STEP 步长]
  …
NEXT                   * 也写作 ENDFOR
```
- `STEP` 缺省为 `1`。
- «bold»负常量步长«/»（`STEP -1`）自动按«bold»下行«/»循环（`i >= 止`）；否则按上行（`i <= 止`）。
- ⚠ 步长是«bold»变量«/»时判不出方向（已知限制，见 `bugs/vml_dbase_bugs/` 的 KNOWN-D01）。

##### 2.6 多分支
```
DO CASE
  CASE 条件
    …
  CASE 条件
    …
  OTHERWISE
    …
ENDCASE
```
按顺序判第一个为真的 `CASE`；`OTHERWISE` 可省。

##### 2.7 跳出 / 继续
- `EXIT` —— 跳出最内层 `DO WHILE` / `FOR`。
- `LOOP` —— 回到最内层循环的条件判断处。

##### 2.8 子程序
```
PROCEDURE 名 [(形参, …)]
FUNCTION 名 [(形参, …)]
  …
  RETURN [表达式]
```
- `FUNCTION` 有返回值（无 `RETURN` 值时返回 0）；`PROCEDURE` 无返回值。
- «bold»可递归。«/»
- 形参在«bold»栈«/»上（`[R12+12+off]`），变量在«bold»数据段«/»（全局可见）。

调用：
```
DO 名 [WITH 实参, …]        * 当语句调（忽略返回值）
结果 = 名(实参, …)          * 当表达式调（用返回值）
```
实参个数与形参不一致时按传入的个数对齐（多传忽略、少传读到未定义值 —— 本子集不校验）。

#### 3. 内置函数

| 函数 | 返回 | 说明 |
|:--|:--|:--|
| `LEN(s)` | Int | 字符串长度（`strlen`） |
| `UPPER(s)` / `LOWER(s)` | Str | 转大写 / 小写（`cs_to_upper` / `cs_to_lower`） |
| `SUBSTR(s, 起 [, 长])` | Str | 子串，«bold»`起` 是 1 基«/»；省略 `长` 取到末尾 |
| `STR(n)` | Str | 数值转字符串（整数 `int_to_str`，浮点 `float_to_str`） |
| `VAL(s)` | Int | 字符串转整数（`str_to_int`） |
| `INT(x)` | Int | 截断取整（向零，浮点则 `D2I`） |
| `MOD(a, b)` | Int | 取模 |
| `ABS(x)` | 同参 | 绝对值（整数 `abs`，浮点 `fabs`） |
| `ROUND(x [, n])` | 同参 | 四舍五入到 `n` 位小数（缺省 `n=0`）。«bold»浮点用 `floor(x*10^n+0.5)/10^n`；整数原样«/» |

#### 4. 库函数直呼

任何«bold»不是«/»内置、也«bold»不是«/»用户定义的函数名，一律编译成 `CALL <原名>`，由链接器
解析到共享库。所以 `ui_2d_clear(0)`、`tty_puts("x")`、`dbf_open("a.dbf")` 都能直接写。

- 实参一律按 «bold»4 字节整数«/»传（浮点实参会被截断）—— 共享库接口绝大多数收整数/指针。
- 返回值当«bold»整数«/»用（字符串返回函数返回的是指针，可直接参与 `+` 拼接或 `STR` 之外的处理）。
- `Lib/dbase/builtin.vml` 已挂上打印簇、字符串、转换、数学、`dbf` 等模块。
  要用别的库，在 `Lib/dbase/builtin.vml` 里补一条 `.linked`。

#### 5. 与共享库的调用约定

子程序与库函数都走同一套：«bold»实参从右到左压栈«/»，被调方从 `[R12+12+4i]`（8 字节类型从对应偏移）读，
«bold»调用方清栈«/»。返回值在 `R0`（Int/Str/Bool）或 `D0`（Float）。

#### 6. 明确不支持（本子集）

- 数组 / 下标（`[` 是字符串引号）。
- 日期类型、宏替换 `&var`。
- 数据库命令形式（`USE` / `SELECT` / `INDEX` / `APPEND` / `REPLACE` / `LOCATE` / `SKIP` / …）
  —— 识别到就«bold»整行跳过«/»（不报错）。要读写 DBF 用 `dbf_*` 库函数。
- 屏幕布局命令 `@ 行,列 SAY …`（整行跳过）。
- 行输入 `ACCEPT` / `INPUT` / `WAIT`（整行跳过）。

> 跳过类命令«bold»不报错也不生成代码«/» —— 这是刻意的：老程序里这类命令遍地都是，
> 报错会把"能跑的程序"挡在门外。«bold»要判断某一行有没有真的生效，看编译产物里有没有对应指令。«/»

## 编译器 README

### dBase（xBase）编译器

«bold»路径«/»：`VMLPrepares/DBaseCompiler/`
«bold»语言 key«/»：`dbase` ｜ «bold»命名空间«/»：`DBaseCompiler` ｜ «bold»源码扩展名«/»：`.prg`
«bold»语言库«/»：`Lib/dbase/`（`builtin.vml` + 声明文件 `shared.prg`）
«bold»完成度«/»：🟢 子集生产可用（「完整运算 + 控制流 + 函数调用」齐备，能写一般程序）

#### 一句话

把 xBase 系（dBASE III/IV/FoxPro/Clipper 风格）的 `.prg` 源码编译成 VML，
覆盖注释、输出、赋值、四则/比较/逻辑运算、`IF`/`DO WHILE`/`FOR`/`DO CASE`、
`PROCEDURE`/`FUNCTION`/`RETURN`/`DO … WITH`，以及一组字符串/数值内置函数。

> «bold»别与 `dbf` 混淆«/»：仓库里的 `scripts/vml-dbf-probe/` 与 `Lib/shared/dbf.vml` 是
> «bold»DBF 数据文件格式«/»的库/探针，«bold»不是«/»一门语言。本目录是 «bold»dBase 语言前端«/»，两者互不相干。

#### 功能

- ✅ Lexer / Parser / CodeGenerator（继承 `CompilerBase` 的 `LexerBase` / `ParserBase<>` / `CodeGeneratorBase`）
- ✅ 注释：行首 `*`（整行）、行内 `&&`
- ✅ 输出：`?`（带换行）、`??`（不带换行）、可多表达式（逗号分隔）
- ✅ 赋值：`var = e`、`var := e`、`STORE e TO v1, v2 …`
- ✅ 声明：`PUBLIC` / `PRIVATE` / `LOCAL`（本子集里等于"登记变量"）
- ✅ 类型：整数（i32）、浮点（f64）、字符串、逻辑（`.T.`/`.F.`）
- ✅ 运算：`+ - * / % «bold»`（`^` 同 `«/»`）、比较 `= == <> # != < > <= >=`、逻辑 `.AND.`/`.OR.`/`.NOT.`
- ✅ 控制流：`IF…ELSEIF…ELSE…ENDIF`、`DO WHILE…ENDDO`（«bold»前置判断«/»）、`FOR…TO…STEP…NEXT`、`DO CASE…CASE…OTHERWISE…ENDCASE`、`EXIT`/`LOOP`
- ✅ 子程序：`PROCEDURE`/`FUNCTION`（可带形参、可递归）、`RETURN [值]`、`DO 名 [WITH 实参…]`
- ✅ 内置函数：`LEN` `UPPER` `LOWER` `SUBSTR` `STR` `VAL` `INT` `MOD` `ABS` `ROUND`
- ✅ 库函数直呼：任何未定义的函数名 → `CALL <原名>`（`ui_*` / `tty_*` / `dbf_*` … 直接可用）
- ✅ 大小写不敏感（关键字、标识符都按大写归并；库函数名«bold»保留原拼写«/»交链接器）
- ⚠️ 子集之外：数组、日期类型、宏替换 `&var`、`USE`/`SELECT`/`INDEX` 等 DB 命令形式（整行跳过）

#### 用法

```bash
# 开发期（本机的 csproj 是 Exe，可直接跑）
dotnet run --project VMLPrepares/DBaseCompiler -- foo.prg -o foo.vml

# 走 vmlcli（收尾接线后）
vmlcli foo.prg --lang dbase
```

#### 类型模型（重要）

dBase 是«bold»动态类型«/»语言；本前端做«bold»流不敏感«/»的静态近似：

| 轨道 | 宽度 | 寄存器 | 谁落在这条轨 |
|:--|:--|:--|:--|
| Int / Bool / Str | 4 字节 | `R0` | 整数、逻辑、字符串指针 |
| Float | 8 字节（双精度） | `D0` | 带小数点/指数的数值 |

- «bold»变量«/»一律落在«bold»数据段«/»（`var_<名>`）—— dBase 的内存变量本来就是全局可见的，
  这也省掉整套栈帧局部量管理。«bold»只有子程序形参«/»走栈（`[R12+12+off]`，与共享库的
  `__stdcall` 实参区同一套约定）。
- 每个表达式的类型由«bold»字面量形态 + 变量类型 + 函数返回类型«/»推断（`CodeGenerator.Analyze` 预扫描两趟）。
- «bold»赋值会按目标变量的声明类型转换«/» RHS（`x = 1` 而 `x` 是浮点时自动 `I2D`）。
- ⚠ 流不敏感：同一个变量在不同分支被赋成不同类型时，«bold»全程序按最后那个类型«/»算。要么避免，
  要么显式 `INT()` / 转换。

#### 与 `dbf` 库的关系

«bold»DBF 数据文件读写是既有库的活，本前端不重复实现。«/» `Lib/dbase/builtin.vml` 已
`.linked "../shared/dbf.vml"`，程序里直接：

```
h = dbf_open("data.dbf")
? "records=" + STR(dbf_records(h))
dbf_close(h)
```

`dbf_*` 是«bold»普通库函数«/»（未定义名 → `CALL dbf_xxx`），与上面那条"库函数直呼"同一条路。

#### 探针

```bash
scripts/vml-dbase-probe/run.sh          # 通过 19 / 失败 0 / 已知红 1（通道：vmlcli --lang dbase）
```

«bold»两段都走 `vmlcli`（真实通道）«/»：① `vmlcli <case.prg> --lang dbase --vml <tmp>.vml`（编译+汇编+链接）、
② `vmlcli <tmp>.vml`（装载运行）。«bold»别用«/» `scripts/vml-dbase-probe/runner/` 那份自带工装当判据 ——
它自己拼链接集、会补上 vmlcli 缺的模块，从而掩盖「`Lib/dbase/` 缺模块集」这类缺陷（首次接线时正是它漏报的）。

> ⚠ «bold»`Lib/dbase/` 必须有自己的模块集«/»（`dotnet run --project third_party/vml/tools/GenLib -- -m -l dbase -r third_party/vml`）。
> 否则产物头部的裸名 `.linked`（`console.vml` …）会解析到«bold»别家语言«/»的模块上、链接期报一串
> 「未定义的函数 'printx_int32'」。详见 `INTEGRATION.md` 第 ⓿ 步。

#### 相关文档

- 语言规范：`DBASE_LANGUAGE_SPEC.md`（英文版 `DBASE_LANGUAGE_SPEC.en.md`）
- 接进 vmlcli / 手机端的接线清单：`INTEGRATION.md`
- 缺陷清单：`bugs/vml_dbase_bugs/DBASE问题清单.md`
