我正在使用PLY创建一个计算器,并且希望能够将像这样的矩阵[[11,1];[22,4];[13,3]]
解析为列表列表,以提供给我自己的Matrix类,以进行进一步的计算。
到目前为止,这是我的代码。这里的三个重要功能是p_comma
,p_semicolon
和p_brack
。其余纯粹用于计算和优先级。
def p_operations(p):
""" expression : sixth
sixth : fifth
fifth : fourth
fourth : third
third : second
second : first
first : NUMBER
first : IMAGINE
"""
p[0] = p[1]
def p_comma(p):
""" sixth : sixth ',' fifth """
if isinstance(p[1], list):
p[1].append(p[3])
p[0] = p[1]
else:
p[0] = [p[1],p[3]]
def p_semicolon(p):
""" sixth : sixth ';' fifth """
if isinstance(p[1], list):
p[1].append(p[3])
p[0] = p[1]
else:
p[0] = [p[1],p[3]]
def p_plus(p):
""" fifth : fifth '+' fourth """
p[0] = p[1] + p[3]
def p_minus(p):
""" fifth : fifth '-' fourth """
p[0] = p[1] - p[3]
def p_implicit_times(p):
""" fourth : fourth second """
p[0] = p[1] * p[2]
def p_times(p):
""" fourth : fourth '*' third """
p[0] = p[1] * p[3]
def p_divide(p):
""" fourth : fourth '/' third """
p[0] = p[1] / p[3]
def p_modulo(p):
""" fourth : fourth '%' third """
p[0] = p[1] % p[3]
def p_floor_divide(p):
""" fourth : fourth FLOORDIV third """
p[0] = p[1] // p[3]
def p_unary_minus(p):
""" third : '-' third """
p[0] = -p[2]
def p_power(p):
""" second : first '^' third """
p[0] = p[1] ** p[3]
def p_paren(p):
""" first : '(' expression ')' """
p[0] = p[2]
def p_brack(p):
""" first : '[' expression ']' """
if type(p[2][0]) == list:
p[0] = [p[2]]
else:
p[0] = Matrix.Matrix(p[2])
这里的问题是我的解决方案不适用于诸如[[1]]
这样的棘手问题,而且即使没有括号,解析也能正常工作,这不是我想要的。
最重要的是,我坚信,有一种更好的解决方案可以找到。
有人可以帮我吗?
答案 0 :(得分:1)
并非所有事物都是expression
:-)
特别是,矩阵括号内的内容是用分号分隔的行的列表,每行是用逗号分隔的表达式的列表。您的语法应该反映出这个简单的事实,而不是仅仅将这些列表混入expression
非终结符中。否则,您会发现它在上下文之外接受分号和逗号分隔的列表。我想这是您提出问题的基础。
而且,正如我认为我们已经讨论的那样,如果您的操作功能需要进行测试,则可能表明您没有利用语法。确实是这种情况。
所以让我们从顶部开始。矩阵是用方括号括起来的行的列表,用分号分隔。换句话说:
matrix : '[' row_list ']'
row_list : row
| row_list ';' row
一行是用逗号分隔的值列表(现在为表达式),用方括号括起来:
row : '[' value_list ']'
value_list : expression
| value_list ',' expression
现在,我们可以编写动作函数了。这些也很简单。
def p_list_first(p):
"""value_list : expression
row_list : row
"""
p[0] = [ p[1] ]
def p_list_extend(p):
"""value_list : value_list ',' expression
row_list : row_list ';' row
"""
p[0] = p[1]
p[0].append(p[3])
# Another way of writing this action:
# p[0] = p[1] + [ p[3] ]
# That's cleaner, in that it doesn't modify the previous value.
# But it's less efficient because it creates a new list every time.
def p_row(p):
"""row : '[' value_list ']' """
p[0] = p[2]
def p_matrix(p):
"""matrix : '[' row_list ']' """
p[0] = Matrix.Matrix(p[2])
它将替换您的逗号,分号,小写和第六个规则。剩下的唯一事情就是添加first : matrix
。 (此外,p_row
操作与您的p_paren
操作相同,因此可以根据需要将它们组合在一起。)
两个要点:
如果可以用自己的母语描述语法,则可以为其编写语法。语法只是表达同一件事的一种更正式的方式。 (至少,一旦您不再被递归所吓倒,这并不复杂:“列表是一个值,或者您可以通过添加逗号和值来扩展列表”应该很容易理解。)
语法应该能够解析输入,而无需测试之前解析过的内容。
第二个原则不是绝对的。例如,在这种情况下,您可能希望禁止row
元素中嵌套matrix
中的值。您可以将其写为语法,但这会涉及到令人讨厌的重复。实际上,对于row_list
操作来说,验证他们正在处理的expression
不是Matrix
可能更简单。