#!/usr/bin/python3

import textwrap as tw
import io
import sys
import os

class Command(object):

    commandTypes = {'C_ARITHMETIC': lambda toks:
                        len(toks) == 1 and toks[0] in CodeWriter.instructions,
                    'C_PUSH': lambda toks: len(toks) == 3 and \
                        toks[0] == 'push',
                    'C_POP': lambda toks: len(toks) == 3 and \
                        toks[0] == 'pop',
                    'C_LABEL': lambda toks: len(toks) == 2 and \
                        toks[0] == 'label',
                    'C_GOTO': lambda toks: len(toks) == 2 and \
                        toks[0] == 'goto',
                    'C_IF': lambda toks: len(toks) == 2 and \
                        toks[0] == 'if-goto',
                    'C_FUNCTION': lambda toks: len(toks) == 3 and \
                        toks[0] == 'function',
                    'C_RETURN': lambda toks: len(toks) == 1 and \
                        toks[0] == 'return',
                    'C_CALL': lambda toks: len(toks) == 3 and \
                        toks[0] == 'call'}
     
    @staticmethod
    def _getCommandT(tokens):
        for c, tester in Command.commandTypes.items():
            if tester(tokens):
                return c
        raise (Exception("Not any of 7 recognizable command types " +  str(tokens)))
        return None

    def __init__(self, line):
        if len(line) == 0:
            self.valid = False
            return 
        self.valid = True

        tokens = line.split()
        command = Command._getCommandT(tokens)
        self.type = command
        
        # Set arguments
        if command == 'C_ARITHMETIC':
            self.arg1 = tokens[0]
        if command in ['C_POP', 'C_PUSH', 'C_LABEL', 'C_GOTO', 'C_IF', 'C_FUNCTION', 'C_CALL']:
            self.arg1 = tokens[1]
        if command in ['C_POP', 'C_PUSH', 'C_FUNCTION', 'C_CALL']:
            self.arg2 = tokens[2]
        

class Parser(object):
    
    def __init__(self, filestream):
        self.stream = filestream
        self.curCommand = None
        self.nextCommand = self._getNextCommand()

    def hasMoreCommands(self):
        ''' Are there any more commands? '''
        return self.nextCommand.valid

    def _getNextCommand(self):
        stream = self.stream
        while True:
            line = stream.readline()
            if line == '':
                return Command('') # Empty command
            line = line.strip()
            comment_index = line.find ('//')
            if comment_index != -1:
                line = line[:comment_index]
            if line != '':
                break
        return Command(line)   

    def advance(self):
        ''' 
            Reads the next command from input
            and makes it current command. Should
            be called only if 'hasMoreCommands'
        '''
        self.curCommand = self.nextCommand
        self.nextCommand = self._getNextCommand()

    def commandType(self):
        return self.curCommand.type

    def getArg1(self):
        c = self.curCommand
        return hasattr(c, 'arg1') and c.arg1 or "NO_ARG"

    def getArg2(self):
        c = self.curCommand
        return hasattr(c, 'arg2') and c.arg2 or "NO_ARG"
    

class CodeWriter(object):

    segment_symbols = {
        'local' : 'LCL',
        'argument' : 'ARG',
        'this' : 'THIS',
        'that' : 'THAT',
    }

    segment_addrs = {
        'pointer' : '3',
        'temp' : '5'
    }

    instructions = {

        'add' : 
            ['@SP',
             'M=M-1',
             'A=M',
             'D=M',
             'A=A-1',
             'D=D+M',
             '@SP',
             'A=M-1',
             'M=D'],

        'eq' :
            ['@SP',   
             'M=M-1',
             'A=M',
             'D=M',
             'A=A-1',
             'D=D-M',
             '@ELSE{0}',
             'D; JEQ',
             '@SP',
             'A=M-1',
             'M=0',
             '@ENDELSE{0}',
             '0; JMP',
             '(ELSE{0})',
             '@SP',
             'A=M-1',
             'M=-1',
             '(ENDELSE{0})'
            ],

        'lt' :
            ['@SP',   
             'M=M-1',
             'A=M',
             'D=M',
             'A=A-1',
             'D=D-M',
             '@ELSE{0}',
             'D; JGT',
             '@SP',
             'A=M-1',
             'M=0',
             '@ENDELSE{0}',
             '0; JMP',
             '(ELSE{0})',
             '@SP',
             'A=M-1',
             'M=-1',
             '(ENDELSE{0})'
            ],

        'gt' :
            ['@SP',   
             'M=M-1',
             'A=M',
             'D=M',
             'A=A-1',
             'D=D-M',
             '@ELSE{0}',
             'D; JLT',
             '@SP',
             'A=M-1',
             'M=0',
             '@ENDELSE{0}',
             '0; JMP',
             '(ELSE{0})',
             '@SP',
             'A=M-1',
             'M=-1',
             '(ENDELSE{0})'
            ],

        'sub' : 
            ['@SP',
             'M=M-1',
             'A=M',
             'D=M',
             'A=A-1',
             'D=M-D',
             '@SP',
             'A=M-1',
             'M=D'],

        'neg' :
            ['@SP',
             'A=M',
             'A=A-1',
             'M=-M'],

        'not' :
            ['@SP',
             'A=M',
             'A=A-1',
             'M=!M'],

        'and' : 
            ['@SP',
             'M=M-1',
             'A=M',
             'D=M',
             'A=A-1',
             'D=D&M',
             '@SP',
             'A=M-1',
             'M=D'],

        'or' :
            ['@SP',
             'M=M-1',
             'A=M',
             'D=M',
             'A=A-1',
             'D=D|M',
             '@SP',
             'A=M-1',
             'M=D'],

        'C_PUSH' : 
            ['@SP',
             'M=M+1',
             'A=M-1',
             'M=D'],

        'C_POP' :
            ['@SP',
             'M=M-1',
             'A=M',
             'D=M']

    }

    for c in instructions:
        instructions[c] = '\n'.join(instructions[c]) + '\n'

    def __init__(self, outstream):
        self.outstream = outstream
        self.labelCounter = 0
        
    def setFileName(self, newName):
        ls = newName.split('/')
        self.filename = ls[-1]
        self.funcname = 'global'

    def writeArithmetics(self, command):
        out = CodeWriter.instructions[command]
        if command in ['gt', 'lt', 'eq']:
            out = out.format(self.labelCounter)
            self.labelCounter += 1
        self.outstream.write(out)

    def writePushPop(self, command, segment, index):
        genericInstr = CodeWriter.instructions[command]
        if command == 'C_PUSH':
            if segment == 'constant':
                add = '@' + index + '\n'
                add += 'D=A\n'
            else:
                add = ''
                if segment in CodeWriter.segment_symbols:
                    add += '@' + CodeWriter.segment_symbols[segment] + '\n'
                    add += 'D=M\n'
                elif segment in CodeWriter.segment_addrs:
                    add += '@' + CodeWriter.segment_addrs[segment] + '\n'
                    add += 'D=A\n'
                elif segment == 'static':
                    add += '@' + self.filename + '.' + index + '\n'
                    add += 'D=A\n'
                if segment != 'static':
                    add += '@' + index + '\n'
                    add += 'D=D+A\n'
                add += 'A=D\n'
                add += 'D=M\n'
            out = add + genericInstr
        elif command == 'C_POP':
            add = '@R13\n'
            add += 'M=D\n'
            if segment in CodeWriter.segment_symbols:
                add += '@' + CodeWriter.segment_symbols[segment] + '\n'
                add += 'D=M\n'
            elif segment in CodeWriter.segment_addrs:
                add += '@' + CodeWriter.segment_addrs[segment] + '\n'
                add += 'D=A\n'
            elif segment == 'static':
                add += '@' + self.filename + '.' + index + '\n'
                add += 'D=A\n'
            if segment != 'static':
                add += '@' + index + '\n'
                add += 'D=D+A\n'
            add += '@R14\n'
            add += 'M=D\n'
            add += '@R13\n'
            add += 'D=M\n'
            add += '@R14\n'
            add += 'A=M\n'
            add += 'M=D\n'
            out = genericInstr + add
        else:
            raise(Exception("Neither push nor pop in 'writePushPop'"))
        self.outstream.write(out)

    def writeLabel(self, label):
        out = '(' + self.funcname + '$' + label + ')'
        self.outstream.write( out  + '\n' )

    def writeGoto(self, label):
        out = ['@' + self.funcname + '$' + label,
               '0; JMP']
        self.outstream.write( '\n'.join(out) + '\n' )

    def writeIf(self, label):
        out = CodeWriter.instructions['C_POP']
        out += '@' + self.funcname + '$' + label + '\n'
        out += 'D; JNE' + '\n'
        self.outstream.write( out )
        
    def writeCall(self, funcName, numArgs):

        pushInstr = CodeWriter.instructions['C_PUSH']
        ret_addr = 'ret_addr' + str(self.labelCounter)
        self.labelCounter += 1

        out = [
                # Push return address
                '@' + ret_addr + '\n',
                'D=A\n',
                pushInstr,
                # Push old lcl address
                '@LCL\n',
                'D=M\n',
                pushInstr,
                # Push old arg address
                '@ARG\n',
                'D=M\n',
                pushInstr,
                # Push old this address
                '@THIS\n',
                'D=M\n',
                pushInstr,
                # Push old that address
                '@THAT\n',
                'D=M\n',
                pushInstr,
                # Update lcl
                '@SP\n',
                'D=M\n',
                '@LCL\n',
                'M=D\n',
                # Update arg 
                '@SP\n',
                'D=M\n',
                '@5\n',
                'D=D-A\n',
                '@' + numArgs + '\n',
                'D=D-A\n',
                '@ARG\n',
                'M=D\n',
                # Instruct jump to function
                '@' + funcName + '\n',
                '0; JMP\n',
                '(' + ret_addr + ')\n'
            ]

        self.outstream.write( ''.join(out) )
        

    def writeReturn(self):

        def getFrame(offset):
            assert (offset <= 0)
            offset = str(-offset)
            out = '@frame\n'
            out += 'D=M\n'
            out += '@' + offset + '\n'
            out += 'D=D-A\n'
            out += 'A=D\n'
            out += 'D=M'

            return out
            
            
        out = [
            # Set frame variable
            '@LCL',
            'D=M',
            '@frame',
            'M=D',
            # Set ret variable
            getFrame(-5),
            '@ret',
            'M=D',
            # Reposition return value
            '@SP',
            'A=M-1',
            'D=M',
            '@ARG',
            'A=M',
            'M=D', 
            # Reposition stack pointer
            '@ARG',
            'D=M+1',
            '@SP',
            'M=D',
            # Reset 'that'
            getFrame(-1),
            '@THAT',
            'M=D',
            # Reset 'this'
            getFrame(-2),
            '@THIS',
            'M=D',
            # Reset 'arg'
            getFrame(-3),
            '@ARG',
            'M=D',
            # Reset 'lcl'
            getFrame(-4),
            '@LCL',
            'M=D',
            # Jump back to caller
            '@ret',
            'A=M',
            '0; JMP'
        ]
            
        self.outstream.write( '\n'.join(out) + '\n' )
            

    def writeFunction(self, funcName, numLocals):
        
        numLocals = int(numLocals)

        labelFunction = '(' + funcName + ')\n'
        reservingInstr = '@SP\nM=M+1\nA=M-1\nM=0\n'
            
        self.outstream.write ( labelFunction + (reservingInstr * numLocals) )
        self.funcName = funcName
        

    def close(self):
        self.outstream.close()

    def writeInit(self, bootstrap_needed):
        if not bootstrap_needed:
            return

        startup = [
            # Set stack pointer
            '@256',
            'D=A',
            '@SP',
            'M=D'
        ]

        self.outstream.write( '\n'.join(startup) + '\n' )
            

    def finalize(self):
        final = [
            '(TERM)',
            '@TERM',
            '0; JMP'
        ]
        
        self.outstream.write ( '\n'.join(final) + '\n' )


