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 解析工具

Download history 1448/week @ 2024-05-03 2077/week @ 2024-05-10 1875/week @ 2024-05-17 2432/week @ 2024-05-24 2437/week @ 2024-05-31 1634/week @ 2024-06-07 2540/week @ 2024-06-14 2715/week @ 2024-06-21 2218/week @ 2024-06-28 2271/week @ 2024-07-05 1641/week @ 2024-07-12 1855/week @ 2024-07-19 1636/week @ 2024-07-26 2007/week @ 2024-08-02 1820/week @ 2024-08-09 1688/week @ 2024-08-16

7,475 每月下载量
用于 7 crates

Apache-2.0/MIT

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