Browse Source

language: de-reserve ORDL (a gm2/FileIO helper, not a Modula-2 builtin)

Track B (gap13): ORDL is not a reserved word in Modula-2, so
PROCEDURE ORDL (fileio.def/mod) must be accepted.  Remove ORDL from the
grammar's keyword set (the ORD production and ImpName); it is now an
ordinary identifier.

Coco/R's generated CONTEXT handling calls ORDL, so the scanner frame now
takes `FROM FileIO IMPORT ORDL`, and runtime/syslib/FileIO exports ORDL
(the host FileIO already did).  t_compat drops its ORDL probe.

The standard built-ins (ORD/CHR/VAL/...) remain keyword-style; fully
de-reserving them is a larger parser refactor.

Corpus 402 -> 405 (fileio.def/mod, baseimport), 0 crashes.  Suite
238/238 (t_builtinname); fixpoint OK, 3,812,771 bytes.  gaps gap13
retired.
Eric Streit 11 hours ago
parent
commit
e6ddad6bee

+ 2 - 2
README.md

@@ -47,8 +47,8 @@ docs/              STATUS.md, plan.md, plan-*.md, analysis_*.md, summary_*.md
 
 ## Status (2026-10-18)
 
-- Suite **237/237**; self-hosting **fixpoint OK** (byte-identical
-  stage2/stage3, 3,823,257 bytes); corpus **402/612 compile-OK,
+- Suite **238/238**; self-hosting **fixpoint OK** (byte-identical
+  stage2/stage3, 3,812,771 bytes); corpus **405/612 compile-OK,
   0 crashes** (`tools/v3-corpus`; the corpus is gm2's testsuite, so it
   mixes conforming Modula-2 with gm2 leniencies — see `docs/gaps/`).
 - Active branch **`resolve-check`** (holds the AST/`Lower`/parse

+ 1 - 0
compiler/run_tests.sh

@@ -100,6 +100,7 @@ expect_run t_constreal.mod 42
 expect_run t_constlong.mod 42
 expect_run t_procaddr.mod 42
 expect_run t_convert.mod 42
+expect_run t_builtinname.mod 42
 expect_run t_setword.mod 42
 expect_run t_procval.mod 42
 expect_run t_real64.mod 42

+ 2 - 2
compiler/src/M2.atg

@@ -1161,7 +1161,7 @@ PRODUCTIONS
                                              END
                                            END; .)
     | ( "TSIZE" | "SIZE" | "ADR" | "HIGH" | "LEN"
-      | "CHR" | "ORD" | "ORDL" | "VAL" | "ABS" | "CAP"
+      | "CHR" | "ORD" | "VAL" | "ABS" | "CAP"
       | "ODD" | "TRUNC" | "FLOAT"
       | "UCHR" | "CHR8" | "UORD"
       | "INC" | "DEC"
@@ -3666,7 +3666,7 @@ PRODUCTIONS
  AND NOT SymTab.IsIntFamily(et) THEN
                                              SemError(211) END;
                                            t := SymTab.CharType(); .)
-    | ( "ORD" | "ORDL" ) "(" Expr<et, q> ")"
+    | "ORD" "(" Expr<et, q> ")"
     (. astNode := AST.MakeNode(AST.NkCall);
    AST.SetChild(astNode, 0, AST.MakeLeaf(AST.NkIdent, "ORD"));
    IF astCur # AST.NoNode THEN AST.SetChild(astNode, 1, astCur) END;

+ 6 - 6
compiler/src/M2.lst

@@ -1180,7 +1180,7 @@ Listing:
  1161                                               END
  1162                                             END; .)
  1163      | ( "TSIZE" | "SIZE" | "ADR" | "HIGH" | "LEN"
- 1164        | "CHR" | "ORD" | "ORDL" | "VAL" | "ABS" | "CAP"
+ 1164        | "CHR" | "ORD" | "VAL" | "ABS" | "CAP"
  1165        | "ODD" | "TRUNC" | "FLOAT"
  1166        | "UCHR" | "CHR8" | "UORD"
  1167        | "INC" | "DEC"
@@ -3685,7 +3685,7 @@ Listing:
  3666   AND NOT SymTab.IsIntFamily(et) THEN
  3667                                               SemError(211) END;
  3668                                             t := SymTab.CharType(); .)
