Example #1
0
class CompilationEngine:

    ##############################################  Consructor  ############################################

    # Creates a new compilation engine
    # with the given input and output.
    # The next routine calles must be compileClass().
    def __init__(self, inputLines, filePath):
        # input information
        self.lines = inputLines
        self.destFile = filePath
        # Grammar rules
        grammar = JackGrammar()
        self.subroutineDec = grammar.getSubroutineDec()
        self.subs = grammar.getSubs()
        self.varDecs = grammar.getVarDecs()
        self.statements = grammar.getStatements()
        self.terms = grammar.getTerms()
        self.unaryOp = grammar.getUnaryOp()
        self.op = grammar.getOp()
        self.classVarDec = grammar.getClassVarDec()
        # global variables
        self.currentToken = ""
        self.currentTokenType = ""
        self.currentLine = ""
        self.nextToken = ""
        self.nextTokenType = ""
        self.isSubroutine = False
        self.className = ""
        self.currentFuncionName = ""
        self.nArgs = 0
        self.isRight = False
        self.had_else = False
        self.whileLabelIndex = 0
        self.ifLabelIndex = 0
        self.isConstructor = False
        self.isMethod = False
        self.isArray = False
        self.wasRightArray = False
        self.wasArgument = False
        # SymbolTable
        self.symbolTable = SymbolTable()
        # VMWriter
        self.vmwriter = VMWriter(filePath)

##############################################  Compile Class  ##############################################

# Compiles a complete class.

    def CompileClass(self):
        self.compileClassHelper()

    # Grammar:
    # class className {classVarDec* subroutine*}
    def compileClassHelper(self):
        if (len(self.lines) == 0):
            return

        self.manageNextTranslation()
        if self.currentTokenType == "identifier":
            self.className = self.currentToken
            self.compileClassHelper()
        elif self.currentTokenType == "symbol":
            self.compileClassHelper()
        elif self.currentToken in self.classVarDec:
            self.compileClassVarDec()
            self.compileClassHelper()
        elif self.currentToken in self.subroutineDec:
            self.CompileSubroutineDec()
            self.compileClassHelper()
        elif self.currentToken == "class":
            self.compileClassHelper()

##############################################  Compile ClassVarDec  ##############################################

    def compileClassVarDec(self):
        kind = ""
        type = ""
        while (self.currentToken not in self.subroutineDec):
            kind = self.currentToken
            self.manageNextTranslation()
            type = self.currentToken
            self.manageNextTranslation()
            name = self.currentToken
            self.symbolTable.Define(name, type, kind)

            self.manageNextTranslation()

            while self.currentToken != ";":
                self.manageNextTranslation()
                name = self.currentToken
                self.symbolTable.Define(name, type, kind)
                self.manageNextTranslation()

            self.manageNextTranslation()
        if self.currentToken == "constructor":
            self.isConstructor = True
        self.compileSubroutineDecHelper()
        self.isMethod = False

##############################################  Compile SubroutineDec  ##############################################

# Compiles a static declaration or a field declaration.

    def CompileSubroutineDec(self):
        self.compileSubroutineDecHelper()
        self.isMethod = False

    # Grammar:
    # 'constructor', 'function', 'method'
    # ('void' | type) subroutineName (parameterList)
    # subroutineBody
    def compileSubroutineDecHelper(self):
        if (len(self.lines) == 0):
            return

        self.checkNextToken()
        if self.nextToken == "}":
            return
        if self.currentToken == "method":
            self.isMethod = True
        if self.currentToken != ")":
            self.manageNextTranslation()
        if self.currentToken in self.subroutineDec:
            self.compileSubroutineDecHelper()
        elif self.currentToken in self.subs:
            self.compileSubroutineDecHelper()

        elif self.currentTokenType == "identifier":
            self.currentFuncionName = self.currentToken
            self.symbolTable.startSubroutine()
            self.compileSubroutineDecHelper()

        elif self.currentTokenType == "symbol":
            if self.currentToken == "(":
                self.compileParameterList()
                self.compileSubroutineDecHelper()
            elif self.currentToken == ")":
                self.getSubroutineBody()
            elif self.currentToken == ",":
                self.compileSubroutineDecHelper()

