5

Lex と YACC (実際には Flex と Bison) でコンパイラを作成しています。この言語では、任意のシンボル (C# など) への無制限の前方参照が可能です。問題は、識別子が何であるかを知らずに言語を解析できないことです。

私が知っている唯一の解決策は、ソース全体を lex してから「幅優先」解析を行うことです。これにより、クラス宣言や関数宣言などのより高いレベルのものは、それらを使用する関数の前に解析されます。ただし、これは大きなファイルの場合に大量のメモリを必要とし、YACC で処理するのは困難です (宣言/本文の種類ごとに個別の文法を作成する必要があります)。また、レクサーを手書きする必要があります (これはそれほど問題ではありません)。

効率についてはあまり気にしません (それでも重要ですが)。これは、完了したらコンパイラ自体を書き直すためですが、そのバージョンを高速にしたいと考えています (高速な一般的なLex/YACC では実行できないが、手動で実行できる手法については、それらも提案してください)。したがって、現時点では、開発の容易さが最も重要な要素です。

この問題に対する良い解決策はありますか? これは通常、C# や Java などの言語のコンパイラでどのように行われますか?

4

2 に答える 2

8

それを解析することは完全に可能です。識別子とキーワードの間にはあいまいさがありますが、lex はキーワードを優先することで喜んで対処します。

他にどんな問題があるかわかりません。解析段階で識別子が有効かどうかを判断する必要はありません。解析する際に、解析ツリーまたは抽象構文ツリー (違いは微妙ですが、この議論の目的には関係ありません) のいずれかを構築しています。その後、解析中に生成した AST に対してパスを実行して、ネストされたシンボル テーブル構造を構築します。次に、AST を介して別のパスを実行して、使用されている識別子が有効であることを確認します。これに続いて、AST を介して 1 つ以上の追加の解析を行い、出力コードまたはその他の中間データ構造を生成すれば完了です!

編集: それがどのように行われるかを確認したい場合は、Mono C# コンパイラのソース コードを確認してください。これは実際には C や C++ ではなく C# で書かれていますが、yacc に非常によく似た Jay の .NET ポートを使用しています。

于 2009-05-31T18:32:12.667 に答える
1

1つのオプションは、トークンをスキャンしてキャッシュするだけで前方参照に対処することです (「パニックモード」エラー回復のようなもの)。ファイル全体を実行したら、戻って、以前に解析しなかったビットを再解析してみてください。

レクサーを手書きする必要があることについて。しないでください。lex を使用して通常のパーサーを生成し、手書きの shim を介してそれを読み取るだけで、キャッシュからパーサーにフィードしたり、lex が作成したものをフィードしたりできます。

いくつかの文法を作成することに関しては、yacc ファイルのプリプロセッサで少し楽しんでください。同じ元のソースからすべてを作成できるはずです。

于 2009-06-01T16:43:30.120 に答える