質問

解析ツリーを作成した後、シンボルテーブルにデータを入力する必要があります。

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

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

さて、私が知っているのはその特定の ID の語彙素値と行番号 (字句解析後) だけであるため、識別子の型と範囲を知るにはどうすればよいでしょうか。

全体をどうやって理解すればいいですか。ありがとう。

役に立ちましたか?

解決

私が知っているのは、その特定のIDの語彙体値と線番号(語彙分析後)であるため、識別子のタイプ、範囲を知るにはどうすればよいですか。

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

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

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), を追加します。 ParenExprNode 木の根元まで。また、 AddExprNode の子として ParenExprNode. 。文法のルール 2 による削減を適用するときは、そのロジックをパーサーにハードコーディングする必要があります。

木:

    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;
}

等々...

他のヒント

私が知っているのは、その特定のID

のlexeme値と行番号です。

それは本当ではありません。あなたはそれが解析木で宣言されている場所を知っています。このステップを実行します。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top