/* -*- Mode: java -*- */ /* Input file to JJTree and JavaCC to generate Ptolemy II Parser */ options { LOOKAHEAD=1; //DEBUG_PARSER = true; //DEBUG_TOKEN_MANAGER = true; MULTI = true; STATIC = false; NODE_USES_PARSER = false; } PARSER_BEGIN(PtParser) /* 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. PT_COPYRIGHT_VERSION_2 COPYRIGHTENDKEY Created : May 1998 */ package ptolemy.data.expr; import ptolemy.kernel.*; import ptolemy.kernel.util.*; import ptolemy.data.*; import ptolemy.math.Complex; import java.util.ArrayList; import java.util.Hashtable; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Collections; import java.util.StringTokenizer; import java.io.*; ////////////////////////////////////////////////////////////////////// //// PTParser.jjt /** This file implements an expression parser for Ptolemy II using the JavaCC parser generator. It can handle all the basic arithmetic operators (*, /, +, -, %, ^), relational operators (<, <=, >, >=, == !=), logical operators(&&, ||, !), bitwise operators (&, |, #, ~) and, using reflection, all of the functionality available in the java.lang.Math package.
By editing the ASTFunctionNode file it is also relatively easy to allow references to other functions. This provides an easy mechanism to extend the range to the parser e.g. have a tcl(...) function that passes the string to a Tcl interpreter and retuns the result.
Functional if is supported via the following syntax: (boolean) ? (value1) : (value2)
Extensibility is also supported by allowing method calls on the Tokens, the syntax is (value1).method(comma separated arguments)
JavaCC by itself simply generates a file (and support files) that allow an input to be parsed, it does not return a parse tree. For the purposes of type checking we require a parse tree, and this is obtained using JJTree, a preprocessor for JavaCC.
JJtree operates by annotating the grammar file to support the generation of the parse tree. Thus the process is
The parser can also be passed a symbol table of ptolemy.data.expr.Variables which the expression to be parsed can reference.
Anything between quotes(") or apostrophes(') is taken to be one string. Strings are allowed to contain newlines or carriage returns. In addition, these characters, as well as other special characters, can be escaped using the standard Java syntax (\n, \t, \077, etc.).
The expressions recognized follow as close as possible the syntax of Java. In particular the operator precedences implemented here follow exactly those in Java. Any type conversions that are performed are lossless. If the user wants lossy conversions, explicit casts will be necessary.
Complex number are specified by an i or j after the imaginary part of the number. Long numbers are specified by an l or L after an integer number.
Users can register constants with the parser and also register classes where functions that may be called are defined. For a more thorough description of what the Parser is designed to do, please consult the Ptolemy II design document (or contact nsmyth@eecs)
Note that the parsers created by JavaCC generally have quite a bit of
internal state. On the other hand, the parse trees generated by this
parser are much smaller. It is also fairly cheap to traverse a parse
tree in order to evaluate it. Thus it is usually preferable to cache
parse trees and create a new parser when the cached parse tree becomes
invalid.
@author Neil Smyth, Steve Neuendorffer
@version $Id: PtParser.jjt,v 1.117 2005/03/24 02:30:18 cxh Exp $
@since Ptolemy II 1.0
@Pt.ProposedRating Yellow (nsmyth)
@Pt.AcceptedRating Yellow (yuhong)
@see ptolemy.data.expr.ASTPtBitwiseNode
@see ptolemy.data.expr.ASTPtFunctionApplicationNode
@see ptolemy.data.expr.ASTPtFunctionDefinitionNode
@see ptolemy.data.expr.ASTPtFunctionalIfNode
@see ptolemy.data.expr.ASTPtLeafNode
@see ptolemy.data.expr.ASTPtLogicalNode
@see ptolemy.data.expr.ASTPtMethodCallNode
@see ptolemy.data.expr.ASTPtProductNode
@see ptolemy.data.expr.ASTPtRelationalNode
@see ptolemy.data.expr.ASTPtRootNode
@see ptolemy.data.expr.ASTPtSumNode
@see ptolemy.data.expr.ASTPtUnaryNode
@see ptolemy.data.Token
*/
public class PtParser {
boolean debug = false;
public PtParser() {
this(new StringReader(""));
_initialize();
}
/** Returns the list of undefined variables after parsing the given String.
* @param stringIn The expression to be parsed
* @exception IllegalActionException If the parse fails.
* @return The list of undefined variables.
* @deprecated Use a visitor with a ParseTreeFreeVariableCollector
* instead.
*/
public LinkedList getUndefinedList(String stringIn)
throws IllegalActionException {
ASTPtRootNode rootNode = generateParseTree(stringIn);
ParseTreeFreeVariableCollector collector =
new ParseTreeFreeVariableCollector();
Set vars = collector.collectFreeVariables(rootNode);
return new LinkedList(vars);
}
/** Generates a parse tree from the given String. The root node is
* returned. To evaluate the parse tree, use a ParseTreeEvaluator.
* @param stringIn The expression to be parsed.
* @exception IllegalActionException If the parse fails.
* @return The root node of the parse tree.
*/
public ASTPtRootNode generateParseTree(String stringIn)
throws IllegalActionException {
Reader reader = new StringReader(stringIn);
this.ReInit(reader);
ASTPtRootNode rootNode;
try {
// Parse the expression to obtain the parse tree
// start() can generate a TokenMgrError if we parse a "\0"
rootNode = start();
if (debug) {
rootNode.displayParseTree(" ");
}
} catch (Throwable throwable) {
throw new IllegalActionException(null, throwable,
"Error parsing expression \"" + stringIn + "\"");
}
ASTPtRootNode primary = (ASTPtRootNode)rootNode.jjtGetChild(0);
primary.jjtSetParent(null);
return primary;
}
/** Generates a parse tree from the given String, which may optionally
* contain an assignment. The root node is
* returned. If the string represents an assignment, then the
* toplevel node will be an ASTPtAssignmentNode.
* @param stringIn The expression to be parsed.
* @exception IllegalActionException If the parse fails.
* @return The root node of the parse tree.
*/
public ASTPtRootNode generateSimpleAssignmentParseTree(String stringIn)
throws IllegalActionException {
Reader reader = new StringReader(stringIn);
this.ReInit(reader);
ASTPtRootNode rootNode;
try {
// Parse the expression to obtain the parse tree
rootNode = startSimpleAssignment();
if (debug) rootNode.displayParseTree(" ");
} catch (Throwable throwable) {
throw new IllegalActionException(null, throwable,
"Error parsing expression \""
+ stringIn + "\"");
}
ASTPtRootNode primary = (ASTPtRootNode)rootNode.jjtGetChild(0);
primary.jjtSetParent(null);
return primary;
}
/** Generates a parse tree from the given String, which is interpreted
* in "String Mode" instead of as an operator expression. In
* string mode, the expression is a literal string, except for
* identifiers which are denoted by $param. The root node is
* returned. To evaluate the parse tree, use a ParseTreeEvaluator.
* @param stringIn The expression to be parsed.
* @exception IllegalActionException If the parse fails.
* @return The root node of the parse tree.
*/
public ASTPtRootNode generateStringParseTree(String stringIn)
throws IllegalActionException {
Reader reader = new StringReader(stringIn);
this.ReInit(reader);
ASTPtRootNode rootNode;
try {
// Parse the expression to obtain the parse tree
token_source.SwitchTo(StringMode);
// startString() can generate a TokenMgrError
rootNode = startString();
if (debug) {
rootNode.displayParseTree(" ");
}
} catch (Throwable throwable) {
throw new IllegalActionException(null, throwable,
"Error parsing expression \"" + stringIn + "\"");
} finally {
token_source.SwitchTo(DEFAULT);
}
ASTPtRootNode primary = (ASTPtRootNode)rootNode.jjtGetChild(0);
primary.jjtSetParent(null);
return primary;
}
/** Generates a parse tree from the given String.
* The string will be parsed according to rules for assignment lists.
* The returned node is a RootNode containing one assignment
* node for each assignment in the expression.
*
* @param stringIn The expression to be parsed.
* @exception IllegalActionException If the parse fails.
* @return The root node of the parse tree.
*/
public Map generateAssignmentMap(String stringIn)
throws IllegalActionException {
Reader reader = new StringReader(stringIn);
this.ReInit(reader);
Map map;
try {
// Parse the expression to obtain the parse tree.
// startAssignmentList() might throw TokenMgrError which is
// an Error, not an Exception, so we catch Throwables here.
map = startAssignmentList();
} catch (Throwable throwable) {
throw new IllegalActionException(null, throwable,
"Error parsing expression \"" + stringIn + "\"");
}
return map;
}
/** Return the list of classes the parser searches
* when a function call is encountered. The
* classes are searched in the same order that they were registered
* with the parser, so the classes that are most likely to be
* searched should be registered first. This method is synchronized
* so that it can be called safely from multiple parsers.
* @return An unmodifiable list that can be iterated over.
*/
public static synchronized List getRegisteredClasses() {
if (_classesSearched == null) {
return new ArrayList();
}
return Collections.unmodifiableList(new ArrayList(_classesSearched));
}
/** Add a constant to the list of constants that the parser recognizes.
* It is a static method. The constants are stored in a hash table by
* the Constants class. The entry for each name is a ptolemy.data.Token
* of the appropriate type. The value for the constant can be given
* in a ptolemy.data.Token or in one of the data wrapper classes
* in java.lang.
* @param name The string name that the parser will recognize.
* @param value An Object constraining the value associated with
* the constant.
* @exception IllegalArgumentException If the constant cannot
* be registered with the parser.
*/
public static void registerConstant(String name, Object value)
throws IllegalArgumentException {
if ( (value == null) || (name == null)) {
throw new IllegalArgumentException("PtParser: cannot register " +
"a constant if either the name or value object is null.");
}
ptolemy.data.Token tmp;
if (value instanceof ptolemy.data.Token) {
tmp = (ptolemy.data.Token)value;
} else if (value instanceof Integer) {
tmp = new IntToken(((Integer)value).intValue());
} else if (value instanceof Double) {
tmp = new DoubleToken(((Double)value).doubleValue());
} else if (value instanceof Long) {
tmp = new LongToken(((Long)value).longValue());
} else if (value instanceof String) {
tmp = new StringToken((String)value);
} else if (value instanceof Boolean) {
tmp = new BooleanToken(((Boolean)value).booleanValue());
} else if (value instanceof Complex) {
tmp = new ComplexToken((Complex)value);
} else {
throw new IllegalArgumentException("PtParser: cannot register " +
name + " as a constant of the parser.");
}
Constants.add(name, tmp);
return;
}
/** Add a class to the list of classes that the parser searches
* when a function call is encountered.
* It is a static method. It stores the classes in a LinkedList. The
* classes are searched in the same order that they were registered
* with the parser, so the classes that are most likely to be
* searched should be registered first. This method is synchronized
* so that it can be called safely from multiple parsers.
* @param newClassName The fully qualified name of the Class to
* be added to the search path for functions.
* @exception IllegalArgumentException If the Class named by the
* argument cannot not be found.
*/
public static synchronized void registerFunctionClass(String newClassName)
throws IllegalArgumentException {
if (_classesSearched == null) {
_classesSearched = new ArrayList();
}
try {
Class newClass = Class.forName(newClassName);
if(!_classesSearched.contains(newClass)) {
_classesSearched.add(newClass);
}
} catch (ClassNotFoundException ex) {
throw new IllegalArgumentException(
"Could not find " + newClassName + ".");
}
CachedMethod.clear();
}
/** Initialize the static variable containing the classes searched by
* the parser upon encountering a function call.
*/
private void _initialize() {
if (!_alreadyInitialized) {
_alreadyInitialized = true;
registerFunctionClass("java.lang.Math");
registerFunctionClass("java.lang.Double");
registerFunctionClass("java.lang.Integer");
registerFunctionClass("java.lang.Long");
registerFunctionClass("java.lang.String");
registerFunctionClass("ptolemy.data.MatrixToken");
registerFunctionClass("ptolemy.data.RecordToken");
registerFunctionClass("ptolemy.data.expr.UtilityFunctions");
registerFunctionClass("ptolemy.data.expr.FixPointFunctions");
registerFunctionClass("ptolemy.math.Complex");
registerFunctionClass("ptolemy.math.ExtendedMath");
registerFunctionClass("ptolemy.math.IntegerMatrixMath");
registerFunctionClass("ptolemy.math.DoubleMatrixMath");
registerFunctionClass("ptolemy.math.ComplexMatrixMath");
registerFunctionClass("ptolemy.math.LongMatrixMath");
registerFunctionClass("ptolemy.math.IntegerArrayMath");
registerFunctionClass("ptolemy.math.DoubleArrayStat");
registerFunctionClass("ptolemy.math.ComplexArrayMath");
registerFunctionClass("ptolemy.math.LongArrayMath");
registerFunctionClass("ptolemy.math.SignalProcessing");
registerFunctionClass("ptolemy.math.FixPoint");
}
}
/* Flag indicating whether the default set of classes searched
* by the parser has already been loaded.
*/
private static boolean _alreadyInitialized = false;
/* Stores the classes that are searched by the parser when a
* function call is parsed. It is static, and by default only
* contains the java.lang.Math class.
*/
private static List _classesSearched;
}
PARSER_END(PtParser)
/* COMMENTS */
SKIP :
{
"//" : SingleLineCommentMode
|
"/*" : MultiLineCommentMode
}