/****************************************************************************
 * Copyright (c) 2010 Andreas Unger and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Andreas Unger - initial API and implementation 
 ****************************************************************************/

grammar org.eclipselabs.mscript.language.Mscript
hidden(WS, ML_COMMENT, SL_COMMENT)

import "platform:/resource/org.eclipselabs.mscript.typesystem/model/TypeSystem.ecore"
import "platform:/resource/org.eclipselabs.mscript.language/model/Mscript.ecore"
import 'http://www.eclipse.org/emf/2002/Ecore' as ecore

Module:
	(definitions+=Definition)*;
	
Definition:
	DataTypeDefinition |
	FunctionDefinition;

/*
 * DataTypeDefinition
 */
 
DataTypeDefinition:
	EnumerationDefinition |
	RecordDefinition |
	TypeAliasDefinition;

/*
 * Enumeration
 */
 	
EnumerationDefinition:
	'enum' name=ValidID '{'
		(literalDeclarations+=EnumerationLiteralDeclaration (',' literalDeclarations+=EnumerationLiteralDeclaration)*)?
	'}';
	
EnumerationLiteralDeclaration:
	name=ValidID;
	
/*
 * TypeAlias
 */
 
TypeAliasDefinition:
	'type' name=ValidID '=' type=PrimitiveType ';';	

/*
 * Record
 */

RecordDefinition:
	'record' name=ValidID '{'
		(fieldDeclarations+=RecordFieldDeclaration)*
	'}'; 

RecordFieldDeclaration:
	name=ValidID ':' type=DataTypeSpecifier ';';

/*
 * Function
 */
 
FunctionDefinition:
	(kind=FunctionKind)? 'func' name=ValidID
			('<' templateParameterDeclarations+=TemplateParameterDeclaration (',' templateParameterDeclarations+=TemplateParameterDeclaration)* '>')?
			'(' (inputParameterDeclarations+=InputParameterDeclaration (',' inputParameterDeclarations+=InputParameterDeclaration)*)? ')'
			'->' outputParameterDeclarations+=OutputParameterDeclaration (',' outputParameterDeclarations+=OutputParameterDeclaration)* '{'
		(checks+=Check |
		assertions+=Assertion |
		functionObjectDeclarations+=FunctionObjectDeclaration |
		stateVariableDeclarations+=StateVariableDeclaration |
		equations+=Equation)*
	'}';

enum FunctionKind:
	Stateful='stateful' | Continuous='continuous';
	
TemplateParameterDeclaration:
	name=ValidID;

InputParameterDeclaration:
	name=ValidID;

OutputParameterDeclaration:
	name=ValidID;

Check:
	'check'
			('<' templateArguments+=Expression (',' templateArguments+=Expression)* '>')?
			'(' (inputParameterTypes+=DataTypeSpecifier (',' inputParameterTypes+=DataTypeSpecifier)*)? ')'
			'->' outputParameterTypes+=DataTypeSpecifier (',' outputParameterTypes+=DataTypeSpecifier)* ';';

enum AssertionStatusKind:
	Info='info' |
	Warning='warning' |
	Error='error' |
	Fatal='fatal';

Assertion:
	(static?='static')? 'assert' condition=Expression ':' statusKind=AssertionStatusKind message=Expression ';';

StateVariableDeclaration:
	'var' name=ValidID ';';
	
FunctionObjectDeclaration:
	'fob' name=ValidID ':' functionName=[FunctionDefinition|QualifiedName] ('<' templateArguments+=Expression (',' templateArguments+=Expression)* '>')? ';';

Equation:
	(initial?='initial')? leftHandSide=Expression '=' rightHandSide=Expression ';';

/*
 * Data type specifier
 */

DataTypeSpecifier:
	definedType=(PrimitiveType | ArrayType) | type=[DataType];

PrimitiveType:
	NumericType | BooleanType | StringType;
	
NumericType:
	RealType | IntegerType | ComplexType | GaussianType;
	
