📄 dql.g
字号:
header
{
package cn.myapps.core.dynaform.document.dql;
import org.hibernate.hql.ast.*;
import org.hibernate.hql.ast.util.*;
import cn.myapps.base.action.ParamsTable;
}
class DqlBaseParser extends Parser;
options
{
exportVocab=Dql;
buildAST=true;
k=3; // For 'not like', 'not in', etc.
}
tokens
{
// -- HQL Keyword tokens --
ALL="all";
ANY="any";
AND="and";
AS="as";
ASCENDING="asc";
AVG="avg";
BETWEEN="between";
CLASS="class";
COUNT="count";
DELETE="delete";
DESCENDING="desc";
DOT;
DISTINCT="distinct";
ELEMENTS="elements";
ESCAPE="escape";
EXISTS="exists";
FALSE="false";
FETCH="fetch";
FROM="from";
FULL="full";
GROUP="group";
HAVING="having";
IN="in";
INDICES="indices";
INNER="inner";
INSERT="insert";
INTO="into";
IS="is";
JOIN="join";
LEFT="left";
LIKE="like";
ILIKE="ilike";
MAX="max";
MIN="min";
NEW="new";
NOT="not";
NULL="null";
OR="or";
ORDER="order";
OUTER="outer";
PROPERTIES="properties";
RIGHT="right";
SELECT="select";
SET="set";
SOME="some";
SUM="sum";
TRUE="true";
UNION="union";
UPDATE="update";
VERSIONED="versioned";
WHERE="where";
// -- SQL tokens --
// These aren't part of HQL, but the SQL fragment parser uses the HQL lexer, so they need to be declared here.
CASE="case";
END="end";
ELSE="else";
THEN="then";
WHEN="when";
ON="on";
WITH="with";
// -- EJBQL tokens --
BOTH="both";
EMPTY="empty";
LEADING="leading";
MEMBER="member";
OBJECT="object";
OF="of";
TRAILING="trailing";
// -- Synthetic token types --
AGGREGATE; // One of the aggregate functions (e.g. min, max, avg)
ALIAS;
CONSTRUCTOR;
CASE2;
EXPR_LIST;
FILTER_ENTITY; // FROM element injected because of a filter expression (happens during compilation phase 2)
IN_LIST;
INDEX_OP;
IS_NOT_NULL;
IS_NULL; // Unary 'is null' operator.
METHOD_CALL;
NOT_BETWEEN;
NOT_IN;
NOT_LIKE;
NOT_ILIKE;
ORDER_ELEMENT;
QUERY;
RANGE;
ROW_STAR;
SELECT_FROM;
UNARY_MINUS;
UNARY_PLUS;
VECTOR_EXPR; // ( x, y, z )
WEIRD_IDENT; // Identifiers that were keywords when they came in.
// Literal tokens.
CONSTANT;
NUM_DOUBLE;
NUM_FLOAT;
NUM_LONG;
JAVA_CONSTANT;
}
{
/** True if this is a filter query (allow no FROM clause). **/
private boolean filter = false;
/**
* Sets the filter flag.
* @param f True for a filter query, false for a normal query.
*/
public void setFilter(boolean f) {
filter = f;
}
/**
* Returns true if this is a filter query, false if not.
* @return true if this is a filter query, false if not.
*/
public boolean isFilter() {
return filter;
}
/**
* This method is overriden in the sub class in order to provide the
* 'keyword as identifier' hack.
* @param token The token to retry as an identifier.
* @param ex The exception to throw if it cannot be retried as an identifier.
*/
public AST handleIdentifierError(Token token,RecognitionException ex) throws RecognitionException, TokenStreamException {
// Base implementation: Just re-throw the exception.
throw ex;
}
/**
* This method looks ahead and converts . <token> into . IDENT when
* appropriate.
*/
public void handleDotIdent() throws TokenStreamException {
}
/**
* Returns the negated equivalent of the expression.
* @param x The expression to negate.
*/
public AST negateNode(AST x) {
// Just create a 'not' parent for the default behavior.
return ASTUtil.createParent(astFactory, NOT, "not", x);
}
/**
* Returns the 'cleaned up' version of a comparison operator sub-tree.
* @param x The comparison operator to clean up.
*/
public AST processEqualityExpression(AST x) throws RecognitionException {
return x;
}
public void weakKeywords() throws TokenStreamException { }
public void processMemberOf(Token n,AST p,ASTPair currentAST) { }
}
setClause
: (SET^ assignment (COMMA! assignment)*)
;
assignment
: stateField EQ^ newValue
;
// "state_field" is the term used in the EJB3 sample grammar; used here for easy reference.
// it is basically a property ref
stateField
: path
;
// this still needs to be defined in the ejb3 spec; additiveExpression is currently just a best guess,
// although it is highly likely I would think that the spec may limit this even more tightly.
newValue
: concatenation
;
withClause
: WITH^ logicalExpression
;
// expressions
// Note that most of these expressions follow the pattern
// thisLevelExpression :
// nextHigherPrecedenceExpression
// (OPERATOR nextHigherPrecedenceExpression)*
// which is a standard recursive definition for a parsing an expression.
//
// Operator precedence in HQL
// lowest --> ( 7) OR
// ( 6) AND, NOT
// ( 5) equality: ==, <>, !=, is
// ( 4) relational: <, <=, >, >=,
// LIKE, NOT LIKE, BETWEEN, NOT BETWEEN, IN, NOT IN
// ( 3) addition and subtraction: +(binary) -(binary)
// ( 2) multiplication: * / %, concatenate: ||
// highest --> ( 1) +(unary) -(unary)
// [] () (method call) . (dot -- identifier qualification)
// aggregate function
// () (explicit parenthesis)
//
// Note that the above precedence levels map to the rules below...
// Once you have a precedence chart, writing the appropriate rules as below
// is usually very straightfoward
logicalExpression
: expression
;
// Main expression rule
expression
: logicalOrExpression
;
// level 7 - OR
logicalOrExpression
: logicalAndExpression ( OR^ logicalAndExpression )*
;
// level 6 - AND, NOT
logicalAndExpression
: negatedExpression ( AND^ negatedExpression )*
;
// NOT nodes aren't generated. Instead, the operator in the sub-tree will be
// negated, if possible. Expressions without a NOT parent are passed through.
negatedExpression!
{ weakKeywords(); } // Weak keywords can appear in an expression, so look ahead.
: NOT^ x:negatedExpression { #negatedExpression = negateNode(#x); }
| y:equalityExpression { #negatedExpression = #y; }
;
//## OP: EQ | LT | GT | LE | GE | NE | SQL_NE | LIKE;
// level 5 - EQ, NE
equalityExpression
: x:relationalExpression (
( EQ^
| is:IS^ { #is.setType(IS); } (NOT! { #is.setType(NE); } )?
| NE^
| ne:SQL_NE^ { #ne.setType(NE); }
) y:relationalExpression)* {
// Post process the equality expression to clean up 'is null', etc.
#equalityExpression = processEqualityExpression(#equalityExpression);
}
;
// level 4 - LT, GT, LE, GE, LIKE, NOT LIKE, BETWEEN, NOT BETWEEN
// NOTE: The NOT prefix for LIKE and BETWEEN will be represented in the
// token type. When traversing the AST, use the token type, and not the
// token text to interpret the semantics of these nodes.
relationalExpression
: concatenation (
( ( ( LT^ | GT^ | LE^ | GE^ ) additiveExpression )* )
// Disable node production for the optional 'not'.
| (n:NOT!)? (
// Represent the optional NOT prefix using the token type by
// testing 'n' and setting the token type accordingly.
(i:IN^ {
#i.setType( (n == null) ? IN : NOT_IN);
#i.setText( (n == null) ? "in" : "not in");
}
inList)
| (b:BETWEEN^ {
#b.setType( (n == null) ? BETWEEN : NOT_BETWEEN);
#b.setText( (n == null) ? "between" : "not between");
}
betweenList )
| (l:LIKE^ {
#l.setType( (n == null) ? LIKE : NOT_LIKE);
#l.setText( (n == null) ? "like" : "not like");
}
concatenation likeEscape)
| (l2:ILIKE^ {
#l2.setType( (n == null) ? ILIKE : NOT_ILIKE);
#l2.setText( (n == null) ? "ilike" : "not ilike");
}
concatenation likeEscape)
| (MEMBER! (OF!)? p:path! {
processMemberOf(n,#p,currentAST);
} ) )
)
;
likeEscape
: (ESCAPE^ concatenation)?
;
inList
: x:compoundExpr
{ #inList = #([IN_LIST,"inList"], #inList); }
;
betweenList
: concatenation AND! concatenation
;
//level 4 - string concatenation
concatenation
: additiveExpression
( c:CONCAT^ { #c.setType(EXPR_LIST); #c.setText("concatList"); }
additiveExpression
( CONCAT! additiveExpression )*
{ #concatenation = #([METHOD_CALL, "||"], #([IDENT, "concat"]), #c ); } )?
;
// level 3 - binary plus and minus
additiveExpression
: multiplyExpression ( ( PLUS^ | MINUS^ ) multiplyExpression )*
;
// level 2 - binary multiply and divide
multiplyExpression
: unaryExpression ( ( STAR^ | DIV^ ) unaryExpression )*
;
// level 1 - unary minus, unary plus, not
unaryExpression
: MINUS^ {#MINUS.setType(UNARY_MINUS);} unaryExpression
| PLUS^ {#PLUS.setType(UNARY_PLUS);} unaryExpression
| caseExpression
| quantifiedExpression
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -