PLY:解析错误。规则意外匹配空标记集

PLY : Parsing error. Rule unexpectedly matching empty set of tokens

所以我正在尝试使用 PLY 构建解析器,但它不起作用。它给了我似乎无法解决的错误。我提取了一小部分我放在另一个文件中的解析器,用于测试目的:它也不起作用。

这是我似乎无法解决的简化问题:

输入示例

#Trying to parse properties following the patter X1[some text]X2[other text]
#Simplified input used
GM[1]US[hello]

根据我的理解,解析器读取第一个 属性 (GM[1]) 并在第二个上触发错误。他正在采取尽可能短的减少(我的规则确实允许)并读取单个 属性。然后,第二个没有更多规则可遵循,从而触发错误。

所以问题是:你能帮我正确编写规则吗?

parser.py

#For simplicity, i only give you the rules, and removed operations on the tree.

def p_properties(self,p):
    "properties : property property_list"

def p_property_list(self,p):
    "property_list : "
    "               | properties"

def p_property(self,p):
    "property : PROPERTY_NAME single_property_content"

def p_single_property_content(self,p):
    "single_property_content : OBRACKET TEXT CBRACKET"

起始规则是"properties"。我希望输入 属性 (GM[1]) 匹配规则 属性,第二个匹配匹配 property_list。但我认为第一条规则匹配 属性property_list (因为 property_list 中的空生产),这会减少到 properties。然后,没有更多规则可用于读取第二个 属性。

解析器的输出如下:

State  : 0
Stack  : . LexToken(PROPERTY_NAME,'GM',1,0)
Action : Shift and goto state 2

State  : 2
Stack  : PROPERTY_NAME . LexToken(OBRACKET,'[',1,2)
Action : Shift and goto state 4

State  : 4
Stack  : PROPERTY_NAME OBRACKET . LexToken(TEXT,'1',1,3)
Action : Shift and goto state 7

State  : 7
Stack  : PROPERTY_NAME OBRACKET TEXT . LexToken(CBRACKET,']',1,4)
Action : Shift and goto state 8

State  : 8
Defaulted state 8: Reduce using 4
Stack  : PROPERTY_NAME OBRACKET TEXT CBRACKET . None
Action : Reduce rule [single_property_content -> OBRACKET TEXT CBRACKET] with ['[','1',']'] and goto state 5
Result : <tuple @ 0x7fea37eb4588> (('single_property_content', '1'))

State  : 5
Defaulted state 5: Reduce using 3
Stack  : PROPERTY_NAME single_property_content . None
Action : Reduce rule [property -> PROPERTY_NAME single_property_content] with ['GM',<tuple @ 0x7fea37eb4588>] and goto state 3
Result : <tuple @ 0x7fea3964f798> (('property', 'GM', ('single_property_con ...)

State  : 3
Defaulted state 3: Reduce using 2
Stack  : property . None
Action : Reduce rule [property_list -> <empty>] with [] and goto state 6
Result : <NoneType @ 0xa3f020> (None)

State  : 6
Defaulted state 6: Reduce using 1
Stack  : property property_list . None
Action : Reduce rule [properties -> property property_list] with [<tuple @ 0x7fea3964f798>,None] and goto state 1
Result : <tuple @ 0x7fea3964f678> (('properties', ('property', 'GM', ('sing ...)

State  : 1
Stack  : properties . LexToken(PROPERTY_NAME,'US',1,5)
ERROR: Error  : properties . LexToken(PROPERTY_NAME,'US',1,5)
Error while parsing : LexToken(PROPERTY_NAME,'US',1,5)

State  : 1
Stack  : properties . error
ERROR: Error  : properties . error

State  : 0
Stack  : . error
ERROR: Error  : . error

State  : 0
Stack  : . LexToken(OBRACKET,'[',1,7)
ERROR: Error  : . LexToken(OBRACKET,'[',1,7)

State  : 0
Stack  : . LexToken(TEXT,'hello',1,8)
ERROR: Error  : . LexToken(TEXT,'hello',1,8)

State  : 0
Stack  : . LexToken(CBRACKET,']',1,13)
ERROR: Error  : . LexToken(CBRACKET,']',1,13)

State  : 0
Stack  : . $end
ERROR: Error  : . $end
None

我已经用 15 种不同的方式(右递归,左递归)修改了这些规则,但都无济于事。你们能告诉我我做错了什么吗?

编辑 在实施解决方案时遇到了一些麻烦(仅当我解析了 2 个元素但解析了更多创建的错误时才起作用),感谢@rici 的帮助,我设法让它起作用。

解决方案

def p_properties(self,p):
    """
    properties : property_list property
    """
    p[0] = ('properties',p[1], p[2])


def p_property_list(self,p):
    """
    property_list : property_list property
                    | 
    """

    if(len(p)==3):
        p[0] = ('property_list',p[1],p[2])

def p_property(self,p):
    """
    property : PROPERTY_NAME property_content_list
    """
    p[0] = ('property', p[1], p[2])

输入

GM[1]AP[hello]AW[world]C[1.this is a comment]C[2.this is a comment]C[3.this is a comment]
TM[50]WR[alalala]GM[will it fail?]GM[should succeed]

结果

('properties', ('property_list', ('property_list', ('property_list', 
('property_list', ('property_list', ('property_list', 
('property_list',('property_list', ('property_list', None, 
('property', 'GM', ('property_content', '1'))),
('property', 'AP', ('property_content','hello'))),
('property', 'AW', ('property_content', 'world'))), 
('property', 'C', ('property_content', '1.this is a comment'))), 
('property', 'C', ('property_content', '2.this is a comment'))), 
('property', 'C', ('property_content', '3.this is a comment'))), 
('property', 'TM', ('property_content', '50'))), ('property', 'WR', 
('property_content', 'alalala'))), ('property', 'GM', 
('property_content','will it fail?'))),
('property', 'GM', ('property_content', 'should succeed')))

感谢您的帮助。

Loïc.

Ply 解析器的产品包含在文档字符串中,它是函数开头的第一个 字符串文字(如果有的话)。

因此 p_property_list 的文档字符串是 property_list:。 (您可以通过查看 p_property_list.__doc__ 来确认这一点。)这就是 Ply 看到的全部内容。如果您想为单个函数提供多个产品,请使用单个多行字符串文字,这就是为什么您通常会将原始字符串文字视为文档字符串:

def p_property_list(self, p):
    """property_list:
                    | property_list property
    """
    # ...

但是,在这种情况下使用两个不同的函数是很常见的。见下文。


除非必要,否则不要使用右递归。正确的语法是:

property_list: property
             | property_list property

property: PROPERTY_NAME single_property_content

如果愿意,您可以调用第一个产品 properties,但您不需要两个不同的非终端。 (您可能也不需要 single_property_content,但这取决于您的其余语法。)

在 Ply 中,不需要将归约函数与非终结符对齐。对于 reduce 操作不同的不同替代方案使用不同的函数是很常见的,并且您通常可以为不同的非终端组合相似的产生式。举个简单的例子:

# This function can be used as the base case of any list non-terminal
# whose semantic value is a list (and whose base case is not empty)
def p_list_base(self, p):
    """property_list: property
       function_list: function
    """
    p[0] = [p[1]]

# This function can be used as the recursion of any list non-terminal
# whose semantic value is a list.
def p_list_recur(self, p):
    """property_list: property_list property
       function_list: function_list function
    """
    p[1].append(p[2])
    p[0] = p[1]