Java VMTranslator Part II

用Java写一个翻译器,将Java的字节码翻译成汇编语言 

目录

程序控制流开发

基本思路

核心代码

实验结果,使用例子进行验证

函数调用

基本思路

核心代码

实验结果,使用例子进行验证

 Parser

CodeWriter

Main


程序控制流开发

Java VMTranslator Part II_第1张图片

基本思路

在project7的基础上将带有控制流的vm字节码翻译成asm汇编文件,既然是翻译,那就是字符串替换问题,在第一部分的程序控制流实现中,我们要做的就是用asm汇编语言实现goto、if-goto和label。

首先是label,这个非常简单,就直接换成(label)就完了。

然后是goto label,这个也简单,拿到label的值直接跳转就行。

最后是if-goto,这个我们首先要搞懂它是什么意思?从课件上可以知道,if-goto的效果是当栈顶元素不为0时发生跳转,并且弹栈。

Java VMTranslator Part II_第2张图片

因此我们首先将栈顶元素取出来,然后栈指针自减,当栈顶元素不为0时跳转。

Java VMTranslator Part II_第3张图片

核心代码

首先是parser类的修改,在代码调试的时候发现这次的vm字节码文件出现了有连续空格的情况,之前的parser没有对这种情况的处理,这次更新一下,增加将连续空格变成单个空格的处理。

Java VMTranslator Part II_第4张图片

然后是parser判断vm指令类型的函数需要增加对label、goto和if-goto指令类型的判断。

Java VMTranslator Part II_第5张图片

然后最主要的就是codeWriter的功能增加,增加了对label、goto和if-goto指令字符串的替换。

Java VMTranslator Part II_第6张图片

然后主函数就是增加对label、goto和if-goto情况的判断即可。

Java VMTranslator Part II_第7张图片

实验结果,使用例子进行验证

首先是BasicLoop的测试,如下图所示,成功通过测试。

Java VMTranslator Part II_第8张图片

然后是斐波那契的测试,如下图所示,成功通过测试,可见控制流的实现通过了。

Java VMTranslator Part II_第9张图片

函数调用

Java VMTranslator Part II_第10张图片

Java VMTranslator Part II_第11张图片

Java VMTranslator Part II_第12张图片

基本思路

首先解决call,这个比较复杂,这个主要是保存在调用函数之前当前程序的状态,主要是LCL、ARG、THIS和THAT,还有函数调用者的返回地址。

接下来我们分别一部分一部分的讲解每一个应该怎么操作。

Java VMTranslator Part II_第13张图片

Push retAddrLabel比较简单,就是将调用者当前的地址保存下来,将调用返回地址压入栈中,当然这个地址需要处理一下,因为我们要区分开每一次调用的label。

Java VMTranslator Part II_第14张图片

对于push LCL/ARG/THIS/THAT的操作是一样的,都是类似于project7中push pointer的操作,即拿到对应的字段值,把它压进栈即可。

Java VMTranslator Part II_第15张图片

对于ARG=SP-5-nArgs,直接翻译,让ARG的值为SP-5-nArgs。

Java VMTranslator Part II_第16张图片

LCL=SP这个也好翻译,而goto functionName就直接跳过去就行了,但是要记得把调用返回地址标号写在后面,因为调用完函数之后要回来。

Java VMTranslator Part II_第17张图片

对于函数定义function functionName nVars,根据课件可知我们需要进行nVars次push constant 0的操作。

Java VMTranslator Part II_第18张图片

因此只需要写上function名字的label后调用nVars次我们之前在project7写的push constant 0的翻译就行了。

Java VMTranslator Part II_第19张图片

对于return而言,基本上就是做的call的反操作,把call时期保存的函数调用者的状态给还原,其中主要的就是拿到函数返回地址以及把之前压入栈的字段值恢复。

因为这个return涉及到的操作很多,我们还是需要一个部分一个部分的讲解。

Java VMTranslator Part II_第20张图片

首先需要一个局部变量拿到我们函数调用之前写入LCL的栈指针的值。

Java VMTranslator Part II_第21张图片

然后根据这个调用前的栈指针的值我们可以拿到之前压入栈的函数返回地址。

Java VMTranslator Part II_第22张图片

然后把函数返回值写入ARG,这里是project7的pop argument 0的操作。

Java VMTranslator Part II_第23张图片

然后是恢复函数调用者时期SP的值。

Java VMTranslator Part II_第24张图片

恢复THAT/THIS/ARG/LCL字段的值。

Java VMTranslator Part II_第25张图片

最后回到函数调用者的地址。

Java VMTranslator Part II_第26张图片

然后是最后两个测试程序需要调用它们的启动函数。

Java VMTranslator Part II_第27张图片

