Commit 3b7bf68e by Tianqi Yang

feat(operator): allow operator overloading for binary/unary/print op

Add support for operator overloading: - Binary operator: + : add - : sub * : mul / : div % : mod &&: and ||: or ==: eq !=: ne < : lt > : gt <=: le >=: ge - Unary operator: - : neg ! : not - print function Binary operator overloading: - Try to find op and rop, if ambiguous then issue an error Modify AST: - Add member callExpr to some of the nodes to store operator function Add errors: - BinaryOperatorAmbiguousError: Ambiguous candidates for binary operator - BinaryOperatorNotFoundError No method match the binary operator - UnaryOperatorNotFoundError No method match the unary operator
parent 060d67eb
......@@ -22,7 +22,7 @@ public class BadPrintArgError extends DecafError {
@Override
protected String getErrMsg() {
return "incompatible argument " + count + ": " + type
+ " given, int/bool/string expected";
+ " given, int/bool/string or class with method 'print()' expected";
}
}
package decaf.error;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import decaf.Location;
import decaf.symbol.Function;
import decaf.tree.Tree;
import decaf.type.Type;
import decaf.utils.CommonUtils;
import decaf.utils.FuncUtils;
public class BinaryOperatorAmbiguousError extends DecafError {
private final static Map<Integer, String> BinOpSymbol = new HashMap<Integer, String>();
static {
BinOpSymbol.put(Tree.PLUS, "+" );
BinOpSymbol.put(Tree.MINUS, "-" );
BinOpSymbol.put(Tree.MUL, "*" );
BinOpSymbol.put(Tree.DIV, "/" );
BinOpSymbol.put(Tree.MOD, "%" );
BinOpSymbol.put(Tree.AND, "&&");
BinOpSymbol.put(Tree.OR, "||");
BinOpSymbol.put(Tree.EQ, "==");
BinOpSymbol.put(Tree.NE, "!=");
BinOpSymbol.put(Tree.LT, "<" );
BinOpSymbol.put(Tree.GT, ">" );
BinOpSymbol.put(Tree.LE, "<=");
BinOpSymbol.put(Tree.GE, ">=");
}
Type left;
String op;
Type right;
List<Function> functions;
public BinaryOperatorAmbiguousError (Type left, int op, Type right, List<Function> functions, Location location) {
super(location);
this.left = left;
this.op = BinOpSymbol.get(op);
this.right = right;
this.functions = CommonUtils.copyList(functions);
}
@Override
protected String getErrMsg() {
String msg = "call of overloaded operator '" + left + " " + op + " " + right + "' is ambiguous, candidates are :\n";
for (Function func : functions) {
msg += " *** note : candidate at " + func.getLocation() + " : " + func.getReturnType() + " " + FuncUtils.getArgListString(func.getName(), func.getType().getArgList(), func.isStatik()) + "\n";
}
return msg.trim();
}
}
package decaf.error;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import decaf.Location;
import decaf.symbol.Function;
import decaf.tree.Tree;
import decaf.type.Type;
import decaf.utils.CommonUtils;
import decaf.utils.FuncUtils;
public class BinaryOperatorNotFoundError extends DecafError {
private final static Map<Integer, String> BinOpSymbol = new HashMap<Integer, String>();
static {
BinOpSymbol.put(Tree.PLUS, "+" );
BinOpSymbol.put(Tree.MINUS, "-" );
BinOpSymbol.put(Tree.MUL, "*" );
BinOpSymbol.put(Tree.DIV, "/" );
BinOpSymbol.put(Tree.MOD, "%" );
BinOpSymbol.put(Tree.AND, "&&");
BinOpSymbol.put(Tree.OR, "||");
BinOpSymbol.put(Tree.EQ, "==");
BinOpSymbol.put(Tree.NE, "!=");
BinOpSymbol.put(Tree.LT, "<" );
BinOpSymbol.put(Tree.GT, ">" );
BinOpSymbol.put(Tree.LE, "<=");
BinOpSymbol.put(Tree.GE, ">=");
}
Type left;
String op;
Type right;
public BinaryOperatorNotFoundError (Type left, int op, Type right, Location location) {
super(location);
this.left = left;
this.op = BinOpSymbol.get(op);
this.right = right;
}
@Override
protected String getErrMsg() {
String msg = "no match method for call to operator '" + left + " " + op + " " + right + "'";
return msg;
}
}
package decaf.error;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import decaf.Location;
import decaf.symbol.Function;
import decaf.tree.Tree;
import decaf.type.Type;
import decaf.utils.CommonUtils;
import decaf.utils.FuncUtils;
public class UnaryOperatorNotFoundError extends DecafError {
private final static Map<Integer, String> UnOpSymbol = new HashMap<Integer, String>();
static {
UnOpSymbol.put(Tree.NEG, "-" );
UnOpSymbol.put(Tree.NOT, "!" );
}
Type expr;
String op;
public UnaryOperatorNotFoundError (Type expr, int op, Location location) {
super(location);
this.expr = expr;
this.op = UnOpSymbol.get(op);
}
@Override
protected String getErrMsg() {
String msg = "no match method for call to operator '" + op + " " + expr + "'";
return msg;
}
}
package decaf.translate;
import java.util.Iterator;
import java.util.Stack;
import decaf.tree.Tree;
......@@ -60,6 +61,12 @@ public class TransPass2 extends Tree.Visitor {
@Override
public void visitBinary(Tree.Binary expr) {
if (expr.callExpr != null) {
expr.callExpr.accept(this);
expr.val = expr.callExpr.val;
return;
}
expr.left.accept(this);
expr.right.accept(this);
switch (expr.tag) {
......@@ -167,6 +174,12 @@ public class TransPass2 extends Tree.Visitor {
@Override
public void visitUnary(Tree.Unary expr) {
if (expr.callExpr != null) {
expr.callExpr.accept(this);
expr.val = expr.callExpr.val;
return;
}
expr.expr.accept(this);
switch (expr.tag){
case Tree.NEG:
......@@ -217,7 +230,11 @@ public class TransPass2 extends Tree.Visitor {
@Override
public void visitPrint(Tree.Print printStmt) {
Iterator<Tree.CallExpr> ceIter = printStmt.callExprs.iterator();
for (Tree.Expr r : printStmt.exprs) {
if (r.type.isClassType()) {
ceIter.next().accept(this);
} else {
r.accept(this);
tr.genParm(r.val);
if (r.type.equal(BaseType.BOOL)) {
......@@ -227,6 +244,8 @@ public class TransPass2 extends Tree.Visitor {
} else if (r.type.equal(BaseType.STRING)) {
tr.genIntrinsicCall(Intrinsic.PRINT_STRING);
}
ceIter.next();
}
}
}
......
......@@ -680,6 +680,8 @@ public abstract class Tree {
public List<Expr> exprs;
public List<CallExpr> callExprs;
public Print(List<Expr> exprs, Location loc) {
super(PRINT, loc);
this.exprs = exprs;
......@@ -884,6 +886,8 @@ public abstract class Tree {
public Expr expr;
public CallExpr callExpr;
public Unary(int kind, Expr expr, Location loc) {
super(kind, loc);
this.expr = expr;
......@@ -922,6 +926,8 @@ public abstract class Tree {
public Expr left;
public Expr right;
public CallExpr callExpr;
public Binary(int kind, Expr left, Expr right, Location loc) {
super(kind, loc);
this.left = left;
......
package decaf.typecheck;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import decaf.Driver;
......@@ -16,6 +20,8 @@ import decaf.error.BadNewArrayLength;
import decaf.error.BadPrintArgError;
import decaf.error.BadReturnTypeError;
import decaf.error.BadTestExpr;
import decaf.error.BinaryOperatorAmbiguousError;
import decaf.error.BinaryOperatorNotFoundError;
import decaf.error.BreakOutOfLoopError;
import decaf.error.ClassNotFoundError;
import decaf.error.DecafError;
......@@ -30,6 +36,7 @@ import decaf.error.NotClassMethodError;
import decaf.error.RefNonStaticError;
import decaf.error.SubNotIntError;
import decaf.error.ThisInStaticFuncError;
import decaf.error.UnaryOperatorNotFoundError;
import decaf.error.UndeclVarError;
import decaf.frontend.Parser;
import decaf.scope.ClassScope;
......@@ -64,12 +71,27 @@ public class TypeCheck extends Tree.Visitor {
@Override
public void visitBinary(Tree.Binary expr) {
expr.type = checkBinaryOp(expr.left, expr.right, expr.tag, expr.loc);
expr.type = checkBinaryOp(expr.left, expr.right, expr.tag, expr, expr.loc);
}
@Override
public void visitUnary(Tree.Unary expr) {
expr.expr.accept(this);
if (expr.expr.type.isClassType()) {
FuncUtils.getCallFunctionReturn returnVal = FuncUtils.getCallFunction(expr.expr, expr.tag == Tree.NEG ? "op__neg__" : "op__not__", new ArrayList<>(), table, currentFunction, expr.getLocation());
if (returnVal.ok) {
expr.callExpr = new Tree.CallExpr(returnVal.symbol.isStatik()?null:expr.expr, expr.tag == Tree.NEG ? "op__neg__" : "op__not__", new ArrayList<>(), expr.getLocation());
expr.callExpr.symbol = returnVal.symbol;
expr.callExpr.isArrayLength = false;
expr.type = returnVal.symbol.getReturnType();
} else {
issueError(new UnaryOperatorNotFoundError(expr.expr.type, expr.tag, expr.getLocation()));
expr.type = BaseType.ERROR;
}
return;
}
if(expr.tag == Tree.NEG){
if (expr.expr.type.equal(BaseType.ERROR)
|| expr.expr.type.equal(BaseType.INT)) {
......@@ -438,14 +460,30 @@ public class TypeCheck extends Tree.Visitor {
@Override
public void visitPrint(Tree.Print printStmt) {
int i = 0;
printStmt.callExprs = new ArrayList<>();
for (Tree.Expr e : printStmt.exprs) {
e.accept(this);
i++;
if (!e.type.equal(BaseType.ERROR) && !e.type.equal(BaseType.BOOL)
if (e.type.isClassType()) {
FuncUtils.getCallFunctionReturn returnVal = FuncUtils.getCallFunction(e, "print", new ArrayList<>(), table, currentFunction, e.getLocation());
if (returnVal.ok) {
Tree.CallExpr callExpr = new Tree.CallExpr (returnVal.symbol.isStatik()?null:e, "print", new ArrayList<>(), e.getLocation());
callExpr.symbol = returnVal.symbol;
callExpr.isArrayLength = false;
printStmt.callExprs.add(callExpr);
} else {
printStmt.callExprs.add(null);
issueError(new BadPrintArgError(e.getLocation(), Integer
.toString(i), e.type.toString()));
}
} else if (!e.type.equal(BaseType.ERROR) && !e.type.equal(BaseType.BOOL)
&& !e.type.equal(BaseType.INT)
&& !e.type.equal(BaseType.STRING)) {
printStmt.callExprs.add(null);
issueError(new BadPrintArgError(e.getLocation(), Integer
.toString(i), e.type.toString()));
} else {
printStmt.callExprs.add(null);
}
}
}
......@@ -529,10 +567,75 @@ public class TypeCheck extends Tree.Visitor {
Driver.getDriver().issueError(error);
}
private Type checkBinaryOp(Tree.Expr left, Tree.Expr right, int op, Location location) {
private final static Map<Integer, String> BinOpFuncName = new HashMap<Integer, String>();
static {
BinOpFuncName.put(Tree.PLUS, "add");
BinOpFuncName.put(Tree.MINUS, "sub");
BinOpFuncName.put(Tree.MUL, "mul");
BinOpFuncName.put(Tree.DIV, "div");
BinOpFuncName.put(Tree.MOD, "mod");
BinOpFuncName.put(Tree.AND, "and");
BinOpFuncName.put(Tree.OR, "or" );
BinOpFuncName.put(Tree.EQ, "eq" );
BinOpFuncName.put(Tree.NE, "ne" );
BinOpFuncName.put(Tree.LT, "lt" );
BinOpFuncName.put(Tree.GT, "gt" );
BinOpFuncName.put(Tree.LE, "le" );
BinOpFuncName.put(Tree.GE, "ge" );
}
private Type checkBinaryOp(Tree.Expr left, Tree.Expr right, int op, Tree.Binary expr, Location location) {
left.accept(this);
right.accept(this);
if (left.type.isClassType() || right.type.isClassType()) {
if (left.type.equal(BaseType.ERROR) || right.type.equal(BaseType.ERROR)) {
return BaseType.ERROR;
}
FuncUtils.getCallFunctionReturn leftFunc = (left.type.isClassType() ? FuncUtils.getCallFunction(left, "op__" + BinOpFuncName.get(op) + "__", Arrays.asList(right), table, currentFunction, location) : (new FuncUtils.getCallFunctionReturn(new BinaryOperatorNotFoundError(left.type, op, right.type, location))));
FuncUtils.getCallFunctionReturn rightFunc = (right.type.isClassType() ? FuncUtils.getCallFunction(right, "rop__" + BinOpFuncName.get(op) + "__", Arrays.asList(left), table, currentFunction, location) : (new FuncUtils.getCallFunctionReturn(new BinaryOperatorNotFoundError(left.type, op, right.type, location))));
if (leftFunc.ok && rightFunc.ok) {
boolean leq = false, req = false;
if (leftFunc.symbol.getType().getArgList().get(leftFunc.symbol.isStatik()?0:1).equal(right.type)) {
leq = true;
}
if (rightFunc.symbol.getType().getArgList().get(rightFunc.symbol.isStatik()?0:1).equal(left.type)) {
req = true;
}
if (leq && !req) {
rightFunc.ok = false;
} else if (req && !leq) {
leftFunc.ok = false;
} else {
issueError(new BinaryOperatorAmbiguousError(left.type, op, right.type, Arrays.asList(leftFunc.symbol, rightFunc.symbol), location));
if (leftFunc.symbol.getReturnType().equal(rightFunc.symbol.getReturnType())) {
return leftFunc.symbol.getReturnType();
} else {
return BaseType.ERROR;
}
}
}
if (leftFunc.ok && !rightFunc.ok) {
expr.callExpr = new Tree.CallExpr(leftFunc.symbol.isStatik()?null:left, "op__" + BinOpFuncName.get(op) + "__", Arrays.asList(right), location);
expr.callExpr.symbol = leftFunc.symbol;
expr.callExpr.isArrayLength = false;
return leftFunc.symbol.getReturnType();
} else if (!leftFunc.ok && rightFunc.ok) {
expr.callExpr = new Tree.CallExpr(rightFunc.symbol.isStatik()?null:right, "rop__" + BinOpFuncName.get(op) + "__", Arrays.asList(left), location);
expr.callExpr.symbol = rightFunc.symbol;
expr.callExpr.isArrayLength = false;
return rightFunc.symbol.getReturnType();
} else if ((op == Tree.EQ || op == Tree.NE) && (left.type.compatible(right.type) || right.type.compatible(left.type))) {
return BaseType.BOOL;
} else {
issueError(new BinaryOperatorNotFoundError(left.type, op, right.type, location));
return BaseType.ERROR;
}
}
if (left.type.equal(BaseType.ERROR) || right.type.equal(BaseType.ERROR)) {
switch (op) {
case Tree.PLUS:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment