一、 实验目的
编译原理在JAVA可测试性中的应用。通过对源程序进行分析,在特定位置增加特定的打印语句,方便对程序执行过程的跟踪定位。比如:函数出入口增加打印语句。
- 实验要求
- 读取一个JAVA文件,进行语法解析,在函数出入口增加打印语句,并将改写后的程序更新到JAVA文件中去。
输入: 从控制台读入一个JAVA程序文件
输出:更新后的JAVA程序文件, 增加了打印语句
2. 输入输出文件示例:(仅为示意)
输入文件:
public class helloworld {
public static int Add(int a, int b){
int ret;
ret =a+b;
return ret;
}
public static void main(String[] args) {
System.out.println("result="+Add(1,2));
}
}
输出文件:
public class helloworld {
public static int Add(int a, int b){
System.out.println("Enter Function helloworld::Add(a="+a+",b="+b+")!");
int ret;
ret =a+b;
System.out.println("Exit Function helloworld::Add(a="+a+",b="+b+")!");
return ret;
}
public static void main(String[] args) {
System.out.println("Enter Function helloworld::main(args="+args.toString()+")!");
System.out.println("result="+Add(1,2));
System.out.println("Exit Function helloworld::main(args="+args.toString()+")!");
}
}
下面将上传词法分析,因为我饿了,要去吃饭了,张玲玲老师的编译原理实验课使人秃头
package test0601;
import java.util.ArrayList;
import java.util.Scanner;
/**
* 项目名称:test
* 类名称:lex
* 类描述:
*
* @author LYL
*Date 2018年12月6日
*/
public class lex {
// 自定义的枚举类型,单词符号类别定义,与实验的表格要求一致
public enum symbol {
period(".", 0), plus("+", 1), minus("-", 2), times("*", 3), slash("/", 4), eql("=", 5), neq("<>", 6),
lss("<", 7), leq("<=", 8), gtr(">", 9), geq(">=", 10), lparen("(", 11), rparen(")", 12), semicolon(";", 13),
classsym("class", 14), publicsym("public", 15), privatesym("private", 16), ifsym("if", 17),
staticsym("static", 18), whilesym("while", 19), returnsym("return", 20), ident("IDENT", 21),
number("number", 22), nil("nil", 23), voidsym("void", 24), intsym("int", 25), charsym("char", 26),
stringsym("String", 26), lbracesym("{", 27), rbrace("}", 28), lsquare("[", 29), rsquare("]", 29),
protectedsym("protected", 30), finalsym("final", 31), commasym(",", 32), langsym("~", 33);
private String strName;
private int iIndex;
private symbol(String name, int index) {
this.strName = name;
this.iIndex = index;
}
public String getStrName() {
return strName;
}
public void setStrName(String strName) {
this.strName = strName;
}
public int getiIndex() {
return iIndex;
}
public void setiIndex(int iIndex) {
this.iIndex = iIndex;
}
}
// 单词类定义二元组:(单词类别, 单词名)
public class aWord {
String name;
symbol symtype;
private aWord(String name, symbol symtype) {
this.name = name;
this.symtype = symtype;
}
public String toString() {
return "(" + this.symtype.iIndex + "," + this.name.trim() + ")";
}
}
/********************************************************************************
* 变量说明: line 从终端读入的字符串; 当前所指位置在计数器 iCurPos, 字符为ch token 正在识别的单词字符串;当前所指位置在计数器
* iIndex sym 每个单词符号种类,来源于symbol, 例:symbol.number keyword 保留字表 , 包括:begin, do,
* end , if, then, while(已排好序) Symlist 识别出的符号表,每个元素是一个单词的二元组(sym, token)
*********************************************************************************/
String line;
int iCurPos = 0; // 当前所指位置在计数器 iCurPos
char[] token; // 当前所指位置在计数器
int iIndex = 0; // 当前所指位置在计数器 iInde
int Err;
symbol sym;
String[] keyword = { "if", "while", "String", "class", "boolean", "char", "class", "int", "private", "protected",
"public", "return", "static", "void" };
ArrayList<aWord> Symlist;
// 从终端读入一行程序
public String getProgram() {
// 此处需要从控制台输入文件内容 将多行文件处理为单行字符串
System.out.println("请在此输入并以空行结束");
StringBuilder str = new StringBuilder();
Scanner scanner = new Scanner(System.in);
while (true) {
String text = scanner.nextLine().trim();
if ("".equals(text)) {
break;
}
str.append(text);
}
// 读取实现
line = str.toString() + "~";
return line;
}
// 增加一个单词到符号表里
public void AddaWordtoList() {
aWord aWord;
aWord = new aWord((new String(token)).trim(), sym);
Symlist.add(aWord);
}
// Main函数:主入口
public static void main(String[] args) {
lex lex = new lex();
lex.Symlist = new ArrayList<aWord>();
lex.Symlist.clear();
lex.line = lex.getProgram();
lex.iCurPos = 0;
lex.Err = 0;
System.out.println("预处理后的字符串:\n" + lex.line + "\n");
// ==========词法&语法分析:一趟完成==========
// lex.getSym(); // 向前看
while ((lex.iCurPos < lex.line.length()) && (lex.line.charAt(lex.iCurPos) != '~')) {
lex.getSym();
// 把单词加到符号表中去
lex.AddaWordtoList();
}
System.out.println("程序开始输出单词表");
// 输出单词符号表
for (int i = 0; i < lex.Symlist.size(); i++) {
System.out.print(lex.Symlist.get(i).toString().trim() + " ");
// 这个判断是为了格式化输出代码,在后期输出代码的过程中可以借鉴使用,哪里该加向后空格,哪里该向前空格,还是忙意思的
if (lex.Symlist.get(i).name.equals(";") || lex.Symlist.get(i).name.equals("{")
|| lex.Symlist.get(i).name.equals("}")) {
System.out.println();
System.out.print(" ");
}
}
// 将文件写到新的文件中,多次操作覆盖原文件,懒的话就打印到控制台
System.out.println("\n**********程序运行结束**********");
}
public void getSym() {
boolean flag = false;
iIndex = 0;
token = new char[20];
sym = symbol.nil;
char ch = line.charAt(iCurPos++); // 当前指针所指字符,iCurPos全局指针,iIndex字符串局部指针
/*********************************************************
*
*************** TODO: 单词识别 ***************
*
* 只需要做的是:针对不同的字符识别出相应的 token 和 sym 值
*
*********************************************************/
while (ch == ' ') {// 遇到空则继续读取下一个值,
ch = line.charAt(iCurPos++);
}
if (ch >= '0' && ch <= '9') {
// 继续读取数字,直到非数字出现
do {
token[iIndex++] = ch;
ch = line.charAt(iCurPos++);
} while (ch >= '0' && ch <= '9');
// 去掉最后一次判断累加的1
iCurPos--;
// 处理数字
sym = symbol.number;
} else if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z') {
// 处理begin, do, end , if, then, while
// 一直读单词直到再次读到非字母非数字
do {
token[iIndex++] = ch;
ch = line.charAt(iCurPos++);
} while (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch >= '0' && ch <= '9');
// 去掉最后一次判断累加的1
iCurPos--;
// char与String类型转换,去掉空格并且赋给key
String key = String.valueOf(token).trim();
// 匹配关键字
for (int i = 0; i < keyword.length; i++) { // 表示取出数组keyword[]中的每一个元素,就是循环一次
String string = keyword[i];
if (string.equals(key)) {
// 匹配枚举类的value()
for (symbol c : symbol.values()) {
String str = c.getStrName();
if (str.equals(key)) {
sym = c;
// 修改标记
flag = true;
break;
}
}
break;
}
}
if (!flag) {
sym = symbol.ident;
}
} else
// 处理特殊字符
switch (ch) {
case '=': {
token[iIndex] = ch;
sym = symbol.eql;
break;
}
case ',': {
token[iIndex] = ch;
sym = symbol.semicolon;
break;
}
case '+': {
token[iIndex++] = ch;
sym = symbol.plus;
break;
}
case '-': {
token[iIndex++] = ch;
sym = symbol.minus;
break;
}
case '*': {
token[iIndex++] = ch;
sym = symbol.times;
break;
}
case '"': // "打印的内容"
{
token[iIndex++] = ch;
sym = symbol.times;
break;
}
case '(': {
token[iIndex++] = ch;
sym = symbol.lparen;
break;
}
case ')': {
token[iIndex++] = ch;
sym = symbol.rparen;
break;
}
case ';': {
token[iIndex++] = ch;
sym = symbol.semicolon;
break;
}
case '{': {
token[iIndex++] = ch;
sym = symbol.lbracesym;
break;
}
case '}': {
token[iIndex++] = ch;
sym = symbol.rbrace;
break;
}
case '[': {
token[iIndex++] = ch;
sym = symbol.lsquare;
break;
}
case ']': {
token[iIndex++] = ch;
sym = symbol.rsquare;
break;
}
case '.': {
token[iIndex++] = ch;
sym = symbol.period;
break;
}
case '~': { // 作为结束标识符
break;
}
}
// 没有匹配到合适的单词符号,出错处理
if (sym == symbol.nil) {
System.out.println("Error: Position " + iCurPos + " occur the unexpected char \'" + ch + "\'.");
}
}
}
词法分析截图
我就分享到这里,下面的就自己动手把!
或者可以去用csdn积分去下载我的完整版项目代码,项目名称:南京晓庄学院编译原理实验六-JAVA文件的自动打点
我接下来的思路有两种:
第一种:直接对字符表进行操作,当字符找到需要打印的语句是就插入一个打印,可实现
第二种:进行语法分析,在语法分析的过程中检测到字符,拼接字符串加入Symlist中,可实现
有问题可Email留言:lyl94207@163.com