LCC编译器的源程序分析(6)词法分析

在最开始的例子程序里,程序是由一些单词和符号组成的。其实程序就是一串长长的字符串,这些字符串是按一定的规则编写的,那么就需要检查这些单词和符号是否符合定义的规则。在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和常量的识别。

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License