This is the 2019 May Ballot of Clinical Quality Language, STU4
For a full list of available versions, see the Directory of published versions .
Clinical Decision Support Work Group | Maturity Level: 4 | Ballot Status: STU 4 |
This page contains the Antlr 4.0 grammar for Clinical Quality Language.
grammar cql; /* * Clinical Quality Language Grammar Specification * Version 1.4 - STU4 Ballot */ import fhirpath; /* * Parser Rules */ library : libraryDefinition? usingDefinition* includeDefinition* codesystemDefinition* valuesetDefinition* codeDefinition* conceptDefinition* parameterDefinition* statement* ; /* * Definitions */ libraryDefinition : 'library' qualifiedIdentifier ('version' versionSpecifier)? ; usingDefinition : 'using' modelIdentifier ('version' versionSpecifier)? ; includeDefinition : 'include' qualifiedIdentifier ('version' versionSpecifier)? ('called' localIdentifier)? ; localIdentifier : identifier ; accessModifier : 'public' | 'private' ; parameterDefinition : accessModifier? 'parameter' identifier (typeSpecifier)? ('default' expression)? ; codesystemDefinition : accessModifier? 'codesystem' identifier ':' codesystemId ('version' versionSpecifier)? ; valuesetDefinition : accessModifier? 'valueset' identifier ':' valuesetId ('version' versionSpecifier)? codesystems? ; codesystems : 'codesystems' '{' codesystemIdentifier (',' codesystemIdentifier)* '}' ; codesystemIdentifier : (libraryIdentifier '.')? identifier ; libraryIdentifier : identifier ; codeDefinition : accessModifier? 'code' identifier ':' codeId 'from' codesystemIdentifier displayClause? ; conceptDefinition : accessModifier? 'concept' identifier ':' '{' codeIdentifier (',' codeIdentifier)* '}' displayClause? ; codeIdentifier : (libraryIdentifier '.')? identifier ; codesystemId : STRING ; valuesetId : STRING ; versionSpecifier : STRING ; codeId : STRING ; /* * Type Specifiers */ typeSpecifier : namedTypeSpecifier | listTypeSpecifier | intervalTypeSpecifier | tupleTypeSpecifier | choiceTypeSpecifier ; namedTypeSpecifier : (qualifier '.')* identifier ; modelIdentifier : identifier ; listTypeSpecifier : 'List' '<' typeSpecifier '>' ; intervalTypeSpecifier : 'Interval' '<' typeSpecifier '>' ; tupleTypeSpecifier : 'Tuple' '{' tupleElementDefinition (',' tupleElementDefinition)* '}' ; tupleElementDefinition : identifier typeSpecifier ; choiceTypeSpecifier : 'Choice' '<' typeSpecifier (',' typeSpecifier)* '>' ; /* * Statements */ statement : expressionDefinition | contextDefinition | functionDefinition ; expressionDefinition : 'define' accessModifier? identifier ':' expression ; contextDefinition : 'context' (modelIdentifier '.')? identifier ; functionDefinition : 'define' accessModifier? 'function' identifier '(' (operandDefinition (',' operandDefinition)*)? ')' ('returns' typeSpecifier)? ':' (functionBody | 'external') ; operandDefinition : identifier typeSpecifier ; functionBody : expression ; /* * Expressions */ querySource : retrieve | qualifiedIdentifier | '(' expression ')' ; aliasedQuerySource : querySource alias ; alias : identifier ; queryInclusionClause : withClause | withoutClause ; withClause : 'with' aliasedQuerySource 'such that' expression ; withoutClause : 'without' aliasedQuerySource 'such that' expression ; retrieve : '[' (contextIdentifier '->')? namedTypeSpecifier (':' (codePath 'in')? terminology)? ']' ; contextIdentifier : qualifiedIdentifier ; codePath : qualifiedIdentifier ; terminology : qualifiedIdentifier | expression ; qualifier : identifier ; query : sourceClause letClause? queryInclusionClause* whereClause? returnClause? sortClause? ; sourceClause : 'from'? aliasedQuerySource (',' aliasedQuerySource)* ; letClause : 'let' letClauseItem (',' letClauseItem)* ; letClauseItem : identifier ':' expression ; whereClause : 'where' expression ; returnClause : 'return' ('all' | 'distinct')? expression ; sortClause : 'sort' ( sortDirection | ('by' sortByItem (',' sortByItem)*) ) ; sortDirection : 'asc' | 'ascending' | 'desc' | 'descending' ; sortByItem : expressionTerm sortDirection? ; qualifiedIdentifier : (qualifier '.')* identifier ; expression : expressionTerm #termExpression | retrieve #retrieveExpression | query #queryExpression | expression 'is' 'not'? ('null' | 'true' | 'false') #booleanExpression | expression ('is' | 'as') typeSpecifier #typeExpression | 'cast' expression 'as' typeSpecifier #castExpression | 'not' expression #notExpression | 'exists' expression #existenceExpression | expression 'properly'? 'between' expressionTerm 'and' expressionTerm #betweenExpression | ('duration' 'in')? pluralDateTimePrecision 'between' expressionTerm 'and' expressionTerm #durationBetweenExpression | 'difference' 'in' pluralDateTimePrecision 'between' expressionTerm 'and' expressionTerm #differenceBetweenExpression | expression ('<=' | '<' | '>' | '>=') expression #inequalityExpression | expression intervalOperatorPhrase expression #timingExpression | expression ('=' | '!=' | '~' | '!~') expression #equalityExpression | expression ('in' | 'contains') dateTimePrecisionSpecifier? expression #membershipExpression | expression 'and' expression #andExpression | expression ('or' | 'xor') expression #orExpression | expression 'implies' expression #impliesExpression | expression ('|' | 'union' | 'intersect' | 'except') expression #inFixSetExpression ; dateTimePrecision : 'year' | 'month' | 'week' | 'day' | 'hour' | 'minute' | 'second' | 'millisecond' ; dateTimeComponent : dateTimePrecision | 'date' | 'time' | 'timezoneoffset' ; pluralDateTimePrecision : 'years' | 'months' | 'weeks' | 'days' | 'hours' | 'minutes' | 'seconds' | 'milliseconds' ; expressionTerm : term #termExpressionTerm | expressionTerm '.' invocation #invocationExpressionTerm | expressionTerm '[' expression ']' #indexedExpressionTerm | 'convert' expression 'to' (typeSpecifier | unit) #conversionExpressionTerm | ('+' | '-') expressionTerm #polarityExpressionTerm | ('start' | 'end') 'of' expressionTerm #timeBoundaryExpressionTerm | dateTimeComponent 'from' expressionTerm #timeUnitExpressionTerm | 'duration' 'in' pluralDateTimePrecision 'of' expressionTerm #durationExpressionTerm | 'difference' 'in' pluralDateTimePrecision 'of' expressionTerm #differenceExpressionTerm | 'width' 'of' expressionTerm #widthExpressionTerm | 'successor' 'of' expressionTerm #successorExpressionTerm | 'predecessor' 'of' expressionTerm #predecessorExpressionTerm | 'singleton' 'from' expressionTerm #elementExtractorExpressionTerm | 'point' 'from' expressionTerm #pointExtractorExpressionTerm | ('minimum' | 'maximum') namedTypeSpecifier #typeExtentExpressionTerm | expressionTerm '^' expressionTerm #powerExpressionTerm | expressionTerm ('*' | '/' | 'div' | 'mod') expressionTerm #multiplicationExpressionTerm | expressionTerm ('+' | '-' | '&') expressionTerm #additionExpressionTerm | 'if' expression 'then' expression 'else' expression #ifThenElseExpressionTerm | 'case' expression? caseExpressionItem+ 'else' expression 'end' #caseExpressionTerm | ('distinct' | 'flatten') expression #aggregateExpressionTerm | ('expand' | 'collapse') expression ('per' (dateTimePrecision | expression))? #setAggregateExpressionTerm ; caseExpressionItem : 'when' expression 'then' expression ; dateTimePrecisionSpecifier : dateTimePrecision 'of' ; relativeQualifier : 'or before' | 'or after' ; offsetRelativeQualifier : 'or more' | 'or less' ; exclusiveRelativeQualifier : 'less than' | 'more than' ; quantityOffset : (quantity offsetRelativeQualifier?) | (exclusiveRelativeQualifier quantity) ; temporalRelationship : ('on or'? ('before' | 'after')) | (('before' | 'after') 'or on'?) ; intervalOperatorPhrase : ('starts' | 'ends' | 'occurs')? 'same' dateTimePrecision? (relativeQualifier | 'as') ('start' | 'end')? #concurrentWithIntervalOperatorPhrase | 'properly'? 'includes' dateTimePrecisionSpecifier? ('start' | 'end')? #includesIntervalOperatorPhrase | ('starts' | 'ends' | 'occurs')? 'properly'? ('during' | 'included in') dateTimePrecisionSpecifier? #includedInIntervalOperatorPhrase | ('starts' | 'ends' | 'occurs')? quantityOffset? temporalRelationship dateTimePrecisionSpecifier? ('start' | 'end')? #beforeOrAfterIntervalOperatorPhrase | ('starts' | 'ends' | 'occurs')? 'properly'? 'within' quantity 'of' ('start' | 'end')? #withinIntervalOperatorPhrase | 'meets' ('before' | 'after')? dateTimePrecisionSpecifier? #meetsIntervalOperatorPhrase | 'overlaps' ('before' | 'after')? dateTimePrecisionSpecifier? #overlapsIntervalOperatorPhrase | 'starts' dateTimePrecisionSpecifier? #startsIntervalOperatorPhrase | 'ends' dateTimePrecisionSpecifier? #endsIntervalOperatorPhrase ; term : invocation #invocationTerm | literal #literalTerm | externalConstant #externalConstantTerm | intervalSelector #intervalSelectorTerm | tupleSelector #tupleSelectorTerm | instanceSelector #instanceSelectorTerm | listSelector #listSelectorTerm | codeSelector #codeSelectorTerm | conceptSelector #conceptSelectorTerm | '(' expression ')' #parenthesizedTerm ; ratio : quantity ':' quantity ; literal : ('true' | 'false') #booleanLiteral | 'null' #nullLiteral | STRING #stringLiteral | NUMBER #numberLiteral | DATETIME #dateTimeLiteral | TIME #timeLiteral | quantity #quantityLiteral | ratio #ratioLiteral ; intervalSelector : // TODO: Consider this as an alternative syntax for intervals... (would need to be moved up to expression to make it work) //expression ( '..' | '*.' | '.*' | '**' ) expression; 'Interval' ('['|'(') expression ',' expression (']'|')') ; tupleSelector : 'Tuple'? '{' (':' | (tupleElementSelector (',' tupleElementSelector)*)) '}' ; tupleElementSelector : identifier ':' expression ; instanceSelector : namedTypeSpecifier '{' (':' | (instanceElementSelector (',' instanceElementSelector)*)) '}' ; instanceElementSelector : identifier ':' expression ; listSelector : ('List' ('<' typeSpecifier '>')?)? '{' (expression (',' expression)*)? '}' ; displayClause : 'display' STRING ; codeSelector : 'Code' STRING 'from' codesystemIdentifier displayClause? ; conceptSelector : 'Concept' '{' codeSelector (',' codeSelector)* '}' displayClause? ; // NOTE: These keywords are commented out because of an issue with the ANTLR tooling. In 4.5, having these keywords // as identifiers results in unacceptable parsing performance. In 4.6+, having them as identifiers results in incorrect // parsing. See Github issue [#343](https://github.com/cqframework/clinical_quality_language/issues/343) for more detail identifier : IDENTIFIER | DELIMITEDIDENTIFIER | QUOTEDIDENTIFIER //| 'all' | 'Code' | 'code' | 'Concept' | 'concept' //| 'contains' | 'date' | 'display' //| 'distinct' | 'end' //| 'exists' //| 'not' | 'start' | 'time' | 'timezoneoffset' | 'version' //| 'where' ; QUOTEDIDENTIFIER : '"' (ESC | .)*? '"' ; fragment ESC : '\\' ([`'"\\/fnrt] | UNICODE) // allow \`, \', \", \\, \/, \f, etc. and \uXXX ;
NOTE: CQL extends from the FHIRPath grammar, and so uses the core production rules defined there. The FHIRPath grammar is included here for reference only, the FHIRPath grammar below is part of the FHIRPath specification. —-
grammar fhirpath; // Grammar rules // [FHIRPath](http://hl7.org/fhirpath/2019May) Normative Ballot 3 //prog: line (line)*; //line: ID ( '(' expr ')') ':' expr '\r'? '\n'; expression : term #termExpression | expression '.' invocation #invocationExpression | expression '[' expression ']' #indexerExpression | ('+' | '-') expression #polarityExpression | expression ('*' | '/' | 'div' | 'mod') expression #multiplicativeExpression | expression ('+' | '-' | '&') expression #additiveExpression | expression ('is' | 'as') typeSpecifier #typeExpression | expression '|' expression #unionExpression | expression ('<=' | '<' | '>' | '>=') expression #inequalityExpression | expression ('=' | '~' | '!=' | '!~') expression #equalityExpression | expression ('in' | 'contains') expression #membershipExpression | expression 'and' expression #andExpression | expression ('or' | 'xor') expression #orExpression | expression 'implies' expression #impliesExpression //| (IDENTIFIER)? '=>' expression #lambdaExpression ; term : invocation #invocationTerm | literal #literalTerm | externalConstant #externalConstantTerm | '(' expression ')' #parenthesizedTerm ; literal : '{' '}' #nullLiteral | ('true' | 'false') #booleanLiteral | STRING #stringLiteral | NUMBER #numberLiteral | DATETIME #dateTimeLiteral | TIME #timeLiteral | quantity #quantityLiteral ; externalConstant : '%' ( identifier | STRING ) ; invocation // Terms that can be used after the function/member invocation '.' : identifier #memberInvocation | function #functionInvocation | '$this' #thisInvocation | '$index' #indexInvocation | '$total' #totalInvocation ; function : identifier '(' paramList? ')' ; paramList : expression (',' expression)* ; quantity : NUMBER unit? ; unit : dateTimePrecision | pluralDateTimePrecision | STRING // UCUM syntax for units of measure ; dateTimePrecision : 'year' | 'month' | 'week' | 'day' | 'hour' | 'minute' | 'second' | 'millisecond' ; pluralDateTimePrecision : 'years' | 'months' | 'weeks' | 'days' | 'hours' | 'minutes' | 'seconds' | 'milliseconds' ; typeSpecifier : qualifiedIdentifier ; qualifiedIdentifier : identifier ('.' identifier)* ; identifier : IDENTIFIER | DELIMITEDIDENTIFIER | 'as' | 'contains' | 'in' | 'is' ; /**************************************************************** Lexical rules *****************************************************************/ // Not sure why, but with these as lexical rules, when the grammar is imported into CQL, they are not correctly recognized // Moving the same rules into the literal production rule above corrects the issue //EMPTY // : '{' '}' // ; // To create an empty array (and avoid a NULL literal) //BOOL // : 'true' // | 'false' // ; DATETIME : '@' [0-9][0-9][0-9][0-9] // year ( ( '-'[0-9][0-9] // month ( ( '-'[0-9][0-9] // day ('T' TIMEFORMAT?)? ) | 'T' )? ) | 'T' )? ('Z' | ('+' | '-') [0-9][0-9]':'[0-9][0-9])? // timezoneOffset ; TIME : '@' 'T' TIMEFORMAT ; fragment TIMEFORMAT : [0-9][0-9] (':'[0-9][0-9] (':'[0-9][0-9] ('.'[0-9]+)?)?)? ; IDENTIFIER : ([A-Za-z] | '_')([A-Za-z0-9] | '_')* // Added _ to support CQL (FHIR could constrain it out) ; DELIMITEDIDENTIFIER : '`' (ESC | .)*? '`' ; STRING : '\'' (ESC | .)*? '\'' ; // Also allows leading zeroes now (just like CQL and XSD) NUMBER : [0-9]+('.' [0-9]+)? ; // Pipe whitespace to the HIDDEN channel to support retrieving source text through the parser. WS : [ \r\n\t]+ -> channel(HIDDEN) ; COMMENT : '/*' .*? '*/' -> channel(HIDDEN) ; LINE_COMMENT : '//' ~[\r\n]* -> channel(HIDDEN) ; fragment ESC : '\\' ([`'\\/fnrt] | UNICODE) // allow \`, \', \\, \/, \f, etc. and \uXXX ; fragment UNICODE : 'u' HEX HEX HEX HEX ; fragment HEX : [0-9a-fA-F] ;