package LBJ2; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.PrintStream; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.ListIterator; import java.util.zip.GZIPOutputStream; import LBJ2.IR.*; import LBJ2.classify.Classifier; import LBJ2.infer.EqualityArgumentReplacer; import LBJ2.infer.FirstOrderEquality; import LBJ2.io.HexOutputStream; /** * This pass generates Java code from an AST, but does not perform any * training. * * @author Nick Rizzolo **/ public class TranslateToJava extends Pass { /** The commented message appearing at the top of all generated files. */ public static final String disclaimer = "// Modifying this comment will cause the next execution of LBJ2 to " + "overwrite this file."; /** * This array contains string descriptions of methods that don't need to be * overridden when generating code for a learner. **/ private static final String[] noOverride = { "native public int hashCode()", "public LBJ2.classify.Feature featureValue(java.lang.Object a0)", "public LBJ2.classify.FeatureVector classify(java.lang.Object a0)", "public LBJ2.classify.FeatureVector[] classify(java.lang.Object[] a0)", "public boolean equals(java.lang.Object a0)", "public double realValue(java.lang.Object a0)", "public double[] realValueArray(java.lang.Object a0)", "public java.lang.Object clone()", "public java.lang.String discreteValue(java.lang.Object a0)", "public java.lang.String getInputType()", "public java.lang.String getOutputType()", "public java.lang.String toString()", "public java.lang.String[] allowableValues()", "public java.lang.String[] discreteValueArray(java.lang.Object a0)", "public java.util.LinkedList getCompositeChildren()", "public short valueIndexOf(java.lang.String a0)", "public void learn(java.lang.Object a0)", "public void learn(java.lang.Object[] a0)", "public void unclone()" }; /** * The prefix of the name of the temporary variable in which a constraint's * computed value should be stored. This variable is only used when * {@link #constraintMode} is unset. **/ private static final String constraintResult = "LBJ2$constraint$result$"; /** Used for collecting the string representation of a method body. */ private StringBuffer methodBody; /** The indent level when collecting the method body. */ private int indent; /** Lets AST children know about the node they are contained in. */ private CodeGenerator currentCG; /** * Lets {@link VariableDeclaration}s know if they are contained in the * initialization portion of the header of a <code>for</code> loop. **/ private boolean forInit; /** * Filenames that have been generated during the processing of one * statement. **/ private HashSet files; /** * When this flag is set, code generated for constraint expressions will * create {@link LBJ2.infer.Constraint} objects rather than computing the * value of the constraint expression. **/ private boolean constraintMode; /** * This variable is appended to the {@link #constraintResult} variable to * form the name of a new temporary variable. **/ private int constraintResultNumber; /** The current constraint result variable name. */ private String constraintResultName; /** * Lets AST children know the index that a given quantification variable * occupies in an {@link EqualityArgumentReplacer}'s vector; the keys of * the map are names of quantification variables, and the values are * <code>Integer</code>s. **/ private HashMap quantificationVariables; /** * Lets AST children know the index that a given context variable occupies * in an {@link EqualityArgumentReplacer}'s vector; the keys of the map are * names of context variables, and the values are <code>Integer</code>s. **/ private HashMap contextVariables; /** * Lets AST nodes know how deeply nested inside * {@link QuantifiedConstraintExpression}s they are. **/ private int quantifierNesting; /** * Associates an AST with this pass. * * @param ast The AST to associate with this pass. **/ public TranslateToJava(AST ast) { super(ast); methodBody = new StringBuffer(); files = new HashSet(); } /** * Uses the current value of {@link #indent} to append the appropriate * number of spaces to {@link #methodBody}. **/ private void appendIndent() { for (int i = 0; i < indent; ++i) methodBody.append(" "); } /** * Uses the current value of {@link #indent} to append the appropriate * number of spaces to {@link #methodBody}, followed by the argument * string. * * @param text The text to append after the indent. **/ private void appendIndent(String text) { appendIndent(); methodBody.append(text); } /** * Appends the current indent via {@link #appendIndent()}, then appends the * argument string and a newline. * * @param text The text to append as a new line. **/ private void appendLine(String text) { appendIndent(text); methodBody.append("\n"); } /** * Sets the current code generator for this translator. * * @param cg The new current code generator. **/ public void setCurrentCG(CodeGenerator cg) { currentCG = cg; } /** * Sets the indentation level. * * @param i The new indentation level. **/ public void setIndent(int i) { indent = i; } /** * Gives access to the {@link #methodBody} member variable so that this * pass can be invoked selectively on some subset of a method body. * * @return The contents of {@link #methodBody} in a <code>String</code>. **/ public String getMethodBody() { return methodBody.toString(); } /** * Create a <code>PrintStream</code> that writes to a Java file * corresponding to the specified {@link CodeGenerator}. * * @param node The code producing node. * @return The stream, or <code>null</code> if it couldn't be created. **/ public static PrintStream open(CodeGenerator node) { return open(node.getName() + ".java"); } /** * Create a <code>PrintStream</code> that writes to the specified file. * * @param name The name of the file to open. * @return The stream, or <code>null</code> if it couldn't be created. **/ public static PrintStream open(String name) { if (Main.generatedSourceDirectory != null) { name = Main.generatedSourceDirectory + File.separator + name; String[] directories = name.split("\\" + File.separator + "+"); File directory = new File(directories[0]); for (int i = 1; i < directories.length - 1; ++i) { directory = new File(directory + File.separator + directories[i]); if (!directory.exists() && !directory.mkdir()) { System.err.println("Can't create directory '" + directory + "'."); return null; } } } else if (Main.sourceDirectory != null) name = Main.sourceDirectory + File.separator + name; Main.fileNames.add(name); PrintStream out = null; try { out = new PrintStream(new FileOutputStream(name)); } catch (Exception e) { System.err.println("Can't open '" + name + "' for output: " + e); } return out; } /** * Generate the code that overrides certain methods of * {@link LBJ2.learn.Learner} to check types and call themselves on the * unique instance; also declares other methods and fields of the * classifier's implementation. The explicitly overridden methods are: * <ul> * <li> <code>getInputType()</code> </li> * <li> <code>getOutputType()</code> </li> * <li> <code>allowableValues()</code> </li> * <li> <code>learn(Object)</code> </li> * <li> <code>learn(Object[])</code> </li> * <li> <code>classify(Object)</code> </li> * <li> <code>classify(Object[])</code> </li> * </ul> * * In addition, any methods defined by any subclass of * {@link LBJ2.learn.Learner} down to the super class of this learner are * overridden to call the super class's implementation on the unique * instance. * * @param out The stream to write to. * @param lce The {@link LearningClassifierExpression} representing the * learner. **/ public static void generateLearnerBody(PrintStream out, LearningClassifierExpression lce) { String lceName = lce.name.toString(); String field = null; boolean cachedInMap = false; out.println(" public static boolean isTraining;"); out.println(" public static " + lceName + " instance;\n"); out.println(" public static " + lceName + " getInstance()"); out.println(" {"); out.println(" loadInstance();"); out.println(" return instance;"); out.println(" }\n"); if (lce.cacheIn != null) { field = lce.cacheIn.toString(); cachedInMap = field.equals(ClassifierAssignment.mapCache); if (cachedInMap) out.println(" private static final WeakHashMap __valueCache " + "= new WeakHashMap();\n"); } HashSet invoked = (HashSet) SemanticAnalysis.invokedGraph.get(lceName); if (invoked != null && invoked.size() > 0) { for (Iterator I = invoked.iterator(); I.hasNext(); ) { String name = (String) I.next(); String nameNoDots = name.replace('.', '$'); out.println(" private static final " + name + " __" + nameNoDots + " = new " + name + "();"); } out.println(); } if (lce.parameterSets.size() == 0) { out.println(" private " + lceName + "(boolean b)"); out.println(" {"); out.print(" super("); if (lce.learnerParameterBlock != null) out.print("new Parameters()"); else { if (lce.learnerConstructor.arguments.size() > 0) { out.print(lce.learnerConstructor.arguments); if (lce.attributeString.length() != 0) out.print(", "); } if (lce.attributeString.length() != 0) out.print("attributeString"); } out.println(");"); out.println(" containingPackage = \"" + AST.globalSymbolTable.getPackage() + "\";"); out.println(" name = \"" + lceName + "\";"); out.println(" setEncoding(" + lce.featureEncoding + ");"); if (lce.labeler != null) out.println(" setLabeler(new " + lce.labeler.name + "());"); out.println(" setExtractor(new " + lce.extractor.name + "());"); out.println(" isClone = false;"); out.println(" }\n"); } out.println(" public static TestingMetric getTestingMetric() { return " + lce.testingMetric + "; }\n"); if (lce.singleExampleCache) { out.println( " private static ThreadLocal __cache = new ThreadLocal(){ };"); out.println(" private static ThreadLocal __exampleCache = " + "new ThreadLocal(){ };"); out.println(" public static void clearCache() { __exampleCache = new " + "ThreadLocal() { }; }"); } if (lce.attributeString.length() != 0 && lce.learnerParameterBlock == null) out.println(" private static final String attributeString = \"" + lce.attributeString + "\";"); out.println("\n private boolean isClone;\n"); out.println(" public void unclone() { isClone = false; }\n"); out.println(" public " + lceName + "()"); out.println(" {"); String fqName = AST.globalSymbolTable.getPackage(); if (fqName.length() > 0) fqName += "."; fqName += lceName; out.println(" super(\"" + fqName + "\");"); out.println(" isClone = true;"); out.println(" }\n"); out.println(" public " + lceName + "(String modelPath, String lexiconPath) { " + "this(new Parameters(), modelPath, lexiconPath); }"); out.println(" public " + lceName + "(Parameters p, String modelPath, String lexiconPath)"); out.println(" {"); out.println(" super(p);"); out.println(" try {"); out.println(" lcFilePath = new java.net.URL(\"file:\" + " + "modelPath);"); out.println(" lexFilePath = new java.net.URL(\"file:\" + " + "lexiconPath);"); out.println(" }"); out.println(" catch (Exception e) {"); out.println(" System.err.println(\"ERROR: Can't create model or " + "lexicon URL: \" + e);"); out.println(" e.printStackTrace();"); out.println(" System.exit(1);"); out.println(" }\n"); out.println(" if (new java.io.File(modelPath).exists()) {"); out.println(" readModel(lcFilePath);"); out.println(" readLexiconOnDemand(lexFilePath);"); out.println(" }"); out.println(" else {"); out.println(" containingPackage = \"" + AST.globalSymbolTable.getPackage() + "\";"); out.println(" name = \"" + lceName + "\";"); if (lce.labeler != null) out.println(" setLabeler(new " + lce.labeler.name + "());"); out.println(" setExtractor(new " + lce.extractor.name + "());"); out.println(" }\n"); out.println(" isClone = false;"); out.println(" }\n"); LBJ2.IR.Type input = lce.argument.getType(); String inputString = input.toString(); int line = lce.line + 1; typeReturningMethods(out, input, lce.returnType); out.println("\n public void learn(Object example)"); out.println(" {"); out.println(" if (isClone)"); out.println(" {"); out.print( generateTypeChecking(" ", lceName, "Classifier", false, inputString, lce.line, "example", true)); out.println(" loadInstance();"); out.println(" instance.learn(example);"); out.println(" return;"); out.println(" }\n"); out.println(" if (example instanceof Object[])"); out.println(" {"); out.println(" Object[] a = (Object[]) example;"); out.println(" if (a[0] instanceof int[])"); out.println(" {"); out.println(" super.learn((int[]) a[0], (double[]) a[1], (int[]) " + "a[2], (double[]) a[3]);"); out.println(" return;"); out.println(" }"); out.println(" }\n"); out.println(" super.learn(example);"); out.println(" }\n"); out.println(" public void learn(Object[] examples)"); out.println(" {"); out.println(" if (isClone)"); out.println(" {"); out.print( generateTypeChecking(" ", lceName, "Classifier", true, inputString, lce.line, "examples", true)); out.println(" loadInstance();"); out.println(" instance.learn(examples);"); out.println(" return;"); out.println(" }\n"); out.println(" super.learn(examples);"); out.println(" }\n"); StringBuffer preamble = new StringBuffer(); StringBuffer body = new StringBuffer(); StringBuffer post = null; preamble.append( " if (isClone)\n" + " {\n"); preamble.append( generateTypeChecking(" ", lceName, "Classifier", false, inputString, lce.line, "__example", true)); preamble.append( " loadInstance();\n" + " return instance.$METHOD$(__example);\n" + " }\n\n"); preamble.append( " if (__example instanceof Object[])\n" + " {\n" + " Object[] a = (Object[]) __example;\n" + " if (a[0] instanceof int[])\n" + " return super.$METHOD$((int[]) a[0], (double[]) a[1]);\n" + " }\n\n"); boolean primitive = lce.returnType.type == ClassifierReturnType.DISCRETE || lce.returnType.type == ClassifierReturnType.REAL; if (lce.evaluation == null) { body.append(" __result = super."); body.append(primitive ? "featureValue" : "classify"); body.append("(__example);\n"); } else { TranslateToJava translator = new TranslateToJava(null); translator.setRoot(lce.evaluation); translator.setCurrentCG(lce); translator.run(); body.append(" __result = "); body.append(translator.getMethodBody()); body.append(";\n"); } if (lce.checkDiscreteValues) { post = new StringBuffer(); String variable = "__result"; String indent = ""; if (!primitive) { post.append( " for (int __i = 0; __i < __result.featuresSize(); ++__i)\n" + " {\n" + " Feature __f = __result.getFeature(__i);\n"); variable = "__f"; indent = " "; } post.append(indent); post.append(" if ("); post.append(variable); post.append(".getValueIndex() == -1)\n"); post.append(indent); post.append(" {\n"); post.append(indent); post.append(" System.err.println(\"Classifier "); post.append(lceName); post.append(" defined on line "); post.append(line); post.append(" of "); post.append(Main.sourceFilename); post.append(" tried to produce a feature with value '\" + "); post.append(variable); post.append(".getStringValue() + \"' which is not allowable.\");\n"); post.append(indent); post.append(" System.exit(1);\n"); post.append(indent); post.append(" }\n"); if (!primitive) post.append(" }\n"); } generateClassificationMethods( out, lce, preamble.toString(), body.toString(), false, lce.evaluation != null, post == null ? null : post.toString()); out.println("\n public FeatureVector[] classify(Object[] examples)"); out.println(" {"); out.println(" if (isClone)"); out.println(" {"); out.print( generateTypeChecking(" ", lceName, "Classifier", true, inputString, lce.line, "examples", true)); out.println(" loadInstance();"); out.println(" return instance.classify(examples);"); out.println(" }\n"); out.println(" FeatureVector[] result = super.classify(examples);"); if (lce.checkDiscreteValues) { out.println(" for (int i = 0; i < result.length; ++i)"); out.println(" for (int j = 0; j < result[i].featuresSize(); ++j)"); out.println(" {"); out.println(" Feature f = result[i].getFeature(j);"); out.println(" if (f.getValueIndex() == -1)"); out.println(" {"); out.println(" System.err.println(\"Classifier " + lceName + " defined on line " + line + " of " + Main.sourceFilename + " tried to produce a feature with value '\" + " + "f.getStringValue() + \"' which is not allowable.\");"); out.println(" System.exit(1);"); out.println(" }"); out.println(" }\n"); } out.println(" return result;"); out.println(" }\n"); String pack = AST.globalSymbolTable.getPackage(); String fullName = pack + (pack.length() == 0 ? "" : ".") + lceName; out.println(" public static void main(String[] args)"); out.println(" {"); out.println(" String testParserName = null;"); out.println(" String testFile = null;"); out.println(" Parser testParser = getTestParser();\n"); out.println(" try"); out.println(" {"); out.println(" if (!args[0].equals(\"null\"))"); out.println(" testParserName = args[0];"); out.println(" if (args.length > 1) testFile = args[1];\n"); out.println(" if (testParserName == null && testParser == null)"); out.println(" {"); out.println(" System.err.println(\"The \\\"testFrom\\\" clause " + "was not used in the learning classifier expression " + "that\");"); out.println(" System.err.println(\"generated this classifier, so " + "a parser and input file must be specified.\\n\");"); out.println(" throw new Exception();"); out.println(" }"); out.println(" }"); out.println(" catch (Exception e)"); out.println(" {"); out.println(" System.err.println(\"usage: " + fullName + " \\\\\");"); out.println(" System.err.println(\" <parser> <input " + "file> [<null label> [<null label> ...]]\\n\");"); out.println(" System.err.println(\" * <parser> must be the " + "fully qualified class name of a Parser, or " + "\\\"null\\\"\");"); out.println(" System.err.println(\" to use the default as " + "specified by the \\\"testFrom\\\" clause.\");"); out.println(" System.err.println(\" * <input file> is the " + "relative or absolute path of a file, or \\\"null\\\" " + "to\");"); out.println(" System.err.println(\" use the parser arguments " + "specified by the \\\"testFrom\\\" clause. <input\");"); out.println(" System.err.println(\" file> can also be " + "non-\\\"null\\\" when <parser> is \\\"null\\\" (when the " + "parser\");"); out.println(" System.err.println(\" specified by the " + "\\\"testFrom\\\" clause has a single string argument\");"); out.println(" System.err.println(\" constructor) to use an " + "alternate file.\");"); out.println(" System.err.println(\" * A <null label> is a label " + "(or prediction) that should not count towards\");"); out.println(" System.err.println(\" overall precision and " + "recall assessments.\");"); out.println(" System.exit(1);"); out.println(" }\n"); out.println(" if (testParserName == null && testFile != null && " + "!testFile.equals(\"null\"))"); out.println(" testParserName = testParser.getClass().getName();"); out.println(" if (testParserName != null)"); out.println(" testParser = " + "LBJ2.util.ClassUtils.getParser(testParserName, new " + "Class[]{ String.class }, new String[]{ testFile });"); out.println(" " + lceName + " classifier = new " + lceName + "();"); out.println(" TestDiscrete tester = new TestDiscrete();"); out.println(" for (int i = 2; i < args.length; ++i)"); out.println(" tester.addNull(args[i]);"); out.println(" TestDiscrete.testDiscrete(tester, classifier, " + "classifier.getLabeler(), testParser, true, 0);"); out.println(" }\n"); generateHashingMethods(out, lceName); Class lceClass = AST.globalSymbolTable.classForName(lce.learnerName.toString()); if (lceClass == null) { reportError(lce.line, "Could not locate class for learner '" + lce.learnerName + "'."); return; } Method[] methods = lceClass.getMethods(); for (int i = 0; i < methods.length; ++i) { int modifiers = methods[i].getModifiers(); if (Modifier.isFinal(modifiers) || Modifier.isPrivate(modifiers) || Modifier.isProtected(modifiers) || Modifier.isStatic(modifiers)) continue; Class returned = methods[i].getReturnType(); String name = methods[i].getName(); Class[] parameters = methods[i].getParameterTypes(); String sig = signature(methods[i], modifiers, returned, name, parameters); if (Arrays.binarySearch(noOverride, sig) >= 0) continue; out.println("\n " + sig); out.println(" {"); out.println(" if (isClone)"); out.println(" {"); out.println(" loadInstance();"); out.print(" "); if (!returned.equals(void.class)) out.print("return "); out.print("instance." + name + "("); if (parameters.length > 0) { out.print("a0"); for (int j = 1; j < parameters.length; ++j) out.print(", a" + j); } out.println(");"); if (returned.equals(void.class)) out.println(" return;"); out.println(" }\n"); out.print(" "); if (!returned.equals(void.class)) out.print("return "); out.print("super." + name + "("); if (parameters.length > 0) { out.print("a0"); for (int j = 1; j < parameters.length; ++j) out.print(", a" + j); } out.println(");"); out.println(" }"); } if (lce.parameterSets.size() == 0) { out.println(); out.println(" public static class Parameters extends " + lce.learnerName + ".Parameters"); out.println(" {"); if (lce.learnerParameterBlock != null) { TranslateToJava translator = new TranslateToJava(null); translator.setRoot(lce.learnerParameterBlock); translator.setCurrentCG(lce); translator.setIndent(3); translator.run(); out.println(" public Parameters()"); out.println(translator.getMethodBody()); } else out.println(" public Parameters() { super((" + lce.learnerName + ".Parameters) new " + lceName + "(false).getParameters()); }"); out.println(" }"); } } /** * This method generates a string signature of the given method. The * arguments other than <code>m</code> are supplied as arguments for * efficiency reasons, since this method is only called by one other * method. * * @see #generateLearnerBody(PrintStream,LearningClassifierExpression) * @param m The method object. * @param modifiers The integer representation of the method's modifiers. * @param returned The return type of the method. * @param name The name of the method. * @param parameters The parameter types of the method. * @return A string description of the method suitable for comparison with * the elements of the {@link #noOverride} array. **/ public static String signature(Method m, int modifiers, Class returned, String name, Class[] parameters) { Class[] thrown = m.getExceptionTypes(); String result = ""; if (Modifier.isAbstract(modifiers)) result += "abstract "; if (Modifier.isFinal(modifiers)) result += "final "; if (Modifier.isNative(modifiers)) result += "native "; if (Modifier.isPrivate(modifiers)) result += "private "; if (Modifier.isProtected(modifiers)) result += "protected "; if (Modifier.isPublic(modifiers)) result += "public "; if (Modifier.isStatic(modifiers)) result += "static "; if (Modifier.isStrict(modifiers)) result += "strictfp "; if (Modifier.isSynchronized(modifiers)) result += "synchronized "; result += makeTypeReadable(returned.getName()) + " " + name + "("; if (parameters.length > 0) { result += makeTypeReadable(parameters[0].getName()) + " a0"; for (int j = 1; j < parameters.length; ++j) result += ", " + makeTypeReadable(parameters[j].getName()) + " a" + j; } result += ")"; if (thrown.length > 0) { result += " throws " + thrown[0].getName(); for (int j = 1; j < thrown.length; ++j) result += ", " + thrown[j].getName(); } return result; } /** * The value returned by the <code>Class.getName()</code> method is not * recognizable as a type by <code>javac</code> if the given class is an * array; this method produces a representation that is recognizable by * <code>javac</code>. This method also replaces '$' characters with '.' * characters, under the assumption that '$' only appears in the name of an * inner class. * * @param name The name of a class as produced by * <code>Class.getName()</code>. * @return A string representation of the class recognizable by * <code>javac</code>. **/ public static String makeTypeReadable(String name) { for (int i = name.indexOf('$'); i != -1; i = name.indexOf('$', i + 1)) name = name.substring(0, i) + '.' + name.substring(i + 1); if (name.charAt(0) != '[') return name; while (name.charAt(0) == '[') name = name.substring(1) + "[]"; switch (name.charAt(0)) { case 'B': return "boolean" + name.substring(1); case 'C': return "char" + name.substring(1); case 'D': return "double" + name.substring(1); case 'F': return "float" + name.substring(1); case 'I': return "int" + name.substring(1); case 'J': return "long" + name.substring(1); case 'L': int colon = name.indexOf(';'); return name.substring(1, colon) + name.substring(colon + 1); case 'S': return "short" + name.substring(1); case 'Z': return "boolean" + name.substring(1); } assert false : "Unrecognized type string: " + name; return null; } /** * Generate code that overrides the methods of {@link Classifier} that * return type information. The methods overridden are: * <ul> * <li> <code>getInputType()</code> </li> * <li> <code>getOutputType()</code> </li> * <li> <code>allowableValues()</code> </li> * </ul> * * @param out The stream to write to. * @param input The input type of the classifier whose code this is. * @param output The return type of the classifier whose code this is. **/ public static void typeReturningMethods(PrintStream out, LBJ2.IR.Type input, ClassifierReturnType output) { out.println(" public String getInputType() { return \"" + input.typeClass().getName() + "\"; }"); out.println(" public String getOutputType() { return \"" + output.getTypeName() + "\"; }"); if (output.values.size() > 0) { ConstantList values = output.values; out.print("\n private static String[] __allowableValues = "); boolean isBoolean = false; if (output.values.size() == 2) { ASTNodeIterator I = values.iterator(); String v1 = I.next().toString(); String v2 = I.next().toString(); if ((v1.equals("false") || v1.equals("\"false\"")) && (v2.equals("true") || v2.equals("\"true\""))) { isBoolean = true; out.println("DiscreteFeature.BooleanValues;"); } } if (!isBoolean) { ASTNodeIterator I = values.iterator(); String v = I.next().toString(); if (v.charAt(0) != '"') v = "\"" + v + "\""; out.print("new String[]{ " + v); while (I.hasNext()) { v = I.next().toString(); if (v.charAt(0) != '"') v = "\"" + v + "\""; out.print(", " + v); } out.println(" };"); } out.println(" public static String[] getAllowableValues() { return " + "__allowableValues; }"); out.println(" public String[] allowableValues() { return " + "__allowableValues; }"); } } /** * Generates code that overrides the {@link Classifier#classify(Object[])} * method so that it checks the types of its arguments. * * @param out The stream to write to. * @param name The name of the classifier whose code this is. * @param input The input type of the classifier whose code this is. * @param line The line number on which this classifier is defined. **/ public static void typeCheckClassifyArray(PrintStream out, String name, LBJ2.IR.Type input, int line) { out.println(" public FeatureVector[] classify(Object[] examples)"); out.println(" {"); out.print( generateTypeChecking(" ", name, "Classifier", true, input.toString(), line, "examples", false)); out.println(" return super.classify(examples);"); out.println(" }"); } /** * Generates the <code>equals(Object)</code> method, which evaluates to * <code>true</code> whenever the two objects are of the same type. This * method should not be called when generating code for a * {@link InferenceDeclaration}. * * @param out The stream to write to. * @param name The name of the node whose <code>equals(Object)</code> * method is being generated. **/ public static void generateHashingMethods(PrintStream out, String name) { out.println(" public int hashCode() { return \"" + name + "\".hashCode(); }"); out.println(" public boolean equals(Object o) { return o instanceof " + name + "; }"); } /** * Generates the code appearing at the beginning of, for example, many * classifiers' {@link Classifier#classify(Object)} methods that checks to * see if that input <code>Object</code> has the appropriate type. * * @param indent The whitespace indentation in the generated code. * @param name The name of the {@link CodeGenerator} whose input is * being checked. * @param type The type of {@link CodeGenerator} whose input is * being checked (capitalized). * @param array Whether or not the method being type checked takes * an array of the input type. * @param input The correct input type of the {@link CodeGenerator}. * @param line The line number on which the {@link CodeGenerator} * appears. * @param exampleName The name of the example variable. * @param preExtracted Whether or not the generated code should allow * object arrays containing indexed features, as a * learner can take as input. * @return The generated code. **/ public static StringBuffer generateTypeChecking( String indent, String name, String type, boolean array, String input, int line, String exampleName, boolean preExtracted) { StringBuffer result = new StringBuffer(); result.append(indent); result.append("if (!("); result.append(exampleName); result.append(" instanceof "); result.append(input); if (array) result.append("[]"); if (preExtracted) { result.append(" || "); result.append(exampleName); result.append(" instanceof Object[]"); if (array) result.append("[]"); } result.append("))\n"); result.append(indent); result.append("{\n"); result.append(indent); result.append(" String type = "); result.append(exampleName); result.append(" == null ? \"null\" : "); result.append(exampleName); result.append(".getClass().getName();\n"); result.append(indent); result.append(" System.err.println(\""); result.append(type); result.append(" '"); result.append(name); result.append("("); result.append(input); result.append(")' defined on line "); result.append(line + 1); result.append(" of "); result.append(Main.sourceFilename); result.append(" received '\" + type + \"' as input.\");\n"); result.append(indent); result.append(" new Exception().printStackTrace();\n"); result.append(indent); result.append(" System.exit(1);\n"); result.append(indent); result.append("}\n\n"); return result; } /** * Generates code that instantiates a primitive feature. * * @param discrete Whether or not the feature is discrete. * @param array Whether or not the feature comes from an array. * @param ref Code referring to an instance of the classifier from * which package and name information will be taken for * this feature. * @param id Code that evaluates to the value of the feature's * identifier. * @param value Code that evaluates to the feature's value if * <code>array</code> is <code>false</code>, or the array * containing the feature's value if <code>array</code> * is <code>true</code>. * @param index Code that evaluates to the array index of this feature. * This parameter is ignored if it is <code>null</code> * or <code>array</code> is <code>false</code>. * @param values Code that evaluates to the number of possible values * that the feature can take. If set to * <code>null</code>, * <code>"allowableValues().length"</code> is * substituted. This parameter is ignored if * <code>array</code> is <code>false</code>. * @param arrayInfo Code that evaluates to both the index and length * arguments in a feature constructor, separated by a * comma. If this parameter is <code>null</code>, it * defaults to <code>index</code> followed by * <code>value</code> with <code>".length"</code> * appended. **/ private static String primitiveFeatureConstructorInvocation( boolean discrete, boolean array, String ref, String id, String value, String index, String values, String arrayInfo) { StringBuffer buffer = new StringBuffer("new "); if (ref.length() > 0) ref += "."; buffer.append(discrete ? "Discrete" : "Real"); buffer.append(array ? "Array" : "Primitive"); buffer.append("StringFeature("); buffer.append(ref); buffer.append("containingPackage, "); buffer.append(ref); buffer.append("name, "); buffer.append(id); buffer.append(", "); buffer.append(value); if (array && index != null) { buffer.append('['); buffer.append(index); buffer.append(']'); } if (discrete) { buffer.append(", valueIndexOf("); buffer.append(value); if (array && index != null) { buffer.append('['); buffer.append(index); buffer.append(']'); } if (values == null) values = "allowableValues().length"; buffer.append("), (short) "); buffer.append(values); } if (array) { buffer.append(", "); if (arrayInfo != null) buffer.append(arrayInfo); else { buffer.append(index); buffer.append(", "); buffer.append(value); buffer.append(".length"); } } buffer.append(")"); return buffer.toString(); } /** * This method generates the methods that return the features and values * representing a classification. Implementations generated here take care * of all caching we may want to take place. The explicitly overridden * methods are a subset (depending on the classifier's type) of: * * <ul> * <li> <code>classify(Object)</code> </li> * <li> <code>featureValue(Object)</code> </li> * <li> <code>discreteValue(Object)</code> </li> * <li> <code>discreteValueArray(Object)</code> </li> * <li> <code>realValue(Object)</code> </li> * <li> <code>realValueArray(Object)</code> </li> * </ul> * * <p> If <code>bodyPrimitive</code> is <code>true</code>, * <code>body</code> should implement a method that the caller assumes * takes the same argument as the generated classifier and returns the * appropriate primitive type (<code>String</code> or <code>double</code>) * via a <code>return</code> statement. Otherwise, <code>body</code> * should implement a method whose argument is <code>Object * __example</code> and which stores the result of its computation in a * variable named <code>__result</code> which has already been declared to * have either type {@link LBJ2.classify.Feature} or * {@link LBJ2.classify.FeatureVector} as appropriate. * * <p> If <code>post</code> is non-<code>null</code>, the code therein will * have access to the values computed by <code>body</code>. If * <code>bodyPrimitive</code> is <code>true</code>, those values can be * accessed via a primitive variable named <code>__cachedValue</code>. * Otherwise, they must be accessed via the aforementioned * <code>__result</code> variable. * * @param out The stream to write to. * @param classifierExp The classifier for which code is being generated. * @param preamble This code will be executed before any call to any * of the generated methods. Any occurrences of the * string <code>"$METHOD$"</code> (without the * quotes) inside <code>preamble</code> will be * replaced with the name of the method inside which * code is currently being placed. If left * <code>null</code>, it defaults to the code * generated by * {@link #generateTypeChecking(String,String,String,boolean,String,int,String,boolean)}. * @param body Generated code that computes the classification * result (in a <code>FeatureVector</code>, * <code>Feature</code>, <code>String</code>, or * <code>double</code> as appropriate). * @param bodyPrimitive Set to <code>true</code> if the code in * <code>body</code> computes a single * <code>String</code> or <code>double</code>. * @param bodyArgCast Set to <code>true</code> if the code in * <code>body</code> assumes the classifier's * argument in the original source code will be in * scope. * @param post Generated code that performs any post-processing * that may be necessary on the computed * classification result before it is finally * returned. This parameter can be <code>null</code> * if post-processing is not necessary. **/ private static void generateClassificationMethods( PrintStream out, ClassifierExpression classifierExp, String preamble, String body, boolean bodyPrimitive, boolean bodyArgCast, String post) { String name = classifierExp.name.toString(); Argument arg = classifierExp.argument; String input = arg.getType().toString(); String field = classifierExp.cacheIn == null ? null : classifierExp.cacheIn.toString(); boolean cachedInMap = ClassifierAssignment.mapCache.equals(field); boolean anyCache = classifierExp.singleExampleCache || field != null; boolean discrete, array, generator; { ClassifierReturnType crt = classifierExp.returnType; discrete = crt.type == ClassifierReturnType.DISCRETE || crt.type == ClassifierReturnType.DISCRETE_ARRAY || crt.type == ClassifierReturnType.DISCRETE_GENERATOR; array = crt.type == ClassifierReturnType.DISCRETE_ARRAY || crt.type == ClassifierReturnType.REAL_ARRAY; generator = crt.type == ClassifierReturnType.DISCRETE_GENERATOR || crt.type == ClassifierReturnType.REAL_GENERATOR || crt.type == ClassifierReturnType.MIXED_GENERATOR; } String primitiveFeatureType = discrete ? "discrete" : "real"; String primitiveType = discrete ? "String" : "double"; String cachedValueType = primitiveType; if (array) cachedValueType += "[]"; String valueMethodName = primitiveFeatureType + "Value"; if (array) valueMethodName += "Array"; String cachedMethodReturnType = "Feature"; if (array || generator) cachedMethodReturnType += "Vector"; if (preamble == null) preamble = generateTypeChecking( " ", name, "Classifier", false, input, classifierExp.line, "__example", false) .toString(); if (anyCache) out.println(" private " + cachedMethodReturnType + " cachedFeatureValue(Object __example)"); else if (array || generator) out.println(" public FeatureVector classify(Object __example)"); else if (!bodyPrimitive) out.println(" public Feature featureValue(Object __example)"); if (anyCache || !bodyPrimitive) { out.println(" {"); if (classifierExp.singleExampleCache) { out.println(" if (__example == __exampleCache.get()) return (" + cachedMethodReturnType + ") __cache.get();"); out.println(" __exampleCache.set(__example);"); } if (field != null) { if (cachedInMap) { if (!array && !discrete) out.println(" Double __dValue = " + "(Double) __valueCache.get(__example);"); } else out.println(" " + arg + " = (" + input + ") __example;"); out.print(" " + cachedValueType + " __cachedValue = "); if (cachedInMap) { if (!array && !discrete) out.println("__dValue == null ? Double.NaN : " + "__dValue.doubleValue();"); else out.println("(" + cachedValueType + ") __valueCache.get(__example);"); } else out.println(field + ";"); out.print("\n if ("); if (!array && !discrete) out.print("Double.doubleToLongBits("); out.print("__cachedValue"); if (!array && !discrete) out.print(")"); out.print(" != "); if (!array && !discrete) out.print("Double.doubleToLongBits(Double.NaN)"); else out.print("null"); out.println(")"); out.println(" {"); out.print(" " + cachedMethodReturnType + " result = "); if (array) { out.println("new FeatureVector();"); out.println(" for (int i = 0; i < __cachedValue.length; ++i)"); out.print(" result.addFeature("); } out.print( primitiveFeatureConstructorInvocation( discrete, array, "", "\"\"", "__cachedValue", "i", null, null)); if (array) out.print(")"); out.println(";"); if (classifierExp.singleExampleCache) out.println(" __cache.set(result);"); out.println(" return result;"); out.println(" }\n"); } if (bodyPrimitive) { if (field != null) { out.print(" __cachedValue = "); if (!cachedInMap) out.print(field + " = "); out.println("_" + valueMethodName + "(__example);"); if (cachedInMap) { out.print(" __valueCache.put(__example, "); if (!discrete) out.print("new Double("); out.print("__cachedValue"); if (!discrete) out.print(")"); out.println(");"); } } else out.println(" " + primitiveType + " __cachedValue = _" + valueMethodName + "(__example);"); if (post != null) { out.println(); out.println(post); } out.println(" Feature __result = " + primitiveFeatureConstructorInvocation( discrete, false, "", "\"\"", "__cachedValue", null, null, null) + ";"); } else { if (!anyCache) out.print( preamble.replaceAll("\\$METHOD\\$", (array || generator ? "classify" : "featureValue"))); if (bodyArgCast && (field == null || cachedInMap)) out.println(" " + arg + " = (" + input + ") __example;\n"); out.println(" " + cachedMethodReturnType + " __result;"); out.print(body); if (field != null) { out.print(" __cachedValue = "); if (!cachedInMap) out.print(field + " = "); out.println("__result." + (array ? valueMethodName : (discrete ? "getStringValue" : "getStrength")) + "();"); if (cachedInMap) { out.print(" __valueCache.put(__example, "); if (!discrete && !array) out.print("new Double("); out.print("__cachedValue"); if (!discrete && !array) out.print(")"); out.println(");"); } } if (post != null) { out.println(); out.println(post); } } if (classifierExp.singleExampleCache) out.println(" __cache.set(__result);"); out.println(" return __result;"); out.println(" }"); } if (anyCache || !(array || generator)) { out.println("\n public FeatureVector classify(Object __example)"); out.println(" {"); if (anyCache) out.print(preamble.replaceAll("\\$METHOD\\$", "classify")); out.print(" return "); if (!(array || generator)) out.print("new FeatureVector("); out.print((anyCache ? "cachedFeatureValue" : "featureValue") + "(__example)"); if (!(array || generator)) out.print(")"); out.println(";"); out.println(" }"); } if (!generator) { if (!array && (anyCache || bodyPrimitive)) { out.println("\n public Feature featureValue(Object __example)"); out.println(" {"); if (anyCache) out.print(preamble.replaceAll("\\$METHOD\\$", "featureValue")); if (anyCache) out.println(" return cachedFeatureValue(__example);"); else { out.println(" " + cachedValueType + " result = " + valueMethodName + "(__example);"); out.println( " return " + primitiveFeatureConstructorInvocation( discrete, false, "", "\"\"", "result", null, null, null) + ";"); } out.println(" }"); } if (anyCache || array || !bodyPrimitive || post != null) { out.println("\n public " + cachedValueType + " " + valueMethodName + "(Object __example)"); out.println(" {"); if (anyCache) out.print(preamble.replaceAll("\\$METHOD\\$", valueMethodName)); if (array && field != null) { out.println(" cachedFeatureValue(__example);"); if (!cachedInMap) out.println(" " + arg + " = (" + input + ") __example;"); out.println(" return " + (cachedInMap ? "(" + cachedValueType + ") __valueCache.get(__example)" : field) + ";"); } else if (!anyCache && bodyPrimitive && post != null) { out.print(preamble.replaceAll("\\$METHOD\\$", valueMethodName)); out.println(" " + cachedValueType + " __cachedValue = _" + valueMethodName + "(__example);\n"); out.println(post); out.println(" return __cachedValue;"); } else out.println(" return " + (anyCache ? "cachedFeatureValue" : (array ? "classify" : "featureValue")) + "(__example)." + (array ? valueMethodName : (discrete ? "getStringValue" : "getStrength")) + "();"); out.println(" }"); } if (bodyPrimitive) { boolean helper = anyCache || post != null; out.println("\n " + (helper ? "private" : "public") + " " + cachedValueType + " " + (helper ? "_" : "") + valueMethodName + "(Object __example)"); out.println(" {"); if (!helper) out.print(preamble.replaceAll("\\$METHOD\\$", valueMethodName)); if (bodyArgCast) out.println(" " + arg + " = (" + input + ") __example;\n"); out.print(body); out.println(" }"); } } } /** * Compress the textual representation of an {@link ASTNode}, convert to * ASCII hexadecimal, and write the result to the specified stream. * * @param buffer The text representation to be written. * @param out The stream to write to. **/ public static void compressAndPrint(StringBuffer buffer, PrintStream out) { PrintStream converter = null; ByteArrayOutputStream converted = new ByteArrayOutputStream(); try { converter = new PrintStream(new GZIPOutputStream(new HexOutputStream(converted))); } catch (Exception e) { System.err.println("Could not create converter stream."); System.exit(1); } converter.print(buffer.toString()); converter.close(); try { converted.writeTo(out); } catch (Exception e) { System.err.println("Could not write the converted stream."); System.exit(1); } } /** * Runs this pass on all nodes of the indicated type. * * @param ast The node to process. **/ public void run(AST ast) { if (!RevisionAnalysis.noChanges) { quantificationVariables = new HashMap(); contextVariables = new HashMap(); runOnChildren(ast); } } /** * Code is only generated for a {@link ClassifierName} when it is the only * {@link ClassifierExpression} on the right hand side of the arrow (and * there really shouldn't be a reason that a programmer would want to write * such a declaration, but if he does, it will work). * * @param cn The node to process. **/ public void run(ClassifierName cn) { String cnName = cn.name.toString(); if (cn.name == cn.referent || !RevisionAnalysis.revisionStatus.get(cnName) .equals(RevisionAnalysis.REVISED)) return; System.out.println("Generating code for " + cnName); PrintStream out = open(cn); if (out == null) return; out.println(disclaimer); out.println("// " + cn.shallow() + "\n"); ast.symbolTable.generateHeader(out); String field = null; boolean cachedInMap = false; if (cn.cacheIn != null) { field = cn.cacheIn.toString(); cachedInMap = field.equals(ClassifierAssignment.mapCache); if (cachedInMap) out.println("import java.util.WeakHashMap;"); } out.println("\n"); if (cn.comment != null) out.println(cn.comment); out.println("public class " + cnName + " extends Classifier"); out.println("{"); if (cachedInMap) out.println(" private static final WeakHashMap __valueCache " + "= new WeakHashMap();"); String referentNoDots = cn.referent.toString().replace('.', '$'); out.println(" private static final " + cn.referent + " __" + referentNoDots + " = new " + cn.referent + "();\n"); if (cn.singleExampleCache) { out.println( " private static ThreadLocal __cache = new ThreadLocal(){ };"); out.println(" private static ThreadLocal __exampleCache = " + "new ThreadLocal(){ };"); out.println(" public static void clearCache() { __exampleCache = new " + "ThreadLocal(){ }; }\n"); } out.println(" public " + cnName + "()"); out.println(" {"); out.println(" containingPackage = \"" + AST.globalSymbolTable.getPackage() + "\";"); out.println(" name = \"" + cnName + "\";"); out.println(" }\n"); typeReturningMethods(out, cn.argument.getType(), cn.returnType); out.println(); boolean array = cn.returnType.type == ClassifierReturnType.DISCRETE_ARRAY || cn.returnType.type == ClassifierReturnType.REAL_ARRAY; boolean generator = cn.returnType.type == ClassifierReturnType.DISCRETE_GENERATOR || cn.returnType.type == ClassifierReturnType.REAL_GENERATOR || cn.returnType.type == ClassifierReturnType.MIXED_GENERATOR; StringBuffer body = new StringBuffer(); body.append(" __result = __"); body.append(referentNoDots); body.append("."); body.append(array || generator ? "classify" : "featureValue"); body.append("(__example);\n"); generateClassificationMethods(out, cn, null, body.toString(), false, false, null); out.println(); typeCheckClassifyArray(out, cnName, cn.argument.getType(), cn.line); out.println(); generateHashingMethods(out, cnName); out.println("}\n"); out.close(); } /** * Generates code for all nodes of the indicated type. * * @param cc The node to process. **/ public void run(CodedClassifier cc) { String ccName = cc.name.toString(); String fileName = ccName + ".java"; if (fileName.indexOf("$$") != -1) files.add(fileName); if (!RevisionAnalysis.revisionStatus.get(ccName) .equals(RevisionAnalysis.REVISED)) return; System.out.println("Generating code for " + ccName); PrintStream out = open(fileName); if (out == null) return; out.println(disclaimer); out.print("// "); compressAndPrint(cc.shallow(), out); out.println("\n"); ast.symbolTable.generateHeader(out); String field = null; boolean cachedInMap = false; if (cc.cacheIn != null) { field = cc.cacheIn.toString(); cachedInMap = field.equals(ClassifierAssignment.mapCache); if (cachedInMap) out.println("import java.util.WeakHashMap;"); } out.println("\n"); if (cc.comment != null) out.println(cc.comment); out.println("public class " + ccName + " extends Classifier"); out.println("{"); if (cachedInMap) out.println(" private static final WeakHashMap __valueCache " + "= new WeakHashMap();"); HashSet invoked = (HashSet) SemanticAnalysis.invokedGraph.get(ccName); if (invoked != null && invoked.size() > 0) { for (Iterator I = invoked.iterator(); I.hasNext(); ) { String name = (String) I.next(); String nameNoDots = name.replace('.', '$'); out.println(" private static final " + name + " __" + nameNoDots + " = new " + name + "();"); } out.println(); } if (cc.singleExampleCache) { out.println( " private static ThreadLocal __cache = new ThreadLocal(){ };"); out.println(" private static ThreadLocal __exampleCache = " + "new ThreadLocal(){ };"); out.println(" public static void clearCache() { __exampleCache = new " + "ThreadLocal(){ }; }\n"); } out.println(" public " + ccName + "()"); out.println(" {"); out.println(" containingPackage = \"" + AST.globalSymbolTable.getPackage() + "\";"); out.println(" name = \"" + ccName + "\";"); out.println(" }\n"); LBJ2.IR.Type input = cc.argument.getType(); typeReturningMethods(out, input, cc.returnType); out.println(); indent = 2; forInit = false; constraintMode = false; methodBody.delete(0, methodBody.length()); currentCG = cc; for (ASTNodeIterator I = cc.body.iterator(); I.hasNext(); ) { I.next().runPass(this); methodBody.append("\n"); } StringBuffer body = new StringBuffer(); StringBuffer post = null; boolean primitive = cc.returnType.type == ClassifierReturnType.DISCRETE || cc.returnType.type == ClassifierReturnType.REAL; if (primitive) { boolean discrete = cc.returnType.type == ClassifierReturnType.DISCRETE; body = methodBody; if (discrete && cc.returnType.values.size() > 0) { post = new StringBuffer(); post.append(" if (valueIndexOf(__cachedValue) == -1)\n" + " {\n" + " System.err.println(\"Classifier '"); post.append(ccName); post.append("' defined on line "); post.append(cc.line + 1); post.append(" of "); post.append(Main.sourceFilename); post.append(" produced '\" + __cachedValue + \"' as a feature " + "value, which is not allowable.\");\n" + " System.exit(1);\n" + " }\n"); } } else { body.append(" __result = new FeatureVector();\n"); boolean array = cc.returnType.type == ClassifierReturnType.DISCRETE_ARRAY || cc.returnType.type == ClassifierReturnType.REAL_ARRAY; if (array) body.append(" int __featureIndex = 0;\n"); if (cc.returnType.type == ClassifierReturnType.DISCRETE_GENERATOR || cc.returnType.type == ClassifierReturnType.REAL_GENERATOR) body.append(" String __id;\n"); if (cc.returnType.type == ClassifierReturnType.DISCRETE_ARRAY || cc.returnType.type == ClassifierReturnType.DISCRETE_GENERATOR || cc.returnType.type == ClassifierReturnType.REAL_ARRAY || cc.returnType.type == ClassifierReturnType.REAL_GENERATOR) { body.append(" "); body.append( cc.returnType.type == ClassifierReturnType.DISCRETE_ARRAY || cc.returnType.type == ClassifierReturnType.DISCRETE_GENERATOR ? "String" : "double"); body.append(" __value;\n"); } body.append("\n"); body.append(methodBody); if (array) { post = new StringBuffer(); post.append( " for (int __i = 0; __i < __result.featuresSize(); ++__i)\n" + " __result.getFeature(__i)" + ".setArrayLength(__featureIndex);\n"); } } generateClassificationMethods( out, cc, null, body.toString(), primitive, true, post == null ? null : post.toString()); out.println(); typeCheckClassifyArray(out, ccName, input, cc.line); out.println(); generateHashingMethods(out, ccName); out.println("}\n"); out.close(); } /** * Generates code for all nodes of the indicated type. * * @param cg The node to process. **/ public void run(CompositeGenerator cg) { String cgName = cg.name.toString(); String fileName = cgName + ".java"; if (fileName.indexOf("$$") != -1) { files.add(fileName); runOnChildren(cg); } else { files.clear(); runOnChildren(cg); final String prefix = cgName + "$$"; File[] leftOvers = new File(System.getProperty("user.dir")).listFiles( new FilenameFilter() { public boolean accept(File directory, String name) { int i = name.lastIndexOf('.'); if (i == -1) return false; String javaFile = name.substring(0, i) + ".java"; return name.startsWith(prefix) && !files.contains(javaFile); } }); for (int i = 0; i < leftOvers.length; ++i) if (leftOvers[i].exists() && !leftOvers[i].delete()) reportError(0, "Could not delete '" + leftOvers[i].getName() + "'."); } if (!RevisionAnalysis.revisionStatus.get(cgName) .equals(RevisionAnalysis.REVISED)) return; System.out.println("Generating code for " + cgName); PrintStream out = open(fileName); if (out == null) return; out.println(disclaimer); out.print("// "); compressAndPrint(cg.shallow(), out); out.println("\n"); ast.symbolTable.generateHeader(out); out.println("\n"); if (cg.comment != null) out.println(cg.comment); out.println("public class " + cgName + " extends Classifier"); out.println("{"); { HashSet declared = new HashSet(); for (ClassifierExpressionList.ClassifierExpressionListIterator I = cg.components.listIterator(); I.hasNext(); ) { String name = I.nextItem().name.toString(); if (declared.add(name)) { String nameNoDots = name.replace('.', '$'); out.println(" private static final " + name + " __" + nameNoDots + " = new " + name + "();"); } } } if (cg.singleExampleCache) { out.println( "\n private static ThreadLocal __cache = new ThreadLocal(){ };"); out.println(" private static ThreadLocal __exampleCache = " + "new ThreadLocal(){ };"); out.println(" public static void clearCache() { __exampleCache = new " + "ThreadLocal() { }; }"); } out.println("\n public " + cgName + "()"); out.println(" {"); out.println(" containingPackage = \"" + AST.globalSymbolTable.getPackage() + "\";"); out.println(" name = \"" + cgName + "\";"); out.println(" }\n"); LBJ2.IR.Type input = cg.argument.getType(); typeReturningMethods(out, input, cg.returnType); out.println(); StringBuffer body = new StringBuffer(); body.append(" __result = new FeatureVector();\n"); for (ClassifierExpressionList.ClassifierExpressionListIterator I = cg.components.listIterator(); I.hasNext(); ) { ClassifierExpression component = I.nextItem(); String nameNoDots = ("__" + component.name).replace('.', '$'); if (component.returnType.type == ClassifierReturnType.DISCRETE || component.returnType.type == ClassifierReturnType.REAL) { body.append(" __result.addFeature("); body.append(nameNoDots); body.append(".featureValue(__example));\n"); } else { body.append(" __result.addFeatures("); body.append(nameNoDots); body.append(".classify(__example));\n"); } } generateClassificationMethods(out, cg, null, body.toString(), false, false, null); out.println(); typeCheckClassifyArray(out, cgName, input, cg.line); out.println(); generateHashingMethods(out, cgName); out.println("\n public java.util.LinkedList getCompositeChildren()"); out.println(" {"); out.println(" java.util.LinkedList result = new " + "java.util.LinkedList();"); for (ClassifierExpressionList.ClassifierExpressionListIterator I = cg.components.listIterator(); I.hasNext(); ) { String nameNoDots = ("__" + I.nextItem().name).replace('.', '$'); out.println(" result.add(" + nameNoDots + ");"); } out.println(" return result;"); out.println(" }"); out.println("}\n"); out.close(); } /** * Generates code for all nodes of the indicated type. * * @param ii The node to process. **/ public void run(InferenceInvocation ii) { String iiName = ii.name.toString(); if (!RevisionAnalysis.revisionStatus.get(iiName) .equals(RevisionAnalysis.REVISED)) return; System.out.println("Generating code for " + iiName); PrintStream out = open(ii); if (out == null) return; out.println(disclaimer); out.println("// " + ii.shallow() + "\n"); ast.symbolTable.generateHeader(out); String field = null; boolean cachedInMap = false; if (ii.cacheIn != null) { field = ii.cacheIn.toString(); cachedInMap = field.equals(ClassifierAssignment.mapCache); if (cachedInMap) out.println("import java.util.WeakHashMap;"); } out.println("\n"); if (ii.comment != null) out.println(ii.comment); out.println("public class " + iiName + " extends Classifier"); out.println("{"); if (cachedInMap) out.println(" private static final WeakHashMap __valueCache " + "= new WeakHashMap();"); String iiClassifierName = ii.classifier.toString(); out.println(" private static final " + iiClassifierName + " __" + iiClassifierName + " = new " + iiClassifierName + "();\n"); if (ii.singleExampleCache) { out.println( " private static ThreadLocal __cache = new ThreadLocal(){ };"); out.println(" private static ThreadLocal __exampleCache = " + "new ThreadLocal(){ };"); out.println(" public static void clearCache() { __exampleCache = new " + "ThreadLocal(){ }; }\n"); } out.println(" public " + iiName + "()"); out.println(" {"); out.println(" containingPackage = \"" + AST.globalSymbolTable.getPackage() + "\";"); out.println(" name = \"" + iiName + "\";"); out.println(" }\n"); ClassifierType iiType = (ClassifierType) ii.classifier.typeCache; LBJ2.IR.Type input = iiType.getInput(); typeReturningMethods(out, input, iiType.getOutput()); out.println(); InferenceType inferenceType = (InferenceType) ii.inference.typeCache; String iiInference = ii.inference.toString(); String fqInferenceName = iiInference; if (ast.symbolTable.containsKey(ii.inference) && ast.symbolTable.getPackage().length() != 0) fqInferenceName = ast.symbolTable.getPackage() + "." + fqInferenceName; StringBuffer body = new StringBuffer(" "); body.append(inferenceType.getHeadType().toString()); body.append(" head = "); body.append(iiInference); body.append(".findHead(("); body.append(input.toString()); body.append(") __example);\n"); body.append(" "); body.append(iiInference); body.append(" inference = ("); body.append(iiInference); body.append(") InferenceManager.get(\""); body.append(fqInferenceName); body.append("\", head);\n\n"); body.append( " if (inference == null)\n" + " {\n" + " inference = new " + ii.inference + "(head);\n" + " InferenceManager.put(inference);\n" + " }\n\n" + " String result = null;\n\n" + " try { result = inference.valueOf(__"); body.append(iiClassifierName); body.append(", __example); }\n" + " catch (Exception e)\n" + " {\n" + " System.err.println(\"LBJ ERROR: Fatal error while " + "evaluating classifier "); body.append(iiName); body.append(": \" + e);\n" + " e.printStackTrace();\n" + " System.exit(1);\n" + " }\n\n" + " return result;\n"); generateClassificationMethods(out, ii, null, body.toString(), true, false, null); out.println(); typeCheckClassifyArray(out, iiName, input, ii.line); out.println(); generateHashingMethods(out, iiName); out.println("}\n"); out.close(); } /** * Generates code for all nodes of the indicated type. * * @param lce The node to process. **/ public void run(LearningClassifierExpression lce) { String lceName = lce.name.toString(); String fileName = lceName + ".java"; if (fileName.indexOf("$$") != -1) { files.add(fileName); runOnChildren(lce); } else { files.clear(); runOnChildren(lce); final String prefix = lceName + "$$"; File[] leftOvers = new File(System.getProperty("user.dir")).listFiles( new FilenameFilter() { public boolean accept(File directory, String name) { int i = name.lastIndexOf('.'); if (i == -1) return false; String javaFile = name.substring(0, i) + ".java"; return name.startsWith(prefix) && !files.contains(javaFile); } }); for (int i = 0; i < leftOvers.length; ++i) if (leftOvers[i].exists() && !leftOvers[i].delete()) reportError(0, "Could not delete '" + leftOvers[i].getName() + "'."); } if ((lce.parser == null ? !RevisionAnalysis.revisionStatus.get(lceName) .equals(RevisionAnalysis.REVISED) : RevisionAnalysis.revisionStatus.get(lceName) .equals(RevisionAnalysis.UNAFFECTED) || lce.startingRound > 1) || lce.onlyCodeGeneration) // In the last condition above involving lce.startingRound, note that // before setting lce.startingRound > 1, RevisionAnalysis also ensures // that the lce is unaffected other than the number of rounds and that // there will be no parameter tuning or cross validation. return; System.out.println("Generating code for " + lceName); PrintStream out = open(fileName); if (out == null) return; out.println(disclaimer); out.println("// \n"); ast.symbolTable.generateHeader(out); if (lce.cacheIn != null) { String field = lce.cacheIn.toString(); boolean cachedInMap = field.equals(ClassifierAssignment.mapCache); if (cachedInMap) out.println("import java.util.WeakHashMap;"); } out.println("\n"); if (lce.comment != null) out.println(lce.comment); out.println("public class " + lceName + " extends " + lce.learnerName); out.println("{"); out.println(" private static void loadInstance()"); out.println(" {"); out.println(" if (instance == null) instance = new " + lceName + "(true);"); out.println(" }\n"); String formalParameterString = ""; String firstArgumentsString = ""; String argumentString = ""; if (lce.parameterSets.size() > 0) { for (ListIterator I = lce.parameterSets.listIterator(); I.hasNext(); ) { ParameterSet e = (ParameterSet) I.next(); StringBuffer typeStringB = new StringBuffer(); e.type.write(typeStringB); formalParameterString += ", " + e.type + " " + e.getParameterName(); firstArgumentsString += ", " + e.getFirst(); argumentString += ", " + e.getParameterName(); } formalParameterString = formalParameterString.substring(2); firstArgumentsString = firstArgumentsString.substring(2); argumentString = argumentString.substring(2); out.print(" private " + lceName + "(boolean b) { this("); if (lce.learnerParameterBlock == null) out.print(firstArgumentsString); else out.print("new Parameters()"); out.println("); }"); out.print(" private " + lceName + "("); if (lce.learnerParameterBlock == null) out.print(formalParameterString); else out.print("Parameters parameters"); out.println(")"); out.println(" {"); out.print(" super("); if (lce.learnerParameterBlock == null) { if (lce.learnerConstructor.arguments.size() > 0) { out.print(argumentString); if (lce.attributeString.length() != 0) { out.print(", "); } } if (lce.attributeString.length() != 0) out.print("attributeString"); } else out.print("parameters"); out.println(");"); out.println(" containingPackage = \"" + AST.globalSymbolTable.getPackage() + "\";"); out.println(" name = \"" + lceName + "\";"); out.println(" setEncoding(" + lce.featureEncoding + ");"); if (lce.labeler != null) out.println(" setLabeler(new " + lce.labeler.name + "());"); out.println(" setExtractor(new " + lce.extractor.name + "());"); out.println(" isClone = false;"); out.println(" }\n"); } out.println(" public static Parser getParser() { return " + lce.parser + "; }"); out.println(" public static Parser getTestParser() { return " + lce.testParser + "; }\n"); generateLearnerBody(out, lce); if (lce.parameterSets.size() > 0) { out.println(); out.println(" public static class Parameters extends " + lce.learnerName + ".Parameters"); out.println(" {"); out.println(" public Parameters() { this(" + firstArgumentsString + "); }"); out.println(" public Parameters(" + formalParameterString + ")"); if (lce.learnerParameterBlock != null) { TranslateToJava translator = new TranslateToJava(null); translator.setRoot(lce.learnerParameterBlock); translator.setCurrentCG(currentCG); translator.setIndent(3); translator.run(); out.println(translator.getMethodBody()); } else { out.println(" {"); out.println(" super((" + lce.learnerName + ".Parameters) new " + lceName + "(" + argumentString + ").getParameters());"); out.println(" }"); } out.println(" }"); } out.println("}\n"); out.close(); } /** * Generates code for all nodes of the indicated type. * * @param c The node to process. **/ public void run(Conjunction c) { String cName = c.name.toString(); String fileName = cName + ".java"; if (fileName.indexOf("$$") != -1) { files.add(fileName); runOnChildren(c); } else { files.clear(); runOnChildren(c); final String prefix = cName + "$$"; File[] leftOvers = new File(System.getProperty("user.dir")).listFiles( new FilenameFilter() { public boolean accept(File directory, String name) { int i = name.lastIndexOf('.'); if (i == -1) return false; String javaFile = name.substring(0, i) + ".java"; return name.startsWith(prefix) && !files.contains(javaFile); } }); for (int i = 0; i < leftOvers.length; ++i) if (leftOvers[i].exists() && !leftOvers[i].delete()) reportError(0, "Could not delete '" + leftOvers[i].getName() + "'."); } if (!RevisionAnalysis.revisionStatus.get(cName) .equals(RevisionAnalysis.REVISED)) return; System.out.println("Generating code for " + cName); PrintStream out = open(fileName); if (out == null) return; out.println(disclaimer); out.println("// " + c.shallow() + "\n"); ast.symbolTable.generateHeader(out); out.println("\n"); if (c.comment != null) out.println(c.comment); out.println("public class " + cName + " extends Classifier"); out.println("{"); String leftName = c.left.name.toString(); String rightName = c.right.name.toString(); out.println(" private static final " + leftName + " left = new " + leftName + "();"); if (!leftName.equals(rightName)) out.println(" private static final " + rightName + " right = new " + c.right.name + "();\n"); if (c.singleExampleCache) { out.println( " private static ThreadLocal __cache = new ThreadLocal(){ };"); out.println(" private static ThreadLocal __exampleCache = " + "new ThreadLocal(){ };"); out.println(" public static void clearCache() { __exampleCache = new " + "ThreadLocal() { }; }\n"); } out.println(" public " + cName + "()"); out.println(" {"); out.println(" containingPackage = \"" + AST.globalSymbolTable.getPackage() + "\";"); out.println(" name = \"" + cName + "\";"); out.println(" }\n"); LBJ2.IR.Type input = c.argument.getType(); typeReturningMethods(out, input, c.returnType); out.println(); boolean primitive = c.returnType.type == ClassifierReturnType.DISCRETE || c.returnType.type == ClassifierReturnType.REAL; boolean mixed = c.returnType.type == ClassifierReturnType.MIXED_GENERATOR; int leftType = c.left.returnType.type; int rightType = c.right.returnType.type; boolean sameType = leftType == rightType; boolean leftIsGenerator = leftType == ClassifierReturnType.DISCRETE_GENERATOR || rightType == ClassifierReturnType.REAL_GENERATOR; boolean leftIsPrimitive = leftType == ClassifierReturnType.DISCRETE || leftType == ClassifierReturnType.REAL; boolean rightIsPrimitive = rightType == ClassifierReturnType.DISCRETE || rightType == ClassifierReturnType.REAL; boolean bothMulti = !leftIsPrimitive && !rightIsPrimitive; StringBuffer body = new StringBuffer(); if (primitive) body.append(" __result = left.featureValue(__example)" + ".conjunction(right.featureValue(__example), this);\n"); else { body.append(" __result = new FeatureVector();\n"); if (leftIsPrimitive) body.append(" Feature lf = left.featureValue(__example);\n"); else body.append( " FeatureVector leftVector = left.classify(__example);\n" + " int N = leftVector.featuresSize();\n"); if (c.left.equals(c.right)) { // SemanticAnalysis ensures that neither classifier is primitive here. body.append( " for (int j = 1; j < N; ++j)\n" + " {\n"); body.append( " Feature rf = leftVector.getFeature(j);\n" + " for (int i = 0; i < j; ++i)\n" + " __result.addFeature(leftVector.getFeature(i)" + ".conjunction(rf, this));\n" + " }\n"); } else { if (rightIsPrimitive) body.append(" Feature rf = right.featureValue(__example);\n"); else body.append( " FeatureVector rightVector = right.classify(__example);\n" + " int M = rightVector.featuresSize();\n"); String in = ""; if (!leftIsPrimitive) { body.append( " for (int i = 0; i < N; ++i)\n" + " {\n" + " Feature lf = leftVector.getFeature(i);\n"); in += " "; } if (!rightIsPrimitive) { body.append(in); body.append(" for (int j = 0; j < M; ++j)\n"); body.append(in); body.append(" {\n"); body.append(in); body.append(" Feature rf = rightVector.getFeature(j);\n"); in += " "; } if (mixed || leftIsGenerator && sameType) { body.append(in); body.append(" if (lf.equals(rf)) continue;\n"); } body.append(in); body.append(" __result.addFeature(lf.conjunction(rf, this));\n"); if (!rightIsPrimitive) { in = in.substring(2); body.append(in); body.append(" }\n"); } if (!leftIsPrimitive) body.append(" }\n"); body.append("\n"); } if (bothMulti) body.append(" __result.sort();\n"); } generateClassificationMethods(out, c, null, body.toString(), false, false, null); out.println(); typeCheckClassifyArray(out, cName, input, c.line); out.println(); generateHashingMethods(out, cName); out.println("}\n"); out.close(); } /** * Generates code for all nodes of the indicated type. * * @param cd The node to process. **/ public void run(ConstraintDeclaration cd) { String cdName = cd.name.toString(); String fileName = cdName + ".java"; if (!RevisionAnalysis.revisionStatus.get(cdName) .equals(RevisionAnalysis.REVISED)) return; System.out.println("Generating code for " + cdName); PrintStream out = open(fileName); if (out == null) return; out.println(disclaimer); out.print("// "); compressAndPrint(cd.shallow(), out); out.println("\n"); ast.symbolTable.generateHeader(out); out.println("\n"); if (cd.comment != null) out.println(cd.comment); out.println("public class " + cdName + " extends ParameterizedConstraint"); out.println("{"); HashSet invoked = (HashSet) SemanticAnalysis.invokedGraph.get(cdName); if (invoked != null && invoked.size() > 0) { for (Iterator I = invoked.iterator(); I.hasNext(); ) { String name = (String) I.next(); String nameNoDots = name.replace('.', '$'); out.println(" private static final " + name + " __" + nameNoDots + " = new " + name + "();"); } out.println(); } String fqName = ast.symbolTable.getPackage(); if (fqName.length() > 0) fqName += "."; fqName += cdName; out.println(" public " + cdName + "() { super(\"" + fqName + "\"); }\n"); LBJ2.IR.Type input = cd.argument.getType(); out.println(" public String getInputType() { return \"" + input.typeClass().getName() + "\"; }\n"); indent = 2; forInit = false; constraintMode = false; methodBody.delete(0, methodBody.length()); currentCG = cd; for (ASTNodeIterator I = cd.body.iterator(); I.hasNext(); ) { I.next().runPass(this); methodBody.append("\n"); } out.println(" public String discreteValue(Object __example)"); out.println(" {"); out.print( generateTypeChecking(" ", cdName, "Constraint", false, input.toString(), cd.line, "__example", false)); out.println(" " + cd.argument + " = (" + input + ") __example;\n"); out.println(methodBody); out.println(" return \"true\";"); out.println(" }"); out.println(); typeCheckClassifyArray(out, cdName, input, cd.line); out.println(); generateHashingMethods(out, cdName); indent = 2; forInit = false; constraintMode = true; methodBody.delete(0, methodBody.length()); for (ASTNodeIterator I = cd.body.iterator(); I.hasNext(); ) { I.next().runPass(this); methodBody.append("\n"); } out.println("\n public FirstOrderConstraint makeConstraint(Object " + "__example)"); out.println(" {"); out.print( generateTypeChecking(" ", cdName, "Constraint", false, input.toString(), cd.line, "__example", false)); out.println(" " + cd.argument + " = (" + input + ") __example;"); out.println(" FirstOrderConstraint __result = new " + "FirstOrderConstant(true);\n"); out.println(methodBody); out.println(" return __result;"); out.println(" }"); out.println("}\n"); out.close(); } /** * Generates code for all nodes of the indicated type. * * @param in The node to process. **/ public void run(InferenceDeclaration in) { in.constraint.runPass(this); String inName = in.name.toString(); String fileName = inName + ".java"; if (!RevisionAnalysis.revisionStatus.get(inName) .equals(RevisionAnalysis.REVISED)) return; System.out.println("Generating code for " + inName); PrintStream out = open(fileName); if (out == null) return; out.println(disclaimer); out.print("// "); compressAndPrint(in.shallow(), out); out.println("\n"); ast.symbolTable.generateHeader(out); out.println("import java.util.*;\n\n"); currentCG = in; String defaultNormalizer = "new IdentityNormalizer()"; if (in.comment != null) out.println(in.comment); out.println("public class " + inName + " extends " + in.algorithm.name); out.println("{"); if (in.containsTypeSpecificNormalizer()) { out.println(" private static final HashMap normalizers = new " + "HashMap();"); out.println(" static"); out.println(" {"); for (int i = 0; i < in.normalizerDeclarations.length; ++i) { if (in.normalizerDeclarations[i].learner != null) out.println(" normalizers.put(new " + in.normalizerDeclarations[i].learner + "(), " + in.normalizerDeclarations[i].normalizer + ");"); else defaultNormalizer = in.normalizerDeclarations[i].normalizer.toString(); } out.println(" }\n"); } else for (int i = 0; i < in.normalizerDeclarations.length; ++i) defaultNormalizer = in.normalizerDeclarations[i].normalizer.toString(); indent = 1; forInit = false; constraintMode = false; methodBody.delete(0, methodBody.length()); for (int i = 0; i < in.headFinders.length; ++i) in.headFinders[i].runPass(this); out.println(methodBody); out.println(" public " + inName + "() { }"); out.println(" public " + inName + "(" + in.head.getType() + " head)"); out.println(" {"); out.print(" super(head"); if (in.algorithm.arguments.size() > 0) out.print(", " + in.algorithm.arguments); out.println(");"); out.println(" constraint = new " + in.constraint.name + "().makeConstraint(head);"); out.println(" }\n"); out.println(" public String getHeadType() { return \"" + in.head.getType().typeClass().getName() + "\"; }"); out.println(" public String[] getHeadFinderTypes()"); out.println(" {"); out.print(" return new String[]{ \"" + in.headFinders[0].argument.getType().typeClass().getName() + "\""); for (int i = 1; i < in.headFinders.length; ++i) out.print(", \"" + in.headFinders[i].argument.getType().typeClass().getName() + "\""); out.println(" };"); out.println(" }\n"); out.println(" public Normalizer getNormalizer(Learner c)"); out.println(" {"); if (in.containsTypeSpecificNormalizer()) { out.println(" Normalizer result = (Normalizer) normalizers.get(c);"); out.println(" if (result == null)"); out.println(" result = " + defaultNormalizer + ";"); out.println(" return result;"); } else out.println(" return " + defaultNormalizer + ";"); out.println(" }"); out.println("}\n"); out.close(); } /** * Runs this pass on all nodes of the indicated type. * * @param h The node to process. **/ public void run(InferenceDeclaration.HeadFinder h) { appendIndent("public static "); methodBody.append(((InferenceDeclaration) currentCG).head.getType()); methodBody.append(" findHead(" + h.argument + ")\n"); ++indent; h.body.runPass(this); --indent; methodBody.append("\n\n"); } /** * Runs this pass on all nodes of the indicated type. * * @param b The node to process. **/ public void run(Block b) { --indent; appendLine("{"); ++indent; runOnChildren(b); methodBody.append("\n"); --indent; appendIndent("}"); ++indent; } /** * Runs this pass on all nodes of the indicated type. * * @param l The node to process. **/ public void run(StatementList l) { ASTNodeIterator I = l.iterator(); if (!I.hasNext()) return; if (I.hasNext()) I.next().runPass(this); while (I.hasNext()) { methodBody.append("\n"); I.next().runPass(this); } } /** * Runs this pass on all nodes of the indicated type. * * @param s The node to process. **/ public void run(AssertStatement s) { appendIndent("assert "); s.condition.runPass(this); if (s.message != null) { methodBody.append(" : "); s.message.runPass(this); } methodBody.append(";"); } /** * Runs this pass on all nodes of the indicated type. * * @param s The node to process. **/ public void run(BreakStatement s) { appendIndent("break"); if (s.label != null) { methodBody.append(" "); methodBody.append(s.label); } methodBody.append(";"); } /** * Runs this pass on all nodes of the indicated type. * * @param s The node to process. **/ public void run(ContinueStatement s) { appendIndent("continue"); if (s.label != null) { methodBody.append(" "); methodBody.append(s.label); } methodBody.append(";"); } /** * Runs this pass on all nodes of the indicated type. * * @param s The node to process. **/ public void run(ExpressionStatement s) { if (s.expression instanceof ConstraintStatementExpression) s.expression.runPass(this); else { appendIndent(); s.expression.runPass(this); methodBody.append(";"); } } /** * Runs this pass on all nodes of the indicated type. * * @param s The node to process. **/ public void run(ForStatement s) { appendIndent("for ("); if (s.initializers != null) { s.initializers.runPass(this); methodBody.append("; "); } else if (s.initializer != null) { forInit = true; s.initializer.runPass(this); methodBody.append(" "); forInit = false; } else methodBody.append("; "); if (s.condition != null) s.condition.runPass(this); methodBody.append("; "); if (s.updaters != null) s.updaters.runPass(this); methodBody.append(")\n"); ++indent; s.body.runPass(this); --indent; } /** * Runs this pass on all nodes of the indicated type. * * @param s The node to process. **/ public void run(IfStatement s) { appendIndent("if ("); s.condition.runPass(this); methodBody.append(")\n"); ++indent; s.thenClause.runPass(this); --indent; if (s.elseClause != null) { methodBody.append("\n"); appendLine("else"); ++indent; s.elseClause.runPass(this); --indent; } } /** * Runs this pass on all nodes of the indicated type. * * @param s The node to process. **/ public void run(LabeledStatement s) { appendIndent(s.label + ": "); s.statement.runPass(this); } /** * Runs this pass on all nodes of the indicated type. * * @param s The node to process. **/ public void run(ReturnStatement s) { appendIndent(); if (currentCG instanceof CodedClassifier && ((CodedClassifier) currentCG).returnType.type == ClassifierReturnType.DISCRETE) { String literal = toStringLiteral(s.expression); methodBody.append("return "); if (literal != null) methodBody.append(literal); else { methodBody.append("\"\" + ("); s.expression.runPass(this); methodBody.append(')'); } } else { methodBody.append("return "); s.expression.runPass(this); } methodBody.append(";"); } /** * If the given expression can be converted to a string at compile time, * this method returns that string. * * @param e The given expression. * @return The compile time conversion of the expression to a string, or * <code>null</code> if it wasn't possible to convert. **/ private static String toStringLiteral(Expression e) { if (e instanceof Constant) { Constant c = (Constant) e; if (c.typeCache instanceof PrimitiveType) return "\"" + c.value + "\""; return c.value; } return null; } /** * Runs this pass on all nodes of the indicated type. * * @param ss The node to process. **/ public void run(SenseStatement ss) { CodedClassifier currentCC = (CodedClassifier) currentCG; if (ss.value instanceof MethodInvocation) { MethodInvocation m = (MethodInvocation) ss.value; if (m.isClassifierInvocation) { ClassifierReturnType invokedType = ((ClassifierType) m.name.typeCache).getOutput(); int t = invokedType.type; if ((currentCC.returnType.type == ClassifierReturnType.DISCRETE_GENERATOR || currentCC.returnType.type == ClassifierReturnType.REAL_GENERATOR) && (t == ClassifierReturnType.DISCRETE_GENERATOR || t == ClassifierReturnType.REAL_GENERATOR || t == ClassifierReturnType.DISCRETE_ARRAY || t == ClassifierReturnType.REAL_ARRAY)) { appendIndent("__id = "); String s = toStringLiteral(ss.name); if (s != null) methodBody.append(s); else { methodBody.append("\"\" + ("); ss.name.runPass(this); methodBody.append(")"); } methodBody.append(";\n"); appendLine("{"); ++indent; appendIndent("FeatureVector __temp = "); ss.value.runPass(this); methodBody.append(";\n"); appendLine("for (int __i = 0; __i < __temp.featuresSize(); ++__i)"); ++indent; boolean isDiscrete = t == ClassifierReturnType.DISCRETE_GENERATOR || t == ClassifierReturnType.DISCRETE_ARRAY; appendIndent("__result.addFeature(new "); methodBody.append(isDiscrete ? "Discrete" : "Real"); methodBody.append("ReferringStringFeature"); methodBody.append("(this, __id, ("); methodBody.append(isDiscrete ? "Discrete" : "Real"); methodBody.append("Feature) __temp.getFeature(__i)"); if (currentCC.returnType.values.size() > 0 && !currentCC.returnType.values.equals(invokedType.values)) { methodBody.append(", "); methodBody.append(m.name.toString()); methodBody.append(".getAllowableValues()"); } methodBody.append("));\n"); indent -= 2; appendIndent("}"); return; } else if ((currentCC.returnType.type == ClassifierReturnType.DISCRETE_ARRAY || currentCC.returnType.type == ClassifierReturnType.REAL_ARRAY) && (t == ClassifierReturnType.DISCRETE_ARRAY || t == ClassifierReturnType.REAL_ARRAY)) { appendLine("{"); ++indent; boolean isDiscrete = t == ClassifierReturnType.DISCRETE_ARRAY; appendIndent("FeatureVector __temp = "); ss.value.runPass(this); methodBody.append(";\n"); appendLine("for (int __i = 0; __i < __temp.featuresSize(); ++__i)"); appendLine("{"); ++indent; appendLine("Feature __f = __temp.getFeature(__i);"); appendIndent("__value = __f."); methodBody.append(isDiscrete ? "getStringValue" : "getStrength"); methodBody.append("();\n"); appendIndent("__result.addFeature("); methodBody.append( primitiveFeatureConstructorInvocation( isDiscrete, true, "this", "\"\"", "__value", null, "" + currentCC.returnType.values.size(), "__featureIndex++, 0")); methodBody.append(");\n"); --indent; appendLine("}"); --indent; appendIndent("}"); return; } } } boolean discrete = currentCC.returnType.type == ClassifierReturnType.DISCRETE_GENERATOR || currentCC.returnType.type == ClassifierReturnType.DISCRETE_ARRAY; if (ss.senseall) { if (ss.name != null) { // if we're inside a generator appendIndent("Object __values = "); ss.name.runPass(this); methodBody.append(";\n\n"); appendLine("if (__values instanceof java.util.Collection)"); appendLine("{"); ++indent; appendLine( "for (java.util.Iterator __I = ((java.util.Collection) " + "__values).iterator(); __I.hasNext(); )"); appendLine("{"); ++indent; appendLine("__id = __I.next().toString();"); senseFeature(ss, currentCC, discrete, discrete ? "\"true\"" : "1"); methodBody.append("\n"); --indent; appendLine("}"); --indent; appendLine("}"); appendLine("else"); appendLine("{"); ++indent; appendLine( "for (java.util.Iterator __I = ((java.util.Map) " + "__values).entrySet().iterator(); __I.hasNext(); )"); appendLine("{"); ++indent; appendLine( "java.util.Map.Entry __e = (java.util.Map.Entry) __I.next();"); appendLine("__id = __e.getKey().toString();"); appendIndent("__value = "); methodBody.append( discrete ? "__e.getValue().toString()" : "((Double) __e.getValue()).doubleValue()"); methodBody.append(";\n"); senseFeature(ss, currentCC, discrete, null); methodBody.append("\n"); --indent; appendLine("}"); --indent; appendLine("}"); } else { appendIndent("java.util.Collection __values = "); ss.value.runPass(this); methodBody.append(";\n\n"); appendLine( "for (java.util.Iterator __I = ((java.util.Collection) " + "__values).iterator(); __I.hasNext(); )"); appendLine("{"); ++indent; appendIndent("__value = "); methodBody.append( discrete ? "__I.next().toString()" : "((Double) __I.next()).doubleValue()"); methodBody.append(";\n"); senseFeature(ss, currentCC, discrete, null); methodBody.append("\n"); --indent; appendIndent("}"); } } else { if (ss.name != null) { // if we're inside a generator appendIndent("__id = "); String s = toStringLiteral(ss.name); if (s != null) methodBody.append(s); else { methodBody.append("\"\" + ("); ss.name.runPass(this); methodBody.append(")"); } methodBody.append(";\n"); } appendIndent("__value = "); if (discrete) { String s = toStringLiteral(ss.value); if (s != null) methodBody.append(s); else { methodBody.append("\"\" + ("); ss.value.runPass(this); methodBody.append(")"); } } else ss.value.runPass(this); methodBody.append(";\n"); senseFeature(ss, currentCC, discrete, null); } } /** * Generates the statement that adds a new feature of the appropriate type * to the returned <code>FeatureVector</code> when a <code>sense</code> * statement is executed. The code generated by this method assumes the * following: * <ul> * <li> * if the containing classifier is a generator, code has already been * generated to set the value of a string named <code>__id</code> * representing the identifier of the new feature and * <li> * if <code>value</code> is <code>null</code>, code has already been * generated to set the value of a string or double as appropriate * named <code>__value</code> representing the value of the new * feature. * </ul> * * @param s The <code>sense</code> statement. * @param cc The current coded classifier. * @param discrete Whether or not <code>cc</code> is discrete. * @param value Generated code evaluating to the new feature's value. * If this parameter is <code>null</code>, it will default * to <code>"__value"</code>. **/ private void senseFeature(SenseStatement s, CodedClassifier cc, boolean discrete, String value) { appendIndent("__result.addFeature("); boolean array = s.name == null; String id = array ? "\"\"" : "__id"; if (value == null) value = "__value"; methodBody.append( primitiveFeatureConstructorInvocation( discrete, array, "this", id, value, null, "" + cc.returnType.values.size(), "__featureIndex++, 0")); methodBody.append(");"); } /** * Runs this pass on all nodes of the indicated type. * * @param s The node to process. **/ public void run(SwitchStatement s) { appendIndent("switch ("); s.expression.runPass(this); methodBody.append(")\n"); s.block.runPass(this); } /** * Runs this pass on all nodes of the indicated type. * * @param s The node to process. **/ public void run(SynchronizedStatement s) { appendIndent("synchronized ("); s.data.runPass(this); methodBody.append(")\n"); ++indent; s.block.runPass(this); --indent; } /** * Runs this pass on all nodes of the indicated type. * * @param s The node to process. **/ public void run(ThrowStatement s) { appendIndent("throw "); s.exception.runPass(this); methodBody.append(";\n"); } /** * Runs this pass on all nodes of the indicated type. * * @param s The node to process. **/ public void run(TryStatement s) { appendLine("try"); ++indent; s.block.runPass(this); --indent; s.catchList.runPass(this); if (s.finallyBlock != null) { appendLine("finally"); s.finallyBlock.runPass(this); } } /** * Runs this pass on all nodes of the indicated type. * * @param s The node to process. **/ public void run(VariableDeclaration s) { if (!forInit) appendIndent(); if (s.isFinal) methodBody.append("final "); s.type.runPass(this); ASTNodeIterator N = s.names.iterator(); methodBody.append(" " + N.next()); ExpressionList.ExpressionListIterator I = s.initializers.listIterator(); Expression i = I.nextItem(); if (i != null) { methodBody.append(" = "); i.runPass(this); } while (N.hasNext()) { methodBody.append(", " + N.next()); i = I.nextItem(); if (i != null) { methodBody.append(" = "); i.runPass(this); } } methodBody.append(";"); } /** * Runs this pass on all nodes of the indicated type. * * @param s The node to process. **/ public void run(WhileStatement s) { appendIndent("while ("); s.condition.runPass(this); methodBody.append(")\n"); ++indent; s.body.runPass(this); --indent; } /** * Runs this pass on all nodes of the indicated type. * * @param s The node to process. **/ public void run(DoStatement s) { appendLine("do"); ++indent; s.body.runPass(this); --indent; appendIndent("while ("); s.condition.runPass(this); methodBody.append(");\n"); } /** * Runs this pass on all nodes of the indicated type. * * @param l The node to process. **/ public void run(SwitchGroupList l) { ASTNodeIterator I = l.iterator(); if (!I.hasNext()) return; I.next().runPass(this); while (I.hasNext()) { methodBody.append("\n"); I.next().runPass(this); } } /** * Runs this pass on all nodes of the indicated type. * * @param g The node to process. **/ public void run(SwitchGroup g) { appendIndent(); g.labels.runPass(this); methodBody.append("\n"); ++indent; g.statements.runPass(this); --indent; } /** * Runs this pass on all nodes of the indicated type. * * @param l The node to process. **/ public void run(SwitchLabelList l) { ASTNodeIterator I = l.iterator(); if (!I.hasNext()) return; I.next().runPass(this); while (I.hasNext()) { methodBody.append(" "); I.next().runPass(this); } } /** * Runs this pass on all nodes of the indicated type. * * @param l The node to process. **/ public void run(SwitchLabel l) { methodBody.append("case "); l.value.runPass(this); methodBody.append(":"); } /** * Runs this pass on all nodes of the indicated type. * * @param l The node to process. **/ public void run(CatchList l) { ASTNodeIterator I = l.iterator(); if (!I.hasNext()) return; I.next().runPass(this); while (I.hasNext()) { methodBody.append("\n"); I.next().runPass(this); } } /** * Runs this pass on all nodes of the indicated type. * * @param c The node to process. **/ public void run(CatchClause c) { appendIndent("catch ("); c.argument.runPass(this); methodBody.append(")\n"); ++indent; c.block.runPass(this); --indent; } /** * Runs this pass on all nodes of the indicated type. * * @param a The node to process. **/ public void run(Argument a) { if (a.getFinal()) methodBody.append("final "); a.getType().runPass(this); methodBody.append(" " + a.getName()); } /** * This method generates the code for a new temporary variable used when * translating constraints. * * @param name The name of the temporary variable. **/ private void constraintTemporary(String name) { appendIndent(); if (constraintMode) methodBody.append("FirstOrderConstraint "); else methodBody.append("boolean "); methodBody.append(name); if (constraintMode) methodBody.append(" = null;\n"); else methodBody.append(";\n"); } /** * Runs this pass on all nodes of the indicated type. * * @param e The node to process. **/ public void run(ConstraintStatementExpression e) { constraintResultNumber = 0; appendLine("{"); ++indent; if (constraintMode && e.constraint.containsQuantifiedVariable()) { StringBuffer buffer = new StringBuffer(); int i = 0; HashSet referenced = e.constraint.getVariableTypes(); for (Iterator I = referenced.iterator(); I.hasNext(); ) { Argument a = (Argument) I.next(); LBJ2.IR.Type t = a.getType(); if (t.quantifierArgumentType) continue; for (int j = 0; j < indent; ++j) buffer.append(" "); buffer.append("LBJ$constraint$context["); buffer.append(i); buffer.append("] = "); if (t instanceof PrimitiveType) { String primitiveTypeName = null; if (((PrimitiveType) t).type == PrimitiveType.INT) primitiveTypeName = "Integer"; else { primitiveTypeName = t.toString(); primitiveTypeName = Character.toUpperCase(primitiveTypeName.charAt(0)) + primitiveTypeName.substring(1); } buffer.append("new "); buffer.append(primitiveTypeName); buffer.append("("); } buffer.append(a.getName()); if (t instanceof PrimitiveType) buffer.append(")"); buffer.append(";\n"); contextVariables.put(a.getName(), new Integer(i++)); } appendIndent("Object[] LBJ$constraint$context = new Object["); methodBody.append(i); methodBody.append("];\n"); methodBody.append(buffer); } String childResultName = constraintResult + constraintResultNumber; constraintResultName = childResultName; constraintTemporary(childResultName); quantifierNesting = 0; e.constraint.runPass(this); appendIndent(); if (constraintMode) { methodBody.append("__result = new FirstOrderConjunction(__result, "); methodBody.append(childResultName); methodBody.append(");\n"); } else { methodBody.append("if (!"); methodBody.append(childResultName); methodBody.append(") return \"false\";\n"); } --indent; appendIndent("}"); } /** * Runs this pass on all nodes of the indicated type. * * @param e The node to process. **/ public void run(BinaryConstraintExpression e) { String myResultName = constraintResultName; String leftResultName = constraintResult + ++constraintResultNumber; appendLine("{"); ++indent; constraintTemporary(leftResultName); constraintResultName = leftResultName; e.left.runPass(this); if (constraintMode || e.operation.operation == Operator.DOUBLE_IMPLICATION) { String rightResultName = constraintResult + ++constraintResultNumber; constraintTemporary(rightResultName); constraintResultName = rightResultName; e.right.runPass(this); appendIndent(myResultName); if (constraintMode) { methodBody.append(" = new FirstOrder"); if (e.operation.operation == Operator.LOGICAL_CONJUNCTION) methodBody.append("Conjunction"); else if (e.operation.operation == Operator.LOGICAL_DISJUNCTION) methodBody.append("Disjunction"); else if (e.operation.operation == Operator.IMPLICATION) methodBody.append("Implication"); else methodBody.append("DoubleImplication"); methodBody.append("("); methodBody.append(leftResultName); methodBody.append(", "); methodBody.append(rightResultName); methodBody.append(");\n"); } else { methodBody.append(" = "); methodBody.append(leftResultName); methodBody.append(" == "); methodBody.append(rightResultName); methodBody.append(";\n"); } } else { appendIndent("if ("); if (e.operation.operation == Operator.LOGICAL_DISJUNCTION) methodBody.append("!"); methodBody.append(leftResultName); methodBody.append(")\n"); ++indent; constraintResultName = myResultName; e.right.runPass(this); --indent; appendIndent("else "); methodBody.append(myResultName); methodBody.append(" = "); methodBody.append(e.operation.operation == Operator.LOGICAL_DISJUNCTION || e.operation.operation == Operator.IMPLICATION); methodBody.append(";\n"); } --indent; appendLine("}"); } /** * Runs this pass on all nodes of the indicated type. * * @param e The node to process. **/ public void run(NegatedConstraintExpression e) { String myResultName = constraintResultName; String childResultName = constraintResult + ++constraintResultNumber; appendLine("{"); ++indent; constraintTemporary(childResultName); constraintResultName = childResultName; e.constraint.runPass(this); appendIndent(myResultName); methodBody.append(" = "); if (constraintMode) methodBody.append("new FirstOrderNegation("); else methodBody.append("!"); methodBody.append(childResultName); if (constraintMode) methodBody.append(")"); methodBody.append(";\n"); --indent; appendLine("}"); } /** * Generates the code necessary at the top of a replacer method * implementation to declare the variables that will be used in the method. * * @param expression The expression to be evaluted in the replacer method. **/ private void generateReplacerMethodEnvironment(Expression expression) { for (Iterator I = expression.getVariableTypes().iterator(); I.hasNext(); ) { Argument a = (Argument) I.next(); LBJ2.IR.Type type = a.getType(); String primitiveTypeName = null; if (type instanceof PrimitiveType) { if (((PrimitiveType) type).type == PrimitiveType.INT) primitiveTypeName = "Integer"; else { primitiveTypeName = type.toString(); primitiveTypeName = Character.toUpperCase(primitiveTypeName.charAt(0)) + primitiveTypeName.substring(1); } } appendIndent(); a.runPass(this); methodBody.append(" = ("); if (primitiveTypeName == null) type.runPass(this); else methodBody.append("(" + primitiveTypeName); methodBody.append(") "); if (type.quantifierArgumentType) { methodBody.append("quantificationVariables.get("); methodBody.append( ((Integer) quantificationVariables.get(a.getName())) .intValue()); methodBody.append(")"); } else { methodBody.append("context["); methodBody.append( ((Integer) contextVariables.get(a.getName())).intValue()); methodBody.append("]"); } if (primitiveTypeName != null) methodBody.append(")." + type + "Value()"); methodBody.append(";\n"); } } /** * Translates an expression from a quantified * {@link ConstraintEqualityExpression} into the appropriate method of an * {@link EqualityArgumentReplacer}. * * @param right Indicates if <code>expression</code> comes * from the right hand side of the equality. * @param expression The expression. * @param isDiscreteLearner This flag is set if <code>expression</code> * represents a variable. **/ private void generateEARMethod(boolean right, Expression expression, boolean isDiscreteLearner) { appendIndent("public "); methodBody.append(isDiscreteLearner ? "Object" : "String"); methodBody.append(" get"); methodBody.append(right ? "Right" : "Left"); methodBody.append(isDiscreteLearner ? "Object" : "Value"); methodBody.append("()\n"); appendLine("{"); ++indent; generateReplacerMethodEnvironment(expression); appendIndent("return "); if (isDiscreteLearner) ((MethodInvocation) expression).arguments.runPass(this); else { methodBody.append("\"\" + ("); expression.runPass(this); methodBody.append(")"); } methodBody.append(";\n"); --indent; appendLine("}"); } /** * Translates an unquantified expression not representing a first order * variable from a {@link ConstraintEqualityExpression} into an argument of * a {@link FirstOrderEquality}. * * @param left This flag is set if <code>expression</code> came from * the left hand side of the equality. * @param expression The expression. **/ private void generateNotVariable(boolean left, Expression expression) { if (left) methodBody.append("("); methodBody.append("\"\" + ("); expression.runPass(this); methodBody.append(")"); if (left) methodBody.append(")"); } /** * Translates an expression representing a first order variable from a * {@link ConstraintEqualityExpression} into an argument of a * {@link FirstOrderEquality}. * * @param expression The expression. * @param isQuantified This flag is set if <code>expression</code> contains * a quantified variable. **/ private void generateVariable(Expression expression, boolean isQuantified) { MethodInvocation method = (MethodInvocation) expression; methodBody.append("new FirstOrderVariable("); methodBody.append(("__" + method.name).replace('.', '$')); if (isQuantified) methodBody.append(", null)"); else { methodBody.append(", "); method.arguments.runPass(this); methodBody.append(")"); } } /** * Runs this pass on all nodes of the indicated type. * * @param e The node to process. **/ public void run(ConstraintEqualityExpression e) { String myResultName = constraintResultName; boolean leftIsDiscreteLearner = e.leftIsDiscreteLearner; boolean rightIsDiscreteLearner = e.rightIsDiscreteLearner; boolean leftIsQuantified = e.leftIsQuantified; boolean rightIsQuantified = e.rightIsQuantified; Expression left = e.left; Expression right = e.right; if (!leftIsDiscreteLearner && rightIsDiscreteLearner) { leftIsDiscreteLearner = true; rightIsDiscreteLearner = false; leftIsQuantified ^= rightIsQuantified; rightIsQuantified ^= leftIsQuantified; leftIsQuantified ^= rightIsQuantified; Expression temp = left; left = right; right = temp; } if (!(constraintMode && (leftIsQuantified || rightIsQuantified))) { appendIndent(myResultName); methodBody.append(" = "); if (constraintMode) { methodBody.append("new FirstOrder"); if (leftIsDiscreteLearner) { if (rightIsDiscreteLearner) methodBody.append("EqualityWithVariable"); else methodBody.append("EqualityWithValue"); methodBody.append("("); methodBody.append(e.operation.operation == Operator.CONSTRAINT_EQUAL); methodBody.append(", "); generateVariable(left, false); methodBody.append(", "); if (rightIsDiscreteLearner) generateVariable(right, false); else generateNotVariable(false, right); methodBody.append(");\n"); return; } methodBody.append("Constant("); } if (e.operation.operation == Operator.CONSTRAINT_NOT_EQUAL) methodBody.append("!"); generateNotVariable(true, left); methodBody.append(".equals("); generateNotVariable(false, right); methodBody.append(")"); if (constraintMode) methodBody.append(")"); methodBody.append(";\n"); return; } appendLine("{"); ++indent; appendLine("EqualityArgumentReplacer LBJ$EAR ="); ++indent; appendIndent("new EqualityArgumentReplacer(LBJ$constraint$context"); if (!(leftIsQuantified && rightIsQuantified)) { methodBody.append(", "); methodBody.append(leftIsQuantified); } methodBody.append(")\n"); appendLine("{"); ++indent; if (leftIsQuantified) generateEARMethod(false, left, leftIsDiscreteLearner); if (rightIsQuantified) { if (leftIsQuantified) methodBody.append("\n"); generateEARMethod(true, right, rightIsDiscreteLearner); } --indent; appendLine("};"); --indent; appendIndent(myResultName); methodBody.append(" = new FirstOrderEquality"); if (leftIsDiscreteLearner) { if (rightIsDiscreteLearner) methodBody.append("WithVariable"); else methodBody.append("WithValue"); } else methodBody.append("TwoValues"); methodBody.append("("); methodBody.append(e.operation.operation == Operator.CONSTRAINT_EQUAL); methodBody.append(", "); if (leftIsDiscreteLearner) generateVariable(left, leftIsQuantified); else if (leftIsQuantified) methodBody.append("null"); else generateNotVariable(true, left); methodBody.append(", "); if (rightIsDiscreteLearner) generateVariable(right, rightIsQuantified); else if (rightIsQuantified) methodBody.append("null"); else generateNotVariable(false, right); methodBody.append(", LBJ$EAR);\n"); --indent; appendLine("}"); } /** * Runs this pass on all nodes of the indicated type. * * @param e The node to process. **/ public void run(ConstraintInvocation e) { String myResultName = constraintResultName; if (!(constraintMode && e.invocationIsQuantified)) { appendIndent(myResultName); methodBody.append((" = __" + e.invocation.name).replace('.', '$')); if (constraintMode) methodBody.append(".makeConstraint("); else methodBody.append(".discreteValue("); e.invocation.arguments.runPass(this); methodBody.append(")"); if (!constraintMode) methodBody.append(".equals(\"true\")"); methodBody.append(";\n"); return; } appendLine("{"); ++indent; appendLine("InvocationArgumentReplacer LBJ$IAR ="); ++indent; appendLine("new InvocationArgumentReplacer(LBJ$constraint$context)"); appendLine("{"); ++indent; appendLine("public Object compute()"); appendLine("{"); ++indent; Expression argument = e.invocation.arguments.listIterator().nextItem(); generateReplacerMethodEnvironment(argument); appendIndent("return "); argument.runPass(this); methodBody.append(";\n"); --indent; appendLine("}"); --indent; appendLine("};"); --indent; appendIndent(myResultName); methodBody.append(" = new QuantifiedConstraintInvocation("); methodBody.append(("__" + e.invocation.name).replace('.', '$')); methodBody.append(", LBJ$IAR);\n"); --indent; appendLine("}"); } /** * {@link UniversalQuantifierExpression}s and * {@link ExistentialQuantifierExpression}s generate their code through * this method. * * @param e The node to process. **/ private void generateSimpleQuantifier(QuantifiedConstraintExpression e) { boolean universal = e instanceof UniversalQuantifierExpression; String myResultName = constraintResultName; if (!constraintMode) { String inductionVariable = "__I" + quantifierNesting; appendLine("{"); ++indent; appendIndent(myResultName); methodBody.append(" = "); methodBody.append(universal); methodBody.append(";\n"); appendIndent("for (java.util.Iterator "); methodBody.append(inductionVariable); methodBody.append(" = ("); e.collection.runPass(this); methodBody.append(").iterator(); "); methodBody.append(inductionVariable); methodBody.append(".hasNext() && "); if (!universal) methodBody.append("!"); methodBody.append(myResultName); methodBody.append("; )\n"); appendLine("{"); ++indent; appendIndent(); e.argument.runPass(this); methodBody.append(" = ("); e.argument.getType().runPass(this); methodBody.append(") "); methodBody.append(inductionVariable); methodBody.append(".next();\n"); ++quantifierNesting; e.constraint.runPass(this); --quantifierNesting; --indent; appendLine("}"); --indent; appendLine("}"); return; } appendLine("{"); ++indent; String childResultName = constraintResult + ++constraintResultNumber; constraintTemporary(childResultName); constraintResultName = childResultName; quantificationVariables.put(e.argument.getName(), new Integer(quantifierNesting++)); e.constraint.runPass(this); --quantifierNesting; if (!e.collectionIsQuantified) { appendIndent(myResultName); methodBody.append(" = new "); if (universal) methodBody.append("Universal"); else methodBody.append("Existential"); methodBody.append("Quantifier(\""); methodBody.append(e.argument.getName()); methodBody.append("\", "); e.collection.runPass(this); methodBody.append(", "); methodBody.append(childResultName); methodBody.append(");\n"); } else { appendLine("QuantifierArgumentReplacer LBJ$QAR ="); ++indent; appendLine("new QuantifierArgumentReplacer(LBJ$constraint$context)"); appendLine("{"); ++indent; appendLine("public java.util.Collection getCollection()"); appendLine("{"); ++indent; generateReplacerMethodEnvironment(e.collection); appendIndent("return "); e.collection.runPass(this); methodBody.append(";\n"); --indent; appendLine("}"); --indent; appendLine("};"); --indent; appendIndent(myResultName); methodBody.append(" = new "); if (universal) methodBody.append("Universal"); else methodBody.append("Existential"); methodBody.append("Quantifier(\""); methodBody.append(e.argument.getName()); methodBody.append("\", null, "); methodBody.append(childResultName); methodBody.append(", LBJ$QAR);\n"); } --indent; appendLine("}"); } /** * Runs this pass on all nodes of the indicated type. * * @param e The node to process. **/ public void run(UniversalQuantifierExpression e) { generateSimpleQuantifier(e); } /** * Runs this pass on all nodes of the indicated type. * * @param e The node to process. **/ public void run(ExistentialQuantifierExpression e) { generateSimpleQuantifier(e); } /** * {@link AtLeastQuantifierExpression}s and * {@link AtMostQuantifierExpression}s generate their code through this * method. * * @param e The node to process. **/ public void generateBoundedQuantifier(QuantifiedConstraintExpression e) { boolean atleast = e instanceof AtLeastQuantifierExpression; AtLeastQuantifierExpression ale = null; AtMostQuantifierExpression ame = null; if (atleast) ale = (AtLeastQuantifierExpression) e; else ame = (AtMostQuantifierExpression) e; String myResultName = constraintResultName; String childResultName = constraintResult + ++constraintResultNumber; if (!constraintMode) { appendLine("{"); ++indent; String m = "LBJ$m$" + quantifierNesting; String bound = "LBJ$bound$" + quantifierNesting; appendIndent("int "); methodBody.append(m); methodBody.append(" = 0;\n"); appendIndent("int "); methodBody.append(bound); methodBody.append(" = "); if (atleast) ale.lowerBound.runPass(this); else ame.upperBound.runPass(this); methodBody.append(";\n"); String inductionVariable = "__I" + quantifierNesting; appendIndent("for (java.util.Iterator "); methodBody.append(inductionVariable); methodBody.append(" = ("); e.collection.runPass(this); methodBody.append(").iterator(); "); methodBody.append(inductionVariable); methodBody.append(".hasNext() && "); methodBody.append(m); if (atleast) methodBody.append(" < "); else methodBody.append(" <= "); methodBody.append(bound); methodBody.append("; )\n"); appendLine("{"); ++indent; appendIndent(); e.argument.runPass(this); methodBody.append(" = ("); e.argument.getType().runPass(this); methodBody.append(") "); methodBody.append(inductionVariable); methodBody.append(".next();\n"); constraintTemporary(childResultName); constraintResultName = childResultName; ++quantifierNesting; e.constraint.runPass(this); --quantifierNesting; appendIndent("if ("); methodBody.append(childResultName); methodBody.append(") ++"); methodBody.append(m); methodBody.append(";\n"); --indent; appendLine("}"); appendIndent(myResultName); methodBody.append(" = "); methodBody.append(m); if (atleast) methodBody.append(" >= "); else methodBody.append(" <= "); methodBody.append(bound); methodBody.append(";\n"); --indent; appendLine("}"); return; } appendLine("{"); ++indent; constraintTemporary(childResultName); constraintResultName = childResultName; quantificationVariables.put(e.argument.getName(), new Integer(quantifierNesting++)); e.constraint.runPass(this); --quantifierNesting; if (!(e.collectionIsQuantified || atleast && ale.lowerBoundIsQuantified || !atleast && ame.upperBoundIsQuantified)) { appendIndent(myResultName); methodBody.append(" = new "); if (atleast) methodBody.append("AtLeast"); else methodBody.append("AtMost"); methodBody.append("Quantifier(\""); methodBody.append(e.argument.getName()); methodBody.append("\", "); e.collection.runPass(this); methodBody.append(", "); methodBody.append(childResultName); methodBody.append(", "); if (atleast) methodBody.append(ale.lowerBound); else methodBody.append(ame.upperBound); methodBody.append(");\n"); } else { appendLine("QuantifierArgumentReplacer LBJ$QAR ="); ++indent; appendIndent("new QuantifierArgumentReplacer(LBJ$constraint$context"); if (!(e.collectionIsQuantified && (atleast && ale.lowerBoundIsQuantified || !atleast && ame.upperBoundIsQuantified))) { methodBody.append(", "); methodBody.append(e.collectionIsQuantified); } methodBody.append(")\n"); appendLine("{"); ++indent; if (e.collectionIsQuantified) { appendLine("public java.util.Collection getCollection()"); appendLine("{"); ++indent; generateReplacerMethodEnvironment(e.collection); appendIndent("return "); e.collection.runPass(this); methodBody.append(";\n"); --indent; appendLine("}"); } if (atleast && ale.lowerBoundIsQuantified || !atleast && ame.upperBoundIsQuantified) { if (e.collectionIsQuantified) methodBody.append("\n"); appendLine("public int getBound()"); appendLine("{"); ++indent; if (atleast) generateReplacerMethodEnvironment(ale.lowerBound); else generateReplacerMethodEnvironment(ame.upperBound); appendIndent("return "); if (atleast) ale.lowerBound.runPass(this); else ame.upperBound.runPass(this); methodBody.append(";\n"); --indent; appendLine("}"); } --indent; appendLine("};"); --indent; appendIndent(myResultName); methodBody.append(" = new "); if (atleast) methodBody.append("AtLeast"); else methodBody.append("AtMost"); methodBody.append("Quantifier(\""); methodBody.append(e.argument.getName()); methodBody.append("\", "); if (e.collectionIsQuantified) methodBody.append("null"); else e.collection.runPass(this); methodBody.append(", "); methodBody.append(childResultName); methodBody.append(", "); if (atleast && ale.lowerBoundIsQuantified || !atleast && ame.upperBoundIsQuantified) methodBody.append("0"); else if (atleast) ale.lowerBound.runPass(this); else ame.upperBound.runPass(this); methodBody.append(", LBJ$QAR);\n"); } --indent; appendLine("}"); } /** * Runs this pass on all nodes of the indicated type. * * @param e The node to process. **/ public void run(AtLeastQuantifierExpression e) { generateBoundedQuantifier(e); } /** * Runs this pass on all nodes of the indicated type. * * @param e The node to process. **/ public void run(AtMostQuantifierExpression e) { generateBoundedQuantifier(e); } /** * Runs this pass on all nodes of the indicated type. * * @param l The node to process. **/ public void run(ExpressionList l) { ASTNodeIterator I = l.iterator(); if (!I.hasNext()) return; I.next().runPass(this); while (I.hasNext()) { methodBody.append(", "); I.next().runPass(this); } } /** * Runs this pass on all nodes of the indicated type. * * @param e The node to process. **/ public void run(ArrayCreationExpression e) { if (e.parenthesized) methodBody.append("("); methodBody.append("new "); e.elementType.runPass(this); int d = 0; for (ASTNodeIterator I = e.sizes.iterator(); I.hasNext(); ++d) { methodBody.append("["); I.next().runPass(this); methodBody.append("]"); } for (; d < e.dimensions; ++d) methodBody.append("[]"); if (e.parenthesized) methodBody.append(")"); } /** * Runs this pass on all nodes of the indicated type. * * @param e The node to process. **/ public void run(ArrayInitializer e) { if (e.parenthesized) methodBody.append("("); methodBody.append("{ "); e.values.runPass(this); methodBody.append(" }"); if (e.parenthesized) methodBody.append(")"); } /** * Runs this pass on all nodes of the indicated type. * * @param e The node to process. **/ public void run(CastExpression e) { if (e.parenthesized) methodBody.append("("); methodBody.append("("); e.type.runPass(this); methodBody.append(") "); e.expression.runPass(this); if (e.parenthesized) methodBody.append(")"); } /** * Runs this pass on all nodes of the indicated type. * * @param e The node to process. **/ public void run(Conditional e) { if (e.parenthesized) methodBody.append("("); e.condition.runPass(this); methodBody.append(" ? "); e.thenClause.runPass(this); methodBody.append(" : "); e.elseClause.runPass(this); if (e.parenthesized) methodBody.append(")"); } /** * Runs this pass on all nodes of the indicated type. * * @param e The node to process. **/ public void run(Constant e) { if (e.parenthesized) methodBody.append("("); methodBody.append(e.value); if (e.parenthesized) methodBody.append(")"); } /** * Runs this pass on all nodes of the indicated type. * * @param e The node to process. **/ public void run(ParameterSet e) { methodBody.append(e.getParameterName()); } /** * Runs this pass on all nodes of the indicated type. * * @param e The node to process. **/ public void run(InstanceofExpression e) { if (e.parenthesized) methodBody.append("("); e.left.runPass(this); methodBody.append(" instanceof "); e.right.runPass(this); if (e.parenthesized) methodBody.append(")"); } /** * Runs this pass on all nodes of the indicated type. * * @param e The node to process. **/ public void run(Assignment e) { if (e.parenthesized) methodBody.append("("); e.left.runPass(this); methodBody.append(" " + e.operation + " "); e.right.runPass(this); if (e.parenthesized) methodBody.append(")"); } /** * Runs this pass on all nodes of the indicated type. * * @param e The node to process. **/ public void run(IncrementExpression e) { if (e.parenthesized) methodBody.append("("); runOnChildren(e); if (e.parenthesized) methodBody.append(")"); } /** * Runs this pass on all nodes of the indicated type. * * @param e The node to process. **/ public void run(InstanceCreationExpression e) { if (e.parenthesized) methodBody.append("("); if (e.parentObject != null) { e.parentObject.runPass(this); methodBody.append("."); } methodBody.append("new "); e.name.runPass(this); methodBody.append("("); e.arguments.runPass(this); methodBody.append(")"); if (e.parenthesized) methodBody.append(")"); } /** * Runs this pass on all nodes of the indicated type. * * @param e The node to process. **/ public void run(MethodInvocation e) { if (e.parenthesized) methodBody.append("("); if (e.isClassifierInvocation) { methodBody.append(("__" + e.name).replace('.', '$') + "."); ClassifierType invokedType = (ClassifierType) e.name.typeCache; int t = invokedType.getOutput().type; if (t == ClassifierReturnType.DISCRETE) methodBody.append("discreteValue("); else if (t == ClassifierReturnType.REAL) methodBody.append("realValue("); else if (!e.isSensedValue) { if (t == ClassifierReturnType.DISCRETE_ARRAY) methodBody.append("discreteValueArray("); else if (t == ClassifierReturnType.REAL_ARRAY) methodBody.append("realValueArray("); } else methodBody.append("classify("); if (invokedType.getInput() instanceof ArrayType) methodBody.append("(Object) "); e.arguments.runPass(this); methodBody.append(")"); } else { if (e.parentObject != null) { e.parentObject.runPass(this); methodBody.append("."); } e.name.runPass(this); methodBody.append("("); e.arguments.runPass(this); methodBody.append(")"); } if (e.parenthesized) methodBody.append(")"); } /** * Runs this pass on all nodes of the indicated type. * * @param be The node to process. **/ public void run(BinaryExpression be) { if (be.parenthesized) methodBody.append("("); be.left.runPass(this); methodBody.append(" " + be.operation + " "); be.right.runPass(this); if (be.parenthesized) methodBody.append(")"); } /** * Runs this pass on all nodes of the indicated type. * * @param e The node to process. **/ public void run(UnaryExpression e) { if (e.parenthesized) methodBody.append("("); runOnChildren(e); if (e.parenthesized) methodBody.append(")"); } /** * Runs this pass on all nodes of the indicated type. * * @param e The node to process. **/ public void run(FieldAccess e) { if (e.parenthesized) methodBody.append("("); e.object.runPass(this); methodBody.append("." + e.name); if (e.parenthesized) methodBody.append(")"); } /** * Runs this pass on all nodes of the indicated type. * * @param e The node to process. **/ public void run(SubscriptVariable e) { if (e.parenthesized) methodBody.append("("); e.array.runPass(this); methodBody.append("["); e.subscript.runPass(this); methodBody.append("]"); if (e.parenthesized) methodBody.append(")"); } /** * Runs this pass on all nodes of the indicated type. * * @param n The node to process. **/ public void run(Name n) { if (n.parenthesized) methodBody.append("("); boolean translated = false; if (currentCG != null && n.name.length > 1) { HashSet invoked = (HashSet) SemanticAnalysis.invokedGraph.get(currentCG.getName()); if (invoked != null) { String className = n.toString(); className = className.substring(0, className.lastIndexOf('.')); String fieldOrMethod = n.name[n.name.length - 1]; if (invoked.contains(className)) { String nameNoDots = className.replace('.', '$'); methodBody.append("__"); methodBody.append(nameNoDots); methodBody.append("."); methodBody.append(fieldOrMethod); translated = true; } } } if (!translated) methodBody.append(n); if (n.parenthesized) methodBody.append(")"); } /** * Runs this pass on all nodes of the indicated type. * * @param t The node to process. **/ public void run(ArrayType t) { t.type.runPass(this); methodBody.append("[]"); } /** * Runs this pass on all nodes of the indicated type. * * @param t The node to process. **/ public void run(PrimitiveType t) { methodBody.append(t); } /** * Runs this pass on all nodes of the indicated type. * * @param t The node to process. **/ public void run(ReferenceType t) { methodBody.append(t); } /** * Runs this pass on all nodes of the indicated type. * * @param o The node to process. **/ public void run(Operator o) { methodBody.append(o); } }