web-dev-qa-db-ja.com

シフトを削除するために文法を改革することで、if-then-elseの競合を減らす

特定の文法のバイソンのシフト削減競合を削除するにはどうすればよいですか?

 selection-stmt -> if ( expression ) statement |
                      if ( expression ) statement else statement

修正された文法を与える解決策をいただければ幸いです。

18
Aakash Anuj

はるかに簡単な解決策があります。 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.

十分であろう。

38
akim

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...
_

whateverstmtで終わらない他の_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;_のようなルールは分割されません。

6
Chris Dodd

次のように、他の場合は通常のステートメントよりも高いレベルにします。

statements:
  statements lineEnd statement
| statements lineEnd IfStat
| statements lineEnd IfElseStat
| IfStat
| IfElseStat
;
IfStat:
  if ( statement )
;
IfElse:
  IfStat else statement
;
0
harry