武汉理工大学编译原理课程设计.docx
《武汉理工大学编译原理课程设计.docx》由会员分享,可在线阅读,更多相关《武汉理工大学编译原理课程设计.docx(34页珍藏版)》请在冰豆网上搜索。
武汉理工大学编译原理课程设计
学号:
课程设计
题目
24点速算游戏
学院
计算机科学与技术学院
专业
软件工程
班级
软件zy1302班
姓名
指导教师
饶文碧
2016
年
1
月
8
日
课程设计任务书
学生姓名:
专业班级:
软件zy1302班
指导教师:
饶文碧工作单位:
计算机科学与技术学院
题目:
24点速算游戏
1.目的
通过设计、编制、调试一个24点速算程序,加深对语法及语义分析原理的理解,并实现词法分析程序对单词序列的词法检查和分析。
2.设计内容及要求
程序输入:
1-12中的4个数字;
程序输出:
由上述4个数字及“+,-,*,/”组成的计算结果为24的算术表达式;
(1)学号5,16,27的同学选择任意方法完成以上任务,最终输出正确的算术表达式。
(2)写出算术表达式的符合分析方法要求的文法,给出分析方法的思想,完成分析程序设计。
(3)编制好分析程序后,设计若干用例,上机测试并通过所设计的分析程序。
3.上机时间安排
设计时间:
第18周,周四上午8:
00-12:
00,周五下午2:
00-5:
30。
指导教师签名:
年月日
系主任(或责任教师)签名:
年月日
1引言
课程设计是对学生的一种全面综合训练,是与课堂听讲、自学和练习相辅相成的必不可少的一个教学环节。
通常,设计题中的问题比平时的练习题要复杂,也更接近实际。
编译原理这门课程安排的课程设计的目的是旨在要求学生进一步巩固课堂上所学的理论知识,深化理解和灵活掌握教学内容,选择合适的数据逻辑结构表示问题,然后编制算法和程序完成设计要求,从而进一步培养学生独立思考问题、分析问题、解决实际问题的动手能力。
要求学生在上机前应认真做好各种准备工作,熟悉机器的操作系统和语言的集成环境,独立完成算法编制和程序代码的编写。
随着科技发展和社会进步,尤其是计算机大范围的普及,计算机应用逐渐由大规模计算的海量数据处理转向大规模的事物处理和对工作流的管理,这就产生以台式计算机为核心的管理系统。
本次课程设计的题目是速算24点,是在80年代成为一种流行的游戏,在中国把这游戏叫做“24点游戏”。
计算24点游戏:
任意输入4位数字,利用+,-,*,/四则运算使之得到结果24。
输出所有不同算法的计算表达式,可为运算优先级而使用括号。
速算24点游戏始于何年何月已无从考究,但它以自己独具的数学魅力和丰富的内涵正逐渐被越来越多的人们所接受。
这种游戏方式简单易学,能健脑益智,是一项极为有益的活动。
笔者抛弃常用的编程方式(直接根据表达式进行求值判断是否值为24),采用构造编译器的模式,对表达式进行词法分析、语法分析和语义分析,进而得到表达式的值,尽管这样有些大材小用,但是对于我理解编译原理有极大的帮助。
2文法及中间代码形式的描述
2.1文法的描述
自己根据表达式的特点归纳了一个初始文法G[E]:
E–>E+T|E-T|T
T–>T*F|T/F|F
F–>(E)|d
但据观察,上述文法有左递归性,将上述文法消除左递归可得出下面的文法:
E–>TE’
E’–>+TE’|-TE’|ε
T–>*FT’|/FT’|ε
F–>(E)|d
3词法分析方法
词法分析时编译的第一个阶段,他的主要任务是从左至右逐个字符地对源程序进行扫描,产生一个个单词序列,用以语法分析。
执行词法分析的程序称为词法分析程序或扫描程序。
自定义类Token存放识别出的单词属性,Token数据结构如下:
publicclassToken
{
privateStringtokenType;//单词种别码
privateStringtokenValue;//单词值
publicToken(StringtokenType,StringtokenValue)
{
this.tokenType=tokenType;
this.tokenValue=tokenValue;
}
publicStringgetTokenType()
{
returntokenType;
}
publicStringgetTokenValue()
{
returntokenValue;
}
}
该词法分析器能够识别运算符:
+-*/,能够识别界符:
(),能够识别数字,其DFA在下面的词法分析详细描述部分有介绍。
4语法分析方法
语法分析是编译程序的核心部分。
语法分析的作用是识别由词法分析给出的单词符号序列是否是给定文法的正确句子,目前语法分析常用的方法有自顶向下分析和自底向上分析两大类。
本文采用自顶向下分析中的递归下降法来分析条件表达式。
递归子程序法是比较简单直观易于构造的一种语法分析方法。
其实现思想:
文法中每个非终结符对应一个递归过程(子程序),每个过程的功能是识别由该非终结符推出的串,当某非终结符的产生式有多个候选式时能够符合LL
(1)文法形式可唯一地确定选择某个候选式进行推导。
本课设还采用了语法制导翻译技术,在进行语法分析的同时进行语义分析,该表达式的语义也就是该表达式对应的值,对整个单词符号序列的语法分析进行完后,也就能够得到其所对应的值。
5详细的算法描述
5.1词法分析
词法分析阶段是编译过程的第一个阶段,是编译的基础。
这个阶段的任务是从左到右一个字符一个字符地读入源程序,即对构成源程序的字符流进行扫描然后根据构词规则识别单词(也称单词符号或符号)。
词法分析程序实现这个任务。
词法分析程序可以使用Lex等工具自动生成。
词法分析是编译程序的第一个阶段且是必要阶段;词法分析的核心任务是扫描、识别单词且对识别出的单词给出定性、定长的处理;实现词法分析程序的常用途径:
自动生成,手工生成。
本次课程设计采用手工生成,原因:
表达式词法分析十分简单,不需要自动生成;笔者希望从此次构造词法分析器的过程中,加深对编译原理的认知。
自定义类Token存放识别出的单词属性,其结构上文已有介绍。
表达式的词法分析程序对应的DFA如下:
笔者为语法分析器构造了一个接口,凡是实现了此接口的类,都能够实现词法分析的功能,达到了低耦合的效果,下列代码是词法分析的核心代码:
ExpressionLexer.java:
publicinterfaceExpressionLexer
{
/**
*对给定的字符串,进行词法分析,返回单词队列
*@paramstring字符串,分析的对象
*@return单词队列
*/
Queuescan(Stringstring);
}
ExpressionLexerImpl.java:
publicclassExpressionLexerImplimplementsExpressionLexer
{
Stringstring;
intindex;
privatevoidinit(Stringstring)
{
this.string=string;
index=0;
}
privatecharpeekChar()
{
if(index==string.length()-1&&string.charAt(index)!
='#')
{
string=string+"#";
}
returnstring.charAt(index);
}
privatecharpollChar()
{
charc=string.charAt(index);
index++;
returnc;
}
@Override
publicQueuescan(Strings)
{
init(s);
Queuetokens=newArrayDeque<>();
while(index{
switch(peekChar())
{
case'(':
pollChar();
tokens.offer(newToken("(","("));
break;
case')':
pollChar();
tokens.offer(newToken(")",")"));
break;
case'+':
pollChar();
tokens.offer(newToken("+","+"));
break;
case'-':
pollChar();
tokens.offer(newToken("-","-"));
break;
case'*':
pollChar();
tokens.offer(newToken("*","*"));
break;
case'/':
pollChar();
tokens.offer(newToken("/","/"));
break;
case'9':
case'8':
case'7':
case'6':
case'5':
case'4':
case'3':
case'2':
case'1':
case'0':
Stringdigit=""+pollChar();
if(Character.isDigit(peekChar()))
digit+=pollChar();
tokens.offer(newToken("digit",digit));
break;
case'#':
pollChar();
tokens.offer(newToken("end","#"));
returntokens;
default:
try
{
thrownewException("scan()");
}catch(Exceptione)
{
System.out.println("scan():
somethingwrong..");
e.printStackTrace();
}
}
}
returntokens;
}
}
5.2语法分析
语法分析是编译过程的一个逻辑阶段。
语法分析的任务是在词法分析的基础上将单词序列组合成各类语法短语,如“程序”,“语句”,“表达式”等等,语法分析程序判断源程序在结构上是否正确,源程序的结构由上下文无关文法描述,语法分析程序可以用YACC等工具自动生成。
目前,已存在许多语法分析的方法。
但就产生语法树的方向而言,可大致把他们分为自底向上和自顶向下两大类。
目前比较流行LL分析法和LR分析法。
本课程设计采用自顶向下的语法分析,将下述文法G[E],进行改写:
E–>E+T|E-T|T
T–>T*F|T/F|F
F–>(E)|d
消除左递归可得出下面的文法:
E–>TE’
E’–>+TE’|-TE’|ε
T–>*FT’|/FT’|ε
F–>(E)|d
经推导证明,改写后的文法是LL
(1)文法,可以使用递归下降法构造语法分析器,笔者为语法分析器也设定了接口,达到低耦合的目标,核心代码如下:
ExpressionParser.java:
publicinterfaceExpressionParser
{
/**
*对给定的token串进行语法分析,语义分析
*@paramtokensToken队列,分析的对象
*@return算术表达式的值,分析的结果
*/
doubleparse(Queuetokens);
}
ExpressionParserImpl.java:
publicclassExpressionParserImplimplementsExpressionParser
{
privateQueuetokens;
privatevoidinit(Queuetokens)
{
this.tokens=tokens;
}
privateTokenpeekToken()
{
returntokens.peek();
}
privateTokenpollToken()
{
returntokens.poll();
}
@Override
publicdoubleparse(Queuet)
{
init(t);
returnexpression();
}
privatedoubleexpression()
{
doubletermValue=term();
returnexpression_(termValue);
}
privatedoubleexpression_(doubletermValue)
{
doubleresult=0.0;
switch(peekToken().getTokenType())
{
case"+":
pollToken();
result=termValue+term();
result=expression_(result);
break;
case"-":
pollToken();
result=termValue-term();
result=expression_(result);
break;
case")":
case"end":
result=termValue;
break;
default:
try
{
thrownewException("expression_()");
}catch(Exceptione)
{
System.out.println("expression_():
somethingwrong..");
e.printStackTrace();
}
}
returnresult;
}
privatedoubleterm()
{
doublefactorValue=factor();
returnterm_(factorValue);
}
privatedoublefactor()
{
doubleresult=0.0;
switch(peekToken().getTokenType())
{
case"(":
pollToken();
result=expression();
pollToken();
break;
case"digit":
result=Double.parseDouble(pollToken().getTokenValue());
break;
default:
try
{
thrownewException("factor()");
}catch(Exceptione)
{
System.out.println("factor():
somethingwrong..");
e.printStackTrace();
}
}
returnresult;
}
privatedoubleterm_(doublefactorValue)
{
doubleresult=0.0;
switch(peekToken().getTokenType())
{
case"*":
pollToken();
result=factorValue*factor();
result=term_(result);
break;
case"/":
pollToken();
result=factorValue/factor();
result=term_(result);
break;
case"+":
case"-":
case")":
case"end":
result=factorValue;
break;
default:
try
{
thrownewException("term_()");
}catch(Exceptione)
{
System.out.println("term_():
somethingwrong..");
e.printStackTrace();
}
}
returnresult;
}
}
6源程序清单
Token.java:
publicclassToken
{
privateStringtokenType;//单词种别码
privateStringtokenValue;//单词值
publicToken(StringtokenType,StringtokenValue)
{
this.tokenType=tokenType;
this.tokenValue=tokenValue;
}
publicStringgetTokenType()
{
returntokenType;
}
publicStringgetTokenValue()
{
returntokenValue;
}
}
ExpressionLexer.java:
publicinterfaceExpressionLexer
{
/**
*对给定的字符串,进行词法分析,返回单词队列
*@paramstring字符串,分析的对象
*@return单词队列
*/
Queuescan(Stringstring);
}
ExpressionLexerImpl.java:
publicclassExpressionLexerImplimplementsExpressionLexer
{
Stringstring;
intindex;
privatevoidinit(Stringstring)
{
this.string=string;
index=0;
}
privatecharpeekChar()
{
if(index==string.length()-1&&string.charAt(index)!
='#')
{
string=string+"#";
}
returnstring.charAt(index);
}
privatecharpollChar()
{
charc=string.charAt(index);
index++;
returnc;
}
@Override
publicQueuescan(Strings)
{
init(s);
Queuetokens=newArrayDeque<>();
while(index{
switch(peekChar())
{
case'(':
pollChar();
tokens.offer(newToken("(","("));
break;
case')':
pollChar();
tokens.offer(newToken(")",")"));
break;
case'+':
pollChar();
tokens.offer(newToken("+","+"));
break;
case'-':
pollChar();
tokens.offer(newToken("-","-"));
break;
case'*':
pollChar();
tokens.offer(newToken("*","*"));
break;
case'/':
pollChar();
tokens.offer(newToken("/","/"));
break;
case'9':
case'8':
case'7':
case'6':
case'5':
case'4':
case'3':
case'2':
case'1':
case'0':
Stringdigit=""+pollChar();
if(Character.isDigit(peekChar()))
digit+=pollChar();
tokens.offer(newToken("digit",digit));
break;
case'#':
pollChar();
tokens.offer(newToken("end","#"));
returntokens;
default:
try
{
thrownewException("scan()");
}catch(Exceptione)
{
System.out.println("scan():
somethingwrong..");
e.printStackTrace();
}
}
}
returntokens;
}
}
ExpressionParser.java:
publicinterfaceExpressionParser
{
/**
*对给定的token串进行语法分析,语义分析
*@paramtokensToken队列,分析的对象
*@return算术表达式的值,分析的结果
*/
doubleparse(Queuetokens);
}
ExpressionParserImpl.java:
publicclassExpressionParserImplimplementsExpressionParser
{
privateQueuetokens;
privatevoidinit(Queuetokens)
{
this.tokens=tokens;
}
privateTokenpeekToken()
{
returntokens.peek();
}
privateTokenpo