/** * Evaluate the child with the given index of the given node. This is usually called while * visiting the given node. * * @exception IllegalActionException If an evaluation error occurs. */ protected ptolemy.data.Token _evaluateChild(ASTPtRootNode node, int i) throws IllegalActionException { ASTPtRootNode child = (ASTPtRootNode) node.jjtGetChild(i); _traceEnter(child); child.visit(this); _traceLeave(child); return _evaluatedChildToken; }
/** * Loop through all of the children of this node, visiting each one of them; this will cause their * token value to be determined. * * @param node The node whose children are evaluated. * @exception IllegalActionException If an evaluation error occurs. */ protected ptolemy.data.Token[] _evaluateAllChildren(ASTPtRootNode node) throws IllegalActionException { int numChildren = node.jjtGetNumChildren(); ptolemy.data.Token[] tokens = new ptolemy.data.Token[numChildren]; for (int i = 0; i < numChildren; i++) { /* ASTPtRootNode child = (ASTPtRootNode) */ node.jjtGetChild(i); tokens[i] = _evaluateChild(node, i); } return tokens; }
public void jjtClose() { super.jjtClose(); // We cannot assume that the result of a function call is // constant, even when the arguments to the function are. _isConstant = false; }
/** * Loop through all of the children of this node, visiting each one of them, which will cause * their token value to be determined. */ protected Type[] _inferAllChildren(ASTPtRootNode node) throws IllegalActionException { Type[] types = new Type[node.jjtGetNumChildren()]; int numChildren = node.jjtGetNumChildren(); for (int i = 0; i < numChildren; i++) { _inferChild(node, i); Type type = _inferredChildType; if (type == null) { throw new RuntimeException("node " + node.jjtGetChild(i) + " has no type."); } types[i] = type; } return types; }
/** * Add a record to the current trace corresponding to the start of the evaluation of the given * node. If the trace is null, then do nothing. */ protected void _traceEnter(ASTPtRootNode node) { if (_trace != null) { for (int i = 0; i < _depth; i++) { _trace.append(" "); } _trace.append("Entering node " + node.getClass().getName() + "\n"); _depth++; } }
/** * Evaluate the parse tree with the specified root node using the specified scope to resolve the * values of variables. * * @param node The root of the parse tree. * @param scope The scope for evaluation. * @return The result of evaluation. * @exception IllegalActionException If an error occurs during evaluation. */ public ptolemy.data.Token evaluateParseTree(ASTPtRootNode node, ParserScope scope) throws IllegalActionException { _scope = scope; // Evaluate the value of the root node. node.visit(this); // and return it. _scope = null; return _evaluatedChildToken; }
/** * Add a record to the current trace corresponding to the completion of the evaluation of the * given node. If the trace is null, then do nothing. */ protected void _traceLeave(ASTPtRootNode node) { if (_trace != null) { _depth--; for (int i = 0; i < _depth; i++) { _trace.append(" "); } _trace.append( "Node " + node.getClass().getName() + " evaluated to " + _evaluatedChildToken + "\n"); } }
/** * Trace the evaluation of the parse tree with the specified root node using the specified scope * to resolve the values of variables. * * @param node The root of the parse tree. * @param scope The scope for evaluation. * @return The trace of the evaluation. * @exception IllegalActionException If an error occurs during evaluation. */ public String traceParseTreeEvaluation(ASTPtRootNode node, ParserScope scope) throws IllegalActionException { _scope = scope; _trace = new StringBuffer(); _depth = 0; _traceEnter(node); try { // Evaluate the value of the root node. node.visit(this); _traceLeave(node); } catch (Exception ex) { // If an exception occurs, then bind the exception into // the trace and return the trace. _trace(ex.toString()); } _scope = null; // Return the trace. String trace = _trace.toString(); _trace = null; return trace; }
/** * Evaluate a logical AND or OR on the children of the specified node. * * @param node The specified node. * @exception IllegalActionException If an evaluation error occurs. */ public void visitLogicalNode(ASTPtLogicalNode node) throws IllegalActionException { if (node.isConstant() && node.isEvaluated()) { _evaluatedChildToken = node.getToken(); return; } // Note that we do not always evaluate all of the children... // We perform short-circuit evaluation instead and evaluate the // children in order until the final value is determined, after // which point no more children are evaluated. int numChildren = node.jjtGetNumChildren(); _assert(numChildren > 0, node, "The number of child nodes must be greater than zero"); _evaluateChild(node, 0); ptolemy.data.Token result = _evaluatedChildToken; if (!(result instanceof BooleanToken)) { throw new IllegalActionException( "Cannot perform logical " + "operation on " + result + " which is a " + result.getClass().getName()); } // Make sure that exactly one of AND or OR is set. _assert(node.isLogicalAnd() ^ node.isLogicalOr(), node, "Invalid operation"); // Perform both the short-circuit AND and short-circuit OR in // one piece of code. // FIXME: I dislike that this is not done in the token classes... boolean flag = node.isLogicalAnd(); for (int i = 0; i < numChildren; i++) { ASTPtRootNode child = (ASTPtRootNode) node.jjtGetChild(i); // Evaluate the child child.visit(this); // Get its value. ptolemy.data.Token nextToken = _evaluatedChildToken; if (!(nextToken instanceof BooleanToken)) { throw new IllegalActionException( "Cannot perform logical " + "operation on " + nextToken + " which is a " + result.getClass().getName()); } if (flag != ((BooleanToken) nextToken).booleanValue()) { _evaluatedChildToken = (BooleanToken.getInstance(!flag)); // Note short-circuit eval. return; } } _evaluatedChildToken = (BooleanToken.getInstance(flag)); if (node.isConstant()) { node.setToken(_evaluatedChildToken); } }
/** * Evaluate the first child, and depending on its (boolean) result, evaluate either the second or * the third child. The result of that evaluation becomes the result of the specified node. * * @param node The specified node. * @exception IllegalActionException If an evaluation error occurs. */ public void visitFunctionalIfNode(ASTPtFunctionalIfNode node) throws IllegalActionException { if (node.isConstant() && node.isEvaluated()) { _evaluatedChildToken = node.getToken(); return; } int numChildren = node.jjtGetNumChildren(); if (numChildren != 3) { // A functional-if node MUST have three children in the parse // tree. throw new InternalErrorException( "PtParser error: a functional-if node does not have " + "three children in the parse tree."); } // evaluate the first sub-expression _evaluateChild(node, 0); ptolemy.data.Token test = _evaluatedChildToken; if (!(test instanceof BooleanToken)) { throw new IllegalActionException( "Functional-if must branch on a boolean, but instead was " + test.toString() + " an instance of " + test.getClass().getName()); } boolean value = ((BooleanToken) test).booleanValue(); // Choose the correct sub-expression to evaluate, // and type check the other. if (_typeInference == null) { _typeInference = new ParseTreeTypeInference(); } ASTPtRootNode tokenChild; ASTPtRootNode typeChild; if (value) { tokenChild = (ASTPtRootNode) node.jjtGetChild(1); typeChild = (ASTPtRootNode) node.jjtGetChild(2); } else { tokenChild = (ASTPtRootNode) node.jjtGetChild(2); typeChild = (ASTPtRootNode) node.jjtGetChild(1); } tokenChild.visit(this); ptolemy.data.Token token = _evaluatedChildToken; Type type = _typeInference.inferTypes(typeChild, _scope); Type conversionType = (Type) TypeLattice.lattice().leastUpperBound(type, token.getType()); token = conversionType.convert(token); _evaluatedChildToken = (token); if (node.isConstant()) { node.setToken(_evaluatedChildToken); } }
/** * Apply a function to the children of the specified node. This also handles indexing into * matrices and arrays, which look like function calls. * * <p>In the simplest cases, if the function is being applied to an expression that evaluated to a * FunctionToken, an ArrayToken, or a MatrixToken, then the function application is simply applied * to the available arguments. * * <p>More complex is if the function is being applied to an expression that does not evaluate as * above, resulting in three cases: Of primary interest is a function node that represents the * invocation of a Java method registered with the expression parser. This method uses the * reflection mechanism in the CachedMethod class to find the correct method, based on the types * of the arguments and invoke it. See that class for information about how method arguments are * matched. * * <p>A second case is the eval() function, which is handled specially in this method. The * argument to the function is evaluated, and the parsed as a string using the expression parser. * The result is then evaluated *in this evaluator*. This has the effect that any identifiers are * evaluated in the same scope as the original expression. * * <p>A third case is the matlab() function, which is also handled specially in this method, * allowing the evaluation of expressions in matlab if matlab is installed. The format of the * function is covered in {@link ptolemy.data.expr.MatlabUtilities#evaluate(String, Set, * ParserScope)} . * * @param node The specified node. * @exception IllegalActionException If an evaluation error occurs. */ public void visitFunctionApplicationNode(ASTPtFunctionApplicationNode node) throws IllegalActionException { // First check to see if the name references a valid variable. ptolemy.data.Token value = null; String functionName = node.getFunctionName(); if ((functionName != null) && (_scope != null)) { value = _scope.get(functionName); } // The first child contains the function name as an id. It is // ignored, and not evaluated unless necessary. int argCount = node.jjtGetNumChildren() - 1; Type[] argTypes = new Type[argCount]; ptolemy.data.Token[] argValues = new ptolemy.data.Token[argCount]; // First try to find a signature using argument token values. for (int i = 0; i < argCount; i++) { // Save the resulting value. _evaluateChild(node, i + 1); ptolemy.data.Token token = _evaluatedChildToken; argValues[i] = token; argTypes[i] = token.getType(); } if ((value != null) || (functionName == null)) { // The value of the first child should be either a FunctionToken, // an ArrayToken, or a MatrixToken. ptolemy.data.Token result; // Evaluate it, if necessary. if (value == null) { value = _evaluateChild(node, 0); } if (value instanceof ArrayToken) { if (argCount == 1) { result = _evaluateArrayIndex(node, value, argValues[0]); } else { // FIXME need better error message when the first child // is, say, an array expression throw new IllegalActionException( "Wrong number of indices " + "when referencing " + node.getFunctionName()); } } else if (value instanceof MatrixToken) { if (argCount == 2) { result = _evaluateMatrixIndex(node, value, argValues[0], argValues[1]); } else { // FIXME need better error message when the first child // is, say, a matrix expression throw new IllegalActionException( "Wrong number of indices " + "when referencing " + node.getFunctionName()); } } else if (value instanceof FunctionToken) { FunctionToken function = (FunctionToken) value; // check number of children against number of arguments of // function if (function.getNumberOfArguments() != argCount) { throw new IllegalActionException( "Wrong number of " + "arguments when applying function " + value.toString()); } result = function.apply(argValues); } else { // FIXME: It might be the a parameter is // shadowing a built-in function, in which // case, thrown an exception seems bogus. // The value cannot be indexed or applied // throw exception. throw new IllegalActionException("Cannot index or apply arguments to " + value.toString()); } _evaluatedChildToken = (result); return; } if (node.getFunctionName().compareTo("eval") == 0) { if (argCount == 1) { ptolemy.data.Token token = argValues[0]; if (token instanceof StringToken) { // Note that we do not want to store a reference to // the parser, because parsers take up alot of memory. PtParser parser = new PtParser(); ASTPtRootNode tree = parser.generateParseTree(((StringToken) token).stringValue()); // Note that we evaluate the recursed parse tree // in the same scope as this parse tree. tree.visit(this); // _evaluatedChildToken = (tree.getToken()); // FIXME cache? return; } } throw new IllegalActionException( "The function \"eval\" is" + " reserved for reinvoking the parser, and takes" + " exactly one String argument."); } if (node.getFunctionName().compareTo("matlab") == 0) { _evaluateChild(node, 1); ptolemy.data.Token token = _evaluatedChildToken; if (token instanceof StringToken) { String expression = ((StringToken) token).stringValue(); ParseTreeFreeVariableCollector collector = new ParseTreeFreeVariableCollector(); Set freeVariables = collector.collectFreeVariables(node, _scope); _evaluatedChildToken = MatlabUtilities.evaluate(expression, freeVariables, _scope); return; } else { throw new IllegalActionException( "The function \"matlab\" is" + " reserved for invoking the matlab engine, and takes" + " a string matlab expression argument followed by" + " a list of variable names that the matlab expression" + " refers to."); } } // If not a special function, then reflect the name of the function. ptolemy.data.Token result = _functionCall(node.getFunctionName(), argTypes, argValues); _evaluatedChildToken = (result); }
/** * Assert that the given boolean value, which describes the given parse tree node, is true. If it * is false, then throw a new InternalErrorException that describes the node and includes the * given message. * * @param flag The flag that is asserted to be true. * @param node The node on which the assertion is asserted. * @param message The message to include in the exception. * @exception InternalErrorException If the assertion is violated. Note that this is a runtime * exception, so it need not be declared explicitly. */ protected void _assert(boolean flag, ASTPtRootNode node, String message) { if (!flag) { throw new InternalErrorException(message + ": " + node.toString()); } }
/** * Infer the type of the parse tree with the specified root node using the specified scope to * resolve the values of variables. * * @param node The root of the parse tree. * @param scope The scope for evaluation. * @return The result of evaluation. * @exception IllegalActionException If an error occurs during evaluation. */ public Type inferTypes(ASTPtRootNode node, ParserScope scope) throws IllegalActionException { _scope = scope; node.visit(this); _scope = null; return _inferredChildType; }
/** * Infer the type of the parse tree with the specified root node. * * @param node The root of the parse tree. * @return The result of evaluation. * @exception IllegalActionException If an evaluation error occurs. */ public Type inferTypes(ASTPtRootNode node) throws IllegalActionException { node.visit(this); return _inferredChildType; }
protected void _setType(ASTPtRootNode node, Type type) { // System.out.println("type of " + node + " is " + type); _inferredChildType = type; node.setType(type); }
/** * Visit the child with the given index of the given node. This is usually called while visiting * the given node. */ protected Type _inferChild(ASTPtRootNode node, int i) throws IllegalActionException { ASTPtRootNode child = (ASTPtRootNode) node.jjtGetChild(i); child.visit(this); return _inferredChildType; }