【编译系统01】编译器 - 词法分析器(lexial)的设计思路
时间:2019/11/29
首先,词法分析器由一个扫描器与状态机组成。
一. 词法分析器整体设计流程
二、设计细节
1. code.txt:
我们假设读取下面文本
2.符号类型的设计
我们使用 enum 数据结构,其好处有两点:
1. 只能选取其成员中的一个。
2. 可以直接用符号的名字命名变量。
1 enum symbol { 2 KW_MAIN, KW_IF, KW_ELSE, KW_WHILE, KW_INT, KW_COUT, KW_CIN, KW_ENDL, KW_RETURN, // 关键字 3 LBRACE, RBRACE, LPAREN, RPAREN, SEMI, COMMA, // 界符 4 ASSIGN, ADD, MINUS, TIMES, DIVIDE, // 运算 5 EQ, NE, G, GE, B, BE, IN, OUT, // 判断标志位 6 STR, NUM, ID, // 字符串,数字,标识符 7 END // 文件结尾 8 };
3. 扫描器
扫描器会仅仅读取一个字符,先将旧的字符保存,再将读取的字符存储在全局变量ch中,如果读取成功,返回0,否则返回1.
1 int getCh() { 2 oldch = ch; 3 if (fscanf_s(fin, "%c", &ch) == EOF) { 4 return 0; 5 } 6 else { 7 return 1; 8 } 9 }
4.有限状态机:
其通过当前所读取的字符 ch 来判断其种类。
1)数字:比如如果为 '0',则表示数字,依次"读取-计算-存储"直到不是数字时再退出,将值保存在num中,sym赋值NUM,之后返回。
1 else if(ch>='0' && ch<='9') 2 { 3 int value = 0; 4 do { 5 value = value * 10 + ch -'0'; 6 f = getCh(); 7 } while (ch >= '0' && ch <= '9'); // 判断条件一定要明确 8 9 sym = NUM; 10 num = value; 11 12 }
2) ‘=’ 与 ‘==’的区分:当遇到 = 时,超前读取一个字符,来判断。 如果是 == ,在继续读取一个字符(供之后判断使用),否则不用读取。
1 case '=': 2 f = getCh(); 3 if (ch == '=') { 4 sym = EQ; 5 // 再往前读一行 6 f = getCh(); 7 } 8 else { 9 sym = ASSIGN; 10 } 11 break;
3) 变量名 与 关键字之前的区分:我们设计了一个数组,当为 变量名或关键字时进行判断。
1 /* 2 关键字检索器:将标识符与id区分开来,并打上标记 sym; 3 */ 4 const int arrLen = 9; 5 const char * keyWords[arrLen] = { "main","if","else","while","int","cout","cin","endl","return" }; 6 const enum symbol keySymbols[arrLen] = { KW_MAIN, KW_IF, KW_ELSE, KW_WHILE, KW_INT, KW_COUT, KW_CIN, KW_ENDL, KW_RETURN }; 7 8 void checkKeyWord() { 9 // 将id来遍历 keyWords[] 数组,打上标签 10 int flag = 0; // 标记是否是关键字 11 for (int i = 0; i < arrLen; i++) { 12 if (strcmp(id, keyWords[i]) == 0) { 13 sym = keySymbols[i]; // 完成赋值 14 flag = 1; 15 break; 16 } 17 } 18 if (flag == 0) 19 sym = ID; 20 }
三、验证检查的可靠性
1. 我们先简单地写了一下测试代码:
// // 测试词法扫描器 // string filename = "C:\\Users\\97905\\source\\repos\\T编译器\\Debug\\abc.txt"; int ErrorCode = fopen_s(&fin, filename.c_str(), "r"); if (0 == ErrorCode) { cout << "打开文件成功" << endl; } else { cout << "错误码: " << ErrorCode << endl; return 0; } // 循环解析符号 getCh(); // 启动时必须先读取第一个字符 while (getSym()) { if (sym == STR) { // 输出字符串 cout << "(string," << str << ")"<< endl; } else if (sym == NUM) { // 输出数字 cout << "(NUM," << num << ")" << endl; } else if (sym == ID) { cout << "(ID," << id << ")" << endl; } else { cout <<"(关键字或字符," << symbolName[sym] <<")" << endl; } } if (sym == END) { cout << "文件读取结束" << endl; } return 0;
2. 将上面那个代码填入进去,最终的测试结果如下。
符合测试要求
四、下一步计划
写一个简单的 "int i;" 与 "i = 3;"的 语法分析程序 与 语义分析程序。
"int i;" 时建立变量记录表
"i = 3;" 时在变量表中查询变量,并且赋值。
这个计划用 MAP 数据结构来实现(毕竟查找比较方便)
预计更新日期:2019/11/30