一、实验目的
1.1总体目的
1.1.1 掌握词法分析的基本原理;
1.1.2. 理解词法分析在编译程序过程中的作用;
1.1.3. 熟悉关键字表等相关的数据结构与单词的分类方法;
1.1.4. 加深对编译原理的理解,掌握词法分析器的实现方法和技术,同时,将JAVA的理论知识结合实际,锻炼编程技术,强调良好的程序设计风格。
1.2程序目的
利用JAVA语言针对C语言编制一个一遍扫描的编译程序。从文件中识别出各个单词,识别出所取的单词的类型,并且对代码中的词法错误进行提示。
二、实验内容
根据编译原理中的词法分析原理,利用Java语言针对C语言编写一个词法分析程序:
输入:打开一个C语言程序的源代码文件,将其读入程序输入框。
处理:对输入框中的代码进行词法分析,分离出关键字、标识符、数值、运算符和界符。
输出:在词法分析结果表中输出每个单词所在行号、类型以及它所对应的编码。其中,编码是自定义的,一种类型对应一组编码。词法分析结果显示在词法分析错误信息栏,提示错误个数、错误所在行号,并对某些词法错误原因进行说明。
三、实验需求
针对C语言程序代码进行词法分析器,从指定文件中读入预分析的源程序,从左至右扫描源程序的字符串,按照词法规则(正则文法规则)识别出一个个正确的单词,并转换成该单词相应的二元式(种别码、属性值)以便之后进行语法分析使用。
同时,按照给定的规则,识别出单词符号作为输出,发现其中的语法错误,不同类别的字符通过相应的函数模块来分析识别,使程序能够正确识别文法所规定的任何组织形式的字符组合,将所有的分析状态显示在词法分析器中。最后在错误分析栏中显示该文件中C语言代码的词法错误个数、错误所在行,并对错误原因进行说明。
四、主要数据结构介绍
4.1关键字编码
4.2标识符统一编码 100
4.3数值统一编码 200
4.4界符编码
4.5运算符编码
4.6全局变量含义
int row:语法错误出现的所在列数
int line:语法错误出现的所在行数
int err:语法错误的个数
int begin:当前程序扫描在字符串中的开始位置
int end:当前程序扫描在字符串中的结束位置
4.7局部变量定义
int i:选择第i个字符进行检测
int state:单词类型判断标志
int N:文件长度
char c:当前遍历的字符
string str:输入字符串
int flag:退出标志
五、主要模块算法介绍
5.1总体流程介绍
说明:state为输入字符状态标志,根据输入字符不同类型选择不同处理。
5.2主要分支选择算法介绍
5.2.1 case0情况算法
5.2.2 case 1情况算法
if(c == '+'){state = 0;error_text.append("\t运算符\t\t401 "+"\t++"+'\n');
} else if(c == '=')
{state = 0;error_text.append("\t运算符\t\t402 "+"\t+="+'\n');
}else
{state = 0;
if(isDigit(content.charAt(i - 2)))//isDigit函数判断是否为数值
error_text.append("\t数 值\t" +"\t200 "+content.substring(begin, i-1) + '\n');
error_text.append("\t运算符\t\t403 "+"\t+"+'\n');//输出结果
i--;row--;
}
说明:对运算符++、+=、+以及数值进行判断结果输出。
5.2.3 case 2情况算法
if(c == '-') error_text.append("\t运算符\t\t404 "+"\t--"+'\n');
else if(c == '=') error_text.append("\t运算符\t\t405 "+"\t-="+'\n');
else if(c=='>') error_text.append("\t运算符\t\t423 "+"\t->"+'\n');
else{ error_text.append("\t运算符\t\t406 "+"\t-"+'\n');i--;row--;}
state = 0;
说明:对运算符--、-= 、->、-,进行判断结果输出。
5.2.4 case 3情况算法
if(c == '=')error_text.append("\t运算符\t\t407 "+"\t*="+'\n');
else{error_text.append("\t运算符\t\t408 "+"\t*"+'\n');i--;row--;}
state = 0;
说明:对运算符*=,*,进行判断结果输出。
5.2.5 case 4情况算法
if(c == '/'){while((c) != '\n'){c = content.charAt(i);i++;}
state = 0;error_text.append("\t注释部分\t\t// \n");}
else if(c == '='){state = 0;error_text.append("\t运算符\t\t409 "+"\t/="+'\n');}
else{state = 0;error_text.append("\t运算符\t\t410 "+"\t/"+'\n');i--;row--;}
说明:对注释、运算符/=、/,进行判断结果输出。
5.2.6 case 5情况算法
if(c == '='){error_text.append("\t运算符\t\t411 "+"\t!="+'\n');state = 0;}
else{state = 0;i--;row--;error_text.append("\t运算符\t\t412 "+"\t!"+'\n');}
说明:对运算符!=、!,进行判断结果输出。
5.2.7 case 6情况算法
if(c == '='){error_text.append("\t运算符\t\t413 "+"\t>="+'\n');state = 0;}
if(c=='>') {error_text.append("\t运算符\t\t426 "+"\t>>"+'\n');state=0;}
else{state = 0;i--;row--;error_text.append("\t运算符\t\t414 "+"\t>"+'\n');}
说明:对运算符>=、>>、>,进行判断结果输出。
5.2.8 case 7情况算法
if(c == '='){error_text.append("\t运算符\t\t415 "+"\t<="+'\n');state = 0;}
if(c=='<') {error_text.append("\t运算符\t\t427 "+"\t<<"+'\n');state=0;}
else{state = 0;i--;row--;error_text.append("\t运算符\t\t416 "+"\t<"+'\n');}
说明:对运算符<=、<<、<,进行判断结果输出。
5.2.9 case 8情况算法
if(c == '='){error_text.append("\t运算符\t\t417 "+"\t=="+'\n');state = 0;}
else{state=0;i--;row--;error_text.append("\t运算符\t\t418 "+"\t="+'\n');}
说明:对运算符==、=,进行判断结果输出。
5.2.10 case 9情况算法
state = 0;i--;row = 1;line ++;
说明:如果输入的字符是回车,直接将行数加1,列数为1。
5.2.11 case 10情况算法
if(isLetter(c) || isDigit(c)){state = 10;}
else{end = i;String id = content.substring(begin, end);
if(isKey(id)!=0) {int t=isKey(id);error_text.append("\t关键字\t\t"+t+ id + '\n');
} elseerror_text.append("\t标志符\t" +"\t100"+id + '\n');i--;row--;state = 0;}
说明:如果输入的是字母或者数字,state=10,否则,该字母或数字结束,取出,判断是否为关键字,输出判断结果。其中isKey()为判断单词是否为关键字的函数。
5.2.12 case 11情况算法
if(c == 'e' || c == 'E')state = 13;else if(isDigit(c) || c == '.'){}else {if(isLetter(c)){err++;String b=end_text.getText();b+="错误: line " + line + " row " + row + " 数字格式错误或者标志符错误\n";end_text.setText(b);
error_text.append("错误: line " + line + " row " + row + " 数字格式错误或者标志符错误\n");}int temp = i;i = find(i,content);row += (i - temp);state = 0;}
说明:对用科学计数法表示的数值的判断及错误情况分析。
5.2.13 case 12情况算法
String id = "";while(c != '<'){id += c;i++;c = content.charAt(i);}
if(id.trim().equals("include")){while(c != '>' && ( c != '\n')){i++;
c = content.charAt(i);}if(c == '>')error_text.append("\t头文件引入 \n");
}else {err++;String d=end_text.getText();d+="错误: " + "line " + line + ", row " + row + " 语法错误\n";end_text.setText(d);error_text.append("错误: " + "line " + line + ", row " + row + " 语法错误\n");}state = 0;
说明:对于头文件引入正确性的判断。
5.2.14 case 13情况算法
if(c == '+' || c == '-' || isDigit(c)){i++;c = content.charAt(i);while(isDigit(c)){
i++;c = content.charAt(i);}if(isLetter(c) || c == '.') {err++;String e=end_text.getText();e+="错误: line " + line + " row " + row + " 指数格式错误!\n";end_text.setText(e);error_text.append("错误: line " + line + " row " + row + " 指数格式错误!\n");state = 0;int temp = i;i = find(i,content);row += (i - temp);
}else{end = i;error_text.append("\t指 数\t\t" + content.substring(begin, end) + '\n');}state = 0;}
说明:对于指数格式的检测、判断以及分析结果输出。
5.2.15 case 14情况算法
if(c == '&'){if(isDigit(content.charAt(i - 2)))error_text.append("\t数 值\t" +"\t200 "+content.substring(begin, i-1) + '\n');error_text.append("\t运算符\t"+"\t419 "+"\t&&"+ "\n");}else{i--;error_text.append("\t运算符\t"+"\t420 "+"\t&"+ "\n");}state = 0;
说明:运算符&、&&的判断输出,以及对于数值情况的处理。
5.2.16 case 15情况算法
if(c == '|') {if(isDigit(content.charAt(i - 2)))error_text.append("\t数 值\t" +"\t200"+content.substring(begin, i-1) + '\n');error_text.append("\t运算符\t"+"\t421 "+"\t||"+ "\n");} else{i--;error_text.append("\t运算符\t"+"\t428 "+"\t|"+ "\n");}state = 0;
说明:运算符|、||的判断输出,以及对于数值情况的处理。
5.2.17 其他情况算法
对于state为其他值时,处理情况仅为对应标号输出。
5.2.18 子函数功能简介
Compiler():用java语言编写的词法分析器的控制面板,通过其菜单栏选项可以将C语言程序导入到词法分析器中,开始进行词法分析。
checkLexical():用C语言编写的词法分析器,对导入的C语言程序进行词法分析,并显示相应的分析状态和错误提示。
isLetter():判断导入的C语言程序的词法类型是否属于英文字母。如果是,返回true;否则,返回false。
isDigit():判断导入的C语言程序的词法类型是否属于阿拉伯数字。如果是,返回true;否则,返回false。
isKey():判断导入的C语言程序是否为关键字,根据之前预定义的符号常量,返回相应的数值。如果不属于其中任何一种符号,则返回0;
find():根据词法分析器扫描的相应位置寻找分隔符空格、括号、回车等,随后返回字符串的长度。
六、 程序运行环境及使用说明
6.1程序运行环境
6.1.1 操作系统环境
该应用程序由JAVA代码编写,所以具有JAVA应用程序“一处编写,到处运行”的强大优势,换言之,该词法分析系统可以适用于任意一种操作系统。
6.1.2 软件环境
该应用程序需要在java的相关编程软件上运行,如Netbeans,Eclipse,JBuilder等
6.2使用说明
第一步装载本程序中的相关代码,第二步运行主程序,生成程序主界面,第三步点击“文件”菜单项,选择“打开”子菜单,程序运行主界面的左边的文本区域会显示调入的C语言源程序代码,第四步点击“词法分析”菜单,选择“开始”子菜单,程序运行主界面的上部文本区域为词法分析结果,下部区域为分析后的词法错误具体情况以及错误数目。
七、实验结果测试情况
运行主函数,主界面如图7.1所示:
图7.1 程序运行主界面
点击“文件”菜单,如图7.2所示:
图7.2 打开“文件”菜单项主界面
寻找目标文件,如图7.3所示:
图7.3 寻找目标文件界面
读入C语言源程序代码,如图7.4所示:
图7.4 读入C语言源程序代码界面
然后点击“词法分析”菜单,如图7.5所示:
图7.5 打开“词法分析”界面
词法分析结果如图7.6所示:
图7.6 分析结果界面
当读入一个有词法错误的C语言代码程序时,如图7.7所示:
图7.7 调入有语义错误的C语言代码界面
词法分析结果如图7.8所示:
图7.8 有语义错误的C语言代码词法分析结果界面
八、小组对实验结果的自我评价
8.1 不足之处:
8.1.1该项目主要是针对C语言源程序的词法分析,翻开C语言课本,一共有近40个运算符,而我们小组实现的只有26个针对常用运算符的分析;
8.1.2对于转义字符,欠缺了考虑;
8.1.3词法分析结果未能实现保存,以便为语法分析提供基础
8.2欣慰之处:
为期一个多月的编译原理课程实训已经结束了,在这一个多月的时间里,本小组成员本着一个认真的态度完成每一项任务,及时上交实训作业,老师的鼓励和肯定给予了我们很大的动力,最终我们小组完成了3个实训项目的主体功能。
回首本次实训,曾经茫然,去请教老师、同学;曾经也遇到过很多困难,有过很多争论,但就是在这样的过程中,我们锻炼了很多:小组的协同、合作能力,我们彼此沟通、交流表达自己思想的能力,我们的编程能力,我们的自主学习能力。
经历本次实训,相信我们小组的每个人都可以从中学到很多东西,这次实训的宝贵经验也肯定将会成为我们在以后成长道路上的一笔丰富财富! 谢谢老师!
九、任课教师对实验结果的评分