Skip to main content

tabnas/
lib.rs

1// Copyright (c) 2013-2026 Richard Rodger, MIT License
2
3#![doc = include_str!("../README.md")]
4#![allow(clippy::result_large_err)]
5
6pub const VERSION: &str = "0.12.8";
7
8pub mod builtins;
9pub mod context;
10pub mod error;
11pub mod grammar;
12pub mod lexer;
13mod merge;
14pub mod options;
15pub mod parser;
16pub mod rule;
17mod text;
18pub mod token;
19mod tracked;
20use crate::tracked::Tracked;
21pub mod utility;
22pub mod value;
23
24pub use context::{ActionError, Context, ContextSeed, InstanceInfo};
25pub use error::{RecoveredAt, TabnasError};
26pub use grammar::{
27    GrammarError, GrammarGroups, GrammarSetting, GrammarSettingAlt, GrammarSettingRule, GrammarSpec,
28};
29pub use lexer::{Lexer, RelexCheckpoint};
30pub use merge::MergeError;
31pub use options::MAX_RULE_HISTORY;
32pub use options::{
33    BudgetCheck, BudgetOptions, ColorOptions, CommentDef, CommentSuffixMatcher, ConfigModifier,
34    ContextParsePrepare, DebugOptions, DebugOutput, DebugPrintOptions, DebugSourceFormatter,
35    ErrMsgOptions, ErrorSuffix, ErrorSuffixCallback, ErrorSuffixContext, FixedOptions, FixedToken,
36    ImperativeCommentSuffixMatcher, ImperativeLexCheck, ImperativeLexMatcher,
37    ImperativeTextModifier, InfoOptions, LexCheck, LexCheckResult, LexCheckToken, LexMatcher,
38    LexMatcherCallback, LexMatcherFactory, ListOptions, MapMerge, MapOptions, MatchToken,
39    MatchTokenCallback, MatchTokenMatcher, MatchTokenResult, MatchValue, Options, ParseOptions,
40    ParsePrepare, ParsePrepareWithInstance, ParserOptions, ParserStart, ParserStartWithContext,
41    ParserStartWithInstance, RecoverOptions, ResultOptions, RewindOptions, SafeOptions,
42    SpaceOptions, TextModifier, ValueDef, ValueOptions, ValueTextModifier, ValueTransform,
43};
44pub use parser::{Continuations, ParseRecovery, Parser};
45pub use rule::{
46    ActionBinding, AltAction, AltActionBinding, AltBack, AltBackWithMatch, AltCondition,
47    AltConditionWithLexer, AltConditionWithLexerAndMatch, AltConditionWithMatch, AltError,
48    AltErrorWithMatch, AltMatch, AltModifier, AltModifierWithMatch, AltNext, AltNextWithMatch,
49    AltSpec, CompareOp, Condition, Rule, RuleDone, RuleDoneAlt, RuleName, RuleSnapshot, RuleSpec,
50    RuleState, StateAction,
51};
52pub use token::{
53    name_to_tin, tin_name, Point, Site, Tin, Token, TokenCode, TokenText, TokenValFunc, TIN_AA,
54    TIN_BD, TIN_CA, TIN_CB, TIN_CL, TIN_CM, TIN_CS, TIN_LN, TIN_MAX, TIN_NR, TIN_OB, TIN_OS,
55    TIN_SP, TIN_ST, TIN_TX, TIN_UK, TIN_VL, TIN_ZZ,
56};
57pub use value::{ListRef, MapRef, Text, Value};
58
59use indexmap::IndexMap;
60use std::any::Any;
61use std::collections::HashMap;
62use std::fmt;
63use std::panic::{catch_unwind, AssertUnwindSafe};
64use std::sync::atomic::{AtomicU64, Ordering};
65use std::sync::Arc;
66
67static NEXT_INSTANCE_ID: AtomicU64 = AtomicU64::new(1);
68
69type DecorationValue = dyn Any + Send + Sync;
70type DecorationEquality = dyn Fn(&DecorationValue) -> bool + Send + Sync;
71
72/// A type-erased value attached to a parser instance by a native plugin.
73///
74/// Equality-aware decorations created with [`Decoration::new`] can be
75/// compared when instances merge. Opaque decorations compare by shared
76/// allocation identity, which permits closures and other native handles to
77/// be inherited without pretending they have value equality.
78#[derive(Clone)]
79pub struct Decoration {
80    value: Arc<DecorationValue>,
81    equals: Arc<DecorationEquality>,
82    type_name: &'static str,
83}
84
85impl Decoration {
86    pub fn new<T>(value: T) -> Self
87    where
88        T: Any + PartialEq + Send + Sync,
89    {
90        let value = Arc::new(value);
91        let comparable = value.clone();
92        Self {
93            value,
94            equals: Arc::new(move |other| {
95                other
96                    .downcast_ref::<T>()
97                    .is_some_and(|other| comparable.as_ref() == other)
98            }),
99            type_name: std::any::type_name::<T>(),
100        }
101    }
102
103    pub fn opaque<T>(value: T) -> Self
104    where
105        T: Any + Send + Sync,
106    {
107        let value = Arc::new(value);
108        let identity = value.clone();
109        Self {
110            value,
111            equals: Arc::new(move |other| {
112                other
113                    .downcast_ref::<T>()
114                    .is_some_and(|other| std::ptr::eq(identity.as_ref(), other))
115            }),
116            type_name: std::any::type_name::<T>(),
117        }
118    }
119
120    pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
121        self.value.downcast_ref()
122    }
123
124    pub fn is<T: Any>(&self) -> bool {
125        self.value.is::<T>()
126    }
127
128    pub fn type_name(&self) -> &'static str {
129        self.type_name
130    }
131
132    pub(crate) fn equivalent(&self, other: &Self) -> bool {
133        self.type_name == other.type_name && (self.equals)(other.value.as_ref())
134    }
135}
136
137impl fmt::Debug for Decoration {
138    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
139        formatter
140            .debug_struct("Decoration")
141            .field("type_name", &self.type_name)
142            .finish_non_exhaustive()
143    }
144}
145
146pub type Action = Arc<dyn Fn(&mut Rule) + Send + Sync>;
147pub type ContextAction =
148    Arc<dyn Fn(&mut Rule, &mut Context) -> Result<(), ActionError> + Send + Sync>;
149pub type TokenSubscriber = Arc<dyn Fn(&Token) + Send + Sync>;
150pub type LexSubscriber = Arc<dyn Fn(&mut Token, &mut Rule, &mut Context) + Send + Sync>;
151pub type RuleSubscriber = Arc<dyn Fn(&mut Rule, &mut Context) + Send + Sync>;
152pub type RuleDoneSubscriber = Arc<dyn Fn(&Rule, &Context, &RuleDone) + Send + Sync>;
153/// A check the parse loop runs at every step; see [`Tabnas::parse_guard`].
154pub type ParseGuard = Arc<dyn Fn(&Context) -> bool + Send + Sync>;
155
156pub type PluginCallback = Arc<dyn Fn(&mut Tabnas, &Value) -> Result<(), PluginError> + Send + Sync>;
157
158/// Native Rust plugin descriptor. The explicit name replaces JavaScript's
159/// `Function.name` and provides a stable namespace for plugin options.
160#[derive(Clone)]
161pub struct Plugin {
162    pub name: String,
163    pub defaults: Value,
164    callback: PluginCallback,
165}
166
167impl Plugin {
168    pub fn new(
169        name: impl Into<String>,
170        callback: impl Fn(&mut Tabnas, &Value) -> Result<(), PluginError> + Send + Sync + 'static,
171    ) -> Self {
172        Self {
173            name: name.into(),
174            defaults: Value::object(IndexMap::new()),
175            callback: Arc::new(callback),
176        }
177    }
178
179    pub fn with_defaults(mut self, defaults: Value) -> Self {
180        self.defaults = defaults;
181        self
182    }
183}
184
185impl fmt::Debug for Plugin {
186    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
187        formatter
188            .debug_struct("Plugin")
189            .field("name", &self.name)
190            .field("defaults", &self.defaults)
191            .field("callback", &"<function>")
192            .finish()
193    }
194}
195
196#[derive(Debug, Clone, PartialEq, Eq)]
197pub struct PluginError(pub String);
198
199impl fmt::Display for PluginError {
200    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
201        formatter.write_str(&self.0)
202    }
203}
204
205impl std::error::Error for PluginError {}
206
207#[derive(Clone)]
208pub struct Tabnas {
209    pub id: String,
210    /// Identifier of the instance this parser was derived from.
211    pub parent_id: Option<String>,
212    /// Resolved configuration used by the lexer and parser.
213    ///
214    /// Wrapped so that writing to it is noticed: the shared copy handed
215    /// to each parse is built once and reused until something takes a
216    /// mutable path to this. Reads and writes both work as they did.
217    pub options: Tracked<Options>,
218    /// The `options` a parse gets, prepared once. Cloning a thirty-field
219    /// `Options` per `parse()` call was 7% of a small parse.
220    prepared_options: PreparedOptions,
221    /// The whole assembled `Parser`, prepared once. See `PreparedParser`.
222    prepared_parser: PreparedParser,
223    /// Accumulated option input before `config.modify` callbacks run. Keeping
224    /// this separate prevents non-idempotent modifiers from compounding on
225    /// each grammar overlay or derived instance.
226    pub(crate) raw_options: Options,
227    pub rules: Tracked<IndexMap<String, RuleSpec>>,
228    pub actions: Tracked<HashMap<String, Action>>,
229    pub context_actions: Tracked<HashMap<String, ContextAction>>,
230    pub matched_actions: Tracked<HashMap<String, AltAction>>,
231    pub state_actions: Tracked<HashMap<String, StateAction>>,
232    pub token_subscribers: Tracked<Vec<TokenSubscriber>>,
233    pub lex_subscribers: Tracked<Vec<LexSubscriber>>,
234    pub rule_subscribers: Tracked<Vec<RuleSubscriber>>,
235    pub rule_done_subscribers: Tracked<Vec<RuleDoneSubscriber>>,
236    /// Checks the parse loop runs at every step, by name; see
237    /// [`Tabnas::parse_guard`].
238    pub parse_guards: Tracked<IndexMap<String, ParseGuard>>,
239    pub plugins: Tracked<Vec<Plugin>>,
240    pub plugin_options: IndexMap<String, Value>,
241    /// Plugin-attached named values carried to derived instances.
242    pub decorations: IndexMap<String, Decoration>,
243    pub(crate) alt_conditions: HashMap<String, AltCondition>,
244    pub(crate) alt_match_conditions: HashMap<String, AltConditionWithMatch>,
245    pub(crate) alt_lexer_conditions: HashMap<String, AltConditionWithLexer>,
246    pub(crate) alt_lexer_match_conditions: HashMap<String, AltConditionWithLexerAndMatch>,
247    pub(crate) alt_modifiers: HashMap<String, AltModifier>,
248    pub(crate) alt_match_modifiers: HashMap<String, AltModifierWithMatch>,
249    pub(crate) alt_errors: HashMap<String, AltError>,
250    pub(crate) alt_match_errors: HashMap<String, AltErrorWithMatch>,
251    pub(crate) alt_pushes: HashMap<String, AltNext>,
252    pub(crate) alt_match_pushes: HashMap<String, AltNextWithMatch>,
253    pub(crate) alt_replaces: HashMap<String, AltNext>,
254    pub(crate) alt_match_replaces: HashMap<String, AltNextWithMatch>,
255    pub(crate) alt_backtracks: HashMap<String, AltBack>,
256    pub(crate) alt_match_backtracks: HashMap<String, AltBackWithMatch>,
257    pub(crate) match_token_refs: HashMap<String, (MatchTokenCallback, bool)>,
258    pub(crate) value_transform_refs: HashMap<String, ValueTransform>,
259    pub(crate) text_modifier_refs: HashMap<String, TextModifier>,
260    pub(crate) lex_check_refs: HashMap<String, LexCheck>,
261    pub(crate) comment_suffix_refs: HashMap<String, CommentSuffixMatcher>,
262    pub(crate) match_value_refs: HashMap<String, MatchTokenCallback>,
263    pub(crate) parse_prepare_refs: HashMap<String, ParsePrepare>,
264    pub(crate) budget_check_refs: HashMap<String, BudgetCheck>,
265    pub(crate) lex_match_refs: HashMap<String, LexMatcherCallback>,
266    pub(crate) imperative_lex_match_refs: HashMap<String, ImperativeLexMatcher>,
267    pub(crate) lex_match_factory_refs: HashMap<String, LexMatcherFactory>,
268    pub(crate) error_suffix_refs: HashMap<String, ErrorSuffixCallback>,
269    pub(crate) config_modifier_refs: HashMap<String, ConfigModifier>,
270    pub(crate) parser_start_refs: HashMap<String, ParserStart>,
271    pub(crate) parser_start_instance_refs: HashMap<String, ParserStartWithInstance>,
272    pub(crate) parser_start_context_refs: HashMap<String, ParserStartWithContext>,
273    pub(crate) map_merge_refs: HashMap<String, MapMerge>,
274}
275
276impl Default for Tabnas {
277    fn default() -> Self {
278        Self::new()
279    }
280}
281
282impl Tabnas {
283    pub fn new() -> Self {
284        Self::with_options(Options::default())
285    }
286
287    pub fn with_options(options: Options) -> Self {
288        let sequence = NEXT_INSTANCE_ID.fetch_add(1, Ordering::Relaxed);
289        let id = format!(
290            "Tabnas/{sequence}{}",
291            if options.tag.is_empty() || options.tag == "-" {
292                String::new()
293            } else {
294                format!("/{}", options.tag)
295            }
296        );
297        let plugin_options = options.plugin.clone();
298        let tabnas = Tabnas {
299            id,
300            parent_id: None,
301            raw_options: options.clone(),
302            options: Tracked::new(options),
303            prepared_options: PreparedOptions::default(),
304            prepared_parser: PreparedParser::default(),
305            rules: Tracked::new(IndexMap::new()),
306            actions: Tracked::new(HashMap::new()),
307            context_actions: Tracked::new(HashMap::new()),
308            matched_actions: Tracked::new(HashMap::new()),
309            state_actions: Tracked::new(HashMap::new()),
310            token_subscribers: Tracked::new(Vec::new()),
311            lex_subscribers: Tracked::new(Vec::new()),
312            rule_subscribers: Tracked::new(Vec::new()),
313            rule_done_subscribers: Tracked::new(Vec::new()),
314            parse_guards: Tracked::new(IndexMap::new()),
315            plugins: Tracked::new(Vec::new()),
316            plugin_options,
317            decorations: IndexMap::new(),
318            alt_conditions: HashMap::new(),
319            alt_match_conditions: HashMap::new(),
320            alt_lexer_conditions: HashMap::new(),
321            alt_lexer_match_conditions: HashMap::new(),
322            alt_modifiers: HashMap::new(),
323            alt_match_modifiers: HashMap::new(),
324            alt_errors: HashMap::new(),
325            alt_match_errors: HashMap::new(),
326            alt_pushes: HashMap::new(),
327            alt_match_pushes: HashMap::new(),
328            alt_replaces: HashMap::new(),
329            alt_match_replaces: HashMap::new(),
330            alt_backtracks: HashMap::new(),
331            alt_match_backtracks: HashMap::new(),
332            match_token_refs: HashMap::new(),
333            value_transform_refs: HashMap::new(),
334            text_modifier_refs: HashMap::new(),
335            lex_check_refs: HashMap::new(),
336            comment_suffix_refs: HashMap::new(),
337            match_value_refs: HashMap::new(),
338            parse_prepare_refs: HashMap::new(),
339            budget_check_refs: HashMap::new(),
340            lex_match_refs: HashMap::new(),
341            imperative_lex_match_refs: HashMap::new(),
342            lex_match_factory_refs: HashMap::new(),
343            error_suffix_refs: HashMap::new(),
344            config_modifier_refs: HashMap::new(),
345            parser_start_refs: HashMap::new(),
346            parser_start_instance_refs: HashMap::new(),
347            parser_start_context_refs: HashMap::new(),
348            map_merge_refs: HashMap::new(),
349        };
350        tabnas.emit_debug_config();
351        tabnas
352    }
353
354    pub fn rule(&mut self, spec: RuleSpec) -> &mut Self {
355        self.rules.insert(spec.name.clone(), spec);
356        self
357    }
358
359    /// Create or modify a rule in place, mirroring the imperative plugin
360    /// entry point in the TypeScript and Go engines.
361    pub fn define_rule(
362        &mut self,
363        name: impl Into<String>,
364        define: impl FnOnce(&mut RuleSpec),
365    ) -> &mut Self {
366        let name = name.into();
367        let spec = self
368            .rules
369            .entry(name.clone())
370            .or_insert_with(|| RuleSpec::new(name));
371        define(spec);
372        self
373    }
374
375    /// Create or modify a rule with a read-only parser snapshot. This is the
376    /// Rust spelling of the canonical `RuleDefiner(rs, parser)` callback and
377    /// gives a definer access to resolved options, tokens, and peer rules.
378    pub fn define_rule_with_parser(
379        &mut self,
380        name: impl Into<String>,
381        define: impl FnOnce(&mut RuleSpec, &Parser),
382    ) -> &mut Self {
383        let name = name.into();
384        self.rules
385            .entry(name.clone())
386            .or_insert_with(|| RuleSpec::new(name.clone()));
387        let parser = self.parser();
388        let spec = self
389            .rules
390            .get_mut(&name)
391            .expect("rule was inserted before its parser view was built");
392        define(spec, &parser);
393        self
394    }
395
396    /// Remove a named rule. Removing a rule that is absent is a no-op.
397    pub fn remove_rule(&mut self, name: &str) -> Option<RuleSpec> {
398        self.rules.shift_remove(name)
399    }
400
401    /// Apply and retain a native plugin. Defaults, previously accumulated
402    /// options for the same plugin, and call-site options are deep-merged in
403    /// that order. Panics are contained and returned as `PluginError`.
404    pub fn use_plugin(
405        &mut self,
406        plugin: Plugin,
407        options: Option<Value>,
408    ) -> Result<&mut Self, PluginError> {
409        let name = plugin.name.to_lowercase();
410        if name.is_empty() {
411            return Err(PluginError(
412                "Tabnas::use_plugin: plugin name is empty".into(),
413            ));
414        }
415        let current = self
416            .plugin_options
417            .get(&name)
418            .cloned()
419            .unwrap_or_else(|| Value::object(IndexMap::new()));
420        let merged = merge_plugin_values(
421            merge_plugin_values(current, plugin.defaults.clone()),
422            options.unwrap_or(Value::Undefined),
423        );
424        self.plugin_options.insert(name.clone(), merged.clone());
425        self.options.plugin.insert(name.clone(), merged.clone());
426        self.raw_options.plugin.insert(name, merged.clone());
427        self.plugins.push(plugin.clone());
428        match catch_unwind(AssertUnwindSafe(|| (plugin.callback)(self, &merged))) {
429            Ok(Ok(())) => Ok(self),
430            Ok(Err(error)) => Err(error),
431            Err(payload) => Err(PluginError(format!(
432                "plugin {} panicked: {}",
433                plugin.name,
434                panic_message(payload)
435            ))),
436        }
437    }
438
439    /// Return the resolved option bag for a plugin name.
440    pub fn plugin_options(&self, name: &str) -> Option<&Value> {
441        self.plugin_options.get(&name.to_lowercase())
442    }
443
444    /// Deep-merge an option bag into the named plugin namespace.
445    pub fn set_plugin_options(&mut self, name: impl Into<String>, options: Value) -> &mut Self {
446        let name = name.into().to_lowercase();
447        let current = self
448            .plugin_options
449            .get(&name)
450            .cloned()
451            .unwrap_or_else(|| Value::object(IndexMap::new()));
452        let merged = merge_plugin_values(current, options);
453        self.plugin_options.insert(name.clone(), merged.clone());
454        self.options.plugin.insert(name.clone(), merged.clone());
455        self.raw_options.plugin.insert(name, merged);
456        self
457    }
458
459    /// Create a child parser from this instance's options and re-run its
460    /// installed plugins so option-conditional grammar is rebuilt.
461    pub fn derive(&self, modify: impl FnOnce(&mut Options)) -> Result<Self, PluginError> {
462        let mut raw_options = if self.options.config_modify.is_empty() {
463            // Direct typed option mutation is part of the native Rust API.
464            // With no modifier-created delta, the public tree is the source.
465            self.options.peek().clone()
466        } else {
467            self.raw_options.clone()
468        };
469        modify(&mut raw_options);
470        let mut options = raw_options.clone();
471        options.refresh_configuration().map_err(PluginError)?;
472        let mut child = Self::with_options(options);
473        child.parent_id = Some(self.id.clone());
474        child.raw_options = raw_options;
475        child.plugin_options = self.plugin_options.clone();
476        child.decorations = self.decorations.clone();
477        child.inherit_function_references(self);
478        // The guards travel as the budget does, in the options above: a
479        // plugin re-run below that installs one again under the same name
480        // replaces it rather than adding a second.
481        child.parse_guards = Tracked::new(self.parse_guards.peek().clone());
482        for plugin in self.plugins.iter() {
483            let options = child
484                .plugin_options
485                .get(&plugin.name.to_lowercase())
486                .cloned();
487            child.use_plugin(plugin.clone(), options)?;
488        }
489        Ok(child)
490    }
491
492    fn inherit_function_references(&mut self, parent: &Self) {
493        // Rust binds serialized function names through an instance registry
494        // rather than a JavaScript function-valued `GrammarSpec.ref` object.
495        // A derived instance must retain that registry so later grammar
496        // overlays can resolve the same names. Re-run plugins may replace an
497        // entry with the same name, exactly as on the parent.
498        self.actions = parent.actions.clone();
499        self.context_actions = parent.context_actions.clone();
500        self.matched_actions = parent.matched_actions.clone();
501        self.state_actions = parent.state_actions.clone();
502        self.alt_conditions = parent.alt_conditions.clone();
503        self.alt_match_conditions = parent.alt_match_conditions.clone();
504        self.alt_lexer_conditions = parent.alt_lexer_conditions.clone();
505        self.alt_lexer_match_conditions = parent.alt_lexer_match_conditions.clone();
506        self.alt_modifiers = parent.alt_modifiers.clone();
507        self.alt_match_modifiers = parent.alt_match_modifiers.clone();
508        self.alt_errors = parent.alt_errors.clone();
509        self.alt_match_errors = parent.alt_match_errors.clone();
510        self.alt_pushes = parent.alt_pushes.clone();
511        self.alt_match_pushes = parent.alt_match_pushes.clone();
512        self.alt_replaces = parent.alt_replaces.clone();
513        self.alt_match_replaces = parent.alt_match_replaces.clone();
514        self.alt_backtracks = parent.alt_backtracks.clone();
515        self.alt_match_backtracks = parent.alt_match_backtracks.clone();
516        self.match_token_refs = parent.match_token_refs.clone();
517        self.value_transform_refs = parent.value_transform_refs.clone();
518        self.text_modifier_refs = parent.text_modifier_refs.clone();
519        self.lex_check_refs = parent.lex_check_refs.clone();
520        self.comment_suffix_refs = parent.comment_suffix_refs.clone();
521        self.match_value_refs = parent.match_value_refs.clone();
522        self.parse_prepare_refs = parent.parse_prepare_refs.clone();
523        self.budget_check_refs = parent.budget_check_refs.clone();
524        self.lex_match_refs = parent.lex_match_refs.clone();
525        self.imperative_lex_match_refs = parent.imperative_lex_match_refs.clone();
526        self.lex_match_factory_refs = parent.lex_match_factory_refs.clone();
527        self.error_suffix_refs = parent.error_suffix_refs.clone();
528        self.config_modifier_refs = parent.config_modifier_refs.clone();
529        self.parser_start_refs = parent.parser_start_refs.clone();
530        self.parser_start_instance_refs = parent.parser_start_instance_refs.clone();
531        self.parser_start_context_refs = parent.parser_start_context_refs.clone();
532        self.map_merge_refs = parent.map_merge_refs.clone();
533    }
534
535    /// Combine two tagged parser instances without modifying either source.
536    /// Options are conflict-checked against the shared defaults and rule
537    /// alternates are interleaved deterministically in a fresh token space.
538    pub fn merge(&self, other: &Self) -> Result<Self, MergeError> {
539        merge::merge(self, other)
540    }
541
542    /// Create a fresh standalone instance. The receiver's rules, plugins,
543    /// subscribers, callbacks, and custom token registrations are not copied.
544    pub fn empty(&self) -> Self {
545        Self::with_options(Options::empty())
546    }
547
548    /// `empty` with an explicit typed option set.
549    pub fn empty_with_options(&self, options: Options) -> Self {
550        Self::with_options(options)
551    }
552
553    /// Resolve or allocate a named token identity for typed matcher effects
554    /// and imperative rule construction.
555    pub fn token(&mut self, name: impl Into<String>) -> Tin {
556        let name = name.into();
557        let tin = self.options.register_token(name.clone());
558        self.raw_options.register_token(name);
559        tin
560    }
561
562    /// Resolve or allocate a token and bind it to a fixed source literal.
563    pub fn token_with_source(&mut self, name: impl Into<String>, source: impl Into<String>) -> Tin {
564        let name = name.into();
565        let name = if name.starts_with('#') {
566            name
567        } else {
568            format!("#{name}")
569        };
570        let source = source.into();
571        let tin = self.token(name.clone());
572        let token = FixedToken {
573            name: name.clone(),
574            tin,
575            source,
576        };
577        self.options
578            .fixed
579            .tokens
580            .insert(name.clone(), token.clone());
581        self.raw_options.fixed.tokens.insert(name, token);
582        tin
583    }
584
585    /// Return an independent snapshot of the resolved configuration.
586    pub fn config(&self) -> Options {
587        self.options.peek().clone()
588    }
589
590    /// Human-readable, deterministic description of this instance's public
591    /// grammar and lexer configuration. This carries the intentional
592    /// introspection helper provided by the mature Go runtime.
593    pub fn describe(&self) -> String {
594        use std::collections::BTreeSet;
595        use std::fmt::Write as _;
596
597        let mut output = String::from("=== Tabnas Instance ===\n");
598        let _ = writeln!(output, "Id: {}", self.id);
599        let _ = writeln!(output, "Tag: {}", self.options.tag);
600
601        output.push_str("\n--- Tokens ---\n");
602        let mut token_names = BTreeSet::new();
603        token_names.extend(self.options.tokens.keys().cloned());
604        token_names.extend(self.options.fixed.tokens.keys().cloned());
605        token_names.extend(self.options.match_tokens.keys().cloned());
606        for name in token_names {
607            if let Some(tin) = self.options.token(&name) {
608                let _ = writeln!(output, "  {name} = {tin}");
609            }
610        }
611
612        output.push_str("\n--- Fixed Tokens ---\n");
613        let mut fixed = self.options.fixed.tokens.values().collect::<Vec<_>>();
614        fixed.sort_by(|left, right| {
615            left.source
616                .cmp(&right.source)
617                .then_with(|| left.name.cmp(&right.name))
618        });
619        for token in fixed {
620            let _ = writeln!(
621                output,
622                "  {:?} -> {} ({})",
623                token.source, token.name, token.tin
624            );
625        }
626
627        output.push_str("\n--- Rules ---\n");
628        for (name, rule) in self.rules.iter() {
629            let _ = writeln!(
630                output,
631                "  {name}: open={} close={} bo={} ao={} bc={} ac={}",
632                rule.open.len(),
633                rule.close.len(),
634                rule.bo.len() + rule.bo_fns.len() + rule.bo_state_fns.len(),
635                rule.ao.len() + rule.ao_fns.len() + rule.ao_state_fns.len(),
636                rule.bc.len() + rule.bc_fns.len() + rule.bc_state_fns.len(),
637                rule.ac.len() + rule.ac_fns.len() + rule.ac_state_fns.len(),
638            );
639        }
640
641        if !self.options.lex.matchers.is_empty() {
642            output.push_str("\n--- Custom Matchers ---\n");
643            for (name, matcher) in &self.options.lex.matchers {
644                let _ = writeln!(output, "  {name} (priority={})", matcher.order);
645            }
646        }
647
648        let _ = writeln!(output, "\n--- Plugins: {} ---", self.plugins.len());
649        for plugin in self.plugins.iter() {
650            let _ = writeln!(output, "  {}", plugin.name);
651        }
652        output.push_str("\n--- Subscriptions ---\n");
653        let _ = writeln!(
654            output,
655            "  Token subscribers: {}",
656            self.token_subscribers.len()
657        );
658        let _ = writeln!(output, "  Lex subscribers: {}", self.lex_subscribers.len());
659        let _ = writeln!(
660            output,
661            "  Rule subscribers: {}",
662            self.rule_subscribers.len()
663        );
664        let _ = writeln!(
665            output,
666            "  RuleDone subscribers: {}",
667            self.rule_done_subscribers.len()
668        );
669        let _ = writeln!(output, "  Parse guards: {}", self.parse_guards.len());
670
671        output.push_str("\n--- Config ---\n");
672        let _ = writeln!(output, "  FixedLex: {}", self.options.fixed.lex);
673        let _ = writeln!(output, "  SpaceLex: {}", self.options.space.lex);
674        let _ = writeln!(output, "  LineLex: {}", self.options.line.lex);
675        let _ = writeln!(output, "  TextLex: {}", self.options.text.lex);
676        let _ = writeln!(output, "  NumberLex: {}", self.options.number.lex);
677        let _ = writeln!(output, "  CommentLex: {}", self.options.comment.lex);
678        let _ = writeln!(output, "  StringLex: {}", self.options.string.lex);
679        let _ = writeln!(output, "  ValueLex: {}", self.options.value.lex);
680        let _ = writeln!(output, "  MapExtend: {}", self.options.map.extend);
681        let _ = writeln!(output, "  ListProperty: {}", self.options.list.property);
682        let _ = writeln!(output, "  SafeKey: {}", self.options.safe.key);
683        let _ = writeln!(output, "  FinishRule: {}", self.options.rule.finish);
684        let _ = writeln!(output, "  RuleStart: {}", self.options.rule.start);
685        output
686    }
687
688    /// Install the lightweight lexer/rule trace provided by the mature Go
689    /// runtime. The sink receives one complete line per event; use
690    /// `enable_trace` to write those lines to the configured debug output.
691    pub fn enable_trace_with(&mut self, sink: impl Fn(&str) + Send + Sync + 'static) -> &mut Self {
692        let sink = Arc::new(sink);
693        let lex_sink = sink.clone();
694        self.subscribe_lex(move |token, _, context| {
695            lex_sink(&format!(
696                "[lex] {} tin={} src={:?} val={} at {}:{}",
697                token.name,
698                token.tin,
699                token.src,
700                context.options.debug.format_source(&token.val),
701                token.site.ri,
702                token.site.ci
703            ));
704        });
705        self.subscribe_rules(move |rule, context| {
706            sink(&format!(
707                "[rule] {} state={:?} node={} ki={}",
708                rule.name,
709                rule.state,
710                context.options.debug.format_source(&rule.node.borrow()),
711                context.iteration
712            ));
713        });
714        self
715    }
716
717    pub fn enable_trace(&mut self) -> &mut Self {
718        self.subscribe_lex(|token, _, context| {
719            context.options.debug.write(&format!(
720                "[lex] {} tin={} src={:?} val={} at {}:{}",
721                token.name,
722                token.tin,
723                token.src,
724                context.options.debug.format_source(&token.val),
725                token.site.ri,
726                token.site.ci
727            ));
728        });
729        self.subscribe_rules(|rule, context| {
730            context.options.debug.write(&format!(
731                "[rule] {} state={:?} node={} ki={}",
732                rule.name,
733                rule.state,
734                context.options.debug.format_source(&rule.node.borrow()),
735                context.iteration
736            ));
737        });
738        self
739    }
740
741    pub(crate) fn emit_debug_config(&self) {
742        if self.options.debug.print.config {
743            self.options.debug.write(&format!("{:#?}", self.options));
744        }
745    }
746
747    /// Mutate the accumulated typed options and rebuild resolved
748    /// configuration without discarding installed grammar rules.
749    pub fn set_options(
750        &mut self,
751        modify: impl FnOnce(&mut Options),
752    ) -> Result<&mut Self, PluginError> {
753        let mut raw_options = if self.options.config_modify.is_empty() {
754            self.options.peek().clone()
755        } else {
756            self.raw_options.clone()
757        };
758        modify(&mut raw_options);
759        let mut resolved = raw_options.clone();
760        resolved.refresh_configuration().map_err(PluginError)?;
761        self.raw_options = raw_options;
762        *self.options = resolved;
763        self.plugin_options = self.options.plugin.clone();
764        self.emit_debug_config();
765        Ok(self)
766    }
767
768    /// Installed plugins in application order.
769    pub fn installed_plugins(&self) -> Vec<Plugin> {
770        self.plugins.peek().clone()
771    }
772
773    /// Attach an equality-comparable native value to this instance.
774    pub fn decorate<T>(&mut self, name: impl Into<String>, value: T) -> &mut Self
775    where
776        T: Any + PartialEq + Send + Sync,
777    {
778        self.decorations.insert(name.into(), Decoration::new(value));
779        self
780    }
781
782    /// Attach a native value without requiring `PartialEq`. Opaque values
783    /// compare by identity when instances are merged.
784    pub fn decorate_opaque<T>(&mut self, name: impl Into<String>, value: T) -> &mut Self
785    where
786        T: Any + Send + Sync,
787    {
788        self.decorations
789            .insert(name.into(), Decoration::opaque(value));
790        self
791    }
792
793    pub fn decoration<T: Any>(&self, name: &str) -> Option<&T> {
794        self.decorations
795            .get(name)
796            .and_then(Decoration::downcast_ref)
797    }
798
799    pub fn decoration_entry(&self, name: &str) -> Option<&Decoration> {
800        self.decorations.get(name)
801    }
802
803    /// Rule specs in declaration order.
804    pub fn rule_specs(&self) -> Vec<&RuleSpec> {
805        self.rules.values().collect()
806    }
807
808    /// Rule names in declaration order.
809    pub fn rule_names(&self) -> Vec<String> {
810        self.rules.keys().cloned().collect()
811    }
812
813    pub fn token_set(&self, name: &str) -> Option<Vec<Tin>> {
814        self.options
815            .token_set
816            .get(name.trim_start_matches('#'))
817            .cloned()
818    }
819
820    pub fn set_token_set(&mut self, name: impl Into<String>, tins: Vec<Tin>) -> &mut Self {
821        let name = name.into();
822        let name = name.trim_start_matches('#').to_owned();
823        self.options.token_set.insert(name.clone(), tins.clone());
824        self.raw_options.token_set.insert(name, tins);
825        self
826    }
827
828    /// Resolve the token claimed by one fixed source string.
829    pub fn fixed(&self, source: &str) -> Option<Tin> {
830        self.options
831            .fixed
832            .tokens
833            .values()
834            .find(|token| token.source == source)
835            .map(|token| token.tin)
836    }
837
838    /// Resolve the source literal associated with a fixed token identity.
839    pub fn fixed_source(&self, tin: Tin) -> Option<&str> {
840        self.options
841            .fixed
842            .tokens
843            .values()
844            .find(|token| token.tin == tin)
845            .map(|token| token.source.as_str())
846    }
847
848    pub fn token_name(&self, tin: Tin) -> String {
849        self.options.token_name(tin)
850    }
851
852    pub fn action(
853        &mut self,
854        name: impl Into<String>,
855        action: impl Fn(&mut Rule) + Send + Sync + 'static,
856    ) -> &mut Self {
857        self.actions.insert(name.into(), Arc::new(action));
858        self
859    }
860
861    pub fn subscribe_tokens(
862        &mut self,
863        subscriber: impl Fn(&Token) + Send + Sync + 'static,
864    ) -> &mut Self {
865        self.token_subscribers.push(Arc::new(subscriber));
866        self
867    }
868
869    /// Subscribe to every lexer token, including ignored trivia. The
870    /// subscriber may annotate or replace token fields before parsing uses it.
871    pub fn subscribe_lex(
872        &mut self,
873        subscriber: impl Fn(&mut Token, &mut Rule, &mut Context) + Send + Sync + 'static,
874    ) -> &mut Self {
875        self.lex_subscribers.push(Arc::new(subscriber));
876        self
877    }
878
879    pub fn subscribe_rules(
880        &mut self,
881        subscriber: impl Fn(&mut Rule, &mut Context) + Send + Sync + 'static,
882    ) -> &mut Self {
883        self.rule_subscribers.push(Arc::new(subscriber));
884        self
885    }
886
887    pub fn subscribe_rule_done(
888        &mut self,
889        subscriber: impl Fn(&Rule, &Context, &RuleDone) + Send + Sync + 'static,
890    ) -> &mut Self {
891        self.rule_done_subscribers.push(Arc::new(subscriber));
892        self
893    }
894
895    pub fn action_with_context(
896        &mut self,
897        name: impl Into<String>,
898        action: impl Fn(&mut Rule, &mut Context) -> Result<(), ActionError> + Send + Sync + 'static,
899    ) -> &mut Self {
900        self.context_actions.insert(name.into(), Arc::new(action));
901        self
902    }
903
904    /// Register a named alternate action with the canonical matched-alt
905    /// argument and token-error return channel.
906    pub fn action_with_match_ref(
907        &mut self,
908        name: impl Into<String>,
909        action: impl Fn(&mut Rule, &mut Context, &mut AltMatch) -> Result<Option<Token>, ActionError>
910            + Send
911            + Sync
912            + 'static,
913    ) -> &mut Self {
914        self.matched_actions.insert(name.into(), Arc::new(action));
915        self
916    }
917
918    /// Register a typed rule lifecycle reference such as `@top-bo`,
919    /// `@top-ao/prepend`, or `@top-bc/replace`. Serialized grammar loading
920    /// wires reserved names onto their matching rule phase.
921    pub fn state_action_ref(
922        &mut self,
923        name: impl Into<String>,
924        action: impl Fn(&mut Rule, &mut Context) -> Result<(), ActionError> + Send + Sync + 'static,
925    ) -> &mut Self {
926        self.context_actions.insert(name.into(), Arc::new(action));
927        self
928    }
929
930    /// Register a lifecycle reference with canonical `next` and chained
931    /// output-token arguments.
932    pub fn state_action_with_next_ref(
933        &mut self,
934        name: impl Into<String>,
935        action: impl Fn(
936                &mut Rule,
937                &mut Context,
938                Option<&RuleSnapshot>,
939                Option<Token>,
940            ) -> Result<Option<Token>, ActionError>
941            + Send
942            + Sync
943            + 'static,
944    ) -> &mut Self {
945        self.state_actions.insert(name.into(), Arc::new(action));
946        self
947    }
948
949    /// Register a typed function reference for a serialized alternate `c`.
950    pub fn alt_condition(
951        &mut self,
952        name: impl Into<String>,
953        condition: impl Fn(&mut Rule, &mut Context) -> bool + Send + Sync + 'static,
954    ) -> &mut Self {
955        self.alt_conditions.insert(name.into(), Arc::new(condition));
956        self
957    }
958
959    /// Register the canonical alternate condition shape, including the live
960    /// effective match record.
961    pub fn alt_condition_with_match(
962        &mut self,
963        name: impl Into<String>,
964        condition: impl Fn(&mut Rule, &mut Context, &mut AltMatch) -> bool + Send + Sync + 'static,
965    ) -> &mut Self {
966        self.alt_match_conditions
967            .insert(name.into(), Arc::new(condition));
968        self
969    }
970
971    /// Register a serialized alternate condition that can re-enter the live
972    /// lexer. Use `Lexer::next_raw_for_rule` when ignored tokens must remain
973    /// observable, matching the canonical TypeScript lexer callback surface.
974    pub fn alt_condition_with_lexer(
975        &mut self,
976        name: impl Into<String>,
977        condition: impl for<'source> Fn(&mut Rule, &mut Context, &mut Lexer<'source>) -> bool
978            + Send
979            + Sync
980            + 'static,
981    ) -> &mut Self {
982        self.alt_lexer_conditions
983            .insert(name.into(), Arc::new(condition));
984        self
985    }
986
987    /// Register the complete canonical alternate-condition surface, with the
988    /// shared live match record and controlled access to the active lexer.
989    pub fn alt_condition_with_lexer_and_match(
990        &mut self,
991        name: impl Into<String>,
992        condition: impl for<'source> Fn(&mut Rule, &mut Context, &mut AltMatch, &mut Lexer<'source>) -> bool
993            + Send
994            + Sync
995            + 'static,
996    ) -> &mut Self {
997        self.alt_lexer_match_conditions
998            .insert(name.into(), Arc::new(condition));
999        self
1000    }
1001
1002    /// Register a typed function reference for a serialized alternate `h`.
1003    pub fn alt_modifier(
1004        &mut self,
1005        name: impl Into<String>,
1006        modifier: impl Fn(AltSpec, &mut Rule, &mut Context) -> AltSpec + Send + Sync + 'static,
1007    ) -> &mut Self {
1008        self.alt_modifiers.insert(name.into(), Arc::new(modifier));
1009        self
1010    }
1011
1012    pub fn alt_modifier_with_match(
1013        &mut self,
1014        name: impl Into<String>,
1015        modifier: impl Fn(AltMatch, &mut Rule, &mut Context, Option<&RuleSnapshot>) -> AltMatch
1016            + Send
1017            + Sync
1018            + 'static,
1019    ) -> &mut Self {
1020        self.alt_match_modifiers
1021            .insert(name.into(), Arc::new(modifier));
1022        self
1023    }
1024
1025    /// Register a typed function reference for a serialized alternate `e`.
1026    pub fn alt_error(
1027        &mut self,
1028        name: impl Into<String>,
1029        error: impl Fn(&mut Rule, &mut Context) -> Option<Token> + Send + Sync + 'static,
1030    ) -> &mut Self {
1031        self.alt_errors.insert(name.into(), Arc::new(error));
1032        self
1033    }
1034
1035    pub fn alt_error_with_match(
1036        &mut self,
1037        name: impl Into<String>,
1038        error: impl Fn(&mut Rule, &mut Context, &mut AltMatch) -> Option<Token> + Send + Sync + 'static,
1039    ) -> &mut Self {
1040        self.alt_match_errors.insert(name.into(), Arc::new(error));
1041        self
1042    }
1043
1044    /// Register a typed function reference for a serialized alternate `p`.
1045    pub fn alt_push(
1046        &mut self,
1047        name: impl Into<String>,
1048        route: impl Fn(&mut Rule, &mut Context) -> Option<String> + Send + Sync + 'static,
1049    ) -> &mut Self {
1050        self.alt_pushes.insert(name.into(), Arc::new(route));
1051        self
1052    }
1053
1054    pub fn alt_push_with_match(
1055        &mut self,
1056        name: impl Into<String>,
1057        route: impl Fn(&mut Rule, &mut Context, &mut AltMatch) -> Option<String> + Send + Sync + 'static,
1058    ) -> &mut Self {
1059        self.alt_match_pushes.insert(name.into(), Arc::new(route));
1060        self
1061    }
1062
1063    /// Register a typed function reference for a serialized alternate `r`.
1064    pub fn alt_replace(
1065        &mut self,
1066        name: impl Into<String>,
1067        route: impl Fn(&mut Rule, &mut Context) -> Option<String> + Send + Sync + 'static,
1068    ) -> &mut Self {
1069        self.alt_replaces.insert(name.into(), Arc::new(route));
1070        self
1071    }
1072
1073    pub fn alt_replace_with_match(
1074        &mut self,
1075        name: impl Into<String>,
1076        route: impl Fn(&mut Rule, &mut Context, &mut AltMatch) -> Option<String> + Send + Sync + 'static,
1077    ) -> &mut Self {
1078        self.alt_match_replaces.insert(name.into(), Arc::new(route));
1079        self
1080    }
1081
1082    /// Register a typed function reference for a serialized alternate `b`.
1083    pub fn alt_backtrack(
1084        &mut self,
1085        name: impl Into<String>,
1086        backtrack: impl Fn(&mut Rule, &mut Context) -> usize + Send + Sync + 'static,
1087    ) -> &mut Self {
1088        self.alt_backtracks.insert(name.into(), Arc::new(backtrack));
1089        self
1090    }
1091
1092    pub fn alt_backtrack_with_match(
1093        &mut self,
1094        name: impl Into<String>,
1095        backtrack: impl Fn(&mut Rule, &mut Context, &mut AltMatch) -> usize + Send + Sync + 'static,
1096    ) -> &mut Self {
1097        self.alt_match_backtracks
1098            .insert(name.into(), Arc::new(backtrack));
1099        self
1100    }
1101
1102    /// Register an effect-based function reference for
1103    /// `options.match.token`. The callback may consume only a non-empty prefix
1104    /// of the remaining source; invalid results are ignored.
1105    pub fn match_token_ref(
1106        &mut self,
1107        name: impl Into<String>,
1108        eager: bool,
1109        matcher: impl Fn(&str) -> Option<MatchTokenResult> + Send + Sync + 'static,
1110    ) -> &mut Self {
1111        self.match_token_refs
1112            .insert(name.into(), (Arc::new(matcher), eager));
1113        self
1114    }
1115
1116    /// Register an effect-based high-priority value matcher for a serialized
1117    /// `options.match.value.<name>.match` function reference.
1118    pub fn match_value_ref(
1119        &mut self,
1120        name: impl Into<String>,
1121        matcher: impl Fn(&str) -> Option<MatchTokenResult> + Send + Sync + 'static,
1122    ) -> &mut Self {
1123        self.match_value_refs.insert(name.into(), Arc::new(matcher));
1124        self
1125    }
1126
1127    /// Register a typed transformer for a regexp-backed
1128    /// `options.value.def.<name>.val` function reference.
1129    ///
1130    /// The slice contains the whole match followed by capture groups;
1131    /// unmatched optional groups are represented by empty strings, matching
1132    /// the Go port's cross-language callback shape.
1133    pub fn value_transform_ref(
1134        &mut self,
1135        name: impl Into<String>,
1136        transform: impl Fn(&[String]) -> Value + Send + Sync + 'static,
1137    ) -> &mut Self {
1138        self.value_transform_refs
1139            .insert(name.into(), Arc::new(transform));
1140        self
1141    }
1142
1143    /// Register an effect-based unquoted-text value modifier for use from a
1144    /// serialized `options.text.modify` reference.
1145    pub fn text_modifier_ref(
1146        &mut self,
1147        name: impl Into<String>,
1148        modifier: impl Fn(Value) -> Value + Send + Sync + 'static,
1149    ) -> &mut Self {
1150        self.text_modifier_refs
1151            .insert(name.into(), TextModifier::new(modifier));
1152        self
1153    }
1154
1155    /// Register a full canonical text modifier. It runs after the text/value
1156    /// matcher has produced a token and receives live lexer, rule, context,
1157    /// and resolved-option access.
1158    pub fn imperative_text_modifier_ref(
1159        &mut self,
1160        name: impl Into<String>,
1161        modifier: impl for<'source> Fn(Value, &mut Lexer<'source>, &mut Rule, &mut Context, &Options) -> Value
1162            + Send
1163            + Sync
1164            + 'static,
1165    ) -> &mut Self {
1166        self.text_modifier_refs
1167            .insert(name.into(), TextModifier::new_imperative(modifier));
1168        self
1169    }
1170
1171    /// Register an effect-based lexer preflight hook for serialized matcher
1172    /// options such as `options.string.check` or `options.fixed.check`.
1173    pub fn lex_check_ref(
1174        &mut self,
1175        name: impl Into<String>,
1176        check: impl Fn(&str) -> LexCheckResult + Send + Sync + 'static,
1177    ) -> &mut Self {
1178        self.lex_check_refs
1179            .insert(name.into(), LexCheck::new(check));
1180        self
1181    }
1182
1183    /// Register a canonical live-lexer preflight hook. It may inspect and
1184    /// advance the cursor and return a token built by that lexer.
1185    pub fn imperative_lex_check_ref(
1186        &mut self,
1187        name: impl Into<String>,
1188        check: impl for<'source> Fn(&mut Lexer<'source>) -> LexCheckResult + Send + Sync + 'static,
1189    ) -> &mut Self {
1190        self.lex_check_refs
1191            .insert(name.into(), LexCheck::new_imperative(check));
1192        self
1193    }
1194
1195    /// Register an effect-based custom matcher factory reference for a
1196    /// serialized `options.lex.match.<name>.make` entry.
1197    pub fn lex_match_ref(
1198        &mut self,
1199        name: impl Into<String>,
1200        matcher: impl Fn(&str) -> Option<LexCheckToken> + Send + Sync + 'static,
1201    ) -> &mut Self {
1202        self.lex_match_refs.insert(name.into(), Arc::new(matcher));
1203        self
1204    }
1205
1206    /// Register a full native lexer matcher for a serialized
1207    /// `options.lex.match.<name>.make` reference. The matcher owns cursor
1208    /// advancement and may inspect or modify the active rule and context.
1209    pub fn imperative_lex_match_ref(
1210        &mut self,
1211        name: impl Into<String>,
1212        matcher: impl for<'source> Fn(&mut Lexer<'source>, &mut Rule, &mut Context) -> Option<Token>
1213            + Send
1214            + Sync
1215            + 'static,
1216    ) -> &mut Self {
1217        self.imperative_lex_match_refs
1218            .insert(name.into(), Arc::new(matcher));
1219        self
1220    }
1221
1222    /// Register a setup-time matcher factory for a serialized
1223    /// `options.lex.match.<name>.make` reference. It sees the fully resolved
1224    /// options and returns the persistent matcher, or `None` to disable it.
1225    pub fn lex_match_factory_ref(
1226        &mut self,
1227        name: impl Into<String>,
1228        factory: impl Fn(&Options) -> Option<ImperativeLexMatcher> + Send + Sync + 'static,
1229    ) -> &mut Self {
1230        self.lex_match_factory_refs
1231            .insert(name.into(), Arc::new(factory));
1232        self
1233    }
1234
1235    /// Register a typed dynamic renderer for a serialized
1236    /// `options.errmsg.suffix` function reference.
1237    pub fn error_suffix_ref(
1238        &mut self,
1239        name: impl Into<String>,
1240        render: impl Fn(&ErrorSuffixContext) -> String + Send + Sync + 'static,
1241    ) -> &mut Self {
1242        self.error_suffix_refs.insert(name.into(), Arc::new(render));
1243        self
1244    }
1245
1246    /// Register a typed load-time mutator for a serialized
1247    /// `options.config.modify.<name>` function reference.
1248    pub fn config_modifier_ref(
1249        &mut self,
1250        name: impl Into<String>,
1251        modifier: impl Fn(&mut Options) + Send + Sync + 'static,
1252    ) -> &mut Self {
1253        self.config_modifier_refs
1254            .insert(name.into(), ConfigModifier::new(modifier));
1255        self
1256    }
1257
1258    /// Register the complete canonical configuration callback shape. The
1259    /// first argument is the mutable resolved configuration; the second is
1260    /// the immutable accumulated option input for this configure pass.
1261    pub fn config_modifier_with_options_ref(
1262        &mut self,
1263        name: impl Into<String>,
1264        modifier: impl Fn(&mut Options, &Options) + Send + Sync + 'static,
1265    ) -> &mut Self {
1266        self.config_modifier_refs
1267            .insert(name.into(), ConfigModifier::with_options(modifier));
1268        self
1269    }
1270
1271    /// Register a typed replacement parse entry point for a serialized
1272    /// `options.parser.start` function reference.
1273    pub fn parser_start_ref(
1274        &mut self,
1275        name: impl Into<String>,
1276        start: impl Fn(&str) -> Result<Value, Box<TabnasError>> + Send + Sync + 'static,
1277    ) -> &mut Self {
1278        self.parser_start_refs.insert(name.into(), Arc::new(start));
1279        self
1280    }
1281
1282    /// Register the mature parser-start shape, including the owning instance
1283    /// and caller metadata.
1284    pub fn parser_start_with_instance_ref(
1285        &mut self,
1286        name: impl Into<String>,
1287        start: impl Fn(&str, &Tabnas, &Value) -> Result<Value, Box<TabnasError>> + Send + Sync + 'static,
1288    ) -> &mut Self {
1289        self.parser_start_instance_refs
1290            .insert(name.into(), Arc::new(start));
1291        self
1292    }
1293
1294    /// Register the complete canonical parser-start shape, including the
1295    /// optional parent-context seed supplied to `parse_with_context`.
1296    pub fn parser_start_with_context_ref(
1297        &mut self,
1298        name: impl Into<String>,
1299        start: impl Fn(&str, &Tabnas, &Value, Option<&ContextSeed>) -> Result<Value, Box<TabnasError>>
1300            + Send
1301            + Sync
1302            + 'static,
1303    ) -> &mut Self {
1304        self.parser_start_context_refs
1305            .insert(name.into(), Arc::new(start));
1306        self
1307    }
1308
1309    /// Register a duplicate-map-value merger for a serialized
1310    /// `options.map.merge` function reference.
1311    pub fn map_merge_ref(
1312        &mut self,
1313        name: impl Into<String>,
1314        merge: impl Fn(Value, Value, &mut Rule, &mut Context) -> Value + Send + Sync + 'static,
1315    ) -> &mut Self {
1316        self.map_merge_refs.insert(name.into(), Arc::new(merge));
1317        self
1318    }
1319
1320    /// Register an effect-based terminator probe for a serialized comment
1321    /// definition's `suffix` option.
1322    pub fn comment_suffix_ref(
1323        &mut self,
1324        name: impl Into<String>,
1325        matcher: impl Fn(&str) -> Option<String> + Send + Sync + 'static,
1326    ) -> &mut Self {
1327        self.comment_suffix_refs
1328            .insert(name.into(), CommentSuffixMatcher::new(matcher));
1329        self
1330    }
1331
1332    /// Register a canonical live-lexer comment suffix probe. Cursor changes
1333    /// made while probing are rolled back; only the returned token's non-empty
1334    /// source prefix is consumed as the suffix.
1335    pub fn imperative_comment_suffix_ref(
1336        &mut self,
1337        name: impl Into<String>,
1338        matcher: impl for<'source> Fn(&mut Lexer<'source>) -> Option<Token> + Send + Sync + 'static,
1339    ) -> &mut Self {
1340        self.comment_suffix_refs
1341            .insert(name.into(), CommentSuffixMatcher::new_imperative(matcher));
1342        self
1343    }
1344
1345    pub fn parse_budget(
1346        &mut self,
1347        check_every_n: usize,
1348        check: impl Fn(&Context) -> bool + Send + Sync + 'static,
1349    ) -> &mut Self {
1350        self.options.parse.budget.check_every_n = check_every_n;
1351        self.options.parse.budget.on_check = Some(Arc::new(check));
1352        self
1353    }
1354
1355    /// Register a typed function reference for serialized
1356    /// `options.parse.budget.onCheck`.
1357    pub fn parse_budget_ref(
1358        &mut self,
1359        name: impl Into<String>,
1360        check: impl Fn(&Context) -> bool + Send + Sync + 'static,
1361    ) -> &mut Self {
1362        self.budget_check_refs.insert(name.into(), Arc::new(check));
1363        self
1364    }
1365
1366    /// Install a named check that the parse loop runs at every step, ahead
1367    /// of the budget. A check that returns `false` stops the parse with the
1368    /// `cancel` code, as the budget does.
1369    ///
1370    /// A guard is the budget's counterpart for a grammar rather than for
1371    /// its caller. The budget is one slot: [`Tabnas::parse_budget`]
1372    /// replaces it in place, so a check a grammar kept there went whenever
1373    /// a caller set a budget of its own after installing the grammar. The
1374    /// grammars use that check to bound nesting, because a `Value` drops
1375    /// and displays by recursion, one frame per level, and a stack
1376    /// overflow ends the process. Guards are kept apart from the budget
1377    /// and from the options, so neither a budget nor a grammar document
1378    /// applied later removes one; a derived instance carries them, and a
1379    /// merge keeps both sides'. Only [`Tabnas::remove_parse_guard`] does.
1380    ///
1381    /// The name is the guard's identity: installing a second guard under
1382    /// a name already in use replaces the first. A grammar layered on
1383    /// another uses that to change the check its base installed, as JSONC
1384    /// raises the depth jsonic allows. Guards run in the order their names
1385    /// were first installed, before the budget, from the second step on,
1386    /// the steps the budget can run on. Each runs at every step, so it
1387    /// has to be cheap. A guard that panics fails the parse with an
1388    /// error, as a panicking budget does.
1389    ///
1390    /// Rust only: the TypeScript and Go engines have no guards, as their
1391    /// grammars have no depth limits to keep.
1392    pub fn parse_guard(
1393        &mut self,
1394        name: impl Into<String>,
1395        check: impl Fn(&Context) -> bool + Send + Sync + 'static,
1396    ) -> &mut Self {
1397        self.parse_guards.insert(name.into(), Arc::new(check));
1398        self
1399    }
1400
1401    /// Remove the named guard, if one is installed. See
1402    /// [`Tabnas::parse_guard`].
1403    pub fn remove_parse_guard(&mut self, name: &str) -> &mut Self {
1404        self.parse_guards.shift_remove(name);
1405        self
1406    }
1407
1408    pub fn parse_prepare(
1409        &mut self,
1410        prepare: impl Fn(&mut Context) + Send + Sync + 'static,
1411    ) -> &mut Self {
1412        self.options
1413            .parse
1414            .prepare
1415            .push(ParsePrepare::Context(Arc::new(prepare)));
1416        self
1417    }
1418
1419    /// Add a pre-parse hook with access to the owning parser and the exact
1420    /// caller metadata supplied to this parse.
1421    pub fn parse_prepare_with_instance(
1422        &mut self,
1423        prepare: impl Fn(&Tabnas, &mut Context, &Value) + Send + Sync + 'static,
1424    ) -> &mut Self {
1425        self.options
1426            .parse
1427            .prepare
1428            .push(ParsePrepare::WithInstance(Arc::new(prepare)));
1429        self
1430    }
1431
1432    /// Register a typed function reference for one named serialized
1433    /// `options.parse.prepare` callback.
1434    pub fn parse_prepare_ref(
1435        &mut self,
1436        name: impl Into<String>,
1437        prepare: impl Fn(&mut Context) + Send + Sync + 'static,
1438    ) -> &mut Self {
1439        self.parse_prepare_refs
1440            .insert(name.into(), ParsePrepare::Context(Arc::new(prepare)));
1441        self
1442    }
1443
1444    /// Register the complete canonical pre-parse callback shape for a
1445    /// serialized `options.parse.prepare` function reference.
1446    pub fn parse_prepare_with_instance_ref(
1447        &mut self,
1448        name: impl Into<String>,
1449        prepare: impl Fn(&Tabnas, &mut Context, &Value) + Send + Sync + 'static,
1450    ) -> &mut Self {
1451        self.parse_prepare_refs
1452            .insert(name.into(), ParsePrepare::WithInstance(Arc::new(prepare)));
1453        self
1454    }
1455
1456    pub fn parse(&self, src: &str) -> Result<Value, TabnasError> {
1457        self.parser().parse_for(self, src, Value::Undefined)
1458    }
1459
1460    pub fn parse_with_meta(&self, src: &str, meta: Value) -> Result<Value, TabnasError> {
1461        self.parser().parse_for(self, src, meta)
1462    }
1463
1464    pub fn parse_with_context(
1465        &self,
1466        src: &str,
1467        meta: Value,
1468        parent: &ContextSeed,
1469    ) -> Result<Value, TabnasError> {
1470        self.parser()
1471            .parse_for_with_context(self, src, meta, parent)
1472    }
1473
1474    pub fn continuations(&self, src: &str) -> Continuations {
1475        self.parser().continuations_for(self, src)
1476    }
1477
1478    pub fn parse_recover(&self, src: &str) -> ParseRecovery {
1479        self.parser().parse_recover_for(self, src, Value::Undefined)
1480    }
1481
1482    pub fn parse_recover_with_meta(&self, src: &str, meta: Value) -> ParseRecovery {
1483        self.parser().parse_recover_for(self, src, meta)
1484    }
1485
1486    pub fn parse_recover_with_context(
1487        &self,
1488        src: &str,
1489        meta: Value,
1490        parent: &ContextSeed,
1491    ) -> ParseRecovery {
1492        self.parser()
1493            .parse_recover_for_with_context(self, src, meta, parent)
1494    }
1495
1496    /// The sum of every counter the assembled parser is built from.
1497    /// Each only increases, so any change moves the total.
1498    fn grammar_generation(&self) -> u64 {
1499        self.options
1500            .generation()
1501            .wrapping_add(self.rules.generation())
1502            .wrapping_add(self.actions.generation())
1503            .wrapping_add(self.context_actions.generation())
1504            .wrapping_add(self.matched_actions.generation())
1505            .wrapping_add(self.state_actions.generation())
1506            .wrapping_add(self.token_subscribers.generation())
1507            .wrapping_add(self.lex_subscribers.generation())
1508            .wrapping_add(self.rule_subscribers.generation())
1509            .wrapping_add(self.rule_done_subscribers.generation())
1510            .wrapping_add(self.parse_guards.generation())
1511            .wrapping_add(self.plugins.generation())
1512    }
1513
1514    fn parser(&self) -> Arc<Parser> {
1515        self.prepared_parser
1516            .get(self.grammar_generation(), || self.build_parser())
1517    }
1518
1519    fn build_parser(&self) -> Parser {
1520        let mut p = Parser::from_shared(self.prepared_options.get(&self.options));
1521        p.set_instance_info(InstanceInfo {
1522            id: self.id.clone(),
1523            parent_id: self.parent_id.clone(),
1524            tag: self.options.tag.clone(),
1525            plugins: self
1526                .plugins
1527                .iter()
1528                .map(|plugin| plugin.name.clone())
1529                .collect(),
1530            rule_names: self.rule_names(),
1531        });
1532        for spec in self.rules.values() {
1533            p.add_rule(spec.clone());
1534        }
1535        for (name, action) in self.actions.iter() {
1536            p.add_action(name.clone(), action.clone());
1537        }
1538        for (name, action) in self.context_actions.iter() {
1539            p.add_context_action(name.clone(), action.clone());
1540        }
1541        for (name, action) in self.matched_actions.iter() {
1542            p.add_matched_action(name.clone(), action.clone());
1543        }
1544        for (name, action) in self.state_actions.iter() {
1545            p.add_state_action(name.clone(), action.clone());
1546        }
1547        for subscriber in self.token_subscribers.iter() {
1548            p.add_token_subscriber(subscriber.clone());
1549        }
1550        for subscriber in self.lex_subscribers.iter() {
1551            p.add_lex_subscriber(subscriber.clone());
1552        }
1553        for subscriber in self.rule_subscribers.iter() {
1554            p.add_rule_subscriber(subscriber.clone());
1555        }
1556        for subscriber in self.rule_done_subscribers.iter() {
1557            p.add_rule_done_subscriber(subscriber.clone());
1558        }
1559        for guard in self.parse_guards.values() {
1560            p.add_parse_guard(guard.clone());
1561        }
1562        p
1563    }
1564
1565    /// Strict JSON parser setup: the rule set of `ts/test/json-plugin.ts`
1566    /// and `go/jsonplugin_test.go`, over stricter options. Those fixtures
1567    /// leave `escapeStrict` off, keep the `'` and `` ` `` escapes and
1568    /// exclude only `00`-prefixed numbers, so they accept `\x41`,
1569    /// `\u{41}`, `01`, `+1`, `.5` and `1.`; this preset rejects all of
1570    /// them, as `JSON.parse` does. The shared fixtures pin neither way,
1571    /// and `ci/rust/json-fuzz.js` compares the two over generated input
1572    /// that stays inside strict JSON.
1573    pub fn make_json() -> Self {
1574        let mut opts = Options::default();
1575        opts.text.lex = false;
1576        opts.comment.lex = false;
1577        opts.map.extend = false;
1578        opts.lex.empty = false;
1579        opts.rule.finish = false;
1580        opts.rule.include = "json".to_string();
1581
1582        opts.number.hex = false;
1583        opts.number.oct = false;
1584        opts.number.bin = false;
1585        opts.number.sep = None;
1586        // The core number matcher is intentionally lenient; reject leading
1587        // plus/dot forms, leading zeroes, and a trailing decimal point for
1588        // the strict-JSON compatibility grammar.
1589        opts.number.exclude = Some(r"^(?:\+|[+-]?\.|-?0\d)|\.$".to_string());
1590
1591        opts.string.chars = "\"".to_string();
1592        opts.string.multi_chars = "".to_string();
1593        opts.string.allow_unknown = false;
1594        opts.string.escape_strict = true;
1595        for escape in ['v', '\'', '`'] {
1596            opts.string.escape.remove(&escape);
1597        }
1598
1599        let mut tn = Tabnas::with_options(opts);
1600
1601        // 1. Rule: val
1602        let mut val = RuleSpec::new("val");
1603        val.bo.push("@val-bo".to_string());
1604        val.bc.push("@val-bc".to_string());
1605
1606        // val.open
1607        val.open.push(AltSpec {
1608            s: vec![vec![TIN_OB]],
1609            p: Some("map".to_string()),
1610            b: 1,
1611            g: "map,json".to_string(),
1612            ..Default::default()
1613        });
1614        val.open.push(AltSpec {
1615            s: vec![vec![TIN_OS]],
1616            p: Some("list".to_string()),
1617            b: 1,
1618            g: "list,json".to_string(),
1619            ..Default::default()
1620        });
1621        val.open.push(AltSpec {
1622            s: vec![vec![TIN_TX, TIN_NR, TIN_ST, TIN_VL]],
1623            g: "val,json".to_string(),
1624            ..Default::default()
1625        });
1626
1627        // val.close
1628        val.close.push(AltSpec {
1629            s: vec![vec![TIN_ZZ]],
1630            g: "end,json".to_string(),
1631            ..Default::default()
1632        });
1633        val.close.push(AltSpec {
1634            s: vec![],
1635            b: 1,
1636            g: "more,json".to_string(),
1637            ..Default::default()
1638        });
1639        tn.rule(val);
1640
1641        // 2. Rule: map
1642        let mut map = RuleSpec::new("map");
1643        map.bo.push("@map-bo".to_string());
1644        let mut n_pk = HashMap::new();
1645        n_pk.insert("pk".to_string(), 0);
1646
1647        map.open.push(AltSpec {
1648            s: vec![vec![TIN_OB], vec![TIN_CB]],
1649            b: 1,
1650            n: n_pk.clone(),
1651            g: "map,json".to_string(),
1652            ..Default::default()
1653        });
1654        map.open.push(AltSpec {
1655            s: vec![vec![TIN_OB]],
1656            p: Some("pair".to_string()),
1657            n: n_pk,
1658            g: "map,json,pair".to_string(),
1659            ..Default::default()
1660        });
1661
1662        map.close.push(AltSpec {
1663            s: vec![vec![TIN_CB]],
1664            g: "end,json".to_string(),
1665            ..Default::default()
1666        });
1667        tn.rule(map);
1668
1669        // 3. Rule: list
1670        let mut list = RuleSpec::new("list");
1671        list.bo.push("@list-bo".to_string());
1672
1673        list.open.push(AltSpec {
1674            s: vec![vec![TIN_OS], vec![TIN_CS]],
1675            b: 1,
1676            g: "list,json".to_string(),
1677            ..Default::default()
1678        });
1679        list.open.push(AltSpec {
1680            s: vec![vec![TIN_OS]],
1681            p: Some("elem".to_string()),
1682            g: "list,elem,json".to_string(),
1683            ..Default::default()
1684        });
1685
1686        list.close.push(AltSpec {
1687            s: vec![vec![TIN_CS]],
1688            g: "end,json".to_string(),
1689            ..Default::default()
1690        });
1691        tn.rule(list);
1692
1693        // 4. Rule: pair
1694        let mut pair = RuleSpec::new("pair");
1695        pair.bc.push("@pair-bc".to_string());
1696
1697        let mut u_pair = HashMap::new();
1698        u_pair.insert("pair".to_string(), Value::Bool(true));
1699
1700        pair.open.push(AltSpec {
1701            s: vec![vec![TIN_ST], vec![TIN_CL]],
1702            p: Some("val".to_string()),
1703            u: u_pair,
1704            a: vec!["@pairkey".to_string()],
1705            g: "map,pair,key,json".to_string(),
1706            ..Default::default()
1707        });
1708
1709        pair.close.push(AltSpec {
1710            s: vec![vec![TIN_CA]],
1711            r: Some("pair".to_string()),
1712            g: "map,pair,json".to_string(),
1713            ..Default::default()
1714        });
1715        pair.close.push(AltSpec {
1716            s: vec![vec![TIN_CB]],
1717            b: 1,
1718            g: "map,pair,json".to_string(),
1719            ..Default::default()
1720        });
1721        tn.rule(pair);
1722
1723        // 5. Rule: elem
1724        let mut elem = RuleSpec::new("elem");
1725        elem.bc.push("@elem-bc".to_string());
1726
1727        elem.open.push(AltSpec {
1728            s: vec![],
1729            p: Some("val".to_string()),
1730            g: "list,elem,val,json".to_string(),
1731            ..Default::default()
1732        });
1733
1734        elem.close.push(AltSpec {
1735            s: vec![vec![TIN_CA]],
1736            r: Some("elem".to_string()),
1737            g: "list,elem,json".to_string(),
1738            ..Default::default()
1739        });
1740        elem.close.push(AltSpec {
1741            s: vec![vec![TIN_CS]],
1742            b: 1,
1743            g: "list,elem,json".to_string(),
1744            ..Default::default()
1745        });
1746        tn.rule(elem);
1747
1748        tn
1749    }
1750}
1751
1752impl fmt::Display for Tabnas {
1753    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1754        formatter.write_str(&self.id)
1755    }
1756}
1757
1758fn panic_message(payload: Box<dyn std::any::Any + Send>) -> String {
1759    if let Some(message) = payload.downcast_ref::<&str>() {
1760        (*message).to_string()
1761    } else if let Some(message) = payload.downcast_ref::<String>() {
1762        message.clone()
1763    } else {
1764        "non-string panic payload".into()
1765    }
1766}
1767
1768pub(crate) fn merge_plugin_values(base: Value, overlay: Value) -> Value {
1769    const DANGEROUS: [&str; 3] = ["__proto__", "constructor", "prototype"];
1770    match (base, overlay) {
1771        (base, Value::Undefined) => base,
1772        (Value::Object(base), Value::Object(overlay)) => {
1773            let mut base = crate::value::unwrap_arc(base);
1774            for (key, value) in crate::value::unwrap_arc(overlay) {
1775                if DANGEROUS.contains(&key.as_str()) {
1776                    continue;
1777                }
1778                let previous = base.shift_remove(&key).unwrap_or(Value::Undefined);
1779                base.insert(key, merge_plugin_values(previous, value));
1780            }
1781            Value::object(base)
1782        }
1783        (Value::Array(base), Value::Array(overlay)) => {
1784            let length = base.len().max(overlay.len());
1785            let mut base = crate::value::unwrap_arc(base).into_iter();
1786            let mut overlay = crate::value::unwrap_arc(overlay).into_iter();
1787            Value::array(
1788                (0..length)
1789                    .map(|_| match (base.next(), overlay.next()) {
1790                        (Some(base), Some(overlay)) => merge_plugin_values(base, overlay),
1791                        (Some(base), None) => base,
1792                        (None, Some(overlay)) => overlay,
1793                        (None, None) => unreachable!("length comes from both iterators"),
1794                    })
1795                    .collect(),
1796            )
1797        }
1798        (_, overlay) => overlay,
1799    }
1800}
1801
1802/// One shared `Options` per configuration, rebuilt when the
1803/// configuration changes and shared by every parse until it does.
1804///
1805/// A `Mutex` rather than a `RefCell` because `Tabnas` is `Send + Sync`
1806/// and should stay that way, and `Arc` rather than `Rc` for the same
1807/// reason. Both are per `parse()` call, not per token, which is why
1808/// the atomics do not show up.
1809#[derive(Default)]
1810struct PreparedOptions(std::sync::Mutex<Option<(u64, Arc<Options>)>>);
1811
1812impl PreparedOptions {
1813    fn get(&self, options: &crate::tracked::Tracked<Options>) -> Arc<Options> {
1814        let generation = options.generation();
1815        let mut slot = self.0.lock().expect("prepared options lock");
1816        if let Some((prepared_at, ref prepared)) = *slot {
1817            if prepared_at == generation {
1818                return Arc::clone(prepared);
1819            }
1820        }
1821        let mut prepared_value = options.peek().clone();
1822        prepared_value.sort_for_lexing();
1823        let prepared = Arc::new(prepared_value);
1824        *slot = Some((generation, Arc::clone(&prepared)));
1825        prepared
1826    }
1827}
1828
1829/// A cloned instance prepares its own; it does not inherit the
1830/// original's, which may already be stale for it.
1831impl Clone for PreparedOptions {
1832    fn clone(&self) -> Self {
1833        PreparedOptions::default()
1834    }
1835}
1836
1837/// The assembled `Parser` for a configuration, prepared once and shared
1838/// by every parse until the configuration changes.
1839///
1840/// This is only possible because a `Parser` is `Send + Sync`: it holds
1841/// `Arc<Options>` and `Arc<RuleSpec>`, and its actions are already
1842/// `Arc<dyn Fn .. + Send + Sync>`. An earlier attempt at this was
1843/// abandoned when the parser still held `Rc`s, because caching one
1844/// would have cost `Tabnas` its own `Sync` without saying so.
1845///
1846/// The key is the sum of the generations of every `Tracked` field the
1847/// parser is built from. Summing is enough because each counter only
1848/// ever increases, so any change moves the total.
1849#[derive(Default)]
1850struct PreparedParser(std::sync::Mutex<Option<(u64, Arc<Parser>)>>);
1851
1852impl PreparedParser {
1853    fn get(&self, generation: u64, build: impl FnOnce() -> Parser) -> Arc<Parser> {
1854        let mut slot = self.0.lock().expect("prepared parser lock");
1855        if let Some((prepared_at, ref prepared)) = *slot {
1856            if prepared_at == generation {
1857                return Arc::clone(prepared);
1858            }
1859        }
1860        let prepared = Arc::new(build());
1861        *slot = Some((generation, Arc::clone(&prepared)));
1862        prepared
1863    }
1864}
1865
1866impl Clone for PreparedParser {
1867    fn clone(&self) -> Self {
1868        PreparedParser::default()
1869    }
1870}