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}