Rust语法分析库lrtable的使用,lrtable提供高性能LR(1)解析表生成与语法分析功能

lrtablecfgrammar 接收语法并从中创建 LR 状态表。很少有用户会直接对其功能感兴趣,除了那些进行高级语法分析形式的用户。

以下是一个使用 lrtable 的完整示例代码:

use cfgrammar::yacc::YaccKind;
use lrpar::Lexer;
use lrtable::{from_yacc, StateTable};

// 定义语法规则
const GRAMMAR: &str = r#"
%start Expr
%%
Expr -> Expr '+' Term
      | Term;
Term -> Term '*' Factor
      | Factor;
Factor -> 'id'
        | '(' Expr ')';
"#;

fn main() {
    // 生成 LR 解析表
    let (gr, _) = cfgrammar::yacc::YaccGrammar::new(YaccKind::Original, GRAMMAR).unwrap();
    let lrtable = from_yacc(&gr, YaccKind::Original).unwrap();
    
    // 打印状态表信息
    println!("Number of states: {}", lrtable.num_states());
    println!("Number of actions: {}", lrtable.num_actions());
    
    // 示例:解析输入字符串
    let input = "id + id * id";
    let lexer = lrlex::LRNonStreamingLexer::new(input.as_bytes());
    let result = lrpar::parse(&lrtable, lexer);
    
    match result {
        Ok(ast) => println!("Parse successful: {:?}", ast),
        Err(e) => println!("Parse error: {:?}", e),
    }
}

完整示例代码:

// 引入必要的库
use cfgrammar::yacc::YaccKind;
use lrlex::LRNonStreamingLexer;
use lrpar;
use lrtable::{from_yacc, StateTable};

// 定义语法规则字符串
const GRAMMAR: &str = r#"
%start Expr
%%
Expr -> Expr '+' Term
      | Term;
Term -> Term '*' Factor
      | Factor;
Factor -> 'id'
        | '(' Expr ')';
"#;

fn main() {
    // 从YACC语法生成语法对象
    let (gr, _) = cfgrammar::yacc::YaccGrammar::new(YaccKind::Original, GRAMMAR).unwrap();
    
    // 从语法生成LR状态表
    let lrtable = from_yacc(&gr, YaccKind::Original).unwrap();
    
    // 打印状态表统计信息
    println!("状态数量: {}", lrtable.num_states());
    println!("动作数量: {}", lrtable.num_actions());
    
    // 定义要解析的输入字符串
    let input = "id + id * id";
    
    // 创建词法分析器
    let lexer = LRNonStreamingLexer::new(input.as_bytes());
    
    // 使用LR状态表进行解析
    let result = lrpar::parse(&lrtable, lexer);
    
    // 处理解析结果
    match result {
        Ok(ast) => println!("解析成功: {:?}", ast),
        Err(e) => println!("解析错误: {:?}", e),
    }
}

1 回复

Rust语法分析库lrtable的使用指南

概述

lrtable是一个高性能的Rust库,专门用于生成LR(1)解析表和执行语法分析。它提供了完整的LR(1)解析器生成功能,支持构建自定义语法分析器。

主要特性

  • 高效的LR(1)解析表生成算法
  • 支持自定义语法规则定义
  • 提供详细的语法分析错误报告
  • 零成本抽象和高性能实现

安装方法

在Cargo.toml中添加依赖:

[dependencies]
lrtable = "0.4"

基本使用方法

1. 定义语法规则

use lrtable::lr1::{Grammar, Production, Symbol};

let mut grammar = Grammar::new();

// 定义非终结符
let expr = grammar.add_nonterminal("Expr");
let term = grammar.add_nonterminal("Term");
let factor = grammar.add_nonterminal("Factor");

// 定义产生式规则
grammar.add_production(expr, vec![
    Production::new(expr, vec![Symbol::NonTerminal(term)]),
    Production::new(expr, vec![
        Symbol::NonTerminal(expr), 
        Symbol::Terminal("+".to_string()), 
        Symbol::NonTerminal(term)
    ])
]);

grammar.add_production(term, vec![
    Production::new(term, vec![Symbol::NonTerminal(factor)]),
    Production::new(term, vec![
        Symbol::NonTerminal(term), 
        Symbol::Terminal("*".to_string()), 
        Symbol::NonTerminal(factor)
    ])
]);

2. 生成解析表

use lrtable::lr1::LRTables;

let lr_tables = LRTables::construct(&grammar).unwrap();

3. 执行语法分析

