MUA Interpreter

MUA是ZJU编程语言原理(Principle of Programming Language)课程中用到的编程语言。本项目要做的工作是使用JAVA语言完成一个MUA语言的解释器,通过命令行与用户进行交互,解析并运行用户输入的代码。

本项目仅能够针对课程的测试点实现基本要求,但还有很多部分需要完善(例如在遇见具有语法错误的代码时,不能进行解析,而是整个程序会报错,函数的参数数量有限制,save和load的实际操作,etc.),以及有很多部分可以优化(例如各class的构造,其实有一些可以去掉),会在后续进行更新。

以下是对于该语言基本语法以及解析器实现方式的说明。

基本语法

首先要注意的是,MUA是一个解释型语言,但是和Python不同,MUA语言中,空格、tab、换行等不能标记一个语句的结束,或者分支情况。

基本数据类型value

数字number,字word,表list,布尔bool

  • 数字的字面量以[0~9]或’-‘开头,不区分整数,浮点数
  • 字的字面量以双引号" 开头,不含空格,采用Unicode编码。在 " 后的任何内容,直到空格(包括空格、tab和回车)为止的字符(不含空格),都是这个字的一部分,包括其中可能有的 "[] 等符号
  • 表的字面量量以方括号 [] 包含,其中的元素以空格分隔;元素可是任意类型;元素类型可不一致;
    • 表的第一个元素和[之间,以及最后一个元素和]之间不需要由空格分隔
    • 表中的字不需要"引导
    • 这是一个有三层表的字面量的例子:[a [b [c d] e]]
  • 布尔量只有两个值: truefalse
  • 数字和布尔量在计算时可以被看作是字的特殊形式,即在字面量和变量中的字,当其中的内容是数字或布尔量时,总是可以根据需要自动被转换成数字或布尔量

名字name

一个以字母开头,只含有字母和数字及下划线的字,可以用做名字,名字区分大小写。

函数名和变量名都算是name

word只有在作为name使用时需要加"(例如在makesaveprintisname以及字表操作等等),其他时候(比如在list中,调用函数时,print里面的输出等等)不需要用"

基本操作operation

基本形式:操作名 参数

操作名是一个名字,与参数间以空格分隔。参数可以有多个,多个参数间以空格分隔。每个操作所需的参数数量是确定的,所以不需要括号或语句结束符号。有的操作有返回值,有的没有。

一个程序就是操作的序列。

基本操作有:

  • // :注释,从//到行末均为注释

  • make <name> <value> : 将value绑定到name上,绑定后的名字位于命名空间。此文档中的基本操作的名字不能重新命名

    make “b “a –> thing “b == a, thing :b == value of a

  • thing <name> :返回word所绑定的值

  • :<name> :与thing相同

    :a == thing “a

  • erase <name> :清除word所绑定的值

  • isname <word> :返回word是否是一个名字,true/false

    isname “a

  • print <value> :输出value

    print “a –> a
    print 1 –> 1
    print true –> true
    print :a –> 6 (如果前面make “a 6)
    print [a b c] –> a b c

  • read :返回一个从标准输入读取的数字或字

  • 运算符operator

    • add , sub, mul , div , mod<operator> <number> <number>
    • eq , gt , lt<operator> <number|word> <number|word>
    • and , or<operator> <bool> <bool>
    • notnot <bool>
  • readlist :返回一个从标准输入读取的一行,构成一个表,行中每个以空格分隔的部分是list的一个元素

    • readlist读入的只可能是单层的表
  • repeat <number> <list> :运行list中的代码number次

函数定义和调用

定义

make <name> [<list1> <list2>],其中

  • name为函数名
  • list1为参数表
  • list2为操作表

以下为函数定义的例子:

1
2
3
4
make "prt [
[a]
[print :a]
]

调用

<functionName> <arglist>,其中:

  • <functionName>为make中定义的函数名,不需要双引号"
  • <arglist>是参数表<arglist>中的值和函数定义时的<list1>中名字进行一一对应绑定

以下为函数调用的例子:prt "hello

本地变量

  • 在函数中访问(读取)变量的值的时候,首先访问本地,如果本地不存在,则访问全局

  • 在函数中做 make 时,永远只写本地:

    • 检查本函数内是否存在这个名字,如果存在,则对已有的变量赋值;
    • 否,则在本地定义⼀个新的变量

函数相关的操作

  • output <value> :设定value为返回给调用者的值,但是不停止执行

  • stop :停止执行

  • export :将本地make的值输出到全局
    • 如果全局没有这个变量,则增加⼀个全局变量
    • 如果全局已经有了同名的变量,则替换全部变量的值

