词法分析是编译原理中最容易理解的,就算没有了解过编译原理,也能写出一个词法分析器。我们不用理解正则表达式,不用理解状态机原理,就可以轻松的完成词法的分析。
这里首先介绍下自顶向下的解析过程,所谓的自顶向下,按我的理解,就是从一个大的集合解析到小的集合。例如:解析一个文件,那么进入文件,解析一个函数,进入一个函数,解析局部变量,解析表达式,进入表达式,解析变量、常量等等,最终完成一个C文件的解析过程。整个过程,其实就是一个猜测的过程。但是这个过程中,我们必须依赖于文件中的每个词(token),token可以看成是解析过程中的一个单位。
例如:
1. 关键词有:int char double long for while ......
2. 运算符有:+ - * / ......
3. 数字常量:12 0x34 3.45
4. 字符串 :"hello"
...
等等.
那么我们必须实现一个函数get_token,执行这个函数,我们获取文件中的一个token。例如现在一个C文件:
int main(int agrc, char **argv ){
return 0;
}
那么多次执行get_token,分别得到的token为:
int
main
( <----③
int
argc
...
...
除了一个get_token函数外,还需要一个叫做put_back的函数,因为脚本解析是一个猜测的过程。有时候我们必须知道下一个的token是什么,才能判断该走哪个分支。还是上面的例子,在③的地方,我们得到了"(",所以知道main是一个函数,那么如果该token不是"(", 而是"=", 我们知道它不是一个函数,而是一个基本的变量定义,并且需要初始化。那么我们必须调用put_back函数,把该token重新放到缓存中,使得下次get_token的时候,还会拿到这个token,而不是下个token。
至于get_token和put_back函数如何实现,我就不多说了。我使用了最笨的方法,无非就是每个字符一个一个的向后扫描,判断是该返回什么标示。每个token被分为各种类型token_type:
enum tok_types{ DELIMITER = 1, IDENTIFIER, KEYWORD, TEMP, STRING, CHARACTOR, NUMBER, TYPE, BLOCK, PRECOMPILE };
类型 意义 例如
-----------------------------------------------------------------------------
DELIMITER 标示分隔符 ; | + -
IDENTIFIER 标示ID标示符 var hello
KEYWORD 关键字 int char while do
STRING 字符串 "string"
CHARACTOR 字符 'c'
NUMBER 数字常量 123 012 0x34
TYPE 类型 typedef int int32; 那么int32就被标示为TYPE
BLOCK 块标志 { }
PRECOMPILE 预编译行 #define
TEMP 保留
词法分析的目的就是扫描源码,区分出这些类型,变返回该token。供解释器的其他模块使用。