/*
 * Copyright 2008 Marc Bischof 
 * based on simpel.g by Matthieu Riou 
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 *
 *     http://www.apache.org/licenses/LICENSE-2.0 
 *
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 */
grammar BPELscript;

options {
    output=AST; 
    ASTLabelType=CommonTree;
}
tokens {
    ROOT; PROCESS; PICK; SEQUENCE; FLOW; FLOWS; IF; ELSIF; ELSE; WHILE; UNTIL; FOR; INVOKE;PROC_STMTS;
    RECEIVE; REPLY; ASSIGN; THROW; WAIT; EXIT; TIMEOUT; TRY; CATCH; CATCH_ALL; SCOPE; EVENT;
    ALARM; ONMESSAGE; COMPENSATION; COMPENSATE; CORRELATION; CORR_MAP; PARTNERLINK; VARIABLE; VALIDATE; BLOCK_PARAM; 
    SIGNAL; JOIN; WITH; MAP; NOP; RETHROW; 
    EXPR; EXT_EXPR; XML_LITERAL; CALL; NAMESPACE; NS; PATH; EXTENSION; EXTENSIONACT; IMPORT; MESSAGES; CORRSETS; CORRSET;
    XML; JS;
    PID; VARIABLES; PARTNERLINKS; PORTTYPE; STD_ATTR;ONALARM;REPEATEVERY;EVENTHDL;MESSAGE; TERMINATION; 
    MSGEX; FAULTNAME; MSGTYPE; VITYPE; VIELT;FAULTELT;
}
@parser::header {
/*
 * Copyright 2008 Marc Bischof 
 * based on simpel.g by Matthieu Riou 
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 *
 *     http://www.apache.org/licenses/LICENSE-2.0 
 *
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 */

package iaas.bpelscript.antlr;
}

@lexer::header {
/*
 * Copyright 2008 Marc Bischof 
 * based on simpel.g by Matthieu Riou 
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 *
 *     http://www.apache.org/licenses/LICENSE-2.0 
 *
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 */

package iaas.bpelscript.antlr;
}

// MAIN BPEL SYNTAX
program	:	declaration+ -> ^(ROOT declaration+);
	
declaration	:	process | sub_declaration;

sub_declaration
		:	namespace | extension | imports;

// Process
process
	:	('@queryLanguage' queryLg=STRING)?
		('@expressionLanguage' exprLg=STRING)?	
		sjf=SJF?
		exitOnStandardFault=EOSF?
		'process' ns_id std_attr
		j+=ajoin? s+=asignal* 
		block eventHdl?
	->	^(PROCESS ns_id block eventHdl? $queryLg? $exprLg? $sjf? $exitOnStandardFault? std_attr ajoin? asignal*);
	
proc_stmts
	:	(join SEMI)? proc_stmt (s+=signal SEMI)* 
	-> 	^(PROC_STMTS join? signal* proc_stmt);

proc_stmt
	:	//structured stmts
		if_ex | flow | pick | while_ex | until_ex | foreach | scope_ex | ext_act 
		| try_ex | corr_sets
		//simple stmts
		| ((invoke | receive | reply | assign | throw_ex | rethrow_ex | alarm | timeout | exit
		| variables | validate | partner_links | compensate | nop | messages) SEMI!);

block		:	'{' proc_stmts+ '}' -> ^(SEQUENCE proc_stmts+);
	
scope_block	:	'{' sub_declaration* proc_stmts+ '}' -> ^(SEQUENCE sub_declaration* proc_stmts+);

param_block	:	'{' ('|' in+=ID (',' in+=ID)* '|')? proc_stmts+ '}' -> ^(SEQUENCE $in* proc_stmts+);

body		:	block | proc_stmts;

// Structured activities
pick	:	CREATE_INST? std_attr
		'pick' '{' onMessage+ onAlarm* '}' -> ^(PICK onMessage+ onAlarm* CREATE_INST? std_attr);

onMessage 
        	:	portType? msgEx?
		'onMessage' '(' p=ID ',' o=ID (',' correlation)? ')' with_ex?
        		param_block 
        		-> ^(ONMESSAGE param_block portType? msgEx? ID ID correlation? with_ex?);
        
onAlarm 
	:	// use syntactic predicate to garantie that at least one expression must be there 
		// (also garanties that standard attributes are not used in this context)
		{input.LT(1).getText().equals("alarm") || input.LT(1).getText().equals("timeout") || input.LT(1).getText().equals("repeatEvery")}? 
		(alarm | timeout)? repeatEvery? {input.LT(1).getText().equals("{")}? scope_short
	->	^(ONALARM alarm? timeout? repeatEvery? scope_short);
        
