001/* A visitor for parse trees of the guard expressions.
002
003 Copyright (c) 2003-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.domains.modal.kernel;
029
030import java.util.LinkedList;
031import java.util.Set;
032
033import ptolemy.data.BooleanToken;
034import ptolemy.data.ScalarToken;
035import ptolemy.data.Token;
036import ptolemy.data.expr.ASTPtLeafNode;
037import ptolemy.data.expr.ASTPtLogicalNode;
038import ptolemy.data.expr.ASTPtRelationalNode;
039import ptolemy.data.expr.ASTPtRootNode;
040import ptolemy.data.expr.ParseTreeEvaluator;
041import ptolemy.data.expr.ParseTreeFreeVariableCollector;
042import ptolemy.data.expr.ParserScope;
043import ptolemy.data.expr.PtParserConstants;
044import ptolemy.kernel.util.IllegalActionException;
045
046///////////////////////////////////////////////////////////////////
047//// ParseTreeEvaluatorForGuardExpression
048
049/**
050 This class extends the ParseTreeEvaluator class. It is specially
051 designed for guard expressions associated with transitions of FSM. An object
052 of this class is a visitor that visits a parse tree of a guard expression
053 and evaluates it into a token. Meanwhile, this visitor stores the type and
054 difference of all relations of the guard expression into a relation list.
055 Here a relation means an expression that does not contain a logical operator.
056 <p>
057 This visitor has two modes of operation: <i>construction</i> mode and
058 <i>update mode</i>. During the construction mode, this visitor constructs a
059 relation list where each element of the list corresponds to a relation of
060 the guard expression. The order of the elements is fixed and it is the same
061 as the order that the relations appear in the guard expression. If the guard
062 expression changes, the relation list will be reconstructed. During the
063 update mode, the relation list gets updated only. The order of the elements
064 get updated is the same order the relations of the guard expression get
065 evaluated.
066 <p>
067 When this visitor evaluates the parse tree, if the visiting node is a leaf
068 node and the evaluated token is a boolean token, or the visiting node
069 is a relational node, the visiting node is treated as a relation. The visitor
070 evaluates the 'difference' and 'relationType' of this relation, and stores
071 the evaluation results into the corresponding element in the relation list.
072 <p>
073 The 'difference' of a relation is calculated in the following way.
074 For a leaf node evaluated as a boolean token, the difference is
075 0. This situation corresponds to the "true", or "false", or "x_isPresent"
076 elements in a guard expression. For a relational node with the format
077 (scalarLeft relationOperator scalarRight), the difference is the absolute
078 double value of (scalarLeft - scalarRight). For details of relation type,
079 see {@link RelationType}.
080 <p>
081 If the evaluator is in the construction mode, the relation information is
082 added into the relation list, if it is in the update mode, the corresponding
083 element of the relation List gets updated.
084 <p>
085 Note, this evaluator does not use short-circuit evaluation on
086 logical nodes, meaning all nodes will be evaluated.
087
088 @author Haiyang Zheng
089 @version $Id$
090 @since Ptolemy II 8.0
091 @Pt.ProposedRating Red (hyzheng)
092 @Pt.AcceptedRating Red (hyzheng)
093 @see RelationList
094 @see ptolemy.data.expr.ParseTreeEvaluator
095 */
096public class ParseTreeEvaluatorForGuardExpression extends ParseTreeEvaluator {
097    /** Construct a parse tree evaluator for a guard expression of the
098     *  given relation list and the error tolerance for evaluation the relations.
099     *  The relation stores the information of the relation. After the parse
100     *  tree evaluator is created, it is always in construction mode.
101     *  @param relationList The relation list.
102     *  @param errorTolerance The errorTolerance.
103     */
104    public ParseTreeEvaluatorForGuardExpression(RelationList relationList,
105            double errorTolerance) {
106        _absentDiscreteVariables = new LinkedList<String>();
107        _constructingRelationList = true;
108        _errorTolerance = errorTolerance;
109        _relationIndex = 0;
110        _relationList = relationList;
111        _variableCollector = new ParseTreeFreeVariableCollector();
112    }
113
114    ///////////////////////////////////////////////////////////////////
115    ////                         public methods                    ////
116
117    /** Evaluate the parse tree with the specified root node using
118     *  the specified scope to resolve the values of variables.
119     *  @param node The root of the parse tree.
120     *  @param scope The scope for evaluation.
121     *  @return The result of evaluation.
122     *  @exception IllegalActionException If an error occurs during
123     *   evaluation.
124     */
125    @Override
126    public Token evaluateParseTree(ASTPtRootNode node, ParserScope scope)
127            throws IllegalActionException {
128        _relationIndex = 0;
129        Token result = super.evaluateParseTree(node, scope);
130        if (_constructingRelationList) {
131            _constructingRelationList = false;
132        }
133        return result;
134    }
135
136    /** Return the list of relations (boolean-valued expressions that do not
137     *  contain a logical operator, such as comparison expressions) in the
138     *  guard expression of this transition.
139     *  @return The list of relations.
140     */
141    public RelationList getRelationList() {
142        return _relationList;
143    }
144
145    /** Set parse tree evaluator to be in construction mode.
146     *  Clear the relation list and the relation list
147     *  will be populated, based on the nodes in the parse tree.
148     */
149    public void setConstructionMode() {
150        _constructingRelationList = true;
151        _relationList.destroy();
152    }
153
154    /** Visit the leaf node. It is evaluated the same way as normal parse tree
155     *  evaluator, except that if the the result is a boolean token, the
156     *  information about the node (the difference and relationType) is either
157     *  added into the relation list or used to update the corresponding element
158     *  in the relation list, depending on the evaluator mode.
159     *  @param node The leaf node to be evaluated.
160     *  @exception IllegalActionException If the super class method
161     *  visitLeafNode throws the IllegalActionException.
162     */
163    @Override
164    public void visitLeafNode(ASTPtLeafNode node)
165            throws IllegalActionException {
166
167        // Note: based on the *_isPresent variable, we figure out
168        // the discrete variables and do not evaluate it when it is
169        // not present.
170        String nodeName = node.getName();
171
172        // Check whether this leaf node contains a discrete variable.
173        // If there is a discrete variable, record its name as
174        // the discreteVariableName.
175        String discreteVariableName = "";
176        if (nodeName != null) {
177            int variableNameEndIndex = nodeName.indexOf("_isPresent");
178            if (variableNameEndIndex != -1) {
179                discreteVariableName = nodeName.substring(0,
180                        variableNameEndIndex);
181            }
182        }
183
184        // Note usually the usage is "x_isPresent && x" or
185        // "x_isPresent" If we know that the nodeName is one of the
186        // absent discrete variables, such as x, we do not evaluate
187        // the "x" after the "&&".
188        if (_absentDiscreteVariables.contains(nodeName)) {
189            // Set the result token to be false token
190            // because the variable is discrete and has no value.
191            _evaluatedChildToken = new BooleanToken(false);
192            // If the current mode of the evaluator is the construction mode,
193            // add a relation node into the relation list.
194            if (_constructingRelationList) {
195                _relationList.addRelation(RelationType.INVALID, 0.0);
196            } else {
197                // Only increment the relation index but do not update
198                // the relation node.
199                _relationIndex++;
200                // Round the _relationIndex.
201                if (_relationIndex >= _relationList.length()) {
202                    _relationIndex -= _relationList.length();
203                }
204            }
205            return;
206        }
207
208        // evaluate the leaf node.
209        super.visitLeafNode(node);
210
211        // Record the evaluation result.
212        ptolemy.data.Token result = _evaluatedChildToken;
213        // Ignore the result that is not a boolean token, which may be a scalar.
214        if (!(result instanceof BooleanToken)) {
215            return;
216        }
217
218        // If the result is a boolean token, calculate the relation type.
219        // Meanwhile, if the nodeName is "x_isPresent", the discreteVariableName
220        // is "x", based on the evaluation results of the node, we add or remove
221        // the discrete variable from the list of absent discrete variables.
222        if (((BooleanToken) result).booleanValue()) {
223            _relationType = RelationType.TRUE;
224            if (_absentDiscreteVariables.contains(discreteVariableName)) {
225                // remove the discrete variable from the absent discrete variables list
226                _absentDiscreteVariables.remove(discreteVariableName);
227            }
228        } else {
229            _relationType = RelationType.FALSE;
230            if (!_absentDiscreteVariables.contains(discreteVariableName)) {
231                // add the discrete variable into the absent discrete variables list
232                _absentDiscreteVariables.add(discreteVariableName);
233            }
234        }
235
236        // In this case, the difference is always 0.0.
237        _difference = 0.0;
238        if (_constructingRelationList) {
239            _relationList.addRelation(_relationType, _difference);
240        } else {
241            _relationList.setRelation(_relationIndex, _relationType,
242                    _difference);
243            _relationIndex++;
244            // Round the _relationIndex.
245            if (_relationIndex >= _relationList.length()) {
246                _relationIndex -= _relationList.length();
247            }
248        }
249    }
250
251    /** Visit the logical node. This visitor does not use short-circuit
252     *  evaluation. It evaluates both the nodes beside the logical operator.
253     *  @param node The logical node to be evaluated.
254     *  @exception IllegalActionException If the super class method
255     *  visitLogicalNode throws the IllegalActionException.
256     */
257    @Override
258    public void visitLogicalNode(ASTPtLogicalNode node)
259            throws IllegalActionException {
260
261        // If the node is a constant and has been evaluated, do nothing.
262        if (node.isConstant() && node.isEvaluated()) {
263            return;
264        }
265
266        // Note that we do evaluate all the children nodes...
267        // We evaluate ALL the children in order until the final value is
268        // determined.
269        // This is the reason that we can not use the visitLogicalNode() method
270        // of the supr class directly.
271        int numChildren = node.jjtGetNumChildren();
272        _assert(numChildren > 0, node,
273                "The number of child nodes must be greater than zero");
274        ptolemy.data.Token result = _evaluateChild(node, 0);
275        if (!(result instanceof BooleanToken)) {
276            throw new IllegalActionException(
277                    "Cannot perform logical " + "operation on " + result
278                            + " which is a " + result.getClass().getName());
279        }
280
281        // Make sure that exactly one of AND or OR is set.
282        _assert(node.isLogicalAnd() ^ node.isLogicalOr(), node,
283                "Invalid operation");
284        boolean flag = node.isLogicalAnd();
285        for (int i = 1; i < numChildren; i++) {
286            // Evaluate the child
287            ptolemy.data.Token nextToken = _evaluateChild(node, i);
288            if (!(nextToken instanceof BooleanToken)) {
289                throw new IllegalActionException(
290                        "Cannot perform logical " + "operation on " + nextToken
291                                + " which is a " + result.getClass().getName());
292            }
293            if (flag) {
294                result = ((BooleanToken) nextToken).and((BooleanToken) result);
295            } else {
296                result = ((BooleanToken) nextToken).or((BooleanToken) result);
297            }
298        }
299        _evaluatedChildToken = result;
300    }
301
302    /** Visit the relation node. The evaluation part is the same as normal
303     *  parseTreeEvaluator, except that information about each relation (the
304     *  difference and relationType) is either added into the relation list or
305     *  used to update the corresponding element in the relation list, depending
306     *  on the evaluator mode.
307     *  @param node The relation node to be evaluated.
308     *  @exception IllegalActionException If the super class method
309     *  visitRelationNode throws the IllegalActionException.
310     */
311    @Override
312    public void visitRelationalNode(ASTPtRelationalNode node)
313            throws IllegalActionException {
314
315        // If the node is a constant and has been evaluated, do nothing.
316        if (node.isConstant() && node.isEvaluated()) {
317            return;
318        }
319
320        // Check whether the relation node has some absent discrete variables.
321        // If yes, skip the node, otherwise, evaluate (visit) the node.
322        // For example, if we have "x_isPresent && x < 10.0", in the
323        // visitLeafNode() method, we should know that x is either present or
324        // absent. If x is absent, we do not evaluate the "x < 10.0" part here.
325        Set<?> variablesOfNode = _variableCollector.collectFreeVariables(node);
326
327        for (String variableName : _absentDiscreteVariables) {
328            if (variablesOfNode.contains(variableName)) {
329                // Set the result token to be false token
330                // because the variable is discrete and has no value.
331                // Note usually the usage is "x_isPresent && x == 1.0"
332                _evaluatedChildToken = new BooleanToken(false);
333                if (_constructingRelationList) {
334                    _relationList.addRelation(RelationType.INVALID, 0.0);
335                } else {
336                    // Only update _relationIndex but do not change relation node.
337                    _relationIndex++;
338                    if (_relationIndex >= _relationList.length()) {
339                        _relationIndex -= _relationList.length();
340                    }
341                }
342                return;
343            }
344        }
345
346        ptolemy.data.Token[] tokens = _evaluateAllChildren(node);
347
348        int numChildren = node.jjtGetNumChildren();
349        _assert(numChildren == 2, node,
350                "The number of child nodes must be two");
351        ptolemy.data.expr.Token operator = node.getOperator();
352        ptolemy.data.Token leftToken = tokens[0];
353        ptolemy.data.Token rightToken = tokens[1];
354
355        ptolemy.data.Token result;
356        if (operator.kind == PtParserConstants.EQUALS
357                || operator.kind == PtParserConstants.NOTEQUALS) {
358            // If the operator is about equal or not-equal relations,
359            if (operator.kind == PtParserConstants.EQUALS) {
360                result = leftToken.isCloseTo(rightToken, _errorTolerance);
361            } else {
362                result = leftToken.isCloseTo(rightToken, _errorTolerance).not();
363            }
364            // If both the left and right tokens are scalars:
365            // The following code basically works as a level crossing detector
366            // that detects level crossing in both rising and falling
367            // directions.
368            // Note we cannot tell whether two double values exactly equal,
369            // therefore, we need an error tolerance. This is the only place
370            // where error tolerance is used.
371            // Note that subtraction is not supported for BooleanToken,
372            // unlike other scalars.
373            if (leftToken instanceof ScalarToken
374                    && rightToken instanceof ScalarToken
375                    && !(leftToken instanceof BooleanToken)) {
376                // handle the relations like x == 2.0
377                ScalarToken difference = (ScalarToken) leftToken
378                        .subtract(rightToken);
379                if (((BooleanToken) result).booleanValue()) {
380                    _relationType = RelationType.EQUAL_INEQUAL;
381                } else {
382                    if (difference.doubleValue() < 0) {
383                        _relationType = RelationType.LESS_THAN;
384                    } else {
385                        _relationType = RelationType.GREATER_THAN;
386                    }
387                }
388                _difference = difference.doubleValue();
389            } else {
390                // handle the relations like x == true, x == "str", or x!= false
391                if (((BooleanToken) result).booleanValue()) {
392                    _relationType = RelationType.TRUE;
393                } else {
394                    _relationType = RelationType.FALSE;
395                }
396                _difference = 0.0;
397            }
398        } else {
399            // If the operator is neither about equal nor not-equal relations,
400            // both tokens must be scalar tokens.
401            if (!(leftToken instanceof ScalarToken
402                    && rightToken instanceof ScalarToken)) {
403                throw new IllegalActionException("The " + operator.image
404                        + " operator can only be applied between scalars and not "
405                        + "between a " + leftToken.getType() + " and a "
406                        + rightToken.getType() + ".");
407            }
408            ScalarToken leftScalar = (ScalarToken) leftToken;
409            ScalarToken rightScalar = (ScalarToken) rightToken;
410            // A relation needs strictly satisfied.
411            if (operator.kind == PtParserConstants.GTE) {
412                result = leftScalar.isLessThan(rightScalar).not();
413            } else if (operator.kind == PtParserConstants.GT) {
414                result = rightScalar.isLessThan(leftScalar);
415            } else if (operator.kind == PtParserConstants.LTE) {
416                result = rightScalar.isLessThan(leftScalar).not();
417            } else if (operator.kind == PtParserConstants.LT) {
418                result = leftScalar.isLessThan(rightScalar);
419            } else {
420                throw new IllegalActionException(
421                        "Invalid operation " + operator.image + " between "
422                                + leftToken.getClass().getName() + " and "
423                                + rightToken.getClass().getName());
424            }
425            if (((BooleanToken) result).booleanValue()) {
426                _relationType = RelationType.TRUE;
427            } else {
428                _relationType = RelationType.FALSE;
429            }
430            _difference = ((ScalarToken) leftScalar.subtract(rightScalar))
431                    .doubleValue();
432        }
433
434        _difference = Math.abs(_difference);
435        if (_constructingRelationList) {
436            _relationList.addRelation(_relationType, _difference);
437        } else {
438            _relationList.setRelation(_relationIndex, _relationType,
439                    _difference);
440            _relationIndex++;
441            if (_relationIndex >= _relationList.length()) {
442                _relationIndex -= _relationList.length();
443            }
444        }
445
446        _evaluatedChildToken = result;
447        return;
448    }
449
450    ///////////////////////////////////////////////////////////////////
451    ////                         private variables                 ////
452
453    // The list of discrete variables without values.
454    private LinkedList<String> _absentDiscreteVariables;
455
456    // Flag to indicate the parse tree evaluator in construction mode
457    // of update mode.
458    private boolean _constructingRelationList;
459
460    // The metric for relations.
461    private double _difference;
462
463    // The error tolerance.
464    private double _errorTolerance;
465
466    // private relation node index
467    private int _relationIndex;
468
469    // The list to store the relation nodes and leaf nodes with
470    // boolean tokens inside a guard expression.
471    private RelationList _relationList;
472
473    // the relation types have 6 integer values with meaning:
474    // 0: invalid; 1: true; 2: false; 3: equal/inequal; 4: less_than: 5: bigger_than.
475    private int _relationType;
476
477    // variable collector
478    private ParseTreeFreeVariableCollector _variableCollector;
479}