def echoHelp():
    print (
            tw.dedent(
            '''
                # Provide single vm file #
                Usage:   > ./VMtranslator file.vm
                Output:  file.asm

                # Or provide directory containing vm files #
                Usage:   > ./VMtranslator dir
                Output:  dir/file1.asm dir/file2.asm ...
            '''
            )
          )
    exit(1) # Exit with non-zero status

def getFiles(arg):
    files = []
    if arg.endswith('.vm'):
        files.append(arg)
        slashIndex = arg.rfind('/')
        resFile = arg[:slashIndex + 1] + arg[slashIndex + 1: -3] + '.asm'
    elif os.path.isdir(arg):
        for filename in os.listdir(arg):
            if filename.endswith('.vm'):
                files.append(arg + '/' + filename)
        base = arg[arg.rfind('/') + 1:]
        resFile = arg + '/' + base + '.asm'
    else:
        raise (Exception("Neiter .vm file nor directory"))
    streamsAndNames = map(lambda fileName: (open(fileName, 'r'), fileName,), files)
    return list(streamsAndNames), resFile

if __name__ == '__main__':
    print ("[Python VMtranslator]")
    numArgs = len(sys.argv)
    if numArgs != 2:
        echoHelp()         

    fileStreams, resName = getFiles(sys.argv[1])
    
    if len(fileStreams) == 0:
        echoHelp()

    # If we compile full-fledged program we add bootsrap code
    add_bootstrap_code = bool (len(fileStreams) > 1)
    if add_bootstrap_code:
        initMockFile = io.StringIO('call Sys.init 0\n')
        fileStreams = [(initMockFile, 'mock_init',)] + fileStreams

    codeWriter = CodeWriter(open(resName, 'w'))
    codeWriter.writeInit(add_bootstrap_code)
    for fileTuple in fileStreams:
        f, fileName = fileTuple

        print ("Processing {0}".format(fileName))

        codeWriter.setFileName(fileName)
        parser = Parser(f)
        while parser.hasMoreCommands():
            parser.advance()
            if parser.commandType() == 'C_ARITHMETIC':
                codeWriter.writeArithmetics(parser.getArg1())    
            elif parser.commandType() in ['C_PUSH', 'C_POP']:
                codeWriter.writePushPop(parser.commandType(), \
                                        parser.getArg1(), parser.getArg2())
            elif parser.commandType() == 'C_LABEL':
                codeWriter.writeLabel(parser.getArg1())
            elif parser.commandType() == 'C_GOTO':
                codeWriter.writeGoto(parser.getArg1())
            elif parser.commandType() == 'C_IF':
                codeWriter.writeIf(parser.getArg1())
            elif parser.commandType() == 'C_FUNCTION':
                codeWriter.writeFunction(parser.getArg1(), parser.getArg2())
            elif parser.commandType() == 'C_CALL':
                codeWriter.writeCall(parser.getArg1(), parser.getArg2())
            elif parser.commandType() == 'C_RETURN':
                codeWriter.writeReturn()
            else:
                raise(Exception("Parser returns unknowk command type"))
        f.close()

    codeWriter.finalize()
    codeWriter.close()
                
            

7e0e13db6e295f4dfc43b0de943d17510dd22c9b
<mask token>


