Esempio n. 1
0
class CompilationEngine:
    def __init__(self, inputPath, outputPath):
        self.tokenizer = Tokenizer(inputPath)
        self.outputFile = open(outputPath, 'w')
        self.tokenizer.advance()
        self.indentLevel = 0

    def CompileClass(self):
        """
        Compiles a complete class.
        """
        self.EnterScope("class")

        self.ConsumeKeyword([Keyword.CLASS])
        self.ConsumeIdentifier()  # className
        self.ConsumeSymbol('{')

        while (self.IsKeyword([Keyword.STATIC, Keyword.FIELD])):
            self.CompileClassVarDec()

        # subroutineDec*
        while (self.IsKeyword([Keyword.CONSTRUCTOR, Keyword.FUNCTION,
                               Keyword.METHOD])):
            self.CompileSubroutine()

        self.ConsumeSymbol('}')

        self.ExitScope("class")
        self.outputFile.close()

    def CompileClassVarDec(self):
        """
        Compiles a static declaration or a field declaration.
        """
        self.EnterScope("classVarDec")

        self.ConsumeKeyword([Keyword.STATIC, Keyword.FIELD])
        self.ConsumeType()
        self.ConsumeIdentifier()  # varName

        while (self.IsSymbol([','])):
            self.ConsumeSymbol(',')
            self.ConsumeIdentifier()  # varName

        self.ConsumeSymbol(';')

        self.ExitScope("classVarDec")

    def CompileSubroutine(self):
        """
        Compiles a complete method, function, or constructor.
        """
        self.EnterScope("subroutineDec")

        self.ConsumeKeyword([Keyword.CONSTRUCTOR, Keyword.FUNCTION,
                             Keyword.METHOD])
        if (self.IsKeyword([Keyword.VOID])):
            self.ConsumeKeyword([Keyword.VOID])
        else:
            self.ConsumeType()

        self.ConsumeIdentifier()  # subroutineName

        self.ConsumeSymbol('(')
        self.CompileParameterList()
        self.ConsumeSymbol(')')

        self.CompileSubroutineBody()

        self.ExitScope("subroutineDec")

    def CompileSubroutineBody(self):
        self.EnterScope("subroutineBody")

        self.ConsumeSymbol('{')
        while (self.IsKeyword([Keyword.VAR])):
            self.CompileVarDec()
        self.CompileStatements()
        self.ConsumeSymbol('}')

        self.ExitScope("subroutineBody")

    def CompileParameterList(self):
        """
        Compiles a (possibly empty) parameter list,
        not including the enclosing "()".
        """
        self.EnterScope("parameterList")

        if (not self.IsSymbol([')'])):
            self.ConsumeType()
            self.ConsumeIdentifier()

        while(self.IsSymbol([','])):
            self.ConsumeSymbol(',')
            self.ConsumeType()
            self.ConsumeIdentifier()

        self.ExitScope("parameterList")

    def CompileVarDec(self):
        """
        Compiles a var declaration.
        """
        self.EnterScope("varDec")

        self.ConsumeKeyword([Keyword.VAR])
        self.ConsumeType()
        self.ConsumeIdentifier()  # varName
        while (self.IsSymbol([','])):
            self.ConsumeSymbol(',')
            self.ConsumeIdentifier()  # varName

        self.ConsumeSymbol(';')

        self.ExitScope("varDec")

    def CompileStatements(self):
        """
        Compiles a sequence of statements, not including the
        enclosing "{}".
        """
        self.EnterScope("statements")

        while self.IsKeyword([Keyword.LET, Keyword.IF, Keyword.WHILE,
                              Keyword.DO, Keyword.RETURN]):
            if self.IsKeyword([Keyword.LET]):
                self.CompileLet()

            if self.IsKeyword([Keyword.IF]):
                self.CompileIf()

            if self.IsKeyword([Keyword.WHILE]):
                self.CompileWhile()

            if self.IsKeyword([Keyword.DO]):
                self.CompileDo()

            if self.IsKeyword([Keyword.RETURN]):
                self.CompileReturn()

        self.ExitScope("statements")

    def CompileDo(self):
        """
        Compiles a do statement.
        """
        self.EnterScope("doStatement")

        self.ConsumeKeyword([Keyword.DO])
        self.ConsumeIdentifier()
        if self.IsSymbol(['.']):
            self.ConsumeSymbol('.')
            self.ConsumeIdentifier()
        self.ConsumeSymbol('(')
        self.CompileExpressionList()
        self.ConsumeSymbol(')')
        self.ConsumeSymbol(';')

        self.ExitScope("doStatement")

    def CompileLet(self):
        """
        Compiles a let statement.
        """
        self.EnterScope("letStatement")

        self.ConsumeKeyword([Keyword.LET])
        self.ConsumeIdentifier()
        if self.IsSymbol(['[']):
            self.ConsumeSymbol('[')
            self.CompileExpression()
            self.ConsumeSymbol(']')
        self.ConsumeSymbol('=')
        self.CompileExpression()
        self.ConsumeSymbol(';')

        self.ExitScope("letStatement")

    def CompileWhile(self):
        """
        Compiles a while statement.
        """
        self.EnterScope("whileStatement")

        self.ConsumeKeyword([Keyword.WHILE])
        self.ConsumeSymbol('(')
        self.CompileExpression()
        self.ConsumeSymbol(')')

        self.ConsumeSymbol('{')
        self.CompileStatements()
        self.ConsumeSymbol('}')

        self.ExitScope("whileStatement")

    def CompileReturn(self):
        """
        Compiles a return statement.
        """
        self.EnterScope("returnStatement")

        self.ConsumeKeyword([Keyword.RETURN])
        if not self.IsSymbol([';']):
            self.CompileExpression()
        self.ConsumeSymbol(';')

        self.ExitScope("returnStatement")

    def CompileIf(self):
        """
        Compiles an if statement, possibly with a trailing
        else clause.
        """
        self.EnterScope("ifStatement")

        self.ConsumeKeyword([Keyword.IF])
        self.ConsumeSymbol('(')
        self.CompileExpression()
        self.ConsumeSymbol(')')

        self.ConsumeSymbol('{')
        self.CompileStatements()
        self.ConsumeSymbol('}')

        if self.IsKeyword([Keyword.ELSE]):
            self.ConsumeKeyword([Keyword.ELSE])
            self.ConsumeSymbol('{')
            self.CompileStatements()
            self.ConsumeSymbol('}')

        self.ExitScope("ifStatement")

    def CompileExpression(self):
        """
        Compiles an expression.
        """
        self.EnterScope("expression")

        op_symbols = ['+', '-', '*', '/', '&', '|', "<", ">", '=']
        self.CompileTerm()
        while (self.IsSymbol(op_symbols)):
            self.ConsumeSymbol(self.tokenizer.symbol())
            self.CompileTerm()

        self.ExitScope("expression")

    def CompileTerm(self):
        """
        Compiles a term.
        """
        self.EnterScope("term")

        keyword_constants = [Keyword.TRUE, Keyword.FALSE, Keyword.NULL,
                             Keyword.THIS]
        unary_symbols = ['-', '~']

        if self.IsType(TokenType.INT_CONST):
            self.ConsumeIntegerConstant()

        elif self.IsType(TokenType.STRING_CONST):
            self.ConsumeStringConstant()

        elif self.IsKeyword(keyword_constants):
                self.ConsumeKeyword(keyword_constants)

        elif self.IsSymbol(['(']):
            self.ConsumeSymbol('(')
            self.CompileExpression()
            self.ConsumeSymbol(')')

        elif self.IsSymbol(unary_symbols):
            self.ConsumeSymbol(self.tokenizer.symbol())
            self.CompileTerm()
        else:
            self.ConsumeIdentifier()
            if self.IsSymbol(['[']):    # varName '[' expression ']'
                self.ConsumeSymbol('[')
                self.CompileExpression()
                self.ConsumeSymbol(']')
            elif self.IsSymbol(['(']):  # subroutineCall
                self.ConsumeSymbol('(')
                self.CompileExpressionList()
                self.ConsumeSymbol(')')
            elif self.IsSymbol(['.']):
                self.ConsumeSymbol('.')
                self.ConsumeIdentifier()
                self.ConsumeSymbol('(')
                self.CompileExpressionList()
                self.ConsumeSymbol(')')

        self.ExitScope("term")

    def CompileExpressionList(self):
        """
        Compiles a (possibly empty) comma-separated
        list of expressions.
        """
        self.EnterScope("expressionList")

        if not self.IsSymbol(')'):
            self.CompileExpression()

        while self.IsSymbol([',']):
            self.ConsumeSymbol(',')
            self.CompileExpression()

        self.ExitScope("expressionList")

    def IsKeyword(self, keyword_list):
        return (self.IsType(TokenType.KEYWORD) and
                self.tokenizer.keyword() in keyword_list)

    def IsSymbol(self, symbol_list):
        return (self.IsType(TokenType.SYMBOL) and
                self.tokenizer.symbol() in symbol_list)

    def IsType(self, tokenType):
        return self.tokenizer.tokenType() == tokenType

    def ConsumeType(self):
        if (self.tokenizer.tokenType() == TokenType.IDENTIFIER):
            self.ConsumeIdentifier()
        else:
            self.ConsumeKeyword([Keyword.INT, Keyword.CHAR, Keyword.BOOLEAN])

    def ConsumeKeyword(self, keywordList):
        self.VerifyTokenType(TokenType.KEYWORD)
        actual = self.tokenizer.keyword()
        if actual not in keywordList:
            raise Exception("Expected keywords: {}, Actual: {}".
                            format(keywordList, actual))
        self.OutputTag("keyword", actual)
        if self.tokenizer.hasMoreTokens():
            self.tokenizer.advance()

    def ConsumeSymbol(self, symbol):
        self.VerifyTokenType(TokenType.SYMBOL)
        actual = self.tokenizer.symbol()
        if actual != symbol:
            raise Exception("Expected symbol: {}, Actual: {}".
                            format(symbol, actual))
        self.OutputTag("symbol", actual)
        if self.tokenizer.hasMoreTokens():
            self.tokenizer.advance()

    def ConsumeIntegerConstant(self):
        self.VerifyTokenType(TokenType.INT_CONST)
        self.OutputTag("integerConstant", self.tokenizer.intVal())
        if self.tokenizer.hasMoreTokens():
            self.tokenizer.advance()

    def ConsumeStringConstant(self):
        self.VerifyTokenType(TokenType.STRING_CONST)
        self.OutputTag("stringConstant", self.tokenizer.stringVal())
        if self.tokenizer.hasMoreTokens():
            self.tokenizer.advance()

    def ConsumeIdentifier(self):
        self.VerifyTokenType(TokenType.IDENTIFIER)
        self.OutputTag("identifier", self.tokenizer.identifier())
        if self.tokenizer.hasMoreTokens():
            self.tokenizer.advance()

    def VerifyTokenType(self, tokenType):
        actual = self.tokenizer.tokenType()
        if actual != tokenType:
            raise Exception("Expected token type: {}, Actual: {}".
                            format(tokenType, actual))

    def EnterScope(self, name):
        self.Output("<{}>".format(name))
        self.indentLevel += 1

    def ExitScope(self, name):
        self.indentLevel -= 1
        self.Output("</{}>".format(name))

    def OutputTag(self, tag, value):
        self.Output("<{}> {} </{}>".format(tag, value, tag))

    def Output(self, text):
        self.outputFile.write(("  " * self.indentLevel) + text + '\n')
