Я экспериментирую с lex и yacc и столкнулся с какой-то странной проблемой, но думаю, что лучше всего показать вам свой код, прежде чем подробно рассказывать об этой проблеме. Это мой лексер:
%{ #include #include #include "y.tab.h" void yyerror(char *); %} %% [a-zA-Z]+ { yylval.strV = yytext; return ID; } [0-9]+ { yylval.intV = atoi(yytext); return INTEGER; } [\n] { return *yytext; } [ \t] ; . yyerror("invalid character"); %% int yywrap(void) { return 1; }
Это мой парсер:
%{ #include int yydebug=1; void prompt(); void yyerror(char *); int yylex(void); %} %union { int intV; char *strV; } %token INTEGER ID %% program: program statement EOF { prompt(); } | program EOF { prompt(); } | { prompt(); } ; args: /* empty */ | args ID { printf(":%s ", $2); } ; statement: ID args { printf("%s", $1); } | INTEGER { printf("%d", $1); } ; EOF: '\n' %% void yyerror(char *s) { fprintf(stderr, "%s\n", s); } void prompt() { printf("> "); } int main(void) { yyparse(); return 0; }
Очень простой язык, состоящий не более чем из строк и целых чисел и базового REPL. Теперь вы заметите в синтаксическом анализаторе, что args выводятся с ведущим двоеточием, при этом предполагается, что в сочетании с первым шаблоном правила оператора взаимодействие с REPL будет выглядеть примерно так:
> aaa aa a :aa :a aaa>
Однако взаимодействие таково:
> aaa aa a :aa :a aaa aa aa >
Почему идентификатор токена в следующем правиле
statement: ID args { printf("%s", $1); } | INTEGER { printf("%d", $1); } ;
имеют семантическое значение общей входной строки, включая новую строку? Как моя грамматика может быть переработана так, чтобы взаимодействие я намеревался?
Вы должны сохранять строки токенов, поскольку они читаются, если вы хотите, чтобы они оставались действительными. Я изменил правило утверждения следующим образом:
statement: ID { printf("<%s> ", $1); } args { printf("%s", $ 1); } | INTEGER { printf("%d", $1); } ;
Затем, с вашим входом, я получаю вывод:
> aaa aa a :aa :a aaa aa a >
Обратите внимание, что во время считывания начального идентификатора токен – именно то, что вы ожидали. Но поскольку вы не сохранили токен, строка была изменена к моменту, когда вы вернетесь к ее распечатке после анализа args
.
Я думаю, что существует конфликт ассоциативности между постановками args и statement. Это подтверждается (частичным) выходом файла bison -v
parser.output:
Nonterminals, with rules where they appear $accept (6) on left: 0 program (7) on left: 1 2 3, on right: 0 1 2 statement (8) on left: 4 5, on right: 1 args (9) on left: 6 7, on right: 4 7 EOF (10) on left: 8, on right: 1 2
В самом деле, я с трудом пытаюсь понять, что ваша грамматика пытается принять. В качестве побочного примечания я, вероятно, переместил бы вашу EOF-продукцию в лексер как маркер EOL; это упростит повторную синхронизацию ошибок анализа.
Лучшее объяснение ваших намерений было бы полезно.