/* -*- 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-2014 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 returns 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

?
X.jjt --> JJTREE --> X.jj --> JAVACC --> X.java + support files

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.

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 70398 2014-10-22 23:44:32Z cxh $ @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 */ @SuppressWarnings("unused") 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; } public String cleanupString(String str) throws ParseException { // Check to see that the start and the end are the same. String start = str.substring(0, 1); if(!token.image.endsWith(start)) { throw new ParseException( "Found unterminated string: " + token.image); } // Now cut the " from each end of the string. int len = token.image.length(); String tidied = token.image.substring(1, (len -1)); // Resolve escape sequences in the string. StringTokenizer st = new StringTokenizer(tidied, "\\", true); boolean escape = false; String x = new String(); while (st.hasMoreTokens()) { String tok = st.nextToken(); if (escape) { // The previous character was a backslash that started // an escape sequence. escape = false; int trailingCharIndex = 1; switch (tok.charAt(0)) { case 'n': x += "\n"; break; case 't': x += "\t"; break; case 'b': x += "\b"; break; case 'r': x += "\r"; break; case 'f': x += "\f"; break; case '\\': x += "\\"; break; case '\'': x += "\'"; break; case '"': x += "\""; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': // The escape sequence is a character // specified in octal. int i; for (i = 1; i < tok.length(); i++) { // The octal sequence stops at the first // non-octal character. char c = tok.charAt(i); if (! (c == '0' || c == '1' || c == '2' || c == '3' || c == '4' || c == '5' || c == '6' || c == '7')) break; } trailingCharIndex = i; int octVal = Integer.parseInt( tok.substring(0, trailingCharIndex), 8); x += (char)octVal; break; default: throw new ParseException("Unknown backslash sequence: " + "\\" + tok); } if (trailingCharIndex < tok.length()) { // Keep any remaining characters. x += tok.substring(trailingCharIndex); } } else if (tok.equals("\\")) { // Start an escape sequence. escape = true; } else { // Keep regular text. x += tok; } } if(escape) { throw new ParseException("Unterminated backslash sequence in " + "string: " + token.image); } return x; } /** 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"); registerFunctionClass("ptolemy.data.ObjectToken"); } } /* 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 } SKIP : { : DEFAULT } SKIP : { : DEFAULT } SKIP : { < ~[] > } /* Now come to proper tokens */ SKIP : { " " | "\r" | "\t" | "\n" } /* TOKEN : { < EOL: "\n" > } */ TOKEN : /* Arithmetic operators */ { < PLUS: "+" > | < MINUS: "-" > | < MULTIPLY: "*" > | < DIVIDE: "/" > | < MODULO: "%" > | < POWER: "^" > } TOKEN : /* Assignment */ { < OPENPAREN: "(" > | < CLOSEPAREN: ")" > | < OPENBRACE: "{" > | < CLOSEBRACE: "}" > | < OPENBRACKET: "[" > | < CLOSEBRACKET: "]" > | < COMMA: "," > | < PERIOD: "." > | < COLON: ":" > | < QUESTION: "?" > | < OPENUNION: "{|" > | < CLOSEUNION: "|}" > } TOKEN : /* Relational operators */ { < GT: ">" > | < LT: "<" > | < GTE: ">=" > | < LTE: "<=" > | < NOTEQUALS: "!=" > | < EQUALS: "==" > } TOKEN : /* Boolean & bitwise operators */ { < COND_AND: "&&" > | < COND_OR: "||" > | < BOOL_NOT: "!" > | < BITWISE_NOT: "~" > | < AND: "&" > | < OR: "|" > | < XOR: "#" > } TOKEN : /* Shift operators */ { < SHL: "<<" > | < SHR : ">>" > | < LSHR: ">>>" > } TOKEN : /* Numeric literals */ { < INTEGER: ()? | ()? | ()? > | < #INTEGER_FORMAT_SPEC: (["l","L"]) | (["s", "S"]) | (["u","U"] ["b","B"]) > | < #DECIMAL_LITERAL: ["1"-"9"] (["0"-"9"])* > | < #HEX_LITERAL: "0" ["x","X"] (["0"-"9","a"-"f","A"-"F"])+ > | < #OCTAL_LITERAL: "0" (["0"-"7"])* > | < #EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ > | < DOUBLE: (["0"-"9"])+ "." (["0"-"9"])+ ()? (["f","F","d","D","p","P" ])? | "." (["0"-"9"])+ ()? (["f","F","d","D","p","P"])? | (["0"-"9"])+ (["f","F","d","D","p","P"])? | (["0"-"9"])+ ()? ["f","F","d","D","p","P"] > | < COMPLEX: (["0"-"9"])+ "." (["0"-"9"])+ ()? (["i","j"])? | "." (["0"-"9"])+ ()? (["i","j"])? | (["0"-"9"])+ (["i","j"])? | (["0"-"9"])+ ()? ["i","j"] > } TOKEN : /*Boolean literals */ { } TOKEN : /* function definition */ { } < DEFAULT, StringModeIDBrace, StringModeIDParen > TOKEN : /* Function names */ { < ID: ( )+ (["0"-"9", "$", "@"] | | "::")* > | < #LETTER: ["a"-"z", "A"-"Z", "_"] > } TOKEN : /* literal strings are given using single or double quotes. */ { < STRING: ( ("\"" ("\\\"" | ~["\""])* ("\"")?) | ("'" (~["'"])* ("'")?) )> } TOKEN : /* Assignment */ { < SETEQUALS: "=" > | < SEPARATOR: ";" > } < StringMode > TOKEN : /* literal strings are any string that does not contain a lone dollar sign. */ { < SMSTRING : (("$$" | ~["$"])+)> | < SMDOLLAR : "$" > : StringModeIDNone | < SMDOLLARBRACE : "${" > : StringModeIDBrace | < SMDOLLARPAREN : "$(" > : StringModeIDParen } < StringModeIDNone > TOKEN : { // String mode identifiers with lone dollars can't contain dollars. < SMID: ( )+ (["0"-"9", "@"] | )* > :StringMode | < #SMLETTER: ["a"-"z", "A"-"Z", "_"] > } < StringModeIDBrace > TOKEN : { < SMIDBRACE: () > | < SMBRACE : > : StringMode } < StringModeIDParen > TOKEN : { < SMIDPAREN: () > | < SMPAREN : > : StringMode } TOKEN : { < ERROR: ~[] > } // A simple, optional assigment used by the expression evaluator ASTPtRootNode startSimpleAssignment() #PtRootNode : { } { ( LOOKAHEAD( ) assignment() | expression() ) { return jjtThis; } } Map startAssignmentList() #PtRootNode : { Map map = new LinkedHashMap(); } { assignment() { ASTPtAssignmentNode node = (ASTPtAssignmentNode) jjtree.popNode(); map.put(node.getIdentifier(), node); } ( assignment() { ASTPtAssignmentNode node2 = (ASTPtAssignmentNode) jjtree.popNode(); map.put(node2.getIdentifier(), node2); })* { return map; } } void assignment() #PtAssignmentNode : {} { assignmentIdentifier() expression() } // A string mode expression, using $parameter substitution. ASTPtRootNode startString() #PtRootNode : { } { ( stringModeElement() ( stringModeElement() { Token token = Token.newToken(PtParserConstants.PLUS); token.kind = PtParserConstants.PLUS; jjtThis._lexicalTokens.add(token); } )* ) #PtSumNode(>1) { return jjtThis; } } void stringModeElement() #void : {} { stringModeString() | identifier() | identifier() | ( { token_source.SwitchTo(DEFAULT); } expression() { token_source.SwitchTo(StringMode); } ) #PtSumNode } void stringModeString() #void : {} { { jjtThis._ptToken = new StringToken(token.image.replaceAll("\\$\\$","\\$")); jjtThis._isConstant = true; } #PtLeafNode } ASTPtRootNode start() #PtRootNode : {} { expression() { return jjtThis; } } void expression() #void : {} { ( LOOKAHEAD( ) functionDefinition() | funcIf() ) } void funcIf() #void : {} { ( logicalOr() ( expression() expression() )? ) #PtFunctionalIfNode(>1) } void logicalOr() #void : {Token x;} { ( logicalAnd() ( x = logicalAnd() { jjtThis._lexicalToken = x; } )* ) #PtLogicalNode(>1) } void logicalAnd() #void : {Token x;} { ( bitwiseOr() ( x = bitwiseOr() { jjtThis._lexicalToken = x; } )* ) #PtLogicalNode(>1) } void bitwiseOr() #void : {Token x;} { ( bitwiseXor() ( x = bitwiseXor() { jjtThis._lexicalToken = x; } )* ) #PtBitwiseNode(>1) } void bitwiseXor() #void : {Token x;} { ( bitwiseAnd() ( x = bitwiseAnd() { jjtThis._lexicalToken = x; } )* ) #PtBitwiseNode(>1) } void bitwiseAnd() #void : {Token x;} { ( logicalEquals() ( x = logicalEquals() { jjtThis._lexicalToken = x; } )* ) #PtBitwiseNode(>1) } void logicalEquals() #void : {Token x;} { ( relational() ( ( x = | x = ) relational() { jjtThis._lexicalToken = x; } )? ) #PtRelationalNode(>1) } void power() #void : {Token x;} { ( unary() ( unary() )* ) #PtPowerNode(>1) } void relational() #void : {Token x;} { ( shift() ( ( x = | x = | x = | x = ) shift() { jjtThis._lexicalToken = x; } )? ) #PtRelationalNode(>1) } void shift() #void : {Token x;} { ( sum() ( ( x = | x = | x = ) sum() { jjtThis._lexicalToken = x; } )? ) #PtShiftNode(>1) } void sum() #void : {Token x;} { ( term() ( ( x = | x = ) term() { jjtThis._lexicalTokens.add(x); } )* ) #PtSumNode(>1) } void term() #void : {Token x;} { ( power() ( ( x = | x = | x = ) power() { jjtThis._lexicalTokens.add(x); } )* ) #PtProductNode(>1) } void unary() #void : {Token x;} { LOOKAHEAD({ getToken(1).kind == MINUS && getToken(2).kind != INTEGER }) ( x = element() { jjtThis._isMinus = true; jjtThis._lexicalToken = x; } ) #PtUnaryNode | ( x = element() { jjtThis._isNot = true; jjtThis._lexicalToken = x; } ) #PtUnaryNode | ( x = element() { jjtThis._isBitwiseNot = true; jjtThis._lexicalToken = x; } ) #PtUnaryNode | element() } /* void element() #void : { boolean isMethodCall = false; } { // Note that omitting the parens is possible for a record... ( primaryElement() [ {jjtThis._methodName = token.image; isMethodCall = true;} ( [ funcIf() ( funcIf() )* ] )? ] ) #PtMethodCallNode(isMethodCall) } */ /*void element() #void : { boolean hasName = false; boolean hasCall = false; String name = null; } { ( ( primaryElement() [ { name = token.image; hasName = true; } ] [ [ expression() ( expression() )* ] { hasCall = true; } ] { jjtThis._methodName = name; } ) #PtMethodCallNode(hasName) ) #PtFunctionApplicationNode(!hasName && hasCall) }*/ void element() #void : { boolean hasName = false; boolean hasCall = false; String name = null; ASTPtRootNode child; } { primaryElement() { child = (ASTPtRootNode) jjtree.popNode(); } ( ( ( { jjtree.pushNode(child); name = token.image; } [ LOOKAHEAD() ( [ expression() ( expression() )* ] ) ] ) #PtMethodCallNode { child = (ASTPtRootNode) jjtree.popNode(); ((ASTPtMethodCallNode) child)._methodName = name; } ) | ( ( { jjtree.pushNode(child); } [ expression() ( expression() )* ] ) #PtFunctionApplicationNode { child = (ASTPtRootNode) jjtree.popNode(); } ) )* { jjtree.pushNode(child); } } void integer() #void : { int len; boolean neg = false; String x; } { ( { neg = true; } | ) { try { x = token.image.toLowerCase(); if (neg) x = "-" + x; len = x.length(); int radix; boolean mustBeLong = x.endsWith("l"); boolean mustBeShort = x.endsWith("s"); boolean mustBeUnsignedByte = x.endsWith("ub"); int prefixLength; int suffixLength; if(mustBeLong) { suffixLength = 1; } else if (mustBeShort) { suffixLength = 1; } else if(mustBeUnsignedByte) { suffixLength = 2; } else { suffixLength = 0; } if (x.startsWith("0x") ) { // Input is a hex number. radix = 16; prefixLength = 2; } else if(x.startsWith("0")) { // Input is an octal number. radix = 8; prefixLength = 0; } else { // Input is a decimal number. radix = 10; prefixLength = 0; } // Strip off the radix prefix and the length suffix. x = x.substring(prefixLength, len - suffixLength); if (mustBeLong) { // If the size was specified as long, then create a long. jjtThis._ptToken = new LongToken(Long.parseLong(x, radix)); } else if (mustBeShort) { // If the size was specified as short, then create a short. jjtThis._ptToken = new ShortToken(Short.parseShort(x, radix)); } else if (mustBeUnsignedByte) { // If the size was specified as unsignedbyte, // then create an unsigned byte, truncating if necessary. jjtThis._ptToken = new UnsignedByteToken(Integer.parseInt(x, radix)); } else { // Try to infer the size. Inferred sizes are at least // integer. try { jjtThis._ptToken = new IntToken(Integer.parseInt(x, radix)); } catch (NumberFormatException nfe) { jjtThis._ptToken = new LongToken(Long.parseLong(x, radix)); } } jjtThis._isConstant = true; } catch (NumberFormatException ee) { throw new ParseException( "Unable to convert token " + token.image + " to an integer or long"); } } #PtLeafNode } void primaryElement() #void : { int len; String x; } { ( { try { x = token.image.toLowerCase(); len = x.length(); Double imag = new Double(x.substring(0, len-1 )); Complex value = new Complex(0, imag.doubleValue()); jjtThis._ptToken = new ComplexToken(value); jjtThis._isConstant = true; } catch (NumberFormatException ee) { throw new ParseException( "Unable to convert token " + token.image + " to a complex number."); } } ) #PtLeafNode | ( { try { x = token.image.toLowerCase(); len = x.length(); if (x.endsWith("f")) { Float value = new Float(x.substring(0, len - 1)); jjtThis._ptToken = new FloatToken(value .floatValue()); } else if (x.endsWith("d") || x.endsWith("p")) { // all floating point numbers are double Double value = new Double(x.substring(0, len-1 )); if (x.endsWith("p")) { jjtThis._ptToken = new PetiteToken(value.doubleValue()); } else { jjtThis._ptToken = new DoubleToken(value.doubleValue()); } } else { Double value = new Double(x); jjtThis._ptToken = new DoubleToken(value.doubleValue()); } jjtThis._isConstant = true; } catch (NumberFormatException ee) { throw new ParseException( "Unable to convert token " + token.image + " to an float or double"); } } ) #PtLeafNode | integer() | ( { jjtThis._ptToken = new StringToken(cleanupString(token.image)); jjtThis._isConstant = true; } ) #PtLeafNode | ( { if (token.image.equalsIgnoreCase("TRUE")) { jjtThis._ptToken = BooleanToken.TRUE; } else if (token.image.equalsIgnoreCase("FALSE")) { jjtThis._ptToken = BooleanToken.FALSE; } jjtThis._isConstant = true; } ) #PtLeafNode | LOOKAHEAD() expression() | LOOKAHEAD( ( | ) ) orderedRecordConstruct() | LOOKAHEAD() matrixConstruct() | LOOKAHEAD( ( | ) ) recordConstruct() | LOOKAHEAD() unionConstruct() | LOOKAHEAD( ) nilArrayConstruct() | LOOKAHEAD( ) arrayConstruct() | LOOKAHEAD( ) function() | identifier() } void functionDefinition() #PtFunctionDefinitionNode : {} { [ { jjtThis._argList.add(token.image); } optTypeSpecifier() ( { jjtThis._argList.add(token.image); } optTypeSpecifier())* ] expression() { // A hack for type specification, since we don't have types in // the syntax. ParseTreeTypeInference inference = new ParseTreeTypeInference(); int argCount = jjtThis.getArgumentNameList().size(); jjtThis._argTypes = new ptolemy.data.type.Type[argCount]; for(int i = 0; i < argCount; i++) { ASTPtRootNode argChild = ((ASTPtRootNode)jjtThis.jjtGetChild(i)); ptolemy.data.type.Type type = inference.inferTypes(argChild); jjtThis._argTypes[i] = type; } } } void optTypeSpecifier() #void : {} { ( funcIf()) | { ASTPtLeafNode node = new ASTPtLeafNode(JJTPTLEAFNODE); node._name = "general"; node._ptToken = new GeneralToken(); node._ptType = ptolemy.data.type.BaseType.GENERAL; node._isConstant = true; jjtree.pushNode(node); } } void function() #PtFunctionApplicationNode : {} { identifier() [ expression() ( expression() )* ] } void assignmentIdentifier() #PtLeafNode : {StringBuffer name;} { { // Store the name of this identifier... This will be the // name of a variable, a constant, or an undefined identifier. // Note that this name is not actually resolved into a value // until the parse tree is evaluated. name = new StringBuffer(token.image); } ( { name.append("."); name.append(token.image); })* ( ( { name.append("("); name.append(token.image); name.append(")"); } | { name.append("("); name.append(token.image); name.append(")"); }) )? { jjtThis._name = name.toString(); } } void identifier() #PtLeafNode : {} { ( | | | ) { // Store the name of this identifier... This will be the // name of a variable, a constant, or an undefined identifier. // Note that this name is not actually resolved into a value // until the parse tree is evaluated. // NOTE: Parser constants *are not* evaluated here. Doing // this means that they can shadow other variables in scope, // which is bad. jjtThis._name = token.image; } } void matrixConstruct() #PtMatrixConstructNode : { int i; int nRows = 0; } { { ++jjtThis._nColumns; ++nRows; } funcIf() /* first term */ ( ( ( { ++jjtThis._nColumns; } funcIf() )* /* first row */ ( { ++nRows; i = 0; } funcIf() { ++i; } ( funcIf() { ++i; } )* /* one more row */ { /* Assert that the following rows have the same number of terms as the first row. */ if ( i != jjtThis._nColumns ) { throw new ParseException("PtParser: error parsing matrix " + "construction, the row " + nRows + " does not have the same number of " + "terms as the first row."); } } )* { jjtThis._form = 1; jjtThis._nRows = nRows; } ) | ( funcIf() funcIf() ( { ++nRows; } funcIf() funcIf() funcIf() )* { jjtThis._form = 2; jjtThis._nRows = nRows; } ) ) } void recordConstruct() #PtRecordConstructNode : { Token x = null; } { ( (x = {jjtThis._fieldNames.add(cleanupString(x.image));} funcIf()) | (x = {jjtThis._fieldNames.add(x.image);} funcIf()) ) ( ( (x = {jjtThis._fieldNames.add(cleanupString(x.image));} funcIf()) | (x = {jjtThis._fieldNames.add(x.image);} funcIf())) )* } void orderedRecordConstruct() #PtOrderedRecordConstructNode : { Token x = null; } { ( (x = {jjtThis._fieldNames.add(cleanupString(x.image));} funcIf()) | (x = {jjtThis._fieldNames.add(x.image);} funcIf()) ) ( ( (x = {jjtThis._fieldNames.add(cleanupString(x.image));} funcIf()) | (x = {jjtThis._fieldNames.add(x.image);} funcIf())) )* } void unionConstruct() #PtUnionConstructNode : { Token x = null; } { x = {jjtThis._labelNames.add(x.image);} funcIf() ( x = {jjtThis._labelNames.add(x.image);} funcIf())* } void arrayConstruct() #PtArrayConstructNode : { } { funcIf() ( funcIf() )* } void nilArrayConstruct() #PtArrayConstructNode : { } { }