class CodeWriter(object):
    <mask token>
    <mask token>
    <mask token>
    for c in instructions:
        instructions[c] = '\n'.join(instructions[c]) + '\n'

    def __init__(self, outstream):
        self.outstream = outstream
        self.labelCounter = 0

    def setFileName(self, newName):
        ls = newName.split('/')
        self.filename = ls[-1]
        self.funcname = 'global'

    def writeArithmetics(self, command):
        out = CodeWriter.instructions[command]
        if command in ['gt', 'lt', 'eq']:
            out = out.format(self.labelCounter)
            self.labelCounter += 1
        self.outstream.write(out)

    def writePushPop(self, command, segment, index):
        genericInstr = CodeWriter.instructions[command]
        if command == 'C_PUSH':
            if segment == 'constant':
                add = '@' + index + '\n'
                add += 'D=A\n'
            else:
                add = ''
                if segment in CodeWriter.segment_symbols:
                    add += '@' + CodeWriter.segment_symbols[segment] + '\n'
                    add += 'D=M\n'
                elif segment in CodeWriter.segment_addrs:
                    add += '@' + CodeWriter.segment_addrs[segment] + '\n'
                    add += 'D=A\n'
                elif segment == 'static':
                    add += '@' + self.filename + '.' + index + '\n'
                    add += 'D=A\n'
                if segment != 'static':
                    add += '@' + index + '\n'
                    add += 'D=D+A\n'
                add += 'A=D\n'
                add += 'D=M\n'
            out = add + genericInstr
        elif command == 'C_POP':
            add = '@R13\n'
            add += 'M=D\n'
            if segment in CodeWriter.segment_symbols:
                add += '@' + CodeWriter.segment_symbols[segment] + '\n'
                add += 'D=M\n'
            elif segment in CodeWriter.segment_addrs:
                add += '@' + CodeWriter.segment_addrs[segment] + '\n'
                add += 'D=A\n'
            elif segment == 'static':
                add += '@' + self.filename + '.' + index + '\n'
                add += 'D=A\n'
            if segment != 'static':
                add += '@' + index + '\n'
                add += 'D=D+A\n'
            add += '@R14\n'
            add += 'M=D\n'
            add += '@R13\n'
            add += 'D=M\n'
            add += '@R14\n'
            add += 'A=M\n'
            add += 'M=D\n'
            out = genericInstr + add
        else:
            raise Exception("Neither push nor pop in 'writePushPop'")
        self.outstream.write(out)

    def writeLabel(self, label):
        out = '(' + self.funcname + '$' + label + ')'
        self.outstream.write(out + '\n')
    <mask token>

    def writeIf(self, label):
        out = CodeWriter.instructions['C_POP']
        out += '@' + self.funcname + '$' + label + '\n'
        out += 'D; JNE' + '\n'
        self.outstream.write(out)

    def writeCall(self, funcName, numArgs):
        pushInstr = CodeWriter.instructions['C_PUSH']
        ret_addr = 'ret_addr' + str(self.labelCounter)
        self.labelCounter += 1
        out = ['@' + ret_addr + '\n', 'D=A\n', pushInstr, '@LCL\n', 'D=M\n',
            pushInstr, '@ARG\n', 'D=M\n', pushInstr, '@THIS\n', 'D=M\n',
            pushInstr, '@THAT\n', 'D=M\n', pushInstr, '@SP\n', 'D=M\n',
            '@LCL\n', 'M=D\n', '@SP\n', 'D=M\n', '@5\n', 'D=D-A\n', '@' +
            numArgs + '\n', 'D=D-A\n', '@ARG\n', 'M=D\n', '@' + funcName +
            '\n', '0; JMP\n', '(' + ret_addr + ')\n']
        self.outstream.write(''.join(out))

    def writeReturn(self):

        def getFrame(offset):
            assert offset <= 0
            offset = str(-offset)
            out = '@frame\n'
            out += 'D=M\n'
            out += '@' + offset + '\n'
            out += 'D=D-A\n'
            out += 'A=D\n'
            out += 'D=M'
            return out
        out = ['@LCL', 'D=M', '@frame', 'M=D', getFrame(-5), '@ret', 'M=D',
            '@SP', 'A=M-1', 'D=M', '@ARG', 'A=M', 'M=D', '@ARG', 'D=M+1',
            '@SP', 'M=D', getFrame(-1), '@THAT', 'M=D', getFrame(-2),
            '@THIS', 'M=D', getFrame(-3), '@ARG', 'M=D', getFrame(-4),
            '@LCL', 'M=D', '@ret', 'A=M', '0; JMP']
        self.outstream.write('\n'.join(out) + '\n')

    def writeFunction(self, funcName, numLocals):
        numLocals = int(numLocals)
        labelFunction = '(' + funcName + ')\n'
        reservingInstr = '@SP\nM=M+1\nA=M-1\nM=0\n'
        self.outstream.write(labelFunction + reservingInstr * numLocals)
        self.funcName = funcName

    def close(self):
        self.outstream.close()

    def writeInit(self, bootstrap_needed):
        if not bootstrap_needed:
            return
        startup = ['@256', 'D=A', '@SP', 'M=D']
        self.outstream.write('\n'.join(startup) + '\n')

    def finalize(self):
        final = ['(TERM)', '@TERM', '0; JMP']
        self.outstream.write('\n'.join(final) + '\n')


<mask token>

<mask token>


class Parser(object):
    <mask token>
    <mask token>

    def _getNextCommand(self):
        stream = self.stream
        while True:
            line = stream.readline()
            if line == '':
                return Command('')
            line = line.strip()
            comment_index = line.find('//')
            if comment_index != -1:
                line = line[:comment_index]
            if line != '':
                break
        return Command(line)
    <mask token>
    <mask token>
    <mask token>

    def getArg2(self):
        c = self.curCommand
        return hasattr(c, 'arg2') and c.arg2 or 'NO_ARG'


class CodeWriter(object):
    segment_symbols = {'local': 'LCL', 'argument': 'ARG', 'this': 'THIS',
        'that': 'THAT'}
    segment_addrs = {'pointer': '3', 'temp': '5'}
    instructions = {'add': ['@SP', 'M=M-1', 'A=M', 'D=M', 'A=A-1', 'D=D+M',
        '@SP', 'A=M-1', 'M=D'], 'eq': ['@SP', 'M=M-1', 'A=M', 'D=M',
        'A=A-1', 'D=D-M', '@ELSE{0}', 'D; JEQ', '@SP', 'A=M-1', 'M=0',
        '@ENDELSE{0}', '0; JMP', '(ELSE{0})', '@SP', 'A=M-1', 'M=-1',
        '(ENDELSE{0})'], 'lt': ['@SP', 'M=M-1', 'A=M', 'D=M', 'A=A-1',
        'D=D-M', '@ELSE{0}', 'D; JGT', '@SP', 'A=M-1', 'M=0', '@ENDELSE{0}',
        '0; JMP', '(ELSE{0})', '@SP', 'A=M-1', 'M=-1', '(ENDELSE{0})'],
        'gt': ['@SP', 'M=M-1', 'A=M', 'D=M', 'A=A-1', 'D=D-M', '@ELSE{0}',
        'D; JLT', '@SP', 'A=M-1', 'M=0', '@ENDELSE{0}', '0; JMP',
        '(ELSE{0})', '@SP', 'A=M-1', 'M=-1', '(ENDELSE{0})'], 'sub': ['@SP',
        'M=M-1', 'A=M', 'D=M', 'A=A-1', 'D=M-D', '@SP', 'A=M-1', 'M=D'],
        'neg': ['@SP', 'A=M', 'A=A-1', 'M=-M'], 'not': ['@SP', 'A=M',
        'A=A-1', 'M=!M'], 'and': ['@SP', 'M=M-1', 'A=M', 'D=M', 'A=A-1',
        'D=D&M', '@SP', 'A=M-1', 'M=D'], 'or': ['@SP', 'M=M-1', 'A=M',
        'D=M', 'A=A-1', 'D=D|M', '@SP', 'A=M-1', 'M=D'], 'C_PUSH': ['@SP',
        'M=M+1', 'A=M-1', 'M=D'], 'C_POP': ['@SP', 'M=M-1', 'A=M', 'D=M']}
    for c in instructions:
        instructions[c] = '\n'.join(instructions[c]) + '\n'

    def __init__(self, outstream):
        self.outstream = outstream
        self.labelCounter = 0

    def setFileName(self, newName):
        ls = newName.split('/')
        self.filename = ls[-1]
        self.funcname = 'global'

    def writeArithmetics(self, command):
        out = CodeWriter.instructions[command]
        if command in ['gt', 'lt', 'eq']:
            out = out.format(self.labelCounter)
            self.labelCounter += 1
        self.outstream.write(out)

    def writePushPop(self, command, segment, index):
        genericInstr = CodeWriter.instructions[command]
        if command == 'C_PUSH':
            if segment == 'constant':
                add = '@' + index + '\n'
                add += 'D=A\n'
            else:
                add = ''
                if segment in CodeWriter.segment_symbols:
                    add += '@' + CodeWriter.segment_symbols[segment] + '\n'
                    add += 'D=M\n'
                elif segment in CodeWriter.segment_addrs:
                    add += '@' + CodeWriter.segment_addrs[segment] + '\n'
                    add += 'D=A\n'
                elif segment == 'static':
                    add += '@' + self.filename + '.' + index + '\n'
                    add += 'D=A\n'
                if segment != 'static':
                    add += '@' + index + '\n'
                    add += 'D=D+A\n'
                add += 'A=D\n'
                add += 'D=M\n'
            out = add + genericInstr
        elif command == 'C_POP':
            add = '@R13\n'
            add += 'M=D\n'
            if segment in CodeWriter.segment_symbols:
                add += '@' + CodeWriter.segment_symbols[segment] + '\n'
                add += 'D=M\n'
            elif segment in CodeWriter.segment_addrs:
                add += '@' + CodeWriter.segment_addrs[segment] + '\n'
                add += 'D=A\n'
            elif segment == 'static':
                add += '@' + self.filename + '.' + index + '\n'
                add += 'D=A\n'
            if segment != 'static':
                add += '@' + index + '\n'
                add += 'D=D+A\n'
            add += '@R14\n'
            add += 'M=D\n'
            add += '@R13\n'
            add += 'D=M\n'
            add += '@R14\n'
            add += 'A=M\n'
            add += 'M=D\n'
            out = genericInstr + add
        else:
            raise Exception("Neither push nor pop in 'writePushPop'")
        self.outstream.write(out)

    def writeLabel(self, label):
        out = '(' + self.funcname + '$' + label + ')'
        self.outstream.write(out + '\n')

    def writeGoto(self, label):
        out = ['@' + self.funcname + '$' + label, '0; JMP']
        self.outstream.write('\n'.join(out) + '\n')

    def writeIf(self, label):
        out = CodeWriter.instructions['C_POP']
        out += '@' + self.funcname + '$' + label + '\n'
        out += 'D; JNE' + '\n'
        self.outstream.write(out)

    def writeCall(self, funcName, numArgs):
        pushInstr = CodeWriter.instructions['C_PUSH']
        ret_addr = 'ret_addr' + str(self.labelCounter)
        self.labelCounter += 1
        out = ['@' + ret_addr + '\n', 'D=A\n', pushInstr, '@LCL\n', 'D=M\n',
            pushInstr, '@ARG\n', 'D=M\n', pushInstr, '@THIS\n', 'D=M\n',
            pushInstr, '@THAT\n', 'D=M\n', pushInstr, '@SP\n', 'D=M\n',
            '@LCL\n', 'M=D\n', '@SP\n', 'D=M\n', '@5\n', 'D=D-A\n', '@' +
            numArgs + '\n', 'D=D-A\n', '@ARG\n', 'M=D\n', '@' + funcName +
            '\n', '0; JMP\n', '(' + ret_addr + ')\n']
        self.outstream.write(''.join(out))

    def writeReturn(self):

        def getFrame(offset):
            assert offset <= 0
            offset = str(-offset)
            out = '@frame\n'
            out += 'D=M\n'
            out += '@' + offset + '\n'
            out += 'D=D-A\n'
            out += 'A=D\n'
            out += 'D=M'
            return out
        out = ['@LCL', 'D=M', '@frame', 'M=D', getFrame(-5), '@ret', 'M=D',
            '@SP', 'A=M-1', 'D=M', '@ARG', 'A=M', 'M=D', '@ARG', 'D=M+1',
            '@SP', 'M=D', getFrame(-1), '@THAT', 'M=D', getFrame(-2),
            '@THIS', 'M=D', getFrame(-3), '@ARG', 'M=D', getFrame(-4),
            '@LCL', 'M=D', '@ret', 'A=M', '0; JMP']
        self.outstream.write('\n'.join(out) + '\n')

    def writeFunction(self, funcName, numLocals):
        numLocals = int(numLocals)
        labelFunction = '(' + funcName + ')\n'
        reservingInstr = '@SP\nM=M+1\nA=M-1\nM=0\n'
        self.outstream.write(labelFunction + reservingInstr * numLocals)
        self.funcName = funcName

    def close(self):
        self.outstream.close()

    def writeInit(self, bootstrap_needed):
        if not bootstrap_needed:
            return
        startup = ['@256', 'D=A', '@SP', 'M=D']
        self.outstream.write('\n'.join(startup) + '\n')

    def finalize(self):
        final = ['(TERM)', '@TERM', '0; JMP']
        self.outstream.write('\n'.join(final) + '\n')


