• Как создать алгоритм синтаксического анализа списка токенов?

    Vindicar
    @Vindicar
    RTFM!
    Ну для начала, ты неправильно ставишь цель. После синтаксического разбора у тебя будет не список, а древовидная структура данных, а итоговый код будет результатом обхода этой структуры в глубину.
    Так что читай про восходящий алгоритм. Идея у него простая. У тебя должен быть набор правил твоей грамматики, например:
    ЦИКЛ_WHILE = 'while' '(' ВЫРАЖЕНИЕ ')' ОПЕРАТОР
    Заглавными я записал нетерминальные символы - что-то, что раскрывается далее в последовательность токенов.
    Остальное - терминальные символы, т.е. токены.
    Итак, у тебя есть цепочка символов - терминальных или нетерминальных. Нетерминальные хранят в себе кусочек дерева программы, терминальные хранят в себе один токен.
    1. Перебираешь правила, ищешь правило, соответствующее текущей позиции в цепочке. Для начала цепочки это будет правило "программа в целом".
    2. Строишь узел дерева, соответствующий найденному правилу.
    3. Помещаешь в этот узел терминальные символы, входящие в правило. В примере выше это будут 'while' и '('. Сдвигаешь позицию в цепочке.
    4. Если в правиле дальше идёт нетерминальный символ, рекурсивно обрабатываешь цепочку по правилу для этого символа, начиная с текущей позиции. Когда обработка закончится, у тебя позиция в цепочке сместится на конец последовательности символов для этого нетерминала, и можно будет продолжить для следующего символа в правиле.
    5. Когда правило закончилось - возвращаешь полученное мини-дерево, чтобы вышележащий код его встроил в свой узел дерева.
    6. Если есть альтернативы, сохраняешь текущую позицию в цепочке и пробуешь альтернативы по очереди. Если альтернатива не сработала (правило тут не подходит), то возвращаешься к сохранённой позиции и пробуешь уже с другой альтернативой.
    7. Если в какой-то момент ни одно правило не подошло, или если мы получили неожиданный терминал, есть синтаксическая ошибка в коде.

    Это сравнительно легко реализуется через косвенную рекурсию. Т.е. у тебя в парсере будет много методов nfrjuj вида (псевдокод!):
    def parse_while_node(tokens: List[Token], pos: int) -> TreeNode, int:
        """Принимает список токенов и позицию в нём, возвращает мини-дерево и новую позицию в списке."""
        node = WhileNode() # узел дерева разбора, соответствующий циклу while
        assert tokens[pos].type == 'while'  # проверяем фиксированный токен, выкидываешь исключение если это не он
        pos += 1
        assert tokens[pos].type == '('
        pos += 1
        # обращаемся к другому правилу для разбора нетерминального символа ВЫРАЖЕНИЕ
        # сохраняем мини-дерево для выражения условия и обновляем позицию
        node.condition, pos = parse_expression_node(tokens, pos)
        assert tokens[pos].type == ')'
        pos += 1
        # аналогично парсим нетерминал ОПЕРАТОР
        node.body, pos = parse_operator_node(tokens, pos)
        # возвращаем вызвавшему нас коду наше минидерево с узлом while на вершине,
        # а также позицию где мы остановились
        return node, pos

    А для реализации правила вида
    ОПЕРАТОР = ПРИСВАИВАНИЕ | ВЕТВЛЕНИЕ | ЦИКЛ_WHILE
    можно будет сделать так:
    def parse_operator_node(tokens: List[Token], pos: int) -> TreeNode, int:
        variants = [parse_assignment_node, parse_if_node, parse_while_node]
        for variant in variants:
            try: # пробуем вариант из ветвления
                node, pos = variant(tokens, pos)
            except:
                pass # вариант не подошёл - пробуем другой
            else: # вариант подошёл
                return node, pos
        else: # ни один вариант не подошёл
            raise Exception()
    Ответ написан
    1 комментарий
  • Как заставить telebot пересылать сообщения других ботов?

    @twistfire92
    Python backend developer
    Вроде как боты не умеют общаться друг с другом, телегой это не предусмотрено
    Ответ написан
    Комментировать