Dangling Else問題に関するWikipediaの記事のこの文は理解できません。
[Dangling Elseの問題]は、コンパイラの構築、特にスキャナーレス解析でしばしば発生する問題です。
誰かがスキャナーレス解析技術がこの問題を悪化させる可能性があることを私に説明できますか?問題は文法にあるようです-曖昧だからです-解析手法の選択ではありません。私は何が欠けていますか?
if a then if b then s1 else s2
その文法はあいまいです。
Dangling Else問題に関するWikipediaの記事のこの文は理解できません。
[Dangling Elseの問題]は、コンパイラの構築、特にスキャナーレス解析でしばしば発生する問題です。
誰かがスキャナーレス解析技術がこの問題を悪化させる可能性があることを私に説明できますか?問題は文法にあるようです-曖昧だからです-解析手法の選択ではありません。私は何が欠けていますか?
if a then if b then s1 else s2
その文法はあいまいです。
回答:
私の最良の推測は、ウィキペディアの記事の文章は、E。Visserの仕事に対する誤解に起因しているということです。
スキャナーレスパーサーの文法(つまり、言語を文字のシーケンスのセットとして代わりにトークンのシーケンスのセットとして記述する言語)は、トークンを文字のストリングとして個別に記述しますが、多くのあいまいさがあります。E. Visser論文スキャナーレス一般化LRパーサーの曖昧性除去フィルター(*)は、あいまいさを解決するためのメカニズムをいくつか提案しています。しかし、この論文では、 "dangling else problem"という名前の正確なあいまいさがスキャナーレスパーサーに関連しているとは述べていません(また、このメカニズムはスキャナーレスパーサーに特に役立つことさえありません)。
それを解決するメカニズムを提案するという事実は、別のあいまいさ解決メカニズム(オペレーターの優先順位と優先順位)としての暗黙のステートメントではありません。ネストの結果として通常の文法に存在しますが、最長一致ルールで処理されるものは可能です)。
(*)これはおそらく、スキャナーレスパーサーに関する別の記事を参照している場合でも、スキャナーレスパーサーに関するウィキペディアの記事の基礎となる論文です。
問題を述べるために、Dangling Else Problemはコード構文仕様のあいまいさであり、次のifやelseの場合、他のifに属している場合、それは不明確かもしれません。
最も単純で古典的な例:
if(conditionA)
if(conditionB)
doFoo();
else
doBar();
それは、心によって言語仕様の詳細を知らない人には、不明であるif
取得else
(をし、この特定のコードスニペットは、半ダースの言語で有効ですが、それぞれに異なっ行う場合があります)。
Dangling Elseコンストラクトは、スキャナーレスパーサー実装の潜在的な問題を引き起こします。これは、パーサーがトークン化(アセンブリまたはコンパイル中の中間言語にダイジェスト)するのに十分であるとパーサーが認識するまで、一度に1文字ずつファイルストリームを丸toみする戦略であるためです。これにより、パーサーは最小限の状態を維持できます。解析されたトークンをファイルに書き込むのに十分な情報があると判断するとすぐに、そうします。それがスキャナーレスパーサーの最終目標です。高速、シンプル、軽量のコンパイル。
句読点の前後の改行と空白は無意味であると仮定すると(ほとんどのCスタイル言語のように)、このステートメントはコンパイラーに次のように表示されます。
if(conditionA)if(conditionB)doFoo();else doBar;
コンピューターに完全に解析できるので、見てみましょう。私が持っているまで、私は一度に1つのキャラクターを取得します:
if(conditionA)
ああ、私はそれが何を意味するか知っています(C#で)、それは「push
evalスタックにconditionAがあり、それがbrfalse
真でなければ次のセミコロンの後のステートメントにジャンプするために呼び出す」ことを意味します。現時点ではセミコロンが表示されていないため、ここでは、この命令の後にジャンプオフセットを次のスペースに設定し、セミコロンが表示されるまでさらに命令を挿入するにつれてそのオフセットを増やします。解析を続行しています...
if(conditionB)
OK、これは同様のIL操作のペアに解析され、解析した命令の直後に実行されます。セミコロンが表示されないので、前のステートメントのジャンプオフセットを2つのコマンド(プッシュ用とブレーク用)の長さだけ増やし、探し続けます。
doFoo();
わかりました、簡単です。それが「call
doFoo」です。そして、それは私が見るセミコロンですか?まあ、それは素晴らしい、それは行の終わりです。これらの2つのコマンドの長さだけ、両方のブロックのジャンプオフセットをインクリメントし、気にしなかったことを忘れます。OK
else
... ええとああ。これは見た目ほど単純ではありません。OK、私はちょうどやっていたが、何を忘れたelse
手段は、私はすでに見てきたことを、条件付きbreak文のどこかにありますので、私はそれは、そこにされて、うん...振り返ってみましょうbrfalse
、私はいくつかの「conditionBは」上押した直後にそれが何であれ、スタック。OK、break
次のステートメントとして無条件が必要になりました。その後に続くステートメントは間違いなく私の条件付きブレークのターゲットなので、私はそれが正しいことを確認し、入れた無条件のブレークを増やします。
doBar();
それは簡単です。「call
doBar」。そしてセミコロンがあり、ブレースは見たことがありません。だから、無条件の人break
は、それが何であれ、次のステートメントにジャンプする必要があります。
だから、私たちは何を持っています...(注:午後10時であり、ビットオフセットを16進数に変換したり、これらのコマンドで関数の完全なILシェルを埋めたりしたくないので、これは単なる擬似ILです通常はバイトオフセットがある行番号を使用します):
ldarg.1 //conditionA
brfalse <line 6> //jumps to "break"
ldarg.2 //conditionB
brfalse <line 7> //jumps to "call doBar"
call doFoo
break <line 8> //jumps beyond statement in scope
call doBar
<line 8 is here>
まあ、それは実際に正しく実行されます。ルール(ほとんどのCスタイル言語のように)がelse
最も近いであるということif
です。実行のネスティングに従うようにインデントされ、次のように実行されます。conditionAがfalseの場合、スニペットの残り全体がスキップされます。
if(conditionA)
if(conditionB)
doFoo();
else
doBar();
...しかし、外側のif
ステートメントに関連付けられたブレークbreak
は内側 の最後のステートメントにジャンプし、ステートメントif
全体を超えて実行ポインターを取得するため、セレンディピティによってそうなります。これは余分な不要なジャンプであり、この例がより複雑な場合、この方法で解析およびトークン化すると機能しなくなる可能性があります。
また、言語仕様がダングリングelse
が最初のものif
に属していると言っており、conditionAがfalseの場合doBarが実行され、conditionAがtrueでconditionBではない場合は何も起こらない場合はどうなりますか?
if(conditionA)
if(conditionB)
doFoo();
else
doBar();
パーサーは最初if
に存在したことを忘れていたため、この単純なパーサーアルゴリズムは正しいコードを生成せず、効率的なコードは言うまでもありません。
現在、パーサーはより長い時間にわたってif
sとelse
s を記憶するのに十分賢いかもしれませんが、言語仕様がelse
2 if
番目のsが最初のsに一致するif
ことを示している場合if
、一致するelse
sを持つ2つのsで問題を引き起こします:
if(conditionA)
if(conditionB)
doFoo();
else
doBar();
else
doBaz();
パーサーは最初を見て、最初else
に一致してから、if
2番目を見て、パニック「地獄はまた何をしていた」モードに入ります。この時点で、パーサーは変更可能な状態でかなり多くのコードを取得しているため、出力ファイルストリームに既にプッシュされているはずです。
これらすべての問題とwhat-ifに対する解決策があります。しかし、そのスマートである必要があるコードはパーサーアルゴリズムの複雑さを増すか、パーサーをこの馬鹿にする言語仕様により、言語ソースコードの冗長性が増しend if
ます。if
ステートメントにがある場合はブロックしますelse
(両方とも他の言語スタイルでよく見られます)。
これは、2つのif
ステートメントの単なる1つの単純な例であり、コンパイラーが下す必要があるすべての決定と、とにかく非常に簡単に台無しになる可能性がある場所を調べます。これは、あなたの質問におけるウィキペディアからの無害な声明の背後にある詳細です。