У вас есть ответ на ваш вопрос в том виде, в котором вы его задали.
Вам необходимо сохранить состояние . Сложная часть - идентификация государства. Сохранение это просто.
Ваша проблема в том, что синтаксический анализатор "имеет состояние", когда он начинает синтаксический анализ некоторого правила грамматики. (Это усложняется, если вы используете парсер LALR, который объединяет разбор многих правил в одно состояние). Это состояние состоит из:
- состояние входа (например, где находится поток ввода?).
- состояние стека разбора (какой левый контекст виден до сих пор?)
- где анализатор должен продолжить работу в случае успеха, а где продолжить работу в случае ошибки
Когда вы анализируете и сталкиваетесь с альтернативой выбора, как вы описали, вам нужно «сохранить состояние», запустить пробный анализ первого термина. В случае успеха вы можете выбросить сохраненное состояние и продолжить. В случае неудачи восстановите состояние и попробуйте второй (и n-й варианты). (Легче быть безмозглым и просто сохранять состояние независимо от того, сталкиваешься ли ты с альтернативой, но решать тебе).
Как вы можете сохранить состояние? Вставьте его в стопку. (Обычно у вас есть стек разбора, это довольно удобное место! Если вам это не нравится, добавьте еще один стек, но вы обнаружите его, и стек разбора в целом перемещается синхронно; я просто заставляю стек разбора содержать запись с все, что мне нужно, включая пространство для ввода. И вы найдете «стек вызовов», удобный для частей состояния (см. ниже).
Первым делом нужно сохранить местоположение input ; это, вероятно, позиция источника файла и по причинам оптимизации, вероятно, индекс буфера. Это просто скаляр, поэтому его довольно легко сохранить. Восстановление Входной поток может быть сложнее; нет никакой гарантии, что сканер входного синтаксического анализатора находится где-то рядом, где он был. Таким образом, вам нужно переместить файл, перечитать любой буфер и переместить любой указатель входного буфера. Некоторые простые проверки могут сделать это статистически дешевым: сохранить позицию файла первого символа любого буфера; затем определение необходимости повторного чтения буфера - это вопрос сравнения позиции сохраненного файла с позицией начального файла буфера. Остальное должно быть очевидным.
Вы вернетесь назад через меньшее количество буферов (например, ваш парсер будет работать быстрее), если вы измените свою грамматику, чтобы минимизировать это. В вашей конкретной грамматике у вас есть «(a | ab) c», который можно переписать вручную в «a b? C». Последний, по крайней мере, не будет возвращаться к тому, что представляет собой.
Нечетная часть сохраняет стек разбора. Что ж, вам не нужно этого делать, потому что ваш пробный анализ будет только расширять имеющийся у вас стек разбора и восстанавливать его до состояния разбора, независимо от того, будет ли ваш подпроцесс успешным или неудачным.
"где анализатор выходит из строя при сбое" и "где он проходит в случае успеха" - это всего лишь два скаляра. Вы можете представить их в виде индексов блоков кода синтаксического анализа и счетчиков программ (например, продолжений) или в качестве адреса возврата в стеке вызовов (см. «Другой параллельный стек!»), За которым следует условный тест на успех / сбой.
Если вам нужны подробности о последнем, посмотрите мой SO ответ о парсерах рекурсивного спуска с ручным кодированием.
Если вы начнете строить деревья или делать что-то еще в качестве побочного эффекта при разборе, вам придется выяснить, как захватить / сохранить состояние побочной сущности и восстановить ее. Но что бы это ни было, вы положите его в стек.