基于YACC的TINY语法分析器的构建

实验要求

运用YACC,针对TINY语言,构造一个语法分析器。

实验方案

1.文法:

exp → exp addop term|term
addop → +|-
term → term mulop factor|factor
mulop → *
factor → (exp)|number

2.设计输入文件内容:
在这里插入图片描述

(注:无换行)

3.设计应有的输出:
在这里插入图片描述

4.Lex与Yacc的数据传递原理与方法
首先,在设计程序中,由于定义了自己的main()函数,它在某个点上调用了yyparse()。YACC会创建相应的yyparse()函数,并在y.tab.c中结束该函数。

yyparse()函数读取一个『标识符/值对』(token/value pairs)流,这些流需要事先就提供,这些流可以是手写的代码提供的,也可以是lex生成的。而在lex与Yacc的联合使用中,把这个工作丢给了lex。

每次yylex()调用都会返回一个整数值,该值代表了一个标识符类型(token type)。它告诉Yacc,已经读取了这种标识符。该标识符可以有一个值,它应该存放在yylval变量中。

但是在该文法中,yylval的变量类型并不是始终都是同一种类型,可以看到主要有int类型的number和char类型的’+’,’-’,’*’。

所以在这里为yylval定义了%union来解决这个问题,通过

%union{
   char chr;
   int integer;
}

让yylval包含了一个char型的变量与一个int型的变量来获取从lex中得到的数据,之后再利用

%token <integer> number
%type <integer> exp
%type <chr> term factor

来标识number、exp、term、factor的类型,%token表示这是一个终结符的类型,而%type只是标注类型。
之后,在lex语句中也需要修改,通过类似于从结构体中获取变量的
yylval.integer=atoi(yytext);
来控制返回的变量。

%left 表示左结合,%right 表示右结合。最后列出的定义拥有最高的优先权。因此在这个例子中乘法拥有比加法和减法更高的优先权。+ - * 所有这三个算术符都是左结合的。运用这个简单的技术,我们可以消除文法的歧义。

 %left '+' '-'
 %left '*'

分析表parsing table

理论和设计(描述parsing table在实验方案中的作用,观察并输出parsing table)
分析表:
在这里插入图片描述

分析表包含action动作表以及goto转移表,action表的每一列,对应文法中的终结符号或者$,每一项表示一个语法分析动作,goto表中的每一列对应着文法中的非终结符,每一项表示某一项遇到的每一个状态遇到某一非终结符进入的状态。
sn:将符号a、状态n压入栈
rn:用第n个产生式进行规约
利用win_bison -v test_yacc.y生成分析表
在这里插入图片描述

生成test_yacc.output文件
分析表:
在这里插入图片描述

四、内容和步骤
1、针对TINY语言给出 yacc的y文件的代码:
test_yacc.y

%{
#include <stdio.h>
#include<ctype.h>
%}

%union{
   char chr;
   int integer;
}

%token <integer> number
%type <integer> exp
%type <chr> term factor
%left '+' '-'
%left '*'

%% 
command : exp { printf("the end of this calculation is %d\n",$1); }
        ;/*允许打印结果*/   
		    
exp     : exp '+' term { $$ = $1 + $3; }
        | exp '-' term { $$ = $1 - $3; }
        | term { $$ = $1; }
        ;

term    : term '*' factor { $$ = $1 * $3; }
        | factor { $$ = $1; }
        ;

factor  : number       { $$ = $1; }
        | '(' exp ')'  { $$ = $2; }
        ;
%%

int yyerror(char *s)
{
    fprintf(stderr,"error:%s\n",s);
    return 0; 
}       

2、给出.l文件的代码:
test_lex.l

%{
#include <ctype.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#define false 0
#define ture 1
#include "test_yacc.tab.h"
%}

digit [0-9]

%%
{digit}+   {
    yylval.integer=atoi(yytext);
    printf("number:%d\n",yylval.integer);
    return(number);
}

\+   {
    yylval.chr=yytext[0];
    printf("opterator:%c\n",yylval.chr);
    return('+');
}

\-   {
    yylval.chr=yytext[0];
    printf("oprator:%c\n",yylval.chr);
    return('-');
}

\*   {
    yylval.chr=yytext[0];
    printf("oprator:%c\n",yylval.chr);
    return('*');
}

"("   {
    yylval.chr=yytext[0];
    printf("separator:%c\n",yylval.chr);
    return('(');
}
    
")"   {
    yylval.chr=yytext[0];
    printf("separtor:%c\n",yylval.chr);
    return(')');
}

;   {
   return(';');
}

[ \t]+  {
   printf("lexical analyzer error\n");
   /*忽略空格*/
}

quit  {
   printf("Bye!\n");
   exit(0);
}

%%
int yywrap()
{
    return(1);
}

3.main函数代码:
main2.c

#include"lex.yy.c" 
#include"test_yacc.tab.c"
#include <stdlib.h>
#include <stdio.h>
extern int yyparse();
  
int main(int argc, char* argv[]){
    extern FILE *yyin;
    if((yyin=fopen("test.txt","rt"))==NULL){
       perror("无法打开文件test.txt\n") ;
       exit(1);
    }
    if (yyparse()==1){
        fprintf(stderr,"parser error\n");
        exit(1);
    }
    printf("yyparse() completed successfully!\n");
    return 0;
}

3、实验具体步骤
安装flex和bison。flex和 bison 是两个用来生成程序的工具,它们用来词法和语法分析器。flex 和 bison 本身不是词法或语法分析器,利用它们生成的程序才是。
新建文本文件,更改名称为test_lex.l,敲入代码,新建文本文件,更改名称为test_yacc.y,敲入代码(后缀不能改,名称自己取,但是要注意不同名字下#include”test_yacc.tab.c”不一样,需要更改)
打开控制台,进入到刚才所建立文件所在的文件夹,输入:
在这里插入图片描述

得到:
在这里插入图片描述

之后加入test.txt文件并写入算式文本,并对主函数文件main2.c进行编译运行

五、实验结果:
截图
在这里插入图片描述

六、实验结论:
分析和总结
  利用工具的可以在实现语法分析和词法分析的时候,只需要根据lex和yacc工具给出的格式进行编写就可以很轻松的完成词法分析和语法分析,不用考虑整个程序的流程,等琐碎的细节,并且可以使用工具轻松输出分析表。
  手写编程程序来进行词法分析和语法分析,可以较为全面的把控所有的细节,对于输入,输出,token的定义,等等方面可以进行更加详细的设计。但缺点也很明显,对于初学者来说需要更大的学习成本,并且在手写编程的过程中若对原理不熟练会出现各种各样的问题,整个过程相对更耗时,需要更多时间才能体会到语法分析器的妙用。
实验中出现的冲突及解决过程描述
  使用%union之后,%token number语句中,%token是用来描述终结符,而%type才是非终结符的类型描述。
  使用%left语句需要确定’+’、’-’和’*’的优先级
  需要确定编译的顺序,确保顺序正确,按步骤执行,才能正确

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值