Now we are ready to extend our calculator to cover the full range of arithmetic expressions (well, at least the ones you learned in elementary school). Here is the next calculator example, calculator3:
MST --- 现在我们准备扩展我们的计算器以涵盖所有算术表达式(嗯,至少是你在小学学过的那些)。这是下一个计算器示例 calculator3:
GPT --- 现在我们准备扩展我们的计算器,涵盖完整的算术表达式(好吧,至少是你在小学学到的那些)。下面是下一个计算器示例,
calculator3
:
use std::str::FromStr;
grammar;
pub Expr: i32 = {
<l:Expr> "+" <r:Factor> => l + r,
<l:Expr> "-" <r:Factor> => l - r,
Factor,
};
Factor: i32 = {
<l:Factor> "*" <r:Term> => l * r,
<l:Factor> "/" <r:Term> => l / r,
Term,
};
Term: i32 = {
Num,
"(" <Expr> ")",
};
Num: i32 = {
r"[0-9]+" => i32::from_str(<>).unwrap(),
};
Perhaps the most interesting thing about this example is the way it encodes precedence. The idea of precedence of course is that in an expression like 2+3*4, we want to do the multiplication first, and then the addition. It is pretty straightforward to express precedence in your grammar by structuring it in tiers -- for example, here we have the nonterminal Expr, which covers all expressions. It consists of a series of factors that are added or subtracted from one another. A Factor is then a series of terms that are multiplied or divided. Finally, a Term is either a single number or, using parenthesis, an entire expr.
MST --- 也许这个例子最有趣的地方是它对优先级进行编码的方式。当然,优先级的思想是,在像 2+3*4 这样的表达式中,我们想先做乘法,然后做加法。通过分层构建语法来表达语法中的优先级非常简单 —— 例如,这里我们有非终端 Expr,它涵盖了所有表达式。它由一系列相互添加或减去的因素组成。因子是一系列相乘或除以的项。最后,Term 可以是单个数字,也可以是整个 expr(使用括号)。
GPT --- 这个例子中最有趣的地方可能是它如何编码运算优先级。运算优先级的概念当然是,在像
2+3*4
这样的表达式中,我们希望先进行乘法运算,然后再进行加法运算。通过将文法结构分为多个层次来表示优先级是非常简单的——例如,在这里我们有一个非终结符Expr
,它表示所有的表达式。它由一系列可以相加或相减的因子组成。一个Factor
由一系列可以相乘或相除的项组成。最后,Term
要么是一个单独的数字,要么是通过括号包围的整个expr
。
Abstracting from this example, the typical pattern for encoding precedence is to have one nonterminal per precedence level, where you begin with the operators of lowest precedence (+, -), add in the next highest precedence level (*, /), and finish with the bare "atomic" expressions like Num. Finally, you add in a parenthesized version of your top-level as an atomic expression, which lets people reset.
MST --- 从此示例中抽象出,编码优先级的典型模式是每个优先级都有一个非终止符,其中以最低优先级的运算符 (+、-) 开头,加上下一个最高优先级 (*、/),并以裸露的“原子”表达式(如 Num)结束。最后,将 top-level 的括号版本添加为原子表达式,这样人们就可以重置。
GPT --- 从这个例子中抽象出来,编码优先级的典型模式是为每个优先级级别定义一个非终结符,从最低优先级的运算符(如
+
、-
)开始,再添加下一个优先级级别的运算符(如*
、/
),最后是像Num
这样的基本“原子”表达式。最后,你为顶层表达式添加一个带括号的版本,作为一种原子表达式,这样用户就可以通过括号来重设优先级。
To see why this works, consider the two possible parse trees for something like 2+3*4:
MST --- 要了解为什么这样做,请考虑两种可能的解析树,例如 2+3*4:
GPT --- 为了理解为什么这种方式有效,考虑一下像
2+3*4
这样的表达式的两种可能的解析树:
2 + 3 * 4 2 + 3 * 4
| | | | | | | | | |
| | +-Factor-+ OR +-Expr-+ | |
| | | | | |
+-Expr -+ +----Factor-+
In the first one, we give multiplication higher precedence, and in the second one, we (incorrectly) give addition higher precedence. If you look at the grammar now, you can see that the second one is impossible: a Factor cannot have an Expr as its left-hand side. This is the purpose of the tiers: to force the parser into the precedence you want.
MST --- 在第一个中,我们给乘法更高的优先级,而在第二个中,我们(错误地)给加法更高的优先级。如果你现在看一下语法,你可以看到第二个是不可能的: Factor 的左侧不能有 Expr。这就是 tiers 的用途:强制解析器达到您想要的优先级。
GPT --- 在第一个解析树中,我们给乘法更高的优先级,而在第二个解析树中,我们(错误地)给加法更高的优先级。如果你现在查看文法,你会发现第二种解析树是不可能的:一个
Factor
不能将一个Expr
作为其左侧。层次结构的作用就是:强制解析器遵循你希望的优先级。
Tiered expressions can also be generated by Lalrpop using the precedence and assoc attribute macros. These macros generate the same thing as tiered expressions do, but they can reduce code complexity when working with many levels of precedence. The above Expr grammar can be rewritten to the following using them:
MST --- 分层表达式也可以由 Lalrpop 使用 precedence 和 assoc 属性宏生成。这些宏生成的内容与分层表达式相同,但在使用多个优先级时,它们可以降低代码复杂性。可以使用它们将上述 Expr 语法重写为以下内容:
GPT --- LALRPOP 还可以通过
precedence
和assoc
属性宏生成分层表达式。这些宏生成的效果与分层表达式相同,但当处理多个优先级级别时,它们可以减少代码复杂性。上述的Expr
文法可以通过这些宏重写为以下形式:
pub Expr: i32 = {
#[precedence(level="0")] // Highest precedence
Term,
#[precedence(level="1")] #[assoc(side="left")]
<l:Expr> "*" <r:Expr> => l * r,
<l:Expr> "/" <r:Expr> => l / r,
#[precedence(level="2")] #[assoc(side="left")]
<l:Expr> "+" <r:Expr> => l + r,
<l:Expr> "-" <r:Expr> => l - r,
};
The precedence level specifies the order of operations starting from zero. In this example it means that 13 + 7 * 3 would be evaluated as (13 + (7 * 3)) because multiplication has a lower precedence level than addition.
MST --- 优先级别指定从零开始的操作顺序。在此示例中,这意味着 13 + 7 * 3 的计算结果为 (13 + (7 * 3)),因为乘法的优先级低于加法的优先级。
GPT ---
precedence
级别指定了从零开始的操作顺序。在这个例子中,它意味着13 + 7 * 3
会被计算为(13 + (7 * 3))
,因为乘法的优先级低于加法。
By using assoc you can specify if the expression is left-associative or right-associative. This is required to make the grammar unambiguous, otherwise 1 + 2 + 3 could both be interpreted as (1 + (2 + 3)) and ((1 + 2) + 3).
MST --- 通过使用 assoc,您可以指定表达式是左关联还是右关联。这是使语法明确所必需的,否则 1 + 2 + 3 都可以解释为 (1 + (2 + 3)) 和 ((1 + 2) + 3)。
GPT --- 通过使用
assoc
,你可以指定表达式是左结合还是右结合。这对于使文法无歧义是必需的,否则像1 + 2 + 3
这样的表达式可能既可以解释为(1 + (2 + 3))
,也可以解释为((1 + 2) + 3)
。
Finally, note that we only write pub before the nonterminal we're interested in parsing (Expr) and not any of the helpers. Nonterminals marked pub have extra code generated, like the new() method used to access the parser from other modules. If you get a warning about an unused new() method on FooParser, drop the pub from nonterminal Foo.
MST --- 最后,请注意,我们只在我们感兴趣的非终端 (Expr) 之前编写 pub,而不编写任何帮助程序。标记为 pub 的非终端会生成额外的代码,例如用于从其他模块访问解析器的 new() 方法。如果您在 FooParser 上收到有关未使用的 new() 方法的警告,请从非终端 Foo 中删除 pub。
标签:Handling,优先级,precedence,Expr,pub,---,5.4,expressions,表达式 From: https://www.cnblogs.com/Tifahfyf/p/18653199GPT --- 最后,注意我们只在我们感兴趣的非终结符(
Expr
)前写pub
,而不是任何辅助函数。标记为pub
的非终结符会生成额外的代码,比如new()
方法,用于从其他模块访问解析器。如果你收到关于FooParser
的未使用new()
方法的警告,可以去掉Foo
非终结符的pub
修饰符。