Rust语法解析库nom-language的使用:高效文本解析与语言处理工具
Rust语法解析库nom-language的使用:高效文本解析与语言处理工具
安装
在项目目录中运行以下Cargo命令:
cargo add nom-language
或者在Cargo.toml中添加以下行:
nom-language = "0.1.0"
基本使用示例
nom-language是基于nom构建的语法解析库,专门用于高效文本解析和语言处理。以下是一个基本示例:
use nom_language::prelude::*;
// 定义一个简单的解析器来匹配"hello"后跟一个标识符
fn hello_parser(input: &str) -> IResult<&str, &str> {
// 组合多个解析器
let (input, _) = tag("hello")(input)?;
let (input, _) = multispace1(input)?;
// 标识符由字母组成
alpha1(input)
}
fn main() {
let input = "hello world";
match hello_parser(input) {
Ok((remaining, matched)) => {
println!("Matched: '{}', Remaining: '{}'", matched, remaining);
}
Err(e) => {
println!("Error: {:?}", e);
}
}
}
完整示例:解析简单算术表达式
下面是一个更完整的示例,展示如何使用nom-language解析简单的算术表达式:
use nom_language::prelude::*;
use nom_language::branch::alt;
use nom_language::character::complete::{char, digit1};
use nom_language::combinator::map;
use nom_language::multi::separated_list0;
use nom_language::sequence::delimited;
#[derive(Debug)]
enum Expr {
Number(i32),
Add(Box<Expr>, Box<Expr>),
Sub(Box<Expr>, Box<Expr>),
Mul(Box<Expr>, Box<Expr>),
Div(Box<Expr>, Box<Expr>),
}
// 解析数字
fn number(input: &str) -> IResult<&str, Expr> {
map(digit1, |s: &str| {
Expr::Number(s.parse().unwrap())
})(input)
}
// 解析括号表达式
fn parens(input: &str) -> IResult<&str, Expr> {
delimited(
char('('),
expr,
char(')')
)(input)
}
// 解析因子(数字或括号表达式)
fn factor(input: &str) -> IResult<&str, Expr> {
alt((
number,
parens,
))(input)
}
// 解析项(乘除法)
fn term(input: &str) -> IResult<&str, Expr> {
let (input, mut left) = factor(input)?;
let (input, result) = separated_list0(
alt((char('*'), char('/'))),
factor,
)(input)?;
for (op, right) in result {
left = match op {
'*' => Expr::Mul(Box::new(left), Box::new(right)),
'/' => Expr::Div(Box::new(left), Box::new(right)),
_ => unreachable!(),
};
}
Ok((input, left))
}
// 解析表达式(加减法)
fn expr(input: &str) -> IResult<&str, Expr> {
let (input, mut left) = term(input)?;
let (input, result) = separated_list0(
alt((char('+'), char('-'))),
term,
)(input)?;
for (op, right) in result {
left = match op {
'+' => Expr::Add(Box::new(left), Box::new(right)),
'-' => Expr::Sub(Box::new(left), Box::new(right)),
_ => unreachable!(),
};
}
Ok((input, left))
}
fn main() {
let input = "3*(2+4)-5";
match expr(input) {
Ok((remaining, expr)) => {
println!("Parsed expression: {:?}", expr);
println!("Remaining input: '{}'", remaining);
}
Err(e) => {
println!("Error: {:?}", e);
}
}
}
高级特性
nom-language还支持更高级的解析功能,如错误处理、自定义错误类型和流式解析。以下是一个错误处理示例:
use nom_language::error::{ErrorKind, ParseError};
use nom_language::Err;
fn custom_parser(input: &str) -> IResult<&str, &str, String> {
if input.is_empty() {
return Err(Err::Error(String::from("Empty input")));
}
if input.starts_with("error") {
return Err(Err::Failure(String::from("Explicit error case")));
}
Ok((&input[1..], &input[0..1]))
}
fn main() {
match custom_parser("test") {
Ok((remaining, matched)) => {
println!("Matched: '{}', Remaining: '{}'", matched, remaining);
}
Err(Err::Error(e)) => {
println!("Recoverable error: {}", e);
}
Err(Err::Failure(e)) => {
println!("Unrecoverable error: {}", e);
}
Err(Err::Incomplete(_)) => {
println!("Need more data");
}
}
}
性能提示
nom-language是为高效解析设计的,以下是一些性能优化建议:
- 尽可能使用
&str
而不是String
作为输入和输出类型 - 避免在解析器中分配内存
- 使用
nom_language::bytes::complete
模块中的解析器处理二进制数据 - 对于复杂语法,考虑使用
nom_language::multi
和nom_language::combinator
模块中的组合器
nom-language是一个强大的文本解析工具,特别适合构建编程语言解析器、配置文件解析器和协议解析器。
1 回复
Rust语法解析库nom-language的使用:高效文本解析与语言处理工具
简介
nom-language是一个基于nom构建的Rust语法解析库,专门用于高效解析编程语言和其他结构化文本。它建立在著名的nom解析器组合库之上,提供了更高级别的抽象,特别适合语言处理任务。
nom-language的主要特点:
- 高性能:基于nom的零拷贝解析策略
- 组合式:使用解析器组合子构建复杂语法
- 模块化:可重用和组合的解析组件
- 错误处理:丰富的错误报告功能
安装
在Cargo.toml中添加依赖:
[dependencies]
nom-language = "0.5"
基本使用方法
1. 定义简单语法
use nom_language::*;
// 定义一个简单的算术表达式语法
let grammar = grammar! {
expr => {
term >>
many0!(alt!(
preceded!(char!('+'), term) |
preceded!(char!('-'), term)
)
};
term => {
factor >>
many0!(alt!(
preceded!(char!('*'), factor) |
preceded!(char!('/'), factor)
))
};
factor => {
alt!(
delimited!(char!('('), expr, char!(')')) |
double
)
};
};
let input = "3.14 * (2 + 5)";
let result = grammar.parse(input);
println!("{:?}", result);
2. 处理自定义语言
use nom_language::{*, types::*};
// 定义一个简单的类C语言语法
let c_like = grammar! {
program => many0!(declaration);
declaration => {
type_specifier >>
identifier >>
opt!(delimited!(char!('('), parameter_list, char!(')'))) >>
block
};
type_specifier => alt!(tag!("int") | tag!("float") | tag!("void"));
parameter_list => separated_list!(char!(','), param_declaration);
param_declaration => {
type_specifier >>
identifier
};
block => delimited!(char!('{'), many0!(statement), char!('}'));
statement => alt!(expression_statement | return_statement);
expression_statement => terminated!(expression, char!(';'));
return_statement => preceded!(tag!("return"), expression_statement);
expression => assignment_expression;
assignment_expression => {
logical_or_expression >>
opt!(preceded!(tag!("="), assignment_expression))
};
// ... 其他表达式规则
};
let input = r#"
int main() {
return 0;
}
"#;
match c_like.parse(input) {
Ok((remaining, ast)) => println!("Parsed AST: {:?}", ast),
Err(e) => println!("Error: {:?}", e),
}
3. 错误处理和恢复
nom-language提供了强大的错误处理功能:
use nom_language::{*, error::VerboseError};
let grammar = grammar! {
// ... 之前的算术表达式语法
}.with_error::<VerboseError<String>>();
let input = "3 + * 5";
match grammar.parse(input) {
Ok((rest, value)) => println!("Success: {:?}", value),
Err(Err::Error(e)) | Err(Err::Failure(e)) => {
println!("Parse error:");
for error in e.errors {
println!("- {:?}", error);
}
}
_ => {}
}
高级功能
1. 自定义AST节点
use nom_language::{*, types::*};
#[derive(Debug)]
enum Expr {
Number(f64),
BinOp(Box<Expr>, BinOp, Box<Expr>),
// ...
}
#[derive(Debug)]
enum BinOp {
Add, Sub, Mul, Div,
// ...
}
let expr_parser = map!(
// ... 之前的表达式解析规则
|(lhs, ops, rhs)| {
ops.into_iter().fold(lhs, |left, (op, right)| {
Expr::BinOp(Box::new(left), op, Box::new(right))
})
}
);
2. 语义动作
use nom_language::*;
let grammar = grammar! {
// ... 语法规则
// 在解析过程中执行语义动作
statement => {
expr: expression >>
char!(';') >>
(|expr| {
// 在这里处理表达式
println!("Found statement with expr: {:?}", expr);
expr
})
};
};
3. 词法分析与语法分析分离
use nom_language::{*, lexer::*};
// 先定义词法分析器
let lexer = lexer! {
r#"[ \t\n\r]"# => |_| None, // 跳过空白
r#"\d+\.\d+|\d+"# => |s| Some(Token::Number(s.parse().unwrap())),
r#"[a-zA-Z_][a-zA-Z0-9_]*"# => |s| Some(Token::Ident(s.to_string())),
r#"\+|\-|\*|\/|\=|\;"# => |s| Some(Token::Op(s.to_string())),
// ... 其他词法规则
};
// 然后定义语法分析器,处理Token流
let parser = grammar! {
expr => {
term: term >>
ops: many0!(pair!(op, term)) >>
(|(term, ops)| {
ops.into_iter().fold(term, |left, (op, right)| {
// 构建AST
})
})
};
// ... 其他语法规则
};
let input = "3 + 4 * 5";
let tokens = lexer.lex(input).collect::<Result<Vec<_>, _>>().unwrap();
let ast = parser.parse(&tokens[..]).unwrap();
性能提示
- 尽量使用
&str
而不是String
来避免分配 - 对于大型语法,考虑将语法定义拆分为多个模块
- 使用
nom_language::whitespace
模块中的预定义解析器处理空白 - 对于性能关键部分,考虑使用
nom_language::bits
进行位级解析
完整示例
下面是一个完整的算术表达式解析器示例,包含AST构建和错误处理:
use nom_language::*;
use std::fmt;
// 定义AST节点
#[derive(Debug, Clone)]
enum Expr {
Number(f64),
BinOp(Box<Expr>, BinOp, Box<Expr>),
}
#[derive(Debug, Clone)]
enum BinOp {
Add, Sub, Mul, Div,
}
impl fmt::Display for Expr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Expr::Number(n) => write!(f, "{}", n),
Expr::BinOp(lhs, op, rhs) => write!(f, "({} {} {})", lhs, op, rhs),
}
}
}
impl fmt::Display for BinOp {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
BinOp::Add => write!(f, "+"),
BinOp::Sub => write!(f, "-"),
BinOp::Mul => write!(f, "*"),
BinOp::Div => write!(f, "/"),
}
}
}
fn main() {
// 定义算术表达式语法
let grammar = grammar! {
// 主表达式规则
expr => {
term: term >>
ops: many0!(pair!(op, term)) >>
(|(term, ops)| {
ops.into_iter().fold(term, |left, (op, right)| {
Expr::BinOp(Box::new(left), op, Box::new(right))
})
}
};
// 项规则 (处理乘除法)
term => {
factor: factor >>
ops: many0!(pair!(mul_div_op, factor)) >>
(|(factor, ops)| {
ops.into_iter().fold(factor, |left, (op, right)| {
Expr::BinOp(Box::new(left), op, Box::new(right))
})
}
};
// 因子规则 (处理数字和括号表达式)
factor => {
alt!(
delimited!(char!('('), expr, char!(')')) |
map!(double, |n| Expr::Number(n))
)
};
// 加减操作符
op => {
alt!(
map!(char!('+'), |_| BinOp::Add) |
map!(char!('-'), |_| BinOp::Sub)
)
};
// 乘除操作符
mul_div_op => {
alt!(
map!(char!('*'), |_| BinOp::Mul) |
map!(char!('/'), |_| BinOp::Div)
)
};
}.with_error::<VerboseError<String>>();
// 测试表达式
let expressions = [
"3 + 4 * 5",
"(1 + 2) * 3",
"10 / 2 - 3",
"3 + * 5", // 错误表达式
];
for input in &expressions {
println!("解析表达式: {}", input);
match grammar.parse(*input) {
Ok((rest, expr)) => {
println!("解析成功: {}", expr);
if !rest.is_empty() {
println!("警告: 未解析的剩余输入: '{}'", rest);
}
}
Err(Err::Error(e)) | Err(Err::Failure(e)) => {
println!("解析错误:");
for error in e.errors {
println!("- {:?}", error);
}
}
_ => println!("未知错误"),
}
println!();
}
}
这个完整示例展示了如何:
- 定义AST数据结构
- 构建完整的算术表达式语法解析器
- 处理运算符优先级(乘除法优先于加减法)
- 处理括号表达式
- 提供详细的错误报告
- 格式化输出解析结果
运行这个示例将解析多个表达式,包括正确和错误的输入,并显示解析结果或错误信息。