001/* A visitor for parse trees of the expression language that infers types.
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 BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024
025 */
026package ptolemy.data.expr;
027
028import java.util.LinkedList;
029import java.util.List;
030
031import ptolemy.data.ObjectToken;
032import ptolemy.kernel.util.IllegalActionException;
033
034///////////////////////////////////////////////////////////////////
035//// ParseTreeSpecializer
036
037/**
038 This class reduces a parse tree, given a scope of bound variables.  If
039 an identifier is not found in the given scope, then the identifier is
040 bound to any constants registered with the expression parser.  If any
041 subtrees of the parse tree become constant, they are evaluated and
042 replaced with leaf nodes containing the evaluated result.
043
044 @author Steve Neuendorffer
045 @version $Id$
046 @since Ptolemy II 2.1
047 @Pt.ProposedRating Red (neuendor)
048 @Pt.AcceptedRating Red (cxh)
049 @see ptolemy.data.expr.ASTPtRootNode
050 */
051public class ParseTreeSpecializer extends AbstractParseTreeVisitor {
052    ///////////////////////////////////////////////////////////////////
053    ////                         public methods                    ////
054
055    /** Return a new parse tree resulting from the specialization of
056     *  the given parse tree.  Every identifier reference is replaced
057     *  by constants according to the given scope.  Constant subtrees
058     *  are replaced with constant leaf nodes.  Exclude the given set
059     *  of names from being replaced.  The given parse tree is not
060     *  destroyed in the process.
061     *  @param node The node
062     *  @param excludedNames the names to be excluded
063     *  @param scope The scope
064     *  @return The new parse tree
065     *  @exception IllegalActionException If the node can't be cloned
066     *  or if thrown while visiting.
067     */
068    public ASTPtRootNode specialize(ASTPtRootNode node, List excludedNames,
069            ParserScope scope) throws IllegalActionException {
070        _excludedNames = excludedNames;
071        _scope = scope;
072        _evaluator = new ParseTreeEvaluator();
073
074        try {
075            _result = (ASTPtRootNode) node.clone();
076            _result._parent = null;
077        } catch (CloneNotSupportedException ex) {
078            throw new IllegalActionException(null, ex,
079                    "Failed to clone node for specialization");
080        }
081
082        _result.visit(this);
083        _evaluator = null;
084        _scope = null;
085        _excludedNames = null;
086
087        ASTPtRootNode result = _result;
088        _result = null;
089        return result;
090    }
091
092    @Override
093    public void visitArrayConstructNode(ASTPtArrayConstructNode node)
094            throws IllegalActionException {
095        _defaultVisit(node);
096    }
097
098    @Override
099    public void visitBitwiseNode(ASTPtBitwiseNode node)
100            throws IllegalActionException {
101        _defaultVisit(node);
102    }
103
104    @Override
105    public void visitFunctionApplicationNode(ASTPtFunctionApplicationNode node)
106            throws IllegalActionException {
107        // Check to see if we are referencing a function closure in scope.
108        ptolemy.data.Token value = null;
109        String functionName = node.getFunctionName();
110
111        if (_scope != null && functionName != null) {
112            if (!_excludedNames.contains(functionName)) {
113                value = _scope.get(node.getFunctionName());
114                if (value instanceof ObjectToken) {
115                    // Do not specialize ObjectToken.
116                    value = null;
117                }
118            }
119        }
120
121        if (value == null) {
122            // Just visit arguments other than the first.
123            int numChildren = node.jjtGetNumChildren();
124
125            for (int i = 1; i < numChildren; i++) {
126                _visitChild(node, i);
127            }
128        } else {
129            _defaultVisit(node);
130        }
131    }
132
133    @Override
134    public void visitFunctionDefinitionNode(ASTPtFunctionDefinitionNode node)
135            throws IllegalActionException {
136        List excludedNames = new LinkedList(_excludedNames);
137
138        // Don't substitute any names in the parse tree that are
139        // bound in the definition.
140        excludedNames.addAll(node.getArgumentNameList());
141
142        List oldExcludedNames = _excludedNames;
143        _excludedNames = excludedNames;
144
145        // Recurse, with the new set of bound identifiers.
146        node.getExpressionTree().visit(this);
147
148        _excludedNames = oldExcludedNames;
149    }
150
151    @Override
152    public void visitFunctionalIfNode(ASTPtFunctionalIfNode node)
153            throws IllegalActionException {
154        _defaultVisit(node);
155    }
156
157    @Override
158    public void visitLeafNode(ASTPtLeafNode node)
159            throws IllegalActionException {
160        if (node.isConstant() && node.isEvaluated()) {
161            return;
162        }
163
164        if (!_excludedNames.contains(node.getName())) {
165            ptolemy.data.Token token = null;
166
167            if (_scope != null) {
168                token = _scope.get(node.getName());
169            }
170
171            if (token == null) {
172                token = Constants.get(node.getName());
173            }
174
175            if (token != null) {
176                node.setToken(token);
177                node.setConstant(true);
178
179                // Reset the name, since it no longer makes sense.
180                node._name = null;
181                return;
182            }
183
184            throw new IllegalActionException(
185                    "The ID " + node.getName() + " is undefined.");
186        }
187    }
188
189    @Override
190    public void visitLogicalNode(ASTPtLogicalNode node)
191            throws IllegalActionException {
192        _defaultVisit(node);
193    }
194
195    @Override
196    public void visitMatrixConstructNode(ASTPtMatrixConstructNode node)
197            throws IllegalActionException {
198        _defaultVisit(node);
199    }
200
201    @Override
202    public void visitMethodCallNode(ASTPtMethodCallNode node)
203            throws IllegalActionException {
204        _defaultVisit(node);
205    }
206
207    @Override
208    public void visitPowerNode(ASTPtPowerNode node)
209            throws IllegalActionException {
210        _defaultVisit(node);
211    }
212
213    @Override
214    public void visitProductNode(ASTPtProductNode node)
215            throws IllegalActionException {
216        _defaultVisit(node);
217    }
218
219    @Override
220    public void visitRecordConstructNode(ASTPtRecordConstructNode node)
221            throws IllegalActionException {
222        _defaultVisit(node);
223    }
224
225    @Override
226    public void visitRelationalNode(ASTPtRelationalNode node)
227            throws IllegalActionException {
228        _defaultVisit(node);
229    }
230
231    @Override
232    public void visitShiftNode(ASTPtShiftNode node)
233            throws IllegalActionException {
234        _defaultVisit(node);
235    }
236
237    @Override
238    public void visitSumNode(ASTPtSumNode node) throws IllegalActionException {
239        _defaultVisit(node);
240    }
241
242    @Override
243    public void visitUnaryNode(ASTPtUnaryNode node)
244            throws IllegalActionException {
245        _defaultVisit(node);
246    }
247
248    ///////////////////////////////////////////////////////////////////
249    ////                         protected methods                 ////
250
251    /** Return true if all of the children of this node are constant.
252     *  @param node the node
253     *  @return True if all of the children of this node are constant.
254     */
255    protected boolean _childrenAreConstant(ASTPtRootNode node) {
256        int numChildren = node.jjtGetNumChildren();
257
258        for (int i = 0; i < numChildren; i++) {
259            ASTPtRootNode child = (ASTPtRootNode) node.jjtGetChild(i);
260
261            if (!child.isConstant()) {
262                return false;
263            }
264        }
265
266        return true;
267    }
268
269    protected void _defaultVisit(ASTPtRootNode node)
270            throws IllegalActionException {
271        _visitAllChildren(node);
272
273        boolean isConstant = _childrenAreConstant(node);
274
275        if (isConstant) {
276            _replaceConstantNode(node);
277        }
278    }
279
280    protected void _replaceConstantNode(ASTPtRootNode node)
281            throws IllegalActionException {
282        // Create the replacement
283        ASTPtLeafNode newNode = new ASTPtLeafNode(
284                PtParserTreeConstants.JJTPTLEAFNODE);
285        ptolemy.data.Token token = _evaluator.evaluateParseTree(node, _scope);
286        newNode.setToken(token);
287        newNode.setType(token.getType());
288        newNode.setConstant(true);
289
290        ASTPtRootNode parent = (ASTPtRootNode) node._parent;
291
292        if (parent == null) {
293            _result = newNode;
294        } else {
295            // Replace the old with the new.
296            newNode._parent = parent;
297
298            int index = parent._children.indexOf(node);
299            parent._children.set(index, newNode);
300        }
301    }
302
303    protected List _excludedNames;
304
305    protected ASTPtRootNode _result;
306
307    protected ParserScope _scope;
308
309    protected ParseTreeEvaluator _evaluator;
310}