基于JAVA的TPL解释器
编写一个Java程序,该程序读取一个文件中的TPL指令(见下文),并执行这些指令。该语言编写的程序每一行都必须以这些单词中的一个开头,不区分大小写。具体代码可参考链接 https://download.csdn.net/download/qq_43372640/12016782
(ps:原意只是为了让大家参考,所以文件只包括代码说明和源代码(图片形式),没有java文件,如果想要做出效果,需要自己写进软件里生成java文件,请谨慎考虑是否下载)
关于TPL
TPL(Tiny Programming Language)是为本次练习所设计的一种非常简易的编程语言。该语言是完全线性的,它没有分支或循环,每个语句都是按顺序执行。TPL能够处理文本和简单的算法,它具有整型和字符串型两个变量类型。它有8个保留字,具体如下表所示。
保留字名称 | 描述 |
---|---|
# | 注释符 |
INTEGER | 声明整型变量 |
STRING | 声明字符串型变量 |
LET | 分配一个变量 |
CALCULATE | 执行算术运算 |
写入数据到控制台 | |
PRINTLN | 在PRINT执行基础上附带一个空行 |
END | 终止程序 |
关键字解释
1、# 是注释符,所以任何以#字符开头的行都被完全忽略。
例:
#ignore this line!
2、INTEGER 可以声明一个整型变量,默认值为0。
例:
INTEGER myInt
这等同于JAVA中的表达“int myInt=0;”
3、STRING 可以声明一个字符串型变量,默认值为空串。
例:
STRING myString
这等同于JAVA中的表达“String myString;”
4、LET 将值分配给变量(整数或双引号括起来的字符串)。
例:
LET myInt=42
LET myString="Hello World!"
5、CALCULATE 对数值或变量执行数值计算,并将结果赋值给变量(必须提前声明)。支持四个运算符:+, -, *和/(分别为加法、减法,乘法和整数除法)。
例:
CALCULATE myInt=2*2
CALCULATE myInt=myInt+24
CALCULATE myInt=intA/intB
6、PRINT 和 PRINTLN 可以将内容打印到控制台上,唯一的区别是PRINTLN打印完后追加一个新的行(类似Java中的Systme.out.println)。它们可以打印作为参数的文本或变量(字符串或整数)。
例:
PRINTLN "Hello World!"
PRINT myString
PRINTLN myInt
7、END将终止程序。
TPL完整程序样例
下面是一个示例TPL输入文件,用于计算5的阶乘。这个文件放在.txt文件中,方便程序调用
#A TPL Program to calculate the factorial of 5
INTEGER myInt
INTEGER factorial
STRING myString
LET mystring="Factorial Program"
LET myInt=5
CALCULATE factorial=myInt*4
CALCULATE factorial=factorial*3
CALCULATE factorial=factorial*2
PRINTLN mystring
PRINTLN
PRINT "The factorial of "
PRINT myInt
PRINT " is "
PRINTLN factorial
END
程序打印出来的结果如下:
Factorial CalculationThe factorial of 10 is 3628800
程序实现思路
由于本人能力有限,使用的方法比较傻,就是一行一行去检验,通过对应的关键字来实现相应功能。
- 首先,先取到文件每行的内容,这里我是通过while语句一行一行读取,并通过hanghao++来进行行号的记录。
BufferedReader br; //BufferedReader()函数,作用:将字符流放入缓存里
br = new BufferedReader(new FileReader("G:\\2019-2020\\java\\TheFactorialOf5.txt"));
//读取文件路径的文件while((str = br.readLine()) != null){//读取文件一行的内容hanghao++;......
}
- 将该行内容全部小写化,这里的操作是为了不区分大小写,如果有需要可以省略也可以大写化。
str1 = str.toLowerCase();// 把str小写化
- 把该行字符串空格全部删去,因为无法确定有多少空格,以及空格位置,这将会导致后续的判断产生很大困难。
str2 = str1.replaceAll(" ", ""); // 去除空格
//通过replaceAll(String a,String b)函数来首先,即把字符串里面的 a 替换成 b
//这里把 " "(空格)给替换成 ""(即删掉)了
- 判别每行的最前面的关键字是哪个,这里我用了最简单你的if语句来首先。其中charAt(int a)函数表示提取该字符串第a+1个字符,如String str=“hello”,则str.charAt(1)=‘e’。而substring(int a, int b)函数则是提取字符串,a是要提取字符串的开始的索引号,如str的0就对应’h’,而b对应结束的位置+1,如str的5对应’o’。equal()则是比较是否一样。
ch1 = str2.charAt(0);// 获取每行第一个字符
if (ch1 == '#')/*--遇见#,注释符,任何以#字符开头的行都被完全忽略。--*/
if (str2.substring(0,3).equals("end"))/*--遇见end,终止程序,并打印一条提示来表明--*/
if (str2.substring(0,7).equals("integer")) /*-- 遇见integer,声明一个整型变量,默认值为0--*/
if (str2.substring(0,6).equals("string"))/*-- 遇见string,声明一个字符串型变量,默认值为空串--*/
if (str2.substring(0,3).equals("let"))/*--遇见let,将值分配给变量(整数或双引号括起来的字符串)--*/
if (str2.substring(0,9).equals("calculate"))/*--遇见calculate,对数值或变量执行数值计算--*/
if (str2.substring(0,5).equals("print"))/*--遇见print将内容打印到控制台上--*/
if (str2.substring(0,7).equals("println"))/*--遇见println将内容打印到控制台上--*/
- 然后就可以进行相应操作:
1、遇见 # ,由于是在while循环里判断的,因此直接continue即可。
2、遇见 end ,直接break即可。
3、遇见 integer 和 string ,使用哈希表来存储。判断最后一个字节是什么,如果是数字,则存储时直接存等号后 " ,然后是字符串,则存双引号之间的字符串。下面会给大家一个例子,大家可以参考。
4、遇见 calculate ,通过判断local_yuansuanfu= str2.indexOf(’+’)(也可以是-、*、/)是大于0来判断这里的运算符是哪个,然后通过强制类型转换等方式来进行计算。注意,要先判断是否在哈希表里已经存在。下面会给大家一个例子,大家可以参考。
5、遇见 print 和 println ,判断相关可能性,进行System.out.print()或System.out.println()就可以了,因为代码比较简单,这里就不给例子了。
//遇见 integer 和 string 的伪代码如下
local_denghao = str2.indexOf('=');//获取等号的位置
len = str2.length();//获取长度
ch2 = str2.charAt(len - 1);//获取每行最后一个字符
if (ch2 >= '0' && ch2 <= '9') //如果最后一个字符是数字map.put(str2.substring(3, local_denghao), str2.substring(local_denghao + 1, len));
/*把let后的第一个位置到等号的位置间的字符串第一个位置前的位置留下作为变量存到第一个位置,把等号的位置到最后一个字符位置间的字符串留下作为这个变量的内容存到第二个位置。*/
else if (ch2 == '"') { //如果最后一个字符是"map.put(str2.substring(3, local_yinhao - 1), str.substring(local_yinhao1 + 1, len_yuanlai-1));
/*把let后一个位置到第一个引号前的位置间的字符串作为变量存到第一个位置,把第一个引号后开始到最后一个引号间的字符串作为这个变量的内容存到第二个位置。*/
//遇见 calculate 的伪代码如下
/*--判断两个元素是否已经存在于map中--*/
while (keys.hasNext()) {//hasNext()检查序列中是否还有元素String key = (String) keys.next();//next()获得序列中的下一个元素,需要强制转换成自己需要的类型if (str2.substring(local_denghao + 1, local_yunsuanfu).equals(key)) //查看运算符前是否是已有变量number1 = true;if (str2.substring(local_yunsuanfu + 1, len).equals(key)) //查看运算符后是否是已有变量number2 = true;
}
/*--运算符前后都是已有变量,如CALCULATE myInt=intA/intB--*/
if(number1==true&&number2==true) {int num1 = Integer.parseInt( map.get(str2.substring(local_denghao + 1, local_yunsuanfu)) );//取运算符前的变量的值并变成int整数型int num2 = Integer.parseInt( map.get(str2.substring(local_yunsuanfu + 1, len)) );//取运算符后的变量的值并变成int整数型map.put( str2.substring(9, local_denghao),String.valueOf(yunsuan(num1,num2)) );//将相应计算后的值转为String字符串型,并放入map中,即赋值给等号前的变量
}
//以此类推其他几种情况,这里运算就是通过判断好运算符然后把两个数字进行相关运算,代码比较简单,可以自行编写
最终效果图
如果有需要,大家也可以去最开始提到的网址处下载相关资源包,里面有完整代码和使用说明,不过代码不是文本,是图片!上述功能虽然可以实现,但代码有一些逻辑上的错误,功能也有所缺失,请谨慎观看。