ladder/demo/demo_std.ld · 10005 字节 · 下载原文
(* demo_std.ld —— Ladder 第一层:**标准输入输出**(std)
demo_std.ld -- Ladder layer one: **standard input/output** (std)
Ladder(梯形图)是 IEC 61131-3 的**工业控制**语言 —— PLC 那一套。
Ladder (ladder diagram) is IEC 61131-3's **industrial control** language -- the PLC world.
它的"输出"不是控制台图形,而是四条**裸打印语句**:
Its "output" is not console graphics but four **bare print statements**:
PRINT_STR "字符串"
PRINT_STR "string"
PRINT_INT 整数表达式
PRINT_INT integer expression
PRINT_FLOAT 浮点表达式
PRINT_FLOAT float expression
PRINT_CHAR 字符码
PRINT_CHAR character code
这四条是前端**专门认的**(`Parser.IsBarePrintStatement`),
These four are **specially recognized** by the frontend (`Parser.IsBarePrintStatement`);
它们是 Ladder 的"标准输出"那一层。
they are Ladder's "standard output" layer.
⚠ **2026-10-01 更正**:这里原写着「别的函数名一律被编成 `CALL LADDER_<名字>` 而
(!) **Corrected 2026-10-01**: this used to say "every other function name is compiled to `CALL LADDER_<name>` and
链不上任何东西 ⇒ 图形与宿主 UI 两层**整层不可达**」—— 那曾经是真的,现在**不是**了。
links against nothing => the graphics and host UI layers are **entirely unreachable**" -- that was once true, it **is not** anymore.
两条缺陷都已修(① 调用语句被静默丢弃;② 库调用实参反转且只传 4 个),
Both defects are fixed (1. call statements were silently dropped; 2. library-call arguments were reversed and only 4 were passed),
现在 Ladder 能正常调 `ui_*`,第四层例程见本目录的 **`demo_ui.ld`**。
so Ladder can now call `ui_*` normally; see **`demo_ui.ld`** in this directory for the layer-four example.
(`demo_tty.ld` 讲的是 ANSI 彩色那一层,与本条无关。)
(`demo_tty.ld` covers the ANSI-color layer instead, and is unrelated to this note.)
跑法(桌面 vmlcli):
dotnet scripts/vmlcli/bin/Release/net10.0/vmlcli.dll Examples/ladder/demo_std.ld
期望输出(逐字节):
Expected output (byte-for-byte):
=== Ladder 标准输出 demo ===
=== Ladder stdout demo ===
字符串: 你好,世界
string: hello, world (the label and the text are printed in Chinese)
整数: 42
integer: 42
计算: 7 * 6 = 42
computed: 7 * 6 = 42
A - B * 2 = 7
整除: 17 / 5 = 3
integer division: 17 / 5 = 3
实数: 2.5
=== done ===
◆ 三条本前端的写法要求(实测)
Three syntax requirements of this frontend (measured)
① 程序骨架是 `PROGRAM <名> / VAR … END_VAR / BEGIN … END_PROGRAM`,
1. The program skeleton is `PROGRAM <name> / VAR ... END_VAR / BEGIN ... END_PROGRAM`,
每条语句以 `;` 结尾,注释用 `(* … *)`。
each statement ends with `;`, and comments use the paren-star form.
② 变量**必须**在 `VAR` 段声明(`R : INT;`)—— 直接写 `R := 1` 会报
2. Variables **must** be declared in the `VAR` section (`R : INT;`) -- writing `R := 1` directly reports
「未声明的变量 'R'」。这点与 BASIC/Pascal 不同,别下意识省掉。
"undeclared variable 'R'". This differs from BASIC/Pascal, so do not habitually leave it out.
③ ⚠ **标签(`LBL_x:`)仍会吃掉它后面的语句**(2026-10-04 复验仍成立)。
3. (!) **A label (`LBL_x:`) still swallows the statement after it** (re-checked 2026-10-04: still true).
实测最小复现(`Examples/ladder/` 下现写的,两行之差):
Smallest reproduction (written under `Examples/ladder/`; the two lines differ by one token):
S := 7; PRINT_INT S; ⇒ 打出 7 ✅
S := 7; PRINT_INT S; => prints 7 OK
S := 7; LBL_A: PRINT_INT S; ⇒ 什么都不打 ❌
S := 7; LBL_A: PRINT_INT S; => prints nothing FAIL
~~同批还有一条:`IF <条件> THEN … END_IF` 的**块体不执行**~~ ✅ **已修好(2026-10-04 复验)**:
~~Also from that batch: the **body of `IF <cond> THEN ... END_IF` did not execute**~~ (fixed; re-checked 2026-10-04):
`IF S = 1 THEN … END_IF`(S:=1)与 `IF B THEN … END_IF`(B:=TRUE)的块体都正常执行、`ELSE` 分支也对。
the bodies of `IF S = 1 THEN ... END_IF` (S:=1) and `IF B THEN ... END_IF` (B:=TRUE) both run fine, and the `ELSE` branch is correct too.
循环也不必靠标签 —— `WHILE … DO … END_WHILE`、`FOR i := a TO b DO … END_FOR` 都能用
Loops no longer need labels either -- `WHILE ... DO ... END_WHILE` and `FOR i := a TO b DO ... END_FOR` both work
(实测 `WHILE I < 5 DO S := S + I; … END_WHILE` ⇒ S = 10;`FOR` 求和 ⇒ 15)。
(measured: `WHILE I < 5 DO S := S + I; ... END_WHILE` => S = 10; a `FOR` sum => 15).
本文件仍写成**直线代码**(写法无害,见下条)。
This file is still written as **straight-line code** (harmless; see the next item).
◆ ⚠ **一条算术缺陷**(本文件刻意绕开,实测)
(!) **One arithmetic defect** (this file deliberately avoids it; measured)
✅ ~~① **`MOD` 只取右操作数**(不是取模)~~ **已修好(2026-10-04 复验)**:
OK ~~1. **`MOD` keeps only the right operand** (not a modulo)~~ **fixed (re-checked 2026-10-04)**:
`17 MOD 5` ⇒ **2**(旧记 5)、`5 MOD 17` ⇒ **5**(旧记 17)、`R := 17 MOD 5` ⇒ R = 2。
`17 MOD 5` => **2** (used to read 5), `5 MOD 17` => **5** (used to read 17), `R := 17 MOD 5` => R = 2.
⚠ ② **浮点的二元运算只取右操作数**(2026-10-04 复验仍成立):
(!) 2. **Binary float operations keep only the right operand** (re-checked 2026-10-04: still true):
`3.0 * 100.0` ⇒ 100、`100.0 * 3.0` ⇒ 3、`3.0 + 100.0` ⇒ 100、`100.0 + 3.0` ⇒ 3
本条的形状是**右操作数活下来、左操作数与运算符一起消失**,
The shape is **the right operand survives while the left operand and the operator both disappear**,
而**整数的 `+ - * /` 都是对的**(`7 * 6` = 42、`17 / 5` = 3、`17 - 5 * 2` = 7)。
whereas **integer `+ - * /` are all correct** (`7 * 6` = 42, `17 / 5` = 3, `17 - 5 * 2` = 7).
⇒ 所以本文件里:浮点只打印**字面量**(取模现在可以用了,但本文件照旧没用 —— 写法无害)。
=> so in this file: floats are only printed as **literals** (`MOD` works now, but this file still avoids it -- harmless).
(顺带一提,`PRINT_FLOAT 2.5` 打出来是 `2.5`。原注说"Ladder 是本批五门语言里
(By the way, `PRINT_FLOAT 2.5` prints `2.5`. The old note said "Ladder is the only one of this batch of five languages
唯一能把浮点打对的一门"—— 2026-10-04 复验**已不再唯一**:BASIC 的 `PRINT 2.5` ⇒ `2.5`、
that prints floats correctly" -- as of the 2026-10-04 re-check that is **no longer the only one**: BASIC's `PRINT 2.5` => `2.5`,
Fortran 的 `print *, 2.5` ⇒ `2.5`,两家的浮点打印也都修好了。)
and Fortran's `print *, 2.5` => `2.5`; float printing is fixed in both of them now.)
*)
PROGRAM DemoStd
VAR
A : INT;
B : INT;
C : INT;
(* 界面语言:0 = 中文 / 1 = 英文(`ui_scr_get_language` 是宿主 syscall,开局查一次存起来)。
Language: 0 = Chinese / 1 = English (query the host syscall once at startup). *)
LANG : INT;
END_VAR
BEGIN
LANG := ui_scr_get_language();
IF LANG = 0 THEN PRINT_STR "=== Ladder 标准输出 demo ===" ELSE PRINT_STR "=== Ladder stdout demo ===" END_IF;
PRINT_CHAR 10;
(* ① 字符串字面量(含中文 —— 源码按 UTF-8 存,词法器直通)
-- 1. String literal (contains Chinese -- the source is stored as UTF-8 and the lexer passes it straight through) -- *)
IF LANG = 0 THEN PRINT_STR "字符串: 你好,世界" ELSE PRINT_STR "String: hello, world" END_IF;
PRINT_CHAR 10;
(* ② 整数
-- 2. Integers -- *)
IF LANG = 0 THEN PRINT_STR "整数: " ELSE PRINT_STR "Integer: " END_IF;
PRINT_INT 42;
PRINT_CHAR 10;
(* ③ 常量算术(表达式直接进打印语句)
-- 3. Constant arithmetic (the expression goes straight into the print statement) -- *)
IF LANG = 0 THEN PRINT_STR "计算: 7 * 6 = " ELSE PRINT_STR "Computed: 7 * 6 = " END_IF;
PRINT_INT 7 * 6;
PRINT_CHAR 10;
(* ④ 变量算术 —— 先在 VAR 段声明,再赋值,再打印。
-- 4. Variable arithmetic -- declare in the VAR section first, assign, then print.
顺带把优先级也验一下:A - B * 2 = 17 - 10 = 7
(also verifies precedence: A - B * 2 = 17 - 10 = 7) *)
A := 17;
B := 5;
C := A - B * 2;
PRINT_STR "A - B * 2 = ";
PRINT_INT C;
PRINT_CHAR 10;
(* ⑤ 整除(`/` 在整数上是整除;`MOD` 现已修好,见文件头,这里照旧不用)
-- 5. Integer division (`/` on integers is integer division; `MOD` is fixed now, see the file header, still unused here) -- *)
IF LANG = 0 THEN PRINT_STR "整除: 17 / 5 = " ELSE PRINT_STR "Integer division: 17 / 5 = " END_IF;
PRINT_INT 17 / 5;
PRINT_CHAR 10;
(* ⑥ 浮点:只打**字面量**(浮点的二元运算也是坏的,见文件头)
-- 6. Floats: only **literals** are printed (binary float operations are broken too, see the file header) -- *)
IF LANG = 0 THEN PRINT_STR "实数: " ELSE PRINT_STR "Real: " END_IF;
PRINT_FLOAT 2.5;
PRINT_CHAR 10;
PRINT_STR "=== done ===";
PRINT_CHAR 10;
END_PROGRAM