grammar be.uantwerpen.ansymo.semanticadaptation.SemanticAdaptation with org.eclipse.xtext.common.Terminals

import "http://www.eclipse.org/emf/2002/Ecore" as ecore
generate semanticAdaptation "http://www.uantwerpen.be/ansymo/semanticadaptation/SemanticAdaptation"

SemanticAdaptation:
	(imports+=Import)*
	('module' name=ID)?
	(elements+=Component)*;

Import:
	'import' module=[SemanticAdaptation];
	
Component:
	GlobalFMU | Connection;

Connection:
	src=SpecifiedPort '->' tgt=SpecifiedPort;

GlobalFMU:
	AtomicFMU | CompositeFMU | Adaptation;

FMU:
	AtomicFMU | InnerFMU | CompositeFMU | Adaptation;

AtomicFMU:
	'fmu' type=FMUType name=ID
	//('type' type=ModelType)?
	'at' path=STRING // to be found here
	('input' 'ports' inports+=Port ("," inports+=Port)* )?
	('output' 'ports' outports+=Port ("," outports+=Port)* )?
	//('full' 'internal' 'dependencies')? // not needed but extracted from the FMU (at-clause)
	;

InnerFMU: // slightly different syntax
	'inner' 'fmu' type=FMUType name=ID
	('at' path=STRING)? // optional in case of a semantic adaptation, where we still want to specify the ports
	('with' 'input' 'ports' inports+=Port ("," inports+=Port)* )?
	('with' 'output' 'ports' outports+=Port ("," outports+=Port)* )?
	;
	
CompositeFMU:
	'fmu' type=FMUType name=ID
	'at' path=STRING // to be generated here
	'composed' 'of' instances+=[FMU] (',' instances+=[FMU])*
	('input' 'ports' inports+=Port ("," inports+=Port)*)?
	('output' 'ports' outports+=Port ("," outports+=Port)* )?;

Adaptation:
	({Adaptation} 'semantic' 'adaptation' (reactiveness=ReactiveOrDelayed)? (machine=MooreOrMealy)? type=FMUType (name=ID)? | // this is a type or a name?
	{AdaptationWithSpecificMaster} 'semantic' 'adaptation' (reactiveness=ReactiveOrDelayed)? (machine=MooreOrMealy)? type=FMUType (name=ID)? 'without' 'master')
	'at' path=STRING // to be generated here
	inner=InnerFMUDeclaration
	('input' 'ports' inports+=Port ("," inports+=Port)*)? // only in case of instance-specific SA
	('output' 'ports' outports+=Port ("," outports+=Port)* )? // only in case of instance-specific SA
	(params+=ParamDeclaration)*
//	(globalvars+=Declaration)*
	// algebraic loop generates an FMU encapsulating the loop
	// note that the order of semantic adaptations is prefixed in case of a algebraic loop:
	// adaptations on internal signals of the loop are inner most, then the algebraic loop iteration, then adaptations on external signals
	(generalrules+=GeneraleRule)*
	(control=ControlRuleBlock)?
	(in=InRulesBlock)?
	(out=OutRulesBlock)?
//	('contains' '{' adaptations+=Adaptation+ '}')? // I prefer no structural information here, this belongs to a graphical model
	;

enum ReactiveOrDelayed:
	REACTIVE='reactive' | DELAYED='delayed';

enum MooreOrMealy:
	MOORE='moore' | MEALY='mealy';

FMUType:
	name=ID; // TODO: maybe better to declare types and 
	
ModelType: DE | UT | CT | DT;
	
DE: 'DE' | 'de' | 'DiscreteEvent' | 'Discrete' 'Event' | 'discrete' 'event';
UT: 'UT' | 'ut' | 'Untimed' | 'untimed';
CT: 'CT' | 'ct' | 'ContinuousTime' | 'Continuous' 'Time' | 'continuous' 'time';
DT: 'DT' | 'dt' | 'DiscreteTime' | 'Discrete' 'Time' | 'discrete' 'time';