##############################################  Compile SubroutineBody  ##############################################

    def getSubroutineBody(self):
        self.subroutineBodyhelper()

    def subroutineBodyhelper(self):
        self.checkNextToken()

        if self.nextToken == "}":
            self.manageNextTranslation()
            return
        if self.currentToken == "}" and self.had_else:
            self.had_else = False
            return
        self.manageNextTranslation()

        if self.currentTokenType == "symbol":
            if self.currentToken == "{":
                self.compileVarDec()
                self.checkNextToken()

        self.subroutineBodyhelper()

##############################################  Compile VarDec  ##############################################

# Compiles a var declaration.

    def compileVarDec(self):
        self.manageNextTranslation()
        if self.currentToken == "var":
            self.manageNextTranslation()
            type = self.currentToken
            self.varDecHelper(type)
        elif self.currentToken in self.statements:
            self.vmwriter.WriteFunction(
                self.className + "." + self.currentFuncionName,
                str(self.symbolTable.getSize()))
            if self.isConstructor:
                count = self.symbolTable.VarCount("field")
                self.vmwriter.writePush("constant", count)
                self.vmwriter.WriteCall("Memory.alloc", 1)
                self.vmwriter.writePop("pointer", 0)
                self.isConstructor = False
            elif self.isMethod:
                self.vmwriter.writePush("argument", 0)
                self.vmwriter.writePop("pointer", 0)
            self.compileStatements()

    def varDecHelper(self, type):
        self.manageNextTranslation()
        if self.currentToken in self.varDecs or self.currentTokenType in self.varDecs:
            self.symbolTable.Define(self.currentToken, type, "var")
            self.varDecHelper(type)
        elif self.currentTokenType == "symbol":
            if self.currentToken == ";":
                self.compileVarDec()
            else:
                self.varDecHelper(type)

##############################################  Compile ParameterList  ##############################################

# Compiles a (possibly empty) parameter list,
# not including the enclosing "()".

    def compileParameterList(self):
        if self.isMethod:
            self.symbolTable.Define("this", self.className, "argument")
        self.manageNextTranslation()

        if self.currentToken != ")":
            self.paramListHelper()

    def paramListHelper(self):
        if self.currentTokenType == "symbol":
            if self.currentToken == ",":
                self.manageNextTranslation()
                self.paramListHelper()

        else:
            type = self.currentToken
            if type != "Array":
                type = self.currentTokenType
            self.manageNextTranslation()
            varName = self.currentToken
            self.symbolTable.Define(varName, type, "arg")
            self.manageNextTranslation()
            self.checkNextToken()
            self.paramListHelper()

##############################################  Compile Statements  ##############################################

# Compiles a sequence of statements, not including the enclosing "{}".

    def compileStatements(self):
        self.compileStatementsHelper()

    def compileStatementsHelper(self):
        if self.currentToken == "do":
            self.compileDo()
            self.manageNextTranslation()
            self.compileStatementsHelper()
        elif self.currentToken == "let":
            self.compileLet()
            if self.nextToken == ";":
                self.manageNextTranslation()
            self.manageNextTranslation()
            self.compileStatementsHelper()
        elif self.currentToken == "while":
            self.compileWhile()
            self.manageNextTranslation()
            self.compileStatementsHelper()
        elif self.currentToken == "if":
            saved = self.ifLabelIndex
            self.compileIf()
            self.checkNextToken()
            self.checkNextToken()
            if self.nextToken == "else":
                self.manageNextTranslation()
                self.vmwriter.WriteGoto("IF_END_LABEL" + str(saved))
                self.vmwriter.WriteLabel("IF_FALSE_LABEL" + str(saved))
                self.had_else = True
                self.compileIf()
                self.vmwriter.WriteLabel("IF_END_LABEL" + str(saved))
            else:
                self.vmwriter.WriteLabel("IF_FALSE_LABEL" + str(saved))
            self.checkNextToken()
            if self.currentToken == "{" or self.currentToken == "}":
                self.manageNextTranslation()

            self.checkNextToken()
            self.compileStatementsHelper()
        elif self.currentToken == "return":
            self.compileReturn()

