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

时间:2012-03-14 13:45:45

标签: c compiler-construction symbol-table

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

我必须存储

等信息

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

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

我如何得到整件事。感谢。

2 个答案:

答案 0 :(得分:2)

  

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

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

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

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

我保留一个全局变量来管理范围。每次我输入范围变化的块时,我都会增加currScope。同样,我会保留currClasscurrMethod个变量来存储符号名称,类型,偏移等,以便以后进行。

<强>更新

  

现在说,每次遇到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的词汇值和行号

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