在最开始的例子程序里,程序是由一些单词和符号组成的。其实程序就是一串长长的字符串,这些字符串是按一定的规则编写的,那么就需要检查这些单词和符号是否符合定义的规则。在
C
语言里,就是定义了
C
语法和语义。在最开始的例子里,
C
编译器最先进行词法分析的语句是下面这句:
typedef unsigned int size_t;
那么
C
编译器是怎么样把上面的字符串识别出来的呢?其实词法分析就是把上面的字符串识别为下面的单词:
typedef
unsigned
int
size_t
;
为了简单和比较方便,词法分析里会把这些单词用一个数字进行标识的,这样就容易存储和分析了。目标已经很明确,现在就来分析一下
LCC
的词法分析代码。
#001 init(argc, argv);
#002
#003 t = gettok();
#004
#005 (*IR->progbeg)(argc, argv);
在初始化函数
init
后面,就调用了词法分析函数
gettok
获取第一个记号。它的代码如下:
#001 int gettok(void)
#002 {
#003 for (;;)
#004 {
#005 register unsigned char *rcp = cp;
#006 while (map[*rcp]&BLANK)
#007 rcp++;
#008
#009 if (limit - rcp < MAXTOKEN)
#010 {
#011 cp = rcp;
#012 fillbuf();
#013 rcp = cp;
#014 }
#015
#016 src.file = file;
#017 src.x = (char *)rcp - line;
#018 src.y = lineno;
#019 cp = rcp + 1;
#020 switch (*rcp++)
#021 {
#022 case '/':
#023 if (*rcp == '*')
#024 {
#025 int c = 0;
#026 for (rcp++; *rcp != '/' || c != '*'; )
#027 if (map[*rcp]&NEWLINE) {
#028 if (rcp < limit)
#029 c = *rcp;
#030 cp = rcp + 1;
#031 nextline();
#032 rcp = cp;
#033 if (rcp == limit)
#034 break;
#035 } else
#036 c = *rcp++;
#037 if (rcp < limit)
#038 rcp++;
#039 else
#040 error("unclosed comment/n");
#041 cp = rcp;
#042 continue;
#043 }
#044 return '/';
#045 case '<':
#046 if (*rcp == '=') return cp++, LEQ;
#047 if (*rcp == '<') return cp++, LSHIFT;
#048 return '<';
#049 case '>':
#050 if (*rcp == '=') return cp++, GEQ;
#051 if (*rcp == '>') return cp++, RSHIFT;
#052 return '>';
#053 case '-':
#054 if (*rcp == '>') return cp++, DEREF;
#055 if (*rcp == '-') return cp++, DECR;
#056 return '-';
#057 case '=': return *rcp == '=' ? cp++, EQL : '=';
#058 case '!': return *rcp == '=' ? cp++, NEQ : '!';
#059 case '|': return *rcp == '|' ? cp++, OROR : '|';
#060 case '&': return *rcp == '&' ? cp++, ANDAND : '&';
#061 case '+': return *rcp == '+' ? cp++, INCR : '+';
#062 case ';': case ',': case ':':
#063 case '*': case '~': case '%': case '^': case '?':
#064 case '[': case ']': case '{': case '}': case '(': case ')':
#065 return rcp[-1];
#066 case '/n': case '/v': case '/r': case '/f':
#067 nextline();
#068 if (cp == limit) {
#069 tsym = NULL;
#070 return EOI;
#071 }
#072 continue;
#073
第
3
行是一个没有条件
for
循环,就是想识别出一个记号才返回。
第
5
行是获取当前输入缓冲区的指针。
第
6
行、第
7
行是去掉空白字符。
第
9
行到第
14
行是判断输入缓冲区小于最大的记号时,就重新从文件里获取源程序,填充到缓冲区里。
第
16
行到第
18
行是记录前记号的开始位置,以便出错时可以定位出错的源程序位置。
第
19
行是处理识别一个字符的移动指针,让它指向下一个字符。
第
20
行开始,就是根据第一个字符来进行识别处理,它是使用一个
switch
来实的。
第
22
行到第
44
行是识别了
C
程序的注释,并把这些注释删除。
第
45
行到第
48
行是识别小于
’<’
,小于等于
’<=’
,左移
’<<’
。
第
49
行到第
52
行是识别大于,大于等于,右移。
第
53
行到第
56
行是识别引用
’->’
,自减
’--‘
,减号
’-‘
。
第
57
行是识别等号或赋值。
第
58
行是识别不等或非操作符。
第
59
行是或和位或运行算符。
第
60
行是与和位与运算符。
第
61
行是自加和加号运算符。
第
62
行到
65
行是分号、逗号等等单个符号识别。
第
66
行到第
72
行是换行符识别,并获取下一行代码。
到这里就已经分析了很多符号的处理,后面主要是关键字、
ID
和常量的识别。