Python PLY Parser — анализ матрицы как списка списков

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

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

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


person cuzureau    schedule 16.04.2020    source источник


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

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

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

person rici    schedule 16.04.2020
comment
Еще раз большое спасибо Ричи. если вы можете описать синтаксис на своем родном языке, вы можете написать для него и грамматику. = Я постараюсь напомнить об этом. Это легко понять, но не так просто применить. Не все является выражением = выражение является зарезервированным словом, верно? Сколько их? Почему в документах об этом ничего нет? - person cuzureau; 16.04.2020
comment
@cuzureau: Основная проблема при применении этого правила заключается в том, что мы иногда пытаемся описать синтаксис с точки зрения того, чем он не является. Это не очень хорошо переводится в грамматику, потому что CFG не закрыты по дополнению, и даже регулярные выражения (которые закрыты по дополнению) имеют тенденцию неуправляемо взрываться, когда вы пытаетесь их отрицать. Но в большинстве случаев синтаксис будет иметь простое описание на обычном языке, и это всегда хорошее начало. - person rici; 16.04.2020
comment
expression не является зарезервированным словом. (Есть только несколько слов, которые являются особенными для Ply. Единственные, которые приходят на ум, это error и eof.) То, что я имел в виду под не всем, является выражением, именно это: ваша грамматика пытается заставить списки вещей быть expressions но это не так (с точки зрения вашей грамматики), потому что вы действительно не хотите, чтобы списки без скобок можно было использовать, как если бы они были выражениями. Поэтому вам нужно разобрать их с помощью другого нетерминала. - person rici; 16.04.2020
comment
@cuzureau: я отредактировал второй абзац, чтобы сделать это утверждение более ясным. - person rici; 16.04.2020