声 明:原创文章,转载注明出处http://www.cnblogs.com/lucasysfeng/p/4842310.html
第一节、工欲善其事,必先利其器。
笔者不会过多地陈述理论,而是希望通过实践还原一个C编译器的完成过程。
先来看一个简单的源文件main.c:
#include <stdio.h>
int main()
{
printf(“Hello World\n”);
return 0;
}
我们会用gcc main.c生成可执行文件a.out, 那么问题来了,gcc是如何识别include int main printf return这些词汇的呢,又是如何根据这些词汇做出相应操作的呢?答案是使用lex和yacc.
lex 代表 lexical analyzar(词法分析器),yacc 代表 yet another compiler compiler(编译器代码生成器)。lex和yacc在UNIX下分别叫flex和bison. 简单地理解下lex&yacc, lex词法分析器,读取文件中的关键词(后面说到的token标记其实可看做关键词);然后把关键词递交给yacc,yacc对一些关键词进行匹配,看它们是否符合一定的语法逻辑,如果符合就进行相应动作。
我们使用lex&yacc写编译器,所以先来学习下lex&yacc吧。
第二节、一个简单的lex程序。
跟着笔者将下面的程序编译运行一遍,相信你会有所收获。
1. 程序代码。
/* 第一段 */
%{
int chars = 0;
int words = 0;
int lines = 0;
%}
/* 第二段 */
%%
[a-zA-Z]+ { words++; chars += strlen(yytext); }
\n { chars++; lines++; }
. { chars++; }
%%
/* 第三段 */
main(int argc, char **argv)
{
yylex();
printf("%8d%8d%8d\n", lines, words, chars);
}
分析:
这段lex程序的作用是:根据输入的字符串,输出其行数、单词数和字符的个数。
(1) %%把文件分为3段,第一段是c和lex的全局声明,第二段是规则段,第三段是c代码。
(2) 第一段的c代码要用%{和%}括起来,第三段的c代码不用。
(3) 第二段是规则段,[a-zA-Z]+ \n . 是正则表达式,表示匹配的内容,{}内的是c编写的动作。
上面程序中yytext是lex变量,匹配模式的文本存储在这一变量中。yylex()这一函数开始分析,它由lex自动生成。关于lex变量和函数后续介绍,这里只是通过简单的lex程序来认识lex.
2、按照下面过程编译运行。
#flex test.l
#gcc lex.yy.c –lfl
#./a.out
然后输入一段文字,按ctrl+d结束输入,则会输出行数,单词数和字符的个数。
编译过程和运行结果等见下图:
第三节、lex进阶。
修改第二节程序,将正则表达式放在全局声明中,使逻辑更清晰。
%{
int chars = 0;
int words = 0;
int lines = 0;
%}
mywords [a-zA-Z]+
mylines \n
mychars .
%%
{mywords} { words++; chars += strlen(yytext); }
{mylines} { chars++; lines++; }
{mychars} { chars++; }
%%
main(int argc, char **argv)
{
yylex();
printf("%8d%8d%8d\n", lines, words, chars);
}
编译运行同第二节。
第四节、lex再进阶—循环扫描。
下面给出一个lex程序,这个程序在扫描到 + 或 - 时做一个特殊输出。当调用yylex()函数时,若扫描到return对应的标记时,yylex返回,且值就为return后的值;若没扫描到return对应的标记,yylex继续执行,不返回。下次调用自动从前一次的扫描位置处开始。
%{
enum yytokentype
{
ADD = 259,
SUB = 260,
};
%}
myadd "+"
mysub "-"
myother .
%%
{myadd} { return ADD; }
{mysub} { return SUB; }
{myother} { printf("Mystery character\n"); }
%%
main(int argc, char **argv)
{
int tok;
while (tok = yylex())
{
if (tok == ADD || tok == SUB)
{
printf("meet + or -\n");
}
else
{
printf("this else statement will not be printed, \
because if yylex return,the retrun value must be ADD or SUB.");
}
}
}
编译同上,运行结果见下图:
到现在,仅仅介绍了lex,后续会介绍yacc.