2016-04-05 1 views
0

を得ていないこれは私のレモンパーサー文法であるレモン+ re2cの正しいルールの解像度

%nonassoc IMPLICATION. 
%nonassoc PERIOD. 
%nonassoc NEWLINE. 
%nonassoc END. 
%nonassoc STRING. 

program ::= in END. 
in ::= . 
in ::= in rule NEWLINE. 
in ::= in rule. 
rule ::= STRING(A) IMPLICATION STRING(B) PERIOD. {cout<<A->token<<endl; cout<<B->token<<endl;} 

私の入力文字列が

p<-body1. 
q<-body3. 

である私は、出力が

p 
body1 
q 
body3 

であることを期待していますが、代わりに、私は出力を得ています

q 
q 
\n (Empty line here) 
\n (Empty line here) 

正しい順序でトークンを渡していると確信しています。パーサーが間違った入力で構文解析エラーをスローすることがあるため、確認しました。ここで

は私が間違って何が起こっているかのよう途方に暮れています

do 
{ 
    token = lexer.scan(); // returns an int with the type of token 
    Token* t = new Token(lexer.getTokenValue().c_str()); 

    lpmlnParse(pParser, token, t); 
}while(token != PARSE_TOKEN_END); 

をパーサーにトークンを渡すために使用するコードです。誰かが私を正しい方向に向けることができますか?

+0

結果を説明するための十分な情報がありません。特に、表示されているプログラム(ここではカンマはどこから来たものですか)によって出力が生成されないためです。どんな答えもちょうど推測だろう。 [mcve]を作成してください。* minimal * – rici

+0

@riciが更新されました。 –

答えて

1

これは、スキャナがどのように動作するか、またはlexer.getTokenValue()の値が何であるか、またはTokenコンストラクタがその引数をどのように使用しているかを示していないためです。

しかし、私たちはlexerオブジェクトは、すべてのトークンがスキャンされた後、マッチしたテキストに割り当てられているプラ​​イベートstd::stringメンバー含まれていることを想像してみましょう:

struct lexer { 
    // ... 
    int scan() { 
    int toke; 
    const char* start = current_; 
    /* re2c stuff */ 
    tstring_.assign(start, current_ - start); 
    return toke; 
    } 
    const std::string& getTokenValue() const { 
    return tstring_; 
    } 
    std::string tstring_; 
    const char* current_; 
}; 

をとするのではなく、(Tokenconst char*部材を備えているとしstd::string):

struct Token { 
    explicit Token(const char* s) : str_(s) {} 
    const char* str_; 
} 

少なくとも、観察された動作を説明します。

lexer.scan()を連続して呼び出すと、tstring_の内容が上書きされます。 (一般的には、std::string::assignは内部文字配列を再割り当てすることができますが、現代のC++ライブラリは短い文字列の最適化を使用しており、サンプルコードのすべてのトークンは短い文字列ですので、ここでは起こりません)。

std::string::c_strTokenのどちらのコンストラクタも文字のコピーを作成しないため、新しく作成されたTokenは、スキャンが進行するにつれて上書き(または悪化)する内部バッファのポインタを持ちます。

したがって、Tokenが最初に作成されたときの縮小アクションでは、Tokenの文字列値が異なることになります。

それでも、qがおそらくp->body1.を減らしたルールで印刷されている理由を説明するにはまだ十分ではありません。

bisonとは異なり、lemonパーサーは、先読みの最適化を試行しません。 bison生成されたパーサーは、先読みトークンが縮小またはシフトのどちらを行うかを決定する必要がない場合に、先読みトークンが要求される前に縮小を実行します。対照的に、lemonで生成されたパーサーは、先読みトークンが使用可能になったときにのみ減少します。この場合、製品番号rule ::= STRING(A) IMPLICATION STRING(B) PERIOD.の削減はPERIODに続くトークンに依存しませんが、レモンパーサーは次のトークンを待機します。

文法から、次のトークンがNEWLINEであると予想されるかもしれませんが、その場合、出力には2つの改行文字(またはセマンティックアクションで改行文字も出力するので4つの空白行)が表示されます。それが当てはまらないので、レキシカーがNEWLINEトークンを返すのではなく、改行文字をスキップしていると推測するかもしれません。その場合、NEWLINEトークンはオプションであるため(in rulein rule NEWLINEの両方が有効なので)、文法は引き続き機能します。先読みトークンは、次のSTRINGトークンになります。これはqとなります。 q->body3.の後の先読みトークンはNEWLINEではなくENDとなるため、対応するトークン文字列は改行ではなく空であると考えられます。

上記の推測がすべて有効であれば、Tokenオブジェクトのconst char* str_;std::string str_;に置き換えるなどして、トークン文字列のコピーを作成するという解決策があります。その場合、const char*コンストラクタをconst std::string&コンストラクタ、または単純なstd::stringコンストラクタに置き換えると、std::string::c_str()を使用する必要はありません。

+0

私はあなたの答えに基づいていたコードに従っていくつかの変更を加えました。ありがとう –

関連する問題