表达式计算

允许使用以下运算符对数字进行计算:+-*/%()

为了方便便识别,要求表达式的外⾯面必须有括号()包围。

支持负数

(5+-2) –> 3.0

判断

  • if <bool> <list1> <list2> :如果bool为真,则执行list1,否则执行list2。list均可以为空表
  • isnumber <value> :返回value是否是数字
  • isword <value> :返回value是否是字
  • islist <value> :返回value是否是表
  • isbool <value> :返回value是否是布尔量
  • isempty <word|list> : 返回word或list是否是空

数值计算

  • random <number> :返回[0,number)的一个随机数
  • sqrt <number> :返回number的平方根
  • int <number> : floor the int

字表处理

  • word <word> <word|number|bool> :将两个word合并为一个word,第二个值可以是word、number或bool

  • sentence <value1> <value2> :将value1和value2合并成一个表,两个值的元素并列,value1的在value2的前面

    返回结果为list
    sentence “a “b -> a b
    sentence [a] [b] -> a b
    sentence “a [b] -> a b
    sentence [a [b c] d] [x y] -> a [b c] d x y

  • list <value1> <value2> :将两个值合并为一个表,如果值为表,则不打开这个表

    list “a “b -> a b
    list [a] [b] -> [a] [b]

  • join <list> <value> :将value作为list的最后一个元素加入到list中(如果value是表,则整个value成为表的最后一个元素)

    join [a b] “c -> a b c
    join [a b] [c] -> a b [c]
    join [a b] first [c] -> a b c

  • first <word|list> :返回word的第一个字符,或list的第一个元素

    first “abc -> a (word)
    first [a b c] -> a (word)
    first [[a] b] -> a (list)
    first “123 -> 1 (word)
    islist first [] -> true
    isempty first [] -> true

  • last <word|list> :返回word的最后一个字符,list的最后一个元素

  • butfirst <word|list> :返回除第一个元素外剩下的表,或除第一个字符外剩下的字

    注意返回是list
    islist butfirst [a b c] -> true
    islist butfirst [a b] -> true

  • butlast <word|list> :返回除最后一个元素外剩下的表,或除最后一个字符外剩下的字

其他操作

  • wait <number> :等待number个ms

  • save <word> :保存当前命名空间在word文件中

    save “a.mua

  • load <word> :从word文件中装载内容,加入当前命名空间

  • erall :清除当前命名空间的全部内容

  • poall :列出当前命名空间的全部名字

既有名字

系统提供了一些常⽤用的量量,或可以由其他操作实现但是常用的操作,作为固有的名字。这些名字是可以被删除(erase)的。

  • pi :3.14159
  • run <list> :运行list中的代码

具体实现

整体结构

项目中创建的class介绍如下:

command

MUA语句中的每个单词都是一个command,包含numberboollistwordoperatorexpressionfile几个类别,具有名字、类别、值(针对number、bool、list、expression,根据类别不同有不同类型的值)几个属性(其中list会被看成一个整体)。

例如语句 if eq thing “a 6 [print :a] [print 0]

name type value
if operation /
eq operation /
thing operation /
“a word /
6 number 6 (double)
[print :a] list [print :a] (String)
[print 0] list [print 0] (String)

command的构造可以有三种方式,构造一个空的command、提供name和type(在构造时根据name判断并填充value)、提供name(构造时根据name判断并填充type和value),具体实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
command(){}

command(String name, String type){
commandName = name;
commandType = type;
setCommandValue(commandType);
}

command(String name){
commandName = name;
analyzeType(name);
setCommandValue(commandType);
}

