Просмотр исходного кода

Ulm-Oberon validator pilot (ulmo oracle, builds, tests)

Eric Streit 4 дней назад
Родитель
Сommit
23761556eb
5 измененных файлов с 327 добавлено и 0 удалено
  1. 5 0
      grammars/Oberon2/README.md
  2. 55 0
      grammars/Ulm/README.md
  3. 45 0
      grammars/Ulm/build.sh
  4. 62 0
      grammars/Ulm/tests/deep.mod
  5. 160 0
      grammars/Ulm/ulm_oberon.atg

+ 5 - 0
grammars/Oberon2/README.md

@@ -33,6 +33,11 @@ with optional value, `&`/`~`/`OR`, `IN`/`IS`, sets with `..`
 ranges, `NIL`, `NEW` (no dispose), `DIV`/`MOD`, `IMPORT` lists,
 `FORWARD` (`PROCEDURE ^ name`) declarations.
 
+Note: the reference `Grammars/Voc/voc_oberon.atg` is byte-identical
+to the Oberon-2 grammar (modulo its header comment), and
+`Grammars/Oberon-grammar/original_oberon.atg` differs only in its tags
+line — this single validator covers all three.
+
 ## Build
 
 ```sh

+ 55 - 0
grammars/Ulm/README.md

@@ -0,0 +1,55 @@
+# Ulm-Oberon validator pilot
+
+`ulm_oberon.atg` describes classic Oberon as implemented by `ulmo`,
+ported from the reference (`Grammars/Ulm/Ulml-Oberon.atg`, extracted
+from ulmo's own `OberonParser`). The reference is written for a newer
+Coco/R generation (`/* */` comments, implicit whitespace), so the
+port starts with: comment conversion, explicit `IGNORE CASE`,
+`IGNORE CHR(9)..CHR(13)`, `COMMENTS FROM "(*" TO "*)" NESTED`, and a
+`CHR(10)` `eol` set. It then needed the same repair family as the
+Oberon-2 pilot, all diagnosed against `ulmo` (frontend verdicts
+only — its backend crashes with an assertion on valid code, exit
+still 0, so the harness reads `**` markers, never exit codes):
+
+1. Duplicate `CASE` labels in `statement`: ident-led `assignment`
+   vs `ProcedureCall` merged into `IdentTail`.
+2. The optional-prefix trap in `qualident` (`[ident "."]` committed
+   on any identifier): `ident { "." ident }`.
+3. The designator guard `"(" qualident ")"` shadowed calls with
+   non-identifier arguments: `"(" [ ExpList ] ")"`.
+4. Missing `;` after the module header ident (reference parses
+   `MODULE T` then expects `END`).
+5. `DEFINITION` modules need bodyless procedures, but an optional
+   body greedily eats the module's `END`: split into
+   `DefinitionModule` (bodyless `DefProcDecl`) vs `Module`
+   (body required) — disjoint `DEFINITION`/`MODULE` keywords, no
+   greediness possible.
+6. Strings are `"`-quoted in Ulm Oberon (`'hi'` is "invalid string
+   delimiter"); single-quote chars like `'a'` do not exist
+   (`41X` hex form does).
+
+Ulm facts confirmed by probe: case-insensitive, `CASE` with `|`
+and `ELSE`, `LOOP`/`EXIT`, `RETURN` with value, `&`/`~`, `IN`,
+`WITH` guarded-pointer only, declaration order enforced
+(`ulmo` rejects `VAR` after procedures — the validator stays
+order-free, documented leniency), filenames must match module
+names, backend assertion crashes are ignored (frontend-only rule).
+
+## Build
+
+```sh
+./build.sh        # -> build/Ulm (build/ is git-ignored)
+```
+
+Needs `CR` (Coco/R), `gm2 -fiso`, the `parser.frm`/`scanner.frm`
+frames and `FileIO.def/.mod` — all resolved from the environment with
+sensible defaults (`CRFRAMES`, `M2LIB`, `CR`, `GM2`).
+
+## Differential results (Ulm vs `ulmo`, frontend)
+
+Accept battery (15 cases: all statements, `CASE`/`ELSE`, `LOOP`/
+`EXIT`, `ORD`, floats, `RETURN`) + `tests/deep.mod` (records,
+pointers, multi-dim arrays, nested routines, guarded `WITH`):
+**0 false positives**. Broken mutants (8): **0 missed**, positions
+close. (`i := 1` without `;` is accepted by `ulmo` too — separator
+leniency, correctly skipped.)

