36个版本
0.13.7 | 2024年6月14日 |
---|---|
0.13.4 | 2024年1月4日 |
0.13.3 | 2023年9月21日 |
0.13.1 | 2023年1月27日 |
0.1.1 | 2018年12月18日 |
#15 in 解析工具
7,475 每月下载量
用于 7 crates
475KB
10K SLoC
lrpar
lrpar
提供了一个兼容Yacc的解析器(其中语法可以在编译时或运行时生成)。它可以接受传统的 .y
文件并将它们转换为惯用的Rust解析器。
如果你是 lrpar
的新手,请阅读“快速入门指南”。 “grmtools书”和API参考有更详细的信息。您可以在以下位置找到您使用的lrpar版本的适当文档
最新版本 | master |
---|---|
快速入门指南 | 快速入门指南 |
grmtools书 | grmtools书 |
lrpar API | lrpar API |
示例
假设我们想为简单的计算器语言静态生成一个解析器(假设我们能够使用 lrlex
进行词法分析)。我们需要在我们的项目中添加一个 build.rs
文件,该文件静态编译词法分析和解析器。虽然我们可以单独执行这两个步骤,但使用 lrlex
最容易,因为它一次为我们完成两项工作。因此,我们的 build.rs
文件如下
use cfgrammar::yacc::YaccKind;
use lrlex::CTLexerBuilder;
fn main() {
CTLexerBuilder::new()
.lrpar_config(|ctp| {
ctp.yacckind(YaccKind::Grmtools)
.grammar_in_src_dir("calc.y")
.unwrap()
})
.lexer_in_src_dir("calc.l")
.unwrap()
.build()
.unwrap();
}
其中 src/calc.l
如下
%%
[0-9]+ "INT"
\+ "+"
\* "*"
\( "("
\) ")"
[\t ]+ ;
和 src/calc.y
如下
%start Expr
%avoid_insert "INT"
%%
Expr -> Result<u64, ()>:
Expr '+' Term { Ok($1? + $3?) }
| Term { $1 }
;
Term -> Result<u64, ()>:
Term '*' Factor { Ok($1? * $3?) }
| Factor { $1 }
;
Factor -> Result<u64, ()>:
'(' Expr ')' { $2 }
| 'INT'
{
let v = $1.map_err(|_| ())?;
parse_int($lexer.span_str(v.span()))
}
;
%%
// Any functions here are in scope for all the grammar actions above.
fn parse_int(s: &str) -> Result<u64, ()> {
match s.parse::<u64>() {
Ok(val) => Ok(val),
Err(_) => {
eprintln!("{} cannot be represented as a u64", s);
Err(())
}
}
}
因为我们指定了我们的Yacc文件是 Grmtools
格式,所以每个规则都有一个独立的Rust类型,所有函数都符合这个类型(在这种情况下,所有规则都有相同的类型,但这不是必需的)。
一个简单的 src/main.rs
如下
use std::io::{self, BufRead, Write};
use lrlex::lrlex_mod;
use lrpar::lrpar_mod;
// Using `lrlex_mod!` brings the lexer for `calc.l` into scope.
lrlex_mod!("calc.l");
// Using `lrpar_mod!` brings the parser for `calc.y` into scope.
lrpar_mod!("calc.y");
fn main() {
// Get the `LexerDef` for the `calc` language.
let lexerdef = calc_l::lexerdef();
let stdin = io::stdin();
loop {
print!(">>> ");
io::stdout().flush().ok();
match stdin.lock().lines().next() {
Some(Ok(ref l)) => {
if l.trim().is_empty() {
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) = calc_y::parse(&lexer);
for e in errs {
println!("{}", e.pp(&lexer, &calc_y::token_epp));
}
match res {
Some(Ok(r)) => println!("Result: {}", r),
_ => eprintln!("Unable to evaluate expression.")
}
}
_ => break
}
}
}
现在我们可以 cargo run
项目并评估简单表达式
>>> 2 + 3
Result: 5
>>> 2 + 3 * 4
Result: 14
>>> (2 + 3) * 4
Result: 20
lrpar
还内置了高级的 错误恢复
>>> 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
依赖
~4–13MB
~142K SLoC