# V3 step 5.1 — foreign functions + SysIO (done 2026-09-22) First stdlib landing: the compiler can now call the host. Suite 88/88 (45 run — incl. 4 trap-aborts, 4 sessions, 1 session-output — + 43 reject). LL(1)-clean, zero gm2 warnings. ## EXTERNAL procedures (FFI) - Grammar: `PROCEDURE P(...); EXTERNAL;` — a body-less procedure in an implementation. `MarkExternal` flags the current heading; its header/scope are discarded (`AbortFunc`), so no function is emitted. - SymTab: `ext` + `link` on the procedure node; `MarkExternal`, `IsExternal`, `ProcLink`. The flag rides the node, and the implementation resumes the *definition's* node (`ResumeProc`), so clients that `IMPORT`/`FROM`-import the routine also see it as external. - QbeGen: `Mangled` binds an external procedure to its link name (default: its own name — a valid C symbol; `m2write` etc.). - **No static link for C callees**: `CallBegin`/`CallEnd` gained an `isExt` flag; the hidden static-link argument is omitted for external calls (the shim signature has no link). ## Runtime shim (`runtime/syslib/shim.c`) Small C layer that knows the compiler's memory layout. A string (`ARRAY OF CHAR`) value is a descriptor `{ LONGCARD count, bytes }`; the compiler passes its address as an `l`. Shim exports: `m2write(desc)`, `m2writeln()`, `m2writeint(v)`, `m2exit(code)`. Linked into every session image by the harness. ## SysIO library (`tests/sysio.def` + `.mod`) Classic `DEFINITION`/`IMPLEMENTATION` (separate compilation): `Write(VAR s : ARRAY OF CHAR)`, `WriteLn`, `WriteInt(n)`, each a thin Modula-2 wrapper over an `EXTERNAL` shim routine. This also exercises passing a **string literal** as a `VAR ARRAY OF CHAR` actual: literals now register their static descriptor address in the actual-address note table. ## Tests `Hello` → stdout `Hello, Modula-2!` then `42`, exit 0 — the first V3 program with real output, checked by the new `expect_run_files_out` harness (exit code + first output line). Harness refactor: `build_session` (compile→qbe→cc with the shim) shared by `expect_run_files` / `expect_run_files_out`. ## Deferred to 5.2+ `SYSTEM`/`Storage` (`malloc`/`free` wrappers), `Strings`, `TextIO`, `WholeIO`, `Math`, `Files`; explicit external link names (`EXTERNAL "c_name"`); string escapes (so literals are raw bytes — newlines come from `WriteLn`); reading input.