编译原理课程设计报告.docx
- 文档编号:25203814
- 上传时间:2023-06-06
- 格式:DOCX
- 页数:23
- 大小:276.53KB
编译原理课程设计报告.docx
《编译原理课程设计报告.docx》由会员分享,可在线阅读,更多相关《编译原理课程设计报告.docx(23页珍藏版)》请在冰豆网上搜索。
编译原理课程设计报告
《编译原理》课程设计报告
一、课程设计目的
通过课程设计进一步理解高级语言在计算机中的执行过程,了解现代编译器的运作机制,加深对编译原理中重点算法和编译技术的理解,提高自己自学和理解的能力。
学会如何利用已有软件JFLex、Java_cup对词法分析器及语法分析器的构造。
二、设计概述
本tiger语言编译器的编译过程涉及到编译五个阶段中的二个,即词法分析器、语法分析器。
其中语法分析后还完成了语法树的打印的构造以及类型检查。
词法分析器由JFLex编译正则式生成,词法分析器编译产生式生成,语法分析器由CUP生成。
结果通过GUI界面呈现在使用者面前。
编译程序需要在单词级别上来分析和翻译源程序,所以首先要识别出单词,而词法分析部分的任务是:
从左至右扫描源程序的字符串,按照词法规则(正则文法规则)识别出一个个正确的单词,并转换成该单词相应的二元式(种别码、属性值)交给语法分析使用。
因此,词法分析是编译的基础。
执行词法分析的程序称为词法分析器。
语法分析是编译程序的核心部分,其主要任务是确定语法结构,检查语法错误,报告错误的性质和位置,并进行适当的纠错工作。
三、设计过程
(一)设计构思
程序主要完成三大功能模块:
词法分析器、语法分析器、GUI人机交互界面。
词法分析器由JFLex编译正则式生成,其中必须为外界提供一个获取记号流的接口,实验中定为java_cup.runtime.Symbolnext_token。
语法分析器是建立在词法分析器上的,故必须包含词法分析器以便获得记号流,next_token为语法分析器提供TOKEN,语法分析器的对外接口是:
java_cup.runtime.Symboldebug_parse(),同时返回语法树的根节点。
GUI界面是提供人机交互的,它能够依次显示词法分析阶段分析得到的所有TOKEN的信息,语法阶段生成的语法树,另外对于词法和语法阶段出现的错误在“错误提示”文本框中一一列举出来,提供用户改进代码的信息。
流程图:
词法错误
记号流
TOKEN
语法树
语法、类型错误
类及文件说明:
包Absyn
Print.java//printoutthesyntaxtree
……//TheabstractsyntaxclassesforTiger
包errormsg
ErrorMsg.java//usedtoproduceerrormessageswithdetailinformation
包java_cup.runtime//SupportforCUPparser
包parse
Grm.java//Parseranalyser
MainInterface.java//GUIinterface
Sym.java//tokens'decimalrepresentation
Yylex.java//Lexeranalyser
包Semant
Entry.java//forbindingsinvalueenvironment
Env.java//holdsavalue,typeenvironmentandanerrorprinter
ExpTy.java//holdstheresultoftranslatingandtype-checking
FunEntry.java//forfunctionbindings
Semant.java//themaintype-checkingmodule
VarEntry.java//forvariablebindings
包Symbol
Symbol.java//makesstringsintouniqueSymbolobjects
Table.java//doesenvironmentswithScopes
包Translate
包Types//describesTiger_languagetypes
(二)词法分析器
这部分的工作主要是熟悉JFLex的使用以及正确书写正则式,通过正则式的书写,生成相应的词法分析器。
1.JFLex的使用
修改jflex-1.4.2\bin中的jflex.bat文件中的参数,JAVA_HOME=%JAVA_HOME%\lib\classes.zip;
JFLEX_HOME=%JFLEX_HOME%\lib\JFlex.jar;
运行jflex.bat并给选择输入文件(tiger.jlex),在没有错误的情况下,它在输出目录下生成一个Yylex.java的词法分析器。
tiger.jlex文件开始部分的书写格式如下:
packageparse;//将词法分析器打包在Parse下
importerrormsg.ErrorMsg;//导入类
%%
%cup//支持与cup的关联
%line//支持yyline()调用
%column//支持yycolumn()调用
%char
%stateCOMMENT,STRING//声明两个新的状态
%{//设置所有可能的词法错误类型
StringBufferstr=newStringBuffer();
privatestaticfinalStringerrorM[]={
"Error:
Unmatchedend-of-commentpunctuation.",
"Error:
Unmatchedstart-of-commentpunctuation.",
"Error:
Unclosedstring.",
"Error:
Illegalcharacter."
};
privatevoidnewline(){//保持errormsg里的行数记录变量的正确性
errorMsg.newline(yychar);
}
privatevoiderr(Strings){……}//通过errormsg报错
privatejava_cup.runtime.Symboltok(intkind,Objectvalue){
returnnewjava_cup.runtime.Symbol(kind,yychar,yychar+yylength(),value);//返回所识别的TOKEN
}
publicYylex(java.io.FileReaders,ErrorMsge){
this(s);//重载Yylex构造函数,方便错误输出
errorMsg=e;
}
privateErrorMsgerrorMsg;//ErrorMsg对象声明
privateintcomment_count=0;//变量,用于处理注释嵌套
%}
%eofval{
{//到文件末尾时判断最后的状态是否是YYINITIAL,若不是则报相应的错误
if(zzLexicalState!
=0){
switch(zzLexicalState){
case2:
{err(errorM[E_STARTCOMMENT]);
break;}
case4:
{err(errorM[E_UNCLOSEDSTR]);
break;}
default:
{/*donothing*/}
}
}
returntok(sym.EOF,null);
}
%eofval}
//一些基本类型简化表达式的定义
ALPHA=[A-Za-z]//字母
DIGIT=[0-9]//数字
LINE_TERMINATOR=\r|\n|\r\n//换行符
NONNEWLINE_WHITE_SPACE_CHAR=[\\t\f\b\012]//非换行空白符
WHITE_SPACE_CHAR={LINE_TERMINATOR}|NONNEWLINE_WHITE_SPACE_CHAR}
//空白符
%%
2.正规表达式的书写
Tiger需要识别的变量和字符有:
整型常量、字符常量、操作符、括符、分隔符、关键字、标识符、COMMENT。
整型常量:
一个整形数字,即字母的任意组合。
字符常量:
用双引号引起来的字符,可跨行但行尾必须以\结尾,行首\开始。
字符常量中间可包含转义字符如:
\r\n\t\f\”\\\xyzx,y,z表示0~127的整数。
操作符:
+-*/>>=<<==:
=&|
括符:
(){}[]
分隔符:
;:
关键字:
whiledofortoifelsethenletinendtypevararrayofbreaknilfunction
标识符:
以字符开头,加上字符、数字、下划线的任意组合
评论:
以/*开头,以*/结尾中间可嵌套任意个但必须是封闭的。
一般的字符或者保留字只需返回它的名字就可以了,例如:
在YYINITAIL的状态下识别到最长匹配串为“array”,则直接返回token,编号为sym.java中已经定义的数字编号,书写的格式如下:
下面着重谈一下对于字符串和注释的处理:
字符串的处理:
在YYINITIAL状态下遇到一个”时表示识别字符常量的开始,此时就要进入先前声明的STRING状态,先前声明的字符串str用来保存已识别的字符常量(初始化为空),代码如下:
当识别的过程中如果碰到转义字符\n\t\"\\,在str后添加相应的字符,碰到非转移字符就直接添加到str后面,忽略两个反斜杠之间的所有空白字符,代码如下:
特别是当碰到\跟上三位数字时,应该判断数值范围是否在ASCII范围中
str.append((char)Integer.valueOf(newString(yytext().getBytes(),1,3)).intValue());}
当再次识别到"时标志着字符串的识别结束,此时将str中的字符串内容作为String的内容返回,同时应该返回YYINITIAL状态,继续识别其他的TOKEN
yybegin(YYINITIAL);
returntok(sym.STRING,str.toString());}
识别到这些token以外的其他字符,则直接报错
注释的处理:
在YYINITIAL状态下遇到一个/*时表示识别注释的开始,此时就要进入先前声明的COMMENT状态,同时comment_count加1,以实现注释嵌套的实现。
如果在COMMENT状态下又一次识别到/*表示另一个注释嵌套的开始,此时也需要对comment_count进行加1的操作,代码如下:
yybegin(COMMENT);
comment_count=comment_count+1;}
但是如果在YYINITIAL状态下遇到一个*/则是一个错误,需要报出,代码如下:
在COMMENT状态下遇到*/则表示着一个嵌套中的注释结束,此时需要对comment_count进行减1的操作,如果操作之后其值为0,表示整个注释已经结束,回到YYINITIAL状态,如若不然则继续在COMMENT的状态下识别,代码如下:
comment_count=comment_count-1;
if(comment_count==0)
yybegin(YYINITIAL);}
识别到这些token以外的其他字符,则直接忽略
3.词法分析器的结构
词法分析器实现了Lexer接口,也就是实现了Tokennext_token()这个方法,不断调用此方法就可以获得所有的记号流。
使用词法分析器时需要指定输入流InputStream以及ErrorMsg,InputStream完成源程序的读取,ErrorMsg完成对错误信息的输出。
至此词法分析器的构造已经基本基本结束了。
(三)语法分析器
完成语法分析器的主要步骤是cup文件的编写,由于语法树的数据结构已给出,所以产生式的编写可以直接参考tiger语言的语法模式,当然这其中产生了许多冲突,但是可以通过设置符号的优先级来解决移进和规约的冲突。
1.CUP的使用
CUP的使用需要在命令行的环境下进行,打开命令提示符,输入如下所示的语句,
既可以在当前目录下生成语法分析器Grm.java,Grm.cup的书写格式如下所示:
Cup开始部分的书写格式如下:
packageparse;//产生在parse包里面
importAbsyn.*;
importjavax.swing.*;
actioncode{:
staticSymbol.Symbolsym(Strings){//将字符串转化为Symbol
returnSymbol.Symbol.symbol(s);
}
:
};
parsercode{:
Yylexlexer;
publicstaticExpparseResult;//记录语法树根节点的变量
errormsg.ErrorMsgerrorMsg;//输出语法错误的ErrorMsg对象声明
publicvoidsyntax_error(java_cup.runtime.Symbolcurrent){
report_error("Syntaxerror("+current.sym+")",(java_cup.runtime.Symbol)current);
}
publicvoidreport_error(Stringmessage,java_cup.runtime.Symboltok){
errorMsg.error(tok.left,message);
}
publicGrm(errormsg.ErrorMsgerr,JTextAreatext){
this();//语法分析器的构造
errorMsg=err;
textin=text;
}
:
};
initwith{:
//语法分析器初始化,从界面文本框中获取输入流
lexer=newYylex(newjava.io.StringBufferInputStream(textin.getText()),errorMsg);
:
};
scanwith{:
java_cup.runtime.Symbolsymb=lexer.debug_next_token();
for(;(symb.sym==sym.error)&&symb.sym!
=sym.EOF;)
symb=lexer.debug_next_token();
returnsymb;
:
};//同过debug_next_token获取token并打印出token
//终结符
terminalStringSTRING;
terminalIntegerINT;
terminalCOMMA,COLON,SEMICOLON,LPAREN,RPAREN,LBRACK,RBRACK,LBRACE,RBRACE,DOT;
terminalPLUS,MINUS,TIMES,DIVIDE,EQ,NEQ,LT,LE,GT,GE,AND,OR,ASSIGN,UMINUS;
terminalARRAY,IF,THEN,ELSE,WHILE,FOR,TO,DO,LET,IN,END,OF,BREAK,NIL,FUNCTION,VAR,TYPE,ID;
//终结符优先级次序
precedencerightDO,ELSE,THEN;
precedencenonassocASSIGN;
precedenceleftOR;
precedenceleftAND;
precedencenonassocEQ,NEQ,LT,LE,GT,GE;
precedenceleftPLUS,MINUS;
precedenceleftTIMES,DIVIDE;
precedenceleftUMINUS;
precedenceleftLBRACK;
₪关于优先级次序的说明:
由于在语法分析的过程中会产生许多移进和规约的冲突,通过设置终结符的优先级可以避免一些情况的发生。
将PLUS、MINUS的优先级定义在
TIMES、DIVIDE之前即可使乘除的优先级高于加减运算的优先级。
另外通过设置比较运算符的有限次序,可以避免分析时的移进--归约冲突。
此外在实际的cup生成过程中,还会出现一些其他的冲突,例如以下两个冲突的解决:
№.1
出现冲突
betweenlvalue:
:
=ID(*)
andlvalue:
:
=ID(*)LBRACKexprRBRACK
andexpr:
:
=ID(*)LBRACKexprRBRACKOFexpr
undersymbolLBRACK
解决办法:
把leftLBRACK设为最高优先级,这样的话当碰到是归约ID还是移进LBRACK的冲突时,系统会自动根据优先级而去执行移进的动作。
№.2
出现冲突
betweenexpr:
:
=IFexprTHENexpr(*)
andexpr:
:
=IFexprTHENexpr(*)ELSEexpr
undersymbolELSE
解决办法:
把rightDO,ELSE,THEN设为最低优先级,通过precedenceright
ELSE,THEN语句;定义为右结合的then将与else结合,故先移进。
№.3
负号和减号识别时有冲突,解决方案是重新定义一个终结符UMINUS算数运算符中最高优先级,负号时重新赋予UMINUS的优先级。
2.语法的书写
按照TigerManual上的语法规则,将上面的语法一一写入cup文件的后面,必要的时候定义适当的非终结符来完成语法的结构。
每一条语法规则都应该返回其相应的语句类型,这些类型都已经在Absyn包中定义好了,只要根据语句的类型按照定义返回即可。
其中,要注意的是一些链表的连接,如ExpList、FieldList等,需要保证这些对象的连接正确性。
首先,在语法分析前完成非终结符的定义,定义如下:
nonterminalFieldListtype_fields,type_fields_n,type_fields_ex;
nonterminalFieldExpListfield_list,field_list_n,field_list_ex;
nonterminalExpListexpr_list,expr_list_n,expr_list_ex,expr_seq,expr_seq_n,expr_seq_ex;
nonterminalDecListdeclaration_list,declaration_list_ex;
nonterminalTytype;
nonterminalDecdeclaration,type_declaration,variable_declaration,function_declaration;
nonterminalExpexpr,program;//文法开始符
nonterminalVarlvalue;
其次在文法开始符的语法句的执行语句中,将第一个Exp传给先前定义的parseResul,代码如下:
startwithprogram;
program:
:
=expr:
e{:
parser.parseResult=e;:
};
对于一些常规的语法,只需返回相应的语句类型即可,如赋值语句
expr:
:
=lvalue:
lASSIGNexpr:
e
{:
RESULT=newAssignExp(lleft,l,e);:
};
对于需要连接起来的类型,如ExpList,FieldExpList,FieldList,DecList,可以通过消除左递归的方法来将多个具有向后指针的类型的语句相连,另外链表还可以为空,下面举ExpList的例子来说明处理的方法:
expr_list:
:
=expr_list_n:
l…………………………………………………
(1)
{:
RESULT=l;:
}
|{:
RESULT=null;:
};
expr_list_n:
:
=expr:
eexpr_list_ex:
x…………………………………
(2)
{:
RESULT=newExpList(e,x);:
};
expr_list_ex:
:
=COMMAexpr_list_n:
l……………………………………(3)
{:
RESULT=l;:
}
|{:
RESULT=null;:
};
在
(1)开
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 编译 原理 课程设计 报告