← 都来码 / Dolaima← 例程索引ladder/demo/demo_std.ld原文

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