← 都来码← 文档索引dBase原文English

dBase

xBase 系(dBASE III/IV、FoxPro、Clipper 风格)的 .prg:IF / DO WHILE / FOR / DO CASE、PROCEDURE / FUNCTION / RETURN / DO … WITH,以及一组字符串 / 数值内置函数。

大小写不敏感;任何未定义的函数名一律按库函数发 CALL,所以 ui_* / tty_* / dbf_* 直接就能写。

在手机上怎么跑

vml run examples/dbase/fizzbuzz.prg

编译要等一会儿(C 那种要一两分钟,脚本类语言几秒)。程序跑起来后屏幕底部就是手柄,方向键 + 四个动作键都在;点右上角返回可以回到命令行。

写法要点

示例

文件演示什么
fizzbuzz.prgFizzBuzz(FOR + MOD + ?)
primes.prg2..40 的素数(FUNCTION + 循环 + 逻辑返回)
strings.prg内置字符串函数 + DO … WITH 调过程

实测踩过的坑


下面的内容是从 VML 源码里直接带的(third_party/vml/VMLPrepares/DBaseCompiler/): README 讲这个前端支持什么、怎么编;语言规范 讲语法本身。上游一改,这里重新生成就是最新的。

语言规范

dBase(xBase)语言子集规范

本文档描述 都来码 VML 平台的 dBase 前端支持的那一部分语言。它是"做子集"的规范:这里没写的东西不一定不支持,但这里写的都支持并已跑通。英文版见 DBASE_LANGUAGE_SPEC.en.md。

版本:v1.0 | 日期:2026-10-13 | 修订者:深圳市探索智能科技有限公司

规范标准

