# lab3 **Repository Path**: vistorjeff/lab3 ## Basic Information - **Project Name**: lab3 - **Description**: 编译原理实验三 - **Primary Language**: C - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-06-11 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README globals.h的介绍: 1. globals是全局文件,包含了要引用的c标准库,同时也定义了token的类型。 2. 使用extern关键字声明了source(输入),listing(输出)和lineno(指出当前token属于哪一行)。 3. 定义了语法分析树的结构,父节点和子节点的物理连接一般来说只有一个左孩子,节点的最大孩子数是3个(if语句中)。节点的sibling属性是用来连接兄弟节点的。因此,语法分析树的结构就是,父节点连接孩子节点和其父节点,每个孩子节点又会和他的兄弟节点线性连接。 4. 文件最后定义了一些标签,用来在main函数中决定输出(会影响包含的头文件) util.c的介绍 1. util.c的函数声明在util.h文件中。 2. printToken()函数和实验二基本一样。 3. 定义了几个新建语法分析树节点的函数,分别是新建statement节点,新建method节点,新建expression节点和新建param节点。 4. 定义了一个copyString()函数,用法就是copyString(tokenString),避免节点中的属性值随着tokenString的变化而变化(因为是指针赋值)。 5. 定义了一个indentno,用来记录缩进,更好地展示语法分析树的结构。printSpace()函数就是打印缩进的。 6. printParamType()函数是打印param节点中对应的参数变量类型的。 7. printTree()函数以递归的方式打印整个语法树,每递归一次都会缩进。当tree指针(指向当前的节点)非空的时候,函数根据节点的类型nodekind选择打印的格式,打印完该节点后,使用循环printTree全部子节点(递归),循环结束后,把tree指针指向兄弟节点。 scan.c的介绍 1. getToken()函数和实验二的思路一样,但是使用的函数不一样。 2. getNextChar()函数使用linepos变量读取一个char。函数会使用fgets一次读入一行数据并获取该行的字符长度记为bufsize,当linepos超过bufsize的时候,就把linepos清零,然后读取下一行。正常情况下,每次返回lineBuf[linepos++]。 3. ungetNextChar()函数是把当前字符退回到输入流中,就是linepos--,下一次还是读取这个字符。 4. 定义了一个包含全部关键字的数组,读完一个token之后就去和数组里面的str比较,不一样的话这个token是变量名,返回ID,一样的话返回对应的token。因为这个数组定义的问题,识别的关键字是大小写敏感的。 parse.c的介绍 1. syntaxError()函数是用来打印错误信息的。 2. match(TokenType)函数用来匹配一个token,如果相同,就跳过这个token,get下一个。如果不相同,就打印错误信息,这个token是属于下一个statement的,不需要跳过。 3. 定义了一系列用于递归的函数,这东西是看着文法来实现的(但我只用了一部分老师给的文法),例如: if_stmt -> IF expression THEN statement ELSE statement END 那么对于这个文法,就可以: TreeNode *if_stmt(void) { TreeNode *t = newStmtNode(IfK); // 调用函数创建一个statement节点,并标记为IfK说明这是一个if语句 match(IF); // 匹配一个IF,注意此时已经跳过了IF t->child[0] = expression(); // 进去expression函数等着返回吧,注意看函数返回之后token是否更新,这里假设更新了 match(THEN); t->child[1] = statement(); // 同理 match(ELSE); t->child[2] = statement(); // 同理 match(END); return t; } 对于有多个statement,我用的是stmt_sequence: TreeNode *stmt_sequence(void) { TreeNode *t = statement(); // 这个statement节点作为最左节点,后面就是statement节点通过sibling指针串起来 TreeNode *p = t; // 不是终结符的话,就还没有结束,这个ELSE我也不知道是不是,先写着 while ((token != ENDFILE) && (token != END) && (token != ELSE)) { TreeNode *q; q = statement(); if (q != NULL) { if (t == NULL) t = p = q; else /* now p cannot be NULL either */ { p->sibling = q; p = q; } } } return t; } 4. parse()函数从文法的入口进去。