注意
本文最后更新于 2023-07-30,文中内容可能已过时。
本篇来描述一下数值运算相关的内容。
本篇描述的只是最简单的实现方式, 并没有提供什么优化技巧。
详细内容
数值运算应该编程里必不可少的一部分, 所以一个脚本语言引擎应该需要实现数值运算的功能。
下面来看一段代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
a = 1;
b = 2;
c = a + b;
d = a - b;
e = a / d;
f = a * b;
g = a % d;
h = a ^ b;
i = a | b;
j = a & b;
k = ~ a ;
|
大概需要实现的运算类型就是 加减乘除,取模以及位运算。 读者可以根据自己的需求只实现部分的运算符号, 或者实现更多的符号。 (自定义新的符号)
基本逻辑
这里只讨论单个运算符, 不讨论多个运算符的情况。 比如 d = a + b + c
这种暂时不讨论。
基本逻辑其实很简单。
- 取出N个数字
- 进行运算
- 将结果放回去
几个运算符的差异部分,基本就是第二步,进行运算的部分。
N个操作数, 产生一个结果的运算符(+,-,*,/等)的第一步和第三步的内容基本是一致的。
基于Map 的实现逻辑
下面尝试解析一下 c = a + b
的实现过程。
-
从Map中取出key为a, b 的数字。
-
在宿主语言中进行运算 (c/cpp/java/…) 然后获得结果
-
将结果保存到Map中。 (Key为 c)
下面看一段示例代码
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
|
// java 代码, 代码并没有经过测试,只是解释思路使用
// c = a + b;
public class Test {
public void test (){
// key: 变量名, value: 变量值
Map<String, Object> map = new HashMap<>();
// 第一个操作数
String first = "a";
// 第二个操作数
String second = "b";
String result = "c";
// 1
Object a = map.get(first);
Object b = map.get(second);
// null check 和 类型check 这里就省略了。
// 2
// Number 是一个自定义类型, 因为篇幅问题,对声明进行了省略。
Number c = Number.add( ((Number) a), ((Number) b) );
// 3
map.put( result, c );
}
}
|
基于栈的实现逻辑
本部分笔者并没有实现过,只是基于自己的知识和理解来写的内容。
下面同样尝试解析一下 c = a + b
的实现过程。
- 取出 a 对应的数值, 进行压栈
- 取出 b 对应的数值, 进行压栈
- 执行 加号指令 | 这里应该是基于栈的核心部分
- 取出栈顶的2个元素
- 进行相加
- 将结果压栈
- 将栈顶元素出栈,放入到c对应的位置。
这里使用局部变量表和栈进行配合使用, 而不使用Map。
下面看示例代码
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
|
// 同样使用 Java做示例, 代码只是演示说明使用
// c = a + b ;
public class Test {
// 局部变量表
private Object[] localVarTable = new Object [30];
// 栈
private Stack<Object> stack = new Stack<>();
/**
* 根据变量名获取变量对应的 局部变量表的索引
*/
public int getIndex(String name){
// todo
return 0;
}
/**
* 执行加号指令
*/
public void add(){
// check size 和别的check 暂时都直接省略了
Number a = (Number) stack.pop();
Number b = (Number) stack.pop();
// 运算, 并将结果入栈
stack.push( Number.add(a,b) );
}
public void test(){
// 第一个操作数
String first = "a";
// 第二个操作数
String second = "b";
String result = "c";
// 1, 2
// 因为要节省篇幅, 所以大部分的 error check 就不做了
Object a = localVarTable[getIndex(first)];
Object b = localVarTable[getIndex(second)];
// 入栈
stack.push(a);
stack.push(b);
// 3
add();
// 4
Object c = stack.pop();
localVarTable[getIndex(result)] = c;
}
}
|
基于 寄存器的实现逻辑
本部分笔者并没有实现过,只是基于自己的知识和理解来写的内容。
这里假设存在3个寄存器: a, b, c 。 每一个寄存器都可以放任意类型的值。
- 取出变量a的值, 放入a 寄存器
- 取出变量b的值, 放入b 寄存器
- 执行加法运算, 将结果放入c寄存器
- 取出c寄存器的值, 放回局部变量表
- 清空所有寄存器
下面看示例代码
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
|
// c = a + b ;
public class Test {
// 局部变量表
private Object[] localVarTable = new Object [30];
// 寄存器
private Object a = null;
private Object b = null;
private Object c = null;
/**
* 根据变量名获取变量对应的 局部变量表的索引
*/
public int getIndex(String name){
// todo
return 0;
}
/**
* 清理寄存器
*/
public void clearReg(){
a = b = c = null;
}
/**
* 执行加法运算, 运算逻辑总是将 寄存器 a,b 相加,并把值放入寄存器 c
*/
public void add(){
// 将会省略 check
c = Number.add( (Number) a, (Number) b ) ;
}
/**
* 这里的变量名, 改用传参的形式
*/
public void test(String first,String second, String result ){
// 1, 2
a = localVarTable[getIndex(first)];
b = localVarTable[getIndex(second)];
// 3
add();
// 4
localVarTable[getIndex(result)] = c;
// 5
clearReg();
}
}
|
其他
本篇介绍了几种实现机制, 读者可以根据自己的需求,能力进行选择。
关于复合运算(d = a + b + c), 将会在下节进行介绍。