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}