Разбор блоков как Python - PullRequest
0 голосов
/ 01 мая 2018

Я пишу парсер лексера + в JFlex + CUP, и я хотел иметь подобный Python синтаксис для блоков; то есть отступ отмечает уровень блока.

Я не уверен, как с этим справиться, и нужно ли это делать на лексическом или синтаксическом уровне.

Мой текущий подход состоит в том, чтобы решить проблему на лексическом уровне - символы новой строки анализируются как разделители инструкций, и когда один из них обрабатывается, я перемещаю лексер в специальное состояние, которое проверяет, сколько символов находится перед новой строкой, и запоминает в каком столбце началась последняя строка, и соответственно вводит и открывает блок или закрывает блок символа.

Однако я сталкиваюсь со всеми проблемами. Например:

  1. JFlex не может соответствовать пустым строкам, поэтому в моих инструкциях должен быть хотя бы один пробел после каждой новой строки.
  2. При таком подходе я не могу закрыть два блока одновременно.

Мой подход правильный? Должен ли я делать что-то другое?

1 Ответ

0 голосов
/ 01 мая 2018

Ваш подход к обработке отступов в лексере, а не в парсере, верен. Ну, это выполнимо в любом случае, но обычно это более простой способ, и именно так это делает сам Python (или, по крайней мере, CPython и PyPy).

Я не знаю много о JFlex, и вы не дали нам никакого кода для работы, но я могу объяснить в общих чертах.

Для вашей первой проблемы вы уже переводите лексер в специальное состояние после новой строки, так что «захватить 0 или более пробелов» должно быть выполнимо путем выхода из обычного потока вещей и просто запустив регулярное выражение для линия.

Для вашей второй проблемы самое простое решение (и то, которое использует Python) - сохранить стек отступов. Я продемонстрирую кое-что более простое, чем то, что делает Python.

Первый:

indents = [0]

После каждого перехода на новую строку вводите 0 или более пробелов как spaces. Тогда:

if len(spaces) == indents[-1]:
    pass
elif len(spaces) > indents[-1]:
    indents.append(len(spaces))
    emit(INDENT_TOKEN)
else:
    while len(spaces) != indents[-1]:
        indents.pop()
        emit(DEDENT_TOKEN)

Теперь ваш парсер просто видит INDENT_TOKEN и DEDENT_TOKEN, которые ничем не отличаются, скажем, от OPEN_BRACE_TOKEN и CLOSE_BRACE_TOKEN в языке, подобном Си.

Если вам нужна лучшая обработка ошибок - поднимите какую-нибудь ошибку токенизатора, а не неявную IndexError, возможно, используйте < вместо !=, чтобы вы могли обнаружить, что вы зашли слишком далеко вместо исчерпания стек (для лучшего восстановления после ошибок, если вы хотите продолжать выдавать дальнейшие ошибки вместо загрузки первой) и т. д.

Пример реального кода кода (с обработкой ошибок, табуляцией, пробелами, экранированием обратной косой черты и обработкой несинтаксического отступа внутри выражений в скобках и т. Д.) См. tokenize документы и источник в stdlib.

...