Skip to main content

ParseTable

Struct ParseTable 

pub struct ParseTable {
    pub token_actions: TiVec<StateId, TiVec<TokenId, Action>>,
    pub goto_actions: TiVec<StateId, TiVec<NontermId, Action>>,
    pub rules: TiVec<RuleId, Rule>,
    pub id_to_token: TiVec<TokenId, String>,
    pub nonterm_names: TiVec<NontermId, String>,
    pub display_names: TiVec<DisplayId, String>,
    pub start_states: Vec<(String, StateId)>,
    pub end_states: Vec<(String, StateId)>,
    pub filtered_terminals: OnceLock<Vec<bool>>,
}
Expand description

The LALR parse table. token_actions is a locked cross-phase contract: per-state rows, dense state ids, carrying both shift and reduce-lookahead terminals — accept-sets and accepts() read through it; a shift-only row would silently shrink them. goto_actions stays per-state addressable too: choices() re-merges both maps. Loader-preserved order is canon — state ids, rule order, __ANON ids pass through exactly as serialized, never renumbered.

Must be compiler/loader-built.

  • The engine (ParserState::feed_token) indexes these rows and pops the value stack by rule arity without re-checking bounds — the hot loop trusts the table’s internal consistency.
  • The fields are pub for the loader/conformance crate, but a hand-built table with a dangling action target, id, or rule arity panics inside the parse.
  • Build via build_lalr/load_grammar, never by populating the fields directly.

Fields§

§token_actions: TiVec<StateId, TiVec<TokenId, Action>>

[state][terminal id] — shift and reduce entries.

§goto_actions: TiVec<StateId, TiVec<NontermId, Action>>

[state][nonterminal id] — always shift-style goto targets.

§rules: TiVec<RuleId, Rule>

Reduce targets, referenced by index from action cells.

§id_to_token: TiVec<TokenId, String>

Terminal names by id — MUST agree with the lexer’s conf-order ids (one id space across lexer and table).

§nonterm_names: TiVec<NontermId, String>§display_names: TiVec<DisplayId, String>

Tree data names indexed by Rule::display_id.

§start_states: Vec<(String, StateId)>

Per declared start symbol.

§end_states: Vec<(String, StateId)>§filtered_terminals: OnceLock<Vec<bool>>

Lazily-computed per-token-id “always filter_out” flag for the reduce-time fold (see Self::filtered_terminals) — grammar-constant, so computed once and shared across every parse rather than rebuilt per fold. Construct empty (OnceLock::new()); it fills on first use.

Implementations§

§

impl ParseTable

pub fn token_id(&self, name: &str) -> Option<TokenId>

pub fn filtered_terminals(&self) -> &[bool]

Per-token-id “always filter_out” flag for the reduce-time fold: true iff the terminal is filter_out in EVERY rule it appears in (none keeping it via keep_all_tokens). Those tokens the tree never materializes, so the fold’s shift skips them (a zero-span marker) rather than on_leaf-ing a value it would only drop. Grammar-constant — computed once, shared.

pub fn rpn_wire_safe(&self) -> bool

Whether the reduce-time RPN wire fast path (hyperlark-wasm’s _parseFoldRpn) folds this grammar byte-identically to the arena+serialize path. That sink emits a post-order stream — a token at shift, a rule record at reduce — and skips filtered_terminals at shift so a rule’s kept-child count is just the values it consumed. Two grammar properties make that exact:

  • no placeholder holes: a [...] optional under maybe_placeholders inserts a None between already-emitted children — an append-only buffer can’t. Checked against the actual built RuleShapes (not the raw flag), so a grammar with no optionals is safe even under maybe_placeholders=true (e.g. calc); and
  • a clean filter: no terminal is filter_out in one rule yet kept in another. An unclean terminal isn’t always-filtered, so it is NOT skipped at shift, and a rule that drops it would miscount its children.

Both hold for typical grammars (json, calc). The general path serves the rest. Cheap and grammar-constant; the caller (a per-instance binding) caches the answer rather than recomputing per parse.

pub fn display_id(&self, name: &str) -> Option<DisplayId>

The data_id whose display name is name — the rule-name peer of Self::token_id (filtered tree cursors take the numeric id). The ambiguity sentinel names (_ambig/_iambig/_inter) occupy no ParseTable::display_names slot and resolve to None; compare Self::data_name, which synthesizes them on the way out.

pub fn is_splice_display(&self, data_id: DisplayId) -> bool

Whether data_id names a _-transparent (splice) display — a rule whose reduce produces no tree node of its own, its children splicing into the parent instead.

Grammar-shape knowledge belongs to core (see RouteSet::build), and bindings need it to REJECT such a display where a user names a rule to hook or stream: there is no node to hand back, and seeding one into a RouteSet collapses routing to all-hooked, which for a streaming binding means emitting every node in the parse instead of none.

pub fn data_name(&self, data_id: DisplayId) -> &str

Tree data name for a data_id, resolving the ambiguity sentinels (AMBIG_DATA_ID/IAMBIG_DATA_ID/INTER_DATA_ID) that carry no ParseTable::display_names slot. Every consumer of Earley ambiguity='explicit' trees MUST route through this rather than index display_names directly (which would panic on a sentinel id).

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.