编译原理学习(二)——Yacc实现中缀表达式转后缀

博客围绕后缀表达式的实现展开,在C++环境下,YYSTYPE声明为char*,词法分析需识别运算符、多位十进制整数、空白字符和标识符ID。对于多位十进制整数和标识符,将读到的字符存为字符串并赋给yylval。语法分析部分需将计算值修改成字符串的拷贝和连接,还给出了实现代码文件。

Yacc实现中缀表达式转后缀

由于需要返回的是一个后缀表达式,是一个字符串,因此YYSTYPE需要声明为char*, 而词法分析函数中不仅需要分析运算符,多位十进制整数,空白字符,还需要识别标识符ID。而对于多位十进制整数,不再需要得到整数值,而是需要得到整数对应的字符串。对于多位十进制整数,当读到一个字符为整数字符时,连续读接下来的数字字符直至读到不为数字字符的字符,调用ungetc 函数将读出的非数字字符放回缓冲区,将读到的若干数字字符存为一个字符串, 最后需要在字符串的最后添加结束符\0, 将这个字符串的地址赋给yylval。

NUMBER 的翻译模式执行动作就修改为将yylval的值拷贝给NUMBER, 这里需要注意的是,这里翻译模式的代码中并未出现yylval,却依然完成了赋值操作,这是因为yacc 与lex 默认将yylval 的值赋给了识别出的标识符。

ID 与NUMBER 思路类似,声明一个名为ID的token 和一个全局的字符串变量yylval, 用于赋给ID 值,由于标识符是有数字、字母和下划线组成的,而第一个字符不可以为数字,因此思路为当读到一个字符为字母或下划线时,连续读接下来的字符,直到出现一个不是数字或字母或下划线的字符停止,调用ungetc 函数将读出的非数字非字母非下划线字符放回缓冲区,将读到的若干字符存为一个字符串,将这个字符串的地址赋给yylval。ID 的翻译模式执行动作就修改为将$1 的值赋给ID。对于加减乘除、括号、负号、空白字符的词法分析部分不需修改,但语法分析部分需要将计算值修改成字符串的拷贝和连接。

实现代码如下:
postexpr.y文件,编译方法见我上一篇文章末尾编译原理学习(一)——Yacc实现计算器

%{
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#define YYSTYPE char*
char idstr[50];
char numstr[50];
int yylex();
extern int yyparse();
FILE* yyin;
void yyerror(const char*s);
%}
%token NUMBER
%token ID
%token ADD SUB MUL DIV LKO RKO
%left LKO
%left ADD SUB
%left MUL DIV
%right UMINUS
%right RKO
%%

lines : lines expr '\n'{printf("%s\n",$2);}
      | lines '\n'
      |
      ;

expr : expr ADD expr {$$=(char*)malloc(50*sizeof(char)); strcpy($$,$1);
                      strcat($$,$3);strcat($$,"+"); }
     | expr SUB expr {$$=(char*)malloc(50*sizeof(char)); strcpy($$,$1);
                      strcat($$,$3);strcat($$,"-"); }
     | expr MUL expr {$$=(char*)malloc(50*sizeof(char)); strcpy($$,$1);
                      strcat($$,$3);strcat($$,"*"); }
     | expr DIV expr {$$=(char*)malloc(50*sizeof(char)); strcpy($$,$1);
                      strcat($$,$3);strcat($$,"/"); }
     | LKO expr RKO {$$=(char*)malloc(50*sizeof(char));strcpy($$,$2);}
     | NUMBER   {$$ = (char*)malloc(50*sizeof(char)); strcpy($$,$1);
                 strcat($$," ");}
     | ID   {$$=(char*)malloc(50*sizeof(char));
             strcpy($$,$1);strcat($$," ");}


%%


int yylex()
{
      int t;
      while(1){
          t=getchar();     
          if(t==' '||t=='\t');
          else if((t>='0'&& t<='9')){
                   int ti=0;
                  while((t>='0'&& t<='9')){
                  numstr[ti]=t;
                  t=getchar();
                  ti++;
                }
           numstr[ti]='\0';
           yylval=numstr;
           ungetc(t,stdin);
           return NUMBER;
         }
      else if((t>='a'&&t<='z')||(t>='A'&&t<='Z')||(t=='_')){
              int ti=0;
              while((t>='a'&&t<='z')||(t>='A'&&t<='Z')
                     ||(t=='_')||(t>='0'&& t<='9')){
                      idstr[ti]=t;
                      ti++;
                      t=getchar();
                  }
               idstr[ti]='\0';
               yylval=idstr;
               ungetc(t,stdin);
               return ID;
             }
       else 
          {
            switch(t)
           {
              case '+':
                   return ADD;
              case '-':
                   return SUB;
              case '*':
                   return MUL;
              case '/':
                   return DIV;
              case'(':
                   return LKO;
              case')':
                   return RKO;
              default: 
                   return t;
            }
          }
      return t;
   }
}

int main(void)
{
   yyin = stdin;
  do{
      yyparse();
    }while(!feof(yyin));
  return 0;
}
void yyerror(const char* s)
{
   fprintf(stderr,"Parse error:%s\n",s);
   exit(1);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值