def main(infile_path: str, outfile_path: str): print(infile_path) vm_files = [path for path in glob.glob(infile_path + "/*.vm")] code_writer = CodeWriter(filepath=outfile_path) code_writer.write_init() for filepath in vm_files: print(filepath) parser = Parser(filepath=filepath) code_writer.set_file_path(filepath) while parser.hasMoreCommands(): cmd = parser.commandType() if cmd == C_ARITHMETIC: code_writer.writeArithmetic(parser.arithmetic()) elif cmd == C_PUSH or cmd == C_POP: code_writer.writePushPop(cmd, parser.arg1(), index=int(parser.arg2())) elif cmd == C_LABEL: code_writer.writeLabel(parser.arg1()) elif cmd == C_GOTO: code_writer.writeGoto(parser.arg1()) elif cmd == C_IF: code_writer.writeIf(parser.arg1()) elif cmd == C_FUNCTION: code_writer.writeFunction(parser.arg1(), parser.arg2()) elif cmd == C_RETURN: code_writer.writeReturn() elif cmd == C_CALL: code_writer.writeCall(parser.arg1(), parser.arg2()) parser.advance() code_writer.close()
def main(): '''Main entry point for the script.''' # For each .vm file, create a parser object filetrue = os.path.isfile(sys.argv[1]) dirtrue = os.path.isdir(sys.argv[1]) vmfiles = [] # Rename directory as a ".asm" file for later use finame = os.path.basename(os.path.normpath(sys.argv[1])) + ".asm" # Get file path with .asm file appended dirname = os.path.join(sys.argv[1], finame) # Create list of files to convert and add to asm file if dirtrue: cw = CodeWriter(dirname) fi = os.listdir(sys.argv[1]) for names in fi: if names.endswith(".vm"): vmfiles.append(sys.argv[1] + names) elif filetrue: di = sys.argv[1] if di.endswith(".vm"): vmfiles.append(di) tr = vmfiles[0] trs = tr.replace("vm", "asm") cw = CodeWriter(trs) else: print "invalid filetype: only input .vm files" else: print "usage: 'python <file.vm> or <dirname/>'" out = cw.constructor() cw.writeInit(out) with out as outfile: for files in vmfiles: # Create new instance of class Parser() p = cw.setFileName(files) with p.constructor() as infile: for line in infile: if p.commandType(line) == "comments": pass elif p.commandType(line) == "C_ARITHMETIC": cw.writeArithmetic(outfile, p.args(line)[0]) elif p.commandType(line) == "C_IF": # Handle if-goto command cw.writeIf(outfile, p.args(line)[1]) elif p.commandType(line) == "C_GOTO": # Handle goto command cw.writeGoto(outfile, p.args(line)[1]) elif p.commandType(line) == "C_RETURN": # Return function result cw.writeReturn(outfile) elif p.commandType(line) == "C_LABEL": # Set label address cw.writeLabel(outfile, p.args(line)[1]) elif p.commandType(line) == "C_CALL": # Handle function calls cw.writeCall(outfile, p.args(line)[1], p.args(line)[2]) elif p.commandType(line) == "C_FUNCTION": cw.writeFunction(outfile, p.args(line)[1], p.args(line)[2]) elif p.commandType(line) == "C_PUSH" or "C_POP": cw.writePushPop(outfile, p.commandType(line), p.args(line)[1], p.args(line)[2])
class CodeWriterTest(unittest.TestCase): def setUp(self): self.tmpdir = TemporaryDirectory() os.chdir(self.tmpdir.name) self.assembly_filename = 'test_output' self.code_writer = CodeWriter(self.assembly_filename) self.maxDiff = None def __del__(self): self.tmpdir.cleanup() def test_add(self): self.code_writer.writeArithmetic('add') self.assertGeneratedAssemblyEqual([ '@SP', 'M=M-1', 'A=M', 'D=M', '@SP', 'M=M-1', 'A=M', 'D=D+M', 'M=D', '@SP', 'M=M+1' ]) def test_sub(self): self.code_writer.writeArithmetic('sub') self.assertGeneratedAssemblyEqual([ '@SP', 'M=M-1', 'A=M', 'D=M', '@SP', 'M=M-1', 'A=M', 'D=M-D', 'M=D', '@SP', 'M=M+1' ]) def test_neq(self): self.code_writer.writeArithmetic('neg') self.assertGeneratedAssemblyEqual([ '@SP', 'M=M-1', 'A=M', 'M=-M', '@SP', 'M=M+1' ]) def test_and(self): self.code_writer.writeArithmetic('and') self.assertGeneratedAssemblyEqual([ '@SP', 'M=M-1', 'A=M', 'D=M', '@SP', 'M=M-1', 'A=M', 'M=M&D', '@SP', 'M=M+1' ]) def test_or(self): self.code_writer.writeArithmetic('or') self.assertGeneratedAssemblyEqual([ '@SP', 'M=M-1', 'A=M', 'D=M', '@SP', 'M=M-1', 'A=M', 'M=M|D', '@SP', 'M=M+1' ]) def test_not(self): self.code_writer.writeArithmetic('not') self.assertGeneratedAssemblyEqual([ '@SP', 'M=M-1', 'A=M', 'M=!M', '@SP', 'M=M+1' ]) def test_eq(self): self.code_writer.writeArithmetic('eq') self.assertGeneratedAssemblyEqual([ '@SP', 'M=M-1', 'A=M', 'D=M', '@SP', 'M=M-1', 'A=M', 'D=D-M', '@test_output.1', 'D;JEQ', '@test_output.0', '0;JMP', '(test_output.1)', '@SP', 'A=M', 'M=-1', '@test_output.2', '0;JMP', '(test_output.0)', '@SP', 'A=M', 'M=0', '@test_output.2', '0;JMP', '(test_output.2)', '@SP', 'M=M+1' ]) def test_lt(self): self.code_writer.writeArithmetic('lt') self.assertGeneratedAssemblyEqual([ '@SP', 'M=M-1', 'A=M', 'D=M', '@SP', 'M=M-1', 'A=M', 'D=M-D', '@test_output.1', 'D;JLT', '@test_output.0', '0;JMP', '(test_output.1)', '@SP', 'A=M', 'M=-1', '@test_output.2', '0;JMP', '(test_output.0)', '@SP', 'A=M', 'M=0', '@test_output.2', '0;JMP', '(test_output.2)', '@SP', 'M=M+1' ]) def test_gt(self): self.code_writer.writeArithmetic('gt') self.assertGeneratedAssemblyEqual([ '@SP', 'M=M-1', 'A=M', 'D=M', '@SP', 'M=M-1', 'A=M', 'D=D-M', '@test_output.1', 'D;JLT', '@test_output.0', '0;JMP', '(test_output.1)', '@SP', 'A=M', 'M=-1', '@test_output.2', '0;JMP', '(test_output.0)', '@SP', 'A=M', 'M=0', '@test_output.2', '0;JMP', '(test_output.2)', '@SP', 'M=M+1' ]) def test_write_push_pop_ignores_trailing_comments(self): self.code_writer.writePushPop('push constant 37 // this is a comment') self.assertGeneratedAssemblyEqual([ '@37', 'D=A', '@SP', 'A=M', 'M=D', '@SP', 'M=M+1' ]) def test_push_constant(self): self.code_writer.writePushPop('push constant 37') self.assertGeneratedAssemblyEqual([ '@37', 'D=A', '@SP', 'A=M', 'M=D', '@SP', 'M=M+1' ]) def test_push_static(self): self.code_writer.writePushPop('push static 37') self.assertGeneratedAssemblyEqual([ '@test_output.37', 'D=M', '@SP', 'A=M', 'M=D', '@SP', 'M=M+1' ]) def test_push_argument(self): self.code_writer.writePushPop('push argument 800') self.assertGeneratedAssemblyEqual([ '@ARG', 'D=M', '@800', 'D=A+D', 'A=D', 'D=M', '@SP', 'A=M', 'M=D', '@SP', 'M=M+1' ]) def test_push_temp(self): self.code_writer.writePushPop('push temp 876') self.assertGeneratedAssemblyEqual([ '@5', 'D=A', '@876', 'D=A+D', 'A=D', 'D=M', '@SP', 'A=M', 'M=D', '@SP', 'M=M+1' ]) def test_push_pointer_0(self): self.code_writer.writePushPop('push pointer 0') self.assertGeneratedAssemblyEqual([ '@THIS', 'D=M', '@SP', 'A=M', 'M=D', '@SP', 'M=M+1' ]) def test_push_pointer_1(self): self.code_writer.writePushPop('push pointer 1') self.assertGeneratedAssemblyEqual([ '@THAT', 'D=M', '@SP', 'A=M', 'M=D', '@SP', 'M=M+1' ]) def test_pop_static(self): self.code_writer.writePushPop('pop static 37') self.assertGeneratedAssemblyEqual([ '@SP', 'M=M-1', 'A=M', 'D=M', '@test_output.37', 'M=D' ]) def test_pop_argument(self): self.code_writer.writePushPop('pop argument 800') self.assertGeneratedAssemblyEqual([ '@ARG', 'D=M', '@800', 'D=A+D', '@R13', 'M=D', '@SP', 'M=M-1', 'A=M', 'D=M', '@R13', 'A=M', 'M=D' ]) def test_pop_temp(self): self.code_writer.writePushPop('pop temp 876') self.assertGeneratedAssemblyEqual([ '@5', 'D=A', '@876', 'D=A+D', '@R13', 'M=D', '@SP', 'M=M-1', 'A=M', 'D=M', '@R13', 'A=M', 'M=D' ]) def test_pop_pointer_0(self): self.code_writer.writePushPop('pop pointer 0') self.assertGeneratedAssemblyEqual([ '@SP', 'M=M-1', 'A=M', 'D=M', '@THIS', 'M=D' ]) def test_pop_pointer_1(self): self.code_writer.writePushPop('pop pointer 1') self.assertGeneratedAssemblyEqual([ '@SP', 'M=M-1', 'A=M', 'D=M', '@THAT', 'M=D' ]) def test_write_function(self): self.code_writer.writeFunction('function SimpleFunction.test 2') self.assertGeneratedAssemblyEqual([ '(test_output.SimpleFunction.test)', '@SP', 'D=M', 'A=D', 'M=0', '@SP', 'M=M+1', '@SP', 'D=M', 'A=D', 'M=0', '@SP', 'M=M+1' ]) def test_write_return(self): self.code_writer.writeReturn() self.assertGeneratedAssemblyEqual([ '@LCL', 'D=M', '@R13', 'M=D', 'D=D-1', 'D=D-1', 'D=D-1', 'D=D-1', 'D=D-1', 'A=D', 'D=M', '@R14', 'M=D', '@SP', 'M=M-1', 'A=M', 'D=M', '@ARG', 'A=M', 'M=D', '@ARG', 'D=M+1', '@SP', 'M=D', '@R13', 'M=M-1', 'A=M', 'D=M', '@THAT', 'M=D', '@R13', 'M=M-1', 'A=M', 'D=M', '@THIS', 'M=D', '@R13', 'M=M-1', 'A=M', 'D=M', '@ARG', 'M=D', '@R13', 'M=M-1', 'A=M', 'D=M', '@LCL', 'M=D', '@R14', 'A=M', '0;JMP' ]) def test_write_call(self): self.code_writer.writeCall('call Sys.init 0') self.assertGeneratedAssemblyEqual([ '@test_output.Sys.init.return', 'D=A', '@SP', 'A=M', 'M=D', '@SP', 'M=M+1', '@LCL', 'D=M', '@SP', 'A=M', 'M=D', '@SP', 'M=M+1', '@ARG', 'D=M', '@SP', 'A=M', 'M=D', '@SP', 'M=M+1', '@THIS', 'D=M', '@SP', 'A=M', 'M=D', '@SP', 'M=M+1', '@THAT', 'D=M', '@SP', 'A=M', 'M=D', '@SP', 'M=M+1', '@SP', 'D=M', 'D=D-1', 'D=D-1', 'D=D-1', 'D=D-1', 'D=D-1', '@ARG', 'M=D', '@SP', 'D=M', '@LCL', 'M=D', '@test_output.Sys.init', '0;JMP', '@9999', '(test_output.Sys.init.return)' ]) def test_write_bootstrap(self): self.code_writer.writeBootstrap() self.assertGeneratedAssemblyEqual([ '@256', 'D=A', '@SP', 'M=D' ]) def assertGeneratedAssemblyEqual(self, assembly=[]): self.assertAssemblyEqual(self.assembly_filename, assembly) def assertAssemblyEqual(self, file_name, assembly=[]): with open(file_name, 'r') as f: self.assertEqual([x.rstrip() for x in f.readlines()], assembly)
def main(path): """Entry point for the vm translator.""" vm_files_paths = get_vm_files(path) if not os.path.exists(path): print("invalid file path") sys.exit(1) if os.path.isdir(path): dirname = os.path.dirname(path) name = os.path.basename(dirname) path = f"{dirname}/{name}" isdir = True else: path = os.path.splitext(path)[0] isdir = False # Create single code write module code_writer = CodeWriter(f"{path}.asm") if isdir: code_writer.writeInit() for vm_file_path in vm_files_paths: filestream = open(vm_file_path, "r") parser = Parser(filestream) filestream.close() # write to assembly file code_writer.setFileName(os.path.basename(vm_file_path)) while parser.hasMoreCommands(): parser.advance() command_type = parser.commandType() if (command_type == CommandType.C_PUSH or command_type == CommandType.C_POP): segment = parser.arg1() index = parser.arg2() code_writer.writePushPop( command_type, segment, int(index) ) elif command_type == CommandType.C_ARITHMETIC: command = parser.arg1() code_writer.writeArithmetic(command) elif command_type == CommandType.C_LABEL: label = parser.arg1() code_writer.writeLabel(label) elif command_type == CommandType.C_GOTO: label = parser.arg1() code_writer.writeGoto(label) elif command_type == CommandType.C_IF: label = parser.arg1() code_writer.writeIf(label) elif command_type == CommandType.C_FUNCTION: label = parser.arg1() number_of_locals = int(parser.arg2()) code_writer.writeFunction(label, number_of_locals) elif command_type == CommandType.C_RETURN: code_writer.writeReturn() elif command_type == CommandType.C_CALL: functioname = parser.arg1() number_of_args = int(parser.arg2()) code_writer.writeCall(functioname, number_of_args) print(code_writer.filestream.get_global_counter()) code_writer.close()