InnerFMUDeclaration:
	/*'for' type=ModelType*/ {InnerFMUDeclarationList} 'for' ('fmu' instances+=[FMU] (',' instances+=[FMU])* /*| 'signal' ports+=SpecifiedPort (',' ports+=SpecifiedPort)**/) | // only multiple instances in case of CT because we only have CT master?
	{InnerFMUDeclarationFull} ( 'for' fmus+=InnerFMU )+ (connection+=Connection)*
;

GeneraleRule:
	AlgebraicLoopSolution | MultiRate | Derivative;

AlgebraicLoopSolution:
	{AlgebraicLoopSolutionFixPointIteration} 'successive' 'substitution' 'starts' 'at' signals+=SpecifiedPort (('and'|',') signals+=SpecifiedPort)? 'with' params+=SettingAssignment ('and' params+=SettingAssignment)* | 
	{AlgebraicLoopSolutionDelayIntroduction} 'delay' 'at' signals+=SpecifiedPort (('and'|',') signals+=SpecifiedPort)?; // TODO: must be in in or out rule
	
MultiRate:
	'multiply' 'rate' rate=INT 'times' ('with' interpolation=Interpolation)?; // default first order interpolation

Interpolation:
	({ZeroOrderInterpolation} 'no' | {FirstOrderInterpolation} 'first' 'order' | {SecondOrderInterpolation} 'second' 'order' | {ThirdOrderInterpolation} 'third' 'order' | {FourthOrderInterpolation} 'fourth' 'order') 'interpolation' | // automatically does lower order interpolation in the beginning of simulation when no previous signal values are available
	{CustomOrderInterpolation} 'interpolation' 'of' 'order' order=INT | 
	{CustomInterpolation} 'interpolation' function=AnonymousFunction;
	
Derivative:
	{PredefinedDerivative} 'with' (directionalderivative?='directionalderivative'? & timederivative?='timederivative'?) |
	{DefinedDerivative} 'extract' (extractdirectionalderivative?='directionalderivative'? & // decide sensitivity
				extracttimederivative?='timederivative'? & // decide sensitivity
				extractsensitivity+=AnonymousFunction* );  // new sensitivity definitions

InOutRules:
	InRulesBlock | OutRulesBlock;

InRulesBlock:
	{InRulesBlock} ('in' globalvars+=Declaration)* 'in' 'rules' ('with' setting+=GeneralSetting (',' setting+=GeneralSetting)*)? '{' iterationvars+=Declaration* rules+=DataRule* '}';

OutRulesBlock:
	{OutRulesBlock} ('out' globalvars+=Declaration)* 'out' 'rules' ('with' setting+=GeneralSetting (',' setting+=GeneralSetting)*)? '{' iterationvars+=Declaration* rules+=DataRule* '}';

ControlRuleBlock:
	('control' globalvars+=Declaration)* rule=ControlRule; // TODO: multiple rules or just one?

DataRule: // condition -> state transition --> output function
	(((condition=RuleCondition "->" statetransitionfunction=StateTransitionFunction) | ConditionStateTransition | ConditionLessRule) ("-->" outputfunction=OutputFunction)? | AlgebraicLoopSolution) ';';

GeneralSetting:
	{GeneralVariableSetting} strategy=RuleStrategy params+=SettingAssignment ('and' params+=SettingAssignment)* | 
	OutputFunction;
	
SettingAssignment:
	setting=RuleSetting '=' expr=Expression;
	
RuleCondition:
	{RuleCondition} condition=Expression (params+=SettingAssignment ('and' params+=SettingAssignment)*)? | 
	{CompositeRuleCondition} '{' statements+=Statement* returnstatement=ReturnStatement '}';

RuleSetting:
	{AbsoluteTolerance} 'absolute' 'tolerance' | // todo relative/absolute tolerance (look up float equality)
	{RelativeTolerance} 'relative' 'tolerance'; // todo relative/absolute tolerance (look up float equality)
	// otherwise null
	
RuleStrategy:
	{Crossing} 'crossing';
	
