Skip to content
Snippets Groups Projects
Commit 32c6f60d authored by Xun Yang's avatar Xun Yang
Browse files

Merge branch 'string_concat' into 'new-string'

String concat

See merge request !27
parents 06967a16 be4d8bf8
No related branches found
No related tags found
3 merge requests!30New new string,!27String concat,!20Master
......@@ -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
......
......@@ -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
......
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':
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment