课程首页

作业首页

作业说明

image-20210123140625489.png

姑且说一下,我几次测试遇见的问题:

  1. Add.asm是A指令是最简单的形式,只有一个数字,只需要处理C指令;
  2. Max.asm中A指令有符号(symbol,如R1)和变量(variable);
  3. Rect.asm中A指令有变量重用,也就是同一个变量使用多次;
  4. 最后一个文件的考察点似乎就是上面所有的结合起来。

设计思路

读写文件

不管怎么样,需要把文件按行读入、写回;需要掌握一种高级语言的读写文件操作。当然也可像写算法题一样使用控制台,但那样就达不到练习的效果。

使用正则清除空白和注释

使用正则来排除一部分干扰,算是比较快的了。正则基本上我是看一次忘一次,平时使用比较生。另外一提,line.replaceAll()不是直接对line进行修改的,也是因此,我是用正则修改后发现字符串没有变化,不断检查正则。应当修改为line = line.replaceAll()的形式。

先写C指令

C指令都是读指令,然后去查表算是比较固定的。使用散列表结构,直接对字符串进行建表查找:注意别抄错了数字。这样简单的asm文件就能直接编译了,可以在控制台检查输出。

在读文件的时候解决标签(label)

标签的结构为(label)独占一行,而在编译文件中不显示标签,这一点是课程专门强调的。标签就转化为下一个代码行号(从1开始计数)。

复杂的A指令

@value,这里的value,可能是常数、可能是符号(symbol)、可能是标签、可能是变量。

符号:使用散列表直接查找。

标签:在第一轮扫描中标记好跳转行号。

变量:看是否是新变量,还是已经使用过的变量,从16号寄存器开始排变量。

输出

设置好文件路径,输出为hack文件格式。

我的代码

我使用了不太熟悉的Java,想要加强规范练习,不过写的时候还是感觉太乱。

  1. //Reader.java
  2. import java.util.*;
  3. import java.io.*;
  4. /* read the file.asm, and delete spaces and comments
  5. * there are 2 formats of the comments
  6. * 1.//text
  7. * 2.code, do something //
  8. */
  9. public class Reader {
  10. public static ArrayList<String> modifiedFile;
  11. public static HashMap<String, String> symbolTable = new HashMap<String, String>();
  12. Reader(String path) {
  13. symbolTable.put("R0", "0");
  14. symbolTable.put("R1", "1");
  15. symbolTable.put("R2", "2");
  16. symbolTable.put("R3", "3");
  17. symbolTable.put("R4", "4");
  18. symbolTable.put("R5", "5");
  19. symbolTable.put("R6", "6");
  20. symbolTable.put("R7", "7");
  21. symbolTable.put("R8", "8");
  22. symbolTable.put("R9", "9");
  23. symbolTable.put("R10", "10");
  24. symbolTable.put("R11", "11");
  25. symbolTable.put("R12", "12");
  26. symbolTable.put("R13", "13");
  27. symbolTable.put("R14", "14");
  28. symbolTable.put("R15", "15");
  29. symbolTable.put("SCREEN", "16384");
  30. symbolTable.put("KBD", "24576");
  31. symbolTable.put("SP", "0");
  32. symbolTable.put("LCL", "1");
  33. symbolTable.put("ARG", "2");
  34. symbolTable.put("THIS", "3");
  35. symbolTable.put("THAT", "4");
  36. try {
  37. modifiedFile = readAsmFile(path);
  38. } catch (IOException e) {
  39. // TODO Auto-generated catch block
  40. e.printStackTrace();
  41. }
  42. }
  43. public ArrayList<String> getModifiedFile() {
  44. return modifiedFile;
  45. }
  46. public ArrayList<String> readAsmFile(String fileName) throws IOException {
  47. ArrayList<String> inFile = new ArrayList<String>();
  48. HashMap<String, String> labelTable = new HashMap<String, String>();
  49. String line = null;
  50. String labelString = null;
  51. String labelNum = null;
  52. FileReader fr = new FileReader(fileName);
  53. BufferedReader br = new BufferedReader(fr);
  54. while((line = br.readLine()) != null) {
  55. line = line.replaceAll("[\s]*", ""); //delete spaces
  56. line = line.replaceAll("//.*", ""); //delete comments
  57. if(line.equals("")) {
  58. continue;
  59. } else if(line.startsWith("(") && line.endsWith(")")) { //label
  60. labelString = line.substring(1, line.length()-1);
  61. labelNum = Integer.toString(inFile.size());
  62. // System.out.println(labelString + " " + labelNum);
  63. labelTable.put(labelString, labelNum);
  64. continue;
  65. }
  66. // System.out.println(line);
  67. inFile.add(line);
  68. }
  69. br.close();
  70. fr.close();
  71. ArrayList<String> processedFile = new ArrayList<String>();
  72. HashMap<String, String> variableTable = new HashMap<String, String>();
  73. int variablePos = 16;
  74. for(String opt: inFile) { //change symbol, variable and label to number
  75. if(opt.startsWith("@")) { //A-instruction
  76. String sub = opt.substring(1);
  77. // System.out.println(sub);
  78. if(symbolTable.containsKey(sub)) { //symbol
  79. opt = "@" + symbolTable.get(sub);
  80. } else if(labelTable.containsKey(sub)) { //label
  81. opt = "@" + labelTable.get(sub);
  82. } else if(sub.matches("[0-9]*")) {
  83. opt = "@" + sub;
  84. }else { //variable, take care of reusing variable!
  85. if(!variableTable.containsKey(sub)) {
  86. variableTable.put(sub, Integer.toString(variablePos));
  87. variablePos ++;
  88. }
  89. opt = "@" + variableTable.get(sub);
  90. }
  91. }
  92. System.out.println(opt);
  93. processedFile.add(opt);
  94. }
  95. return processedFile;
  96. }
  97. // public static void main(String[] args) {
  98. // String path = "D:\\download\\Programs\\nand2tetris\\projects\\06\\max\\";
  99. // String fileName = "Max.asm";
  100. // Reader r = new Reader(path + fileName);
  101. // r.getModifiedFile();
  102. // }
  103. }

