You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4.6KB


story: E03-S03 owner: e03_expr_stmt_parser status: done started: 2026-11-07 depends-on: [E03-S02]

E03-S03 Claim

Intended change

Replace the pass-2 token scan in src/compiler/parser.s with a recursive descent statement parser and a precedence-climbing expression parser (spec 7 and 8). Every statement line is parsed in both passes: syntax errors are reported in pass 1, name/member/access errors (the E03-S02 lookups, now driven by the parser) in pass 2. A syntax error unwinds to the statement and skips its line. Expression nesting is bounded so the hardware stack cannot overflow. For tests, pass 1 can record each expression in postfix order (operands by column, then operators), which shows precedence and associativity directly.

File area

  • src/compiler/parser.s, src/common/parser.inc
  • src/common/diagcat.inc (appended entries), docs/diagnostics.md
  • tests/parser/, scripts/build-parser-test.ps1, scripts/verify-parser-test.ps1
  • docs/architecture.md, docs/language-spec.md (only if a decision is needed), docs/decisions.md
  • planning/claims/E03-S03.md
  • generated files under build/

Acceptance evidence

  • Implements documented precedence and left/right associativity: prec.n8 records each statement's expression in postfix order in x128 and all 15 match prec.expect (a - b - c -> a b - c -, a / b / c, - - a, not d and d -> d not d and, d or d and d, mixed levels, comparisons below arithmetic, groups). Mutations: making binary operators right associative, or swapping the +/* levels, each fail the golden.
  • Parses every version 1 statement form: forms.n8 (assignment to a local, field, bare field, and element; call statements including chained calls and calls as arguments; if/else nested; while; for with and without step, negative step; return with and without a value; print of string, char, boolean, and word; delete; new with an expression argument) and all 7 accepted grammar fixtures parse with no diagnostics.
  • Rejects multiple statements per line and cross-line expressions: multi_stmt.n8 gives E021 at the second print (2:11) and line_cont.n8 gives E022 at the end of total = 1 + (3:14); both were pending since E03-S01 and now pass as ordinary rejected fixtures. syntax.n8 adds a = 1 a = 2 (E021) and print with no operand (E022).
  • Recovers sufficiently to report a later independent error: syntax.n8 reports 15 independent syntax errors on consecutive lines (missing )/], not-a-statement, unassignable target, =/to missing in for, stray )/*, else outside an if and twice in one, an expression nested too deeply) and then a pass-2 unknown name; a lexer error is not followed by a cascaded E022.

Coordination notes

  • No other claim is active. Types of operators and conversions remain E03-S04; the parser only carries the member-chain type that E03-S02's access checks need.

Work log

  • Appended E038 NOT A STATEMENT, E039 CANNOT ASSIGN TO THIS, E040 ‘]’ EXPECTED, E041 TO EXPECTED, E042 ‘=’ EXPECTED, and E225 EXPRESSION NESTED TOO DEEPLY; docs regenerated.
  • The pass-2 token scan (scan_refs) is gone; its name and member lookups are called by the parser, so all E03-S02 fixtures pass unchanged.
  • Expression depth is bounded at 16 levels; the harness records the lowest hardware SP seen in ex_enter and fails below $60 (seen: $A7).
  • The write-and-check demo (which links the parser) passes; its Vim typing once doubled a key at 120 ms per key, which the demo's read-back caught, so its default is now 200 ms.

Completion

  • Tests run: scripts/verify-parser-test.ps1 (final run and two mutations), scripts/build-diag-test.ps1 catalog check, scripts/demo-write-check.ps1.
  • Results: 31 parser fixtures pass (no pending ones remain); both mutations caught; demo passes.
  • Files changed: src/compiler/parser.s, src/common/parser.inc, src/common/diagcat.inc, docs/diagnostics.md, docs/architecture.md, tests/parser/ (prec, forms, syntax fixtures, harness trace and SP dump), scripts/build-parser-test.ps1, scripts/verify-parser-test.ps1, scripts/demo-write-check.ps1, this claim.
  • Remaining risks: return is accepted in the program body (spec 7 does not restrict it; E03-S04/E04 must define what it does there). Parenthesized groups do not carry a member-chain type, so (x).f is not access-checked. Literals accept postfix syntax (5.x) that E03-S04 must reject as a type error. A bare call f() reports the name or E021, not a dedicated message (D123). The trace hook writes without a bound; only the test enables it.

Powered by TurnKey Linux.