1 个不稳定版本

使用旧的 Rust 2015

0.0.1 2016年6月16日

#34 in #lr

1MB
19K SLoC

OCaml 19K SLoC // 0.3% comments Rust 429 SLoC // 0.1% comments Shell 16 SLoC // 0.4% comments

此仓库包含 Menhir 的一个分支。Menhir 是一个最初为 [OCaml] (https://ocaml.org.cn) 语言设计的 LR(1) 解析生成器,能够生成 OCaml 和 Coq 代码。此仓库扩展并修改了 Menhir,以便它能够生成 [Rust] (https://rust-lang.net.cn) 代码。

该项目仍在进行中。目前 Rust 后端相当简单:它编码在表中,并通过一个简单的 LR(1) 自动机循环运行。Menhir 的大多数高级功能,如错误处理和记录令牌位置,尚未实现。

用法

可以使用以下命令编译和安装 Menhir:

make PREFIX=/somewhere
make PREFIX=/somewhere install

省略 PREFIX 将 Menhir 安装在 /usr/local(需要 root 权限)。

要使用生成器,您必须使用一个 build.rs 脚本来指导 Cargo 如何调用生成器对您的语法进行操作。假设您在 src 文件夹中有一个 parser.rsy 文件。使用以下 build.rs 脚本:

use std::env;
use std::process::{Command, Stdio};

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();
    let menhir  = "/path/to/menhir";
    let s = Command::new(menhir).args(&["--rust", "--no-stdlib", "--base"])
                                .arg(&format!("{}/parser", out_dir)).arg("src/parser.rsy")
                                .stdout(Stdio::inherit()).stderr(Stdio::inherit())
                                .status().unwrap();
    assert!(s.success());
    println!("cargo:rerun-if-changed=src/parser.rsy");
}

Menhir 的路径可能因您的安装而异。如果您将 Menhir 安装在二进制文件在 PATH 中,您可以使用 menhir。请注意,我们在这里使用 --no-stdlib 选项,因为 Menhir stdlib 中包含 OCaml 语义动作,而 Rust 替代品尚未编写。

rerun-if-changed 行存在是为了确保每次 parser.rsy 改变时都会运行构建脚本。您可能希望省略它,因为 Cargo 的默认设置是在任何不属于 crate 的文件更改时运行它,但如果您在树中有其他要修改的文件,并且不想在修改时运行脚本,则它很有用。

然后在您的 Cargo.toml 文件中使用以下配置

[package]
# ...
build = "build.rs"

[dependencies]
# ...
menhir_runtime = { path = "/path/to/menhir/src/rust_runtime/" }

这次更改路径以指向 Menhir 源树的位置。这是必需的,因为 menhir_runtime 包尚未在 crates.io 上提供。

编写解析生成器

编写解析生成器的方式基本上与正常 Menhir 解析器相同,但有以下几个例外:

  • 每个非终结符的类型都必须使用 %type <Type> 符号 指定,就像在编写 Coq 解析器时一样,因为 Rust 的类型推断能力不足。
  • 如果您使用参数化非终结符,还必须指定实例的类型,例如为 list 非终结符使用 %type <Type> 符号列表。请注意,这是 Menhir 的一个非文档化特性,并且可能并不真正稳定。

解析器接口

生成的解析器包含一个针对每个起始非终结符的函数,以下是其签名(以 main 非终结符为例)

fn main<Lexer>(lexer: &mut Lexer) -> Result<T, ()>
    where Lexer: Iterator<Item == Token>

其中 T 是 main 的语义动作类型,如语法描述中指定。Result 的第二个参数是 unit,因为尚未实现错误处理。请注意,这个接口完全不稳定,并且在实现错误处理时可能会很快改变。

此接口应与 [RustLex] (https://github.com/LeoTestard/RustLex) 词汇生成器兼容。

无运行时依赖