Python PLY 解析器 - 将矩阵解析为列表列表

Python PLY Parser - Parsing Matrix as List of Lists

我正在使用 PLY 创建一个计算器,我希望能够解析像这样的矩阵:[[11,1];[22,4];[13,3]] 到列表列表中,以便进一步提供给我自己的矩阵 class计算。

到目前为止,这是我的代码。这里的三个重要函数是p_commap_semicolonp_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]]而且即使没有括号,解析也能正常工作,这不是我想要的。

最重要的是,我坚信可以找到更好的解决方案。

有人可以帮我吗?

并不是所有的东西都是 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 操作相同,因此如果您愿意,可以将它们合并。)

两个要点:

  1. 如果你能用你自己的母语描述语法,你可以为它写一个语法。语法只是表达同一事物的一种更正式的方式。 (至少,一旦你不再被递归吓倒,递归也不复杂:"a list is a value or you can extend a list by adding a comma and a value" 应该很容易理解。)

  2. 语法应该能够解析输入而无需测试之前解析的内容。

第二个原则不是绝对的。例如,在这种情况下,您可能希望禁止 row 中的值嵌套在 matrix 元素中。你可以把它写成一个语法,但它会涉及大量的重复; row_list 操作验证它们正在处理的 expression 不是 Matrix.

实际上可能更简单