web-dev-qa-db-ja.com

GCCおよびClangパーサーは本当に手書きですか?

GCCとLLVM-Clangは、手書きの再帰降下パーサー、およびnotマシン生成、Bison-Flexベース、ボトムを使用しているようです解析中。

ここに誰かがこれが事実であることを確認してもらえますか?もしそうなら、なぜ主流のコンパイラフレームワークは手書きのパーサーを使用するのですか?

Updateこのトピックに関する興味深いブログはこちら

83
JCLL

はい:

72

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(ドラフト)を処理するようになりました。

97
Ira Baxter

Clangのパーサーは手書きの再帰下降パーサーであり、他のいくつかのオープンソースおよび商用のCおよびC++フロントエンドも同様です。

Clangは、いくつかの理由で再帰下降パーサーを使用します。

  • パフォーマンス:手書きのパーサーを使用すると、必要に応じてホットパスを最適化して高速パーサーを記述でき、常にそのパフォーマンスを制御できます。高速なパーサーを使用することで、「実際の」パーサーが通常使用されない他の開発ツール(IDEでの構文の強調表示やコード補完など)でClangを使用できるようになりました。
  • 診断とエラー回復:手書きの再帰下降パーサーで完全に制御されているため、一般的な問題を検出する特別なケースを簡単に追加できます優れた診断とエラー回復を提供します(たとえば、 http://clang.llvm.org/features.html#expressivediags を参照)。自動生成されたパーサーでは、ジェネレーターの機能に制限されます。
  • Simplicity:再帰下降パーサーは、記述、理解、およびデバッグが簡単です。解析の専門家である必要はありませんし、パーサーを拡張/改善するための新しいツールを学ぶ必要もありません(これはオープンソースプロジェクトにとって特に重要です)が、それでも素晴らしい結果を得ることができます。

全体として、C++コンパイラーにとっては、それほど重要ではありません。C++の構文解析部分は重要ですが、それでも簡単な部分の1つであるため、単純に保つほうが有利です。セマンティック分析---特に名前検索、初期化、オーバーロード解決、テンプレートのインスタンス化---解析よりも桁違いに複雑です。証明が必要な場合は、Clangの「セマ」コンポーネント(セマンティック分析用)と「構文解析」コンポーネント(解析用)のコードとコミットの配布を確認してください。

30
Doug

gccのパーサーは手書きです。 。 clangについても同じことが疑われます。これにはおそらくいくつかの理由があります。

  • パフォーマンス:特定のタスク用に手動で最適化したものは、ほとんどの場合、一般的なソリューションよりも優れたパフォーマンスを発揮します。通常、抽象化はパフォーマンスに影響します
  • タイミング:少なくともGCCの場合、GCCは多くの無料の開発者ツールよりも前のものです(1987年に登場)。当時はyaccなどの無料版はありませんでしたが、FSFの人々にとってこれは優先事項だったと思います。

これはおそらく「ここで発明されていない」症候群のケースではありませんが、「私たちが必要とするもののために特別に最適化されたものは何もなかったので、独自に作成しました」。

8
Rafe Kettler

奇妙な答えがあります!

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パーサー開発の次のステップになるのでしょうか?

6
reuns