特定の文法のバイソンのシフト削減競合を削除するにはどうすればよいですか?
selection-stmt -> if ( expression ) statement |
if ( expression ) statement else statement
修正された文法を与える解決策をいただければ幸いです。
はるかに簡単な解決策があります。 LRパーサーがどのように機能するかを知っている場合は、ここで競合が発生することを知っています。
if ( expression ) statement * else statement
ここで、星はカーソルの現在の位置を示します。パーサーが答えなければならない質問は、「シフトすべきか、それとも削減すべきか」です。通常、else
を最も近いif
にバインドします。つまり、ここでelse
トークンをシフトします。ここで減らすということは、else
が「古い」if
にバインドされるのを待つことを意味します。
ここで、パーサジェネレータに「トークン"else"
とルール「stm-> if(exp)stm」の間にshift/reduceの競合がある場合、トークンが勝つ必要がある」ことを「伝え」ます。これを行うには、ルールの優先順位に「名前を付けて」(例:"then"
)、"then"
の優先順位が"else"
よりも低いことを指定します。何かのようなもの:
// Precedences go increasing, so "then" < "else".
%nonassoc "then"
%nonassoc "else"
%%
stm: "if" "(" exp ")" stm %prec "then"
| "if" "(" exp ")" stm "else" stm
bison構文を使用します。
実際、私のお気に入りの答えは、"then"
と"else"
に同じ優先順位を付けることです。優先順位が等しい場合、シフトしたいトークンと削減したいルールの間の結びつきを断ち切るために、Bison/Yaccは結合性を調べます。ここでは、いわば右結合性を促進したい(より正確には、「シフト」を促進したい)ので、次のようになります。
%right "then" "else" // Same precedence, but "shift" wins.
十分であろう。
If-elseの場合の中央のstatement
は、ぶら下がっているif(ifが他にない場合)になることはできない(または終わる)ことができないという事実を認識する必要があります。これを行う最も簡単な方法は、stmt
ルールを2つに分割することです。
_stmt -> stmt-ending-with-dangling-if | stmt-not-ending-with-dangling-if
stmt-not-ending-with-dangling-if ->
if ( expression ) stmt-not-ending-with-dangling-if else stmt-not-ending-with-dangling-if |
...other statements not ending with dangling if...
stmt-ending-with-dangling-if ->
if ( expression ) stmt |
if ( expression ) stmt-not-ending-with-dangling-if else stmt-ending-with-dangling-if |
...other statements ending with dangling if...
_
whatever
がstmt
で終わらない他の_stmt -> whatever
_ルールは_stmt-not-ending-with-if
_ルールに入りますが、stmt
で終わるstmt
ルールは2つのバージョンに分割されます。 _not-ending-with-if
_ルールの_not-ending-with-if
_バージョンと_dangling-if
_ルールの_dangling-if
_バージョン。
編集
他のプロダクションとのより完全な文法:
_stmt : stmt-ending-with-dangling-if | stmt-not-ending-with-dangling-if
stmt-not-ending-with-dangling-if :
IF '(' expr ')' stmt-not-ending-with-dangling-if ELSE stmt-not-ending-with-dangling-if |
WHILE '(' expr ')' stmt-not-ending-with-dangling-if |
DO stmt WHILE '(' expr ')' ';' |
expr ';' |
'{' stmt-list '}'
stmt-ending-with-dangling-if:
IF '(' expr ')' stmt |
IF '(' expr ')' stmt-not-ending-with-dangling-if ELSE stmt-ending-with-dangling-if |
WHILE '(' expr ')' stmt-ending-with-dangling-if
_
WHILE (expr) stmt
のようなルールは(stmt
で終わるため)2つに分割されますが、_expr;
_のようなルールは分割されません。
次のように、他の場合は通常のステートメントよりも高いレベルにします。
statements:
statements lineEnd statement
| statements lineEnd IfStat
| statements lineEnd IfElseStat
| IfStat
| IfElseStat
;
IfStat:
if ( statement )
;
IfElse:
IfStat else statement
;