Ваш подход к обработке отступов в лексере, а не в парсере, верен. Ну, это выполнимо в любом случае, но обычно это более простой способ, и именно так это делает сам 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.