- 3669      | ( "ORD" | "ORDL" ) "(" Expr<et, q> ")"
+ 3669      | "ORD" "(" Expr<et, q> ")"
  3670      (. astNode := AST.MakeNode(AST.NkCall);
  3671     AST.SetChild(astNode, 0, AST.MakeLeaf(AST.NkIdent, "ORD"));
  3672     IF astCur # AST.NoNode THEN AST.SetChild(astNode, 1, astCur) END;
@@ -4124,11 +4124,11 @@ Listing:
 
 Statistics:
 
-  nr of terminals:       113 (limit   400)
+  nr of terminals:       112 (limit   400)
   nr of non-terminals:    90 (limit   210)
-  nr of pragmas:           0 (limit   387)
-  nr of symbolnodes:     203 (limit   500)
-  nr of graphnodes:     1294 (limit  1500)
+  nr of pragmas:           0 (limit   388)
+  nr of symbolnodes:     202 (limit   500)
+  nr of graphnodes:     1289 (limit  1500)
   nr of conditionsets:    12 (limit   100)
   nr of charactersets:    16 (limit   250)
 

+ 1 - 0
compiler/src/scanner.frm

@@ -2,6 +2,7 @@ IMPLEMENTATION MODULE -->modulename;
 
 (* Scanner generated by Coco/R - using the FileIO library supplied with this project. *)
 
+FROM FileIO IMPORT ORDL;
 IMPORT FileIO, Storage;
 FROM Storage IMPORT ALLOCATE;  (* gm2 needs this for NEW substitution *)
 

+ 13 - 0
compiler/tests/t_builtinname.mod

@@ -0,0 +1,13 @@
+MODULE TBuiltinName;
+// ORDL is not a reserved word in Modula-2 (it is a gm2/FileIO helper),
+// so a user procedure may be named ORDL.  Exit 42.
+VAR ExitCode : INTEGER;
+
+PROCEDURE ORDL(n : INTEGER) : CARDINAL;
+BEGIN
+  RETURN VAL(CARDINAL, n + 1)
+END ORDL;
+
+BEGIN
+  IF ORDL(41) = 42 THEN ExitCode := 42 ELSE ExitCode := 1 END
+END TBuiltinName.

+ 2 - 3
compiler/tests/t_compat.mod

@@ -1,8 +1,8 @@
 MODULE TCompat;
 // GNU/classic compatibility accepted by V3 for self-hosting the
 // Coco/R-generated sources: '&' (AND) and '~' (NOT), LENGTH (alias of
-// LEN), CAP, ORDL, the octal character literal 0C, and empty
-// THEN/ELSE bodies. Exit 42.
+// LEN), CAP, the octal character literal 0C, and empty THEN/ELSE
+// bodies. Exit 42.
 VAR ExitCode : INTEGER;
 VAR s : ARRAY [0..2] OF CHAR;
 VAR c : CHAR;
@@ -21,7 +21,6 @@ BEGIN
   END;                               // empty ELSE
   c := 0C;                           // octal char literal -> NUL
   IF ~(c = CHR(0)) THEN n := 0 END;  // ~ = NOT
-  n := n + ORDL(0);                  // ORDL
   IF n = 42 THEN ELSE n := 0 END;    // empty THEN + ELSE
   ExitCode := n
 END TCompat.

+ 8 - 6
docs/STATUS.md

@@ -4,10 +4,10 @@ This answers "where are we now?".
 
 ## Numbers
 
-- Suite **237/237** (`compiler/run_tests.sh`).
+- Suite **238/238** (`compiler/run_tests.sh`).
 - Self-hosting **FIXPOINT OK** — stage2 and stage3 images are
-  byte-identical (3,823,257 bytes).
-- Corpus **402 / 612** sources compile with 0 errors, **0 crashes**
+  byte-identical (3,812,771 bytes).
+- Corpus **405 / 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.
@@ -68,8 +68,10 @@ parameters, constraints, nested generics, generic classes.
   (`DEFINITION/IMPLEMENTATION MODULE FOR "C"`, `EXPORT UNQUALIFIED`),
   and ISO `CONVERT(T, x)` (a `VAL` sibling), set↔word `VAL`/`CAST`
   (BITSET), and **enumeration literals with their type** (export/import/
-  qualified; `imp*`): corpus 382 → 402.  Fixed a `SHIFT`-by-a-negative
-  fault and the qualified-const value (`LoadDesignator`).
+  qualified; `imp*`) and **`ORDL` de-reserved** (a gm2/FileIO helper, not
+  a built-in; `fileio`/`baseimport`): corpus 382 → 405.  Fixed a
+  `SHIFT`-by-a-negative fault and the qualified-const value
+  (`LoadDesignator`).
 - The **corpus harness** now resolves **transitive** imports (it used to
   under-provide `.def`s); this more accurate harness is the method from
   2026-10-18.
@@ -119,7 +121,7 @@ is the 10 gm2-leniency files now correctly rejected).
 ```sh
 cd compiler
 ./build.sh          # M2.atg -> scanner/parser/driver -> ./M2
-./run_tests.sh      # 237/237
+./run_tests.sh      # 238/238
 cd ..
 ./bootstrap/fixpoint.sh   # FIXPOINT OK (byte-identical stage2/stage3)
 ```

