现在的位置: 首页 > 综合 > 正文

C语言的lex和yacc工具说明

2013年08月23日 ⁄ 综合 ⁄ 共 3148字 ⁄ 字号 评论关闭
Lex工具
-------
Lex工具是一种词法分析程序生成器,它可以根据词法规则说明书的要求来生成单词识别程序,由该程序识别出输入文本中的各个单词。
1、lex程序的结构

-定义部分
-规则部分
-用户子程序部分

其中规则部分是必须的,定义和用户子程序部分是任选的。

(1) 定义部分
定义部分起始于"%{"符号,终止于"%}"符号,其间可以是包括include语句、声明语句在内的C语句。
%{
#include "stdio.h"
#include "y.tab.h"
extern int lineno;
%}

(2) 规则部分
规则部分起始于"%%"符号,终止于"%%"符号,其间则是词法规则。词法规则由模式和动作两部分组成。模式部分可以由任意的正则表达式组成,动作部分是由C语言语句组成,这些语句用来对所匹配的模式进行相应处理。需要注意的是,lex将识别出来的单词存放在yytext[]字符数据中,因此该数组的内容就代表了所识别出来的单词的内容。

%%
[/t] {;}
[0-9]+/.?|[0-9]*/.[0-9]+
{ sscanf(yytext,"%1f", &yylval.val);
return NUMBER; }
/n { lineno++;return '/n'; }
. { return yytex+[0]; }
%%

(3) 用户子程序部分
用户子程序部分可以包含用C语言编写的子程序,而这些子程序可以用在前面的动作中,这样就可以达到简化编程的目的。下面是带有用户子程序的lex程序片段。
"/*" skipcmnts();
. /* rest of rules */
%%
skipcmnts()
{
for ( ; ; )
{
while (input()!='*');
if(input()!='/')
unput(yytext[yylen-1]);
else return;
}

2、lex工具的使用方法
首先编写一个lex程序
vi lex.l
%{
#include "stdio.h"
%}
%%
[/n] ;
[0-9]+ printf("Interger: %s /n",yytext);
[0-9]*/.[0-9]+ printf("Float: %s/n",yytext);
[a-zA-Z][a-zA-Z0-9]* printf("Word:%s/n",yytext);
. printf("Other symbol:%c/n",yytext[0]);
%%

然后使用lex将lex.l转换成C语言程序
$lex lex.l
使用上述命令产生的C语言程序为lex.yy.c
然后使用C编译程序将lex.yy.c编译成可执行程序regn
$cc -c lex.yy.c
$cc lex.yy.o -ll -o regn
下面可以使用regn来识别单词
$vi testfile
x=355
y=113
p=x/y

# ./regn < testfile
Word:x
Other symbol:=
Interger: 355
Word:y
Other symbol:=
Interger: 113
Word:p
Other symbol:=
Word:x
Other symbol:/
Word:y
#

yacc工具
--------
yacc工具是一种语法分析程序生成器,它可以将有关某种语言的语法说明书转换成相应的语法分析程序,由该程序完成对相应语言中语句的语法分析工作。

1、yacc程序结构
在使用yacc工具前,必须首先编写yacc程序,因为有关语法分析程序是根据yacc程序生成的。yacc程序实际上是有关语法规则的说明书,它也是由定义部分、规则部分和子程序部分组成的。yacc程序的定义部分类似于lex程序的定义部分,只是在其后可带有yacc声明,其中包括词法单词、语法变量、优先级和结合性信息。yacc程序的规则部分由语法规则和相应的动作组成,子程序部分可以包括在前面规则部分用到的子程序定义。接下来是main主程序,它调用yyparse子程序来对输入进行语法分析,而yyparse反复地调用yylex子程序来获得输入单词,在语法出错时可通过yyerror子程序来处理。

2、yacc工具的使用方法
实例:我们将yacc程序分成片段,把这些片段组合在一起就是yacc程序。我们要使用的语法规则是一个有关四则运算的语法规则,可用BNF范式描述
list: expr /n
list expr /n
expr :NUMBER
expr + expr
expr - expr
expr * expr
expr / expr
(expr)
其含义是list是一个表达式序列,每个后面带有一个新行。表达式是一个数值,或是由运算符连起来的两个表达式,以及用圆括号括起来的表达式。
下面是有关上述语法规则的yacc程序片段。
$vi hoc.y
%{
#define YYSTYPE double
%}
%token NUMBER
%left '+' '-'
%left '*' '/'
%%
list:
| list '/n'
| list expr '/n' { printf("/t%. 8g/n",$2);}
;
expr : NUMBER {$$=$1;}
| expr '+' expr {$$ = $1 + $3; }
| expr '-' expr {$$ = $1 - $3; }
| expr '*' expr {$$ = $1 * $3; }
| expr '/' expr {$$ = $1 / $3; }
| '('expr')' {$$ = $2; }
%%
上述yacc程序片段实际上是它的定义部分和规则部分。在yacc声明部分,%token NUMBER表明了NUMBER是一个单词符号,%left则表明了运算符号的左结合性,并且'*'和'/'和优先级比'+'和'-'的优先级高。在yacc程序的规则部分,备用规则是用'|'隔开的,规则中的动作实际上是C语句序列,其中$n(即$1,$2等)是用来引用规则中的第几个成份,而$$则代表了整个规则的返回值。

下面的yacc程序片段是main主程序
#include <stdio.h>
#include <ctype.h>
char *progname;
int lineno=1;
main(argc,argv)
int argc;
char *argv[];
{ progname = argv[0];
yyparse();
}

main主程序调用yyparse子程序来处理来处理输入,而yyparse又是通过yylex子程序来获得输入单词并通过yyerror子程序来报告出错信息。下面是有关这两个子程序的yacc程序片段

yylex()
{ int c;
while ((c=getchar()) == ' ' || c=='/t') ;
if (c==EOF)
return 0;
if (c=='.'||isdigit(c)){
ungetc(c,stdin);
scanf("%lf", &yylval);
return NUMBER;
}
if(c=='/n')
lineno++;
return c;
}
yyerror(s)
char *s;
{ warning (s,(char *)0);
}
warning(s,t)
char *s,*t;
{ fprintf(stderr,"%s:%s",progname,s);
if(t)
fprintf(stderr,"%s",t);
fprintf(stderr," near line %d/n",lineno);
}

这样就完成了整个yacc程序
接下来就使用 yacc将hoc.y转换成C语言程序
$yacc hoc.y
使用上述命令产生的C语言程序为y.tab.c,这时可以使用C编译程序将它编译成可执行程序hoc.
$cc y.tab.c -o hoc

下面是使用hoc的例子
# ./hoc
4*3*2
24
(1+2)*(3+4)
21
1/2
0.5
355/133
2.6691729
-3-4
./hoc:Syntax error near line 5
上述结果显示中,分别表明了计算结果,最后一次计算出错的原因是由于在规则定义中未来定义单目减运算符号。

 

抱歉!评论已关闭.