Browse Source

docs: bare-EBNF grammar extraction + session save 2026-09-19

Eric Streit 3 tuần trước cách đây
mục cha
commit
6e85222dac
2 tập tin đã thay đổi với 380 bổ sung và 0 xóa
  1. 314 0
      docs/grammar.md
  2. 66 0
      docs/session-2026-09-19.md

+ 314 - 0
docs/grammar.md

@@ -0,0 +1,314 @@
+# m2compiler-V3 grammar (bare EBNF)
+
+Extracted from `compiler/src/M2.atg` (Coco/R PRODUCTIONS
+with semantic actions and attributes stripped). Terminals in
+double quotes are literals; lowercase names are tokens.
+
+```ebnf
+  M2
+    = Unit "." .
+  (* Units: program modules compile fully; DEFINITION and
+     IMPLEMENTATION modules parse + check now but lower in step 4
+     (each ends with one 230); same for nested local modules. *)
+  Unit
+    = DefUnit
+    | ImplUnit
+    | ProgModule .
+  DefUnit
+    = "DEFINITION" "MODULE"
+      GetIdent
+      ";"
+      { Import }
+      { ConstBlock | TypeBlock | VarBlock
+      | ProcHeading ";"              }
+      "END"
+      GetIdent .
+  ImplUnit
+    = "IMPLEMENTATION" "MODULE"
+      GetIdent
+      ";"
+      { Import }
+      DeclSeq
+      [ "BEGIN"
+        [ StatSeq ] ]
+      "END"
+      GetIdent .
+  ProgModule
+    = "MODULE"
+      GetIdent
+      [ Priority ]
+      ";"
+      { Import }
+      DeclSeq
+      [ "BEGIN"
+        [ StatSeq ] ]
+      "END"
+      GetIdent .
+  DeclSeq
+    = { ConstBlock | TypeBlock | VarBlock | ProcDecl ";"
+      | NestedModule ";" | ClassItem ";" } .
+  (* Local module, Wirth form. Parses + checks fully (own scope);
+     lowering waits for step 4, marked by one 230 at END. *)
+  NestedModule
+    = "MODULE"
+      GetIdent
+      [ Priority ]
+      ";"
+      { Import }
+      [ Export ]
+      DeclSeq
+      [ "BEGIN"
+        [ StatSeq ] ]
+      "END"
+      GetIdent                       .
+  Priority
+    = "[" integer "]"                    .
+  Export
+    = "EXPORT"                          (* names wire up in step 4 *)
+      [ "QUALIFIED" ]
+      GetIdent
+      { "," GetIdent }
+      ";" .
+  Import
+    = "FROM"
+      GetIdent
+      "IMPORT"
+      ImpList ";"
+    | "IMPORT"
+      ImpList ";" .
+  ImpList
+    = GetIdent
+      { ","
+        GetIdent                      } .
+  (* Opaque TYPE declarations (definition modules). The targetless
+     alias resolves to InvalidType until step 4 completes it. *)
+  (* Scalar-phase TYPEs: named types, integer subranges, enumerations.
+     Opaque "TYPE T;" needs isDef (definition units); elsewhere 231.
+     Composite forms (ARRAY/RECORD/SET/POINTER) arrive with step 3. *)
+  TypeBlock
+    = "TYPE" { TypeItem ";" | ClassItem ";" } .
+  TypeItem
+    = GetIdent
+      ( "=" Type
+      |                                  ) .
+  Type
+    = TypeIdent
+    | Subrange
+    | Enum .
+  TypeIdent
+    = GetIdent                        .
+  Subrange
+    = "[" Expr ".." Expr
+
+      "]" .
+  Enum
+    = "("
+      GetIdent
+      { "," GetIdent                  }
+      ")" .
+  (* Clarion-form classes (docs/OOP.txt): declaration + single
+     inheritance + IMPLEMENTATION blocks. Scopes and member checks
+     now; lowering (vtable, dispatch, THIS) later — one 230 per
+     class/impl block. Methods end with ";" per the Table example
+     (not "," as in the sketch). No underscores in identifiers. *)
+  (* Single CLASS item in both loops: separating declaration from
+     IMPLEMENTATION at the loop level needs 2-token lookahead
+     (CLASS ident vs CLASS IMPLEMENTATION), which LL(1) cannot do.
+     The second token decides after CLASS is consumed. A misplaced
+     CLASS IMPLEMENTATION inside TYPE still parses (harmless: the
+     whole unit ends 230 until lowering). *)
+  ClassItem
+    = "CLASS" ( "IMPLEMENTATION" ClassImplRest | ClassRest ) .
+  ClassRest
+    = GetIdent
+      [ Parents ]
+      ";"
+      { ClassField ";" }
+      { MethodHeading ";"            }
+      "END"
+      GetIdent                       .
+  Parents
+    = "(" Parent1
+      { "," GetIdent                  }
+      ")" .
+  Parent1
+    = GetIdent                        .
+  ClassField
+    = GetIdent
+      ( "=" GetIdent
+      |
+        { "," GetIdent                }
+        ":" TypeIdent                 ) .
+  MethodHeading
+    =
+      [ "VIRTUAL"                        ]
+      ProcHeading                    .
+  ClassImplRest
+    = GetIdent
+      ";" { MethodImpl ";" }
+      [ "BEGIN"
+        [ StatSeq ] ]
+      "END"
+      GetIdent                       .
+  MethodImpl
+    = MethodHeading ";"
+
+      ( "FORWARD"
+      | Block                        ) .
+  ConstBlock
+    = "CONST" { ConstDecl ";" } .
+  ConstDecl
+    = GetIdent
+      "="
+      Expr                        .
+  VarBlock
+    = "VAR" { VarDecl ";" } .
+  VarDecl
+    = VarIdents ":"
+      TypeIdent                       .
+  VarIdents
+    = GetIdent
+      { ","
+        GetIdent                      } .
+  ParIdents
+    = GetIdent
+      { "," GetIdent                  } .
+  (* Procedure headings enter now (scopes, params, result); bodies
+     parse + check with one 230 at END (lowering = step 4).
+     FORWARD marks; the body heading re-enters (signature compare
+     tightens in step 4). *)
+  ProcHeading
+    = "PROCEDURE"
+      GetIdent
+      [ FormalParams ]
+      [ ":" TypeIdent                 ] .
+  FormalParams
+    = "(" [ ParamSection { ";" ParamSection } ] ")" .
+  ParamSection
+    =
+      [ "VAR"                            ]
+      ParIdents ":" TypeIdent    .
+  ProcDecl
+    = ProcHeading ";"
+      ( "FORWARD"
+      | Block                        ) .
+  Block
+    = DeclSeq
+      [ "BEGIN"
+        [ StatSeq ] ]
+      "END"
+      GetIdent                       .
+  StatSeq
+    = Statement { ";" Statement } .
+  Statement
+    = Assign
+    | IfStat
+    | WhileStat
+    | RepeatStat
+    | LoopStat
+    | ForStat
+    | CaseStat
+    | ReturnStat
+    | "EXIT"                             .
+  Assign
+    = Design ":="
+      Expr                       .
+  IfStat
+    = "IF"
+      Expr
+      "THEN" StatSeq
+      { "ELSIF"
+        Expr
+        "THEN" StatSeq                   }
+      [ "ELSE"
+        StatSeq ]
+      "END"                              .
+  WhileStat
+    = "WHILE"
+      Expr
+      "DO" StatSeq
+      "END"                              .
+  RepeatStat
+    = "REPEAT"
+      StatSeq
+      "UNTIL" Expr                 .
+  LoopStat
+    = "LOOP"
+      StatSeq
+      "END"                              .
+  (* FOR with static-sign BY (literal, non-zero; 220 otherwise).
+     Runtime direction would need a compare-select; the literal
+     sign picks cslew/csegew at "DO" time. *)
+  ForStat
+    = "FOR"
+      GetIdent
+      ":=" Expr
+      "TO" Expr
+      [ "BY" Expr              ]
+      "DO"
+      StatSeq
+      "END"                              .
+  CaseStat
+    = "CASE" Expr
+      "OF" CaseAlt
+      { "|" CaseAlt }
+      [ "ELSE" StatSeq ]
+      "END"                              .
+  (* Compare-chain lowering: each alternative ends its match-tests
+     with "jmp lAfter", so the no-match fallthrough skips the body:
+     "cmp; jnz(lBody,lF); lF: ... ; jmp lAfter; lBody: S; jmp lEnd;
+     lAfter:". Falls into the next alternative, ELSE, or END. *)
+  CaseAlt
+    =
+      CaseLabel
+      { "," CaseLabel }
+      ":"
+      StatSeq                            .
+  CaseLabel
+    = Expr
+      [ ".." Expr                ] .
+  ReturnStat
+    = "RETURN"
+      [ Expr                       ] .
+  Design
+    = GetIdent                        .
+  Expr
+    = SimExpr
+      [ Rel SimExpr
+         ] .
+  Rel
+    = "="
+    | "#"
+    | "<"
+    | "<="
+    | ">"
+    | ">="                               .
+  SimExpr
+    =
+      [ "+" | "-"                        ]
+      Term
+      { AddOp Term
+         } .
+  AddOp
+    = "+"
+    | "-"
+    | "OR"                               .
+  Term
+    = Fact { MulOp Fact
+       } .
+  MulOp
+    = "*"
+    | "/"
+    | "DIV"
+    | "MOD"
+    | "AND"                              .
+  Fact
+    = integer
+    | real
+    | string
+    | Design
+    | "(" Expr ")"
+    | "NOT" Fact                 .
+  GetIdent
+    = ident                              .
+```

