多行 PLY 解析器

Multiline PLY parser

我正在使用 PLY 来解析跨越多行(或通过“;”分隔)的算术表达式。我不确定是否要忽略换行符,因为我真的不需要它们。如果忽略它们(return None in t_NEWLINE),是否有必要在制作中考虑它们?

def t_NEWLINE(self, t):
    r"""\n+|;+"""
    t.lexer.lineno += t.value.count("\n") + t.value.count(";")
    return t # or not

def p_expression(self, t):
    """ expression : ..."""

def p_expressions(self, t):
    """ expressions : expression
                    | expressions NEWLINE expression
    """
    # Do I need NEWLINE at all? How does the production figure out where expression ends without NEWLINE?

这似乎更像是一个语言设计问题,而不是实现问题,当然,即使设计已完全指定,实现也可能并不明显。

允许多行表达式的语言有几种可能的方法,我完全不清楚您对实现哪一种感兴趣:

  1. Python风格。表达式以 ; 或换行符终止,但括号表达式 ((...)[...]{...}).

    [=75= 中的换行符将被忽略]
  2. ECMAscript 风格。表达式需要以 ; 结束,但如果下一个标记不能扩展表达式,将在行尾自动插入 ;

  3. 最大咀嚼。表达式是最长可能的标记流,可以将其解析为表达式,但是 ; 可以在必要时用于分隔表达式。

    我实际上不知道实现此选项的通用语言。但是它(或一些变体)可以编写,例如,

    a = 3    b = 7    c = a + b
    

    这看起来应该是不含糊的。

实施策略。

1。括号隐藏换行符。

在语言变得复杂之前,这实现起来相当简单。对于简单的表达式,您只需要保留一个全局括号深度计数,它随着(任何类型的)左括号增加而随着右括号减少;那么 \n 规则 return 如果括号深度为 0,则为 ;,否则吞下换行符。

我个人觉得这种续行风格很烦人,尽管每次我回到 Python 编程时我很快就习惯了它。尽管如此,它总是有点烦人,你不能写:

x = some * long * sub-expression       +
    another * long * sub-expression    +
    magical-constant

这在 ECMAscript 中工作得很好。

2。自动分号

事实证明,您可以在词法分析器的 ECMAscript 中处理这个问题,方法是查找一行中的第一个标记和合法标记对字典中的前一个标记。如果令牌对不在字典中,则使用分号令牌 returned 而不是行中的第一个令牌;该标记会被记住,以便下一次调用词法分析器时 return 它。构建合法的 token-pairs 字典并不简单,验证它是否正确更难,但只需要在每次更改语法时都这样做:)

ECMAscript 实际上有一些规则的例外情况,因此一些原本合法的表达式在用换行符分隔时变得非法。这些例外可以通过从合法对字典中删除一些标记对来实现(碰巧)。例如,++ post-increment operator 不能用换行符与其操作数分开,因此在下面插入一个分号:

a
++b

否则会出现语法错误。 (前两个标记将被解析为 a++,然后无法解析 b。)

另一个有趣的案例是:

a + b
(c + d).format("x")

这可以解析为函数调用:

a + b(c + d).format("x")

所以最好在 b( 之间自动插入分号。

3。最大咀嚼

这实际上只是自动分号插入的一个变体,即使两个标记之间没有换行符也可以插入分号。由于上述例外情况,您可能希望拥有两组不同的合法令牌对,或者将某些令牌对标记为仅在未用换行符分隔时才有效。