nand2tetris - vm translator and..

.. Jack Analyzer!

VM Translator

MemorySegment.java
public enum MemorySegment {
    ARGUMENT, LOCAL, STATIC, CONSTANT, THIS, THAT, POINTER, TEMP;
 
    public static MemorySegment fromSegment(String segment) {
        switch (segment.toUpperCase()) {
            case "ARGUMENT":
                return ARGUMENT;
            case "LOCAL":
                return LOCAL;
            case "STATIC":
                return STATIC;
            case "CONSTANT":
                return CONSTANT;
            case "THIS":
                return THIS;
            case "THAT":
                return THAT;
            case "POINTER":
                return POINTER;
            case "TEMP":
                return TEMP;
        }
        return null;
    }
}

CommandType.java
public enum CommandType {
 
    C_ARITHMETIC, C_PUSH, C_POP, C_LABEL, C_GOTO, C_IF_GOTO, C_FUNCTION, C_RETURN, C_CALL, IGNORED;
 
    public static CommandType fromString(String command) {
        final ArithmeticCommand arithmeticCommand = ArithmeticCommand.fromCommand(command);
 
        if (arithmeticCommand != null) {
            return C_ARITHMETIC;
        }
 
        switch (command.toUpperCase()) {
            case "PUSH":
                return C_PUSH;
            case "POP":
                return C_POP;
            case "LABEL":
                return C_LABEL;
            case "GOTO":
                return C_GOTO;
            case "IF-GOTO":
                return C_IF_GOTO;
            case "FUNCTION":
                return C_FUNCTION;
            case "CALL":
                return C_CALL;
            case "RETURN":
                return C_RETURN;
        }
 
        return null;
    }
}

ArithmeticCommand.java
public enum ArithmeticCommand {
 
    ADD, SUB, NEG, EQ, GT, LT, AND, OR, NOT;
 
    public static ArithmeticCommand fromCommand(String arithmeticCommand) {
        switch (arithmeticCommand.toUpperCase()) {
            case "ADD":
                return ADD;
            case "SUB":
                return SUB;
            case "NEG":
                return NEG;
            case "EQ":
                return EQ;
            case "GT":
                return GT;
            case "LT":
                return LT;
            case "AND":
                return AND;
            case "OR":
                return OR;
            case "NOT":
                return NOT;
        }
        return null;
    }
}

Parser.java
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
 
public class Parser {
 
    private final Scanner fileScanner;
 
    private String currentCommand;
    private CommandType currentCommandType;
 
    // Opens the input file / stream
    public Parser(File sourceFile) throws FileNotFoundException {
        fileScanner = new Scanner(sourceFile);
    }
 
    public boolean hasMoreCommands() {
        return fileScanner.hasNext();
    }
 
    // Reads the next command from the input and makes it the current
    // command.
    public void advance() {
        currentCommand = fileScanner.nextLine().trim();
    }
 
    // Returns a constant representing the type of the current command.
    // C_Arithmetic is returned for all the arithmetic / logical commands.
    public CommandType commandType() {
        if (currentCommand == null || currentCommand.length() == 0) {
            return CommandType.IGNORED;
        }
        if (currentCommand.startsWith("//")) {
            return CommandType.IGNORED;
        }
        final String[] split = currentCommand.split("\\s");
        final String command = split[0];
        currentCommandType = CommandType.fromString(command);
        return currentCommandType;
    }
 
    // Returns the first argument of the current command.
    // In case of C_ARITHMETIC the command itself is returned.
    public String arg1() {
        final String[] split = currentCommand.split("\\s");
        if (currentCommandType == CommandType.C_ARITHMETIC) {
            return split[0];
        } else {
            return split[1];
        }
    }
 
    // Returns the second argument of the current command.
    public String arg2() {
        final String[] split = currentCommand.split("\\s");
        return split[2];
    }
}

CodeWriter.java
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
 
public class CodeWriter {
 
    final PrintWriter pw;
 
    private String sourceFileName;
 
    // Opens the output sourceFile / stream and gets ready to write into it.
    public CodeWriter(final File targetFile) throws IOException {
        this.pw = new PrintWriter(targetFile);
    }
 
    // Writes the output sourceFile the assembly code that implements the given
    // arithmetic command.
    public void writeArithmetic(ArithmeticCommand arithmeticCommand) throws IOException {
        switch (arithmeticCommand) {
            case ADD:
                writeAdd();
                break;
            case SUB:
                writeSub();
                break;
            case NEG:
                writeNeg();
                break;
            case EQ:
                writeEQ();
                break;
            case GT:
                writeGT();
                break;
            case LT:
                writeLT();
                break;
            case AND:
                writeAnd();
                break;
            case OR:
                writeOr();
                break;
            case NOT:
                writeNot();
                break;
        }
    }
 
    // Writes the output sourceFile the assembly code that implements the given command,
    // where command is either C_PUSH or C_POP.
    public void writePushPop(CommandType commandType, MemorySegment memorySegment, int index) {
        switch (commandType) {
            case C_PUSH:
                writePush(memorySegment, index);
                break;
            case C_POP:
                writePop(memorySegment, index);
                break;
        }
    }
 
    public void writeInit() {
        pw.println("@256");
        pw.println("D = A");
        pw.println("@SP");
        pw.println("M = D");
        writeCall("Sys.init", 0);
    }
 
    public void writeLabel(String label) {
        pw.println("(" + label + ")");
    }
 
    public void writeGoto(String label) {
        unCondJumpToLabel(label);
    }
 
    // pop the stacks top most value
    // if the value is not zero, jump to label
    // else do not jump..
    public void writeIfGoto(String label) {
        decrementStackPointer();
        popStackValueToRegisterD();
        pw.println("@" + label);
        pw.println("D;JNE");
    }
 
    public void writeFunction(String functionName, int numLocals) {
        writeLabel(functionName);
        // initialise local variables to 0..
        for (int i = 0; i < numLocals; i++) {
            loadStackAddressToRegisterA();
            pw.println("M = 0");
            incrementStackPointer();
        }
    }
 
