Сдвиг зубров вместо уменьшения. С уменьшением / уменьшением ошибок
На моем языке я могу написать
a = 1
b = 2
if true { } else { }
if true { } **Here is the problem**
else {}
Мой грамматик не поддерживает переводы строк между утверждениями. Else можно использовать только с if. Когда я добавляю optionNL в моем правиле
IfExpr:
IF rval optionalNL codeBlock optionalNL ELSE codeBlock
| IF rval optionalNL codeBlock
Необязательный NL перед остальным вызывает 3 уменьшения / уменьшения. Причина в том, что он может сократить использование второго правила в IfExpr или уменьшить до exprLoop, где он допускает много новых строк между выражениями.
Независимо от того, что я делаю (я пытался написать%prec перед необязательными NL и ELSE), он всегда сводится к exprLoop, в котором случаи bison дают мне ошибку synax в else. Как мне сказать бизону сместиться в этой точке (к необязательному NL еще) вместо уменьшения? (к exprLoop, вызывающему ошибку).
файл примера для тестирования
%%
program:
exprLoop;
exprLoop:
exprLoop2 expr
| exprLoop2
exprLoop2:
| exprLoop2 expr EOS
| exprLoop2 EOS
;
expr:
'i' Var optEOS '{' '}'
| 'i' Var optEOS '{' '}' optEOS 'e' '{' '}'
EOS: '\n' ;
Var: 'v';
optEOS: | optEOS EOS
%%
//this can be added to the lex file
[iev] { return *yytext; }
y.output http://www.pastie.org/707448
Альтернатива.y и вывод. Вы можете видеть, что он смотрит вперед, видя \n, и не знает, как уменьшить правило или продолжать. Я изменяю меняю порядок правил, чтобы получить разные результаты. Но он всегда ожидает \n или всегда ожидает другое, поэтому одно правило всегда игнорируется. состояние 15
9 expr: 'i' Var optEOS '{' '}' . [$end, '\n']
10 | 'i' Var optEOS '{' '}' . 'e' '{' '}'
11 | 'i' Var optEOS '{' '}' . '\n' 'e' '{' '}'
'e' shift, and go to state 16
'\n' shift, and go to state 17
'\n' [reduce using rule 9 (expr)]
$default reduce using rule 9 (expr)
Спасибо Кинопико за ответ
Я изменил его код, чтобы не было конфликтов, и работал над тем, чтобы сделать его более гибким. Вот мои файлы
test.y
%{
#include <stdio.h>
%}
%%
program: expr { printf ("First expr\n"); }
| program expr { printf ("Another expr\n"); }
expr:
if optEOS { printf ("IF only\n"); }
| if optEOS else optEOS { printf ("IF/ELSE\n"); }
if: 'i' Var optEOS '{' optEOS '}'
else: 'e' optEOS '{' optEOS '}'
EOS: '\n'
Var: 'v'
optEOS:
| EOS optEOS { ;}//printf ("many EOS\n"); }
%%
int main(int argc, char **argv)
{
int i;
printf("starting\n");
if(argc < 2) {
printf("Reading from stdin\n");
yyparse();
return 0;
}
for(i = 1; i < argc; i++) {
FILE *f;
char fn[260];
sprintf(fn, "./%s", argv[i]);
f = fopen(fn, "r");
if(!f) {
perror(argv[i]);
return (1);
}
printf("Running '%s'\n", argv[i]);
yyrestart(f);
yyparse();
fclose(f);
printf("done\n");
}
return 0;
}
test.y
%{
#include <stdio.h>
#include "y.tab.h"
%}
%option noyywrap
%%
[ \t] { }
\n { return *yytext; }
. { return *yytext; }
%%
int yyerror ()
{
printf ("syntax error\n");
exit (1);
}
тестовый файл, который автоматически запускался после компиляции
i v { }
i v { }
e { }
i v { }
e { }
i v {
} e {
}
i v { }
i v { } i v { } e { }
i v
{ } i v { } e { } i v { } e {
} i v {
} e
{ }
4 ответа
Я не очень хорошо понимаю вашу проблему, поэтому я начал с нуля:
Это моя грамматика:
%{
#include <stdio.h>
%}
%%
program: expr { printf ("First expr\n") }
| program EOS { printf ("Ate an EOS\n") }
| program expr { printf ("Another expr\n") }
expr:
ifeos { printf ("IF only\n"); }
| ifelse { printf ("IF/ELSE\n"); }
ifelse: ifeos else
| if else
ifeos: if EOS
| ifeos EOS
if: 'i' Var optEOS '{' '}'
else: 'e' '{' '}'
EOS: '\n'
Var: 'v'
optEOS:
| EOS optEOS { printf ("many EOS\n") }
%%
Вот лексер:
%{
#include <stdio.h>
#include "1763243.tab.h"
%}
%option noyywrap
%%
[iev\{\}\n] { return *yytext; }
\x20 { }
%%
int yyerror ()
{
printf ("syntax error\n");
exit (1);
}
int main () {
yyparse ();
}
Вот некоторые тестовые данные:
iv {} iv {} е {} iv {} е {} iv {} e {} iv {}
Вот вывод:
Если только Первый опыт ЕСЛИ ЕЩЕ Еще один expr Съел EOS ЕСЛИ ЕЩЕ Еще один expr Съел EOS ЕСЛИ ЕЩЕ Еще один expr Съел EOS Если только Еще один expr
Остался конфликт сдвига / уменьшения.
Согласно "Lex & Yacc", разрешение по умолчанию при уменьшении / уменьшении является первым определенным правилом, так что, как вы говорите, выигрывает exprLoop, поэтому я предполагаю, что оно определено первым.
Но переключение порядка может не решить проблему так, как вы ожидаете.
Дальнейшее чтение (стр. 237) показывает, что вам нужно больше смотреть в будущее, что не подходит для стандартного yacc/bison. Но у Bison есть режим GLR, который может быть полезен.
Проблема в том, что:
IfExpr:
IF rval optionalNL codeBlock optionalNL ELSE codeBlock
| IF rval optionalNL codeBlock
требует двухстороннего просмотра после кодового блока, чтобы увидеть 'else' после новой строки, если это то, что есть. Вы можете избежать этого, дублируя опциональный NL в обоих правилах if:
IfExpr:
IF rval optionalNL codeBlock optionalNL ELSE codeBlock
| IF rval optionalNL codeBlock optionalNL
Теперь синтаксическому анализатору не нужно выбирать между двумя правилами до тех пор, пока не будет проанализирован опциональный NL, что позволит ему увидеть ELSE (или его отсутствие) в запросе с одним токеном.
Потенциальный недостаток здесь заключается в том, что второе правило if (но не первое) теперь будет поглощать любые завершающие символы новой строки, поэтому, если ваша грамматика для программ требует новой строки между каждым оператором, она не найдет одну после if без elses и уже была потребляются.
Одна вещь, которую вы можете сделать, это полностью разобрать переводы строк, используя для них правило lex. Таким образом, не имеет значения, где находятся новые строки. Это то, что делают C/C++... переводы строк в значительной степени игнорируются.