其中analyzeType是用于根据name判断command类型的方法,setCommandValue是根据name和type设置value的方法。这两个方法的实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public void analyzeType(String name) {
char firstChar = name.charAt(0);
if( (firstChar >= '0' && firstChar <= '9') || firstChar == '-') {
//number eg.6
commandType = "number";
}else if(firstChar == '"') {
if(isNumber(name.substring(1))) {
//number eg."6
commandName = name.substring(1);
commandType = "number";
}else {
//word eg."ab
commandType = "word";
}
}else if(name.equals("true") || name.equals("false")) {
//boolean (true/false)
commandType = "bool";
}else if(firstChar == '['){
//list
commandType = "list";
}else if(firstChar == '(') {
//expression
commandType = "expression";
}else if(name.indexOf('.') != -1 && name.trim().substring(name.indexOf('.')).equals(".mua")) {
commandType = "file";
}else {
//operator
commandType = "operator";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void setCommandValue(String type) {
if(type.equals("number")) {
numValue = Double.valueOf(commandName);
}else if(type.equals("bool")) {
if(commandName.equals("true")) {
boolValue = true;
}else {
boolValue = false;
}
}else if(type.equals("list")){
listString = commandName.substring(1, commandName.length()-1);
}else if(type.equals("expression")) {
exprString = commandName.substring(1, commandName.length()-1);
}
}

commandStack

输入的MUA语句会被作为一个commandStack类存储起来,顾名思义,commandStack是一个由很多command组成的stack,通过Vector来组织。例如if eq thing "a 6 [print :a] [print 0]将会被分为七个command,存储在一个commandStack里。

commandStack的构造包括空白构造、根据一个MUA语句进行构造两种方式,实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
commandStack(){}

//put a line of commands into stack
commandStack(String line){
line = line.replace(":", "thing \"").trim();
while(!line.equals("")) {
command newCom;
if(line.charAt(0) == '[') {
//list
int comp = 0;
for(int i=0; i<line.length(); i++) {
if(line.charAt(i) == '[') {
comp--;
} else if(line.charAt(i) == ']') {
comp++;
}
if(comp == 0) {
newCom = new command((line.substring(0, i+1)).trim());
commands.addElement(newCom);
if(i == line.length() - 1) {
line = "";
}else {
line = line.substring(i+1).trim();
}
break;
}
}
} else if(line.charAt(0) == '(') {
//expression
int comp = 0;
for(int i=0; i<line.length(); i++) {
if(line.charAt(i) == '(') {
comp--;
} else if(line.charAt(i) == ')') {
comp++;
}
if(comp == 0) {
newCom = new command((line.substring(0, i+1)).trim());
commands.addElement(newCom);
if(i == line.length() - 1) {
line = "";
}else {
line = line.substring(i+1).trim();
}
break;
}
}
} else if(line.indexOf(' ') != -1) {
//more than one command left
newCom = new command(line.substring(0, line.indexOf(' ')).trim());
commands.addElement(newCom);
line = line.substring(line.indexOf(' ')+1).trim();
} else {
newCom = new command(line);
commands.addElement(newCom);
line = "";
}
}
}

commandStack的方法仿照Vector,包含get(index)setElementAt(newCommand, index)insertElementAt(newCommand, index)removeElementAt(index)subStack(begin, end)subStack(begin),便于操作。

variable

变量。每个被make过的word都会被作为一个变量存储起来,包含numberboolwordlist几个类别,具有名字、类别、值(不同类别有不同类型的值)几个属性。每一种类别的变量都有一个单独的构造函数,此外还有拷贝构造,以及未赋值的变量构造。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//empty variable
variable(String name){
varName = name;
varType = "";
}

//eg. make "a 6
variable(String name, double value){
varName = name;
numValue = value;
varType = "number";
}

//eg. make "a true
variable(String name, boolean bool){
varName = name;
boolValue = bool;
varType = "bool";
}

//eg. make "b "a, varType of b is word
variable(String name, String str){
varName = name;
if(str.charAt(0) == '[') {
changeListStr(str);
varType = "list";
}else {
wordName = str;
varType = "word";
}
}

//copy construction
variable(variable var) {
varName = var.getName();
varType = var.getType();
if(varType == "number") {
numValue = var.getNumValue();
}else if(varType == "bool") {
boolValue = var.getBoolValue();
}else {
wordName = var.getWordName();
}
}

getValue()chageValue(newValue)方法,可以用于获取和改变变量的值

varStack

varStackvariable组成的 stack,存储当前命名空间下的所有变量,整个程序会有一个全局的varStack,每个函数都有其对应的局部的varStack,使用Vector来管理。

含有isname方法,用于判断当前变量空间是否含有对应名字的变量。

含有往varStack中添加variable的方法,有add(variable)add(name)add(name, value)几种方式,要注意的是,如果在栈中有与新添加的变量同名的变量,则进行覆盖。

可以根据name来移除变量(remove(name))、获取变量的值(getValue(name))、查找对应变量(find(name))。

此外,包含仿照Vector的方法,size() removeElementAt(index)

function

函数。包含名字、变量列表、操作列表(分别用stack来存当前函数对应的variablecommandStack,每一句操作都是一个commandStack)

除了空白构造和拷贝构造之外,可以通过提供名字、变量列表、操作列表来构造,其中,变量列表和操作列表以String输入,在构造时存储在对应stack中,通过Vector来管理

funcStack

funcStackfunction组成的 stack,存储所有的函数

exprMember

表达式中的每个成员,包含名字、类型(number/operator)、值(针对number)三个属性

expression

用stack存储一整句表达式,可以通过操作stack来进行运算。

要注意的是,-可能有减法和负数两种含义,要在构造时进行判断,如果为减法,exprMember的类型将是operator,如果是负数,则与其后面的数字一起组成新的数值,类型是number

将一整句表达式拆开并存储为由exprMember组成的stack的构造如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
expression(String expr){
exprMember newMem;
expr = expr.trim();
while(!expr.equals("")) {
if(Character.isDigit(expr.charAt(0))){
if(expr.length() == 1) {
newMem = new exprMember(expr.substring(0, 1), "number");
members.add(newMem);
expr = expr.substring(1).trim();
}else {
for(int i=1; i<expr.length(); i++) {
if(!Character.isDigit(expr.charAt(i)) && expr.charAt(i) != '.' || i == expr.length()-1) {
if(i == expr.length()-1) {
newMem = new exprMember(expr.substring(0, i+1), "number");
}else {
newMem = new exprMember(expr.substring(0, i), "number");
}
members.add(newMem);
expr = expr.substring(i).trim();
break;
}
}
}
}else if(expr.charAt(0) == '-' && (members.size() == 0 || members.get(members.size()-1).getType() == "operator")) {
//judge whether the '-' represents negative or minus
//if it is negative, do it first
for(int i=1; i<expr.length(); i++) {
if(!Character.isDigit(expr.charAt(i))) {
newMem = new exprMember(expr.substring(0, i), "number");
members.add(newMem);
expr = expr.substring(i).trim();
break;
}
}
}else {
newMem = new exprMember(expr.substring(0, 1), "operator");
members.add(newMem);
expr = expr.substring(1).trim();
}
}
}

整体实现

整体的实现通过栈进行,分析commandStack中各command的属性,对其中的operator进行对应操作,取出operator以及对应的操作数,操作完之后把结果放回栈中。

预处理

为方便后续处理,在通过MUA语句构造commandStack时,将所有的都替换成thing "

读入MUA语句

读入时跳过所有注释

读入时要注意的是判断是否读完一整句话,如果没有读完(list或者expression,或者一句话的操作数不足),需要继续读下一行,拼起来作为完整的语句。

运行

由于可能会出现多个语句在同一行的情况,因此只能从前往后运行。

判断当前operator的操作数是否被运算完全,即后面的对应数量的command是否都不是operator类型。如果都不是,说明一个语句已完全,则运行当前operator,否则,递归判断遇到的下一个operator

函数运行

函数运行时,先按照变量列表,使用传递给函数的参数对函数变量空间的对应变量进行值改变,然后依次运行操作列表中的操作。通过依次调用funcStackfunction中负责运行函数的方法进行运行。

需要注意的是:

  • 如果在操纵列表中出现stop,直接返回。

  • 为了实现递归操作,在运行函数时使用拷贝构造函数,构造一个完全相同的新函数再运行,而非直接操作原来的函数。

实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
if(func.isFunc("\"" + oper_command.get(begin_index).getCommandName())) {
String funcName = "\"" + oper_command.get(begin_index).getCommandName();
String funcVars = "";
//pass parameters to the function in order
for(int i=0; i<func.funcVarNum(funcName); i++) {
funcVars += oper_command.get(begin_index+i+1).getCommandName() + " ";
}
commandStack funcVarStack = new commandStack(funcVars);
//run the function
retstr = func.runFunc(funcName, funcVarStack);
if(retstr.equals("") || retstr.equals("STOP!!!!")) {
//no output or is stopped (in case of recursion)
for(int i=func.funcVarNum(funcName); i>=0; i--) {
oper_command.removeElementAt(begin_index+i);
}
}else {
newCom = new command(retstr);
oper_command.setElementAt(newCom, begin_index);
for(int i=func.funcVarNum(funcName); i>0; i--) {
oper_command.removeElementAt(begin_index+i);
}
}
}

funcStack中的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public String runFunc(String funcName, commandStack funcVarStack) throws IOException {
String ret = "";
//find the newly created function to run
for(int i=functions.size()-1; i>=0; i--) {
if(functions.get(i).getName().equals(funcName)) {
//create a same function to run (in case of recursion)
function new_func = new function(functions.get(i));
ret = new_func.run(funcVarStack);
if(ret == "STOP!!!!") {
//the function is stopped
return ret;
}
break;
}
}
return ret;
}

function中的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public String run(commandStack funcVarStack) throws IOException {
String ret = "";
int var_index = 0;
String varName = "";
String varValue = "";
//passing parameters
for(int i=0; i<getVarNum(); i++) {
varName = funcVariable.get(i).getName();
varValue = funcVarStack.get(i).getCommandName();
funcVariable.get(i).changeValue(varValue);
}
//run each operations in order
commandStack nextComStack;
while(funcOperation.size()!=0) {
nextComStack = funcOperation.get(0);
ret = CommandAnalyze.judge(nextComStack, funcVariable, 0);
funcOperation.remove(0);
//if the function is stopped
if(ret == "STOP!!!!") {
return ret;
}
}
return ret;
}

表达式运算

为方便起见,在表达式运算中与commandStack中的处理相反,将所有thing "都替换成:来操作。

表达式运算的几个特殊情况:

  • 处理内部含有()的表达式,通过递归进行
  • 含有变量的表达式,检测到下一个字符是+-*/%中的一个,或者到达表达式结尾,则说明变量名结束。根据检测到的变量名,在变量空间中用对应的数值来替代
  • 含有函数的表达式,检测函数名的方式与检测变量名类似。先运行对应函数,然后再将运算得到的值替代表达式中的函数名,运算表达式。

实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
public static String manageExpression(String expression, varStack vars) throws IOException {
//expression with()
expression = expression.trim();
expression = expression.substring(1, expression.length()-1);
expression = expression.replace("thing \"", ":");
String result = "";
//contains (), recursive
for(int i=0; i<expression.length(); i++) {
if(expression.charAt(i) == '(') {
int comp = 0;
for(int j=i; j<expression.length(); j++) {
if(expression.charAt(j) == '(') {
comp--;
}else if(expression.charAt(j) == ')') {
comp++;
}
if(comp == 0) {
String newExp = expression.substring(i, j+1);
result = manageExpression(newExp, vars);
expression = expression.substring(0, i) + result + expression.substring(j+1);
i--;
break;
}
}
}
}
//vars
for(int i=0; i<expression.length(); i++) {
if(expression.charAt(i) == ':') {
String varName;
for(int j=i+1; j<expression.length(); j++) {
if(isOper(expression.charAt(j)) || j == expression.length()-1) {
if(j == expression.length()-1) {
varName = "\"" + expression.substring(i+1, j+1).trim();
expression = expression.substring(0,i) + vars.getValue(varName);
}else {
varName = "\"" + expression.substring(i+1, j).trim();
expression = expression.substring(0,i) + vars.getValue(varName) + expression.substring(j);
}
i--;
break;
}
}
}
}
//function
for(int i=0; i<expression.length(); i++) {
if(!Character.isDigit(expression.charAt(i)) && !isOper(expression.charAt(i)) && expression.charAt(i) != ' ' && expression.charAt(i) != '.') {
String funcName;
String retstr;
commandStack funcVarStack;
for(int j=i; j<expression.length(); j++) {
if(expression.charAt(j) == ' ') {
funcName = "\"" + expression.substring(i, j);
//single variable
for(int k=j+1; k<expression.length(); k++) {
if(expression.charAt(k) == ' ' || k == expression.length()-1) {
if(k == expression.length()-1) {
funcVarStack = new commandStack(expression.substring(j+1, k+1));
retstr = func.runFunc(funcName, funcVarStack);
expression = expression.substring(0,i) + retstr;
}else {
funcVarStack = new commandStack(expression.substring(j+1, k+1));
retstr = func.runFunc(funcName, funcVarStack);
expression = expression.substring(0,i) + retstr + expression.substring(k);
}
i--;
break;
}
}
break;
}
}
}
}
expression expr = new expression(expression);
result = expr.calculate();
return result;
}

其中calculate()expression类中的方法,原理是通过expression中构造的栈进行运算,按顺序取operator,同时取出它两边的number作为operand进行运算,然后将新的值放回到栈中。由于表达式中只有+-*/%几种运算符,就根据优先级顺序,先处理*/%,再处理+-

基本操作

make <name> <value>make <name> [<list1> <list2>]

如果定义的是函数,创建新的函数并加入函数栈中。如果定义的是变量,创建新的变量并加入当前变量空间的变量栈中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
case "make":
command next1 = oper_command.get(begin_index+1);
command next2 = oper_command.get(begin_index+2);
if(next2.getCommandType().equals("list")) {
//if the second operand is a list, the first operand is the name of a function
//add the function to stack
func.add(next1.getCommandName(), next2.getCommandName());
//store the number of operands of the new function
if(func.funcVarNum(next1.getCommandName()) == 1) {
oneOperand.add(next1.getCommandName().substring(1));
}else if(func.funcVarNum(next1.getCommandName()) == 2) {
twoOperand.add(next1.getCommandName().substring(1));
}else if(func.funcVarNum(next1.getCommandName()) == 3) {
threeOperand.add(next1.getCommandName().substring(1));
}else if(func.funcVarNum(next1.getCommandName()) == 0) {
noOperand.add(next1.getCommandName().substring(1));
}
}else if(next2.getCommandType().equals("word")) {
//eg. make "b "a -> thing "b == a, thing :b == value of a
oper_var.add(next1.getCommandName(), next2.getCommandName());
}else if(next2.getCommandType().equals("number")) {
//eg. make "a 6 -> put 6 in a
oper_var.add(next1.getCommandName(), next2.getNumValue());
}else if(next2.getCommandType().equals("list")){
oper_var.add(next1.getCommandName(), next2.getListString());
}else{
//eg. make "a true
oper_var.add(next1.getCommandName(), next2.getBoolValue());
}
//remove the used commands
oper_command.removeElementAt(begin_index+2);
oper_command.removeElementAt(begin_index+1);
oper_command.removeElementAt(begin_index);
break;
thing <name>

从变量栈中取出对应变量的值并取代

1
2
3
4
5
6
7
case "thing":
command next = oper_command.get(begin_index+1);
//get the next command
newCom = new command(oper_var.getValue(next.getCommandName()), oper_var.getType(next.getCommandName()));
oper_command.setElementAt(newCom, begin_index);
oper_command.removeElementAt(begin_index+1);
break;
erase <name>

从变量栈中删掉对应名字的变量

1
2
3
4
5
6
7
case "erase":
next = oper_command.get(begin_index+1);
//remove the next command
oper_var.remove(next.getCommandName());
oper_command.removeElementAt(begin_index+1);
oper_command.removeElementAt(begin_index);
break;
isname <word>

在变量栈和函数栈中都进行查找,如果当前word存在于变量栈或函数栈中,则为name

1
2
3
4
5
6
7
8
9
10
11
12
case "isname":
String result;
//is a variable or a function, true, else, false
if((oper_var.isname(oper_command.get(begin_index+1).getCommandName()).equals("true")) || (func.isFunc(oper_command.get(begin_index+1).getCommandName())) ) {
result = "true";
}else {
result = "false";
}
newCom = new command(result, "bool");
oper_command.setElementAt(newCom, begin_index);
oper_command.removeElementAt(begin_index+1);
break;

输出,要注意不同格式的输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
case "print":
next = oper_command.get(begin_index+1);
if(next.getCommandType().equals("word")) {
//print a word, eg. print "a --> a
System.out.println(next.getCommandName().substring(1));
}else if(next.getCommandType().equals("list")) {
System.out.println(next.getCommandName().trim().substring(1, next.getCommandName().trim().length()-1));
}
else {
//print number or bool, etc, eg. print 1
System.out.println(next.getCommandName());
}
oper_command.removeElementAt(begin_index+1);
oper_command.removeElementAt(begin_index);
break;
read

返回从标准输入读取的数字或字

1
2
3
4
5
6
7
8
9
10
11
12
case "read":
//read in a number/word, classify it
if(scan.hasNext()) {
String input = scan.nextLine();
if(isNumber(input)) {
newCom = new command(input.trim(), "number");
}else {
newCom = new command('"'+input.trim(), "word");
}
oper_command.setElementAt(newCom, begin_index);
}
break;
readlist

读入单层的表

这里好像没做对,后续更新

1
2
3
4
5
6
7
8
case "readlist":
//read in a list (single layer, in one line)
if(scan.hasNext()) {
String input = scan.nextLine();
newCom =new command(input.trim(), "list");
oper_command.setElementAt(newCom, begin_index);
}
break;
repeat <number> <list>

<list>中的内容作为一个新的commandStack,进行循环操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
case "repeat":
//loop
next1 = oper_command.get(begin_index+1);
next2 = oper_command.get(begin_index+2);
if(!next1.getCommandType().equals("number") || !next2.getCommandType().equals("list")){
System.out.println("ERROR: repeat <number> <list>!");
}
int repeatNum = (int) next1.getNumValue(); //loop times
int repeatCnt = 0; //the number of execution already take place
commandStack newComStack;
while(repeatCnt != repeatNum) {
//create a new stack to store commands in the list, and run again
newComStack = new commandStack(next2.getListString());
retstr = judge(newComStack, oper_var, 0);
repeatCnt++;
}
oper_command.removeElementAt(begin_index+2);
oper_command.removeElementAt(begin_index+1);
oper_command.removeElementAt(begin_index);
break;
算数运算

包括add、sub、mul、div、mod,以add为例

1
2
3
4
5
6
7
8
case "add":
next1 = oper_command.get(begin_index+1);
next2 = oper_command.get(begin_index+2);
newCom = new command(next1.getNumValue()+next2.getNumValue()+"", "number");
oper_command.setElementAt(newCom, begin_index);
oper_command.removeElementAt(begin_index+2);
oper_command.removeElementAt(begin_index+1);
break;
大小比较

包括eq、gt、lt,以eq为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
case "eq":
next1 = oper_command.get(begin_index+1);
next2 = oper_command.get(begin_index+2);
if(next1.getCommandType().equals("word")) {
if(next1.getCommandType().equals("word")) {
if(oper_var.getNumValue(next1.getCommandName()) == oper_var.getNumValue(next2.getCommandName())){
newCom = new command("true", "bool");
}else {
newCom = new command("false", "bool");
}
}else {
if(oper_var.getNumValue(next1.getCommandName()) == next2.getNumValue()){
newCom = new command("true", "bool");
}else {
newCom = new command("false", "bool");
}
}
}else {
if(next1.getCommandType().equals("word")) {
if(next1.getNumValue() == oper_var.getNumValue(next2.getCommandName())){
newCom = new command("true", "bool");
}else {
newCom = new command("false", "bool");
}
}else {
if(next1.getNumValue() == next2.getNumValue()){
newCom = new command("true", "bool");
}else {
newCom = new command("false", "bool");
}
}
}
oper_command.setElementAt(newCom, begin_index);
oper_command.removeElementAt(begin_index+2);
oper_command.removeElementAt(begin_index+1);
break;
逻辑运算

包括and、or、not,以and为例

1
2
3
4
5
6
7
8
9
10
11
12
case "and":
next1 = oper_command.get(begin_index+1);
next2 = oper_command.get(begin_index+2);
if(next1.getBoolValue() && next2.getBoolValue()) {
newCom = new command("true", "bool");
}else {
newCom = new command("false", "bool");
}
oper_command.setElementAt(newCom, begin_index);
oper_command.removeElementAt(begin_index+2);
oper_command.removeElementAt(begin_index+1);
break;
if <bool> <list1> <list2>

根据条件是true或者false判断运行哪个<list>,将对应<list>中的操作作为一个新的commandStack进行运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
case "if":
next1 = oper_command.get(begin_index+1);
command list1 = oper_command.get(begin_index+2);
command list2 = oper_command.get(begin_index+3);
if(next1.getCommandName().equals("true")) {
//run the commands in list1
newComStack = new commandStack(list1.getListString());
retstr = judge(newComStack, oper_var, 0);
}else {
//run the commands in list2
newComStack = new commandStack(list2.getListString());
retstr = judge(newComStack, oper_var, 0);
}
oper_command.removeElementAt(begin_index+3);
oper_command.removeElementAt(begin_index+2);
oper_command.removeElementAt(begin_index+1);
oper_command.removeElementAt(begin_index);
break;
run <list>

ifrepeat中的做法,将<list>视作新的commandStack进行运行

1
2
3
4
5
6
7
case "run":
next = oper_command.get(begin_index+1);
newComStack = new commandStack(next.getListString());
retstr = judge(newComStack, oper_var, 0);
oper_command.removeElementAt(begin_index+1);
oper_command.removeElementAt(begin_index);
break;
判断

包括isnumber, isword, islist, isbool, isempty,即判断下一个command的类别即可,以isnumber为例

1
2
3
4
5
6
7
8
9
10
11
case "isnumber":
next1 = oper_command.get(begin_index+1);
if(next1.getCommandType().equals("number")){
result = "true";
}else {
result = "false";
}
newCom = new command(result, "bool");
oper_command.setElementAt(newCom, begin_index);
oper_command.removeElementAt(begin_index+1);
break;
字表合并

包括word, sentence, list, join,将后面两个字/表合并为新的表,以word为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
case "word":
next1 = oper_command.get(begin_index+1);
next2 = oper_command.get(begin_index+2);
String newword;
if(next2.getCommandType().equals("word")) {
newword = next1.getCommandName().substring(1) + next2.getCommandName().substring(1);
}else {
newword = next1.getCommandName().substring(1) + next2.getCommandName();
}
newCom = new command("\"" + newword);
oper_command.setElementAt(newCom, begin_index);
oper_command.removeElementAt(begin_index+2);
oper_command.removeElementAt(begin_index+1);
break;
字表提取

first, butfirst, last, butlast,提取出对应位置的字/表,按照要求进行处理之后放回栈。以firstbutfirst为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
case "first":
next = oper_command.get(begin_index+1);
if(next.getCommandType().equals("word")) {
//first "abc -> a (word)
newCom = new command(next.getCommandName().substring(0, 2));

}else if(next.getCommandType().equals("list")){
//first [a b c] -> a (word)
newComStack = new commandStack(next.getCommandName().trim().substring(1, next.getCommandName().trim().length()-1));
//in the above example, a is recognized as an operator, not a word. but it should be a word
if(newComStack.get(0).getCommandType().equals("operator")) {
//it should be a word
newCom = new command("\"" + newComStack.get(0).getCommandName());
}else {
//first [[a] b] -> [a] (list)
newCom = newComStack.get(0);
}
}else {
//first "123 -> 1 (word)
newCom = new command("\"" + next.getCommandName().substring(0, 1));
}
oper_command.setElementAt(newCom, begin_index);
oper_command.removeElementAt(begin_index+1);
break;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
case "butfirst":
next = oper_command.get(begin_index+1);
if(next.getCommandType().equals("word")) {
newCom = new command("\"" + next.getCommandName().substring(2, next.getCommandName().trim().length()));

}else if(next.getCommandType().equals("list")){
newComStack = new commandStack(next.getCommandName().trim().substring(1, next.getCommandName().trim().length()-1));
newStr = "";
for(int i=1; i<newComStack.size(); i++) {
newCom = newComStack.get(i);
newStr += newCom.getCommandName() + " ";
}
newCom = new command("[" + newStr + "]");
}else {
newCom = new command("\"" + next.getCommandName().substring(1, next.getCommandName().trim().length()));
}
oper_command.setElementAt(newCom, begin_index);
oper_command.removeElementAt(begin_index+1);
break;
save <word>

用了取巧的方式,只保存当前空间的变量和函数,而不是将所有代码都保存下来。可能要改。

将变量和函数变成对应make格式的MUA语句进行输出和保存。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
case "save":
next = oper_command.get(begin_index+1);
File fout = new File(next.getCommandName().substring(1));
FileWriter fw = new FileWriter(fout);

variable currentVar;
function currentFunc;
for(int i=0; i<oper_var.size(); i++) {
currentVar = oper_var.get(i);
newStr = "make " + currentVar.getName() + " " + currentVar.getValue() + "\n";
fw.write(newStr);
}
for(int i=0; i<func.size(); i++) {
currentFunc = func.get(i);
newStr = "make " + currentFunc.getName() + " [[" + currentFunc.getVarList() + "] [" + currentFunc.getOperList() + "]]\n";
fw.write(newStr);
}
fw.close();
oper_command.removeElementAt(begin_index+1);
oper_command.removeElementAt(begin_index);
break;
load <word>

读取对应文件,依次运行其中的语句

1
2
3
4
5
6
7
8
9
10
11
case "load":
next = oper_command.get(begin_index+1);
FileReader fr = new FileReader(next.getCommandName().substring(1));
BufferedReader br = new BufferedReader(fr);
while((newStr = br.readLine()) != null) {
newComStack = new commandStack(newStr);
judge(newComStack, var, 0);
}
oper_command.removeElementAt(begin_index+1);
oper_command.removeElementAt(begin_index);
break;
erall

清空变量栈和函数栈

1
2
3
4
5
6
7
8
9
case "erall":
while(oper_var.size()!=0) {
oper_var.removeElementAt(0);
}
while(func.size()!=0) {
func.removeElementAt(0);
}
oper_command.removeElementAt(begin_index);
break;
poall

列出变量栈和函数栈的全部内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
case "poall":
System.out.print("variables: ");
for(int i=0; i<oper_var.size(); i++) {
System.out.print(oper_var.get(i).getName());
if(i != oper_var.size()-1) {
System.out.print(", ");
}
}
System.out.println();
System.out.print("functions: ");
for(int i=0; i<func.size(); i++) {
System.out.print(func.get(i).getName());
if(i != func.size()-1) {
System.out.print(", ");
}
}
System.out.println();
oper_command.removeElementAt(begin_index);
break



完整代码请见github,欢迎star :)