Lex和Yacc应用教程(三).使用变量
草木瓜 20070512
一、序
早在两个月前就想对Lex和Yacc作系列的阐述,然而工作的事情实在太多,很难抽出空
静下心去总结学习。不觉感慨国内工作环境恶劣,加班是家常便饭,整天基本都是在做
一些简单大量的重复,甚至徒劳无用。
在《初识Lex》一文中主要从入门角度总结了Lex,《再识Lex和Yacc》一文在可以简单
使用Lex情况基础,介绍了Lex和Yacc的算法理论,并说明如何将Lex和Yacc结合起来使
用。
这里我们需要对Lex和Yacc使用方法做进一步研究,以备能理解后面会引入的语法树。
<本系列文章的地址:http://blog.csdn.net/liwei_cmg/category/207528.aspx>
本系列所有示例均在RedHat Linux 8测试通过。
二、示例
我们在以前的基础上,深入一步,设计一个简单的计算器,包含+,-,*,/四项操作,且
支持()运算符,其中对值可以进行变量保存,并打印出内部的分析信息。
Lex文件全内容(lexya_b.l):
---------------------------------------------
%{
#include <stdlib.h>
#include "lexya_b.tab.h"
void yyerror(char *);
void add_buff(char *);
extern char sBuff[10][20];
extern int iX;
extern int iY;
%}
%%
[a-z] { yylval = *yytext; add_buff(yytext); return VAR; }
[0-9]+ { yylval = atoi(yytext); add_buff(yytext); return INT; }
[-+()=*/] { yylval = *yytext; add_buff(yytext); return *yytext; }
[/n] { yylval = *yytext; iY=0;iX++; return *yytext; }
[/t] ;/* 去除空格 */
. yyerror("无效字符");
%%
void add_buff(char * buff) {
sBuff[iX][iY]=*buff; iY++;
}
int yywrap(void) {
return 1;
}
Yacc文件全内容(lexya_b.y):
---------------------------------------------
%{
#include <stdlib.h>
int yylex(void);
void yyerror(char *);
void debuginfo(char *, int *, char *);
void printinfo();
int sMem[256];
char sBuff[10][20]={0};
int iX=0;
int iY=0;
%}
%token INT VAR
%left '+' '-'
%left '*' '/'
%%
program:
program statement
|
;
statement:
expr { printf("%d/n",$1); }
| VAR '=' expr { debuginfo("=",yyvsp,"110"); sMem[$1]=$3; }
| statement '/n' { printf("--------------------------------/n/n"); }
;
expr:
INT { debuginfo("INT",yyvsp,"0"); $$ = $1; }
| VAR { debuginfo("VAR",yyvsp,"1"); $$ = sMem[$1]; }
| expr '*' expr { debuginfo("*",yyvsp,"010"); $$ = $1 * $3; }
| expr '/' expr { debuginfo("/",yyvsp,"010"); $$ = $1 / $3; }
| expr '+' expr { debuginfo("+",yyvsp,"010"); $$ = $1 + $3; }
| expr '-' expr { debuginfo("-",yyvsp,"010"); $$ = $1 - $3; }
| '(' expr ')' { debuginfo("()",yyvsp,"101"); $$ = $2; }
;
%%
void debuginfo(char * info,int * vsp, char * mark) {
/* */
printf("--RULE: %s /n", info);
int i=0;
int ilen=strlen(mark);
for(i=0;i>=1-ilen;i--) {
if(mark[ilen+i-1]=='1')
printf("$%d %d %c /n", i+ilen, vsp[i], vsp[i]);
else
printf("$%d %d /n", i+ilen, vsp[i]);
}
printinfo();
}
void printinfo() {
int i=0;
printf("--STATEMENT: /n");
/*
for(i=0;i<=iX;i++)
printf("%s /n",sBuff[i]);
*/
if(iY==0)
printf("%s /n",sBuff[iX-1]);
else
printf("%s /n",sBuff[iX]);
printf("/n");
}
void yyerror(char *s) {
printf("%s/n", s);
}
int main(void) {
yyparse();
return 0;
}
编译文件脚本(yacclex)全内容:
---------------------------------------------
rm lexya_$1.tab.c
rm lexya_$1.tab.h
rm lex.yy.c
bison -d lexya_$1.y
lex lexya_$1.l
gcc -g -o parser lex.yy.c lexya_$1.tab.c
待解释编译文本(input)全内容:
---------------------------------------------
a=4+2*(3-2-1)+6
b=1-10/(6+4)+8
c=a-b
a
b
c
命令行输入编译编译器:
./yacclex b
进文件进行解释:
./parser < input
10
8
2
文中成功了使用了变量,并且操作符运算优先级也没有问题。
其实细看过《Lex和Yacc应用方法(二).再识Yacc》一文后,理解上面的例子就很轻
松了。
这里只是做了一些扩充变化:
1.增加了全局数组sMem来存放变量,不过变量名有限制,只支持单字符。
2.增加了全局数组sBuff存放分析过的语句
3.增加debuginfo打印堆栈信息
4.增加printinfo打印目前的分析语句
要进行内部分析,就需要剖析生成的c文件,对程序(parser)进行跟踪调试。
(注:Lex编译时加上d参数,会在程序解释时输出详细的调试信息。如:lex -d lexya_$1.l)
通过本示例再加上实际对lexya_b.tab.c的分析理解,会对lex,yacc理论有更进一步的
理解。
四、增加支持的变量字符数
上面的例子只支持单字符的变量,想支持多字符,需要定义一全局变量如:
struct varIndex
{
int iValue;
char sMark[10];
};
同时打印信息加入对变量的显示,下面列出全部文件内容,比较简单,不再
附加说明。
头文件(userdef.h)
typedef struct {
int iValue;
char sMark[10];
} varIndex;
varIndex strMem[256];
Lex文件:
%{
#include <stdlib.h>
#include "userdef.h"
#include "lexya_c.tab.h"
void yyerror(char *);
void add_buff(char *);
void add_var(char *);
extern char sBuff[10][20];
extern int iBuffX;
extern int iBuffY;
extern varIndex strMem[256];
extern int iMaxIndex;
extern int iCurIndex;
%}
%%
[a-zA-Z][a-zA-Z0-9]* { add_var(yytext); yylval = iCurIndex; add_buff(yytext); return VAR; }
[0-9]+ { yylval = atoi(yytext); add_buff(yytext); return INT; }
[-+()=*/] { yylval = *yytext; add_buff(yytext); return *yytext; }
[/n] { yylval = *yytext; iBuffY=0;iBuffX++; return *yytext; }
[/t] ;/* 去除空格 */
. yyerror("无效字符");
%%
void add_buff(char * buff) {
strcat(sBuff[iBuffX],buff);
iBuffY+=strlen(buff);
}
void add_var(char *mark) {
if(iMaxIndex==0){
strcpy(strMem[0].sMark,mark);
iMaxIndex++;
iCurIndex=0;
return;
}
int i;
for(i=0;i<=iMaxIndex-1;i++) {
if(strcmp(strMem[i].sMark,mark)==0) {
iCurIndex=i;
return;
}
}
strcpy(strMem[iMaxIndex].sMark,mark);
iCurIndex=iMaxIndex;
iMaxIndex++;
}
int yywrap(void) {
return 1;
}
Yacc文件:
%{
#include <stdlib.h>
#include "userdef.h"
int yylex(void);
void yyerror(char *);
void debug_info(char *, int *, char *);
void stm_info();
extern varIndex strMem[256];
int iMaxIndex=0;
int iCurIndex=0;
char sBuff[10][20]={0};
int iBuffX=0;
int iBuffY=0;
%}
%token INT VAR
%left '+' '-'
%left '*' '/'
%%
program:
program statement
|
;
statement:
expr { printf("%d/n",$1); }
| VAR '=' expr { debug_info("=",yyvsp,"210"); strMem[$1].iValue=$3; }
| statement '/n' { printf("--------------------------------/n/n"); }
;
expr:
INT { debug_info("INT",yyvsp,"0"); $$ = $1; }
| VAR { debug_info("VAR",yyvsp,"2"); $$ = strMem[$1].iValue; }
| expr '*' expr { debug_info("*",yyvsp,"010"); $$ = $1 * $3; }
| expr '/' expr { debug_info("/",yyvsp,"010"); $$ = $1 / $3; }
| expr '+' expr { debug_info("+",yyvsp,"010"); $$ = $1 + $3; }
| expr '-' expr { debug_info("-",yyvsp,"010"); $$ = $1 - $3; }
| '(' expr ')' { debug_info("()",yyvsp,"101"); $$ = $2; }
;
%%
void debug_info(char * info,int * vsp, char * mark) {
/* */
printf("--RULE: %s /n", info);
int i=0;
int ilen=strlen(mark);
for(i=0;i>=1-ilen;i--) {
switch(mark[ilen+i-1]){
case '1':
printf("$%d %d %c /n", i+ilen, vsp[i], vsp[i]);
break;
case '0':
printf("$%d %d /n", i+ilen, vsp[i]);
break;
case '2':
printf("$%d %s %d/n", i+ilen, strMem[vsp[i]].sMark, strMem[vsp[i]].iValue);
break;
}
}
stm_info();
}
void stm_info() {
int i=0;
printf("--STATEMENT: /n");
/*
for(i=0;i<=iBuffX;i++)
printf("%s /n",sBuff[i]);
*/
if(iBuffY==0)
printf("%s /n",sBuff[iBuffX-1]);
else
printf("%s /n",sBuff[iBuffX]);
printf("/n");
}
void yyerror(char *s) {
printf("%s/n", s);
}
int main(void) {
yyparse();
return 0;
}
五、最后
本文说明文字较少,主要是前两篇说得已经够多了,对照代码应该比较容易理解,
下一篇将着重介绍语法树的构建,也才是真正应用的开始。