22

我按照此网页上“优先级爬升”部分中给出的解释,使用具有各种一元前缀和二进制中缀运算符的优先级爬升算法来实现算术评估器。我还想包括三元运算符(即三元条件运算符?:)。

网页上给出的算法使用以下语法:

E --> Exp(0) 
Exp(p) --> P {B Exp(q)} 
P --> U Exp(q) | "(" E ")" | v
B --> "+" | "-"  | "*" |"/" | "^" | "||" | "&&" | "="
U --> "-"

如何将三元运算符合并到此语法中?

4

3 回答 3

26

具体来说,我将以 C/C++/Java?:为例。

似乎在那些语言中,运算符允许在and?:之间进行任何有效的表达式,包括由自身组成的表达式和由运算符组成的表达式,其优先级低于,例如and的优先级(例如:、、、)。?:?:?:=,a ? b = c : da ? b , c : da ? b ? c : d : e

这表明您应该以与解析表达式时完全相同的方式处理?和。当您解析出 时,它作为一个整体,是一个二元运算符。因此,您以相同的方式解析和,但前者是一个表达式,而后者是一个二元运算符(其中嵌入了一个表达式)。:()? expr :( expr )? expr :

现在这? expr :是一个二元运算符(与“二元性”a ?-expr-: b没有什么不同a * b),您应该能够像您已经支持的任何其他二元运算符一样支持它。

就个人而言,我不会费心去拆分?:成各自独立的二元运算符。最后,它仍然是一个三元运算符,它必须链接到 3 个操作数,并在表达式评估时作为一个整体考虑。如果您遵循您在问题中提到的文章中的方法并正在创建一个 AST,那么您就可以了,?:有一个左子节点,一个右子节点(与任何其他二元运算符一样),此外,还有一个中间子节点节点。

整体的优先级?-expr-:应该是低的。不过,在 C/C++(以及 Java?)中,它并不是最低的。由你决定你想要它是什么。

到目前为止,我们还没有讨论?:. 在 C/C++ 和 Java 中,?-expr-:就像赋值运算符一样是右结合的=。同样,由您决定将其设为左关联或保持右关联。

还有这个:

E --> P {B P}
P --> v | "(" E ")" | U P
B --> "+" | "-" | "*" | "/" | "^"
U --> "-"

应该变成这样:

E --> P {B P}
P --> v | "(" E ")" | U P
B --> "+" | "-" | "*" | "/" | "^" | "?" E ":"
U --> "-"
于 2013-09-20T06:18:15.100 回答
6

我在搜索有关将三元运算符转换为反向波兰表示法 (RPN) 的信息时遇到了这个问题,所以虽然我没有可靠的实现来实现?:具有优先爬升的运算符,但我确实认为我可以提供一些线索使用两个堆栈将?:运算符转换为 RPN ......这类似于您给出的网页中的 Shutting Yard 算法。

(编辑)我必须指出我在这里所做的不是很有效(必须评估三元运算符的两个分支),但要演示?:在现有框架中合并新运算符 ( ) 是多么容易( RPN 转换代码)(通过声明?:两个最低优先级)。

我想从一些示例开始,说明如何?:根据我的解析器将表达式 with 转换为 RPN ...我添加了两个运算符而不是一个,?and :,但我认为这不会有很大的不同,因为:并且?将永远放在一起(RPN和原始表达式具有相同数量的令牌更方便)。在示例中,您可以看到它是如何工作的。

示例 1:1 + ((0+1) ? 2 : 3 + 8) + 4

RPN:1 0 1 + 2 3 8 + : ? + 4 +

现在让我们评估 RPN。

1 - 将第一个元素推入堆栈,我们遇到了第一个二元运算符:

1 0 1和 operator +,我们添加顶部 2 个元素,将堆栈变成1 1

2 - 然后再推三个元素,我们遇到了第二个二元运算符:

1 1 2 3 8 +------>1 1 2 11

3 - 现在我们有:?。这实际上告诉我们选择后续分支堆栈顶部的第二个最顶部元素2)或替代分支(堆栈顶部11的元素) . 由于谓词为 1(真),我们选择 2 并丢弃 11。解析器弹出 3 个元素(谓词/结果/替代)并推回选择的元素(在本例中为结果分支),因此堆栈变为1

1 2

4 - 消耗剩余的元素:

1 2 +------>3

3 4 +------>7


示例 2:1 + ((0+0+0?0:0) ? 2 : (3 + 8)) + 4

RPN:1 0 0 + 0 + 0 0 : ? 2 3 8 + : ? + 4 +

评估:

开始:

1 0 0 + 0 + 0 0 : ? 2 3 8 + : ? + 4 +

将 0 加到 0 后:

1 0 0 + 0 0 : ? 2 3 8 + : ? + 4 +

将 0 加到 0 后:

1 0 0 0 : ? 2 3 8 + : ? + 4 +

第一个:?选择后0

1 0 2 3 8 + : ? + 4 +

添加 3 和 8 后:

1 0 2 11 : ? + 4 +

?:选择11后:

1 11 + 4 +

添加 1 和 11 后:

12 4 +

最后:

16


这可能表明可以将表达式?:转换为反向波兰符号。正如网页所说,RPN 和 AST 是等价的,因为它们可以相互转换,三元运算符应该能够以类似的方式通过 Precedence Climbing 实现。

需要做的一件事似乎是?:运算符的“优先级”(或优先级)。我在尝试 RPN 转换时实际上遇到了它。我给了问号和冒号最低优先级:

正如您从上面的示例中看到的那样,每当我们要执行?:时,优先分支替代分支谓词应该已经被评估,产生一个数字。这是由优先级保证的。

以下是优先级(优先级)表。

! ~> * / %> + -> &> ^> |> &&> ||> ?>:

?and的:优先级最低,在 1?2+3:4+5 这样的表达式中表示,?并且:永远不会接受它们周围的操作数。

?出现:在RPN之前。据我了解,这只是一种设计选择,因为甚至不一定必须首先拆分为 2 个运算符。:?:?

希望这可以帮助..


参考:http ://en.cppreference.com/w/cpp/language/operator_precedence

于 2013-06-05T03:02:51.563 回答
3

定义冒号的优先级低于问号。换句话说,一个?b : c 将被解析为 (a ? b) : c。这让解析器可以构造一个 (if/then/empty-else) 抽象语法节点,然后由“: 运算符”对其进行操作以提供所需的 else 组件。

优先关系还保留了运算符的可组合性。例如,一个?乙:丙?d : e 解析为 (a ? b) : (c ? d) : e,正如人们所期望的那样。

希望这可以帮助。

于 2013-09-10T08:15:01.850 回答