AQL Syntax Specification
The following Antlr grammar expresses the AQL syntax.
// Author: Bostjan Lah
// (c) Copyright, Marand, http://www.marand.com
// Licensed under LGPL: http://www.gnu.org/copyleft/lesser.html
// Based on AQL grammar by Ocean Informatics: http://www.openehr.org/wiki/download/attachments/2949295/EQL_v0.6.grm?version=1&modificationDate=1259650833000
grammar Aql;
options {
output=AST;
ASTLabelType=CommonTree;
backtrack=true;
memoize=true;
}
tokens {
ORDERDESC;
ORDERASC;
}
@header {
package com.marand.thinkehr.aql.antlr;
import com.marand.thinkehr.aql.AqlRecognitionException;
}
@lexer::header{
package com.marand.thinkehr.aql.antlr;
import com.marand.thinkehr.aql.AqlRecognitionException;
}
@members{
public void displayRecognitionError(String[] tokenNames, RecognitionException e)
{
throw new AqlRecognitionException(e);
}
}
// Rule Definitions
//<Query> ::= <Select> <From>
// | <Select> <From> <Where>
// | <Select> <From> <OrderBy> ! is this allowed?
// | <Select> <From> <Where> <OrderBy>
query : select from where? orderBy? ';'!? EOF!;
//<Select> ::= 'SELECT' <SelectExpr>
// | 'SELECT' <TOP> <SelectExpr>
select : SELECT top? selectExpr -> ^(SELECT top? selectExpr);
//<Top> ::= 'TOP' Integer
// | 'TOP' Integer 'BACKWARD'
// | 'TOP' Integer 'FORWARD'
top : TOP INTEGER FORWARD? -> ^(TOP INTEGER)
| TOP INTEGER BACKWARD -> ^(TOP INTEGER BACKWARD);
//<Where> ::= 'WHERE' <IdentifiedExpr>
where : WHERE identifiedExpr -> ^(WHERE identifiedExpr);
//<OrderBy> ::= 'ORDER BY' <OrderBySeq>
orderBy : ORDERBY orderBySeq -> ^(ORDERBY orderBySeq);
//<OrderBySeq> ::= <OrderByExpr>
// | <OrderByExpr> ',' <OrderBySeq>
orderBySeq
: orderByExpr (','! orderByExpr)*;
//<OrderByExpr> ::= <IdentifiedPath>
// | <IdentifiedPath> 'DESCENDING'
// | <IdentifiedPath> 'ASCENDING'
// | <IdentifiedPath> 'DESC'
// | <IdentifiedPath> 'ASC'
orderByExpr
: identifiedPath (DESCENDING|DESC) -> ^(ORDERDESC identifiedPath)
| identifiedPath (ASCENDING|ASC)? -> ^(ORDERASC identifiedPath);
//<SelectExpr> ::= <IdentifiedPathSeq>
selectExpr
: identifiedPathSeq;
//! When multiple paths provided, each IdentifiedPath must represent an object of type DataValue
//<IdentifiedPathSeq> ::= <IdentifiedPath//>
// | <IdentifiedPath> 'as' Identifier
// | <IdentifiedPath> ',' <IdentifiedPathSeq>
// | <IdentifiedPath> 'as' Identifier ',' <IdentifiedPathSeq>
identifiedPathSeq
: selectVar (','! selectVar)*;
selectVar
: identifiedPath^ asIdentifier?;
asIdentifier
: AS IDENTIFIER;
//<From> ::= 'FROM' <FromExpr> ! stop or/and without root class
// | 'FROM' <FromEHR> <ContainsExpr>
// | 'FROM' <FromEHR>
//! 'FROM' <ContainsOr>
from : FROM fromExpr -> ^(FROM fromExpr)
| FROM ehrContains -> ^(FROM ehrContains);
//<FromEHR> ::= 'EHR' <StandardPredicate>
// | 'EHR' Identifier <StandardPredicate>
// | 'EHR' Identifier
fromEHR : EHR IDENTIFIER -> ^(EHR IDENTIFIER)
| EHR IDENTIFIER standardPredicate -> ^(EHR IDENTIFIER standardPredicate)
| EHR standardPredicate -> ^(EHR standardPredicate);
ehrContains
: fromEHR (CONTAINS^ contains)?;
//<IdentifiedExpr> ::= <IdentifiedEquality>
// | <IdentifiedExprBoolean>
// |'(' <IdentifiedExprBoolean> ')'
//identifiedExpr
// : identifiedExprBoolean;
// | '('! identifiedExprBoolean ')'!;
//<IdentifiedExprBoolean> ::= <IdentifiedExpr> 'OR' <IdentifiedExpr>
// | <IdentifiedExpr> 'AND' <IdentifiedExpr>
// | <IdentifiedExpr> 'XOR' <IdentifiedExpr>
// | 'NOT''(' <IdentifiedExprBoolean> ')'
// | 'NOT' <IdentifiedEquality>
identifiedExpr
: identifiedExprAnd ((OR|XOR)^ identifiedExprAnd)*;
identifiedExprAnd
: identifiedEquality (AND^ identifiedEquality)*;
//<IdentifiedEquality> ::= <IdentifiedOperand> ComparableOperator <IdentifiedOperand>
// | <IdentifiedOperand> 'matches' '{' <MatchesOperand> '}'
// | <IdentifiedOperand> 'matches' RegExPattern
// | 'EXISTS' <IdentifiedPath>
identifiedEquality
: identifiedOperand ((MATCHES^ '{'! matchesOperand '}'!)|(COMPARABLEOPERATOR^ identifiedOperand))
| EXISTS identifiedPath -> ^(EXISTS identifiedPath)
| '('! identifiedExpr ')'!
| NOT^ identifiedEquality
;
// | identifiedOperand 'matches' '{' matchesOperand '}'
// | identifiedOperand 'matches' REGEXPATTERN
// | 'EXISTS' identifiedPath;
//<IdentifiedOperand> ::= <Operand> | <IdentifiedPath>
identifiedOperand
: operand | identifiedPath;
//!<IdentifiedOperand> ::= <Operand> | <RelativePath>
//<IdentifiedPath>::= Identifier
// | Identifier <Predicate>
// | Identifier '/' <ObjectPath>
// | Identifier <Predicate> '/' <ObjectPath>
identifiedPath
: IDENTIFIER predicate? ('/' objectPath)? -> ^(IDENTIFIER predicate? objectPath?);
//! | Identifer <AbsolutePath>
//! | Identifer <Predicate> <AbsolutePath>
//<Predicate> ::= <NodePredicate>
predicate
: nodePredicate;
//<NodePredicate> ::= '['<NodePredicateOr>']'
nodePredicate
: OPENBRACKET nodePredicateOr CLOSEBRACKET;
//<NodePredicateOr> ::= <NodePredicateAnd>
// | <NodePredicateOr> 'or' <NodePredicateAnd>
nodePredicateOr
: nodePredicateAnd (OR^ nodePredicateAnd)*;
//<NodePredicateAnd> ::= <NodePredicateComparable>
// | <NodePredicateAnd> 'and' <NodePredicateComparable>
nodePredicateAnd
: nodePredicateComparable (AND^ nodePredicateComparable)*;
//<NodePredicateComparable> ::= <PredicateOperand> ComparableOperator <PredicateOperand>
// | NodeId
// | NodeId ',' String ! <NodeId> and name/value = <String> shortcut
// | NodeId ',' parameter ! <NodeId> and name/value = <Parameter> shortcut
// | <NodePredicateRegEx> ! /items[{/at0001.*/}], /items[at0001 and name/value matches {//}]
// | ArchetypeId
// | ArchetypeId ',' String ! <NodeId> and name/value = <String> shortcut
// | ArchetypeId ',' parameter ! <NodeId> and name/value = <Parameter> shortcut
nodePredicateComparable
: NODEID (COMMA^ (STRING|PARAMETER))?
| ARCHETYPEID (COMMA^ (STRING|PARAMETER))?
| predicateOperand ((COMPARABLEOPERATOR^ predicateOperand)|(MATCHES^ REGEXPATTERN))
| REGEXPATTERN //! /items[{/at0001.*/}], /items[at0001 and name/value matches {//}]
;
//<NodePredicateRegEx> ::= RegExPattern
// | <PredicateOperand> 'matches' RegExPattern
nodePredicateRegEx
: REGEXPATTERN
| predicateOperand MATCHES^ REGEXPATTERN;
//<MatchesOperand> ::= <ValueListItems>
// | UriValue
matchesOperand
: valueListItems | URIVALUE;
//! <ValueList> ::= '{'<ValueListItems>'}'
//<ValueListItems> ::= <Operand>
// |<Operand> ',' <ValueListItems>
valueListItems
: operand (','! operand)*;
//<URI> ::= '{' UriValue '}'
uri : '{' URIVALUE '}';
//<ArchetypePredicate> ::= '[' ArchetypeId ']'
// | '[' Parameter ']'
// | '[' RegExPattern ']'
archetypePredicate
: OPENBRACKET (archetypeId|PARAMETER|REGEXPATTERN) CLOSEBRACKET;
archetypeId
: ARCHETYPEID;
//<VersionPredicate> ::= '[' <VersionPredicateOptions> ']'
versionPredicate
: OPENBRACKET versionPredicateOptions CLOSEBRACKET;
//<VersionPredicateOptions> ::= 'latest_version' | 'all_versions'
versionPredicateOptions
: 'latest_version' | ALL_VERSIONS;
//<StandardPredicate> ::= '[' <PredicateExpr> ']'
standardPredicate
: '['! predicateExpr ']'!;
//<PredicateExpr> ::= <PredicateOr>
predicateExpr
: predicateOr;
//<PredicateOr> ::= <PredicateAnd>
// | <PredicateOr> 'or' <PredicateAnd>
predicateOr
: predicateAnd (OR^ predicateAnd)*;
// : (predicateOr 'or')? predicateAnd; !!!
//<PredicateAnd> ::= <PredicateEquality>
// | <PredicateAnd> 'and' <PredicateEquality>
predicateAnd
: predicateEquality (AND^ predicateEquality)*;
// : (predicateAnd 'and')? predicateEquality; !!!
//<PredicateEquality> ::= <PredicateOperand> ComparableOperator <PredicateOperand>
predicateEquality
: predicateOperand COMPARABLEOPERATOR^ predicateOperand;
//<PredicateOperand> ::= !Identifier
// !| Identifier PathItem
// | <ObjectPath>
// | <Operand>
predicateOperand
: objectPath | operand;
//<Operand> ::= String | Integer | Float | Date | Parameter | Boolean
operand: STRING | INTEGER | FLOAT | DATE | PARAMETER | BOOLEAN;
//<ObjectPath> ::= <PathPart>
// | <PathPart> '/' <ObjectPath>
objectPath
: pathPart ('/' pathPart)*;
//<PathPart> ::= Identifier
// | Identifier <Predicate>
pathPart
: IDENTIFIER predicate?;
//<FromExpr> ::= <SimpleClassExpr>
// | <SimpleClassExpr> <ContainsExpr>
fromExpr: containsExpression;
contains: simpleClassExpr (CONTAINS^ containsExpression)?;
//! Check thislass
//<ContainsExpr>::= 'CONTAINS' <ContainsExpression>
// !'CONTAINS' <ContainsOr>
//<ContainsExpression> ::= <ClassExpr>
// | <ContainExpressionBoolean>
// |'(' <ContainExpressionBoolean> ')'
containsExpression
: containExpressionBool (boolOp containsExpression)?
// | '(' containExpressionBool ')' -> ^(OPEN containExpressionBool)
;
//<ContainExpressionBoolean> ::= <ContainsExpression> 'OR' <ContainsExpression>
// | <ContainsExpression> 'AND' <ContainsExpression>
// | <ContainsExpression> 'XOR' <ContainsExpression>
containExpressionBool
: contains
| '(' containsExpression ')' -> ^(OPEN containsExpression CLOSE);
boolOp : OR|XOR|AND;
//<ClassExpr>::= <SimpleClassExpr>
// | '(' <SimpleClassExpr> <ContainsExpr> ')'
// | <SimpleClassExpr> <ContainsExpr>
//classExpr
// : '(' simpleClassExpr ')'
// | simpleClassExpr
// ;
//<SimpleClassExpr>::= Identifier ! RM_TYPE_NAME
// | Identifier Identifier ! RM_TYPE_NAME variable
// | <ArchetypedClassExpr>
// | <VersionedClassExpr>
// | <VersionClassExpr>
// ! | <IdentifiedObjectExpr> ! need to be used once VersionedClassExpr is removed
simpleClassExpr
: IDENTIFIER IDENTIFIER? //! RM_TYPE_NAME .. RM_TYPE_NAME variable
| archetypedClassExpr
| versionedClassExpr
| versionClassExpr;
// ! | <IdentifiedObjectExpr> ! need to be used once VersionedClassExpr is removed
//<ArchetypedClassExpr>::= Identifier <ArchetypePredicate> ! RM_TYPE_NAME [archetype_id]
// | Identifier Identifier <ArchetypePredicate> ! RM_TYPE_NAME variable [archetype_id]
archetypedClassExpr
: IDENTIFIER^ IDENTIFIER? archetypePredicate; //! RM_TYPE_NAME variable? [archetype_id]
//! need to be used once VersionedClassExpr is removed
//!<IdentifiedObjectExpr>::= Identifier <StandardPredicate> ! RM_TYPE_NAME [path operator operand]
//! | Identifier Identifier <StandardPredicate> ! RM_TYPE_NAME variable [path operator operand]
//<VersionedClassExpr>::= 'VERSIONED_OBJECT'
// | 'VERSIONED_OBJECT' Identifier
// | 'VERSIONED_OBJECT' <StandardPredicate>
// | 'VERSIONED_OBJECT' Identifier <StandardPredicate>
versionedClassExpr
: VERSIONED_OBJECT^ IDENTIFIER? standardPredicate?;
//<VersionClassExpr>::= 'VERSION'
// | 'VERSION' Identifier
// | 'VERSION' <StandardPredicate>
// | 'VERSION' Identifier <StandardPredicate>
// | 'VERSION' <VersionPredicate>
// | 'VERSION' Identifier <VersionPredicate>
versionClassExpr
: VERSION^ IDENTIFIER? (standardPredicate|versionPredicate)?;
//
// LEXER PATTERNS
//
WS : ( ' '
| '\t'
| '\r'
| '\n'
) {$channel=HIDDEN;}
;
SELECT : ('S'|'s')('E'|'e')('L'|'l')('E'|'e')('C'|'c')('T'|'t') ;
TOP : ('T'|'t')('O'|'o')('P'|'p') ;
FORWARD : ('F'|'f')('O'|'o')('R'|'r')('W'|'w')('A'|'a')('R'|'r')('D'|'d') ;
BACKWARD : ('B'|'b')('A'|'a')('C'|'c')('K'|'k')('W'|'w')('A'|'a')('R'|'r')('D'|'d') ;
AS : ('A'|'a')('S'|'s') ;
CONTAINS : ('C'|'c')('O'|'o')('N'|'n')('T'|'t')('A'|'a')('I'|'i')('N'|'n')('S'|'s') ;
WHERE : ('W'|'w')('H'|'h')('E'|'e')('R'|'r')('E'|'e') ;
ORDERBY : ('O'|'o')('R'|'r')('D'|'d')('E'|'e')('R'|'r')(' ')('B'|'b')('Y'|'y') ;
FROM : ('F'|'f')('R'|'r')('O'|'o')('M'|'m') ;
DESCENDING : ('D'|'d')('E'|'e')('S'|'s')('C'|'c')('E'|'e')('N'|'n')('D'|'d')('I'|'i')('N'|'n')('G'|'g') ;
ASCENDING : ('A'|'a')('S'|'s')('C'|'c')('E'|'e')('N'|'n')('D'|'d')('I'|'i')('N'|'n')('G'|'g') ;
DESC : ('D'|'d')('E'|'e')('S'|'s')('C'|'c') ;
ASC : ('A'|'a')('S'|'s')('C'|'c') ;
EHR : 'EHR';
AND : ('A'|'a')('N'|'n')('D'|'d') ;
OR : ('O'|'o')('R'|'r') ;
XOR : ('X'|'x')('O'|'o')('R'|'r') ;
NOT : ('N'|'n')('O'|'o')('T'|'t') ;
MATCHES : ('M'|'m')('A'|'a')('T'|'t')('C'|'c')('H'|'h')('E'|'e')('S'|'s') ;
EXISTS: ('E'|'e')('X'|'x')('I'|'i')('S'|'s')('T'|'t')('S'|'s') ;
VERSION : 'VERSION';
VERSIONED_OBJECT : 'VERSIONED_OBJECT';
ALL_VERSIONS
: 'all_versions';
fragment
ESC_SEQ
: '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
| UNICODE_ESC
| OCTAL_ESC
;
fragment
OCTAL_ESC
: '\\' ('0'..'3') ('0'..'7') ('0'..'7')
| '\\' ('0'..'7') ('0'..'7')
| '\\' ('0'..'7')
;
fragment
UNICODE_ESC
: '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
;
fragment
HEX_DIGIT : ('0'..'9'|'a'..'f'|'A'..'F') ;
QUOTE : '\'';
fragment
DIGIT : '0'..'9';
fragment
HEXCHAR : DIGIT|'a'|'A'|'b'|'B'|'c'|'C'|'d'|'D'|'e'|'E'|'f'|'F';
fragment
LETTER
: 'a'..'z'|'A'..'Z';
fragment
ALPHANUM
: LETTER|DIGIT;
fragment
LETTERMINUSA
: 'b'..'z'|'B'..'Z';
fragment
LETTERMINUST
: 'a'..'s'|'A'..'S'|'u'..'z'|'U'..'Z';
fragment
IDCHAR : ALPHANUM|'_';
fragment
IDCHARMINUST
: LETTERMINUST|DIGIT|'_';
fragment
URISTRING
: ALPHANUM|'_'|'-'|'/'|':'|'.'|'?'|'&'|'%'|'$'|'#'|'@'|'!'|'+'|'='|'*';
fragment
REGEXCHAR
: URISTRING|'('|')'|'\\'|'^'|'{'|'}'|']'|'[';
// Terminal Definitions
//Boolean = 'true' | 'false'
BOOLEAN : 'true' | 'false' | 'TRUE' | 'FALSE' ;
//!NodeId = 'a''t'{Digit}{Digit}{Digit}{Digit}
//! conflict with Identifier
//!NodeId = 'at'({Digit}{Digit}{Digit}{Digit}('.0'*('.'{NonZeroDigit}{Digit}*)+|('.'{NonZeroDigit}{Digit}*)*)|'0''.0'*('.'{NonZeroDigit}{Digit}*)+|('.'{NonZeroDigit}{Digit}*)+)
//NodeId = 'at'({Digit}+('.'{Digit}+)*)
//NODEID : 'at' DIGIT+ ('.' DIGIT+)*;
NODEID : 'at' DIGIT+ ('.' DIGIT+)*; // DIGIT DIGIT DIGIT DIGIT;
//!Identifier = {Letter}({Alphanumeric}|'_')* ! Conflicts with UID
//!Identifier = {Letter}{IdChar}* ! Conflicts with extended NodeId
//! restricted to allow only letters after the 4th character due to conflict with extended NodeId
//!Identifier = {Letter}{IdChar}?{IdChar}?{IdChar}?({Letter}|'_')* !Conficts with NodeId which may have any length of digit, such as at0.9
//Identifier = {LetterMinusA}{IdCharMinusT}?{IdChar}* | 'a''t'?(({letter}|'_')*|{LetterMinusT}{Alphanumeric}*)
// ???
IDENTIFIER
: ('a'|'A') (ALPHANUM|'_')*
| LETTERMINUSA IDCHAR*
;
//!PathItem = '/'{Letter}({Alphanumeric}|'_')*
//Integer = {Digit}+
INTEGER : '-'? DIGIT+;
//Float = {Digit}+'.'{Digit}+
FLOAT : '-'? DIGIT+ '.' DIGIT+;
//Date = ''{Digit}{Digit}{Digit}{Digit}'-'{Digit}{Digit}'-'{Digit}{Digit}''
DATE : '\'' DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT 'T' DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT '.' DIGIT DIGIT DIGIT '+' DIGIT DIGIT DIGIT DIGIT '\'';
//!Parameter = '$'{letter}({Alphanumeric}|'_')*
//Parameter = '$'{letter}{IdChar}*
PARAMETER
: '$' LETTER IDCHAR*;
//! could constrain UID further
//UniqueId = {digit}+('.'{digit}+)+'.'{digit}+ ! OID
// | {Hex Char}+('-'{Hex Char}+)+ ! UUID
UNIQUEID: DIGIT+ ('.' DIGIT+)+ '.' DIGIT+ // OID
| HEXCHAR+ ('-' HEXCHAR+)+ // UUID
;
//! could constrain ArchetypeId further
//!ArchetypeId = {Letter}+'-'{Letter}+'-'({Letter}|'_')+'.'({Letter}|'_'|'-')+'.v'{Digit}+('.'{Digit}+)? ! not allow a number in archetype id concept, such as openEHR-EHR-OBSERVATION.laboratory-hba1c.v1
//ArchetypeId = {Letter}+'-'{Letter}+'-'({Letter}|'_')+'.'({IdChar}|'-')+'.v'{Digit}+('.'{Digit}+)?
ARCHETYPEID
: LETTER+ '-' LETTER+ '-' (LETTER|'_')+ '.' (IDCHAR|'-')+ '.v' DIGIT+ ('.' DIGIT+)?
;
//ComparableOperator = '=' | '!=' | '>' | '>=' | '<' | '<='
COMPARABLEOPERATOR
: '=' | '!=' | '>' | '>=' | '<' | '<='
;
//UriValue = {Letter}+'://'({UriString}|'['|']'|', '''|'')*
// |{Letter}+':'({UriString}|'['|']'|'')*
URIVALUE: LETTER+ '://' (URISTRING|'['|']'|', \''|'\'')*
// | LETTER+ ':' (URISTRING|'['|']'|'\'')*
;
//RegExPattern = '{/'{RegExChar}+'/}'
REGEXPATTERN
: '{/' REGEXCHAR+ '/}';
//String = '"'{String Char}*'"'
// | ''{String Char}*''
STRING
: '\'' ( ESC_SEQ | ~('\\'|'\'') )* '\''
| '"' ( ESC_SEQ | ~('\\'|'"') )* '"'
;
SLASH : '/';
COMMA : ',';
OPENBRACKET
: '[';
CLOSEBRACKET
: ']';
OPEN : '(';
CLOSE : ')';