    // R[13 - 15] can be used as general purpose registers by the VM
    public void writeCall(String functionName, int numArgs) {
        final String returnAddress = String.valueOf(System.currentTimeMillis());
 
        try {
            Thread.sleep(2);
        } catch (InterruptedException ignored) {}
 
        // Push return address
        pw.println("@" + returnAddress);
        pw.println("D = A");
        loadStackAddressToRegisterA();
        pw.println("M = D");
        incrementStackPointer();
 
        // Push LCL
        pw.println("@LCL");
        pw.println("D = M");
        loadStackAddressToRegisterA();
        pw.println("M = D");
        incrementStackPointer();
 
        // Push ARG
        pw.println("@ARG");
        pw.println("D = M");
        loadStackAddressToRegisterA();
        pw.println("M = D");
        incrementStackPointer();
 
        // Push THIS
        pw.println("@THIS");
        pw.println("D = M");
        loadStackAddressToRegisterA();
        pw.println("M = D");
        incrementStackPointer();
 
        // Push THAT
        pw.println("@THAT");
        pw.println("D = M");
        loadStackAddressToRegisterA();
        pw.println("M = D");
        incrementStackPointer();
 
        // ARG = SP - n - 5
        pw.println("@SP");
        pw.println("D = M");
        pw.println("@" + numArgs);
        pw.println("D = D - A");
        pw.println("@5");
        pw.println("D = D - A");
        pw.println("@ARG");
        pw.println("M = D");
 
        // LCL = SP ie. reposition LCL
        pw.println("@SP");
        pw.println("D = M");
        pw.println("@LCL");
        pw.println("M = D");
 
        // goto functionName
        writeGoto(functionName);
        writeLabel(returnAddress);
    }
 
    // R[13 - 15] can be used as general purpose registers by the VM
    public void writeReturn() {
        // FRAME is a temporary variable
        // FRAME == R13
        pw.println("@LCL");
        pw.println("D = M");
        pw.println("@R13");
        pw.println("M = D");
 
        // Put the return address in a temp variable (R14)
        // (RETADDR == *(FRAME - 5)) = (RETADDR == R14)
        pw.println("@R13");
        pw.println("D = M");
        pw.println("@5");
        pw.println("D = D - A");
        pw.println("A = D");
        pw.println("D = M");
        pw.println("@R14");
        pw.println("M = D");
 
        // Copy return value to D
        pw.println("@SP");
        pw.println("A = M");
        pw.println("A = A - 1");
        pw.println("D = M");
 
        pw.println("@ARG");
        pw.println("A = M");
        pw.println("M = D");
 
        // Restore Stack Pointer of the caller
        // SP = ARG + 1
        pw.println("@ARG");
        pw.println("D = M");
        pw.println("D = D + 1");
        pw.println("@SP");
        pw.println("M = D");
 
        // THAT = *(FRAME - 1)
        pw.println("@R13");
        pw.println("D = M");
        pw.println("@1");
        pw.println("D = D - A");
        pw.println("A = D");
        pw.println("D = M");
        pw.println("@THAT");
        pw.println("M = D");
 
        // THIS = *(FRAME - 2)
        pw.println("@R13");
        pw.println("D = M");
        pw.println("@2");
        pw.println("D = D - A");
        pw.println("A = D");
        pw.println("D = M");
        pw.println("@THIS");
        pw.println("M = D");
 
        // ARG = *(FRAME - 3)
        pw.println("@R13");
        pw.println("D = M");
        pw.println("@3");
        pw.println("D = D - A");
        pw.println("A = D");
        pw.println("D = M");
        pw.println("@ARG");
        pw.println("M = D");
 
        // LCL = *(FRAME - 4)
        pw.println("@R13");
        pw.println("D = M");
        pw.println("@4");
        pw.println("D = D - A");
        pw.println("A = D");
        pw.println("D = M");
        pw.println("@LCL");
        pw.println("M = D");
 
        // goto RET
        pw.println("@R14");
        pw.println("A = M");
        pw.println("0;JMP");
    }
 
    public void close() throws IOException {
        pw.flush();
        pw.close();
    }
 
    private void writeAdd() {
        decrementStackPointer();
        popStackValueToRegisterD();
        decrementStackPointer();
        loadStackAddressToRegisterA();
        pw.println("D = D + M");
        loadStackAddressToRegisterA();
        pw.println("M = D");
        incrementStackPointer();
    }
 
    private void writeSub() {
        decrementStackPointer();
        popStackValueToRegisterD();
        decrementStackPointer();
        loadStackAddressToRegisterA();
        pw.println("D = D - M");
        pw.println("D = -D");
        loadStackAddressToRegisterA();
        pw.println("M = D");
        incrementStackPointer();
    }
 
    private void writeNeg() {
        decrementStackPointer();
        loadStackAddressToRegisterA();
        pw.println("M = -M");
        incrementStackPointer();
    }
 
    private void writeEQ() {
        final String uniqueLabel = String.valueOf(System.currentTimeMillis());
        try {
            Thread.sleep(2);
        } catch (InterruptedException ignored) {
        }
        final String uniqueLabelTrue = uniqueLabel + ".true";
        final String uniqueLabelFalse = uniqueLabel + ".false";
        final String uniqueLabelEnd = uniqueLabel + ".end";
 
        // true : -1
        // false: 0
        decrementStackPointer();
        popStackValueToRegisterD();
        decrementStackPointer();
        loadStackAddressToRegisterA();
        pw.println("D = D - M");
        pw.println("@" + uniqueLabelTrue);
        pw.println("D;JEQ");
        pw.println("@" + uniqueLabelFalse);
        pw.println("D;JNE");
        pw.println("(" + uniqueLabelTrue + ")");
        loadStackAddressToRegisterA();
        setSPToTrue();
        unCondJumpToLabel(uniqueLabelEnd);
        pw.println("(" + uniqueLabelFalse + ")");
        loadStackAddressToRegisterA();
        pw.println("M = 0");
        unCondJumpToLabel(uniqueLabelEnd);
        pw.println("(" + uniqueLabelEnd + ")");
        incrementStackPointer();
    }
 
    private void writeGT() {
        final String uniqueLabel = String.valueOf(System.currentTimeMillis());
        try {
            Thread.sleep(2);
        } catch (InterruptedException ignored) {
        }
        final String uniqueLabelTrue = uniqueLabel + ".true";
        final String uniqueLabelFalse = uniqueLabel + ".false";
        final String uniqueLabelEnd = uniqueLabel + ".end";
 
        // true : -1
        // false: 0
        decrementStackPointer();
        popStackValueToRegisterD();
        decrementStackPointer();
        loadStackAddressToRegisterA();
        pw.println("D = M - D");
        pw.println("@" + uniqueLabelTrue);
        pw.println("D;JGT");
        pw.println("@" + uniqueLabelFalse);
        pw.println("D;JLE");
        pw.println("(" + uniqueLabelTrue + ")");
        loadStackAddressToRegisterA();
        setSPToTrue();
        unCondJumpToLabel(uniqueLabelEnd);
        pw.println("(" + uniqueLabelFalse + ")");
        loadStackAddressToRegisterA();
        pw.println("M = 0");
        unCondJumpToLabel(uniqueLabelEnd);
        pw.println("(" + uniqueLabelEnd + ")");
        incrementStackPointer();
    }
 
