| /* |
| * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0, which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * This Source Code may also be made available under the following Secondary |
| * Licenses when the conditions for such availability set forth in the |
| * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, |
| * version 2 with the GNU Classpath Exception, which is available at |
| * https://www.gnu.org/software/classpath/license.html. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 |
| */ |
| |
| /* |
| * EJBQL.g |
| * |
| * Created on November 12, 2001 |
| */ |
| |
| header |
| { |
| package com.sun.jdo.spi.persistence.support.ejb.ejbqlc; |
| |
| import antlr.MismatchedTokenException; |
| import antlr.MismatchedCharException; |
| import antlr.NoViableAltException; |
| import antlr.NoViableAltForCharException; |
| import antlr.TokenStreamRecognitionException; |
| |
| import java.util.ResourceBundle; |
| import org.glassfish.persistence.common.I18NHelper; |
| } |
| |
| //===== Lexical Analyzer Class Definitions ===== |
| |
| /** |
| * This class defines the lexical analysis for the EJBQL compiler. |
| * |
| * @author Michael Bouschen |
| * @author Shing Wai Chan |
| */ |
| class EJBQLLexer extends Lexer; |
| options |
| { |
| k = 2; |
| exportVocab = EJBQL; |
| charVocabulary = '\u0000'..'\uFFFE'; //NOI18N |
| caseSensitiveLiterals = false; |
| } |
| |
| tokens { |
| |
| // keywords |
| SELECT = "select"; //NOI18N |
| FROM = "from"; //NOI18N |
| WHERE = "where"; //NOI18N |
| DISTINCT = "distinct"; //NOI18N |
| OBJECT = "object"; //NOI18N |
| NULL = "null"; //NOI18N |
| TRUE = "true"; //NOI18N |
| FALSE = "false"; //NOI18N |
| NOT = "not"; //NOI18N |
| AND = "and"; //NOI18N |
| OR = "or"; //NOI18N |
| BETWEEN = "between"; //NOI18N |
| LIKE = "like"; //NOI18N |
| IN = "in"; //NOI18N |
| AS = "as"; //NOI18N |
| UNKNOWN = "unknown"; //NOI18N |
| EMPTY = "empty"; //NOI18N |
| MEMBER = "member"; //NOI18N |
| OF = "of"; //NOI18N |
| IS = "is"; //NOI18N |
| |
| // function/operator names treated as keywords |
| ESCAPE = "escape"; //NOI18N |
| CONCAT = "concat"; //NOI18N |
| SUBSTRING = "substring"; //NOI18N |
| LOCATE = "locate"; //NOI18N |
| LENGTH = "length"; //NOI18N |
| ABS = "abs"; //NOI18N |
| SQRT = "sqrt"; //NOI18N |
| MOD = "mod"; //NOI18N |
| |
| // aggregate functions |
| AVG = "avg"; //NOI18N |
| MAX = "max"; //NOI18N |
| MIN = "min"; //NOI18N |
| SUM = "sum"; //NOI18N |
| COUNT = "count"; //NOI18N |
| |
| // order by |
| ORDER = "order"; //NOI18N |
| BY = "by"; //NOI18N |
| ASC = "asc"; //NOI18N |
| DESC = "desc"; //NOI18N |
| |
| // relational operators |
| EQUAL; |
| NOT_EQUAL; |
| GE; |
| GT; |
| LE; |
| LT; |
| |
| // arithmetic operators |
| PLUS; |
| MINUS; |
| STAR; |
| DIV; |
| |
| // literals |
| STRING_LITERAL; |
| INT_LITERAL; |
| LONG_LITERAL; |
| FLOAT_LITERAL; |
| DOUBLE_LITERAL; |
| |
| // other token types |
| IDENT; |
| DOT; |
| INPUT_PARAMETER; |
| |
| // lexer internal token types |
| LPAREN; |
| RPAREN; |
| COMMA; |
| WS; |
| HEX_DIGIT; |
| EXPONENT; |
| FLOAT_SUFFIX; |
| UNICODE_DIGIT; |
| UNICODE_STR; |
| } |
| |
| { |
| /** |
| * The width of a tab stop. |
| * This value is used to calculate the correct column in a line |
| * conatining a tab character. |
| */ |
| protected static final int TABSIZE = 4; |
| |
| /** */ |
| protected static final int EOF_CHAR = 65535; // = (char) -1 = EOF |
| |
| /** I18N support. */ |
| protected final static ResourceBundle msgs = |
| I18NHelper.loadBundle(EJBQLLexer.class); |
| |
| /** |
| * |
| */ |
| public void tab() |
| { |
| int column = getColumn(); |
| int newColumn = (((column-1)/TABSIZE)+1)*TABSIZE+1; |
| setColumn(newColumn); |
| } |
| |
| /** */ |
| public void reportError(int line, int column, String s) |
| { |
| ErrorMsg.error(line, column, s); |
| } |
| |
| /** Report lexer exception errors caught in nextToken(). */ |
| public void reportError(RecognitionException e) |
| { |
| handleANTLRException(e); |
| } |
| |
| /** Lexer error-reporting function. */ |
| public void reportError(String s) |
| { |
| ErrorMsg.error(0, 0, s); |
| } |
| |
| /** Lexer warning-reporting function. */ |
| public void reportWarning(String s) |
| { |
| throw new EJBQLException(s); |
| } |
| |
| /** |
| * |
| */ |
| public static void handleANTLRException(ANTLRException ex) |
| { |
| if (ex instanceof MismatchedCharException) { |
| MismatchedCharException mismatched = (MismatchedCharException)ex; |
| if (mismatched.mismatchType == MismatchedCharException.CHAR) { |
| if (mismatched.foundChar == EOF_CHAR) { |
| ErrorMsg.error(mismatched.getLine(), mismatched.getColumn(), |
| //TBD: bundle key |
| I18NHelper.getMessage(msgs, "EXC_UnexpectedEOF")); //NOI18N |
| } |
| else { |
| ErrorMsg.error(mismatched.getLine(), mismatched.getColumn(), |
| I18NHelper.getMessage(msgs, "EXC_ExpectedCharFound", //NOI18N |
| String.valueOf((char)mismatched.expecting), |
| String.valueOf((char)mismatched.foundChar))); |
| } |
| return; |
| } |
| } |
| else if (ex instanceof MismatchedTokenException) { |
| MismatchedTokenException mismatched = (MismatchedTokenException)ex; |
| Token token = mismatched.token; |
| if ((mismatched.mismatchType == MismatchedTokenException.TOKEN) && |
| (token != null)) { |
| if (token.getType() == Token.EOF_TYPE) { |
| ErrorMsg.error(token.getLine(), token.getColumn(), |
| //TBD: bundle key |
| I18NHelper.getMessage(msgs, "EXC_UnexpectedEOF")); //NOI18N |
| } |
| else { |
| ErrorMsg.error(token.getLine(), token.getColumn(), |
| I18NHelper.getMessage(msgs, "EXC_SyntaxErrorAt", token.getText())); //NOI18N |
| } |
| return; |
| } |
| } |
| else if (ex instanceof NoViableAltException) { |
| Token token = ((NoViableAltException)ex).token; |
| if (token != null) { |
| if (token.getType() == Token.EOF_TYPE) { |
| ErrorMsg.error(token.getLine(), token.getColumn(), |
| //TBD: bundle key |
| I18NHelper.getMessage(msgs, "EXC_UnexpectedEOF")); //NOI18N |
| } |
| else { |
| ErrorMsg.error(token.getLine(), token.getColumn(), |
| I18NHelper.getMessage(msgs, "EXC_UnexpectedToken", token.getText())); //NOI18N |
| } |
| return; |
| } |
| } |
| else if (ex instanceof NoViableAltForCharException) { |
| NoViableAltForCharException noViableAlt = (NoViableAltForCharException)ex; |
| ErrorMsg.error(noViableAlt.getLine(), noViableAlt.getColumn(), |
| I18NHelper.getMessage(msgs, "EXC_UnexpectedChar", new Character(noViableAlt.foundChar)));//NOI18N |
| } |
| else if (ex instanceof TokenStreamRecognitionException) { |
| handleANTLRException(((TokenStreamRecognitionException)ex).recog); |
| } |
| |
| // no special handling from aboves matches the exception if this line is reached => |
| // make it a syntax error |
| int line = 0; |
| int column = 0; |
| if (ex instanceof RecognitionException) { |
| line = ((RecognitionException)ex).getLine(); |
| column = ((RecognitionException)ex).getColumn(); |
| } |
| ErrorMsg.error(line, column, I18NHelper.getMessage(msgs, "EXC_SyntaxError")); //NOI18N |
| } |
| } |
| |
| // OPERATORS |
| |
| LPAREN : '(' ; |
| RPAREN : ')' ; |
| COMMA : ',' ; |
| //DOT : '.' ; |
| EQUAL : '=' ; |
| NOT_EQUAL : "<>" ; //NOI18N |
| GE : ">=" ; //NOI18N |
| GT : ">" ; //NOI18N |
| LE : "<=" ; //NOI18N |
| LT : '<' ; |
| PLUS : '+' ; |
| DIV : '/' ; |
| MINUS : '-' ; |
| STAR : '*' ; |
| |
| // Whitespace -- ignored |
| WS |
| : ( ' ' |
| | '\t' |
| | '\f' |
| ) |
| { _ttype = Token.SKIP; } |
| ; |
| |
| NEWLINE |
| : ( "\r\n" //NOI18N |
| | '\r' |
| | '\n' |
| ) |
| { |
| newline(); |
| _ttype = Token.SKIP; |
| } |
| ; |
| |
| // input parameter |
| INPUT_PARAMETER |
| : '?' ('1'..'9') ('0'..'9')* |
| ; |
| |
| // string literals |
| STRING_LITERAL |
| : '\'' ( "''" | ESC | ~'\'' )* '\'' //NOI18N |
| ; |
| |
| // escape sequence -- note that this is protected; it can only be called |
| // from another lexer rule -- it will not ever directly return a token to |
| // the parser |
| // There are various ambiguities hushed in this rule. The optional |
| // '0'...'9' digit matches should be matched here rather than letting |
| // them go back to STRING_LITERAL to be matched. ANTLR does the |
| // right thing by matching immediately; hence, it's ok to shut off |
| // the FOLLOW ambig warnings. |
| protected |
| ESC |
| : '\\' |
| ( options { warnWhenFollowAmbig = false; } |
| : 'n' |
| | 'r' |
| | 't' |
| | 'b' |
| | 'f' |
| | '"' //NOI18N |
| // Note, EJBQL uses a quote to escape a quote |
| // | '\'' |
| | '\\' |
| | ('u')+ HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT |
| | ('0'..'3') |
| ( |
| options { |
| warnWhenFollowAmbig = false; |
| } |
| : ('0'..'7') |
| ( |
| options { |
| warnWhenFollowAmbig = false; |
| } |
| : '0'..'7' |
| )? |
| )? |
| | ('4'..'7') |
| ( |
| options { |
| warnWhenFollowAmbig = false; |
| } |
| : ('0'..'7') |
| )? |
| )? |
| ; |
| |
| // hexadecimal digit |
| protected |
| HEX_DIGIT |
| : ('0'..'9'|'A'..'F'|'a'..'f') |
| ; |
| |
| // a numeric literal |
| INT_LITERAL |
| { |
| boolean isDecimal=false; |
| int tokenType = DOUBLE_LITERAL; |
| } |
| : '.' {_ttype = DOT;} |
| (('0'..'9')+ {tokenType = DOUBLE_LITERAL;} |
| (EXPONENT)? |
| (tokenType = FLOATINGPOINT_SUFFIX)? |
| {_ttype = tokenType;} )? |
| | ( '0' {isDecimal = true;} // special case for just '0' |
| ( ('x'|'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 therefor ok to hush warning. |
| options { |
| warnWhenFollowAmbig=false; |
| } |
| : HEX_DIGIT |
| )+ |
| | ('0'..'7')+ // octal |
| )? |
| | ('1'..'9') ('0'..'9')* {isDecimal=true;} // non-zero decimal |
| ) |
| ( ('l'|'L') { _ttype = LONG_LITERAL; } |
| |
| // only check to see if it's a float if looks like decimal so far |
| | {isDecimal}? |
| {tokenType = DOUBLE_LITERAL;} |
| ( '.' ('0'..'9')* (EXPONENT)? |
| ( tokenType = FLOATINGPOINT_SUFFIX)? |
| | EXPONENT ( tokenType = FLOATINGPOINT_SUFFIX)? |
| | tokenType = FLOATINGPOINT_SUFFIX |
| ) |
| { _ttype = tokenType; } |
| )? |
| ; |
| |
| // a couple protected methods to assist in matching floating point numbers |
| protected |
| EXPONENT |
| : ('e'|'E') ('+'|'-')? ('0'..'9')+ |
| ; |
| |
| protected |
| FLOATINGPOINT_SUFFIX returns [int tokenType] |
| : 'f' { tokenType = FLOAT_LITERAL; } |
| | 'F' { tokenType = FLOAT_LITERAL; } |
| | 'd' { tokenType = DOUBLE_LITERAL; } |
| | 'D' { tokenType = DOUBLE_LITERAL; } |
| ; |
| |
| // an identifier. Note that testLiterals is set to true! This means |
| // that after we match the rule, we look in the literals table to see |
| // if it's a literal or really an identifer |
| |
| IDENT |
| options {paraphrase = "an identifier"; testLiterals=true;} //NOI18N |
| : ( 'a'..'z' |
| | 'A'..'Z' |
| | '_' |
| | '$' |
| | UNICODE_ESCAPE |
| | c1:'\u0080'..'\uFFFE' |
| { |
| if (!Character.isJavaIdentifierStart(c1)) { |
| ErrorMsg.error(getLine(), getColumn(), |
| I18NHelper.getMessage(msgs, "EXC_UnexpectedChar", String.valueOf(c1)));//NOI18N |
| } |
| } |
| ) |
| ( 'a'..'z' |
| | 'A'..'Z' |
| | '_' |
| | '$' |
| | '0'..'9' |
| | UNICODE_ESCAPE |
| | c2:'\u0080'..'\uFFFE' |
| { |
| if (!Character.isJavaIdentifierPart(c2)) { |
| ErrorMsg.error(getLine(), getColumn(), |
| I18NHelper.getMessage(msgs, "EXC_UnexpectedChar", String.valueOf(c2)));//NOI18N |
| } |
| } |
| )* |
| ; |
| |
| protected |
| UNICODE_ESCAPE |
| : '\\' ('u')+ HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT |
| { |
| try { |
| String tmp = text.toString(); |
| char c = (char)Integer.parseInt(tmp.substring(tmp.length() - 4, |
| tmp.length()), 16); |
| // problems using ANTLR feature $setText => use generated code |
| text.setLength(_begin); |
| text.append(new Character(c).toString()); |
| } |
| catch (NumberFormatException ex) { |
| ErrorMsg.fatal(I18NHelper.getMessage(msgs, |
| "ERR_UnexpectedExceptionUnicode"), ex); //NOI18N |
| } |
| } |
| ; |
| |
| //===== Parser Class Definitions ===== |
| |
| /** |
| * This class defines the syntax analysis (parser) of the EJBQL compiler. |
| * |
| * @author Michael Bouschen |
| */ |
| class EJBQLParser extends Parser; |
| |
| options { |
| k = 2; // two token lookahead |
| exportVocab = EJBQL; |
| buildAST = true; |
| ASTLabelType = "EJBQLAST"; // NOI18N |
| } |
| |
| tokens |
| { |
| // root |
| QUERY; |
| |
| // special from clause tokenes |
| RANGE; |
| |
| // special dot expresssion |
| CMP_FIELD_ACCESS; |
| SINGLE_CMR_FIELD_ACCESS; |
| COLLECTION_CMR_FIELD_ACCESS; |
| |
| // identifier |
| IDENTIFICATION_VAR; |
| IDENTIFICATION_VAR_DECL; |
| ABSTRACT_SCHEMA_NAME; |
| CMP_FIELD; |
| SINGLE_CMR_FIELD; |
| COLLECTION_CMR_FIELD; |
| |
| // operators |
| UNARY_MINUS; |
| UNARY_PLUS; |
| NOT_BETWEEN; |
| NOT_LIKE; |
| NOT_IN; |
| NOT_NULL; |
| NOT_EMPTY; |
| NOT_MEMBER; |
| } |
| |
| { |
| /** I18N support. */ |
| protected final static ResourceBundle msgs = |
| I18NHelper.loadBundle(EJBQLParser.class); |
| |
| /** ANTLR method called when an error was detected. */ |
| public void reportError(RecognitionException ex) |
| { |
| EJBQLLexer.handleANTLRException(ex); |
| } |
| |
| /** ANTLR method called when an error was detected. */ |
| public void reportError(String s) |
| { |
| ErrorMsg.error(0, 0, s); |
| } |
| |
| /** */ |
| public void reportError(int line, int column, String s) |
| { |
| ErrorMsg.error(line, column, s); |
| } |
| |
| /** ANTLR method called when a warning was detected. */ |
| public void reportWarning(String s) |
| { |
| throw new EJBQLException(s); |
| } |
| |
| /** |
| * This method wraps the root rule in order to handle |
| * ANTLRExceptions thrown during parsing. |
| */ |
| public void query () |
| { |
| try { |
| root(); |
| } |
| catch (ANTLRException ex) { |
| EJBQLLexer.handleANTLRException(ex); |
| } |
| } |
| } |
| |
| // ---------------------------------- |
| // rules |
| // ---------------------------------- |
| |
| root! |
| : s:selectClause f:fromClause w:whereClause o:orderbyClause EOF! |
| { |
| // switch the order of subnodes: the fromClause should come first, |
| // because it declares the identification variables used in the |
| // selectClause and the whereClause |
| #root = #(#[QUERY,"QUERY"], #f, #s, #w); //NOI18N |
| if (#o != null) { |
| #root.addChild(#o); |
| } |
| } |
| ; |
| |
| selectClause |
| : SELECT^ ( DISTINCT )? projection |
| ; |
| |
| projection |
| : p:pathExpr |
| { |
| if (#p.getType() != DOT) { |
| ErrorMsg.error(#p.getLine(), #p.getColumn(), |
| I18NHelper.getMessage(msgs, "EXC_SyntaxErrorAt", //NOI18N |
| #p.getText())); |
| } |
| } |
| | OBJECT^ LPAREN! IDENT RPAREN! |
| | ( AVG^ | MAX^ | MIN^ | SUM^ | COUNT^ ) LPAREN! (DISTINCT)? pathExpr RPAREN! |
| ; |
| |
| fromClause |
| : FROM^ identificationVarDecl ( COMMA! identificationVarDecl )* |
| ; |
| |
| identificationVarDecl |
| : collectionMemberDecl |
| | rangeVarDecl |
| ; |
| |
| collectionMemberDecl |
| : IN^ LPAREN! pathExpr RPAREN! ( AS! )? IDENT |
| ; |
| |
| rangeVarDecl! |
| : abstractSchemaName:. ( AS! )? i:IDENT |
| { |
| #abstractSchemaName.setType(ABSTRACT_SCHEMA_NAME); |
| #rangeVarDecl = #(#[RANGE,"RANGE"], #abstractSchemaName, #i); //NOI18N |
| } |
| ; |
| |
| whereClause |
| : WHERE^ conditionalExpr |
| | // empty rule |
| { |
| // Add where true in the case the where clause is omitted |
| #whereClause = #(#[WHERE,"WHERE"], #[TRUE,"TRUE"]); //NOI18N |
| } |
| ; |
| |
| pathExpr |
| : IDENT ( DOT^ IDENT )* |
| ; |
| |
| conditionalExpr |
| : conditionalTerm ( OR^ conditionalTerm )* |
| ; |
| |
| conditionalTerm |
| : conditionalFactor ( AND^ conditionalFactor )* |
| ; |
| |
| conditionalFactor |
| : ( NOT^ )? conditionalPrimary |
| ; |
| |
| conditionalPrimary |
| : ( betweenExpr )=> betweenExpr |
| | ( likeExpr )=> likeExpr |
| | ( inExpr )=> inExpr |
| | ( nullComparisonExpr )=> nullComparisonExpr |
| | ( emptyCollectionComparisonExpr )=> emptyCollectionComparisonExpr |
| | ( collectionMemberExpr )=> collectionMemberExpr |
| | comparisonExpr |
| ; |
| |
| betweenExpr |
| : arithmeticExpr ( n:NOT! )? BETWEEN^ arithmeticExpr AND! arithmeticExpr |
| { |
| // map NOT BETWEEN to single operator NOT_BETWEEN |
| if (#n != null) |
| #BETWEEN.setType(NOT_BETWEEN); |
| } |
| ; |
| |
| likeExpr |
| : pathExpr ( n:NOT! )? LIKE^ ( stringLiteral | parameter ) escape |
| { |
| // map NOT LIKE to single operator NOT_LIKE |
| if(#n != null) |
| #LIKE.setType(NOT_LIKE); |
| } |
| ; |
| |
| escape |
| : ESCAPE^ ( stringLiteral | parameter ) |
| | // empty rule |
| ; |
| |
| inExpr |
| : pathExpr ( n:NOT! )? IN^ LPAREN! inCollectionElement ( COMMA! inCollectionElement )* RPAREN! |
| { |
| // map NOT BETWEEN to single operator NOT_IN |
| if (#n != null) |
| #IN.setType(NOT_IN); |
| } |
| ; |
| |
| nullComparisonExpr |
| : ( pathExpr | parameter ) IS! ( n:NOT! )? NULL^ |
| { |
| // map NOT NULL to single operator NOT_NULL |
| if (#n != null) |
| #NULL.setType(NOT_NULL); |
| } |
| ; |
| |
| emptyCollectionComparisonExpr |
| : pathExpr IS! ( n:NOT! )? EMPTY^ |
| { |
| // map IS NOT EMPTY to single operator NOT_EMPTY |
| if (#n != null) |
| #EMPTY.setType(NOT_EMPTY); |
| } |
| ; |
| |
| collectionMemberExpr |
| : ( pathExpr | parameter ) |
| ( n:NOT! )? MEMBER^ ( OF! )? pathExpr |
| { |
| // map NOT MEMBER to single operator NOT_MEMBER |
| if (#n != null) |
| #MEMBER.setType(NOT_MEMBER); |
| } |
| ; |
| |
| comparisonExpr |
| : arithmeticExpr ( ( EQUAL^ | NOT_EQUAL^ | LT^ | LE^ | GT^ | GE^ ) arithmeticExpr )* |
| ; |
| |
| arithmeticExpr |
| : arithmeticTerm ( ( PLUS^ | MINUS^ ) arithmeticTerm )* |
| ; |
| |
| arithmeticTerm |
| : arithmeticFactor ( (STAR^ | DIV^ ) arithmeticFactor )* |
| ; |
| |
| arithmeticFactor |
| : MINUS^ {#MINUS.setType(UNARY_MINUS);} arithmeticFactor |
| | PLUS^ {#PLUS.setType(UNARY_PLUS);} arithmeticFactor |
| | arithmeticPrimary |
| ; |
| |
| arithmeticPrimary |
| : pathExpr |
| | literal |
| | LPAREN! conditionalExpr RPAREN! |
| | parameter |
| | function |
| ; |
| |
| function |
| : CONCAT^ LPAREN! conditionalExpr COMMA! conditionalExpr RPAREN! |
| | SUBSTRING^ LPAREN! conditionalExpr COMMA! conditionalExpr COMMA! conditionalExpr RPAREN! |
| | LENGTH^ LPAREN! conditionalExpr RPAREN! |
| | LOCATE^ LPAREN! conditionalExpr COMMA! conditionalExpr ( COMMA! conditionalExpr )? RPAREN! |
| | ABS^ LPAREN! conditionalExpr RPAREN! |
| | SQRT^ LPAREN! conditionalExpr RPAREN! |
| | MOD^ LPAREN! conditionalExpr COMMA! conditionalExpr RPAREN! |
| ; |
| |
| parameter |
| : INPUT_PARAMETER |
| ; |
| |
| orderbyClause |
| : ORDER^ BY! orderbyItem ( COMMA! orderbyItem )* |
| | // empty rule |
| ; |
| |
| orderbyItem |
| : pathExpr direction |
| ; |
| |
| direction |
| : ASC |
| | DESC |
| | // empty rule |
| { |
| // ASC is added as default |
| #direction = #[ASC, "ASC"]; //NOI18N |
| } |
| ; |
| |
| inCollectionElement |
| : literal |
| | parameter |
| ; |
| |
| literal |
| : TRUE |
| | FALSE |
| | stringLiteral |
| | INT_LITERAL |
| | LONG_LITERAL |
| | FLOAT_LITERAL |
| | DOUBLE_LITERAL |
| ; |
| |
| stringLiteral |
| : s:STRING_LITERAL |
| { |
| // strip quotes from the token text |
| String text = #s.getText(); |
| #s.setText(text.substring(1,text.length()-1)); |
| } |
| ; |