<mask token>

<mask token>


class Command(object):
    <mask token>
    <mask token>

    def __init__(self, line):
        if len(line) == 0:
            self.valid = False
            return
        self.valid = True
        tokens = line.split()
        command = Command._getCommandT(tokens)
        self.type = command
        if command == 'C_ARITHMETIC':
            self.arg1 = tokens[0]
        if command in ['C_POP', 'C_PUSH', 'C_LABEL', 'C_GOTO', 'C_IF',
            'C_FUNCTION', 'C_CALL']:
            self.arg1 = tokens[1]
        if command in ['C_POP', 'C_PUSH', 'C_FUNCTION', 'C_CALL']:
            self.arg2 = tokens[2]


class Parser(object):

    def __init__(self, filestream):
        self.stream = filestream
        self.curCommand = None
        self.nextCommand = self._getNextCommand()

    def hasMoreCommands(self):
        """ Are there any more commands? """
        return self.nextCommand.valid

    def _getNextCommand(self):
        stream = self.stream
        while True:
            line = stream.readline()
            if line == '':
                return Command('')
            line = line.strip()
            comment_index = line.find('//')
            if comment_index != -1:
                line = line[:comment_index]
            if line != '':
                break
        return Command(line)

    def advance(self):
        """ 
            Reads the next command from input
            and makes it current command. Should
            be called only if 'hasMoreCommands'
        """
        self.curCommand = self.nextCommand
        self.nextCommand = self._getNextCommand()

    def commandType(self):
        return self.curCommand.type

    def getArg1(self):
        c = self.curCommand
        return hasattr(c, 'arg1') and c.arg1 or 'NO_ARG'

    def getArg2(self):
        c = self.curCommand
        return hasattr(c, 'arg2') and c.arg2 or 'NO_ARG'


