Exemple #1
0
    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("}")
Exemple #2
0
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)