【编译系统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

 

posted @ 2019-11-29 09:22  OneTrainee  阅读(1006)  评论(0编辑  收藏  举报