作业说明
姑且说一下,我几次测试遇见的问题:
- Add.asm是A指令是最简单的形式,只有一个数字,只需要处理C指令;
- Max.asm中A指令有符号(symbol,如
R1
)和变量(variable); - Rect.asm中A指令有变量重用,也就是同一个变量使用多次;
- 最后一个文件的考察点似乎就是上面所有的结合起来。
设计思路
读写文件
不管怎么样,需要把文件按行读入、写回;需要掌握一种高级语言的读写文件操作。当然也可像写算法题一样使用控制台,但那样就达不到练习的效果。
使用正则清除空白和注释
使用正则来排除一部分干扰,算是比较快的了。正则基本上我是看一次忘一次,平时使用比较生。另外一提,line.replaceAll()
不是直接对line进行修改的,也是因此,我是用正则修改后发现字符串没有变化,不断检查正则。应当修改为line = line.replaceAll()
的形式。
先写C指令
C指令都是读指令,然后去查表算是比较固定的。使用散列表结构,直接对字符串进行建表查找:注意别抄错了数字。这样简单的asm文件就能直接编译了,可以在控制台检查输出。
在读文件的时候解决标签(label)
标签的结构为(label)
独占一行,而在编译文件中不显示标签,这一点是课程专门强调的。标签就转化为下一个代码行号(从1开始计数)。
复杂的A指令
@value
,这里的value,可能是常数、可能是符号(symbol)、可能是标签、可能是变量。
符号:使用散列表直接查找。
标签:在第一轮扫描中标记好跳转行号。
变量:看是否是新变量,还是已经使用过的变量,从16号寄存器开始排变量。
输出
设置好文件路径,输出为hack文件格式。
我的代码
我使用了不太熟悉的Java,想要加强规范练习,不过写的时候还是感觉太乱。
//Reader.java
import java.util.*;
import java.io.*;
/* read the file.asm, and delete spaces and comments
* there are 2 formats of the comments
* 1.//text
* 2.code, do something //
*/
public class Reader {
public static ArrayList<String> modifiedFile;
public static HashMap<String, String> symbolTable = new HashMap<String, String>();
Reader(String path) {
symbolTable.put("R0", "0");
symbolTable.put("R1", "1");
symbolTable.put("R2", "2");
symbolTable.put("R3", "3");
symbolTable.put("R4", "4");
symbolTable.put("R5", "5");
symbolTable.put("R6", "6");
symbolTable.put("R7", "7");
symbolTable.put("R8", "8");
symbolTable.put("R9", "9");
symbolTable.put("R10", "10");
symbolTable.put("R11", "11");
symbolTable.put("R12", "12");
symbolTable.put("R13", "13");
symbolTable.put("R14", "14");
symbolTable.put("R15", "15");
symbolTable.put("SCREEN", "16384");
symbolTable.put("KBD", "24576");
symbolTable.put("SP", "0");
symbolTable.put("LCL", "1");
symbolTable.put("ARG", "2");
symbolTable.put("THIS", "3");
symbolTable.put("THAT", "4");
try {
modifiedFile = readAsmFile(path);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public ArrayList<String> getModifiedFile() {
return modifiedFile;
}
public ArrayList<String> readAsmFile(String fileName) throws IOException {
ArrayList<String> inFile = new ArrayList<String>();
HashMap<String, String> labelTable = new HashMap<String, String>();
String line = null;
String labelString = null;
String labelNum = null;
FileReader fr = new FileReader(fileName);
BufferedReader br = new BufferedReader(fr);
while((line = br.readLine()) != null) {
line = line.replaceAll("[\s]*", ""); //delete spaces
line = line.replaceAll("//.*", ""); //delete comments
if(line.equals("")) {
continue;
} else if(line.startsWith("(") && line.endsWith(")")) { //label
labelString = line.substring(1, line.length()-1);
labelNum = Integer.toString(inFile.size());
// System.out.println(labelString + " " + labelNum);
labelTable.put(labelString, labelNum);
continue;
}
// System.out.println(line);
inFile.add(line);
}
br.close();
fr.close();
ArrayList<String> processedFile = new ArrayList<String>();
HashMap<String, String> variableTable = new HashMap<String, String>();
int variablePos = 16;
for(String opt: inFile) { //change symbol, variable and label to number
if(opt.startsWith("@")) { //A-instruction
String sub = opt.substring(1);
// System.out.println(sub);
if(symbolTable.containsKey(sub)) { //symbol
opt = "@" + symbolTable.get(sub);
} else if(labelTable.containsKey(sub)) { //label
opt = "@" + labelTable.get(sub);
} else if(sub.matches("[0-9]*")) {
opt = "@" + sub;
}else { //variable, take care of reusing variable!
if(!variableTable.containsKey(sub)) {
variableTable.put(sub, Integer.toString(variablePos));
variablePos ++;
}
opt = "@" + variableTable.get(sub);
}
}
System.out.println(opt);
processedFile.add(opt);
}
return processedFile;
}
// public static void main(String[] args) {
// String path = "D:\\download\\Programs\\nand2tetris\\projects\\06\\max\\";
// String fileName = "Max.asm";
// Reader r = new Reader(path + fileName);
// r.getModifiedFile();
// }
}
这是第二版,通过第一个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!");
}
}
提交记录
总结
本周使用高级语言写一个编译器,总体来说就是使用高级语言读文件、处理字符串、写文件三个步骤,逻辑上来说不是很难,可以说是这课程6次作业中对我最容易的一次。
课程推荐Python和Java来写,可能对我个人而言使用C++会更快,但还是选择不是很熟悉的Java,以作业驱动的方式,督促自己练习不熟悉的部分。