这是第二版,通过第一个Add测试,read部分无需这么麻烦,只需读入、清除空格和注释即可。

//Translator.java
import java.util.*;

/*//Add.asm Sample
 * @2
 * D=A
 * @3
 * D=D+A
 * @0
 * M=D
 */
public class Translator {
    public static HashMap<String, String> jumpTable = new HashMap<String, String>();
    public static HashMap<String, String> destTable = new HashMap<String, String>();
    public static HashMap<String, String> compTable = new HashMap<String, String>();

    private static ArrayList<String> transFile;

    Translator(ArrayList<String> modifiedFile) {
        jumpTable.put("JGT", "001");
        jumpTable.put("JEQ", "010");
        jumpTable.put("JGE", "011");
        jumpTable.put("JLT", "100");
        jumpTable.put("JNE", "101");
        jumpTable.put("JLE", "110");
        jumpTable.put("JMP", "111");

        destTable.put("M", "001");
        destTable.put("D", "010");
        destTable.put("MD", "011");
        destTable.put("A", "100");
        destTable.put("AM", "101");
        destTable.put("AD", "110");
        destTable.put("AMD", "111");

        compTable.put("0", "0101010");
        compTable.put("1", "0111111");
        compTable.put("-1", "0111010");
        compTable.put("D", "0001100");
        compTable.put("A", "0110000");
        compTable.put("!D", "0001101");
        compTable.put("!A", "0110001");
        compTable.put("-D", "0001111");
        compTable.put("-A", "0110011");
        compTable.put("D+1", "0011111");
        compTable.put("A+1", "0110111");
        compTable.put("D-1", "0001110");
        compTable.put("A-1", "0110010");
        compTable.put("D+A", "0000010");
        compTable.put("D-A", "0010011");
        compTable.put("A-D", "0000111");
        compTable.put("D&A", "0000000");
        compTable.put("D|A", "0010101");

        compTable.put("M", "1110000");
        compTable.put("!M", "1110001");
        compTable.put("-M", "1110011");
        compTable.put("M+1", "1110111");
        compTable.put("M-1", "1110010");
        compTable.put("D+M", "1000010");
        compTable.put("D-M", "1010011");
        compTable.put("M-D", "1000111");
        compTable.put("D&M", "1000000");
        compTable.put("D|M", "1010101");

        transFile = transAsm2Bin(modifiedFile);
    }

