Как я могу создать абстрактное синтаксическое дерево с учетом «|»? (Слой/Якк)

Учитывая следующую грамматику:

expr : expr '+' term | expr '-' term | term
term : term '*' factor | term '/' factor | factor
factor : '(' expr ')' | identifier | number

Это мой код с использованием слоя:

from ply import lex, yacc

tokens = [
    "identifier",
    "number",
    "plus",
    "minus",
    "mult",
    "div"
]

t_ignore = r" \t"
t_identifier = r"^[a-zA-Z]+$"
t_number = r"[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?"
t_plus = r"\+"
t_minus = r"-"
t_mult = r"\*"
t_div = r"/"

def p_stmt(p):
    """stmt : expr"""
    p[0] = ("stmt", p[1])

def p_expr(p):
    """expr : expr plus term 
            | expr minus term 
            | term"""
    p[0] = ("expr", p[1], p[2]) # Problem here <<<

def p_term(p):
    """term : term mult factor 
            | term div factor 
            | factor"""

def p_factor(p):
    """factor : '(' expr ')' 
              | identifier 
              | number"""


if __name__ == "__main__":
    lex.lex()
    yacc.yacc()
    data = "32 + 10"
    result = yacc.parse(data)
    print(result)

Как я должен построить AST с выражением, если я не могу получить доступ к операторам? Я мог бы разделить функции, такие как p_expr_plus, но в этом случае я бы исключил приоритет операций. документы не очень полезны, так как я новичок и не могу решить эту проблему проблема. Лучший материал, который я нашел на эту тему, это, но он не рассматривает сложность приоритета операторов.

РЕДАКТИРОВАТЬ: я не могу получить доступ к p2 или p[3], так как я получаю IndexError (соответствует только термину). В PDF-файле, на который я ссылаюсь, они явно помещают оператор внутри кортежа, например: ('+', p1, p2), и, таким образом, демонстрируя мою проблему с учетом приоритет (я не могу разделить функции, выражение есть выражение, должен быть способ рассмотреть каналы и получить доступ к любому оператору).


person Ericson Willians    schedule 04.08.2016    source источник
comment
Я не понимаю, почему вы считаете, что не можете разделить функции из-за приоритета. Проблем с приоритетом нет. На самом деле вы не используете приоритет; грамматика однозначна, и ей присущ приоритет операций. Разделение нетерминала между двумя разными функциями действия не меняет грамматику и приводит к более простым действиям.   -  person rici    schedule 05.08.2016


Ответы (1)


Насколько я понимаю, в p[0] = ("expr", p[1], p[2]) p[1] будет левым выражением, p[2] будет оператором, а p[3] (которое вы не используете) будет правым термином.

Просто используйте p[2] для определения оператора, добавьте p[3], так как он вам понадобится, и все будет готово.

Кроме того, вы должны проверить, сколько элементов есть у p, поскольку, если последнее правило, | term""", соответствует, p будет иметь только два элемента вместо четырех.

Взгляните на фрагмент из примера GardenSnake:

def p_comparison(p):
    """comparison : comparison PLUS comparison
                  | comparison MINUS comparison
                  | comparison MULT comparison
                  | comparison DIV comparison
                  | comparison LT comparison
                  | comparison EQ comparison
                  | comparison GT comparison
                  | PLUS comparison
                  | MINUS comparison
                  | power"""
    if len(p) == 4:
        p[0] = binary_ops[p[2]]((p[1], p[3]))
    elif len(p) == 3:
        p[0] = unary_ops[p[1]](p[2])
    else:
        p[0] = p[1]
person Haroldo_OK    schedule 04.08.2016
comment
Проблема в том, что когда я использую p[3], я получаю индекс списка за пределами допустимого диапазона. Оператор не считается. В приведенном мной PDF-файле они явно сохраняют оператор: ('+', p[1], p[2]). Проблема в том, что это может быть любой оператор, и мне нужно учитывать приоритет. - person Ericson Willians; 04.08.2016
comment
О верно. Это должно быть из-за последней строки, | term""": при совпадении этого последнего правила p будет только два элемента вместо четырех. - person Haroldo_OK; 04.08.2016
comment
Это странно, поскольку 32 + 10 должно соответствовать выражению плюс термин, поскольку и выражение, и термин в конечном итоге являются числом. - person Ericson Willians; 04.08.2016
comment
Изначально да, но обратите внимание, что правило является рекурсивным, поэтому expr будет соответствовать снова, для 32, что соответствует последнему правилу. Я бы порекомендовал поставить точку останова или лог при запуске функции, чтобы вы могли лучше понять, что я имею в виду. - person Haroldo_OK; 04.08.2016
comment
О, теперь я вижу на вашем примере ... Я видел конец рекурсии (настолько очевидно, что я как-то забыл там основной принцип). Благодарю вас! - person Ericson Willians; 04.08.2016