/**
 * Define a grammar called FhirMapper
 */
 grammar mapping;

// starting point for parsing a mapping file
// in case we need nested ConceptMaps, we need to have this rule:
// structureMap : mapId conceptMap* structure* imports* group+

structureMap
    : mapId structure* imports* const* group+ EOF
    ;

mapId
	: 'map' url '=' identifier
	;

url
    : DELIMITEDIDENTIFIER
    ;

identifier
    : IDENTIFIER
    | DELIMITEDIDENTIFIER
    ;

structure
	: 'uses' url structureAlias? 'as'  modelMode
	;

structureAlias
    : 'alias' identifier
    ;

imports
	: 'imports' url
	;

const 
    : 'let' id '=' fhirPath ';' // which might just be a literal

group
	: 'group' id parameters extends? typeMode? rules
	;

rules
    : '{' rule* '}'
    ;

typeMode
    : '<<' groupTypeMode '>>'
    ;

extends
    : 'extends' id
    ;

parameters
    : '(' parameter (',' parameter)+ ')'
    ;

parameter
    : inputMode id type?
	;

type
    : ':' identifier
    ;

rule
 	: ruleSources ('->' ruleTargets)? dependent? ruleName? ';'
 	;

ruleName
    : id
    ;

ruleSources
    : ruleSource (',' ruleSource)*
    ;

ruleSource
    :  ruleContext sourceType? sourceCardinality? sourceDefault? sourceListMode? alias? whereClause? checkClause? log?
    ;

ruleTargets
    : ruleTarget (',' ruleTarget)*
    ;

sourceType
    : ':' identifier
    ;

sourceCardinality
    : INTEGER '..' upperBound
    ;

upperBound
    : INTEGER
    | '*'
    ;

ruleContext
	: identifier ('.' identifier)*
	;

sourceDefault
    : 'default' '(' fhirPath ')'
    ;

alias
	: 'as' identifier
	;

whereClause
    : 'where' '(' fhirPath ')'
    ;

checkClause
    : 'check' '(' fhirPath ')'
    ;

log
    : 'log' '(' fhirPath ')'
    ;

dependent
    : 'then' (invocation (',' invocation)* rules? | rules)
    ;

ruleTarget
    : ruleContext ('=' transform)? alias? targetListMode?
    | invocation alias?     // alias is not required when simply invoking a group
    ;

transform
    : literal           // trivial constant transform
    | ruleContext       // 'copy' transform
    | invocation        // other named transforms
    ;

invocation
    : identifier '(' paramList? ')'
    ;

paramList
    : param (',' param)*
    ;

param
    : literal
    | id
    ;

fhirPath
    : literal       // insert reference to FhirPath grammar here
    ;

literal
    : INTEGER
    | NUMBER
    | STRING
    | DATETIME
    | DATE
    | TIME
    | BOOL
    ;

groupTypeMode
    : 'types' | 'type+'
    ;

sourceListMode
    : 'first' | 'not_first' | 'last' | 'not_last' | 'only_one'
    ;

targetListMode
   : 'first' | 'share' | 'last' | 'single'
   ;

inputMode
   : 'source' | 'target'
   ;

modelMode           // StructureMapModelMode binding
    : 'source' | 'queried' | 'target' | 'produced'
    ;



    /*
     * Syntax for embedded ConceptMaps excluded for now
     *
    conceptMap
        : 'conceptMap' '"#'	IDENTIFIER '{' (prefix)+ conceptMapping '}'
        ;

    prefix
    	: 'prefix' conceptMappingVar '=' URL
    	;

    conceptMappingVar
    	:  IDENTIFIER
    	;
    conceptMapping
    	:  conceptMappingVar ':' field
    	   (('<=' | '=' | '==' | '!=' '>=' '>-' | '<-' | '~') conceptMappingVar ':' field) | '--'
    	;
    */




/****************************************************************
    Lexical rules from FhirPath
*****************************************************************/

BOOL
        : 'true'
        | 'false'
        ;

DATE
        : '@' DATEFORMAT
        ;

DATETIME
        : '@' DATEFORMAT 'T' (TIMEFORMAT TIMEZONEOFFSETFORMAT?)?
        ;

TIME
        : '@' 'T' TIMEFORMAT
        ;

fragment DATEFORMAT
        : [0-9][0-9][0-9][0-9] ('-'[0-9][0-9] ('-'[0-9][0-9])?)?
        ;

fragment TIMEFORMAT
        : [0-9][0-9] (':'[0-9][0-9] (':'[0-9][0-9] ('.'[0-9]+)?)?)?
        ;

fragment TIMEZONEOFFSETFORMAT
        : ('Z' | ('+' | '-') [0-9][0-9]':'[0-9][0-9])
        ;

id
        : ([A-Za-z] )([A-Za-z0-9])*            
        ;

IDENTIFIER
        : ([A-Za-z] | '_')([A-Za-z0-9] | '_')*            // Added _ to support CQL (FHIR could constrain it out)
        ;

DELIMITEDIDENTIFIER
        '`' (ESC | .)*? '`'
        ;

STRING
        : '\'' (ESC | .)*? '\''
        ;

INTEGER
    : [0-9]+
    ;

// Also allows leading zeroes now (just like CQL and XSD)
NUMBER
    : INTEGER ('.' [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]
        ;