PageRenderTime 38ms CodeModel.GetById 13ms app.highlight 15ms RepoModel.GetById 2ms app.codeStats 0ms

/src/org/ooc/frontend/model/BinaryOperation.java

http://github.com/nddrylliog/ooc
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}