    private void writeLT() {
        final String uniqueLabel = String.valueOf(System.currentTimeMillis());
        try {
            Thread.sleep(2);
        } catch (InterruptedException ignored) {
        }
        final String uniqueLabelTrue = uniqueLabel + ".true";
        final String uniqueLabelFalse = uniqueLabel + ".false";
        final String uniqueLabelEnd = uniqueLabel + ".end";
 
        // true : -1
        // false: 0
        decrementStackPointer();
        popStackValueToRegisterD();
        decrementStackPointer();
        loadStackAddressToRegisterA();
        pw.println("D = M - D");
        pw.println("@" + uniqueLabelTrue);
        pw.println("D;JLT");
        pw.println("@" + uniqueLabelFalse);
        pw.println("D;JGE");
        pw.println("(" + uniqueLabelTrue + ")");
        loadStackAddressToRegisterA();
        setSPToTrue();
        unCondJumpToLabel(uniqueLabelEnd);
        pw.println("(" + uniqueLabelFalse + ")");
        loadStackAddressToRegisterA();
        pw.println("M = 0"); // set *SP to false
        unCondJumpToLabel(uniqueLabelEnd);
        pw.println("(" + uniqueLabelEnd + ")");
        incrementStackPointer();
    }
 
    private void writeAnd() {
        decrementStackPointer();
        popStackValueToRegisterD();
        decrementStackPointer();
        loadStackAddressToRegisterA();
        pw.println("M = D & M");
        incrementStackPointer();
    }
 
    private void writeOr() {
        decrementStackPointer();
        popStackValueToRegisterD();
        decrementStackPointer();
        loadStackAddressToRegisterA();
        pw.println("M = D | M");
        incrementStackPointer();
    }
 
    private void writeNot() {
        decrementStackPointer();
        loadStackAddressToRegisterA();
        pw.println("M = !M");
        incrementStackPointer();
    }
 
    private void writePush(MemorySegment memorySegment, int index) {
        switch (memorySegment) {
            case CONSTANT:
                pw.println("@" + index);
                pw.println("D = A");
                loadStackAddressToRegisterA();
                pw.println("M = D");
                break;
            case LOCAL:
                writePushForMemoryLocation("@LCL", index);
                break;
            case ARGUMENT:
                writePushForMemoryLocation("@ARG", index);
                break;
            case THIS:
                writePushForMemoryLocation("@THIS", index);
                break;
            case THAT:
                writePushForMemoryLocation("@THAT", index);
                break;
            case STATIC:
                pw.println("@" + sourceFileName + "." + index);
                pw.println("D = M");
                loadStackAddressToRegisterA();
                pw.println("M = D");
                break;
            case POINTER:
                // push THIS or THAT pointer (pointer itself) to stack.
                if (index == 0) {
                    pw.println("@THIS");
                }
                if (index == 1) {
                    pw.println("@THAT");
                }
                pw.println("D = M");
                loadStackAddressToRegisterA();
                pw.println("M = D");
                break;
            case TEMP:
                // TEMP is not a pointer, general purpose data register.
                // TEMP is between R5 - R12
                pw.println("@" + (5 + index));
                pw.println("D = M");
                loadStackAddressToRegisterA();
                pw.println("M = D");
                break;
        }
        incrementStackPointer();
    }
 
    private void writePop(MemorySegment memorySegment, int index) {
        decrementStackPointer();
        switch (memorySegment) {
            case LOCAL:
                writePopForMemoryLocation(index, "@LCL");
                break;
            case ARGUMENT:
                writePopForMemoryLocation(index, "@ARG");
                break;
            case THIS:
                writePopForMemoryLocation(index, "@THIS");
                break;
            case THAT:
                writePopForMemoryLocation(index, "@THAT");
                break;
            case STATIC:
                popStackValueToRegisterD();
                pw.println("@" + sourceFileName + "." + index);
                pw.println("M = D");
                break;
            case POINTER:
                popStackValueToRegisterD();
                if (index == 0) {
                    pw.println("@THIS");
                }
                if (index == 1) {
                    pw.println("@THAT");
                }
                pw.println("M = D");
                break;
            case TEMP:
                popStackValueToRegisterD();
                pw.println("@" + (index + 5));
                pw.println("M = D");
                break;
        }
    }
 
    private void writePushForMemoryLocation(String memoryLocation, int index) {
        // Increment memory location base address by index.
        pw.println(memoryLocation);
        pw.println("D = M");
        pw.println("@" + index);
        pw.println("D = D + A");
        pw.println(memoryLocation);
        pw.println("M = D");
 
        // D = *memoryLocation
        pw.println(memoryLocation);
        pw.println("A = M");
        pw.println("D = M");
 
        // *SP = D
        loadStackAddressToRegisterA();
        pw.println("M = D");
 
        // Reset memory location base address.
        pw.println(memoryLocation);
        pw.println("D = M");
        pw.println("@" + index);
        pw.println("D = D - A");
        pw.println(memoryLocation);
        pw.println("M = D");
    }
 
    private void writePopForMemoryLocation(int index, String memoryLocation) {
        pw.println(memoryLocation);
        pw.println("D = M");
        pw.println("@" + index);
        pw.println("D = D + A");
        pw.println(memoryLocation);
        pw.println("M = D");
        popStackValueToRegisterD();
        pw.println(memoryLocation);
        pw.println("A = M");
        pw.println("M = D");
        pw.println(memoryLocation);
        pw.println("D = M");
        pw.println("@" + index);
        pw.println("D = D - A");
        pw.println(memoryLocation);
        pw.println("M = D");
    }
 
    private void setSPToTrue() {
        pw.println("M = 1");
        pw.println("M = -M"); // set *SP to true
    }
 
    private void unCondJumpToLabel(String uniqueLabelEnd) {
        pw.println("@" + uniqueLabelEnd);
        pw.println("0;JMP");
    }
 
    private void incrementStackPointer() {
        pw.println("@SP");
        pw.println("M = M + 1");
    }
 
    private void decrementStackPointer() {
        pw.println("@SP");
        pw.println("M = M - 1");
    }
 
    private void popStackValueToRegisterD() {
        loadStackAddressToRegisterA();
        pw.println("D = M");
    }
 
    private void loadStackAddressToRegisterA() {
        pw.println("@SP");
        pw.println("A = M");
    }
 
