class CompEngine: def __init__(self, f): self.file_name = f self.Tokens = Tokenizer(open(f, 'r').read()) self.outStr = '' self.curr_level = 0 def get_xml(self): return self.outStr def PrTkn(self, s): for i in range(self.curr_level): self.outStr += ' ' typ, name = s self.outStr += ' '.join(('<' + typ + '>', name, '</' + typ + '>')) self.outStr += '\n' def Beg(self, stm): for i in range(self.curr_level): self.outStr += ' ' self.outStr += '<' + stm + '>\n' self.curr_level += 1 # print(self.outStr) def End(self, stm): self.curr_level -= 1 for i in range(self.curr_level): self.outStr += ' ' self.outStr += '</' + stm + '>\n' # print(self.outStr) def eat(self, s): # print(self.outStr) a = self.Tokens.eat(s) if (a): self.PrTkn((a, s)) # print(self.outStr) def eat_next(self): # print(self.outStr) a = self.Tokens.next_token() if (a): self.PrTkn(a) # print(self.outStr, 2) def compileClass(self): self.Beg('class') self.eat('class') self.eat(self.Tokens.curr_token()[1]) #class Name self.eat('{') while (self.Tokens.curr_token()[1] in ('static', 'field')): self.compileClassVarDec() while (self.Tokens.curr_token()[1] in ('constructor', 'function', 'method')): self.compileSubroutineDec() self.eat('}') self.End('class') def compileClassVarDec(self): self.Beg('classVarDec') if (self.Tokens.curr_token()[1] in ('static', 'field')): self.eat(self.Tokens.curr_token()[1]) else: print('Expected: static or field') sys.exit() if (self.Tokens.curr_token()[1] in ('char', 'int', 'boolean')): self.eat_next() elif (self.Tokens.curr_token()[0] == 'identifier' ): #check for class_name self.eat_next() else: print('expected vartype0') self.compileVarName() typ, tok = self.Tokens.curr_token() while (tok == ','): self.eat(',') self.compileVarName() typ, tok = self.Tokens.curr_token() self.eat(';') self.End('classVarDec') def compileSubroutineDec(self): self.Beg('subroutineDec') if (self.Tokens.curr_token()[1] in ('function', 'method')): self.eat(self.Tokens.curr_token()[1]) elif (self.Tokens.curr_token()[1] == 'constructor'): self.eat_next() # self.eat_next() #for new <constructor Square new> else: print('Expected: constructor or fucntion or method') sys.exit() if (self.Tokens.curr_token()[1] in ('char', 'int', 'boolean', 'void')): self.eat_next() elif (self.Tokens.curr_token()[0] == 'identifier'): self.eat_next() else: print('Expected Type, got none') sys.exit() self.compileSubroutineName() self.eat('(') self.compileParameterList() self.eat(')') self.compileSubroutineBody() self.End('subroutineDec') def compileParameterList(self): self.Beg('parameterList') if (self.Tokens.curr_token()[1] != ')'): if (self.Tokens.curr_token()[1] in ('char', 'int', 'boolean')): self.eat_next() elif (self.Tokens.curr_token()[0] == 'identifier' ): #check for class_name self.eat_next() else: print("exprected vartype1") self.compileVarName() typ, tok = self.Tokens.curr_token() while (tok == ','): self.eat(',') if (self.Tokens.curr_token()[1] in ('char', 'int', 'boolean')): self.eat_next() elif (self.Tokens.curr_token()[0] == 'identifier' ): #check for class_name self.eat_next() else: print("exprected vartype1") self.compileVarName() typ, tok = self.Tokens.curr_token() self.End('parameterList') def compileSubroutineBody(self): self.Beg('subroutineBody') self.eat('{') typ, tok = self.Tokens.curr_token() while (tok == 'var'): self.compileVarDec() typ, tok = self.Tokens.curr_token() self.compileStatements() self.eat('}') self.End('subroutineBody') def compileVarDec(self): self.Beg('varDec') self.eat('var') if (self.Tokens.curr_token()[1] in ('char', 'int', 'boolean')): self.eat_next() elif (self.Tokens.curr_token()[0] == 'identifier' ): #check for class_name self.eat_next() else: print("exprected vartype2, got :", self.Tokens.curr_token()[1]) self.compileVarName() typ, tok = self.Tokens.curr_token() while (tok == ','): self.eat(',') self.compileVarName() typ, tok = self.Tokens.curr_token() self.eat(';') self.End('varDec') def compileVarName(self): self.eat_next() def compileSubroutineName(self): self.eat_next() def compileWhileStatement(self): self.Beg('whileStatement') self.eat('while') self.eat('(') self.compileExpression() self.eat(')') self.eat('{') self.compileStatements() self.eat('}') self.End('whileStatement') # while (self.Tokens.next_token() != ) def compileStatements(self): self.Beg('statements') while (self.Tokens.curr_token()[1] != '}'): self.compileStatement() self.End('statements') def compileStatement(self): __, tok = self.Tokens.curr_token() if tok == 'let': self.compileLet() elif tok == 'if': self.compileIf() elif tok == 'while': self.compileWhileStatement() elif tok == 'do': self.compileDo() elif tok == 'return': self.compileReturn() else: print("Expected a statement, but not found ") sys.exit() def compileLet(self): self.Beg('letStatement') self.eat('let') # self.compileVarName() self.eat_next() if (self.Tokens.curr_token()[1] == '['): self.eat('[') self.compileExpression() self.eat(']') self.eat('=') self.compileExpression() self.eat(';') self.End('letStatement') def compileIf(self): self.Beg('ifStatement') self.eat('if') self.eat('(') self.compileExpression() self.eat(')') self.eat('{') self.compileStatements() self.eat('}') if (self.Tokens.curr_token()[1] == 'else'): self.eat('else') self.eat('{') self.compileStatements() self.eat('}') self.End('ifStatement') def compileDo(self): self.Beg('doStatement') self.eat('do') self.compileSubroutineCall() self.eat(';') self.End('doStatement') def compileReturn(self): self.Beg('returnStatement') self.eat('return') if (self.Tokens.curr_token()[1] != ';'): self.compileExpression() self.eat(';') self.End('returnStatement') def compileExpression(self): # print(1) self.Beg('expression') self.compileTerm() op = '+-*/|=' sop = ['>', '<', '&'] # print(self.Tokens.curr_token()[1]) while (self.Tokens.curr_token()[1] in op or self.Tokens.curr_token()[1] in sop): # print(50) self.eat(self.Tokens.curr_token()[1]) self.compileTerm() self.End('expression') def compileTerm(self): # there was some confusion here, there is likely to be a bug self.Beg('term') typ, tok = self.Tokens.curr_token() if tok in '-~': self.eat_next() self.compileTerm() elif typ == 'integerConstant' or typ == 'stringConstant' or typ == 'keyword': self.eat(tok) elif typ == 'identifier': ntyp, ntok = self.Tokens.peek_ahead() if (ntok == '.' or ntok == '('): self.compileSubroutineCall() elif (ntok == '['): self.compileVarName() self.eat('[') self.compileExpression() self.eat(']') else: # print(50) self.compileVarName() elif tok == '(': self.eat('(') self.compileExpression() self.eat(')') self.End('term') def compileSubroutineCall(self): # self.Beg('subroutineCall') typ, tok = self.Tokens.curr_token() ntyp, ntok = self.Tokens.peek_ahead() if (ntok == '.'): self.eat(tok) self.eat(ntok) self.compileSubroutineName() else: self.eat_next() self.eat('(') self.compileExpressionList() self.eat(')') # self.End('subroutineCall') def compileExpressionList(self): self.Beg('expressionList') typ, tok = self.Tokens.curr_token() if (tok != ')'): self.compileExpression() ntyp, ntok = self.Tokens.curr_token() while (ntok != ')'): self.eat(',') self.compileExpression() ntyp, ntok = self.Tokens.curr_token() # print(ntok) # print(ntok) # sys.exit() self.End('expressionList')
class CompEngine: def __init__(self, f): self.file_name = f self.err_file = f[:-5] + '.err' self.Tokens = Tokenizer(open(f, 'r').read(), self.err_file) self.outStr = '' self.curr_level = 0 self.class_symbol_table = {} self.field_count = 0 self.static_count = 0 self.class_name = '' self.vmCode = '' self.numLabels = 0 self.op_table = { '+': 'add', '-': 'sub', '&': 'and', '|': 'or', '<': 'lt', '>': 'gt', '=': 'eq', '*': 'call Math.multiply 2', '/': 'call Math.divide 2' } self.subR_symbol_table = {} self.lcl_count = 0 self.arg_count = 0 def add_class_var(self, name, kind, typ): # print('class: ', typ) if (kind == 'static'): self.class_symbol_table[name] = { 'kind': kind, 'type': typ, 'index': self.static_count } self.static_count += 1 elif (kind == 'field'): self.class_symbol_table[name] = { 'kind': kind, 'type': typ, 'index': self.field_count } self.field_count += 1 def add_subR_var(self, name, kind, typ): # print('subR: ', typ) if (kind == 'argument'): self.subR_symbol_table[name] = { 'kind': kind, 'type': typ, 'index': self.arg_count } self.arg_count += 1 elif (kind == 'local'): self.subR_symbol_table[name] = { 'kind': kind, 'type': typ, 'index': self.lcl_count } self.lcl_count += 1 def get_xml(self): return self.outStr def get_vm(self): return self.vmCode def PrTkn(self, s): for i in range(self.curr_level): self.outStr += ' ' typ, name = s self.outStr += ' '.join(('<' + typ + '>', name, '</' + typ + '>')) self.outStr += '\n' def Beg(self, stm): for i in range(self.curr_level): self.outStr += ' ' self.outStr += '<' + stm + '>\n' self.curr_level += 1 # print(self.outStr) def End(self, stm): self.curr_level -= 1 for i in range(self.curr_level): self.outStr += ' ' self.outStr += '</' + stm + '>\n' # print(self.outStr) def eat(self, s): # print(self.outStr) a = self.Tokens.eat(s) if (a): self.PrTkn((a, s)) # print(self.outStr) def eat_next(self): # print(self.outStr) a = self.Tokens.next_token() if (a): self.PrTkn(a) # print(self.outStr, 2) def compileClass(self): self.Beg('class') self.eat('class') self.class_name = self.Tokens.curr_token()[1] self.eat(self.Tokens.curr_token()[1]) #class Name self.eat('{') while (self.Tokens.curr_token()[1] in ('static', 'field')): self.compileClassVarDec() while (self.Tokens.curr_token()[1] in ('constructor', 'function', 'method')): self.compileSubroutineDec() self.eat('}') self.End('class') def compileClassVarDec(self): self.Beg('classVarDec') kind = self.Tokens.curr_token()[1] if (self.Tokens.curr_token()[1] in ('static', 'field')): kind = self.Tokens.curr_token()[1] self.eat(self.Tokens.curr_token()[1]) else: print('Expected: static or field') sys.exit() typ = self.Tokens.curr_token()[1] if (self.Tokens.curr_token()[1] in ('char', 'int', 'boolean')): self.eat_next() elif (self.Tokens.curr_token()[0] == 'identifier' ): #check for class_name self.eat_next() else: print('expected vartype0') varN = self.Tokens.curr_token()[1] self.compileVarName() self.add_class_var(varN, kind, typ) tok = self.Tokens.curr_token()[1] while (tok == ','): self.eat(',') varN = self.Tokens.curr_token()[1] self.compileVarName() self.add_class_var(varN, kind, typ) tok = self.Tokens.curr_token()[1] self.eat(';') self.End('classVarDec') def compileSubroutineDec(self): self.Beg('subroutineDec') self.arg_count = 0 self.lcl_count = 0 self.subR_symbol_table = {} s = self.Tokens.curr_token()[1] if (self.Tokens.curr_token()[1] in ('constructor', 'function', 'method')): self.currSubRoutineType = self.Tokens.curr_token()[1] self.eat(self.Tokens.curr_token()[1]) else: err_out = 'ERROR: Expected <keyword> but ' + s + '.\n' open(self.err_file, 'w').write(err_out) sys.exit() self.currSubRoutineReturnType = self.Tokens.curr_token()[1] s = self.Tokens.curr_token()[1] if (self.Tokens.curr_token()[1] in ('char', 'int', 'boolean', 'void')): self.eat_next() elif (self.Tokens.curr_token()[0] == 'identifier'): self.eat_next() else: err_out = 'ERROR: Expected <keyword> but ' + s + '.\n' open(self.err_file, 'w').write(err_out) sys.exit() return False self.currSubRoutineName = self.Tokens.curr_token()[1] self.compileSubroutineName() if (self.currSubRoutineType == 'method'): self.add_subR_var('this', 'argument', self.class_name) self.eat('(') self.compileParameterList() self.eat(')') self.compileSubroutineBody() self.End('subroutineDec') def compileParameterList(self): self.Beg('parameterList') if (self.Tokens.curr_token()[1] != ')'): varType = self.Tokens.curr_token()[1] if (self.Tokens.curr_token()[1] in ('char', 'int', 'boolean')): self.eat_next() elif (self.Tokens.curr_token()[0] == 'identifier' ): #check for class_name self.eat_next() else: print("exprected vartype1") varName = self.Tokens.curr_token()[1] self.add_subR_var(varName, 'argument', varType) self.compileVarName() typ, tok = self.Tokens.curr_token() while (tok == ','): self.eat(',') varType = self.Tokens.curr_token()[1] if (self.Tokens.curr_token()[1] in ('char', 'int', 'boolean')): self.eat_next() elif (self.Tokens.curr_token()[0] == 'identifier' ): #check for class_name self.eat_next() else: print("exprected vartype1") varName = self.Tokens.curr_token()[1] self.add_subR_var(varName, 'argument', varType) self.compileVarName() typ, tok = self.Tokens.curr_token() self.End('parameterList') def compileSubroutineBody(self): self.Beg('subroutineBody') self.eat('{') typ, tok = self.Tokens.curr_token() while (tok == 'var'): self.compileVarDec() typ, tok = self.Tokens.curr_token() self.vmCode += 'function ' + self.class_name + '.' + self.currSubRoutineName + ' ' + str( self.lcl_count) + '\n' if (self.currSubRoutineType == 'constructor'): self.vmCode += 'push constant ' + str(self.field_count) + '\n' self.vmCode += 'call Memory.alloc 1\n' self.vmCode += 'pop pointer 0\n' elif (self.currSubRoutineType == 'method'): self.vmCode += 'push argument 0\n' self.vmCode += 'pop pointer 0\n' # self.vmCode += 'push this 0\n' self.compileStatements() self.eat('}') self.End('subroutineBody') def compileVarDec(self): self.Beg('varDec') self.eat('var') varType = self.Tokens.curr_token()[1] if (self.Tokens.curr_token()[1] in ('char', 'int', 'boolean')): self.eat_next() elif (self.Tokens.curr_token()[0] == 'identifier' ): #check for class_name self.eat_next() else: print("exprected vartype2, got :") varName = self.Tokens.curr_token()[1] self.add_subR_var(varName, 'local', varType) self.compileVarName() typ, tok = self.Tokens.curr_token() while (tok == ','): self.eat(',') varName = self.Tokens.curr_token()[1] self.add_subR_var(varName, 'local', varType) self.compileVarName() typ, tok = self.Tokens.curr_token() self.eat(';') self.End('varDec') def compileVarName1(self): typ, varName = self.Tokens.curr_token() varKind, varIndex, varType = 0, 0, 0 id1 = varName if (varName in self.subR_symbol_table.keys()): varKind, varIndex, varType = self.subR_symbol_table[id1][ 'kind'], self.subR_symbol_table[id1][ 'index'], self.subR_symbol_table[id1]['type'] elif (varName in self.class_symbol_table.keys()): varKind, varIndex, varType = self.class_symbol_table[id1][ 'kind'], self.class_symbol_table[id1][ 'index'], self.class_symbol_table[id1]['type'] elif (typ == 'identifier'): print('Variable', varName, 'used but was undeclared') err_out = 'Declaration error: ' + varName + ' undeclared.' open(self.err_file, 'w').write(err_out) sys.exit() else: print('Expected identifier, but got', typ) err_out = 'ERROR: Expecting <identifier> but ' + varName + '\n' open(self.err_file, 'w').write(err_out) sys.exit() self.eat_next() return varName, varKind, varType, varIndex def compileVarName(self): self.eat_next() def compileSubroutineName(self): self.eat_next() def compileWhileStatement(self): tLabel = self.numLabels self.numLabels += 2 self.Beg('whileStatement') self.eat('while') self.eat('(') self.vmCode += 'label ' + self.class_name + '_' + str(tLabel) + '\n' self.compileExpression() self.vmCode += 'not\n' self.vmCode += 'if-goto ' + self.class_name + '_' + str(tLabel + 1) + '\n' self.eat(')') self.eat('{') self.compileStatements() self.eat('}') self.vmCode += 'goto ' + self.class_name + '_' + str(tLabel) + '\n' self.vmCode += 'label ' + self.class_name + '_' + str(tLabel + 1) + '\n' self.End('whileStatement') # while (self.Tokens.next_token() != ) def compileStatements(self): self.Beg('statements') while (self.Tokens.curr_token()[1] != '}'): self.compileStatement() self.End('statements') def compileStatement(self): __, tok = self.Tokens.curr_token() if tok == 'let': self.compileLet() elif tok == 'if': self.compileIf() elif tok == 'while': self.compileWhileStatement() elif tok == 'do': self.compileDo() elif tok == 'return': self.compileReturn() else: err_out = 'ERROR: Expected <keyword> but ' + tok + '.\n' open(self.err_file, 'w').write(err_out) sys.exit() def compileLet(self): self.Beg('letStatement') self.eat('let') varName, varKind, varType, varIndex = self.compileVarName1() # self.eat_next() if (self.Tokens.curr_token()[1] == '['): self.eat('[') self.compileExpression() self.eat(']') if (varKind == 'field'): varKind = 'this' self.vmCode += 'push ' + varKind + ' ' + str(varIndex) + '\n' self.vmCode += 'add\n' self.eat('=') self.compileExpression() self.vmCode += 'pop temp 0\n' self.vmCode += 'pop pointer 1\n' self.vmCode += 'push temp 0\n' self.vmCode += 'pop that 0\n' self.eat(';') else: self.eat('=') self.compileExpression() if (varKind == 'field'): varKind = 'this' self.vmCode += 'pop ' + varKind + ' ' + str(varIndex) + '\n' self.eat(';') self.End('letStatement') def compileIf(self): tLabel = self.numLabels self.numLabels += 2 self.Beg('ifStatement') self.eat('if') self.eat('(') self.compileExpression() self.eat(')') self.eat('{') self.vmCode += 'not\n' self.vmCode += 'if-goto ' + self.class_name + '_' + str(tLabel) + '\n' self.compileStatements() self.eat('}') self.vmCode += 'goto ' + self.class_name + '_' + str(tLabel + 1) + '\n' self.vmCode += 'label ' + self.class_name + '_' + str(tLabel) + '\n' if (self.Tokens.curr_token()[1] == 'else'): self.eat('else') self.eat('{') self.compileStatements() self.eat('}') self.vmCode += 'label ' + self.class_name + '_' + str(tLabel + 1) + '\n' self.End('ifStatement') def compileDo(self): self.Beg('doStatement') self.eat('do') self.compileSubroutineCallDo() self.eat(';') self.End('doStatement') def compileReturn(self): self.Beg('returnStatement') self.eat('return') if (self.Tokens.curr_token()[1] != ';'): self.compileExpression() else: self.vmCode += 'push constant 0\n' self.eat(';') self.vmCode += 'return\n' self.End('returnStatement') def compileExpression(self): # print(1) self.Beg('expression') self.compileTerm() opr = '+-*/|=' sop = ['>', '<', '&'] # print(self.Tokens.curr_token()[1]) while (self.Tokens.curr_token()[1] in opr or self.Tokens.curr_token()[1] in sop): op = self.op_table[self.Tokens.curr_token()[1]] self.eat(self.Tokens.curr_token()[1]) self.compileTerm() self.vmCode += op + '\n' self.End('expression') def compileTerm(self): # there was some confusion here, there is likely to be a bug self.Beg('term') typ, tok = self.Tokens.curr_token() if tok in '-~': self.eat_next() self.compileTerm() if (tok == '-'): self.vmCode += 'neg\n' elif (tok == '~'): self.vmCode += 'not\n' elif typ == 'integerConstant': self.eat(tok) self.vmCode += 'push constant ' + str(tok) + '\n' elif typ == 'stringConstant': self.eat(tok) strlen = len(tok) self.vmCode += 'push constant ' + str(strlen) + '\n' self.vmCode += 'call String.new 1\n' for i in range(strlen): self.vmCode += 'push constant ' + str(ord(tok[i])) + '\n' self.vmCode += 'call String.appendChar 2\n' elif typ == 'keyword': self.eat_next() if tok == 'true': self.vmCode += 'push constant 0\n' self.vmCode += 'not\n' elif tok == 'false' or tok == 'null': self.vmCode += 'push constant 0\n' elif tok == 'this': self.vmCode += 'push pointer 0\n' else: err_out = 'ERROR: Expecting <keywordConstant> but ' + tok + '\n' open(self.err_file, 'w').write(err_out) sys.exit() elif typ == 'identifier': ntyp, ntok = self.Tokens.peek_ahead() if (ntok == '.' or ntok == '('): self.compileSubroutineCallTerm() elif (ntok == '['): varName, varKind, varType, varIndex = self.compileVarName1() if (varKind == 'field'): varKind = 'this' self.eat('[') self.compileExpression() self.vmCode += 'push ' + varKind + ' ' + str( varIndex ) + '\n' #Check next 2 lines, different from slides self.vmCode += 'add\n' self.vmCode += 'pop pointer 1\n' self.vmCode += 'push that 0\n' self.eat(']') else: varName, varKind, varType, varIndex = self.compileVarName1() if (varKind == 'field'): varKind = 'this' self.vmCode += 'push ' + varKind + ' ' + str(varIndex) + '\n' elif tok == '(': self.eat('(') self.compileExpression() self.eat(')') self.End('term') def compileSubroutineCallDo(self): # self.Beg('subroutineCall') typ, tok = self.Tokens.curr_token() ntyp, ntok = self.Tokens.peek_ahead() id1 = tok obKind, obIndex, obType = 0, 0, 0 #Possibility of a bug here id2 = '' if (ntok == '.'): self.eat(tok) self.eat(ntok) __, id2 = self.Tokens.curr_token() if (id1 in self.subR_symbol_table.keys()): obKind, obIndex, obType = self.subR_symbol_table[id1][ 'kind'], self.subR_symbol_table[id1][ 'index'], self.subR_symbol_table[id1]['type'] if (obKind == 'field'): obKind = 'this' self.vmCode += 'push ' + obKind + ' ' + str(obIndex) + '\n' elif (id1 in self.class_symbol_table.keys()): obKind, obIndex, obType = self.class_symbol_table[id1][ 'kind'], self.class_symbol_table[id1][ 'index'], self.class_symbol_table[id1]['type'] if (obKind == 'field'): obKind = 'this' self.vmCode += 'push ' + obKind + ' ' + str(obIndex) + '\n' self.compileSubroutineName() else: self.eat_next() self.vmCode += 'push pointer 0\n' self.eat('(') nP = self.compileExpressionList() self.eat(')') if (ntok == '.'): if (obKind != 0): self.vmCode += 'call ' + obType + '.' + id2 + ' ' + str( nP + 1) + '\n' else: self.vmCode += 'call ' + id1 + '.' + id2 + ' ' + str(nP) + '\n' else: self.vmCode += 'call ' + self.class_name + '.' + id1 + ' ' + str( nP + 1) + '\n' self.vmCode += 'pop temp 0\n' # self.End('subroutineCall') def compileSubroutineCallTerm(self): # self.Beg('subroutineCall') typ, tok = self.Tokens.curr_token() ntyp, ntok = self.Tokens.peek_ahead() id1 = tok obKind, obIndex, obType = 0, 0, 0 #Possibility of a bug here if (ntok == '.'): self.eat(tok) self.eat(ntok) __, id2 = self.Tokens.curr_token() if (id1 in self.subR_symbol_table.keys()): obKind, obIndex, obType = self.subR_symbol_table[id1][ 'kind'], self.subR_symbol_table[id1][ 'index'], self.subR_symbol_table[id1]['type'] if (obKind == 'field'): obKind = 'this' self.vmCode += 'push ' + obKind + ' ' + str(obIndex) + '\n' elif (id1 in self.class_symbol_table.keys()): obKind, obIndex, obType = self.class_symbol_table[id1][ 'kind'], self.class_symbol_table[id1][ 'index'], self.class_symbol_table[id1]['type'] if (obKind == 'field'): obKind = 'this' self.vmCode += 'push ' + obKind + ' ' + str(obIndex) + '\n' self.compileSubroutineName() else: self.eat_next() self.vmCode += 'push pointer 0\n' self.eat('(') nP = self.compileExpressionList() self.eat(')') if (ntok == '.'): if (obKind != 0): self.vmCode += 'call ' + obType + '.' + id2 + ' ' + str( nP + 1) + '\n' else: self.vmCode += 'call ' + id1 + '.' + id2 + ' ' + str(nP) + '\n' else: self.vmCode += 'call ' + self.class_name + '.' + id1 + ' ' + str( nP + 1) + '\n' # self.End('subroutineCall') def compileExpressionList(self): self.Beg('expressionList') typ, tok = self.Tokens.curr_token() numEx = 0 if (tok != ')'): self.compileExpression() numEx += 1 ntyp, ntok = self.Tokens.curr_token() while (ntok != ')'): self.eat(',') self.compileExpression() numEx += 1 ntyp, ntok = self.Tokens.curr_token() # print(ntok) # print(ntok) # sys.exit() self.End('expressionList') return numEx
class CompEngine: def __init__(self, f): self.file_name = f self.err_file = f[:-5] + '.err' self.Tokens = Tokenizer(open(f, 'r').read(), self.err_file) self.outStr = '' self.curr_level = 0 self.class_symbol_table = {} self.field_count = 0 self.static_count = 0 self.class_name = '' self.vmCode = '' self.numLabels = 0 self.op_table = { '+': 'add', '-': 'sub', '&': 'and', '|': 'or', '<': 'lt', '>': 'gt', '=': 'eq', '*': 'call Math.multiply 2', '/': 'call Math.divide 2' } self.subR_symbol_table = {} self.lcl_count = 0 self.arg_count = 0 def add_class_var(self, name, kind, typ): # Adds a variable to the class symbol table. if (kind == 'static'): self.class_symbol_table[name] = { 'kind': kind, 'type': typ, 'index': self.static_count } self.static_count += 1 elif (kind == 'field'): self.class_symbol_table[name] = { 'kind': kind, 'type': typ, 'index': self.field_count } self.field_count += 1 def add_subR_var(self, name, kind, typ): # Adds a variable to the subroutine symbol table. if (kind == 'argument'): self.subR_symbol_table[name] = { 'kind': kind, 'type': typ, 'index': self.arg_count } self.arg_count += 1 elif (kind == 'local'): self.subR_symbol_table[name] = { 'kind': kind, 'type': typ, 'index': self.lcl_count } self.lcl_count += 1 def get_xml(self): return self.outStr def get_vm(self): return self.vmCode def PrTkn(self, s): #This was a helper function for the XML file to get the proper indentation. for i in range(self.curr_level): self.outStr += ' ' typ, name = s self.outStr += ' '.join(('<' + typ + '>', name, '</' + typ + '>')) self.outStr += '\n' def Beg(self, stm): #This was a helper function for the XML file to print the staring of a new instance eg. <whileStament>. for i in range(self.curr_level): self.outStr += ' ' self.outStr += '<' + stm + '>\n' self.curr_level += 1 def End(self, stm): #This was a helper function for the XML file to print the ending of a new instance eg. </whileStament>. self.curr_level -= 1 for i in range(self.curr_level): self.outStr += ' ' self.outStr += '</' + stm + '>\n' def eat(self, s): # Calls the eat function of the Tokenizer. # (That functions compares the next token with the expected token <s>, and throws an error in case of a mismatch) a = self.Tokens.eat(s) if (a): self.PrTkn((a, s)) def eat_next(self): # Moves the tokenizer, to the next token. a = self.Tokens.next_token() if (a): self.PrTkn(a) def compileClass(self): self.Beg('class') self.eat('class') self.class_name = self.Tokens.curr_token()[1] self.eat(self.Tokens.curr_token()[1]) #class Name self.eat('{') while (self.Tokens.curr_token()[1] in ('static', 'field')): self.compileClassVarDec() while (self.Tokens.curr_token()[1] in ('constructor', 'function', 'method')): self.compileSubroutineDec() self.eat('}') self.End('class') def compileClassVarDec(self): self.Beg('classVarDec') kind = self.Tokens.curr_token()[1] if (self.Tokens.curr_token()[1] in ('static', 'field')): kind = self.Tokens.curr_token()[1] self.eat(self.Tokens.curr_token()[1]) else: err_out = 'ERROR: Expected <keyword> but ' + kind + '.\n' open(self.err_file, 'w').write(err_out) sys.exit() typ = self.Tokens.curr_token()[1] if (self.Tokens.curr_token()[1] in ('char', 'int', 'boolean')): self.eat_next() elif (self.Tokens.curr_token()[0] == 'identifier' ): #check for class_name self.eat_next() else: err_out = 'ERROR: Expected <variable type> but ' + typ + '.\n' open(self.err_file, 'w').write(err_out) sys.exit() varN = self.Tokens.curr_token()[1] self.compileVarName() self.add_class_var(varN, kind, typ) tok = self.Tokens.curr_token()[1] while (tok == ','): self.eat(',') varN = self.Tokens.curr_token()[1] self.compileVarName() self.add_class_var(varN, kind, typ) tok = self.Tokens.curr_token()[1] self.eat(';') self.End('classVarDec') def compileSubroutineDec(self): self.Beg('subroutineDec') self.arg_count = 0 self.lcl_count = 0 self.subR_symbol_table = {} s = self.Tokens.curr_token()[1] if (self.Tokens.curr_token()[1] in ('constructor', 'function', 'method')): self.currSubRoutineType = self.Tokens.curr_token()[1] self.eat(self.Tokens.curr_token()[1]) else: err_out = 'ERROR: Expected <keyword> but ' + s + '.\n' open(self.err_file, 'w').write(err_out) sys.exit() self.currSubRoutineReturnType = self.Tokens.curr_token()[1] s = self.Tokens.curr_token()[1] if (self.Tokens.curr_token()[1] in ('char', 'int', 'boolean', 'void')): self.eat_next() elif (self.Tokens.curr_token()[0] == 'identifier'): self.eat_next() else: err_out = 'ERROR: Expected <keyword> but ' + s + '.\n' open(self.err_file, 'w').write(err_out) sys.exit() return False self.currSubRoutineName = self.Tokens.curr_token()[1] self.compileSubroutineName() if (self.currSubRoutineType == 'method'): self.add_subR_var('this', 'argument', self.class_name) self.eat('(') self.compileParameterList() self.eat(')') self.compileSubroutineBody() self.End('subroutineDec') def compileParameterList(self): self.Beg('parameterList') if (self.Tokens.curr_token()[1] != ')'): varType = self.Tokens.curr_token()[1] if (self.Tokens.curr_token()[1] in ('char', 'int', 'boolean')): self.eat_next() elif (self.Tokens.curr_token()[0] == 'identifier' ): #check for valid identifier. self.eat_next() else: print("exprected vartype1") err_out = 'ERROR: ' + varType + '.\n' open(self.err_file, 'w').write(err_out) sys.exit() print("exprected vartype1") varName = self.Tokens.curr_token()[1] self.add_subR_var(varName, 'argument', varType) self.compileVarName() typ, tok = self.Tokens.curr_token() while (tok == ','): self.eat(',') varType = self.Tokens.curr_token()[1] if (self.Tokens.curr_token()[1] in ('char', 'int', 'boolean')): self.eat_next() elif (self.Tokens.curr_token()[0] == 'identifier' ): #check for valid identifier. self.eat_next() else: err_out = 'ERROR: ' + varType + '.\n' open(self.err_file, 'w').write(err_out) sys.exit() print("exprected vartype1") varName = self.Tokens.curr_token()[1] self.add_subR_var(varName, 'argument', varType) self.compileVarName() typ, tok = self.Tokens.curr_token() self.End('parameterList') def compileSubroutineBody(self): self.Beg('subroutineBody') self.eat('{') typ, tok = self.Tokens.curr_token() while (tok == 'var'): self.compileVarDec() typ, tok = self.Tokens.curr_token() self.vmCode += 'function ' + self.class_name + '.' + self.currSubRoutineName + ' ' + str( self.lcl_count) + '\n' if (self.currSubRoutineType == 'constructor'): self.vmCode += 'push constant ' + str(self.field_count) + '\n' self.vmCode += 'call Memory.alloc 1\n' self.vmCode += 'pop pointer 0\n' elif (self.currSubRoutineType == 'method'): self.vmCode += 'push argument 0\n' self.vmCode += 'pop pointer 0\n' self.compileStatements() self.eat('}') self.End('subroutineBody') def compileVarDec(self): self.Beg('varDec') self.eat('var') varType = self.Tokens.curr_token()[1] if (self.Tokens.curr_token()[1] in ('char', 'int', 'boolean')): self.eat_next() elif (self.Tokens.curr_token()[0] == 'identifier' ): #check for class_name self.eat_next() else: print("exprected vartype2, got :") err_out = 'ERROR:' + s + '\n' open(self.err_file, 'w').write(err_out) sys.exit() varName = self.Tokens.curr_token()[1] self.add_subR_var(varName, 'local', varType) self.compileVarName() typ, tok = self.Tokens.curr_token() while (tok == ','): self.eat(',') varName = self.Tokens.curr_token()[1] self.add_subR_var(varName, 'local', varType) self.compileVarName() typ, tok = self.Tokens.curr_token() self.eat(';') self.End('varDec') def compileVarName1(self): #This functions checks for the varName in the symbol table and if it doesn't exist, throws an error. typ, varName = self.Tokens.curr_token() varKind, varIndex, varType = 0, 0, 0 id1 = varName if (varName in self.subR_symbol_table.keys()): varKind, varIndex, varType = self.subR_symbol_table[id1][ 'kind'], self.subR_symbol_table[id1][ 'index'], self.subR_symbol_table[id1]['type'] elif (varName in self.class_symbol_table.keys()): varKind, varIndex, varType = self.class_symbol_table[id1][ 'kind'], self.class_symbol_table[id1][ 'index'], self.class_symbol_table[id1]['type'] elif (typ == 'identifier'): print('Variable', varName, 'used but was undeclared') err_out = 'Declaration error: ' + varName + ' undeclared.' open(self.err_file, 'w').write(err_out) sys.exit() else: print('Expected identifier, but got', typ) err_out = 'ERROR: Expecting <identifier> but ' + varName + '\n' open(self.err_file, 'w').write(err_out) sys.exit() self.eat_next() return varName, varKind, varType, varIndex def compileVarName(self): #Initially this function had a more useful role, but now it doesn't serve much purpose. self.eat_next() def compileSubroutineName(self): #Initially this function had a more useful role, but now it doesn't serve much purpose. self.eat_next() def compileWhileStatement(self): tLabel = self.numLabels self.numLabels += 2 self.Beg('whileStatement') self.eat('while') self.eat('(') self.vmCode += 'label ' + self.class_name + '_' + str(tLabel) + '\n' self.compileExpression() self.vmCode += 'not\n' self.vmCode += 'if-goto ' + self.class_name + '_' + str(tLabel + 1) + '\n' self.eat(')') self.eat('{') self.compileStatements() self.eat('}') self.vmCode += 'goto ' + self.class_name + '_' + str(tLabel) + '\n' self.vmCode += 'label ' + self.class_name + '_' + str(tLabel + 1) + '\n' self.End('whileStatement') def compileStatements(self): self.Beg('statements') while (self.Tokens.curr_token()[1] != '}'): self.compileStatement() self.End('statements') def compileStatement(self): __, tok = self.Tokens.curr_token() if tok == 'let': self.compileLet() elif tok == 'if': self.compileIf() elif tok == 'while': self.compileWhileStatement() elif tok == 'do': self.compileDo() elif tok == 'return': self.compileReturn() else: err_out = 'ERROR: Expected <keyword> but ' + tok + '.\n' open(self.err_file, 'w').write(err_out) sys.exit() def compileLet(self): self.Beg('letStatement') self.eat('let') varName, varKind, varType, varIndex = self.compileVarName1() if (self.Tokens.curr_token()[1] == '['): self.eat('[') self.compileExpression() self.eat(']') if (varKind == 'field'): varKind = 'this' self.vmCode += 'push ' + varKind + ' ' + str(varIndex) + '\n' self.vmCode += 'add\n' self.eat('=') self.compileExpression() self.vmCode += 'pop temp 0\n' self.vmCode += 'pop pointer 1\n' self.vmCode += 'push temp 0\n' self.vmCode += 'pop that 0\n' self.eat(';') else: self.eat('=') self.compileExpression() if (varKind == 'field'): varKind = 'this' self.vmCode += 'pop ' + varKind + ' ' + str(varIndex) + '\n' self.eat(';') self.End('letStatement') def compileIf(self): tLabel = self.numLabels self.numLabels += 2 self.Beg('ifStatement') self.eat('if') self.eat('(') self.compileExpression() self.eat(')') self.eat('{') self.vmCode += 'not\n' self.vmCode += 'if-goto ' + self.class_name + '_' + str(tLabel) + '\n' self.compileStatements() self.eat('}') self.vmCode += 'goto ' + self.class_name + '_' + str(tLabel + 1) + '\n' self.vmCode += 'label ' + self.class_name + '_' + str(tLabel) + '\n' if (self.Tokens.curr_token()[1] == 'else'): self.eat('else') self.eat('{') self.compileStatements() self.eat('}') self.vmCode += 'label ' + self.class_name + '_' + str(tLabel + 1) + '\n' self.End('ifStatement') def compileDo(self): self.Beg('doStatement') self.eat('do') self.compileSubroutineCallDo() self.eat(';') self.End('doStatement') def compileReturn(self): self.Beg('returnStatement') self.eat('return') if (self.Tokens.curr_token()[1] != ';'): self.compileExpression() else: self.vmCode += 'push constant 0\n' self.eat(';') self.vmCode += 'return\n' self.End('returnStatement') def compileExpression(self): self.Beg('expression') self.compileTerm() opr = '+-*/|=' sop = ['>', '<', '&'] while (self.Tokens.curr_token()[1] in opr or self.Tokens.curr_token()[1] in sop): op = self.op_table[self.Tokens.curr_token()[1]] self.eat(self.Tokens.curr_token()[1]) self.compileTerm() self.vmCode += op + '\n' self.End('expression') def compileTerm(self): #I have implemented this function slightly differently than in slides. # I have split it's functionality between this function and compileSubroutineTerm. self.Beg('term') typ, tok = self.Tokens.curr_token() if tok in '-~': self.eat_next() self.compileTerm() if (tok == '-'): self.vmCode += 'neg\n' elif (tok == '~'): self.vmCode += 'not\n' elif typ == 'integerConstant': self.eat(tok) self.vmCode += 'push constant ' + str(tok) + '\n' elif typ == 'stringConstant': self.eat(tok) strlen = len(tok) self.vmCode += 'push constant ' + str(strlen) + '\n' self.vmCode += 'call String.new 1\n' for i in range(strlen): self.vmCode += 'push constant ' + str(ord(tok[i])) + '\n' self.vmCode += 'call String.appendChar 2\n' elif typ == 'keyword': self.eat_next() if tok == 'true': self.vmCode += 'push constant 0\n' self.vmCode += 'not\n' elif tok == 'false' or tok == 'null': self.vmCode += 'push constant 0\n' elif tok == 'this': self.vmCode += 'push pointer 0\n' else: err_out = 'ERROR: Expecting <keywordConstant> but ' + tok + '\n' open(self.err_file, 'w').write(err_out) sys.exit() elif typ == 'identifier': ntyp, ntok = self.Tokens.peek_ahead() if (ntok == '.' or ntok == '('): self.compileSubroutineCallTerm() elif (ntok == '['): varName, varKind, varType, varIndex = self.compileVarName1() if (varKind == 'field'): varKind = 'this' self.eat('[') self.compileExpression() self.vmCode += 'push ' + varKind + ' ' + str( varIndex ) + '\n' #Check next 2 lines, different from slides self.vmCode += 'add\n' self.vmCode += 'pop pointer 1\n' self.vmCode += 'push that 0\n' self.eat(']') else: varName, varKind, varType, varIndex = self.compileVarName1() if (varKind == 'field'): varKind = 'this' self.vmCode += 'push ' + varKind + ' ' + str(varIndex) + '\n' elif tok == '(': self.eat('(') self.compileExpression() self.eat(')') self.End('term') def compileSubroutineCallDo(self): # self.Beg('subroutineCall') typ, tok = self.Tokens.curr_token() ntyp, ntok = self.Tokens.peek_ahead() id1 = tok obKind, obIndex, obType = 0, 0, 0 id2 = '' if (ntok == '.'): self.eat(tok) self.eat(ntok) __, id2 = self.Tokens.curr_token() if (id1 in self.subR_symbol_table.keys()): obKind, obIndex, obType = self.subR_symbol_table[id1][ 'kind'], self.subR_symbol_table[id1][ 'index'], self.subR_symbol_table[id1]['type'] if (obKind == 'field'): obKind = 'this' self.vmCode += 'push ' + obKind + ' ' + str(obIndex) + '\n' elif (id1 in self.class_symbol_table.keys()): obKind, obIndex, obType = self.class_symbol_table[id1][ 'kind'], self.class_symbol_table[id1][ 'index'], self.class_symbol_table[id1]['type'] if (obKind == 'field'): obKind = 'this' self.vmCode += 'push ' + obKind + ' ' + str(obIndex) + '\n' self.compileSubroutineName() else: self.eat_next() self.vmCode += 'push pointer 0\n' self.eat('(') nP = self.compileExpressionList() self.eat(')') if (ntok == '.'): if (obKind != 0): self.vmCode += 'call ' + obType + '.' + id2 + ' ' + str( nP + 1) + '\n' else: self.vmCode += 'call ' + id1 + '.' + id2 + ' ' + str(nP) + '\n' else: self.vmCode += 'call ' + self.class_name + '.' + id1 + ' ' + str( nP + 1) + '\n' self.vmCode += 'pop temp 0\n' # self.End('subroutineCall') def compileSubroutineCallTerm(self): # self.Beg('subroutineCall') typ, tok = self.Tokens.curr_token() ntyp, ntok = self.Tokens.peek_ahead() id1 = tok obKind, obIndex, obType = 0, 0, 0 if (ntok == '.'): self.eat(tok) self.eat(ntok) __, id2 = self.Tokens.curr_token() if (id1 in self.subR_symbol_table.keys()): obKind, obIndex, obType = self.subR_symbol_table[id1][ 'kind'], self.subR_symbol_table[id1][ 'index'], self.subR_symbol_table[id1]['type'] if (obKind == 'field'): obKind = 'this' self.vmCode += 'push ' + obKind + ' ' + str(obIndex) + '\n' elif (id1 in self.class_symbol_table.keys()): obKind, obIndex, obType = self.class_symbol_table[id1][ 'kind'], self.class_symbol_table[id1][ 'index'], self.class_symbol_table[id1]['type'] if (obKind == 'field'): obKind = 'this' self.vmCode += 'push ' + obKind + ' ' + str(obIndex) + '\n' self.compileSubroutineName() else: self.eat_next() self.vmCode += 'push pointer 0\n' self.eat('(') nP = self.compileExpressionList() self.eat(')') if (ntok == '.'): if (obKind != 0): self.vmCode += 'call ' + obType + '.' + id2 + ' ' + str( nP + 1) + '\n' else: self.vmCode += 'call ' + id1 + '.' + id2 + ' ' + str(nP) + '\n' else: self.vmCode += 'call ' + self.class_name + '.' + id1 + ' ' + str( nP + 1) + '\n' # self.End('subroutineCall') def compileExpressionList(self): self.Beg('expressionList') typ, tok = self.Tokens.curr_token() numEx = 0 if (tok != ')'): self.compileExpression() numEx += 1 ntyp, ntok = self.Tokens.curr_token() while (ntok != ')'): self.eat(',') self.compileExpression() numEx += 1 ntyp, ntok = self.Tokens.curr_token() self.End('expressionList') return numEx