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 つの reduce/reduce を引き起こします。理由は、IfExpr の 2 番目のルールを使用して削減できるか、式の間に多くの改行を許可する exprLoop に削減できるためです。

私が何をしても (optionalNL と ELSE の前に %prec を書いてみました)、常に exprLoop に縮小されます。削減する代わりに、この時点で (optionalNL else に) シフトするように bison に指示するにはどうすればよいですか? (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 を期待するため、1 つのルールは常に無視されます。状態 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 さん、ご回答ありがとうございます。

私は彼のコードを競合がないように変更し、より柔軟にすることに取り組みました。ここに私のファイルがあります

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

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

ここにいくつかのテスト入力があります:

iv { }
iv { }
e{}
iv { }

e{}
iv { } e { }
iv { }

出力は次のとおりです。

IFのみ
最初の式
IF/ELSE
別の式
EOSを食べた
IF/ELSE
別の式
EOSを食べた
IF/ELSE
別の式
EOSを食べた
IFのみ
別の式

shift/reduce 競合が残っています。

于 2009-11-21T16:39:11.753 に答える
1

「Lex & Yacc」によると、reduce/reduce のデフォルトの解決は最初に定義されたルールなので、exprLoop が勝つと言うので、最初に定義されていると仮定します。

ただし、順序を入れ替えても、期待どおりに問題が解決しない場合があります。

さらに読むと (237 ページ)、標準の yacc/bison のオプションではない先読みが必要なようです。しかし、Bison にはGLR モードがあり、役に立つかもしれません。

于 2009-11-21T02:39:47.617 に答える
0

できることの 1 つは、lex ルールを使用して改行を完全に解析することです。このように、改行がどこにあるかは問題ではありません。これは C/C++ が行うことです...改行はほとんど無視されます。

于 2009-11-21T03:39:25.357 に答える
0

問題はそれです:

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

改行の後に「else」がある場合は、コードブロックの後に 2 トークンの先読みが必要です。これは、両方の if ルールで optionalNL を複製することで回避できます。

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

これで、パーサーは、optionalNL が解析されるまで 2 つのルールのどちらかを決定する必要がなくなり、1 トークン先読みで ELSE (またはその欠落) を認識できるようになります。

ここでの潜在的な欠点は、(最初ではなく) 2 番目の if ルールが末尾の改行を吸収するようになったことです。そのため、プログラムの文法で各ステートメントの間に改行が必要な場合、else がないと if の後に改行が見つからず、既に改行されています。消費されます。

于 2009-11-22T19:54:03.347 に答える