001/* A visitor for parse trees of the expression language.
002
003 Copyright (c) 1998-2014 The Regents of the University of California
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA OR RESEARCH IN MOTION
012 LIMITED BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL,
013 INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS
014 SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA
015 OR RESEARCH IN MOTION LIMITED HAVE BEEN ADVISED OF THE POSSIBILITY OF
016 SUCH DAMAGE.
017
018 THE UNIVERSITY OF CALIFORNIA AND RESEARCH IN MOTION LIMITED
019 SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
021 PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
022 BASIS, AND THE UNIVERSITY OF CALIFORNIA AND RESEARCH IN MOTION
023 LIMITED HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
024 ENHANCEMENTS, OR MODIFICATIONS.
025
026
027 */
028package ptolemy.data.expr;
029
030import java.util.ArrayList;
031import java.util.HashMap;
032import java.util.List;
033import java.util.Map;
034
035import ptolemy.data.Function;
036import ptolemy.data.type.BaseType;
037import ptolemy.data.type.Type;
038import ptolemy.kernel.util.IllegalActionException;
039
040///////////////////////////////////////////////////////////////////
041//// ExpressionFunction
042
043/**
044 An implementation of a function closure that encapsulates an
045 expression tree.  Instances of this class are created during the
046 evaluation of function closure expressions in the expression language,
047 like "function(x:int, y:int) x+y".
048
049 @author Steve Neuendorffer, Xiaojun Liu
050 @version $Id$
051 @since Ptolemy II 2.1
052 @Pt.ProposedRating Yellow (neuendor)
053 @Pt.AcceptedRating Red (neuendor)
054 @see ptolemy.data.expr.ASTPtRootNode
055 */
056public class ExpressionFunction implements Function {
057    /** Construct a function closure that encapsulates an expression tree.
058     *  @param argumentNames The names of the arguments.
059     *  @param argumentTypes The types of the arguments.
060     *  @param exprRoot The Expression tree that describes the function.
061     */
062    public ExpressionFunction(List argumentNames, Type[] argumentTypes,
063            ASTPtRootNode exprRoot) {
064        _argumentNames = new ArrayList(argumentNames);
065        _argumentTypes = argumentTypes;
066        _exprRoot = exprRoot;
067    }
068
069    ///////////////////////////////////////////////////////////////////
070    ////                         public methods                    ////
071
072    /** Apply the function to the list of arguments, which are tokens.
073     *  @param arguments The list of arguments.
074     *  @return The result of applying the function to the given
075     *   arguments.
076     *  @exception IllegalActionException If thrown during evaluating
077     *   the function.
078     */
079    @Override
080    public ptolemy.data.Token apply(ptolemy.data.Token[] arguments)
081            throws IllegalActionException {
082        ParseTreeEvaluator parseTreeEvaluator = new ParseTreeEvaluator();
083
084        // construct a NamedConstantsScope that contains mappings from
085        // argument names to the given argument values
086        Map map = new HashMap();
087
088        for (int i = 0; i < arguments.length; ++i) {
089            String name = (String) _argumentNames.get(i);
090            ptolemy.data.Token argument = arguments[i];
091            map.put(name, argument);
092
093            // Below is technically correct, but it prevents well typed
094            // recursive function definitions.  I'm sure there's a well
095            // known solution to this, but I don't have the time to figure it
096            // out at the moment.
097            //      map.put(name, _argumentTypes[i].convert(argument));
098        }
099
100        NamedConstantsScope argumentsScope = new NamedConstantsScope(map);
101        return parseTreeEvaluator.evaluateParseTree(_exprRoot, argumentsScope);
102    }
103
104    /** Return the number of arguments of the function.
105     *  @return The number of arguments of the function.
106     */
107    @Override
108    public int getNumberOfArguments() {
109        return _argumentNames.size();
110    }
111
112    /** Return true if this function is congruent to the given
113     *  function.  Classes should implement this method so that
114     *  two functions are congruent under any renaming of the
115     *  bound variables of the function.  For simplicity, a
116     *  function need only be congruent to other functions of the
117     *  same class.
118     *  @param function The function to check congruency against.
119     *  @return True if this function is congruent.
120     */
121    @Override
122    public boolean isCongruent(Function function) {
123        return toString().compareTo(function.toString()) == 0;
124
125        // FIXME: The above is not terribly nice...  It would be nice
126        // to allow function equivalence under bound variable
127        // renaming.  However, I got stuck trying to implement this,
128        // and decided I didn't want to spend any more time on it...
129        // SN - 4/18/2003
130
131        /**
132         if (!(function instanceof ExpressionFunction)) {
133         return false;
134         }
135         ExpressionFunction expressionFunction = (ExpressionFunction)function;
136         // The functions must have the same number of arguments.
137         if (getNumberOfArguments() != function.getNumberOfArguments()) {
138         return false;
139         }
140         // Construct the renaming map.
141         Map renaming = new HashMap();
142         Iterator argNames = expressionFunction._argumentNames.iterator();
143         for (Iterator names = _argumentNames.iterator();
144         names.hasNext();) {
145         String name = (String)names.next();
146         String argName = (String)argNames.next();
147         renaming.put(name, argName);
148         }
149         return _exprRoot.isCongruent(expressionFunction._exprRoot,
150         renaming);
151         */
152    }
153
154    /** Return a string representation of this function.
155     */
156    @Override
157    public String toString() {
158        StringBuffer buffer = new StringBuffer("(function(");
159        int n = _argumentNames.size();
160
161        for (int i = 0; i < n; i++) {
162            if (i > 0) {
163                buffer.append(", ");
164            }
165
166            buffer.append((String) _argumentNames.get(i));
167
168            Type type = _argumentTypes[i];
169
170            if (type != BaseType.GENERAL) {
171                buffer.append(":");
172                buffer.append(type.toString());
173            }
174        }
175
176        buffer.append(") ");
177
178        ParseTreeWriter writer = new ParseTreeWriter();
179        String string = writer.printParseTree(_exprRoot);
180        buffer.append(string);
181        buffer.append(")");
182        return buffer.toString();
183    }
184
185    ///////////////////////////////////////////////////////////////////
186    ////                         private variables                 ////
187    // The root of the expression tree.
188    private ASTPtRootNode _exprRoot;
189
190    // The list of argument names that are bound in this function closure.
191    private List _argumentNames;
192
193    // The list of the (monomorphic) types of the arguments.
194    private Type[] _argumentTypes;
195}