启动函数就是初始化栈指针的值为256,然后调用它们写好的Sys.init函数。

Java VMTranslator Part II_第28张图片

还有根据最后一个测试StaticTest的调试结果可以知道不同vm文件的static字段应该是不一样的,难怪需要setFileName函数的存在,因为我没有这个函数就一直跑不对。

Java VMTranslator Part II_第29张图片

核心代码

parser判断vm指令类型的函数需要增加对call、function和return指令类型的判断。

Java VMTranslator Part II_第30张图片

然后最主要的就是codeWriter的功能增加,增加了对call、function和return指令字符串的替换,其中call和return就一一写入相应的字符串,call还需要区分开每一次调用的地址,因此调用label需要加入序号。

而function的处理,则重复调用project7写的push constant 0进行翻译。

Java VMTranslator Part II_第31张图片

然后主函数就是增加对label、goto和if-goto情况的判断即可。

Java VMTranslator Part II_第32张图片

还有调用启动函数的编写。

Java VMTranslator Part II_第33张图片

因为这里出现了多个vm文件,因此我们需要在主函数里对输入文件路径做一些处理,我们首先创建一个文件容器,先判断这个文件路径是否是单个文件还是一个文件夹,如果是单个文件,把这个文件装进容器。如果是一个文件夹,那么把这个文件夹目录下所有vm文件装进容器。

Java VMTranslator Part II_第34张图片

然后对于容器里的每一个文件都生成一个parser解析器就行解析翻译。

Java VMTranslator Part II_第35张图片

实验结果,使用例子进行验证

SimpleFunction先注释掉我们启动的初始化函数,因为它已经包含了启动函数,测试结果如下图所示,测试成功。

Java VMTranslator Part II_第36张图片

然后是这个非常非常长的斐波那契测试,这里包括了两个vm文件,同时我们启用写好的调用启动函数代码,测试结果如下图所示,成功通过。

Java VMTranslator Part II_第37张图片

然后是最后的考验StaticTest,第一次测试其实是失败的,为什么吗,因为一开始我没有搞懂为什么有个setFileName函数要写,于是我就没写,然后就在这里调试的时候发现,对于不同的vm文件需要不同的static字段,后来重写了project7的pop和push函数,加上了区分不同vm文件的static函数。

然后就终于搞定了。

 Parser

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.Objects;
import java.util.Scanner;

public class Parser {
    private String command = null;
    private final Scanner scanner;
    private String cmd0 = null;
    private String cmd1 = null;
    private int cmd2;

    public Parser(File file) throws FileNotFoundException {
        scanner = new Scanner(new FileReader(file));
    }

    public boolean hasMoreCommands() {
        boolean hasMore = false;
        while (scanner.hasNextLine()) {
            command = scanner.nextLine();
            command = command.replaceAll("\\s+", " "); //将连续的空格替换成单个空格
            if (!Objects.equals(command, "") && command.charAt(0) != '/') { //去掉空白行和注释
                String[] pure = command.split("/");
                command = pure[0];
                hasMore = true;
                break;
            }
        }
        return hasMore;
    }

    public void advance() {
        String[] cmd = command.split(" ");
        cmd0 = cmd[0];
        if (cmd.length > 1) {
            cmd1 = cmd[1];
            if (cmd.length > 2) {
                cmd2 = Integer.parseInt(cmd[2]);
            }
        }
    }

    public String commandType() {
        if (Objects.equals(cmd0, "push")) {
            return "C_PUSH";
        } else if (Objects.equals(cmd0, "pop")) {
            return "C_POP";
        } else if (Objects.equals(cmd0, "label")) {
            return "C_LABEL";
        } else if (Objects.equals(cmd0, "goto")) {
            return "C_GOTO";
        } else if (Objects.equals(cmd0, "if-goto")) {
            return "C_IF";
        } else if (Objects.equals(cmd0, "call")) {
            return "C_CALL";
        } else if (Objects.equals(cmd0, "function")) {
            return "C_FUNCTION";
        } else if (Objects.equals(cmd0, "return")) {
            return "C_RETURN";
        } else {
            cmd1 = cmd0;
            return "C_ARITHMETIC";
        }
    }

    public String arg1() {
        return cmd1;
    }

    public int arg2() {
        return cmd2;
    }

    public void close() {
        scanner.close();
    }

}

CodeWriter

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Objects;

public class CodeWriter {
    private final FileWriter asm;
    private String asmCommand;
    private String fileName="";
    private final HashMap vmToAsm = new HashMap<>();
    private int jump = 0;