StateTransitionFunction:
	{StateTransitionFunction} expression=Expression |
	{StateTransitionFunction} '{' statements+=Statement* '}' |
	{StateTransitionFunction} assignment=Assignment;// |
	//{StoreNull} ('null' | 'nothing') |
	//{StateTransitionFunction} 'todo';

ConditionLessRule:
	assignment=Assignment;

ConditionStateTransition:
	quantization=Quantize;

Quantize returns ConditionStoreStrategy:
	{FiniteQuantize} 'quantize' '(' signal=SpecifiedPort ',' zones+=StringLiteral (',' boundaries+=ArithmeticExpression ',' zones+=StringLiteral)* ')' |
	{FunctionQuantize} 'quantize' '(' signal=SpecifiedPort ',' '=' function=ArithmeticExpression ')' |
	{LinearQuantize} 'quantize' signal=SpecifiedPort 'every' steps=ArithmeticExpression ('with'? 'offset' offset=ArithmeticExpression)?;
	
OutputFunction:
	{NoHold} 'no' 'hold' |
	{ZeroOrderHold} (('zero' 'order' 'hold') | 'ZOH') |
	{FirstOrderHold} (('first' 'order' 'hold') | 'FOH') |
	{CustomOutputFunction} expr=Expression |
	{CompositeOutputFunction} '{' statements+=Statement* (returnstatement=ReturnStatement)? '}';
	
ControlRule:
	{ImpliedControlRule} 'implied' | // the default
	{TriggeredControlRule} 'triggered' 'by' condition=Expression |
	{PeriodicControlRule} 'periodic' ('at' init_time=REALTYPE)? 'every' period=REALTYPE | 
	{MultipliedControlRule} 'multiplied' multiplication=INT 'times' | 
	{CustomControlRule} 'control' '{' ControlRulestatements+=Statement* returnstatement=ReturnStatement '}'; // TODO: how do we know if control is given?

AnonymousFunction: // has access to ports
	{FunctionExpression} '{=' code=Expression '}'| 
	{FunctionBody} '{' statements+=Statement* returnstatement=ReturnStatement '}' |  // should return something
	FunctionDeclaration; // TODO: should return something
	
SpecifiedPort: // if ports need owners
	(owner=[FMU] '.' )? port=[Port];

Port:
	// TODO: add initial values to ports (to do 1st, 2nd, ... order stuff)
	// TODO: add internal destination/source of port
	// Unity conversions: https://pint.readthedocs.io/en/0.7.2/
	name=ID (':=' initval=LiteralOrArray)? ( multiplicity=Multiplicity )? ( '(' unity=Unity ')' )? ('->' target=SpecifiedPort | '-->' dependency+=SpecifiedPort (dependency+=SpecifiedPort)*)?;
	// TODO: -> output port of wrapped FMU to output port of SA FMU (only used in SA!)

Multiplicity:
	'[' (lower=INT '..')? upper=INT ']';

Unity:
	DivideUnity;
	
DivideUnity returns Unity:
	MultiplyUnity (({DivideUnity.left=current} '/') right=MultiplyUnity)*;

MultiplyUnity returns Unity:
	AtomicUnity (({MultiplyUnity.left=current} '.') right=AtomicUnity)*;

AtomicUnity:
	name=ID ('^' (power=INT | power=INTTYPE))?; // somehow LETTERS as name does not work, so power will never be parsed...

//FunctionBody:
//	statements+=Statement* returnstatement=ReturnStatement;
	
Statement:
	NestableStatement | FunctionDeclaration;

NestableStatement:
	Declaration | (Assignment  ';') | (Procedure  ';') | For | If | BreakStatement;
	
For:
	'for' '(' 'var' iterator=DeclaredParameter 'in' iterable=Range ')' '{' (statements+=NestableStatement)* '}';
	
If:
	'if' '(' ifcondition=Expression ')' '{' (ifstatements+=NestableStatement)* '}' ('else' '{' (elsestatements+=NestableStatement)* '}')?;
	
