RE:从零开始的编译器编写 02 词法分析(1) 木灵的炼金工作室

词法分析

我们在上节说到,词法分析是将源程序的符号转化为计算机能够认识的符号。
具体是什么意思呢?比如一个语句x = x + 15;,这个语句在计算机看来就是一堆char,不具备实际的意义。因此我们需要用字符串处理的方式来告诉计算机:这句话实际上是变量 赋值运算符 变量 求和运算符 整形 语句结束

那么,很直观的,我们需要考虑“一个程序中会出现多少种类型的字符串”的问题。
比如,字符串1.2596应该属于浮点数类型,字符串"abcde"应该属于字符串类型,value应该属于变量类型,>=应该属于运算符类型等等。而这些类型都具有一个自己的特征,那么好了,我们可以根据这个特征来进行一个转换:

x = x + 15; -> 变量 赋值运算符 变量 求和运算符 整形 语句结束

但是,请考虑:这样的转换,是不是存在着数据丢失?
因此我们希望在转换的时候还保存着原始程序中蕴含的信息,即:转换为如下形式:

x = x + 15; -> 变量x 赋值运算符 变量x 求和运算符 整形15 语句结束

我们在部分类型之后附加了一个信息,这个在程序中是很容易做到的。同时我们也观察到,有些类型是不需要附加信息的。

这种所谓“类型”在编译原理术语中被称为”词法单元”或”词素”。

词法单元

一个词法单元通常由种类和附加信息组成。我们可以使用一个类来描述它们。使用类继承是一个很好的策略。
比如整形字面量和字符串字面量的词法单元可以写作如下的形式:

class token {
public:
    const int tag;
    explicit token(int t = 0) : tag(t){};
    virtual void print() const { cout << tag << '\t' << "TOKEN" << '\t' << (char)tag << endl; };
};

class num : public token { 
public:
    const int value;
    explicit num(int v) : value(v), token(NUM){};
    void print() const override { cout << tag << '\t' << "INTGE" << '\t' << value << endl; }
};
class word : public token {
public:
    const string lexeme;
    explicit word(int t, string S) : lexeme(S), token(t){};
    void print() const override { cout << tag << '\t' << "WORDS" << '\t' << lexeme << endl; }
};

我们将程序中可能出现的各种标识符均列举出来,就可以写出词法分析器的第一个文件:token.h。

这个文件,下一节实现。


Copyright AmachiInori 2017-2021. All Right Reserved.
Powered By Jekyll.
amachi.com.cn