class CompilationEngine: """Recursive top-down parser""" def __init__(self, inFile, outFile): """Creates a new compilation engine with the given input and output. The next routine called must be compileClass()""" self.tokenizer = JackTokenizer(inFile) self.targetFile = open(outFile, 'w') self.getNext() self.classTable = None self.className = '' self.writer = VMWriter(outFile) self.labelWhile = 1 self.labelIf = 1 def getNext(self): if self.tokenizer.hasMoreTokens(): self.tokenizer.advance() def compileClass(self): """Compiles a complete class""" self.classTable = SymbolTable() # 'class' className '{' classVarDec* subroutineDec* '}' # class self.getNext() # className self.className = self.tokenizer.getToken() self.getNext() # { self.getNext() token = self.tokenizer.getToken() while token in ["static", "field"]: self.compileDec() token = self.tokenizer.getToken() token = self.tokenizer.getToken() while token in ["constructor", "function", "method"]: self.compileSubroutine() token = self.tokenizer.getToken() # } self.getNext() def compileSubroutine(self): """Compiles a complete method, function, or constructor.""" # subroutine dec self.classTable.startSubroutine() # ('constructor' | 'function' | 'method') ('void' | type) subroutineName '(' parameterList ')' subroutineBody # ('constructor' | 'function' | 'method') subroutineType = self.tokenizer.getToken() self.getNext() # ('void' | type) self.getNext() # subroutineName name = self.tokenizer.getToken() self.getNext() # ( self.getNext() # parameterList self.compileParameterList(subroutineType == 'method') # ) self.getNext() # subroutine body # '{' varDec* statements '}' # { self.getNext() # varDec* while self.tokenizer.getToken() == 'var': self.compileDec() numOfVars = self.classTable.varCount(Toolbox.VAR) if subroutineType == 'function': self.writer.writeFunction(self.className + "." + name, numOfVars) elif subroutineType == 'constructor': self.writer.writeFunction(self.className + "." + name, numOfVars) # push constant (num of fields) # call Memory.alloc 1 # pop pointer 0 fields = self.classTable.varCount(Toolbox.FIELD) self.writer.writePush(Toolbox.CONST, fields) self.writer.writeCall('Memory.alloc', 1) self.writer.writePop(Toolbox.POINTER, 0) else: # method self.writer.writeFunction(self.className + "." + name, numOfVars) # push argument 0 # pop pointer 0 self.writer.writePush(Toolbox.SEG_ARG, 0) self.writer.writePop(Toolbox.POINTER, 0) # statements self.compileStatements() # } self.getNext() def compileParameterList(self, method=False): """Compiles a (possibly empty) parameter list, not including the enclosing "()".""" tokenType, name = '', '' if method: # Add this to method's var list. self.classTable.define(None, None, Toolbox.ARG) if self.tokenizer.tokenType() != self.tokenizer.SYMBOL: # param list not empty while True: tokenType = self.tokenizer.getToken() self.getNext() name = self.tokenizer.getToken() self.classTable.define(name, tokenType, Toolbox.ARG) self.getNext() if self.tokenizer.getToken() == ')': break self.getNext() # ',' def compileStatements(self): # (letStatement | ifStatement | whileStatement | doStatement | returnStatement)* """Compiles a sequence of statements, not including the enclosing "{}".""" token = self.tokenizer.getToken() while token in ["let", "if", "while", "do", "return"]: if token == 'let': self.compileLet() elif token == 'if': self.compileIf() elif token == 'while': self.compileWhile() elif token == 'do': self.compileDo() elif token == 'return': self.compileReturn() token = self.tokenizer.getToken() def compileSubroutineCall(self, name, printIdentifier=True): # subroutineName '(' expressionList ') ' | ( className | varName) '.' subroutineName '(' expressionList ') ' var = None nArgs = 0 if printIdentifier: # subroutineName | ( className | varName) self.getNext() var = self.classTable.searchScope(name) if self.tokenizer.getToken() == '.': if var: # push <this> self.writer.writePush(var[0], var[1]) nArgs += 1 className = var[2] # Use the type instead of the variable name else: className = name self.getNext() subroutineName = self.tokenizer.getToken() self.getNext() else: # push <this> self.writer.writePush(Toolbox.POINTER, 0) nArgs += 1 className = self.className subroutineName = name name = className + '.' + subroutineName # '(' self.getNext() nArgs += self.compileExpressionList() self.writer.writeCall(name, nArgs) # ')' self.getNext() def compileDo(self): # 'do' subroutineCall ';' """Compiles a do statement""" # do self.getNext() # subroutineCall self.compileSubroutineCall(self.tokenizer.getToken()) self.writer.writePop(Toolbox.TEMP, 0) # ; if self.tokenizer.getToken() == ';': self.getNext() def compileLet(self): # 'let' varName ('[' expression ']')? '=' expression ';' """Compiles a let statement""" # let # self.targetFile.write(T_LET) self.getNext() # var name name = self.tokenizer.getToken() # search scope segment, index, type = self.classTable.searchScope(name) self.getNext() # [ array = False if self.tokenizer.getToken() == '[': array = True self.writer.writePush(segment, index) self.getNext() # expression self.compileExpression() # ] self.getNext() self.writer.writeArithmetic('add') # = self.getNext() # expression self.compileExpression() if array: self.writer.writePop(Toolbox.TEMP, 0) self.writer.writePop(Toolbox.TEMP, 1) self.writer.writePush(Toolbox.TEMP, 0) self.writer.writePush(Toolbox.TEMP, 1) self.writer.writePop(Toolbox.POINTER, 1) self.writer.writePop(Toolbox.THAT, 0) else: self.writer.writePop(segment, index) # ; token = self.tokenizer.getToken() if token == ';': self.getNext() def compileWhile(self): # while' '(' expression ')' '{' statements '}' """Compiles a while statement""" # while label = str(self.labelWhile) self.labelWhile += 1 self.writer.writeLabel('while' + label) self.getNext() # ( self.getNext() # expression self.compileExpression() # ) self.getNext() self.writer.writeArithmetic('not') self.writer.writeIf('endwhile' + label) # { self.getNext() # statements self.compileStatements() # } self.getNext() self.writer.writeGoto('while' + label) self.writer.writeLabel('endwhile' + label) def compileReturn(self): # 'return' expression? ';' """Compiles a return statement""" # return self.getNext() # expression if not (self.tokenizer.getToken() == ";"): self.compileExpression() else: self.writer.writePush(Toolbox.CONST, 0) self.writer.writeReturn() # ; self.getNext() def compileIf(self): # 'if' '(' expression ')' '{' statements '}' ( 'else' '{' statements '}' )? """Compiles an if statement, possibly with a trailing else clause""" # if label = 'if' + str(self.labelIf) self.labelIf += 1 self.getNext() # ( self.getNext() # expression self.compileExpression() # ) self.getNext() self.writer.writeArithmetic('not') self.writer.writeIf('else' + label) # { self.getNext() # statements self.compileStatements() # } self.getNext() self.writer.writeGoto('end' + label) self.writer.writeLabel('else' + label) # else if self.tokenizer.getToken() == 'else': self.getNext() # { self.getNext() # expression self.compileStatements() # } self.getNext() self.writer.writeLabel('end' + label) def compileExpression(self): """Compiles an expression""" # term (op term)* self.compileTerm() token = self.tokenizer.getToken() while token in ['+', '/', '-', '*', '&', '|', '>', '<', '=']: self.getNext() self.compileTerm() self.writer.writeArithmetic(token) token = self.tokenizer.getToken() def compileTerm(self): #integerConstant | stringConstant | keywordConstant | varName | varName '[' expression']' | # subroutineCall | '(' expression ')' | unaryOp term """Compiles a term""" token = self.tokenizer.getToken() tokenType = self.tokenizer.tokenType() if tokenType == self.tokenizer.INT_CONST: self.writer.writePush(Toolbox.CONST, token) self.getNext() elif tokenType == self.tokenizer.STRING_CONST: self.writer.writePush(Toolbox.CONST, len(token)) self.writer.writeCall('String.new', 1) for c in token: self.writer.writePush(Toolbox.CONST, ord(c)) self.writer.writeCall('String.appendChar', 2) self.getNext() elif tokenType == self.tokenizer.KEYWORD: # true | false | null | this self.compileKeywordConstant(token) elif tokenType == self.tokenizer.IDENTIFIER: name = token self.getNext() token = self.tokenizer.getToken() if token == '[': self.compileVarName(name) self.getNext() self.compileExpression() self.getNext() self.writer.writeArithmetic('add') self.writer.writePop(Toolbox.POINTER, 1) self.writer.writePush(Toolbox.THAT, 0) elif token in ['(', '.']: self.compileSubroutineCall(name, False) else: self.compileVarName(name) elif token == '(': self.getNext() self.compileExpression() self.getNext() elif token in ['-', '~']: self.compileUnary(token) def compileExpressionList(self): """Compiles a (possibly empty) comma separated list of expressions""" nArgs = 0 if self.tokenizer.getToken() != ')': self.compileExpression() nArgs += 1 while self.tokenizer.getToken() == ',': self.getNext() self.compileExpression() nArgs += 1 return nArgs def compileDec(self): # 'var' type varName (',' varName)* ';' """Compiles a var declaration""" # keyword 'var' token = self.tokenizer.getToken() kind = None if token == 'var': kind = Toolbox.VAR elif token == 'field': kind = Toolbox.FIELD elif token == 'static': kind = Toolbox.STATIC self.getNext() tokenType = self.tokenizer.getToken() # type can be an identifier or a keyword self.getNext() # var name name = self.tokenizer.getToken() self.classTable.define(name, tokenType, kind) self.getNext() while self.tokenizer.tokenType() == self.tokenizer.SYMBOL and self.tokenizer.getToken() == ',': # , self.getNext() name = self.tokenizer.getToken() self.classTable.define(name, tokenType, kind) # var name self.getNext() # ; self.getNext() def compileVarName(self, name): segment, index, type = self.classTable.searchScope(name) self.writer.writePush(segment, index) def compileKeywordConstant(self, keyword): if keyword == 'false' or keyword == 'null': self.writer.writePush(Toolbox.CONST, 0) if keyword == 'true': self.writer.writePush(Toolbox.CONST, 0) self.writer.writeArithmetic('not') if keyword == 'this': self.writer.writePush(Toolbox.POINTER, 0) self.getNext() def compileUnary(self, token): """ Compiles an unary operator with its operand (term) :param token: unary token """ self.getNext() # '~' or '-' self.compileTerm() # operand if token == '-': self.writer.writeArithmetic('neg') else: # token is '~' self.writer.writeArithmetic('not')