FunctionDeclaration:
	'def' name=ID ('(' args+=DeclaredParameter (',' args+=DeclaredParameter)* ')')? 
	'{' statements+=Statement* returnstatement=ReturnStatement '}';

Declaration:
	'var' name=ID (':=' expr=Expression)? ';';

ParamDeclaration:
	'param' name=ID ':=' expr=Expression ';';

ReturnStatement:
	'return' Expression ';';

BreakStatement:
	{BreakStatement} 'break' ';';

DeclaredParameter:
	name=ID;
	
AbstractDeclaration: // can contain overlapping elements as long as it is only used as reference
	Port | ParamDeclaration | Declaration | DeclaredParameter | FunctionDeclaration;

LValueDeclaration: // can contain overlapping elements as long as it is only used as reference
	Port | ParamDeclaration | Declaration | DeclaredParameter; // TODO: fmu.port

Assignment:
	lvalue=Variable ':=' expr=Expression; // TODO: to what can be assigned? only local vars?

Expression:
	OrExpression;

OrExpression returns Expression:
	AndExpression ({Or.left=current} 'or' right=AndExpression)*;

AndExpression returns Expression:
	NotExpression ({And.left=current} 'and' right=NotExpression)*;
	
NotExpression returns Expression:
	{Not} 'not' left=GreaterThanExpression | 
	GreaterThanExpression;
	
GreaterThanExpression returns Expression:
	LessThanExpression ({GreaterThan.left=current} '>' right=ArithmeticExpression)?;
	
LessThanExpression returns Expression:
	GreaterThanOrEqualsExpression ({LessThan.left=current} '<' right=ArithmeticExpression)?;

GreaterThanOrEqualsExpression returns Expression:
	LessThanOrEqualsExpression ({GreaterThanOrEquals.left=current} '>=' right=ArithmeticExpression)?;
	
LessThanOrEqualsExpression returns Expression:
	EqualsExpression ({LessThanOrEquals.left=current} '<=' right=ArithmeticExpression)?;

EqualsExpression returns Expression:
	NotEqualsExpression ({Equals.left=current} '==' right=ArithmeticExpression)?;
	
NotEqualsExpression returns Expression:
	CrossesFromBelowExpression ({NotEquals.left=current} '!=' right=ArithmeticExpression)?;
		
CrossesFromBelowExpression returns Expression:
	CrossesFromAboveExpression ({CrossesFromBelow.left=current} '>!' right=ArithmeticExpression)?;
	
CrossesFromAboveExpression returns Expression:
	BooleanFunction ({CrossesFromAbove.left=current} '<!' right=BooleanFunction)?;
	
BooleanFunction returns Expression:
	//{Otherwise} 'otherwise' '(' ref=SpecifiedPort ')' |
	//{Otherwise} 'otherwise' ref=SpecifiedPort | // AMBIGUOUS!
	{Otherwise} 'otherwise' |
	ArithmeticExpression;

ArithmeticExpression returns Expression:
	Addition;

Addition returns Expression:
	Multiplication (({Plus.left=current} '+' | {Minus.left=current} '-') right=Multiplication)*;

Multiplication returns Expression:
	Range (({Multi.left=current} '*' | {Div.left=current} '/') right=Range)*;

Range returns Expression:
	UnaryOrPrimaryExpression ({Range.left=current} '..' right=UnaryOrPrimaryExpression)?;

UnaryOrPrimaryExpression returns Expression:
	NegationExpression | PrimaryExpression;
	
NegationExpression returns Expression:
	{Neg} '-' right=PrimaryExpression;

PrimaryExpression returns Expression:
	'(' Expression ')' | Literal | BuiltinFunction | Var;

LiteralOrArray:
	{ArrayLiteral} '[' elements+=Literal (',' elements+=Literal)* ']' |
	Literal;

Literal:
	IntLiteral | RealLiteral | StringLiteral | BoolLiteral | NullLiteral; //| StepLiteral;

IntLiteral:
	value=INTTYPE | value=INT;
	
