author: Kevin Lynx email: zmhn320#163.com date: 3.7.2009
词法分析
词法分析属于整个编译流程中的第一个阶段。为什么要把编译过程分为多个阶段,这就
如同软件分层一样,个人觉得是出于降低复杂性的考虑。
再次声明我不会告诉你任何编译原理的理论知识,因为坦率地说我也不会:D。所以我努
力将我们需要了解的概念尽可能简单地告诉你。当然,可能会与教科书不吻合。
什么是词法分析?
词法分析就是把一段话整理成单词集合。举个简单的例子,例如有代码:age = age + 1;,
经过词法分析后,将得到:age、=、age、+、1、;几个符号。为了方便,我称每个单词为一
个token。
词法分析的作用
词法分析分析出来的单词集合,直接作为编译流程中接下来的语法分析的输入。那么语
法分析阶段面对的将是一个一个的token,而不是单个的字符。
例如,在处理age = age + 1;这种语句时,当我们获取到token "="时,我们直接期望接
下来的token应该是个表达式。以单词为单位地处理,比直接处理单个字符简单很多。
词法分析的过程
词法分析的输入是单个字符流,一般我们fopen一个源代码文件,保存在一个char缓存
里,这就是词法分析的输入。而词法分析的最终输出结果就是一个一个的token。
为了处理方便,token并不是单纯的单词。通常我们会将源代码中的所有单词分类,例
如变量名其实都属于一类token。简单的token可定义为:
struct Token
{
int type;
char value[256];
};
type用于表示token的类型,例如一个变量名token的类型是一个标识符。value可以用
来具体地保存这个变量的名字。
对于type的处理,通常会事先定义一组枚举值,例如:
enum { ID, NUM, STRING, IF, ELSE, WHILE, RETURN, FUNCTION }等等用于标示
在一个源代码中可能出现的所有token。
虽然说词法分析的结果是一个token集合,但事实上我们并不是一次做完词法分析。通常
词法分析模块提供一个get_token函数。每次调用该函数时,都返回源代码中下一个token。
例如,有源代码:age = age + 1;
第一次调用get_token将获得 { ID, "age" },第二次获得 { ASSIGN, "=" },第三次
获得{ ID, "age" },等等。
那么,词法分析该如何实现?也就是struct Token get_token()函数如何实现?其实很
简单,你告诉我:给你一个字符串,你如何判断这个字符串全部是数字?
int is_num( const char *str )
{
while( *str != 0 )
{
if( !isdigit( *str++ ) ) return 0;
}
return 1;
}
所以,基本上,词法分析的过程也就是这个过程。就拿标识符举例,典型的标识符一般
以字符开头,然后接着是数字或字符或_,当遇到非法字符时,这个标识符的扫描即结束。
词法分析一般是个while+switch:
struct Token get_token()
{
while( current_char != 0 )
{
switch( current_char )
{
case CHARACTER:
/* 扫描一个标识符 token */
break;
case '=':
/* 获得一个 ASSIGN token */
break;
...
}
}
}
现在,试着去总结一门语言里的每一个token的规则,然后自己去写写看。
代码导读
在本节我将提供kl在googlecode的SVN上的代码,先不要去管代码包中的其他东西。关于
词法的代码可以在kllex.c kllex.h中找到。lex_token是提供给其他模块的接口,用于获取
当前扫描的token。扫描结果可以通过lexState结构体获取。
再次提下版权问题,代码文件以及代码包中我并没有加入任何版权说明,哪怕是GPL。
但是如同我之前说的一样,我不介意你传播、改动此代码,但是请保留原作者信息。当然,
我并不介意你加上@modified by xxx:)。
下载kl源代码:http://klcommon.googlecode.com/files/kllan_0.1.0.zip