lrpar
lrpar
provides a Yacc-compatible parser (where grammars can be generated at
compile-time or run-time). It can take in traditional .y
files and convert
them into an idiomatic Rust parser. More details can be found in the grmtools
book; the
quickstart guide
is a good place to start.
Let's assume we want to statically generate a parser for a simple calculator
language (and let's also assume we are able to use
lrlex
for the
lexer). We need to add a build.rs
file to our project which tells lrpar
to
statically compile the lexer and parser files:
```rust use cfgrammar::yacc::YaccKind; use lrlex::LexerBuilder; use lrpar::CTParserBuilder;
fn main() -> Result<(), Box
where src/calc.l
is as follows:
%%
[0-9]+ "INT"
\+ "+"
\* "*"
\( "("
\) ")"
[\t ]+ ;
and src/calc.y
is as follows:
```
%start Expr
%avoid_insert "INT"
%%
Expr -> Result Term -> Result Factor -> Result fn parseint(s: &str) -> Result Because we specified that our Yacc file is in A simple ```rust use std::io::{self, BufRead, Write}; use lrlex::lrlexmod;
use lrpar::lrparmod; // Using fn main() {
// Get the We can now ``` 2 + 3
Result: 5
2 + 3 * 4
Result: 14
(2 + 3) * 4
Result: 20
``` ``` 2 + + 3
Parsing error at line 1 column 5. Repair sequences found:
1: Delete +
2: Insert INT
Result: 5
2 + 3 3
Parsing error at line 1 column 7. Repair sequences found:
1: Insert *
2: Insert +
3: Delete 3
Result: 11
2 + 3 4 5
Parsing error at line 1 column 7. Repair sequences found:
1: Insert *, Delete 4
2: Insert +, Delete 4
3: Delete 4, Delete 5
4: Insert +, Shift 4, Delete 5
5: Insert +, Shift 4, Insert +
6: Insert *, Shift 4, Delete 5
7: Insert *, Shift 4, Insert *
8: Insert *, Shift 4, Insert +
9: Insert +, Shift 4, Insert *
Result: 17
```Grmtools
format, each rule has a
separate Rust type to which all its functions conform (in this case, all the
rules have the same type, but that's not a requirement).src/main.rs
is as follows:lrlex_mod!
brings the lexer for calc.l
into scope.
lrlexmod!("calc.l");
// Using lrpar_mod!
brings the parser for calc.y
into scope.
lrparmod!("calc.y");LexerDef
for the calc
language.
let lexerdef = calcl::lexerdef();
let stdin = io::stdin();
loop {
print!(">>> ");
io::stdout().flush().ok();
match stdin.lock().lines().next() {
Some(Ok(ref l)) => {
if l.trim().isempty() {
continue;
}
// Now we create a lexer with the lexer
method with which
// we can lex an input.
let lexer = lexerdef.lexer(l);
// Pass the lexer to the parser and lex and parse the input.
let (res, errs) = calcy::parse(&lexer);
for e in errs {
println!("{}", e.pp(&lexer, &calcy::token_epp));
}
match res {
Some(Ok(r)) => println!("Result: {}", r),
_ => eprintln!("Unable to evaluate expression.")
}
}
_ => break
}
}
}
```cargo run
our project and evaluate simple expressions:
lrpar
also comes with advanced error
recovery built-in: