Skip to content

Spec 035: the grammar and the parser on etalii-lark (T164, T165) - #81

Merged
vrenken merged 1 commit into
developfrom
features/035-adp-tool-language-lark
Oct 24, 2026
Merged

vrenken merged 1 commit into
developfrom
features/035-adp-tool-language-lark

Conversation

@vrenken

@vrenken vrenken commented Oct 24, 2026

Copy link
Copy Markdown
Contributor

Requested by Peter · project thread

Delivery 2b of etalii.adp spec 035 (specs/035-adp-tool-language/): tasks T164 and T165, added by the amendment of 2026-10-24 (research R9, etalii.adp #504). ATD's grammar and parser are now built on etalii-lark 0.4.0, which got the accessors this needs in vrenken/EtAlii.Lark#13.

What

adp-atd-grammar (T164)

  • The notation reader keeps its refusals of what ATD's subset does not admit.
  • The modules go to etalii-lark in LALR mode with the contextual lexer and strict mode. The tables, conflicts and terminal collisions are therefore Lark's own verdict, reported in the normalized tables' names: "x", $END.
  • tables::to_json prints Lark's table, read through Lark::parse_table(), as the normalized ParseTables. atd tables equals atd.tables.json byte for byte.
  • Lexer is now a thin wrapper over Lark's lexer.
  • The crate's own LR(0) items, lookaheads, collision DFAs and regex lexer are removed.

adp-atd-syntax (T165)

  • The grammar's modules are embedded at build time, and Lark builds the parser once per process. Nothing is read at run time; the WebAssembly build is unaffected.
  • etalii-lark's interactive parser is fed one token at a time with next_token, each token lexed for its state.
  • T039's recovery runs through the interactive parser, as before:
    • a missing } at the end of the file is fed in;
    • otherwise the text is skipped to the next construct start a state on the stack reads, and the stack is popped there with truncate.
  • Popped and skipped text stays in the file as skipped tokens. The text between tokens is lexed as trivia, so the tree stays lossless.
  • The tree is lowered from Lark's tree (keep_all_tokens, no placeholders).
  • The crate's own table-driven parser and static tables are removed.

Startup. Lark's strict-mode checks take nine tenths of building the parser, so the run-time parser leaves them out. Its tables are the same, and strictness is checked where the tables are: the T032/T034 tests, atd tables, and etalii.adp's grammar job. The first parse then costs about 0.3 s instead of 2 s. etalii-lark and the regex crates it builds with are optimised in debug builds as well; unoptimised, building the grammar took 17 s.

Unchanged tests as the guard

  • T032 (the notation, conflicts, collisions, lexer) and T034 (the tables against Lark's).
  • T035 and T037: lossless examples, schema-valid trees, refused files' error positions, a broken file.
  • The shell's .atd tests: the editor walk, drawing, and accessibility.
  • atd check --corpus refused: all 22 refused files give exactly their findings, ATD1001's message included.

The workspace's fmt, clippy -D warnings and tests pass. So do the WebAssembly build of adp-atd-lsp, atd record --check, sync-etalii-adp.py --check, and cargo-deny's licence check of atd and the language server.

🤖 Generated with Claude Code

As research R9 was amended on 2026-10-24, ATD's grammar and parser are
built on etalii-lark 0.4.0, the Rust port of Lark, instead of a table
builder, lexer and parser of their own.

adp-atd-grammar (T164) keeps the notation reader's refusals of what the
subset does not admit and hands the modules to etalii-lark in LALR mode
with the contextual lexer and strict, so that the tables, the conflicts
and the terminal collisions are Lark's own verdict, reported in the
table's names; it prints Lark's table as the normalized ParseTables
(byte for byte atd.tables.json). Its own LR(0) items, lookaheads,
collision DFAs and regex lexer are removed; T032 and T034 pass
unchanged.

adp-atd-syntax (T165) embeds the grammar's modules at build time and
builds the parser once per process. It feeds etalii-lark's interactive
parser one token at a time, each lexed for its state, drives T039's
error recovery through it (the stack popped with truncate, the
construct start fed), keeps the popped and skipped text as skipped
tokens and lexes the text between tokens as trivia, so the tree stays
lossless, and lowers from Lark's tree. Its own table-driven parser and
static tables are removed; T035, T037 and the shell's .atd tests pass
unchanged, and the refused corpus gives its findings exactly.

The parser is built without strict mode, whose checks take nine tenths
of the build and are made where the tables are: about 0.3 s instead of
2 s. etalii-lark and the regex crates it builds with are optimised in
debug builds too.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@vrenken
vrenken enabled auto-merge October 24, 2026 14:36
@vrenken vrenken self-assigned this Oct 24, 2026
@vrenken
vrenken merged commit 13a4c60 into develop Oct 24, 2026
8 checks passed
@vrenken
vrenken deleted the features/035-adp-tool-language-lark branch October 24, 2026 15:09
vrenken added a commit that referenced this pull request Oct 25, 2026
… offset

Since the parser moved to etalii-lark (#81), each token the parser reads
between its own creates a LexerState with LexerState::new, which counts the
lines of all the text before it. Reading a file's trivia became quadratic:
on the 5,000-line description of SC-006, diagnostics after a keystroke took
1.4 s and completion 1.2 s natively, and 1.9 s and 1.7 s in VS Code over
WebAssembly, against bounds of 1 s and 0.5 s. The lexer's state now starts
at the position by its byte offset, as nothing reads its lines and ATD's
terminals have no anchors: 0.35 s and 0.19 s. The Build now runs the
latency bench, which no job ran, so this is held from here on.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.

1 participant