GCCとLLVM-Clangは、手書きの再帰降下パーサー、およびnotマシン生成、Bison-Flexベース、ボトムを使用しているようです解析中。
ここに誰かがこれが事実であることを確認してもらえますか?もしそうなら、なぜ主流のコンパイラフレームワークは手書きのパーサーを使用するのですか?
Update: このトピックに関する興味深いブログはこちら
はい:
GCCはかつてyacc(バイソン)パーサーを使用していましたが、3.xシリーズのある時点で手書きの再帰降下パーサーに置き換えられました。参照 http://gcc.gnu.org/ wiki/New_C_Parser 関連するパッチ提出へのリンク。
Clangは手書きの再帰降下パーサーも使用します。 http://clang.llvm.org/featuresの終わり近くにある「C、Objective C、C++、Objective C++の単一の統合されたパーサー」セクションを参照してください。 html 。
Cは解析が難しく、C++は本質的に不可能であると言う民俗定理があります。
それは真実ではありません。
真実は、CおよびC++がLALR(1)パーサーを使用して構文解析機構をハッキングせず、シンボルテーブルデータに絡まないように解析するのがかなり難しいということです。実際、GCCはYACCとこのような追加のハッカーを使用して、それらを解析するために使用されていました。 現在、GCCは手書きパーサーを使用していますが、シンボルテーブルハッカーを使用しています。 Clangの人々は、自動化されたパーサージェネレーターを使用しようとしませんでした。私の知る限り、Clangパーサーは常にハンドコーディングされた再帰降下です。
本当のことは、CとC++は、より強力な自動生成パーサー(たとえば、 GLRパーサー )を使用して比較的簡単に解析できることであり、ハックは必要ありません。 Elsa C++パーサーはこの一例です。 C++フロントエンド はもう1つです(すべての「コンパイラ」フロントエンドと同様に、GLRは非常に素晴らしい解析技術です)。
C++フロントエンドはGCCほど高速ではなく、Elsaよりも確かに低速です。他のより差し迫った問題があるにも関わらず、慎重にチューニングすることに少しの労力を費やしました(それでも何百万行ものC++コードで使用されてきました)。 Elsaは、単に一般的であるという理由だけで、GCCよりも遅い可能性があります。最近のプロセッサ速度を考えると、これらの違いは実際にはそれほど重要ではないかもしれません。
しかし、今日広く流通している「本物のコンパイラ」は、10年または20年以上前のコンパイラにルーツを持っています。その後、非効率性がさらに重要になり、GLRパーサーのことを聞いた人はいなかったため、人々は自分が知っている方法を実行しました。 Clangは確かに最近ですが、民俗定理は長い間「説得力」を保持しています。
もうそうする必要はありません。 GLRやその他のパーサーをフロントエンドとして非常に合理的に使用でき、コンパイラの保守性が向上します。
istrueとは、友好的な近隣コンパイラの動作に一致する文法を取得するのが難しいということです。ほぼすべてのC++コンパイラは、元の標準の(ほとんど)を実装しますが、多くのダークコーナー拡張もあります。たとえば、MSコンパイラのDLL仕様など)。強力な解析エンジンがある場合、パーサージェネレーターの制限に合わせて文法を曲げるのではなく、最終的な文法を現実と一致させるために時間を費やすことができます。
2012年11月の編集:この回答を書いて以来、ANSI、GNU、およびMSバリアントの方言を含む完全なC++ 11を処理するためにC++フロントエンドを改善しました。余分なものがたくさんありましたが、解析エンジンを変更する必要はありません。文法規則を修正しました。 didはセマンティック分析を変更する必要があります。 C++ 11は意味的に非常に複雑であり、この作業により、パーサーを実行するための努力が圧倒されます。
編集2015年2月:...完全なC++ 14を処理するようになりました。 (単純なコードのGLR解析、およびC++の悪名高い「最も厄介な解析」については、 人間が読み取れるAST c ++コードから を参照)。
2017年4月の編集:C++ 17(ドラフト)を処理するようになりました。
Clangのパーサーは手書きの再帰下降パーサーであり、他のいくつかのオープンソースおよび商用のCおよびC++フロントエンドも同様です。
Clangは、いくつかの理由で再帰下降パーサーを使用します。
全体として、C++コンパイラーにとっては、それほど重要ではありません。C++の構文解析部分は重要ですが、それでも簡単な部分の1つであるため、単純に保つほうが有利です。セマンティック分析---特に名前検索、初期化、オーバーロード解決、テンプレートのインスタンス化---解析よりも桁違いに複雑です。証明が必要な場合は、Clangの「セマ」コンポーネント(セマンティック分析用)と「構文解析」コンポーネント(解析用)のコードとコミットの配布を確認してください。
gccのパーサーは手書きです。 。 clangについても同じことが疑われます。これにはおそらくいくつかの理由があります。
これはおそらく「ここで発明されていない」症候群のケースではありませんが、「私たちが必要とするもののために特別に最適化されたものは何もなかったので、独自に作成しました」。
奇妙な答えがあります!
C/C++文法はコンテキストに依存しません。 Foo *バーがあるため、状況依存です。あいまいさ。 Fooが型であるかどうかを知るために、typedefのリストを作成する必要があります。
Ira Baxter:GLRのことには意味がありません。あいまいさを含む解析ツリーを構築する理由。解析とは、あいまいさを解決し、構文ツリーを構築することを意味します。 2回目のパスでこれらのあいまいさを解決するので、これはそれほど見苦しくありません。私にとってははるかにいです...
YaccはLR(1)パーサージェネレーター(またはLALR(1))ですが、コンテキストに応じて簡単に変更できます。そして、それはいものは何もありません。 Yacc/BisonはC言語の構文解析を支援するために作成されたため、Cパーサーを生成するための最もuいツールではありません...
GCC 3.xまで、Cパーサーはyacc/bisonによって生成され、解析中にtypedefsテーブルが作成されました。 「解析中」のtypedefsテーブルの構築により、C文法はローカルでコンテキストがなくなり、さらに「ローカルでLR(1)」になります。
現在、Gcc 4.xでは、再帰降下パーサーです。これは、Gcc 3.xとまったく同じパーサーですが、依然としてLR(1)であり、同じ文法規則を持っています。違いは、yaccパーサーが手書きで書き直され、シフト/リデュースがコールスタックに隠され、gcc 3.x yaccのように「state454:if(nextsym == '(')goto state398」がないことです。そのため、gcc noobのコードははるかに「読みにくい」ので、パッチを当て、エラーを処理し、より良いメッセージを出力し、次のコンパイル手順を実行するのが簡単です。
なぜ彼らはyaccから再帰降下に切り替えたのですか? C++を解析するためにyaccを避けることは非常に必要であり、GCCは多言語コンパイラになることを夢見ているため、つまり、コンパイル可能な異なる言語間で最大限のコードを共有するためです。これが、C++およびCパーサーが同じ方法で記述されている理由です。
C++はLR(1)がCのように「ローカル」ではなく、LR(k)でもないため、Cよりも解析が困難です。見る func<4 > 2>
これは、4> 2でインスタンス化されたテンプレート関数です。つまり、func<4 > 2>
はfunc<1>
。これは間違いなくLR(1)ではありません。 func<4 > 2 > 1 > 3 > 3 > 8 > 9 > 8 > 7 > 8>
。これは、再帰呼び出しにより、さらにいくつかの関数呼び出しの代償として、あいまいさを簡単に解決できる場所です(parse_template_parameterはあいまいなパーサー関数です。parse_template_parameter(17tokens)が失敗した場合、parse_template_parameter(15tokens)、parse_template_parameter(13tokens)...までできます)。
なぜyacc/bison再帰サブ文法に追加できないのかはわかりませんが、これがgcc/GNUパーサー開発の次のステップになるのでしょうか?