class CompilationEngine(object): labelID = 0 ############################################ # Constructor def __init__(self, tokenList): self.tokens = tokenList #the list of tagged tokens to process (a copy was previously output as ____T.xml ) #add and delete from this to reack left padding for XML file readability self.indentation = 0 self.vmList = [] ############################################ # static class methods # these methods are owned by the Class not one instance # note they do not have self as the first argument and they have the @staticmethod tag @staticmethod def __getSimpleLabel__(): ''' a static utility method to access the class variable ''' # creates a unique label for flow of control statements result = str(CompilationEngine.labelID) CompilationEngine.labelID += 1 return result ############################################ # instance methods def compileTokens(self): ''' primary call to do the final compilation. returns a list of properly identified structured XML with appropriate indentation.''' #the compilation recursive descent always starts with the <tokens> tag, and then calls __compileClass__(), # if it does not -- fail early because something is wrong, either in the tokenizing or how the file was output. # **use the fail early technique throughout the compilation, you will always know which of a small number of # possibilities you are looking for, if none of them are there raise the exception so you can start debugging result = [] tokenTuple = self.__getNextEntry__() if tokenTuple[TT_XML] == '<tokens>': result.extend( self.__compileClass__() ) tokenTuple = self.__getNextEntry__() if tokenTuple[TT_XML] != '</tokens>': raise RuntimeError('Error, this file was not properly tokenized, missing </tokens>') else: raise RuntimeError('Error, this file was not properly tokenized, missing <tokens>') return result def compileVM(self): return self.vmList ############################################ # private/utility methods def __getNextEntry__(self): ''' removes and returns the next token from the list of tokens as a tuple of the form (token, <tag> token </tag>). TT_TOKEN and TT_XML should be used for accessing the tuple components ''' # updated to handle output of stringConstant token; previously would split string constants at spaces (' ') if self.tokens: nextToken = self.tokens.pop(0) nextToken = nextToken.strip() tokenList = nextToken.split() if len(tokenList) > 1: leftSplit = nextToken.split('> ', 1) # print(leftSplit) rightSplit = leftSplit[1].split(' <', 1) token = rightSplit[0] if 'stringConstant' not in nextToken: token = token.strip() nextEntry = (token, nextToken) else: nextEntry = ("", nextToken) else: raise RuntimeError("No more tokens to process") return nextEntry def __peekAtNextEntry__(self): ''' copies, but does not remove the next token from the list of tokens as a tuple of the form (token, <tag> token </tag>). TT_TOKEN and TT_XML should be used for accessing the tuple components ''' peekedToken = self.tokens[0] tokenList = peekedToken.split() peekedTokenTuple = (tokenList[1], peekedToken) return peekedTokenTuple def __replaceEntry__(self, entry): ''' returns a token to the head of the list. entry must properly be in the form <tag> token </tag> (in other words the TT_XML part) ''' self.tokens.insert(0, entry[TT_XML]) def __compileClass__(self): ''' compiles a class declaration. returning a list of VM commands. ''' self.st = SymbolTable() result = [] result.append( '<class>' ) #structure label for class <class> self.indentation += 2 #indentation level adjustment. it will be paired at the bottom with a negative re-adjustment tokenTuple = self.__getNextEntry__() if tokenTuple[TT_TOKEN] == 'class': result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # keyword class tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # classname identifier self.className = tokenTuple[TT_TOKEN] tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # <symbol> { </symbol> peekedTuple = self.__peekAtNextEntry__() while (peekedTuple[TT_TOKEN] == 'static' or peekedTuple[TT_TOKEN] == 'field'): # if 'static' or 'field' present: result.extend( self.__compileClassVarDec__() ) # 'classVarDec' call peekedTuple = self.__peekAtNextEntry__() peekedTuple = self.__peekAtNextEntry__() while (peekedTuple[TT_TOKEN] in SUBROUTINES): # if 'constructor' or 'function' or 'method' present: result.extend( self.__compileSubroutine__() ) # 'subroutineDec' call peekedTuple = self.__peekAtNextEntry__() tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # <symbol> } </symbol> else: raise RuntimeError('Error, token provided:', tokenTuple[TT_TOKEN], ', is not class') self.indentation -= 2 #indentation level re-adjustment. result.append( '</class>' ) #keyword class return result def __compileClassVarDec__(self): ''' compiles a class variable declaration statement. returning a list of VM commands. ''' result = [] result.append( (self.indentation * ' ') + '<classVarDec>' ) #structure label for class <classVarDec> self.indentation += 2 #indentation level adjustment. it will be paired at the bottom with a negative re-adjustment tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # <keyword> 'static' OR 'field' </keyword> kind = tokenTuple[TT_TOKEN] # 'static' or 'field' tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # <keyword> 'type' </keyword> OR <identifier> 'className' </indentifier> idType = tokenTuple[TT_TOKEN] # type (keyword or className) tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # <identifier> 'varName' </identifier> name = tokenTuple[TT_TOKEN] # variable name self.st.Define(name, idType, kind) # define variable in symbol table result.append( (self.indentation * ' ') + '<SYMBOL-Defined> class.' + name + ' (' + kind + ' ' + idType + ') = ' + str(self.st.IndexOf(name)) + ' </SYMBOL-Defined>') peekedTuple = self.__peekAtNextEntry__() while peekedTuple[TT_TOKEN] == ',': # 0 or more times (if ',' present or until ';' reached): tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # symbol ',' tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # 'varName' indentifier name = tokenTuple[TT_TOKEN] # variable name self.st.Define(name, idType, kind) # define variable in symbol table result.append( (self.indentation * ' ') + '<SYMBOL-Defined> class.' + name + ' (' + kind + ' ' + idType + ') = ' + str(self.st.IndexOf(name)) + ' </SYMBOL-Defined>') peekedTuple = self.__peekAtNextEntry__() tokenTuple = self.__getNextEntry__() if tokenTuple[TT_TOKEN] == ';': result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # symbol ';' else: raise RuntimeError("classVarDec did not close properly") self.indentation -= 2 #indentation level re-adjustment. result.append( (self.indentation * ' ') + '</classVarDec>' ) #keyword classVarDec return result def __compileSubroutine__(self): ''' compiles a function/method. returning a list of VM commands. ''' self.st.startSubroutine() result = [] result.append( (self.indentation * ' ') + '<subroutineDec>' ) #structure label for class <subroutineDec> self.indentation += 2 #indentation level adjustment. it will be paired at the bottom with a negative re-adjustment tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # keyword 'constructor' OR 'function' OR 'method' self.subKeyword = tokenTuple[TT_TOKEN] if self.subKeyword == 'method': # if compiling a method, need to add 'this' to symbol table idType = self.className kind = 'arg' name = 'this' self.st.Define(name, idType, kind) tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # keyword 'void' OR ('type' keyword OR 'className' identifier) self.subroutineType = tokenTuple[TT_TOKEN] # used to identify 'void' methods tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # 'subroutineName' identifier name = tokenTuple[TT_TOKEN] # variable name tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # sybmol '(' result.extend( self.__compileParameterList__() ) # call to 'parameterList' tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # sybmol ')' result.append( (self.indentation * ' ') + '<subroutineBody>' ) # <subroutineBody> self.indentation += 2 #indentation level adjustment. it will be paired at the bottom with a negative re-adjustment tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # symbol '{' peekedTuple = self.__peekAtNextEntry__() while (peekedTuple[TT_TOKEN] == 'var'): # 0 or more times (if keyword 'var' present): result.extend( self.__compileVarDec__() ) # call to 'varDec' peekedTuple = self.__peekAtNextEntry__() funcName = self.className + '.' + name # write the function name declared self.vmList.append( writeFunction(funcName, self.st.VarCount('var')) ) if self.subKeyword == 'constructor': # handle a constructor declaration self.vmList.append( writePush('constant', self.st.VarCount('field')) ) self.vmList.append( writeCall('Memory.alloc', 1) ) self.vmList.append( writePop('pointer', 0) ) elif self.subKeyword == 'method': # handle a method declaration self.vmList.append( writePush(self.st.KindOfVM('this'), self.st.IndexOf('this')) ) self.vmList.append( writePop('pointer', 0) ) result.extend( self.__compileStatements__() ) # call to 'statements' tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # symbol '}' self.indentation -= 2 #indentation level re-adjustment. result.append( (self.indentation * ' ') + '</subroutineBody>' ) # </subroutineBody> self.indentation -= 2 #indentation level re-adjustment. result.append( (self.indentation * ' ') + '</subroutineDec>' ) # </subroutineDec> return result def __compileParameterList__(self): ''' compiles a parameter list from a function/method. returning a list of VM commands. ''' result = [] result.append( (self.indentation * ' ') + '<parameterList>' ) #structure label for class <parameterList> self.indentation += 2 #indentation level adjustment. it will be paired at the bottom with a negative re-adjustment peekedTuple = self.__peekAtNextEntry__() if peekedTuple[TT_TOKEN] != ')': # 0 or 1 times (if 'type' given): kind = 'arg' # arguments declared tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # 'type' keyword OR 'className' identifier idType = tokenTuple[TT_TOKEN] # 'type' or 'className' of argument tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # 'varName' identifier name = tokenTuple[TT_TOKEN] # name of argument self.st.Define(name, idType, kind) # define argument result.append( (self.indentation * ' ') + '<SYMBOL-Defined> subroutine.' + name + ' (' + kind + ' ' + idType + ') = ' + str(self.st.IndexOf(name)) + ' </SYMBOL-Defined>') peekedTuple = self.__peekAtNextEntry__() while peekedTuple[TT_TOKEN] == ',': # 0 or more times (if ',' present): tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # symbol ',' tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # 'type' keyword OR 'className' identifier idType = tokenTuple[TT_TOKEN] # 'type' or 'className' of argument tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # 'varName' indentifier name = tokenTuple[TT_TOKEN] # name of argument self.st.Define(name, idType, kind) # define argument result.append( (self.indentation * ' ') + '<SYMBOL-Defined> subroutine.' + name + ' (' + kind + ' ' + idType + ') = ' + str(self.st.IndexOf(name)) + ' </SYMBOL-Defined>') peekedTuple = self.__peekAtNextEntry__() self.indentation -= 2 #indentation level re-adjustment. result.append( (self.indentation * ' ') + '</parameterList>' ) # </parameterList> return result def __compileVarDec__(self): ''' compiles a single variable declaration line. returning a list of VM commands. ''' result = [] result.append( (self.indentation * ' ') + '<varDec>' ) #structure label for class <varDec> self.indentation += 2 #indentation level adjustment. it will be paired at the bottom with a negative re-adjustment tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # keyword 'var' kind = 'var' # local variables declared tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # 'type' keyword OR 'className' identifier idType = tokenTuple[TT_TOKEN] # type of local variable tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # 'varName' identifier name = tokenTuple[TT_TOKEN] # name of local variable self.st.Define(name, idType, kind) # define local varialbe result.append( (self.indentation * ' ') + '<SYMBOL-Defined> subroutine.' + name + ' (' + kind + ' ' + idType + ') = ' + str(self.st.IndexOf(name)) + ' </SYMBOL-Defined>') peekedTuple = self.__peekAtNextEntry__() while peekedTuple[TT_TOKEN] == ',': # 0 or more times (if ',' present): tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # symbol ',' tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # 'varName' indentifier name = tokenTuple[TT_TOKEN] # name of local variable self.st.Define(name, idType, kind) # define local varialbe result.append( (self.indentation * ' ') + '<SYMBOL-Defined> subroutine.' + name + ' (' + kind + ' ' + idType + ') = ' + str(self.st.IndexOf(name)) + ' </SYMBOL-Defined>') peekedTuple = self.__peekAtNextEntry__() tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # symbol ';' self.indentation -= 2 #indentation level re-adjustment. result.append( (self.indentation * ' ') + '</varDec>' ) # </varDec> return result def __compileStatements__(self): ''' compiles statements. returning a list of VM commands. assumes any leading and trailing braces are be consumed by the caller''' result = [] result.append( (self.indentation * ' ') + '<statements>' ) #structure label for class <statements> self.indentation += 2 #indentation level adjustment. it will be paired at the bottom with a negative re-adjustment peekedTuple = self.__peekAtNextEntry__() # if '__Statement' present, call it 0 or more times: while peekedTuple[TT_TOKEN] in ('let', 'if', 'while', 'do', 'return'): if peekedTuple[TT_TOKEN] == 'let': # if letStatement: result.extend( self.__compileLet__() ) # call to 'compileLet' elif peekedTuple[TT_TOKEN] == 'if': # OR if ifStatement: result.extend( self.__compileIf__() ) # call to 'compileIf' elif peekedTuple[TT_TOKEN] == 'while': # OR if whileStatement: result.extend( self.__compileWhile__() ) # call to 'compileWhile' elif peekedTuple[TT_TOKEN] == 'do': # OR if doStatement: result.extend( self.__compileDo__() ) # call to 'compileDo' elif peekedTuple[TT_TOKEN] == 'return': # OR if returnStatement: result.extend( self.__compileReturn__() ) # call to 'compileReturn' peekedTuple = self.__peekAtNextEntry__() self.indentation -= 2 #indentation level re-adjustment. result.append( (self.indentation * ' ') + '</statements>' ) # </statements> return result def __compileDo__(self): ''' compiles a function/method call. returning a list of VM commands. ''' result = [] result.append( (self.indentation * ' ') + '<doStatement>' ) #structure label <doStatement> self.indentation += 2 #indentation level adjustment. it will be paired at the bottom with a negative re-adjustment tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # keyword 'do' result.extend( self.__compileSubroutineCall__() ) # call to 'subroutineCall' tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # sybmol ';' if self.functionName: # if there was a two-part functionName declared (with object) in the subroutineCall if self.st.tableLookup(self.st.TypeOf(self.callerName)) not in ('int', 'char', 'boolean') and self.st.tableLookup(self.callerName) in ('subroutine', 'class'): # and if the object has been defined self.vmList.append( writeCall(self.st.TypeOf(self.callerName) + '.' + self.functionName, self.args + 1) ) # call the type of the object and the functionName else: self.vmList.append( writeCall(self.callerName + '.' + self.functionName, self.args) ) # otherwise, just call the function as written else: self.vmList.append( writeCall(self.className + '.' + self.callerName, self.args + 1) ) # if no object, just call the written function self.vmList.append( writePop('temp', 0) ) # pop the result to 'temp 0' self.indentation -= 2 #indentation level re-adjustment. result.append( (self.indentation * ' ') + '</doStatement>' ) # </doStatement> return result def __compileLet__(self): ''' compiles a variable assignment statement. returning a list of VM commands. ''' result = [] result.append( (self.indentation * ' ') + '<letStatement>' ) #structure label <letStatement> self.indentation += 2 #indentation level adjustment. it will be paired at the bottom with a negative re-adjustment tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # keyword 'let' tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # identifier 'varName' name = tokenTuple[TT_TOKEN] # identifier to be used result.append( (self.indentation * ' ') + '<SYMBOL-Used> ' + self.st.tableLookup(name) + '.' + name + ' (' + self.st.KindOf(name) + ' ' + self.st.TypeOf(name) + ') = ' + str(self.st.IndexOf(name)) + ' </SYMBOL-Used>') arrayDeclared = False # no array declared yet peekedTuple = self.__peekAtNextEntry__() if peekedTuple[TT_TOKEN] == '[': # 0 or 1 times (if '[' present): arrayDeclared = True # we have entered an array declaration tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # sybmol '[' result.extend( self.__compileExpression__() ) # call to 'expression' tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # symbol ']' tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # sybmol '=' result.extend( self.__compileExpression__() ) # call to 'expression' tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # sybmol ';' if arrayDeclared: # if an array has been declared, get the correct memory address and store the value self.vmList.append( writePop('temp', 0) ) self.vmList.append( writePush(self.st.KindOfVM(name), self.st.IndexOf(name)) ) self.vmList.append( 'add' ) self.vmList.append( writePop('pointer', 1) ) self.vmList.append( writePush('temp', 0) ) self.vmList.append( writePop('that', 0) ) else: self.vmList.append(writePop(self.st.KindOfVM(name), self.st.IndexOf(name))) # otherwise, pop the value to the variable self.indentation -= 2 #indentation level re-adjustment. result.append( (self.indentation * ' ') + '</letStatement>' ) # </letStatement> return result def __compileWhile__(self): ''' compiles a while loop. returning a list of VM commands. ''' result = [] result.append( (self.indentation * ' ') + '<whileStatement>' ) #structure label <whileStatement> self.indentation += 2 #indentation level adjustment. it will be paired at the bottom with a negative re-adjustment scopeLabel = CompilationEngine.__getSimpleLabel__() # get a number to make the label unique self.vmList.append( writeLabel('WHILE_TOP_' + scopeLabel) ) # while entry label tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # keyword 'while' tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # symbol '(' result.extend( self.__compileExpression__() ) # call to 'expression' tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # symbol ')' self.vmList.append( 'not' ) # negate the expression self.vmList.append( writeIf('WHILE_EXIT_' + scopeLabel) ) # if-goto statement to allow exit tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # symbol '{' result.extend( self.__compileStatements__() ) # call to 'statements' tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # symbol '}' self.vmList.append( writeGoto('WHILE_TOP_' + scopeLabel) ) # go-to start of while to evaluate self.vmList.append( writeLabel('WHILE_EXIT_' + scopeLabel) ) # while exit label self.indentation -= 2 #indentation level re-adjustment. result.append( (self.indentation * ' ') + '</whileStatement>' ) # </whileStatement> return result def __compileReturn__(self): ''' compiles a function return statement. returning a list of VM commands. ''' result = [] result.append( (self.indentation * ' ') + '<returnStatement>' ) #structure label <returnStatement> self.indentation += 2 #indentation level adjustment. it will be paired at the bottom with a negative re-adjustment tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # keyword 'return' peekedTuple = self.__peekAtNextEntry__() if peekedTuple[TT_TOKEN] != ';': # 0 or 1 times (until ';' reached) result.extend( self.__compileExpression__() ) # call to 'expression' tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # symbol ';' if self.subroutineType == 'void': self.vmList.append( writePush('constant', 0) ) self.vmList.append('return') self.indentation -= 2 #indentation level re-adjustment. result.append( (self.indentation * ' ') + '</returnStatement>' ) # </returnStatement> return result def __compileIf__(self): ''' compiles an if(else)? statement block. returning a list of VM commands. ''' result = [] result.append( (self.indentation * ' ') + '<ifStatement>' ) #structure label <ifStatement> self.indentation += 2 #indentation level adjustment. it will be paired at the bottom with a negative re-adjustment scopeLabel = CompilationEngine.__getSimpleLabel__() # get a number to make the label unique tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # keyword 'if' tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # symbol '(' result.extend( self.__compileExpression__() ) # call to 'expression' tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # symbol ')' self.vmList.append( 'not' ) # negate the expression self.vmList.append( writeIf('DO_ELSE_' + scopeLabel) ) # if-goto 'else' tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # sybmol '{' result.extend( self.__compileStatements__() ) # call to 'statements' tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # symbol '}' self.vmList.append( writeGoto('IF_THEN_COMPLETE_' + scopeLabel) ) # go-to end of if-then self.vmList.append( writeLabel('DO_ELSE_' + scopeLabel) ) # 'else' label peekedTuple = self.__peekAtNextEntry__() if peekedTuple[TT_TOKEN] == 'else': # 0 or 1 times (if 'else' present): tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # keyword 'else' tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # symbol '{' result.extend( self.__compileStatements__() ) # call to 'statements' tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # symbol '}' self.vmList.append( writeLabel('IF_THEN_COMPLETE_' + scopeLabel) ) # end of if-then self.indentation -= 2 #indentation level re-adjustment. result.append( (self.indentation * ' ') + '</ifStatement>' ) # </ifStatement> return result def __compileExpression__(self): ''' compiles an expression. returning a list of VM commands. ''' result = [] result.append( (self.indentation * ' ') + '<expression>' ) #structure label <expression> self.indentation += 2 #indentation level adjustment. it will be paired at the bottom with a negative re-adjustment result.extend ( self.__compileTerm__() ) # call to 'term' peekedTuple = self.__peekAtNextEntry__() while (peekedTuple[TT_TOKEN] in BINARY_OPERATORS or peekedTuple[TT_TOKEN] in UNARY_OPERATORS): # 0 or more times (if 'op' present): tokenTuple = self.__getNextEntry__() operator = tokenTuple[TT_TOKEN] # get the operator result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # 'op' symbol result.extend( self.__compileTerm__() ) # call to 'term' if operator in ('*', '/'): # we need to call the function self.vmList.append( writeCall( writeArithmetic(operator), 2) ) else: self.vmList.append( writeArithmetic(operator) ) # place the operator peekedTuple = self.__peekAtNextEntry__() self.indentation -= 2 #indentation level re-adjustment. result.append( (self.indentation * ' ') + '</expression>' ) # </expression> return result def __compileTerm__(self): ''' compiles a term. returning a list of VM commands. ''' result = [] result.append( (self.indentation * ' ') + '<term>' ) #structure label <term> self.indentation += 2 #indentation level adjustment. it will be paired at the bottom with a negative re-adjustment tokenTuple = self.__getNextEntry__() if tokenTuple[TT_TOKEN] == '(': # if '(': result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # symbol '(' result.extend( self.__compileExpression__() ) # call to 'expression' tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # symbol ')' elif (tokenTuple[TT_TOKEN] in BINARY_OPERATORS or tokenTuple[TT_TOKEN] in UNARY_OPERATORS): # if item in OPERATORS dict: result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # 'unaryOp' symbol result.extend( self.__compileTerm__() ) # call to 'term' self.vmList.append( UNARY_OPERATORS[tokenTuple[TT_TOKEN]]) # add the unary operator else: peekedTuple = self.__peekAtNextEntry__() if (peekedTuple[TT_TOKEN] == '(' or peekedTuple[TT_TOKEN] == '.'): # if 'identifier' followed immediately by '(' or '.': self.__replaceEntry__(tokenTuple) result.extend( self.__compileSubroutineCall__() ) # 'subroutineCall' if self.functionName: # if there was a two-part functionName declared (with object) in the subroutineCall if self.st.tableLookup(self.st.TypeOf(self.callerName)) not in ('int', 'char', 'boolean') and self.st.tableLookup(self.callerName) in ('subroutine', 'class'): # and if the object has been defined self.vmList.append( writeCall(self.st.TypeOf(self.callerName) + '.' + self.functionName, self.args + 1) ) # call the type of the object and the functionName else: self.vmList.append( writeCall(self.callerName + '.' + self.functionName, self.args) ) # otherwise, just call the function as written else: if tokenTuple[TT_TOKEN] in KEYWORDS: result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # keywordConstant if tokenTuple[TT_TOKEN] in ('true', 'false'): self.vmList.append( writePush('constant', 0) ) # 'push constant 0' if tokenTuple[TT_TOKEN] == 'true': self.vmList.append('not') # negate if 'true' elif tokenTuple[TT_TOKEN] == 'this': self.vmList.append( writePush('pointer', 0) ) # 'push pointer 0' elif tokenTuple[TT_TOKEN] == 'null': self.vmList.append( writePush('constant', 0) ) # 'push constant 0' elif 'Constant' in tokenTuple[TT_XML]: segment = 'constant' # 'constant' segment if 'integerConstant' in tokenTuple[TT_XML]: result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # integerConstant index = tokenTuple[TT_TOKEN] # constant number self.vmList.append( writePush(segment, index) ) # 'push constant #' elif 'stringConstant' in tokenTuple[TT_XML]: result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # stringConstant index = len(tokenTuple[TT_TOKEN]) # number of characters in string self.vmList.append( writePush(segment, index) ) # push constant # of chars self.vmList.append( writeCall('String.new', 1) ) # call new string fucntion for ch in tokenTuple[TT_TOKEN]: self.vmList.append( writePush('constant', ord(ch)) ) # get the ASCII values of the characters self.vmList.append( writeCall('String.appendChar', 2) ) # call the appendChar string function on each char else: result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # varName name = tokenTuple[TT_TOKEN] # varName identifier arrayDeclared = False # no array declared result.append( (self.indentation * ' ') + '<SYMBOL-Used> ' + self.st.tableLookup(name) + '.' + name + ' (' + self.st.KindOf(name) + ' ' + self.st.TypeOf(name) + ') = ' + str(self.st.IndexOf(name)) + ' </SYMBOL-Used>') if peekedTuple[TT_TOKEN] == '[': # if expression arrayDeclared = True # entering array declaration tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # symbol '[' result.extend( self.__compileExpression__() ) # call to expression tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # symbol ']' self.vmList.append( writePush(self.st.KindOfVM(name), self.st.IndexOf(name)) ) # push the variable name if arrayDeclared: # if the variable was an array, address the memory offset and store the value self.vmList.append('add') self.vmList.append( writePop('pointer', 1) ) self.vmList.append( writePush('that', 0) ) self.indentation -= 2 #indentation level re-adjustment. result.append( (self.indentation * ' ') + '</term>' ) # </term> return result def __compileExpressionList__(self): ''' compiles a list of expressions. returning a list of VM commands. ''' result = [] result.append( (self.indentation * ' ') + '<expressionList>' ) #structure label <expressionList> self.indentation += 2 #indentation level adjustment. it will be paired at the bottom with a negative re-adjustment peekedTuple = self.__peekAtNextEntry__() if peekedTuple[TT_TOKEN] != ')': # 0 or 1 times (if 'expression' present): result.extend( self.__compileExpression__() ) # 'expression' call self.args += 1 # argument encountered peekedTuple = self.__peekAtNextEntry__() while peekedTuple[TT_TOKEN] == ',': # 0 or more times (if ',' present): tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # symbol ',' result.extend( self.__compileExpression__() ) # 'expression' call self.args += 1 # argument encountered peekedTuple = self.__peekAtNextEntry__() self.indentation -= 2 #indentation level re-adjustment. result.append( (self.indentation * ' ') + '</expressionList>' ) # </expressionList> return result def __compileSubroutineCall__(self): ''' compiles a subroutine call. returning a list of VM commands. ''' result = [] tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # 'subroutineName' identifier OR 'className' identifier OR 'varName' identifier self.callerName = tokenTuple[TT_TOKEN] # funciton to be called or className or objectName self.args = 0 # check the number of arguments self.functionName = None # do we have a method or function name if self.st.KindOfVM(self.callerName) != 'NONE': # if the symbol has been defined result.append( (self.indentation * ' ') + '<SYMBOL-Used> ' + self.st.tableLookup(self.callerName) + '.' + self.callerName + ' (' + self.st.KindOf(self.callerName) + ' ' + self.st.TypeOf(self.callerName) + ') = ' + str(self.st.IndexOf(self.callerName)) + ' </SYMBOL-Used>') peekedTuple = self.__peekAtNextEntry__() if peekedTuple[TT_TOKEN] == '.': # if 'identifier' followed by '.': tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # 'symbol '.' tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # 'subroutineName' identifier self.functionName = tokenTuple[TT_TOKEN] # we have a method or function name if self.functionName: # if there was a two-part functionName declared (with object) in the subroutineCall if self.st.tableLookup(self.st.TypeOf(self.callerName)) not in ('int', 'char', 'boolean') and self.st.tableLookup(self.callerName) in ('subroutine', 'class'): # and if the object has been defined self.vmList.append( writePush(self.st.KindOfVM(self.callerName), self.st.IndexOf(self.callerName)) ) # call the type of the object and the functionName else: self.vmList.append( writePush('pointer', 0) ) # otherwise we're operating on current object tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # symbol '(' result.extend( self.__compileExpressionList__() ) # 'expressionList' call tokenTuple = self.__getNextEntry__() result.append( (self.indentation * ' ') + tokenTuple[TT_XML]) # symbol ')' return result