Browse Source

library: NumberIO/RealInOut/FpuIO; resolve shipped libs in the harness

- corpus harness fidelity: V3_LIBS now defaults to V3's own shipped
  libraries (stdlib + runtime/syslib) and only SYSTEM/libc are treated
  as built-ins without a .def; a file importing Strings/IOChan/StrIO/...
  resolves against the real .def instead of being mislabelled LIB
  (corpus 427 -> 447).
- add the PIM/gm2 I/O family NumberIO, RealInOut, FpuIO (StrIO/InOut
  already shipped): a new shim m2writefix backs the fixed-point writers.
  Regression t_pimio (exit 42).  Corpus 447 -> 467.

Gates: suite 249/249, fixpoint OK (3,882,337 bytes), corpus 467/612,
0 crashes.
Eric Streit 3 days ago
parent
commit
d243d252ad

+ 1 - 0
.gitignore

@@ -8,3 +8,4 @@ tools/topspeed-grammar/build/
 
 
 # gap reproducers: listings
 # gap reproducers: listings
 docs/gaps/*.LST
 docs/gaps/*.LST
+*.tmp

+ 9 - 0
compiler/run_tests.sh

@@ -333,6 +333,15 @@ expect_run_files TStreamFile 42 \
   ../stdlib/chanconsts.def ../stdlib/chanconsts.mod \
   ../stdlib/chanconsts.def ../stdlib/chanconsts.mod \
   ../stdlib/streamfile.def ../stdlib/streamfile.mod \
   ../stdlib/streamfile.def ../stdlib/streamfile.mod \
   t_streamfile.mod
   t_streamfile.mod
+expect_run_files TPimIO 42 \
+  ../runtime/syslib/SysShim.def ../runtime/syslib/SysShim.mod \
+  ../stdlib/inout.def ../stdlib/inout.mod \
+  ../stdlib/strio.def ../stdlib/strio.mod \
+  ../stdlib/numberio.def ../stdlib/numberio.mod \
+  ../stdlib/realio.def ../stdlib/realio.mod \
+  ../stdlib/realinout.def ../stdlib/realinout.mod \
+  ../stdlib/fpuio.def ../stdlib/fpuio.mod \
+  t_pimio.mod
 expect_run_files WrapProg 42 \
 expect_run_files WrapProg 42 \
   ../runtime/syslib/SysShim.def ../runtime/syslib/SysShim.mod \
   ../runtime/syslib/SysShim.def ../runtime/syslib/SysShim.mod \
   ../stdlib/iochan.def ../stdlib/iochan.mod \
   ../stdlib/iochan.def ../stdlib/iochan.mod \

+ 14 - 0
compiler/tests/t_pimio.mod

@@ -0,0 +1,14 @@
+MODULE TPimIO;
+(* PIM/gm2 I/O library family: StrIO, NumberIO, RealInOut, FpuIO. *)
+FROM StrIO IMPORT WriteString, WriteLn;
+FROM NumberIO IMPORT WriteCard;
+FROM RealInOut IMPORT WriteReal;
+FROM FpuIO IMPORT WriteLongInt;
+VAR ExitCode : INTEGER;
+BEGIN
+  WriteString("hello"); WriteLn;
+  WriteCard(42, 3); WriteLn;
+  WriteReal(1.5, 8); WriteLn;
+  WriteLongInt(7, 3); WriteLn;
+  ExitCode := 42
+END TPimIO.

+ 17 - 12
docs/STATUS.md

@@ -4,13 +4,14 @@ This answers "where are we now?".
 
 
 ## Numbers
 ## Numbers
 
 
-- Suite **248/248** (`compiler/run_tests.sh`).
+- Suite **249/249** (`compiler/run_tests.sh`).
 - Self-hosting **FIXPOINT OK** — stage2 and stage3 images are
 - Self-hosting **FIXPOINT OK** — stage2 and stage3 images are
   byte-identical (3,882,337 bytes).
   byte-identical (3,882,337 bytes).
-- Corpus **427 / 612** sources compile with 0 errors, **0 crashes**
-  (`tools/v3-corpus/corpus.sh` over the gm2 testsuite); the rest are
-  genuine language gaps, **gm2 leniencies V3 deliberately rejects**, or
-  unresolved imports.
+- Corpus **467 / 612** sources compile with 0 errors, **0 crashes**
+  (`tools/v3-corpus/corpus.sh` over the gm2 testsuite; the harness now
+  resolves V3's own shipped libraries); the rest are genuine language
+  gaps, **gm2 leniencies V3 deliberately rejects**, or unresolved
+  imports of gm2-only modules.
 - Branch **`master`**; tree clean.
 - Branch **`master`**; tree clean.
 
 
 ## Architecture (current)
 ## Architecture (current)
@@ -134,12 +135,16 @@ declarations still work (`t_fwdproc`, `t_fwdok`).
    `SymTab`'s `MaxTypes` all report `230` (implementation limit) instead
    `SymTab`'s `MaxTypes` all report `230` (implementation limit) instead
    of truncating silently.  Still weak: error recovery and message polish.
    of truncating silently.  Still weak: error recovery and message polish.
 4. **Library holes** — the channel framework grew `ChanConsts` and
 4. **Library holes** — the channel framework grew `ChanConsts` and
-   `StreamFile` (ISO 10514-1) on top of `IOChan` (2026-10-19).  Still
-   missing: the remaining device/IO framework (`docs/wip/`).  Fixing
-   `StreamFile` also fixed a real bug: a **set CONST exported from a
-   definition module** is now emitted under its global name
-   (`<unit>_<name>`), so a client's qualified reference binds instead of
-   hitting an undefined symbol.
+   `StreamFile` (ISO 10514-1) on top of `IOChan`, and the PIM/gm2 I/O
+   family gained `NumberIO`, `RealInOut`, `FpuIO` (`StrIO`/`InOut`
+   already existed).  Also fixed the **corpus harness fidelity**: it now
+   searches V3's own shipped libraries (`stdlib`, `runtime/syslib`) via a
+   default `$V3_LIBS`, so a file importing e.g. `Strings` resolves
+   against the real `.def` instead of being mislabelled `LIB`.
+   Building `StreamFile` also fixed a real bug: a **set CONST exported
+   from a definition module** is now emitted under its global name
+   (`<unit>_<name>`), so a client's qualified reference binds.  Still
+   missing: gm2 runtime modules (`M2RTS`, `Builtins`, `Processes`, …).
 5. **Housekeeping** — `docs/INDEX.md` is the entry point; the
 5. **Housekeeping** — `docs/INDEX.md` is the entry point; the
    unreferenced historical records are under `docs/archive/` (2026-10-19).
    unreferenced historical records are under `docs/archive/` (2026-10-19).
    `resolve-check` was merged to `master`.
    `resolve-check` was merged to `master`.
@@ -149,7 +154,7 @@ declarations still work (`t_fwdproc`, `t_fwdok`).
 ```sh
 ```sh
 cd compiler
 cd compiler
 ./build.sh          # M2.atg -> scanner/parser/driver -> ./M2
 ./build.sh          # M2.atg -> scanner/parser/driver -> ./M2
-./run_tests.sh      # 248/248
+./run_tests.sh      # 249/249
 cd ..
 cd ..
 ./bootstrap/fixpoint.sh   # FIXPOINT OK (byte-identical stage2/stage3)
 ./bootstrap/fixpoint.sh   # FIXPOINT OK (byte-identical stage2/stage3)
 ```
 ```

+ 19 - 6
docs/handoff-2026-10-19-next.md

@@ -2,8 +2,8 @@
 
 
 **Branch state (2026-10-19):** work is on `master`, tree clean.
 **Branch state (2026-10-19):** work is on `master`, tree clean.
 
 
-**Baseline:** suite **248/248**, fixpoint **OK (3,882,337 bytes)**,
-corpus **427/612, 0 crashes**.
+**Baseline:** suite **249/249**, fixpoint **OK (3,882,337 bytes)**,
+corpus **467/612, 0 crashes**.
 
 
 **Policy:** follow **Modula-2**, not gm2 degradation — declaration
 **Policy:** follow **Modula-2**, not gm2 degradation — declaration
 before use (only a `POINTER`'s base may forward-reference).  In
 before use (only a `POINTER`'s base may forward-reference).  In
@@ -121,8 +121,21 @@ message polish, and the library holes (`docs/wip/`).
 - Remaining library work: the rest of the device/IO framework
 - Remaining library work: the rest of the device/IO framework
   (`docs/wip/`).
   (`docs/wip/`).
 
 
-**Baseline:** suite **248/248**, fixpoint **OK (3,882,337 bytes)**,
-corpus **427/612, 0 crashes**.
+## Library holes  ✅ second slice (2026-10-19)
+
+- **Harness fidelity**: `tools/v3-corpus/corpus.sh` now defaults
+  `V3_LIBS` to V3's own shipped libraries (`stdlib`, `runtime/syslib`)
+  and only treats `SYSTEM`/`libc` as built-ins-without-`.def`.  A corpus
+  file importing `Strings`/`IOChan`/`StrIO`/… now resolves against the
+  real `.def` instead of being mislabelled `LIB` (427 → 447).
+- **PIM/gm2 I/O family**: added `NumberIO`, `RealInOut`, `FpuIO`
+  (`StrIO`/`InOut` already shipped; a new `m2writefix` C formatter backs
+  the fixed-point writers).  Regression `t_pimio` (exit 42).  447 → 467.
+- Still missing: gm2 runtime modules (`M2RTS`, `Builtins`, `Processes`,
+  `Preemptive`, `M2Printf`, …).
+
+**Baseline:** suite **249/249**, fixpoint **OK (3,882,337 bytes)**,
+corpus **467/612, 0 crashes**.
 
 
 ## P5 — housekeeping  ✅ DONE (2026-10-19)
 ## P5 — housekeeping  ✅ DONE (2026-10-19)
 
 
@@ -158,9 +171,9 @@ commit with a `v3-*` tag.
 ```sh
 ```sh
 cd .../m2compiler-V3
 cd .../m2compiler-V3
 git checkout master
 git checkout master
-cd compiler && ./build.sh && ./run_tests.sh   # 248/248
+cd compiler && ./build.sh && ./run_tests.sh   # 249/249
 cd .. && TMP="$PWD/.scratch/fixpoint" ./bootstrap/fixpoint.sh
 cd .. && TMP="$PWD/.scratch/fixpoint" ./bootstrap/fixpoint.sh
-tools/v3-corpus/corpus.sh                     # 427 OK, 0 crash
+tools/v3-corpus/corpus.sh                     # 467 OK, 0 crash
 ```
 ```
 
 
 **Recommendation:** P1 (two bounded wins) next, then reserve a separate
 **Recommendation:** P1 (two bounded wins) next, then reserve a separate

+ 14 - 0
runtime/syslib/shim.c

@@ -491,6 +491,20 @@ long m2writereal(double x)
     return m2writerealf(x, 0);
     return m2writerealf(x, 0);
 }
 }
 
 
+/* Write a REAL right-justified in `wid` with `dec` digits after the
+   point (FpuIO.WriteReal/WriteLongReal). */
+long m2writefix(double x, long wid, long dec)
+{
+    char buf[256];
+    int n;
+    if (wid <= 0) wid = 1;
+    if (dec < 0) dec = 0;
+    if (dec > 30) dec = 30;
+    n = snprintf(buf, sizeof buf, "%*.*f", (int)wid, (int)dec, x);
+    if (n > 0) write(1, buf, (size_t)n);
+    return 0;
+}
+
 /* Read a REAL from stdin. */
 /* Read a REAL from stdin. */
 double m2readreal(void)
 double m2readreal(void)
 {
 {

+ 12 - 0
stdlib/fpuio.def

@@ -0,0 +1,12 @@
+DEFINITION MODULE FpuIO;
+(* gm2/PIM floating-point I/O.  V3 models REAL and LONGREAL as the same
+   8-byte type, so both read/write paths are the same. *)
+
+PROCEDURE ReadReal (VAR x : LONGREAL);
+PROCEDURE ReadLongReal (VAR x : LONGREAL);
+PROCEDURE WriteReal (x : LONGREAL; width, decimals : CARDINAL);
+PROCEDURE WriteLongReal (x : LONGREAL; width, decimals : CARDINAL);
+PROCEDURE WriteLongInt (x : LONGINT; width : CARDINAL);
+PROCEDURE WriteLongCard (x : LONGCARD; width : CARDINAL);
+
+END FpuIO.

+ 33 - 0
stdlib/fpuio.mod

@@ -0,0 +1,33 @@
+IMPLEMENTATION MODULE FpuIO;
+(* Thin layer over the C runtime's float/integer printers. *)
+
+PROCEDURE m2writefix (x : REAL; wid : INTEGER; dec : INTEGER);
+  EXTERNAL;
+PROCEDURE m2writeintwidth (v : LONGINT; wid : CARDINAL);
+  EXTERNAL;
+PROCEDURE m2readreal () : REAL;
+  EXTERNAL;
+
+PROCEDURE ReadReal (VAR x : LONGREAL);
+BEGIN x := m2readreal() END ReadReal;
+
+PROCEDURE ReadLongReal (VAR x : LONGREAL);
+BEGIN x := m2readreal() END ReadLongReal;
+
+PROCEDURE WriteReal (x : LONGREAL; width, decimals : CARDINAL);
+BEGIN
+  m2writefix(x, VAL(INTEGER, width), VAL(INTEGER, decimals))
+END WriteReal;
+
+PROCEDURE WriteLongReal (x : LONGREAL; width, decimals : CARDINAL);
+BEGIN
+  m2writefix(x, VAL(INTEGER, width), VAL(INTEGER, decimals))
+END WriteLongReal;
+
+PROCEDURE WriteLongInt (x : LONGINT; width : CARDINAL);
+BEGIN m2writeintwidth(x, width) END WriteLongInt;
+
+PROCEDURE WriteLongCard (x : LONGCARD; width : CARDINAL);
+BEGIN m2writeintwidth(VAL(LONGINT, x), width) END WriteLongCard;
+
+END FpuIO.

+ 9 - 0
stdlib/numberio.def

@@ -0,0 +1,9 @@
+DEFINITION MODULE NumberIO;
+(* Classic PIM numeric I/O (gm2-compatible interface), layered on InOut. *)
+
+PROCEDURE ReadInt (VAR x : INTEGER);
+PROCEDURE ReadCard (VAR x : CARDINAL);
+PROCEDURE WriteInt (x : INTEGER; width : CARDINAL);
+PROCEDURE WriteCard (x : CARDINAL; width : CARDINAL);
+
+END NumberIO.

+ 16 - 0
stdlib/numberio.mod

@@ -0,0 +1,16 @@
+IMPLEMENTATION MODULE NumberIO;
+IMPORT InOut;
+
+PROCEDURE ReadInt (VAR x : INTEGER);
+BEGIN InOut.ReadInt(x) END ReadInt;
+
+PROCEDURE ReadCard (VAR x : CARDINAL);
+BEGIN InOut.ReadCard(x) END ReadCard;
+
+PROCEDURE WriteInt (x : INTEGER; width : CARDINAL);
+BEGIN InOut.WriteInt(x, width) END WriteInt;
+
+PROCEDURE WriteCard (x : CARDINAL; width : CARDINAL);
+BEGIN InOut.WriteCard(x, width) END WriteCard;
+
+END NumberIO.

+ 11 - 0
stdlib/realinout.def

@@ -0,0 +1,11 @@
+DEFINITION MODULE RealInOut;
+(* Classic PIM real I/O, layered on RealIO. *)
+
+VAR
+  Done   : BOOLEAN;
+  termCH : CHAR;
+
+PROCEDURE ReadReal (VAR x : REAL);
+PROCEDURE WriteReal (x : REAL; width : CARDINAL);
+
+END RealInOut.

+ 19 - 0
stdlib/realinout.mod

@@ -0,0 +1,19 @@
+IMPLEMENTATION MODULE RealInOut;
+IMPORT RealIO;
+
+PROCEDURE ReadReal (VAR x : REAL);
+BEGIN
+  RealIO.ReadReal(x);
+  Done := TRUE;
+  termCH := CHR(0)
+END ReadReal;
+
+PROCEDURE WriteReal (x : REAL; width : CARDINAL);
+BEGIN
+  RealIO.WriteRealF(x, width)
+END WriteReal;
+
+BEGIN
+  Done := TRUE;
+  termCH := CHR(0)
+END RealInOut.

+ 7 - 4
tools/v3-corpus/README.md

@@ -14,9 +14,9 @@ touches the corpus) and compiled on its own.  A file with **0 errors**
 is a pass.  Failures are split into:
 is a pass.  Failures are split into:
 
 
 - **CRASH** — `M2` died (rc ≥ 128): a real compiler bug;
 - **CRASH** — `M2` died (rc ≥ 128): a real compiler bug;
-- **LIB** — the file imports a module outside V3's known stdlib set
-  (gm2's `StrIO`/`NumberIO`/`STextIO`/…), so the `undeclared
-  identifier`s are expected, not a language gap;
+- **LIB** — the file imports a module V3 does **not** ship (gm2's
+  `NumberIO`/`FpuIO` are shipped; `M2RTS`/`Builtins`/`Processes`/… are
+  not), so the `undeclared identifier`s are expected, not a language gap;
 - **LANG** — otherwise: a genuine parse/semantic gap in V3.
 - **LANG** — otherwise: a genuine parse/semantic gap in V3.
 
 
 A "pass" is syntactic+semantic cleanup, not a linkable image (many
 A "pass" is syntactic+semantic cleanup, not a linkable image (many
@@ -27,7 +27,10 @@ An IMPLEMENTATION file is compiled together with its sibling
 DEFINITION (same basename, `.def`/`.DEF`) when present, and with the
 DEFINITION (same basename, `.def`/`.DEF`) when present, and with the
 DEFINITION of every module it imports, searched in the source directory
 DEFINITION of every module it imports, searched in the source directory
 and in the colon-separated `$V3_LIBS` directories — so cross-module
 and in the colon-separated `$V3_LIBS` directories — so cross-module
-imports resolve.  E.g.:
+imports resolve.  `$V3_LIBS` defaults to V3's **own** shipped libraries
+(`stdlib/` and `runtime/syslib/`), so a file importing e.g. `Strings`
+or `IOChan` resolves against the real `.def`; point it at an installed
+gm2 library set for a wider net, e.g.:
 
 
 ```sh
 ```sh
 G=$HOME/bin/Modula2/Gm2/lib/gcc/x86_64-pc-linux-gnu/16.0.1/m2
 G=$HOME/bin/Modula2/Gm2/lib/gcc/x86_64-pc-linux-gnu/16.0.1/m2

+ 6 - 1
tools/v3-corpus/corpus.sh

@@ -18,7 +18,12 @@ cd "$(dirname "$0")"
 ROOT="$(cd ../.. && pwd)"
 ROOT="$(cd ../.. && pwd)"
 M2="$ROOT/compiler/M2"
 M2="$ROOT/compiler/M2"
 WORK=/tmp/opencode/v3corpus
 WORK=/tmp/opencode/v3corpus
-KNOWN="SYSTEM libc SysIO TextIO Strings Math Conversions RealIO ProgramArgs IOChan Files Storage WholeIO CharClass SysClock"
+# Library search path: the modules V3 actually ships.  A corpus file that
+# imports one of them should resolve against the real .def, not be
+# assumed present.
+V3_LIBS="${V3_LIBS:-$ROOT/stdlib:$ROOT/runtime/syslib}"
+# Modules with no .def (language/runtime built-ins).
+KNOWN="SYSTEM libc"
 [ -x "$M2" ] || { echo "compiler/M2 missing -- run compiler/build.sh"; exit 1; }
 [ -x "$M2" ] || { echo "compiler/M2 missing -- run compiler/build.sh"; exit 1; }
 [ "$#" -gt 0 ] || set -- \
 [ "$#" -gt 0 ] || set -- \
   "$HOME/Projets/Projets-Modula2/MyWork/gcc-git/gcc/testsuite/gm2/pim/pass" \
   "$HOME/Projets/Projets-Modula2/MyWork/gcc-git/gcc/testsuite/gm2/pim/pass" \