Skip to content

Instantly share code, notes, and snippets.

@miku
Forked from jweissbock/Jack-Hack VM.py
Created July 21, 2012 19:59
Show Gist options
  • Select an option

  • Save miku/3156965 to your computer and use it in GitHub Desktop.

Select an option

Save miku/3156965 to your computer and use it in GitHub Desktop.

Revisions

  1. @jweissbock jweissbock revised this gist Mar 2, 2011. 1 changed file with 5 additions and 1 deletion.
    6 changes: 5 additions & 1 deletion Jack-Hack VM.py
    Original file line number Diff line number Diff line change
    @@ -145,21 +145,23 @@ def WriteLabel(self, b):
    def WriteGOTO(self, b):
    self.file.write("@%s$%s\n" % (self.curFunction, b))
    self.file.write("0;JMP\n")
    return True

    def WriteIf(self, b):
    self.file.write("@SP\nA=M\nD=M\n")
    self.file.write("@SP\nM=M-1\n")
    self.file.write("0;JMP\n")
    self.file.write("@%s\n" % (b, ))
    self.file.write("D;JMP\n")
    return True

    def WriteSPup(self):
    self.file.write("D=A\n")
    self.file.write("@SP\n")
    self.file.write("M=A\n")
    self.file.write("M=D\n")
    self.file.write("@SP\n")
    self.file.write("M=M+1\n")
    self.file.write("M=M+1")
    return True

    def WriteCall(self, f, n):
    @@ -190,6 +192,7 @@ def WriteFunction(self, f, k):
    for i in range(0, k):
    self.file.write("@0\n")
    self.WriteSPup()
    return True

    def WriteReturn(self):
    self.file.write("@LCL\n")
    @@ -230,6 +233,7 @@ def WriteBootstrap(self):
    self.file.write("@SP\n")
    self.file.write("M=256\n")
    self.WriteCall("Sys.init", 0)
    return True

    class VM:
    file = None # stores the file(s) to open
  2. @jweissbock jweissbock revised this gist Mar 2, 2011. 1 changed file with 127 additions and 1 deletion.
    128 changes: 127 additions & 1 deletion Jack-Hack VM.py
    Original file line number Diff line number Diff line change
    @@ -16,6 +16,7 @@ class CodeWriter:
    file = None # file to be writing to
    fileName = None
    compare = 1 # the number of equalities
    curFunction = None # stores the name of the current function

    def __init__(self, name):
    self.file = open(name, 'w')
    @@ -136,6 +137,99 @@ def WritePushPop(self, type, segment, index):
    return True

    return False

    def WriteLabel(self, b):
    self.file.write("(%s$%s)\n" % (self.curFunction, b))
    return True

    def WriteGOTO(self, b):
    self.file.write("@%s$%s\n" % (self.curFunction, b))
    self.file.write("0;JMP\n")

    def WriteIf(self, b):
    self.file.write("@SP\nA=M\nD=M\n")
    self.file.write("@SP\nM=M-1\n")
    self.file.write("0;JMP\n")
    self.file.write("@%s\n" % (b, ))
    self.file.write("D;JMP\n")

    def WriteSPup(self):
    self.file.write("D=A\n")
    self.file.write("@SP\n")
    self.file.write("M=A\n")
    self.file.write("M=D\n")
    self.file.write("@SP\n")
    self.file.write("M=M+1\n")
    return True

    def WriteCall(self, f, n):
    self.file.write("@return-%s\n" % (f, ))
    self.WriteSPup()
    self.file.write("@LCL\n")
    self.WriteSPup()
    self.file.write("@ARG\n")
    self.WriteSPup()
    self.file.write("@THIS\n")
    self.WriteSPup()
    self.file.write("@THAT\n")
    self.WriteSPup()
    self.file.write("@SP\n")
    self.file.write("D=M\n")
    self.file.write("@ARG\n")
    self.file.write("D=D-n\n")
    self.file.write("M=D-5\n")
    self.file.write("@LCL\n")
    self.file.write("M=D\n")
    self.WriteGOTO(f)
    self.file.write("(return-%s)\n" % (f, ))
    return True

    def WriteFunction(self, f, k):
    self.file.write ("(%s)\s" % (f, ))
    curFunction = f
    for i in range(0, k):
    self.file.write("@0\n")
    self.WriteSPup()

    def WriteReturn(self):
    self.file.write("@LCL\n")
    self.file.write("D=M\n")
    self.file.write("@R13\n")
    self.file.write("M=D\n") # R13 is now FRAME
    self.file.write("D=M-5\n")
    self.file.write("@R14\n")
    self.file.write("M=D\n") # R14 stores RET
    self.file.write("@SP\n")
    self.file.write("M=A\n")
    self.file.write("D=M\n")
    self.file.write("@ARG\n")
    self.file.write("A=M\n")
    self.file.write("M=D\n") # *ARG = pop()
    self.file.write("@ARG\n")
    self.file.write("D=M\n")
    self.file.write("@SP\n")
    self.file.write("M=A\n")
    self.file.write("M=D+1\n") # SP = ARG + 1
    self.file.write("@R13\n")
    self.file.write("D=M\n")
    self.file.write("@THAT\n") # THAT = FRAME-1
    self.file.write("M=D-1\n")
    self.file.write("@THIS\n")
    self.file.write("M=D-2\n") # FRAME - 2
    self.file.write("@ARG\n")
    self.file.write("M=D-3\n") # FRAME-3
    self.file.write("@LCL\n")
    self.file.write("M=D-4\n") # FRAME - 4
    self.file.write("@R14\n")
    self.file.write("D=M\n")
    self.file.write("@D\n") # GOTO RET
    self.file.write("0;JMP\n")
    return True

    def WriteBootstrap(self):
    self.file.write("@SP\n")
    self.file.write("M=256\n")
    self.WriteCall("Sys.init", 0)

    class VM:
    file = None # stores the file(s) to open
    @@ -184,14 +278,26 @@ def __commandType(self, line):
    if line.find(" ") > -1:
    seg1 = line[:line.find(" ")].lower()
    else:
    seg1 = line
    seg1 = line.lower()

    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
    elif seg1 == "label":
    return C_LABEL;
    elif seg1 == "goto":
    return C_GOTO
    elif seg1 == "if-goto":
    return C_IF
    elif seg1 == "call":
    return C_CALL
    elif seg1 == "function":
    return C_FUNCTION
    elif seg1 == "return":
    return C_RETURN
    else:
    return C_ERROR

    @@ -224,11 +330,29 @@ def __outFile(self, inFile):
    # what type of line it is
    type = self.__commandType(f)

    # error? return
    if type is C_ERROR:
    import sys
    print "Error on line: %s" % (f, )
    sys.exit()

    # 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))
    elif type is C_LABEL:
    self.fileWriter.WriteLabel(self.__arg1(f))
    elif type is C_ERROR:
    self.fileWriter.WriteGOTO(self.arg1(f))
    elif type is C_IF:
    self.fileWriter.WriteIF(self.__arg1(f))
    elif type is C_CALL:
    self.fileWrite.WriteCall(self.__arg1(f), self.__arg2(f))
    elif type is C_FUNCTION:
    self.fileWrite.WriteFunction(self.__arg1(f), self.__arg2(f))
    elif type is C_RETURN:
    self.fileWrite.WriteReturn()

    # boot strap the VM
    def __init__(self):
    @@ -237,6 +361,8 @@ def __init__(self):
    self.__getFile()
    # the file writer class
    self.fileWriter = CodeWriter(self.dir + ".asm")
    # need the boot write
    self.fileWriter.WriteBootstrap()
    # foreach file out
    print "Writing files..."
    for f in self.file:
  3. @jweissbock jweissbock revised this gist Feb 20, 2011. 1 changed file with 195 additions and 40 deletions.
    235 changes: 195 additions & 40 deletions Jack-Hack VM.py
    Original file line number Diff line number Diff line change
    @@ -1,38 +1,166 @@
    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 # file to be writing to
    file = None # file to be writing to
    fileName = None
    compare = 1 # the number of equalities

    def __init__(self):
    self.file = file
    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 WriteAritmetic(self):
    print "Math"
    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")

    def WritePushPop(self):
    print "Push pop"
    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 # stores the opening file information
    file = None # stores the file(s) to open
    fileWriter = None # the new file to write to

    # 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()
    dir = None # name of the directory

    # get the file
    def __getFile(self):
    input = raw_input("What is the file name/directory: ")

    # loop through each file, write to file
    var = raw_input("What is the file name/directory: ")

    self.file = open(input, 'r')
    # 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):
    @@ -48,24 +176,44 @@ def __isCode(self, line):
    def __parseLine(self, line):
    line = line.strip(" ")
    line = line.rstrip("\n")
    return line
    line = line[:line.find("//")]
    return line.lower()

    # define the command type
    def __commandType(self, line):
    seg1 = line[:line.find(" ")].lower()
    if line.find(" ") > -1:
    seg1 = line[:line.find(" ")].lower()
    else:
    seg1 = line

    if seg1 == "push":
    return self.C_PUSH
    return C_PUSH
    elif seg1 == "pop":
    return self.C_POP
    return C_POP
    elif seg1 in ['add', 'sub', 'neg', 'eq', 'lt', 'gt', 'and', 'or', 'not']:
    return self.C_ARITMETIC
    return C_ARITMETIC
    else:
    return self.C_ERROR
    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):
    for f in self.file:
    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
    @@ -77,18 +225,25 @@ def __outFile(self):
    type = self.__commandType(f)

    # write a line
    if type is self.C_ARITMETIC:
    self.fileWriter.WriteAritmetic()
    elif type in [self.C_PUSH, self.C_POP]:
    self.fileWriter.WritePushPop()

    print f
    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):
    self.fileWriter = CodeWriter()
    # get the files to open to
    print "Getting files..."
    self.__getFile()
    self.__outFile()
    # 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__":
  4. @jweissbock jweissbock renamed this gist Feb 4, 2011. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  5. @jweissbock jweissbock created this gist Feb 4, 2011.
    96 changes: 96 additions & 0 deletions Jack-Hack VM
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,96 @@
    class CodeWriter:
    file # file to be writing to

    def __init__(self):
    self.file = file

    def WriteAritmetic(self):
    print "Math"

    def WritePushPop(self):
    print "Push pop"

    class VM:
    file # stores the opening file information
    fileWriter = None # the new file to write to

    # 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()

    # get the file
    def __getFile(self):
    input = raw_input("What is the file name/directory: ")

    # loop through each file, write to file

    self.file = open(input, 'r')

    # 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")
    return line

    # define the command type
    def __commandType(self, line):
    seg1 = line[:line.find(" ")].lower()

    if seg1 == "push":
    return self.C_PUSH
    elif seg1 == "pop":
    return self.C_POP
    elif seg1 in ['add', 'sub', 'neg', 'eq', 'lt', 'gt', 'and', 'or', 'not']:
    return self.C_ARITMETIC
    else:
    return self.C_ERROR

    # go through the file and do the cool shit
    def __outFile(self):
    for f in self.file:
    # 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 self.C_ARITMETIC:
    self.fileWriter.WriteAritmetic()
    elif type in [self.C_PUSH, self.C_POP]:
    self.fileWriter.WritePushPop()

    print f

    # boot strap the VM
    def __init__(self):
    self.fileWriter = CodeWriter()
    self.__getFile()
    self.__outFile()

    # main execution of the code
    if __name__ == "__main__":
    aVM = VM()