AFAIK、「if」ブロックが中括弧が提供されていない場合、1つのステートメントのみがその中と見なされます。例えば.
if(..)
statement_1;
statement_2;
タブに関係なく、if
ブロック内ではstatement_1
のみが考慮されます。
次のコードはそれとうまくいきません:
int main ()
{
if(false) // outer - if
if(false) // nested - if
cout << "false false\n";
else if(true)
cout << "true\n";
}
上記のコードは何も出力しません。 "true"
が印刷されているはずです。
これは、else if
以降、outerif
ブロック内に自動的にネストされます。 g++ -Wall
は警告を発行しますが、ここでは問題ではありません。中括弧を置くと、すべてが期待どおりにうまくいきます。
動作は実際には異なっていませんが、完全に一貫しています。else if
を含む内部のif
ブロック全体がoneブロックと見なされます。
これは “ dangling -else
problem” として知られる、解析における古典的な曖昧さです。通常のBNFで文法が記述されている場合、これを解析するには2つの有効な方法があります。
末尾のelse
は、外側のブロックまたは内側のブロックの一部です。
ほとんどの言語は、ブロックがパーサーによって貪欲に一致することを(任意に)決定することにより、あいまいさを解決します。つまり、else
[if
]が最も近いif
に割り当てられます。
else
は実際にはinnerif
でグループ化されているため、外側ではグループ化されていません。実際には次のように解析されています
int main ()
{
if(false) // outer - if (never gets executed)
{
if(false) // nested - if
{
cout << "false false\n";
} else if(true) {
cout << "true\n";
}
}
}
中かっこを必要な場所に明示的に配置することで、問題を解決できます。
これは、Cパーサーの観点からは非常に自然なことです。
パーサーは、if-statementを解析するときに、まず条件式を解析し、次に条件の後の最初のステートメントを解析し、次にelseキーワードを探し、elseが存在する場合は解析します2番目の(代替)ステートメント。
ただし、最初のステートメントもifステートメントであるため、パーサーは「if-parser」を再帰的に呼び出します(elseキーワードをテストする前に!)。この再帰呼び出しは、内部のif-elseステートメントを完全に解析し(elseを含む)、トークンの位置をコード全体の「終わりを超えて」移動します。
代替動作を実装する試みは、「外部」と「内部」のifパーサー間の追加の通信を伴う必要があります。外部パーサーは、「内部」に貪欲にならないように通知する必要があります(つまり、elseを食べないようにします)ステートメント)。これにより、言語構文がさらに複雑になります。
何も印刷されません。 2番目のif/else ifが最初のブロックに属する1つのブロックであるため、これはこれと同等です。
if(false) {
if(false) // nested - if
cout << "false false\n";
else if(true)
cout << "true\n";
}
else
ステートメントは常に最も近いif
にアタッチされます。ネストされたif
がなければ、それ自体は意味のあるステートメントを形成しないため、パーサーは続行します。