RealLiteral:
	value=REALTYPE;
	
StringLiteral:
	value=STRING;
	
BoolLiteral:
	value=('true'|'false');
	
//StepLiteral:
//	value=('accept'|'discard');
	
NullLiteral:
	value='null';
	
BuiltinFunction:
	{Floor} name='floor' '(' args+=ArithmeticExpression ')' | 
	{Ceil} name='ceil' '(' args+=ArithmeticExpression ')' | 
	{Round} name='round' '(' args+=ArithmeticExpression (',' args+=ArithmeticExpression)? ')' |
	{Max} name='max' '(' args+=ArithmeticExpression ',' args+=ArithmeticExpression ')' |
	{Min} name='min' '(' args+=ArithmeticExpression ',' args+=ArithmeticExpression ')' |
	{Abs} name='abs' '(' args=ArithmeticExpression ')' |
	{Close} name='is_close' '(' args+=ArithmeticExpression ',' args+=ArithmeticExpression ',' args+=ArithmeticExpression ',' args+=ArithmeticExpression ')' |  // a, b, relative tolerance, absolute tolerance 
	{IsSet} name='is_set' '(' args=Variable ')' | 
	{DoStepFun} name='do_step' '(' fmu=[FMU] ',' H=ArithmeticExpression ')' |
	{GetState} name='get_state' '(' fmu=[FMU] ')' |
	{GetNextInternalTimeStep} name='get_next_time_step' '(' fmu=[FMU] ')' | // is actually the time step returned by do_step(fmu), preceded by get_state and succeeded by set_state
	{Elapsed} name='elapsed' '(' fmu=[FMU] ')';
	
Procedure:
	{Reject} name='reject' |
	{Step} name='local_step' '(' fmu=[FMU] ')' |
	{Discard} name='discard' '(' args=Variable ')' |
	{DoStep} name='do_step' '(' fmu=[FMU] ',' H=ArithmeticExpression ')' |
	{SetState} name='set_state' '(' fmu=[FMU] ',' state=Var ')'; // arg: new proposed step size
	
Var:
	{StepSize} name='H' | 
	{CurrentTime} name='t' |
	{ElapsedTime} name='e' | // min (elapsed(fmu1), elapsed(fmu2), ...)
	{Max} name='MAX' |
	{GenericSignal} name='signal' ('[' (
		index=NOW | 
		index=PREV | 
		index=INTTYPE |
		index=INT // TODO: int must be less than -1, TODO: expression
		) ']')? |
	Variable |
	FunctionCall; // add "directionalderivative"
	
Variable:
	ref=[LValueDeclaration] ('[' (
		index=NOW | 
		index=PREV | 
		index=INTTYPE |
		index=INT  // TODO: int must be less than -1, TODO: expression
		) ']')?; // TODO: NO RECURSION (must terminate)

FunctionCall:
	ref=[FunctionDeclaration] '(' args+=ArithmeticExpression (',' args+=ArithmeticExpression)* ')'; // TODO: NO RECURSION (must terminate)

//SignedInt returns ecore::EInt:
//	('-'|'+')? INT;

//SignedReal returns ecore::EBigDecimal:
//	'-'? REAL;

terminal INTTYPE returns ecore::EInt : '-' INT;
	
terminal REALTYPE returns ecore::EFloat : '-'? ((( INT '.' INT | '.' INT | INT '.') (('e'|'E') ('-'|'+') INT)?) | (INT ('e'|'E') ('-'|'+') INT));
//terminal UNSREALTYPE returns ecore::EFloat : ((( INT '.' INT | '.' INT | INT '.') (('e'|'E') ('-'|'+') INT)?) | (INT ('e'|'E') ('-'|'+') INT));
	
terminal NOW returns ecore::EInt: 'now';
terminal PREV returns ecore::EInt: 'prev';
	
//terminal REAL returns ecore::EBigDecimal: INT? '.' INT;

//terminal LETTERS returns ecore::EString: ('a'..'z' | 'A'..'Z')+;