解析ツリーを作成した後、シンボル テーブルを設定する必要があります。
次のような情報を保存する必要があります
識別子のタイプ、スコープ、オフセットなど。
私が知っているのは、その特定の ID の語彙素値と行番号だけなので (字句解析後)、識別子のタイプとスコープをどのように知ることができますか。
どうやって全体を手に入れたのですか。ありがとう。
解析ツリーを作成した後、シンボル テーブルを設定する必要があります。
次のような情報を保存する必要があります
識別子のタイプ、スコープ、オフセットなど。
私が知っているのは、その特定の 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)
a を追加します。の子としてParenExprNode
も追加します。文法のルール 2 による削減を適用する場合、そのロジックはパーサーにハードコーディングする必要があります。AddExprNode
ParenExprNode
ツリー:
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 の語彙素の値と行番号だけです
それは真実ではない。解析ツリーのどこで宣言されているかを知っているので、必要なものがすべてわかります。このステップは、解析ツリーを処理することによって行います。