RealType:
	{RealType} 'real' unit=Unit;
	
IntegerType:
	{IntegerType} 'int' unit=Unit;
	
ComplexType:
	{ComplexType} 'complex' unit=Unit;

GaussianType:
	{GaussianType} 'gauss' unit=Unit;

BooleanType:
	{BooleanType} 'bool';
	
StringType:
	{StringType} 'string';
	
ArrayType:
	TensorType | =>(definedElementType=PrimitiveType | elementType=[DataType]) '[' dimensions+=ArrayDimension (',' dimensions+=ArrayDimension)* ']';
	
TensorType:
	definedElementType=NumericType '[' dimensions+=ArrayDimension (',' dimensions+=ArrayDimension)* ']';
	
ArrayDimension:
	{ArrayDimension} (size=Expression)?;

/*
 * Expressions
 */

Expression:
	LetExpression | IfExpression | SwitchExpression | RangeExpression;

LetExpression:
	'let' variableDeclarations+=LetExpressionVariableDeclaration (',' variableDeclarations+=LetExpressionVariableDeclaration)* 'in' targetExpression=Expression;
	
LetExpressionVariableDeclaration:
	(parts+=LetExpressionVariableDeclarationPart | '(' parts+=LetExpressionVariableDeclarationPart (',' parts+=LetExpressionVariableDeclarationPart)+ ')') '=' assignedExpression=Expression;

LetExpressionVariableDeclarationPart:
	name=ValidID;
	
IfExpression:
	(static?='static')? 'if' condition=Expression 'then' thenExpression=Expression 'else' elseExpression=Expression;

SwitchExpression:
	(static?='static')? 'switch' controlExpression=Expression
		(cases+=SwitchCase)*
		'default' ':' defaultExpression=Expression;

SwitchCase:
	'case' caseExpression=ImpliesExpression ':' resultExpression=Expression;

RangeExpression returns Expression:
	ImpliesExpression ({RangeExpression.operands+=current} ':' operands+=ImpliesExpression (':' operands+=ImpliesExpression)?)?;

ImpliesExpression returns Expression:
	LogicalOrExpression ({ImpliesExpression.leftOperand=current} '=>' rightOperand=LogicalOrExpression)?;

LogicalOrExpression returns Expression:
	LogicalAndExpression ({LogicalOrExpression.leftOperand=current} '||' rightOperand=LogicalAndExpression)*;
	
LogicalAndExpression returns Expression:
	EqualityExpression ({LogicalAndExpression.leftOperand=current} '&&' rightOperand=EqualityExpression)*;

EqualityExpression returns Expression:
	RelationalExpression ({EqualityExpression.leftOperand=current} operator=EqualityOperator rightOperand=RelationalExpression)*;

enum EqualityOperator:
	EqualTo='==' |
	NotEqualTo='!=';
	
RelationalExpression returns Expression:
	AdditiveExpression =>({RelationalExpression.leftOperand=current} operator=RelationalOperator rightOperand=AdditiveExpression | {TypeTestExpression.expression=current} 'is' type=DataTypeSpecifier)?;

enum RelationalOperator:
	LessThan='<' |
	LessThanOrEqualTo='<=' |
	GreaterThan='>' |
	GreaterThanOrEqualTo='>=';

AdditiveExpression returns Expression:
	MultiplicativeExpression ({AdditiveExpression.leftOperand=current} operator=AdditiveOperator rightOperand=MultiplicativeExpression)*;

enum AdditiveOperator:
	Add='+' |
	Subtract='-';

MultiplicativeExpression returns Expression:
	PowerExpression ({MultiplicativeExpression.leftOperand=current} operator=MultiplicativeOperator rightOperand=PowerExpression)*;

enum MultiplicativeOperator:
	Multiply='*' |
	Divide='/' |
	ElementWiseMultiply='.*' |
	ElementWiseDivide='./';
	
