Проблемы с передачей ключевых слов парсеру - PullRequest
0 голосов
/ 05 июля 2019

Я уже некоторое время гуглю, и с здесь (благодарность мистеру Полу) я узнал, как передать идентификаторы парсеру. Вот что у меня пока так:

from pyparsing import *

class Expressions:
    ParserElement.enablePackrat()

    arith_expr = Forward()

    num = Word(nums) + Optional("." + OneOrMore(Word(nums)))
    opmd = Word("*/", max=1)
    opss = Word("+-", max=1)
    ident = Word(alphas + "_", alphanums + "_")

    fn_call = Group(Optional(delimitedList(arith_expr)))
    arith_operand = fn_call | num | ident

    arith_expr <<= infixNotation(arith_operand, [
        ('-', 1, opAssoc.RIGHT),
        (opmd, 2, opAssoc.LEFT,),
        (opss, 2, opAssoc.LEFT,)
    ])

    def __init__(self, vars):
        if isinstance(vars, list) and vars:
            ids = []
            for x in vars:
                ids.append(x)
            self.ident = MatchFirst(map(Keyword, ids))

    def check(text):
        try:
            result = self.arith_expr.parseString(texto, True)
            print(result)
        except ParseException as exc:
            print(exc)

Тогда, если я иду к консоли Python и делаю это:

vars = ['v1', 'v2', 'v3,1']
e = Expressions(vars)
e.check('10+v1+v2+v3,1-whatever')

печатает whatever как правильный токен, несмотря на то, что он не определен в vars. Как я могу решить это?

1 Ответ

1 голос
/ 06 июля 2019

Переменная ident, определенная в классе Expressions, не является заполнителем, поэтому, когда вы присваиваете ее в своем методе __init__, вы не меняете общий анализатор, а просто определение self.ident (которое создает новый атрибут в экземпляре Expressions и не изменяет идентификатор уровня класса).

Почему бы просто не определить весь синтаксический анализатор в __init__? Затем вы можете определить ident, используя заданные имена переменных, и обойти все проблемы атрибутов class-vs-instance и проблемы update-part-of-a-parser-after-the-fact.

А что этот код должен делать?

        ids = []
        for x in vars:
            ids.append(x)

Существуют гораздо более простые способы копирования значений из одного списка в другой, но почему вы даже делаете копию? Просто определите ident, используя входной список имен переменных (который вы могли бы назвать чем-то, кроме vars, поскольку это противоречит полезному встроенному методу - возможно, вызовите его var_names?).

РЕДАКТИРОВАТЬ: еще несколько заметок

Вам нужно исправить fn_call. Как таковой, у вас будет бесконечная рекурсия, потому что все это список разделенных запятыми arith_exprs. Поскольку вы определяете arith_expr с помощью fn_call, существует рекурсия влево. Я думаю, что вы не полностью скопировали это из другого примера, у вас есть действительное выражение для списка аргументов, которые есть в списке аргументов функции, но вам не хватает имени функции и входящих в нее паренов. Добавьте их, и проблема рекурсии исчезнет.

Один из ваших вариантов - v_3,1. Это странно выглядящий идентификатор, но, к счастью, он не конфликтует ни с какими другими битами в вашем парсере. Но если вы отправите идентификатор «3.1» или «42», все будет очень запутанно. Возможно, вы захотите определить выражение valid_identifier, а затем проверить входящие имена переменных с помощью чего-то вроде:

valid_expression = Word(alphas + '_', alphanums + '_,')
if not all(valid_expression.matches(varname) for varname in varnames):
    raise WhatWereYouThinkingException("invalid identifier specified")
...