Сигнализация об ошибке из правила парсера в PLY - PullRequest
0 голосов
/ 11 июня 2018

Я использую PLY для анализа команд для файла пользовательского определения.Команды определяются по одной в строке, и каждая из них должна начинаться с зарезервированного ключевого слова, за которым следует ряд строк.Мне успешно удалось написать лексер и парсер для грамматики, но у меня возникают проблемы с поднятием SyntaxError из производственного процесса.

Согласно документации PLY , это возможно простовыбрасывая SyntaxError из тела правила синтаксического анализатора:

При необходимости, производственное правило может вручную принудительно заставить анализатор ввести восстановление после ошибки.Это делается путем вызова исключения SyntaxError следующим образом:

def p_production(p):
    'production : some production ...'
    raise SyntaxError

Мой код вызывает SyntaxError в производственном процессе, когда он сталкивается с недопустимым синтаксисом, но при запуске программы эта ошибка не возникает,Вот минимальный рабочий пример:

from ply import lex, yacc

class Parser(object):
    # reserved keyword tokens
    reserved = {
        "r": "R"
    }

    # top level tokens
    tokens = [
        'CHUNK',
        'NEWLINE'
    ]

    # add reserved tokens
    tokens += reserved.values()

    # ignore spaces and tabs
    t_ignore = ' \t'

    def __init__(self):
        # lexer and parser handlers
        self.lexer = lex.lex(module=self)
        self.parser = yacc.yacc(module=self)

    def parse(self, text):
        # pass text to yacc
        self.parser.parse(text, lexer=self.lexer)

    # detect new lines
    def t_newline(self, t):
        r'\n+'
        # generate newline token
        t.type = "NEWLINE"
        return t

    def t_CHUNK(self, t):
        r'[a-zA-Z0-9_=.:]+'
        # check if chunk is a keyword
        t.type = self.reserved.get(t.value.lower(), 'CHUNK')
        return t

    def t_error(self, t):
        raise SyntaxError("token error")

    def p_instruction_list(self, p):
        '''instruction_list : instruction
                            | instruction_list instruction'''
        pass

    # match instruction on their own lines
    def p_instruction(self, p):
        '''instruction : command NEWLINE
                       | NEWLINE'''
        pass

    def p_command(self, p):
        '''command : R CHUNK CHUNK CHUNK CHUNK'''
        # parse command
        if p[2] not in ["a", "b"]:
            raise SyntaxError("invalid thing")

    def p_error(self, p):
        raise SyntaxError("parsing error")

if __name__ == "__main__":
    parser = Parser()
    parser.parse("""
    r a text text text
    r c text text text
    r b text text text
    """)

Вышеприведенный пример работает без вывода чего-либо, что означает, что он успешно проанализировал текст, даже если синтаксическая ошибка должна быть вызвана в p_command из-за строки r c text text text (второй токен c недействителен; допустимо только a или b).

Что я делаю не так?

1 Ответ

0 голосов
/ 11 июня 2018

Вы отвечаете за печать сообщений об ошибках, и вы не делаете:

Одним из важных аспектов ручной установки ошибки является то, что функция p_error() будет НЕ называется в этом случае.Если вам нужно выдать сообщение об ошибке, убедитесь, что вы делаете это в производственной среде, которая вызывает SyntaxError.

Я не верю, что p_error() должно повысить SyntaxError.Следует просто напечатать соответствующее сообщение (или иным образом зарегистрировать факт возникновения ошибки) и разрешить восстановление после ошибки.Но в любом случае, это не вызывается в этом случае, как указано в приведенной выше цитате.

Я не на 100% убежден в том, что лексер повысит SyntaxError.Моя предпочтительная стратегия для лексических ошибок - просто передавать их анализатору и таким образом централизовать обработку ошибок в одном месте.

Если вам не нужно исправлять ошибки, не используйте токен error влюбое правило.Этот токен используется только для восстановления после ошибок.Если вы просто хотите вызвать исключение, как только возникает ошибка, сделайте это в p_error и вызовите p_error явно в тех местах, где оно не будет вызываться автоматически (например, ошибки токена и ошибки, обнаруженные в семантических действиях),Вы можете выбросить ValueError или что-то полученное из него;Я бы держался подальше от SyntaxError, который имеет особое значение для Ply и Python в целом.

...