    public void setSourceFileName(final String sourceFileName) {
        this.sourceFileName = sourceFileName;
    }
}

VMTranslator.java
import java.io.File;
import java.io.IOException;
 
public class VMTranslator {
 
    public static void main(String[] args) throws IOException {
        String source = args[0];
        if (source.endsWith(".vm")) {
            source = source.substring(0, source.lastIndexOf('.'));
        }
        final File initialSourceFile = new File(source);
        final File targetFile;
 
        final boolean isInitialSourceFileDirectory = initialSourceFile.isDirectory();
 
        final File[] sourceFiles;
 
        if (isInitialSourceFileDirectory) {
            targetFile = new File(source + "/" + source + ".asm");
            sourceFiles = initialSourceFile.listFiles();
        } else {
            targetFile = new File(source + ".asm");
            sourceFiles = new File[]{new File(source + ".vm")};
        }
 
        final CodeWriter codeWriter = new CodeWriter(targetFile);
 
        if (isInitialSourceFileDirectory) {
            codeWriter.writeInit();
        }
 
        //noinspection ConstantConditions
        for (File sourceFile : sourceFiles) {
            if (!sourceFile.getName().endsWith(".vm")) {
                continue;
            }
            String sourceFileName = sourceFile.getName();
            codeWriter.setSourceFileName(sourceFileName);
            final Parser parser = new Parser(sourceFile);
            while (parser.hasMoreCommands()) {
                parser.advance();
                final CommandType commandType = parser.commandType();
                switch (commandType) {
                    case C_ARITHMETIC:
                        final String command = parser.arg1();
                        final ArithmeticCommand arithmeticCommand = ArithmeticCommand.fromCommand(command);
                        codeWriter.writeArithmetic(arithmeticCommand);
                        break;
                    case C_PUSH:
                    case C_POP:
                        final String segment = parser.arg1();
                        MemorySegment memorySegment = MemorySegment.fromSegment(segment);
                        codeWriter.writePushPop(commandType, memorySegment, Integer.valueOf(parser.arg2()));
                        break;
                    case C_LABEL:
                        codeWriter.writeLabel(parser.arg1());
                        break;
                    case C_GOTO:
                        codeWriter.writeGoto(parser.arg1());
                        break;
                    case C_IF_GOTO:
                        codeWriter.writeIfGoto(parser.arg1());
                        break;
                    case C_FUNCTION:
                        codeWriter.writeFunction(parser.arg1(), Integer.parseInt(parser.arg2()));
                        break;
                    case C_CALL:
                        codeWriter.writeCall(parser.arg1(), Integer.parseInt(parser.arg2()));
                        break;
                    case C_RETURN:
                        codeWriter.writeReturn();
                        break;
                    case IGNORED:
                        // do nothing..
                        break;
                }
            }
        }
        codeWriter.close();
    }
}

Jack Analyzer

Keyword.java
package biz.tugay.jackanalyzer.tokenizer.token;
 
public enum Keyword {
    CLASS("class"),
    CONSTRUCTOR("constructor"),
    FUNCTION("function"),
    METHOD("method"),
    FIELD("field"),
    STATIC("static"),
    VAR("var"),
    INT("int"),
    CHAR("char"),
    BOOLEAN("boolean"),
    VOID("void"),
    TRUE("true"),
    FALSE("false"),
    NULL("null"),
    THIS("this"),
    LET("let"),
    DO("do"),
    IF("if"),
    ELSE("else"),
    WHILE("while"),
    RETURN("return");
 
    private String jackKeyword;
 
    Keyword(String jackKeyword) {
        this.jackKeyword = jackKeyword;
    }
 
    public String getJackKeyword() {
        return jackKeyword;
    }
 
    public static boolean isJackKeyword(String token) {
        final Keyword[] keywords = Keyword.values();
        for (Keyword keyword : keywords) {
            if (keyword.getJackKeyword().equals(token)) {
                return true;
            }
        }
        return false;
    }
}

Symbol.java
package biz.tugay.jackanalyzer.tokenizer.token;
 
public enum Symbol {
 
    BRACES_L('{'),
    BRACES_R('}'),
    PARANTHESIS_L('('),
    PARANTHESIS_R(')'),
    BRACKETS_L('['),
    BRACKETS_R(']'),
    DOT('.'),
    COMMA(','),
    SEMI_COLON(';'),
    PLUS('+'),
    MINUS('-'),
    MULT('*'),
    DIV('/'),
    AND('&'),
    OR('|'),
    LESSER('<'),
    GREATER('>'),
    EQUALS('='),
    HYPHEN('~');
 
    private char jackSymbol;
 
    Symbol(char jackSymbol) {
        this.jackSymbol = jackSymbol;
    }
 
    public char getJackSymbol() {
        return jackSymbol;
    }
 
    public static boolean isJackSymbol(char token) {
        final Symbol[] symbols = Symbol.values();
        for (Symbol symbol : symbols) {
            if (symbol.getJackSymbol() == token) {
                return true;
            }
        }
        return false;
    }
 
    public static Symbol getJackSymbolFor(char token) {
        final Symbol[] symbols = Symbol.values();
        for (Symbol symbol : symbols) {
            if (symbol.getJackSymbol() == token) {
                return symbol;
            }
        }
        return null;
    }
 
    public static String getSymbolRepresentationFor(Symbol symbol) {
        switch (symbol) {
            case LESSER:
                return "&lt;";
            case GREATER:
                return "&gt;";
            case AND:
                return "&amp;";
            default:
                final char jackSymbol = symbol.getJackSymbol();
                return new String(new char[]{jackSymbol});
        }
    }
}

TokenType.java
package biz.tugay.jackanalyzer.tokenizer.token;
 
public enum TokenType {
    KEYWORD("keyword"), SYMBOL("symbol"), IDENTIFIER("identifier"), 
    INTEGERCONSTANT("integerConstant"), STRINGCONSTANT("stringConstant");
 
    private final String tokenAsString;
 
    TokenType(String tokenAsString) {
        this.tokenAsString = tokenAsString;
    }
 
    public String getTokenAsString() {
        return tokenAsString;
    }
}

TokenTypeDecider.java
package biz.tugay.jackanalyzer.tokenizer.token;
 
public class TokenTypeDecider {
 
