主题:Lex与Yacc的结合
Lex与Yacc的结合
用Lex与Yacc结合起来使用可以很方便的生成一个新语言的编译程序,不过现在很多国内的图书里面对它们的介绍是分开的,单独的介绍也是比较含糊,不是很清晰。把他们如何结合的介绍更是少的可怜。我用他们做了一个能识别整型数值的加、减、乘、除的编译程序。把我的心得写在下面,供大家参考。
首先,我就不介绍Lex的语法规则了,因为在一些书上这些是重点介绍的内容,我先把Lex的源程序写在下面,然后讲解。
%{
#define NUMBER 257
#define PLUS 258
#define SUB 259
#define CHEN 260
#define DIV 261
#define LKUO 262
#define YKUO 263
#include <stdio.h>
#include <stdlib.h>
extern int yylval;
%}
number [0-9]+
%%
{number} {yylval=atoi(yytext);printf("%s",yytext);return NUMBER;}
"+" {printf("%s",yytext);return PLUS;}
"-" {printf("%s",yytext);return SUB;}
"*" {printf("%s",yytext);return CHEN;}
"/" {printf("%s",yytext);return DIV;}
"(" {printf("%s",yytext);return LKUO;}
")" {printf("%s",yytext);return YKUO;}
%%
其中
#define NUMBER 257
#define PLUS 258
#define SUB 259
#define CHEN 260
#define DIV 261
#define LKUO 262
#define YKUO 263
是在Yacc中使用的记号,该记号必须先在YACC中定义,然后,在LEX中使用,YACC告诉LEX需要使用的符号,使用YACC –d 文件.y来生成一个yytab.h的文件,在该文件中内容就是上面的一系列#define 。。。。
extern int yylval; 告诉LEX要引用外部的变量yylval。
%%
{number} {yylval=atoi(yytext);printf("%s",yytext);return NUMBER;}
"+" {printf("%s",yytext);return PLUS;}
"-" {printf("%s",yytext);return SUB;}
"*" {printf("%s",yytext);return CHEN;}
"/" {printf("%s",yytext);return DIV;}
"(" {printf("%s",yytext);return LKUO;}
")" {printf("%s",yytext);return YKUO;}
%%
这一部分就不解释了。
下面是*.y文件了
%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define YYSTYPE int
%}
%token NUMBER
%token PLUS
%token SUB
%token PLUS
%token CHEN
%token DIV
%token LKUO
%token YKUO
%left PLUS SUB
%left CHEN DIV
%%
com: exp {printf("=%d\n",$1);};
exp: exp PLUS fac {$$=$1+$3;}
|exp SUB fac {$$=$1-$3;}
|fac{$$=$1;};
fac:fac CHEN term {$$=$1*$3;}
|fac DIV term {$$=$1/$3;}
|term {$$=$1;};
term:LKUO exp YKUO {$$=$1;}
| NUMBER {$$=$1;};
%%
extern int yylex();
int yyparse();
main()
{
return yyparse();
}
yyerror()
{printf("sytax error");}
其中#define YYSTYPE int是定义记录记号的值栈的类型为int,当然你也可以定义其他的类型。
我们命名第一个文件为sample.l,用命令pclex sample.l 处理,生成sample.c文件。
然后我们命名第二个文件为sample1.y,用命令pcyacc -v -d sample1.y
生成sample1.c、yytab.h、yy.lrt(YACC的分析表文件)。
用vc++或者tc编译一个工程,这个工程包含这两个文件*.c文件,其中在tc中编译这个工程的时候,sample1.c为primary file(该选项在tc中的compile项中设置),生成一个可执行的文件。如niu1.exe,然后编辑一个niu.txt文件,niu.txt文件就是需要识别的数学表达式。如下面的例子:
niu.txt 内容如下
5-2+3*22/2+2-3
用niu1<niu.txt执行结果如下图:
比如niu.txt的内容如下:
用一个错误的语法试试
如果niu.txt的内容如下:
5-3+2+ / 4*2+4 红色代表语法有错误的地方运行结果如下
我以sytax error代表语法错误。
该编译程序还有许多不完善的地方,不过它已经勾勒出了,LEX与YACC结合使用的一个框架,如果你感兴趣的话,可以逐步的完善它。
用Lex与Yacc结合起来使用可以很方便的生成一个新语言的编译程序,不过现在很多国内的图书里面对它们的介绍是分开的,单独的介绍也是比较含糊,不是很清晰。把他们如何结合的介绍更是少的可怜。我用他们做了一个能识别整型数值的加、减、乘、除的编译程序。把我的心得写在下面,供大家参考。
首先,我就不介绍Lex的语法规则了,因为在一些书上这些是重点介绍的内容,我先把Lex的源程序写在下面,然后讲解。
%{
#define NUMBER 257
#define PLUS 258
#define SUB 259
#define CHEN 260
#define DIV 261
#define LKUO 262
#define YKUO 263
#include <stdio.h>
#include <stdlib.h>
extern int yylval;
%}
number [0-9]+
%%
{number} {yylval=atoi(yytext);printf("%s",yytext);return NUMBER;}
"+" {printf("%s",yytext);return PLUS;}
"-" {printf("%s",yytext);return SUB;}
"*" {printf("%s",yytext);return CHEN;}
"/" {printf("%s",yytext);return DIV;}
"(" {printf("%s",yytext);return LKUO;}
")" {printf("%s",yytext);return YKUO;}
%%
其中
#define NUMBER 257
#define PLUS 258
#define SUB 259
#define CHEN 260
#define DIV 261
#define LKUO 262
#define YKUO 263
是在Yacc中使用的记号,该记号必须先在YACC中定义,然后,在LEX中使用,YACC告诉LEX需要使用的符号,使用YACC –d 文件.y来生成一个yytab.h的文件,在该文件中内容就是上面的一系列#define 。。。。
extern int yylval; 告诉LEX要引用外部的变量yylval。
%%
{number} {yylval=atoi(yytext);printf("%s",yytext);return NUMBER;}
"+" {printf("%s",yytext);return PLUS;}
"-" {printf("%s",yytext);return SUB;}
"*" {printf("%s",yytext);return CHEN;}
"/" {printf("%s",yytext);return DIV;}
"(" {printf("%s",yytext);return LKUO;}
")" {printf("%s",yytext);return YKUO;}
%%
这一部分就不解释了。
下面是*.y文件了
%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define YYSTYPE int
%}
%token NUMBER
%token PLUS
%token SUB
%token PLUS
%token CHEN
%token DIV
%token LKUO
%token YKUO
%left PLUS SUB
%left CHEN DIV
%%
com: exp {printf("=%d\n",$1);};
exp: exp PLUS fac {$$=$1+$3;}
|exp SUB fac {$$=$1-$3;}
|fac{$$=$1;};
fac:fac CHEN term {$$=$1*$3;}
|fac DIV term {$$=$1/$3;}
|term {$$=$1;};
term:LKUO exp YKUO {$$=$1;}
| NUMBER {$$=$1;};
%%
extern int yylex();
int yyparse();
main()
{
return yyparse();
}
yyerror()
{printf("sytax error");}
其中#define YYSTYPE int是定义记录记号的值栈的类型为int,当然你也可以定义其他的类型。
我们命名第一个文件为sample.l,用命令pclex sample.l 处理,生成sample.c文件。
然后我们命名第二个文件为sample1.y,用命令pcyacc -v -d sample1.y
生成sample1.c、yytab.h、yy.lrt(YACC的分析表文件)。
用vc++或者tc编译一个工程,这个工程包含这两个文件*.c文件,其中在tc中编译这个工程的时候,sample1.c为primary file(该选项在tc中的compile项中设置),生成一个可执行的文件。如niu1.exe,然后编辑一个niu.txt文件,niu.txt文件就是需要识别的数学表达式。如下面的例子:
niu.txt 内容如下
5-2+3*22/2+2-3
用niu1<niu.txt执行结果如下图:
比如niu.txt的内容如下:
用一个错误的语法试试
如果niu.txt的内容如下:
5-3+2+ / 4*2+4 红色代表语法有错误的地方运行结果如下
我以sytax error代表语法错误。
该编译程序还有许多不完善的地方,不过它已经勾勒出了,LEX与YACC结合使用的一个框架,如果你感兴趣的话,可以逐步的完善它。