class CodeWriter(object):
    segment_symbols = {'local': 'LCL', 'argument': 'ARG', 'this': 'THIS',
        'that': 'THAT'}
    segment_addrs = {'pointer': '3', 'temp': '5'}
    instructions = {'add': ['@SP', 'M=M-1', 'A=M', 'D=M', 'A=A-1', 'D=D+M',
        '@SP', 'A=M-1', 'M=D'], 'eq': ['@SP', 'M=M-1', 'A=M', 'D=M',
        'A=A-1', 'D=D-M', '@ELSE{0}', 'D; JEQ', '@SP', 'A=M-1', 'M=0',
        '@ENDELSE{0}', '0; JMP', '(ELSE{0})', '@SP', 'A=M-1', 'M=-1',
        '(ENDELSE{0})'], 'lt': ['@SP', 'M=M-1', 'A=M', 'D=M', 'A=A-1',
        'D=D-M', '@ELSE{0}', 'D; JGT', '@SP', 'A=M-1', 'M=0', '@ENDELSE{0}',
        '0; JMP', '(ELSE{0})', '@SP', 'A=M-1', 'M=-1', '(ENDELSE{0})'],
        'gt': ['@SP', 'M=M-1', 'A=M', 'D=M', 'A=A-1', 'D=D-M', '@ELSE{0}',
        'D; JLT', '@SP', 'A=M-1', 'M=0', '@ENDELSE{0}', '0; JMP',
        '(ELSE{0})', '@SP', 'A=M-1', 'M=-1', '(ENDELSE{0})'], 'sub': ['@SP',
        'M=M-1', 'A=M', 'D=M', 'A=A-1', 'D=M-D', '@SP', 'A=M-1', 'M=D'],
        'neg': ['@SP', 'A=M', 'A=A-1', 'M=-M'], 'not': ['@SP', 'A=M',
        'A=A-1', 'M=!M'], 'and': ['@SP', 'M=M-1', 'A=M', 'D=M', 'A=A-1',
        'D=D&M', '@SP', 'A=M-1', 'M=D'], 'or': ['@SP', 'M=M-1', 'A=M',
        'D=M', 'A=A-1', 'D=D|M', '@SP', 'A=M-1', 'M=D'], 'C_PUSH': ['@SP',
        'M=M+1', 'A=M-1', 'M=D'], 'C_POP': ['@SP', 'M=M-1', 'A=M', 'D=M']}
    for c in instructions:
        instructions[c] = '\n'.join(instructions[c]) + '\n'

    def __init__(self, outstream):
        self.outstream = outstream
        self.labelCounter = 0

    def setFileName(self, newName):
        ls = newName.split('/')
        self.filename = ls[-1]
        self.funcname = 'global'

    def writeArithmetics(self, command):
        out = CodeWriter.instructions[command]
        if command in ['gt', 'lt', 'eq']:
            out = out.format(self.labelCounter)
            self.labelCounter += 1
        self.outstream.write(out)

    def writePushPop(self, command, segment, index):
        genericInstr = CodeWriter.instructions[command]
        if command == 'C_PUSH':
            if segment == 'constant':
                add = '@' + index + '\n'
                add += 'D=A\n'
            else:
                add = ''
                if segment in CodeWriter.segment_symbols:
                    add += '@' + CodeWriter.segment_symbols[segment] + '\n'
                    add += 'D=M\n'
                elif segment in CodeWriter.segment_addrs:
                    add += '@' + CodeWriter.segment_addrs[segment] + '\n'
                    add += 'D=A\n'
                elif segment == 'static':
                    add += '@' + self.filename + '.' + index + '\n'
                    add += 'D=A\n'
                if segment != 'static':
                    add += '@' + index + '\n'
                    add += 'D=D+A\n'
                add += 'A=D\n'
                add += 'D=M\n'
            out = add + genericInstr
        elif command == 'C_POP':
            add = '@R13\n'
            add += 'M=D\n'
            if segment in CodeWriter.segment_symbols:
                add += '@' + CodeWriter.segment_symbols[segment] + '\n'
                add += 'D=M\n'
            elif segment in CodeWriter.segment_addrs:
                add += '@' + CodeWriter.segment_addrs[segment] + '\n'
                add += 'D=A\n'
            elif segment == 'static':
                add += '@' + self.filename + '.' + index + '\n'
                add += 'D=A\n'
            if segment != 'static':
                add += '@' + index + '\n'
                add += 'D=D+A\n'
            add += '@R14\n'
            add += 'M=D\n'
            add += '@R13\n'
            add += 'D=M\n'
            add += '@R14\n'
            add += 'A=M\n'
            add += 'M=D\n'
            out = genericInstr + add
        else:
            raise Exception("Neither push nor pop in 'writePushPop'")
        self.outstream.write(out)

    def writeLabel(self, label):
        out = '(' + self.funcname + '$' + label + ')'
        self.outstream.write(out + '\n')

    def writeGoto(self, label):
        out = ['@' + self.funcname + '$' + label, '0; JMP']
        self.outstream.write('\n'.join(out) + '\n')

    def writeIf(self, label):
        out = CodeWriter.instructions['C_POP']
        out += '@' + self.funcname + '$' + label + '\n'
        out += 'D; JNE' + '\n'
        self.outstream.write(out)

    def writeCall(self, funcName, numArgs):
        pushInstr = CodeWriter.instructions['C_PUSH']
        ret_addr = 'ret_addr' + str(self.labelCounter)
        self.labelCounter += 1
        out = ['@' + ret_addr + '\n', 'D=A\n', pushInstr, '@LCL\n', 'D=M\n',
            pushInstr, '@ARG\n', 'D=M\n', pushInstr, '@THIS\n', 'D=M\n',
            pushInstr, '@THAT\n', 'D=M\n', pushInstr, '@SP\n', 'D=M\n',
            '@LCL\n', 'M=D\n', '@SP\n', 'D=M\n', '@5\n', 'D=D-A\n', '@' +
            numArgs + '\n', 'D=D-A\n', '@ARG\n', 'M=D\n', '@' + funcName +
            '\n', '0; JMP\n', '(' + ret_addr + ')\n']
        self.outstream.write(''.join(out))

    def writeReturn(self):

        def getFrame(offset):
            assert offset <= 0
            offset = str(-offset)
            out = '@frame\n'
            out += 'D=M\n'
            out += '@' + offset + '\n'
            out += 'D=D-A\n'
            out += 'A=D\n'
            out += 'D=M'
            return out
        out = ['@LCL', 'D=M', '@frame', 'M=D', getFrame(-5), '@ret', 'M=D',
            '@SP', 'A=M-1', 'D=M', '@ARG', 'A=M', 'M=D', '@ARG', 'D=M+1',
            '@SP', 'M=D', getFrame(-1), '@THAT', 'M=D', getFrame(-2),
            '@THIS', 'M=D', getFrame(-3), '@ARG', 'M=D', getFrame(-4),
            '@LCL', 'M=D', '@ret', 'A=M', '0; JMP']
        self.outstream.write('\n'.join(out) + '\n')

    def writeFunction(self, funcName, numLocals):
        numLocals = int(numLocals)
        labelFunction = '(' + funcName + ')\n'
        reservingInstr = '@SP\nM=M+1\nA=M-1\nM=0\n'
        self.outstream.write(labelFunction + reservingInstr * numLocals)
        self.funcName = funcName

    def close(self):
        self.outstream.close()

    def writeInit(self, bootstrap_needed):
        if not bootstrap_needed:
            return
        startup = ['@256', 'D=A', '@SP', 'M=D']
        self.outstream.write('\n'.join(startup) + '\n')

    def finalize(self):
        final = ['(TERM)', '@TERM', '0; JMP']
        self.outstream.write('\n'.join(final) + '\n')


<mask token>

import textwrap as tw
import io
import sys
import os


class Command(object):
    commandTypes = {'C_ARITHMETIC': lambda toks: len(toks) == 1 and toks[0] in
        CodeWriter.instructions, 'C_PUSH': lambda toks: len(toks) == 3 and 
        toks[0] == 'push', 'C_POP': lambda toks: len(toks) == 3 and toks[0] ==
        'pop', 'C_LABEL': lambda toks: len(toks) == 2 and toks[0] ==
        'label', 'C_GOTO': lambda toks: len(toks) == 2 and toks[0] ==
        'goto', 'C_IF': lambda toks: len(toks) == 2 and toks[0] ==
        'if-goto', 'C_FUNCTION': lambda toks: len(toks) == 3 and toks[0] ==
        'function', 'C_RETURN': lambda toks: len(toks) == 1 and toks[0] ==
        'return', 'C_CALL': lambda toks: len(toks) == 3 and toks[0] == 'call'}

    @staticmethod
    def _getCommandT(tokens):
        for c, tester in Command.commandTypes.items():
            if tester(tokens):
                return c
        raise Exception('Not any of 7 recognizable command types ' + str(
            tokens))
        return None

    def __init__(self, line):
        if len(line) == 0:
            self.valid = False
            return
        self.valid = True
        tokens = line.split()
        command = Command._getCommandT(tokens)
        self.type = command
        if command == 'C_ARITHMETIC':
            self.arg1 = tokens[0]
        if command in ['C_POP', 'C_PUSH', 'C_LABEL', 'C_GOTO', 'C_IF',
            'C_FUNCTION', 'C_CALL']:
            self.arg1 = tokens[1]
        if command in ['C_POP', 'C_PUSH', 'C_FUNCTION', 'C_CALL']:
            self.arg2 = tokens[2]


class Parser(object):

    def __init__(self, filestream):
        self.stream = filestream
        self.curCommand = None
        self.nextCommand = self._getNextCommand()

    def hasMoreCommands(self):
        """ Are there any more commands? """
        return self.nextCommand.valid

    def _getNextCommand(self):
        stream = self.stream
        while True:
            line = stream.readline()
            if line == '':
                return Command('')
            line = line.strip()
            comment_index = line.find('//')
            if comment_index != -1:
                line = line[:comment_index]
            if line != '':
                break
        return Command(line)

    def advance(self):
        """ 
            Reads the next command from input
            and makes it current command. Should
            be called only if 'hasMoreCommands'
        """
        self.curCommand = self.nextCommand
        self.nextCommand = self._getNextCommand()

    def commandType(self):
        return self.curCommand.type

    def getArg1(self):
        c = self.curCommand
        return hasattr(c, 'arg1') and c.arg1 or 'NO_ARG'

    def getArg2(self):
        c = self.curCommand
        return hasattr(c, 'arg2') and c.arg2 or 'NO_ARG'


