ladder/ui/demo_ui.ld · 13078 字节 · 下载原文
(* demo_ui.ld —— Ladder 第四层:**最新 UI 接口**(`ui_*`)
demo_ui.ld -- Ladder layer four: the **newest UI API** (`ui_*`)
## 这份文件为什么现在才存在
## Why this file exists only now
此前 Ladder **没有** UI 例程,而且**写不出来**。`demo_std.ld` 的头注释里写着
Previously Ladder had **no** UI example and **could not produce one**. The header comment of `demo_std.ld` says
「图形(BGI)与宿主 UI 两层**整层不可达**」,理由是调用点给一切函数名加了
"the graphics (BGI) and host UI layers are **entirely unreachable**", because the call site added a
`LADDER_` 前缀 —— 那一半 2026-10-01 已经修好(`CalleeSymbol()`:只有**本程序
`LADDER_` prefix to every function name -- that half was fixed on 2026-10-01 (`CalleeSymbol()`: only functions
自己定义的函数**才加前缀,库函数按源码原样传)。**但修完仍然调不到**,
**defined by this program** get the prefix; library functions are passed through as written). **But it still could not be called**,
因为下面两条独立缺陷把调用链彻底堵死:
because the two independent defects below blocked the call chain completely:
① **调用语句被静默丢弃**(`Parser.ParseStStatement`)
1. **Call statements were silently dropped** (`Parser.ParseStStatement`)
标识符分支只认 `x := …` 与 `a[i] := …`,读完 `ui_2d_clear` 撞上 `(` 既不是 `[`
The identifier branch recognized only `x := ...` and `a[i] := ...`; after `ui_2d_clear` it hit a `(` which is
也不是 `:=` ⇒ 落进末尾的「跳过无法识别的语句」,**整条语句消失**(不报错、
neither `[` nor `:=`, so it fell into the trailing "skip unrecognized statement" path and **the whole statement vanished** (no error,
不生成代码)。⇒ Ladder 只能用**表达式位置**的调用(`W := ui_scr_get_w();`),
no code generated). => Ladder could call the library only from **expression position** (`W := ui_scr_get_w();`),
而 UI 那一层**全是只要副作用的 void 调用**,于是整层不可达。
while the UI layer is **all void calls whose only purpose is a side effect**, so the layer was unreachable.
实测(修前):`ui_2d_clear(0);` 编得过、跑得完、退出码 0,
Measured (before the fix): `ui_2d_clear(0);` compiled, ran, exit code 0,
`--trace-draw` **一条绘制都没有**(正对照:同一段程序的 C 版本有 3 行)。
yet `--trace-draw` showed **not a single draw** (control: the C version of the same program shows 3 lines).
② **库调用的实参传错**(`CodeGenerator.Networks.Visit(CallNode)`)
2. **Library-call arguments were passed wrong** (`CodeGenerator.Networks.Visit(CallNode)`)
库函数按 C ABI 取实参(形参 i 在 `[R12+12+4i]`,即**倒序压栈**),
Library functions take arguments per the C ABI -- formal i sits at `[R12+12+4i]`, i.e. **pushed in reverse order** --
而当时走的是 `EmitCallWithRegSave`:它按 **R0,R1,R2,R3** 的顺序压 ⇒
but back then the call went through `EmitCallWithRegSave`, which pushes in the order **R0,R1,R2,R3** =>
栈顶成了 R3 ⇒ **前四个实参整段反转**;且**只压 4 个槽** ⇒ 第 5~8 个实参
the top of stack became R3 => **the first four arguments were fully reversed**; and it **pushed only 4 slots** => arguments 5-8
根本传不过去。实测 `ui_2d_rect(10,10,50,50,0xFFFF0000,1,0,0)` 宿主收到的是
never got through at all. Measured: `ui_2d_rect(10,10,50,50,0xFFFF0000,1,0,0)` arrived at the host as
`(50,50) 10x10 0x00000000 fill=1 lw=46793` —— 与 `x=arg3,y=arg2,w=arg1,h=arg0`
`(50,50) 10x10 0x00000000 fill=1 lw=46793` -- matching `x=arg3,y=arg2,w=arg1,h=arg0`
逐项吻合(`EmitCdeclCall` 一直是现成的正确实现,属于本仓头号形态
item for item (`EmitCdeclCall` had always been a correct ready-made implementation -- this repo's number-one pattern,
「助手已存在但被绕过」)。
"the helper already exists but is bypassed").
两条叠在一起:**看着能调 UI,其实一个像素都没画对**。现在两条都修了,
With the two stacked together, **it looked like the UI could be called, yet not one pixel was drawn correctly**. Both are fixed now,
本文件就是判据 —— `scripts/vml-ui-bindings-probe.sh ladder` 会跑它。
and this file is the probe -- `scripts/vml-ui-bindings-probe.sh ladder` runs it.
## Ladder 侧的写法要求(与 `demo_std.ld` 同一套,本文件照样绕开两个坏地方)
## Ladder-specific rules (same as demo_std.ld; this file also avoids the two broken spots)
① 骨架 `PROGRAM <名> / VAR … END_VAR / BEGIN … END_PROGRAM`,语句以 `;` 结尾,
1. Skeleton: `PROGRAM <name> / VAR ... END_VAR / BEGIN ... END_PROGRAM`, statements end with `;`,
注释用 `(* … *)`;变量**必须**在 `VAR` 段声明(顺手写 `R := 1` 会报「未声明的变量」)。
comments use the paren-star form; variables **must** be declared in the `VAR` section (writing `R := 1` off-hand reports "undeclared variable").
② 调用语句(`ui_2d_rect(...);`)与表达式位置的调用(`W := ui_scr_get_w();`)**都可用**;
2. Both call statements (`ui_2d_rect(...);`) and calls in expression position (`W := ui_scr_get_w();`) **work**;
**标签(`LBL_x:`)仍会吃掉它后面的语句**(2026-10-04 复验仍成立);而
a **label (`LBL_x:`) still swallows the statement that follows it** (re-checked 2026-10-04: still true); and
~~`IF … THEN … END_IF` 的**块体不执行**~~ ✅ **已修好(2026-10-04 复验)**(`WHILE`/`FOR` 循环也随之可用)
~~the **body of `IF ... THEN ... END_IF` did not execute**~~ (fixed; re-checked 2026-10-04) (`WHILE`/`FOR` loops work too)
⇒ 本文件仍写成**直线代码**(写法无害;见 `demo_std.ld` 的证据)。
=> this file is still written as **straight-line code** (harmless; see the evidence in `demo_std.ld`).
③ **常量在 Ladder 里只能写成字面量**:`Lib/ladder/vml_ui.ld` 把 147 条常量
3. **Constants can only be written as literals in Ladder**: `Lib/ladder/vml_ui.ld` lists 147 constants
列成注释(`(* VML_ANCHOR_LEFT = 0 *)`)—— 这门前端**没有外部声明语法**,
as comments (e.g. `VML_ANCHOR_LEFT = 0` written inside a comment) -- this frontend **has no external-declaration syntax**,
所以下面直接用 0/1(`0` = 左对齐,`1` = 填充)。别去 `#include` 那个文件。
so the code below just uses 0/1 (`0` = left-aligned, `1` = filled). Do not `#include` that file.
④ ✅ **`MOD` 已修好(2026-10-04 复验:`17 MOD 5` ⇒ 2)**;⚠ 浮点的二元运算仍只取右操作数
4. `MOD` is fixed (re-checked 2026-10-04: `17 MOD 5` => 2); note that binary float operations still keep only the right operand
(见 `demo_std.ld`)⇒ 本文件只用整数算布局。
(see `demo_std.ld`) => this file computes layout with integers only.
注意颜色字面量 `0xFF1A1A24` 这类**是正常的** —— 十六进制整数与整数 `+ - * /` 都对。
Note that color literals such as `0xFF1A1A24` **are fine** -- hex integers and integer `+ - * /` all work.
⑤ ⚠⚠ **注释里不许再出现一个裸的"开注释记号"**(就是圆括号紧跟星号那两个字符)。
5. (!) **A bare "comment opener" must never appear inside a comment again** (that is, an open-paren followed by an asterisk).
本前端的块注释是**嵌套**计深的(对齐 CODESYS / TwinCAT,见 `Lexer.cs` 的 `ReadComment`),
This frontend's block comments are **nesting** (like CODESYS / TwinCAT; see `Lexer.cs`'s `ReadComment`),
而**到 EOF 都没闭合时一声不响** ⇒ 后面**整个文件**被吞进注释,症状是 `main` 只剩一条
and **if one never closes before EOF it says nothing** => the **whole rest of the file** is swallowed into the comment, the symptom being that `main` is left with only a single
`syscall #3`(所有语句静默消失,不报错)。实测:C 的文档注释里那句 markdown 粗体
`syscall #3` (every statement silently gone, with no error). Measured: that markdown bold phrase in a C doc comment,
「**0 means note off**」抄进 `Lib/ladder/vml_ui.ld` 就成了一个裸开标记 —— 于是
"**0 means note off**", was copied into `Lib/ladder/vml_ui.ld` and became a bare opener -- so
**`#include` 那个声明文件的程序全都不执行**(`GenLib` 已按 `LangSpec.CommentsNest`
**every program that `#include`s that declaration file stopped executing** (`GenLib` now splits the opener
把产物里的开标记拆开修好,但你**手写**注释时仍会踩)。要提它请写"圆括号星号"。
in generated output per `LangSpec.CommentsNest`, but you will still hit this when **hand-writing** comments). To mention it, write "paren-star".
跑法(桌面 vmlcli):
dotnet scripts/vmlcli/bin/Release/net10.0/vmlcli.dll Examples/ladder/demo_ui.ld
期望输出(逐字节):
demo_ui.ld: OK
*)
PROGRAM DemoUi
VAR
W : INT; (* 画布宽 —— 开窗之后才知道,见下面的 ui_scr_get_w
canvas width -- only known after the window is opened; see ui_scr_get_w below *)
H : INT; (* 画布高
canvas height *)
END_VAR
BEGIN
(* ── 开窗:ui_win_open(title, w, h) ─────────────────────────────────────
-- Open the window: ui_win_open(title, w, h) -- *)
(* 尺寸传 0 ⇒ **交给宿主用真实可用区**(开窗前只能估算,估算与实际总有出入,
-- pass size 0 ⇒ **the host uses the real available area** (before the open it can only estimate, and
那正是"内容偏上"的根因)⇒ 所以**尺寸只是申请值**:真正拿到多少,下一句问宿主。
the estimate always differs from reality -- exactly why the content sat high) ⇒ the size is only a **request**: how much you actually get is asked of the host in the next statement. -- *)
ui_win_open("demo_ui.ld (Ladder)", 0, 0);
(* ── 问宿主实际画布尺寸(**表达式位置**的调用,一直就能用)─────────────
-- Ask the host for the actual canvas size (a call in **expression position**, available all along) -- *)
W := ui_scr_get_w();
H := ui_scr_get_h();
(* ── 清屏:ui_2d_clear(color) ─────────────────────────────────────────────
-- Clear the screen: ui_2d_clear(color) -- *)
ui_2d_clear(0xFF101820);
(* ── 图元:ui_2d_rect(x, y, w, h, color, fill, lw, radius) ───────────────
-- Primitives: ui_2d_rect(x, y, w, h, color, fill, lw, radius) -- *)
(* 深色面板(用 W 算宽 —— 窄屏也不会溢出)
-- Dark panel (width computed from W, so it does not overflow on a narrow screen) -- *)
ui_2d_rect(16, 12, W - 32, 120, 0xFF1A1A24, 1, 0, 10);
(* 面板上的标题条
-- Title bar on the panel -- *)
ui_2d_rect(22, 18, W - 44, 24, 0xFF4A90D9, 1, 0, 6);
(* ── ui_2d_circle(cx, cy, r, color, fill, lw) ──────────────────────────── *)
ui_2d_circle(W / 2, 200, 46, 0xFFE06C50, 1, 0);
(* ── ui_2d_line(x1, y1, x2, y2, color, lw) ─────────────────────────────── *)
ui_2d_line(16, 268, W - 16, 268, 0xFF3A3A4A, 2);
(* ── 文字:ui_2d_text(x, y, s, color, size, anchor) ───────────────────────
-- Text: ui_2d_text(x, y, s, color, size, anchor) -- *)
(* anchor:0 = 左对齐(VML_ANCHOR_LEFT,见文件头第 ③ 条)
-- anchor: 0 = left-aligned (VML_ANCHOR_LEFT; see note 3 in the file header) -- *)
ui_2d_text(20, 22, "UI / demo_ui (Ladder)", 0xFFE8E8F0, 15, 0);
ui_2d_text(20, 44, "rect / line / circle / text", 0xFF9AA0B0, 12, 0);
(* ── 交帧 + 关窗:ui_2d_present 是"这一帧画完了"的唯一信号 ─────────────────
-- Present + close: ui_2d_present is the only signal that "this frame is done" -- *)
ui_2d_present();
ui_win_close();
(* 一行确认输出(判据脚本按它判断这份例程真的跑完了)
-- One confirmation line (the probe script uses it to judge that this example really finished) -- *)
PRINT_STR "demo_ui.ld: OK"
PRINT_CHAR 10
END_PROGRAM