0

解析ツリーを作成した後、シンボル テーブルを設定する必要があります。

次のような情報を保存する必要があります

識別子のタイプ、スコープ、オフセットなど。

私が知っているのは、その特定の ID の語彙素値と行番号だけなので (字句解析後)、識別子のタイプとスコープをどのように知ることができますか。

どうやって全体を手に入れたのですか。ありがとう。

4

2 に答える 2

2

私が知っているのは、その特定のIDの語彙素の値と行番号だけであるため(語彙分析後)、どのようにして識別子のタイプ、スコープを知ることができますか。

EJP が述べたように、解析ツリーをステップスルーする必要があります。ツリーは、ソース コードのステートメントと式が評価されるのと同じ順序で各ノードにアクセスして、順序どおりに走査できるように作成されているはずです。ツリー ノードはWhileStmtNodeMethodDeclNode、 などの特定の言語構造にも対応している必要があります。

ツリーを再帰的にステップ実行してシンボル テーブルを構築していて、メソッド本体ノードに入ったとします。次のようなものがあるかもしれません:

public void doAction(MethodBodyNode methodBody) {
    currScope = 2;
    methodBody.getExpr().applyAction(this);
    currScope = 2;
}

スコープを管理するためにグローバル変数を保持しています。スコープが変わるブロックに入るたびに、 をインクリメントしますcurrScope。同様に、後のフェーズのためにシンボル名、タイプ、オフセットなどを格納する変数を維持currClassします。currMethod

アップデート:

たとえば、ツリーをトラバースしているとします。ID に出くわすたびに、タイプ、スコープなどとともにシンボル テーブルに値を入力する必要があります。スコープについては、'{' または関数名に出くわすかどうかを確認しますしかし、これがどのタイプのIDであるかをどうやって知ることができますか。

各ツリー ノードには、構造全体に必要なすべての情報が含まれている必要があります。CUP や Bison などのパーサー ジェネレーターを使用している場合は、文法アクションでツリーを構築する方法を指定できます。例えば

variableDeclaration::= identifier:i identifier:i2 SEMICOLON {: RESULT = new VarDeclNode(i, i2, null); :};
identifier::= ID:i {: RESULT = new IdNode(i.getLineNum(), i.getCharNum(), i.getStringValue()); :};

これらのプロダクションは一致Foo f;し、変数宣言ノードをツリーに追加します。そのノードは、語彙素の行番号、文字番号、および文字列値を含む 2 つの識別子ノードをカプセル化します。最初の識別子ノードは型で、2 番目は変数名です。ID識別子の一致時にレクサーによって返される終端記号です。これについては、ある程度ご存知だと思います。

public class VarDeclNode extends StmtNode {

    private IdNode id;
    private IdNode type;
    private ExprNode expr;

    public VarDeclNode(IdNode id, IdNode type, ExprNode expr) {
        super();
        this.id = id;
        this.type = type;
        this.expr = expr;
    }

}

このようなノードを持つ構文ツリーがあれば、必要なすべての情報が得られます。

2 回目の更新:

カスタム パーサーを使用しているか、生成されたパーサーを使用しているかは問題ではありません。プロダクションの照合時にツリーにノードを追加する際に、1 つの明確なポイントがあります。また、使用している言語は関係ありません。C 構造体は問題なく動作します。

非端末の場合は非端末名として情報を持ち、端末、つまりトークンの場合、トークンの情報、つまり語彙素値、トークン名、および行番号が保存されます

ツリーには、ClassNode、TypeNode、MethodDeclNode、IfStmtNode、ExprNode などの特殊なノードが必要です。1 つのタイプのノードを保存して、非ターミナルとターミナルをその中に入れることはできません。非ターミナルはツリー ノードとして表されます。それを構成するパーツ (通常は非ターミナル自体) 以外に保存する情報はありません。トークン情報を保存しません。語彙素の文字列値を実際に格納するインスタンスは、ほんのわずかしかありません: 識別子用および文字列/ブール/整数リテラル用です。

この例を見てください。Sが に還元される最初の還元中に、ツリーのルートに(S + F)a を追加します。の子としてParenExprNodeも追加します。文法のルール 2 による削減を適用する場合、そのロジックはパーサーにハードコーディングする必要があります。AddExprNodeParenExprNode

ツリー:

    ExprNode (root)
       |
  ParenExprNode
       |
   AddExprNode
   /         \
ExprNode   ExprNode

コード:

struct ExprNode { void* exprNode; };
struct ParenExprNode { void* exprNode; };
struct AddExprNode { void* op1, * op2; };
struct IdNode { char* val; int line; int charNum; };
struct IntLiteralNode { int val; int line; int charNum; };

void reduce_rule_2(ExprNode* expr) {

    //remove stack symbols

    //create nodes
    struct ParenExprNode* parenExpr = malloc(sizeof(struct ParenExprNode));
    struct AddExprNode* addExpr = malloc(sizeof(struct AddExprNode));
    addExpr->op1 = malloc(sizeof(struct ExprNode));
    addExpr->op2 = malloc(sizeof(struct ExprNode));

    //link them
    parenExpr->exprNode = (void*)addExpr;
    expr->exprNode = (void*)parenExpr;
}

次のステップでは、左括弧が入力から削除されます。その後、Sはスタックの一番上にあり、Fルール 1 によって縮小されます。Fは識別子の非終端記号であるため、 で表されIdNodeます。

ツリー:

    ExprNode
       |
  ParenExprNode
       |
   AddExprNode
   /         \
ExprNode   ExprNode
   |
 IdNode

コード:

reduce_rule_2(addExpr->op1);

void reduce_rule_1(ExprNode* expr) {
    //reduce stack symbols
    struct IdNode* id = malloc(sizeof(struct IdNode));
    id->val = parser_matched_text();
    id->lineNum = parser_line_num();
    id->charNum = parser_char_num();
    expr->exprNode = (void*)id;
}

等々...

于 2012-03-17T16:46:35.817 に答える
0

私が知っているのは、その特定の ID の語彙素の値と行番号だけです

それは真実ではない。解析ツリーのどこで宣言されているかを知っているので、必要なものがすべてわかります。このステップは、解析ツリーを処理することによって行います。

于 2012-03-15T00:44:01.653 に答える