RustyLR
LR(1) Parser generator in Rust
[dependencies]
rusty_lr = "0.1.0"
Features
- pure Rust implementation
- DFA construction from CFG
- resolving conflicts of ambiguous grammar
- tracing parser action with callback, also error handling
- construct Tree from parsing result
Build Deterministic Finite Automa (DFA) from Context Free Grammar (CFG)
Sample code in: example/calculator/src/main.rs
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 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.
It 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]).
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.
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