alarm		:	std_attr
			'alarm' '(' expr ')' 
		-> 	^(ALARM  expr std_attr?);
	
timeout	:	std_attr
			'timeout' '('expr ')' 
		->	^(TIMEOUT expr std_attr?);
	
repeatEvery
	:	'repeatEvery' '(' expr ')'
	->	^(REPEATEVERY expr);
	
flow 	:	std_attr
		'parallel' s+=sequence ( 'and' s+=sequence)*
	-> 	^(FLOW  $s+ std_attr) ;

signal		:	'signal' '('ID (',' expr)? ')' -> ^(SIGNAL ID expr?);

asignal	:	'@signal' '('ID (',' expr)? ')' -> ^(SIGNAL ID expr?);

ajoin		:	'@join' '(' k+=ID (',' k+=ID)* (',' expr)? ')' -> ^(JOIN $k+ expr?);

join		:	'join' '(' k+=ID (',' k+=ID)* (',' expr)? ')' -> ^(JOIN $k+ expr?);

if_ex
	:	std_attr
		'if' '(' iex=expr ')' s=sequence ('elseif' '(' eiex+=expr ')' sei+=sequence)* ('else' se=sequence)? 
	-> 	^(IF $iex $s (^(ELSIF $eiex $sei))* (^(ELSE $se))? std_attr);

sequence
	:	std_attr
		j+=ajoin? s+=asignal* 
		b=body
	->	^(SEQUENCE $j? $b $s* std_attr);

scope_sequence
	:	j+=ajoin? s+=asignal* 
		b=scope_block
	->	^(SEQUENCE $j? $b $s*);

while_ex	:	std_attr
			'while' '(' expr ')' s=sequence -> ^(WHILE expr sequence std_attr);

until_ex	:	std_attr
			'repeat' s=sequence 'until' '(' expr ')' -> ^(UNTIL expr sequence std_attr);

foreach
	:	PARALLEL?
		successfulBranchesOnly=SBO? 
		std_attr
		'for' '(' cName=ID '=' init=expr ('to'|SEMI) cond=expr (('finish'|SEMI) complete+=expr)? ')' scope_short
	-> 	^(FOR $cName $init $cond $complete? scope_short PARALLEL? SBO? std_attr);

try_ex		:	'try' body catch_ex* catchAll?-> ^(TRY catch_ex* body?);		

catch_ex
	:	(('@faultMessageType' fMT=STRING) |  faultElt)?
		'catch' '(' ns_id faultVar=ID? ')' block 
	-> 	^(CATCH ns_id block $faultVar? $fMT? faultElt?);
	
catchAll
	:	'catchAll' block
	-> 	^(CATCH block);

scope_ex
	:	ISOLATED? EOSF? SJF?
		'scope' ('(' ID? ')')? scope_sequence scope_stmt 
	-> 	^(SCOPE ID? scope_stmt scope_sequence ISOLATED? EOSF? SJF?);
	
scope_short 
	:	scope_sequence scope_stmt -> ^(SCOPE scope_stmt scope_sequence);

scope_stmt
	:	compensation? termination? eventHdl?
	->	^(SCOPE compensation? termination? eventHdl?);

termination 
	:	'onTermination' body -> ^(TERMINATION body);

eventHdl	
	:	'events' '{' onEvent* onAlarm* '}'
		->	^(EVENTHDL onEvent* onAlarm*);
	
onEvent	
	:	portType? msgEx?
		( msgType | viElt )?					
		(var=ID '=' )? 'event' '(' p=ID ',' o=ID (',' correlation)? ')' with_ex? scope_short 
	-> 	^(EVENT $p $o correlation? with_ex? scope_short $var? portType? msgEx? msgType? viElt?);
		
compensation //compensation handler
	:	'compensation' body -> ^(COMPENSATION body);

with_ex
	 :	 'with' '(' wm+=with_map (',' wm+=with_map)* ')' -> ^(WITH $wm+);

with_map
	:       	ID ':' KEY? path_expr -> ^(MAP ID KEY? path_expr);

// Simple activities
receive
        :		portType? CREATE_INST? msgEx? std_attr
		'receive' '(' p=ID ',' o=ID (',' correlation)? ')' with_ex?
	-> 	^(RECEIVE $p $o correlation? portType? CREATE_INST? msgEx? std_attr with_ex?);

