Skip to content

parser: leftover-token hints diagnose the actual mistake (#732) - #801

Merged
InauguralPhysicist merged 1 commit into
mainfrom
fix-732-parser-hint-specialization
Aug 2, 2026
Merged

parser: leftover-token hints diagnose the actual mistake (#732)#801
InauguralPhysicist merged 1 commit into
mainfrom
fix-732-parser-hint-specialization

Conversation

@InauguralPhysicist

Copy link
Copy Markdown
Collaborator

Closes #732.

The fix

p_end_statement's hint is now specialized on the leftover token and its predecessor — exactly the shape the issue prescribed. All five table rows from the issue now read:

Input New hint
print("hello") calls use 'of', not parentheses: write print of x (or print of [a, b] for several arguments)
n is len(xs) same, for len
def f(a): function definitions are written 'define f(...) as:'
f is x => x + 1 lambda parameters need parentheses: (x) => ...
say x 'say' is not a statement keyword; a call is written 'say of x', and check the name for typos

The specialized identifier arms require the statement to have been a lone bare identifier (p->pos == stmt_start + 1), so genuine statement splits keep the original diagnosis: x is 2 x is 3 and y is w z is 3 both still say "one statement per line". That guard is what keeps the new hints from misdiagnosing the case the old hint was actually right about.

Note 1 from the issue (the add2 of (1, 2) cascade) — also fixed

p_end_statement now resyncs silently when the current statement already reported a parse error (error-count snapshot taken at statement entry). add2 of (1, 2) produces exactly one error — the real expected ')', got ',' — instead of that plus a spurious "unexpected number after statement" with the misleading hint.

Note 2 (precedence errors printing to stdout before failing) is evaluation-order semantics, not diagnostics — left to its own issue.

Stability

The recorded --lint --json / LSP message is unchanged (unexpected X after statement); the hint is display-layer only, so E-codes and downstream consumers see no difference.

Docs + tests

  • docs/llms.txt ladder entries (the E002 note and the run-it signature) rewritten to describe the specialized hints instead of asserting the single wrong cause — including that the caret sits one token after the mistake.
  • Five new examples/errors/ demos pin each hint via # expect-error:; the harness gains an optional # expect-error-count: N header, used to pin the single-error cascade so the suppressed spurious error can't silently return. Suite [90] goes 14 → 19 checks.

Validation

  • Release suite 3355/3355; ASan+UBSan detect_leaks=1 suite 3359/3359, leak tally 0.
  • tools/doc_drift_check.sh clean.
  • The existing two_statements_one_line.eigs expect-error (original hint text) passes unchanged — the control case is pinned both ways.

🤖 Generated with Claude Code

The four most likely model-generated mistakes — print("hello"),
def f(a):, f is x => x + 1, say x — all landed in p_end_statement,
whose blanket " — one statement per line" named a rule none of them
broke, while docs/llms.txt taught the same misdiagnosis (agreement
reading as confirmation). The hint is now specialized on the leftover
and predecessor tokens:

- leftover '(' after an identifier -> "calls use 'of', not parentheses:
  write f of x (or f of [a, b] for several arguments)"
- leftover '=>' -> "lambda parameters need parentheses: (x) => ..."
- leftover identifier after a LONE-identifier statement -> name-check
  hint, with 'def' specifically pointed at "define f(...) as:"
- everything else keeps the original text: x is 2 x is 3 still says
  "one statement per line" (the lone-ident guard is what keeps the new
  arms from misdiagnosing genuine statement splits like y is w z is 3)

Cascade guard from the same pass: a statement that already reported a
parse error resyncs silently instead of stacking a spurious
"unexpected X after statement" on its debris — add2 of (1, 2) now gets
exactly one error, the real "expected ')', got ','".

The recorded --lint --json / LSP message is unchanged ("unexpected X
after statement") — the hint is display-layer, so diagnostic codes and
consumers are stable.

docs/llms.txt validation-ladder entries rewritten to describe the
specialized hints (and that the caret sits one token AFTER the
mistake). Five new examples/errors/ demos pin the hints; the error-
examples harness gains an optional "# expect-error-count: N" header
pinning the single-error cascade (suite [90], 14 -> 19 checks).

Validation: release suite 3355/3355; ASan+UBSan detect_leaks=1 suite
3359/3359, leak tally 0; doc_drift_check clean.

Closes #732

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 2, 2026 05:13

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves EigenScript’s parser diagnostics for “leftover-token after statement” errors by tailoring the hint text to the specific leftover token (and its predecessor), addressing the misleading catch-all “one statement per line” guidance described in #732. It also prevents a known cascade where a real parse error would be followed by a second spurious “unexpected … after statement” error, and it updates the documentation and error-example corpus to lock in the new behavior.

Changes:

  • Specialize p_end_statement’s hint based on the leftover token (e.g., ( / => / identifier-after-lone-ident statement) and suppress follow-on leftover-token errors when the current statement already reported a parse error.
  • Extend the error-example test harness to optionally assert the number of “Parse error …” lines (# expect-error-count:) and add five new examples/errors/*.eigs demos.
  • Update docs/llms.txt, the test-suite check count, and the changelog to reflect the new diagnostics behavior.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/parser.c Adds statement-entry snapshots for cascade suppression and uses leftover-token context to generate a more accurate diagnostic hint.
tests/test_error_examples.sh Adds optional # expect-error-count: support to pin error-cascade behavior in examples.
tests/run_all_tests.sh Updates suite [90] label to reflect the increased number of error-example checks.
examples/errors/unknown_statement_word.eigs New pinned demo for the “unknown statement word” specialized hint.
examples/errors/paren_tuple_call.eigs New pinned demo ensuring the add2 of (1, 2) case yields exactly one parse error.
examples/errors/paren_call.eigs New pinned demo for paren-style call hinting (print("hello")).
examples/errors/def_not_define.eigs New pinned demo for def vs define … as: hinting.
examples/errors/bare_lambda_params.eigs New pinned demo for unparenthesized lambda parameter hinting (x => …).
docs/llms.txt Updates the validation ladder to reflect the specialized hints and caret positioning semantics.
CHANGELOG.md Documents the diagnostics fix and the new error-example pinning behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@InauguralPhysicist
InauguralPhysicist merged commit aa21eb3 into main Aug 2, 2026
19 checks passed
@InauguralPhysicist
InauguralPhysicist deleted the fix-732-parser-hint-specialization branch August 2, 2026 05:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AI-legibility: the four most likely model-generated mistakes all produce "one statement per line", and llms.txt teaches that misdiagnosis

2 participants