Объединение лексера со многими парсерами

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

Но что, если мне нужны два разных парсера поверх одного и того же лексера?

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

Но я могу вызвать только одну функцию верхнего уровня в одном из этих парсеров; не могу вызвать оба одновременно :/

Есть ли способ запустить эти парсеры в каком-то пошаговом режиме? То есть, когда у меня есть новый токен, я хочу передать его обоим парсерам один за другим, но только для того, чтобы продвинуть их на этот один токен, обновить их внутренние состояния и структуры данных, насколько они могут, и немедленно вернуться ждать другого токена.

Я никогда раньше не видел такой конфигурации. Можно ли вообще построить парсер таким образом? Есть какие-то материалы о том, как такой парсер можно структурировать в коде? У него есть какое-то имя?

РЕДАКТИРОВАТЬ 1: я не хочу использовать какой-либо генератор синтаксических анализаторов, а пишу код сам, потому что я хочу узнать, как такие вещи работают внутри.


person SasQ    schedule 22.02.2011    source источник
comment
Вы должны говорить о какой-то конкретной реализации (например, Lex/Yacc); если бы вы реализовывали свой собственный, вы могли бы просто спроектировать его с учетом этого. Так о какой реализации вы говорите?   -  person Oliver Charlesworth    schedule 22.02.2011
comment
Без использования какого-либо генератора парсеров. Написание собственного. Но я не знаю, как должен быть структурирован код, чтобы он был хорошим.   -  person SasQ    schedule 23.02.2011
comment
И в чем проблема? Пусть ваш лексер возвращает ленивый список, и оба ваших парсера будут использовать его. Вы можете складывать столько парсеров и преобразователей, сколько хотите (и лексер не должен ничем отличаться от других парсеров).   -  person SK-logic    schedule 23.02.2011
comment
Проблема не в лексере, а в парсере. Как написать код, чтобы его можно было использовать пошагово, а не один вызов сверху вниз? Является ли синтаксический анализатор push все еще синтаксическим анализатором сверху вниз? Или какое-то другое животное? (снизу вверх?)   -  person SasQ    schedule 23.02.2011


Ответы (1)


Вы описали типичный поток парсера pull. Он вызывается один раз и берет на себя управление до тех пор, пока все его входные данные не будут полностью проанализированы. Парсер сам вызывает лексер, чтобы получить следующий токен. С другой стороны, push-парсер вызывается каждый раз, когда становится доступным новый токен. Таким образом, вы можете вызывать несколько парсеров для каждого нового токена. Классический Bison можно использовать в режиме push (подробности здесь). Генератор парсеров Lemon генерирует парсеры push.

person Artem Zankovich    schedule 22.02.2011
comment
Спасибо. Теперь хоть терминологию знаю. Так что теперь мне интересно, как работает парсер push. Я не использую генератор синтаксических анализаторов, так как хочу понять технику его внутренней работы. - person SasQ; 23.02.2011