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是为高效解析设计的,以下是一些性能优化建议:

  1. 尽可能使用&str而不是String作为输入和输出类型
  2. 避免在解析器中分配内存
  3. 使用nom_language::bytes::complete模块中的解析器处理二进制数据
  4. 对于复杂语法,考虑使用nom_language::multinom_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();

性能提示

  1. 尽量使用&str而不是String来避免分配
  2. 对于大型语法,考虑将语法定义拆分为多个模块
  3. 使用nom_language::whitespace模块中的预定义解析器处理空白
  4. 对于性能关键部分,考虑使用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!();
    }
}

这个完整示例展示了如何:

  1. 定义AST数据结构
  2. 构建完整的算术表达式语法解析器
  3. 处理运算符优先级(乘除法优先于加减法)
  4. 处理括号表达式
  5. 提供详细的错误报告
  6. 格式化输出解析结果

运行这个示例将解析多个表达式,包括正确和错误的输入,并显示解析结果或错误信息。

回到顶部