+ 45 - 0
grammars/Ulm/build.sh

@@ -0,0 +1,45 @@
+#!/bin/bash
+# Build the Ulm-Oberon syntax validator from ulm_oberon.atg.
+# Needs: CR (Coco/R V1.53), gm2 -fiso, parser/scanner frames, FileIO.
+# Note: link via the two-step module list; a single-step link trips
+# over pre-existing errors in this gm2 build's own ISO library .defs.
+set -e
+CR="${CR:-/home/eric/Projets/Projets-Modula2/MyWork/CocoGm2/CR}"
+GM2="${GM2:-/home/eric/bin/Modula2/Gm2/bin/gm2}"
+FRAMES="${CRFRAMES:-/home/eric/Projets/Projets-Modula2/MyWork/Theia/GNU-grammar/build}"
+M2LIB="${M2LIB:-/home/eric/Projets/Projets-Modula2/MyWork/Theia/GNU-grammar/build}"
+HERE="$(cd "$(dirname "$0")" && pwd)"
+B="$HERE/build"
+rm -rf "$B"
+mkdir -p "$B"
+cp "$HERE/ulm_oberon.atg" "$B/"
+cp "$M2LIB/FileIO.def" "$M2LIB/FileIO.mod" "$B/"
+cd "$B"
+echo "=== CR ==="; CRFRAMES="$FRAMES" "$CR" -m -C ulm_oberon.atg
+echo "=== check: no degenerate code ==="
+if grep -q "IF FALSE\|WHILE FALSE" OberonP.mod; then echo "DEGENERATE GENERATION (IF/WHILE FALSE)"; exit 1; fi
+echo "=== patch: EOF check ==="
+python3 - "$B" <<'PYEOF'
+import sys
+b = sys.argv[1]
+s = open(b + '/OberonP.mod').read()
+old = '    OberonS.Reset; Get;\n    Oberon;\n\n  END Parse;'
+assert old in s, 'EOF patch pattern missing'
+s = s.replace(old, '    OberonS.Reset; Get;\n    IF sym # 0 THEN Oberon END;\n    IF sym # 0 THEN SynError(0) END;\n\n  END Parse;')
+open(b + '/OberonP.mod', 'w').write(s)
+print('patched')
+PYEOF
+echo "=== compile ==="
+for m in FileIO OberonS OberonP Oberon; do "$GM2" -fiso -I . -c "$m.mod"; done
+echo "=== link ==="
+# NOTE: the gen step exits 1 on pre-existing errors in gm2's own
+# ISO library .defs, but still writes a usable modules.lst.
+"$GM2" -fiso -I . -fgen-module-list=modules.lst -o /dev/null FileIO.o OberonS.o OberonP.o Oberon.mod || true
+"$GM2" -fiso -I . -fuse-list=modules.lst -o Ulm FileIO.o OberonS.o OberonP.o Oberon.mod
+echo "=== smoke ==="
+printf 'MODULE Hello;\nBEGIN\nEND Hello.\n' > Hello.mod
+./Ulm Hello.mod
+printf 'MODULE T;\nBEGIN\nEND T. GARBAGE\n' > Garbage.mod
+if ./Ulm Garbage.mod 2>&1 | grep -q "Incorrect"; then echo "trailing-garbage rejected"; else echo "EOF CHECK FAILED"; exit 1; fi
+./Ulm "$HERE/tests/deep.mod"
+rm -f Hello.mod Hello.LST Garbage.mod Garbage.LST "$HERE/tests/deep.LST"

+ 62 - 0
grammars/Ulm/tests/deep.mod