reply
	:	portType? faultName? msgEx? std_attr
		'reply' '(' p=ID ',' o=ID (',' in=ID)? (',' correlation)? ')'  with_ex?
	-> 	^(REPLY ID ID ID? correlation? portType? std_attr faultName? msgEx? with_ex?);

invoke
	:	portType? std_attr
		'invoke' '(' p=ID ',' o=ID (',' in=ID)? (',' correlation)? ')' with_ex? compensation?
	-> 	^(INVOKE $p $o $in? correlation? portType? std_attr with_ex? compensation?);

assign
	:	portType? CREATE_INST? VALID? KEEPSRC? IGNORE? faultName? msgEx? std_attr //only receive and invoke
		path_expr PART? '=' rvalue 
	-> 	^(ASSIGN path_expr PART? portType? CREATE_INST? std_attr faultName? msgEx? VALID? KEEPSRC? IGNORE? rvalue);

rvalue
	:	receive
	|	invoke
	|	expr PART?;
	
throw_ex
	:	(('@faultVariable' |'@faultVar') faultVar=ID)? std_attr
		'throw' '(' ns_id ')' -> ^(THROW ns_id $faultVar? std_attr);
		
rethrow_ex
	:	std_attr
		'rethrow' -> ^(RETHROW std_attr);

compensate
	:	std_attr
		'compensate' ('(' target=ID ')')? 
	-> 	^(COMPENSATE ID? std_attr);

exit		:	std_attr
			'exit' -> ^(EXIT std_attr);
	
validate	:	std_attr 
			'validate' v+=ID (',' v+=ID)*-> ^(VALIDATE $v+ std_attr);
	
ext_act  	:	std_attr 
			e=EXT_ACT ->  ^(EXTENSIONACT $e std_attr);

nop		:	std_attr
			'nop' -> ^(NOP std_attr);

// Others
namespace
	:	'namespace' ID '=' STRING SEMI -> ^(NAMESPACE ID STRING);
	
extension
	: 	MUSTUND? 
		'extension' ID '=' STRING SEMI
	-> 	^(EXTENSION ID STRING MUSTUND?);

imports
	: 	viType  
		'import' (id=ID '=' (ns=ID '::' )? loc=STRING ) SEMI 
	-> 	^(IMPORT $id $loc $ns? viType?);

messages //Exchange
	:	'messages' m+=message (',' m+=message)* -> ^(MESSAGES message+);
	
message	:	ID -> ^(MESSAGE ID);
		
variables	:	'var' v+=variable (',' v+=variable)* 
		-> 	^(VARIABLES variable+);

variable
	:	msgType? viType? viElt?
		ID  with_ex?
	-> 	^(VARIABLE ID msgType? viType? viElt? with_ex?);

partner_links
	:	('partnerLink' | 'partnerlink') pl+=partner_link (',' pl+=partner_link)* -> ^(PARTNERLINKS $pl+);
	
partner_link
	:	ID '=' '(' plType=ns_id? (',' roleA=ns_id)? (',' roleB=ns_id)? (',' init=INITPARTNER)? ')'
		-> 	^(PID ID $plType? $roleA? $roleB? $init?);

correlation
	:	'{' corr_mapping (',' corr_mapping)* '}' -> ^(CORRELATION corr_mapping+);

corr_mapping
	:	init=INIT_COR?
		pattern=PATTERN_COR?
		f1=ID
	-> 	^(CORR_MAP $f1 $init? $pattern?);

corr_sets 	:	'correlates' '{'cs+=corr_set ';' (cs+=corr_set ';')* '}' -> ^(CORRSETS $cs+);

corr_set	:	f=ID '(' par+=ID (',' par+=ID)* ')' -> ^(CORRSET $f $par+);

// Expressions
expr		:	s_expr | EXT_EXPR | funct_call;
funct_call	:	p+=ID '(' p+=ID* ')' -> ^(CALL ID+);
s_expr		:	condExpr;
condExpr 	:	aexpr ( ('==' ^|'!=' ^|'<' ^|'>' ^|'<=' ^|'>=' ^) aexpr )?;
aexpr		:	mexpr (('+'|'-') ^ mexpr)*;
mexpr		:	atom (('*'|'/') ^ atom)* | STRING;
atom		:	path_expr | INT | '(' s_expr ')' -> s_expr;
path_expr	:	pelmt+=ns_id ('.' pelmt+=ns_id)* -> ^(PATH $pelmt+);
ns_id		:	(pr=ID '::')? loc=ID -> ^(NS $pr? $loc);

//optional attributes 
portType 	:	('@portType' | '@pt') STRING
		->	^(PORTTYPE STRING);
	