class CodeWriter(object):
    segment_symbols = {'local': 'LCL', 'argument': 'ARG', 'this': 'THIS',
        'that': 'THAT'}
    segment_addrs = {'pointer': '3', 'temp': '5'}
    instructions = {'add': ['@SP', 'M=M-1', 'A=M', 'D=M', 'A=A-1', 'D=D+M',
        '@SP', 'A=M-1', 'M=D'], 'eq': ['@SP', 'M=M-1', 'A=M', 'D=M',
        'A=A-1', 'D=D-M', '@ELSE{0}', 'D; JEQ', '@SP', 'A=M-1', 'M=0',
        '@ENDELSE{0}', '0; JMP', '(ELSE{0})', '@SP', 'A=M-1', 'M=-1',
        '(ENDELSE{0})'], 'lt': ['@SP', 'M=M-1', 'A=M', 'D=M', 'A=A-1',
        'D=D-M', '@ELSE{0}', 'D; JGT', '@SP', 'A=M-1', 'M=0', '@ENDELSE{0}',
        '0; JMP', '(ELSE{0})', '@SP', 'A=M-1', 'M=-1', '(ENDELSE{0})'],
        'gt': ['@SP', 'M=M-1', 'A=M', 'D=M', 'A=A-1', 'D=D-M', '@ELSE{0}',
        'D; JLT', '@SP', 'A=M-1', 'M=0', '@ENDELSE{0}', '0; JMP',
        '(ELSE{0})', '@SP', 'A=M-1', 'M=-1', '(ENDELSE{0})'], 'sub': ['@SP',
        'M=M-1', 'A=M', 'D=M', 'A=A-1', 'D=M-D', '@SP', 'A=M-1', 'M=D'],
        'neg': ['@SP', 'A=M', 'A=A-1', 'M=-M'], 'not': ['@SP', 'A=M',
        'A=A-1', 'M=!M'], 'and': ['@SP', 'M=M-1', 'A=M', 'D=M', 'A=A-1',
        'D=D&M', '@SP', 'A=M-1', 'M=D'], 'or': ['@SP', 'M=M-1', 'A=M',
        'D=M', 'A=A-1', 'D=D|M', '@SP', 'A=M-1', 'M=D'], 'C_PUSH': ['@SP',
        'M=M+1', 'A=M-1', 'M=D'], 'C_POP': ['@SP', 'M=M-1', 'A=M', 'D=M']}
    for c in instructions:
        instructions[c] = '\n'.join(instructions[c]) + '\n'

    def __init__(self, outstream):
        self.outstream = outstream
        self.labelCounter = 0

    def setFileName(self, newName):
        ls = newName.split('/')
        self.filename = ls[-1]
        self.funcname = 'global'

    def writeArithmetics(self, command):
        out = CodeWriter.instructions[command]
        if command in ['gt', 'lt', 'eq']:
            out = out.format(self.labelCounter)
            self.labelCounter += 1
        self.outstream.write(out)

    def writePushPop(self, command, segment, index):
        genericInstr = CodeWriter.instructions[command]
        if command == 'C_PUSH':
            if segment == 'constant':
                add = '@' + index + '\n'
                add += 'D=A\n'
            else:
                add = ''
                if segment in CodeWriter.segment_symbols:
                    add += '@' + CodeWriter.segment_symbols[segment] + '\n'
                    add += 'D=M\n'
                elif segment in CodeWriter.segment_addrs:
                    add += '@' + CodeWriter.segment_addrs[segment] + '\n'
                    add += 'D=A\n'
                elif segment == 'static':
                    add += '@' + self.filename + '.' + index + '\n'
                    add += 'D=A\n'
                if segment != 'static':
                    add += '@' + index + '\n'
                    add += 'D=D+A\n'
                add += 'A=D\n'
                add += 'D=M\n'
            out = add + genericInstr
        elif command == 'C_POP':
            add = '@R13\n'
            add += 'M=D\n'
            if segment in CodeWriter.segment_symbols:
                add += '@' + CodeWriter.segment_symbols[segment] + '\n'
                add += 'D=M\n'
            elif segment in CodeWriter.segment_addrs:
                add += '@' + CodeWriter.segment_addrs[segment] + '\n'
                add += 'D=A\n'
            elif segment == 'static':
                add += '@' + self.filename + '.' + index + '\n'
                add += 'D=A\n'
            if segment != 'static':
                add += '@' + index + '\n'
                add += 'D=D+A\n'
            add += '@R14\n'
            add += 'M=D\n'
            add += '@R13\n'
            add += 'D=M\n'
            add += '@R14\n'
            add += 'A=M\n'
            add += 'M=D\n'
            out = genericInstr + add
        else:
            raise Exception("Neither push nor pop in 'writePushPop'")
        self.outstream.write(out)

    def writeLabel(self, label):
        out = '(' + self.funcname + '$' + label + ')'
        self.outstream.write(out + '\n')

    def writeGoto(self, label):
        out = ['@' + self.funcname + '$' + label, '0; JMP']
        self.outstream.write('\n'.join(out) + '\n')

    def writeIf(self, label):
        out = CodeWriter.instructions['C_POP']
        out += '@' + self.funcname + '$' + label + '\n'
        out += 'D; JNE' + '\n'
        self.outstream.write(out)

    def writeCall(self, funcName, numArgs):
        pushInstr = CodeWriter.instructions['C_PUSH']
        ret_addr = 'ret_addr' + str(self.labelCounter)
        self.labelCounter += 1
        out = ['@' + ret_addr + '\n', 'D=A\n', pushInstr, '@LCL\n', 'D=M\n',
            pushInstr, '@ARG\n', 'D=M\n', pushInstr, '@THIS\n', 'D=M\n',
            pushInstr, '@THAT\n', 'D=M\n', pushInstr, '@SP\n', 'D=M\n',
            '@LCL\n', 'M=D\n', '@SP\n', 'D=M\n', '@5\n', 'D=D-A\n', '@' +
            numArgs + '\n', 'D=D-A\n', '@ARG\n', 'M=D\n', '@' + funcName +
            '\n', '0; JMP\n', '(' + ret_addr + ')\n']
        self.outstream.write(''.join(out))

    def writeReturn(self):

        def getFrame(offset):
            assert offset <= 0
            offset = str(-offset)
            out = '@frame\n'
            out += 'D=M\n'
            out += '@' + offset + '\n'
            out += 'D=D-A\n'
            out += 'A=D\n'
            out += 'D=M'
            return out
        out = ['@LCL', 'D=M', '@frame', 'M=D', getFrame(-5), '@ret', 'M=D',
            '@SP', 'A=M-1', 'D=M', '@ARG', 'A=M', 'M=D', '@ARG', 'D=M+1',
            '@SP', 'M=D', getFrame(-1), '@THAT', 'M=D', getFrame(-2),
            '@THIS', 'M=D', getFrame(-3), '@ARG', 'M=D', getFrame(-4),
            '@LCL', 'M=D', '@ret', 'A=M', '0; JMP']
        self.outstream.write('\n'.join(out) + '\n')

    def writeFunction(self, funcName, numLocals):
        numLocals = int(numLocals)
        labelFunction = '(' + funcName + ')\n'
        reservingInstr = '@SP\nM=M+1\nA=M-1\nM=0\n'
        self.outstream.write(labelFunction + reservingInstr * numLocals)
        self.funcName = funcName

    def close(self):
        self.outstream.close()

    def writeInit(self, bootstrap_needed):
        if not bootstrap_needed:
            return
        startup = ['@256', 'D=A', '@SP', 'M=D']
        self.outstream.write('\n'.join(startup) + '\n')

    def finalize(self):
        final = ['(TERM)', '@TERM', '0; JMP']
        self.outstream.write('\n'.join(final) + '\n')


def echoHelp():
    print(tw.dedent(
        """
                # Provide single vm file #
                Usage:   > ./VMtranslator file.vm
                Output:  file.asm

                # Or provide directory containing vm files #
                Usage:   > ./VMtranslator dir
                Output:  dir/file1.asm dir/file2.asm ...
            """
        ))
    exit(1)


def getFiles(arg):
    files = []
    if arg.endswith('.vm'):
        files.append(arg)
        slashIndex = arg.rfind('/')
        resFile = arg[:slashIndex + 1] + arg[slashIndex + 1:-3] + '.asm'
    elif os.path.isdir(arg):
        for filename in os.listdir(arg):
            if filename.endswith('.vm'):
                files.append(arg + '/' + filename)
        base = arg[arg.rfind('/') + 1:]
        resFile = arg + '/' + base + '.asm'
    else:
        raise Exception('Neiter .vm file nor directory')
    streamsAndNames = map(lambda fileName: (open(fileName, 'r'), fileName),
        files)
    return list(streamsAndNames), resFile


