0298-Nand-汇编器
环境
- Time 2023-07-08
前言
说明
参考:https://www.nand2tetris.org/
参考:https://github.com/AllenWrong/nand2tetris
目标
接上一节,使用 Java 语言实现汇编编译器。
SymbolTable
package com.example.demo;
import java.util.HashMap;
import java.util.stream.IntStream;
public class SymbolTable {
private final HashMap<String, Integer> map;
public SymbolTable() {
this.map = new HashMap<>();
this.addEntry("SP", 0);
this.addEntry("LCL", 1);
this.addEntry("ARG", 2);
this.addEntry("THIS", 3);
this.addEntry("THAT", 4);
IntStream.range(0, 16).forEach(i -> this.addEntry("R" + i, i));
this.addEntry("SCREEN", 16384);
this.addEntry("KBD", 24576);
}
public void addEntry(String symbol, Integer address) {
this.map.put(symbol, address);
}
public boolean contains(String symbol) {
return this.map.containsKey(symbol);
}
public int getAddress(String symbol) {
return this.map.getOrDefault(symbol, -1);
}
}
Code
package com.example.demo;
public class Code {
public String dest(String symbol) {
switch (symbol) {
case "":
return "000";
case "M":
return "001";
case "D":
return "010";
case "MD":
return "011";
case "A":
return "100";
case "AM":
return "101";
case "AD":
return "110";
case "AMD":
return "111";
default:
break;
}
return null;
}
public String comp(String symbol) {
switch (symbol) {
case "0":
return "0101010";
case "1":
return "0111111";
case "-1":
return "0111010";
case "D":
return "0001100";
case "A":
return "0110000";
case "M":
return "1110000";
case "!D":
return "0001101";
case "!A":
return "0110001";
case "!M":
return "1110001";
case "-D":
return "0001111";
case "-A":
return "0110011";
case "-M":
return "1110011";
case "D+1":
return "0011111";
case "A+1":
return "0110111";
case "M+1":
return "1110111";
case "D-1":
return "0001110";
case "A-1":
return "0110010";
case "M-1":
return "1110010";
case "D+A":
return "0000010";
case "D+M":
return "1000010";
case "D-A":
return "0010011";
case "D-M":
return "1010011";
case "A-D":
return "0000111";
case "M-D":
return "1000111";
case "D&A":
return "0000000";
case "D&M":
return "1000000";
case "D|A":
return "0010101";
case "D|M":
return "1010101";
default:
break;
}
return null;
}
public String jump(String symbol) {
switch (symbol) {
case "":
return "000";
case "JGT":
return "001";
case "JEQ":
return "010";
case "JGE":
return "011";
case "JLT":
return "100";
case "JNE":
return "101";
case "JLE":
return "110";
case "JMP":
return "111";
default:
break;
}
return null;
}
}
CommandType
package com.example.demo;
public enum CommandType {
A_COMMAND, C_COMMAND, L_COMMAND, UNKNOWN;
public static CommandType type(String cmd) {
if (cmd.startsWith("@")) {
return CommandType.A_COMMAND;
} else if (cmd.startsWith("(")) {
return CommandType.L_COMMAND;
} else if (cmd.contains(";") || cmd.contains("=")) {
return CommandType.C_COMMAND;
} else {
return CommandType.UNKNOWN;
}
}
public boolean isAorC() {
return this == A_COMMAND || this == C_COMMAND;
}
}
Parser
package com.example.demo;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Parser {
Scanner scanner;
String currentCommand;
int address = 0;
List<String> validInstructionSet;
public Parser(File file) throws FileNotFoundException {
this.scanner = new Scanner(new FileInputStream(file));
validInstructionSet = new ArrayList<>();
}
public boolean hasMoreCommands() {
return this.scanner.hasNextLine();
}
public void advance() {
if (hasMoreCommands()) {
/* when we don't get the command, loop*/
do {
this.currentCommand = this.scanner.nextLine();
} while (!getCommand());
if (this.commandType().isAorC()) {
this.address++;
}
validInstructionSet.add(this.currentCommand);
} else {
this.scanner.close();
}
}
private boolean getCommand() {
String stringLine = this.currentCommand.trim();
/* remove the space line*/
if (stringLine.equals("")) {
return false;
}
/* Remove the comment line*/
if (stringLine.startsWith("//")) {
return false;
}
/* Throw the comment away*/
int index = stringLine.indexOf("//");
if (index != -1) {
this.currentCommand = stringLine.substring(0, index).trim();
return true;
}
this.currentCommand = stringLine.trim();
return true;
}
public CommandType commandType() {
return CommandType.type(this.currentCommand);
}
public boolean isDigit(String symbol) {
return symbol.chars().allMatch(Character::isDigit);
}
public String paddingZero(String str) {
StringBuilder builder = new StringBuilder(str);
while (builder.length() <= 15) {
builder.insert(0, "0");
}
return builder.toString();
}
public String symbol() {
String subStr = null;
if (commandType().equals(CommandType.A_COMMAND)) {
subStr = this.currentCommand.substring(1);
}
if (commandType().equals(CommandType.L_COMMAND)) {
subStr = this.currentCommand.substring(1, this.currentCommand.length() - 1);
}
if (subStr == null) {
return null;
}
if (isDigit(subStr)) {
long addr = Long.parseLong(subStr);
if (addr < 0 || addr > 32767) {// 2^15 - 1
}
String addressStr = Long.toBinaryString(address);
return paddingZero(addressStr);
} else {
return subStr;
}
}
public String dest() {
if (commandType().equals(CommandType.C_COMMAND)) {
String command = this.currentCommand;
if (command.contains("=")) {
return command.substring(0, command.indexOf("="));
} else {
return "";
}
}
return null;
}
public String comp() {
if (commandType().equals(CommandType.C_COMMAND)) {
String command = this.currentCommand;
if (command.contains("=") && command.contains(";")) {
return command.substring(command.indexOf("=") + 1, command.indexOf(";"));
} else if (command.contains(";")) {
return command.substring(0, command.indexOf(";"));
} else if (command.contains("=")) {
return command.substring(command.indexOf("=") + 1);
} else {
return "";
}
}
return null;
}
public String jump() {
if (commandType().equals(CommandType.C_COMMAND)) {
String command = this.currentCommand;
if (command.contains(";")) {
return command.substring(command.indexOf(";") + 1);
} else {
return "";
}
}
return null;
}
}
Assembler
package com.example.demo;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import static com.example.demo.CommandType.L_COMMAND;
public class Assembler {
/**
* store compiling file
*/
File file;
/**
* store the compiled file
*/
File binFile;
Parser parser;
Code code;
SymbolTable symbolTable;
int freeVarAddress = 16;
/**
* the stream to read/write the program file
*/
BufferedWriter writer;
/**
* we think that one file has one symbol table, so we initialize the symbol table in the constructor
*/
public Assembler(File file) throws FileNotFoundException {
this.file = file;
String fileName = file.getName();
String binFilePath = file.getParent() + "\\" + fileName.substring(0, fileName.indexOf(".")) + ".hack";
this.writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(binFilePath)));
this.code = new Code();
this.symbolTable = new SymbolTable();
}
/**
* the first scan of the file
* in this procedure, we collect the symbol of l-command and add them to symbol table.
*
* @throws FileNotFoundException
*/
public void firstScanFile() throws FileNotFoundException {
this.parser = new Parser(this.file);
do {
parser.advance();
CommandType commandType = parser.commandType();
if (commandType == L_COMMAND) {
String subL = parser.currentCommand.substring(1, parser.currentCommand.length() - 1);
if (!parser.isDigit(subL)) {
this.symbolTable.addEntry(subL, parser.address);
}
}
} while (parser.hasMoreCommands());
}
public void secondScanFile() throws IOException {
for (String validInstruction : parser.validInstructionSet) {
parser.currentCommand = validInstruction;
CommandType commandType = parser.commandType();
switch (commandType) {
case A_COMMAND:
String subStr = parser.currentCommand.substring(1);
if (parser.isDigit(subStr)) {
System.out.println(parser.currentCommand + "\t\t" + parser.symbol());
writer.write(parser.symbol() + "\n");
} else {
int address = 0;
if (this.symbolTable.contains(subStr)) {
address = this.symbolTable.getAddress(subStr);
} else {
this.symbolTable.addEntry(subStr, this.freeVarAddress);
address = this.freeVarAddress++;
}
String addressStr = parser.paddingZero(Integer.toBinaryString(address));
System.out.println(parser.currentCommand + "\t\t" + addressStr);
writer.write(addressStr + "\n");
}
break;
case C_COMMAND:
System.out.print(parser.currentCommand + "\t\t");
String dest = code.dest(parser.dest());
String comp = code.comp(parser.comp());
String jump = code.jump(parser.jump());
String binCmd = "111" + comp + dest + jump;
System.out.println(binCmd);
writer.write(binCmd + "\n");
break;
case L_COMMAND:
System.out.println(parser.currentCommand);
break;
default:
break;
}
}
this.writer.flush();
}
}
总结
编写了一个汇编器。

浙公网安备 33010602011771号