diff --git a/plugins/core/hu.bme.mit.gamma.statechart.language/src/hu/bme/mit/gamma/statechart/language/parser/StatechartExpressionLanguageParserAndLinker.java b/plugins/core/hu.bme.mit.gamma.statechart.language/src/hu/bme/mit/gamma/statechart/language/parser/StatechartExpressionLanguageParserAndLinker.java index 872849143..34d99e63e 100644 --- a/plugins/core/hu.bme.mit.gamma.statechart.language/src/hu/bme/mit/gamma/statechart/language/parser/StatechartExpressionLanguageParserAndLinker.java +++ b/plugins/core/hu.bme.mit.gamma.statechart.language/src/hu/bme/mit/gamma/statechart/language/parser/StatechartExpressionLanguageParserAndLinker.java @@ -26,6 +26,7 @@ protected Injector getInjector() { return new StatechartLanguageStandaloneSetup().createInjectorAndDoEMFRegistration(); } + @Override protected Class getParserClass() { return CustomStatechartExpressionLanguageParser.class; } diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/.classpath b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/.classpath new file mode 100644 index 000000000..81fe078c2 --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/.gitignore b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/.gitignore new file mode 100644 index 000000000..ae3c17260 --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/.project b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/.project new file mode 100644 index 000000000..7f5a510b2 --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/.project @@ -0,0 +1,28 @@ + + + hu.bme.mit.gamma.scxml.transformation.commandhandler + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/.settings/org.eclipse.jdt.core.prefs b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..d4540a53f --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,10 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 +org.eclipse.jdt.core.compiler.compliance=17 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=17 diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/META-INF/MANIFEST.MF b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/META-INF/MANIFEST.MF new file mode 100644 index 000000000..84827f024 --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/META-INF/MANIFEST.MF @@ -0,0 +1,20 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: SCXML To Gamma Transformer Commandhandler +Bundle-SymbolicName: hu.bme.mit.gamma.scxml.transformation.commandhandler;singleton:=true +Bundle-Version: 1.0.0.qualifier +Automatic-Module-Name: hu.bme.mit.gamma.scxml.transformation.commandhandler +Bundle-RequiredExecutionEnvironment: JavaSE-17 +Require-Bundle: org.eclipse.core.commands, + ac.soton.scxml, + hu.bme.mit.gamma.scxml.transformation, + org.eclipse.core.resources, + org.eclipse.emf.common, + org.eclipse.emf.ecore, + org.eclipse.jface, + org.eclipse.ui.workbench, + org.eclipse.m2e.maven.runtime, + hu.bme.mit.gamma.statechart.model, + hu.bme.mit.gamma.statechart.language.ui, + org.eclipse.equinox.registry +Export-Package: hu.bme.mit.gamma.scxml.transformation.commandhandler;uses:="org.eclipse.core.commands,hu.bme.mit.gamma.util,hu.bme.mit.gamma.statechart.util" diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/build.properties b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/build.properties new file mode 100644 index 000000000..e9863e281 --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/build.properties @@ -0,0 +1,5 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + plugin.xml diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/icons/gamma-icon-16.png b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/icons/gamma-icon-16.png new file mode 100644 index 000000000..a880d2b84 Binary files /dev/null and b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/icons/gamma-icon-16.png differ diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/plugin.xml b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/plugin.xml new file mode 100644 index 000000000..031ac3acd --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/plugin.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/src/hu/bme/mit/gamma/scxml/transformation/commandhandler/CommandHandler.java b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/src/hu/bme/mit/gamma/scxml/transformation/commandhandler/CommandHandler.java new file mode 100644 index 000000000..0d99f7576 --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation.commandhandler/src/hu/bme/mit/gamma/scxml/transformation/commandhandler/CommandHandler.java @@ -0,0 +1,117 @@ +/******************************************************************************** + * Copyright (c) 2023-2025 Contributors to the Gamma project + * + * 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 + * + * SPDX-License-Identifier: EPL-1.0 + ********************************************************************************/ +package hu.bme.mit.gamma.scxml.transformation.commandhandler; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ui.handlers.HandlerUtil; + +import ac.soton.scxml.ScxmlScxmlType; +import hu.bme.mit.gamma.expression.model.ConstantDeclaration; +import hu.bme.mit.gamma.scxml.transformation.CompositeTraceability; +import hu.bme.mit.gamma.scxml.transformation.Namings; +import hu.bme.mit.gamma.scxml.transformation.ScxmlToGammaCompositeTransformer; +import hu.bme.mit.gamma.statechart.derivedfeatures.StatechartModelDerivedFeatures; +import hu.bme.mit.gamma.statechart.interface_.Component; +import hu.bme.mit.gamma.statechart.interface_.Interface; +import hu.bme.mit.gamma.statechart.interface_.Package; +import hu.bme.mit.gamma.statechart.language.ui.serializer.StatechartLanguageSerializer; +import hu.bme.mit.gamma.statechart.util.StatechartUtil; +import hu.bme.mit.gamma.util.FileUtil; +import hu.bme.mit.gamma.util.GammaEcoreUtil; + +public class CommandHandler extends AbstractHandler { + + protected final FileUtil fileUtil = FileUtil.INSTANCE; + protected final GammaEcoreUtil ecoreUtil = GammaEcoreUtil.INSTANCE; + protected final StatechartUtil statechartUtil = StatechartUtil.INSTANCE; + protected final Logger logger = Logger.getLogger("GammaLogger"); + + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + try { + ISelection sel = HandlerUtil.getActiveMenuSelection(event); + if (sel instanceof IStructuredSelection) { + IStructuredSelection selection = (IStructuredSelection) sel; + Object firstElement = selection.getFirstElement(); + if (selection.size() == 1) { + if (firstElement != null && firstElement instanceof IFile) { + IFile file = (IFile) firstElement; + IContainer parentFolder = file.getParent(); + String fileName = file.getName(); + String extensionlessFileName = fileUtil.getExtensionlessName(fileName); + String parentPath = parentFolder.getFullPath().toString(); + String path = file.getFullPath().toString(); + + // Model processing + ScxmlToGammaCompositeTransformer compositeTransformer = new ScxmlToGammaCompositeTransformer(path); + CompositeTraceability compositeTraceability = compositeTransformer.execute(); + + // Interfaces and type declarations have to be explicitly serialized in another package + List gammaInterfaces = new ArrayList( + compositeTraceability.getInterfaces()); + gammaInterfaces.removeIf(i -> i == null); + ScxmlScxmlType scxmlRoot = compositeTraceability.getScxmlRoot(); + Package gammaInterfacePackage = statechartUtil.createPackage( + Namings.getInterfacePackageName(scxmlRoot)); + gammaInterfacePackage.getInterfaces().addAll(gammaInterfaces); + + List gammaConstants = new ArrayList( + compositeTraceability.getConstantDeclarations()); + gammaInterfacePackage.getConstantDeclarations().addAll(gammaConstants); + + // Pack and serialize asynchronous component + Component rootComponent = compositeTraceability.getRootComponent(); + Package gammaCompositePackage = statechartUtil.wrapIntoPackage(rootComponent); + Collection components = compositeTraceability.getComponents(); + gammaCompositePackage.getComponents().addAll(components); + gammaCompositePackage.getImports() + .addAll(StatechartModelDerivedFeatures.getImportablePackages(gammaCompositePackage)); + gammaCompositePackage.getImports().remove(gammaCompositePackage); + + StatechartLanguageSerializer packageSerializer = new StatechartLanguageSerializer(); + logger.log(Level.INFO, "Start serializing Gamma packages..."); + + // String declarationsPackageFileName = extensionlessFileName + "Declarations.gsm"; + // ecoreUtil.normalSave(gammaInterfacePackage, parentPath, + // declarationsPackageFileName); + String declarationsPackageFileName = extensionlessFileName + "Declarations.gcd"; + packageSerializer.serialize(gammaInterfacePackage, parentPath, declarationsPackageFileName); + + // String compositePackageFileName = extensionlessFileName + ".gsm"; + // ecoreUtil.normalSave(gammaCompositePackage, parentPath, + // compositePackageFileName); + String compositePackageFileName = extensionlessFileName + ".gcd"; + packageSerializer.serialize(gammaCompositePackage, parentPath, compositePackageFileName); + + logger.log(Level.INFO, "The SCXML - Gamma statechart transformation has finished."); + } + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + +} diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/.classpath b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/.classpath new file mode 100644 index 000000000..5cf859b24 --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/.classpath @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/.gitignore b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/.gitignore new file mode 100644 index 000000000..ae3c17260 --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/.project b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/.project new file mode 100644 index 000000000..60a849793 --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/.project @@ -0,0 +1,34 @@ + + + hu.bme.mit.gamma.scxml.transformation + + + + + + org.eclipse.xtext.ui.shared.xtextBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + org.eclipse.xtext.ui.shared.xtextNature + + diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/.settings/org.eclipse.jdt.core.prefs b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..d4540a53f --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,10 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 +org.eclipse.jdt.core.compiler.compliance=17 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=17 diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/META-INF/MANIFEST.MF b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/META-INF/MANIFEST.MF new file mode 100644 index 000000000..d0482add8 --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/META-INF/MANIFEST.MF @@ -0,0 +1,26 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: SCXML To Gamma Transformation +Bundle-SymbolicName: hu.bme.mit.gamma.scxml.transformation;singleton:=true +Bundle-Version: 1.0.0.qualifier +Automatic-Module-Name: hu.bme.mit.gamma.scxml.transformation +Bundle-RequiredExecutionEnvironment: JavaSE-17 +Require-Bundle: ac.soton.scxml, + hu.bme.mit.gamma.statechart.model, + com.google.guava, + org.eclipse.xtext.xbase.lib, + org.eclipse.xtend.lib, + org.eclipse.xtend.lib.macro, + hu.bme.mit.gamma.expression.language, + com.google.inject;bundle-version="3.0.0", + hu.bme.mit.gamma.expression.model, + org.eclipse.xtext +Export-Package: hu.bme.mit.gamma.scxml.transformation +Import-Package: hu.bme.mit.gamma.statechart.language.parser, + org.antlr.runtime, + org.eclipse.xtext, + org.eclipse.xtext.nodemodel, + org.eclipse.xtext.parser, + org.eclipse.xtext.scoping, + org.eclipse.xtext.scoping.impl, + org.eclipse.xtext.util diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/build.properties b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/build.properties new file mode 100644 index 000000000..d8e2f0e92 --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/build.properties @@ -0,0 +1,5 @@ +source.. = src/,\ + xtend-gen/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/AbstractTransformer.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/AbstractTransformer.xtend new file mode 100644 index 000000000..dbe2a473c --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/AbstractTransformer.xtend @@ -0,0 +1,46 @@ +/******************************************************************************** + * Copyright (c) 2023-2025 Contributors to the Gamma project + * + * 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 + * + * SPDX-License-Identifier: EPL-1.0 + ********************************************************************************/ +package hu.bme.mit.gamma.scxml.transformation + +import hu.bme.mit.gamma.action.model.ActionModelFactory +import hu.bme.mit.gamma.expression.model.ExpressionModelFactory +import hu.bme.mit.gamma.expression.util.ExpressionTypeDeterminator2 +import hu.bme.mit.gamma.scxml.transformation.parse.ScxmlGammaExpressionLanguageLinker +import hu.bme.mit.gamma.scxml.transformation.parse.ScxmlGammaExpressionLanguageParser +import hu.bme.mit.gamma.statechart.composite.CompositeModelFactory +import hu.bme.mit.gamma.statechart.interface_.InterfaceModelFactory +import hu.bme.mit.gamma.statechart.phase.PhaseModelFactory +import hu.bme.mit.gamma.statechart.statechart.StatechartModelFactory +import hu.bme.mit.gamma.statechart.util.StatechartUtil +import hu.bme.mit.gamma.util.GammaEcoreUtil +import java.util.logging.Logger + +abstract class AbstractTransformer { + + protected final extension GammaEcoreUtil ecoreUtil = GammaEcoreUtil.INSTANCE + protected final extension StatechartUtil statechartUtil = StatechartUtil.INSTANCE +// protected final extension ActionUtil actionUtil = ActionUtil.INSTANCE +// protected final extension ExpressionUtil expressionUtil = ExpressionUtil.INSTANCE + + protected final extension InterfaceModelFactory interfaceModelFactory = InterfaceModelFactory.eINSTANCE + protected final extension CompositeModelFactory compositeModelFactory = CompositeModelFactory.eINSTANCE + protected final extension StatechartModelFactory statechartModelFactory = StatechartModelFactory.eINSTANCE + protected final extension PhaseModelFactory phaseModelFactory = PhaseModelFactory.eINSTANCE + protected final extension ActionModelFactory actionModelFactory = ActionModelFactory.eINSTANCE + protected final extension ExpressionModelFactory expressionModelFactory = ExpressionModelFactory.eINSTANCE + + protected final ScxmlGammaExpressionLanguageParser expressionLanguageParser = new ScxmlGammaExpressionLanguageParser() + protected final ScxmlGammaExpressionLanguageLinker expressionLanguageLinker = new ScxmlGammaExpressionLanguageLinker() + protected final ExpressionTypeDeterminator2 expressionTypeDeterminator = ExpressionTypeDeterminator2.INSTANCE + + protected final Logger logger = Logger.getLogger("GammaLogger") + +} diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/ActionTransformer.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/ActionTransformer.xtend new file mode 100644 index 000000000..d9501efca --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/ActionTransformer.xtend @@ -0,0 +1,432 @@ +/******************************************************************************** + * Copyright (c) 2023 Contributors to the Gamma project + * + * 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 + * + * SPDX-License-Identifier: EPL-1.0 + ********************************************************************************/ +package hu.bme.mit.gamma.scxml.transformation + +import ac.soton.scxml.ScxmlAssignType +import ac.soton.scxml.ScxmlIfType +import ac.soton.scxml.ScxmlInvokeType +import ac.soton.scxml.ScxmlOnentryType +import ac.soton.scxml.ScxmlOnexitType +import ac.soton.scxml.ScxmlParamType +import ac.soton.scxml.ScxmlRaiseType +import ac.soton.scxml.ScxmlSendType +import ac.soton.scxml.ScxmlStateType +import hu.bme.mit.gamma.action.model.Action +import hu.bme.mit.gamma.expression.model.Expression +import hu.bme.mit.gamma.expression.model.VariableDeclaration +import hu.bme.mit.gamma.statechart.composite.ComponentInstance +import hu.bme.mit.gamma.statechart.interface_.Interface +import hu.bme.mit.gamma.statechart.interface_.Port +import hu.bme.mit.gamma.statechart.interface_.RealizationMode +import hu.bme.mit.gamma.statechart.phase.History +import java.util.function.Function +import java.util.logging.Level +import org.eclipse.emf.ecore.EObject + +import static ac.soton.scxml.ScxmlModelDerivedFeatures.* + +class ActionTransformer extends AtomicElementTransformer { + + protected final extension PortTransformer portTransformer + protected final extension InterfaceTransformer interfaceTransformer + protected final extension EventTransformer eventTransformer + protected final extension ScxmlGammaExpressionTransformer expressionTransformer + + new(StatechartTraceability traceability) { + super(traceability) + + this.portTransformer = new PortTransformer(traceability) + this.interfaceTransformer = new InterfaceTransformer(traceability) + this.eventTransformer = new EventTransformer(traceability) + this.expressionTransformer = new ScxmlGammaExpressionTransformer(traceability) + } + + def Action transformOnentry(ScxmlOnentryType scxmlOnentry) { + logger.log(Level.INFO, "Transforming element (" + scxmlOnentry + ")") + + val scxmlActions = getOnentryActions(scxmlOnentry) + if (!scxmlActions.nullOrEmpty) { + val gammaEntryAction = scxmlActions.transformBlock; + return gammaEntryAction + } + return null + } + + def Action transformOnexit(ScxmlOnexitType scxmlOnexit) { + logger.log(Level.INFO, "Transforming element (" + scxmlOnexit + ")") + + val scxmlActions = getOnexitActions(scxmlOnexit) + if (!scxmlActions.nullOrEmpty) { + val gammaExitAction = scxmlActions.transformBlock; + return gammaExitAction + } + return null + } + + def dispatch Action transformAction(ScxmlAssignType scxmlAssign) { + logger.log(Level.INFO, "Transforming element (" + scxmlAssign + ")") + + val varLoc = scxmlAssign.location + val variable = traceability.getVariable(varLoc) + + val expr = scxmlAssign.expr + if (expr !== null) { + val expression = expressionLanguageParser.preprocessAndParse( + expr, + expressionLanguageLinker.getLinker(traceability) + ) + val gammaAssign = createAssignment(variable as VariableDeclaration, expression) + return gammaAssign + } + + // TODO Assignment by element's child content if expr is not present. + val gammaAssign = createEmptyStatement + + return gammaAssign + } + + def dispatch Action transformAction(ScxmlRaiseType scxmlRaise) { + logger.log(Level.INFO, "Transforming element (" + scxmlRaise + ")") + + val eventString = scxmlRaise.event + val tokens = eventString.split("\\.") + if (tokens.size < 1 || tokens.size > 3) { + throw new IllegalArgumentException( + "Event descriptor " + eventString + " does not contain exactly 1, 2 or 3 dot separated tokens." + ) + } + + var gammaInterface = null as Interface + var gammaPort = null as Port + var isDefault = false + + if (tokens.size == 1) { + isDefault = true + gammaInterface = getOrCreateDefaultInterface() + gammaPort = getOrCreateDefaultPort() + } else { + val interfaceName = tokens.get(tokens.size - 2) + gammaInterface = getOrTransformInterfaceByName(interfaceName) + + if (tokens.size >= 3) { + val portName = tokens.head + gammaPort = getOrTransformPortByName(gammaInterface, portName, RealizationMode.PROVIDED) + } else { + isDefault = true + gammaPort = getOrTransformDefaultInterfacePort(gammaInterface) + } + } + + val eventName = tokens.lastOrNull + + // If a port is specified, the event will be an out event on an interface + // realized in provided mode by the port receiving the event. + // In the case of default interfaces and ports, realization mode is also provided, + // but the trigger events are internal. + val gammaEvent = if (isDefault) { + getOrTransformInternalEvent(gammaInterface, eventName) + } else { + getOrTransformOutEvent(gammaInterface, eventName) + } + + val gammaRaise = createRaiseEventAction(gammaPort, gammaEvent, newArrayList) + return gammaRaise + } + + // TODO actions + // TODO Is traceability entry needed? + def dispatch Action transformAction(ScxmlSendType scxmlSend) { + logger.info("Transforming element (" + scxmlSend + ")") + + // TODO Check nulls / empty substrings + val eventString = scxmlSend.event + val targetInterfacePortString = scxmlSend.targetexpr + val eventTargetString = targetInterfacePortString + "." + eventString + + val tokens = eventTargetString.split("\\.") + if (tokens.size < 2 || tokens.size > 3) { + throw new IllegalArgumentException( + "Event descriptor " + eventString + " does not contain exactly 2 or 3 dot separated tokens." + ) + } + + var gammaInterface = null as Interface + var gammaPort = null as Port + + val interfaceName = tokens.get(tokens.size - 2) + gammaInterface = getOrTransformInterfaceByName(interfaceName) + + if (tokens.size >= 3) { + val portName = tokens.head + gammaPort = getOrTransformPortByName(gammaInterface, portName, RealizationMode.PROVIDED) + } else { + gammaPort = getOrTransformDefaultInterfacePort(gammaInterface) + } + + val eventName = tokens.lastOrNull + val gammaEvent = getOrTransformOutEvent(gammaInterface, eventName) + + // Event elements + // TODO Refactor, extract param and argument transformer method + // TODO Named parameter support + // In the Gamma event, the order of the parameters and arguments matters, they are not named. + // Currently, every event parameter of the same event has to be specified, in exact order. + val gammaArgumentNames = newArrayList + val gammaArgumentExpressions = newArrayList + val arguments = scxmlSend.getContentsOfType(ScxmlParamType) + for (argument : arguments) { + val argName = argument.name + val argExpr = argument.expr + + if (argExpr !== null) { + val argExpression = expressionLanguageParser.preprocessAndParse( + argExpr, + expressionLanguageLinker.getLinker(traceability) + ) + gammaArgumentNames += argName + gammaArgumentExpressions += argExpression + + val argType = expressionTypeDeterminator.getType(argExpression) + + // TODO Traceability; event+param -> event+param + // existsParameter(event, param) + // getOrCreateParameter(event, param): creates if not exists, then returns + if (!gammaEvent.parameterDeclarations.exists[it.name == argName]) { + val gammaEventParameterDeclaration = argType.createParameterDeclaration(argName) + gammaEvent.parameterDeclarations += gammaEventParameterDeclaration + + // TODO Refactor into getOrCreate event parameter method + if (!traceability.containsEventParameter(argName)) { + traceability.putEventParameter(argName, gammaEventParameterDeclaration) + } + } + } + } + + // TODO Reorder event arguments according to parameter names in event + // TODO Mix named and indexed parameter passing + // TODO Name -> Find argument by name + // No name -> get index of element in parent's children list + // Parametric(parameterized)Element-ekre meg lehet csinálni + val orderedGammaArguments = newArrayList + for (eventParameter : gammaEvent.parameterDeclarations) { + val index = gammaArgumentNames.indexOf(eventParameter.name) + /*if (index < 0) { + val index = ecoreUtil.getIndex(0) + }*/ + val argumentExpression = gammaArgumentExpressions.get(index) + orderedGammaArguments += argumentExpression + } + + val gammaRaise = createRaiseEventAction(gammaPort, gammaEvent, orderedGammaArguments) + return gammaRaise + } + + def dispatch Action transformAction(ScxmlIfType scxmlIf) { + logger.log(Level.INFO, "Transforming element (" + scxmlIf + ")") + + val gammaIf = createIfStatement + val cond = scxmlIf.cond + if (!cond.nullOrEmpty) { + val condExpression = expressionLanguageParser.preprocessAndParse( + cond, + expressionLanguageLinker.getLinker(traceability) + ) + + val thenStatements = getIfThenActions(scxmlIf); + val gammaThenStatements = thenStatements.transformBlock + + val gammaConditional = createBranch + gammaConditional.guard = condExpression + gammaConditional.action = gammaThenStatements + gammaIf.conditionals += gammaConditional + } + return gammaIf + } + + // TODO check return type (Action vs other) + // TODO use global section of traceability object to store component types, interfaces etc. + def dispatch Action transformAction(ScxmlInvokeType scxmlInvoke) { + logger.log(Level.INFO, "Transforming element (" + scxmlInvoke + ")") + + // TODO invoked statechart type transformation should already be done at this point, + // or be done lazily by the get call. Either way, ActionTransformer is + // not responsible for invoking contained statechart type transformation. + val invokedTypeSourceURI = scxmlInvoke.src + + // TODO get invoked type traceability + // TODO check component type + val invokedTypeTraceability = traceability.compositeTraceability.getTraceability(invokedTypeSourceURI) + val gammaSubcomponentType = invokedTypeTraceability.statechart /*adapter as AsynchronousComponent*/ + + val gammaSubcomponent = gammaSubcomponentType.instantiateComponent + gammaSubcomponent.name = scxmlInvoke.id + + val missionPhaseStateAnnotation = createMissionPhaseStateAnnotation + + val invokeParameters = scxmlInvoke.param + + // TODO Pass arguments by subcomponent parameter names + val arguments = invokeParameters.filter[it.name == "_argument"] + /* arguments.forEach [ it | + * val gammaArgument = it.expr.transformArgument + * gammaSubcomponent.arguments += gammaArgument + * ] */ + val parameters = gammaSubcomponentType.parameterDeclarations + for (parameter : parameters) { + val argument = arguments.findFirst[it.name == parameter.name] + val gammaArgument = argument.expr.transformArgument + gammaSubcomponent.arguments += gammaArgument + } + + missionPhaseStateAnnotation.component = gammaSubcomponent + + val portBindings = invokeParameters.filter[it.name == "_port_binding"] + portBindings.forEach [ it | + val gammaPortBinding = transformPortBinding(gammaSubcomponent, it.expr) + missionPhaseStateAnnotation.portBindings += gammaPortBinding + ] + + val variableBindings = invokeParameters.filter[it.name == "_variable_binding"] + variableBindings.forEach [ it | + val gammaVariableBinding = transformVariableBinding(gammaSubcomponent, it.expr) + missionPhaseStateAnnotation.variableBindings += gammaVariableBinding + ] + + val history = invokeParameters.findFirst[it.name == "_history"] + if (history !== null) { + val gammaHistory = switch history.expr { + case "shallow": History.SHALLOW_HISTORY + case "deep": History.DEEP_HISTORY + case "no", + default: History.NO_HISTORY + } + missionPhaseStateAnnotation.history = gammaHistory + } + + // TODO State, transition + // Put transformed invoke to stable target state + // TODO Assignment by child content if expr is not present + val parentState = scxmlInvoke.getContainerOfType(ScxmlStateType) + val gammaState = traceability.getState(parentState) + gammaState.annotations += missionPhaseStateAnnotation + + val gammaEmptyAction = createEmptyStatement + return gammaEmptyAction + } + + def Action transformBlock(Iterable actions) { + if (actions.empty) { + return createBlock + } + + val gammaActions = actions.map[it.transformAction].toList + val gammaBlock = gammaActions.wrap + + return gammaBlock + } + + // TODO Connect statechart arguments with statechart parameters by name + private def Expression transformArgument(String argumentSpec) { + val argumentString = argumentSpec.trim + val tokens = argumentString.split("\\.|\\s*\\=\\s*") + if (tokens.size != 2) { + throw new IllegalArgumentException( + "Argument descriptor " + argumentString + " does not contain exactly 2 dot separated tokens." + ) + } + // TODO Trim tokens + // TODO Use parameter name to determine the order of statechart arguments + val targetParameterName = tokens.get(0) + val scxmlArgumentExpression = tokens.get(1) + + // TODO Expression parsing + val gammaArgumentExpression = expressionLanguageParser.preprocessAndParse( + scxmlArgumentExpression, + expressionLanguageLinker.getLinker(traceability) + ) + + return gammaArgumentExpression + } + + // TODO Reuse code - port/variable bindings + // TODO? Pattern, Matcher, regexes etc. + private def transformPortBinding(ComponentInstance instance, String portBindingSpec) { + val bindingString = portBindingSpec.trim + val tokens = bindingString.split("\\.|\\s*\\-\\s*") + if (tokens.size != 3) { + throw new IllegalArgumentException( + "Binding descriptor " + bindingString + " does not contain exactly 3 dot separated tokens." + ) + } + // TODO Trim tokens + val sourcePortName = tokens.get(0) + val targetInstanceName = tokens.get(1) + val targetPortName = tokens.get(2) + + // TODO Throw exception if port does not exist or ambiguous + val gammaSourcePort = traceability.getPort(sourcePortName) + val gammaInstancePortReference = createInstancePortReference(instance, targetInstanceName, targetPortName) + + val gammaPortBinding = createPortBinding(gammaSourcePort, gammaInstancePortReference) + return gammaPortBinding + } + + private def createInstancePortReference(ComponentInstance instance, String instanceName, String scxmlPortName) { + // val instance = traceability.compositeTraceability.getComponentInstance(instanceName) + // TODO Make it more performant to get statechart traceability + // by instance invokeId or source URI. + val statechartTraceability = traceability.compositeTraceability.getTraceabilityById(instanceName) + val port = statechartTraceability.getPort(scxmlPortName) + + val instancePortReference = createInstancePortReference(instance, port) + return instancePortReference + } + + private def transformVariableBinding(ComponentInstance instance, String variableBindingSpec) { + val bindingString = variableBindingSpec.trim + val tokens = bindingString.split("\\.|\\s*\\-\\s*") + if (tokens.size != 3) { + throw new IllegalArgumentException( + "Binding descriptor " + bindingString + " does not contain exactly 3 dot separated tokens." + ) + } + + val sourceVariableName = tokens.get(0) + val targetInstanceName = tokens.get(1) + val targetVariableName = tokens.get(2) + + // TODO Throw exception if port does not exist or ambiguous + val gammaSourceVariable = traceability.getVariable(sourceVariableName) + val gammaInstanceVariableReference = createInstanceVariableReference(instance, targetInstanceName, + targetVariableName) + + val gammaVariableBinding = createVariableBinding + gammaVariableBinding.instanceVariableReference = gammaInstanceVariableReference + gammaVariableBinding.statechartVariable = gammaSourceVariable + return gammaVariableBinding + } + + private def createInstanceVariableReference(ComponentInstance instance, String instanceName, + String scxmlVariableName) { + // val instance = traceability.compositeTraceability.getComponentInstance(instanceName) + // TODO Make it more performant to get statechart traceability + // by instance invokeId or source URI. + val statechartTraceability = traceability.compositeTraceability.getTraceabilityById(instanceName) + val variable = statechartTraceability.getVariable(scxmlVariableName) + + val instanceVariableReference = createInstanceVariableReference + instanceVariableReference.instance = instance + instanceVariableReference.variable = variable + return instanceVariableReference + } + +} diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/AtomicElementTransformer.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/AtomicElementTransformer.xtend new file mode 100644 index 000000000..2be9e9f9c --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/AtomicElementTransformer.xtend @@ -0,0 +1,20 @@ +/******************************************************************************** + * Copyright (c) 2023-2025 Contributors to the Gamma project + * + * 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 + * + * SPDX-License-Identifier: EPL-1.0 + ********************************************************************************/ +package hu.bme.mit.gamma.scxml.transformation + +abstract class AtomicElementTransformer extends AbstractTransformer { + + protected final StatechartTraceability traceability + + new(StatechartTraceability traceability) { + this.traceability = traceability + } +} diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/CompositeElementTransformer.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/CompositeElementTransformer.xtend new file mode 100644 index 000000000..54e898a30 --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/CompositeElementTransformer.xtend @@ -0,0 +1,20 @@ +/******************************************************************************** + * Copyright (c) 2023-2025 Contributors to the Gamma project + * + * 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 + * + * SPDX-License-Identifier: EPL-1.0 + ********************************************************************************/ +package hu.bme.mit.gamma.scxml.transformation + +abstract class CompositeElementTransformer extends AbstractTransformer { + + protected final CompositeTraceability compositeTraceability + + new(CompositeTraceability compositeTraceability) { + this.compositeTraceability = compositeTraceability + } +} diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/CompositeTraceability.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/CompositeTraceability.xtend new file mode 100644 index 000000000..d530b3653 --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/CompositeTraceability.xtend @@ -0,0 +1,221 @@ +/******************************************************************************** + * Copyright (c) 2023-2025 Contributors to the Gamma project + * + * 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 + * + * SPDX-License-Identifier: EPL-1.0 + ********************************************************************************/ +package hu.bme.mit.gamma.scxml.transformation + +import ac.soton.scxml.ScxmlInvokeType +import ac.soton.scxml.ScxmlScxmlType +import hu.bme.mit.gamma.expression.model.ConstantDeclaration +import hu.bme.mit.gamma.expression.model.ParameterDeclaration +import hu.bme.mit.gamma.statechart.composite.AsynchronousComponentInstance +import hu.bme.mit.gamma.statechart.interface_.Component +import hu.bme.mit.gamma.statechart.interface_.Event +import hu.bme.mit.gamma.statechart.interface_.Interface +import hu.bme.mit.gamma.statechart.interface_.Port +import java.util.List +import java.util.Map + +import static com.google.common.base.Preconditions.checkNotNull + +class CompositeTraceability { + + // URI of the root SCXML statechart model to transform + protected final String rootFileURI + + // Resulting root component + protected Component rootComponent + + // Default message queue capacity + protected ConstantDeclaration queueCapacity + + // TODO Add interfaces, events, ports, bindings and channels + // (Needed for the declarations package and serialization of the components package.) + protected final Map statecharts = newHashMap + protected final Map instances = newHashMap + protected final Map ports = newHashMap + + // Global mappings: Interfaces, events, declarations + protected final Map interfaces = newHashMap + protected final Map, Event> internalEvents = newHashMap + protected final Map, Event> inEvents = newHashMap + protected final Map, Event> outEvents = newHashMap + protected final Map allEvents = newHashMap + protected final Map allEventParameters = newHashMap + + // - Asynchronous Component + new(String rootFileURI) { + this.rootFileURI = rootFileURI + } + + // Creates a traceability object for the Gamma transformation of + // the SCXML statechart model found at rootFileURI. + def createStatechartTraceability(String rootFileURI) { + return new StatechartTraceability( + this, + rootFileURI, + interfaces, + queueCapacity, + internalEvents, + inEvents, + outEvents, + allEvents, + allEventParameters + ) + } + + // TODO Get from statecharts or instances map by rootFileURI string, if needed + def getScxmlRoot() { + val rootTraceability = getTraceability(rootFileURI) + val rootScxmlElement = rootTraceability.scxmlRoot + checkNotNull(rootScxmlElement) + return rootScxmlElement + } + + def getRootComponent() { + val rootTraceability = getTraceability(rootFileURI) + + // TODO Which kind of root Gamma component should we return? statechart / adapter / other? + val rootComponent = rootTraceability.statechart + checkNotNull(rootComponent) + return rootComponent + } + + def setRootComponent(Component rootComponent) { + this.rootComponent = rootComponent + } + + def getQueueCapacityDeclaration() { + return queueCapacity + } + + def setQueueCapacity(ConstantDeclaration capacity) { + this.queueCapacity = capacity + } + + // String URI of the SCXML statechart definition source - Statechart Traceability + def putTraceability(String statechartUri, StatechartTraceability statechartTraceability) { + checkNotNull(statechartUri) + checkNotNull(statechartTraceability) + statecharts += statechartUri -> statechartTraceability + } + + def getTraceability(String statechartUri) { + checkNotNull(statechartUri) + val statechartTraceability = statecharts.get(statechartUri) + checkNotNull(statechartTraceability) + return statechartTraceability + } + + // TODO Simplify map structure and getter. + def getTraceabilityById(String scxmlInvokeId) { + checkNotNull(scxmlInvokeId) + val invoke = instances.keySet.findFirst[invoke|invoke.id == scxmlInvokeId] + checkNotNull(invoke) + val statechartTraceability = statecharts.filter[source, _|source == invoke.src].values.head + checkNotNull(statechartTraceability) + return statechartTraceability + } + + def getTraceability(ScxmlScxmlType scxmlRoot) { + checkNotNull(scxmlRoot) + val statechartTraceability = statecharts.values.findFirst[it.scxmlRoot === scxmlRoot] + checkNotNull(statechartTraceability) + return statechartTraceability + } + + def containsTraceability(String statechartUri) { + checkNotNull(statechartUri) + return statecharts.containsKey(statechartUri) + } + + // - Asynchronous Component Instance + def putComponentInstance(ScxmlInvokeType scxmlInvoke, AsynchronousComponentInstance instance) { + checkNotNull(scxmlInvoke) + checkNotNull(instance) + instances += scxmlInvoke -> instance + } + + def getComponentInstance(ScxmlInvokeType scxmlInvoke) { + checkNotNull(scxmlInvoke) + val instance = instances.get(scxmlInvoke) + checkNotNull(instance) + return instance + } + + def getComponentInstance(String scxmlInvokeId) { + checkNotNull(scxmlInvokeId) + val instance = instances.filter[invoke, _|invoke.id == scxmlInvokeId].values.head + checkNotNull(instance) + return instance + } + + // Ports by string identifier (for event strings like 'port.interface.event) + def putPort(String scxmlPortName, Port gammaPort) { + checkNotNull(scxmlPortName) + checkNotNull(gammaPort) + ports += scxmlPortName -> gammaPort + } + + def getPort(String scxmlPortName) { + checkNotNull(scxmlPortName) + val gammaPort = ports.get(scxmlPortName) + checkNotNull(gammaPort) + return gammaPort + } + + def containsPort(String scxmlPortName) { + checkNotNull(scxmlPortName) + return ports.containsKey(scxmlPortName) + } + + // + + def getInEvents() { + return inEvents + } + + def getOutEvents() { + return outEvents + } + + def getInstances() { + return instances + } + + def getStatecharts() { + return statecharts.values.toList + } + + // + + def getConstantDeclarations() { + return List.of(queueCapacity) + } + + def getInterfaces() { + val statechartTraceabilities = statecharts.values + return ( + interfaces.values + + statechartTraceabilities.map[it.defaultInterface] + + statechartTraceabilities.map[it.defaultInterfacePorts.keySet].flatten + ).toSet + } + + def getComponents() { + val statechartTraceabilities = statecharts.values + return ( + instances.values.map[it.getType] + + /* TODO Serialize async adapters */ + /* statechartTraceabilities.map[it.getAdapter] + */ + statechartTraceabilities.map[it.getStatechart] + ).toSet + } + +} diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/DataTransformer.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/DataTransformer.xtend new file mode 100644 index 000000000..91e0a55ea --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/DataTransformer.xtend @@ -0,0 +1,55 @@ +/******************************************************************************** + * Copyright (c) 2023 Contributors to the Gamma project + * + * 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 + * + * SPDX-License-Identifier: EPL-1.0 + ********************************************************************************/ +package hu.bme.mit.gamma.scxml.transformation + +import ac.soton.scxml.ScxmlDataType +import hu.bme.mit.gamma.expression.model.ParameterDeclaration +import hu.bme.mit.gamma.expression.model.VariableDeclaration +import java.util.logging.Level + +class DataTransformer extends AtomicElementTransformer { + + new(StatechartTraceability traceability) { + super(traceability) + } + + // Note: the type of the parameter declaration is inferred from the provided expr of the element. + def ParameterDeclaration transformParameter(ScxmlDataType scxmlData) { + logger.log(Level.INFO, "Transforming element (" + scxmlData + ")") + + val id = scxmlData.id + val expr = scxmlData.expr + val expression = expressionLanguageParser.parse(expr, traceability.variables) + val type = expressionTypeDeterminator.getType(expression) + + val gammaDeclaration = createParameterDeclaration(type, id); + + traceability.put(scxmlData, gammaDeclaration) + + return gammaDeclaration + } + + def VariableDeclaration transformVariable(ScxmlDataType scxmlData) { + logger.log(Level.INFO, "Transforming element (" + scxmlData + ")") + + val id = scxmlData.id + val expr = scxmlData.expr + val expression = expressionLanguageParser.parse(expr, traceability.variables) + val type = expressionTypeDeterminator.getType(expression) + + val gammaDeclaration = createVariableDeclaration(type, id, expression); + + traceability.put(scxmlData, gammaDeclaration) + + return gammaDeclaration + } + +} \ No newline at end of file diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/EventTransformer.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/EventTransformer.xtend new file mode 100644 index 000000000..3d823e7ac --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/EventTransformer.xtend @@ -0,0 +1,83 @@ +/******************************************************************************** + * Copyright (c) 2023 Contributors to the Gamma project + * + * 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 + * + * SPDX-License-Identifier: EPL-1.0 + ********************************************************************************/ +package hu.bme.mit.gamma.scxml.transformation + +import hu.bme.mit.gamma.statechart.interface_.EventDirection +import hu.bme.mit.gamma.statechart.interface_.Interface + +import static hu.bme.mit.gamma.scxml.transformation.Namings.* + +class EventTransformer extends AtomicElementTransformer { + new(StatechartTraceability traceability) { + super(traceability) + } + + def getOrTransformInternalEvent(Interface gammaInterface, String eventName) { + if (traceability.containsInternalEvent(gammaInterface -> eventName)) { + return traceability.getInternalEvent(gammaInterface -> eventName) + } + else { + return transformInternalEvent(gammaInterface, eventName) + } + } + + protected def transformInternalEvent(Interface gammaInterface, String eventName) { + val gammaEvent = transformEvent(gammaInterface, EventDirection.INTERNAL, eventName) + gammaEvent.name = getInternalEventName(eventName) + traceability.putInternalEvent(gammaInterface -> eventName, gammaEvent) + return gammaEvent + } + + def getOrTransformInEvent(Interface gammaInterface, String eventName) { + if (traceability.containsInEvent(gammaInterface -> eventName)) { + return traceability.getInEvent(gammaInterface -> eventName) + } + else { + return transformInEvent(gammaInterface, eventName) + } + } + + protected def transformInEvent(Interface gammaInterface, String eventName) { + val gammaEvent = transformEvent(gammaInterface, EventDirection.IN, eventName) + gammaEvent.name = getInEventName(eventName) + traceability.putInEvent(gammaInterface -> eventName, gammaEvent) + return gammaEvent + } + + def getOrTransformOutEvent(Interface gammaInterface, String eventName) { + if (traceability.containsOutEvent(gammaInterface -> eventName)) { + return traceability.getOutEvent(gammaInterface -> eventName) + } + else { + return transformOutEvent(gammaInterface, eventName) + } + } + + protected def transformOutEvent(Interface gammaInterface, String eventName) { + val gammaEvent = transformEvent(gammaInterface, EventDirection.OUT, eventName) + gammaEvent.name = getOutEventName(eventName) + traceability.putOutEvent(gammaInterface -> eventName, gammaEvent) + return gammaEvent + } + + // This method assumes that the transformation of the interface + // of the respective event has already happened when we transform the event. + private def transformEvent(Interface gammaInterface, EventDirection direction, String eventName) { + val gammaEvent = createEvent + val gammaEventDeclaration = createEventDeclaration + gammaEventDeclaration.event = gammaEvent + gammaEventDeclaration.direction = direction + + gammaInterface.events += gammaEventDeclaration + return gammaEvent + } + +} \ No newline at end of file diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/InterfaceTransformer.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/InterfaceTransformer.xtend new file mode 100644 index 000000000..41b325c3c --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/InterfaceTransformer.xtend @@ -0,0 +1,61 @@ +/******************************************************************************** + * Copyright (c) 2023 Contributors to the Gamma project + * + * 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 + * + * SPDX-License-Identifier: EPL-1.0 + ********************************************************************************/ +package hu.bme.mit.gamma.scxml.transformation + +import static com.google.common.base.Preconditions.checkState +import static hu.bme.mit.gamma.scxml.transformation.Namings.* + +class InterfaceTransformer extends AtomicElementTransformer { + new(StatechartTraceability traceability) { + super(traceability) + } + + def getOrCreateDefaultInterface() { + val defaultInterface = traceability.getDefaultInterface + if (defaultInterface !== null) { + return defaultInterface + } + else { + val gammaInterface = createDefaultInterface + traceability.setDefaultInterface(gammaInterface) + return gammaInterface + } + } + + protected def createDefaultInterface() { + val defaultInterface = createInterface + val defaultInterfaceName = getDefaultInterfaceName(traceability.getTypeName()) + defaultInterface.name = defaultInterfaceName + + return defaultInterface + } + + def getOrTransformInterfaceByName(String interfaceName) { + checkState(interfaceName !== null) + if (traceability.containsInterface(interfaceName)) { + return traceability.getInterface(interfaceName) + } + else { + val gammaInterface = transformInterfaceByName(interfaceName) + traceability.putInterface(interfaceName, gammaInterface) + return gammaInterface + } + } + + protected def transformInterfaceByName(String interfaceName) { + val gammaInterface = createInterface + val gammaInterfaceName = getInterfaceName(interfaceName) + gammaInterface.name = gammaInterfaceName + + return gammaInterface + } + +} \ No newline at end of file diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/Namings.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/Namings.xtend new file mode 100644 index 000000000..8c418ff9e --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/Namings.xtend @@ -0,0 +1,50 @@ +/******************************************************************************** + * Copyright (c) 2023-2025 Contributors to the Gamma project + * + * 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 + * + * SPDX-License-Identifier: EPL-1.0 + ********************************************************************************/ +package hu.bme.mit.gamma.scxml.transformation + +import ac.soton.scxml.ScxmlFinalType +import ac.soton.scxml.ScxmlHistoryType +import ac.soton.scxml.ScxmlParallelType +import ac.soton.scxml.ScxmlScxmlType +import ac.soton.scxml.ScxmlStateType + +class Namings { + + // TODO check default names differ from user defined interface and port names at the end of the transformation + def static String getDefaultPortName(String instanceName) '''«instanceName»_DefaultPort''' + def static String getDefaultInterfaceName(String instanceName) '''«instanceName»_DefaultInterface''' + def static String getDefaultInterfacePortName(String scxmlInterfaceName) '''«scxmlInterfaceName»_DefaultPort''' + def static String getInterfaceName(String scxmlInterfaceName) '''«scxmlInterfaceName»''' + def static String getPortName(String scxmlPortName) '''«scxmlPortName»''' + + def static String getInternalEventName(String scxmlEventName) '''«scxmlEventName»''' + def static String getInEventName(String scxmlEventName) '''«scxmlEventName»''' + def static String getOutEventName(String scxmlEventName) '''«scxmlEventName»''' + + def static String getAdapterName(ScxmlScxmlType scxmlRoot) '''«scxmlRoot.name»Adapter''' + def static String getInternalEventQueueName(ScxmlScxmlType scxmlRoot) '''«scxmlRoot.name»InternalEventQueue''' + def static String getExternalEventQueueName(ScxmlScxmlType scxmlRoot) '''«scxmlRoot.name»ExternalEventQueue''' + + def static String getInterfacePackageName(ScxmlScxmlType scxmlRoot) '''«scxmlRoot.name.toLowerCase»_interfaces''' + def static String getCompositeStatechartName(ScxmlScxmlType scxmlRoot) '''«scxmlRoot.name»''' + def static String getStatechartName(ScxmlScxmlType scxmlRoot) '''«scxmlRoot.name»''' + def static String getRegionName(String scxmlElementName) '''«scxmlElementName»Region''' + + def static String getParallelName(ScxmlParallelType scxmlParallel) '''«scxmlParallel.id»''' + def static String getStateName(ScxmlStateType scxmlState) '''«scxmlState.id»''' + def static String getFinalName(ScxmlFinalType scxmlFinal) '''«scxmlFinal.id»''' + + def static String getInitialName(ScxmlStateType scxmlParentState) '''«scxmlParentState.id»Initial''' + def static String getInitialName(ScxmlScxmlType scxmlRoot) '''«scxmlRoot.name»Initial''' + def static String getShallowHistoryName(ScxmlHistoryType scxmlHistoryState) '''«scxmlHistoryState.id»ShallowHistory''' + def static String getDeepHistoryName(ScxmlHistoryType scxmlHistoryState) '''«scxmlHistoryState.id»DeepHistory''' + +} diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/PortTransformer.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/PortTransformer.xtend new file mode 100644 index 000000000..58414fc48 --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/PortTransformer.xtend @@ -0,0 +1,126 @@ +/******************************************************************************** + * Copyright (c) 2023 Contributors to the Gamma project + * + * 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 + * + * SPDX-License-Identifier: EPL-1.0 + ********************************************************************************/ +package hu.bme.mit.gamma.scxml.transformation + +import hu.bme.mit.gamma.statechart.interface_.Interface +import hu.bme.mit.gamma.statechart.interface_.RealizationMode + +import static com.google.common.base.Preconditions.checkState +import static hu.bme.mit.gamma.scxml.transformation.Namings.* + +class PortTransformer extends AtomicElementTransformer { + + protected final extension InterfaceTransformer interfaceTransformer + + new(StatechartTraceability traceability) { + super(traceability) + + this.interfaceTransformer = new InterfaceTransformer(traceability) + } + + // Gets or creates an interface and a port realizing the interface in the specified mode. + def getOrTransformPort(String scxmlPortName, + String scxmlInterfaceName, RealizationMode realizationMode + ) { + val gammaInterfaceName = getInterfaceName(scxmlInterfaceName) + val gammaInterface = interfaceTransformer.getOrTransformInterfaceByName(gammaInterfaceName) + + val gammaPortName = getPortName(scxmlPortName) + val gammaPort = getOrTransformPortByName(gammaInterface, gammaPortName, realizationMode) + + return gammaPort + } + + def getOrCreateDefaultPort() { + val defaultPort = traceability.getDefaultPort + if (defaultPort !== null) { + return defaultPort + } + else { + val gammaPort = createDefaultPort + traceability.setDefaultPort(gammaPort) + return gammaPort + } + } + + // We assume that the statechart's default interface already exists at this point. + protected def createDefaultPort() { + val defaultInterface = interfaceTransformer.getOrCreateDefaultInterface + + val defaultInterfaceRealization = createInterfaceRealization + defaultInterfaceRealization.realizationMode = RealizationMode.PROVIDED + defaultInterfaceRealization.interface = defaultInterface + + val defaultPort = createPort + defaultPort.name = getDefaultPortName(traceability.getTypeName()) + defaultPort.interfaceRealization = defaultInterfaceRealization + + return defaultPort + } + + def getOrTransformDefaultInterfacePort(Interface gammaInterface) { + checkState(gammaInterface !== null) + if (traceability.containsDefaultInterfacePort(gammaInterface)) { + return traceability.getDefaultInterfacePort(gammaInterface) + } + else { + val gammaPort = transformDefaultInterfacePort(gammaInterface) + traceability.putDefaultInterfacePort(gammaInterface, gammaPort) + return gammaPort + } + } + + // For now, all ports realize their interfaces in provided mode. + protected def transformDefaultInterfacePort(Interface gammaInterface) { + val gammaInterfaceRealization = createInterfaceRealization + gammaInterfaceRealization.realizationMode = RealizationMode.PROVIDED + gammaInterfaceRealization.interface = gammaInterface + + val gammaPort = createPort + val gammaInterfaceName = gammaInterface.name + gammaPort.name = getDefaultInterfacePortName(gammaInterfaceName) + gammaPort.interfaceRealization = gammaInterfaceRealization + + return gammaPort + } + + def getOrTransformPortByName(Interface gammaInterface, + String portName, RealizationMode realizationMode + ) { + checkState(gammaInterface !== null) + checkState(portName !== null) + checkState(realizationMode !== null) + + if (traceability.containsPort(portName)) { + return traceability.getPort(portName) + } + else { + val gammaPort = transformPortByName(gammaInterface, portName, realizationMode) + traceability.putPort(portName, gammaPort) + return gammaPort + } + } + + protected def transformPortByName(Interface gammaInterface, + String portName, RealizationMode realizationMode + ) { + val gammaInterfaceRealization = createInterfaceRealization + gammaInterfaceRealization.realizationMode = realizationMode + gammaInterfaceRealization.interface = gammaInterface + + val gammaPort = createPort + gammaPort.name = getPortName(portName) + gammaPort.interfaceRealization = gammaInterfaceRealization + + return gammaPort + } + +} \ No newline at end of file diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/ScxmlGammaExpressionTransformer.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/ScxmlGammaExpressionTransformer.xtend new file mode 100644 index 000000000..17faa4494 --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/ScxmlGammaExpressionTransformer.xtend @@ -0,0 +1,19 @@ +/******************************************************************************** + * Copyright (c) 2023 Contributors to the Gamma project + * + * 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 + * + * SPDX-License-Identifier: EPL-1.0 + ********************************************************************************/ +package hu.bme.mit.gamma.scxml.transformation + +class ScxmlGammaExpressionTransformer extends AtomicElementTransformer { + + new(StatechartTraceability traceability) { + super(traceability) + } + +} \ No newline at end of file diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/ScxmlToGammaCompositeTransformer.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/ScxmlToGammaCompositeTransformer.xtend new file mode 100644 index 000000000..41dbd8d7a --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/ScxmlToGammaCompositeTransformer.xtend @@ -0,0 +1,53 @@ +/******************************************************************************** + * Copyright (c) 2023 Contributors to the Gamma project + * + * 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 + * + * SPDX-License-Identifier: EPL-1.0 + ********************************************************************************/ +package hu.bme.mit.gamma.scxml.transformation + +class ScxmlToGammaCompositeTransformer extends CompositeElementTransformer { + + // Root element of the SCXML statechart model to transform + protected final String rootFileURI + + new(String rootFileURI) { + this(new CompositeTraceability(rootFileURI), rootFileURI) + } + + new(CompositeTraceability compositeTraceability, String rootFileURI) { + super(compositeTraceability) + this.rootFileURI = rootFileURI + } + + // Entry point for the SCXML to Gamma hierarchical statechart transformation + def execute() { + // Initialize constants + initQueueCapacity + + val rootStatechartTraceability = compositeTraceability.createStatechartTraceability(rootFileURI) + + // Execute root statechart transformation. + // The object rootStatechartTraceability is populated with data during transformation. + val rootStatechartTransformer = new ScxmlToGammaStatechartTransformer(rootStatechartTraceability) + rootStatechartTransformer.execute() + + return compositeTraceability + } + + private def initQueueCapacity() { + // Define default queue capacity + val queueCapacityDeclaration = createConstantDeclaration + queueCapacityDeclaration.name = "QUEUE_CAPACITY" + + val capacity = toIntegerLiteral(4) + queueCapacityDeclaration.expression = capacity + queueCapacityDeclaration.type = createIntegerTypeDefinition + + compositeTraceability.setQueueCapacity(queueCapacityDeclaration) + } +} diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/ScxmlToGammaStatechartTransformer.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/ScxmlToGammaStatechartTransformer.xtend new file mode 100644 index 000000000..c4159bc54 --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/ScxmlToGammaStatechartTransformer.xtend @@ -0,0 +1,737 @@ +/******************************************************************************** + * Copyright (c) 2023-2025 Contributors to the Gamma project + * + * 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 + * + * SPDX-License-Identifier: EPL-1.0 + ********************************************************************************/ +package hu.bme.mit.gamma.scxml.transformation + +import ac.soton.scxml.HistoryTypeDatatype +import ac.soton.scxml.ScxmlDataType +import ac.soton.scxml.ScxmlFinalType +import ac.soton.scxml.ScxmlHistoryType +import ac.soton.scxml.ScxmlInitialType +import ac.soton.scxml.ScxmlInvokeType +import ac.soton.scxml.ScxmlParallelType +import ac.soton.scxml.ScxmlScxmlType +import ac.soton.scxml.ScxmlStateType +import ac.soton.scxml.ScxmlTransitionType +import ac.soton.scxml.TransitionTypeDatatype +import hu.bme.mit.gamma.expression.model.Expression +import hu.bme.mit.gamma.statechart.composite.AsynchronousAdapter +import hu.bme.mit.gamma.statechart.composite.AsynchronousComponent +import hu.bme.mit.gamma.statechart.composite.ControlFunction +import hu.bme.mit.gamma.statechart.composite.DiscardStrategy +import hu.bme.mit.gamma.statechart.derivedfeatures.StatechartModelDerivedFeatures +import hu.bme.mit.gamma.statechart.interface_.RealizationMode +import hu.bme.mit.gamma.statechart.statechart.EntryState +import hu.bme.mit.gamma.statechart.statechart.GuardEvaluation +import hu.bme.mit.gamma.statechart.statechart.InitialState +import hu.bme.mit.gamma.statechart.statechart.OrthogonalRegionSchedulingOrder +import hu.bme.mit.gamma.statechart.statechart.SchedulingOrder +import hu.bme.mit.gamma.statechart.statechart.State +import hu.bme.mit.gamma.statechart.statechart.SynchronousStatechartDefinition +import hu.bme.mit.gamma.statechart.statechart.Transition +import hu.bme.mit.gamma.statechart.statechart.TransitionPriority +import java.math.BigInteger +import java.util.List +import java.util.function.Function +import java.util.logging.Level +import org.eclipse.emf.common.util.URI + +import static ac.soton.scxml.ScxmlModelDerivedFeatures.* +import static hu.bme.mit.gamma.scxml.transformation.Namings.* + +// TODO Scoping in variable transformation and assignment +// TODO History, parallel +class ScxmlToGammaStatechartTransformer extends AtomicElementTransformer { + + // Reference to the composite transformation traceability model + protected final CompositeTraceability compositeTraceability + + protected final extension ActionTransformer actionTransformer + protected final extension DataTransformer dataTransformer + protected final extension PortTransformer portTransformer + protected final extension TriggerTransformer triggerTransformer + + // Root element of the SCXML statechart model to transform + protected ScxmlScxmlType scxmlRoot + + // Contained elements invoking SCXML substatecharts + protected List invokes + + // Asynchronous wrapper component around the synchronous statechart definition + protected AsynchronousAdapter adapter + + // Root element of the Gamma statechart definition as the transformation result + protected SynchronousStatechartDefinition gammaStatechart + + new(StatechartTraceability traceability) { + super(traceability) + + compositeTraceability = traceability.compositeTraceability + + /* TODO Load and set scxmlRoot, create default StDef + * scxmlRoot + * this.gammaStatechart = createSynchronousStatechartDefinition => [ + * it.name = getStatechartName(scxmlRoot) + * ] + */ + this.actionTransformer = new ActionTransformer(traceability) + this.dataTransformer = new DataTransformer(traceability) + this.portTransformer = new PortTransformer(traceability) + this.triggerTransformer = new TriggerTransformer(traceability) + } + + // TODO This will be the execute() method, combine with former statechart transformer execute() + protected def AsynchronousComponent execute() { + + // Put statechart traceability in composite traceability object + compositeTraceability.putTraceability(traceability.fileURI, traceability) + + // TODO Check if not already transformed (at caller side or in this transformation method) + val fileURI = traceability.fileURI + scxmlRoot = loadSubcomponent(fileURI) + traceability.scxmlRoot = scxmlRoot + + gammaStatechart = createSynchronousStatechartDefinition => [ + it.name = getStatechartName(scxmlRoot) + ] + traceability.statechart = gammaStatechart + + // TODO First, transform all invoked statechart types + // (recursively, through transformation calls to types of contained components) + // Then transform individual statecharts with invoke actions, referencing invoked types + // Note: circular references in invoke chains are not supported. + invokes = getAllInvokes(scxmlRoot) + + for (invoke : invokes) { + // TODO Check src | srcexpr + val invokedSrc = invoke.src + + if (!compositeTraceability.containsTraceability(invokedSrc)) { + // Create (and store reference to) new statechart type transformation traceability + val invokedStatechartTraceability = compositeTraceability.createStatechartTraceability(invokedSrc) + compositeTraceability.putTraceability(invokedSrc, invokedStatechartTraceability) + + // Execute root statechart transformation. + // The object rootStatechartTraceability is populated with trace data during transformation. + val invokedStatechartTransformer = new ScxmlToGammaStatechartTransformer(invokedStatechartTraceability) + invokedStatechartTransformer.execute() + } + } + + // Transform statechart after invoked statechart type transformations have finished + // TODO Transform control, action, data and communication elements + return scxmlRoot.transformStatechart + } + + private def loadSubcomponent(String path) { + val fileURI = URI.createPlatformResourceURI(path, true); + val documentRoot = ecoreUtil.normalLoad(fileURI); + + val scxmlRoot = ecoreUtil.getFirstOfAllContentsOfType(documentRoot, ScxmlScxmlType); + return scxmlRoot + } + + protected def transformStatechart(ScxmlScxmlType scxmlRoot) { + val gammaComposite = createScheduledAsynchronousCompositeComponent + gammaComposite.name = getCompositeStatechartName(scxmlRoot) + + // Instantiate transformed invoked child statecharts + for (invoke : invokes) { + val statechartTraceability = compositeTraceability.getTraceability(invoke.src) + + // TODO Extend to deeper composition hierarchy levels, not just one-level adapter children + val gammaSubcomponentType = statechartTraceability.adapter as AsynchronousComponent + + val gammaSubcomponent = gammaSubcomponentType.instantiateAsynchronousComponent + gammaSubcomponent.name = invoke.id + gammaComposite.components += gammaSubcomponent + + compositeTraceability.putComponentInstance(invoke, gammaSubcomponent) + } + + // Create ports, port bindings and channels in the composite component + val datamodels = scxmlRoot.datamodel + if (datamodels !== null) { + val datamodel = datamodels.head + if (datamodel !== null) { + val dataElements = getDataElements(datamodel) + + // TODO Move string literal parts of names to Namings + val portDataElements = dataElements.filter [ it | + it.eContainer.eContainer instanceof ScxmlScxmlType && it.id.startsWith("pro_port_") || + it.id.startsWith("req_port_") + ] + for (portData : portDataElements) { + val gammaPort = portData.getOrCreatePort + gammaComposite.ports += gammaPort + } + + val bindingDataElements = dataElements.filter [ it | + it.eContainer.eContainer instanceof ScxmlScxmlType && it.id.startsWith("binding_") + ] + for (binding : bindingDataElements) { + val gammaPortBinding = binding.createBinding + gammaComposite.portBindings += gammaPortBinding + } + + val channelDataElements = dataElements.filter [ it | + it.eContainer.eContainer instanceof ScxmlScxmlType && it.id.startsWith("channel_") + ] + for (channel : channelDataElements) { + val gammaChannel = channel.createChannel + gammaComposite.channels += gammaChannel + } + } + } + + // TODO Merge methods logically + transformStatechartInnerElements + + return gammaComposite + } + + // TODO Merge this statechart transformation with 'composite' transformation method + // Transformation of the SCXML root element and its contents recursively. + // Invoked types are assumed to be already transformed by this point. + def transformStatechartInnerElements() { + traceability.setStatechart(gammaStatechart) + + logger.log(Level.INFO, "Transforming root element (" + scxmlRoot.name + ")") + + // Configure transition selection and execution + // TODO Check settings! + gammaStatechart.schedulingOrder = SchedulingOrder.BOTTOM_UP + gammaStatechart.orthogonalRegionSchedulingOrder = OrthogonalRegionSchedulingOrder.SEQUENTIAL + gammaStatechart.transitionPriority = TransitionPriority.ORDER_BASED + gammaStatechart.guardEvaluation = GuardEvaluation.BEGINNING_OF_STEP + + val datamodels = scxmlRoot.datamodel + if (datamodels !== null) { + val datamodel = datamodels.head + if (datamodel !== null) { + val dataElements = getDataElements(datamodel) + + // TODO Move string literal parts of names to Namings + val portDataElements = dataElements.filter[ + it.eContainer.eContainer instanceof ScxmlScxmlType && + (it.id.startsWith("pro_port_") || it.id.startsWith("req_port_")) + ] + for (portData : portDataElements) { + val gammaPort = portData.getOrCreatePort + gammaStatechart.ports += gammaPort + } + + // TODO Transform constants, prefix: "const_" + // TODO Extract to method + val parameterDataElements = dataElements.filter[ + it.eContainer.eContainer instanceof ScxmlScxmlType && it.id.startsWith("param_") + ] + for (parameterData : parameterDataElements) { + val gammaParameterDeclaration = dataTransformer.transformParameter(parameterData) + gammaStatechart.parameterDeclarations += gammaParameterDeclaration + } + + val variableDataElements = dataElements.filter[ + !portDataElements.contains(it) && !parameterDataElements.contains(it) + ] + for (variableData : variableDataElements) { + val gammaVariableDeclaration = dataTransformer.transformVariable(variableData) + gammaStatechart.variableDeclarations += gammaVariableDeclaration + } + } + } + + val mainRegion = createRegion => [ + it.name = getRegionName(scxmlRoot.name) + ] + gammaStatechart.regions += mainRegion + + val scxmlStateNodes = getStateNodes(scxmlRoot) + for (scxmlStateNode : scxmlStateNodes) { + if (isParallel(scxmlStateNode)) { + val parallel = scxmlStateNode as ScxmlParallelType + mainRegion.stateNodes += parallel.transformParallel + } else if (isState(scxmlStateNode)) { + val state = scxmlStateNode as ScxmlStateType + mainRegion.stateNodes += state.transformState + } else if (isFinal(scxmlStateNode)) { + val final = scxmlStateNode as ScxmlFinalType + mainRegion.stateNodes += final.transformFinal + } else { + throw new IllegalArgumentException("Object " + scxmlStateNode + " is of unknown SCXML type.") + } + } + + val transitions = getAllTransitions(scxmlRoot) + for (transition : transitions) { + transition.transformTransition + } + + // Transform the SCXML root element's initial attribute, + // or select its first child as initial state if it is not present. + val gammaInitial = createInitialState => [ + it.name = getInitialName(scxmlRoot) + ] + mainRegion.stateNodes += gammaInitial + + val scxmlInitialAttribute = scxmlRoot.initial + val scxmlFirstInitialAttribute = scxmlInitialAttribute?.head + if (scxmlFirstInitialAttribute !== null) { + val gammaInitialTarget = traceability.getStateNodeById(scxmlFirstInitialAttribute) + gammaInitial.createTransition(gammaInitialTarget) + } else { + val firstScxmlStateChild = getFirstChildStateNode(scxmlRoot) as ScxmlStateType + val gammaInitialTarget = traceability.getState(firstScxmlStateChild) + gammaInitial.createTransition(gammaInitialTarget) + } + + // Add all transitions from initial states of Gamma compound states + // specified by scxml initial attributes or document order + gammaStatechart.transitions += traceability.getInitialTransitions + + // Add all ports from traceability + if (traceability.getDefaultPort !== null) { + gammaStatechart.ports += traceability.getDefaultPort + } + gammaStatechart.ports += traceability.defaultInterfacePorts.values + gammaStatechart.ports += traceability.ports.values + + val adapter = wrapIntoAdapter + traceability.adapter = adapter + + return traceability + } + + protected def getOrCreatePort(ScxmlDataType portData) { + val isProvided = portData.id.startsWith("pro_port_") + val realizationMode = isProvided ? RealizationMode.PROVIDED : RealizationMode.REQUIRED + + // Get port name and interface name from port descriptor string + val portString = portData.expr.trim + val tokens = portString.split("\\.") + if (tokens.size < 1 || tokens.size > 2) { + throw new IllegalArgumentException( + "Port descriptor " + portString + " does not contain exactly 1 or 2 dot separated tokens." + ) + } + + var scxmlInterfaceName = "" + var scxmlPortName = "" + + if (tokens.size == 1) { + scxmlInterfaceName = portString + scxmlPortName = portString + } else { + scxmlInterfaceName = tokens.get(tokens.size - 1) + scxmlPortName = tokens.head + } + // + val gammaPort = portTransformer.getOrTransformPort( + scxmlPortName, + scxmlInterfaceName, + realizationMode + ) + + // TODO Put port into composite or statechart traceability? + compositeTraceability.putPort(scxmlPortName, gammaPort) + + return gammaPort + } + + protected def createBinding(ScxmlDataType bindingData) { + val bindingString = bindingData.expr.trim + val tokens = bindingString.split("\\.|\\s*\\-\\s*") + if (tokens.size != 3) { + throw new IllegalArgumentException( + "Binding descriptor " + bindingString + " does not contain exactly 3 dot separated tokens." + ) + } + + val sourcePortName = tokens.get(0) + val targetInstanceName = tokens.get(1) + val targetPortName = tokens.get(2) + + // + val gammaSourcePort = compositeTraceability.getPort(sourcePortName) + val gammaInstancePortReference = createInstancePortReference(targetInstanceName, targetPortName) + + val gammaPortBinding = createPortBinding(gammaSourcePort, gammaInstancePortReference) + return gammaPortBinding + } + + protected def createChannel(ScxmlDataType channelData) { + val channelString = channelData.expr.trim + val tokens = channelString.split("\\.|\\s*\\-\\s*") + if (tokens.size != 4) { + throw new IllegalArgumentException( + "Channel descriptor " + channelString + " does not contain exactly 4 dot separated tokens." + ) + } + + val sourceInstanceName = tokens.get(0) + val sourcePortName = tokens.get(1) + val targetInstanceName = tokens.get(2) + val targetPortName = tokens.get(3) + + // + val sourceInstancePortReference = createInstancePortReference(sourceInstanceName, sourcePortName) + val targetInstancePortReference = createInstancePortReference(targetInstanceName, targetPortName) + + val gammaChannel = createChannel(sourceInstancePortReference, targetInstancePortReference) + return gammaChannel + } + + private def createInstancePortReference(String instanceName, String scxmlPortName) { + val instance = compositeTraceability.getComponentInstance(instanceName) + + // TODO Make it more performant to get statechart traceability + // by instance invokeId or source URI. + val statechartTraceability = compositeTraceability.getTraceabilityById(instanceName) + val port = statechartTraceability.getPort(scxmlPortName) + + val instancePortReference = createInstancePortReference(instance, port) + return instancePortReference + } + + protected def AsynchronousAdapter wrapIntoAdapter() { + adapter = gammaStatechart.wrapIntoAdapter(getAdapterName(scxmlRoot)) + + val allStatechartPorts = StatechartModelDerivedFeatures.getAllPortsWithInput(gammaStatechart) + val allInternalPorts = allStatechartPorts.filter[it|StatechartModelDerivedFeatures.isInternal(it)] + val allExternalPorts = allStatechartPorts.filter[it|!StatechartModelDerivedFeatures.isInternal(it)] + + // Set control specification + // TODO Check if internal port event triggers should be added as control specification triggers. + for (port : allExternalPorts) { + val portEvents = StatechartModelDerivedFeatures.getInputEvents(port) + for (event : portEvents) { + val controlSpecification = createControlSpecification + controlSpecification.controlFunction = ControlFunction.RUN_ONCE + + val eventReference = createPortEventReference + eventReference.port = port + eventReference.event = event + + val trigger = createEventTrigger + trigger.eventReference = eventReference + controlSpecification.trigger = trigger + + adapter.controlSpecifications += controlSpecification + } + } + + // Create internal event queue + val internalEventQueue = createMessageQueue + internalEventQueue.name = getInternalEventQueueName(scxmlRoot) + internalEventQueue.eventDiscardStrategy = DiscardStrategy.INCOMING + internalEventQueue.priority = BigInteger.TWO + + val internalCapacityReference = createDirectReferenceExpression + internalCapacityReference.declaration = traceability.queueCapacityDeclaration + internalEventQueue.capacity = internalCapacityReference + + for (port : allInternalPorts) { + val portEvents = StatechartModelDerivedFeatures.getInputEvents(port) + for (event : portEvents) { + val reference = createPortEventReference + reference.port = port + reference.event = event + + val eventPassing = createEventPassing(reference) + internalEventQueue.eventPassings += eventPassing + } + } + + if (!internalEventQueue.eventPassings.empty) { + adapter.messageQueues += internalEventQueue + } + + // Create external event queue + val externalEventQueue = createMessageQueue + externalEventQueue.name = getExternalEventQueueName(scxmlRoot) + externalEventQueue.eventDiscardStrategy = DiscardStrategy.INCOMING + externalEventQueue.priority = BigInteger.ONE + + val externalCapacityReference = createDirectReferenceExpression + externalCapacityReference.declaration = traceability.queueCapacityDeclaration + externalEventQueue.capacity = externalCapacityReference + + for (port : allExternalPorts) { + val portEvents = StatechartModelDerivedFeatures.getInputEvents(port) + for (event : portEvents) { + val reference = createPortEventReference + reference.port = port + reference.event = event + + val eventPassing = createEventPassing(reference) + externalEventQueue.eventPassings += eventPassing + } + } + + if (!externalEventQueue.eventPassings.empty) { + adapter.messageQueues += externalEventQueue + } + + return adapter + } + + protected def State transformParallel(ScxmlParallelType parallelNode) { + logger.log(Level.INFO, "Transforming element (" + parallelNode.id + ")") + + val gammaParallel = createState => [ + it.name = getParallelName(parallelNode) + ] + + val scxmlStateNodes = getStateNodes(parallelNode) + for (scxmlStateNode : scxmlStateNodes) { + val region = createRegion => [ + it.name = getRegionName(gammaParallel.name) + ] + gammaParallel.regions += region + + if (isParallel(scxmlStateNode)) { + val parallel = scxmlStateNode as ScxmlParallelType + region.stateNodes += parallel.transformParallel + } else if (isState(scxmlStateNode)) { + val state = scxmlStateNode as ScxmlStateType + region.stateNodes += state.transformState + } else if (isFinal(scxmlStateNode)) { + val final = scxmlStateNode as ScxmlFinalType + region.stateNodes += final.transformFinal + } else { + throw new IllegalArgumentException("Object " + scxmlStateNode + " is of unknown SCXML type.") + } + } + + // TODO Transform history pseudo-states (by adding a wrapper compound state perhaps) + // Initial-ok kicserélése a gyerek state-ekben, amely parallelekben találok historyt + // Vagy history node hozzáadása minden gyerek régióhoz, ha van kívül history? + /*val scxmlHistoryStates = parallelNode.history + * for (scxmlHistoryState : scxmlHistoryStates) { + * + }*/ + // Transform onentry and onexit handlers + val onentryActions = parallelNode.onentry + for (onentryAction : onentryActions) { + if (onentryAction !== null) { + gammaParallel.entryActions += onentryAction.transformOnentry + } + } + + val onexitActions = parallelNode.onexit + for (onexitAction : onexitActions) { + if (onexitAction !== null) { + gammaParallel.exitActions += onexitAction.transformOnexit + } + } + + traceability.put(parallelNode, gammaParallel) + + return gammaParallel + } + + protected def State transformState(ScxmlStateType scxmlState) { + logger.log(Level.INFO, "Transforming element (" + scxmlState.id + ")") + + val gammaState = createState => [ + it.name = getStateName(scxmlState) + ] + + traceability.put(scxmlState, gammaState) + + if (isCompoundState(scxmlState)) { + val region = createRegion => [ + it.name = getRegionName(gammaState.name) + ] + gammaState.regions += region + + val scxmlStateNodes = getStateNodes(scxmlState) + for (scxmlStateNode : scxmlStateNodes) { + if (isParallel(scxmlStateNode)) { + val parallel = scxmlStateNode as ScxmlParallelType + region.stateNodes += parallel.transformParallel + } else if (isState(scxmlStateNode)) { + val state = scxmlStateNode as ScxmlStateType + region.stateNodes += state.transformState + } else if (isFinal(scxmlStateNode)) { + val final = scxmlStateNode as ScxmlFinalType + region.stateNodes += final.transformFinal + } else { + throw new IllegalArgumentException( + "Object " + scxmlStateNode + " is of unknown SCXML type.") + } + } + + // Transform history pseudo-states + val scxmlHistoryStates = scxmlState.history + for (scxmlHistoryState : scxmlHistoryStates) { + region.stateNodes += scxmlHistoryState.transform + } + + // Transform the state's initial element or attribute, + // or select its first child as initial state if neither one above is present. + val scxmlInitialElement = scxmlState.initial.head + if (scxmlInitialElement !== null) { + region.stateNodes += scxmlInitialElement.transform + } else { + val gammaInitial = createInitialState => [ + it.name = getInitialName(scxmlState) + ] + region.stateNodes += gammaInitial + + val scxmlInitialAttribute = scxmlState.initial1.head + if (scxmlInitialAttribute !== null) { + val gammaInitialTarget = traceability.getStateNodeById(scxmlInitialAttribute) + val initialTransition = gammaInitial.createTransition(gammaInitialTarget) + traceability.putInitialTransition(initialTransition) + } else { + val firstScxmlStateChild = getFirstChildStateNode(scxmlState) as ScxmlStateType + val gammaInitialTarget = traceability.getState(firstScxmlStateChild) + val initialTransition = gammaInitial.createTransition(gammaInitialTarget) + traceability.putInitialTransition(initialTransition) + } + } + } + + // Transform onentry and onexit handlers + val onentryActions = scxmlState.onentry + for (onentryAction : onentryActions) { + if (onentryAction !== null) { + val gammaOnEntryAction = onentryAction.transformOnentry + if (gammaOnEntryAction !== null) { + gammaState.entryActions += gammaOnEntryAction + } + } + } + + val onexitActions = scxmlState.onexit + for (onexitAction : onexitActions) { + if (onexitAction !== null) { + val gammaOnExitAction = onexitAction.transformOnexit + if (gammaOnExitAction !== null) { + gammaState.exitActions += gammaOnExitAction + } + } + } + + // Transform invoke actions + val invokeActions = scxmlState.invoke + for (invokeAction : invokeActions) { + invokeAction?.transformAction + } + + return gammaState + } + + protected def State transformFinal(ScxmlFinalType scxmlFinal) { + logger.log(Level.INFO, "Transforming element (" + scxmlFinal.id + ")") + + val gammaFinal = createState => [ + it.name = getFinalName(scxmlFinal) + ] + + // Transform onentry and onexit handlers + val onentryActions = scxmlFinal.onentry + for (onentryAction : onentryActions) { + if (onentryAction !== null) { + gammaFinal.entryActions += onentryAction.transformOnentry + } + } + + val onexitActions = scxmlFinal.onexit + for (onexitAction : onexitActions) { + if (onexitAction !== null) { + gammaFinal.exitActions += onexitAction.transformOnexit + } + } + + traceability.put(scxmlFinal, gammaFinal) + + return gammaFinal + } + + protected def InitialState transform(ScxmlInitialType scxmlInitial) { + logger.log(Level.INFO, "Transforming element") + + val parentState = getParentState(scxmlInitial) + val gammaInitial = createInitialState => [ + it.name = getInitialName(parentState) + ] + + traceability.put(scxmlInitial, gammaInitial) + + return gammaInitial + } + + protected def EntryState transform(ScxmlHistoryType scxmlHistory) { + logger.log(Level.INFO, "Transforming element (" + scxmlHistory.id + ")") + + if (scxmlHistory.type == HistoryTypeDatatype.SHALLOW) { + val gammaShallowHistory = createShallowHistoryState => [ + it.name = getShallowHistoryName(scxmlHistory) + ] + + traceability.put(scxmlHistory, gammaShallowHistory) + return gammaShallowHistory + } else { + val gammaDeepHistory = createDeepHistoryState => [ + it.name = getDeepHistoryName(scxmlHistory) + ] + + traceability.put(scxmlHistory, gammaDeepHistory) + return gammaDeepHistory + } + } + + // Internal transitions are not supported by the transformer. + protected def Transition transformTransition(ScxmlTransitionType transition) { + if (transition.type == TransitionTypeDatatype.INTERNAL) { + throw new IllegalArgumentException("Transforming internal transition " + transition + " is not supported.") + } + + val sourceState = getTransitionSource(transition) + val targetId = transition.target.head + + val gammaSource = traceability.getStateNode(sourceState) + val gammaTarget = traceability.getStateNodeById(targetId) + + logger.log(Level.INFO, "Transforming transition" + gammaSource.name + " -> " + gammaTarget.name) + + val gammaTransition = gammaSource.createTransition(gammaTarget) + + // Transform event trigger if present + val eventName = transition.event + if (!eventName.nullOrEmpty) { + val eventTrigger = transformTrigger(eventName) + gammaTransition.trigger = eventTrigger + } + + // Transform guard if present + val guardStr = transition.cond + if (!guardStr.nullOrEmpty) { + val gammaGuardExpression = expressionLanguageParser.preprocessAndParse( + guardStr, + expressionLanguageLinker.getLinker(traceability) + ) + gammaTransition.guard = gammaGuardExpression + } + + // TODO for all types of actions + val effects = transition.assign + transition.raise + if (!effects.nullOrEmpty) { + gammaTransition.effects += effects.transformBlock + } + // + traceability.put(transition, gammaTransition) + + return gammaTransition + } + +} diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/StatechartTraceability.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/StatechartTraceability.xtend new file mode 100644 index 000000000..68a394b51 --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/StatechartTraceability.xtend @@ -0,0 +1,611 @@ +/******************************************************************************** + * Copyright (c) 2023-2025 Contributors to the Gamma project + * + * 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 + * + * SPDX-License-Identifier: EPL-1.0 + ********************************************************************************/ +package hu.bme.mit.gamma.scxml.transformation + +import ac.soton.scxml.ScxmlDataType +import ac.soton.scxml.ScxmlFinalType +import ac.soton.scxml.ScxmlHistoryType +import ac.soton.scxml.ScxmlInitialType +import ac.soton.scxml.ScxmlParallelType +import ac.soton.scxml.ScxmlScxmlType +import ac.soton.scxml.ScxmlStateType +import ac.soton.scxml.ScxmlTransitionType +import hu.bme.mit.gamma.expression.model.ConstantDeclaration +import hu.bme.mit.gamma.expression.model.Declaration +import hu.bme.mit.gamma.expression.model.ParameterDeclaration +import hu.bme.mit.gamma.expression.model.VariableDeclaration +import hu.bme.mit.gamma.statechart.composite.AsynchronousAdapter +import hu.bme.mit.gamma.statechart.interface_.Event +import hu.bme.mit.gamma.statechart.interface_.Interface +import hu.bme.mit.gamma.statechart.interface_.Port +import hu.bme.mit.gamma.statechart.statechart.EntryState +import hu.bme.mit.gamma.statechart.statechart.InitialState +import hu.bme.mit.gamma.statechart.statechart.State +import hu.bme.mit.gamma.statechart.statechart.StateNode +import hu.bme.mit.gamma.statechart.statechart.SynchronousStatechartDefinition +import hu.bme.mit.gamma.statechart.statechart.Transition +import hu.bme.mit.gamma.util.GammaEcoreUtil +import java.util.Map +import java.util.Set +import org.eclipse.emf.ecore.EObject + +import static com.google.common.base.Preconditions.checkNotNull + +class StatechartTraceability { + + // Reference to the composite transformation traceability model + protected final CompositeTraceability compositeTraceability + + // URI of the root SCXML statechart model to transform + protected final String fileURI + + // Root element of the atomic SCXML statechart model to transform. + protected ScxmlScxmlType scxmlRoot + protected SynchronousStatechartDefinition statechart + protected AsynchronousAdapter adapter + protected ConstantDeclaration queueCapacity + + protected final Map parallels = newHashMap + protected final Map states = newHashMap + protected final Map finals = newHashMap + protected final Map initials = newHashMap + protected final Map historyStates = newHashMap + protected final Map transitions = newHashMap + protected final Set initialTransitions = newHashSet + + protected final Map dataElements = newHashMap + protected final Map parameters = newHashMap + // The variables map works only if variables are globally unique and have a global scope + protected final Map variables = newHashMap + + // Internal mappings (internal to the statechart) + protected Port defaultPort + protected Interface defaultInterface + protected final Map defaultInterfacePorts = newHashMap + protected final Map ports = newHashMap + + // Global mappings (from composite traceability) + protected final Map interfaces + protected final Map, Event> internalEvents + protected final Map, Event> inEvents + protected final Map, Event> outEvents + protected final Map allEvents + protected final Map allEventParameters + + protected final extension GammaEcoreUtil ecoreUtil = GammaEcoreUtil.INSTANCE + + // - Synchronous Statechart Definition + new( + CompositeTraceability compositeTraceability, + String rootFileURI, + Map interfaces, + ConstantDeclaration queueCapacity, + Map, Event> internalEvents, + Map, Event> inEvents, + Map, Event> outEvents, + Map allEvents, + Map allEventParameters + ) { + this.compositeTraceability = compositeTraceability + this.fileURI = rootFileURI + this.interfaces = interfaces + this.queueCapacity = queueCapacity + this.internalEvents = internalEvents + this.inEvents = inEvents + this.outEvents = outEvents + this.allEvents = allEvents + this.allEventParameters = allEventParameters + } + + def getScxmlRoot() { + return scxmlRoot + } + + def getTypeName() { + return scxmlRoot.name + } + + def setAdapter(AsynchronousAdapter adapter) { + this.adapter = adapter + } + + def getAdapter() { + return adapter + } + + def getQueueCapacityDeclaration() { + checkNotNull(queueCapacity) + return queueCapacity + } + + def setStatechart(SynchronousStatechartDefinition gammaStatechart) { + checkNotNull(gammaStatechart) + statechart = gammaStatechart + } + + def getStatechart() { + return statechart + } + + // - State (with orthogonal Region children) + def put(ScxmlParallelType scxmlParallel, State gammaParallel) { + checkNotNull(scxmlParallel) + checkNotNull(gammaParallel) + parallels += scxmlParallel -> gammaParallel + } + + def getParallel(ScxmlParallelType scxmlParallel) { + checkNotNull(scxmlParallel) + val gammaParallel = parallels.get(scxmlParallel) + checkNotNull(gammaParallel) + return gammaParallel + } + + def getParallelById(String scxmlParallelId) { + checkNotNull(scxmlParallelId) + val keySet = states.keySet + val scxmlParallel = keySet.findFirst[parallel|parallel.id == scxmlParallelId] + checkNotNull(scxmlParallel) + return getState(scxmlParallel) + } + + // - State (with a Region child if it is compound) + def put(ScxmlStateType scxmlState, State gammaState) { + checkNotNull(scxmlState) + checkNotNull(gammaState) + states += scxmlState -> gammaState + } + + def getState(ScxmlStateType scxmlState) { + checkNotNull(scxmlState) + val gammaState = states.get(scxmlState) + checkNotNull(gammaState) + return gammaState + } + + def getStateById(String scxmlStateId) { + checkNotNull(scxmlStateId) + val keySet = states.keySet + val scxmlState = keySet.findFirst[state|state.id == scxmlStateId] + checkNotNull(scxmlState) + return getState(scxmlState) + } + + // - State + def put(ScxmlFinalType scxmlFinal, State gammaFinal) { + checkNotNull(scxmlFinal) + checkNotNull(gammaFinal) + finals += scxmlFinal -> gammaFinal + } + + def getFinalState(ScxmlFinalType scxmlFinal) { + checkNotNull(scxmlFinal) + val gammaFinal = finals.get(scxmlFinal) + checkNotNull(gammaFinal) + return gammaFinal + } + + def getFinalById(String scxmlFinalId) { + checkNotNull(scxmlFinalId) + val keySet = finals.keySet + val scxmlFinal = keySet.findFirst[final|final.id == scxmlFinalId] + checkNotNull(scxmlFinal) + return getFinalState(scxmlFinal) + } + + // - InitialState + def put(ScxmlInitialType scxmlInitial, InitialState gammaInitial) { + checkNotNull(scxmlInitial) + checkNotNull(gammaInitial) + initials += scxmlInitial -> gammaInitial + } + + def getInitialState(ScxmlInitialType scxmlInitial) { + checkNotNull(scxmlInitial) + val gammaInitial = initials.get(scxmlInitial) + checkNotNull(gammaInitial) + return gammaInitial + } + + // - ShallowHistoryState or DeepHistoryState (stored as an EntryState) + def put(ScxmlHistoryType scxmlHistory, EntryState gammaHistory) { + checkNotNull(scxmlHistory) + checkNotNull(gammaHistory) + historyStates += scxmlHistory -> gammaHistory + } + + def getHistoryState(ScxmlHistoryType scxmlHistory) { + checkNotNull(scxmlHistory) + val gammaHistory = historyStates.get(scxmlHistory) + checkNotNull(gammaHistory) + return gammaHistory + } + + def getHistoryStateById(String scxmlHistoryId) { + checkNotNull(scxmlHistoryId) + val keySet = historyStates.keySet + val scxmlHistory = keySet.findFirst[history|history.id == scxmlHistoryId] + checkNotNull(scxmlHistory) + return getHistoryState(scxmlHistory) + } + + // General functions returning a mapped Gamma StateNode + // Retrieves the mapped Gamma StateNode for an arbitrary transition source state + def getStateNode(EObject scxmlStateNode) { + val gammaState = states.get(scxmlStateNode) + if (gammaState !== null) { + return gammaState as StateNode + } + val gammaParallel = parallels.get(scxmlStateNode) + if (gammaParallel !== null) { + return gammaParallel as StateNode + } + val gammaInitial = initials.get(scxmlStateNode) + if (gammaInitial !== null) { + return gammaInitial as StateNode + } + val gammaHistory = historyStates.get(scxmlStateNode) + checkNotNull(gammaHistory) + return gammaHistory as StateNode + } + + // Retrieves the mapped Gamma StateNode for an arbitrary transition target state id + def getStateNodeById(String scxmlStateNodeId) { + val gammaState = states.entrySet.findFirst[entry|entry.key.id == scxmlStateNodeId]?.value + if (gammaState !== null) { + return gammaState + } + val gammaParallel = parallels.entrySet.findFirst[entry|entry.key.id == scxmlStateNodeId]?.value + if (gammaParallel !== null) { + return gammaParallel + } + val gammaFinal = finals.entrySet.findFirst[entry|entry.key.id == scxmlStateNodeId]?.value + if (gammaFinal !== null) { + return gammaFinal + } + val gammaHistory = historyStates.entrySet.findFirst[entry|entry.key.id == scxmlStateNodeId]?.value + checkNotNull(gammaHistory) + return gammaHistory + + } + + // - Transition + def put(ScxmlTransitionType scxmlTransition, Transition gammaTransition) { + checkNotNull(scxmlTransition) + checkNotNull(gammaTransition) + transitions += scxmlTransition -> gammaTransition + } + + def getTransition(ScxmlTransitionType scxmlTransition) { + checkNotNull(scxmlTransition) + val gammaTransition = transitions.get(scxmlTransition) + checkNotNull(gammaTransition) + return gammaTransition + } + + // Transitions from initial states of Gamma compound states + // specified by scxml initial attributes or document order + def putInitialTransition(Transition gammaTransition) { + checkNotNull(gammaTransition) + initialTransitions += gammaTransition + } + + def getInitialTransitions() { + return initialTransitions + } + + // - ParameterDeclaration + def put(ScxmlDataType scxmlData, ParameterDeclaration gammaDeclaration) { + checkNotNull(scxmlData) + checkNotNull(gammaDeclaration) + dataElements += scxmlData -> gammaDeclaration + + put(scxmlData.id, gammaDeclaration) + } + + def getParameter(ScxmlDataType scxmlData) { + checkNotNull(scxmlData) + val gammaDeclaration = dataElements.get(scxmlData) + checkNotNull(gammaDeclaration) + return gammaDeclaration + } + + // - VariableDeclaration + def put(ScxmlDataType scxmlData, VariableDeclaration gammaDeclaration) { + checkNotNull(scxmlData) + checkNotNull(gammaDeclaration) + dataElements += scxmlData -> gammaDeclaration + + put(scxmlData.id, gammaDeclaration) + } + + def getVariable(ScxmlDataType scxmlData) { + checkNotNull(scxmlData) + val gammaDeclaration = dataElements.get(scxmlData) + checkNotNull(gammaDeclaration) + return gammaDeclaration + } + + // Parameter Declarations by String identifier + private def put(String scxmlParameterName, ParameterDeclaration gammaDeclaration) { + checkNotNull(scxmlParameterName) + checkNotNull(gammaDeclaration) + parameters += scxmlParameterName -> gammaDeclaration + } + + def containsParameter(String scxmlParameterName) { + checkNotNull(scxmlParameterName) + return parameters.containsKey(scxmlParameterName) + } + + def getParameter(String scxmlParameterName) { + checkNotNull(scxmlParameterName) + val gammaDeclaration = parameters.get(scxmlParameterName) + checkNotNull(gammaDeclaration) + return gammaDeclaration + } + + // Variable Declarations by String identifier + private def put(String scxmlVariableName, VariableDeclaration gammaDeclaration) { + checkNotNull(scxmlVariableName) + checkNotNull(gammaDeclaration) + variables += scxmlVariableName -> gammaDeclaration + } + + def containsVariable(String scxmlVariableName) { + checkNotNull(scxmlVariableName) + return variables.containsKey(scxmlVariableName) + } + + def getVariable(String scxmlVariableName) { + checkNotNull(scxmlVariableName) + val gammaDeclaration = variables.get(scxmlVariableName) + checkNotNull(gammaDeclaration) + return gammaDeclaration + } + + // Default port and interface (for event strings like 'event') + def getDefaultInterface() { + return defaultInterface + } + + def setDefaultInterface(Interface defaultInterface) { + this.defaultInterface = defaultInterface + } + + def getDefaultPort() { + return defaultPort + } + + def setDefaultPort(Port defaultPort) { + this.defaultPort = defaultPort + } + + // Default ports of interfaces (for event strings like 'interface.event') + def putDefaultInterfacePort(Interface gammaInterface, Port gammaPort) { + checkNotNull(gammaInterface) + checkNotNull(gammaPort) + defaultInterfacePorts += gammaInterface -> gammaPort + } + + def getDefaultInterfacePort(Interface gammaInterface) { + checkNotNull(gammaInterface) + val gammaPort = defaultInterfacePorts.get(gammaInterface) + checkNotNull(gammaPort) + return gammaPort + } + + def containsDefaultInterfacePort(Interface gammaInterface) { + checkNotNull(gammaInterface) + return defaultInterfacePorts.containsKey(gammaInterface) + } + + // Interfaces by string identifier (for event strings like '[port.]interface.event) + def putInterface(String scxmlInterfaceName, Interface gammaInterface) { + checkNotNull(scxmlInterfaceName) + checkNotNull(gammaInterface) + interfaces += scxmlInterfaceName -> gammaInterface + } + + def getInterface(String scxmlInterfaceName) { + checkNotNull(scxmlInterfaceName) + val gammaInterface = interfaces.get(scxmlInterfaceName) + checkNotNull(gammaInterface) + return gammaInterface + } + + def containsInterface(String scxmlInterfaceName) { + checkNotNull(scxmlInterfaceName) + return interfaces.containsKey(scxmlInterfaceName) + } + + def getInterfaces() { + return interfaces; + } + + def getAllInterfaces() { + var allInterfaces = interfaces.values.toList + allInterfaces += defaultInterfacePorts.keySet.toList + if (getDefaultInterface !== null) { + allInterfaces += getDefaultInterface + } + return allInterfaces + } + + // Ports by string identifier (for event strings like 'port.interface.event) + def putPort(String scxmlPortName, Port gammaPort) { + checkNotNull(scxmlPortName) + checkNotNull(gammaPort) + ports += scxmlPortName -> gammaPort + } + + def getPort(String scxmlPortName) { + checkNotNull(scxmlPortName) + val gammaPort = ports.get(scxmlPortName) + checkNotNull(gammaPort) + return gammaPort + } + + def containsPort(String scxmlPortName) { + checkNotNull(scxmlPortName) + return ports.containsKey(scxmlPortName) + } + + // Internal events by pairs of {Gamma interface; event name} + def putInternalEvent(Pair interfaceEvent, Event gammaEvent) { + checkNotNull(interfaceEvent) + checkNotNull(interfaceEvent.key) + checkNotNull(interfaceEvent.value) + checkNotNull(gammaEvent) + + val interface = interfaceEvent.key + val eventName = interfaceEvent.value + internalEvents += (interface -> eventName) -> gammaEvent + + putEvent(eventName, gammaEvent) + } + + def getInternalEvent(Pair interfaceEvent) { + checkNotNull(interfaceEvent) + checkNotNull(interfaceEvent.key) + checkNotNull(interfaceEvent.value) + + val interface = interfaceEvent.key + val eventName = interfaceEvent.value + val gammaEvent = internalEvents.get(interface -> eventName) + + checkNotNull(gammaEvent) + return gammaEvent + } + + def containsInternalEvent(Pair interfaceEvent) { + checkNotNull(interfaceEvent) + checkNotNull(interfaceEvent.key) + checkNotNull(interfaceEvent.value) + + val interface = interfaceEvent.key + val eventName = interfaceEvent.value + return internalEvents.containsKey(interface -> eventName) + } + + // Input events by pairs of {Gamma interface; event name} + def putInEvent(Pair interfaceEvent, Event gammaEvent) { + checkNotNull(interfaceEvent) + checkNotNull(interfaceEvent.key) + checkNotNull(interfaceEvent.value) + checkNotNull(gammaEvent) + + val interface = interfaceEvent.key + val eventName = interfaceEvent.value + inEvents += (interface -> eventName) -> gammaEvent + + putEvent(eventName, gammaEvent) + } + + def getInEvent(Pair interfaceEvent) { + checkNotNull(interfaceEvent) + checkNotNull(interfaceEvent.key) + checkNotNull(interfaceEvent.value) + + val interface = interfaceEvent.key + val eventName = interfaceEvent.value + val gammaEvent = inEvents.get(interface -> eventName) + + checkNotNull(gammaEvent) + return gammaEvent + } + + def containsInEvent(Pair interfaceEvent) { + checkNotNull(interfaceEvent) + checkNotNull(interfaceEvent.key) + checkNotNull(interfaceEvent.value) + + val interface = interfaceEvent.key + val eventName = interfaceEvent.value + return inEvents.containsKey(interface -> eventName) + } + + // Output events by pairs of {port name; event name} + def putOutEvent(Pair interfaceEvent, Event gammaEvent) { + checkNotNull(interfaceEvent) + checkNotNull(interfaceEvent.key) + checkNotNull(interfaceEvent.value) + checkNotNull(gammaEvent) + + val interface = interfaceEvent.key + val eventName = interfaceEvent.value + outEvents += (interface -> eventName) -> gammaEvent + + putEvent(eventName, gammaEvent) + } + + def getOutEvent(Pair interfaceEvent) { + checkNotNull(interfaceEvent) + checkNotNull(interfaceEvent.key) + checkNotNull(interfaceEvent.value) + + val interface = interfaceEvent.key + val eventName = interfaceEvent.value + val gammaEvent = outEvents.get(interface -> eventName) + + checkNotNull(gammaEvent) + return gammaEvent + } + + def containsOutEvent(Pair interfaceEvent) { + checkNotNull(interfaceEvent) + checkNotNull(interfaceEvent.key) + checkNotNull(interfaceEvent.value) + + val interface = interfaceEvent.key + val eventName = interfaceEvent.value + return outEvents.containsKey(interface -> eventName) + } + + // Events by string identifier + def putEvent(String scxmlEventName, Event gammaEvent) { + checkNotNull(scxmlEventName) + checkNotNull(gammaEvent) + allEvents += scxmlEventName -> gammaEvent + } + + def getEvent(String scxmlEventName) { + checkNotNull(scxmlEventName) + val gammaEvent = allEvents.get(scxmlEventName) + checkNotNull(gammaEvent) + return gammaEvent + } + + def containsEvent(String scxmlEventName) { + checkNotNull(scxmlEventName) + return allEvents.containsKey(scxmlEventName) + } + + // Event parameters by string identifier + def putEventParameter(String scxmlEventParameterName, ParameterDeclaration gammaEventParameter) { + checkNotNull(scxmlEventParameterName) + checkNotNull(gammaEventParameter) + allEventParameters += scxmlEventParameterName -> gammaEventParameter + } + + def getEventParameter(String scxmlEventParameterName) { + checkNotNull(scxmlEventParameterName) + val gammaEventParameter = allEventParameters.get(scxmlEventParameterName) + checkNotNull(gammaEventParameter) + return gammaEventParameter + } + + def containsEventParameter(String scxmlEventParameterName) { + checkNotNull(scxmlEventParameterName) + return allEventParameters.containsKey(scxmlEventParameterName) + } + +} diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/TriggerTransformer.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/TriggerTransformer.xtend new file mode 100644 index 000000000..264cc24ed --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/TriggerTransformer.xtend @@ -0,0 +1,82 @@ +/******************************************************************************** + * Copyright (c) 2023 Contributors to the Gamma project + * + * 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 + * + * SPDX-License-Identifier: EPL-1.0 + ********************************************************************************/ +package hu.bme.mit.gamma.scxml.transformation + +import hu.bme.mit.gamma.statechart.interface_.Interface +import hu.bme.mit.gamma.statechart.interface_.Port +import hu.bme.mit.gamma.statechart.interface_.RealizationMode + +class TriggerTransformer extends AtomicElementTransformer { + + protected final extension PortTransformer portTransformer + protected final extension InterfaceTransformer interfaceTransformer + protected final extension EventTransformer eventTransformer + + new(StatechartTraceability traceability) { + super(traceability) + + this.portTransformer = new PortTransformer(traceability) + this.interfaceTransformer = new InterfaceTransformer(traceability) + this.eventTransformer = new EventTransformer(traceability) + } + + // TODO sanitize and check eventString + def transformTrigger(String eventString) { + val tokens = eventString.split("\\.") + if (tokens.size < 1 || tokens.size > 3) { + throw new IllegalArgumentException( + "Event descriptor " + eventString + " does not contain exactly 1, 2 or 3 dot separated tokens." + ) + } + + var gammaInterface = null as Interface + var gammaPort = null as Port + var isDefault = false + + if (tokens.size == 1) { + isDefault = true + gammaInterface = getOrCreateDefaultInterface() + gammaPort = getOrCreateDefaultPort() + } else { + val interfaceName = tokens.get(tokens.size - 2) + gammaInterface = getOrTransformInterfaceByName(interfaceName) + + if (tokens.size >= 3) { + val portName = tokens.head + gammaPort = getOrTransformPortByName(gammaInterface, portName, RealizationMode.REQUIRED) + } else { + isDefault = true + gammaPort = getOrTransformDefaultInterfacePort(gammaInterface) + } + } + + val eventName = tokens.lastOrNull + + // If a port is specified, the event will be an out event on an interface + // realized in required mode by the port receiving the event. + // In the case of default interfaces and ports, realization mode is provided + // and trigger events are internal. + val gammaEvent = if (isDefault) { + getOrTransformInternalEvent(gammaInterface, eventName) + } else { + getOrTransformOutEvent(gammaInterface, eventName) + } + + val gammaEventReference = createPortEventReference + gammaEventReference.port = gammaPort + gammaEventReference.event = gammaEvent + + val gammaEventTrigger = createEventTrigger + gammaEventTrigger.eventReference = gammaEventReference + return gammaEventTrigger + } + +} diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/parse/ScxmlGammaExpressionLanguageLinker.xtend b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/parse/ScxmlGammaExpressionLanguageLinker.xtend new file mode 100644 index 000000000..52be970fc --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/parse/ScxmlGammaExpressionLanguageLinker.xtend @@ -0,0 +1,106 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Gamma project + * + * 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 + * + * SPDX-License-Identifier: EPL-1.0 + ********************************************************************************/ + +package hu.bme.mit.gamma.scxml.transformation.parse; + +import hu.bme.mit.gamma.expression.model.ExpressionModelFactory +import hu.bme.mit.gamma.scxml.transformation.StatechartTraceability +import hu.bme.mit.gamma.statechart.util.StatechartUtil +import java.util.List +import java.util.function.Function +import org.eclipse.emf.ecore.EObject +import org.eclipse.xtext.nodemodel.ILeafNode + +class ScxmlGammaExpressionLanguageLinker { + + protected final extension StatechartUtil statechartUtil = StatechartUtil.INSTANCE + protected final extension ExpressionModelFactory expressionModelFactory = ExpressionModelFactory.eINSTANCE + + def Function getLinker(StatechartTraceability traceability) { + return new Function { + List tokenList = newArrayList + +// TODO Goal: set references +// TODO Resolve with reflective API, EObject, EClass +// Get eStructuralFeature name from CrossReference +// Get eStructuralFeature from EClass +// semanticElement.setEStructuralFeature(object) + +// TODO ILeafNode parameter +// Get type (gamma type), id (text, scxml id) +// scxml elements with this id +// filter gamma type matches +// OR: filter by type, then filter by id +// 0 elements -> IllegalArgumentException, user scxml model error +// 1 element -> OK, return EObject +// 2+ element -> random selection from options: throw exception +// or post prcessing step after full model transformation: resolve scoping globally +// heuristic: getLevel in tree -> select safer options: global"er" reference +// or properly implement scoping (direct reference expressions) + override apply(ILeafNode node) { + val id = node.text + val grammarElement = node.getGrammarElement + val reference = node.getSemanticElement + + tokenList += id + + // statechart parameter reference + if (traceability.containsParameter(id)) { + val parameterDeclaration = traceability.getParameter(id) + return statechartUtil.createReferenceExpression(parameterDeclaration) + } + + // variable reference + if (traceability.containsVariable(id)) { + val variableDeclaration = traceability.getVariable(id) + return statechartUtil.createReferenceExpression(variableDeclaration) + } + + // port + if (traceability.containsPort(id)) { + val port = traceability.getPort(id) + return port + } + + // event + if (traceability.containsEvent(id)) { + val event = traceability.getEvent(id) + return event + } + + // event parameter + if (traceability.containsEventParameter(id)) { + val eventParameter = traceability.getEventParameter(id) + return eventParameter + } + + /* TODO? Alternative resolution method: get port event parameter reference by token list + * + * if (traceability.containsPort(tokenList.get(0))) { + * val port = traceability.getPort(tokenList.get(0)) + * val interface = port.interfaceRealization.interface + * if (interface !== null && tokenList.length >= 2) { + * val event = interface.events.findFirst[it.event.name == tokenList.get(1)].event + * if (event !== null && tokenList.length >= 3) { + * val parameter = event.parameterDeclarations.findFirst[it.name == tokenList.get(2)] + * if (parameter !== null) { + * return createEventParameterReference(port, parameter) + * } + * } + * } + }*/ + + return null + } + } + } + +} diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/parse/ScxmlGammaExpressionLanguageParser.java b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/parse/ScxmlGammaExpressionLanguageParser.java new file mode 100644 index 000000000..342324862 --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/src/hu/bme/mit/gamma/scxml/transformation/parse/ScxmlGammaExpressionLanguageParser.java @@ -0,0 +1,146 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Gamma project + * + * 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 + * + * SPDX-License-Identifier: EPL-1.0 + ********************************************************************************/ +package hu.bme.mit.gamma.scxml.transformation.parse; + +import static java.util.Map.entry; + +import java.io.StringReader; +import java.util.Map; +import java.util.function.Function; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.xtext.CrossReference; +import org.eclipse.xtext.nodemodel.ICompositeNode; +import org.eclipse.xtext.nodemodel.ILeafNode; +import org.eclipse.xtext.parser.IParseResult; + +import hu.bme.mit.gamma.expression.model.DirectReferenceExpression; +import hu.bme.mit.gamma.expression.model.EnumerationLiteralExpression; +import hu.bme.mit.gamma.expression.model.Expression; +import hu.bme.mit.gamma.expression.model.OpaqueExpression; +import hu.bme.mit.gamma.expression.model.ParameterDeclaration; +import hu.bme.mit.gamma.expression.model.TypeReference; +import hu.bme.mit.gamma.statechart.interface_.Event; +import hu.bme.mit.gamma.statechart.interface_.EventParameterReferenceExpression; +import hu.bme.mit.gamma.statechart.interface_.Port; +import hu.bme.mit.gamma.statechart.language.parser.StatechartExpressionLanguageParserAndLinker; + +public class ScxmlGammaExpressionLanguageParser extends StatechartExpressionLanguageParserAndLinker { + + private static final Map defaultScxmlPreprocessRules = Map.ofEntries( + entry("!", "not"), + entry("&&", "and"), + entry("||", "or")); + + public Expression preprocessAndParse(String expression, Function scope) { + return super.preprocessAndParse2(expression, scope, defaultScxmlPreprocessRules); + } + + @Override + public Expression parse2(String expression, Function scope) { + String trimmedExpression = javaUtil.deparenthesize(expression); + StringReader reader = new StringReader(trimmedExpression); + + IParseResult result = parser.parse(reader); + + if (result.hasSyntaxErrors()) { + return util.createOpaqueExpression(expression); + } + + try { + String typeReferenceId = null; + + ICompositeNode rootNode = result.getRootNode(); + for (ILeafNode node : rootNode.getLeafNodes()) { + EObject grammarElement = node.getGrammarElement(); + System.out.println("ScxmlGammaExpressionParser: " + node.getText()); + if (grammarElement instanceof CrossReference) { + EObject reference = node.getSemanticElement(); + + String text = node.getText(); + // TODO grammarElement.type.classifier + EObject parsedReference = scope.apply(node); + if (parsedReference == null) { + parsedReference = util.createOpaqueExpression(text); + } + + // TODO + // grammarelement.eContainer().getFeature() // e.g. "port" + // parsedReference.eClass().getEStructuralFeature(featureName); + + EObject container = reference.eContainer(); + + /* + * TODO Save EObject (scope) list, define matchable sequences e.g. + * port.event::param, port.event, port.any, variable, parameter etc. If match, + * then resolve with correct types + * + * TODO Implement scoping, inject scope provider + */ + + /// Direct reference expressions (variable, parameter) + if (reference instanceof DirectReferenceExpression ref) { + if (parsedReference instanceof DirectReferenceExpression ref2) { + ref.setDeclaration(ref2.getDeclaration()); + } + } + /// + + /// Event parameter reference expressions + if (reference instanceof EventParameterReferenceExpression ref) { + if (parsedReference instanceof Port port) { + ref.setPort(port); + } + if (parsedReference instanceof Event event) { + ref.setEvent(event); + } + if (parsedReference instanceof ParameterDeclaration eventParameter) { + ref.setParameter(eventParameter); + } + } + /// + + /* TODO Fix commented section if needed + * if (container == null) { // Replace would not work as it is a single element + * return (Expression) parsedReference; } + */ + + /// Unparsable enum literals (e.g., state references) + if (parsedReference instanceof OpaqueExpression) { // I.e., 'parsedReference' was 'null' + if (reference instanceof TypeReference && container instanceof EnumerationLiteralExpression) { + typeReferenceId = text; + continue; // Next node is the literal id + } else if (reference instanceof EnumerationLiteralExpression) { + String enumId = typeReferenceId + "::" + text; + parsedReference = util.createOpaqueExpression(enumId); + + if (container == null) { + // Replace would not work as it is a single element + return (Expression) parsedReference; + } else { + ecoreUtil.replace(parsedReference, reference); + } + } + } + /// + + // TODO Fix commented section if needed + // ecoreUtil.replace(parsedReference, reference); + } + } + + return (Expression) result.getRootASTElement(); + } catch (Exception e) { + return util.createOpaqueExpression(expression); + } + } + +} diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/xtend-gen/.gitignore b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/xtend-gen/.gitignore new file mode 100644 index 000000000..86d0cb272 --- /dev/null +++ b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/xtend-gen/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/xtend-gen/hu/bme/mit/gamma/scxml/transformation/.ScxmlToGammaTransformer.java._trace b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/xtend-gen/hu/bme/mit/gamma/scxml/transformation/.ScxmlToGammaTransformer.java._trace new file mode 100644 index 000000000..7cbe5193e Binary files /dev/null and b/plugins/scxml/hu.bme.mit.gamma.scxml.transformation/xtend-gen/hu/bme/mit/gamma/scxml/transformation/.ScxmlToGammaTransformer.java._trace differ