符号表解析后的人口; 编译器构建

创建解析树后,我现在必须填充符号表。

我必须存储信息

标识符的类型,范围,偏移等。

现在我怎么知道标识符的类型和范围,因为我所知道的是该特定ID的词位值和行号(在词法分析之后)。

我如何得到整个事情。 谢谢。

现在我怎么知道标识符的类型和范围,因为我所知道的是该特定ID的词位值和行号(在词法分析之后)。

正如EJP所提到的,您需要逐步完成解析树。 您的树应该已经创建,以便您可以执行有序遍历,以与评估源代码语句和表达式相同的顺序访问每个节点。 您的树节点还应该与特定的语言构造对应,例如WhileStmtNodeMethodDeclNode等。

假设我正在构建符号表,递归地逐步遍历树,我刚刚输入了一个方法体节点。 我可能会有以下内容:

 public void doAction(MethodBodyNode methodBody) { currScope = 2; methodBody.getExpr().applyAction(this); currScope = 2; } 

我保留一个全局变量来管理范围。 每当我进入范围改变的块时,我都会增加currScope 。 类似地,我将currClasscurrMethod变量保存为与后续阶段的符号名称,类型,偏移等一起存储。

更新:

现在说,我正在遍历树,每当我遇到一个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;匹配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的字符串值:对于标识符和字符串/布尔/整数文字。

看看这个例子。 在S减少到(S + F)的第一次减少期间,您将ParenExprNode附加到树根。 您还将AddExprNode附加为AddExprNodeParenExprNode 。 在应用语法规则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; } 

等等…

我所知道的是该特定ID的词汇值和行号

这不是真的。 您知道在解析树中声明它的位置,它会告诉您所需的一切。 您可以通过处理解析树来执行此步骤。