Skip to content

Refactor Parser Trait to Static Methods and Parameterize Context with Parser Generic#58

Merged
ehwan merged 2 commits into
breaking_changefrom
remove_parser_struct_passing
Jun 19, 2026
Merged

Refactor Parser Trait to Static Methods and Parameterize Context with Parser Generic#58
ehwan merged 2 commits into
breaking_changefrom
remove_parser_struct_passing

Conversation

@ehwan

@ehwan ehwan commented Jun 19, 2026

Copy link
Copy Markdown
Owner

Summary of Changes

This pull request refactors the Parser trait and parser execution contexts (deterministic and nondeterministic) to improve usability, clean up signature parameters, and enhance code simplicity.
Key improvements:

  1. Static Parser Methods: Removed &self and trait object requirements from all Parser trait methods (get_rules, get_states, precedence_types), changing them into static functions returning static slices/options. Bounded trait associated types with 'static.
  2. Context Parameterization: Embedded the P: Parser type directly on the execution Context (and NodeRefIterator for GLR) along with PhantomData<P>. This eliminates the need to pass parser references to context method calls (e.g. .feed(), .accept(), .can_feed(), .expected_token_str(), .trace(), etc.) and avoids turbofish specifications.
  3. Generated Code Cleanup: Unit parser structs (e.g., pub struct GrammarParser;) are now generated instead of struct instances with static reference fields. The unused new() constructor was completely removed.
  4. Examples and Documentation: Updated all calling contexts in calculator, calculator_u8, glr, and json examples, and updated references/examples in documentation (README.md, GLR.md, rusty_lr_executable/README.md) to reflect the new, simpler syntax.

Detailed Modifications

1. rusty_lr_core

  • rusty_lr_core/src/parser/mod.rs:
    • Removed &self / &self references from Parser::get_rules(), Parser::get_states(), and Parser::precedence_types().
    • Added 'static lifetime bounds to Parser associated types (Term, TermClass, NonTerm, State) and DataStack associated types (Term, NonTerm).
  • rusty_lr_core/src/parser/deterministic/context.rs:
    • Redefined Context as Context<P: Parser, Data: DataStack, StateIndex> containing _phantom: PhantomData<P>.
    • Removed parser: &P parameter from all member methods.
  • rusty_lr_core/src/parser/nondeterministic/context.rs:
    • Redefined Context and NodeRefIterator to accept P: Parser as the first generic argument.
    • Implemented manual Clone for NodeRefIterator to bypass P: Clone requirement and updated Context's Clone bounds.
    • Removed parser arguments from all methods.

2. rusty_lr_parser

  • rusty_lr_parser/src/emit.rs:
    • Generated unit structures (pub struct #parser_struct_name;) for the parsers without fields.
    • Implemented static trait methods inside impl Parser for #parser_struct_name utilizing static OnceLock caches.
    • Removed the new() constructor.
    • Updated context type alias definitions to pass the parser struct name as the first generic parameter.
  • rusty_lr_parser/src/parser/lexer.rs:
    • Removed parser argument from feed_recursive and internal calls.
  • rusty_lr_parser/src/grammar.rs:
    • Removed parser instantiation and parameter passing to feed_recursive and accept.
  • rusty_lr_parser/src/parser/parser_expanded.rs:
    • Re-bootstrapped using the new parser generator code to remove the fields/new() methods and utilize static trait implementations.

3. Examples & Documentation

  • Examples:
    • Updated calculator, calculator_u8, glr, and json example binaries to initialize contexts and call .feed() / .accept() without passing the parser reference.
  • Documentation:
    • Updated README.md, GLR.md, and rusty_lr_executable/README.md to show the new simplified usage.
    • Emphasized that users only need to instantiate the context using Context::new() and feed tokens directly.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the rusty_lr API by removing the requirement to pass a parser instance reference to context methods (such as feed, feed_location, accept, can_feed, trace, and backtrace). Instead, the Context struct is now parameterized with the Parser type P, and parser tables/rules are accessed statically. The reviewer raised two critical architectural concerns: first, the new Clone implementation for nondeterministic Context unnecessarily clones transient fields, introducing a breaking Data::ReduceActionError: Clone bound that forces user-defined error types to implement Clone. Second, removing &self from get_rules and get_states forces a 'static lifetime bound on associated types, which prevents zero-copy parsing.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread rusty_lr_core/src/parser/nondeterministic/context.rs
Comment on lines +48 to +63
type Term: 'static;
/// The type of terminal classes.
type TermClass: terminalclass::TerminalClass<Term = Self::Term>;
type TermClass: terminalclass::TerminalClass<Term = Self::Term> + 'static;
/// The type of non-terminal symbols.
type NonTerm: nonterminal::NonTerminal;
type NonTerm: nonterminal::NonTerminal + 'static;
/// The type of the parser state.
type State: State<TermClass = Self::TermClass, NonTerm = Self::NonTerm>;
type State: State<TermClass = Self::TermClass, NonTerm = Self::NonTerm> + 'static;

/// Get list of production rules
fn get_rules(&self) -> &[crate::rule::ProductionRule<Self::TermClass, Self::NonTerm>];
/// Get list of states
fn get_states(&self) -> &[Self::State];
/// Get list of production rules. Returns a static slice since rules are generated at compile time.
fn get_rules() -> &'static [crate::rule::ProductionRule<Self::TermClass, Self::NonTerm>];
/// Get list of states. Returns a static slice since states are generated at compile time.
fn get_states() -> &'static [Self::State];
/// Get the type of precedence for i'th level.
/// `None` if i'th level was defined as %precedence (no reduce type).
fn precedence_types(&self, level: u8) -> Option<crate::rule::ReduceType>;
/// Determined entirely at compile time and is static.
fn precedence_types(level: u8) -> Option<crate::rule::ReduceType>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

By removing &self from get_rules and get_states and returning &'static slices, you have introduced a 'static lifetime bound on the associated types Term, TermClass, NonTerm, and State.

This is a significant architectural limitation because it completely prevents the parser from supporting zero-copy parsing (where tokens or non-terminals borrow from the input string with a lifetime shorter than 'static, e.g., Token<'a>).

If zero-copy parsing is a desired feature or a potential future requirement for this library, you might want to reconsider this refactoring, or parameterize the Parser trait with a lifetime (e.g., Parser<'a>) so that static methods can return slices bound to that lifetime instead of forcing 'static globally.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's okay to have breaking change.
Also, there are no plans to support lifetime-ed Token as a input.

@ehwan ehwan self-assigned this Jun 19, 2026
@ehwan
ehwan merged commit cb89519 into breaking_change Jun 19, 2026
@ehwan
ehwan deleted the remove_parser_struct_passing branch June 19, 2026 09:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant