您的位置:首页 > 其它

Lex和Yacc应用教程(三).使用变量

2007-05-14 22:25 344 查看
 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;
  }
  
  
五、最后

    本文说明文字较少,主要是前两篇说得已经够多了,对照代码应该比较容易理解,
下一篇将着重介绍语法树的构建,也才是真正应用的开始。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息