4

ドラゴンブックを持っているけど、その話題は扱っていないようだ...

最新の言語では、コード内での出現が順不同であっても、特定の変数を使用することができます。

class Foo {
    void bar() {
        plonk = 42;
    }
    int plonk;
}

plonk関数の後に変数が宣言されていてもかまいません。

質問
これを実装するためのベストプラクティス/有用なパターンはありますか? 私の心に浮かぶ2つのアプローチがあります:

  1. 解析中に、目に見えないシンボルのダミー シンボルを追加します。宣言が解析されると、これらのダミーは実際のシンボルに置き換えられます。解析後、ダミーが残っているかどうかを確認し、残っている場合はエラーを出力します。

  2. 解析中はシンボル操作を行わず、AST のみを作成します。AST を解析した後、ノードに応じてシンボルを追加します。たとえば、クラスノードの場合、子のシンボルを追加してから処理します。たとえば、ステートメントブロックは子をステップスルーし、子が処理される直前にシンボルを追加します。

アプローチ1.の方が簡単で、「他のコンパイルユニットのインポート」などにも便利です。

編集:
アプローチ1で見られる問題は、順序付けられたシンボルに対して何らかの処理が必要なことです。たとえば、関数内では、使用する前にローカル シンボルを使用することはできません。

4

1 に答える 1

2

可能であれば、解析中に AST とシンボル テーブルを構築するだけです。次に、AST を介してパスを作成し、シンボルをシンボル テーブル エントリに関連付けます。それが基本的に戦略 2 です。

戦略 1 の問題点は、一般的なケースでは、すべての宣言を見るまで、同じ名前の 2 つのインスタンスが同じシンボルにバインドされていることを必ずしも認識できないことです。たとえば、シンボルのバインド ドメインが関数ブロックである (IMHO の間違いですが、好みはさまざまです) が、使用前にシンボルを宣言する必要がない JavaScript のような言語を考えてみましょう。この場合、関数に名前を付けるシンボルのみを考慮します。

疑似コード (合法的な JavaScript、結局のところ):

function outer() {
  return foo();

  function inner() {
    return foo();

    function foo() {
      return "inner's foo";
    }
  }

  function foo() {
     return "outer's foo";
  }
}

の 2 つの使用法はfoo異なるシンボルを参照しており、これは の最後の定義に到達するまでわかりませんfoo

戦略 2 の問題点は、使用されているシンボルについて何も知らずに AST を構築できるとは限らないことです。たとえば、C では、型名なのか関数に逆参照できるものなの(x)(y)かを知らずに式を解析することはできません。x(これも間違いです、私見ですが、私は誰ですか?)。C++ では、特定のシンボルがテンプレートかどうかも知る必要があります。多くの場合、これはシンボルの「タイプ」ではなく「種類」として記述されます。xC++ では、解析するの「型」を知る必要はありません(x)(y)。持っているかどうかを知る必要があるだけです。このため、C++ では宣言の前に特定のシンボルを使用できますが、宣言がtypedef.

異常なケースやマクロ プロセッサは別として、通常は解析中にスコープを定義し、各宣言をスコープにアタッチすることができます。通常、スコープは非常に単純な方法でネストされるため、スコープ ツリーを構築すると、シンボルが見つかるまでツリーを上っていくだけで、現在のスコープ ノードを指定して任意のシンボルを検索できます。

一部の言語 (Python など) では、宣言はオプションで暗黙的です。このような場合、シンボルが見つからない場合は、2 回目のパスで現在のスコープに新しい定義をアタッチできます。

于 2013-07-20T22:35:47.583 に答える