    public TokenType tokenTypeFor(String token) {
        try {
            //noinspection ResultOfMethodCallIgnored
            Integer.parseInt(token);
            return TokenType.INTEGERCONSTANT;
        } catch (NumberFormatException ignored) {
        }
 
        if (token.startsWith("\"")) {
            return TokenType.STRINGCONSTANT;
        }
 
        if (token.length() == 1) {
            char tokenChar = token.charAt(0);
            if (Symbol.isJackSymbol(tokenChar)) {
                return TokenType.SYMBOL;
            }
        }
 
        if (Keyword.isJackKeyword(token)) {
            return TokenType.KEYWORD;
        }
 
        return TokenType.IDENTIFIER;
    }
}

JackToken.java
package biz.tugay.jackanalyzer.tokenizer;
 
import biz.tugay.jackanalyzer.tokenizer.token.TokenType;
 
public class JackToken {
    private TokenType tokenType;
    private String tokenValue;
 
    public void setTokenType(TokenType tokenType) {
        this.tokenType = tokenType;
    }
 
    public TokenType getTokenType() {
        return tokenType;
    }
 
    public void setTokenValue(String tokenValue) {
        this.tokenValue = tokenValue;
    }
 
    public String getTokenValue() {
        return tokenValue;
    }
}

CommentRemover.java
package biz.tugay.jackanalyzer.tokenizer;
 
public class CommentRemover {
 
    private boolean inString = false;
    private boolean inSingleLineComment = false;
    private boolean inMultiLineComment = false;
 
 
    public char[] removeComments(char[] jackFileContents) {
        int mark = 0;
        char[] jackFileContentsCommentsRemoved = new char[jackFileContents.length];
 
        for (int i = 0; i < jackFileContents.length; i++) {
            char currentChar = jackFileContents[i];
 
            if (inSingleLineComment) {
                if (i == jackFileContents.length - 1 || currentChar == '\n') {
                    inSingleLineComment = false;
                }
                continue;
            }
 
            if (inMultiLineComment) {
                if (currentChar == '/' && jackFileContents[i - 1] == '*') {
                    inMultiLineComment = false;
                }
                continue;
            }
 
            if (currentChar == '/' && jackFileContents[i + 1] == '/') {
                inSingleLineComment = true;
                continue;
            }
 
            if (currentChar == '/' && jackFileContents[i + 1] == '*') {
                inMultiLineComment = true;
                continue;
            }
 
            if (jackFileContents[i] == '\"') {
                switchInString();
            }
 
            if (inString) {
                jackFileContentsCommentsRemoved[mark] = currentChar;
                mark++;
                continue;
            }
 
            if (currentChar == '\n' || currentChar == ' ' || currentChar == '\t') {
                currentChar = ' ';
                if (mark != 0 && jackFileContentsCommentsRemoved[mark - 1] == ' ') {
                    continue;
                } else {
                    jackFileContentsCommentsRemoved[mark] = currentChar;
                }
            } else {
                jackFileContentsCommentsRemoved[mark] = currentChar;
            }
 
            mark++;
        }
 
        int length = mark;
        int srcPos = 0;
 
 
        if (jackFileContentsCommentsRemoved[0] == ' ') {
            srcPos = 1;
            length--;
        }
 
        if (jackFileContentsCommentsRemoved[mark - 1] == ' ') {
            length--;
        }
 
        char[] trimmedArray = new char[length];
        System.arraycopy(jackFileContentsCommentsRemoved, srcPos, trimmedArray, 0, length);
        return trimmedArray;
    }
 
    private void switchInString() {
        inString = !inString;
    }
}

JackTokenizer.java
package biz.tugay.jackanalyzer.tokenizer;
 
import biz.tugay.jackanalyzer.tokenizer.token.Symbol;
import biz.tugay.jackanalyzer.tokenizer.token.TokenType;
import biz.tugay.jackanalyzer.tokenizer.token.TokenTypeDecider;
 
// Removes all comments and white space from the input stream and
// breaks it into Jack-language tokens, as specified by the Jack grammar.
public class JackTokenizer {
 
    private String currentToken;
 
    private char[] jackFileContents;
    private int pos = 0;
 
    public boolean hasMoreTokens() {
        if (pos >= jackFileContents.length) {
            return false;
        }
        return true;
    }
 
    // Gets the next token from the input and makes it the current token.
    public void advance() {
        final StringBuilder stringBuilder = new StringBuilder();
 
        if (jackFileContents[pos] == ' ') {
            pos++;
        }
 
        if (jackFileContents[pos] == '\"') {
            stringBuilder.append(jackFileContents[pos]);
            pos++;
            while (jackFileContents[pos] != '\"') {
                stringBuilder.append(jackFileContents[pos]);
                pos++;
            }
            stringBuilder.append(jackFileContents[pos]);
            pos++;
            currentToken = stringBuilder.toString();
            return;
        }
 
        if (Symbol.isJackSymbol(jackFileContents[pos])) {
            stringBuilder.append(jackFileContents[pos]);
            currentToken = stringBuilder.toString();
            pos++;
            return;
        }
 
        boolean tokenComplete = false;
        while (!tokenComplete) {
            if (pos == jackFileContents.length || jackFileContents[pos] == ' ' || Symbol.isJackSymbol(jackFileContents[pos])) {
                tokenComplete = true;
            } else {
                stringBuilder.append(jackFileContents[pos]);
                pos++;
            }
        }
        this.currentToken = stringBuilder.toString();
    }
 
    public TokenType tokenType() {
        final TokenTypeDecider tokenTypeDecider = new TokenTypeDecider();
        final TokenType tokenType = tokenTypeDecider.tokenTypeFor(currentToken);
        return tokenType;
    }
 
    public String keyword() {
        return currentToken;
    }
 
    public String symbol() {
        final Symbol jackSymbol = Symbol.getJackSymbolFor(currentToken.charAt(0));
        return Symbol.getSymbolRepresentationFor(jackSymbol);
    }
 
    public String identifier() {
        return currentToken;
    }
 
    public String intVal() {
        return currentToken;
    }
 
    public String stringVal() {
        return currentToken.substring(1, currentToken.length() - 1);
    }
 
    public void setJackFileContents(char[] jackFileContents) {
        this.jackFileContents = jackFileContents;
    }
 
    private CommentRemover commentRemover;
 
    public void setCommentRemover(CommentRemover commentRemover) {
        this.commentRemover = commentRemover;
    }
 
    public void removeAllComments() {
        jackFileContents = commentRemover.removeComments(jackFileContents);
    }
 
    public String getCurrentToken() {
        return currentToken;
    }
}

CompilationEngine.java
package biz.tugay.jackanalyzer.compilation;
 
import biz.tugay.jackanalyzer.tokenizer.JackToken;
import biz.tugay.jackanalyzer.tokenizer.token.TokenType;
 
import java.util.*;
 