    public CodeWriter(File file) throws IOException {
        asm = new FileWriter(file);
        String fetch = "@SP\nM=M-1\nA=M\nD=M\nA=A-1\n";
        vmToAsm.put("add", fetch + "M=M+D\n");
        vmToAsm.put("sub", fetch + "M=M-D\n");
        vmToAsm.put("and", fetch + "M=M&D\n");
        vmToAsm.put("or", fetch + "M=M|D\n");
        vmToAsm.put("gt", fetch + "D=M-D\n@TRUE\nD;JGT\n@SP\nA=M-1\nM=0\n@END\n0;JMP\n(TRUE)\n@SP\nA=M-1\nM=-1\n(END)\n");
        vmToAsm.put("eq", fetch + "D=M-D\n@TRUE\nD;JEQ\n@SP\nA=M-1\nM=0\n@END\n0;JMP\n(TRUE)\n@SP\nA=M-1\nM=-1\n(END)\n");
        vmToAsm.put("lt", fetch + "D=M-D\n@TRUE\nD;JLT\n@SP\nA=M-1\nM=0\n@END\n0;JMP\n(TRUE)\n@SP\nA=M-1\nM=-1\n(END)\n");
        vmToAsm.put("neg", "D=0\n@SP\nA=M-1\nM=D-M\n");
        vmToAsm.put("not", "@SP\nA=M-1\nM=!M\n");
    }

    public void writeArithmetic(String vmCommand) throws IOException {
        asmCommand = vmToAsm.get(vmCommand);
        if (Objects.equals(vmCommand, "gt") || Objects.equals(vmCommand, "eq") || Objects.equals(vmCommand, "lt")) {
            asmCommand = asmCommand.replaceAll("TRUE", "TRUE" + jump);
            asmCommand = asmCommand.replaceAll("END", "END" + jump);
            jump++;
        }
        asm.write(asmCommand);
    }

    public void writePushPop(String cmd, String segment, int index) throws IOException {
        if (Objects.equals(cmd, "C_PUSH")) {
            if (Objects.equals(segment, "constant")) {
                asmCommand = "@" + index + "\nD=A\n@SP\nA=M\nM=D\n@SP\nM=M+1\n";
            } else if (Objects.equals(segment, "local")) {
                asmCommand = "@LCL\nD=M\n@" + index + "\nA=D+A\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n";
            } else if (Objects.equals(segment, "argument")) {
                asmCommand = "@ARG\nD=M\n@" + index + "\nA=D+A\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n";
            } else if (Objects.equals(segment, "this")) {
                asmCommand = "@THIS\nD=M\n@" + index + "\nA=D+A\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n";
            } else if (Objects.equals(segment, "that")) {
                asmCommand = "@THAT\nD=M\n@" + index + "\nA=D+A\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n";
            } else if (Objects.equals(segment, "temp")) {
                asmCommand = "@" + (5 + index) + "\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n";
            } else if (Objects.equals(segment, "pointer")) {
                if (index == 0) {
                    asmCommand = "@THIS\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n";
                } else {
                    asmCommand = "@THAT\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n";
                }
            } else if (Objects.equals(segment, "static")) {
                asmCommand = "@" + fileName+ index + "\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n";
            }
        } else {
            if (Objects.equals(segment, "local")) {
                asmCommand = "@LCL\nD=M\n@" + index + "\nD=D+A\n@255\nM=D\n@SP\nM=M-1\nA=M\nD=M\n@255\nA=M\nM=D\n";
            } else if (Objects.equals(segment, "argument")) {
                asmCommand = "@ARG\nD=M\n@" + index + "\nD=D+A\n@255\nM=D\n@SP\nM=M-1\nA=M\nD=M\n@255\nA=M\nM=D\n";
            } else if (Objects.equals(segment, "this")) {
                asmCommand = "@THIS\nD=M\n@" + index + "\nD=D+A\n@255\nM=D\n@SP\nM=M-1\nA=M\nD=M\n@255\nA=M\nM=D\n";
            } else if (Objects.equals(segment, "that")) {
                asmCommand = "@THAT\nD=M\n@" + index + "\nD=D+A\n@255\nM=D\n@SP\nM=M-1\nA=M\nD=M\n@255\nA=M\nM=D\n";
            } else if (Objects.equals(segment, "temp")) {
                asmCommand = "@SP\nM=M-1\nA=M\nD=M\n@" + (5 + index) + "\nM=D\n";
            } else if (Objects.equals(segment, "pointer")) {
                if (index == 0) {
                    asmCommand = "@SP\nM=M-1\nA=M\nD=M\n@THIS\nM=D\n";
                } else {
                    asmCommand = "@SP\nM=M-1\nA=M\nD=M\n@THAT\nM=D\n";
                }
            } else if (Objects.equals(segment, "static")) {
                asmCommand = "@SP\nM=M-1\nA=M\nD=M\n@" + fileName+ index + "\nM=D\n";
            }
        }
        asm.write(asmCommand);
    }

