概述
所有实验的源代码:点此下载
实验目的:
熟悉语法分析器生成工具Yacc的使用,并学会在cygwin下使用bison工具编译Yacc文法说明文件。学习如何使用lex和yacc合作进行语法分析。
实验内容:
根据给出的calculator例子(calculator0,calculator1,calculator2,calculator3)完成下面题目:用lex和yacc写一个计算布尔表达式真值的计算器。
实验要求:
输入为一个布尔表达式,以换行结束。输出为这个布尔表达式的真值(true或false)。
尝试二义文法和非二义文法两种不同的实现方式。布尔表达式二义文法为:S –> S or S | S and S | not S | (S) | true | false,其中优先级or < and < not,or 和 and 左结合,not 右结合。非二义文法请参照表达式非二义文法自己写出来。
在cygwin下用flex,bison和gcc工具将实验调试通过,并写出测试例测试正确性。
重要算法或文法正规式:
首先需要修改原先写的词法分析器,因为现在是要用语法分析器来调用词法分析器,所以需要将词法分析器中的main函数删掉,所有对记号名的宏定义也要删掉,记号在语法分析器中定义,词法分析器只需要包含相应的头文件就可以使用语法分析器所定义的记号名。
修改完词法分析器,下面分别考虑有二义性的文法和无二义性的文法。
对于有二义性的文法,在Yacc使用指南和课本中都提到过如何解决冲突。实验要求中也明确的说明了,优先级or<and<not,or和and左结合,not右结合,因此在定义终结符时,需要指明它们的优先级和结合性,具体如下。
%left OR
%left AND
%right NOT
%left表示左结合,%right表示右结合,越在前面定义的优先级越低,因此优先级是OR<AND<NOT。
为了便于测试,参照之前的词法分析器,将mian函数做一些修改,使之既可以从标准输入中读取测试用例,又可以从文件中读取。
有二义性文法的语法分析器代码:
%{
#include <ctype.h>
#include <stdio.h>
int yylex();
int yyerror(char* s); /* yyerror 声明,使用库中的实现,为避免出现warning */
#define YYSTYPE int /* 将Yacc栈定义为int类型 */
#define YYDEBUG 1 /* 允许debug模式 */
%}
%token TRUE FALSE LPAREN RPAREN ENTER
%left OR
%left AND
%right NOT
%%
/*
S –> S or S | S and S | not S | (S) | true | false
*/
prog : prog exprp
| exprp
;
exprp : s ENTER {printf("nThe result of the bool expression is "); $1 == 0 ? printf("FALSEn") : printf("TRUEn");}
s : s OR s {$$ = $1 || $3;}
|s AND s {$$ = $1 && $3;}
|NOT s {$$ = !$2;}
|LPAREN s RPAREN {$$ = $2;}
|TRUE {$$ = 1;}
|FALSE {$$ = 0;}
;
%%
int main(int argc, char ** argv){
//yydebug = 1;
extern FILE* yyin;
if (argc == 2){
if ((yyin = fopen(argv[1], "r")) == NULL){
printf("Can't open file %sn", argv[1]);
return 1;
}
}
yyparse();
if(argc == 2){
fclose(yyin);
}
return 0;
}
用非二义文法实现时,首先需要根据优先级和结合性,将二义文法修改为无二义文法,修改之后的文法如下。
S->S or T | T
T->T and F | F
F->not F | (S) | false | true
根据无二义文法构造的语法分析器就不会发生冲突了。
无二义文法的语法分析器代码:
%{
#include <ctype.h>
#include <stdio.h>
int yylex();
int yyerror(char* s); /* yyerror 声明,使用库中的实现,为避免出现warning */
#define YYSTYPE int /* 将Yacc栈定义为int类型 */
#define YYDEBUG 1 /* 允许debug模式 */
%}
%token OR AND NOT TRUE FALSE LPAREN RPAREN ENTER
%%
/*
S->S or T | T
T->T and F | F
F->not F | (S) | false | true
*/
prog : prog exprp
| exprp
;
exprp : s ENTER {printf("nThe result of the bool expression is "); $1 == 0 ? printf("FALSEn") : printf("TRUEn");}
s: s OR t {$$ = $1 || $3;}
| t
;
t: t AND f {$$ = $1 && $3;}
| f
;
f: NOT f {$$ = !$2;}
| LPAREN s RPAREN {$$ = $2;}
| FALSE {$$ = 0;}
| TRUE {$$ = 1;}
;
%%
int main(int argc, char ** argv){
//yydebug = 1;
extern FILE* yyin;
if (argc == 2){
if ((yyin = fopen(argv[1], "r")) == NULL){
printf("Can't open file %sn", argv[1]);
return 1;
}
}
yyparse();
if(argc == 2){
fclose(yyin);
}
return 0;
}
词法分析器代码:
/* 把讨厌的注释去掉 */
%{
#include "cal.tab.h"
/*
yylval在cal.y中进行定义,不需要在此文件中再次定义
*/
%}
ws [ t]
digit [0-9]
number {digit}+(.{digit}+)?
%%
/* ECHO是一个宏,相当于 fprintf(yyout, "%s", yytext)*/
<INITIAL>or { return (OR);}
<INITIAL>and { return (AND);}
<INITIAL>not { return (NOT);}
<INITIAL>true { return (TRUE);}
<INITIAL>false { return (FALSE);}
<INITIAL>"(" { return (LPAREN);}
<INITIAL>")" { return (RPAREN);}
{ws} {;}
<INITIAL>"n" { return (ENTER);}
. {printf("nLEX:ERROR! c=%sn", yytext);}
实验结果:
实验所设计的测试用例如下。
(true and false) or false
(true or false) and false
true or false and false
not true or false
true or not true
false or not false
有二义性文法的运行结果。
无二义性文法的运行结果。
遇到的问题、实验难点、解决方法:
最初没搞明白词法分析器和语法分析器链接的原理,因此在用gcc编译时总是报错。后来又重新阅读了一遍Yacc使用指南,发现是记号名的问题,修改之后便能编译成功了。
由于时间较长,忘了如何将有二义性的文法根据它的优先级和结合性改写成无二义文法,因此又重新复习了一下课本的相关章节。
因为是第一次写语法分析器,因此遇到了若干细节上的问题,但最终经过我们的讨论与查阅资料,将问题逐个解决了。
第一次接触makefile文件,最初使用的很不熟练,但渐渐地发现使用makefile能极大的提高调试程序的效率,每当对代码做了修改之后,只需一句make命令便可重新生成可执行文件,当代码需要多次修改时,这样无疑会提供极大的便利。
最后
以上就是开心母鸡为你收集整理的编译原理实验四:验证Yacc的使用的全部内容,希望文章能够帮你解决编译原理实验四:验证Yacc的使用所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复