// Print and advance!
public class CompilationEngine {
 
    private JackToken[] jackTokens;
    int pos = 0;
    public JackToken currentJackToken;
 
    private final StringBuilder stringBuilder = new StringBuilder();
 
    public String compileClass() {
        currentJackToken = jackTokens[pos];
 
        stringBuilder.append("<class>");
        stringBuilder.append("\n");
 
        // <keyword> class </keyword>
        printCurrentToken();
        advanceCurrentJackToken();
 
        // <identifier> main </identifier>
        printCurrentToken();
        advanceCurrentJackToken();
 
        // <symbol> { </symbol>
        printCurrentToken();
        advanceCurrentJackToken();
 
        compileClassVarDeclarations();
        compileSubRoutineDeclarations();
 
        // <symbol> } </symbol>
        printCurrentToken();
        // no more advance!!!
 
        stringBuilder.append("</class>");
        stringBuilder.append("\n");
 
        return stringBuilder.toString();
    }
 
    private void compileClassVarDeclarations() {
        while (!isSubRoutineDeclarationStart(currentJackToken.getTokenValue())) {
            if (currentJackToken.getTokenValue().equals("}")) {
                break;
            }
            compileClassVarDeclaration();
        }
    }
 
    private void compileClassVarDeclaration() {
        stringBuilder.append("<classVarDec>");
        stringBuilder.append("\n");
 
        while (!currentJackToken.getTokenValue().equals(";")) {
            printCurrentToken();
            advanceCurrentJackToken();
        }
        printCurrentToken();
        advanceCurrentJackToken();
 
        stringBuilder.append("</classVarDec>");
        stringBuilder.append("\n");
    }
 
    private void compileSubRoutineDeclarations() {
        while (isSubRoutineDeclarationStart(currentJackToken.getTokenValue())) {
            compileSubRoutineDeclaration();
        }
    }
 
    private void compileSubRoutineDeclaration() {
        stringBuilder.append("<subroutineDec>");
        stringBuilder.append("\n");
 
 
        // <keyword> function | method | constructor </keyword>
        printCurrentToken();
        advanceCurrentJackToken();
 
        // (<keyword> void | int | char | boolean </keyword>) | (<identifier> Foo </identifier>)
        printCurrentToken();
        advanceCurrentJackToken();
 
        // <identifier> main </identifier>
        printCurrentToken();
        advanceCurrentJackToken();
 
        // <symbol> ( </symbol>
        printCurrentToken();
        advanceCurrentJackToken();
 
        compileParameterList();
        // parameter list..
 
        // <symbol> ) </symbol>
        printCurrentToken();
 
        advanceCurrentJackToken();
 
        compileSubRoutineBody();
        stringBuilder.append("</subroutineDec>");
        stringBuilder.append("\n");
    }
 
    private void compileParameterList() {
        stringBuilder.append("<parameterList>");
        stringBuilder.append("\n");
 
        while (!currentJackToken.getTokenValue().equals(")")) {
            printCurrentToken();
            advanceCurrentJackToken();
        }
 
 
        stringBuilder.append("</parameterList>");
        stringBuilder.append("\n");
    }
 
    private void compileSubRoutineBody() {
        stringBuilder.append("<subroutineBody>");
        stringBuilder.append("\n");
 
        // <symbol> { </symbol>
        printCurrentToken();
        advanceCurrentJackToken();
 
        compileVarDecs();
        compileStatements();
 
        // <symbol> } </symbol>
        printCurrentToken();
        advanceCurrentJackToken();
 
        stringBuilder.append("</subroutineBody>");
        stringBuilder.append("\n");
    }
 
    private void compileVarDecs() {
        while (currentJackToken.getTokenValue().equals("var")) {
            stringBuilder.append("<varDec>");
            stringBuilder.append("\n");
 
            // <keyword> var </keyword>
            printCurrentToken();
            advanceCurrentJackToken();
 
            // <keyword> int </keyword>
            printCurrentToken();
            advanceCurrentJackToken();
 
            // <identifier> foo </identifier>
            printCurrentToken();
            advanceCurrentJackToken();
 
            while (currentJackToken.getTokenValue().equals(",")) {
                // <identifier> , </identifier>
                printCurrentToken();
                advanceCurrentJackToken();
 
                // <identifier> bar </identifier>
                printCurrentToken();
                advanceCurrentJackToken();
            }
 
            // <symbol> ; </symbol>
            printCurrentToken();
            advanceCurrentJackToken();
 
            stringBuilder.append("</varDec>");
            stringBuilder.append("\n");
        }
    }
 
    private void compileStatements() {
        stringBuilder.append("<statements>");
        stringBuilder.append("\n");
 
        while (isStatementStart(currentJackToken.getTokenValue())) {
            final String tokenValue = currentJackToken.getTokenValue();
            if (tokenValue.equals("let")) {
                compileLetStatement();
            }
            if (tokenValue.equals("return")) {
                compileReturnStatement();
            }
            if (tokenValue.equals("if")) {
                compileIfStatement();
            }
            if (tokenValue.equals("while")) {
                compileWhileStatement();
            }
            if (tokenValue.equals("do")) {
                stringBuilder.append("<doStatement>");
                stringBuilder.append("\n");
 
                printCurrentToken();
                advanceCurrentJackToken();
 
                compileSubRoutineCall();
 
                printCurrentToken();
                advanceCurrentJackToken();
 
                stringBuilder.append("</doStatement>");
                stringBuilder.append("\n");
            }
        }
 
        stringBuilder.append("</statements>");
        stringBuilder.append("\n");
    }
 
    private void compileWhileStatement() {
        stringBuilder.append("<whileStatement>");
        stringBuilder.append("\n");
 
        // <keyword> while </keyword>
        printCurrentToken();
        advanceCurrentJackToken();
 
        // <symbol> ( </symbol>
        printCurrentToken();
        advanceCurrentJackToken();
 
        compileSingleExpression();
 
        // <symbol> ) </symbol>
        printCurrentToken();
        advanceCurrentJackToken();
 
        // <symbol> { </symbol>
        printCurrentToken();
        advanceCurrentJackToken();
 
        compileStatements();
 
        // <symbol> } </symbol>
        printCurrentToken();
        advanceCurrentJackToken();
 
        stringBuilder.append("</whileStatement>");
        stringBuilder.append("\n");
    }
 
