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.
Pulled forward from step 6 because string libraries need the descriptor length at runtime:
HIGH/LEN fold to immediates from the static
bounds (ArrayHi/ArrayLen).LEN = loadl count,
HIGH = loadl count - 1 (new QbeGen.LoadCount).HIGH(m[i])),
since Design yields the inner descriptor address.t_highlen → 18 (HIGH(a)+LEN(a)+F(a) with a 5-element array).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).runtime/syslib/shim.c)m2setlen(desc, n) (set logical length), m2writechar(c).
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).
Hello (output), StringsProg (assign/concat/length/compare →
Hello World, 11, 1), t_highlen → 18; suite 90/90.
WholeIO, Math, Files, Storage/SYSTEM; explicit external
link names; string escapes; input (Read). Traps still $abort.