Проблема в том, что ваша грамматика рекурсивна .PetitParser использует жадный алгоритм сверху вниз для разбора входной строки.Если вы выполните шаги, вы увидите, что они начинаются с start
, а затем variable -> component -> indexed -> variable
.Это становится циклом, который выполняется бесконечно, не потребляя любого ввода, и является причиной переполнения стека (это практическая левая рекурсивность).
Трюк для решенияСитуация состоит в том, чтобы переписать синтаксический анализатор, добавив промежуточные шаги, чтобы избежать повторного использования.Основная идея заключается в том, что переписанная версия будет использовать как минимум один символ в каждом цикле.Давайте начнем с того, что немного упростим анализатор, рефакторинг нерекурсивных частей «indexed» и «field» и переместим их на дно.
variable
^component, self identifier
component
^indexed / field
indexed
^variable, subscript
field
^variable, fieldName
start
^variable
subscript
^$[ asParser, #digit asParser, $] asParser
fieldName
^$. asParser, self identifier
identifier
^(#letter asParser, (#word asParser) star) flatten
Теперь вы можете легче видеть (следуя циклу) что если рекурсия в variable
заканчивается, идентификатор должен быть найден в начале.Это единственный способ начать, а затем приходит больше ввода (или заканчивается).Давайте назовем эту вторую часть variable'
:
variable
^self identifier, variable'
, теперь variable'
фактически ссылается на что-то с использованным идентификатором, и мы можем безопасно переместить отскок слева от indexed
и field
направо в variable'
:
variable'
component', variable' / nil asParser
component'
^indexed' / field'
indexed'
^subscript
field'
^fieldName
Я написал этот ответ без фактического тестирования кода, но должен быть в порядке.Синтаксический анализатор может быть еще более упрощен, я оставляю это как упражнение;).
Для получения дополнительной информации об устранении левой рекурсии вы можете взглянуть на устранение левой рекурсии