##############################################  Compile do  ##############################################

# Compiles a do statement.

    def compileDo(self):
        self.compileDoHelper()
        self.vmwriter.writePop("temp", 0)

    def compileDoHelper(self):
        self.compileTermHelper()
        self.manageNextTranslation()

##############################################  Compile Let  ##############################################

# Compiles a let statement.

    def compileLet(self):
        self.checkNextToken()
        varName = self.nextToken
        self.isArray = False
        self.wasRightArray = False
        self.isRight = False
        self.compileLetHelper()

        type = self.symbolTable.TypeOf(varName)

        kind = self.symbolTable.KindOf(varName)
        if self.wasRightArray and type == "Array":
            self.vmwriter.writePop("temp", 0)
            self.vmwriter.writePop("pointer", 1)
            self.vmwriter.writePush("temp", 0)
            self.vmwriter.writePop("that", 0)
            self.isArray = False
        else:
            kind = self.symbolTable.KindOf(varName)
            if kind != "none":
                self.vmwriter.writePop(kind, self.symbolTable.IndexOf(varName))

    def compileLetHelper(self):
        self.manageNextTranslation()
        savedName = self.currentToken
        self.manageNextTranslation()
        if self.currentToken == "[":
            self.CompileExpression()
            kind = self.symbolTable.KindOf(savedName)
            indx = self.symbolTable.IndexOf(savedName)
            if kind != "none":
                self.vmwriter.writePush(kind, indx)
            self.vmwriter.WriteArithmetic("add")
            self.manageNextTranslation()
            self.isArray = True
            self.wasRightArray = True
            self.manageNextTranslation()
        if self.currentToken != "=":
            self.manageNextTranslation()
        self.isRight = True
        self.CompileExpression()

##############################################  Compile While  ##############################################

# Compiles a while statement.

    def compileWhile(self):
        self.compileWhileHelper()

    def compileWhileHelper(self):
        if self.currentTokenType == "keyword":
            self.manageNextTranslation()
            self.compileWhileHelper()
        elif self.currentTokenType == "symbol":
            if self.currentToken == "(":
                self.vmwriter.WriteLabel("WHILE_EXPRESSIONS" +
                                         str(self.whileLabelIndex))
                self.CompileExpression()
                self.manageNextTranslation()
                self.vmwriter.WriteArithmetic("not")
                self.vmwriter.WriteIf("WHILE_END" + str(self.whileLabelIndex))
                self.manageNextTranslation()
                self.compileWhileHelper()

            elif self.currentToken == "{":
                saved = self.whileLabelIndex
                self.manageNextTranslation()
                self.whileLabelIndex += 1
                self.compileStatements()
                self.vmwriter.WriteGoto("WHILE_EXPRESSIONS" + str(saved))
                self.vmwriter.WriteLabel("WHILE_END" + str(saved))

##############################################  Compile return  ##############################################

# Compiles a return statement.

    def compileReturn(self):
        self.checkNextToken()
        if self.nextToken != ";":
            self.CompileExpression()
        else:
            self.vmwriter.writePush("constant", 0)

        self.vmwriter.writeReturn()
        self.manageNextTranslation()

##############################################  Compile If  ##############################################

