一、综合训练目的与要求
本综合训练是软件工程专业重要的实践性环节之一,是在学生学习完《编译原理》课程后进行的综合练习。
本课综合训练的目的:
- 巩固和加深学生对编译原理课程基本知识的理解和掌握;
- 提高抽象思维能力以及加深理解编译系统的原理;
- 掌握利用JavaCC开发编译系统的方法;
- 掌握书写设计与实现编译系统说明文档的能力;
- 提高综合运用算法、程序设计语言、数据结构知识的能力。
本课程综合训练的要求:
1.使用文件读入的方式测试编译程序;
2.将词法分析、语法分析和语义分析结果分别以文件方式输出;
3.尽可能实现要求的功能,可以自己决定实现哪些扩展功能;
4.不要求实现界面,重点实现编译功能。(开发时间有剩余的同学可以实现界面。)
编译功能要求:
1.能够根据单词的构词规则,完成MiniC语言中的单词的解析(词法分析),如果不符合单词的构词规则,请给出错误信息。如果源语言符合单词的词法规则,输出<单词种别,单词自身值>二元式;
2.在词法分析的基础上,构造MiniC的递归下降分析文法,利用JavaCC实现递归下降文法。判断源语言是否符合MiniC的语法,如果符合,输出语法树;否则,请给出语法错误信息;
3.在语法分析的基础上,根据属性文法制导翻译,进行语义分析,输出四元式。如果源语言不符合MiniC的语义,请指出错误信息;
4.在实习课的基础上,整个编译系统要能够翻译声明语句、赋值语句、布尔表达式,if-else, for, while,do-while,switch-case等语句嵌套的分析与翻译;符号表、语义错误检查:变量重复声明、变量使用时未定义。
5.扩展内容:可以考虑自增、自减、main()方法调用其它子程序的翻译、数组(二维,多维)翻译、数组下标越界等错误、变量赋值精度错误、函数返回值类型不匹配等;
6.扩展内容:可以添加功能,如break、continue、return语句、一维指针等。
二、综合训练任务描述
首先需要的是大概的确立大致的结构,我们可以使用之前实验所给的MiniC语法,并且对上诉的编译功能要求做出响应的修改和添加;
1.实现功能的顺序可以按着学习和实验的顺序来进行,我们可以先进行词法分析部分的代码编写,添加相应的所需要的词法规则,用代码来描述对象语言的词法规则,将关键字、标识符、分隔符、运算符、常数和其他需要过滤掉的字符;定义完成后对mian函数进行添加修改,使用文件输出流、输入流来对测试代码文件进行读出处理后再将词法分析输出的值和对应的关键字输出到指定的文件中。
图 1 词法分析
2.在完成了对词法分析的操作后我们进一步的来完善我们的语法,语法方面我们可以对之前实验用到的MiniC语法进行修改添加成我们自己的语法,如下:
<程序> ->Type main(){<语句块>}
<语句块> -><语句>|{<语句块>}
<顺序语句> ->(<声明语句>|<赋值语句>)”;”
<声明语句> ->Type ID,ID,…,ID
<赋值语句> ->ID =<表达式>
<条件语句> ->if(<booler表达式>)<语句块>else<语句块>
<循环语句> ->while(<booler表达式>)<语句块>
<for循环语句>->FOR(AssignSentence”;”<booler表达式>”;”AssignSentence){语句块}
<do-while语句>->do(语句块)while( <逻辑>)”;”
<switch-case语句>->switch){(case <INTEGER_LITERAL>”:”(语句块)* break”;”)* [defaults:[语句块]break”;”]}
<booler表达式>-><表达式>(<关系符><表达式>)?((|()))?
<逻辑> -><条件>(<逻辑运算符><条件>)*
<条件> -><表达式>(<关系符><表达式>)?
<表达式> ->//对默认的进行小小的修改
<关系符> -><|<=|>|>=|==|!=
<逻辑运算> ->&& | || (包含在逻辑中)
语法部分根据了进度的变化做了相对的调整,这里就只展示最后修改的。
3.语法分析结束后开始对语义分析的代码编写,创建四元式并且对其进行拉链回填操作。
使用了老师所给的工具代码包来对功能进行实现。下面就将功能要求中的几个主要的功能进行代码的展示和运行结果的截图;
三、编译系统设计
在这里插入图片描述
图 2 编译类与辅助类图关系
多个辅助类,都放在了utils包中,并且几个类中都有联系,而且我们可以在我们的语法类中进行使用。
布尔表达式的代码如下:
ConditionValue Booler()😕/布尔表达式
//因为此处需要对所得的表达式需要拉链,但是要像通过Condition获取Value的话就不能获取QTInfo的对象
{
ConditionValue Value = new ConditionValue();
String str1 = null,str2 = null;
Token op = null;
Token op1= null;
ConditionValue Value2 = new ConditionValue();
ConditionValue Valuet = new ConditionValue();
int qtSize = QTInfo.size;
QTInfo qt1;
QTInfo qt2;
}
{
str1 = Expression()
(op=Relations() str2 = Expression())?
{
// QTInfo qt1;
if(op != null) {
qt1 = new QTInfo(“J”+op.image,str1,str2,QTInfo.size+3);
qtList.addQTInfo(qt1);
//Value.mergeTrue(qt1);
}
else {
qt1 = new QTInfo(“Jnz”,str1,““,QTInfo.size+3);
qtList.addQTInfo(qt1);
//Value.mergeTrue(qt1);
}
qt2 = new QTInfo(“J”,””,“_”,“F”);
qtList.addQTInfo(qt2);
Value.mergeFalse(qt2);
//Value.mergeFalse(qt2);
//Value.backpatchFalseChain(QTInfo.size+1);
}
(
(
op1=< AND >
{//回填?
Valuet = new ConditionValue();
Valuet.mergeFalse(qt2);
//Value.backpatchFalseChain(QTInfo.size+1);
//且出了问题,应该改为且的前后拉假链再重新定义假链
Value.backpatchTrueChain(QTInfo.size+1);
}
| (
op1=< OR >
{//回填?
//Value.mergeTrue(qt1);
Valuet.mergeFalse(qt2);
Valuet.backpatchFalseChain(QTInfo.size+1);
Value.backpatchFalseChain(QTInfo.size+1);
//Value.backpatchTrueChain(QTInfo.size+1);
}
)
)
Value2=Booler()
//只要跑到了这就是开始循环嵌套了,下面的语句是结束的时候跑的
{
if(op1.image.equals(“&&”))
{
//拉链回填?
// Value.mergeFalse(qt2);
// Value.mergeFalse(qt2);
// Value.backpatchFalseChain(QTInfo.size+1);
// Value = new ConditionValue();
Value2.mergeFalse(qt2);
//且不在的第一位的时候可以用,但是第一位时不能直接拉
Value2.backpatchFalseChain(QTInfo.size+1);
}if(op1.image.equals("||"))
{
//拉链回填?
// Valuet.mergeFalse(qt2);
// Valuet.backpatchFalseChain(QTInfo.size+1);
Value2.mergeTrue(qt1);
Value2.backpatchTrueChain(QTInfo.size+1);
}
return Value2;
}
)?
{
return Value;
}
}
If-else语句,每次遇到If都回产生两个四元式,一个是条件为T时一个是条件为F,将T的拉真链,加的拉假链,然后在if中使用条件语句后将此时的QTInfo的数量回填两条链,有else的情况下再添加两个四元式,进行相似的操作。
If-else语句
图 3 if-else语句
While语句,就是使用了Booler表达式作为判断式,随后再将真链回填,再新建假链并且回填假链。
图 4 While循环语句
Switch-case语句,在每次的case中都回创建两条,一条是当当前的值匹配的话就跳到语句块,并且进行拉链回填上一个的假链,再重置这跳链以免拉链回填时回将前面的也一起填了从而导致覆盖问题。也就是每一个case的假链是连在了下一个假链的开始的。另一条是不匹配时跳到下一个case,并且进行拉链回填;之后再对defaults进行定义,直接添加default中的语句块所产生的四元式。
图 5 Switch-case语句
do-while语句,dowhile其实也就是类似于while。只需要对进行一些简单的修改便可,在判断条件正确的情况下返回到do,也就是拉链回填的一些逻辑的变化。
图 6 Do-While循环语句
添加符号表、语义错误检查:变量重复声明、变量使用时未定义:
符号表等相关工具代码老师都有给,添加到我们所使用的工具包中就可以,错误检查可以添加到相关操作的代码函数中。
图 7 变量重复声明
图 8赋值时未定义
图 9 变量使用时未定义
以上这些都能够实现语句的嵌套的分析和翻译。
四、调试与测试
图 10if-else
图 11While
图 12Switch-case
图 13 do-while
图 14 未定义错误检查
图 15 重复定义错误检查
五、实习日志
2021年12月27日 星期一
上午:合并之前的项目,将基本功能实现,写出for循环的语法分析
下午:对合并的项目的代码逻辑进行优化,并且写一部分for循环的语义分析
2021年12月28日 星期二
上午:将for循环的语义分析完成,并且完成do-while循环语句
下午:完成switch-case语法规则和语义分析
2021年12月29日 星期三
上午:添加布尔表达式的语法规则,并思考如何编写布尔表达式的语义分析
下午:完成布尔表达式的语义分析。
2021年12月30日 星期四
上午:添加错误分析
下午:写报告和ppt
2021年12月31日 星期五
上午:答辩
下午;写报告
六、实习总结
实现了基础的功能,也添加了几个额外操作,例如break和return,没有实现太多的额外功能。通过这次为时一周的编译原理实训,实现了一个MiniC语言编译系统的设计与实现,大部分的时间都花在了思考如何去实现语法分析的逻辑和语义分析的逻辑,拉链和回填代码的逻辑。通过对这个编译系统的编写实现,让我对如何去分析程序、词法语法规则以及编写四元式有了更加清晰的理解,逻辑更加清晰。