use lrtable::lr1::Parser;

let mut parser = Parser::new(&lr_tables);
let tokens = vec!["a", "+", "b", "*", "c"];

match parser.parse(tokens.iter().map(|s| s.to_string())) {
    Ok(ast) => {
        println!("解析成功: {:?}", ast);
    }
    Err(e) => {
        println!("解析错误: {}", e);
    }
}

高级用法示例

自定义词法分析器集成

use lrtable::lr1::{Parser, LRTables};

fn tokenize(input: &str) -> Vec<String> {
    input.split_whitespace()
        .map(|s| s.to_string())
        .collect()
}

fn main() {
    // 假设已经构建了语法和解析表
    let lr_tables: LRTables = // ... 解析表构建代码
    
    let input = "x + y * z";
    let tokens = tokenize(input);
    
    let mut parser = Parser::new(&lr_tables);
    let result = parser.parse(tokens.into_iter());
    
    // 处理解析结果
}

错误处理和恢复

use lrtable::lr1::{Parser, ParseError};

let result = parser.parse(tokens);
match result {
    Ok(ast) => process_ast(ast),
    Err(ParseError::SyntaxError { position, expected }) => {
        eprintln!("语法错误在位置 {},期望: {:?}", position, expected);
    }
    Err(e) => {
        eprintln!("其他解析错误: {}", e);
    }
}

完整示例demo

use lrtable::lr1::{Grammar, Production, Symbol, LRTables, Parser, ParseError};

fn main() {
    // 1. 定义语法规则
    let mut grammar = Grammar::new();
    
    // 定义非终结符
    let expr = grammar.add_nonterminal("Expr");
    let term = grammar.add_nonterminal("Term");
    let factor = grammar.add_nonterminal("Factor");
    
    // 定义产生式规则
    grammar.add_production(expr, vec![
        Production::new(expr, vec![Symbol::NonTerminal(term)]),
        Production::new(expr, vec![
            Symbol::NonTerminal(expr), 
            Symbol::Terminal("+".to_string()), 
            Symbol::NonTerminal(term)
        ])
    ]);
    
    grammar.add_production(term, vec![
        Production::new(term, vec![Symbol::NonTerminal(factor)]),
        Production::new(term, vec![
            Symbol::NonTerminal(term), 
            Symbol::Terminal("*".to_string()), 
            Symbol::NonTerminal(factor)
        ])
    ]);
    
    grammar.add_production(factor, vec![
        Production::new(factor, vec![Symbol::Terminal("id".to_string())]),
        Production::new(factor, vec![
            Symbol::Terminal("(".to_string()), 
            Symbol::NonTerminal(expr), 
            Symbol::Terminal(")".to_string())
        ])
    ]);
    
    // 2. 生成解析表
    let lr_tables = LRTables::construct(&grammar).unwrap();
    
    // 3. 执行语法分析
    let mut parser = Parser::new(&lr_tables);
    
    // 模拟输入tokens
    let tokens = vec!["id", "+", "id", "*", "id"];
    
    match parser.parse(tokens.iter().map(|s| s.to_string())) {
        Ok(ast) => {
            println!("解析成功: {:?}", ast);
        }
        Err(ParseError::SyntaxError { position, expected }) => {
            eprintln!("语法错误在位置 {},期望: {:?}", position, expected);
        }
        Err(e) => {
            eprintln!("其他解析错误: {}", e);
        }
    }
}

// 自定义词法分析器函数
fn tokenize(input: &str) -> Vec<String> {
    input.split_whitespace()
        .map(|s| match s {
            "+" => "+".to_string(),
            "*" => "*".to_string(),
            "(" => "(".to_string(),
            ")" => ")".to_string(),
            _ if s.chars().all(|c| c.is_alphabetic()) => "id".to_string(),
            _ => s.to_string()
        })
        .collect()
}

// 处理AST的函数
fn process_ast(ast: lrtable::lr1::AST) {
    println!("处理抽象语法树: {:?}", ast);
}

性能优化建议

  1. 预生成解析表并序列化保存
  2. 使用&str而不是String作为token类型以减少分配
  3. 对于大型语法,考虑使用LALR(1)模式

注意事项

  • 确保语法是LR(1)兼容的,否则构造解析表可能失败
  • 复杂的语法可能需要调整解析表生成参数
  • 生产环境建议添加适当的错误处理和日志记录

这个库特别适合需要自定义领域特定语言(DSL)或构建复杂解析器的场景。

回到顶部