/src/org/ooc/middle/hobgoblins/Checker.java

http://github.com/nddrylliog/ooc · Java · 199 lines · 138 code · 29 blank · 32 comment · 52 complexity · f7a0139f732cee3fae2de9e0b77e6e29 MD5 · raw file

  1. package org.ooc.middle.hobgoblins;
  2. import java.io.IOException;
  3. import java.util.HashMap;
  4. import org.ooc.frontend.BuildParams;
  5. import org.ooc.frontend.model.ClassDecl;
  6. import org.ooc.frontend.model.CoverDecl;
  7. import org.ooc.frontend.model.FunctionCall;
  8. import org.ooc.frontend.model.FunctionDecl;
  9. import org.ooc.frontend.model.Module;
  10. import org.ooc.frontend.model.Node;
  11. import org.ooc.frontend.model.NodeList;
  12. import org.ooc.frontend.model.Type;
  13. import org.ooc.frontend.model.TypeDecl;
  14. import org.ooc.frontend.model.ValuedReturn;
  15. import org.ooc.frontend.model.VariableAccess;
  16. import org.ooc.frontend.model.VariableDecl;
  17. import org.ooc.middle.Hobgoblin;
  18. import org.ooc.middle.OocCompilationError;
  19. import org.ooc.middle.walkers.Opportunist;
  20. import org.ooc.middle.walkers.SketchyNosy;
  21. /**
  22. * The Checker makes sure everything has been resolved properly. It also makes
  23. * sure type names are CamelCase and func/vars camelCase
  24. *
  25. * @author Amos Wenger
  26. */
  27. public class Checker implements Hobgoblin {
  28. final HashMap<String, FunctionDecl> funcNames = new HashMap<String, FunctionDecl>();
  29. final HashMap<TypeDecl, HashMap<String, FunctionDecl>> classFuncNames = new HashMap<TypeDecl, HashMap<String, FunctionDecl>>();
  30. public boolean process(Module module, BuildParams params) throws IOException {
  31. SketchyNosy.get(new Opportunist<Node>() {
  32. public boolean take(Node node, NodeList<Node> stack) throws IOException {
  33. if(node instanceof FunctionCall) checkFunctionCall((FunctionCall) node, stack);
  34. else if(node instanceof VariableAccess) checkVariableAccess((VariableAccess) node, stack);
  35. else if(node instanceof FunctionDecl) checkFunctionDecl((FunctionDecl) node, stack);
  36. else if(node instanceof VariableDecl) checkVariableDecl((VariableDecl) node, stack);
  37. else if(node instanceof TypeDecl) checkTypeDecl((TypeDecl) node, stack);
  38. else if(node instanceof ValuedReturn) checkValuedReturn((ValuedReturn) node, stack);
  39. return true;
  40. }
  41. private void checkFunctionCall(FunctionCall node, NodeList<Node> stack) {
  42. if(node.getImpl() == null) {
  43. throw new OocCompilationError(node, stack,
  44. node.getClass().getSimpleName()+" to "+node.getName()
  45. +" hasn't been resolved :/");
  46. }
  47. }
  48. private void checkVariableAccess(VariableAccess node, NodeList<Node> stack) {
  49. if(node.getRef() == null) {
  50. throw new OocCompilationError(node, stack,
  51. node.getClass().getSimpleName()+" to "+node.getName()
  52. +" hasn't been resolved :S stack = "+stack.toString(true));
  53. }
  54. }
  55. private void checkFunctionDecl(FunctionDecl node, NodeList<Node> stack) {
  56. if(node.getName().length() > 0) {
  57. if(Character.isUpperCase(node.getName().charAt(0)) && !node.isExtern()) {
  58. // turned it into a warning
  59. new OocCompilationError(node, stack,
  60. "Upper-case function name '"+node.getProtoRepr()
  61. +"'. Function should always begin with a lowercase letter, e.g. camelCase").printStackTrace();
  62. }
  63. }
  64. if(!node.isFromPointer()) {
  65. String name = node.getName();
  66. if(node.getTypeDecl() != null) {
  67. name = node.getTypeDecl().toString() + "." + name;
  68. }
  69. String suffixedName = node.getName()+"_"+node.getSuffix();
  70. FunctionDecl other;
  71. if(node.isMember()) {
  72. HashMap<String, FunctionDecl> set = classFuncNames.get(node.getTypeDecl());
  73. if(set == null) {
  74. set = new HashMap<String, FunctionDecl>();
  75. classFuncNames.put(node.getTypeDecl(), set);
  76. }
  77. other = set.put(suffixedName, node);
  78. } else {
  79. other = funcNames.put(suffixedName, node);
  80. }
  81. if(other != null) {
  82. // if either are unversioned, it's an immediate lose
  83. if(node.getVersion() == null || other.getVersion() == null) {
  84. throwError(node, other, stack, name);
  85. }
  86. // if their version is the same, it's a lose too.
  87. if(node.getVersion().equals(other.getVersion())) {
  88. throwError(node, other, stack, name);
  89. }
  90. }
  91. }
  92. }
  93. void throwError(FunctionDecl node, FunctionDecl other, NodeList<Node> stack, String name)
  94. throws OocCompilationError {
  95. if(name.equals("class") && stack.find(CoverDecl.class) != -1) return;
  96. // FIXME debug
  97. new OocCompilationError(node, stack,
  98. "Two functions have the same name '"+name
  99. +"', add suffix to one of them! (even if they have different signatures). e.g. "
  100. +name+": func ~suffix "+node.getArgsRepr()+" -> "+node.getReturnType()).printStackTrace();
  101. throw new OocCompilationError(other, stack, "The other definition is here:");
  102. }
  103. private void checkVariableDecl(VariableDecl node, NodeList<Node> stack) {
  104. Type varDeclType = node.getType();
  105. if(varDeclType != null && varDeclType.getRef() != null && !varDeclType.getRef().isExtern()
  106. && varDeclType.getName().length() > 0
  107. && !(varDeclType.isGeneric())
  108. && Character.isLowerCase(varDeclType.getName().charAt(0))) {
  109. throw new OocCompilationError(varDeclType, stack,
  110. "Variable declaration has type '"+varDeclType.getName()+
  111. "', which begins with a lowercase letter."+
  112. " Types should always begin with an uppercase letter, e.g. CamelCase");
  113. }
  114. /*
  115. for(VariableDeclAtom atom: node.getAtoms()) {
  116. if(atom.getName().length() == 0) continue;
  117. if(Character.isUpperCase(atom.getName().charAt(0)) && !node.getType().isConst()
  118. && node.shouldBeLowerCase()) {
  119. throw new OocCompilationError(atom, stack,
  120. "Upper-case variable name '"+atom.getName()+": "+node.getType()
  121. +"'. Variables should always begin with a lowercase letter, e.g. camelCase");
  122. }
  123. }
  124. */
  125. }
  126. private void checkTypeDecl(TypeDecl node, NodeList<Node> stack)
  127. throws OocCompilationError {
  128. if(node.isExtern() || node.getName().length() == 0) return;
  129. if(Character.isLowerCase(node.getName().charAt(0))) {
  130. throw new OocCompilationError(node, stack,
  131. "Lower-case type name '"+node.getName()
  132. +"'. Types should always begin with a capital letter, e.g. CamelCase (stack = "+stack);
  133. }
  134. if(!(node instanceof ClassDecl)) return;
  135. ClassDecl classDecl = (ClassDecl) node;
  136. if(classDecl.isAbstract()) return;
  137. NodeList<FunctionDecl> functions = new NodeList<FunctionDecl>();
  138. classDecl.getFunctionsRecursive(functions);
  139. for(FunctionDecl decl: functions) {
  140. FunctionDecl realDecl = classDecl.getFunction(decl.getName(), decl.getSuffix(), null);
  141. if(realDecl.isAbstract()) {
  142. throw new OocCompilationError(classDecl, stack, "Class "+classDecl.getName()
  143. +" must implement "+decl.getProtoRepr()+", or be declared abstract. Little help: "+decl.getStub());
  144. }
  145. //ClassDecl baseClass = classDecl.getBaseClass(decl);
  146. //FunctionDecl baseDecl = baseClass.getFunction(realDecl.getName(), realDecl.getSuffix(), null);
  147. // TODO check arg types and return type also
  148. /*
  149. if(baseDecl != null && realDecl.getArguments().size() != baseDecl.getArguments().size()) {
  150. throw new OocCompilationError(decl, stack, "Class "+classDecl.getName()
  151. +" must implement "+decl.getProtoRepr()+" with the same arguments & return type as "
  152. +baseDecl.getArguments()+". realArgs = "+realDecl.getArguments()
  153. +", baseArgs = "+baseDecl.getArguments());
  154. }
  155. */
  156. }
  157. }
  158. private void checkValuedReturn(ValuedReturn node,
  159. NodeList<Node> stack) {
  160. FunctionDecl decl = (FunctionDecl) stack.get(stack.find(FunctionDecl.class));
  161. if(decl.getReturnType().isVoid()) {
  162. throw new OocCompilationError(node, stack,
  163. "Returning a value in function "+decl.getProtoRepr()
  164. +" which is declared as returning nothing");
  165. }
  166. }
  167. }).visit(module);
  168. return false;
  169. }
  170. }