Example #1
0
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])
Example #3
0
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)
Example #4
0
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()