使用 PLY python 将逗号解析为运算符时出现歧义
ambiguity in parsing comma as a operator using PLY python
我有以下标记以及更多标记,但我想保持我的问题简短,这就是为什么不包括整个代码的原因。
tokens = (
'COMMA',
'OP',
'FUNC1',
'FUNC2'
)
def t_OP(t):
r'&|-|\||,'
return t
def t_FUNC1(t):
r'FUNC1'
return t
def t_FUNC2(t):
r'FUNC2'
return t
其他方法:
def FUNC1(param):
return {'a','b','c','d'}
def FUNC2(param,expression_result):
return {'a','b','c','d'}
我在 YACC 中的语法规则是,还有一些,但列出了重要的规则:
'expression : expression OP expression'
'expression : LPAREN expression RPAREN'
'expression : FUNC1 LPAREN PARAM RPAREN'
'expression : FUNC2 LPAREN PARAM COMMA expression RPAREN'
'expression : SET_ITEM'
在我的yacc.py中,以下是与问题相关的方法:
def p_expr_op_expr(p):
'expression : expression OP expression'
if p[2] == '|' or p[2]== ',':
p[0] = p[1] | p[3]
elif p[2] == '&':
p[0] = p[1] & p[3]
elif p[2] == '-':
p[0] = p[1] - p[3]
def p_expr_func1(p):
'expression : FUNC1 LPAREN PARAM RPAREN'
Param = p[3]
Result = ANY(Param)
p[0] = Result
def p_expr_func2(p):
'expression : FUNC2 LPAREN PARAM COMMA expression RPAREN'
Param = p[3]
expression_result = p[5]
Result = EXPAND(Param,expression_result)
p[0] = Result
def p_expr_set_item(p):
'expression : SET_ITEM'
p[0] = {p[1]}
所以,问题是:
如果我为这个语法给出以下输入表达式:
FUNC1("foo"),bar
-- 它正常工作,并给我结果作为 FUNC1("foo") and bar => {a,b,c,d} | {bar}
返回的 SET 的 UNION
但是,如果我给出下面的输入表达式,它会在 , 和 处给出语法错误):
我将括号定义为标记(对于那些认为括号未在标记中定义的人)
FUNC2("foo", FUNC1("foo"),bar)
根据我的说法,这个表达式符合生产规则 'expression : FUNC2 LPAREN PARAM COMMA expression RPAREN'
所以第一个逗号之后的所有内容都应该被很好地视为表达式,它应该匹配 'expression : expression OP expression'
并在遇到逗号作为运算符时执行并集。
如果是这样,那么它也不适用于 FUNC1("foo"),bar
。
我知道我可以通过从 t_OP(t) 中删除 ',' 并添加一个生产规则作为 'expression : expression COMMA expression'
来解决这个问题,这个规则的方法将如下所示:
def p_expr_comma_expr(p):
'expression : expression COMMA expression'
p[0] = p[1] | p[3]
我不愿意包含这条规则,因为它会引入“4 shift/reduce 冲突”。
我真的很想了解为什么它在一种情况下执行,为什么在另一种情况下不执行,以及将“,”视为运算符的方式是什么?
谢谢
Ply 无法知道您希望给定的 、 成为词素 COMMA
还是词素 OP
。或者,更确切地说,它有一种方法,但它总是会选择相同的方法:OP
。这是因为标记函数中的模式在模式变量中的标记之前被测试。
我假设您在程序的某个地方有 t_COMMA = r','
您没有提供的部分。也有可能你有一个 token 函数来识别 COMMA
,在这种情况下,哪个函数先出现就赢。但是不管你怎么做,测试正则表达式的顺序是固定的,所以 、 总是 COMMA
或者总是 OP
。 Specification of Tokens.
的 Ply 手册部分对此进行了很好的解释
就我个人而言,我建议从 OP
中删除逗号并修改语法以在 expression
的定义中使用 COMMA
。正如您所观察到的,您将遇到 shift-reduce 冲突,因此您必须将其包含在您的优先声明中(您也选择从您的问题中省略)。事实上,您可能希望不同的运算符具有不同的优先级,因此您可能希望将不同的运算符分成不同的标记,因为优先级取决于标记。请参阅 precedence declarations.
的 Ply 手册部分中的解释
再添加一条规则解决了我的问题:
expression:expression COMMA expression
添加是因为正如@rici 所说,在像 FUNC2("hello",FUNC1("ghost"))
这样的表达式中,第一个逗号总是被用作运算符。
并添加优先事项删除 4shift/减少冲突。
precedence = (
('left','COMMA'),
('left','OP')
)
我有以下标记以及更多标记,但我想保持我的问题简短,这就是为什么不包括整个代码的原因。
tokens = (
'COMMA',
'OP',
'FUNC1',
'FUNC2'
)
def t_OP(t):
r'&|-|\||,'
return t
def t_FUNC1(t):
r'FUNC1'
return t
def t_FUNC2(t):
r'FUNC2'
return t
其他方法:
def FUNC1(param):
return {'a','b','c','d'}
def FUNC2(param,expression_result):
return {'a','b','c','d'}
我在 YACC 中的语法规则是,还有一些,但列出了重要的规则:
'expression : expression OP expression'
'expression : LPAREN expression RPAREN'
'expression : FUNC1 LPAREN PARAM RPAREN'
'expression : FUNC2 LPAREN PARAM COMMA expression RPAREN'
'expression : SET_ITEM'
在我的yacc.py中,以下是与问题相关的方法:
def p_expr_op_expr(p):
'expression : expression OP expression'
if p[2] == '|' or p[2]== ',':
p[0] = p[1] | p[3]
elif p[2] == '&':
p[0] = p[1] & p[3]
elif p[2] == '-':
p[0] = p[1] - p[3]
def p_expr_func1(p):
'expression : FUNC1 LPAREN PARAM RPAREN'
Param = p[3]
Result = ANY(Param)
p[0] = Result
def p_expr_func2(p):
'expression : FUNC2 LPAREN PARAM COMMA expression RPAREN'
Param = p[3]
expression_result = p[5]
Result = EXPAND(Param,expression_result)
p[0] = Result
def p_expr_set_item(p):
'expression : SET_ITEM'
p[0] = {p[1]}
所以,问题是:
如果我为这个语法给出以下输入表达式:
FUNC1("foo"),bar
-- 它正常工作,并给我结果作为 FUNC1("foo") and bar => {a,b,c,d} | {bar}
但是,如果我给出下面的输入表达式,它会在 , 和 处给出语法错误): 我将括号定义为标记(对于那些认为括号未在标记中定义的人)
FUNC2("foo", FUNC1("foo"),bar)
根据我的说法,这个表达式符合生产规则 'expression : FUNC2 LPAREN PARAM COMMA expression RPAREN'
所以第一个逗号之后的所有内容都应该被很好地视为表达式,它应该匹配 'expression : expression OP expression'
并在遇到逗号作为运算符时执行并集。
如果是这样,那么它也不适用于 FUNC1("foo"),bar
。
我知道我可以通过从 t_OP(t) 中删除 ',' 并添加一个生产规则作为 'expression : expression COMMA expression'
来解决这个问题,这个规则的方法将如下所示:
def p_expr_comma_expr(p):
'expression : expression COMMA expression'
p[0] = p[1] | p[3]
我不愿意包含这条规则,因为它会引入“4 shift/reduce 冲突”。
我真的很想了解为什么它在一种情况下执行,为什么在另一种情况下不执行,以及将“,”视为运算符的方式是什么?
谢谢
Ply 无法知道您希望给定的 、 成为词素 COMMA
还是词素 OP
。或者,更确切地说,它有一种方法,但它总是会选择相同的方法:OP
。这是因为标记函数中的模式在模式变量中的标记之前被测试。
我假设您在程序的某个地方有 t_COMMA = r','
您没有提供的部分。也有可能你有一个 token 函数来识别 COMMA
,在这种情况下,哪个函数先出现就赢。但是不管你怎么做,测试正则表达式的顺序是固定的,所以 、 总是 COMMA
或者总是 OP
。 Specification of Tokens.
就我个人而言,我建议从 OP
中删除逗号并修改语法以在 expression
的定义中使用 COMMA
。正如您所观察到的,您将遇到 shift-reduce 冲突,因此您必须将其包含在您的优先声明中(您也选择从您的问题中省略)。事实上,您可能希望不同的运算符具有不同的优先级,因此您可能希望将不同的运算符分成不同的标记,因为优先级取决于标记。请参阅 precedence declarations.
再添加一条规则解决了我的问题:
expression:expression COMMA expression
添加是因为正如@rici 所说,在像 FUNC2("hello",FUNC1("ghost"))
这样的表达式中,第一个逗号总是被用作运算符。
并添加优先事项删除 4shift/减少冲突。
precedence = (
('left','COMMA'),
('left','OP')
)