PowerExpression returns Expression:
	UnaryExpression ({PowerExpression.operand=current} operator=PowerOperator exponent=UnaryExpression)?;

enum PowerOperator:
	Power='^' |
	ElementWisePower='.^';
	
UnaryExpression returns Expression:
	PostfixExpression | {UnaryExpression} operator=UnaryOperator operand=PostfixExpression;
	
enum UnaryOperator:
	Negate='-' |
	LogicalNot='!';
	
PostfixExpression returns Expression:
	ArrayElementAccess ({PostfixExpression.operand=current} operator=PostfixOperator)?;

enum PostfixOperator:
	Transpose="'";

ArrayElementAccess returns Expression:
	MemberFeatureCall ({ArrayElementAccess.array=current} '[' subscripts+=ArraySubscript (',' subscripts+=ArraySubscript)* ']')*;

MemberFeatureCall returns Expression:
	PrimaryExpression (({MemberVariableAccess.target=current} '.' memberVariable=[CallableElement|ValidID])
		| ({FunctionCall.arguments+=current} '.' feature=[CallableElement|ValidID] '(' (arguments+=Expression (',' arguments+=Expression)*)? ')')
		| ({IterationCall.target=current} '.' identifier=ValidID '(' variables+=IterationVariable (',' variables+=IterationVariable)* (';' accumulator=IterationAccumulator)? (';' breakCondition=Expression)? '|' expression=Expression ')'))*;

AssignableMemberFeatureCall returns Expression:
	AssignablePrimaryExpression (({MemberVariableAccess.target=current} '.' memberVariable=[CallableElement|ValidID]))*;

IterationVariable:
	name=ValidID;

IterationAccumulator:
	name=ValidID '=' initializer=Expression;

ArraySubscript:
	slice?=':' | expression=Expression;

PrimaryExpression returns Expression:
	Literal |
	FeatureCall |
	DerivativeOperator |
	UnitConstructionOperator |
	ArrayConstructionOperator |
	ArrayConcatenationOperator |
	ParenthesizedExpression |
	EndExpression |
	AlgorithmExpression;

AssignablePrimaryExpression returns Expression:
	FeatureCall |
	ParenthesizedExpression;

Literal:
	NumericLiteral | BooleanLiteral | StringLiteral;
	
NumericLiteral:
	RealLiteral | IntegerLiteral;
	
RealLiteral:
	value=ValidDouble (modifier=ValidID)? unit=Unit;
	
IntegerLiteral:
	value=ValidLong (modifier=ValidID)? unit=Unit;

BooleanLiteral:
	{BooleanLiteral} (true?='true' | 'false');

StringLiteral:
	value=STRING;
		
FeatureCall returns Expression:
	VariableAccess | FunctionCall;
	
VariableAccess returns Expression:
	{VariableAccess} feature=[CallableElement|QualifiedName] ('{' stepExpression=StepExpression '}')?;

FunctionCall returns Expression:
	{FunctionCall} feature=[CallableElement|QualifiedName] '(' (arguments+=Expression (',' arguments+=Expression)*)? ')';

StepExpression:
	RangeStepExpression;
	
RangeStepExpression returns StepExpression:
	AdditiveStepExpression ({RangeStepExpression.start=current} '..' end=AdditiveStepExpression)?;

AdditiveStepExpression returns StepExpression:
	NegateStepExpression ({AdditiveStepExpression.leftOperand=current} operator=AdditiveOperator rightOperand=NegateStepExpression)*;

NegateStepExpression returns StepExpression:
	PrimitiveStepExpression | {NegateStepExpression} '-' operand=PrimitiveStepExpression;

PrimitiveStepExpression returns StepExpression:
	StepLiteral | StepN;

StepLiteral:
	value=ValidInt;
	
StepN:
	{StepN} N;

DerivativeOperator:
	'der' variable=[CallableElement|ValidID];

ArrayConstructionOperator:
	'{' expressions+=Expression ((',' expressions+=Expression)* | 'for' iterationClauses+=ArrayConstructionIterationClause (',' iterationClauses+=ArrayConstructionIterationClause)*) '}';

