/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

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