# Compiles an if statement, posibly with a trailing else clause.

    def compileIf(self):
        self.compileIfHelper()

    def compileIfHelper(self):
        self.manageNextTranslation()
        if self.currentTokenType in "symbol":
            if self.currentToken == "(":
                self.CompileExpression()
                self.vmwriter.WriteIf("IF_TRUE_LABEL" + str(self.ifLabelIndex))
                self.vmwriter.WriteGoto("IF_FALSE_LABEL" +
                                        str(self.ifLabelIndex))
                self.vmwriter.WriteLabel("IF_TRUE_LABEL" +
                                         str(self.ifLabelIndex))
                self.manageNextTranslation()
                self.compileIfHelper()
            elif self.currentToken == "{":
                self.ifLabelIndex += 1
                self.manageNextTranslation()
                self.compileStatements()
                if self.currentToken == ";":
                    self.manageNextTranslation()

##############################################  Compile Expression  ##############################################

# Compiles an expression.

    def CompileExpression(self):
        self.compileExpressionHelper()

    def compileExpressionHelper(self):
        self.CompileTerm()

        self.checkNextToken()
        if self.nextToken in self.op:
            self.manageNextTranslation()
            saved = self.currentToken
            self.compileExpressionHelper()
            if saved == "<":
                self.vmwriter.WriteArithmetic("lt")
            elif saved == ">":
                self.vmwriter.WriteArithmetic("gt")
            elif saved == "+":
                self.vmwriter.WriteArithmetic("add")
            elif saved == "-":
                self.vmwriter.WriteArithmetic("sub")
            elif saved == "*":
                self.vmwriter.WriteCall("Math.multiply", 2)
            elif saved == "/":
                self.vmwriter.WriteCall("Math.divide", 2)
            elif saved == "=":
                self.vmwriter.WriteArithmetic("eq")
            elif saved == "|":
                self.vmwriter.WriteArithmetic("or")
            elif saved == "&":
                self.vmwriter.WriteArithmetic("and")

        elif self.nextToken == ",":
            self.manageNextTranslation()