+ 66 - 0
docs/session-2026-09-19.md

@@ -0,0 +1,66 @@
+# Session save — 2026-09-19 (m2compiler-V3)
+
+## Where we are
+
+HEAD: `1505ca8` — `v3-step1.11`, suite **35/35** (15 run + 20 reject).
+Repo: `/home/eric/Projets/Projets-Modula2/MyWork/m2compiler-V3`, no remote
+(push recipe in untracked `git.txt` → `http://git.yojik.eu/eric/M2compiler-V3.git`).
+Uncommitted right now: `docs/grammar.md` (fresh extraction, needs a
+commit decision). `git.txt` intentionally untracked.
+
+## Tag lineage (all green at every tag)
+
+v3-step1 (5/5 integer pipeline) → 1.1 (scope-tree+BST symtab) → 1.2
+(heap-allocated storage) → 1.3 (0x-only hex, a-f, E-exponents, `//`)
+→ 1.4 (3 units, imports, opaque, nesting) → 1.5 (`file:line:col`
+console errors) → 1.6 (showcase1=50) → 1.7 (scalar TYPEs, headings,
+bodies, FORWARD) → 1.8 (showcase2=71) → 1.9 (statements+booleans,
+QBE block discipline) → 1.10 (Clarion classes + corrected OOP.txt
+sketch) → 1.11 (showcase4=44, showcase5=7×230 class frontier);
+v3-step2 (REAL lowering, scalar story complete); docs-only commits
+(OOP.txt ref, root README) untagged between 1.9 and 2.
+
+## Grammar status (`compiler/src/M2.atg`, ~900 lines, LL(1) with one
+benign Coco warning in TypeBlock — verified correct codegen)
+
+Lowers: program modules; CONST/VAR/scalar TYPEs (named, subrange
+w/ folded literal bounds, enum declared); full scalar arith incl.
+REAL (`d`, swtof, cXXd); relations; eager AND/OR/NOT; IF/CASE
+(labels+ranges+ELSE, compare-chain)/WHILE/REPEAT/LOOP+EXIT/FOR-full-BY/
+RETURN(232-checked). Parse-now+230: DEFINITION/IMPLEMENTATION,
+nesting (+EXPORT), proc bodies, FORWARD, enum use, classes
+(decl+single-inheritance+IMPLEMENTATION blocks+VIRTUAL).
+Still syntax errors: ARRAY/RECORD/SET/POINTER, WITH, IN, calls.
+
+## Locked decisions (do not relitigate without cause)
+
+Classic idents (no `_`, R10 reserves leading `_`); `0x`-only hex;
+`#` only; `NOT/AND/OR` only; no `B/C`; unary minus at expr start
+only (Wirth); `;` method separators (example over sketch);
+single inheritance; every ARRAY = LONGCARD count + elements
+(inline, element counts, no NUL); UCHAR=32-bit + UString=count+
+codepoints, UTF-8 edges, hard lex errors; self-host bar =
+byte-identical `.ssa` over 3 stages. Truncating DIV/MOD (`/` on
+ints too, Test2 parity). Eager booleans. Test2-compat QbeGen
+signatures kept for growth.
+
+## Open threads / next = step 3 composites
+
+ARRAY/RECORD/SET/POINTER + length-prefixed layout + WITH + IN;
+`docs/plan.md` + `docs/summary_step3.md` (stub) hold the scope.
+Also pending: unmatched-FORWARD 231, fwd/body signature compare,
+CHAR-arith policy, enum lowering, LONGINT quads, sibling-method
+visibility (step 4 calls), class lowering (vtable/THIS), `0b` +
+digit separators, `grammar.md` regen hook in build.sh (offered,
+not done). A phantom `t_class` mid-session failure never
+reproduced — see `docs/summary_step1.10.md` §3; clean-rebuild
+first if anything similar appears.
+
+## Resume commands
+
+```sh
+cd /home/eric/Projets/Projets-Modula2/MyWork/m2compiler-V3/compiler
+./build.sh && ./run_tests.sh   # expect 35/35
+```
+Then: interactive § statements for composites (ask which first:
+ARRAY+indexing, RECORD+WITH, SET+IN, POINTER).