RustyLR
LR(1) and LALR(1) parser code generator for Rust
[dependencies]
rusty_lr = "0.4.0"
rusty_lr_derive = "0.4.0"
Features
- pure Rust implementation
- compile-time DFA construction from CFG ( with yacc-like syntax )
- customizable reducing action
- resolving conflicts of ambiguous grammar
- tracing parser action with callback, also error handling
- construct Tree from parsing result
Sample
In example/calculator/parser.rs,
// for LR(1) parser, with &str
use lr1_str;
// for LALR(1) parser, with &str
use lalr1_str;
// this define struct `EParser`
// where 'E' is the start symbol
lalr1_str!
In example/calculator/src/main.rs,
The result will be:
"3 "+" 4"="3 + 4"
" 1 "+" 2*(3 + 4) "=" 1 + 2*(3 + 4) "
Result: 15
Number of 'Num' in 1 + 2*(3 + 4) : 4
Build Deterministic Finite Automata (DFA) from Context Free Grammar (CFG)
This section will describe how to build DFA from CFG, on runtime.
1. Define terminal and non-terminal symbols
// must implement these traits
// must implement these traits
/// impl Display for TermType, NonTermType will make related ProductionRule, error message Display-able
Or simply, you can use char or u8 as terminal, and &'static str as non-terminal.
Any type that implements traits above can be used as terminal and non-terminal symbols.
2. Define production rules
Consider the following context free grammar:
A -> A + A (reduce left)
A -> M
This grammar can be written as:
/// type alias
type Token = Token;
/// create grammar
let mut grammar = new;
grammar.add_rule;
grammar.add_rule;
Note that the production rule A -> A + A has a shift/reduce conflict, and the reduce type is set to ReduceType::Left to resolve the conflict. Letting the reduce type to ReduceType::Error will cause an error when a conflict occurs. This is useful when you want to know unexpected conflicts exist in your grammar.
reduce/reduce conflict (e.g. duplicated rules) will be always an error.
3. Build DFA
Calling grammar.build() will build the DFA from the CFG.
let parser:Parser = match grammar.build ;
You must explicitly specify the Augmented non-terminal symbol, and the production rule Augmented -> StartSymbol $ must be defined in the grammar.
The Error type returned from Grammar::build() will contain the error information.
Grammar is Display if both Term and NonTerm is Display, and It is Debug if both Term and NonTerm is Debug.
The returned Parser struct contains the DFA and the production rules(cloned). It is completely independent from the Grammar struct, so you can drop the Grammar struct, or export the Parser struct to another module.
Parse input sequence with generated DFA
1. Parse without callback
For given input sequence, you can parse it with Parser::parse() method. The input sequence must be slice of Term(&[Term]).
If Term is char, you can use &str as input sequence (via Parser::parse_str()).
let terms = vec!;
match parser.parse
Note that the given input sequence must not ends with EOF(the end symbol you provided in Augmented rule). And the final Augmented rule will not be reduced (EOF will be only used for lookahead/reduce check, not shifted).
2. Parse with callback
For complex error handling and tracing parser action, you can implement Callback trait and pass it to Parser::parse_with_callback(...).
let mut callback = ParserCallback;
match parser.parse_with_callback
The output will be:
Reduce by P -> Num
Reduce by M -> P
Reduce by A -> M
Reduce by P -> Num
Reduce by M -> P
Reduce by P -> Num
Reduce by M -> P
Reduce by A -> M
Reduce by P -> Num
Reduce by M -> P
Reduce by A -> M
Reduce by A -> A + A (Left)
Reduce by E -> A
Reduce by P -> ( E )
Reduce by M -> P
Reduce by M -> M * M (Left)
Reduce by A -> M
Reduce by A -> A + A (Left)
Reduce by E -> A
Parse success [Num, Plus, Num, Mul, LeftParen, Num, Plus, Num, RightParen]
3. Tree struct as parsing result
Tree struct is constructed from parsing result. You can access the reduced rule as tree structure.
// get slice of input sequence that this tree holds
let slc = tree.slice;
match tree