解析後のシンボルテーブルの母集団。コンパイラの構築
-
13-12-2019 - |
質問
解析ツリーを作成した後、シンボルテーブルにデータを入力する必要があります。
次のような情報を保存する必要があります
識別子のタイプ、スコープ、オフセットなど。
さて、私が知っているのはその特定の 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値と行番号です。
それは本当ではありません。あなたはそれが解析木で宣言されている場所を知っています。このステップを実行します。