【问题标题】:Parsing right-associative operator (exponents)解析右结合运算符(指数)
【发布时间】:2019-02-06 18:49:43
【问题描述】:

我一直在为自己的语言编写词法分析器/解析器/解释器,到目前为止一切正常。我一直在关注Ruslan Spivak's blog 上的示例(Github 每篇文章的链接)。

我想将我的语言语法扩展到文章中所写的内容,以包括更多运算符,如比较(<>= 等)和指数(**^ 在我的语言中) .我有这个语法:

expression        : exponent ((ADD | SUB) exponent)*
exponent          : term ((POWER) term)*
# this one is right-associative (powers **)

term              : comparison ((MUL | DIV) comparison)*
comparison        : factor ((EQUAl   | L_EQUAL | LESS
                             N_EQUAL | G_EQUAL | GREATER) factor)*
# these are all binary operations



factor            : NUM | STR        | variable
                        | ADD factor | SUB factor
                        | LPAREN expr RPAREN
# different types of 'base' types like integers
# also contains parenthesised expressions which are evalutaed first

在解析令牌方面,我使用了与 Ruslan 的博客中使用的相同的方法。这是一个将解析exponent 行的代码,尽管它的名称处理加法和减法,因为语法说表达式被解析为 exponent_expr (+ / -) exponent_expr

def exponent(self):
    node = self.term()
    while self.current_token.type in (ADD, SUB):
        token = self.current_token

        if token.type == ADD:
            self.consume_token(ADD)
        elif token.type == SUB:
            self.consume_token(SUB)

        node = BinaryOperation(left_node=node,
                               operator=token,
                               right_node=self.term())

    return node

现在这可以很好地解析左关联标记(因为标记流自然地从左到右),但我被困在如何解析右关联指数上。查看这个预期的输入/输出以供参考:

>>> 2 ** 3 ** 2
# should be parsed as...
>>> 2 ** (3 ** 2)
# which is...
>>> 2 ** 9
# which returns...
512

# Mine, at the moment, parses it as...
>>> (2 ** 3) ** 2
# which is...
>>> 8 ** 2
# which returns...
64

为了解决这个问题,我尝试切换 BinaryOperation() 构造函数的左右节点,以使当前节点位于右侧,新节点位于左侧,但这只会使 2**5 解析为 5**2,这给了我 @987654335 @ 而不是预期的32

我可以尝试什么方法?

【问题讨论】:

  • 该语法为比较运算符提供了异常高的优先级。
  • 是的,我刚刚意识到这一点。我错误地把它放在了底部,因为我认为那是更高的优先级,而实际上第一行是最低的优先级

标签: python parsing token right-to-left


【解决方案1】:

您的exponent 函数实际上解析expressions 的事实应该是一个危险信号。实际上,您需要的是一个解析表达式的 expression 函数和一个解析指数的 exponent 函数。

您还混淆了求幂和乘法(以及其他运算)的优先级,因为2 * x ** 4 并不表示(2 * x) ** 4(即16x⁴),而是2 * (x ** 4)。同样的道理,x * 3 < 17 并不意味着 x * (3 < 17),这是你的语法将如何解析它的方式。

通常算术的优先级如下所示:

 comparison     <, <=, ==, ... ( lowest precedence)
 additive       +, -
 multiplicative *, /, %
 unary          +, -
 exponentiation **
 atoms          numbers, variables, parenthesized expressions, etc.

(如果你有像函数调用这样的后缀运算符,它们会介于求幂和原子之间。)

以这种形式修改语法后,指数解析器将如下所示:

def exponent(self):
    node = self.term()
    while self.current_token.type is POWER:
        self.consume_token(ADD)
        node = BinaryOperation(left_node=node,
                               operator=token,
                               right_node=self.exponent())
    return node

最后的递归调用产生右结合性。在这种情况下,递归是可以接受的,因为左操作数和运算符已被使用。因此递归调用不会产生无限循环。

【讨论】:

  • 感谢您的详细回答。非常感激。我现在才意识到我的语法有点倒退,因为顶行表达式实际上是最低优先级,我错误地将比较和指数规则插入到加法和乘法规则周围的相对侧
  • 不过,你的语法是错误的。 term : comparison ((MUL | DIV) comparison)* 导致解析 x * (3 &lt; 17),嗯,unusual。这个产生式的字面意思是,一个术语是一系列比较的产品。
  • 是的,我将不得不重建表达式生成。我想错误是我理解它的唯一方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-02-13
  • 2013-03-01
  • 1970-01-01
  • 2011-09-08
  • 2016-04-08
  • 2011-11-16
  • 2010-11-30
相关资源
最近更新 更多