if __name__ == '__main__':
    print('[Python VMtranslator]')
    numArgs = len(sys.argv)
    if numArgs != 2:
        echoHelp()
    fileStreams, resName = getFiles(sys.argv[1])
    if len(fileStreams) == 0:
        echoHelp()
    add_bootstrap_code = bool(len(fileStreams) > 1)
    if add_bootstrap_code:
        initMockFile = io.StringIO('call Sys.init 0\n')
        fileStreams = [(initMockFile, 'mock_init')] + fileStreams
    codeWriter = CodeWriter(open(resName, 'w'))
    codeWriter.writeInit(add_bootstrap_code)
    for fileTuple in fileStreams:
        f, fileName = fileTuple
        print('Processing {0}'.format(fileName))
        codeWriter.setFileName(fileName)
        parser = Parser(f)
        while parser.hasMoreCommands():
            parser.advance()
            if parser.commandType() == 'C_ARITHMETIC':
                codeWriter.writeArithmetics(parser.getArg1())
            elif parser.commandType() in ['C_PUSH', 'C_POP']:
                codeWriter.writePushPop(parser.commandType(), parser.
                    getArg1(), parser.getArg2())
            elif parser.commandType() == 'C_LABEL':
                codeWriter.writeLabel(parser.getArg1())
            elif parser.commandType() == 'C_GOTO':
                codeWriter.writeGoto(parser.getArg1())
            elif parser.commandType() == 'C_IF':
                codeWriter.writeIf(parser.getArg1())
            elif parser.commandType() == 'C_FUNCTION':
                codeWriter.writeFunction(parser.getArg1(), parser.getArg2())
            elif parser.commandType() == 'C_CALL':
                codeWriter.writeCall(parser.getArg1(), parser.getArg2())
            elif parser.commandType() == 'C_RETURN':
                codeWriter.writeReturn()
            else:
                raise Exception('Parser returns unknowk command type')
        f.close()
    codeWriter.finalize()
    codeWriter.close()

#!/usr/bin/python3

import textwrap as tw
import io
import sys
import os

class Command(object):

    commandTypes = {'C_ARITHMETIC': lambda toks:
                        len(toks) == 1 and toks[0] in CodeWriter.instructions,
                    'C_PUSH': lambda toks: len(toks) == 3 and \
                        toks[0] == 'push',
                    'C_POP': lambda toks: len(toks) == 3 and \
                        toks[0] == 'pop',
                    'C_LABEL': lambda toks: len(toks) == 2 and \
                        toks[0] == 'label',
                    'C_GOTO': lambda toks: len(toks) == 2 and \
                        toks[0] == 'goto',
                    'C_IF': lambda toks: len(toks) == 2 and \
                        toks[0] == 'if-goto',
                    'C_FUNCTION': lambda toks: len(toks) == 3 and \
                        toks[0] == 'function',
                    'C_RETURN': lambda toks: len(toks) == 1 and \
                        toks[0] == 'return',
                    'C_CALL': lambda toks: len(toks) == 3 and \
                        toks[0] == 'call'}
     
    @staticmethod
    def _getCommandT(tokens):
        for c, tester in Command.commandTypes.items():
            if tester(tokens):
                return c
        raise (Exception("Not any of 7 recognizable command types " +  str(tokens)))
        return None

    def __init__(self, line):
        if len(line) == 0:
            self.valid = False
            return 
        self.valid = True

        tokens = line.split()
        command = Command._getCommandT(tokens)
        self.type = command
        
        # Set arguments
        if command == 'C_ARITHMETIC':
            self.arg1 = tokens[0]
        if command in ['C_POP', 'C_PUSH', 'C_LABEL', 'C_GOTO', 'C_IF', 'C_FUNCTION', 'C_CALL']:
            self.arg1 = tokens[1]
        if command in ['C_POP', 'C_PUSH', 'C_FUNCTION', 'C_CALL']:
            self.arg2 = tokens[2]
        

class Parser(object):
    
    def __init__(self, filestream):
        self.stream = filestream
        self.curCommand = None
        self.nextCommand = self._getNextCommand()

    def hasMoreCommands(self):
        ''' Are there any more commands? '''
        return self.nextCommand.valid

    def _getNextCommand(self):
        stream = self.stream
        while True:
            line = stream.readline()
            if line == '':
                return Command('') # Empty command
            line = line.strip()
            comment_index = line.find ('//')
            if comment_index != -1:
                line = line[:comment_index]
            if line != '':
                break
        return Command(line)   

    def advance(self):
        ''' 
            Reads the next command from input
            and makes it current command. Should
            be called only if 'hasMoreCommands'
        '''
        self.curCommand = self.nextCommand
        self.nextCommand = self._getNextCommand()

    def commandType(self):
        return self.curCommand.type

    def getArg1(self):
        c = self.curCommand
        return hasattr(c, 'arg1') and c.arg1 or "NO_ARG"

    def getArg2(self):
        c = self.curCommand
        return hasattr(c, 'arg2') and c.arg2 or "NO_ARG"
    