    public void writeLabel(String label) throws IOException {
        asm.write("(" + label + ")\n");
    }

    public void writeGoto(String label) throws IOException {
        asm.write("@" + label + "\n0;JMP\n");
    }

    public void writeIf(String label) throws IOException {
        asm.write("@SP\nM=M-1\nA=M\nD=M\n@" + label + "\nD;JNE\n");
    }

    public void writeCall(String functionName, int nArgs) throws IOException {
        asm.write("@Caller" + jump + "\nD=A\n@SP\nA=M\nM=D\n@SP\nM=M+1\n");
        asm.write("@LCL\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n");
        asm.write("@ARG\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n");
        asm.write("@THIS\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n");
        asm.write("@THAT\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n");
        asm.write("@SP\nD=M\n@5\nD=D-A\n@" + nArgs + "\nD=D-A\n@ARG\nM=D\n");
        asm.write("@SP\nD=M\n@LCL\nM=D\n");
        asm.write("@" + functionName + "\n0;JMP\n(Caller" + jump + ")\n");
        jump++;
    }

    public void writeReturn() throws IOException {
        asm.write("@LCL\nD=M\n@FRAME\nM=D\n");
        asm.write("@5\nA=D-A\nD=M\n@RET\nM=D\n");
        asm.write("@ARG\nD=M\n@0\nD=D+A\n@255\nM=D\n@SP\nM=M-1\nA=M\nD=M\n@255\nA=M\nM=D\n");
        asm.write("@ARG\nD=M\n@SP\nM=D+1\n");
        asm.write("@FRAME\nD=M-1\nAM=D\nD=M\n@THAT\nM=D\n");
        asm.write("@FRAME\nD=M-1\nAM=D\nD=M\n@THIS\nM=D\n");
        asm.write("@FRAME\nD=M-1\nAM=D\nD=M\n@ARG\nM=D\n");
        asm.write("@FRAME\nD=M-1\nAM=D\nD=M\n@LCL\nM=D\n");
        asm.write("@RET\nA=M\n0;JMP\n");
    }

    public void writeFunction(String functionName, int nArgs) throws IOException {
        asm.write("(" + functionName + ")\n");
        for (int i = 0; i < nArgs; i++) {
            writePushPop("C_PUSH", "constant", 0);
        }
    }

    public void writeInit() throws IOException {
        asm.write("@256\nD=A\n@SP\nM=D\n");
        writeCall("Sys.init", 0);
    }

    public void close() throws IOException {
        asm.close();
    }

    public void setFileName(String fileName) {
        this.fileName = fileName;
    }
}

Main

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Objects;

public class Main {
    public static void main(String[] args) throws IOException {
        File file = new File("C:\\Users\\Yezi\\Desktop\\Java程序设计\\nand2tetris\\projects\\08\\FunctionCalls\\StaticsTest");
        ArrayList vm = new ArrayList<>();
        CodeWriter codeWriter = new CodeWriter(new File("C:\\Users\\Yezi\\Desktop\\Java程序设计\\nand2tetris\\projects\\08\\FunctionCalls\\StaticsTest\\StaticsTest.asm"));
        codeWriter.writeInit();
        if (file.isFile()) {
            vm.add(file);
        } else {
            for (File one : Objects.requireNonNull(file.listFiles())) {
                if (one.getName().endsWith(".vm")) {
                    vm.add(one);
                }
            }
        }
        for (File one : vm) {
            Parser parser = new Parser(one);
            codeWriter.setFileName(one.getName());
            while (parser.hasMoreCommands()) {
                parser.advance();
                if (Objects.equals(parser.commandType(), "C_ARITHMETIC")) {
                    codeWriter.writeArithmetic(parser.arg1());
                } else if (Objects.equals(parser.commandType(), "C_LABEL")) {
                    codeWriter.writeLabel(parser.arg1());
                } else if (Objects.equals(parser.commandType(), "C_GOTO")) {
                    codeWriter.writeGoto(parser.arg1());
                } else if (Objects.equals(parser.commandType(), "C_IF")) {
                    codeWriter.writeIf(parser.arg1());
                } else if (Objects.equals(parser.commandType(), "C_FUNCTION")) {
                    codeWriter.writeFunction(parser.arg1(), parser.arg2());
                } else if (Objects.equals(parser.commandType(), "C_RETURN")) {
                    codeWriter.writeReturn();
                } else if (Objects.equals(parser.commandType(), "C_CALL")) {
                    codeWriter.writeCall(parser.arg1(), parser.arg2());
                } else {
                    codeWriter.writePushPop(parser.commandType(), parser.arg1(), parser.arg2());
                }
            }
            parser.close();
        }
        codeWriter.close();
    }
}

你可能感兴趣的:(Java程序设计,java,开发语言)