# V3 step 5.2 — HIGH/LEN + Strings/TextIO stdlib (done 2026-09-22) Second stdlib landing. Suite 90/90 (47 run — incl. 4 trap-aborts, 4 sessions, 2 session-output, 1 unit — + 43 reject). LL(1)-clean, zero gm2 warnings. ## HIGH / LEN builtins Pulled forward from step 6 because string libraries need the descriptor length at runtime: - Fixed array: `HIGH`/`LEN` fold to immediates from the static bounds (`ArrayHi`/`ArrayLen`). - Open array (formal): read the header — `LEN` = `loadl` count, `HIGH` = `loadl count - 1` (new `QbeGen.LoadCount`). - Non-array → 217. Works on nested designators (`HIGH(m[i])`), since `Design` yields the inner descriptor address. - `t_highlen` → 18 (`HIGH(a)+LEN(a)+F(a)` with a 5-element array). ## Standard library (`stdlib/`, classic form) - `sysio.def/.mod` — `Write`, `WriteLn`, `WriteInt`, `WriteChar` over the shim externals (moved out of `tests/` into `stdlib/`). - `strings.def/.mod` — `Length`, `Assign`, `Concat`, `Compare`, `Copy`. The descriptor header holds the **logical length**: `Assign`/`Concat` call the new shim `m2setlen` so `Length` reports content, not capacity (otherwise a fixed `ARRAY [0..15]` would always report 16). Writes are capacity-bounded (truncate, never overrun). - `textio.def/.mod` — ISO-style `WriteString`/`WriteLn`/`WriteInt`/ `WriteCard`/`WriteChar`, binding the shim directly (avoids the flatten-on-import name shadowing that would make a wrapper named the same as its SysIO target recurse). ## Shim additions (`runtime/syslib/shim.c`) `m2setlen(desc, n)` (set logical length), `m2writechar(c)`. ## Harness `build_session` now accepts paths (anything containing `/` is used as-is), so sessions mix `../stdlib/*.def|mod` with `tests/*`. New `expect_run_files_out` checks the first output line as well as the exit code (`Hello` → `Hello, Modula-2!`, `StringsProg` → `Hello World`). ## Tests `Hello` (output), `StringsProg` (assign/concat/length/compare → `Hello World`, `11`, `1`), `t_highlen` → 18; suite 90/90. ## Deferred to 5.3+ `WholeIO`, `Math`, `Files`, `Storage`/`SYSTEM`; explicit external link names; string escapes; input (`Read`). Traps still `$abort`.