В исходной версии этого вопроса основными правилами в спецификации грамматики были:
input: SEMICOLON {queryNode = NULL;YYACCEPT;} |query SEMICOLON {queryNode = $ 1;YYACCEPT;};
Как я уже говорил в исходной версии этого ответа, эти правила гарантируют, что yacc примет запрос, сопровождаемый точкой с запятой, как только встретится точка с запятой, из-за YYACCEPT
action:
yacc "принимает", потому что вы использовали YYACCEPT
в действии.YYACCEPT
означает «как только эта продукция будет распознана, примите ввод, даже если он не был полностью использован».Так что он делает то, что вы просили.
Затем я предложил удалить действия YYACCEPT
, чтобы синтаксический анализатор не возвращался до тех пор, пока лексер не сообщит о конце ввода:
Если вы хотите принять ввод, только если весь ввод соответствует грамматике, просто не вызывайте YYACCEPT
.Yacc автоматически примет соответствие, если начало производства совпадает, а следующий токен является маркером конца ввода.
Но, конечно, это волшебным образом не приводит к остановке чтения при обнаружении символа новой строки.Все, что он делает, это гарантирует, что если весь ввод является одной командой, она будет принята, а в противном случае она будет отклонена.Но поскольку он проверяет, что ничего не следует за командой, он будет продолжать запрашивать ввод, пока не получит некоторое количество.
Если вы хотите, чтобы лексер считывал только одну строку, которая должна быть допустимой командой, выможно легко сделать это, удалив YYACCEPT
из действий анализатора и заставить сканер возвращать указание конца файла, когда он видит символ новой строки:
\n { return 0; }
(Возвращение нуля - это то, как заканчиваются сигналы сканера-of-input.)
Если вы действительно хотите построить программу, которая читает несколько строк ввода, анализирует каждую строку независимо и возвращает после каждой, то вышеупомянутое решение будет работать нормально.
Вы также можете играть в игры в синтаксическом анализаторе, как с вашим новым предложением, но если сканер возвращает маркер новой строки, когда он видит новую строку.Затем вы могли бы принять или отклонить ввод при получении токена новой строки, используя YYACCEPT
, YYABORT
и error
продукцию:
input: SEMICOLON EOL { queryNode = NULL; YYACCEPT; }
| query SEMICOLON EOL { queryNode = $1; YYACCEPT; }
| query error EOL { YYABORT; }
;
Производство ошибки необходимо для сброса остальныхлинии при синтаксической ошибке.В противном случае следующий вызов синтаксического анализатора начнется с середины строки, которая вызвала ошибку, в слегка непредсказуемой точке (поскольку это будет зависеть от того, удерживал ли анализатор маркер предпросмотра, когда сообщал об ошибке).
Хотя это решение имеет некоторые преимущества, оно несколько сложнее, чем то, которое просто возвращает 0 при прочтении новой строки.Поэтому трудно оправдать дополнительную сложность.
В любом случае ни одно из этих решений не является действительно идеальным.В какой-то момент вам почти наверняка понадобится обрабатывать вводы, которые слишком длинны для удобного ввода в одной строке.
Теперь, когда вы включили свой полный сканер, я вижу, что у вас возникнет еще одна серьезная проблема, поскольку вы не копируете строку токена до ее сохранения в yylval
.Сохранение адреса токена (который является частью внутреннего входного буфера сканера) не является правильным;этот буфер будет изменен без предупреждения сканером (например, когда ему нужно больше ввода).В частности, как только сканер начнет работать со следующим токеном, он перезапишет байт NUL, который он ранее использовал для завершения токена, что будет иметь очевидный эффект, что строка токена изменится на два (или более) последовательных токена.Вы можете найти ряд обсуждений по этой проблеме на этом сайте.