    public ArrayList<String> transAsm2Bin(ArrayList<String> asmFile) {
        ArrayList<String> binFile = new ArrayList<String>();
        for (String line : asmFile) {
            binFile.add(transOneLine(line));
        }
        return binFile;
    }

    public static String transOneLine(String asmLine) {
        String binLine = null;

        if (asmLine.startsWith("@")) { // A-instruction
            String numStr = asmLine.substring(1);
            Integer num = Integer.valueOf(numStr);
            String binNumStr = Integer.toBinaryString(num);
            // 123456789*123456
            binLine = "0000000000000000".substring(0, 16 - binNumStr.length()) + binNumStr;
        } else {
            /*
             * dest = comp ; jump 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 1 1 1 a c1 c2 c3 c4 c5 c6
             * d1 d2 d3 j1 j2 j3
             */
            int semicolonPos = -1;
            String jumpBinNum = "000";
            if ((semicolonPos = asmLine.indexOf(";")) != -1) { // C-instruction with jump
                jumpBinNum = jumpTable.get(asmLine.substring(semicolonPos + 1)); // begin at next pos
            }
            int equalPos = -1;
            String destBinNum = "000";
            if ((equalPos = asmLine.indexOf("=")) != -1) { // C-instruction with dest
                destBinNum = destTable.get(asmLine.substring(0, equalPos));
            }

            String compBinNum = "0000000";
            if (equalPos != -1 && semicolonPos != -1) {
                compBinNum = compTable.get(asmLine.substring(equalPos + 1, semicolonPos));
            } else if (equalPos != -1) { // no jump
                compBinNum = compTable.get(asmLine.substring(equalPos + 1));
            } else if (semicolonPos != -1) { // no dest
                compBinNum = compTable.get(asmLine.substring(0, semicolonPos));
            }
            binLine = "111" + compBinNum + destBinNum + jumpBinNum;
        }
        System.out.println(binLine);
        return binLine;
    }

    public ArrayList<String> getTransFile() {
        return transFile;
    }
}
//Writer.java
import java.io.*;
import java.util.*;
public class Writer {
    private static ArrayList<String> outFile;
    Writer(ArrayList<String> transFile) {
        outFile = transFile;
    }
    public void writeIntoHack(String path) throws IOException {
        FileWriter fw = new FileWriter(new File(path));
        BufferedWriter bw = new BufferedWriter(fw);
        for(String line: outFile) {
            bw.write(line + "\r\n");
        }
        bw.close();
        fw.close();
    }
}
import java.io.IOException;

public class Main {

    public static void main(String[] args) throws IOException {
//        String path = "D:\\download\\Programs\\nand2tetris\\projects\\06\\add\\";
//        String inFileName = "Add.asm";
//        String outFileName = "Add.hack";
//        String path = "D:\\download\\Programs\\nand2tetris\\projects\\06\\max\\";
//        String inFileName = "Max.asm";
//        String outFileName = "Max.hack";
//        String path = "D:\\download\\Programs\\nand2tetris\\projects\\06\\rect\\";
//        String inFileName = "Rect.asm";
//        String outFileName = "Rect.hack";
        String path = "D:\\download\\Programs\\nand2tetris\\projects\\06\\pong\\";
        String inFileName = "Pong.asm";
        String outFileName = "Pong.hack";
        Reader r = new Reader(path+inFileName);
        Translator t = new Translator(r.getModifiedFile());
        Writer w = new Writer(t.getTransFile());
        w.writeIntoHack(path+outFileName);
        System.out.println("Successfully!");
    }
}

提交记录

image-20210123142119995.png

总结

本周使用高级语言写一个编译器,总体来说就是使用高级语言读文件、处理字符串、写文件三个步骤,逻辑上来说不是很难,可以说是这课程6次作业中对我最容易的一次。

课程推荐Python和Java来写,可能对我个人而言使用C++会更快,但还是选择不是很熟悉的Java,以作业驱动的方式,督促自己练习不熟悉的部分。