Rust语法解析与词法分析库lrpar的使用,lrpar实现高效LR(1)解析器生成和自定义语法规则处理
Rust语法解析与词法分析库lrpar的使用,lrpar实现高效LR(1)解析器生成和自定义语法规则处理
lrpar
提供了一个Yacc兼容的解析器(可以在编译时或运行时生成语法)。它可以接收传统的.y
文件并将它们转换为符合Rust习惯的解析器。
示例
假设我们要为一个简单的计算器语言静态生成一个解析器(并假设我们可以使用lrlex
作为词法分析器)。我们需要在项目中添加一个build.rs
文件来静态编译词法分析器和解析器。
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();
}
calc.l词法分析器文件
%%
[0-9]+ "INT"
\+ "+"
\* "*"
\( "("
\) ")"
[\t ]+ ;
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()))
}
;
%%
// 这里定义的函数对所有语法动作都可见
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(())
}
}
}
src/main.rs主文件
use std::io::{self, BufRead, Write};
use lrlex::lrlex_mod;
use lrpar::lrpar_mod;
// 使用`lrlex_mod!`将`calc.l`的词法分析器引入作用域
lrlex_mod!("calc.l");
// 使用`lrpar_mod!`将`calc.y`的解析器引入作用域
lrpar_mod!("calc.y");
fn main() {
// 获取`calc`语言的`LexerDef`
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;
}
// 创建一个词法分析器来对输入进行词法分析
let lexer = lexerdef.lexer(l);
// 将词法分析器传递给解析器,对输入进行词法分析和解析
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
1 回复
Rust语法解析与词法分析库lrpar的使用指南
完整示例
下面是一个完整的计算器实现示例,包含语法定义、词法分析和解析执行:
1. 项目结构
calculator/
├── Cargo.toml
├── build.rs
├── src/
│ ├── main.rs
│ ├── calc.lex
│ └── calc.y
2. Cargo.toml
[package]
name = "calculator"
version = "0.1.0"
edition = "2021"
[dependencies]
lrpar = "0.11"
lrlex = "0.11"
cfgrammar = "0.11"
3. build.rs
use cfgrammar::yacc::YaccKind;
use lrlex::LexerBuilder;
use lrpar::CTParserBuilder;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let lex_rule = LexerBuilder::new()
.rule_files(vec!["src/calc.lex"])
.build()?;
CTParserBuilder::new()
.yacckind(YaccKind::Grmtools)
.grammar_files(vec!["src/calc.y"])
.lexer(lex_rule)
.build()?;
Ok(())
}
4. src/calc.lex
%%
[0-9]+ "INT"
\+ "+"
\* "*"
\( "("
\) ")"
[\t\n ]+ ;
5. src/calc.y
%start Expr
%%
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()))
}
;
%%
// 辅助函数:将字符串转换为整数
fn parse_int(s: &str) -> Result<u64, ()> {
match s.parse::<u64>() {
Ok(val) => Ok(val),
Err(_) => Err(())
}
}
6. src/main.rs
use lrlex::lrlex_mod;
use lrpar::lrpar_mod;
use std::io::{self, Write};
// 生成词法分析和语法分析模块
lrlex_mod!("calc.lex");
lrpar_mod!("calc.y");
fn main() {
println!("简单计算器 (输入q退出)");
loop {
print!(">>> ");
io::stdout().flush().unwrap();
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
if input.trim() == "q" {
break;
}
let lexerdef = calc_lex::lexerdef();
let lexer = lexerdef.lexer(&input);
let (res, errs) = calc_y::parse(&lexer);
// 输出错误信息
for e in errs {
println!("错误: {:?}", e);
}
// 输出结果
match res {
Some(Ok(r)) => println!("结果: {}", r),
_ => println!("无法计算表达式")
}
}
}
7. 运行示例
编译并运行项目:
cargo run
示例交互:
简单计算器 (输入q退出)
>>> 3 + 5 * 2
结果: 13
>>> (4 + 5) * 2
结果: 18
>>> q
示例说明
- 这个完整示例实现了一个支持加减乘除和括号的计算器
- 词法分析器(lex)识别数字和运算符
- 语法分析器(y)定义了运算优先级和结合性
- 主程序提供了交互式REPL环境
- 包含错误处理功能,能报告解析错误
这个示例展示了lrpar和lrlex的基本用法,你可以根据需要扩展语法规则来实现更复杂的功能。