    private void compileIfStatement() {
        stringBuilder.append("<ifStatement>");
        stringBuilder.append("\n");
 
        // <keyword> if </keyword>
        printCurrentToken();
        advanceCurrentJackToken();
 
        // <symbol> ( </symbol>
        printCurrentToken();
        advanceCurrentJackToken();
 
        compileSingleExpression();
 
        // <symbol> ) </symbol>
        printCurrentToken();
        advanceCurrentJackToken();
 
        // <symbol> { </symbol>
        printCurrentToken();
        advanceCurrentJackToken();
 
        compileStatements();
 
        // <symbol> } </symbol>
        printCurrentToken();
        advanceCurrentJackToken();
 
        if (currentJackToken.getTokenValue().equals("else")) {
            // <identifier> else </identifier>
            printCurrentToken();
            advanceCurrentJackToken();
 
            // <symbol> { </symbol>
            printCurrentToken();
            advanceCurrentJackToken();
 
            compileStatements();
 
            // <symbol> } </symbol>
            printCurrentToken();
            advanceCurrentJackToken();
        }
 
        stringBuilder.append("</ifStatement>");
        stringBuilder.append("\n");
    }
 
    private void compileLetStatement() {
        stringBuilder.append("<letStatement>");
        stringBuilder.append("\n");
 
        // <keyword> let </keyword>
        printCurrentToken();
        advanceCurrentJackToken();
 
        // <identifier> a </identifier>
        printCurrentToken();
        advanceCurrentJackToken();
 
        if (currentJackToken.getTokenValue().equals("[")) {
            // <symbol> [ </symbol>
            printCurrentToken();
            advanceCurrentJackToken();
 
            compileSingleExpression();
 
            // <symbol> ] </symbol>
            printCurrentToken();
            advanceCurrentJackToken();
        }
 
        // <symbol> = </symbol>
        printCurrentToken();
        advanceCurrentJackToken();
 
        compileSingleExpression();
 
        // <symbol> ; </symbol>
        printCurrentToken();
        advanceCurrentJackToken();
 
        stringBuilder.append("</letStatement>");
        stringBuilder.append("\n");
    }
 
    private void compileReturnStatement() {
        stringBuilder.append("<returnStatement>");
        stringBuilder.append("\n");
 
        printCurrentToken();
        // <keyword> return </keyword>
 
        advanceCurrentJackToken();
        if (!currentJackToken.getTokenValue().equals(";")) {
            compileSingleExpression();
        }
 
        // <symbol> ; </symbol>
        printCurrentToken();
        advanceCurrentJackToken();
 
        stringBuilder.append("</returnStatement>");
        stringBuilder.append("\n");
    }
 
    private void compileSingleExpression() {
        stringBuilder.append("<expression>");
        stringBuilder.append("\n");
 
        compileTerm();
 
        while (isOp(currentJackToken.getTokenValue())) {
            printCurrentToken();
            advanceCurrentJackToken();
            compileTerm();
        }
 
        stringBuilder.append("</expression>");
        stringBuilder.append("\n");
    }
 
    private void compileTerm() {
        stringBuilder.append("<term>");
        stringBuilder.append("\n");
 
        if (currentJackToken.getTokenType() == TokenType.INTEGERCONSTANT) {
            compileIntegerConstant();
        } else if (currentJackToken.getTokenType() == TokenType.STRINGCONSTANT) {
            compileStringConstant();
        } else if (currentJackToken.getTokenType() == TokenType.KEYWORD) {
            compileKeywordConstant();
        } else if (currentJackToken.getTokenValue().equals("(")) {
            // <symbol> ( </symbol>
            printCurrentToken();
            advanceCurrentJackToken();
 
            compileSingleExpression();
 
            // <symbol> ) </symbol>
            printCurrentToken();
            advanceCurrentJackToken();
        } else if (isUnaryOp(currentJackToken.getTokenValue())) {
            // <symbol> ~ </symbol>
            printCurrentToken();
            advanceCurrentJackToken();
 
            compileTerm();
        } else if (jackTokens[pos + 1].getTokenValue().equals("(") || jackTokens[pos + 1].getTokenValue().equals(".")) {
            compileSubRoutineCall();
        } else {
            compileIdentifier();
            if (jackTokens[pos].getTokenValue().equals("[")) {
                // <symbol> [ </symbol>
                printCurrentToken();
                advanceCurrentJackToken();
 
                compileSingleExpression();
 
                // <symbol> ] </symbol>
                printCurrentToken();
                advanceCurrentJackToken();
            }
        }
 
        stringBuilder.append("</term>");
        stringBuilder.append("\n");
    }
 
    private void compileIdentifier() {
        // <identifier> foo </identifier>
        printCurrentToken();
        advanceCurrentJackToken();
    }
 
    private void compileSubRoutineCall() {
        // <identifier> Keyboard </identifier>
        printCurrentToken();
        advanceCurrentJackToken();
 
        if (currentJackToken.getTokenValue().equals("(")) {
            // <symbol> ( </symbol>
            printCurrentToken();
            advanceCurrentJackToken();
 
            compileExpressionList();
 
            // <symbol> ) </symbol>
            printCurrentToken();
            advanceCurrentJackToken();
        } else {
            // <symbol> . </symbol>
            printCurrentToken();
            advanceCurrentJackToken();
 
            // subRoutineName..
            printCurrentToken();
            advanceCurrentJackToken();
 
            // <symbol> ( </symbol>
            printCurrentToken();
            advanceCurrentJackToken();
 
            compileExpressionList();
 
            // <symbol> ) </symbol>
            printCurrentToken();
            advanceCurrentJackToken();
        }
    }
 
    private void compileExpressionList() {
        stringBuilder.append("<expressionList>");
        stringBuilder.append("\n");
 
        if (!currentJackToken.getTokenValue().equals(")")) {
            compileSingleExpression();
            while (currentJackToken.getTokenValue().equals(",")) {
                printCurrentToken();
                advanceCurrentJackToken();
 
                compileSingleExpression();
            }
        }
 
        stringBuilder.append("</expressionList>");
        stringBuilder.append("\n");
    }
 
    private void compileKeywordConstant() {
        // <keyword> true </keyword>
        printCurrentToken();
        advanceCurrentJackToken();
    }
 
    private void compileIntegerConstant() {
        // <integerConstant> 20 </integerConstant>
        printCurrentToken();
        advanceCurrentJackToken();
    }
 
    private void compileStringConstant() {
        // <stringConstant> hello </string
        printCurrentToken();
        advanceCurrentJackToken();
    }
 
    private void printCurrentToken() {
        stringBuilder.append("<").append(currentJackToken.getTokenType().getTokenAsString()).append(">").append(currentJackToken.getTokenValue()).append("</").append(currentJackToken.getTokenType().getTokenAsString()).append(">");
        stringBuilder.append("\n");
    }
 
