创建解析树后,我现在必须填充符号表。
我必须存储信息
标识符的类型,范围,偏移等。
现在我怎么知道标识符的类型和范围,因为我所知道的是该特定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;
匹配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
附加为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; }
等等…
我所知道的是该特定ID的词汇值和行号
这不是真的。 您知道在解析树中声明它的位置,它会告诉您所需的一切。 您可以通过处理解析树来执行此步骤。
以上就是c/c++开发分享符号表解析后的人口; 编译器构建相关内容,想了解更多C/C++开发(异常处理)及C/C++游戏开发关注计算机技术网(www.ctvol.com)!)。
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/c-cdevelopment/560555.html