/* A visitor for parse trees of the expression language that infers types.
Copyright (c) 1998-2005 The Regents of the University of California.
All rights reserved.
Permission is hereby granted, without written agreement and without
license or royalty fees, to use, copy, modify, and distribute this
software and its documentation for any purpose, provided that the above
copyright notice and the following two paragraphs appear in all copies
of this software.
IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
ENHANCEMENTS, OR MODIFICATIONS.
*/
package ptolemy.data.expr;
import java.util.LinkedList;
import java.util.List;
import ptolemy.data.ObjectToken;
import ptolemy.kernel.util.IllegalActionException;
//////////////////////////////////////////////////////////////////////////
//// ParseTreeSpecializer
/**
This class reduces a parse tree, given a scope of bound variables. If
an identifier is not found in the given scope, then the identifier is
bound to any constants registered with the expression parser. If any
subtrees of the parse tree become constant, they are evaluated and
replaced with leaf nodes containing the evaluated result.
@author Steve Neuendorffer
@version $Id$
@since Ptolemy II 2.1
@Pt.ProposedRating Red (neuendor)
@Pt.AcceptedRating Red (cxh)
@see ptolemy.data.expr.ASTPtRootNode
*/
public class ParseTreeSpecializer extends AbstractParseTreeVisitor {
///////////////////////////////////////////////////////////////////
//// public methods ////
/** Return a new parse tree resulting from the specialization of
* the given parse tree. Every identifier reference is replaced
* by constants according to the given scope. Constant subtrees
* are replaced with constant leaf nodes. Exclude the given set
* of names from being replaced. The given parse tree is not
* destroyed in the process.
*/
public ASTPtRootNode specialize(ASTPtRootNode node, List excludedNames,
ParserScope scope) throws IllegalActionException {
_excludedNames = excludedNames;
_scope = scope;
_evaluator = new ParseTreeEvaluator();
try {
_result = (ASTPtRootNode) node.clone();
_result._parent = null;
} catch (CloneNotSupportedException ex) {
throw new IllegalActionException(null, ex,
"Failed to clone node for specialization");
}
_result.visit(this);
_evaluator = null;
_scope = null;
_excludedNames = null;
ASTPtRootNode result = _result;
_result = null;
return result;
}
public void visitArrayConstructNode(ASTPtArrayConstructNode node)
throws IllegalActionException {
_defaultVisit(node);
}
public void visitBitwiseNode(ASTPtBitwiseNode node)
throws IllegalActionException {
_defaultVisit(node);
}
public void visitFunctionApplicationNode(ASTPtFunctionApplicationNode node)
throws IllegalActionException {
// Check to see if we are referencing a function closure in scope.
ptolemy.data.Token value = null;
String functionName = node.getFunctionName();
if ((_scope != null) && (functionName != null)) {
if (!_excludedNames.contains(functionName)) {
value = _scope.get(node.getFunctionName());
if (value instanceof ObjectToken) {
// Do not specialize ObjectToken.
value = null;
}
}
}
if (value == null) {
// Just visit arguments other than the first.
int numChildren = node.jjtGetNumChildren();
for (int i = 1; i < numChildren; i++) {
_visitChild(node, i);
}
} else {
_defaultVisit(node);
}
}
public void visitFunctionDefinitionNode(ASTPtFunctionDefinitionNode node)
throws IllegalActionException {
List excludedNames = new LinkedList(_excludedNames);
// Don't substitute any names in the parse tree that are
// bound in the definition.
excludedNames.addAll(node.getArgumentNameList());
List oldExcludedNames = _excludedNames;
_excludedNames = excludedNames;
// Recurse, with the new set of bound identifiers.
node.getExpressionTree().visit(this);
_excludedNames = oldExcludedNames;
}
public void visitFunctionalIfNode(ASTPtFunctionalIfNode node)
throws IllegalActionException {
_defaultVisit(node);
}
public void visitLeafNode(ASTPtLeafNode node) throws IllegalActionException {
if (node.isConstant() && node.isEvaluated()) {
return;
}
if (!_excludedNames.contains(node.getName())) {
ptolemy.data.Token token = null;
if (_scope != null) {
token = _scope.get(node.getName());
}
if (token == null) {
token = Constants.get(node.getName());
}
if (token != null) {
node.setToken(token);
node.setConstant(true);
// Reset the name, since it no longer makes sense.
node._name = null;
return;
}
throw new IllegalActionException("The ID " + node.getName()
+ " is undefined.");
}
}
public void visitLogicalNode(ASTPtLogicalNode node)
throws IllegalActionException {
_defaultVisit(node);
}
public void visitMatrixConstructNode(ASTPtMatrixConstructNode node)
throws IllegalActionException {
_defaultVisit(node);
}
public void visitMethodCallNode(ASTPtMethodCallNode node)
throws IllegalActionException {
_defaultVisit(node);
}
public void visitPowerNode(ASTPtPowerNode node)
throws IllegalActionException {
_defaultVisit(node);
}
public void visitProductNode(ASTPtProductNode node)
throws IllegalActionException {
_defaultVisit(node);
}
public void visitRecordConstructNode(ASTPtRecordConstructNode node)
throws IllegalActionException {
_defaultVisit(node);
}
public void visitRelationalNode(ASTPtRelationalNode node)
throws IllegalActionException {
_defaultVisit(node);
}
public void visitShiftNode(ASTPtShiftNode node)
throws IllegalActionException {
_defaultVisit(node);
}
public void visitSumNode(ASTPtSumNode node) throws IllegalActionException {
_defaultVisit(node);
}
public void visitUnaryNode(ASTPtUnaryNode node)
throws IllegalActionException {
_defaultVisit(node);
}
///////////////////////////////////////////////////////////////////
//// protected methods ////
/** Return true if all of the children of this node are constant.
*/
protected boolean _childrenAreConstant(ASTPtRootNode node) {
int numChildren = node.jjtGetNumChildren();
for (int i = 0; i < numChildren; i++) {
ASTPtRootNode child = (ASTPtRootNode) node.jjtGetChild(i);
if (!child.isConstant()) {
return false;
}
}
return true;
}
protected void _defaultVisit(ASTPtRootNode node)
throws IllegalActionException {
_visitAllChildren(node);
boolean isConstant = _childrenAreConstant(node);
if (isConstant) {
_replaceConstantNode(node);
}
}
protected void _replaceConstantNode(ASTPtRootNode node)
throws IllegalActionException {
// Create the replacement
ASTPtLeafNode newNode = new ASTPtLeafNode(
PtParserTreeConstants.JJTPTLEAFNODE);
ptolemy.data.Token token = _evaluator.evaluateParseTree(node, _scope);
newNode.setToken(token);
newNode.setType(token.getType());
newNode.setConstant(true);
ASTPtRootNode parent = (ASTPtRootNode) node._parent;
if (parent == null) {
_result = newNode;
} else {
// Replace the old with the new.
newNode._parent = parent;
int index = parent._children.indexOf(node);
parent._children.set(index, newNode);
}
}
protected List _excludedNames;
protected ASTPtRootNode _result;
protected ParserScope _scope;
protected ParseTreeEvaluator _evaluator;
}