Основная проблема в том, что ваша функция синтаксического анализатора ypparse
не возвращается, пока не сведет весь язык к начальному символу.
Если верхний уровень вашей грамматики что-то вроде:
language : commands ;
commands : command commands | /* empty */ ;
Конечно, машина будет ожидать полный сценарий (завершенный нажатием Ctrl-D).Если ваш интерпретатор придерживается этой логики:
loop:
print("prompt>")
yyparse()
if (empty statement)
break
, он не будет работать, поскольку yyparse
потребляет весь сценарий перед возвратом.
return 0;
решает проблему для этого интерактивного режимапоскольку значение токена 0 указывает парсеру EOF
, что заставляет его думать, что сценарий завершился.
Я не согласен с решением сделать токен \n
.Это только усложнит грамматику (до сих пор незначительный кусок пробела теперь имеет большое значение) и в конечном итоге не сработает, потому что функция yyparse
все равно захочет обработать всю грамматику.То есть, если у вас есть символ новой строки в качестве токена, но начальный символ грамматики представляет весь скрипт, yyparse
все равно не вернется в ваш цикл интерактивной подсказки.
Быстрый и грязный хак позволяетлексер знает, действует ли интерактивный режим.Тогда он может обусловить return 0;
для каждого экземпляра новой строки, если он находится в интерактивном режиме.Если ввод не полный оператор, будет синтаксическая ошибка, так как сценарий в целом заканчивается на новой строке.В обычном режиме чтения файлов ваш лексер может съесть все пустое пространство, не возвращаясь, как раньше, позволяя обрабатывать весь файл одним yyparse
.
Если вы хотите интерактивный ввод и чтение файла без реализации двух режимов:Поведение в лексере, что вы можете сделать, это изменить грамматику так, чтобы она анализировала только одну инструкцию языка: функция yyparse
возвращает значение для каждой инструкции высшего уровня вашего языка.(И лексер ест новые строки, как и раньше, не возвращая 0).Т.е. начальный символ грамматики - это всего лишь одно утверждение (возможно, пустое).Тогда ваш анализатор файлов должен быть реализован как цикл (написанный вами), который вызывает yyparse для получения всех операторов из файла, пока yyparse
не встретит пустой ввод.Недостатком этого подхода является то, что если пользователь вводит неполный синтаксис (например, висящие открытые скобки), синтаксический анализатор будет продолжать сканировать ввод, пока он не будет удовлетворен.Это недружелюбно, как программы, которые используют scanf
для интерактивного ввода данных пользователем (это та же проблема: scanf
- это синтаксический анализатор, который не возвращает до тех пор, пока он не будет удовлетворен).
Другая возможность заключается винтерактивный режим, который выполняет собственный пользовательский ввод вместо вызова yyparse, чтобы получить входные данные и для его анализа.В этом режиме вы читаете ввод пользователя в строковый буфер.Тогда у вас есть парсер, обрабатывающий строковый буфер.Обрабатывать строковый буфер вместо потока FILE *
вполне возможно.Вам просто нужно написать собственную обработку ввода (ваше собственное определение макроса YY_INPUT
).Это подход, который вам в конечном итоге понадобится, если вы реализуете приличный интерактивный режим с редактированием строк и вызовом истории, например, с помощью libedit
или GNU readline
.