/**
  * 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;
  }
Example #3
0
  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;
 }