diff --git a/plugins/de.cau.cs.kieler.kexpressions/src/de/cau/cs/kieler/kexpressions/extensions/KExpressionsValuedObjectExtensions.xtend b/plugins/de.cau.cs.kieler.kexpressions/src/de/cau/cs/kieler/kexpressions/extensions/KExpressionsValuedObjectExtensions.xtend index ddc5c433c3..5fdc6ec861 100644 --- a/plugins/de.cau.cs.kieler.kexpressions/src/de/cau/cs/kieler/kexpressions/extensions/KExpressionsValuedObjectExtensions.xtend +++ b/plugins/de.cau.cs.kieler.kexpressions/src/de/cau/cs/kieler/kexpressions/extensions/KExpressionsValuedObjectExtensions.xtend @@ -120,7 +120,28 @@ class KExpressionsValuedObjectExtensions { def boolean isVariableReference(ValuedObjectReference valuedObjectReference) { valuedObjectReference.valuedObject.isVariableReference - } + } + + def boolean isEnumReference(ValuedObject valuedObject) { + val decl = valuedObject.declaration + if (decl instanceof ReferenceDeclaration) { + val refVO = decl.reference + if (refVO instanceof ValuedObject) { + val refDecl = refVO.declaration + if (refDecl instanceof ClassDeclaration) { + return refDecl.type == ValueType.ENUM + } + } + } else if (decl.isEnum) { + // check direct enum reference + return true + } + return false + } + + def boolean isEnumReference(ValuedObjectReference valuedObjectReference) { + valuedObjectReference.valuedObject.isEnumReference + } def boolean isModelReference(ValuedObject valuedObject) { valuedObject.declaration instanceof ReferenceDeclaration diff --git a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/EquationSynthesis.xtend b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/EquationSynthesis.xtend index 11bc10238a..760cb7c22b 100644 --- a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/EquationSynthesis.xtend +++ b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/EquationSynthesis.xtend @@ -526,7 +526,7 @@ class EquationSynthesis extends SubSynthesis { * @param output Should be true if output nodes should be generated */ private def dispatch KNode performTransformation(ValuedObjectReference reference, List nodes, boolean output) { - if (!reference.isModelReference + if (!reference.isModelReference && !reference.isEnumReference && ( ( reference.isClassReference @@ -599,7 +599,7 @@ class EquationSynthesis extends SubSynthesis { } node.setProperty(output ? OUTPUT_FLAG : INPUT_FLAG, true) - if (reference.isModelReference) { + if (reference.isModelReference && !reference.isEnumReference) { // in case of a model reference the subreference should not be in the label of the node text = reference.valuedObject.reference.serializeHR.toString for (i : reference.indices) { diff --git a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/StateSynthesis.xtend b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/StateSynthesis.xtend index 52a36e126c..c624371358 100644 --- a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/StateSynthesis.xtend +++ b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/StateSynthesis.xtend @@ -170,9 +170,15 @@ class StateSynthesis extends SubSynthesis { //pre-evaluate type val isConnector = state.isConnector + val isInitialConnector = isConnector && state.isInitial; // Basic state style switch state { + case isInitialConnector: { + node.addInitialConnectorFigure + node.getProperty(KlighdProperties.SEMANTIC_FILTER_TAGS).add(SCChartsSemanticFilterTags.CONNECTOR_STATE) + proxy.addInitialConnectorFigure + } case isConnector: { node.addConnectorFigure node.getProperty(KlighdProperties.SEMANTIC_FILTER_TAGS).add(SCChartsSemanticFilterTags.CONNECTOR_STATE) diff --git a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/TransitionSynthesis.xtend b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/TransitionSynthesis.xtend index 29ad88cf1b..d5c7f30490 100644 --- a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/TransitionSynthesis.xtend +++ b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/TransitionSynthesis.xtend @@ -39,6 +39,8 @@ import static de.cau.cs.kieler.sccharts.ui.synthesis.GeneralSynthesisOptions.* import static de.cau.cs.kieler.sccharts.ui.synthesis.styles.ColorStore.Color.* import static extension de.cau.cs.kieler.klighd.syntheses.DiagramSyntheses.* +import de.cau.cs.kieler.sccharts.extensions.SCChartsCoreExtensions +import de.cau.cs.kieler.annotations.extensions.PragmaExtensions /** * Transforms {@link Transition} into {@link KEdge} diagram elements. @@ -67,8 +69,11 @@ class TransitionSynthesis extends SubSynthesis { @Inject extension TransitionStyles @Inject extension ColorStore @Inject extension AdaptiveZoom + @Inject extension SCChartsCoreExtensions + @Inject extension PragmaExtensions override performTranformation(Transition transition) { + val legacySemantics = transition.getSCCharts.hasPragma("LegacySemantics"); val edge = transition.createEdge().associateWith(transition); edge.configureEdgeLOD(transition) @@ -89,7 +94,7 @@ class TransitionSynthesis extends SubSynthesis { edge.addTransitionSpline(); // Modifiers - if (transition.isImplicitlyImmediate) { + if (transition.isImplicitlyImmediate && (legacySemantics || !transition.sourceState.isConnector)) { edge.setImmediateStyle } if (transition.nondeterministic) { diff --git a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/styles/StateStyles.xtend b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/styles/StateStyles.xtend index 8b77f61049..53ac2aefed 100644 --- a/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/styles/StateStyles.xtend +++ b/plugins/de.cau.cs.kieler.sccharts.ui/src/de/cau/cs/kieler/sccharts/ui/synthesis/styles/StateStyles.xtend @@ -110,6 +110,17 @@ class StateStyles { ] } + /** + * Adds an initial connector figure. + */ + def KRoundedRectangle addInitialConnectorFigure(KNode node) { + node.setMinimalNodeSize(0.5f * DEFAULT_FIGURE_MIN_NODE_SIZE, 0.5f * DEFAULT_FIGURE_MIN_NODE_SIZE); + node.addRoundedRectangle(DEFAULT_FIGURE_CORNER_RADIUS, DEFAULT_FIGURE_CORNER_RADIUS, baseLineWidth) => [ + background = STATE_CONNECTOR.color; + foreground = STATE_CONNECTOR.color; + ] + } + /** * Adds a small state figure. */ diff --git a/plugins/de.cau.cs.kieler.sccharts/src/de/cau/cs/kieler/sccharts/definitions/Semantics.java b/plugins/de.cau.cs.kieler.sccharts/src/de/cau/cs/kieler/sccharts/definitions/Semantics.java new file mode 100644 index 0000000000..ac28e0f511 --- /dev/null +++ b/plugins/de.cau.cs.kieler.sccharts/src/de/cau/cs/kieler/sccharts/definitions/Semantics.java @@ -0,0 +1,60 @@ +/* + * KIELER - Kiel Integrated Environment for Layout Eclipse RichClient + * + * http://rtsys.informatik.uni-kiel.de/kieler + * + * Copyright 2026 by + * + Kiel University + * + Department of Computer Science + * + Real-Time and Embedded Systems Group + * + * This code is provided under the terms of the Eclipse Public License (EPL). + */ +package de.cau.cs.kieler.sccharts.definitions; + +import java.util.EnumSet; +import java.util.List; + +import de.cau.cs.kieler.annotations.Pragma; +import de.cau.cs.kieler.annotations.Pragmatable; +import de.cau.cs.kieler.annotations.StringPragma; + +/** + * @author dam, als + */ +public class Semantics { + + public enum SCCharts { + LEAN, CLASSIC + } + + public static String PRAGMA_SEMANTICS = "semantics"; + + public static EnumSet getSemantics(Pragmatable model) { + List sem = null; + for (Pragma p : model.getPragmas()) { + if (PRAGMA_SEMANTICS.equalsIgnoreCase(p.getName())) { + if (p instanceof StringPragma sp) { + if (!sp.getValues().isEmpty()) { + sem = sp.getValues(); + } + } + } + } + if (sem != null) { + return EnumSet.copyOf(sem.stream().map(s -> SCCharts.valueOf(s.toUpperCase())).toList()); + } else { + return EnumSet.noneOf(SCCharts.class); + } + } + + public static boolean hasSemantics(Pragmatable model, SCCharts sem) { + EnumSet semantics = getSemantics(model); + if (semantics.isEmpty() && sem == SCCharts.CLASSIC) { + return true; + } else { + return semantics.contains(sem); + } + } + +} diff --git a/plugins/de.cau.cs.kieler.sccharts/src/de/cau/cs/kieler/sccharts/extensions/SCChartsActionExtensions.xtend b/plugins/de.cau.cs.kieler.sccharts/src/de/cau/cs/kieler/sccharts/extensions/SCChartsActionExtensions.xtend index fae908d9d7..a91f0ae288 100644 --- a/plugins/de.cau.cs.kieler.sccharts/src/de/cau/cs/kieler/sccharts/extensions/SCChartsActionExtensions.xtend +++ b/plugins/de.cau.cs.kieler.sccharts/src/de/cau/cs/kieler/sccharts/extensions/SCChartsActionExtensions.xtend @@ -148,6 +148,10 @@ class SCChartsActionExtensions { def getDuringActions(Scope scope) { scope.actions.filter(DuringAction) } + + def getImmediateDuringActions(Scope scope) { + scope.actions.filter(DuringAction).filter[immediate] + } def getExitActions(Scope scope) { scope.actions.filter(ExitAction) @@ -168,6 +172,10 @@ class SCChartsActionExtensions { def hasDuringActions(Scope scope) { !scope.duringActions.empty } + + def hasImmediateDuringActions(Scope scope) { + !scope.immediateDuringActions.empty + } def hasExitActions(Scope scope) { !scope.exitActions.empty diff --git a/plugins/de.cau.cs.kieler.sccharts/src/de/cau/cs/kieler/sccharts/processors/statebased/codegen/StatebasedCCodeSerializeHRExtensions2.xtend b/plugins/de.cau.cs.kieler.sccharts/src/de/cau/cs/kieler/sccharts/processors/statebased/codegen/StatebasedCCodeSerializeHRExtensions2.xtend new file mode 100644 index 0000000000..3e3807efe5 --- /dev/null +++ b/plugins/de.cau.cs.kieler.sccharts/src/de/cau/cs/kieler/sccharts/processors/statebased/codegen/StatebasedCCodeSerializeHRExtensions2.xtend @@ -0,0 +1,77 @@ +/* + * KIELER - Kiel Integrated Environment for Layout Eclipse RichClient + * + * http://rtsys.informatik.uni-kiel.de/kieler + * + * Copyright 2026 by + * + Kiel University + * + Department of Computer Science + * + Real-Time and Embedded Systems Group + * + * This code is provided under the terms of the Eclipse Public License (EPL). + */ +package de.cau.cs.kieler.sccharts.processors.statebased.codegen + +import com.google.inject.Singleton +import de.cau.cs.kieler.kexpressions.keffects.Assignment +import de.cau.cs.kieler.scg.ControlFlow +import de.cau.cs.kieler.scg.processors.codegen.c.CCodeSerializeHRExtensions +import org.eclipse.xtend.lib.annotations.Data +import org.eclipse.xtend.lib.annotations.Delegate +import de.cau.cs.kieler.kexpressions.keffects.ReferenceCallEffect +import de.cau.cs.kieler.kexpressions.ReferenceCall + +/** + * @author als + * + */ +@Singleton +class StatebasedCCodeSerializeHRExtensions2 extends CCodeSerializeHRExtensions { + + dispatch override CharSequence serializeHR(Assignment assignment) { + // Use assignment serialization for SCH nodes in underlying serializer + return (new DelegationSCGAssignment(assignment) as de.cau.cs.kieler.scg.Assignment).serializeHR + } + + dispatch override CharSequence serializeHR(ReferenceCallEffect e) { + // Delegate to less specific implementation + _serializeHR(e as ReferenceCall) + } +} + +@Data +class DelegationSCGAssignment implements de.cau.cs.kieler.scg.Assignment { + @Delegate val Assignment delegate + + override getNext() { + throw new UnsupportedOperationException("TODO: auto-generated method stub") + } + + override setNext(ControlFlow value) { + throw new UnsupportedOperationException("TODO: auto-generated method stub") + } + + override isIsInitial() { + throw new UnsupportedOperationException("TODO: auto-generated method stub") + } + + override isSchizophrenic() { + throw new UnsupportedOperationException("TODO: auto-generated method stub") + } + + override setIsInitial(boolean value) { + throw new UnsupportedOperationException("TODO: auto-generated method stub") + } + + override setSchizophrenic(boolean value) { + throw new UnsupportedOperationException("TODO: auto-generated method stub") + } + + override getName() { + throw new UnsupportedOperationException("TODO: auto-generated method stub") + } + + override setName(String value) { + throw new UnsupportedOperationException("TODO: auto-generated method stub") + } +} diff --git a/plugins/de.cau.cs.kieler.sccharts/src/de/cau/cs/kieler/sccharts/processors/statebased/lean/codegen/AbstractStatebasedLeanTemplate.xtend b/plugins/de.cau.cs.kieler.sccharts/src/de/cau/cs/kieler/sccharts/processors/statebased/lean/codegen/AbstractStatebasedLeanTemplate.xtend index 5ee35b9d7f..ff3e64a76c 100644 --- a/plugins/de.cau.cs.kieler.sccharts/src/de/cau/cs/kieler/sccharts/processors/statebased/lean/codegen/AbstractStatebasedLeanTemplate.xtend +++ b/plugins/de.cau.cs.kieler.sccharts/src/de/cau/cs/kieler/sccharts/processors/statebased/lean/codegen/AbstractStatebasedLeanTemplate.xtend @@ -12,12 +12,14 @@ */ package de.cau.cs.kieler.sccharts.processors.statebased.lean.codegen +import de.cau.cs.kieler.core.properties.Property import de.cau.cs.kieler.sccharts.ControlflowRegion import de.cau.cs.kieler.sccharts.Scope import de.cau.cs.kieler.sccharts.State import java.util.List import java.util.Map -import de.cau.cs.kieler.core.properties.Property +import org.eclipse.xtend.lib.annotations.AccessorType +import org.eclipse.xtend.lib.annotations.Accessors /** * Common functionality for Lean State-Based Code Generation using a template. @@ -31,10 +33,10 @@ abstract class AbstractStatebasedLeanTemplate { protected State rootState - protected List scopes - protected Map scopeNames - protected Map scopeEnumNames - protected Map contextStructNames + @Accessors(AccessorType.PUBLIC_GETTER) protected List scopes + @Accessors(AccessorType.PUBLIC_GETTER) protected Map scopeNames + @Accessors(AccessorType.PUBLIC_GETTER) protected Map scopeEnumNames + @Accessors(AccessorType.PUBLIC_GETTER) protected Map contextStructNames protected var int regionCounter protected var int stateEnumCounter @@ -124,21 +126,21 @@ abstract class AbstractStatebasedLeanTemplate { /** * Retrieves the unique name of the given {@link Scope} from the name cache. */ - protected def String uniqueName(Scope scope) { + def String uniqueName(Scope scope) { scopeNames.get(scope) } /** * Retrieves the name of the enum literal, created for the given {@link State}, from the name cache. */ - protected def String uniqueEnumName(State state) { + def String uniqueEnumName(State state) { scopeEnumNames.get(state) } /** * Retrieves the name of the context object used in other contexts, created for the given {@link Scope}. */ - protected def String uniqueContextName(Scope scope) { + def String uniqueContextName(Scope scope) { if (scope instanceof State) { if (scope == rootState) { "TickData" @@ -153,7 +155,7 @@ abstract class AbstractStatebasedLeanTemplate { /** * Retrieves the name of the context type, created for the given {@link Scope}. */ - protected def String uniqueContextMemberName(Scope scope) { + def String uniqueContextMemberName(Scope scope) { if (scope instanceof State) { if (scope == rootState) { '''TickData''' diff --git a/plugins/de.cau.cs.kieler.sccharts/src/de/cau/cs/kieler/sccharts/processors/statebased/lean/codegen/c/StatebasedModernLeanCTemplate.xtend b/plugins/de.cau.cs.kieler.sccharts/src/de/cau/cs/kieler/sccharts/processors/statebased/lean/codegen/c/StatebasedModernLeanCTemplate.xtend new file mode 100644 index 0000000000..e210849d28 --- /dev/null +++ b/plugins/de.cau.cs.kieler.sccharts/src/de/cau/cs/kieler/sccharts/processors/statebased/lean/codegen/c/StatebasedModernLeanCTemplate.xtend @@ -0,0 +1,760 @@ +/* + * KIELER - Kiel Integrated Environment for Layout Eclipse RichClient + * + * http://rtsys.informatik.uni-kiel.de/kieler + * + * Copyright 2026 by + * + Kiel University + * + Department of Computer Science + * + Real-Time and Embedded Systems Group + * + * This code is provided under the terms of the Eclipse Public License (EPL). + */ +package de.cau.cs.kieler.sccharts.processors.statebased.lean.codegen.c + +import com.google.inject.Inject +import de.cau.cs.kieler.annotations.extensions.PragmaExtensions +import de.cau.cs.kieler.core.properties.IProperty +import de.cau.cs.kieler.core.properties.Property +import de.cau.cs.kieler.kexpressions.Declaration +import de.cau.cs.kieler.kexpressions.IODeclaration +import de.cau.cs.kieler.kexpressions.NullValue +import de.cau.cs.kieler.kexpressions.ScheduleDeclaration +import de.cau.cs.kieler.kexpressions.extensions.KExpressionsValuedObjectExtensions +import de.cau.cs.kieler.kexpressions.kext.ClassDeclaration +import de.cau.cs.kieler.kicool.compilation.CodeContainer +import de.cau.cs.kieler.kicool.compilation.ExogenousProcessor +import de.cau.cs.kieler.kicool.compilation.codegen.CodeGeneratorNames +import de.cau.cs.kieler.sccharts.ControlflowRegion +import de.cau.cs.kieler.sccharts.DuringAction +import de.cau.cs.kieler.sccharts.EntryAction +import de.cau.cs.kieler.sccharts.Region +import de.cau.cs.kieler.sccharts.SCCharts +import de.cau.cs.kieler.sccharts.Scope +import de.cau.cs.kieler.sccharts.State +import de.cau.cs.kieler.sccharts.Transition +import de.cau.cs.kieler.sccharts.extensions.SCChartsActionExtensions +import de.cau.cs.kieler.sccharts.extensions.SCChartsStateExtensions +import de.cau.cs.kieler.sccharts.extensions.SCChartsTransitionExtensions +import de.cau.cs.kieler.sccharts.processors.statebased.codegen.StatebasedCCodeSerializeHRExtensions2 +import de.cau.cs.kieler.sccharts.processors.statebased.lean.codegen.AbstractStatebasedLeanTemplate +import de.cau.cs.kieler.scg.processors.codegen.c.CCodeGeneratorModule +import de.cau.cs.kieler.scg.processors.codegen.c.CCodeGeneratorStructModule +import de.cau.cs.kieler.scg.processors.codegen.c.CCodeSerializeHRExtensions +import java.util.LinkedHashSet +import java.util.List +import java.util.Map +import java.util.Set + +import static de.cau.cs.kieler.kicool.compilation.codegen.AbstractCodeGenerator.* +import static extension de.cau.cs.kieler.sccharts.definitions.Semantics.* +import static de.cau.cs.kieler.kicool.compilation.codegen.CodeGeneratorNames.* +import static de.cau.cs.kieler.sccharts.processors.statebased.lean.codegen.AbstractStatebasedLeanTemplate.* +import static de.cau.cs.kieler.sccharts.processors.statebased.lean.codegen.c.StatebasedLeanCCodeGenerator.* +import de.cau.cs.kieler.sccharts.definitions.Semantics + +/** + * @author als + * + */ +class StatebasedModernLeanCTemplate extends ExogenousProcessor { + + @Inject extension PragmaExtensions + @Inject extension KExpressionsValuedObjectExtensions + @Inject extension SCChartsStateExtensions + @Inject extension SCChartsActionExtensions + @Inject extension SCChartsTransitionExtensions + @Inject extension StatebasedCCodeSerializeHRExtensions2 serializer + + public static val IProperty ONLY_WARN = + new Property("de.cau.cs.kieler.kicool.codegen.statebased.modern.lean.c.onlyWarn", false) + public static val IProperty STRONG_EXIT = + new Property("de.cau.cs.kieler.kicool.codegen.statebased.modern.lean.c.strongExit", true) + + public static val FN_PREFIX = "_scc_" + public static val STATE_VAR = "_state" + + @Inject StatebasedStructure structure + @Inject CCodeGeneratorStructModule cStructGenerator + + protected Map naming = newHashMap + protected val header = new StringBuilder + protected val source = new StringBuilder + protected State rootState + protected var boolean debug = false + + override getId() { + "de.cau.cs.kieler.sccharts.processors.codegen.statebased.modern.lean.c" + } + + override getName() { + "State-based C Code (Modern-Lean)" + } + + override process() { + naming.put(TICK, environment.getProperty(TICK_FUNCTION_NAME)) + naming.put(RESET, environment.getProperty(RESET_FUNCTION_NAME)) + naming.put(LOGIC, environment.getProperty(LOGIC_FUNCTION_NAME)) + naming.put(TICKDATA, environment.getProperty(TICKDATA_STRUCT_NAME)) + serializer.valuedObjectPrefix = "context->" + cStructGenerator.indentation = " " + rootState = model.rootStates.head + structure.init(rootState, FN_PREFIX.toUpperCase) + + // Handle unsupported features + val level = environment.getProperty(ONLY_WARN) ? environment.warnings : environment.errors + if (rootState.declarations.exists[it instanceof ScheduleDeclaration]) { + level.add("This compilation approach cannot handle scheduling directives!") + } + if (structure.scopes.filter(State).exists[!it.connector && it.outgoingTransitions.exists[it.isImmediate || it.implicitlyImmediate]]) { + level.add("This compilation approach cannot handle immediate transitions!") + } + if (!model.hasSemantics(Semantics.SCCharts.LEAN)) { + level.add("This compilation approach only supports the LeanSCCharts semantics (add '#semantics \"lean\"' to switch semantics)!") + } + + // Generate code + val codeFilename = hostcodeSafeName(rootState.name) + val hFilename = codeFilename + H_EXTENSION + val headerMacroName = ("_" + hFilename.replaceAll("\\.", "_") + "_").toUpperCase + val cFilename = codeFilename + C_EXTENSION + + val cc = new CodeContainer + createHeader(headerMacroName) + cc.addCHeader(hFilename, header.toString).naming.putAll(naming) + + createSource(hFilename) + cc.addCCode(cFilename, source.toString).naming.putAll(naming) + + setModel(cc) + } + + protected def void createHeader(String headerMacroName) { + header.append(''' + /* + * Automatically generated C code by + * KIELER SCCharts - The Key to Efficient Modeling + * + * http://rtsys.informatik.uni-kiel.de/kieler + */ + #ifndef «headerMacroName» + #define «headerMacroName» + «hostcodeHeaderAdditions()» + + «IF rootState.declarations.filter(ClassDeclaration).exists[!host]» + // SCChart's own classes + «FOR d : rootState.declarations.filter(ClassDeclaration).filter[!host].toList.reverseView» + «cStructGenerator.generateClassDeclaration(d, serializer)» + «ENDFOR» + + «ENDIF» + // States of regions in the SCChart + «FOR r : structure.scopes.filter(ControlflowRegion)» + enum «FN_PREFIX.toUpperCase»«structure.uniqueName(r)»_States { + «r.states.map[structure.uniqueEnumName(it)].join(", ")» + }; + «ENDFOR» + + // Internal state of the SCChart + typedef struct { + // Active states in regions + «FOR r : structure.scopes.filter(ControlflowRegion)» + enum «FN_PREFIX.toUpperCase»«structure.uniqueName(r)»_States «structure.uniqueName(r)»_activeState; + «ENDFOR» + + // Entry handling + «FOR s : structure.scopes.filter(State).filter[it.hasTickBehavior]» + char «structure.uniqueName(s)»_entry; + «ENDFOR» + } «FN_PREFIX»«hostcodeSafeName(rootState.name)»_InternalState; + + typedef struct { + // Internal state + «FN_PREFIX»«hostcodeSafeName(rootState.name)»_InternalState «STATE_VAR»; + + // Internal variables + «createDeclarations(rootState.declarations.filter[!(it instanceof IODeclaration) || !(it as IODeclaration).input && !(it as IODeclaration).output].map[it as Declaration])» + + // IO variables + «createDeclarations(rootState.declarations.filter(IODeclaration).filter[input || output])» + } «naming.get(TICKDATA)»; + + void «naming.get(RESET)»(«naming.get(TICKDATA)» *context); + void «naming.get(TICK)»(«naming.get(TICKDATA)» *context); + + #endif + ''') + } + + protected def createDeclarations(Iterable decls) { + cStructGenerator.code.length = 0 // clear string builder + cStructGenerator.generateDeclarations(decls.map[it as Declaration].toList, 0, serializer) + return cStructGenerator.code.toString + } + + protected def void createSource(String hFilename) { + // TODO reset local vars + source.append(''' + /* + * Automatically generated C code by + * KIELER SCCharts - The Key to Efficient Modeling + * + * http://rtsys.informatik.uni-kiel.de/kieler + */ + «hostcodeAdditions()» + #include + #include "«hFilename»" + + /* Internal behavior */ + «FOR s : structure.scopes.reverseView» + «IF s instanceof State» + «createSourceState(s)» + «ELSEIF s instanceof ControlflowRegion» + «createSourceControlflowRegion(s)» + «ENDIF» + «ENDFOR» + + /* External interface */ + void «naming.get(RESET)»(«naming.get(TICKDATA)» *context) { + «FN_PREFIX»«structure.uniqueName(rootState)»_reset(context); + } + + void «naming.get(TICK)»(«naming.get(TICKDATA)» *context) { + «FN_PREFIX»«structure.uniqueName(rootState)»_tick(context); + } + ''') + } + + protected def CharSequence createSourceState(State state) { + if (state.isSuperstate) { + return ''' + + «IF state.hasTickBehavior || state.hasExitActions» + /* + * State «state.label.nullOrEmpty ? state.name : state.label» «IF state != rootState»(«structure.pathID(state)»)«ENDIF» + «IF state != rootState» * «structure.pathInfo(state)»«ENDIF» + */ + «IF state.hasTickBehavior» + static inline void «FN_PREFIX»«structure.uniqueName(state)»_reset(«naming.get(TICKDATA)» *context) { + context->_state.«structure.uniqueName(state)»_entry = 1; + «IF state.isHierarchical» + // Reset regions + «FOR r : state.regions.filter(ControlflowRegion)» + «FN_PREFIX»«structure.uniqueName(r)»_reset(context); + «ENDFOR» + «ENDIF» + } + «ENDIF» + «IF state.hasExitBehavior» + static inline void «FN_PREFIX»«structure.uniqueName(state)»_exit(«naming.get(TICKDATA)» *context) { + «FOR a : state.exitActions» + «IF a.trigger !== null» + if («a.trigger.serializeHR») { + «FOR e : a.effects» + «e.serializeHR»; + «ENDFOR» + } + «ELSE» + «FOR e : a.effects» + «e.serializeHR»; + «ENDFOR» + «ENDIF» + «ENDFOR» + «IF state.isHierarchical && state.regions.exists[hasExitBehavior]» + // Exit inner states + «FOR r : state.regions.filter[hasExitBehavior]» + «FN_PREFIX»«structure.uniqueName(r)»_exit(context); + «ENDFOR» + «ENDIF» + } + «ENDIF» + «IF state.hasTickBehavior» + static inline void «FN_PREFIX»«structure.uniqueName(state)»_tick(«naming.get(TICKDATA)» *context) { + if (context->_state.«structure.uniqueName(state)»_entry) { + «IF state.hasImmediateDuringActions || state.hasEntryActions» + // Entry + «FOR a : state.actions.filter[it instanceof EntryAction || (it instanceof DuringAction && (it as DuringAction).immediate)]» + «IF a.trigger !== null» + if («a.trigger.serializeHR») { + «FOR e : a.effects» + «e.serializeHR»; + «ENDFOR» + } + «ELSE» + «FOR e : a.effects» + «e.serializeHR»; + «ENDFOR» + «ENDIF» + «ENDFOR» + «ENDIF» + «IF state.isHierarchical && state.regions.filter(ControlflowRegion).exists[it.initialState.connector || it.initialState.hasTickBehavior]» + «IF state.hasImmediateDuringActions || state.hasEntryActions» + + «ENDIF» + // Only execute initial states upon entry to prevent immediate transitioning (except for initial connectors) + «FOR r : state.regions.filter(ControlflowRegion)» + «IF r.initialState.connector» + «FN_PREFIX»«structure.uniqueName(r)»_tick(context); + «ELSEIF r.initialState.hasTickBehavior» + «FN_PREFIX»«structure.uniqueName(r.initialState)»_tick(context); + «ENDIF» + «ENDFOR» + + «ENDIF» + } else { + «IF state.hasDuringActions» + // During + «FOR a : state.duringActions» + «IF a.trigger !== null» + if («a.trigger.serializeHR») { + «FOR e : a.effects» + «e.serializeHR»; + «ENDFOR» + } + «ELSE» + «FOR e : a.effects» + «e.serializeHR»; + «ENDFOR» + «ENDIF» + «ENDFOR» + «ENDIF» + «IF state.isHierarchical» + «IF state.hasDuringActions» + + «ENDIF» + // Regions + «FOR r : state.regions.filter(ControlflowRegion)» + «FN_PREFIX»«structure.uniqueName(r)»_tick(context); + «ENDFOR» + «ENDIF» + } + // Disable entry behavior + context->_state.«structure.uniqueName(state)»_entry = 0; + } + «ENDIF» + «ENDIF» + ''' + } + return "" + } + + protected def CharSequence createSourceControlflowRegion(ControlflowRegion region) { + return ''' + + /* + * Region «region.label.nullOrEmpty ? region.name.nullOrEmpty ? "Unnamed" : region.name : region.label» («structure.pathID(region)») + * «structure.pathInfo(region)» + */ + static inline void «FN_PREFIX»«structure.uniqueName(region)»_reset(«naming.get(TICKDATA)» *context) { + context->_state.«structure.uniqueName(region)»_activeState = «structure.uniqueEnumName(region.initialState)»; + «IF region.states.exists[it.hasTickBehavior]» + + // Reset states + «FOR s : region.states.filter[it.hasTickBehavior]» + «FN_PREFIX»«structure.uniqueName(s)»_reset(context); + «ENDFOR» + «ENDIF» + } + «IF region.hasExitBehavior» + static inline void «FN_PREFIX»«structure.uniqueName(region)»_exit(«naming.get(TICKDATA)» *context) { + // Exit acitve state + switch (context->_state.«structure.uniqueName(region)»_activeState) { + «FOR state : region.states.filter[hasExitBehavior]» + case «structure.uniqueEnumName(state)»: // «state.label.nullOrEmpty ? state.name : state.label» + «FN_PREFIX»«structure.uniqueName(state)»_exit(context); + break; + «ENDFOR» + «IF region.states.exists[!hasExitBehavior]» + «FOR state : region.states.filter[!hasExitBehavior]» + case «structure.uniqueEnumName(state)»: // «state.label.nullOrEmpty ? state.name : state.label» + «ENDFOR» + // Intentional fallthrough + // No exit actions + break; + «ENDIF» + } + } + «ENDIF» + static inline void «FN_PREFIX»«structure.uniqueName(region)»_tick(«naming.get(TICKDATA)» *context) { + «IF region.states.exists[it.connector]» + char handle_connector = 0; + «ENDIF» + // States + switch (context->_state.«structure.uniqueName(region)»_activeState) { + «FOR state : region.states.filter[!connector]» + case «structure.uniqueEnumName(state)»: // «state.label.nullOrEmpty ? state.name : state.label» + «IF state.outgoingTransitions.exists[isStrongAbort]» + // Check preemptive transitions (strong aborts) + «FOR t : state.outgoingPreemptiveTransitions» + «IF t.trigger === null» + if (1) { // Priority «t.priority» + «ELSE» + if («t.trigger.serializeHR») { // Priority «t.priority» + «ENDIF» + «IF state.hasExitBehavior && environment.getProperty(STRONG_EXIT)» + // Exit state + «FN_PREFIX»«structure.uniqueName(state)»_exit(context); + + «ENDIF» + «IF !t.effects.empty» + // Effects + «FOR e : t.effects» + «e.serializeHR»; + «ENDFOR» + + «ENDIF» + «IF t.targetState.connector» + // Go to connector «t.targetState.name» («structure.pathID(t.targetState)») + context->_state.«structure.uniqueName(region)»_activeState = «structure.uniqueEnumName(t.targetState)»; + handle_connector = 1; + «ELSE» + // Go to state «t.targetState.label.nullOrEmpty ? t.targetState.name : t.targetState.label» («structure.pathID(t.targetState)») + context->_state.«structure.uniqueName(region)»_activeState = «structure.uniqueEnumName(t.targetState)»; + «IF t.targetState.hasTickBehavior» + «IF !t.isHistory» + «FN_PREFIX»«structure.uniqueName(t.targetState)»_reset(context); + «ENDIF» + «FN_PREFIX»«structure.uniqueName(t.targetState)»_tick(context); + «ENDIF» + «ENDIF» + break; + } + «ENDFOR» + «ENDIF» + «IF state.hasTickBehavior» + // Execute active state + «FN_PREFIX»«structure.uniqueName(state)»_tick(context); + «ENDIF» + «IF state.outgoingTransitions.exists[isTermination]» + // Check termination + char «structure.uniqueName(state)»_canTerminate = «state.synthesizeTerminationCondition»; + «ENDIF» + «IF state.outgoingTransitions.exists[!isStrongAbort]» + // Check non-preemptive transitions + «FOR t : state.outgoingNonPreemptiveTransitions» + «IF t.trigger === null && !t.isTermination» + «IF !t.isFirst(state.outgoingNonPreemptiveTransitions)»else «ENDIF»if (1) { // Priority «t.priority» + «ELSEIF t.trigger === null && t.isTermination» + «IF !t.isFirst(state.outgoingNonPreemptiveTransitions)»else «ENDIF»if («structure.uniqueName(state)»_canTerminate) { // Priority «t.priority» + «ELSEIF t.isTermination» + «IF !t.isFirst(state.outgoingNonPreemptiveTransitions)»else «ENDIF»if («structure.uniqueName(state)»_canTerminate && («t.trigger.serializeHR»)) { // Priority «t.priority» + «ELSE» + «IF !t.isFirst(state.outgoingNonPreemptiveTransitions)»else «ENDIF»if («t.trigger.serializeHR») { // Priority «t.priority» + «ENDIF» + «IF state.hasExitBehavior» + // Exit state + «FN_PREFIX»«structure.uniqueName(state)»_exit(context); + + «ENDIF» + «IF !t.effects.empty» + // Effects + «FOR e : t.effects» + «e.serializeHR»; + «ENDFOR» + + «ENDIF» + «IF t.targetState.connector» + // Go to connector «t.targetState.name» («structure.pathID(t.targetState)») + context->_state.«structure.uniqueName(region)»_activeState = «structure.uniqueEnumName(t.targetState)»; + handle_connector = 1; + «ELSE» + // Go to state «t.targetState.label.nullOrEmpty ? t.targetState.name : t.targetState.label» («structure.pathID(t.targetState)») + context->_state.«structure.uniqueName(region)»_activeState = «structure.uniqueEnumName(t.targetState)»; + «IF t.targetState.hasTickBehavior» + «IF !t.isHistory» + «FN_PREFIX»«structure.uniqueName(t.targetState)»_reset(context); + «ENDIF» + «FN_PREFIX»«structure.uniqueName(t.targetState)»_tick(context); + «ENDIF» + «ENDIF» + } + «ENDFOR» + «ENDIF» + break; + «ENDFOR» + «IF region.states.exists[connector]» + «FOR c : region.states.filter[connector]» + case «structure.uniqueEnumName(c)»: + «ENDFOR» + // Intentional fallthrough + // Connectors are transient and handled separately to enable instantaneous traversal + handle_connector = 1; + break; + «ENDIF» + } + «IF region.states.exists[connector]» + // Instantaneous connector traversal + if (handle_connector) { + «FOR state : region.connectorsInTopologicalOrder» + if (context->_state.«structure.uniqueName(region)»_activeState == «structure.uniqueEnumName(state)») { // «state.label.nullOrEmpty ? state.name : state.label» + «FOR t : state.outgoingTransitions» + «IF t.trigger === null» + «IF !t.isFirst(state.outgoingTransitions)»else «ENDIF»if (1) { // Priority «t.priority» + «ELSE» + «IF !t.isFirst(state.outgoingTransitions)»else «ENDIF»if («t.trigger.serializeHR») { // Priority «t.priority» + «ENDIF» + «IF !t.effects.empty» + // Effects + «FOR e : t.effects» + «e.serializeHR»; + «ENDFOR» + + «ENDIF» + «IF t.targetState.connector» + // Go to next connector «t.targetState.name» («structure.pathID(t.targetState)») + context->_state.«structure.uniqueName(region)»_activeState = «structure.uniqueEnumName(t.targetState)»; + «ELSE» + // Go to state «t.targetState.label.nullOrEmpty ? t.targetState.name : t.targetState.label» («structure.pathID(t.targetState)») + context->_state.«structure.uniqueName(region)»_activeState = «structure.uniqueEnumName(t.targetState)»; + «IF t.targetState.hasTickBehavior» + «FN_PREFIX»«structure.uniqueName(t.targetState)»_reset(context); + «FN_PREFIX»«structure.uniqueName(t.targetState)»_tick(context); + «ENDIF» + «ENDIF» + } + «ENDFOR» + } + «ENDFOR» + } + «ENDIF» + } + ''' + } + + def String synthesizeTerminationCondition(State s) { + return s.regions.filter(ControlflowRegion).map[ r | + if (!r.final) { + if (r.allFinalStates.empty) { + return "0" + } else { + return r.allFinalStates.join("(", " || ", ")", ['''context->_state.«structure.uniqueName(r)»_activeState == «structure.uniqueEnumName(it)»''']) + } + } + ].join(" && ") + } + + def outgoingPreemptiveTransitions(State s) { + return s.outgoingTransitions.filter[isStrongAbort] + } + + def outgoingNonPreemptiveTransitions(State s) { + return s.outgoingTransitions.filter[!isStrongAbort] + } + + def boolean isFirst(Transition t, Iterable l) { + return l.head == t + } + + def Iterable connectorsInTopologicalOrder(ControlflowRegion r) { + val sorted = newLinkedHashSet() + val connectors = newLinkedHashSet + connectors.addAll(r.states.filter[connector]) + for (c : connectors) { + if (!sorted.contains(c)) { + connectorsInTopologicalOrder(c, sorted, newHashSet) + } + } + return sorted + } + def void connectorsInTopologicalOrder(State s, LinkedHashSet sorted, Set visited) { + if (!s.isConnector || sorted.contains(s)) { + return + } + if (visited.contains(s)) { + environment.errors.add("This compilation approach cannot handle instantaneous cycles in connector states!") + return + } + // Mark + visited.add(s) + // Add incoming connectors first + for (pre : s.incomingTransitions.map[sourceState].filter[connector]) { + connectorsInTopologicalOrder(pre, sorted, visited) + } + sorted.add(s) + } + + def List> connectorPaths(State s, Set visited) { + if (visited.contains(s)) { + environment.errors.add("This compilation approach cannot handle instantaneous cycles in connector states!") + return emptyList + } + if (s.connector) { + val paths = newLinkedList + for (t : s.outgoingTransitions) { + visited.add(s) + val subPath = connectorPaths(t.targetState, visited) + visited.remove(s) + if (subPath.empty) { + paths.add(newLinkedList(t)) + } else { + for (p : subPath) { + paths.add(newLinkedList(t) => [it.addAll(p)]) + } + } + } + return paths + } + return emptyList + } + + def boolean hasTickBehavior(State s) { + return s.isHierarchical || s.hasDuringActions || s.hasEntryActions + } + + def boolean hasExitBehavior(Scope s) { + if (s instanceof State) { + return s.hasExitActions || (s.isHierarchical && s.regions.exists[it.hasExitBehavior]) + } else if (s instanceof ControlflowRegion) { + s.states.exists[it.hasExitBehavior] + } + return false + } + + /** + * Adds hostcode additions. These can come from internal sources like the serialization, + * but also from the model via hostcode pragmas. + */ + protected def String hostcodeAdditions() { + val sb = new StringBuilder + val includes = modifications.get(CCodeSerializeHRExtensions.INCLUDES) + for (include : includes) { + sb.append("#include " + include + "\n") + } + + val hostcodePragmas = model.getStringPragmas(CCodeGeneratorModule.HOSTCODE) + model.getStringPragmas(CCodeGeneratorModule.HOSTCODE_C) + for (pragma : hostcodePragmas) { + sb.append(pragma.values.head + "\n") + } + if (hostcodePragmas.size > 0 || includes.size > 0) { + sb.append("\n") + } + return sb.toString + } + + /** + * Adds hostcode additions for header. These can come from internal sources like the serialization, + * but also from the model via hostcode pragmas. + */ + protected def String hostcodeHeaderAdditions() { + val sb = new StringBuilder + val includes = modifications.get(CCodeSerializeHRExtensions.HEADER_INCLUDES) + for (include : includes) { + sb.append("#include " + include + "\n") + } + + if (model.eAllContents.exists[it instanceof NullValue]) { + sb.append("#ifndef NULL\n#define NULL 0\n#endif\n") + } + + val hostcodePragmas = model.getStringPragmas(CCodeGeneratorModule.HOSTCODE) + model.getStringPragmas(CCodeGeneratorModule.HOSTCODE_HEADER) + for (pragma : hostcodePragmas) { + sb.append(pragma.values.head + "\n") + } + if (hostcodePragmas.size > 0 || includes.size > 0) { + sb.append("\n") + } + return sb.toString + } +} + +class StatebasedStructure extends AbstractStatebasedLeanTemplate { + + var State root + protected val ids = newHashMap + + def void init(State rootState, String enumPrefix) { + root = rootState + ids.clear() + scopes = newLinkedList + scopeNames = newHashMap + scopeEnumNames = newHashMap + contextStructNames = newHashMap + regionCounter = 0 + stateEnumCounter = 1 + enumerateScopes(rootState, "", enumPrefix) + } + + def String pathID(Scope scope) { + var id = "" + if (!ids.containsKey(scope)) { + var s = scope + while (s !== null && s !== root) { + switch(s) { + State: { + id = "S" + s.parentRegion.states.indexOf(s) + id + s = s.parentRegion + }, + Region: { + id = "R" + s.parentState.regions.indexOf(s) + id + s = s.parentState + } + } + } + ids.put(scope, id) + } else { + id = ids.get(scope) + } + return id + } + + def String pathInfo(Scope scope) { + var info = "" + var s = scope + while (s !== null) { + val name = s.label.nullOrEmpty ? s.name : s.label + if (info.nullOrEmpty) { + info = name + } else { + info = name + " -> " + info + } + switch(s) { + State: { + s = s.parentRegion + }, + Region: { + s = s.parentState + } + } + } + return info + } + + override enumerateState(State scope, String namePrefix, String enumPrefix) { + // Check if this scope has no unique name yet + if (!scopeNames.containsKey(scope)) { + val name = if (scope == root) { + scope.name.hostcodeSafeName + } else { + '''«pathID(scope)»_«scope.name.hostcodeSafeName»''' + } + // Store this unique state name in the name cache + scopeNames.put(scope, name) + scopeEnumNames.put(scope, enumPrefix + name.toUpperCase) + } + + // Enumerate all the regions in this state, if this is a superstate + for (region : scope.regions.filter(ControlflowRegion)) { + enumerateScopes(region, "", enumPrefix) + } + } + + override enumerateControlflowRegion(ControlflowRegion scope, String namePrefix, String enumPrefix) { + // Check if this scope has no unique name yet + if (!scopeNames.containsKey(scope)) { + var String name + if (scope.name.nullOrEmpty) { + // Generate a name with a running number if no region name is defined + name = '''«pathID(scope)»_anon''' + } else { + name = '''«pathID(scope)»_«scope.name.hostcodeSafeName»''' + } + // Store this unique region name in the name cache + scopeNames.put(scope, name) + contextStructNames.put(scope, pathID(scope) + scope.uniqueName.lowerCapital) + } + // Enumerate all the states contained in the region + for (state : scope.states) { + enumerateScopes(state, scope.uniqueName, enumPrefix) + } + } +} diff --git a/plugins/de.cau.cs.kieler.sccharts/src/de/cau/cs/kieler/sccharts/providers/SCChartsProcessorProvider.xtend b/plugins/de.cau.cs.kieler.sccharts/src/de/cau/cs/kieler/sccharts/providers/SCChartsProcessorProvider.xtend index e834a5e5e1..39c9182231 100644 --- a/plugins/de.cau.cs.kieler.sccharts/src/de/cau/cs/kieler/sccharts/providers/SCChartsProcessorProvider.xtend +++ b/plugins/de.cau.cs.kieler.sccharts/src/de/cau/cs/kieler/sccharts/providers/SCChartsProcessorProvider.xtend @@ -91,7 +91,8 @@ class SCChartsProcessorProvider implements IProcessorProvider { de.cau.cs.kieler.sccharts.processors.AbortRegion, de.cau.cs.kieler.sccharts.processors.scg.SCGAbortRegionProcessor, de.cau.cs.kieler.sccharts.processors.MethodSignaling, - de.cau.cs.kieler.sccharts.processors.analyzers.SCChartsModelData + de.cau.cs.kieler.sccharts.processors.analyzers.SCChartsModelData, + de.cau.cs.kieler.sccharts.processors.statebased.lean.codegen.c.StatebasedModernLeanCTemplate ] } diff --git a/plugins/de.cau.cs.kieler.sccharts/src/de/cau/cs/kieler/sccharts/providers/SCChartsSystemProvider.xtend b/plugins/de.cau.cs.kieler.sccharts/src/de/cau/cs/kieler/sccharts/providers/SCChartsSystemProvider.xtend index 8b66c8cf07..bdd6b42b20 100644 --- a/plugins/de.cau.cs.kieler.sccharts/src/de/cau/cs/kieler/sccharts/providers/SCChartsSystemProvider.xtend +++ b/plugins/de.cau.cs.kieler.sccharts/src/de/cau/cs/kieler/sccharts/providers/SCChartsSystemProvider.xtend @@ -41,6 +41,7 @@ class SCChartsSystemProvider implements ISystemProvider { "system/de.cau.cs.kieler.sccharts.statebased.lean.c.template.kico", "system/de.cau.cs.kieler.sccharts.statebased.lean.cs.c.template.kico", "system/de.cau.cs.kieler.sccharts.statebased.lean.java.template.kico", + "system/de.cau.cs.kieler.sccharts.statebased.modern.lean.c.kico", "system/de.cau.cs.kieler.sccharts.csv.kico", "system/de.cau.cs.kieler.sccharts.statebased.lean.arduino.deploy.kico", "system/de.cau.cs.kieler.sccharts.netlist.arduino.deploy.kico", @@ -61,10 +62,12 @@ class SCChartsSystemProvider implements ISystemProvider { "system/de.cau.cs.kieler.sccharts.simulation.statebased.lean.c.kico", "system/de.cau.cs.kieler.sccharts.simulation.statebased.lean.cs.c.kico", "system/de.cau.cs.kieler.sccharts.simulation.statebased.lean.java.kico", + "system/de.cau.cs.kieler.sccharts.simulation.statebased.modern.lean.c.kico", "system/de.cau.cs.kieler.sccharts.simulation.tts.statebased.c.kico", "system/de.cau.cs.kieler.sccharts.simulation.tts.statebased.lean.c.kico", "system/de.cau.cs.kieler.sccharts.simulation.tts.statebased.lean.cs.c.kico", "system/de.cau.cs.kieler.sccharts.simulation.tts.statebased.lean.java.kico", + "system/de.cau.cs.kieler.sccharts.simulation.tts.statebased.modern.lean.c.kico", "system/de.cau.cs.kieler.c.sccharts.dataflow.kico", "system/de.cau.cs.kieler.sccharts.simulation.priority.java.legacy.kico", "system/de.cau.cs.kieler.sccharts.simulation.tts.priority.java.legacy.kico", diff --git a/plugins/de.cau.cs.kieler.sccharts/src/de/cau/cs/kieler/sccharts/text/validation/SCTXValidator.xtend b/plugins/de.cau.cs.kieler.sccharts/src/de/cau/cs/kieler/sccharts/text/validation/SCTXValidator.xtend index d45d511c84..3dd7a6a59a 100644 --- a/plugins/de.cau.cs.kieler.sccharts/src/de/cau/cs/kieler/sccharts/text/validation/SCTXValidator.xtend +++ b/plugins/de.cau.cs.kieler.sccharts/src/de/cau/cs/kieler/sccharts/text/validation/SCTXValidator.xtend @@ -55,6 +55,7 @@ import de.cau.cs.kieler.sccharts.SCChartsPackage import de.cau.cs.kieler.sccharts.Scope import de.cau.cs.kieler.sccharts.ScopeCall import de.cau.cs.kieler.sccharts.Transition +import de.cau.cs.kieler.sccharts.definitions.Semantics import de.cau.cs.kieler.sccharts.extensions.SCChartsActionExtensions import de.cau.cs.kieler.sccharts.extensions.SCChartsCoreExtensions import de.cau.cs.kieler.sccharts.extensions.SCChartsFixExtensions @@ -75,6 +76,7 @@ import org.eclipse.xtext.validation.AbstractDeclarativeValidator import org.eclipse.xtext.validation.Check import org.eclipse.xtext.validation.CheckType +import static extension de.cau.cs.kieler.sccharts.definitions.Semantics.* import static extension java.lang.String.* /** @@ -134,6 +136,7 @@ class SCTXValidator extends AbstractSCTXValidator { static val String CANNOT_BIND_LITERAL_TO_OUTPUT = "You cannot bind a literal to an output object." static val String DUPLICATE_VARIABLE = "The variable is declared multiple times in this scope." static val String NON_IMMEDIATE_CONNECTOR = "Outgoing transitions of connector states should be marked as immediate." + static val String IMMEDIATE_CONNECTOR = "Outgoing transitions of connector states should not be marked as immediate unless legacy semantics are used." static val String NO_DEFAULT_TRANSITION = "Connector states should have an outgoing transition without trigger." static val String NO_OUTGOING_TRANSITION = "Connector states must have an outgoing transition." static val String NON_REACHABLE_TRANSITION = "The transition is not reachable." @@ -724,9 +727,12 @@ class SCTXValidator extends AbstractSCTXValidator { if(state.connector) { var Transition lastTransition var boolean transitionWithoutTrigger = false + val hasLegacySemantics = state.SCCharts.hasSemantics(Semantics.SCCharts.CLASSIC) for(trans : state.outgoingTransitions) { - if(!trans.isImmediate) { + if(hasLegacySemantics && !trans.isImmediate) { warning(NON_IMMEDIATE_CONNECTOR, trans, null) + } else if (!hasLegacySemantics && trans.isImmediate) { + warning(IMMEDIATE_CONNECTOR, trans, null) } if(trans.trigger === null) { transitionWithoutTrigger = true diff --git a/plugins/de.cau.cs.kieler.sccharts/system/de.cau.cs.kieler.sccharts.simulation.statebased.modern.lean.c.kico b/plugins/de.cau.cs.kieler.sccharts/system/de.cau.cs.kieler.sccharts.simulation.statebased.modern.lean.c.kico new file mode 100644 index 0000000000..23f5f991db --- /dev/null +++ b/plugins/de.cau.cs.kieler.sccharts/system/de.cau.cs.kieler.sccharts.simulation.statebased.modern.lean.c.kico @@ -0,0 +1,5 @@ +developer system de.cau.cs.kieler.sccharts.^simulation.statebased.modern.lean.c + label "State-based Modern-Lean Simulation (C, No-TTS)" + +system de.cau.cs.kieler.sccharts.statebased.modern.lean.c +system de.cau.cs.kieler.^simulation.c \ No newline at end of file diff --git a/plugins/de.cau.cs.kieler.sccharts/system/de.cau.cs.kieler.sccharts.simulation.tts.statebased.modern.lean.c.kico b/plugins/de.cau.cs.kieler.sccharts/system/de.cau.cs.kieler.sccharts.simulation.tts.statebased.modern.lean.c.kico new file mode 100644 index 0000000000..d610198dcb --- /dev/null +++ b/plugins/de.cau.cs.kieler.sccharts/system/de.cau.cs.kieler.sccharts.simulation.tts.statebased.modern.lean.c.kico @@ -0,0 +1,5 @@ +simulation system de.cau.cs.kieler.sccharts.^simulation.tts.statebased.modern.lean.c + label "State-based Modern-Lean Simulation (C)" + +de.cau.cs.kieler.sccharts.processors.takenTransitionSignaling +system de.cau.cs.kieler.sccharts.^simulation.statebased.modern.lean.c \ No newline at end of file diff --git a/plugins/de.cau.cs.kieler.sccharts/system/de.cau.cs.kieler.sccharts.statebased.modern.lean.c.kico b/plugins/de.cau.cs.kieler.sccharts/system/de.cau.cs.kieler.sccharts.statebased.modern.lean.c.kico new file mode 100644 index 0000000000..8310e48058 --- /dev/null +++ b/plugins/de.cau.cs.kieler.sccharts/system/de.cau.cs.kieler.sccharts.statebased.modern.lean.c.kico @@ -0,0 +1,43 @@ +system de.cau.cs.kieler.sccharts.statebased.modern.lean.c + label "State-based Compilation (C) modern-lean-mode" + start config { + "de.cau.cs.kieler.sccharts.processors.dataflow.oo": true + } + +de.cau.cs.kieler.sccharts.processors.reference +post process silent de.cau.cs.kieler.sccharts.processors.unpackClasses +post process silent de.cau.cs.kieler.kicool.deploy.variable.store.init +de.cau.cs.kieler.sccharts.processors.regionactions +de.cau.cs.kieler.sccharts.processors.codeEffects +de.cau.cs.kieler.sccharts.processors.for +de.cau.cs.kieler.sccharts.processors.prTransitions +pre process de.cau.cs.kieler.sccharts.processors.^enum +de.cau.cs.kieler.sccharts.processors.^const +pre process de.cau.cs.kieler.sccharts.processors.period +de.cau.cs.kieler.sccharts.processors.timedautomata +de.cau.cs.kieler.sccharts.processors.initOperator +de.cau.cs.kieler.sccharts.processors.followedBy +de.cau.cs.kieler.sccharts.processors.sequentialFollowedBy +//de.cau.cs.kieler.sccharts.processors.history +[ label "SyncCharts" + de.cau.cs.kieler.sccharts.processors.^signal + de.cau.cs.kieler.sccharts.processors.suspend + de.cau.cs.kieler.sccharts.processors.countDelay + de.cau.cs.kieler.sccharts.processors.^pre +] + +[ label "Scade / Quartz / Esterel V7" + de.cau.cs.kieler.sccharts.processors.weakSuspend + de.cau.cs.kieler.sccharts.processors.deferred + de.cau.cs.kieler.sccharts.processors.^static +] + +[ label "Statecharts" +// de.cau.cs.kieler.sccharts.processors.complexFinalState + de.cau.cs.kieler.sccharts.processors.initialization +] +de.cau.cs.kieler.sccharts.processors.arrayAssignment +// de.cau.cs.kieler.sccharts.processors.userSchedule +// End of Extended +pre process silent de.cau.cs.kieler.sccharts.processors.voRise +de.cau.cs.kieler.sccharts.processors.codegen.statebased.modern.lean.c \ No newline at end of file diff --git a/plugins/de.cau.cs.kieler.scg/src/de/cau/cs/kieler/scg/processors/codegen/c/CCodeGeneratorModule.xtend b/plugins/de.cau.cs.kieler.scg/src/de/cau/cs/kieler/scg/processors/codegen/c/CCodeGeneratorModule.xtend index 47e241ec1f..51108d4a10 100644 --- a/plugins/de.cau.cs.kieler.scg/src/de/cau/cs/kieler/scg/processors/codegen/c/CCodeGeneratorModule.xtend +++ b/plugins/de.cau.cs.kieler.scg/src/de/cau/cs/kieler/scg/processors/codegen/c/CCodeGeneratorModule.xtend @@ -46,9 +46,9 @@ class CCodeGeneratorModule extends SCGCodeGeneratorModule { @Inject Injector injector - protected static val HOSTCODE = PragmaRegistry.register("hostcode", StringPragma, "Allows additional hostcode to be included (e.g. includes).") - protected static val HOSTCODE_C = PragmaRegistry.register("hostcode-c", StringPragma, "Allows additional hostcode to be included (e.g. includes) only for C.") - protected static val HOSTCODE_HEADER = PragmaRegistry.register("hostcode-c-header", StringPragma, "Allows additional hostcode to be included (e.g. includes) only for the C header fie.") + public static val HOSTCODE = PragmaRegistry.register("hostcode", StringPragma, "Allows additional hostcode to be included (e.g. includes).") + public static val HOSTCODE_C = PragmaRegistry.register("hostcode-c", StringPragma, "Allows additional hostcode to be included (e.g. includes) only for C.") + public static val HOSTCODE_HEADER = PragmaRegistry.register("hostcode-c-header", StringPragma, "Allows additional hostcode to be included (e.g. includes) only for the C header fie.") public static val C_EXTENSION = ".c" public static val H_EXTENSION = ".h" diff --git a/plugins/de.cau.cs.kieler.scg/src/de/cau/cs/kieler/scg/processors/codegen/c/CCodeGeneratorStructModule.xtend b/plugins/de.cau.cs.kieler.scg/src/de/cau/cs/kieler/scg/processors/codegen/c/CCodeGeneratorStructModule.xtend index 7446ad07b0..218a8b7142 100644 --- a/plugins/de.cau.cs.kieler.scg/src/de/cau/cs/kieler/scg/processors/codegen/c/CCodeGeneratorStructModule.xtend +++ b/plugins/de.cau.cs.kieler.scg/src/de/cau/cs/kieler/scg/processors/codegen/c/CCodeGeneratorStructModule.xtend @@ -132,7 +132,7 @@ class CCodeGeneratorStructModule extends SCGCodeGeneratorModule { } else if (declaration instanceof ReferenceDeclaration) { for (valuedObject : declaration.valuedObjects) { val myModule = parent as CCodeGeneratorModule - val module = codeGeneratorModuleMap.get(declaration.reference) as CCodeGeneratorModule + val module = codeGeneratorModuleMap?.get(declaration.reference) as CCodeGeneratorModule if (module !== null) { code.append(indentation) code.append(module.struct.name) diff --git a/plugins/de.cau.cs.kieler.simulation/src/de/cau/cs/kieler/simulation/processor/AbstractSimulationTemplateGenerator.xtend b/plugins/de.cau.cs.kieler.simulation/src/de/cau/cs/kieler/simulation/processor/AbstractSimulationTemplateGenerator.xtend index 0ac3ecb108..2c81b43252 100644 --- a/plugins/de.cau.cs.kieler.simulation/src/de/cau/cs/kieler/simulation/processor/AbstractSimulationTemplateGenerator.xtend +++ b/plugins/de.cau.cs.kieler.simulation/src/de/cau/cs/kieler/simulation/processor/AbstractSimulationTemplateGenerator.xtend @@ -37,7 +37,7 @@ abstract class AbstractSimulationTemplateGenerator extends AbstractTemplateGener } def dropHostTypes(Iterable> list) { - return list.filter[!(value.type === ValueType.HOST || "host".equalsIgnoreCase(value.typeName))] + return list.filter[!(it.value.type === ValueType.HOST || it.value.type === ValueType.ENUM || "host".equalsIgnoreCase(it.value.typeName))] } protected def simpleName(String name) { diff --git a/plugins/de.cau.cs.kieler.simulation/src/de/cau/cs/kieler/simulation/processor/CSimulationTemplateGenerator.xtend b/plugins/de.cau.cs.kieler.simulation/src/de/cau/cs/kieler/simulation/processor/CSimulationTemplateGenerator.xtend index 6d71cc16dc..70ee28bebd 100644 --- a/plugins/de.cau.cs.kieler.simulation/src/de/cau/cs/kieler/simulation/processor/CSimulationTemplateGenerator.xtend +++ b/plugins/de.cau.cs.kieler.simulation/src/de/cau/cs/kieler/simulation/processor/CSimulationTemplateGenerator.xtend @@ -239,6 +239,7 @@ class CSimulationTemplateGenerator extends AbstractSimulationTemplateGenerator { case UNSIGNED, case INT, case DOUBLE, + case ENUM, case FLOAT: '''cJSON_CreateNumber(«access»)''' case STRING: '''((«access» != NULL) ? cJSON_CreateStringReference(«access») : cJSON_CreateNull())''' case CLASS, @@ -266,6 +267,7 @@ class CSimulationTemplateGenerator extends AbstractSimulationTemplateGenerator { case UNSIGNED, case INT, case DOUBLE, + case ENUM, case FLOAT: '''cJSON_CreateNumber(«access»)''' case STRING: '''cJSON_CreateString((«access» != NULL) ? «access» : "")''' case CLASS, @@ -321,6 +323,7 @@ class CSimulationTemplateGenerator extends AbstractSimulationTemplateGenerator { return switch(info.type) { case UNSIGNED, case BOOL, + case ENUM, case INT: "->valueint" case DOUBLE, case FLOAT: "->valuedouble" diff --git a/test/de.cau.cs.kieler.sccharts.test.simulation/src/de/cau/cs/kieler/sccharts/test/simulation/SCChartsLeanSimulationExperiment.xtend b/test/de.cau.cs.kieler.sccharts.test.simulation/src/de/cau/cs/kieler/sccharts/test/simulation/SCChartsLeanSimulationExperiment.xtend new file mode 100644 index 0000000000..ea63f9ebff --- /dev/null +++ b/test/de.cau.cs.kieler.sccharts.test.simulation/src/de/cau/cs/kieler/sccharts/test/simulation/SCChartsLeanSimulationExperiment.xtend @@ -0,0 +1,58 @@ +/* + * KIELER - Kiel Integrated Environment for Layout Eclipse RichClient + * + * http://rtsys.informatik.uni-kiel.de/kieler + * + * Copyright 2026 by + * + Kiel University + * + Department of Computer Science + * + Real-Time and Embedded Systems Group + * + * This code is provided under the terms of the Eclipse Public License (EPL). + */ +package de.cau.cs.kieler.sccharts.test.simulation + +import de.cau.cs.kieler.sccharts.SCCharts +import de.cau.cs.kieler.sccharts.text.SCTXStandaloneSetup +import de.cau.cs.kieler.simulation.testing.TestModelData +import de.cau.cs.kieler.test.common.repository.ModelsRepositoryTestRunner +import de.cau.cs.kieler.test.common.simulation.AbstractSimulationTest +import org.junit.Test +import org.junit.runner.RunWith + +import static org.junit.Assume.* +import de.cau.cs.kieler.core.properties.MapPropertyHolder + +/** + * @author als + * + */ +@RunWith(ModelsRepositoryTestRunner) +class SCChartsLeanSimulationExperiment extends AbstractSimulationTest { + + public static val String MODERN_STATEBASED = "de.cau.cs.kieler.sccharts.simulation.statebased.modern.lean.c" + + public static val scchartsInjector = new SCTXStandaloneSetup().createInjectorAndDoEMFRegistration + + new() { + super(scchartsInjector) + } + + override filter(TestModelData modelData) { + return modelData.hasSimulationTrace + && modelData.modelProperties.contains("sccharts") + && !modelData.modelProperties.contains("large") + && !modelData.modelProperties.contains("known-to-fail") + && !modelData.modelProperties.contains("must-fail") + } + + @Test + def void testSimulationModernLeanStatebasedC(SCCharts scc, TestModelData modelData) { + assumeFalse("Has 'simulation-fails' property", modelData.modelProperties.contains("simulation-fails-c")) + + val props = new MapPropertyHolder + props.setProperty(new de.cau.cs.kieler.core.properties.Property("de.cau.cs.kieler.kicool.codegen.statebased.modern.lean.c.onlyWarn"), true) + + startSimulationTest(MODERN_STATEBASED, scc, modelData, "SCChartsLeamSimulationC", props) + } +} \ No newline at end of file