(1)词法分析器的构造
工作流程:
1)首先跳过所有空白字符和注释,直到出现一个不是空白字符且不属于注释的字符,记为ch;
2)如果ch
=非法字符,则报错,转步骤一
=数字,则进行整型常量分析
=英文字母,则进行标识符分析
=单引号,则进行字符串常量分析
=其他(特殊符号),则进行操作符分析
3)返回分析结果(Kind,Value)值。
(2)语法分析器的构造
实现TINY+语言的语法分析、语义分析、汇编代码生成。
具体流程分析:
所需要的数据结构定义(包括Token结构体,Sym结构体,用来存储符号的各种属性,TreeNode结构体用来定义语法树的节点):
#ifndef STRUCT #define STRUCT #pragma once #includeusing namespace std; enum ObjType { OT_FUN = 0, // 函数 OT_VAR, // 变量 OT_CONST, // 常量 }; enum ValType { VT_INT = 100, // 整型数类型 VT_BOOL, // 布尔类型 VT_STRING, // 字符串类型 }; enum NodeType { PROGRAM = 200, // 程序(开始符号)节点 STMT_SEQUENCE, // 语句列表节点 IF_STMT, // 条件语句节点 REPEAT_STMT, // repeat语句节点 ASSIGN_STMT, // 赋值语句节点 READ_STMT, // read语句节点 WRITE_STMT, // write语句节点 WHILE_STMT, // while语句节点 GTR_EXP, // 大于表达式节点 GEQ_EXP, // 大于等于表达式节点 LSS_EXP, // 小于表达式节点 LEQ_EXP, // 小于等于表达式节点 EQU_EXP, // 等于表达式节点 LOG_OR_EXP, // 逻辑或表达式节点 LOG_AND_EXP, // 逻辑与表达式节点 LOG_NOT_EXP, // 逻辑非表达式节点 ADD_EXP, // 加法表达式节点 SUB_EXP, // 减法表达式节点 MUL_EXP, // 乘法表达式节点 DIV_EXP, // 除法表达式节点 FACTOR // 原子节点 ( ) }; enum kind_set { TK_INT = 300, TK_BOOL, TK_STRING, TK_TRUE, TK_FALSE, TK_OR, TK_AND, TK_NOT, TK_WHILE, TK_DO, TK_IF, TK_THEN, TK_ELSE, TK_END, TK_REPEAT, TK_UNTIL, TK_READ, TK_WRITE, TK_ID, TK_GTR = 400, TK_LEQ, TK_GEQ, TK_COMMA, TK_SEMICOLON, TK_ASSIGN, TK_ADD, TK_SUB, TK_MUL, TK_DIV, TK_LP, TK_RP, TK_LSS, TK_EQU, NUM }; typedef struct { int kind; // 符号的Kind值,如TK_IF等 int ival; // 若符号是整型常量,则ival记录该常量的值 string sval; // 若符号是标识符或字符串常量,则sval记录标识符的名字或字符串 }Token; typedef struct { Token tk; // 符号(词法分析器的返回值) ObjType objtype; // 符号对象类型 ValType valtype; // 值类型 //int addr; // 地址 //int size; // 字节数 //int level; // 层次 }Sym; typedef struct _TreeNode { NodeType nodetype; // 节点类型 ValType valtype; // 节点值类型 _TreeNode *child[3]; // 子节点的指针 Token tk; // 当节点是FACTOR类型时该成员才有效 } TreeNode; #endif
然后就是构造词法分析器来获得TOKEN,(词法分析器代码略去,代码有200多行)
定义符号表来存储标识符:
#pragma once #include "Struct.h" #includeusing namespace std; /** * 符号表 */ static int num=0; class SymTable { public: Sym* insertSym(string name) { Sym *sym=new Sym(); Token tk={NULL,NULL,name}; sym->tk=tk; s[num]=sym; num++; return sym; } Sym* findSym(string name) { for(int j=0;j tk.sval) return s[j]; } return NULL; } void delSym(string name) { int temp; for(int j=0;j tk.sval) { temp=j; break; } } for(int j=temp;j
然后就是定义各种节点的分析函数(代码略去,理由同上);
最后就是中间代码的生成。
所遇到的问题:
(1)Token结构体中的sval最初是用char *来存储,问题截图如下:
执行完match之后sval的值就发生改变了。
问题原因:Token temp=_token,temp的sval域是char*的指针,temp = _token后,它与_token的sval的地址是一样的。这样调用match会通过getToken修改_token的值,所以temp的sval的内容也跟着改变了。
解决办法(1):strcpy(temp.sval,_token.sval),这样通过字符串复制就可以了!不过感觉修改的比较多,很冗余。
解决办法(2):将sval的char*类型转换成string类型,这样不用指针就可以的!代码也很简洁。
希望解决的问题:既然temp的sval域和_token的sval的地址是一样的,那么为什么修改_token的值后,两者的值为什么不一样?

