def compileClass(self): self._process_token('class') self.classname = self._process_ttype(IDENTIFIER) self._process_token('{') # open symbol table self.st = VMSymbolTable(self.classname) token = self._get_token() while token != '}': if token in ('field', 'static'): self.compileClassVarDec() elif token in ('constructor', 'method', 'function'): self.compileSubroutine() else: raise Exception("wtf?", token) token = self._get_token() self._process_token("}")
class CompilationEngine(object): """ Effects the actual compilation output. Gets its input from a JackTokenizer and emits its parsed structure into an output file/stream. The output is generated by a series of compilexxx() routines, one for every syntactic element xxx of the Jack grammar. The contract between these routines is that each compilexxx() routine should read the syntactic construct xxx from the input, advance() the tokenizer exactly beyond xxx, and output the parsing of xxx. Thus, compilexxx() may only be called if indeed xxx is the next syntactic element of the input. In the first version of the compiler, described in Chapter 10, this module emits a structured printout of the code, wrapped in XML tags. In the final version of the compiler, described in Chapter 11, this module generates executable VM code. In both cases, the parsing logic and module API are exactly the same. """ _get_token = lambda self: self.tknz.current_token _get_ttype = lambda self: self.tknz.token_type _get_nttype = lambda self: TYPES[self._get_ttype()] def _process_ttype(self, *ttype_options): """ processes a token based on its type. returns token. """ saved = self._get_token() print(GREENON+saved+RESETCOLOR) assert self._get_ttype() in ttype_options, \ r"unexpected token type: " \ r"%s should be in %s" % (TYPES[self._get_ttype()], [TYPES[e] for e in ttype_options]) self.tknz.advance() return saved def _process_token(self, *token_options): """ processes a token based on its format. returns token. """ saved = self._get_token() print(GREENON+saved+RESETCOLOR) if token_options != (True,): assert self._get_token() in token_options, \ r"unexpected token: " \ r"'%s' should be in '%s'" % (self._get_token(), token_options) self.tknz.advance() return saved def __init__(self, tokenizer, vmwriter, classname=None): self.tknz = tokenizer self.vmw = vmwriter self.st = None self.classname = None self.tknz.advance() self.compileClass() self.vmw.close() def compileClass(self): self._process_token('class') self.classname = self._process_ttype(IDENTIFIER) self._process_token('{') # open symbol table self.st = VMSymbolTable(self.classname) token = self._get_token() while token != '}': if token in ('field', 'static'): self.compileClassVarDec() elif token in ('constructor', 'method', 'function'): self.compileSubroutine() else: raise Exception("wtf?", token) token = self._get_token() self._process_token("}") def compileClassVarDec(self): var_kind = self._process_token("static", "field") var_type = self._process_ttype(KEYWORD, IDENTIFIER) variables = [] variables.append(self._process_ttype(IDENTIFIER)) while self._get_token() == ',': self._process_token(',') variables.append(self._process_ttype(IDENTIFIER)) self._process_token(";") scope = self.st.parent for var_name in variables: scope.addVar(var_name, var_type, var_kind) print(scope) def compileSubroutine(self): subr_type = self._process_token("method", "constructor", "function") retr_type = self._process_ttype(KEYWORD, IDENTIFIER) subr_name = self._process_ttype(IDENTIFIER) self.whilecount = 0 self.ifcount = 0 self.st.createScope(subr_name, subr_type) self.current_subr = subr_name self._process_token("(") self.compileParameterList() self._process_token(")") self._process_token('{') while self._get_token() == 'var': self.compileVarDec() self.vmw.write("function %s.%s %s" % (self.classname, self.current_subr, self.st.getScope(self.current_subr).countKind('local'))) if subr_type == 'constructor': class_vars_count = self.st.parent.countKind('field') self.vmw.write("push constant %s" % class_vars_count) self.vmw.write("call Memory.alloc 1") self.vmw.write("pop pointer 0") if subr_type == 'method': self.vmw.write("push argument 0") self.vmw.write("pop pointer 0") if self._get_token() != '}': self.compileStatements() self._process_token("}") def compileStatements(self): while self._get_token() != '}': if self._get_token() == 'do': self.compileDo() elif self._get_token() == 'if': self.compileIf() elif self._get_token() == 'let': self.compileLet() elif self._get_token() == 'while': self.compileWhile() elif self._get_token() == 'return': self.compileReturn() else: raise Exception("invalid statement:", self._get_token()) def compileVarDec(self): self._process_token("var") var_type = self._process_ttype(KEYWORD, IDENTIFIER) variables = [] while True: variables.append(self._process_ttype(IDENTIFIER)) if self._get_token() != ',': break self._process_token(",") self._process_token(";") scope = self.st.getScope(self.current_subr) for var_name in variables: scope.addVar(var_name, var_type, 'local') print(scope) def compileParameterList(self): # it might be empty if self._get_token() == ')': return variables = [] while True: var_type = self._process_ttype(KEYWORD, IDENTIFIER) var_name = self._process_ttype(IDENTIFIER) variables.append((var_name, var_type)) if self._get_token() != ',': break self._process_token(',') scope = self.st.getScope(self.current_subr) for (var_name, var_type) in variables: scope.addVar(var_name, var_type, 'argument') print(scope) def compileDo(self): # TRANSFORM THIS INTO A COMPILE CALL FUNCTION???? """ possible calls: do a.b(); // a is a var => b is a method do c.b(); // c is a class => b isn't a method do m(); // m is a method to this class """ self._process_token('do') a = b = None a = self._process_ttype(IDENTIFIER) # it's an a.b() call if self._get_token() == '.': self._process_token('.') b = self._process_ttype(IDENTIFIER) else: pass # it's an m() call args_count = 0 # check if it's a method call if not b: # it's a call to an inner method self.vmw.write("push pointer 0") args_count += 1 b = a a = self.classname call_class = a elif self.st.isAccessible(self.current_subr, a): # a is a variable var_add = self.st.getAddress(self.current_subr, a) self.vmw.write("push %s %s" % var_add) # push object args_count += 1 call_class = self.st.getVar(self.current_subr, a)['type'] else: # not a method call call_class = a self._process_token("(") args_count += self.compileExpressionList() self._process_token(")") self._process_token(";") self.vmw.write("call %s.%s %s" % (call_class, b, args_count)) self.vmw.write("pop temp 0") # throw out return value def compileExpression(self): operators = {'+': 'add', '-': 'sub', '*': 'call Math.multiply 2', '/': 'call Math.divide 2', '&': 'and', '|': 'or', '<': 'lt', '>': 'gt', '=': 'eq'} self.compileTerm() while self._get_token() in operators: token = self._get_token() self._process_token(token) self.compileTerm() self.vmw.write(operators[token]) def compileTerm(self): ttype = self._get_ttype() token = self._get_token() if ttype == INT_CONST: self.vmw.write("push constant %s" % token) self._process_ttype(INT_CONST) elif ttype == STRING_CONST: str_size = len(token)-2 self.vmw.write("push constant %s" % str_size) self.vmw.write("call String.new 1") for char in token[1:-1]: self.vmw.write("push constant %s" % ord(char)) self.vmw.write("call String.appendChar 2") self._process_ttype(STRING_CONST) elif token in ('true', 'false', 'null'): self.vmw.write("push constant 0") if token == 'true': self.vmw.write("not") self._process_token("true", "false", "null") elif token == 'this': self.vmw.write("push pointer 0") self._process_token("this") elif ttype == IDENTIFIER: a = self._process_ttype(IDENTIFIER) # it's a function call # this section follows pattern of compileDo if self._get_token() in ('.', '('): if self._get_token() == '.': # its an a.b() call self._process_token('.') b = self._process_ttype(IDENTIFIER) else: pass # it's an a() call args_count = 0 # check if it's a method call if not b: # it's a call to an inner method self.vmw.write("push pointer 0") args_count += 1 b = a a = self.classname call_class = a elif self.st.isAccessible(self.current_subr, a): # a is a var var_add = self.st.getAddress(self.current_subr, a) self.vmw.write("push %s %s" % var_add) # push object args_count += 1 call_class = self.st.getVar(self.current_subr, a)['type'] else: # not a method call call_class = a self._process_token("(") args_count += self.compileExpressionList() self._process_token(")") self.vmw.write("call %s.%s %s" % (call_class, b, args_count)) elif self._get_token() == '[': # process a[] assert self.st.isAccessible(self.current_subr, a) var_add = self.st.getAddress(self.current_subr, a) self._process_token("[") self.compileExpression() self._process_token("]") self.vmw.write("push %s %s" % var_add) self.vmw.write("add") self.vmw.write("pop pointer 1") self.vmw.write("push that 0") else: assert self.st.isAccessible(self.current_subr, a) var_add = self.st.getAddress(self.current_subr, a) self.vmw.write("push %s %s" % var_add) elif token == '(': # '(' expression ')' self._process_token('(') self.compileExpression() self._process_token(')') # already done above elif token in ('-', '~'): # unary opeartion term if token == '-': op = 'neg' elif token == '~': op = 'not' self._process_token("-", "~") self.compileTerm() self.vmw.write(op) else: raise Exception("aqui! termo nao identificado", token) def compileExpressionList(self): exps = 0 while self._get_token() != ')': self.compileExpression() exps += 1 if self._get_token() == ',': self._process_token(',') return exps def compileReturn(self): self._process_token("return") if self._get_token() != ';': self.compileExpression() else: self.vmw.write("push constant 0") self._process_token(";") self.vmw.write("return") def compileLet(self): self._process_token("let") var_name = self._process_ttype(IDENTIFIER) assert self.st.isAccessible(self.current_subr, var_name) var_add = self.st.getAddress(self.current_subr, var_name) if self._get_token() == '[': self._process_token("[") self.compileExpression() # push k self._process_token("]") self.vmw.write("push %s %s" % var_add) self.vmw.write("add") self._process_token("=") self.compileExpression() self.vmw.write("pop temp 0") self.vmw.write("pop pointer 1") self.vmw.write("push temp 0") self.vmw.write("pop that 0") else: self._process_token("=") self.compileExpression() self.vmw.write("pop %s %s" % var_add) self._process_token(";") def compileWhile(self): thiswhilecount = self.whilecount self.whilecount += 1 self._process_token('while') self.vmw.write("label WHILE_EXP%s" % thiswhilecount) self._process_token("(") self.compileExpression() self._process_token(")") self._process_token("{") self.vmw.write("not") self.vmw.write("if-goto WHILE_END%s" % thiswhilecount) self.compileStatements() self.vmw.write("goto WHILE_EXP%s" % thiswhilecount) self.vmw.write("label WHILE_END%s" % thiswhilecount) self._process_token("}") def compileIf(self): thisifcount = self.ifcount self.ifcount += 1 self._process_token('if') self._process_token("(") self.compileExpression() self._process_token(")") self.vmw.write("if-goto IF_TRUE%s" % thisifcount) self.vmw.write("goto IF_FALSE%s" % thisifcount) self.vmw.write("label IF_TRUE%s" % thisifcount) self._process_token("{") self.compileStatements() self._process_token("}") if self._get_token() == 'else': self.vmw.write("goto IF_END%s" % thisifcount) self.vmw.write("label IF_FALSE%s" % thisifcount) self._process_token('else') self._process_token("{") self.compileStatements() self._process_token("}") self.vmw.write("label IF_END%s" % thisifcount) else: self.vmw.write("label IF_FALSE%s" % thisifcount)