📄 dql.g
字号:
| atom
;
caseExpression
: CASE^ (whenClause)+ (elseClause)? END!
| CASE^ { #CASE.setType(CASE2); } unaryExpression (altWhenClause)+ (elseClause)? END!
;
whenClause
: (WHEN^ logicalExpression THEN! unaryExpression)
;
altWhenClause
: (WHEN^ unaryExpression THEN! unaryExpression)
;
elseClause
: (ELSE^ unaryExpression)
;
quantifiedExpression
: ( SOME^ | EXISTS^ | ALL^ | ANY^ )
( identifier | collectionExpr)
;
// level 0 - expression atom
// ident qualifier ('.' ident ), array index ( [ expr ] ),
// method call ( '.' ident '(' exprList ') )
atom
: primaryExpression
(
DOT^ identifier
( options { greedy=true; } :
( op:OPEN^ {#op.setType(METHOD_CALL);} exprList CLOSE! ) )?
| lb:OPEN_BRACKET^ {#lb.setType(INDEX_OP);} expression CLOSE_BRACKET!
)*
;
// level 0 - the basic element of an expression
primaryExpression
: identPrimary ( options {greedy=true;} : DOT^ "class" )?
| constant
| COLON^ identifier
// TODO: Add parens to the tree so the user can control the operator evaluation order.
| OPEN! (expressionOrVector) CLOSE!
| PARAM^ (NUM_INT)?
;
// This parses normal expression and a list of expressions separated by commas. If a comma is encountered
// a parent VECTOR_EXPR node will be created for the list.
expressionOrVector!
: e:expression ( v:vectorExpr )? {
// If this is a vector expression, create a parent node for it.
if (#v != null)
#expressionOrVector = #([VECTOR_EXPR,"{vector}"], #e, #v);
else
#expressionOrVector = #e;
}
;
vectorExpr
: COMMA! expression (COMMA! expression)*
;
// identifier, followed by member refs (dot ident), or method calls.
// NOTE: handleDotIdent() is called immediately after the first IDENT is recognized because
// the method looks a head to find keywords after DOT and turns them into identifiers.
identPrimary
: identifier { handleDotIdent(); }
( options { greedy=true; } : DOT^ ( identifier | ELEMENTS | o:OBJECT { #o.setType(IDENT); } ) )*
( options { greedy=true; } :
( op:OPEN^ { #op.setType(METHOD_CALL);} exprList CLOSE! )
)?
// Also allow special 'aggregate functions' such as count(), avg(), etc.
| aggregate
;
//## aggregate:
//## ( aggregateFunction OPEN path CLOSE ) | ( COUNT OPEN STAR CLOSE ) | ( COUNT OPEN (DISTINCT | ALL) path CLOSE );
//## aggregateFunction:
//## COUNT | 'sum' | 'avg' | 'max' | 'min';
aggregate
: ( SUM^ | AVG^ | MAX^ | MIN^ ) OPEN! additiveExpression CLOSE! { #aggregate.setType(AGGREGATE); }
// Special case for count - It's 'parameters' can be keywords.
| COUNT^ OPEN! ( STAR { #STAR.setType(ROW_STAR); } | ( ( DISTINCT | ALL )? ( path | collectionExpr ) ) ) CLOSE!
| collectionExpr
;
//## collection: ( OPEN query CLOSE ) | ( 'elements'|'indices' OPEN path CLOSE );
collectionExpr
: (ELEMENTS^ | INDICES^) OPEN! path CLOSE!
;
// NOTE: compoundExpr can be a 'path' where the last token in the path is '.elements' or '.indicies'
compoundExpr
: collectionExpr
| path
| (OPEN! ( (expression (COMMA! expression)*) ) CLOSE!)
;
exprList
{
AST trimSpec = null;
}
: (t:TRAILING {#trimSpec = #t;} | l:LEADING {#trimSpec = #l;} | b:BOTH {#trimSpec = #b;})?
{ if(#trimSpec != null) #trimSpec.setType(IDENT); }
(
expression
)?
;
constant
: NUM_INT
| NUM_FLOAT
| NUM_LONG
| NUM_DOUBLE
| QUOTED_STRING
| NULL
| TRUE
| FALSE
| EMPTY
;
//## quantifiedExpression: 'exists' | ( expression 'in' ) | ( expression OP 'any' | 'some' ) collection;
//## compoundPath: path ( OPEN_BRACKET expression CLOSE_BRACKET ( '.' path )? )*;
//## path: identifier ( '.' identifier )*;
path
: identifier ( DOT^ { weakKeywords(); } identifier )*
;
// Wraps the IDENT token from the lexer, in order to provide
// 'keyword as identifier' trickery.
identifier
: IDENT
exception
catch [RecognitionException ex]
{
identifier_AST = handleIdentifierError(LT(1),ex);
}
;
// **** LEXER ******************************************************************
class DqlBaseLexer extends Lexer;
options {
exportVocab=Dql; // call the vocabulary "Hql"
testLiterals = false;
k=2; // needed for newline, and to distinguish '>' from '>='.
// HHH-241 : Quoted strings don't allow unicode chars - This should fix it.
charVocabulary='\u0000'..'\uFFFE'; // Allow any char but \uFFFF (16 bit -1, ANTLR's EOF character)
caseSensitive = false;
caseSensitiveLiterals = false;
}
// -- Declarations --
{
// NOTE: The real implementations are in the subclass.
protected void setPossibleID(boolean possibleID) {}
}
// -- Keywords --
EQ: '=';
LT: '<';
GT: '>';
SQL_NE: "<>";
NE: "!=" | "^=";
LE: "<=";
GE: ">=";
COMMA: ',';
OPEN: '(';
CLOSE: ')';
OPEN_BRACKET: '[';
CLOSE_BRACKET: ']';
CONCAT: "||";
PLUS: '+';
MINUS: '-';
STAR: '*';
DIV: '/';
COLON: ':';
PARAM: '?';
IDENT options { testLiterals=true; }
: ID_START_LETTER ( ID_LETTER|'.' )*
{
// Setting this flag allows the grammar to use keywords as identifiers, if necessary.
setPossibleID(true);
}
;
protected
ID_START_LETTER
: '_'
| '$'
| '#'
| 'a'..'z'
| '\u0080'..'\ufffe' // HHH-558 : Allow unicode chars in identifiers
;
protected
ID_LETTER
: ID_START_LETTER
| '0'..'9'
;
QUOTED_STRING
: '\'' ( (ESCqs)=> ESCqs | ~'\'' )* '\''
;
protected
ESCqs
:
'\'' '\''
;
WS : ( ' '
| '\t'
| '\r' '\n' { newline(); }
| '\n' { newline(); }
| '\r' { newline(); }
)
{$setType(Token.SKIP);} //ignore this token
;
//--- From the Java example grammar ---
// a numeric literal
NUM_INT
{boolean isDecimal=false; Token t=null;}
: '.' {_ttype = DOT;}
( ('0'..'9')+ (EXPONENT)? (f1:FLOAT_SUFFIX {t=f1;})?
{
if (t != null && t.getText().toUpperCase().indexOf('F')>=0)
{
_ttype = NUM_FLOAT;
}
else
{
_ttype = NUM_DOUBLE; // assume double
}
}
)?
| ( '0' {isDecimal = true;} // special case for just '0'
( ('x')
( // hex
// the 'e'|'E' and float suffix stuff look
// like hex digits, hence the (...)+ doesn't
// know when to stop: ambig. ANTLR resolves
// it correctly by matching immediately. It
// is therefore ok to hush warning.
options { warnWhenFollowAmbig=false; }
: HEX_DIGIT
)+
| ('0'..'7')+ // octal
)?
| ('1'..'9') ('0'..'9')* {isDecimal=true;} // non-zero decimal
)
( ('l') { _ttype = NUM_LONG; }
// only check to see if it's a float if looks like decimal so far
| {isDecimal}?
( '.' ('0'..'9')* (EXPONENT)? (f2:FLOAT_SUFFIX {t=f2;})?
| EXPONENT (f3:FLOAT_SUFFIX {t=f3;})?
| f4:FLOAT_SUFFIX {t=f4;}
)
{
if (t != null && t.getText().toUpperCase() .indexOf('F') >= 0)
{
_ttype = NUM_FLOAT;
}
else
{
_ttype = NUM_DOUBLE; // assume double
}
}
)?
;
// hexadecimal digit (again, note it's protected!)
protected
HEX_DIGIT
: ('0'..'9'|'a'..'f')
;
// a couple protected methods to assist in matching floating point numbers
protected
EXPONENT
: ('e') ('+'|'-')? ('0'..'9')+
;
protected
FLOAT_SUFFIX
: 'f'|'d'
;
//**********************************
class ExprTreeParser extends TreeParser;
/*
options {
importVocab=DQL;
}
*/
expr[ParamsTable params, int side, int opt] returns [String r=""]
{ String a,b; }
: #(AND a=expr[params,1,AND] b=expr[params,2,AND]) {r = "("+a+" AND "+b+")";}
| #(OR a=expr[params,1,OR] b=expr[params,2,OR]) {r = "("+a+" OR "+b+")";}
| #(EQ a=expr[params,1,EQ] b=expr[params,2,EQ]) {r = "("+a+" = "+b+")";}
| #(LT a=expr[params,1,LT] b=expr[params,2,LT]) {r = "("+a+" < "+b+")";}
| #(GT a=expr[params,1,GT] b=expr[params,2,GT]) {r = "("+a+" > "+b+")";}
| #(NE a=expr[params,1,NE] b=expr[params,2,NE]) {r = "("+a+" <> "+b+")";}
| #(LE a=expr[params,1,LE] b=expr[params,2,LE]) {r = "("+a+" <= "+b+")";}
| #(GE a=expr[params,1,GE] b=expr[params,2,GE]) {r = "("+a+" >= "+b+")";}
| #(CONCAT a=expr[params,1,CONCAT] b=expr[params,2,CONCAT]) {r = "("+a+" || "+b+")";}
| #(PLUS a=expr[params,1,PLUS] b=expr[params,2,PLUS]) {r = "("+a+" + "+b+")";}
| #(MINUS a=expr[params,1,MINUS] b=expr[params,2,MINUS]) {r = "("+a+" - "+b+")";}
| #(STAR a=expr[params,1,STAR] b=expr[params,2,STAR]) {r = "("+a+" * "+b+")";}
| #(DIV a=expr[params,1,DIV] b=expr[params,2,DIV]) {r = "("+a+" / "+b+")";}
| #(LIKE a=expr[params,1,LIKE] b=expr[params,2,LIKE]) {r = "("+a+" LIKE "+b+")";}
| #(ILIKE a=expr[params,1,ILIKE] b=expr[params,2,ILIKE]) {r = "("+a+" LIKE "+b+")";}
| #(NOT_LIKE a=expr[params,1,NOT_LIKE] b=expr[params,2,NOT_LIKE]) {r = "("+a+" NOT LIKE "+b+")";}
| #(NOT_ILIKE a=expr[params,1,NOT_ILIKE] b=expr[params,2,NOT_ILIKE]) {r = "(LOWER("+a+") NOT LIKE LOWER("+b+"))";}
| #(IN a=expr[params,1,IN] b=expr[params,2,IN]) {r = "("+a+" IN "+b+")";}
| #(NOT_IN a=expr[params,1,NOT_IN] b=expr[params,2,NOT_IN]) {r = "("+a+" NOT IN "+b+")";}
| #(IS a=expr[params,1,IS] b=expr[params,2,IS]) {r = "("+a+" IS "+b+")";}
| #(NOT_IS a=expr[params,1,NOT_IS] b=expr[params,2,NOT_IS]) {r = "("+a+" NOT_IS "+b+")";}
| i3:IDENT{r = DQLASTUtil.transTo(i3.getText(),params,side,IDENT,opt);}
| i4:NUM_INT {r = DQLASTUtil.transTo(i4.getText(),params,side,NUM_INT,opt);}
| i5:NUM_FLOAT {r = DQLASTUtil.transTo(i5.getText(),params,side,NUM_FLOAT,opt);}
| i6:NUM_LONG {r = DQLASTUtil.transTo(i6.getText(),params,side,NUM_LONG,opt);}
| i7:NUM_DOUBLE {r = DQLASTUtil.transTo(i7.getText(),params,side,NUM_DOUBLE,opt);}
| i8:QUOTED_STRING {r = DQLASTUtil.transTo(i8.getText(),params,side,QUOTED_STRING,opt);}
| i9:NULL {r = DQLASTUtil.transTo(i9.getText(),params,side,NULL,opt);}
| i10:TRUE {r = DQLASTUtil.transTo(i10.getText(),params,side,TRUE,opt);}
| i11:FALSE {r = DQLASTUtil.transTo(i11.getText(),params,side,FALSE,opt);}
| i12:EMPTY {r = DQLASTUtil.transTo(i12.getText(),params,side,EMPTY,opt);}
| i13:IN_LIST {r = DQLASTUtil.transTo(DQLASTUtil.inListToString(i13),params,side,IN_LIST,opt);}
;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -