解释执行
接下来要做的事情就是解释执行, 我们只需要将中间代码一条一条执行下来就行. 我们可以模仿CPU的执行方式, 使用一个叫pc
的变量存储下一条需要执行的指令的序号. 每次执行一条执行就将pc+1, 当然, 如果是跳转指令要求跳转, 就将值赋给pc
, 而不是加1.
中间代码的意思可以自己理解, 根据代码很容易完成.
上一次忘记讲了, 在生成代码的时候, 由于TreeNode
的存储结构, 导致表达式的计算是从右往左进行, 一般情况下计算出来的结果是没有问题的, 但是在下面的情况会出问题
write 2*9/10
在整数乘法中, 从右往左计算的结果是1
, 从左往右计算的结果是0
.
所以我们在代码中可以看到, interpretAddtiveExp
和interpretTermExp
在逻辑上是一样的, 但是实现起来完全不同.
private String interpretAddtiveExp(TreeNode node) throws InterpretException {
String temp = symbolTable.getTempSymbol().getName();
switch (node.getMiddle().getDataType()) {
case Token.PLUS:
codes.add(new FourCode(FourCode.PLUS, interpretExp(node.getLeft()), interpretExp(node.getRight()), temp));
break;
case Token.MINUS:
codes.add(new FourCode(FourCode.MINUS, interpretExp(node.getLeft()), interpretExp(node.getRight()), temp));
break;
default:
throw new InterpretException("算数运算非法");
}
mLine++;
return temp;
}
/**
* 修正存储结构带来的整数乘除法从右往左的计算错误
* 注意term的TreeNode left一定是factor
* @param node
* @return
* @throws InterpretException
*/
private String interpretTermExp(TreeNode node) throws InterpretException {
String opcode = getOpcode(node.getMiddle().getDataType());
String temp1 = symbolTable.getTempSymbol().getName();
if (node.getRight().getType() == TreeNode.FACTOR) {
codes.add(new FourCode(opcode, interpretExp(node.getLeft()), interpretExp(node.getRight()), temp1));
mLine++;
} else {
codes.add(new FourCode(opcode, interpretExp(node.getLeft()), interpretExp(node.getRight().getLeft()), temp1));
mLine++;
node = node.getRight();
String temp2 = null;
while (node.getRight() != null && node.getRight().getType() != TreeNode.FACTOR) {
opcode = getOpcode(node.getMiddle().getDataType());
temp2 = symbolTable.getTempSymbol().getName();
codes.add(new FourCode(opcode, temp1, interpretExp(node.getRight().getLeft()), temp2));
mLine++;
node = node.getRight();
temp1 = temp2;
}
opcode = getOpcode(node.getMiddle().getDataType());
temp2 = symbolTable.getTempSymbol().getName();
codes.add(new FourCode(opcode, temp1, interpretExp(node.getRight()), temp2));
mLine++;
temp1 = temp2;
}
return temp1;
}
其他的地方就没有什么好说的了.
GUI
我使用了SWT框架实现GUI, 关于SWT框架, 可以参考SWT: A Developer’s Notebook一书.
我的界面比较简单, 只是代码编辑部分使用了一下SWT官方的范例代码, 实现了语法高亮.
交互式窗口
我们是做解释器, 要做一个交互式窗口实在太麻烦, 而且周围有些人完全就是乱作, 虽然效果是一样的, 但是实现方法非常诡异, 这里我们用的方法是将让工程里面存在两个main入口, 一个是带有GUI的入口, 一个是不带有GUI的解释器入口, 解释器入口需要一个参数指明cmm文件的路径. 通过java代码调用一个bat文件(如果是Linux则同理), 传入cmm文件路径, bat文件的作用就是调用解释器入口并传入cmm文件路径.
所以最终我们需要分别打包出两个jar文件, 对应两个入口, 同时组织目录如下:
/cmm.jar
/res/interpret.bat
/res/interpret.jar
最后的成品截图, 打印词法分析结果, 语法分析结果, 中间代码的功能也有, 但是我没截图