diff --git a/src/v2/compiler/02_parse.dag b/src/v2/compiler/02_parse.dag index cfffde79f34..0901d8b11f6 100644 --- a/src/v2/compiler/02_parse.dag +++ b/src/v2/compiler/02_parse.dag @@ -1762,18 +1762,18 @@ fn grammar_validate_for_parse(root: GrammarRoot, at: Locus) -> Outcome) -> ParseSyncState { +fn parse_sync_step(acc: ParseSyncState, tok: Token, sync_tokens: List) -> ParseSyncState { match acc.found_sync { true => ParseSyncState { found_sync: true, result: list_snoc_item(xs: acc.result, item: tok) } false => - match sync_tokens.member(tok.class) { + match contains(xs: sync_tokens, item: tok.class, eq: fn(a, b) { a == b }) { true => ParseSyncState { found_sync: true, result: list_snoc_item(xs: std.algebra.Empty, item: tok) } false => ParseSyncState { found_sync: false, result: std.algebra.Empty } } } } -fn parse_skip_to_sync(tokens: List, sync_tokens: Set) -> ParseSyncState { +fn parse_skip_to_sync(tokens: List, sync_tokens: List) -> ParseSyncState { fold_list( xs: tokens, empty: ParseSyncState { found_sync: false, result: std.algebra.Empty }, diff --git a/src/v2/extdeps/languages/dag.dag b/src/v2/extdeps/languages/dag.dag index a161fafaafc..77a160cf5ba 100644 --- a/src/v2/extdeps/languages/dag.dag +++ b/src/v2/extdeps/languages/dag.dag @@ -3456,17 +3456,15 @@ fn dag_lex_token_symbol(sym: Symbol) -> Bool { ) } -fn dag_sync_tokens() -> Set { - Set { - member: fn(sym) { - sym == ^dag_token_kw_module || - sym == ^dag_token_kw_import || - sym == ^dag_token_kw_alias || - sym == ^dag_token_kw_type || - sym == ^dag_token_kw_data || - sym == ^dag_token_kw_fn - } - } +fn dag_sync_tokens() -> List { + [ + ^dag_token_kw_module, + ^dag_token_kw_import, + ^dag_token_kw_alias, + ^dag_token_kw_type, + ^dag_token_kw_data, + ^dag_token_kw_fn + ] } diff --git a/src/v2/std/grammar.dag b/src/v2/std/grammar.dag index 47a9f1f5965..f8a1fd550f4 100644 --- a/src/v2/std/grammar.dag +++ b/src/v2/std/grammar.dag @@ -289,10 +289,27 @@ type GrammarProduction { emitted: Node } +// SYNC TOKENS ARE AN ENUMERATION, NOT A PREDICATE. +// +// A grammar's error-recovery sync tokens are finite and known at authoring time — every producer in +// the corpus writes either an empty set or a literal list of keyword classes. Carrying them as +// `Set` made them a POINTWISE-POWER characteristic function (`std.types` grounds +// `Set = PointwisePower`), which is the right grounding for a set in general and +// the wrong carrier for six literal keywords: a characteristic function cannot be enumerated, so +// the whole grammar value became unhashable through this one field. +// +// That was not a theoretical loss. `prepare_grammar` is memoized cross-claim on +// (declaration identity, content hash of the grammar), and the interpreter's content-hash walk +// BAILS on a closure — so the memo could never form its key, never store, and never hit, for every +// language in the corpus and not only `.dag`. The measured consequence was a full ~12s grammar +// preparation on every parse, including of an empty module. +// +// The field is therefore the enumeration it always was. `Set` keeps its grounding untouched; what +// changed is that this field stopped borrowing a power-set to carry a list. type GrammarRoot { start: Symbol productions: List - sync_tokens: Set + sync_tokens: List } type ValidatedGrammarRoot { @@ -347,8 +364,8 @@ fn grammar_named_edge(name: Symbol, target: Node) -> Edge { } } -fn grammar_empty_sync_tokens() -> Set { - Set { member: fn(_) { false } } +fn grammar_empty_sync_tokens() -> List { + [] } fn grammar_empty_node() -> Node {