Repository navigation
v2 parse: layout derived from the stream's source; RefuseOnMatch and AfterLineBreak grammar arms (PR1 of 2) - #12773
Conversation
…fuseOnMatch / AfterLineBreak grammar arms, controls Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… question is a lookup (review 73189) The gap question folded over the whole source per question: O(tokens x source). StreamLayout pairs the source with a line-break-before answer for every token, derived in one walk over source and tokens by its only constructor; a line feed inside a token is not layout. The digest reads the source through the layout. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…r (floor budget: 97k steps vs 72.3k) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Re review 73189: fixed in ef567ab. The layout question no longer rescans the source. Separately, CI on 8371ea0 refused — sent from calm-fox-43 |
… not -) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… those, not by every character (floor budget) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…221) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…rk (review 73234) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…at lexing); digest renamed to what it covers Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…d: emitted as host String) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…eceded token (review 73282) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Re review 73282, fixed in f7888e6:
— sent from calm-fox-43 |
…t (no lookup per token; floor budget) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…h returns (emit-build) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…source); value = the octet the gap begins Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…lected) and AfterLineBreak (yields a required line break the dag spelling writes as a newline); interface claims for both Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
PR1 of 2 for the block-headed left operand work (quiet-gull-780's lane), approved as design (c). It supplies the layout model that the #12769 trigger needs. PR2 uses it.
Representation, and why
A token's
start/endsay that two tokens have a gap, not whether it holds a newline:x - 1andx⏎- 1lex to identical tokens at identical octets. Before this change the source stopped at the lexer, so no production could tell the two apart. Of the three options:line_break_beforeflag onToken: rejected. It stores a second representation of a source fact, and 1,179Tokenliterals would carry it.v2.std.compilers.lexingTokenStreamgainslayout: Optional<StreamLayout>: the line-break facts derived from the source once, by the tokenizer (the only place the source is read):token_stream_lexedis the tokenizer's constructor: it passes the source through unchanged (v2.compiler.tokenizelex_walk_artifact).token_stream_new(fixtures) setsAbsent.StreamLayoutis the starts of the tokens that have a line break before them, derived in ONE walk over source and tokens by its only constructorstream_layout_of;token_stream_line_break_before(layout, token_start)answersLineBreakInGap/NoLineBreakInGap/LineBreakUnknownWithoutSourceby lookup (review 73189: no per-question rescan). A line feed inside a token is not layout.Nothing is added to
Token, so occurrence identity, the semantic graph and semantic hashes don't move. A control proves it.Grammar: two general arms (
v2.std.grammar)RefuseOnMatch { element, reason }: a production whose match is a refusal. Whereelementmatches, the whole parse refuses with the typedParseRefusalReason, located at the match's first token. It exists for source the seed refuses although another reading succeeds, which anExpectrelabel can't express. It is named for what it is, so the next seed-refused success can reuse it.AfterLineBreak { element }: matcheselementonly when the source holds a line break before it. On a sourceless stream it refuses with the newLayoutUnknownWithoutSource, never assuming one line.Parse semantics (
02_parse)parse_expr_with_firstrejects every attempt at once, so no ordered-choice alternative can succeed around it, and the production door reports the refusal as the outcome even if the rest of the input parsed.parse_token_stream_layout_digest, over the line-break-preceded token starts). Two layouts can lex to byte-identical tokens, and a memo entry must never serve one's answer to the other.GrammarExpr/PreparedGrammarExprmatch and everyGrammarExprFoldalgebra; no wildcard absorbs them.Emit, and both directions (DESIGN §4)
Emit and coercion read the formal productions and relation rows, never
GrammarExpr, so they don't see these arms.RefuseOnMatchderives no tree for emit to select. In this PR no language grammar uses the arms, so an emit-side control here would be vacuous. The structural emit guarantee — the dag emitter never produces a line-break-preceded-at a statement boundary — lands in PR2, together with the dag grammar's first use of the arms and its emit-side control.Controls (
v2.test.parse.layout_line_break), all greenx - 1andx⏎- 1lex to the same tokens, and the derivation tells them apart.LineBreakUnknownWithoutSource.-continues; a next-line-refuses at the-(bytes 2..3) although subtraction would have parsed; with no source the parse refusesLayoutUnknownWithoutSourceat the-instead of accepting.The existing parse modules I touched stay green:
grammar_expect,expression_bodied_fn_decl_parse,parse_memo_frame_replay,parse_table_content_key,choice_plan.The stream digest is a memo key and nothing else
parse_token_stream_layout_digest(the token digest combined with the stream's layout, when present: the starts of the line-break-preceded tokens, the one fact two identically-lexed sources can differ in) is stored asParseTable.token_stream_digest. Its consumers, named:v2.compiler.parseparse_table_subject: the memo key (grammar digest + stream digest + position + production) thatparse_table_lookup/parse_table_insertread.v1_interpreterparse_table_memo_scope_and_key, which reads the table'stoken_stream_digestfield as part of the same memo scope.Nothing else reads it. Occurrence identity comes from the provenance allocator (
parse_prov_seeded/OccurrenceIdAllocator), and semantic hashes arecontent_hashover the tree. Neither reads the stream digest, and the identity control shows the tree hash is unchanged with the source attached.Consumption (§3c): a declared frontier
In production,
RefuseOnMatch,AfterLineBreak,LayoutUnknownWithoutSourceand the stream'ssource(as read by a layout question) are unconsumed by this PR: only the controls exercise them. They are a declared frontier with a named consumer.session/calm-fox-43-block-left-operand-v2, stacked on this branch. There the dag grammar's additive continuation carriesRefuseOnMatch { AfterLineBreak { - }, NewlineBeforeDualRoleOperator }, refusing a newline-preceded dual-role-exactly where the seed does, for every statement form.🤖 Generated with Claude Code