/src/org/ooc/frontend/model/BinaryOperation.java
Java | 234 lines | 168 code | 41 blank | 25 comment | 39 complexity | 1c38f90be07574d89689b76c5b2d9125 MD5 | raw file
1package org.ooc.frontend.model; 2 3import java.io.IOException; 4import java.util.LinkedHashMap; 5 6import org.ooc.frontend.Visitor; 7import org.ooc.frontend.model.OpDecl.OpType; 8import org.ooc.frontend.model.interfaces.MustBeResolved; 9import org.ooc.frontend.model.interfaces.MustBeUnwrapped; 10import org.ooc.frontend.model.tokens.Token; 11import org.ooc.middle.OocCompilationError; 12import org.ooc.middle.hobgoblins.Resolver; 13 14/** 15 * Binary in the sense that it has a left and a right operand (e.g. binary op, 16 * as opposed to unary op or ternary op) 17 * 18 * Operator precedence chart: 19 * 20 * 10 * / % 21 * 20 + - 22 * 30 << >> 23 * 40 < > <= >= 24 * 50 == != 25 * 60 & 26 * 70 ^ 27 * 80 | 28 * 90 && 29 * 100 || 30 * 110 ?: 31 * 120 = += -= /= *= >>= <<= ^= &= |= 32 */ 33public abstract class BinaryOperation extends Expression implements MustBeUnwrapped, MustBeResolved { 34 35 protected Expression left; 36 protected Expression right; 37 38 public BinaryOperation(Expression left, Expression right, Token startToken) { 39 super(startToken); 40 this.left = left; 41 this.right = right; 42 } 43 44 public Expression getLeft() { 45 return left; 46 } 47 48 public void setLeft(Expression left) { 49 this.left = left; 50 } 51 52 public Expression getRight() { 53 return right; 54 } 55 56 public void setRight(Expression right) { 57 this.right = right; 58 } 59 60 public Type getType() { 61 // FIXME probably not right (haha) 62 return getLeft().getType(); 63 } 64 65 public boolean hasChildren() { 66 return true; 67 } 68 69 public void acceptChildren(Visitor visitor) throws IOException { 70 left.accept(visitor); 71 right.accept(visitor); 72 } 73 74 public boolean isResolved() { 75 return false; 76 } 77 78 public Response resolve(NodeList<Node> stack, Resolver res, boolean fatal) { 79 80 if(left.getType() == null) { 81 if(left instanceof MustBeResolved) { 82 ((MustBeResolved) left).resolve(stack, res, fatal); 83 } 84 if(fatal) { 85 System.out.println("stack = "+stack.toString(true)); 86 throw new OocCompilationError(this, stack, "Can't resolve type of left "+ 87 left+" operand. Wtf?"); 88 } 89 return Response.LOOP; 90 } 91 92 if(right.getType() == null) { 93 if(right instanceof MustBeResolved) { 94 ((MustBeResolved) right).resolve(stack, res, fatal); 95 } 96 if(fatal) { 97 throw new OocCompilationError(this, stack, "Can't resolve type of right " 98 +right+" operand. Seriously."); 99 } 100 return Response.LOOP; 101 } 102 103 OpType opType = getOpType(); 104 105 OpDecl bestOp = null; 106 int bestScore = 0; 107 for(OpDecl op: res.module.getOps()) { 108 int score = getOpScore(stack, opType, op, res); 109 if(score > bestScore) { 110 bestScore = score; 111 bestOp = op; 112 } 113 } 114 for(Import imp: res.module.getAllImports()) { 115 for(OpDecl op: imp.getModule().getOps()) { 116 int score = getOpScore(stack, opType, op, res); 117 if(score > bestScore) { 118 bestScore = score; 119 bestOp = op; 120 } 121 } 122 } 123 124 if(bestOp != null) { 125 NodeList<Argument> args = bestOp.getFunc().getArguments(); 126 Argument leftArg = args.get(0); 127 //Argument rightArg = args.get(1); 128 129 if(leftArg.getType().softEquals(left.getType(), res) && leftArg.getType().getReferenceLevel() == (left.getType().getReferenceLevel() + 1)) { 130 left = new AddressOf(left, left.startToken); 131 } 132 133 FunctionCall call = new FunctionCall(bestOp.getFunc(), startToken); 134 call.getArguments().add(left); 135 call.getArguments().add(right); 136 Node parent = stack.peek(); 137 parent.replace(this, call); 138 call.resolve(stack, res, true); 139 return Response.LOOP; 140 } 141 142 if(opType.isNumeric() && left.getType().getRef() instanceof ClassDecl && left.getType().getPointerLevel() == 0) { 143 throw new OocCompilationError(this, stack, "Using operator "+opType.toPrettyString()+" between non-numeric types." 144 +" Maybe you want to overload it? Do it like this: operator " 145 +opType.toPrettyString()+" (left: "+left.getType()+", right: "+right.getType()+") { ... }"); 146 } 147 148 return Response.OK; 149 150 } 151 152 private int getOpScore(NodeList<Node> stack, OpType opType, OpDecl op, Resolver res) { 153 154 int score = 0; 155 if(op.getOpType() == opType) { 156 if(op.getFunc().getArguments().size() != 2) { 157 throw new OocCompilationError(op, stack, 158 "To overload the "+opType.toPrettyString()+" operator, you need exactly two arguments, not " 159 +op.getFunc().getArgsRepr()); 160 } 161 NodeList<Argument> args = op.getFunc().getArguments(); 162 Argument first = args.get(0); 163 Argument second = args.get(1); 164 if(first.getType().softEquals(left.getType(), res)) { 165 if(second.getType().softEquals(right.getType(), res) || isGeneric(second.getType(), op.getFunc().getTypeParams())) { 166 score += 10; 167 if(first.getType().equals(left.getType())) { 168 // prefer operators where the first argument is a reference to something other than a class 169 if(first.getType().getReferenceLevel() == (left.getType().getReferenceLevel() + 1)) { 170 score += 10; 171 } 172 173 score += 20; 174 } 175 if(second.getType().equals(right.getType())) { 176 score += 20; 177 } 178 } 179 } 180 } 181 return score; 182 183 } 184 185 private boolean isGeneric(Type type, LinkedHashMap<String, TypeParam> linkedHashMap) { 186 return linkedHashMap.containsKey(type.getName()); 187 } 188 189 @Override 190 public boolean replace(Node oldie, Node kiddo) { 191 if(oldie == left) { 192 left = (Expression) kiddo; 193 return true; 194 } 195 196 if(oldie == right) { 197 right = (Expression) kiddo; 198 return true; 199 } 200 201 return false; 202 } 203 204 public boolean unwrap(NodeList<Node> stack) throws IOException { 205 206 // Example: 207 // Mul(1, Add(2, 3)) 208 // Should infact be Add(Mul(1, 2), 3) 209 210 if(right instanceof BinaryOperation) { 211 BinaryOperation opRight = (BinaryOperation) right; 212 if(getPriority() < opRight.getPriority()) { 213 Expression tmp = opRight.getLeft(); 214 opRight.setLeft(this); 215 this.setRight(tmp); 216 stack.peek().replace(this, opRight); 217 return true; 218 } 219 } 220 221 return false; 222 223 } 224 225 public abstract OpType getOpType(); 226 227 public abstract int getPriority(); 228 229 @Override 230 public String toString() { 231 return "(" + left + " " + getOpType().toPrettyString() + " " + right + ")"; 232 } 233 234}