C 解析后的符号表填充;编译大楼
创建解析树之后,我现在必须填充符号表 我必须像这样存储信息 标识符的类型、范围、偏移量等 现在我如何知道标识符的类型和范围,因为我只知道该特定ID的词素值和行号(在词法分析之后) 我是怎么知道整件事的。谢谢 我只知道该特定ID的词素值和行号 那不是真的。您知道它在解析树中的声明位置,它告诉您所需的一切。这一步可以通过处理解析树来完成 现在我如何知道标识符的类型和范围呢 know是该特定ID的词素值和行号(在 词汇分析) 正如EJP所提到的,您需要逐步完成解析树。您应该已经创建了树,这样您就可以按顺序遍历,以计算源代码语句和表达式的相同顺序访问每个节点。树节点还应与特定的语言结构相对应,例如,C 解析后的符号表填充;编译大楼,c,compiler-construction,symbol-table,C,Compiler Construction,Symbol Table,创建解析树之后,我现在必须填充符号表 我必须像这样存储信息 标识符的类型、范围、偏移量等 现在我如何知道标识符的类型和范围,因为我只知道该特定ID的词素值和行号(在词法分析之后) 我是怎么知道整件事的。谢谢 我只知道该特定ID的词素值和行号 那不是真的。您知道它在解析树中的声明位置,它告诉您所需的一切。这一步可以通过处理解析树来完成 现在我如何知道标识符的类型和范围呢 know是该特定ID的词素值和行号(在 词汇分析) 正如EJP所提到的,您需要逐步完成解析树。您应该已经创建了树,这样您就可以按
whilestNode
,MethodDeclNode
,等等
假设我正在构建符号表,递归地遍历树,并且我刚刚输入了一个方法体节点。我可能会有类似以下内容:
public void doAction(MethodBodyNode methodBody) {
currScope = 2;
methodBody.getExpr().applyAction(this);
currScope = 2;
}
我保留了一个全局变量来管理范围。每次我输入一个作用域发生变化的块时,我都会增加currScope
。类似地,我将维护currClass
和currcmethod
变量,以便在以后的阶段使用符号名、类型、偏移量等进行存储
更新:
现在说,我正在穿越这棵树,每次我遇到一个ID,我
必须将值与类型一起输入符号表,
scope和其他,比如scope,我检查是否遇到{或
函数名,但我如何知道这是什么类型的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;
并将变量声明节点附加到树中。该节点封装了两个标识符节点,其中包含词素的行号、字符号和字符串值。第一个标识符节点是类型,第二个是变量名。ID
是返回的终端符号通过lexer匹配标识符。我假设您在某种程度上熟悉这一点
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结构都可以
如果是非终端,则将信息作为非终端名称,如果
是终端,即令牌,然后是令牌中的信息,即词素值,
存储令牌名称和行号
树中必须有专门的节点,例如ClassNode、TypeNode、MethodDeclNode、IfsmtNode、ExprNode。不能只存储一种类型的节点并在其中放置非终端和终端。非终端表示为树节点,除了组成它的部分之外,没有其他信息可存储,这些部分通常是非终端NAL本身。您不会存储任何令牌信息。只有少数实例可以实际存储词素的字符串值:标识符和字符串/布尔/整数文本
请看一个示例。在第一次缩减期间,当S
缩减为(S+F)时
,您将ParenExprNode
附加到树根。您还将AddExprNode
附加为ParenExprNode
的子级。应用语法规则2的约简时,必须将该逻辑硬编码到解析器中
树:
ExprNode (root)
|
ParenExprNode
|
AddExprNode
/ \
ExprNode ExprNode
ExprNode
|
ParenExprNode
|
AddExprNode
/ \
ExprNode ExprNode
|
IdNode
守则:
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;
}
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;
}
在下一步中,从输入中删除左括号。然后,S
位于堆栈顶部,并根据规则1将其缩减为F
。由于F
是标识符的非末端,因此它由IdNode
表示
树:
ExprNode (root)
|
ParenExprNode
|
AddExprNode
/ \
ExprNode ExprNode
ExprNode
|
ParenExprNode
|
AddExprNode
/ \
ExprNode ExprNode
|
IdNode
守则:
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;
}
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;
}
诸如此类……处理是指创建属性语法(继承属性)[我刚刚遇到这些术语,因此我可能完全错了],对于作用域,我可以检查是否遇到了新的一对括号,或者是否有新的函数。@Kraken是的,这就是我的意思。你永远不会只处理词素和行号的符号。所以我使用C,我的parsetree节点有信息词素值和标记名,即if/if或NAme/ID或12/INTEGER。现在,我正在遍历在树上,每次我遇到一个ID时,我都必须在符号表中输入值,以及类型、范围和其他内容,比如我检查是否遇到范围'{'或函数名,但我如何知道这是什么类型的ID。@Kraken:听起来你没有正确构建树。请参阅更新。让我带你看看我迄今为止所做的一切。1.词法分析:正确标记词法,维护在传递源代码期间收集的所有信息(即词法值、标记名、行号).2.语法分析:预测解析器(使用解析表)使用时,我将语法规则维护为数组规则,数组的每个条目都是一个链表,并维护每个规则。同样,也会维护第一组和后续组。现在,我维护一个带有起始符号的堆栈,检查由词法分析器和(coninued)生成的标记列表中的下一个标记..如果下一个标记位于非终结符的第一个中,则将该规则添加为解析树,其中