// ---------------------------------------------------------------------------- // Options options { JAVA_UNICODE_ESCAPE = true; } // ---------------------------------------------------------------------------- // Parser Begin PARSER_BEGIN(ExpressionParser) package prefuse.data.expression.parser; import java.io.StringReader; import java.util.logging.Logger; import prefuse.data.expression.AndPredicate; import prefuse.data.expression.ArithmeticExpression; import prefuse.data.expression.BooleanLiteral; import prefuse.data.expression.ColumnExpression; import prefuse.data.expression.ComparisonPredicate; import prefuse.data.expression.Expression; import prefuse.data.expression.Function; import prefuse.data.expression.FunctionTable; import prefuse.data.expression.IfExpression; import prefuse.data.expression.NotPredicate; import prefuse.data.expression.NumericLiteral; import prefuse.data.expression.ObjectLiteral; import prefuse.data.expression.OrPredicate; import prefuse.data.expression.Predicate; import prefuse.data.expression.XorPredicate; import prefuse.util.StringLib; /** * Parser for statements written in the prefuse expression language. Text * expression are parsed into {@link prefuse.data.expression.Expression} * instances, and can be used as predicates or to create derived * table columns. This parser is implemented using the * JavaCC package. To parse * a text String to an {@link prefuse.data.expression.Expression}, use * the {@link #parse(String)} method. If a parse error occurs, the method * will fail silently and return null. Any generated exception can be * later retrieved using the {@link #getError()} method. One can also * use the {@link #parse(String, boolean)} with a true * boolean argument to request that Exceptions be thrown when * errors occur. * *

Prefuse Expression Language Reference

*