+ 7 - 6
docs/analysis_gaps.md

@@ -261,12 +261,13 @@ production also takes `CAST`): set→`CARDINAL` loads word 0, and
 `constset2`, `constset3` pass.  Corpus **388 -> 392**; suite tests
 `t_convert`, `t_setword`; `docs/gaps/gap07`/`gap08` retired.
 
-Next (recorded, genuine only): **`gap13` builtins as identifiers** (a V3
-bug; needs de-reserving the builtin keyword tokens — a parser refactor);
-`aochar`/`arrayofbyte`/`bytearray` (scalar actual into `ARRAY OF BYTE`);
-`packed` (`__FILE__`/`__LINE__`, gm2-ext); variant empty arms / `ELSE`;
-set-span limits.  The forward-type items above are gm2 leniency, not
-targets.
+Next (recorded, genuine only): the **standard built-ins as identifiers**
+(`ORD`, `CHR`, `VAL`, … are predeclared identifiers, not reserved words;
+de-reserving them is a parser refactor — `ORDL`, the corpus case, is
+done); `aochar`/`arrayofbyte`/`bytearray` (scalar actual into `ARRAY OF
+BYTE`); `packed` (`__FILE__`/`__LINE__`, gm2-ext); variant empty arms /
+`ELSE`; set-span limits.  The forward-type items above are gm2 leniency,
+not targets.
 
 **Also landed (Track B):** **enumeration literals with their type** —
 `SymTab.EnterEnumLits` propagates a type's literals on declaration-as-

+ 6 - 5
docs/gaps/README.md

@@ -54,7 +54,6 @@ The `class` column says what a change would mean:
 | `gap10-var-param-type` | `gap10_var_param_type.mod` | `gm2-ext` | `invalid call` | `testparam2`, `testparam3` |
 | `gap11-bare-proc-stmt` | `gap11_bare_proc_stmt.mod` | `gm2-ext` | `invalid call` | `tinyproc2`, `tinyproc4` |
 | `gap12-variant-empty-arms` | `gap12_variant_empty_arms.mod` | `gm2-ext` | `invalid VarLabel` | `varient2`, `varient3`, `varient8`, `tsize`, `tsize2` |
-| `gap13-builtin-proc-name` | `gap13_builtin_proc_name.mod` | `v3-deviation` | `ident expected` | `fileio.def`, `fileio.mod` |
 | `gap14-builtin-pragma` | `gap14_builtin_pragma.def` | `gm2-ext` | `ident expected` | `builtin.def`, `builtin.mod` |
 | `gap16-wide-set` | `gap16_wide_set.mod` | `impl-limit` | `not supported yet` | `settest1` |
 | `gap17-set-of-cardinal` | `gap17_set_of_cardinal.mod` | `impl-limit` | `not supported yet` | `settest3` |