class CodeWriter(object):

    segment_symbols = {
        'local' : 'LCL',
        'argument' : 'ARG',
        'this' : 'THIS',
        'that' : 'THAT',
    }

    segment_addrs = {
        'pointer' : '3',
        'temp' : '5'
    }

    instructions = {

        'add' : 
            ['@SP',
             'M=M-1',
             'A=M',
             'D=M',
             'A=A-1',
             'D=D+M',
             '@SP',
             'A=M-1',
             'M=D'],

        'eq' :
            ['@SP',   
             'M=M-1',
             'A=M',
             'D=M',
             'A=A-1',
             'D=D-M',
             '@ELSE{0}',
             'D; JEQ',
             '@SP',
             'A=M-1',
             'M=0',
             '@ENDELSE{0}',
             '0; JMP',
             '(ELSE{0})',
             '@SP',
             'A=M-1',
             'M=-1',
             '(ENDELSE{0})'
            ],

        'lt' :
            ['@SP',   
             'M=M-1',
             'A=M',
             'D=M',
             'A=A-1',
             'D=D-M',
             '@ELSE{0}',
             'D; JGT',
             '@SP',
             'A=M-1',
             'M=0',
             '@ENDELSE{0}',
             '0; JMP',
             '(ELSE{0})',
             '@SP',
             'A=M-1',
             'M=-1',
             '(ENDELSE{0})'
            ],

        'gt' :
            ['@SP',   
             'M=M-1',
             'A=M',
             'D=M',
             'A=A-1',
             'D=D-M',
             '@ELSE{0}',
             'D; JLT',
             '@SP',
             'A=M-1',
             'M=0',
             '@ENDELSE{0}',
             '0; JMP',
             '(ELSE{0})',
             '@SP',
             'A=M-1',
             'M=-1',
             '(ENDELSE{0})'
            ],

        'sub' : 
            ['@SP',
             'M=M-1',
             'A=M',
             'D=M',
             'A=A-1',
             'D=M-D',
             '@SP',
             'A=M-1',
             'M=D'],

        'neg' :
            ['@SP',
             'A=M',
             'A=A-1',
             'M=-M'],

        'not' :
            ['@SP',
             'A=M',
             'A=A-1',
             'M=!M'],

        'and' : 
            ['@SP',
             'M=M-1',
             'A=M',
             'D=M',
             'A=A-1',
             'D=D&M',
             '@SP',
             'A=M-1',
             'M=D'],

        'or' :
            ['@SP',
             'M=M-1',
             'A=M',
             'D=M',
             'A=A-1',
             'D=D|M',
             '@SP',
             'A=M-1',
             'M=D'],

        'C_PUSH' : 
            ['@SP',
             'M=M+1',
             'A=M-1',
             'M=D'],

        'C_POP' :
            ['@SP',
             'M=M-1',
             'A=M',
             'D=M']

    }

    for c in instructions:
        instructions[c] = '\n'.join(instructions[c]) + '\n'

    def __init__(self, outstream):
        self.outstream = outstream
        self.labelCounter = 0
        
    def setFileName(self, newName):
        ls = newName.split('/')
        self.filename = ls[-1]
        self.funcname = 'global'

    def writeArithmetics(self, command):
        out = CodeWriter.instructions[command]
        if command in ['gt', 'lt', 'eq']:
            out = out.format(self.labelCounter)
            self.labelCounter += 1
        self.outstream.write(out)

    def writePushPop(self, command, segment, index):
        genericInstr = CodeWriter.instructions[command]
        if command == 'C_PUSH':
            if segment == 'constant':
                add = '@' + index + '\n'
                add += 'D=A\n'
            else:
                add = ''
                if segment in CodeWriter.segment_symbols:
                    add += '@' + CodeWriter.segment_symbols[segment] + '\n'
                    add += 'D=M\n'
                elif segment in CodeWriter.segment_addrs:
                    add += '@' + CodeWriter.segment_addrs[segment] + '\n'
                    add += 'D=A\n'
                elif segment == 'static':
                    add += '@' + self.filename + '.' + index + '\n'
                    add += 'D=A\n'
                if segment != 'static':
                    add += '@' + index + '\n'
                    add += 'D=D+A\n'
                add += 'A=D\n'
                add += 'D=M\n'
            out = add + genericInstr
        elif command == 'C_POP':
            add = '@R13\n'
            add += 'M=D\n'
            if segment in CodeWriter.segment_symbols:
                add += '@' + CodeWriter.segment_symbols[segment] + '\n'
                add += 'D=M\n'
            elif segment in CodeWriter.segment_addrs:
                add += '@' + CodeWriter.segment_addrs[segment] + '\n'
                add += 'D=A\n'
            elif segment == 'static':
                add += '@' + self.filename + '.' + index + '\n'
                add += 'D=A\n'
            if segment != 'static':
                add += '@' + index + '\n'
                add += 'D=D+A\n'
            add += '@R14\n'
            add += 'M=D\n'
            add += '@R13\n'
            add += 'D=M\n'
            add += '@R14\n'
            add += 'A=M\n'
            add += 'M=D\n'
            out = genericInstr + add
        else:
            raise(Exception("Neither push nor pop in 'writePushPop'"))
        self.outstream.write(out)

    def writeLabel(self, label):
        out = '(' + self.funcname + '$' + label + ')'
        self.outstream.write( out  + '\n' )

    def writeGoto(self, label):
        out = ['@' + self.funcname + '$' + label,
               '0; JMP']
        self.outstream.write( '\n'.join(out) + '\n' )

    def writeIf(self, label):
        out = CodeWriter.instructions['C_POP']
        out += '@' + self.funcname + '$' + label + '\n'
        out += 'D; JNE' + '\n'
        self.outstream.write( out )
        
    def writeCall(self, funcName, numArgs):

        pushInstr = CodeWriter.instructions['C_PUSH']
        ret_addr = 'ret_addr' + str(self.labelCounter)
        self.labelCounter += 1

        out = [
                # Push return address
                '@' + ret_addr + '\n',
                'D=A\n',
                pushInstr,
                # Push old lcl address
                '@LCL\n',
                'D=M\n',
                pushInstr,
                # Push old arg address
                '@ARG\n',
                'D=M\n',
                pushInstr,
                # Push old this address
                '@THIS\n',
                'D=M\n',
                pushInstr,
                # Push old that address
                '@THAT\n',
                'D=M\n',
                pushInstr,
                # Update lcl
                '@SP\n',
                'D=M\n',
                '@LCL\n',
                'M=D\n',
                # Update arg 
                '@SP\n',
                'D=M\n',
                '@5\n',
                'D=D-A\n',
                '@' + numArgs + '\n',
                'D=D-A\n',
                '@ARG\n',
                'M=D\n',
                # Instruct jump to function
                '@' + funcName + '\n',
                '0; JMP\n',
                '(' + ret_addr + ')\n'
            ]

        self.outstream.write( ''.join(out) )
        

    def writeReturn(self):

        def getFrame(offset):
            assert (offset <= 0)
            offset = str(-offset)
            out = '@frame\n'
            out += 'D=M\n'
            out += '@' + offset + '\n'
            out += 'D=D-A\n'
            out += 'A=D\n'
            out += 'D=M'

            return out
            
            
        out = [
            # Set frame variable
            '@LCL',
            'D=M',
            '@frame',
            'M=D',
            # Set ret variable
            getFrame(-5),
            '@ret',
            'M=D',
            # Reposition return value
            '@SP',
            'A=M-1',
            'D=M',
            '@ARG',
            'A=M',
            'M=D', 
            # Reposition stack pointer
            '@ARG',
            'D=M+1',
            '@SP',
            'M=D',
            # Reset 'that'
            getFrame(-1),
            '@THAT',
            'M=D',
            # Reset 'this'
            getFrame(-2),
            '@THIS',
            'M=D',
            # Reset 'arg'
            getFrame(-3),
            '@ARG',
            'M=D',
            # Reset 'lcl'
            getFrame(-4),
            '@LCL',
            'M=D',
            # Jump back to caller
            '@ret',
            'A=M',
            '0; JMP'
        ]
            
        self.outstream.write( '\n'.join(out) + '\n' )
            

    def writeFunction(self, funcName, numLocals):
        
        numLocals = int(numLocals)

        labelFunction = '(' + funcName + ')\n'
        reservingInstr = '@SP\nM=M+1\nA=M-1\nM=0\n'
            
        self.outstream.write ( labelFunction + (reservingInstr * numLocals) )
        self.funcName = funcName
        

    def close(self):
        self.outstream.close()

    def writeInit(self, bootstrap_needed):
        if not bootstrap_needed:
            return

        startup = [
            # Set stack pointer
            '@256',
            'D=A',
            '@SP',
            'M=D'
        ]

        self.outstream.write( '\n'.join(startup) + '\n' )
            

    def finalize(self):
        final = [
            '(TERM)',
            '@TERM',
            '0; JMP'
        ]
        
        self.outstream.write ( '\n'.join(final) + '\n' )


def echoHelp():
    print (
            tw.dedent(
            '''
                # Provide single vm file #
                Usage:   > ./VMtranslator file.vm
                Output:  file.asm

                # Or provide directory containing vm files #
                Usage:   > ./VMtranslator dir
                Output:  dir/file1.asm dir/file2.asm ...
            '''
            )
          )
    exit(1) # Exit with non-zero status

def getFiles(arg):
    files = []
    if arg.endswith('.vm'):
        files.append(arg)
        slashIndex = arg.rfind('/')
        resFile = arg[:slashIndex + 1] + arg[slashIndex + 1: -3] + '.asm'
    elif os.path.isdir(arg):
        for filename in os.listdir(arg):
            if filename.endswith('.vm'):
                files.append(arg + '/' + filename)
        base = arg[arg.rfind('/') + 1:]
        resFile = arg + '/' + base + '.asm'
    else:
        raise (Exception("Neiter .vm file nor directory"))
    streamsAndNames = map(lambda fileName: (open(fileName, 'r'), fileName,), files)
    return list(streamsAndNames), resFile

if __name__ == '__main__':
    print ("[Python VMtranslator]")
    numArgs = len(sys.argv)
    if numArgs != 2:
        echoHelp()         

    fileStreams, resName = getFiles(sys.argv[1])
    
    if len(fileStreams) == 0:
        echoHelp()

    # If we compile full-fledged program we add bootsrap code
    add_bootstrap_code = bool (len(fileStreams) > 1)
    if add_bootstrap_code:
        initMockFile = io.StringIO('call Sys.init 0\n')
        fileStreams = [(initMockFile, 'mock_init',)] + fileStreams

    codeWriter = CodeWriter(open(resName, 'w'))
    codeWriter.writeInit(add_bootstrap_code)
    for fileTuple in fileStreams:
        f, fileName = fileTuple

        print ("Processing {0}".format(fileName))

        codeWriter.setFileName(fileName)
        parser = Parser(f)
        while parser.hasMoreCommands():
            parser.advance()
            if parser.commandType() == 'C_ARITHMETIC':
                codeWriter.writeArithmetics(parser.getArg1())    
            elif parser.commandType() in ['C_PUSH', 'C_POP']:
                codeWriter.writePushPop(parser.commandType(), \
                                        parser.getArg1(), parser.getArg2())
            elif parser.commandType() == 'C_LABEL':
                codeWriter.writeLabel(parser.getArg1())
            elif parser.commandType() == 'C_GOTO':
                codeWriter.writeGoto(parser.getArg1())
            elif parser.commandType() == 'C_IF':
                codeWriter.writeIf(parser.getArg1())
            elif parser.commandType() == 'C_FUNCTION':
                codeWriter.writeFunction(parser.getArg1(), parser.getArg2())
            elif parser.commandType() == 'C_CALL':
                codeWriter.writeCall(parser.getArg1(), parser.getArg2())
            elif parser.commandType() == 'C_RETURN':
                codeWriter.writeReturn()
            else:
                raise(Exception("Parser returns unknowk command type"))
        f.close()

    codeWriter.finalize()
    codeWriter.close()
                
            
