2

用我的语言我可以写

a = 1

b = 2
if true { } else { }
if true { } **Here is the problem**
else {}

我的语法不支持语句之间的换行符。else 只能与 if 一起使用。当我在我的规则中添加 optionalNL

IfExpr:
  IF rval optionalNL codeBlock optionalNL ELSE codeBlock
| IF rval optionalNL codeBlock

else 之前的 optionalNL 会导致 3 个减少/减少。原因是它可以使用 IfExpr 中的第二条规则减少或减少到 exprLoop ,它允许表达式之间有许多换行符。

无论我做什么(我尝试在 optionalNL 和 ELSE 之前编写 %prec )它总是减少到 exprLoop ,这种情况下野牛会给我一个语法错误。我如何告诉野牛此时转移(其他可选NL)而不是减少?(到 exprLoop 导致 else 是一个错误)。

用于测试的示例文件

%%
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 要么总是期待一个 else,因此一个规则总是最终被忽略。状态 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)

感谢 Kinopiko的回答

我将他的代码更改为没有冲突,然后努力使其更灵活。这是我的文件

测试.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;
}

测试.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

4 回答 4

5

我不太了解你的问题,所以我从头开始:

这是我的语法:

%{
#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 ();
}

这是一些测试输入:

四{}
四{}
e { }
四{}

e { }
我有 { }
四{}

这是输出:

要是
第一个表达式
如果别的
另一个表达式
吃了一个EOS
如果别的
另一个表达式
吃了一个EOS
如果别的
另一个表达式
吃了一个EOS
要是
另一个表达式

仍然存在移位/减少冲突

于 2009-11-21T16:39:11.753 回答
1

根据“Lex & Yacc”,reduce/reduce 的默认分辨率是第一个定义的规则,所以正如你所说的 exprLoop 获胜,所以我假设它是首先定义的。

但是切换顺序可能无法按照您的预期解决问题。

进一步阅读(第 237 页),您似乎需要更多的前瞻性,这不是标准 yacc/bison 的选项。但是 Bison 确实有GLR 模式,这可能是有用的。

于 2009-11-21T02:39:47.617 回答
0

您可以做的一件事是使用 lex 规则完全解析换行符。这样,换行符在哪里都没有关系。这就是 C/C++ 所做的......换行符在很大程度上被忽略了。

于 2009-11-21T03:39:25.357 回答
0

问题是:

IfExpr:
  IF rval optionalNL codeBlock optionalNL ELSE codeBlock
| IF rval optionalNL codeBlock

如果有的话,需要在代码块之后进行两个令牌前瞻才能看到换行符之后的“else”。您可以通过在两个 if 规则中复制 optionalNL 来避免这种情况:

IfExpr:
  IF rval optionalNL codeBlock optionalNL ELSE codeBlock
| IF rval optionalNL codeBlock optionalNL

现在解析器不必在两个规则之间做出决定,直到解析 optionalNL 之后,让它在单令牌前瞻中看到 ELSE(或它的缺失)。

这里的一个潜在缺点是第二个 if 规则(但不是第一个)现在将吸收任何尾随换行符,因此如果您的程序语法需要在每个语句之间有一个换行符,它不会在没有 else 的 ifs 之后找到一个,而且它已经消耗。

于 2009-11-22T19:54:03.347 回答