0

ここに私のコードがあります:

%{
#include<string.h> 
#include "y.tab.h"
#define DEBUG 0
void yyerror(char* s);
void debug(char* string) {
    if (DEBUG) {
        printf(string);
    }
}
%}  
selector   "selector"[0-9]+
positive   "+"
negtive    "-"
contains   "."
before     "->"
or         "||"
and        "&&"
delim      [ /n/t]  
ws         {delim}*
%%  
{selector} { debug("Lex:SELECTOR\n"); yylval.string = yytext; return     SELECTOR; }  
{positive} { debug("Lex:POSITIVE\n"); return POSITIVE; }  
{negtive}  { debug("Lex:NEGTIVE\n"); return NEGTIVE; }  
{contains} { debug("Lex:BELONGS\n"); return CONTAINS; } 
{before}   { debug("Lex:BEFORE\n"); return BEFORE; }  
{or}       { debug("Lex:OR\n"); return OR; }  
{and}      { debug("Lex:AND\n"); return AND; } 
{ws}       ;
.          { debug("Lex Parser Error"); exit(1); }    
%%

.y:
%{
#include <stdio.h>
#define YYDEBUG 0
int yyparse(void);
void yyerror(char* s);
%}

%union {
    char *string;
}

%token <string> SELECTOR 
%token POSITIVE
%token NEGTIVE
%left CONTAINS
%left BEFORE
%token OR
%token AND

%%
logical_expr : assertion { printf("[reduce] L->A\n"); } 
    | logical_expr AND assertion { printf("[reduce] L->L && A\n");}   
    | logical_expr OR assertion { printf("[reduce] L->L || A\n"); }        
;
assertion : POSITIVE prefix_relation { printf("[reduce] A->+P\n"); }
    | NEGTIVE prefix_relation { printf("[reduce] A->-P\n"); }
;
prefix_relation : prefix_relation BEFORE contain_relation { printf("[reduce] P->P->C\n"); }
    | contain_relation { printf("[reduce] P->C\n");;}
;
contain_relation : contain_relation CONTAINS SELECTOR { printf("[reduce] C->C.S[%s]\n", $3); }
    | SELECTOR { printf("[reduce] C->S[%s]\n", $1); }
;
%%
int main()
{
    return yyparse();
}
void yyerror(char* s)
{
    fprintf(stderr,"%s",s);
}
int yywrap()
{
    return 1;
}

私の入力文字列は次のとおりです。+selector1.selector2||-selector4->selector4

この入力の解析ツリーは次のようになると予想されます。 期待される構文木

yacc によって生成された私のプログラムは、次のような出力を提供します。

[reduce] C->S[selector1]     // stack: +C
[reduce] C->C.S[selector2]   // stack: +C
[reduce] P->C                // stack: +P
[reduce] A->+P               // stack: A
[reduce] L->A                // stack: L
[reduce] C->S[selector3]     // stack: L||-C
[reduce] P->C                // stack: L||-P
[reduce] C->S[selector4]     // stack: L||-P->C

プログラムが shift&& reduce once の実行を停止すると、yylex() からこれ以上シンボルを取得できないように見えますが、スタック内の残りのシンボルを削減してL||-P->C、コード内で解析ツリー全体を生成できるようになると期待しています。

私の期待される出力は次のとおりです。

[reduce] C->S[selector1]     // stack: +C
[reduce] C->C.S[selector2]   // stack: +C
[reduce] P->C                // stack: +P
[reduce] A->+P               // stack: A
[reduce] L->A                // stack: L
[reduce] C->S[selector3]     // stack: L||-C
[reduce] P->C                // stack: L||-P
[reduce] C->S[selector4]     // stack: L||-P->C
[reduce] P->P->C             // stack: L||-P
[reduce] A->-P               // stack: L||A
[reduce] L->L||A             // stack: L
4

1 に答える 1

3

スキャナー (フレックス) の定義には多くの問題があります。

  1. デフォルトの flex ルールはexitエラー メッセージなしで呼び出すだけなので (DEBUGが定義されていてゼロでない場合を除く)、字句エラーが発生すると、プログラムは黙って停止します。yyerrorその場合は呼び出して、目に見えるエラー メッセージを生成する方がはるかに良いでしょう。

  2. EJP がコメントで指摘しているように、delim定義では/nand/tの代わりに\nand\tを使用しているため、改行にもタブにも一致しません。改行はデフォルト ルールでも一致しないため、flex によって生成されたデフォルト ルールにフォールスルーされ、一致しない文字が単に に出力されstdoutます。( を含めること%option nodefaultをお勧めします。これにより、一部の入力がルールに一致しない場合に flex がエラー メッセージを生成します。)

  3. ルールselectorセットyylval.string = yytext. yytextスキャナーの内部ストレージを指し、それが指す文字列は次回yylex呼び出されたときに変更されるため、これを行うことはできません。一致したトークンをスキャナーからパーサーに渡したい場合は、トークンのコピーを作成する必要があります。次に、freeリークを避けるために、文字列が不要になったときにトークンに割り当てられたストレージを確保する必要があります。メモリー。

  4. によって生成されたパーサー、bisonまたはyacc一般的にリダクションを実行する前に先読みトークンを読み取る必要があることに注意する必要があります。その結果、スキャナーがトークンを返すまで、予想される最後の一連のリダクションは実行されません。ENDトークンは、ファイルの終わりを読み取ったときにのみ実行されます。そのため、パーサーを対話的にテストしている場合、ctrl D(Unix で) 入力してファイルの終わりを知らせるまで、最終的な削減は表示されません。

最後に、flexbisonはどちらも、一致するルール (flex) と、一連のシフト、リデュース、その他のパーサー アクション (bison) を示すデバッグ出力を生成できます。独自のデバッグ出力を実装しようとするよりも、これらの機能を使用する方が簡単で信頼性が高くなります。

于 2015-01-18T07:03:32.133 に答える