##############################################  Compile term  ##############################################

    def CompileTerm(self):
        self.compileTermHelper()

        if self.currentToken in self.unaryOp:
            saved = self.currentToken
            self.CompileTerm()
            if saved == "-":
                self.vmwriter.WriteArithmetic("neg")
            else:
                self.vmwriter.WriteArithmetic("not")

        self.isSubroutine = False

    # subName className varName
    def compileTermHelper(self):
        self.manageNextTranslation()

        if self.currentToken in self.terms or self.currentTokenType in self.terms:
            if self.currentTokenType == "integerConstant":
                self.vmwriter.writePush("constant", self.currentToken)
            elif self.currentTokenType == "stringConstant":
                self.vmwriter.writePush("constant", len(self.currentToken))
                self.vmwriter.WriteCall("String.new", 1)
                for ch in self.currentToken:
                    self.vmwriter.writePush("constant", ord(ch))
                    self.vmwriter.WriteCall("String.appendChar", 2)
            elif self.currentTokenType == "keyword":
                if self.currentToken == "null":
                    self.vmwriter.writePush("constant", 0)
                elif self.currentToken == "true":
                    self.vmwriter.writePush("constant", 0)
                    self.vmwriter.WriteArithmetic("not")
                elif self.currentToken == "false":
                    self.vmwriter.writePush("constant", 0)
                elif self.currentToken == "this":
                    self.vmwriter.writePush("pointer", 0)
            elif self.currentTokenType == "identifier":

                if self.isSubroutine:
                    varName = self.currentToken
                    kind = self.symbolTable.KindOf(varName)
                else:
                    self.checkNextToken()
                    if self.nextToken == ";":
                        kind = self.symbolTable.KindOf(self.currentToken)
                        indx = self.symbolTable.IndexOf(self.currentToken)
                        self.vmwriter.writePush(kind, indx)
                        return
                    if self.nextTokenType == "symbol" and self.nextToken != ".":
                        varName = self.currentToken
                        kind = self.symbolTable.KindOf(varName)
                        if kind == "none":
                            self.vmwriter.writePush("pointer", 0)
                            self.isSubroutine = True
                            self.compileTermHelper()
                            self.vmwriter.WriteCall(
                                self.className + "." + varName, self.nArgs + 1)
                            return

                self.currentFuncionName = self.currentToken
                self.isSubroutine = True
                self.checkNextToken()
                savedName = self.currentToken
                savednxt = self.nextToken
                self.checkNextToken()
                if savednxt not in self.op and self.nextToken != ")" and self.nextToken != "]" and self.nextToken != "(":
                    self.manageNextTranslation()
                if self.currentToken == "[":
                    self.CompileExpression()
                    if not self.isRight:
                        self.wasRightArray = True
                    self.isArray = True
                    self.manageNextTranslation()
                if self.symbolTable.KindOf(savedName) != "none":
                    self.vmwriter.writePush(
                        self.symbolTable.KindOf(savedName),
                        self.symbolTable.IndexOf(savedName))
                kind = self.symbolTable.KindOf(self.currentToken)
                if kind == "argument":
                    self.wasArgument = True
                if self.isArray and not self.wasArgument and not self.isSubroutine:
                    if self.isRight:
                        self.isArray = False
                    self.vmwriter.WriteArithmetic("add")
                    self.vmwriter.writePop("pointer", 1)
                    self.vmwriter.writePush("that", 0)
                if self.currentToken == ".":
                    saved = savedName
                    self.manageNextTranslation()

                    toWrite = self.currentFuncionName + "." + self.currentToken
                    savedCurrent = self.currentToken
                    self.compileTermHelper()
                    type = self.symbolTable.TypeOf(savedCurrent)
                    if type == "none":
                        type = self.symbolTable.TypeOf(saved)

                    if type == "none":  # function
                        self.vmwriter.WriteCall(toWrite, self.nArgs)
                    else:
                        kind = self.symbolTable.TypeOf(savedCurrent)
                        indx = self.symbolTable.IndexOf(savedCurrent)
                        if kind != "none":
                            self.vmwriter.writePush(kind, indx)
                        self.currentFuncionName = type
                        saved = self.symbolTable.TypeOf(saved)
                        toWrite = saved + "." + savedCurrent
                        self.nArgs += 1
                        self.vmwriter.WriteCall(toWrite, self.nArgs)

                if self.nextToken not in ["[", "(", "."]:
                    return

                self.compileTermHelper()

        if self.currentTokenType == "symbol":

            if self.currentToken == "(":
                if self.isSubroutine == True:
                    self.nArgs = 0
                    self.CompileExpressionList()
                    self.isSubroutine = False
                else:
                    self.CompileExpression()

                self.manageNextTranslation()

##############################################  Compile ExpressionList  ##############################################

# Compiles a (possibly empty) comma-separated
# list of expressions.

    def CompileExpressionList(self):
        self.checkNextToken()

        if self.nextToken in self.unaryOp:
            self.CompileExpression()
            return

        self.checkNextToken()

        while self.nextToken != ")":
            self.nArgs += 1
            self.CompileExpression()

##############################################  Some Helper Functions  ##############################################

# updates next token (token itself and its type)
# without removing from general list

    def checkNextToken(self):
        toCheck = self.lines[0]
        type = toCheck[1:toCheck.find(">")]
        startIndx = toCheck.find(">") + 2
        endIndx = toCheck.find("<", startIndx) - 1
        token = toCheck[startIndx:endIndx]
        self.nextToken = token
        self.nextTokenType = type

    # removes next token from general list
    # and updates information about current token
    def manageNextTranslation(self):
        toTranslate = self.lines.pop(0)
        self.currentLine = toTranslate
        type = toTranslate[1:toTranslate.find(">")]
        startIndx = toTranslate.find(">") + 2
        endIndx = toTranslate.find("<", startIndx) - 1
        token = toTranslate[startIndx:endIndx]
        self.currentToken = token
        self.currentTokenType = type