创建解析树后,我现在必须填充符号表。
我必须存储
等信息标识符的类型,范围,偏移等。
现在我怎么知道标识符的类型和范围,因为我所知道的是该特定ID的词位值和行号(在词法分析之后)。
我如何得到整件事。感谢。
答案 0 :(得分:2)
现在我怎么知道标识符的类型,范围,因为所有我 know是该特定ID的lexeme值和行号(之后 词汇分析)。
正如EJP所提到的,你需要逐步完成解析树。您的树应该已经创建,以便您可以执行有序遍历,以与评估源代码语句和表达式相同的顺序访问每个节点。您的树节点也应该与特定的语言结构相对应,例如WhileStmtNode
,MethodDeclNode
等
假设我正在构建符号表,递归地遍历树,我刚刚输入了一个方法体节点。我可能会有以下内容:
public void doAction(MethodBodyNode methodBody) {
currScope = 2;
methodBody.getExpr().applyAction(this);
currScope = 2;
}
我保留一个全局变量来管理范围。每次我输入范围变化的块时,我都会增加currScope
。同样,我会保留currClass
和currMethod
个变量来存储符号名称,类型,偏移等,以便以后进行。
<强>更新强>
现在说,每次遇到ID我都会穿越树 必须输入符号表的值以及类型, 范围和其他,说范围我检查我是否遇到'{'或 函数名称,但我怎么知道这是什么类型的ID。
每个树节点都应包含整个构造的所有必要信息。如果您正在使用解析器生成器(如CUP或Bison),则可以指定如何在语法操作中构建树。 E.g。
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结构会很好。
如果是非终端,则将信息作为非终端名称,如果是 是一个终端,即一个令牌,然后令牌中的信息,即lexeme值, 令牌名称和行号存储
您必须在树中拥有专门的节点,例如ClassNode,TypeNode,MethodDeclNode,IfStmtNode,ExprNode。您不能只存储一种类型的节点并将非终端和终端放入其中。非终端被表示为树节点,除了构成它的部分之外,没有其他信息可以存储它,它们本身通常是非终端的。您不会存储任何令牌信息。只有几个实例存储lexeme的字符串值:对于标识符和字符串/布尔/整数文字。
查看this示例。在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
位于堆栈顶部,并且由规则1减少到F
。由于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;
}
等等......
答案 1 :(得分:0)
我所知道的是该特定ID的词汇值和行号
那不是真的。您知道在解析树中声明它的位置,它会告诉您所需的一切。您可以通过处理解析树来执行此步骤。