ArrayConstructionIterationClause:
	variableName=ValidID 'in' collectionExpression=Expression;

ArrayConcatenationOperator:
	'[' rows+=ExpressionList (';' rows+=ExpressionList)* ']';
	
ExpressionList:
	expressions+=Expression (',' expressions+=Expression)*;
	
UnitConstructionOperator:
	'$' unit=ExplicitUnit;

ParenthesizedExpression:
	'(' expressions+=Expression (',' expressions+=Expression)* ')';

EndExpression:
	{EndExpression} 'end';
	
AlgorithmExpression returns Expression:
	{AlgorithmExpression} 'algo' body=Compound;

Compound:
	{Compound} '{' (statements+=Statement)* '}';

Statement:
	Compound |
	Assignment |
	VariableDeclaration |
	IfStatement |
	WhileStatement |
	DoWhileStatement |
	ForStatement |
	ContinueStatement |
	BreakStatement |
	ReturnStatement;
	
Assignment:
	target=AssignableMemberFeatureCall '=' expression=Expression ';';
	
VariableDeclaration:
	'var' name=ValidID '=' initializer=Expression ';';

IfStatement:
	'if' '(' condition=Expression ')' thenStatement=Statement (=>'else' elseStatement=Statement)?;
	
WhileStatement:
	'while' '(' condition=Expression ')' body=Statement;

ForStatement:
	'for' '(' iterationVariable=IterationVariable 'in' collectionExpression=Expression ')' body=Statement;

DoWhileStatement:
	'do' body=Statement 'while' '(' condition=Expression ')' ';';

ContinueStatement:
	{ContinueStatement} 'continue' ';';

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

ReturnStatement:
	'return' expression=Expression ';';

/*
 * Unit expressions
 */
	
Unit:
	{Unit} ('(' ')' | '(' numerator=UnitNumerator ('/' denominator=UnitDenominator)? ')' | numerator=ImplicitUnitNumerator);

ExplicitUnit returns Unit:
	'(' numerator=UnitNumerator ('/' denominator=UnitDenominator)? ')';

UnitNumerator:
	{UnitNumerator} ONE | factors+=UnitFactor ('*' factors+=UnitFactor)*;
	
ImplicitUnitNumerator returns UnitNumerator:
	{UnitNumerator};

UnitDenominator:
	factors+=UnitFactor | '(' factors+=UnitFactor ('*' factors+=UnitFactor)* ')';

UnitFactor:
	symbol=ValidID ('^' exponent=UnitExponent)?;

UnitExponent returns ecore::EInt:
	('-')? ValidInt;

QualifiedName:
  ValidID ('::' ValidID)*;

ValidDouble returns ecore::EDouble:
	REAL;

ValidInt returns ecore::EInt:
	ONE | INT;

ValidLong returns ecore::ELong:
	ONE | INT;

ValidID:
	N | ID;

/*
 * Terminals
 */
 
terminal N:
	'n';

terminal ID:
	('_' | 'a'..'z' | 'A'..'Z') ('0'..'9' | '_' | 'a'..'z' | 'A'..'Z')*;

terminal STRING: 
	'"' (!('\\' | '"') | '\\' ("'" | '"' | '?' | '\\' | 'a' | 'b' | 'f' | 'n' | 'r' | 't' | 'v'))* '"';

terminal ONE:
	'1';

terminal REAL:
	('0'..'9')+ '.' ('0'..'9')* (('e' | 'E') ('+'|'-')? ('0'..'9')+)?;

terminal INT:
	('0'..'9')+;

terminal ML_COMMENT:
	'/*' -> '*/';

terminal SL_COMMENT:
	'//' !('\n'|'\r')* ('\r'? '\n')?;

terminal WS:
	(' '|'\t'|'\r'|'\n')+;

terminal ANY_OTHER:
	.;
