zoukankan      html  css  js  c++  java
  • 【编译系统01】编译器

    时间: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

  • 相关阅读:
    C# WinForm TextBox 作为密码输入框时,如何禁止密码查看器获取密码 ?
    .net 程序运行在不同框架版本下的支持配置(主要是.net4.0 与 .net2.0的兼容)
    比较C#的静态常量(const)和动态常量(static和readonly)
    Linux 本地yum源 、阿里yum源、163yum源的配置安装
    Mysql 单机数据库迁移几种方式
    sed中使用变量及变量中存在特殊字符‘/’处理
    Linux下安装zookeeper、配置zookeeper开机自启动
    MySQL 不同场景下的迁移方案(转载)
    配置YUM源出现Errno 14 Could not open/read repomd.xml 或者 "Couldn't open file /mnt/cdrom/repodata/repomd.xml" 错误的解决办法
    Docker安装Rabbitmq并实现挂载宿主机数据目录
  • 原文地址:https://www.cnblogs.com/onetrainee/p/11955711.html
Copyright © 2011-2022 走看看