Esempio n. 2
0
class CompilationEngine:
    def __init__(self):
        self.local_symbol_table = None
        self.class_symbol_tables = {}
        self.type_size_map = {"int": 1, "bool": 1, "char": 1}
        self.unique_label_index = 0

    def SetClass(self, input_path, output_path):
        self.tokenizer = Tokenizer(input_path)
        self.output_file = open("{0}.xml".format(output_path), 'w')
        self.code_file = open(output_path, 'w')
        self.tokenizer.advance()
        self.indent_level = 0

        self.current_class_name = None
        self.current_sub_name = None

    def CompileClass(self):
        """
        Compiles a complete class.
        """
        self.EnterScope("class")

        self.ConsumeKeyword([Keyword.CLASS])
        self.ConsumeDeclaration("class", None)

        self.class_symbol_tables[self.current_class_name] = SymbolTable()

        self.ConsumeSymbol('{')

        totalSize = 0
        while (self.IsKeyword([Keyword.STATIC, Keyword.FIELD])):
            totalSize += self.CompileClassVarDec()

        self.type_size_map[self.current_class_name] = totalSize

        # subroutineDec*
        while (self.IsKeyword(subroutine_types)):
            self.CompileSubroutine()

        self.ConsumeSymbol('}')

        self.ExitScope("class")
        self.output_file.close()

    def CompileClassVarDec(self):
        """
        Compiles a static declaration or a field declaration.
        """
        self.EnterScope("classVarDec")
        amount = 0
        category = self.tokenizer.keyword()
        self.ConsumeKeyword([Keyword.STATIC, Keyword.FIELD])
        varType = self.ConsumeType()
        self.ConsumeDeclaration(category, varType)
        amount += 1
        while (self.IsSymbol([','])):
            self.ConsumeSymbol(',')
            self.ConsumeDeclaration(category, varType)
            amount += 1

        self.ConsumeSymbol(';')

        self.ExitScope("classVarDec")

        return amount

    def CompileSubroutine(self):
        """
        Compiles a complete method, function, or constructor.
        """
        self.EnterScope("subroutineDec")
        self.local_symbol_table = SymbolTable()

        subType = self.tokenizer.keyword()
        self.ConsumeKeyword([Keyword.CONSTRUCTOR, Keyword.FUNCTION,
                             Keyword.METHOD])
        if (self.IsKeyword([Keyword.VOID])):
            self.ConsumeKeyword([Keyword.VOID])
        else:
            self.ConsumeType()

        # The first param is converted to internal rep. the second is preserved
        self.ConsumeDeclaration(subType, subType)

        if subType == "method":
            self.local_symbol_table.indexList[Categories.ARGUMENT[0]] += 1

        self.ConsumeSymbol('(')
        self.CompileParameterList()
        self.ConsumeSymbol(')')

        self.CompileSubroutineBody()

        self.ExitScope("subroutineDec")

    def CompileSubroutineBody(self):
        self.EnterScope("subroutineBody")

        nVars = 0
        self.ConsumeSymbol('{')
        while (self.IsKeyword([Keyword.VAR])):
            nVars += self.CompileVarDec()

        self.WriteCode("function {0}.{1} {2}".format(self.current_class_name,
                                                     self.current_sub_name,
                                                     str(nVars)))

        entry = self.SymbolTableLookup(self.current_sub_name)

        if entry.type == "constructor":
            self.WriteCode("push constant {0}".
                           format(self.type_size_map[self.current_class_name]))
            self.WriteCode("call Memory.alloc 1")
            self.WriteCode("pop pointer 0")
        elif entry.type == "method":
            self.WriteCode("push argument 0")
            self.WriteCode("pop pointer 0")

        self.CompileStatements()
        self.ConsumeSymbol('}')

        self.ExitScope("subroutineBody")

    def ConsumeDeclaration(self, category, entry_type):
        entry = SymbolTableEntry()
        entry.SetCategory(category)
        entry.name = self.ConsumeIdentifier()
        entry.type = entry_type

        local_categories = [Categories.VAR, Categories.ARGUMENT]
        class_categories = [Categories.SUBROUTINE, Categories.FIELD,
                            Categories.STATIC]

        # Updating current class / subroutine names
        if entry.category == Categories.CLASS:
            self.current_class_name = entry.name
        elif entry.category == Categories.SUBROUTINE:
            self.current_sub_name = entry.name

        # Updating local / class symbol tables
        if entry.category in local_categories:
            self.local_symbol_table.InsertEntry(entry)
        elif entry.category in class_categories:
            self.class_symbol_tables[self.current_class_name].\
                InsertEntry(entry)

    def CompileParameterList(self):
        """
        Compiles a (possibly empty) parameter list,
        not including the enclosing "()".
        """
        self.EnterScope("parameterList")
        nVars = 0

        if (not self.IsSymbol([')'])):
            varType = self.ConsumeType()
            self.ConsumeDeclaration("argument", varType)
            nVars += 1

        while(self.IsSymbol([','])):
            self.ConsumeSymbol(',')
            varType = self.ConsumeType()
            self.ConsumeDeclaration("argument", varType)
            nVars += 1

        self.ExitScope("parameterList")

        return nVars

    def CompileVarDec(self):
        """
        Compiles a var declaration.
        """
        self.EnterScope("varDec")
        nVars = 0
        self.ConsumeKeyword([Keyword.VAR])
        varType = self.ConsumeType()
        self.ConsumeDeclaration("var", varType)
        nVars += 1
        while (self.IsSymbol([','])):
            self.ConsumeSymbol(',')
            self.ConsumeDeclaration("var", varType)
            nVars += 1

        self.ConsumeSymbol(';')

        self.ExitScope("varDec")

        return nVars

    def CompileStatements(self):
        """
        Compiles a sequence of statements, not including the
        enclosing "{}".
        """
        self.EnterScope("statements")

        while self.IsKeyword([Keyword.LET, Keyword.IF, Keyword.WHILE,
                              Keyword.DO, Keyword.RETURN]):
            if self.IsKeyword([Keyword.LET]):
                self.CompileLet()

            if self.IsKeyword([Keyword.IF]):
                self.CompileIf()

            if self.IsKeyword([Keyword.WHILE]):
                self.CompileWhile()

            if self.IsKeyword([Keyword.DO]):
                self.CompileDo()

            if self.IsKeyword([Keyword.RETURN]):
                self.CompileReturn()

        self.ExitScope("statements")

    def CompileDo(self):
        """
        Compiles a do statement.
        """
        self.EnterScope("doStatement")
        self.ConsumeKeyword([Keyword.DO])
        prefix = self.ConsumeIdentifier()
        calleeLocation = None
        subName = None

        if self.IsSymbol(['.']):
            self.ConsumeSymbol('.')
            entry = self.SymbolTableLookup(prefix)
            if entry is not None and entry.category != Categories.CLASS:
                calleeLocation = "{0} {1}".format(entry.segment, entry.index)
                prefix = entry.type
            postfix = self.ConsumeIdentifier()
            subName = "{0}.{1}".format(prefix, postfix)
        else:
            subName = "{0}.{1}".format(self.current_class_name, prefix)
            calleeLocation = "pointer 0"

        nArgs = 0
        # This means we are calling an instance method, so we push it first
        if calleeLocation is not None:
            self.WriteCode("push {0} //Pushing callee".format(calleeLocation))
            nArgs += 1

        self.ConsumeSymbol('(')
        nArgs += self.CompileExpressionList()
        self.ConsumeSymbol(')')
        self.ConsumeSymbol(';')

        self.WriteCode("call {0} {1}".format(subName, nArgs))

        # Get rid of the return value (garbage)
        self.WriteCode("pop temp 0")

        self.ExitScope("doStatement")

    def CompileLet(self):
        """
        Compiles a let statement.
        """
        self.EnterScope("letStatement")

        self.ConsumeKeyword([Keyword.LET])
        varName = self.ConsumeIdentifier()
        entry = self.SymbolTableLookup(varName)
        isArray = False
        if self.IsSymbol(['[']):
            isArray = True
            self.ConsumeSymbol('[')
            self.CompileExpression()
            self.WriteCode("push {0} {1}".
                           format(entry.segment, entry.index))  # array base
            self.WriteCode("add")  # Add offset
            self.ConsumeSymbol(']')
        self.ConsumeSymbol('=')
        self.CompileExpression()
        self.ConsumeSymbol(';')

        if isArray:
            self.WriteCode("pop temp 0")  # Save the expression result
            self.WriteCode("pop pointer 1")  # Align THAT
            self.WriteCode("push temp 0")  # Push the exp result
            # Put the exp result in the array position
            self.WriteCode("pop that 0")
        else:
            self.WriteCode("pop {0} {1}".format(entry.segment, entry.index))

        self.ExitScope("letStatement")

    def CompileWhile(self):
        """
        Compiles a while statement.
        """
        self.EnterScope("whileStatement")

        self.ConsumeKeyword([Keyword.WHILE])
        L1 = self.GenerateUniqueLabel()
        L2 = self.GenerateUniqueLabel()

        # While entry point
        self.WriteCode("label {0}".format(L1))

        # while loop condition
        self.ConsumeSymbol('(')
        self.CompileExpression()
        self.ConsumeSymbol(')')

        # Jump to L2 if condition doesn't hold
        self.WriteCode("not")
        self.WriteCode("if-goto {0}".format(L2))

        # While loop logic
        self.ConsumeSymbol('{')
        self.CompileStatements()
        self.ConsumeSymbol('}')

        # Go back to L1 for another iteration
        self.WriteCode("goto {0}".format(L1))

        # While termination point
        self.WriteCode("label {0}".format(L2))

        self.ExitScope("whileStatement")

    def CompileReturn(self):
        """
        Compiles a return statement.
        """
        self.EnterScope("returnStatement")

        self.ConsumeKeyword([Keyword.RETURN])
        if not self.IsSymbol([';']):
            self.CompileExpression()
        else:
            self.WriteCode("push constant 0")
        self.ConsumeSymbol(';')

        self.WriteCode("return")

        self.ExitScope("returnStatement")

    def CompileIf(self):
        """
        Compiles an if statement, possibly with a trailing
        else clause.
        """
        self.EnterScope("ifStatement")

        self.ConsumeKeyword([Keyword.IF])
        IF_TRUE = self.GenerateUniqueLabel()
        IF_FALSE = self.GenerateUniqueLabel()
        IF_END = self.GenerateUniqueLabel()

        # The if statement condition
        self.ConsumeSymbol('(')
        self.CompileExpression()
        self.ConsumeSymbol(')')

        # Jump to L1 if condition doesn't hold
        self.WriteCode("if-goto {0}".format(IF_TRUE))
        self.WriteCode("goto {0}".format(IF_FALSE))
        self.WriteCode("label {0}".format(IF_TRUE))

        self.ConsumeSymbol('{')
        self.CompileStatements()
        self.ConsumeSymbol('}')

        self.WriteCode("goto {0}".format(IF_END))
        self.WriteCode("label {0}".format(IF_FALSE))
        if self.IsKeyword([Keyword.ELSE]):
            self.ConsumeKeyword([Keyword.ELSE])
            self.ConsumeSymbol('{')
            self.CompileStatements()
            self.ConsumeSymbol('}')

        self.WriteCode("label {0}".format(IF_END))
        self.ExitScope("ifStatement")

    def CompileExpression(self):
        """
        Compiles an expression.
        """
        self.EnterScope("expression")

        self.CompileTerm()
        while (self.IsSymbol(op_symbols.keys())):
            op = self.ConsumeSymbol(self.tokenizer.symbol())
            self.CompileTerm()
            self.WriteCode(op_symbols[op])

        self.ExitScope("expression")

    def CompileTerm(self):
        """
        Compiles a term.
        """
        self.EnterScope("term")

        keyword_constants = [Keyword.TRUE, Keyword.FALSE, Keyword.NULL,
                             Keyword.THIS]
        termName = None

        if self.IsType(TokenType.INT_CONST):
            self.WriteCode("push constant {0}".
                           format(self.ConsumeIntegerConstant()))

        elif self.IsType(TokenType.STRING_CONST):
            self.ConsumeStringConstant()

        elif self.IsKeyword(keyword_constants):
                keyword = self.ConsumeKeyword(keyword_constants)
                if keyword == "false":
                    self.WriteCode("push constant 0")
                elif keyword == "true":
                    self.WriteCode("push constant 0")
                    self.WriteCode("not")
                elif keyword == "this":
                    self.WriteCode("push pointer 0")
                elif keyword == "null":
                    self.WriteCode("push constant 0")

        elif self.IsSymbol(['(']):
            self.ConsumeSymbol('(')
            self.CompileExpression()
            self.ConsumeSymbol(')')

        elif self.IsSymbol(unary_symbols.keys()):
            symbol = self.ConsumeSymbol(self.tokenizer.symbol())
            self.CompileTerm()
            self.WriteCode(unary_symbols[symbol])
        else:
            termName = self.ConsumeIdentifier()
            entry = self.SymbolTableLookup(termName)
            if entry is not None:
                if CategoryUtils.IsIndexed(entry.category):
                    self.WriteCode("push {0} {1} //{2}".
                                   format(CategoryUtils.
                                          GetSegment(entry.category),
                                          entry.index, termName))

            if self.IsSymbol(['[']):  # varName '[' expression ']'
                self.ConsumeSymbol('[')
                self.CompileExpression()
                self.WriteCode("add")
                self.WriteCode("pop pointer 1")
                self.WriteCode("push that 0")
                self.ConsumeSymbol(']')
            elif self.IsSymbol(['(']):  # subroutineCall
                self.ConsumeSymbol('(')
                self.WriteCode("call {0} {1}".
                               format(termName, self.CompileExpressionList()))
                self.ConsumeSymbol(')')
            elif self.IsSymbol(['.']):
                self.ConsumeSymbol('.')
                funcName = self.ConsumeIdentifier()
                entry = self.GetSubroutineEntry(termName, funcName)
                extraParam = 0
                if entry is not None and entry.type == "method":
                    termName = self.SymbolTableLookup(termName).type
                    extraParam = 1

                self.ConsumeSymbol('(')
                self.WriteCode("call {0}.{1} {2}".
                               format(termName, funcName,
                                      self.CompileExpressionList() +
                                      extraParam))
                self.ConsumeSymbol(')')

        self.ExitScope("term")

        return termName

    def GetSubroutineEntry(self, prefix, postfix):
        entry = self.SymbolTableLookup(postfix)
        if entry is not None:
            return entry

        varEntry = self.SymbolTableLookup(prefix)
        if varEntry is not None:
            return self.ClassSymbolTableLookup(postfix, varEntry.type)

        return None

    def CompileExpressionList(self):
        """
        Compiles a (possibly empty) comma-separated
        list of expressions.
        """
        self.EnterScope("expressionList")
        nArgs = 0
        if not self.IsSymbol(')'):
            self.CompileExpression()
            nArgs += 1

        while self.IsSymbol([',']):
            self.ConsumeSymbol(',')
            self.CompileExpression()
            nArgs += 1

        self.ExitScope("expressionList")

        return nArgs

    def IsKeyword(self, keyword_list):
        return (self.IsType(TokenType.KEYWORD) and
                self.tokenizer.keyword() in keyword_list)

    def IsSymbol(self, symbol_list):
        return (self.IsType(TokenType.SYMBOL) and
                self.tokenizer.symbol() in symbol_list)

    def IsType(self, tokenType):
        return self.tokenizer.tokenType() == tokenType

    def ConsumeType(self):
        if (self.tokenizer.tokenType() == TokenType.IDENTIFIER):
            return self.ConsumeIdentifier()
        else:
            return self.ConsumeKeyword([Keyword.INT, Keyword.CHAR,
                                        Keyword.BOOLEAN])

    def ConsumeKeyword(self, keywordList):
        self.VerifyTokenType(TokenType.KEYWORD)
        actual = self.tokenizer.keyword()
        if actual not in keywordList:
            raise Exception("Expected keywords: {}, Actual: {}".
                            format(keywordList, actual))

        self.OutputTag("keyword", actual)
        if self.tokenizer.hasMoreTokens():
            self.tokenizer.advance()

        return actual

    def ConsumeSymbol(self, symbol):
        self.VerifyTokenType(TokenType.SYMBOL)
        actual = self.tokenizer.symbol()
        if actual != symbol:
            raise Exception("Expected symbol: {}, Actual: {}".
                            format(symbol, actual))
        self.OutputTag("symbol", actual)
        if self.tokenizer.hasMoreTokens():
            self.tokenizer.advance()

        return actual

    def ConsumeIntegerConstant(self):
        self.VerifyTokenType(TokenType.INT_CONST)
        actual = self.tokenizer.intVal()
        self.OutputTag("integerConstant", self.tokenizer.intVal())
        if self.tokenizer.hasMoreTokens():
            self.tokenizer.advance()

        return actual

    def ConsumeStringConstant(self):
        self.VerifyTokenType(TokenType.STRING_CONST)
        actual = self.tokenizer.stringVal()
        self.WriteCode("push constant {0}".format(len(actual)))
        self.WriteCode("call String.new 1")
        for c in actual:
            self.WriteCode("push constant {0}".format(ord(c)))
            self.WriteCode("call String.appendChar 2")
        self.OutputTag("stringConstant", self.tokenizer.stringVal())
        if self.tokenizer.hasMoreTokens():
            self.tokenizer.advance()

        return actual

    def ConsumeIdentifier(self):
        self.VerifyTokenType(TokenType.IDENTIFIER)
        actual = self.tokenizer.identifier()
        self.OutputTag("identifierName", self.tokenizer.identifier())
        if self.tokenizer.hasMoreTokens():
            self.tokenizer.advance()

        return actual

    def VerifyTokenType(self, tokenType):
        actual = self.tokenizer.tokenType()
        if actual != tokenType:
            raise Exception("Expected token type: {}, Actual: {}".
                            format(tokenType, actual))

    def EnterScope(self, name):
        self.Output("<{}>".format(name))
        self.indent_level += 1

    def ExitScope(self, name):
        self.indent_level -= 1
        self.Output("</{}>".format(name))

    def ClassSymbolTableLookup(self, name, containingClass):
        return self.class_symbol_tables[containingClass].GetEntry(name)

    def SymbolTableLookup(self, name):
        entry = self.local_symbol_table.GetEntry(name)
        if entry is not None:
            return entry
        else:
            return self.ClassSymbolTableLookup(name, self.current_class_name)

    def WriteCode(self, line):
        self.code_file.write(line + '\n')

    def OutputTag(self, tag, value):
        self.Output("<{}> {} </{}>".format(tag, value, tag))

    def Output(self, text):
        self.output_file.write(("  " * self.indent_level) + text + '\n')

    def GenerateUniqueLabel(self):
        self.unique_label_index += 1
        return "pfl{0}".format(self.unique_label_index - 1)