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

http://github.com/nddrylliog/ooc · Java · 265 lines · 213 code · 48 blank · 4 comment · 78 complexity · 9acc105d6c661a45b7da778d3b62d7a6 MD5 · raw file

  1. package org.ooc.frontend.model;
  2. import java.io.IOException;
  3. import org.ooc.frontend.Levenshtein;
  4. import org.ooc.frontend.Visitor;
  5. import org.ooc.frontend.model.interfaces.MustBeResolved;
  6. import org.ooc.frontend.model.tokens.Token;
  7. import org.ooc.middle.OocCompilationError;
  8. import org.ooc.middle.hobgoblins.Resolver;
  9. public class MemberAccess extends VariableAccess {
  10. protected Expression expression;
  11. public MemberAccess(String variable, Token startToken) {
  12. this(new VariableAccess("this", startToken), variable, startToken);
  13. }
  14. public MemberAccess(Expression expression, String variable, Token startToken) {
  15. super(variable, startToken);
  16. this.expression = expression;
  17. }
  18. public MemberAccess(Expression expression, VariableAccess variableAccess, Token startToken) {
  19. super(variableAccess.getName(), startToken);
  20. this.expression = expression;
  21. }
  22. public Expression getExpression() {
  23. return expression;
  24. }
  25. public void setExpression(Expression expression) {
  26. this.expression = expression;
  27. }
  28. @Override
  29. public void accept(Visitor visitor) throws IOException {
  30. visitor.visit(this);
  31. }
  32. @Override
  33. public void acceptChildren(Visitor visitor) throws IOException {
  34. expression.accept(visitor);
  35. super.acceptChildren(visitor);
  36. }
  37. @Override
  38. public boolean replace(Node oldie, Node kiddo) {
  39. if(super.replace(oldie, kiddo)) return true;
  40. if(oldie == expression) {
  41. expression = (Expression) kiddo;
  42. return true;
  43. }
  44. return false;
  45. }
  46. @Override
  47. public Response resolve(NodeList<Node> stack, Resolver res, boolean fatal) {
  48. Type exprType = expression.getType();
  49. if(exprType == null) {
  50. int typeIndex = stack.find(TypeDecl.class);
  51. if(typeIndex != -1) {
  52. TypeDecl typeDecl = (TypeDecl) stack.get(typeIndex);
  53. VariableDecl var = typeDecl.getVariable(getName());
  54. if(var != null && var.isStatic()) {
  55. ref = var;
  56. if(expression instanceof VariableAccess) {
  57. VariableAccess varAcc = (VariableAccess) expression;
  58. if(((VariableAccess) expression).getName().equals("this")) {
  59. varAcc = new VariableAccess(typeDecl.getName(), expression.startToken);
  60. expression = varAcc;
  61. varAcc.resolve(stack, res, fatal);
  62. }
  63. }
  64. return Response.OK;
  65. }
  66. }
  67. if(expression instanceof VariableAccess && ((VariableAccess) expression).getRef() instanceof NamespaceDecl) {
  68. NamespaceDecl ns = ((NamespaceDecl) ((VariableAccess) expression).getRef());
  69. // push imported modules ...
  70. for(Import imp: ns.getImports()) {
  71. stack.push(imp.getModule());
  72. }
  73. // push the namespace as a "sentinel".
  74. stack.push(ns);
  75. VariableAccess varAcc = new VariableAccess(getName(), expression.startToken);
  76. varAcc.resolve(stack, res, fatal);
  77. ref = varAcc.getRef();
  78. stack.pop();
  79. for(int i = 0; i < ns.getImports().size(); i++) {
  80. stack.pop();
  81. }
  82. stack.peek().replace(this, varAcc);
  83. return Response.OK;
  84. }
  85. if(fatal) {
  86. throw new OocCompilationError(this, stack, "Couldn't resolve type of "+expression);
  87. }
  88. return Response.LOOP;
  89. }
  90. exprType = exprType.getFlatType(res);
  91. if(exprType.getRef() == null) exprType.resolve(res);
  92. if(!tryResolve(stack, exprType, res)) {
  93. tryResolve(stack, exprType.getFlatType(res), res);
  94. }
  95. if(ref != null && ref.getType() == null) {
  96. MustBeResolved must = ref;
  97. must.resolve(stack, res, fatal);
  98. }
  99. if(ref != null) {
  100. if(expression instanceof VariableAccess) {
  101. VariableAccess varAcc = (VariableAccess) expression;
  102. if(varAcc.getRef() instanceof TypeDecl && !(varAcc.getRef() instanceof TypeParam)) {
  103. if(ref instanceof VariableDecl) {
  104. VariableDecl varDecl = (VariableDecl) ref;
  105. if(!varDecl.isStatic() && !varDecl.getName().equals("class")) {
  106. throw new OocCompilationError(this, stack,
  107. "Trying to access member variable "+exprType
  108. +"."+getName()+" as if it were static. But it's not.");
  109. }
  110. }
  111. }
  112. }
  113. if(ref instanceof FunctionDecl && !expression.getType().getName().equals("Class")) {
  114. MemberAccess membAcc = new MemberAccess(expression, "class", startToken);
  115. this.expression = membAcc;
  116. stack.push(this);
  117. membAcc.resolve(stack, res, fatal);
  118. stack.pop(this);
  119. }
  120. }
  121. if(fatal && ref == null && !dead) {
  122. String message = "Can't resolve access to member "+exprType+"."+getName();
  123. String guess = guessCorrectName((TypeDecl) exprType.getRef());
  124. if(guess != null) {
  125. message += " Did you mean "+exprType+"."+guess+" ?";
  126. }
  127. throw new OocCompilationError(this, stack, message);
  128. }
  129. return (ref == null && !dead) ? Response.LOOP : Response.OK;
  130. }
  131. private String guessCorrectName(final TypeDecl typeDeclaration) {
  132. if(typeDeclaration == null) {
  133. return null;
  134. }
  135. int bestDistance = Integer.MAX_VALUE;
  136. String bestMatch = null;
  137. for(VariableDecl decl: typeDeclaration.getVariables()) {
  138. int distance = Levenshtein.distance(getName(), decl.getName());
  139. if(distance < bestDistance) {
  140. bestDistance = distance;
  141. bestMatch = decl.getName();
  142. }
  143. }
  144. if(bestDistance > 3) return null;
  145. return bestMatch;
  146. }
  147. private boolean tryResolve(NodeList<Node> stack, Type exprType, Resolver res) {
  148. if(dead) return true;
  149. if(exprType == null) return false;
  150. Declaration decl = exprType.getRef();
  151. if(decl == null) {
  152. return false;
  153. }
  154. if(!(decl instanceof TypeDecl)) {
  155. throw new OocCompilationError(this, stack,
  156. "Trying to access to a member of not a TypeDecl, but a "+decl);
  157. }
  158. TypeDecl typeDecl = (TypeDecl) decl;
  159. if(ref == null) {
  160. ref = typeDecl.getVariable(getName());
  161. }
  162. if(ref == null && getName().equals("size") && exprType.getPointerLevel() > 0) {
  163. FunctionCall sizeofArray = new FunctionCall("sizeof", startToken);
  164. sizeofArray.getArguments().add(expression);
  165. FunctionCall sizeofType = new FunctionCall("sizeof", startToken);
  166. sizeofType.getArguments().add(new TypeAccess(expression.getType().dereference(), expression.startToken));
  167. Div div = new Div(sizeofArray, sizeofType, startToken);
  168. stack.peek().replace(this, new Parenthesis(div, startToken));
  169. dead = true;
  170. return false;
  171. }
  172. if(ref == null && exprType.getRef() instanceof CoverDecl && getName().equals("class")) {
  173. MemberCall membCall = new MemberCall(expression, "class", "", startToken);
  174. if(!stack.peek().replace(this, membCall)) {
  175. throw new OocCompilationError(this, stack, "Couldn't replace class access with member call");
  176. }
  177. membCall.resolve(stack, res, true);
  178. dead = true;
  179. return false;
  180. }
  181. if(expression instanceof VariableAccess
  182. && ((VariableAccess) expression).getRef() instanceof TypeDecl
  183. && (getName().equals("size") || getName().equals("instanceSize")
  184. || getName().equals("super") || getName().equals("name"))) {
  185. if(!exprType.getName().equals("Class")) {
  186. MemberAccess membAcc = new MemberAccess(expression, "class", startToken);
  187. this.expression = membAcc;
  188. stack.push(this);
  189. membAcc.resolve(stack, res, true);
  190. stack.pop(this);
  191. tryResolve(stack, expression.getType(), res);
  192. return true;
  193. }
  194. }
  195. if(ref == null) {
  196. ref = typeDecl.getFunction(getName(), "", null);
  197. if(ref != null) {
  198. FunctionDecl fDecl = (FunctionDecl) ref;
  199. if(fDecl.getTypeDecl() instanceof ClassDecl) {
  200. ClassDecl baseClass = ((ClassDecl) fDecl.getTypeDecl()).getBaseClass(fDecl);
  201. ref = baseClass.getFunction(fDecl.getName(), fDecl.getSuffix(), null);
  202. }
  203. }
  204. }
  205. return ref != null;
  206. }
  207. @Override
  208. public String toString() {
  209. if(expression instanceof VariableAccess) {
  210. VariableAccess varAcc = (VariableAccess) expression;
  211. //return getClass().getSimpleName()+" "+varAcc.getName()+":"+varAcc.getType()+"->"+getName()+":"+getType();
  212. return varAcc.getName()+"->"+getName();
  213. }
  214. //return "MemberAccess|"+expression+"->"+getName()+":"+getType();
  215. return expression+"->"+getName();
  216. }
  217. }