Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/72.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/xamarin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 解析后的符号表填充;编译大楼_C_Compiler Construction_Symbol Table - Fatal编程技术网

C 解析后的符号表填充;编译大楼

C 解析后的符号表填充;编译大楼,c,compiler-construction,symbol-table,C,Compiler Construction,Symbol Table,创建解析树之后,我现在必须填充符号表 我必须像这样存储信息 标识符的类型、范围、偏移量等 现在我如何知道标识符的类型和范围,因为我只知道该特定ID的词素值和行号(在词法分析之后) 我是怎么知道整件事的。谢谢 我只知道该特定ID的词素值和行号 那不是真的。您知道它在解析树中的声明位置,它告诉您所需的一切。这一步可以通过处理解析树来完成 现在我如何知道标识符的类型和范围呢 know是该特定ID的词素值和行号(在 词汇分析) 正如EJP所提到的,您需要逐步完成解析树。您应该已经创建了树,这样您就可以按

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

我必须像这样存储信息

标识符的类型、范围、偏移量等

现在我如何知道标识符的类型和范围,因为我只知道该特定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)生成的标记列表中的下一个标记..如果下一个标记位于非终结符的第一个中,则将该规则添加为解析树,其中