-
-
Save modal/838807 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import glob | |
| # declare types of commands | |
| C_ARITMETIC = object() | |
| C_PUSH = object() | |
| C_POP = object() | |
| C_LABEL = object() | |
| C_GOTO = object() | |
| C_IF = object() | |
| C_FUNCTION = object() | |
| C_RETURN = object() | |
| C_CALL = object() | |
| C_ERROR = object() | |
| class CodeWriter: | |
| file = None # file to be writing to | |
| fileName = None | |
| compare = 1 # the number of equalities | |
| def __init__(self, name): | |
| self.file = open(name, 'w') | |
| self.fileName = name.strip() | |
| def __del__(self): | |
| self.file.close() | |
| def WriteAritmetic(self, arg1): | |
| # plus or minus | |
| if arg1 in ['add', 'sub', 'and', 'or']: | |
| if arg1 == 'add': line = 'D=D+M' | |
| elif arg1 == 'sub': line = 'D=M-D' | |
| elif arg1 == 'and': line = 'D=D&M' | |
| elif arg1 == 'or': line = 'D=D|M' | |
| self.file.write("@SP\n") #A=0, M[0]=258 | |
| self.file.write("M=M-1\n") #M[0]=258-1 | |
| self.file.write("A=M\n") #A=257 | |
| self.file.write("D=M\n") #D=M[257] (8) | |
| self.file.write("A=A-1\n") #A=256 | |
| self.file.write("%s\n" % (line, )) | |
| self.file.write("M=D\n") #M[256]=D | |
| self.file.write("D=A+1\n") #A=257 | |
| self.file.write("@SP\n") | |
| self.file.write("M=D\n") | |
| return True | |
| # unary operations | |
| elif arg1 in ['neg', 'not']: | |
| line = 'M=-M' if arg1 == 'neg' else 'M=!M' | |
| self.file.write("@SP\n") | |
| self.file.write("M=M-1\n") | |
| self.file.write("A=M\n") | |
| self.file.write("%s\n" % (line, )) | |
| self.file.write("D=A+1\n") | |
| self.file.write("@SP\n") | |
| self.file.write("M=D\n") | |
| # in case they are equal | |
| elif arg1 in ['eq', 'lt', 'gt']: | |
| if arg1 == "eq": | |
| line = "D;JEQ" | |
| elif arg1 == "lt": | |
| line = "D;JLT" | |
| elif arg1 == "gt": | |
| line = "D;JGT" | |
| self.file.write("@SP\n") | |
| self.file.write("M=M-1\n") | |
| self.file.write("A=M\n") | |
| self.file.write("D=M\n") | |
| self.file.write("A=A-1\n") | |
| self.file.write("D=M-D\n") | |
| self.file.write("@EQ%d\n" % (self.compare, )) | |
| self.file.write("%s\n" % (line, )) | |
| self.file.write("@0\n") | |
| self.file.write("D=-A\n") | |
| self.file.write("@SP\n") | |
| self.file.write("A=M\n") | |
| self.file.write("M=D\n") | |
| self.file.write("@SP\n") | |
| self.file.write("M=M+1\n") | |
| self.file.write("@doneEQ%d\n" % (self.compare, )) | |
| self.file.write("0;JMP\n") | |
| self.file.write("(EQ%d)\n" % (self.compare, )) | |
| self.file.write("@1\n") | |
| self.file.write("D=A\n") | |
| self.file.write("@SP\n") | |
| self.file.write("A=M\n") | |
| self.file.write("M=D\n") | |
| self.file.write("@SP\n") | |
| self.file.write("M=M+1\n") | |
| self.file.write("(doneEQ%d)\n" % (self.compare, )) | |
| self.compare += 1 | |
| return False | |
| def WritePushPop(self, type, segment, index): | |
| # deal with the value | |
| value = 0 | |
| if segment == "constant": | |
| self.file.write("@%s\n" % (str(index), )) | |
| self.file.write("D=A\n") | |
| elif segment in ['local', 'argument', 'this', 'that']: | |
| if segment == 'local': key = "LCL" | |
| elif segment == 'argument': key = "ARG" | |
| elif segment == 'this': key = 'THIS' | |
| elif segment == 'that': key = 'THAT' | |
| self.file.write("@%s\n" % (str(key), )) | |
| self.file.write("D=A\n") | |
| self.file.write("@%s\n" % (str(index), )) | |
| self.file.write("D=D+M\n") | |
| elif segment in ['pointer', 'temp']: | |
| if segment == 'pointer': base = 3 | |
| elif segment == 'temp': base = 5 | |
| self.file.write("@%s\n" % (base, )) | |
| self.file.write("D=A\n") | |
| self.file.write("@%s\n" % (index, )) | |
| self.file.write("D=D+A\n") | |
| elif segment in ['static']: | |
| self.file.write("@%s.%s\n" % (self.fileName[:-4], index)) | |
| self.file.write("D=M\n") | |
| if type is C_PUSH: | |
| self.file.write("@SP\n") | |
| self.file.write("A=M\n") | |
| self.file.write("M=D\n") | |
| self.file.write("D=A+1\n") | |
| self.file.write("@SP\n") | |
| self.file.write("M=D\n") | |
| return True | |
| elif type is C_POP: | |
| self.file.write("@R13\n") | |
| self.file.write("M=D\n") | |
| self.file.write("@SP\n") | |
| self.file.write("A=M-1\n") | |
| self.file.write("D=M\n") | |
| self.file.write("@R13\n") | |
| self.file.write("A=M\n") | |
| self.file.write("M=D\n") | |
| return True | |
| return False | |
| class VM: | |
| file = None # stores the file(s) to open | |
| fileWriter = None # the new file to write to | |
| dir = None # name of the directory | |
| # get the file | |
| def __getFile(self): | |
| var = raw_input("What is the file name/directory: ") | |
| # if ends in .vm just one file, otherwise directory | |
| if var[len(var)-3:len(var)] == '.vm': | |
| self.file = [var] | |
| self.dir = var[:-3] | |
| else: | |
| # directory, check if ends in /, otherwise add | |
| if var[-1] <> "/": | |
| self.dir = var | |
| var = "%s/" % var | |
| else: | |
| self.dir = var[:-1] | |
| # glob through them all for .vm files | |
| self.file = glob.glob(var + "*.vm") | |
| return True | |
| # check if the line is actually code | |
| def __isCode(self, line): | |
| output = line.lstrip(" ") | |
| if output.startswith("//"): # check if it is a comment | |
| return False | |
| elif output.startswith('\n'): # check if it just a new line | |
| return False | |
| else: | |
| return True | |
| # parse the line | |
| def __parseLine(self, line): | |
| line = line.strip(" ") | |
| line = line.rstrip("\n") | |
| line = line[:line.find("//")] | |
| return line.lower() | |
| # define the command type | |
| def __commandType(self, line): | |
| if line.find(" ") > -1: | |
| seg1 = line[:line.find(" ")].lower() | |
| else: | |
| seg1 = line | |
| if seg1 == "push": | |
| return C_PUSH | |
| elif seg1 == "pop": | |
| return C_POP | |
| elif seg1 in ['add', 'sub', 'neg', 'eq', 'lt', 'gt', 'and', 'or', 'not']: | |
| return C_ARITMETIC | |
| else: | |
| return C_ERROR | |
| # get the first arguement | |
| def __arg1(self, input): | |
| if input.find(" ") < 0: | |
| return input | |
| else: | |
| input = input[input.find(" ")+1:] | |
| return input[:input.find(" ")] | |
| # def get the second arguement | |
| def __arg2(self, input): | |
| input = input[input.find(" ")+1:] | |
| return input[input.find(" ")+1:] | |
| # go through the file and do the cool shit | |
| def __outFile(self, inFile): | |
| # open new file | |
| input = open(inFile, 'r') | |
| # loop through | |
| for f in input: | |
| # check if it is a comment or a newline | |
| if not self.__isCode(f): | |
| continue | |
| # parse the line | |
| f = self.__parseLine(f) | |
| # what type of line it is | |
| type = self.__commandType(f) | |
| # write a line | |
| if type is C_ARITMETIC: | |
| self.fileWriter.WriteAritmetic(self.__arg1(f)) | |
| elif type in [C_PUSH, C_POP]: | |
| self.fileWriter.WritePushPop(type, self.__arg1(f), self.__arg2(f)) | |
| # boot strap the VM | |
| def __init__(self): | |
| # get the files to open to | |
| print "Getting files..." | |
| self.__getFile() | |
| # the file writer class | |
| self.fileWriter = CodeWriter(self.dir + ".asm") | |
| # foreach file out | |
| print "Writing files..." | |
| for f in self.file: | |
| self.__outFile(f) | |
| # destroy the filewrite - to close the file | |
| print "Done" | |
| del self.fileWriter | |
| # main execution of the code | |
| if __name__ == "__main__": | |
| aVM = VM() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment