跟我一起写编译器(一)——lex&yacc【原创】

声      明:原创文章,转载注明出处 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. 程序代码。

文件名:test.l
/* 第一段 */ 
%{
	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.

文章不错,支持一下

我来评几句
登录后评论

已发表评论数()

相关站点

热门文章