解析后的符号表填充;编译器构建
-
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;
并将变量声明节点追加到树中。该节点封装了包含lexeme的行号、字符号和字符串值的两个标识符节点。第一个标识符节点是类型,第二个是变量名。 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;
}
}
当你有一个像这样的节点的语法树时,你就得到了你需要的所有信息。
第二次更新:
无论您是使用自定义解析器还是生成解析器,都有一个明显的点,您可以在匹配生产时将节点添加到树中。你用的是什么语言并不重要。C结构会做得很好。
如果它是一个非终端有信息作为Nonterminals名称,如果它 是终端即一个令牌,然后是令牌中的信息,即词素值, 存储令牌名称和行号
您必须在树中有专门的节点,例如ClassNode,TypeNode,MethodDeclNode,IfStmtNode,ExprNode。您不能只存储一种类型的节点并将非终端和终端放入其中。一个非终端被表示为一个树节点,在组成它的部分(通常是非终端本身)旁边没有其他信息可以存储它。您不会存储任何令牌信息。只有少数几个实例可以实际存储lexeme的字符串值:用于标识符和字符串/布尔值/整数字面值。
看看 这 例子。在第一次减少时 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值和行号
这不是真的。您知道它在解析树中声明的位置,它告诉您所需的一切。您通过处理解析树来完成此步骤。