BASIC语法中"END"和"END IF"之间的碰撞,使用Lark。



我正在尝试创建一个使用lark的lalr解析器,并且我很难在" end"语句和诸如" end If"之类的语句之间修复碰撞。这是语法的简化版本:

%ignore /[ tf]+/
program: _nlopt _part_list
_part_list: (stmt | block) _nl _part_list
          |
_nlopt: _nl
      |
_nl: _NEWLINE _nl
   | _NEWLINE
block: if_block
stmt: print_stmt
    | end_stmt
end_stmt: END_KW
if_block: IF_KW expr THEN_KW _nl block_body endif_stmt
endif_stmt: END_KW IF_KW
block_body: _block_body_item block_body
          |
_block_body_item: stmt _nl
print_stmt: PRINT_KW expr
?expr: NUMERIC_LITERAL
_NEWLINE: "n"
NUMERIC_LITERAL: /[-+]?d+(.d*)?[!#%&]?/
END_KW: "end"i
IF_KW: "if"i
PRINT_KW: "print"i
THEN_KW: "then"i

如果我尝试使用这样的代码尝试此语法:

parser = Lark(grammar, start='program', parser='lalr')
prog = r"""
if 1 then
   print 200
end if
"""
t = parser.parse(prog)
print(t.pretty())

这就是我从百灵鸟那里得到的:

Traceback (most recent call last):
  File "test.py", line 230, in <module>
    t = parser.parse(prog)
  File "/home/user/.pyenv/versions/venv/lib/python3.6/site-packages/lark/lark.py", line 250, in parse
    return self.parser.parse(text)
  File "/home/user/.pyenv/versions/venv/lib/python3.6/site-packages/lark/parser_frontends.py", line 37, in parse
    return self.parser.parse(token_stream, *[sps] if sps is not NotImplemented else [])
  File "/home/user/.pyenv/versions/venv/lib/python3.6/site-packages/lark/parsers/lalr_parser.py", line 68, in parse
    for token in stream:
  File "/home/user/.pyenv/versions/venv/lib/python3.6/site-packages/lark/lexer.py", line 341, in lex
    for x in l.lex(stream, self.root_lexer.newline_types, self.root_lexer.ignore_types):
  File "/home/user/.pyenv/versions/venv/lib/python3.6/site-packages/lark/lexer.py", line 175, in lex
    raise UnexpectedCharacters(stream, line_ctr.char_pos, line_ctr.line, line_ctr.column, allowed=allowed, state=self.state)
lark.exceptions.UnexpectedCharacters: No terminal defined for 'i' at line 4 col 5
end if
    ^
Expecting: ['__IGNORE_0', '_NEWLINE']

如果我删除" end_stmt"规则,则不会发生。有没有办法修复语法以使其不会发生?

默认情况下,百灵鸟不会警告您有关语法中的换档冲突,而是默默地解决了这些冲突,而是默默地解决了这些冲突。通常,这会导致一个不能解析您想要的解析器 - 就像这里一样。您可以通过将debug = True标志传递给Lark()来警告您有关此类冲突的警告。这样,您就会看到甚至在通过测试找到问题之前出现问题,甚至可能会获得有关问题所在的地方的有用信息。

启用了debug选项,您会收到一个警告,有一个轮换冲突,END_KW可能意味着当前的block_body已经结束,或者可能是end_stmt。这是一个问题,因为LALR(1(解析器只能向前看一个令牌,但是我们必须向前看一个令牌,以查看end之后是否有if才能正确确定要选择哪个选项。

您可以通过将end if变成这样的单个令牌来解决这个问题:

ENDIF_KW: /end[ tf]+if/i

然后使用ENDIF_KW而不是END_KW IF_KW

ps:请注意,如果您使用earley解析而不是lalr(1(。

我与基本语法有同样的冲突。基本语言是lalr(2(或LR(2(,因为结束时,如果结束,等等。LRSTAR解析器发电机可以创建LR(2(解析器。

最新更新