001/* PtRootNode is the root node of the parse tree. It is also the base
002 class for all Ptolemy II nodes as each node is a root node for the
003 tree below it.
004
005 Copyright (c) 1998-2015 The Regents of the University of California.
006 All rights reserved.
007 Permission is hereby granted, without written agreement and without
008 license or royalty fees, to use, copy, modify, and distribute this
009 software and its documentation for any purpose, provided that the above
010 copyright notice and the following two paragraphs appear in all copies
011 of this software.
012
013 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
014 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
015 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
016 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
017 SUCH DAMAGE.
018
019 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
020 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
021 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
022 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
023 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
024 ENHANCEMENTS, OR MODIFICATIONS.
025
026 PT_COPYRIGHT_VERSION_2
027 COPYRIGHTENDKEY
028
029
030 Created : May 1998
031
032 */
033package ptolemy.data.expr;
034
035import java.util.ArrayList;
036import java.util.Iterator;
037import java.util.Map;
038
039import ptolemy.kernel.util.IllegalActionException;
040
041///////////////////////////////////////////////////////////////////
042//// ASTPtRootNode
043
044/**
045 The parse tree created from the expression string consists of a
046 hierarchy of node objects, each of which is an instance of a class
047 derived from this class. This is because each node is a root node for
048 the portion of the parse tree below it.
049 <p>
050 Each node in the parse tree stores its type and state information
051 in a ptolemy.data.Token variable. A parent node uses the type and value of
052 the ptolemy.data.Tokens contained in its child nodes to evaluate the type
053 and value of the ptolemy.data.Token it should contain.
054 <P>
055 When a node has more than one child nodes, the lexical tokens relating
056 the child nodes are stored in the parent node. Thus if we parsed a string
057 such as "2+4-9", the child nodes would be leaf nodes containing
058 ptolemy.data.Token's with values 2, 4 and 9, and the parent node would
059 store the lexical tokens representing the "+" and the "-".
060 <p>
061 The tree is evaluated in a top down manner, calling evaluateParseTree() on the
062 children of each node before resolving the type of the current node.
063
064 @author Neil Smyth
065 @version $Id$
066 @since Ptolemy II 0.2
067 @Pt.ProposedRating Yellow (nsmyth)
068 @Pt.AcceptedRating Red (cxh)
069 @see ptolemy.data.expr.PtParser
070 @see ptolemy.data.Token
071 */
072public class ASTPtRootNode implements Node, Cloneable {
073    public ASTPtRootNode(int i) {
074        _id = i;
075    }
076
077    public ASTPtRootNode(PtParser p, int i) {
078        this(i);
079    }
080
081    ///////////////////////////////////////////////////////////////////
082    ////                         public methods                    ////
083
084    /** Clone the parse tree node by invoking the clone() method of
085     *  the base class (java.lang.Object).
086     *  @return A new parse tree node.
087     *  @exception CloneNotSupportedException If the superclass clone()
088     *   method throws it.
089     */
090    @Override
091    public Object clone() throws CloneNotSupportedException {
092        ASTPtRootNode node = (ASTPtRootNode) super.clone();
093
094        if (_children != null) {
095            node._children = new ArrayList(_children.size());
096
097            // Deeply clone all the children.
098            for (Iterator i = _children.iterator(); i.hasNext();) {
099                ASTPtRootNode child = (ASTPtRootNode) i.next();
100                ASTPtRootNode clone = (ASTPtRootNode) child.clone();
101                node._children.add(clone);
102                clone._parent = node;
103            }
104        }
105
106        return node;
107    }
108
109    /** Override this method if you want to customize how the node dumps
110     *  out its children.
111     *  @param prefix The prefix, which is processed by
112     *  {@link #toString(String)}.
113     */
114    public void displayParseTree(String prefix) {
115        if (_ptToken != null) {
116            String str = toString(prefix) + ", Token type: ";
117            str = str + _ptToken.getClass().getName() + ", Value: ";
118            System.out.println(str + _ptToken.toString());
119        } else {
120            System.out.println(toString(prefix) + "  _ptToken is null");
121        }
122
123        if (_children != null) {
124            for (int i = 0; i < _children.size(); ++i) {
125                ASTPtRootNode n = (ASTPtRootNode) _children.get(i);
126
127                if (n != null) {
128                    n.displayParseTree(prefix + " ");
129                }
130            }
131        }
132    }
133
134    /** Evaluate the parse tree.
135     *  @exception IllegalActionException If an error occurs
136     *  trying to evaluate the PtToken type and/or value to be stored in
137     *  node in the tree.
138     *  @return The token contained by the root node for the parse tree.
139     *  @deprecated Use a ParseTreeEvaluator instead.
140     */
141    @Deprecated
142    public ptolemy.data.Token evaluateParseTree()
143            throws IllegalActionException {
144        ParseTreeEvaluator evaluator = new ParseTreeEvaluator();
145        return evaluator.evaluateParseTree(this);
146    }
147
148    /** Return the evaluated token value of this node.  This value may be
149     *  set during parsing, if this represents a constant value, or may be
150     *  set during parse tree evaluation.
151     *  @return The evaluated token.
152     */
153    public ptolemy.data.Token getToken() {
154        return _ptToken;
155    }
156
157    /** Return the type of this node.  This value may be set during
158     *  parsing, if this represents a constant value, or may be set
159     *  during parse tree evaluation.
160     *  @return The type of this node.
161     */
162    public ptolemy.data.type.Type getType() {
163        return _ptType;
164    }
165
166    /** Return true if this node is (hierarchically) congruent to the
167     *  given node, under the given renaming of bound identifiers.
168     *  Derived classes should extend this method to add additional
169     *  necessary congruency checks.
170     *  @param node The node to compare to.
171     *  @param renaming A map from String to String that gives a
172     *  renaming from identifiers in this node to identifiers in the
173     *  given node.
174     *  @return True if the node is congruent.
175     */
176    public boolean isCongruent(ASTPtRootNode node, Map renaming) {
177        // Check to see that they are the same kind of node.
178        if (node._id != _id) {
179            return false;
180        }
181
182        // Empty children are allowed
183        if (node._children == null && _children == null) {
184            return true;
185        }
186
187        // But both must be empty
188        if (node._children == null || _children == null) {
189            return false;
190        }
191
192        // Check that they have the same number of children.
193        if (node._children.size() != _children.size()) {
194            return false;
195        }
196
197        // Check that their children are congruent.
198        Iterator children = _children.iterator();
199        Iterator nodeChildren = node._children.iterator();
200
201        while (children.hasNext()) {
202            ASTPtRootNode child = (ASTPtRootNode) children.next();
203            ASTPtRootNode nodeChild = (ASTPtRootNode) nodeChildren.next();
204
205            if (!child.isCongruent(nodeChild, renaming)) {
206                return false;
207            }
208        }
209
210        return true;
211    }
212
213    /** Return true if this node represents a constant value.  This will
214     *  be set to true if the node is a constant leaf node (either it is a
215     *  constant leaf node, or a pure function with constant children.)
216     *  @return True if the node represents a constant value.
217     */
218    public boolean isConstant() {
219        return _isConstant;
220    }
221
222    /** Return true if this node has had its token value set to something
223     *  other than null.
224     *  @return True if this node has had its token value set to something
225     *  other than null.
226     */
227    public boolean isEvaluated() {
228        return _ptToken != null;
229    }
230
231    @Override
232    public void jjtAddChild(Node n, int i) {
233        if (_children == null) {
234            _children = new ArrayList();
235        }
236
237        if (i >= _children.size()) {
238            while (_children.size() <= i) {
239                _children.add(null);
240            }
241        }
242
243        _children.set(i, n);
244    }
245
246    @Override
247    public void jjtClose() {
248        if (_children != null) {
249            // Trim the list of children, to reduce memory usage.
250            _children.trimToSize();
251
252            // Check to see if this node is constant, i.e. it has
253            // only constant children.
254            _isConstant = true;
255
256            for (int i = 0; i < _children.size(); ++i) {
257                ASTPtRootNode ch = (ASTPtRootNode) jjtGetChild(i);
258
259                if (!ch._isConstant) {
260                    _isConstant = false;
261                    break;
262                }
263            }
264        }
265    }
266
267    @Override
268    public Node jjtGetChild(int i) {
269        return (Node) _children.get(i);
270    }
271
272    @Override
273    public int jjtGetNumChildren() {
274        return _children == null ? 0 : _children.size();
275    }
276
277    @Override
278    public Node jjtGetParent() {
279        return _parent;
280    }
281
282    @Override
283    public void jjtOpen() {
284    }
285
286    @Override
287    public void jjtSetParent(Node n) {
288        _parent = n;
289    }
290
291    /** Set whether this node is a constant.  In almost all cases this
292     *  is statically determined when the parse tree is first parsed,
293     *  and this method is not normally called.  However, it can be
294     *  useful to transform a parse tree, which can make parts of the
295     *  parse tree constant which were not before.  This method is
296     *  provided for those transformations.
297     *  @param flag If true, then this node is a constant.
298     */
299    public void setConstant(boolean flag) {
300        _isConstant = flag;
301    }
302
303    /** Set the value of this node.  This may be set during parsing,
304     *  if the node is a constant node, or during evaluation of the
305     *  expression.
306     *  @param token The value of this node.
307     */
308    public void setToken(ptolemy.data.Token token) {
309        _ptToken = token;
310    }
311
312    /** Set the type of this node.  This may be set during parsing,
313     *  if the node is a constant node, or during evaluation of the
314     *  expression.
315     *  @param type The type of this node.
316     */
317    public void setType(ptolemy.data.type.Type type) {
318        _ptType = type;
319    }
320
321    /** Return the string value of this node.
322     * You can override these two methods in subclasses of RootNode
323     * to customize the way the node appears when the tree is dumped.
324     * If your output uses more than one line you should override
325     * toString(String), otherwise overriding toString() is probably
326     * all you need to do.
327     * @return The string value of this node.
328     */
329    @Override
330    public String toString() {
331        return PtParserTreeConstants.jjtNodeName[_id] + ":" + _isConstant + ":"
332                + _ptType + ":" + _ptToken;
333    }
334
335    /** Return the prefix, followed by the string value of this node.
336     *  @param prefix The prefix
337     *  @return The prefix followed by the string value of this node.
338     */
339    public String toString(String prefix) {
340        return prefix + toString();
341    }
342
343    /** Traverse this node with the given visitor.
344     *  Subclasses should override this method to invoke the appropriate
345     *  method in the visitor.
346     *  @param visitor The visitor.
347     *  @exception IllegalActionException Always thrown in this base
348     *  class the visit() method is not implemented here.
349     */
350    public void visit(ParseTreeVisitor visitor) throws IllegalActionException {
351        throw new IllegalActionException(
352                "The visit() method is not " + " implemented for nodes of type "
353                        + getClass().getName() + ".");
354    }
355
356    ///////////////////////////////////////////////////////////////////
357    ////                         protected variables               ////
358
359    /** The parent node. */
360    protected Node _parent;
361
362    /** The children. */
363    protected ArrayList _children;
364
365    /** The id. */
366    protected int _id;
367
368    /** Each node stores its type and state information in this variable.
369     */
370    protected ptolemy.data.Token _ptToken;
371
372    /** The type of this node.
373     */
374    protected ptolemy.data.type.Type _ptType;
375
376    /** Flags whether the parse tree under this root evaluates to a constant.
377     */
378    protected boolean _isConstant = false;
379}