微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何使用 Rust 的解析器库 nom 正确解析这个程序

如何解决如何使用 Rust 的解析器库 nom 正确解析这个程序

我想解析一个由这个 BNF 表示的程序:

<log_and_expression> := <and_expression> (<space>* "&&" <space>* <and_expression>)*
<and_expression>     := <unary_expression> (<space>* "&" <space>* <unary_expression>)*
<unary_expression>   := ("&" <space>*)* <identifier>
<identifier>         := "a" | "b" | "c" | ...
<space>              := " " | "\t" | "\n" | "\r"

这个BNF解析小程序。 log_and 表示逻辑与。

我使用解析器库 nom 编写了 Rust 程序:

use nom::{
    branch::{alt,permutation},bytes::complete::{escaped_transform,is_not,tag,take_while_m_n},character::complete::{
        char,digit1,hex_digit1,line_ending,multispace0,multispace1,none_of,space0,space1,},combinator::{all_consuming,map,opt,value},error::VerboseError,multi::{many0,many1},number::complete::double,sequence::{delimited,tuple},IResult,};

#[derive(Debug,PartialEq,Clone)]
pub struct LogAndExpression {
    pub and_expression: AndExpression,pub tail: Vec<AndExpression>,}
#[derive(Debug,Clone)]
pub struct AndExpression {
    pub unary_expression: UnaryExpression,pub tail: Vec<UnaryExpression>,Clone)]
pub struct UnaryExpression {
    pub unary_operators: Vec<String>,pub identifier: String,}

pub fn log_and_expression(s: &str) -> IResult<&str,LogAndExpression> {
    map(
        tuple((
            and_expression,many0(
                map(
                    tuple((
                        multispace0,tag("&&"),and_expression,)),|(_,_,expression)| expression,)
            ),|(expression,vec)| LogAndExpression {
            and_expression: expression,tail: vec,)(s)
}

pub fn and_expression(s: &str) -> IResult<&str,AndExpression> {
    map(
        tuple((
            unary_expression,tag("&"),unary_expression,vec)| AndExpression {
            unary_expression: expression,)(s)
}

pub fn unary_expression(s: &str) -> IResult<&str,UnaryExpression> {
    map(
        tuple((
            many0(
                map(
                    tuple((
                        tag("&"),multispace0
                    )),|(opr,_): (&str,&str)| opr.to_owned(),is_not(" &"),|(unary_operators,identifier)| UnaryExpression {
            unary_operators: unary_operators,identifier: identifier.to_owned(),)(s)
}

fn main() {
    println!("{:?}",log_and_expression("a && b"));
}

这段代码输出解析a && b的结果。 log_and_expression 解析程序。

我想要的结果是 a LogAnd b。 但结果是:

Ok(("",LogAndExpression { and_expression: AndExpression { unary_expression: UnaryExpression { unary_operators: [],identifier: "a" },tail: [UnaryExpression { unary_operators: ["&"],identifier: "b" }] },tail: [] }))

这意味着a And &b

如何使用 nom 正确解析此程序?

解决方法

列出的 BNF 规则是不明确的,因为没有简单的方法来区分解析 a&&b 的正确方法。它可以被解析为 a && ba & &b,并且由于后一个选项是 nom 解析器版本将尝试解析的第一个选项,它将首先返回该选项。

log_and_expression 函数中,解析器将调用 and_expression 函数,该函数将正确匹配 a 标识符后跟第一个 &,然后它将调用 { {1}} 函数来解析余数,在这种情况下可以正确解析为一元 unary_expression 表达式,因为该规则明确允许 & b& 之间存在空格。这给你你得到的输出(这不是你想要的)

解决此问题的一种方法是重写解析器,使其匹配最小的一元表达式,后跟 bmany0 后跟另一个一元表达式,或 && 后跟通过一元表达式。类似的东西

&

如果它首先尝试匹配 <expr> = <unary_expression> (<space>* <operator_pair> <space>*)* <operator_pair> = ("&&" <space>* <unary_expression>) | ("&" <space>* <unary_expression>) ,那么这将是解析 && 的首选方式,并且替代形式只能在用空格书写时才能被解析,如 {{1} }.这可能需要构建“运算符”和“一元表达式”对的列表,然后需要将它们重新组装成您正在寻找的正确的树状格式。将其作为 2 阶段过程执行此操作的一个优点是,您可以在支持具有不同优先级的多个运算符时正确应用操作顺序。

这种方法有一个简单的例子,名称为 here

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。