package operators; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import parser.ExpressionNode; import parser.RecursiveDescentParser; import parser.Value; import valueTypes.BooleanValue; import valueTypes.CharacterSeparatedStatementPair; import valueTypes.ColorValue; import valueTypes.DecimalValue; import valueTypes.ErrorValue; import valueTypes.FunctionValue; import valueTypes.IntegerValue; import valueTypes.NullValue; import valueTypes.StringValue; import variables.Variable; /** * The class which defines most operators, such as +,-,*,/,sin,cos,sqrt, etc. * * @author Curran Kelleher * */ public class Operators { /** * The static Maps of the unary and binary operators. They only get filled * once, this is an optimization for the case where many new instances of * RecursiveDescentParser are created. (These maps first get populated when * the first RecursiveDescentParser is constructed.) */ private static Map<String, BinaryOperatorCreator> binaryOperators = null; private static Map<String, UnaryOperatorCreator> unaryOperators = null; /** * Populates the specified parent RecursiveDescentParser, (via callbacks to * the addBinaryOperator() method), with all available binary operators. * * @param parent * the parent RecursiveDescentParser which will be populated with * unary operators via callbacks to the addBinaryOperator() * method */ public static void getBinaryOperators(RecursiveDescentParser parent) { createAllBinaryOperators(); for (Iterator it = binaryOperators.entrySet().iterator(); it.hasNext();) { Entry entry = (Entry) it.next(); parent.addBinaryOperator((String) entry.getKey(), (BinaryOperatorCreator) entry.getValue()); } } /** * Ensures the static binaryOperators Map is populated with all standard * binary operators. * */ private static void createAllBinaryOperators() { if (binaryOperators == null) { binaryOperators = new HashMap<String, BinaryOperatorCreator>(); binaryOperators.put("+", new BinaryOperatorCreator(1) { public BinaryOperator create(ExpressionNode left, ExpressionNode right) { return new RealNumberBinaryOperator('+', left, right) { double evaluate(double l, double r) { return l + r; } }; } public String getDescription() { return "plus, the addition operator"; } }); binaryOperators.put("-", new Minus()); binaryOperators.put("*", new BinaryOperatorCreator(2) { public BinaryOperator create(ExpressionNode left, ExpressionNode right) { return new RealNumberBinaryOperator('*', left, right) { double evaluate(double l, double r) { return l * r; } }; } public String getDescription() { return "times, the multiplication operator"; } }); binaryOperators.put("/", new BinaryOperatorCreator(2) { public BinaryOperator create(ExpressionNode left, ExpressionNode right) { return new RealNumberBinaryOperator('/', left, right) { double evaluate(double l, double r) { return l / r; } }; } public String getDescription() { return "divide by, the division operator"; } }); binaryOperators.put("%", new BinaryOperatorCreator(2) { public BinaryOperator create(ExpressionNode left, ExpressionNode right) { return new RealNumberBinaryOperator('%', left, right) { double evaluate(double l, double r) { return l % r; } }; } public String getDescription() { return "modulo, calculates the remainder. For example, 10%7 is read \"ten mod (or modulo) seven\" and results in 3. The % operator is not restricted to integers."; } }); binaryOperators.put("^", new BinaryOperatorCreator(3) { public BinaryOperator create(ExpressionNode left, ExpressionNode right) { return new RealNumberBinaryOperator('^', left, right) { double evaluate(double l, double r) { return Math.pow(l, r); } }; } public String getDescription() { return "a^b = a to the power of b."; } }); binaryOperators.put("E", new BinaryOperatorCreator(4) { public BinaryOperator create(ExpressionNode left, ExpressionNode right) { return new RealNumberBinaryOperator('E', left, right) { double evaluate(double l, double r) { return l * Math.pow(10, r); } }; } public String getDescription() { return "times 10 to the power of... (a)E(b) = a*(10^b). For example, 5E2 = 500"; } }); binaryOperators.put("<", new BinaryOperatorCreator(0) { public BinaryOperator create(ExpressionNode left, ExpressionNode right) { return new BinaryOperator("<", left, right) { BooleanValue persistantResult = new BooleanValue(false); @SuppressWarnings("unchecked") public Value evaluate() { Value leftResult = leftChild.evaluate(); Value rightResult = rightChild.evaluate(); if (leftResult instanceof Comparable && rightResult instanceof Comparable) { try { persistantResult.value = ((Comparable) leftResult) .compareTo(rightResult) < 0; return persistantResult; } catch (Exception e) { } } return new ErrorValue(leftResult + " and " + rightResult + " cannot be compared"); } }; } public String getDescription() { return "the \"less than\" comparison. a<b yields true if a is less than b, false otherwise"; } }); binaryOperators.put("<=", new BinaryOperatorCreator(0) { public BinaryOperator create(ExpressionNode left, ExpressionNode right) { return new BinaryOperator("<=", left, right) { BooleanValue persistantResult = new BooleanValue(false); @SuppressWarnings("unchecked") public Value evaluate() { Value leftResult = leftChild.evaluate(); Value rightResult = rightChild.evaluate(); if (leftResult instanceof Comparable && rightResult instanceof Comparable) { try { persistantResult.value = ((Comparable) leftResult) .compareTo(rightResult) < 1; return persistantResult; } catch (Exception e) { } } return new ErrorValue(leftResult + " and " + rightResult + " cannot be compared"); } }; } public String getDescription() { return "the \"less than or equal to\" comparison. a<=b yields true if a is less than or equal to b, false otherwise"; } }); binaryOperators.put(">", new BinaryOperatorCreator(0) { public BinaryOperator create(ExpressionNode left, ExpressionNode right) { return new BinaryOperator(">", left, right) { BooleanValue persistantResult = new BooleanValue(false); @SuppressWarnings("unchecked") public Value evaluate() { Value leftResult = leftChild.evaluate(); Value rightResult = rightChild.evaluate(); if (leftResult instanceof Comparable && rightResult instanceof Comparable) { try { persistantResult.value = ((Comparable) leftResult) .compareTo(rightResult) > 0; return persistantResult; } catch (Exception e) { } } return new ErrorValue(leftResult + " and " + rightResult + " cannot be compared"); } }; } public String getDescription() { return "the \"greater than\" comparison. a>b yields true if a is greater than b, false otherwise"; } }); binaryOperators.put(">=", new BinaryOperatorCreator(0) { public BinaryOperator create(ExpressionNode left, ExpressionNode right) { return new BinaryOperator(">=", left, right) { BooleanValue persistantResult = new BooleanValue(false); @SuppressWarnings("unchecked") public Value evaluate() { Value leftResult = leftChild.evaluate(); Value rightResult = rightChild.evaluate(); if (leftResult instanceof Comparable && rightResult instanceof Comparable) { try { persistantResult.value = ((Comparable) leftResult) .compareTo(rightResult) > -1; return persistantResult; } catch (Exception e) { } } return new ErrorValue(leftResult + " and " + rightResult + " cannot be compared"); } }; } public String getDescription() { return "the \"greater than or equal to\" comparison. a>=b yields true if a is greater than or equal to b, false otherwise"; } }); binaryOperators.put("==", new BinaryOperatorCreator(0) { public BinaryOperator create(ExpressionNode left, ExpressionNode right) { return new BinaryOperator("==", left, right) { BooleanValue persistantResult = new BooleanValue(false); public Value evaluate() { Value leftResult = leftChild.evaluate(); Value rightResult = rightChild.evaluate(); persistantResult.value = leftResult .equals(rightResult); return persistantResult; } }; } public String getDescription() { return "the \"equal to\" comparison. a==b yields true if a is equal to b, false otherwise"; } }); binaryOperators.put("!=", new BinaryOperatorCreator(0) { public BinaryOperator create(ExpressionNode left, ExpressionNode right) { return new BinaryOperator("!=", left, right) { BooleanValue persistantResult = new BooleanValue(false); public Value evaluate() { Value leftResult = leftChild.evaluate(); Value rightResult = rightChild.evaluate(); persistantResult.value = !leftResult .equals(rightResult); return persistantResult; } }; } public String getDescription() { return "the \"not equal to\" comparison. a!=b yields true if a is not equal to b, false otherwise"; } }); binaryOperators.put("&&", new BinaryOperatorCreator(-1) { public BinaryOperator create(ExpressionNode left, ExpressionNode right) { return new BinaryOperator("&&", left, right) { BooleanValue persistantResult = new BooleanValue(false); public Value evaluate() { Value leftResult = leftChild.evaluate(); Value rightResult = rightChild.evaluate(); try { persistantResult.value = ((BooleanValue) leftResult).value && ((BooleanValue) rightResult).value; return persistantResult; } catch (Exception e) { return new ErrorValue( "&& is not a valid operator for " + leftResult + " and " + rightResult); } } }; } public String getDescription() { return "the boolean AND operation, only works with two booleans."; } }); binaryOperators.put("||", new BinaryOperatorCreator(-1) { public BinaryOperator create(ExpressionNode left, ExpressionNode right) { return new BinaryOperator("||", left, right) { BooleanValue persistantResult = new BooleanValue(false); public Value evaluate() { Value leftResult = leftChild.evaluate(); Value rightResult = rightChild.evaluate(); try { persistantResult.value = ((BooleanValue) leftResult).value || ((BooleanValue) rightResult).value; return persistantResult; } catch (Exception e) { return new ErrorValue( "|| is not a valid operator for " + leftResult + " and " + rightResult); } } }; } public String getDescription() { return "the boolean OR operation, only works with two booleans."; } }); // The turnary ? : operator set: binaryOperators.put("?", new BinaryOperatorCreator(-3) { public BinaryOperator create(ExpressionNode left, ExpressionNode right) { return new BinaryOperator("?", left, right) { public Value evaluate() { Value leftResult = leftChild.evaluate(); // Value rightResult = rightChild.evaluate(); if (leftResult instanceof BooleanValue) if (rightChild instanceof CharacterSeparatedStatementPair ? ((CharacterSeparatedStatementPair) rightChild) .getSymbol().equals(":") : false) if (((BooleanValue) leftResult).value) return ((CharacterSeparatedStatementPair) rightChild) .getLeftStatement().evaluate(); else return ((CharacterSeparatedStatementPair) rightChild) .getRightStatement().evaluate(); else return new ErrorValue( rightChild + " should be a colon separated statement pair for the ? : operator"); else return new ErrorValue( leftResult + " is an invalid condition for the ? : operator"); } }; } public String getDescription() { return "the \"?\" part of the ?: ternary operator. \"a<b?c=2:c=3\" executes \"c=2\" if a is less than b, or executes \"c=3\" otherwise."; } }); binaryOperators.put(":", new BinaryOperatorCreator(-2) { public BinaryOperator create(ExpressionNode left, ExpressionNode right) { return new CharacterSeparatedStatementPair(":", left, right); } public String getDescription() { return "the \":\" part of the ?: ternary operator. \"a<b?c=2:c=3\" executes \"c=2\" if a is less than b, or executes \"c=3\" otherwise."; } }); binaryOperators.put(";", new BinaryOperatorCreator(-5) { public BinaryOperator create(ExpressionNode left, ExpressionNode right) { return new CharacterSeparatedStatementPair(";", left, right); } public String getDescription() { return "a statement separator"; } }); binaryOperators.put(",", new BinaryOperatorCreator(-5) { public BinaryOperator create(ExpressionNode left, ExpressionNode right) { return new CharacterSeparatedStatementPair(",", left, right); } public String getDescription() { return "an argument separator"; } }); binaryOperators.put("=", new BinaryOperatorCreator(-4) { public BinaryOperator create(ExpressionNode left, ExpressionNode right) { return new BinaryOperator("=", left, right) { public Value evaluate() { Value rightValue = rightChild.evaluate(); try { return ((Variable) leftChild).set(rightValue); } catch (Exception e) { Value leftValue = leftChild.evaluate(); return new ErrorValue( "'=' is not a valid operator for the types " + leftValue.getType() + " and " + rightValue.getType() + ", so " + leftValue + " = " + rightValue + " could not be evaluated"); } } }; } public String getDescription() { return "the assignment operator. Takes a variable name on the left, and a value on the right. For example, \"a=5\" changes the value of a to 5"; } }); } } /** * Populates the specified parent RecursiveDescentParser, (via callbacks to * the addUnaryOperator() method), with all available unary operators. * * @param parent * the parent RecursiveDescentParser which will be populated with * unary operators via callbacks to the addUnaryOperator() method */ public static void getUnaryOperators(RecursiveDescentParser parent) { createAllUnaryOperators(); for (Iterator it = unaryOperators.entrySet().iterator(); it.hasNext();) { Entry entry = (Entry) it.next(); parent.addUnaryOperator((String) entry.getKey(), (UnaryOperatorCreator) entry.getValue()); } } /** * Ensures the static unaryOperators Map is populated with all standard * unary operators. * */ private static void createAllUnaryOperators() { if (unaryOperators == null) { unaryOperators = new HashMap<String, UnaryOperatorCreator>(); unaryOperators.put("sin", new MathematicalUnaryOperatorCreator() { public UnaryOperator create(ExpressionNode child) { return new RealNumberUnaryOperator("sin", child) { double evaluate(double x) { return Math.sin(x); } }; } public String getDescription() { return "the sine function"; } }); unaryOperators.put("cos", new MathematicalUnaryOperatorCreator() { public UnaryOperator create(ExpressionNode child) { return new RealNumberUnaryOperator("cos", child) { double evaluate(double x) { return Math.cos(x); } }; } public String getDescription() { return "the cosine function"; } }); unaryOperators.put("tan", new MathematicalUnaryOperatorCreator() { public UnaryOperator create(ExpressionNode child) { return new RealNumberUnaryOperator("tan", child) { double evaluate(double x) { return Math.tan(x); } }; } public String getDescription() { return "the tangent function, tan(x) = sin(x)/cos(x)"; } }); unaryOperators.put("asin", new MathematicalUnaryOperatorCreator() { public UnaryOperator create(ExpressionNode child) { return new RealNumberUnaryOperator("asin", child) { double evaluate(double x) { return Math.asin(x); } }; } public String getDescription() { return "arcSine, the inverse of the sine function"; } }); unaryOperators.put("acos", new MathematicalUnaryOperatorCreator() { public UnaryOperator create(ExpressionNode child) { return new RealNumberUnaryOperator("acos", child) { double evaluate(double x) { return Math.acos(x); } }; } public String getDescription() { return "arcCosine, the inverse of the cosine function"; } }); unaryOperators.put("atan", new MathematicalUnaryOperatorCreator() { public UnaryOperator create(ExpressionNode child) { return new RealNumberUnaryOperator("atan", child) { double evaluate(double x) { return Math.atan(x); } }; } public String getDescription() { return "arcTangent, the inverse of the tangent function"; } }); unaryOperators.put("cot", new MathematicalUnaryOperatorCreator() { public UnaryOperator create(ExpressionNode child) { return new RealNumberUnaryOperator("cot", child) { double HALF_PI = Math.PI / 2; double evaluate(double x) { return Math.tan(HALF_PI - x); } }; } public String getDescription() { return "the cotangent function, cot(x) = cos(x)/sin(x)"; } }); unaryOperators.put("sec", new MathematicalUnaryOperatorCreator() { public UnaryOperator create(ExpressionNode child) { return new RealNumberUnaryOperator("sec", child) { double evaluate(double x) { return 1.0 / Math.cos(x); } }; } public String getDescription() { return "the secant function, sec(x) = 1/cos(x)"; } }); unaryOperators.put("csc", new MathematicalUnaryOperatorCreator() { public UnaryOperator create(ExpressionNode child) { return new RealNumberUnaryOperator("csc", child) { double evaluate(double x) { return 1.0 / Math.sin(x); } }; } public String getDescription() { return "the cosecant function, csc(x) = 1/sin(x)"; } }); unaryOperators.put("ln", new MathematicalUnaryOperatorCreator() { public UnaryOperator create(ExpressionNode child) { return new RealNumberUnaryOperator("ln", child) { double evaluate(double x) { return Math.log(x); } }; } public String getDescription() { return "natural logarithm, log base e"; } }); unaryOperators.put("log", new MathematicalUnaryOperatorCreator() { public UnaryOperator create(ExpressionNode child) { return new RealNumberUnaryOperator("ln", child) { double conversion = Math.log(10); double evaluate(double x) { // cast down and up in order to round to whole // numbers // when they are very close. return (double) ((float) (Math.log(x) / conversion)); } }; } public String getDescription() { return "logarithm base 10"; } }); unaryOperators.put("abs", new MathematicalUnaryOperatorCreator() { public UnaryOperator create(ExpressionNode child) { return new RealNumberUnaryOperator("abs", child) { double evaluate(double x) { return Math.abs(x); } }; } public String getDescription() { return "absolute value"; } }); unaryOperators.put("sqrt", new MathematicalUnaryOperatorCreator() { public UnaryOperator create(ExpressionNode child) { return new RealNumberUnaryOperator("sqrt", child) { double evaluate(double x) { return Math.sqrt(x); } }; } public String getDescription() { return "square root"; } }); unaryOperators.put("round", new MathematicalUnaryOperatorCreator() { public UnaryOperator create(ExpressionNode child) { return new RealNumberUnaryOperator("round", child) { double evaluate(double x) { return Math.round(x); } }; } public String getDescription() { return "round to the nearest whole number"; } }); unaryOperators.put("floor", new MathematicalUnaryOperatorCreator() { public UnaryOperator create(ExpressionNode child) { return new RealNumberUnaryOperator("floor", child) { double evaluate(double x) { return Math.floor(x); } }; } public String getDescription() { return "floor: floor(3.3) = 3, floor(3.9) = 3"; } }); unaryOperators.put("ceil", new MathematicalUnaryOperatorCreator() { public UnaryOperator create(ExpressionNode child) { return new RealNumberUnaryOperator("ceil", child) { double evaluate(double x) { return Math.ceil(x); } }; } public String getDescription() { return "ceiling: ceil(3.3) = 4, ceil(3.9) = 4"; } }); unaryOperators.put("createFunction", new UnaryOperatorCreator() { public UnaryOperator create(ExpressionNode child) { return new UnaryOperator(child) { public Value evaluate() { return new FunctionValue(child); } /** * @return a string representation of this * UnaryOperator. */ public String toString() { return "createFunction(" + child + ")"; } }; } public String getDescription() { return "creates a function, which can be evaluated by the executeFunction() operator"; } public int getType() { return UnaryOperatorCreator.LANGUAGE_CONSTRUCT; } }); unaryOperators.put("executeFunction", new MathematicalUnaryOperatorCreator() { public UnaryOperator create(ExpressionNode child) { return new UnaryOperator(child) { public Value evaluate() { // return new FunctionValue(child); Value value = child.evaluate(); if (value instanceof FunctionValue) return ((FunctionValue) value) .executeFunction(); else return new ErrorValue( "executeFunction only takes a FunctionValue as an argument, not a " + value.getType() + "; " + value.toString()); } /** * @return a string representation of this * UnaryOperator. */ public String toString() { return "executeFunction(" + child + ")"; } }; } public String getDescription() { return "executes a function, which can be created by the createFunction() operator"; } public int getType() { return UnaryOperatorCreator.LANGUAGE_CONSTRUCT; } }); unaryOperators.put("if", new UnaryOperatorCreator() { public UnaryOperator create(ExpressionNode child) { return new UnaryOperator(child) { FunctionValue associatedFunction = null; public Value evaluate() { Value condition = child.evaluate(); if (condition instanceof BooleanValue) if (((BooleanValue) condition).value) { if (associatedFunction != null) { associatedFunction.executeFunction(); return NullValue.NULL; } else return new ErrorValue( "no associated function for \"if\" statement!"); } else return NullValue.NULL; else return new ErrorValue( "the value " + condition + " is not a BooleanValue, so it is an invalid condition for an \"if\" statement"); } public String toString() { return "if(" + child + ")"; } public boolean linkAssociatedFunction( FunctionValue associatedFunction) { this.associatedFunction = associatedFunction; return true; } }; } public String getDescription() { return "the \"if\" construct, which executes an associated function if the argument is true. For example, when \"if(a<b){c = 5}\" is executed, c will be set to 5 if a is less than b"; } public int getType() { return UnaryOperatorCreator.LANGUAGE_CONSTRUCT; } }); unaryOperators.put("while", new MathematicalUnaryOperatorCreator() { public UnaryOperator create(ExpressionNode child) { return new UnaryOperator(child) { FunctionValue associatedFunction = null; public Value evaluate() { Value condition = child.evaluate(); if (condition instanceof BooleanValue) if (((BooleanValue) condition).value) { if (associatedFunction != null) { boolean conditionBoolean = true; try { while (conditionBoolean) { associatedFunction .executeFunction(); conditionBoolean = ((BooleanValue) child .evaluate()).value; } } catch (Exception e) { return new ErrorValue( "error occured in evaluating the condition " + child); } return NullValue.NULL; } else return new ErrorValue( "no associated function for \"while\" statement!"); } else return NullValue.NULL; else return new ErrorValue( "the value " + condition + " is not a BooleanValue, so it is an invalid condition for a \"while\" statement"); } public String toString() { return "while(" + child + ")" + associatedFunction; } public boolean linkAssociatedFunction( FunctionValue associatedFunction) { this.associatedFunction = associatedFunction; return true; } }; } public String getDescription() { return "the \"while\" loop construct, which executes an associated function as long as the argument is true. For example, when \"{while(b < 100){b = b+1}\" is executed, the function \"b = b+1\" will be executed as many times as it takes for \"b < 100\" to be false, so after the execution of the while loop is finished, b will be 100"; } public int getType() { return UnaryOperatorCreator.LANGUAGE_CONSTRUCT; } }); unaryOperators.put("for", new MathematicalUnaryOperatorCreator() { public UnaryOperator create(ExpressionNode child) { return new ForLoop(child); } public String getDescription() { return "the \"for\" loop construct, of the form \"for(initialisation;condition;after){statement(s)}\", which executes an associated function as long as the condition is true."; } public int getType() { return UnaryOperatorCreator.LANGUAGE_CONSTRUCT; } }); unaryOperators.put("createErrorValue", new UnaryOperatorCreator() { public UnaryOperator create(ExpressionNode child) { return new UnaryOperator(child) { public Value evaluate() { Value value = child.evaluate(); if (value instanceof StringValue) return new ErrorValue(((StringValue) value) .toString()); else return new ErrorValue( "createErrorValue() takes a string as an argument, " + value + " is an invalid argument."); } }; } /** * * @return a human-readable description of the unary operator * which this UnaryOperatorCreator creates. */ public String getDescription() { return "creates an ErrorValue with the specified message. for example, \"createErrorValue(\"this error is because of ...\")"; } /** * Gets the type flag which denotes which type of operator the * unary operator which this UnaryOperatorCreator creates can be * generally classified as. * * @return MATHEMATICAL, LANGUAGE_CONSTRUCT, COMMAND or * MISCELLANEOUS */ public int getType() { return UnaryOperatorCreator.MISCELLANEOUS; } }); unaryOperators.put("createColor", new UnaryOperatorCreator() { public UnaryOperator create(ExpressionNode child) { return new UnaryOperator(child) { ColorValue reUsableValue = new ColorValue(.5, .5, .5); public Value evaluate() { if (child instanceof CharacterSeparatedStatementPair) { CharacterSeparatedStatementPair listOfVariables = (CharacterSeparatedStatementPair) child; if (listOfVariables.getSymbol().equals(",")) { ExpressionNode[] statements = ((CharacterSeparatedStatementPair) child) .extractAllStatements(); if (statements.length == 3 || statements.length == 4) { double[] rgbValues = new double[statements.length]; for (int i = 0; i < statements.length; i++) { Value currentValue = statements[i] .evaluate(); if (currentValue instanceof DecimalValue) rgbValues[i] = ((DecimalValue) currentValue).value; else return new ErrorValue( "createColor(redBetweenZeroAndOne,greenBetweenZeroAndOne,blueBetweenZeroAndOne" + (statements.length == 4 ? "opacityBetweenZeroAndOne" : "") + ") takes " + statements.length + " numbers as arguments. The type of argument number " + (1 + i) + " is " + currentValue .getType() + ", and is therefore not a valid argument."); } if (statements.length == 3) reUsableValue.set(rgbValues[0], rgbValues[1], rgbValues[2]); else // if statements.length == 4 reUsableValue.set(rgbValues[0], rgbValues[1], rgbValues[2], rgbValues[3]); return reUsableValue; } else return new ErrorValue( "createColor(redBetweenZeroAndOne,greenBetweenZeroAndOne,blueBetweenZeroAndOne,opacityBetweenZeroAndOne) takes 3 or 4, not" + statements.length + ", numbers as arguments."); } else return new ErrorValue( "createColor(redBetweenZeroAndOne,greenBetweenZeroAndOne,blueBetweenZeroAndOne,opacityBetweenZeroAndOne) takes 3 or 4 numbers separated by commas \",\" as arguments." + listOfVariables .getSymbol() + " is an invalid argument separator."); } else return new ErrorValue( "createColor(redBetweenZeroAndOne,greenBetweenZeroAndOne,blueBetweenZeroAndOne,opacityBetweenZeroAndOne) takes 3 or 4 numbers separated by commas \",\" as arguments. " + child.toString() + " is an invalid argument."); } }; } /** * * @return a human-readable description of the unary operator * which this UnaryOperatorCreator creates. */ public String getDescription() { return "creates a color based on the specified red, green, and blue arguments, which range from 0 to 1. A fourth alpha, or opacity, argument is optional, also ranging from 0 to 1, and defaults to 1 (completely opaque) if not specified."; } /** * Gets the type flag which denotes which type of operator the * unary operator which this UnaryOperatorCreator creates can be * generally classified as. * * @return MATHEMATICAL, LANGUAGE_CONSTRUCT, COMMAND or * MISCELLANEOUS */ public int getType() { return UnaryOperatorCreator.MISCELLANEOUS; } }); unaryOperators.put("integer", new UnaryOperatorCreator() { public UnaryOperator create(ExpressionNode child) { return new UnaryOperator(child) { public Value evaluate() { Value value = child.evaluate(); if (value instanceof DecimalValue) return new IntegerValue((int) Math .round(((DecimalValue) value).value)); else return new ErrorValue( "createErrorValue() takes a number as an argument, " + value + " is an invalid argument."); } }; } /** * * @return a human-readable description of the unary operator * which this UnaryOperatorCreator creates. */ public String getDescription() { return "creates an IntegerValue with the specified number (which gets rounded if it's not an integer)."; } /** * Gets the type flag which denotes which type of operator the * unary operator which this UnaryOperatorCreator creates can be * generally classified as. * * @return MATHEMATICAL, LANGUAGE_CONSTRUCT, COMMAND or * MISCELLANEOUS */ public int getType() { return UnaryOperatorCreator.MISCELLANEOUS; } }); } } } abstract class MathematicalUnaryOperatorCreator implements UnaryOperatorCreator { public int getType() { return UnaryOperatorCreator.MATHEMATICAL; } } class ForLoop extends UnaryOperator { ExpressionNode[] threeStatements = null; public ForLoop(ExpressionNode child) { super(child); if (child instanceof CharacterSeparatedStatementPair) if (((CharacterSeparatedStatementPair) child).getSymbol().equals( ";")) threeStatements = ((CharacterSeparatedStatementPair) child) .extractAllStatements(); if (threeStatements == null) { threeStatements = new ExpressionNode[1]; threeStatements[0] = new ErrorValue( "the \"for\" loop construct takes three statements separated by semicolons, so " + child + " is invalid in a \"for\" loop."); } } FunctionValue associatedFunction = null; public Value evaluate() { if (associatedFunction != null) { if (threeStatements.length == 3) { Value resultFromInitializer = threeStatements[0].evaluate(); if (resultFromInitializer instanceof ErrorValue) return resultFromInitializer; boolean conditionBoolean; Value resultFromCondition = threeStatements[1].evaluate(); if (resultFromCondition instanceof BooleanValue) conditionBoolean = ((BooleanValue) resultFromCondition).value; else return new ErrorValue( "the condition in the \"for\" loop must be a BooleanValue, but the contidion " + resultFromCondition + " is a " + resultFromCondition.getType() + ", not a BooleanValue."); Value endStatementResult; try { while (conditionBoolean) { associatedFunction.executeFunction(); if ((endStatementResult = threeStatements[2].evaluate()) instanceof ErrorValue) return endStatementResult; conditionBoolean = ((BooleanValue) threeStatements[1] .evaluate()).value; } } catch (Exception e) { return new ErrorValue( "error occured in evaluating the condition " + child); } return NullValue.NULL; } else return new ErrorValue( "the \"for\" loop construct takes three statements separated by semicolons, so " + child + " is invalid in a \"for\" loop."); } else return new ErrorValue("no associated function for \"for\" loop!"); } /** * @return a string representation of this ForLoop. */ public String toString() { StringBuffer b = new StringBuffer(); b.append("for("); for (int i = 0; i < threeStatements.length; i++) b.append(threeStatements[i].toString() + ((i < threeStatements.length - 1) ? "; " : "")); b.append(")"); b.append(associatedFunction.toString()); return b.toString(); } public boolean linkAssociatedFunction(FunctionValue associatedFunction) { this.associatedFunction = associatedFunction; return true; } }