字段值
目标标准dBase/xBase 子集
发布年份—
完成度子集(未逐项统计)
测试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 是事实标准 —— 它没有正式的标准编号,各家实现(dBASE III/IV、FoxPro、Clipper…)互有出入,所以本文只描述本前端实际接受的那一部分。前端大小写不敏感(词法器按大写归并;唯一例外是库函数名,它们要一字不差地交给链接器,故保留原始拼写);有意不跑 C 预处理器(dBase 里 # 是不等号,一见 # 就跑预处理会把每个不等号当成指令、静默改语义);数据库命令字(USE / SELECT / COUNT / TOTAL…)不进关键字表(它们同时是极常见的变量名,进去会让 total = 0 被当成命令整行跳掉),改在语句层按标识符文本识别并整行跳过。内置函数之外的名字一律发 CALL <原名>,由链接器解析到共享库。

支持的语言特性

本前端接受的语言特性按下方原有章节组织,每节都写明「收什么写法、是什么语义」:

详见下方各节。

1. 词法

1.1 注释

* 在别处是乘号(x = 5 * 2)——判据是"是不是本行第一个非空白字符"。

1.2 大小写

dBase 大小写不敏感:关键字(IF/Do/endif)、内置函数名、用户变量名/过程名都按大写归并。唯一的例外是库函数名(ui_2d_clear / dbf_open 这种)—— 它们要一字不差地交给链接器,所以保留原始拼写。

1.3 字面量
形态例子说明
整数42、-7走 Int 轨(4 字节)
浮点3.14、1.5e3、.5走 Float 轨(8 字节双精度)
字符串"abc"、'abc'、[abc]三种引号都收(xBase 惯例:[ 是引号,不是下标)
逻辑.T. / .F.(也收 .Y. / .N.)值 1 / 0

字符串里的双写引号表示一个引号字面量:"say ""hi""" → say "hi"。

1.4 运算符(按优先级从低到高)
优先级运算符
1(最低).OR.
2.AND.
3= == <> # != < <= > >=
4+ -
5* / %
6** ^(右结合)
7(最高)一元 - + .NOT.
1.5 类型提升

2. 语句

语句以换行终结(; 也当换行)。支持的语句:

2.1 输出
? 表达式 [, 表达式 …]      * 打印后换行
?? 表达式 [, 表达式 …]     * 打印不换行(`:` 是 ?? 的同义写法)
?                          * 空 ? 只输出一个换行

表达式之间直接拼接(不加分隔符)。字符串原样打印,整数按十进制,浮点按小数。

2.2 赋值
x = 42
x := 42
STORE 42 TO x
STORE 42 TO a, b, c        * 一个值赋给多个变量
m.x = 42                   * `m.` 前缀(内存变量)—— 与本文件里对应

赋值按目标变量的声明类型转换右值(见 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
2.6 多分支
DO CASE
  CASE 条件
    …
  CASE 条件
    …
  OTHERWISE
    …
ENDCASE

按顺序判第一个为真的 CASE;OTHERWISE 可省。

2.7 跳出 / 继续
2.8 子程序
PROCEDURE 名 [(形参, …)]
FUNCTION 名 [(形参, …)]
  …
  RETURN [表达式]

调用:

DO 名 [WITH 实参, …]        * 当语句调(忽略返回值)
结果 = 名(实参, …)          * 当表达式调(用返回值)

实参个数与形参不一致时按传入的个数对齐(多传忽略、少传读到未定义值 —— 本子集不校验)。

3. 内置函数

函数返回说明
LEN(s)Int字符串长度(strlen)
UPPER(s) / LOWER(s)Str转大写 / 小写(cs_to_upper / cs_to_lower)
SUBSTR(s, 起 [, 长])Str子串,起 是 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)。浮点用 floor(x*10^n+0.5)/10^n;整数原样

4. 库函数直呼

任何不是内置、也不是用户定义的函数名,一律编译成 CALL <原名>,由链接器解析到共享库。所以 ui_2d_clear(0)、tty_puts("x")、dbf_open("a.dbf") 都能直接写。

5. 与共享库的调用约定

子程序与库函数都走同一套:实参从右到左压栈,被调方从 [R12+12+4i](8 字节类型从对应偏移)读, 调用方清栈。返回值在 R0(Int/Str/Bool)或 D0(Float)。

6. 明确不支持(本子集)

跳过类命令不报错也不生成代码 —— 这是刻意的:老程序里这类命令遍地都是,报错会把"能跑的程序"挡在门外。要判断某一行有没有真的生效,看编译产物里有没有对应指令。

编译器 README

dBase(xBase)编译器

路径:VMLPrepares/DBaseCompiler/ 语言 key:dbase | 命名空间:DBaseCompiler | 源码扩展名:.prg 语言库:Lib/dbase/(builtin.vml + 声明文件 shared.prg) 完成度:🟢 子集生产可用(「完整运算 + 控制流 + 函数调用」齐备,能写一般程序)

一句话

把 xBase 系(dBASE III/IV/FoxPro/Clipper 风格)的 .prg 源码编译成 VML,覆盖注释、输出、赋值、四则/比较/逻辑运算、IF/DO WHILE/FOR/DO CASE、 PROCEDURE/FUNCTION/RETURN/DO … WITH,以及一组字符串/数值内置函数。

别与 dbf 混淆:仓库里的 scripts/vml-dbf-probe/ 与 Lib/shared/dbf.vml 是 DBF 数据文件格式的库/探针,不是一门语言。本目录是 dBase 语言前端,两者互不相干。

功能

用法

# 开发期(本机的 csproj 是 Exe,可直接跑)
dotnet run --project VMLPrepares/DBaseCompiler -- foo.prg -o foo.vml

# 走 vmlcli(收尾接线后)
vmlcli foo.prg --lang dbase

类型模型(重要)

dBase 是动态类型语言;本前端做流不敏感的静态近似:

轨道宽度寄存器谁落在这条轨
Int / Bool / Str4 字节R0整数、逻辑、字符串指针
Float8 字节(双精度)D0带小数点/指数的数值

与 dbf 库的关系

DBF 数据文件读写是既有库的活,本前端不重复实现。 Lib/dbase/builtin.vml 已 .linked "../shared/dbf.vml",程序里直接:

h = dbf_open("data.dbf")
? "records=" + STR(dbf_records(h))
dbf_close(h)

dbf_* 是普通库函数(未定义名 → CALL dbf_xxx),与上面那条"库函数直呼"同一条路。

探针

scripts/vml-dbase-probe/run.sh          # 通过 19 / 失败 0 / 已知红 1(通道:vmlcli --lang dbase)

两段都走 vmlcli(真实通道):① vmlcli <case.prg> --lang dbase --vml <tmp>.vml(编译+汇编+链接)、 ② vmlcli <tmp>.vml(装载运行)。别用 scripts/vml-dbase-probe/runner/ 那份自带工装当判据 —— 它自己拼链接集、会补上 vmlcli 缺的模块,从而掩盖「Lib/dbase/ 缺模块集」这类缺陷(首次接线时正是它漏报的)。

⚠ Lib/dbase/ 必须有自己的模块集(dotnet run --project third_party/vml/tools/GenLib -- -m -l dbase -r third_party/vml)。否则产物头部的裸名 .linked(console.vml …)会解析到别家语言的模块上、链接期报一串「未定义的函数 'printx_int32'」。详见 INTEGRATION.md 第 ⓿ 步。

相关文档