package LBJ2.IR; import java.util.Arrays; import LBJ2.Pass; /** * Represents a set of possible parameters, used when doing * parameter-tuning. The list of possible values for a * parameter is stored in {@link #parameterList} as an * <code>ExpressionList</code>. * * <p>The set can be defined either as a comma-separated list * of possible values, or it can be defined as a range in terms * of a start value, end value, and increment factor, which * can then be converted into the explicit list of possible values. * * <p>The LBJ syntax for defining the parameter set is to declare * the possible values inside of double curly braces, either as * an explicit list or as a range. The following two examples * are equivalent: * <ul> * <li><code>{{2,3,4,5}}</code></li> * <li><code>{{2->5:1}}</code></li> * </ul> * * @author Michael Paul **/ public class ParameterSet extends Expression { /** Remembers how many instances of this class have been instantiated. */ public static int count; /** The list of possible values for this parameter. */ private ExpressionList parameterList; /** * The name of the parameter that will be printed in method signatures in * the generated code. **/ private String parameterName; /** The start value for the range. */ public Expression start; /** The end value for the range. */ public Expression end; /** The factor to increment by. */ public Expression increment; /** The most specific type for the values in this set. */ public Type type; /** * <code>true</code> iff this parameter set appears inside the * <code>rounds</code> clause of a {@link LearningClassifierExpression}. **/ public boolean inRounds; /** * Initializing constructor. Sets the list of possible parameter values. * * @param list The list of possible values for the parameter **/ public ParameterSet(ExpressionList list) { this(-1, -1, list); } /** * Full constructor. Sets the list of possible parameter values. * * @param line The line on which the source code represented by this * node is found. * @param byteOffset The byte offset from the beginning of the source file * at which the source code represented by this node is * found. * @param list The list of possible values for the parameter. **/ public ParameterSet(int line, int byteOffset, ExpressionList list) { super(line, byteOffset); parameterList = list; parameterName = "a" + count++; } /** * Initializing constructor. Sets the range parameters. * * @param s The start value. * @param e The end value. * @param i The increment factor. **/ public ParameterSet(Expression s, Expression e, Expression i) { this(-1, -1, s, e, i); } /** * Full constructor. Sets the range parameters. * * @param line The line on which the source code represented by this * node is found. * @param byteOffset The byte offset from the beginning of the source file * at which the source code represented by this node is * found. * @param s The start value. * @param e The end value. * @param i The increment factor. **/ public ParameterSet(int line, int byteOffset, Expression s, Expression e, Expression i) { super(line, byteOffset); start = s; end = e; increment = i; parameterName = "a" + count++; } /** Returns the value of {@link #parameterName}. */ public String getParameterName() { return parameterName; } /** <code>true</code> iff this parameter set was specified as a range. */ public boolean isRange() { return start != null; } /** Returns a list iterator over {@link #parameterList}. */ public ExpressionList.ExpressionListIterator listIterator() { return parameterList.listIterator(); } /** Returns the first element of the list. */ public Expression getFirst() { ExpressionList.ExpressionListIterator iterator = listIterator(); return iterator.hasNext() ? iterator.nextItem() : null; } /** * Converts this parameter set's {@link #start}, {@link #end}, and * {@link #increment} expressions (which must represent {@link Constant}s * of a {@link PrimitiveType} other than <code>boolean</code>) into an * explicit list of values. The {@link #type} field must be set * appropriately for before calling this method. **/ public void convertRange() { if (start == null) throw new IllegalArgumentException( "Can't call ParameterSet.convertRange() when start == null"); if (type == null) throw new IllegalArgumentException( "Can't call ParameterSet.convertRange() when type == null"); if (!(type instanceof PrimitiveType)) throw new IllegalArgumentException( "Can't call ParameterSet.convertRange() when type isn't " + "primitive"); PrimitiveType pt = (PrimitiveType) type; if (pt.type == PrimitiveType.BOOLEAN) throw new IllegalArgumentException( "Can't call ParameterSet.convertRange() when type is boolean"); parameterList = new ExpressionList(); if (pt.type == PrimitiveType.CHAR) { int s = (int) ((Constant) start).value.charAt(1); int e = (int) ((Constant) end).value.charAt(1); int i = Integer.parseInt(((Constant) increment).value); int m = i / Math.abs(i); e *= m; for (int j = s; m*j <= e; j += i) parameterList.add( new Constant(this.line, this.byteOffset, "" + ((char) j))); } else { double s = Double.parseDouble(((Constant) start).value); double e = Double.parseDouble(((Constant) end).value); double i = Double.parseDouble(((Constant) increment).value); double m = i / Math.abs(i); e *= m; for (double a = s; m*a <= e; a += i) { String num = ""; switch (pt.type) { case PrimitiveType.FLOAT: case PrimitiveType.DOUBLE: num += a; break; case PrimitiveType.BYTE: case PrimitiveType.SHORT: case PrimitiveType.INT: case PrimitiveType.LONG: num += Math.round(a); break; } parameterList.add(new Constant(this.line, this.byteOffset, num)); } } // If we didn't add the end value, add it /* if (lastEntry != e) { Double num = new Double(e); list.add(new Constant(this.line, this.byteOffset, num.toString())); } */ } /** * Parses integers out of every constant in the set and returns them in a * sorted array. If this parameter set was specified as a range, it is * assumed that {@link #convertRange()} has already been called. **/ public int[] toSortedIntArray() { int[] values = new int[parameterList.size()]; ExpressionList.ExpressionListIterator I = listIterator(); for (int i = 0; I.hasNext(); ++i) values[i] = Integer.parseInt(((Constant) I.next()).value); Arrays.sort(values); return values; } /** * Assuming that {@link #convertRange()} has already been called (if * necessary) and that every expression in {@link #parameterList} is a * {@link Constant}, this method produces an array of <code>String</code>s * containing the values of the constants. The return type of the method * is <code>Object[]</code> so that its elements can be replaced by objects * of other types, which is convenient during parameter tuning. **/ public Object[] toStringArray() { Object[] values = new Object[parameterList.size()]; ExpressionList.ExpressionListIterator I = listIterator(); for (int i = 0; I.hasNext(); ++i) values[i] = ((Constant) I.next()).value; return values; } /** * Returns an iterator used to successively access the children of this * node. * * @return An iterator used to successively access the children of this * node. **/ public ASTNodeIterator iterator() { if (start != null) { ASTNodeIterator result = new ASTNodeIterator(3); result.children[0] = start; result.children[1] = end; result.children[2] = increment; return result; } return parameterList.iterator(); } /** * Two parameter sets are equivalent when their constituent expressions are * the same. * * @return <code>true</code> iff this object is the same as the argument. **/ public boolean equals(Object o) { if (!(o instanceof ParameterSet)) return false; ParameterSet p = (ParameterSet) o; return start == null ? parameterList.equals(p.parameterList) : start.equals(p.start) && end.equals(p.end) && increment.equals(p.increment); } /** A hash code based on the hash codes of the constituent expressions. */ public int hashCode() { return start == null ? parameterList.hashCode() : 31 * start.hashCode() + 23 * end.hashCode() + 17 * increment.hashCode(); } /** * Creates a new object with the same primitive data, and recursively * creates new member data objects as well. * * @return The clone node. **/ public Object clone() { ParameterSet c = new ParameterSet(this.line, this.byteOffset, (ExpressionList) parameterList.clone()); c.start = start; c.end = end; c.increment = increment; return c; } /** * Ensures that the correct <code>run()</code> method is called for this * type of node. * * @param pass The pass whose <code>run()</code> method should be called. **/ public void runPass(Pass pass) { pass.run(this); } /** * Writes a string representation of this <code>ASTNode</code> to the * specified buffer. The representation written is parsable by the LBJ2 * compiler, but not very readable. * * @param buffer The buffer to write to. **/ public void write(StringBuffer buffer) { if (parenthesized) buffer.append("("); buffer.append("{{ "); if (start != null) { start.write(buffer); buffer.append(" -> "); end.write(buffer); buffer.append(" : "); increment.write(buffer); } else parameterList.write(buffer); buffer.append(" }}"); if (parenthesized) buffer.append(")"); } }