* The prefuse expression language provides a convenient way of creating manipulable statements * over data in a prefuse data structure. For example, the expression language can be used to * write {@link prefuse.data.expression.Predicate} instances for querying and filtering a table * or graph, or to create arbitrary expressions over a data set to generate new, derived data * fields that can in turn be subject to additional processing or visualization. For example, * the {@link prefuse.data.tuple.TupleSet#tuples(prefuse.data.expression.Predicate)} method * uses a Predicate to filter the requested set of tuples, and the * {@link prefuse.data.Table#addColumn(java.lang.String,prefuse.data.expression.Expression)} * method creates a new table column whose values are generated by the provided Expression. * The expression machinery is used * throughout the toolkit -- it underlies the filtering and query optimization features, * is a key component of {@link prefuse.data.query dynamic query bindings}, and is used to * create the rule chains evaluated by the * {@link prefuse.render.DefaultRendererFactory}, * {@link prefuse.action.assignment.ColorAction}, * {@link prefuse.action.assignment.ShapeAction}, * {@link prefuse.action.assignment.FontAction}, and * {@link prefuse.action.assignment.SizeAction} classes. *

*

* The {@link prefuse.data.expression.Expression} interface is quite simple: given a single * Tuple, compute and return a value. The returned value could be an Object, a boolean, an int, * or other primitive type. Individual methods for each type are available, and which ones are * legal to call for any given Expression depends on the type of Expression. *

*

* Expressions can be created directly in Java, by instantiating and chaining together the * desired Expression instances. This process, however, can be somewhat tedious, so prefuse * also provides a built-in parser/compiler for generating these chains of Expression * instances from a textual language. The language is based on a subset of SQL, the * standard language for database queries. If you have ever written a "WHERE" clause in * a SQL "SELECT" query, you probably know most of the language already. To parse an * expression, simply pass a text string containing an expression statement to the * {@link prefuse.data.expression.parser.ExpressionParser#parse(java.lang.String)} * method. If the string parses successfully, the parsed Expression instance will be * returned. *

*

* Below is the reference for the language, including literal types, data field references, * basic operators, and included functions. If need be, you can also introduce a new * function by creating a new instance of the {@link prefuse.data.expression.Function} interface * and registering it with the {@link prefuse.data.expression.FunctionTable} class. *

*

* All keywords and functions in the prefuse expression language can be written in * either uppercase or lowercase. Writing in mixed-case, however, will likely result in parse * errors. *

* *

Literal Values and Data Field References

*

The fundamental building blocks of the expression language, representing data values * or referencing the contents of a Tuple data field.

* * *

Operators and Control Flow

*

Basic operators and control flow structures for the expression language.

* * *

General Functions

*

General purpose functions.

* * *

Mathematical Functions

*

Functions for performing mathematical calculations.

* * *

String Functions

*

Functions for processing text strings.

* * *

Color Functions

*

Functions for generating, translating, and interpolating color values.

* * *

Visualization Functions

*

These functions can only be used when the Tuple being evaluated is * a VisualItem, and provide access to data group information of the VisualItem's * Visualization. Individual visual data fields can be accessed directly using * a data field reference. For example, _x, _y, * _hover, _highlight, _fillColor would * evaluate to references for the x-coordinate, y-coordinate, mouse hover status, * highlight status, and fill color, respectively.

* * * @author jeffrey heer */ public class ExpressionParser { private static final Logger s_logger = Logger.getLogger(ExpressionParser.class.getName()); private static boolean s_init = false; private static Throwable s_error; /** * Parse an expression. * @param expr the expression text to parse * @param throwsException true if this method should throw an * exception if an error occurs or should fail quietly * @return the parsed Expression, or null if the parse failed * and throwsException is false */ public synchronized static Expression parse(String expr, boolean throwsException) { // initialize the parser if ( !s_init ) { new ExpressionParser(new StringReader(expr)); s_init = true; } else { ExpressionParser.ReInit(new StringReader(expr)); } // attempt to parse the expression try { Expression e = Parse(); s_error = null; s_logger.info("Parsed Expression: "+e); return e; } catch ( ParseException t ) { s_error = t; if ( throwsException ) { throw t; } else { s_logger.warning("Expression Parse Error: " + t.getMessage() + "\n" + StringLib.getStackTrace(t)); return null; } } } /** * Parse an expression. This method does not throw an exception if * a parse error occurs. Use {@link #getError()} to access any * generated exceptions. * @param expr the expression text to parse * @return the parsed Expression, or null if the parse failed */ public synchronized static Expression parse(String expr) { return parse(expr, false); } /** * Parse an expression as a predicate. This method does not throw an * exception if a parse error occurs. Use {@link #getError()} to access * any generated exceptions. * @param expr the expression text to parse * @return the parsed Expression, or null if the parse failed */ public synchronized static Predicate predicate(String expr) { Expression ex = parse(expr, false); if ( ex == null ) { return null; } else if ( ex instanceof Predicate ) { return (Predicate) ex; } else { s_error = new ClassCastException("Expression is not a predicate"); return null; } } /** * Get the last error, if any, generated by a parse operation. * @return the last error generated during parsing */ public synchronized static Throwable getError() { return s_error; } /** * Replace escape sequences with represented characters. This * includes newlines, tabs, and quotes. * @param s the input String, possibly with escape sequences * @return a String with recognized escape sequences properly replaced */ private static String unescape(String s) { int len = s.length(), base = 0, idx; String escapes = "tnrbf\\\"'"; String chars = "\t\n\r\b\f\\\"'"; StringBuffer sbuf = null; while ( (idx=s.indexOf('\\',base)) != -1) { if ( sbuf != null ) sbuf.append(s.substring(base, idx)); if (idx+1 == len) break; // find escape character char c = s.charAt(idx+1); // find the index of the escape character int cidx = escapes.indexOf(c); if (cidx == -1) { // no match, so continue sbuf.append('\\'); sbuf.append(c); } else { // replace escape sequence with true char if ( sbuf == null ) sbuf = new StringBuffer(s.substring(base, idx)); sbuf.append(chars.charAt(cidx)); } // skip over escape sequence base = idx + 2; } if ( sbuf != null && base < len ) sbuf.append(s.substring(base)); return ( sbuf == null ? s : sbuf.toString() ); } } // end of class ExpressionParser PARSER_END(ExpressionParser) // ---------------------------------------------------------------------------- // Token definitions /* characters to skip */ SKIP : { " " | "\t" | "\n" | "\r" | "\f" } /* keywords */ TOKEN : { < TRUE : "TRUE"|"true" > | < FALSE : "FALSE"|"false" > | < NULL : "NULL"|"null" > | < IF : "IF"|"if" > | < THEN : "THEN"|"then" > | < ELSE : "ELSE"|"else" > | < AND : "AND"|"and"|"&&" > | < OR : "OR"|"or"|"||" > | < NOT : "NOT"|"not"|"!" > | < XOR : "XOR"|"xor" > } /* literal values */ TOKEN : { < INT : | | > | < #DECIMAL_LITERAL: ["1"-"9"] (["0"-"9"])* > | < #HEX_LITERAL: "0" ["x","X"] (["0"-"9","a"-"f","A"-"F"])+ > | < #OCTAL_LITERAL: "0" (["0"-"7"])* > | < LONG : ["l","L"] > | < DOUBLE : (["0"-"9"])+ "." (["0"-"9"])* ()? | "." (["0"-"9"])* ()? | (["0"-"9"])+ > | < FLOAT : ["f","F"] > | < #EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ > | < STRING: "\"" ( (~["\"","\\","\n","\r"]) | ("\\" ( ["n","t","b","r","f","\\","'","\""] | ["0"-"7"] ( ["0"-"7"] )? | ["0"-"3"] ["0"-"7"] ["0"-"7"] ) ) )* "\"" | "'" ( (~["'","\\","\n","\r"]) | ("\\" ( ["n","t","b","r","f","\\","'","\""] | ["0"-"7"] ( ["0"-"7"] )? | ["0"-"3"] ["0"-"7"] ["0"-"7"] ) ) )* "'" > } /* identifiers */ TOKEN : { < QUOTED: "[" (~["]"])+ "]" > | < IDENTIFIER: (|)* > | < #LETTER: [ "\u0024", "\u0041"-"\u005a", "\u005f", "\u0061"-"\u007a", "\u00c0"-"\u00d6", "\u00d8"-"\u00f6", "\u00f8"-"\u00ff", "\u0100"-"\u1fff", "\u3040"-"\u318f", "\u3300"-"\u337f", "\u3400"-"\u3d2d", "\u4e00"-"\u9fff", "\uf900"-"\ufaff" ] > | < #DIGIT: [ "\u0030"-"\u0039", "\u0660"-"\u0669", "\u06f0"-"\u06f9", "\u0966"-"\u096f", "\u09e6"-"\u09ef", "\u0a66"-"\u0a6f", "\u0ae6"-"\u0aef", "\u0b66"-"\u0b6f", "\u0be7"-"\u0bef", "\u0c66"-"\u0c6f", "\u0ce6"-"\u0cef", "\u0d66"-"\u0d6f", "\u0e50"-"\u0e59", "\u0ed0"-"\u0ed9", "\u1040"-"\u1049" ] > } /* separators */ TOKEN : { < LPAREN: "(" > | < RPAREN: ")" > } /* operators */ TOKEN : { < EQ: "=" | "==" > | < GT: ">" > | < LT: "<" > | < LE: "<=" > | < GE: ">=" > | < NE: "!=" | "<>" > | < ADD: "+" > | < SUB: "-" > | < MUL: "*" > | < DIV: "/" > | < POW: "^" > | < MOD: "%" > } // ---------------------------------------------------------------------------- // Grammar definitions String Name() : { Token t; } { t= { return t.image; } } String Quoted() : { Token t; } { t= { return t.image.substring(1,t.image.length()-1); } } Expression Parse() : { Expression e; } { e=Expression() { return e; } | { throw new ParseException("No expression provided"); } } Expression Expression() : { Expression e; } { e=OrExpression() { return e; } } Expression OrExpression() : { Expression l, r; } { l=XorExpression() ( r=XorExpression() { if ( l instanceof OrPredicate ) { ((OrPredicate)l).add((Predicate)r); } else { l = new OrPredicate((Predicate)l,(Predicate)r); } } )* { return l; } } Expression XorExpression() : { Expression l, r; } { l=AndExpression() ( r=AndExpression() { if ( l instanceof XorPredicate ) { ((XorPredicate)l).add((Predicate)r); } else { l = new XorPredicate((Predicate)l,(Predicate)r); } } )* { return l; } } Expression AndExpression() : { Expression l, r; } { l=EqualityExpression() ( r=EqualityExpression() { if ( l instanceof AndPredicate ) { ((AndPredicate)l).add((Predicate)r); } else { l = new AndPredicate((Predicate)l,(Predicate)r); } } )* { return l; } } Expression EqualityExpression() : { Expression l, r; Token t; int op; } { l=RelationalExpression() ( ( t= | t= ) r=RelationalExpression() { op = (t.kind==EQ ? ComparisonPredicate.EQ : ComparisonPredicate.NEQ); l = new ComparisonPredicate(op, l, r); } )* { return l; } } Expression RelationalExpression() : { Expression l, r; Token t; int op=-1; } { l=AdditiveExpression() ( ( t= | t= | t= | t= ) r=AdditiveExpression() { switch ( t.kind ) { case LT: op = ComparisonPredicate.LT; break; case GT: op = ComparisonPredicate.GT; break; case LE: op = ComparisonPredicate.LTEQ; break; case GE: op = ComparisonPredicate.GTEQ; break; } l = new ComparisonPredicate(op, l, r); } )* { return l; } } Expression AdditiveExpression() : { Expression l, r; Token t; int op=-1; } { l=MultiplicativeExpression() ( ( t= | t= | t= ) r=MultiplicativeExpression() { switch ( t.kind ) { case ADD: op = ArithmeticExpression.ADD; break; case SUB: op = ArithmeticExpression.SUB; break; case MOD: op = ArithmeticExpression.MOD; break; } l = new ArithmeticExpression(op, l, r); } )* { return l; } } Expression MultiplicativeExpression() : { Expression l, r; Token t; int op=-1; } { l=UnaryExpression() ( ( t= | t=
| t= ) r=UnaryExpression() { switch ( t.kind ) { case MUL: op = ArithmeticExpression.MUL; break; case DIV: op = ArithmeticExpression.DIV; break; case POW: op = ArithmeticExpression.POW; break; } l = new ArithmeticExpression(op, l, r); } )* { return l; } } Expression UnaryExpression() : { Expression e; Token t;} { ( t= | t= ) e=UnaryExpression() { if ( t.kind == SUB && e instanceof NumericLiteral ) { Number n = (Number)e.get(null); if ( n instanceof Integer ) { return new NumericLiteral(-1*n.intValue()); } if ( n instanceof Double ) { return new NumericLiteral(-1*n.doubleValue()); } if ( n instanceof Long ) { return new NumericLiteral(-1*n.longValue()); } if ( n instanceof Float ) { return new NumericLiteral(-1*n.floatValue()); } else { return new ArithmeticExpression(ArithmeticExpression.MUL, new NumericLiteral(-1), e); } } else if ( t.kind == SUB ) { return new ArithmeticExpression(ArithmeticExpression.MUL, new NumericLiteral(-1), e); } else { return e; } } | e=UnaryExpressionNotPlusMinus() { return e; } | e=PrimaryExpression() { return e; } } Expression UnaryExpressionNotPlusMinus() : { Expression e; } { e=UnaryExpression() { if ( e instanceof NotPredicate ) { return ((NotPredicate)e).getPredicate(); } else { if ( !(e instanceof Predicate) ) { throw new ParseException("Can't negate a non-predicate"); } else { return new NotPredicate((Predicate)e); } } } } Expression PrimaryExpression() : { Expression e; } { e=Literal() { return e; } | e=IfStatement() { return e; } | e=Identifier() { return e; } | e=Expression() { return e; } } Expression Literal() : { Token t; } { t= { return new NumericLiteral(Integer.parseInt(t.image)); } | t= { return new NumericLiteral(Long.parseLong(t.image.substring(0,t.image.length()-1))); } | t= { return new NumericLiteral(Float.parseFloat(t.image)); } | t= { return new NumericLiteral(Double.parseDouble(t.image)); } | t= { String s = unescape(t.image.substring(1, t.image.length()-1)); return new ObjectLiteral(s); } | { return new BooleanLiteral(true); } | { return new BooleanLiteral(false); } | { return new ObjectLiteral(null); } } Expression Identifier() : { String s; Function f=null; Expression e; } { s=Quoted() { return new ColumnExpression(s); } | s=Name() ( { f = FunctionTable.createFunction(s); } ( e=Expression() { f.addParameter(e); } ("," e=Expression() { f.addParameter(e); } )* )? )? { return f==null ? new ColumnExpression(s) : (Expression)f; } } Expression IfStatement() : /* * The disambiguating algorithm of JavaCC automatically binds dangling * else's to the innermost if statement. The LOOKAHEAD specification * is to tell JavaCC that we know what we are doing. */ { Expression p, t, e; } { p=Expression() t=Expression() e=Expression() { if ( !(p instanceof Predicate) ) throw new ParseException("IF-statement test must be a predicate"); return new IfExpression((Predicate)p, t, e); } }