std_attr	:	('@name' name=STRING)? suppressJoinFailure=SJF?
		->	^(STD_ATTR $name? $suppressJoinFailure?);
	
msgEx 	:	('@messageExchange' | '@mex') STRING
		->	^(MSGEX STRING);

msgType 	:	('@messageType' | '@msgType') msgT=STRING
		->	^(MSGTYPE STRING);

//var or import type
viType 	:	'@type' type=STRING
		->	^(VITYPE STRING);
	
viElt		:	'@element' elt=STRING
		->	^(VIELT STRING);

faultName	:	('@faultName' | '@fault') STRING
		->	^(FAULTNAME STRING);

faultElt	:	'@faultElement' STRING
		->	^(FAULTELT STRING);	

// LEXER RULES
EXT_EXPR		:	'[' (options {greedy=false;} : .)* ']';
EXT_ACT		:	pre='{{{' (options {greedy=false;} : c=.)* post='}}}';

// Basic tokens
KEY			:	'in' | 'out' | 'inout';
SEMI			:	';';
ID			:	(LETTER | '_' ) (LETTER | DIGIT | '_' | '-' )*;
INT			:	(DIGIT )+ ;
STRING		:	'"' ( ESCAPE_SEQ | ~('\\'|'"') )* '"';
ESCAPE_SEQ		:	'\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\');
SL_COMMENTS	:	('#'|'//') .* CR { $channel = HIDDEN; };
CR			:	('\r' | '\n' )+ { $channel = HIDDEN; };
WS			:	( ' ' | '\t' )+ { skip(); };
fragment DIGIT	:	'0'..'9';
fragment LETTER  	:	 'a'..'z' | 'A'..'Z';

// Boolean annotations as LEXER rules
protected EOSF
	:	('@exitOnStandardFault' | '@exit') {setText("exitOnStandardFault=\"yes\"");}
	|	('@exitOnStandardFault no' | '@exit no') {setText("exitOnStandardFault=\"no\"");};
	
protected SJF
	:	('@suppressJoinFailure' | '@dpe') {setText("suppressJoinFailure=\"yes\"");}
	|	('@suppressJoinFailure no' | '@dpe no') {setText("suppressJoinFailure=\"no\"");};
	
protected PARALLEL
	:	('@parallel' | '@par') {setText("parallel=\"yes\"");}
	|	('@parallel no' | '@par no') {setText("parallel=\"no\"");};
	
protected SBO
	:	('@successfulBranchesOnly' | '@success' | '@sbo') {setText("successfulBranchesOnly=\"yes\"");}
	|	('@successfulBranchesOnly no' | '@sbo no') {setText("successfulBranchesOnly=\"no\"");};
	
protected INITPARTNER
	:	('@initializePartner' | '@init') {setText("initializePartnerRole=\"yes\"");}
	|	('@initializePartner no' | '@init no') {setText("initializePartnerRole=\"no\"");};

protected INIT_COR
	:	('!' | 'force') {setText("initiate=\"yes\"");}
	|	('?' | 'join') {setText("initiate=\"join\"");};
		
protected PATTERN_COR
	:	('@>' | 'request') {setText("pattern=\"request\"");}
	|	('@<' | 'response') {setText("pattern=\"response\"");}
	|	('@><' | 'request-response') {setText("pattern=\"request-response\"");};
	
protected ISOLATED
	:	'@isolated' {setText("isolated=\"yes\"");}
	|	'@isolated no' {setText("isolated=\"no\"");};
	
protected CREATE_INST
	:	('@createInstance' | '@ci') {setText(" createInstance=\"yes\"");}
	|	('@createInstance no' | '@ci no') {setText(" createInstance=\"not\"");};

protected VALID
	:	'@validate' {setText("validate=\"yes\"");}
	|	'@validate no' {setText("validate=\"no\"");}	;
	
protected KEEPSRC
	:	('@keepSrcElementName' | '@keepSrc') {setText("keepSrcElementName=\"yes\"");}
	|	('@keepSrcElementName no' | '@keepSrc no') {setText("keepSrcElementName=\"no\"");};
	
protected IGNORE
	:	'@ignoreMisssingFromData' | '@ignore' {setText("ignoreMisssingFromData=\"yes\"");}
	|	('@ignoreMisssingFromData no' | '@ignore no') {setText("ignoreMisssingFromData=\"no\"");};

protected PART
	:	'@part' {setText("part");};

protected MUSTUND
	:	'@mustUnderstand' {setText("mustUnderstand=\"yes\"");}
	|	'@mustUnderstand no' {setText("mustUnderstand=\"no\"");};