grammar org.yakindu.sct.model.stext.SText with org.yakindu.base.expressions.Expressions

generate stext "http://www.yakindu.org/sct/statechart/SText"

import "http://www.eclipse.org/emf/2002/Ecore" as ecore
import "http://www.yakindu.org/sct/sgraph/2.0.0" as sgraph
import "http://www.yakindu.org/base/types/2.0.0" as types
import "http://www.yakindu.org/base/base/2.0.0" as base
import "http://www.yakindu.org/base/expressions/Expressions" as exp
/* ---- root rules ----
These root rules are not relevant for the grammar integration in the statechart. They just integrate the different start rules consistently 
into a single grammar. 
*/
Root:
	(roots+=DefRoot)*;

DefRoot:
	StatechartRoot | StateRoot | TransitionRoot;

StatechartRoot:
	'@@statechart@@' def=StatechartSpecification;

StateRoot:
	'@@state@@' def=StateSpecification;

TransitionRoot:
	'@@transition@@' def=TransitionSpecification;
/* ---- start rules ----
Define the starting points used by the statechart integration. These rules hook in the concrete rules of the specific grammar.
*/
ScopedElement returns sgraph::ScopedElement:
	StatechartSpecification;

StatechartSpecification:
	{StatechartSpecification} ('namespace' namespace=FQN)?
	(scopes+=StatechartScope)*;

StateSpecification:
	scope=StateScope;

TransitionSpecification:
	reaction=TransitionReaction;

Scope returns sgraph::Scope:
	(StateScope | StatechartScope);

StateScope returns sgraph::Scope:
	{SimpleScope} (reactions+=(LocalReaction))*;

StatechartScope:
	InterfaceScope | InternalScope | ImportScope;

NamedInterfaceScope returns base::NamedElement:
	InterfaceScope;

InterfaceScope:
	{InterfaceScope} 'interface' (name=XID)? ':' (declarations+=(EventDefinition | VariableDefinition | TypeAliasDefinition |
	OperationDefinition))*;

InternalScope:
	{InternalScope} 'internal' ':' ((declarations+=(EventDefinition | VariableDefinition | OperationDefinition | TypeAliasDefinition)) | (reactions +=LocalReaction))*
		;
	
ImportScope:
	 {ImportScope} 'import' ':' (imports+=[types::Package|QID])*;

QID:
	ID ('.' ID)*;

Declaration returns types::Declaration:
	EventDefinition | VariableDefinition | OperationDefinition | TypeAliasDefinition;

EventDefinition:
	(direction=Direction)? 'event' name=XID (':' typeSpecifier=TypeSpecifier)?;

enum Direction returns types::Direction:
	LOCAL='local' | IN='in' | OUT='out';

VariableDefinition:
	{VariableDefinition} (const?='const'|'var') ((readonly?='readonly')? & (external?='external')?) name=XID ':' typeSpecifier=TypeSpecifier
	('=' initialValue=Expression)?; /* ---- operation definition ---- */ 
	
TypeAliasDefinition:
	{TypeAliasDefinition} 'alias' name=XID ':' typeSpecifier=TypeSpecifier;

OperationDefinition:
	{OperationDefinition} 'operation' name=XID '(' (parameters+=Parameter (',' parameters+=Parameter)*)? ')' (':'
	typeSpecifier=TypeSpecifier)?;

TypeSpecifier returns types::TypeSpecifier:
	type=[types::Type|FQN] ('<' (typeArguments+=TypeSpecifier (',' typeArguments+=TypeSpecifier)*'>'))?;

Parameter returns types::Parameter:
	name=XID ':' typeSpecifier=TypeSpecifier;

Reaction returns sgraph::Reaction:
	LocalReaction | TransitionReaction;

LocalReaction:
	(trigger=ReactionTrigger) => ('/' effect=ReactionEffect);

TransitionReaction:
	{TransitionReaction} (trigger=StextTrigger)? ('/' effect=ReactionEffect)? ('#' (properties+=TransitionProperty)*)?;

StextTrigger returns sgraph::Trigger:
	ReactionTrigger | DefaultTrigger;

ReactionTrigger returns sgraph::Trigger:
	{ReactionTrigger} ((triggers+=EventSpec ("," triggers+=EventSpec)* (=> '[' guard=Guard ']')?) | ('[' guard=Guard
	']'));

DefaultTrigger returns sgraph::Trigger:
	{DefaultTrigger} ('default' | 'else');

Guard:
	{Guard} expression=Expression;

ReactionEffect returns sgraph::Effect:
	{ReactionEffect} actions+=(Expression | EventRaisingExpression) (=> ';' actions+=(Expression |
	EventRaisingExpression))*;

TransitionProperty returns sgraph::ReactionProperty:
	EntryPointSpec | ExitPointSpec;

EntryPointSpec:
	'>' entrypoint=ID;

ExitPointSpec:
	exitpoint=ID '>';

EventSpec:
	RegularEventSpec | TimeEventSpec | BuiltinEventSpec;

// Use SimpleFeatureCall for eventSpec to avoid guard ambiguity with array access
RegularEventSpec:
	event=SimpleFeatureCall;

SimpleFeatureCall returns exp::Expression:
	SimpleElementReferenceExpression ({exp::FeatureCall.owner=current} '.' feature=[ecore::EObject]
	(=> operationCall?='('
	(args+=Expression (',' args+=Expression)*)?
	')')?)*;

SimpleElementReferenceExpression returns exp::ElementReferenceExpression:
	reference=[ecore::EObject|ID]
	(=> operationCall?='('
	(args+=Expression (',' args+=Expression)*)?
	')')?;

TimeEventSpec:
	type=TimeEventType value=ConditionalExpression unit=TimeUnit;

enum TimeEventType:
	after | every;

BuiltinEventSpec:
	EntryEvent | ExitEvent | AlwaysEvent;

EntryEvent:
	{EntryEvent} 'entry';

ExitEvent:
	{ExitEvent} 'exit';

AlwaysEvent:
	{AlwaysEvent} ('always' | 'oncycle');

EventRaisingExpression returns exp::Expression:
	{EventRaisingExpression} 'raise' event=FeatureCall (':' value=Expression)?;

EventValueReferenceExpression returns exp::Expression:
	{EventValueReferenceExpression} 'valueof' '(' value=FeatureCall ')';

ActiveStateReferenceExpression returns exp::Expression:
	{ActiveStateReferenceExpression} 'active' '(' value=[sgraph::State|FQN] ')';

PrimaryExpression returns exp::Expression:
	PrimitiveValueExpression | FeatureCall | ActiveStateReferenceExpression | EventValueReferenceExpression |
	ParenthesizedExpression;

enum TimeUnit:
	second='s' | millisecond='ms' | microsecond='us' | nanosecond='ns';

XID:
	ID | 'namespace' | 'interface' | 'internal' | 'event' | 'local' | 'in' | 'out' | 'var' | 'readonly' | 'external' |
	'operation' | 'default' | 'else' | 'entry' | 'exit' | 'always' | 'oncycle' | 'raise' | 'valueof' | 'active';

FQN:
	XID ('.' XID)*;