@@ -0,0 +1,62 @@
+MODULE Deep;
+
+CONST N = 10;
+  Neg = -5;
+  Pi = 3.14159;
+  Hello = "hi";
+
+TYPE
+  IntArr = ARRAY 10 OF INTEGER;
+  Matrix = ARRAY 2, 3 OF INTEGER;
+  Point = RECORD x, y: INTEGER END;
+  PInt = POINTER TO RECORD v: INTEGER END;
+  Node = POINTER TO NodeDesc;
+  NodeDesc = RECORD
+    key: INTEGER;
+    left, right: Node
+  END;
+
+VAR
+  i, j: INTEGER;
+  ch: CHAR;
+  b: BOOLEAN;
+  r: REAL;
+  a: IntArr;
+  pt: Point;
+  p: PInt;
+  t: Node;
+
+PROCEDURE Sum(a, b: INTEGER): INTEGER;
+BEGIN
+  RETURN a + b
+END Sum;
+
+PROCEDURE Fill(VAR a: IntArr; val: INTEGER);
+VAR k: INTEGER;
+BEGIN
+  k := 1;
+  WHILE k <= N DO a[k] := val; k := k + 1 END
+END Fill;
+
+BEGIN
+  i := 1;
+  WHILE i < N DO i := i + 1 END;
+  REPEAT
+    CASE i OF
+      1: i := 1
+    | 2, 3: i := 2
+    ELSE i := 9
+    END
+  UNTIL i > N;
+  LOOP
+    IF i > N THEN EXIT END;
+    i := i + 1
+  END;
+  pt.x := Sum(1, 2);
+  NEW(p); p.v := 1;
+  NEW(t); t.key := 5;
+  WITH t: Node DO t.key := 6 END;
+  r := 1.5E-3;
+  b := (i = 1) & (j # 2) OR ~b;
+  Fill(a, 0)
+END Deep.

+ 160 - 0
grammars/Ulm/ulm_oberon.atg

@@ -0,0 +1,160 @@
+COMPILER Oberon
+IGNORE CASE
+
+  (* Oberon grammar extracted from Ulm's Oberon compiler (ulmo)
+     Source: src/compiler/OberonParser.om and src/compiler/OberonLex.om
+     Coco/R grammar for the Oberon language as implemented by ulmo 0.11.0 *)
+
+  (* ------------------------------------------------------------------ *)
+  (* Character classes                                                   *)
+  (* ------------------------------------------------------------------ *)
+
+  CHARACTERS
+    letter   = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" .
+    digit    = "0123456789" .
+    hexdigit = digit + "ABCDEF" .
+    noQuote  = ANY - '"' - "'" .
+    eol = CHR(10) .
+IGNORE CHR(9) .. CHR(13)
+COMMENTS FROM "(*" TO "*)" NESTED
+
+  (* ------------------------------------------------------------------ *)
+  (* Scanner tokens                                                      *)
+  (* ------------------------------------------------------------------ *)
+
+  TOKENS
+    ident   = letter { letter | digit } .
+    integer = digit { digit } | digit { hexdigit } "H" .
+    real    = digit { digit } "." { digit }
+              [ ("E" | "D") [ "+" | "-" ] digit { digit } ] .
+    charconst = digit { hexdigit } "X" .
+    string  = '"' { noQuote } '"' .
+
+  (* ------------------------------------------------------------------ *)
+  (* Grammar productions                                                 *)
+  (* ------------------------------------------------------------------ *)
+
+  PRODUCTIONS
+
+    Oberon                 = CompilationUnit .
+
+    CompilationUnit        = DefinitionModule | Module .
+
+    DefinitionModule       = "DEFINITION" ident ";"
+                             [ ImportList ]
+                             [ DefinitionSequence ]
+                             "END" ident "."
+                           .
+
+    DefinitionSequence     = { ConstSection | TypeSection | VarSection | DefProcDecl } .
+
+    DefProcDecl            = ProcedureHeading ";" .
+
+    Module                 = "MODULE" ident ";"
+                             [ ImportList ]
+                             [ DeclarationSequence ]
+                             [ "BEGIN" StatementSequence ]
+                             "END" ident "."
+                           .
+
+    ImportList             = "IMPORT"
+                             ( ident [ ":=" ident ]
+                               { "," ident [ ":=" ident ] } )
+                             ";"
+                           .
+
+    DeclarationSequence    = { ConstSection | TypeSection | VarSection | ProcedureDeclaration ";" }
+                              { ForwardDeclaration ";" } .
+
+    ConstSection = "CONST" { ConstantDeclaration ";" } .
+    TypeSection = "TYPE" { TypeDeclaration ";" } .
+    VarSection = "VAR" { VariableDeclaration ";" } .
+
+    ConstantDeclaration   = identdef "=" ConstExpression .
+    TypeDeclaration        = identdef "=" type .
+    VariableDeclaration    = IdentList ":" type .
+
+    identdef               = ident [ "*" ] .
+    IdentList              = identdef { "," identdef } .
+
+    qualident              = ident { "." ident } .
+
+    ConstExpression        = expression .
+
+    type                   = qualident | ArrayType | RecordType
+                           | PointerType | ProcedureType .
+
+    ArrayType              = "ARRAY" length { "," length } "OF" type .
+    length                 = ConstExpression .
+
+    RecordType             = "RECORD" [ "(" BaseType ")" ]
+                             FieldListSequence "END" .
+    BaseType               = qualident .
+    FieldListSequence       = FieldList { ";" FieldList } .
+    FieldList              = [ IdentList ":" type ] .
+
+    PointerType            = "POINTER" "TO" type .
+    ProcedureType          = "PROCEDURE" [ FormalParameters ] .
+
+    FormalParameters      = "(" [ FPSection { ";" FPSection } ] ")"
+                             [ ":" qualident ] .
+    FPSection              = [ "VAR" ] ident { "," ident } ":" FormalType .
+    FormalType             = { "ARRAY" "OF" } ( qualident | ProcedureType ) .
+
+    designator             = qualident
+                             { "." ident | "[" ExpList "]"
+                               | "(" [ ExpList ] ")" | "^" } .
+    ExpList                = expression { "," expression } .
+
+    expression             = SimpleExpression [ relation SimpleExpression ] .
+    relation               = "=" | "#" | "<" | "<=" | ">" | ">=" | "IN" | "IS" .
+
+    SimpleExpression       = [ "+" | "-" ] term { AddOperator term } .
+    AddOperator            = "+" | "-" | "OR" .
+
+    term                   = factor { MulOperator factor } .
+    MulOperator            = "*" | "/" | "DIV" | "MOD" | "&" .
+
+    factor                 = number | charconst | string | "NIL" | set
+                           | designator [ ActualParameters ]
+                           | "(" expression ")" | "~" factor .
+
+    number                 = integer | real .
+
+    set                    = "{" [ element { "," element } ] "}" .
+    element                = expression [ ".." expression ] .
+
+    ActualParameters       = "(" [ ExpList ] ")" .
+
+    statement              = [ ident IdentTail | IfStatement
+                           | CaseStatement | WhileStatement | RepeatStatement
+                           | LoopStatement | WithStatement | "EXIT"
+                           | "RETURN" [ expression ] ] .
+  IdentTail = { "." ident | "[" ExpList "]" | "(" [ ExpList ] ")" | "^" }
+              [ ":=" expression | "(" [ ExpList ] ")" ] .
+
+
+    StatementSequence       = statement { ";" statement } .
+
+    IfStatement            = "IF" expression "THEN" StatementSequence
+                             { "ELSIF" expression "THEN" StatementSequence }
+                             [ "ELSE" StatementSequence ] "END" .
+
+    CaseStatement          = "CASE" expression "OF" case { "|" case }
+                             [ "ELSE" StatementSequence ] "END" .
+    case                   = [ CaseLabelList ":" StatementSequence ] .
+    CaseLabelList          = CaseLabels { "," CaseLabels } .
+    CaseLabels             = ConstExpression [ ".." ConstExpression ] .
+
+    WhileStatement         = "WHILE" expression "DO" StatementSequence "END" .
+    RepeatStatement        = "REPEAT" StatementSequence "UNTIL" expression .
+    LoopStatement          = "LOOP" StatementSequence "END" .
+    WithStatement          = "WITH" qualident ":" qualident "DO" StatementSequence "END" .
+
+    ProcedureDeclaration   = ProcedureHeading ";" ProcedureBody ident .
+    ProcedureHeading       = "PROCEDURE" [ "*" ] identdef [ FormalParameters ] .
+    ProcedureBody          = DeclarationSequence [ "BEGIN" StatementSequence ] "END" .
+
+    ForwardDeclaration     = "PROCEDURE" "^" ident [ "*" ] [ FormalParameters ] .
+
+END Oberon.