    private void advanceCurrentJackToken() {
        pos++;
        setCurrentJackToken();
    }
 
    private void setCurrentJackToken() {
        currentJackToken = jackTokens[pos];
    }
 
    public void setJackTokens(final List<JackToken> jackTokens) {
        this.jackTokens = jackTokens.toArray(new JackToken[0]);
    }
 
    // Helper methods..
    private boolean isSubRoutineDeclarationStart(final String currentTokenValue) {
        final Set<String> validSubRoutineDeclaration = new HashSet<String>() {
            {
                add("constructor");
                add("function");
                add("method");
            }
        };
        if (validSubRoutineDeclaration.contains(currentTokenValue)) {
            return true;
        }
        return false;
    }
 
    private boolean isStatementStart(final String currentTokenValue) {
        final Set<String> statementStarts = new HashSet<String>() {
            {
                add("let");
                add("if");
                add("while");
                add("do");
                add("return");
            }
        };
        if (statementStarts.contains(currentTokenValue)) {
            return true;
        }
        return false;
    }
 
 
    private boolean isOp(String tokenValue) {
        final Set<String> ops = new HashSet<String>() {
            {
                add("+");
                add("-");
                add("*");
                add("/");
                add("&");
                add("|");
                add("<");
                add(">");
                add("=");
                add("&lt;");
                add("&gt;");
                add("&amp;");
            }
        };
        if (ops.contains(tokenValue)) {
            return true;
        }
        return false;
    }
 
    private boolean isUnaryOp(String tokenValue) {
        final Set<String> ops = new HashSet<String>() {
            {
                add("-");
                add("~");
            }
        };
        if (ops.contains(tokenValue)) {
            return true;
        }
        return false;
    }
}

JackAnalyzer.java
package biz.tugay.jackanalyzer;
 
import biz.tugay.jackanalyzer.compilation.CompilationEngine;
import biz.tugay.jackanalyzer.tokenizer.CommentRemover;
import biz.tugay.jackanalyzer.tokenizer.JackToken;
import biz.tugay.jackanalyzer.tokenizer.JackTokenizer;
import biz.tugay.jackanalyzer.tokenizer.token.TokenType;
 
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
 
public class JackAnalyzer {
 
    public static void main(String[] args) throws IOException {
        String source = args[0];
 
        if (source.endsWith(".jack")) {
            source = source.substring(0, source.lastIndexOf('.'));
        }
 
        final File initialSourceFile = new File(source);
 
        final boolean isInitialSourceFileDirectory = initialSourceFile.isDirectory();
 
        String dirName = "";
        if (isInitialSourceFileDirectory) {
            dirName = source;
        }
 
        final File[] sourceFiles;
 
        if (isInitialSourceFileDirectory) {
            sourceFiles = initialSourceFile.listFiles();
        } else {
            sourceFiles = new File[]{new File(source + ".jack")};
        }
 
        assert sourceFiles != null;
 
        for (File sourceFile : sourceFiles) {
            if (!sourceFile.getName().endsWith(".jack")) {
                continue;
            }
 
            String targetTokenFileName;
            if (isInitialSourceFileDirectory) {
                targetTokenFileName = dirName + "/" + sourceFile.getName().substring(0, sourceFile.getName().lastIndexOf('.')) + "T.xml";
            } else {
                targetTokenFileName = source + "T.xml";
            }
 
            String targetCompilationFileName;
            if (isInitialSourceFileDirectory) {
                targetCompilationFileName = dirName + "/" + sourceFile.getName().substring(0, sourceFile.getName().lastIndexOf('.')) + ".xml";
            } else {
                targetCompilationFileName = source + ".xml";
            }
 
            final File targetTokenFile = new File(targetTokenFileName);
            final File targetCompilationFile = new File(targetCompilationFileName);
 
            char[] charArray = readFileToCharArray(sourceFile);
 
            // Setup classes
            final JackTokenizer jackTokenizer = new JackTokenizer();
            final CommentRemover commentRemover = new CommentRemover();
            jackTokenizer.setCommentRemover(commentRemover);
            jackTokenizer.setJackFileContents(charArray);
 
            // Clean up the .jack file contents
            jackTokenizer.removeAllComments();
 
            List<JackToken> jackTokens = new ArrayList<JackToken>();
 
            while (jackTokenizer.hasMoreTokens()) {
                jackTokenizer.advance();
                final TokenType tokenType = jackTokenizer.tokenType();
                final JackToken jackToken = new JackToken();
                jackToken.setTokenType(tokenType);
                switch (tokenType) {
                    case IDENTIFIER:
                        jackToken.setTokenValue(jackTokenizer.identifier());
                        break;
                    case INTEGERCONSTANT:
                        jackToken.setTokenValue(jackTokenizer.intVal());
                        break;
                    case KEYWORD:
                        jackToken.setTokenValue(jackTokenizer.keyword());
                        break;
                    case SYMBOL:
                        jackToken.setTokenValue(jackTokenizer.symbol());
                        break;
                    case STRINGCONSTANT:
                        jackToken.setTokenValue(jackTokenizer.stringVal());
                        break;
                }
                jackTokens.add(jackToken);
            }
 
            final StringBuilder jackTokensStringBuilder = new StringBuilder();
            for (JackToken jackToken : jackTokens) {
                jackTokensStringBuilder.append("<").append(jackToken.getTokenType().getTokenAsString()).append(">").append(jackToken.getTokenValue()).append("</").append(jackToken.getTokenType().getTokenAsString()).append(">");
                jackTokensStringBuilder.append("\n");
            }
 
            final CompilationEngine compilationEngine = new CompilationEngine();
            compilationEngine.setJackTokens(jackTokens);
 
            final String tokensString = jackTokensStringBuilder.toString();
            final String compilationString = compilationEngine.compileClass();
 
            final FileWriter tokenFileWriter = new FileWriter(targetTokenFile);
            tokenFileWriter.write(tokensString);
            tokenFileWriter.flush();
            tokenFileWriter.close();
 
            final FileWriter compilationFileWriter = new FileWriter(targetCompilationFile);
            compilationFileWriter.write(compilationString);
            compilationFileWriter.flush();
            compilationFileWriter.close();
        }
    }
 
    private static char[] readFileToCharArray(File file) throws FileNotFoundException {
        String theString;
        Scanner scanner = new Scanner(file);
 
        theString = scanner.nextLine();
        while (scanner.hasNextLine()) {
            theString = theString + "\n" + scanner.nextLine();
        }
 
        return theString.toCharArray();
    }
}