# 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.)