コード例 #1
0
class CompilationEngine:
    def __init__(self, tokenizer, outputFile):
        self.XMLCode = []
        self.CodeIndent = 0
        self.tokenizer = tokenizer
        self.symbolTable = SymbolTable()
        self.vmWriter = VMWriter(outputFile)
        self.class_name = None
        self.segment_local_dict = segment_dict
        self.while_count = 0
        self.if_count = 0

    def nextToken(self):
        "advancing and retreiving the next token by Tokenizer"
        self.tokenizer.advance()
        current_token = self.tokenizer.getCurrentToken()
        token_type = self.tokenizer.typeOfToken()
        return current_token, token_type

    def compileToken(self, token):
        current_token, token_type = self.nextToken()
        self.compileLine(current_token, token_type)
        return current_token

    def compileTitle(self, title_name, isEntered):
        self.XMLCode.append(self.writeXMLCodeTitle(title_name, isEntered))

    def compileLine(self, current_token, token_type):
        self.XMLCode.append(self.writeXMLCodeLine(current_token, token_type))

    def writeXMLCodeLine(self, token_to_write, type_of_token):
        """writes into XML line all sorts of tokens which are not title
        @:param token_to_write: the current token to write
        @:param type_of_token: the current's token's type
        @:return: the XML code line"""

        return " " * self.CodeIndent + "<" + str(
            type_of_token) + ">" + " " + token_to_write.lstrip(
            ) + " " + "</" + str(type_of_token) + ">"

    def writeXMLCodeTitle(self, type_of_token, isEntered):
        """writes into XML line all sorts of tokens which are  titles
                @:param isEntered: a boolean parameter implies if the current token is already opened
                 if it is the token's first occurance isEntered== True, and False otherwise
                @:param type_of_token: the current's token's type
                @:return: the XML code line"""

        if isEntered == True:  #if the title is opening
            myLine = " " * self.CodeIndent + "<" + str(type_of_token) + ">"
            self.CodeIndent += 2  #indent the lines of all other tokens within the current_token's scope
            return myLine
        else:
            #if isEntered == False we have to close the title token
            self.CodeIndent -= 2
            myLine = " " * self.CodeIndent + "</" + str(type_of_token) + ">"
            return myLine

    def compileIdentifier(self, isClass=False):
        current_token, token_type = self.nextToken()
        if isClass:
            self.class_name = current_token  # i've changed it because it was swapped

        self.XMLCode.append(self.writeXMLCodeLine(current_token, token_type))
        return current_token

    def compileClass(self):
        self.compileTitle("class", True)
        self.compileToken(
            "class"
        )  # increnmenting the tokenizer and checking if the current token is indeed a class declaration
        self.compileIdentifier(True)
        self.compileToken("{")
        current_token, token_type = self.nextToken()
        while current_token in ["field", "static"]:
            self.compileClassVarDeclaration(current_token, token_type)
            current_token, token_type = self.nextToken()

        while current_token in ["constructor", "function", "method"]:
            self.compilesubRoutineDec(current_token, token_type)
            current_token, token_type = self.nextToken()

        self.compileToken("}")
        self.compileTitle("class", False)
        return self.XMLCode

    def compileClassVarDeclaration(self, current_token, token_type):
        #self.compileTitle("classVarDec",True)  # first opening a new class title in XML
        #self.compileLine(current_token,token_type)
        VarKind = self.tokenizer.getCurrentToken()
        current_token, token_type = self.nextToken()
        VarType = current_token
        #self.compileType(current_token, token_type)
        VarName = self.compileIdentifier()
        self.symbolTable.define(VarName, VarType, VarKind)
        current_token, token_type = self.nextToken()
        while current_token == ",":  # also the validation itself so no need of compile token
            self.compileLine(current_token, token_type)
            VarName = self.compileIdentifier()
            self.symbolTable.define(VarName, VarType, VarKind)
            current_token, token_type = self.nextToken()
        self.compileLine(current_token, token_type)
        self.compileTitle("classVarDec", False)

    def compileType(self, current_token, token_type):
        if current_token in ["int", "char", "boolean", self.class_name]:
            self.compileLine(current_token, token_type)
        else:
            self.compileLine(current_token, token_type)
        return current_token

    def compilesubRoutineDec(self, current_token, token_type):
        self.symbolTable.startSubroutine()
        self.if_count = 0
        self.while_count = 0
        #self.compileTitle("subroutineDec", True)
        #self.compileLine(current_token, token_type)
        func_type = self.tokenizer.getCurrentToken()

        current_token, token_type = self.nextToken()
        return_type = current_token
        if current_token in ["int", "char", "boolean", self.class_name
                             ] or current_token == "void":
            self.compileLine(current_token, token_type)
        func_name = self.compileIdentifier()
        self.symbolTable.set_functionName(func_name, self.class_name)
        self.symbolTable.setFuncType(func_type)
        self.symbolTable.setReturnType(return_type)
        if func_type == "method":
            self.symbolTable.define("this", return_type, "arg")
        self.compileToken("(")
        self.compileParameterList()
        self.compileLine(")", "symbol")
        self.compileSubroutineBody()
        self.compileTitle("subroutineDec", False)

    def compileParameterList(self):
        self.compileTitle("parameterList", True)
        current_token, token_type = self.nextToken()
        while current_token != ")":
            VarType = self.compileType(current_token, token_type)
            VarName = self.compileIdentifier()
            self.symbolTable.define(VarName, VarType, "arg")
            current_token, token_type = self.nextToken()
            if current_token == ",":
                self.compileLine(current_token, token_type)
                current_token, token_type = self.nextToken()
        self.compileTitle("parameterList", False)

    def compileSubroutineBody(self):
        #self.compileTitle("subroutineBody", True)# first opening a new subroutineBody title in XML
        self.compileToken("{")

        current_token = self.tokenizer.showNextToken()
        while current_token == "var":
            self.compilevarDec()
            current_token = self.tokenizer.showNextToken()
        self.vmWriter.writeFunction(self.symbolTable.function_name,
                                    str(self.symbolTable.VarCount("var")))
        # check wether the function type is method or constructor /*todo*/
        if self.symbolTable.function_type == "method":

            self.vmWriter.writePush("argument", "0")
            self.vmWriter.writePop("pointer", "0")

        if self.symbolTable.function_type == "constructor":
            #slide 6 (7:00) about constructors
            field_num = self.symbolTable.VarCount("field")
            self.vmWriter.writePush("constant", str(field_num))
            self.vmWriter.writeCall("Memory.alloc", "1")
            self.vmWriter.writePop("pointer", "0")
        current_token, token_type = self.nextToken()
        self.compileStatements(current_token, token_type)
        #self.compileLine("}","symbol")
        #self.compileTitle("subroutineBody", False)

    def compilevarDec(self):
        #self.compileTitle("varDec", True)
        current_token, token_type = self.nextToken()
        VarKind = "var"
        current_token, token_type = self.nextToken()
        VarType = current_token
        current_token, token_type = self.nextToken()
        VarName = current_token
        self.symbolTable.define(VarName, VarType, VarKind)
        #self.compileLine(current_token, token_type)
        current_token, token_type = self.nextToken()
        while current_token == ",":
            #self.compileLine(current_token, token_type)
            VarName = self.compileIdentifier()
            self.symbolTable.define(VarName, VarType, VarKind)
            current_token, token_type = self.nextToken()
        #self.compileLine(";", "symbol")
        #self.compileTitle("varDec", False)

    def compileStatements(self, current_token, token_type):
        self.compileTitle("statements", True)
        while current_token != "}":
            if current_token == "let":
                self.compileLet()
            elif current_token == "if":
                self.compileIf()
            elif current_token == "while":
                self.compileWhile()
            elif current_token == "do":
                self.compileDo()
            elif current_token == "return":
                self.compileReturn()
            current_token, token_type = self.nextToken()
        self.compileTitle("statements", False)

    def compileLet(self):
        self.compileTitle("letStatement", True)
        self.compileLine("let", "keyword")
        current_token, token_type = self.nextToken()
        temp = current_token  # saves the new local variable
        current_token, token_type = self.nextToken()
        if current_token == "[":  # if the left side is array
            self.compileLine("[", "symbol")
            self.compileExpression()
            self.compileLine("]", "symbol")
            var_memory_segment = self.segment_local_dict[
                self.symbolTable.KindOf(temp)]  #the kind of current var
            var_index = self.symbolTable.IndexOf(temp)
            self.vmWriter.writePush(var_memory_segment, str(var_index))
            self.vmWriter.WriteArithmetic(
                "add"
            )  #if the left side is  an array - add the index to get the value
            self.nextToken()

        #self.compileLine("=","symbol")
        self.compileExpression()
        #self.compileLine(";", "symbol")
        #self.compileTitle("letStatement", False)
        var_kind = self.symbolTable.KindOf(temp)
        var_type = self.symbolTable.TypeOf(temp)
        index_segment = self.symbolTable.IndexOf(temp)
        kind_seg = self.segment_local_dict[var_kind]
        if current_token == "[" and kind_seg in ["local", "argument"]:
            #slide 8 (20:00) !!!
            self.vmWriter.writePop("temp", "0")
            self.vmWriter.writePop("pointer", "1")
            self.vmWriter.writePush("temp", "0")
            self.vmWriter.writePop("that", "0")
        else:
            #if the first var was not an Arrayso just pop the value from the expression to it
            self.vmWriter.writePop(kind_seg, str(index_segment))

    def compileIf(self):
        #self.compileTitle("ifStatement", True)
        #self.compileLine("if", "keyword")
        self.compileToken("(")
        self.compileExpression()
        self.compileLine(")", "symbol")

        if_true_label = "TRUE" + str(
            self.if_count)  #define true and false labels!!
        if_false_label = "FALSE" + str(self.if_count)
        end_if_label = "END" + str(self.if_count)
        self.if_count += 1
        self.vmWriter.WriteIf(
            if_true_label
        )  # after pushing to stack the expression write labels
        self.vmWriter.WriteGoto(if_false_label)

        self.compileToken("{")
        self.vmWriter.WriteLabel(
            if_true_label
        )  # put the TRUE_LABEL at the begining of the if clause
        current_token, token_type = self.nextToken()
        self.compileStatements(current_token, token_type)
        #self.compileLine("}","symbol")

        #check if there is "else" statement
        temp = self.tokenizer.showNextToken()
        if temp == "else":
            #put the END_LABEL and put the False_label If the condionn not true
            self.vmWriter.WriteGoto(end_if_label)
            self.vmWriter.WriteLabel(if_false_label)
            current_token, token_type = self.nextToken()
            #self.compileLine("else","keyword")
            self.compileToken("{")
            current_token, token_type = self.nextToken()
            self.compileStatements(current_token, token_type)
            self.vmWriter.WriteLabel(end_if_label)
            #self.compileLine("}","symbol")
        else:
            self.vmWriter.WriteLabel(if_false_label)
        #self.compileTitle("ifStatement", False)

    def compileWhile(self):
        #self.compileTitle("whileStatement", True)
        #self.compileLine("while", "keyword")
        while_start_label = "STRAT" + str(
            self.while_count)  # defining astart and stop labels!!
        while_end_label = "END" + str(self.while_count)
        self.while_count += 1

        self.vmWriter.WriteLabel(while_start_label)
        self.compileToken("(")
        self.compileExpression()
        #self.compileLine(")","symbol")
        #negating the boolean expression and check whether to enter or not
        self.vmWriter.WriteArithmetic("not")
        self.vmWriter.WriteIf(while_end_label)
        self.compileToken("{")
        current_token, token_type = self.nextToken()
        self.compileStatements(current_token, token_type)
        self.vmWriter.WriteGoto(while_start_label)
        self.vmWriter.WriteLabel(while_end_label)
        #self.compileLine("}", "symbol")
        #self.compileTitle("whileStatement", False)

    def compileDo(self):
        #self.compileTitle("doStatement", True)
        #self.compileLine("do", "keyword")
        self.compileSubroutineCall()
        self.vmWriter.writePop("temp", "0")
        self.nextToken()
        #self.nextToken()
        #self.compileLine(";","symbol")
        #self.compileTitle("doStatement", False)

    def compileReturn(self):
        #self.compileTitle("returnStatement", True)
        #self.compileLine("return", "keyword")
        if self.symbolTable.return_type == "void":
            self.vmWriter.writePush("constant",
                                    "0")  # always push 0 to the stack!!
            temp = self.tokenizer.showNextToken()
            while not temp == ";":
                self.nextToken()
                temp = self.tokenizer.showNextToken()
        else:
            temp = self.tokenizer.showNextToken()
            if temp != ";":
                self.compileExpression()
            else:
                self.vmWriter.writePush("constant", "0")
        self.vmWriter.writeReturn()

        #self.compileLine(";","symbol")
        #self.compileTitle("returnStatement", False)

    def compileExpression(self):
        #self.compileTitle("expression", True)
        self.compileTerm()
        current_token, token_type = self.nextToken()
        while current_token in op:
            self.compileLine(current_token, token_type)
            self.compileTerm()
            self.vmWriter.WriteArithmetic(
                current_token
            )  # after two expression/terms we will write arithmetic
            current_token, token_type = self.nextToken()
        self.compileTitle("expression", False)

    def compileExpressionList(self):
        num_of_args = 0
        self.compileTitle("expressionList", True)
        current_token = self.tokenizer.showNextToken()
        if current_token != ")":
            self.compileExpression()
            num_of_args += 1  # add the first parameter found inside paranthesis( )
            while (self.tokenizer.getCurrentToken() == ","):
                num_of_args += 1
                self.compileLine(",", "symbol")
                self.compileExpression()
        if current_token == ")":
            self.nextToken()
        self.compileTitle("expressionList", False)
        return num_of_args

    def compileTerm(self):
        #self.compileTitle("term", True)
        current_token, token_type = self.nextToken()
        kind = self.symbolTable.KindOf(current_token)
        temp = self.tokenizer.showNextToken()
        if token_type == "int_constant":
            # self.compileLine(current_token,"integerConstant")
            self.vmWriter.writePush("constant", str(current_token))

        elif kind == "var" and temp != "[" and temp != ".":
            self.vmWriter.writePush(
                "local", str(self.symbolTable.IndexOf(current_token)))

        elif kind == "arg" and current_token != "this":
            self.vmWriter.writePush(
                "argument", str(self.symbolTable.IndexOf(current_token)))

        elif token_type == "string_constant":
            #self.compileLine(current_token.strip("\""),"stringConstant")
            string_constant = current_token.strip("\"")
            string_lenth = len(string_constant)
            self.vmWriter.writePush("constant", str(string_lenth))
            self.vmWriter.writeCall("String.new", 1)
            #now getting the numerical value of each char in the string and append them
            for char in string_constant:
                self.vmWriter.writePush("constant", str(ord(char)))
                self.vmWriter.writeCall("String.appendChar", "2")

        elif kind == "static" and temp != "." and temp != "[":
            self.vmWriter.writePush(
                "static", str(self.symbolTable.IndexOf(current_token)))

        elif kind == "field" and temp != "." and temp != "]":
            self.vmWriter.writePush(
                "this", str(self.symbolTable.IndexOf(current_token)))

        elif current_token in [
                "true", "false", "null", "this"
        ]:  # not the kind it only means that the tokens itself is one of them
            #self.compileLine(current_token,token_type) #its a keyword!
            if current_token == "true":
                self.vmWriter.writePush("constant", "0")
                self.vmWriter.WriteArithmetic("not")
            elif current_token == "this":
                self.vmWriter.writePush("pointer", "0")
            else:  # its false or null
                self.vmWriter.writePush("constant", "0")
        # if its ( expression )
        elif current_token == "(":
            self.compileLine(current_token, token_type)
            self.compileExpression()
            self.compileLine(")", "symbol")
        #if its Unary Op
        elif current_token in ["~", "-"]:
            self.compileLine(current_token, token_type)
            self.compileTerm()
            if current_token == "~":
                self.vmWriter.WriteArithmetic("not")
            else:
                self.vmWriter.WriteArithmetic("neg")
        #if its varName [ expression ]
        elif token_type == "identifier" and temp != ".":  # maybe add and temp != ";"  ????????
            self.compileLine(current_token, token_type)
            if temp == "[":
                self.compileLine(temp, "symbol")
                var_name = current_token
                current_token, token_type = self.nextToken()
                self.compileExpression()
                self.compileLine("]", "symbol")
                var_memory_segment = self.segment_local_dict[
                    self.symbolTable.KindOf(
                        var_name)]  # the kind of current var
                var_index = self.symbolTable.IndexOf(var_name)
                self.vmWriter.writePush(var_memory_segment, str(var_index))
                self.vmWriter.WriteArithmetic(
                    "add")  # if the left side is  an array
                self.vmWriter.writePop("pointer", "1")
                self.vmWriter.writePush("that", "0")

        # subroutine
        elif token_type == "identifier" and temp in ["(", "."]:
            self.tokenizer.previousToken()
            self.compileSubroutineCall()
        # if its a var_name
        elif token_type == "identifier" and not temp in ["[", "(", "."]:
            var_name = current_token
            var_memory_segment = self.segment_local_dict[
                self.symbolTable.KindOf(var_name)]  # the kind of current var
            var_index = self.symbolTable.IndexOf(var_name)
            self.vmWriter.writePush(var_memory_segment, str(var_index))
        self.compileTitle("term", False)

    def compileSubroutineCall(self):
        num_of_args = 0
        func_name = self.compileIdentifier(
        )  # func_name first contain the class name
        current_token, token_type = self.nextToken()
        if current_token == ".":
            var_kind = self.symbolTable.KindOf(func_name)
            var_index = self.symbolTable.IndexOf(func_name)
            var_type = self.symbolTable.TypeOf(func_name)
            if var_type != False:  # enter only if it is a method called by an object
                num_of_args += 1  # num of arguments of a method starts from 1 because of the "self" argument
                func_name = var_type
                mem_segment = self.segment_local_dict[var_kind]
                self.vmWriter.writePush(
                    mem_segment, str(var_index)
                )  # push memory segment to stack be calling function
            func_name += "."
            func_name += self.compileIdentifier()
            #self.compileLine(".", "symbol")
            self.compileToken("(")
            num_of_args += self.compileExpressionList()
            self.compileLine(")", "symbol")
            self.vmWriter.writeCall(func_name, str(num_of_args))

        else:
            self.vmWriter.writePush("pointer", "0")
            num_of_args += 1
            func_name = self.class_name + "." + func_name
            self.compileLine("(", "symbol")
            num_of_args += self.compileExpressionList()
            self.compileLine(")", "symbol")
            self.vmWriter.writeCall(func_name, str(num_of_args))