@@ -78,7 +77,6 @@ gap09_scalar_to_bytearray.mod   invalid call            (scalar -> ARRAY OF BYTE
 gap10_var_param_type.mod        invalid call            (VAR actual type)
 gap11_bare_proc_stmt.mod        invalid call            (bare proc value)
 gap12_variant_empty_arms.mod    invalid VarLabel
-gap13_builtin_proc_name.mod     ident expected          (ORDL)
 gap14_builtin_pragma.def        ident expected          (__BUILTIN__)
 gap16_wide_set.mod              not supported yet       (span > 65536)
 gap17_set_of_cardinal.mod       not supported yet       (unbounded base)
@@ -96,9 +94,12 @@ gap22_file_line.mod             invalid Fact            (__LINE__)
 - **`gap08` set↔word `CAST`/`VAL` — fixed** (2026-10-18): `VAL`/`CAST`
   between a set and its machine word (`constset`, `constset2`,
   `constset3` pass).  `packed` still needs `gap22` (`__FILE__`/`__LINE__`).
-- **`gap13` builtins as identifiers** — `ORD`, `CHR`, `VAL`, `ORDL`, …
-  are *predeclared identifiers, not reserved words*, so V3 must accept
-  them as user names.  (This is a V3 deviation, not a corpus gap.)
+- **`gap13` builtins as identifiers — the corpus case is fixed**
+  (2026-10-18): `ORDL` (a gm2/FileIO helper, not a Modula-2 built-in) is
+  no longer reserved, so `PROCEDURE ORDL` is accepted (`fileio.def/mod`,
+  `baseimport` pass; the scanner frame now imports `FileIO.ORDL`).  The
+  *standard* built-ins (`ORD`, `CHR`, `VAL`, …) are still keyword-style —
+  de-reserving them is a larger parser refactor.
 - **`gap15` enumeration literals with an imported type — fixed**
   (2026-10-18): literals now travel with the type through re-export,
   FROM-import and qualified access; also fixed the qualified-const value

+ 0 - 10
docs/gaps/gap13_builtin_proc_name.mod

@@ -1,10 +0,0 @@
-MODULE Gap13BuiltinProcName;
-(* V3 bug: `ORDL` is a predeclared identifier, not a reserved word, so
-   `PROCEDURE ORDL ...` is legal Modula-2.  V3 wrongly tokenises the
-   builtins as keywords and rejects it.  Class: v3-deviation (V3 is
-   stricter than the standard).
-   Corpus: fileio.def, fileio.mod.
-   V3: ident expected. *)
-PROCEDURE ORDL(i : INTEGER) : CARDINAL;
-BEGIN RETURN 0 END ORDL;
-BEGIN END Gap13BuiltinProcName.

+ 17 - 5
docs/summary_set-constants-2026-10-18.md

@@ -1,12 +1,13 @@
 # Session summary — SET CONST + builtin CONST folding 2026-10-18
 
-Branch **`resolve-check`**.  Suite **237/237**; fixpoint **OK
-(3,823,257 bytes)**; corpus **402/612 compile-OK, 0 crashes**.
+Branch **`resolve-check`**.  Suite **238/238**; fixpoint **OK
+(3,812,771 bytes)**; corpus **405/612 compile-OK, 0 crashes**.
 
 Tags: `v3-set-constants`, `v3-const-builtin-fold`, `v3-const-conv-fold`,
 `v3-proc-addr`, `v3-proc-val`, `v3-real64`, `v3-shift-import`,
 `v3-foreign-module`, `v3-m2-conformance`, `v3-strict-decl-order`,
-`v3-convert`, `v3-set-word`, `v3-enum-import`, `v3-gap-examples`.
+`v3-convert`, `v3-set-word`, `v3-enum-import`, `v3-ordl`,
+`v3-gap-examples`.
 
 ## Conformance note
 
@@ -155,6 +156,17 @@ legal exception.  Corpus **398 → 388**: the 10 files dropped
 leniency.  Forward references to module-level **variables**/**procedures**
 from a body (the two-phase parse) remain — a separate, larger item.
 
+## Track B: `ORDL` de-reserved
+
+`ORDL` is a gm2/FileIO helper, not a Modula-2 built-in; V3 reserved it,
+so `PROCEDURE ORDL` (`fileio.def/mod`) was rejected.  It is now an
+ordinary identifier: the scanner frame takes `FROM FileIO IMPORT ORDL`
+(Coco/R's generated CONTEXT handling calls `ORDL`), `runtime/syslib/
+FileIO` exports it, and `t_compat` drops its ORDL probe.  (`ORD`, `CHR`,
+`VAL`, … remain keyword-style — de-reserving the *standard* built-ins is
+a larger parser refactor.)  Corpus **402 → 405**; suite test
+`t_builtinname`; `docs/gaps/gap13` retired.
+
 ## Track B: enum literals travel with their type
 
 Modula-2 exports/imports a type's **enumeration literals** with the type.
@@ -237,7 +249,7 @@ modules),
 ```sh
 cd .../m2compiler-V3
 git checkout resolve-check
-cd compiler && ./build.sh && ./run_tests.sh   # 237/237
+cd compiler && ./build.sh && ./run_tests.sh   # 238/238
 cd .. && TMP="$PWD/.scratch/fixpoint" ./bootstrap/fixpoint.sh
-M2FLAGS="" ./.scratch/corpusx.sh              # 402 OK, 0 crash
+M2FLAGS="" ./.scratch/corpusx.sh              # 405 OK, 0 crash
 ```

+ 2 - 0
runtime/syslib/FileIO.def

@@ -39,6 +39,8 @@ PROCEDURE WriteLn (f: File);
 PROCEDURE WriteString (f: File; str: ARRAY OF CHAR);
 PROCEDURE WriteInt (f: File; int: INTEGER; wid: CARDINAL);
 PROCEDURE WriteCard (f: File; card, wid: CARDINAL);
+PROCEDURE ORDL (n: INT32) : CARDINAL;
+(* ORD for a 32-bit signed value (classic FileIO helper). *)
 
 PROCEDURE SLENGTH (stringVal: ARRAY OF CHAR): CARDINAL;
 PROCEDURE Assign (source: ARRAY OF CHAR; VAR destination: ARRAY OF CHAR);

+ 5 - 0
runtime/syslib/FileIO.mod

@@ -59,6 +59,11 @@ BEGIN
   SysShim.fwriteintw(f, card, wid)
 END WriteCard;
 
+PROCEDURE ORDL (n: INT32) : CARDINAL;
+BEGIN
+  RETURN VAL(CARDINAL, n)
+END ORDL;
+
 PROCEDURE SLENGTH (stringVal: ARRAY OF CHAR): CARDINAL;
 BEGIN
   RETURN LEN(stringVal)