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