diff --git a/ExprPrimaryNodes.py b/ExprPrimaryNodes.py index 043b18beea79d0ac1acf87da0b4b14f1e44ca281..d04a094a14649292f69e51014d66b4b0767f8852 100644 --- a/ExprPrimaryNodes.py +++ b/ExprPrimaryNodes.py @@ -93,16 +93,22 @@ def helperDisambigName(node): # args exprs, exprs expr COMMA exprs class ArgsNode(ASTNode): # always list all fields in the init method to show the class structure - def __init__(self, parseTree, typeName): + def __init__(self, parseTree, typeName, exprs = []): self.parseTree = parseTree self.exprs = [] # a list of expressions self.env = None self.children = [] self.typeName = typeName # the type (class/interface) this node belongs under - exprs = getParseTreeNodes(['expr'], parseTree) - for e in exprs: - self.exprs.append(makeNodeFromExpr(e, typeName)) + if exprs == []: + exprs = getParseTreeNodes(['expr'], parseTree) + for e in exprs: + self.exprs.append(makeNodeFromExpr(e, typeName)) + else: + # manually induce exprs + # for converting string concatenation to a method call + # see ExprPrimaryNodes:ExprNode on string concat + self.exprs = exprs self.children.extend(self.exprs) @@ -582,6 +588,49 @@ class ExprNode(ASTNode): or (self.right.myType.name =='java.lang.String' and self.left.myType.name not in ['void'])) and self.op == '+': self.myType = TypeStruct('java.lang.String', self.env.getNode('java.lang.String', 'type')) self.myType.link(self.env) + + # var1 + var2 === String.valueOf(var1).concat( String.valueOf(var2) ) + + # methodInvoc name LPAREN args RPAREN (because it's static) + # MethodInvNode + # ID = NameNode + # - name = "String.valueOf" + # args = ArgsNode + # - exprs = [self.left] + + # create MethodInvNode to call String.valueOf(left) and String.valueOf(right) + # 1. Make NameNode for String.valueOf + valueOfNameNode = NameNode(self.parseTree, True, self.typeName, "String.valueOf") + valueOfNameNode.env = self.env + # 2. Make ArgsNode for left and right + leftArg = ArgsNode(self.parseTree, self.typeName, [self.left]) + rightArg = ArgsNode(self.parseTree, self.typeName, [self.right]) + # 3. Put it all together + valueOfMethodInvLeft = MethodInvNode(self.parseTree, self.typeName, valueOfNameNode, leftArg) + valueOfMethodInvRight = MethodInvNode(self.parseTree, self.typeName, valueOfNameNode, rightArg) + # 4. Check type to be safe + # valueOfMethodInvLeft.checkType() + # valueOfMethodInvRight.checkType() + + + # methodInvoc primary PERIOD ID LPAREN args RPAREN + # MethodInvNode + # primary = self.left + # ID = NameNode + # - name = "concat" + # args = ArgsNode + # - exprs = [self.right] + + # create MethodInvNode to call left.concat(right) + # 1. Make NameNode for String.concat (name = "concat") + concatNameNode = NameNode(self.parseTree, True, self.typeName, "concat") + # 2. Make ArgsNode for right + rightArgMethodInv = ArgsNode(self.parseTree, self.typeName, [valueOfMethodInvRight]) + # 3. Put it all together + self.concatMethodInv = MethodInvNode(self.parseTree, self.typeName, concatNameNode, rightArgMethodInv, valueOfMethodInvLeft) + # 4. Check type to be safe + self.concatMethodInv.checkType() + return raise Exception("ERROR: Incompatible types. Left of {} type can't be used with right of {} type on operation {}".format(self.left.myType.name, self.right.myType.name, self.op)) @@ -701,6 +750,20 @@ class ExprNode(ASTNode): # String Add TODO if (self.left.myType.name =='java.lang.String' or self.right.myType.name =='java.lang.String') \ and self.op == '+': + # String.valueOf(left) + # if not hasattr(self.valueOfMethodInvLeft, "code"): + # self.valueOfMethodInvLeft.codeGen() + # self.code += self.valueOfMethodInvLeft.code + + # String.valueOf(right) + # if not hasattr(self.valueOfMethodInvLeft, "code"): + # self.valueOfMethodInvLeft.codeGen() + # self.code += self.valueOfMethodInvLeft.code + + # ( String.valueOf(right) ).concat( left ) + if not hasattr(self.concatMethodInv, "code"): + self.concatMethodInv.codeGen() + self.code += self.concatMethodInv.code return # Number Add, Subtract, Multiply if self.op in ['+', '-', '*']: @@ -928,7 +991,7 @@ class FieldAccessNode(ASTNode): # methodInvoc class MethodInvNode(ASTNode): # always list all fields in the init method to show the class structure - def __init__(self, parseTree, typeName): + def __init__(self, parseTree, typeName, ID = '', args = None, primary = None): self.parseTree = parseTree self.primary = None # can be empty self.ID = '' # can be either ID or compID @@ -938,12 +1001,23 @@ class MethodInvNode(ASTNode): self.method = None self.typeName = typeName # the type (class/interface) this node belongs under - # input parse tree is either: methodInvoc primary PERIOD ID LPAREN args RPAREN - # methodInvoc name LPAREN args RPAREN - self.ID = NameNode(parseTree.children[-4], True, typeName) - self.args = ArgsNode(parseTree.children[-2], typeName) - if parseTree.children[0].name == 'primary': - self.primary = makeNodeFromAllPrimary(parseTree.children[0], typeName) + if ID == '' or args is None: + # input parse tree is either: methodInvoc primary PERIOD ID LPAREN args RPAREN + # methodInvoc name LPAREN args RPAREN + self.ID = NameNode(parseTree.children[-4], True, typeName) + self.args = ArgsNode(parseTree.children[-2], typeName) + if parseTree.children[0].name == 'primary': + self.primary = makeNodeFromAllPrimary(parseTree.children[0], typeName) + self.override = False + else: + # manually induce ID and args + # for converting string concatenation to a method call + # see ExprPrimaryNodes:ExprNode on string concat + self.ID = ID + self.args = args + if primary is not None: + self.primary = primary + self.override = True self.children.append(self.primary) self.children.append(self.args) @@ -986,7 +1060,7 @@ class MethodInvNode(ASTNode): if m: # check static - if (not self.ID.shouldBeStatic) and 'static' in m.mods: + if (not self.ID.shouldBeStatic and not self.override) and 'static' in m.mods: raise Exception("ERROR: Non-static access of static method {}.".format(m.name)) # check protected diff --git a/NameNode.py b/NameNode.py index 859f25fdeb799941053e0a70b25754de7ab09056..70ce82cf4de5bb8c57ac120ea9f530274ac48b5b 100644 --- a/NameNode.py +++ b/NameNode.py @@ -19,7 +19,7 @@ from codeGenNodes import genNameNode class NameNode(ASTNode): - def __init__(self, parseTree, methodInvoke, typeName): + def __init__(self, parseTree, methodInvoke, typeName, name = ""): self.parseTree = parseTree self.name = "" # str: stores original lex of the name (ID/COMPID) @@ -40,7 +40,14 @@ class NameNode(ASTNode): self.staticField = None self.prefixNodes = [] # stores the past prefix links so we can evaluate them one by one - self.name = getParseTreeNodes(["ID", "COMPID"], parseTree)[0].lex + if name == "": + self.name = getParseTreeNodes(["ID", "COMPID"], parseTree)[0].lex + else: + # manually induce name + # for converting string concatenation to a method call + # see ExprPrimaryNodes:ExprNode on string concat + self.name = name + self.IDs = self.name.split(".") # Updates the resolved/identified prefix diff --git a/UnitNodes.py b/UnitNodes.py index 606114766223e0f486d3e7238a877ad841611c2f..15d466e337b367f3cdc9881cbb090a9a02660ff7 100644 --- a/UnitNodes.py +++ b/UnitNodes.py @@ -1,7 +1,7 @@ from AST import ASTNode, getParseTreeNodes from Environment import Env from TheTypeNode import TypeNode, TypeStruct -from CodeGenUtils import p, getLStringLabel +from CodeGenUtils import p, getLStringLabel, importHelper # LiteralNode # ParamNode @@ -55,20 +55,88 @@ class LiteralNode(ASTNode): self.code += p("mov", "eax", "0", " set to literal false") return - # char TODO + # char if self.name == 'char': - self.code += p("mov", "eax", str(self.getConstant()), " set to literal char") + self.code += p("mov", "eax", str("dword " + self.getConstant()), " set to literal char") return - # string TODO - # if self.name == 'java.lang.String': - # n = getLStringLabel() - # lStringLabel = "_literalstring" + n + # string + if self.name == 'java.lang.String': + self.code += ";Start of String Literal Creation for class " + self.typeName + "\n" + # generate array of characters + # then call new String(array) + + # remove quotation marks + value = self.value[1:-1] - # self.code += p(lStringLabel + ":", "") - # self.code += p("db", str(self.getConstant())) - # self.code += p("mov", "eax", "[" + lStringLabel + "]", " set to literal string") - # return + # FIRST: create char array for this string + # 1. Allocating space for the char array in heap + # Note: uses ecx to temporarily store the array length + self.code += ";Start of char array creation\n" + \ + p(instruction="mov", arg1="eax", arg2=str(len(value)), comment="array length") + \ + p(instruction="push", arg1="ecx", comment="saving ecx's value") + \ + p(instruction="mov", arg1="ecx", arg2="eax", comment="ecx now stroes the array's length") + \ + p(instruction="add", arg1="eax", arg2=2, comment="number of items to allocate on heap") + \ + p(instruction="imul", arg1="eax", arg2=4, comment="number of bytes to allocate on heap") + \ + p(instruction="call", arg1="__malloc", comment="allocating memory for array on heap") + + # 2. Pointing first item to vtable + aLabel = "A_char" + self.code += p(instruction="extern", arg1=aLabel) + \ + p(instruction="mov", arg1="[eax]", arg2="dword "+aLabel, comment="first item is vtable pointer") + + # 3. Storing length in the second slot + self.code += p(instruction="mov", arg1="[eax+4]", arg2="ecx", comment="storing array length in second slot") + + # 4. populate array with this string literal's chars + self.code += ";Start of populating char array\n" + # loop through string + for i in range(len(value)): + self.code += p(instruction="mov", arg1="[eax+"+str(8+i*4)+"]", arg2=str("dword '"+value[i]+"'"), comment="a["+str(i)+"]="+str(value[i])) + self.code += ";End of populating char array\n" + + # 5. Restoring ecx + self.code += p(instruction="pop", arg1="ecx", comment="restoring register ecx") + self.code += ";End of char array creation\n" + + # SECOND: create new string object with the char array + + # push array pointer + self.code += p(instruction="push", arg1="eax", comment="save pointer to char array") + + # 1. alloc enough space for this object + classDef = self.env.getNode('java.lang.String', 'type') + fieldOffset = classDef.fieldOffset + numFields = len(fieldOffset) + numBytes = (numFields + 1) * 4 + self.code += "; Creating an object for class " + self.typeName + "\n" + self.code += p(instruction="mov", arg1="eax", arg2=numBytes, comment="allocating memory for object") + \ + p(instruction="call", arg1="__malloc") + + # 2. Pointing first item to vtable + self.code += importHelper(classDef.name, self.typeName, "C_"+classDef.name) + self.code += p(instruction="mov", arg1="[eax]", arg2="dword C_" + classDef.name, comment="first item is vtable pointer") + + # restore pointer to our char array + self.code += p("pop", "ebx", comment="restore pointer to our char array") + + # 3. Calling constructor + self.code += "; Calling constructor for String\n" + self.code += p(instruction="push", arg1="eax", comment="pushing object as first argument") + # Evaluate arguments and pushing parameters + self.code += p("push", "ebx", comment="add our char array as arg") + + # 4. call String::String(char[] chars) + label = "M_String_String_char" + self.code += importHelper(classDef.name, self.typeName, label) + self.code += p(instruction="call", arg1=label, comment="Calling constructor") + + # 4. Popping parameters and pointer to object + self.code += p(instruction="add", arg1="esp", arg2=4, comment="Popping parameters") + self.code += p(instruction="pop", arg1="eax", comment="eax now contains pointer to newly created object") + self.code += ";End of object creation\n" + self.code += ";End of String Literal Creation for class " + self.typeName + "\n" + return # null if self.name == 'null':