Python PLY Parser - Матрица синтаксического анализа как список списков - PullRequest
0 голосов
/ 16 апреля 2020

Я создаю калькулятор, используя 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]], а также анализ работает, даже когда есть без скобок, а это не то, чего я хочу.

Больше всего я твердо верю, что можно найти лучшее решение.

Может ли кто-нибудь помочь мне с этим?

1 Ответ

1 голос
/ 16 апреля 2020

Не все является 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. если вы можете описать синтаксис на своем родном языке, вы можете написать для него грамматику. Грамматика - это просто более формальный способ сказать то же самое. (По крайней мере, после того, как вы перестанете запугиваться рекурсией, что тоже не сложно: «список - это значение, или вы можете расширить список, добавив запятую и значение», это будет довольно легко понять.)

  2. Грамматика должна иметь возможность анализировать входные данные без проверки того, что было проанализировано ранее.

Этот второй принцип не является абсолютным. В этом случае, например, вы можете запретить вложенным элементам matrix значения в row. Вы могли бы написать это как грамматику, но это будет включать раздражающее количество дублирования; для действий row_list может быть проще проверить, что expression, который они обрабатывают, не является Matrix.

...