From 3c10dcd02c70d141b4608895b85dbb20adf18ed1 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Mon, 4 Apr 2022 14:39:46 -0500 Subject: [PATCH 01/56] Added a new failing test for TAN messages --- .../DistributedPhysicalActionUpstream.lf | 44 +++++++++++++++++++ test/C/src/lib/PassThrough.lf | 9 ++++ 2 files changed, 53 insertions(+) create mode 100644 test/C/src/federated/DistributedPhysicalActionUpstream.lf create mode 100644 test/C/src/lib/PassThrough.lf diff --git a/test/C/src/federated/DistributedPhysicalActionUpstream.lf b/test/C/src/federated/DistributedPhysicalActionUpstream.lf new file mode 100644 index 0000000000..301340c782 --- /dev/null +++ b/test/C/src/federated/DistributedPhysicalActionUpstream.lf @@ -0,0 +1,44 @@ +target C { timeout: 2 secs }; + +import PassThrough from "../lib/PassThrough.lf" +import TestCount from "../lib/TestCount.lf" + +preamble {= + int counter = 1; + void callback(void *a) { + schedule(a, counter++); + } + // Simulate time passing before a callback occurs. + void* take_time(void* a) { + while (counter < 40) { + instant_t sleep_time = MSEC(30); + lf_nanosleep(sleep_time); + callback(a); + } + return NULL; + } +=} + +reactor WithPhysicalAction { + output out:int; + state thread_id:lf_thread_t(0); + physical action act(0):int; + reaction(startup) -> act {= + // start new thread, provide callback + lf_thread_create(&self->thread_id, &take_time, act); + =} + + reaction(act) -> out {= + SET(out, act->value); + =} +} + +federated reactor { + a = new WithPhysicalAction(); + m1 = new PassThrough(); + m2 = new PassThrough(); + test = new TestCount(num_inputs=39); + a.out -> m1.in; + m1.out -> m2.in; + m2.out -> test.in; +} diff --git a/test/C/src/lib/PassThrough.lf b/test/C/src/lib/PassThrough.lf new file mode 100644 index 0000000000..c7ebcf6f3c --- /dev/null +++ b/test/C/src/lib/PassThrough.lf @@ -0,0 +1,9 @@ +target C; + +reactor PassThrough { + input in:int; + output out:int; + reaction(in) -> out {= + SET(out, in->value); + =} +} From c4666fc751e01a509309e3c5b884e4d4130a7777 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Mon, 4 Apr 2022 14:49:36 -0500 Subject: [PATCH 02/56] Updated reactor-c --- org.lflang/src/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index 2831081660..83d8d00fe6 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 2831081660675a1db84139afae91ba904b06872e +Subproject commit 83d8d00fe6df82bc63a52e61afa425a023c551f4 From 126d5628c237cd4fd870ebb01cbc2037dc4fab00 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Mon, 4 Apr 2022 15:21:07 -0500 Subject: [PATCH 03/56] Updated the test --- .../federated/DistributedPhysicalActionUpstream.lf | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/test/C/src/federated/DistributedPhysicalActionUpstream.lf b/test/C/src/federated/DistributedPhysicalActionUpstream.lf index 301340c782..1cfe8d510e 100644 --- a/test/C/src/federated/DistributedPhysicalActionUpstream.lf +++ b/test/C/src/federated/DistributedPhysicalActionUpstream.lf @@ -4,14 +4,14 @@ import PassThrough from "../lib/PassThrough.lf" import TestCount from "../lib/TestCount.lf" preamble {= - int counter = 1; + int _counter = 1; void callback(void *a) { - schedule(a, counter++); + schedule_int(a, 0, _counter++); } // Simulate time passing before a callback occurs. void* take_time(void* a) { - while (counter < 40) { - instant_t sleep_time = MSEC(30); + while (_counter < 4) { + instant_t sleep_time = MSEC(100); lf_nanosleep(sleep_time); callback(a); } @@ -36,9 +36,7 @@ reactor WithPhysicalAction { federated reactor { a = new WithPhysicalAction(); m1 = new PassThrough(); - m2 = new PassThrough(); - test = new TestCount(num_inputs=39); + test = new TestCount(num_inputs=3); a.out -> m1.in; - m1.out -> m2.in; - m2.out -> test.in; + m1.out -> test.in; } From 7612e07ac5f40f7d34f8419e16db54a5eac8fc8c Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Mon, 4 Apr 2022 15:21:15 -0500 Subject: [PATCH 04/56] Updated reactor-c --- org.lflang/src/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index 83d8d00fe6..54b31c837c 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 83d8d00fe6df82bc63a52e61afa425a023c551f4 +Subproject commit 54b31c837c523036572c4cc44e1d4cc693b7bc80 From 05ea29b78f4c0336922dbf58ce4fcb868bee3659 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Mon, 4 Apr 2022 15:41:16 -0500 Subject: [PATCH 05/56] Lowered the interval to add stress --- org.lflang/src/lib/c/reactor-c | 2 +- .../federated/DistributedPhysicalActionUpstream.lf | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index 54b31c837c..b0e8500281 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 54b31c837c523036572c4cc44e1d4cc693b7bc80 +Subproject commit b0e85002819c5239bf43feaa3af436200af3aadd diff --git a/test/C/src/federated/DistributedPhysicalActionUpstream.lf b/test/C/src/federated/DistributedPhysicalActionUpstream.lf index 1cfe8d510e..c7771d5f35 100644 --- a/test/C/src/federated/DistributedPhysicalActionUpstream.lf +++ b/test/C/src/federated/DistributedPhysicalActionUpstream.lf @@ -1,4 +1,7 @@ -target C { timeout: 2 secs }; +target C { + timeout: 4 secs, + coordination-options: {advance-message-interval: 30 msec} +}; import PassThrough from "../lib/PassThrough.lf" import TestCount from "../lib/TestCount.lf" @@ -10,8 +13,8 @@ preamble {= } // Simulate time passing before a callback occurs. void* take_time(void* a) { - while (_counter < 4) { - instant_t sleep_time = MSEC(100); + while (_counter < 150) { + instant_t sleep_time = MSEC(10); lf_nanosleep(sleep_time); callback(a); } @@ -36,7 +39,7 @@ reactor WithPhysicalAction { federated reactor { a = new WithPhysicalAction(); m1 = new PassThrough(); - test = new TestCount(num_inputs=3); + test = new TestCount(num_inputs=149); a.out -> m1.in; m1.out -> test.in; } From 286483ecf29a0fd1ad6ffe318629f40b262dbcab Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Tue, 5 Apr 2022 13:02:19 -0500 Subject: [PATCH 06/56] Updated reactor-c --- org.lflang/src/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index b0e8500281..808a189fda 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit b0e85002819c5239bf43feaa3af436200af3aadd +Subproject commit 808a189fdabd3bb88550b887ed97d2630aebf981 From f77a7ae862267ba56b2a270f44a9201dbed8de5f Mon Sep 17 00:00:00 2001 From: eal Date: Tue, 5 Apr 2022 17:24:52 -0700 Subject: [PATCH 07/56] Align with reactor-c --- org.lflang/src/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index 808a189fda..5ac67c4913 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 808a189fdabd3bb88550b887ed97d2630aebf981 +Subproject commit 5ac67c4913120026f3659afb60d480b6dd543399 From be1de0f0c298afd2063cfae53fcb5b3107d9c678 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Fri, 13 May 2022 14:52:46 +0200 Subject: [PATCH 08/56] modes: Implemented new handling of startup/shutdown/reset in modes Only in C --- .../diagram/synthesis/util/ModeDiagrams.java | 10 +- .../ui/contentassist/LFProposalProvider.java | 17 -- .../LFSemanticHighlightingCalculator.java | 22 -- org.lflang/src/lib/c/reactor-c | 2 +- org.lflang/src/org/lflang/LinguaFranca.xtext | 16 +- .../org/lflang/generator/ModeInstance.java | 63 +---- .../lflang/generator/ReactionInstance.java | 7 +- .../org/lflang/generator/ReactorInstance.java | 38 +-- .../org/lflang/generator/TriggerInstance.java | 18 +- .../org/lflang/generator/c/CGenerator.java | 40 ++- .../lflang/generator/c/CModesGenerator.java | 39 ++- .../generator/c/CReactionGenerator.java | 244 +++++++++++------- .../generator/c/CTriggerObjectsGenerator.java | 4 - .../generator/python/PythonModeGenerator.java | 6 +- .../org/lflang/validation/LFValidator.java | 9 - test/C/src/modal_models/ModalStartup.lf | 90 ------- .../src/modal_models/ModalStartupShutdown.lf | 152 +++++++++++ test/C/src/modal_models/ModalStateReset.lf | 8 + 18 files changed, 427 insertions(+), 358 deletions(-) delete mode 100644 test/C/src/modal_models/ModalStartup.lf create mode 100644 test/C/src/modal_models/ModalStartupShutdown.lf diff --git a/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ModeDiagrams.java b/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ModeDiagrams.java index 77436059d5..7e6e1f20b3 100644 --- a/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ModeDiagrams.java +++ b/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ModeDiagrams.java @@ -53,11 +53,11 @@ import org.lflang.diagram.synthesis.styles.LinguaFrancaShapeExtensions; import org.lflang.diagram.synthesis.styles.LinguaFrancaStyleExtensions; import org.lflang.generator.ModeInstance; -import org.lflang.generator.ModeInstance.ModeTransitionType; import org.lflang.generator.ModeInstance.Transition; import org.lflang.generator.ReactorInstance; import org.lflang.lf.Action; import org.lflang.lf.Mode; +import org.lflang.lf.ModeTransition; import org.lflang.lf.Timer; import com.google.common.collect.LinkedHashMultimap; @@ -228,9 +228,9 @@ public void handleModes(List nodes, ReactorInstance reactor) { } // add transitions - var representedTargets = new HashSet>(); + var representedTargets = new HashSet>(); for (var transition : ListExtensions.reverseView(mode.transitions)) { - if (!representedTargets.contains(new Pair(transition.target, transition.type))) { + if (!representedTargets.contains(new Pair(transition.target, transition.type))) { var edge = _kEdgeExtensions.createEdge(); edge.setSource(modeNode); edge.setTarget(modeNodes.get(transition.target)); @@ -240,7 +240,7 @@ public void handleModes(List nodes, ReactorInstance reactor) { associateWith(edge, transition.getDefinition()); } else { // Bundle similar transitions - representedTargets.add(new Pair(transition.target, transition.type)); + representedTargets.add(new Pair(transition.target, transition.type)); } } } @@ -430,7 +430,7 @@ private void addTransitionFigure(KEdge edge, Transition transition) { _kRenderingExtensions.setForeground(spline, MODE_FG); _linguaFrancaStyleExtensions.boldLineSelectionStyle(spline); - if (transition.type == ModeTransitionType.HISTORY) { + if (transition.type == ModeTransition.HISTORY) { addHistoryDecorator(spline); } else { KRendering arrowDecorator = _kPolylineExtensions.addHeadArrowDecorator(spline); diff --git a/org.lflang.ui/src/org/lflang/ui/contentassist/LFProposalProvider.java b/org.lflang.ui/src/org/lflang/ui/contentassist/LFProposalProvider.java index 7c822a2d96..78262951a2 100644 --- a/org.lflang.ui/src/org/lflang/ui/contentassist/LFProposalProvider.java +++ b/org.lflang.ui/src/org/lflang/ui/contentassist/LFProposalProvider.java @@ -23,12 +23,6 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ***************/ package org.lflang.ui.contentassist; -import org.eclipse.emf.ecore.EObject; -import org.eclipse.xtext.Assignment; -import org.eclipse.xtext.ui.editor.contentassist.ContentAssistContext; -import org.eclipse.xtext.ui.editor.contentassist.ICompletionProposalAcceptor; -import org.lflang.generator.ModeInstance.ModeTransitionType; - /** * Code completion adjustments for LF. * @@ -38,15 +32,4 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * @author{Alexander Schulz-Rosengarten } */ public class LFProposalProvider extends AbstractLFProposalProvider { - - @Override - public void completeVarRefOrModeTransition_Modifier(EObject model, Assignment assignment, ContentAssistContext context, ICompletionProposalAcceptor acceptor) { - // call implementation of superclass - super.completeVarRefOrModeTransition_Modifier(model, assignment, context, acceptor); - - // code completion for mode transitions - for (var value : ModeTransitionType.values()) { - acceptor.accept(createCompletionProposal(value.getKeyword(), context)); - } - } } diff --git a/org.lflang.ui/src/org/lflang/ui/highlighting/LFSemanticHighlightingCalculator.java b/org.lflang.ui/src/org/lflang/ui/highlighting/LFSemanticHighlightingCalculator.java index 865f9dc1e3..32a796dadb 100644 --- a/org.lflang.ui/src/org/lflang/ui/highlighting/LFSemanticHighlightingCalculator.java +++ b/org.lflang.ui/src/org/lflang/ui/highlighting/LFSemanticHighlightingCalculator.java @@ -23,17 +23,12 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ***************/ package org.lflang.ui.highlighting; -import org.eclipse.xtext.ide.editor.syntaxcoloring.DefaultSemanticHighlightingCalculator; -import org.eclipse.xtext.ide.editor.syntaxcoloring.HighlightingStyles; import org.eclipse.xtext.ide.editor.syntaxcoloring.IHighlightedPositionAcceptor; import org.eclipse.xtext.ide.editor.syntaxcoloring.ISemanticHighlightingCalculator; import org.eclipse.xtext.parser.IParseResult; import org.eclipse.xtext.resource.ILocationInFileProvider; import org.eclipse.xtext.resource.XtextResource; import org.eclipse.xtext.util.CancelIndicator; -import org.lflang.ASTUtils; -import org.lflang.generator.ModeInstance.ModeTransitionType; -import org.lflang.lf.LfPackage; import org.lflang.lf.Model; import com.google.inject.Inject; @@ -61,23 +56,6 @@ public void provideHighlightingFor(XtextResource resource, IHighlightedPositionA return; } Model model = (Model) parseResult.getRootASTElement(); - - // Provide keyword highlighting for special mode transitions - for (var reactor : model.getReactors()) { - for (var reaction : ASTUtils.allReactions(reactor)) { - for (var effect : reaction.getEffects()) { - if (effect.getModifier() != null) { - if (ModeTransitionType.KEYWORDS.contains(effect.getModifier())) { - var pos = locator.getSignificantTextRegion(effect, LfPackage.eINSTANCE.getVarRef_Modifier(), 0); - if (pos != null) { - acceptor.addPosition(pos.getOffset(), pos.getLength(), HighlightingStyles.KEYWORD_ID); - } - } - } - } - } - } - } } diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index aaa5c455a3..7bfd123ee8 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit aaa5c455a3943da29f398bb843b406613c5060d9 +Subproject commit 7bfd123ee897dba9d1b2f94dcd6101f60dba8dea diff --git a/org.lflang/src/org/lflang/LinguaFranca.xtext b/org.lflang/src/org/lflang/LinguaFranca.xtext index 6e071503cd..57074e2a67 100644 --- a/org.lflang/src/org/lflang/LinguaFranca.xtext +++ b/org.lflang/src/org/lflang/LinguaFranca.xtext @@ -202,7 +202,10 @@ Reaction: (deadline=Deadline)?; TriggerRef: - VarRef | startup?='startup' | shutdown?='shutdown'; + BuiltinTriggerRef | VarRef; + +BuiltinTriggerRef: + type = BuiltinTrigger; Deadline: 'deadline' '(' delay=Value ')' code=Code; @@ -280,8 +283,7 @@ VarRef: | interleaved?='interleaved' '(' (variable=[Variable] | container=[Instantiation] '.' variable=[Variable]) ')' ; VarRefOrModeTransition returns VarRef: - VarRef | modifier=ID '(' variable=[Mode] ')' // using String field instead of an enum rule keeps user namespace cleaner -; + VarRef | transition=ModeTransition '(' variable=[Mode] ')'; Assignment: (lhs=[Parameter] ( @@ -457,6 +459,12 @@ enum ActionOrigin: enum Visibility: NONE | PRIVATE='private' | PUBLIC='public'; +enum BuiltinTrigger: + STARTUP='startup' | SHUTDOWN='shutdown' | RESET='reset'; + +enum ModeTransition: + RESET='reset' | HISTORY='continue'; + // Note: time units are not keywords, otherwise it would reserve // a lot of useful identifiers (like 's' or 'd'). // The validator ensures the unit is valid. @@ -479,7 +487,7 @@ Token: 'mutable' | 'input' | 'output' | 'timer' | 'action' | 'reaction' | 'startup' | 'shutdown' | 'after' | 'deadline' | 'mutation' | 'preamble' | 'new' | 'federated' | 'at' | 'as' | 'from' | 'widthof' | 'const' | 'method' | - 'interleaved' | 'mode' | 'initial' | + 'interleaved' | 'mode' | 'initial' | 'reset' | 'continue' | // Other terminals NEGINT | TRUE | FALSE | // Action origins diff --git a/org.lflang/src/org/lflang/generator/ModeInstance.java b/org.lflang/src/org/lflang/generator/ModeInstance.java index 38ffb0f64c..27abc01b12 100644 --- a/org.lflang/src/org/lflang/generator/ModeInstance.java +++ b/org.lflang/src/org/lflang/generator/ModeInstance.java @@ -24,13 +24,11 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY package org.lflang.generator; -import java.util.Arrays; import java.util.LinkedList; import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; import org.lflang.lf.Mode; +import org.lflang.lf.ModeTransition; import org.lflang.lf.VarRef; /** @@ -181,59 +179,6 @@ private void collectMembers() { } } - //////////////////////////////////////////////////// - // Transition type enum. - - public enum ModeTransitionType { - RESET("reset"), HISTORY("continue"); - - public static ModeTransitionType DEFAULT = ModeTransitionType.RESET; - public static Set KEYWORDS = Arrays.stream(values()).map(v -> v.keyword).collect(Collectors.toUnmodifiableSet()); - - private final String keyword; - private ModeTransitionType(String keyword) { - this.keyword = keyword; - } - - /** - * @return the keyword for this enum. - */ - public String getKeyword() { - return keyword; - } - - /** - * Returns the 'Mode Transition Types' with the - * specified keyword. - * - * @param keyword the keyword. - * @return the matching enumerator or null. - */ - public static ModeTransitionType getByKeyword(String keyword) { - if (keyword != null && !keyword.isEmpty()) { - for (var type : values()) { - if (type.keyword.equals(keyword)) { - return type; - } - } - } - return null; - } - - /** - * Returns the 'Mode Transition Types' for the - * given transition effect. - * - * @param definition the AST definition. - * @return the matching enumerator or default value if no valid modifier is present. - */ - public static ModeTransitionType getModeTransitionType(VarRef definition) { - var type = getByKeyword(definition.getModifier()); - return type != null ? type : DEFAULT; - } - - } - //////////////////////////////////////////////////// // Data class. @@ -241,14 +186,14 @@ public static class Transition extends NamedInstance { public final ModeInstance source; public final ModeInstance target; public final ReactionInstance reaction; - public final ModeTransitionType type; + public final ModeTransition type; Transition(ModeInstance source, ModeInstance target, ReactionInstance reaction, VarRef definition) { super(definition, source.parent); this.source = source; this.target = target; this.reaction = reaction; - this.type = ModeTransitionType.getModeTransitionType(definition); + this.type = definition.getTransition() == null ? ModeTransition.RESET : definition.getTransition(); } @Override @@ -261,7 +206,7 @@ public ReactorInstance root() { return this.parent.root(); } - public ModeTransitionType getType() { + public ModeTransition getType() { return type; } diff --git a/org.lflang/src/org/lflang/generator/ReactionInstance.java b/org.lflang/src/org/lflang/generator/ReactionInstance.java index a0f9d53432..77d4a74d21 100644 --- a/org.lflang/src/org/lflang/generator/ReactionInstance.java +++ b/org.lflang/src/org/lflang/generator/ReactionInstance.java @@ -36,6 +36,7 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY import org.lflang.TimeUnit; import org.lflang.TimeValue; import org.lflang.lf.Action; +import org.lflang.lf.BuiltinTriggerRef; import org.lflang.lf.Port; import org.lflang.lf.Reaction; import org.lflang.lf.Timer; @@ -126,10 +127,8 @@ public ReactionInstance( timerInstance.dependentReactions.add(this); this.sources.add(timerInstance); } - } else if (trigger.isStartup()) { - this.triggers.add(parent.getOrCreateStartup(trigger)); - } else if (trigger.isShutdown()) { - this.triggers.add(parent.getOrCreateShutdown(trigger)); + } else if (trigger instanceof BuiltinTriggerRef) { + this.triggers.add(parent.getOrCreateBuiltinTrigger((BuiltinTriggerRef) trigger)); } } // Next handle the ports that this reaction reads. diff --git a/org.lflang/src/org/lflang/generator/ReactorInstance.java b/org.lflang/src/org/lflang/generator/ReactorInstance.java index 1f88acd6e9..6a4fa02976 100644 --- a/org.lflang/src/org/lflang/generator/ReactorInstance.java +++ b/org.lflang/src/org/lflang/generator/ReactorInstance.java @@ -27,10 +27,12 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY package org.lflang.generator; import java.util.ArrayList; +import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Set; import org.lflang.ASTUtils; @@ -38,6 +40,8 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY import org.lflang.TimeValue; import org.lflang.generator.TriggerInstance.BuiltinTriggerVariable; import org.lflang.lf.Action; +import org.lflang.lf.BuiltinTrigger; +import org.lflang.lf.BuiltinTriggerRef; import org.lflang.lf.Connection; import org.lflang.lf.Delay; import org.lflang.lf.Input; @@ -318,14 +322,14 @@ public ParameterInstance getParameter(String name) { * Return the startup trigger or null if not used in any reaction. */ public TriggerInstance getStartupTrigger() { - return startupTrigger; + return builtinTriggers.get(BuiltinTrigger.STARTUP); } /** * Return the shutdown trigger or null if not used in any reaction. */ public TriggerInstance getShutdownTrigger() { - return shutdownTrigger; + return builtinTriggers.get(BuiltinTrigger.SHUTDOWN); } /** @@ -675,11 +679,8 @@ public TimeValue getTimeValue(Delay d) { /** The generator that created this reactor instance. */ protected ErrorReporter reporter; // FIXME: This accumulates a lot of redundant references - /** The startup trigger. Null if not used in any reaction. */ - protected TriggerInstance startupTrigger = null; - - /** The shutdown trigger. Null if not used in any reaction. */ - protected TriggerInstance shutdownTrigger = null; + /** The map of used built-in triggers. */ + protected Map> builtinTriggers = new HashMap<>(); /** * The LF syntax does not currently support declaring reactions unordered, @@ -722,25 +723,14 @@ protected void createReactionInstances() { } /** - * Returns the startup trigger or create a new one if none exists. - */ - protected TriggerInstance getOrCreateStartup(TriggerRef trigger) { - if (startupTrigger == null) { - startupTrigger = new TriggerInstance<>( - TriggerInstance.BuiltinTrigger.STARTUP, trigger, this); - } - return startupTrigger; - } - - /** - * Returns the shutdown trigger or create a new one if none exists. + * Returns the built-in trigger or create a new one if none exists. */ - protected TriggerInstance getOrCreateShutdown(TriggerRef trigger) { - if (shutdownTrigger == null) { - shutdownTrigger = new TriggerInstance<>( - TriggerInstance.BuiltinTrigger.SHUTDOWN, trigger, this); + protected TriggerInstance getOrCreateBuiltinTrigger(BuiltinTriggerRef trigger) { + if (!builtinTriggers.containsKey(trigger.getType())) { + builtinTriggers.put(trigger.getType(), + new TriggerInstance<>(trigger.getType(), trigger, this)); } - return shutdownTrigger; + return builtinTriggers.get(trigger.getType()); } //////////////////////////////////////// diff --git a/org.lflang/src/org/lflang/generator/TriggerInstance.java b/org.lflang/src/org/lflang/generator/TriggerInstance.java index 21b02527ac..11260e662a 100644 --- a/org.lflang/src/org/lflang/generator/TriggerInstance.java +++ b/org.lflang/src/org/lflang/generator/TriggerInstance.java @@ -29,6 +29,7 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY import java.util.LinkedHashSet; import java.util.Set; +import org.lflang.lf.BuiltinTrigger; import org.lflang.lf.TriggerRef; import org.lflang.lf.Variable; import org.lflang.lf.impl.VariableImpl; @@ -66,16 +67,6 @@ public TriggerInstance(BuiltinTrigger type, TriggerRef trigger, ReactorInstance super((T)(new BuiltinTriggerVariable(type, trigger)), parent); builtinTriggerType = type; } - - ///////////////////////////////////////////// - //// Public Fields - - /** - * Special builtin trigger types. - */ - public enum BuiltinTrigger { - STARTUP, SHUTDOWN - } ///////////////////////////////////////////// //// Public Methods @@ -132,6 +123,13 @@ public boolean isStartup() { return builtinTriggerType == BuiltinTrigger.STARTUP; } + /** + * Return true if this trigger is "startup"./ + */ + public boolean isReset() { + return builtinTriggerType == BuiltinTrigger.RESET; + } + /** * Return true if this trigger is a builtin one. */ diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.java b/org.lflang/src/org/lflang/generator/c/CGenerator.java index 156ee3cec5..b2d20c7736 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.java @@ -362,6 +362,7 @@ public class CGenerator extends GeneratorBase { private int timerCount = 0; private int startupReactionCount = 0; private int shutdownReactionCount = 0; + private int resetReactionCount = 0; private int modalReactorCount = 0; private int modalStateResetCount = 0; @@ -716,6 +717,7 @@ private void generateCodeForCurrentFederate( initializeTriggerObjects.pr(String.join("\n", "int _lf_startup_reactions_count = 0;", "int _lf_shutdown_reactions_count = 0;", + "int _lf_reset_reactions_count = 0;", "int _lf_timer_triggers_count = 0;", "int _lf_tokens_with_ref_count_count = 0;" )); @@ -731,8 +733,14 @@ private void generateCodeForCurrentFederate( // If there are timers, create a table of timers to be initialized. code.pr(CTimerGenerator.generateDeclarations(timerCount)); + // If there are startup reactions, create a table of triggers. + code.pr(CReactionGenerator.generateBuiltinTriggersTable(startupReactionCount, "startup")); + // If there are shutdown reactions, create a table of triggers. - code.pr(CReactionGenerator.generateShutdownTriggersTable(shutdownReactionCount)); + code.pr(CReactionGenerator.generateBuiltinTriggersTable(shutdownReactionCount, "shutdown")); + + // If there are reset reactions, create a table of triggers. + code.pr(CReactionGenerator.generateBuiltinTriggersTable(resetReactionCount, "reset")); // If there are modes, create a table of mode state to be checked for transitions. code.pr(CModesGenerator.generateModeStatesTable( @@ -797,7 +805,7 @@ private void generateCodeForCurrentFederate( )); // Generate function to trigger startup reactions for all reactors. - code.pr(CReactionGenerator.generateLfTriggerStartupReactions(startupReactionCount)); + code.pr(CReactionGenerator.generateLfTriggerStartupReactions(startupReactionCount, hasModalReactors)); // Generate function to schedule timers for all reactors. code.pr(CTimerGenerator.generateLfInitializeTimer(timerCount)); @@ -821,9 +829,7 @@ private void generateCodeForCurrentFederate( // Generate function to schedule shutdown reactions if any // reactors have reactions to shutdown. - code.pr(CReactionGenerator.generateLfTriggerShutdownReactions( - shutdownReactionCount - )); + code.pr(CReactionGenerator.generateLfTriggerShutdownReactions(shutdownReactionCount, hasModalReactors)); // Generate an empty termination function for non-federated // execution. For federated execution, an implementation is @@ -833,10 +839,19 @@ private void generateCodeForCurrentFederate( code.pr("void terminate_execution() {}"); } + // Generate functions for modes + code.pr(CModesGenerator.generateLfInitializeModes( + hasModalReactors + )); code.pr(CModesGenerator.generateLfHandleModeChanges( hasModalReactors, modalStateResetCount )); + code.pr(CReactionGenerator.generateLfModeTriggeredReactions( + startupReactionCount, + resetReactionCount, + hasModalReactors + )); } } @@ -1537,10 +1552,10 @@ public void generateReaction(Reaction reaction, ReactorDecl decl, int reactionIn } /** - * Record startup and shutdown reactions. + * Record startup, shutdown, and reset reactions. * @param instance A reactor instance. */ - private void recordStartupAndShutdown(ReactorInstance instance) { + private void recordBuiltinTriggers(ReactorInstance instance) { // For each reaction instance, allocate the arrays that will be used to // trigger downstream reactions. for (ReactionInstance reaction : instance.reactions) { @@ -1571,6 +1586,10 @@ private void recordStartupAndShutdown(ReactorInstance instance) { "trace_trigger, "+addDoubleQuotes(description+".shutdown")+");" )); } + } else if (trigger.isReset()) { + temp.pr("_lf_reset_reactions[_lf_reset_reactions_count++] = &"+reactionRef+";"); + resetReactionCount += currentFederate.numRuntimeInstances(reactor); + foundOne = true; } } if (foundOne) initializeTriggerObjects.pr(temp.toString()); @@ -1889,7 +1908,7 @@ public void generateReactorInstance(ReactorInstance instance) { generateParameterInitialization(instance); initializeOutputMultiports(instance); initializeInputMultiports(instance); - recordStartupAndShutdown(instance); + recordBuiltinTriggers(instance); // Next, initialize the "self" struct with state variables. // These values may be expressions that refer to the parameter values defined above. @@ -2028,6 +2047,11 @@ public void generateStateVariableInitializations(ReactorInstance instance) { var mode = stateVar.eContainer() instanceof Mode ? instance.lookupModeInstance((Mode) stateVar.eContainer()) : instance.getMode(false); + // In the current concept state variables are not automatically reset. + // Instead they need to be manually reset using a reset triggered reaction. + // Yet, the code to generate an automatic reset is kept present but inactive to prepare for changes to this + // semantics and allow e.g. for a convenience annotation that enables reset for a specific variable/type. + mode = null; initializeTriggerObjects.pr(CStateGenerator.generateInitializer( instance, selfRef, diff --git a/org.lflang/src/org/lflang/generator/c/CModesGenerator.java b/org.lflang/src/org/lflang/generator/c/CModesGenerator.java index 8c695400fa..f95db17f20 100644 --- a/org.lflang/src/org/lflang/generator/c/CModesGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CModesGenerator.java @@ -44,7 +44,8 @@ public static void generateDeclarations( constructorCode.pr(mode, String.join("\n", "self->_lf__modes["+i+"].state = &_lf_self_base->_lf__mode_state;", "self->_lf__modes["+i+"].name = \""+mode.getName()+"\";", - "self->_lf__modes["+i+"].deactivation_time = 0;" + "self->_lf__modes["+i+"].deactivation_time = 0;", + "self->_lf__modes["+i+"].flags = 0;" )); if (initialMode < 0 && mode.isInitial()) { initialMode = i; @@ -58,7 +59,7 @@ public static void generateDeclarations( "// Initialize mode state", "_lf_self_base->_lf__mode_state.parent_mode = NULL;", "_lf_self_base->_lf__mode_state.initial_mode = &self->_lf__modes["+initialMode+"];", - "_lf_self_base->_lf__mode_state.active_mode = _lf_self_base->_lf__mode_state.initial_mode;", + "_lf_self_base->_lf__mode_state.current_mode = _lf_self_base->_lf__mode_state.initial_mode;", "_lf_self_base->_lf__mode_state.next_mode = NULL;", "_lf_self_base->_lf__mode_state.mode_change = no_transition;" )); @@ -110,14 +111,32 @@ public static String generateLfHandleModeChanges( } return String.join("\n", "void _lf_handle_mode_changes() {", - " _lf_process_mode_changes(", - " _lf_modal_reactor_states, ", - " _lf_modal_reactor_states_size, ", - " " + (modalStateResetCount > 0 ? "_lf_modal_state_reset" : "NULL") + ", ", - " " + (modalStateResetCount > 0 ? "_lf_modal_state_reset_size" : "0") + ", ", - " _lf_timer_triggers, ", - " _lf_timer_triggers_size", - " );", + " _lf_process_mode_changes(", + " _lf_modal_reactor_states, ", + " _lf_modal_reactor_states_size, ", + " " + (modalStateResetCount > 0 ? "_lf_modal_state_reset" : "NULL") + ", ", + " " + (modalStateResetCount > 0 ? "_lf_modal_state_reset_size" : "0") + ", ", + " _lf_timer_triggers, ", + " _lf_timer_triggers_size", + " );", + "}" + ); + } + + /** + * Generate code to call `_lf_initialize_modes`. + * + * @param hasModalReactors True if there is modal model reactors, false otherwise + */ + public static String generateLfInitializeModes(boolean hasModalReactors) { + if (!hasModalReactors) { + return ""; + } + return String.join("\n", + "void _lf_initialize_modes() {", + " _lf_initialize_mode_states(", + " _lf_modal_reactor_states, ", + " _lf_modal_reactor_states_size);", "}" ); } diff --git a/org.lflang/src/org/lflang/generator/c/CReactionGenerator.java b/org.lflang/src/org/lflang/generator/c/CReactionGenerator.java index 3e0961e035..decf4e4800 100644 --- a/org.lflang/src/org/lflang/generator/c/CReactionGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CReactionGenerator.java @@ -17,13 +17,14 @@ import org.lflang.generator.CodeBuilder; import org.lflang.generator.ReactionInstance; import org.lflang.generator.TriggerInstance; -import org.lflang.generator.ModeInstance.ModeTransitionType; import org.lflang.lf.Action; import org.lflang.lf.ActionOrigin; +import org.lflang.lf.BuiltinTriggerRef; import org.lflang.lf.Code; import org.lflang.lf.Input; import org.lflang.lf.Instantiation; import org.lflang.lf.Mode; +import org.lflang.lf.ModeTransition; import org.lflang.lf.Output; import org.lflang.lf.Port; import org.lflang.lf.Reaction; @@ -175,7 +176,7 @@ public static String generateInitializationForReaction(String body, reactionInitialization.pr( "reactor_mode_t* " + name + " = &self->_lf__modes[" + idx + "];\n" + "lf_mode_change_type_t _lf_" + name + "_change_type = " - + (ModeTransitionType.getModeTransitionType(effect) == ModeTransitionType.HISTORY ? + + (effect.getTransition() == ModeTransition.HISTORY ? "history_transition" : "reset_transition") + ";" ); @@ -876,6 +877,7 @@ public static void generateReactionAndTriggerStructs( var outputsOfContainedReactors = new LinkedHashMap(); var startupReactions = new LinkedHashSet(); var shutdownReactions = new LinkedHashSet(); + var resetReactions = new LinkedHashSet(); for (Reaction reaction : ASTUtils.allReactions(reactor)) { if (currentFederate.contains(reaction)) { // Create the reaction_t struct. @@ -895,12 +897,18 @@ public static void generateReactionAndTriggerStructs( if (triggerAsVarRef.getContainer() != null) { outputsOfContainedReactors.put(triggerAsVarRef.getVariable(), triggerAsVarRef.getContainer()); } - } - if (trigger.isStartup()) { - startupReactions.add(reactionCount); - } - if (trigger.isShutdown()) { - shutdownReactions.add(reactionCount); + } else if (trigger instanceof BuiltinTriggerRef) { + switch(((BuiltinTriggerRef) trigger).getType()) { + case STARTUP: + startupReactions.add(reactionCount); + break; + case SHUTDOWN: + shutdownReactions.add(reactionCount); + break; + case RESET: + resetReactions.add(reactionCount); + break; + } } } // Create the set of sources read but not triggering. @@ -966,45 +974,15 @@ public static void generateReactionAndTriggerStructs( } } - // Handle startup triggers. + // Handle builtin triggers. if (startupReactions.size() > 0) { - body.pr(String.join("\n", - "trigger_t _lf__startup;", - "reaction_t* _lf__startup_reactions["+startupReactions.size()+"];" - )); - if (isFederatedAndDecentralized) { - constructorCode.pr("self->_lf__startup.intended_tag = (tag_t) { .time = NEVER, .microstep = 0u};"); - } - var i = 0; - for (Integer reactionIndex : startupReactions) { - constructorCode.pr("self->_lf__startup_reactions["+i+++"] = &self->_lf__reaction_"+reactionIndex+";"); - } - constructorCode.pr(String.join("\n", - "self->_lf__startup.last = NULL;", - "self->_lf__startup.reactions = &self->_lf__startup_reactions[0];", - "self->_lf__startup.number_of_reactions = "+startupReactions.size()+";", - "self->_lf__startup.is_timer = false;" - )); + generateBuiltinTriggerdReactionsArray(startupReactions, "startup", body, constructorCode, isFederatedAndDecentralized); } - // Handle shutdown triggers. if (shutdownReactions.size() > 0) { - body.pr(String.join("\n", - "trigger_t _lf__shutdown;", - "reaction_t* _lf__shutdown_reactions["+shutdownReactions.size()+"];" - )); - if (isFederatedAndDecentralized) { - constructorCode.pr("self->_lf__shutdown.intended_tag = (tag_t) { .time = NEVER, .microstep = 0u};"); - } - var i = 0; - for (Integer reactionIndex : shutdownReactions) { - constructorCode.pr("self->_lf__shutdown_reactions["+i+++"] = &self->_lf__reaction_"+reactionIndex+";"); - } - constructorCode.pr(String.join("\n", - "self->_lf__shutdown.last = NULL;", - "self->_lf__shutdown.reactions = &self->_lf__shutdown_reactions[0];", - "self->_lf__shutdown.number_of_reactions = "+shutdownReactions.size()+";", - "self->_lf__shutdown.is_timer = false;" - )); + generateBuiltinTriggerdReactionsArray(shutdownReactions, "shutdown", body, constructorCode, isFederatedAndDecentralized); + } + if (resetReactions.size() > 0) { + generateBuiltinTriggerdReactionsArray(resetReactions, "reset", body, constructorCode, isFederatedAndDecentralized); } // Next handle actions. @@ -1109,59 +1087,147 @@ private static void createTriggerT( } } - public static String generateShutdownTriggersTable(int shutdownReactionCount) { + public static void generateBuiltinTriggerdReactionsArray( + Set reactions, + String name, + CodeBuilder body, + CodeBuilder constructorCode, + boolean isFederatedAndDecentralized + ) { + body.pr(String.join("\n", + "trigger_t _lf__"+name+";", + "reaction_t* _lf__"+name+"_reactions["+reactions.size()+"];" + )); + if (isFederatedAndDecentralized) { + constructorCode.pr("self->_lf__"+name+".intended_tag = (tag_t) { .time = NEVER, .microstep = 0u};"); + } + var i = 0; + for (Integer reactionIndex : reactions) { + constructorCode.pr("self->_lf__"+name+"_reactions["+i+++"] = &self->_lf__reaction_"+reactionIndex+";"); + } + constructorCode.pr(String.join("\n", + "self->_lf__"+name+".last = NULL;", + "self->_lf__"+name+".reactions = &self->_lf__"+name+"_reactions[0];", + "self->_lf__"+name+".number_of_reactions = "+reactions.size()+";", + "self->_lf__"+name+".is_timer = false;" + )); + } + + public static String generateBuiltinTriggersTable(int reactionCount, String name) { return String.join("\n", List.of( - "// Array of pointers to shutdown triggers.", - (shutdownReactionCount > 0 ? - "reaction_t* _lf_shutdown_reactions["+shutdownReactionCount+"]" : - "reaction_t** _lf_shutdown_reactions = NULL") + ";", - "int _lf_shutdown_reactions_size = "+shutdownReactionCount+";" - )); + "// Array of pointers to "+name+" triggers.", + (reactionCount > 0 ? + "reaction_t* _lf_"+name+"_reactions["+reactionCount+"]" : + "reaction_t** _lf_"+name+"_reactions = NULL") + ";", + "int _lf_"+name+"_reactions_size = "+reactionCount+";" + )); } /** * Generate the _lf_trigger_startup_reactions function. */ - public static String generateLfTriggerStartupReactions(int startupReactionCount) { - return String.join("\n", - "void _lf_trigger_startup_reactions() {", - (startupReactionCount > 0 ? - String.join("\n", - " for (int i = 0; i < _lf_startup_reactions_size; i++) {", - " if (_lf_startup_reactions[i] != NULL) {", - " #ifdef MODAL_REACTORS", - " if (!_lf_mode_is_active(_lf_startup_reactions[i]->mode)) {", - " // Mode is not active. Remember to trigger startup when the mode", - " // becomes active.", - " _lf_startup_reactions[i]->mode->should_trigger_startup = true;", - " continue;", - " }", - " #endif", - " _lf_trigger_reaction(_lf_startup_reactions[i], -1);", - " }", - " }" - ) : - ""), - "}" - ); + public static String generateLfTriggerStartupReactions(int startupReactionCount, boolean hasModalReactors) { + var s = new StringBuilder(); + s.append("void _lf_trigger_startup_reactions() {"); + if (startupReactionCount > 0) { + s.append("\n"); + if (hasModalReactors) { + s.append(String.join("\n", + " for (int i = 0; i < _lf_startup_reactions_size; i++) {", + " if (_lf_startup_reactions[i] != NULL) {", + " if (_lf_startup_reactions[i]->mode != NULL) {", + " // Skip reactions in modes", + " continue;", + " }", + " _lf_trigger_reaction(_lf_startup_reactions[i], -1);", + " }", + " }", + " _lf_handle_mode_startup_reset_reactions(", + " _lf_startup_reactions, _lf_startup_reactions_size,", + " NULL, 0,", + " _lf_modal_reactor_states, _lf_modal_reactor_states_size);" + )); + } else { + s.append(String.join("\n", + " for (int i = 0; i < _lf_startup_reactions_size; i++) {", + " if (_lf_startup_reactions[i] != NULL) {", + " _lf_trigger_reaction(_lf_startup_reactions[i], -1);", + " }", + " }" + )); + } + s.append("\n"); + } + s.append("}\n"); + return s.toString(); } - public static String generateLfTriggerShutdownReactions(int shutdownReactionCount) { - return String.join("\n", - "bool _lf_trigger_shutdown_reactions() {", - (shutdownReactionCount > 0 ? - String.join("\n", - " for (int i = 0; i < _lf_shutdown_reactions_size; i++) {", - " if (_lf_shutdown_reactions[i] != NULL) {", - " _lf_trigger_reaction(_lf_shutdown_reactions[i], -1);", - " }", - " }" - ) : - ""), - " // Return true if there are shutdown reactions.", - " return (_lf_shutdown_reactions_size > 0);", - "}" - ); + /** + * Generate the _lf_trigger_shutdown_reactions function. + */ + public static String generateLfTriggerShutdownReactions(int shutdownReactionCount, boolean hasModalReactors) { + var s = new StringBuilder(); + s.append("bool _lf_trigger_shutdown_reactions() {\n"); + if (shutdownReactionCount > 0) { + if (hasModalReactors) { + s.append(String.join("\n", + " for (int i = 0; i < _lf_shutdown_reactions_size; i++) {", + " if (_lf_shutdown_reactions[i] != NULL) {", + " if (_lf_shutdown_reactions[i]->mode != NULL) {", + " // Skip reactions in modes", + " continue;", + " }", + " _lf_trigger_reaction(_lf_shutdown_reactions[i], -1);", + " }", + " }", + " _lf_handle_mode_shutdown_reactions(_lf_shutdown_reactions, _lf_shutdown_reactions_size);", + " return true;" + )); + } else { + s.append(String.join("\n", + " for (int i = 0; i < _lf_shutdown_reactions_size; i++) {", + " if (_lf_shutdown_reactions[i] != NULL) {", + " _lf_trigger_reaction(_lf_shutdown_reactions[i], -1);", + " }", + " }", + " return true;" + )); + } + s.append("\n"); + } else { + s.append(" return false;\n"); + } + s.append("}\n"); + return s.toString(); + } + + /** + * Generate the _lf_handle_mode_triggered_reactions function. + */ + public static String generateLfModeTriggeredReactions( + int startupReactionCount, + int resetReactionCount, + boolean hasModalReactors + ) { + if (!hasModalReactors) { + return ""; + } + var s = new StringBuilder(); + s.append("void _lf_handle_mode_triggered_reactions() {\n"); + s.append(" _lf_handle_mode_startup_reset_reactions(\n"); + if (startupReactionCount > 0) { + s.append(" _lf_startup_reactions, _lf_startup_reactions_size,\n"); + } else { + s.append(" NULL, 0,\n"); + } + if (resetReactionCount > 0) { + s.append(" _lf_reset_reactions, _lf_reset_reactions_size,\n"); + } else { + s.append(" NULL, 0,\n"); + } + s.append(" _lf_modal_reactor_states, _lf_modal_reactor_states_size);\n"); + s.append("}\n"); + return s.toString(); } /** Generate a reaction function definition for a reactor. diff --git a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java index a22628a388..8334797889 100644 --- a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java @@ -102,10 +102,6 @@ public static String generateInitializeTriggerObjects( // Allocate the memory for triggers used in federated execution code.pr(CGeneratorExtension.allocateTriggersForFederate(federate, startTimeStepIsPresentCount, isFederated, isFederatedAndDecentralized)); - code.pr(String.join("\n", - "_lf_startup_reactions = (reaction_t**)calloc(" + startupReactionCount + ", sizeof(reaction_t*));", - "_lf_startup_reactions_size = " + startupReactionCount + ";" - )); code.pr(initializeTriggerObjects.toString()); // Assign appropriate pointers to the triggers diff --git a/org.lflang/src/org/lflang/generator/python/PythonModeGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonModeGenerator.java index dc465c357b..4597f42f6d 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonModeGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonModeGenerator.java @@ -4,6 +4,8 @@ import org.eclipse.emf.ecore.util.EcoreUtil; import org.lflang.generator.CodeBuilder; +import org.lflang.lf.BuiltinTrigger; +import org.lflang.lf.BuiltinTriggerRef; import org.lflang.lf.LfFactory; import org.lflang.lf.Mode; import org.lflang.lf.Reaction; @@ -43,8 +45,8 @@ public static void generateStartupReactionsInModesIfNeeded(List reactor private static void generateStartupReactionsInReactor(Reactor reactor) { // Create a reaction with a startup trigger - TriggerRef startupTrigger = LfFactory.eINSTANCE.createTriggerRef(); - startupTrigger.setStartup(true); + BuiltinTriggerRef startupTrigger = LfFactory.eINSTANCE.createBuiltinTriggerRef(); + startupTrigger.setType(BuiltinTrigger.STARTUP); Reaction baseReaction = LfFactory.eINSTANCE.createReaction(); baseReaction.getTriggers().add(startupTrigger); diff --git a/org.lflang/src/org/lflang/validation/LFValidator.java b/org.lflang/src/org/lflang/validation/LFValidator.java index 1a45cdc141..204c477079 100644 --- a/org.lflang/src/org/lflang/validation/LFValidator.java +++ b/org.lflang/src/org/lflang/validation/LFValidator.java @@ -61,7 +61,6 @@ import org.lflang.TargetProperty; import org.lflang.TimeValue; import org.lflang.federated.serialization.SupportedSerializers; -import org.lflang.generator.ModeInstance.ModeTransitionType; import org.lflang.generator.NamedInstance; import org.lflang.lf.Action; import org.lflang.lf.ActionOrigin; @@ -1254,14 +1253,6 @@ public void checkWidthSpec(WidthSpec widthSpec) { } } } - - @Check(CheckType.FAST) - public void checkModeModifier(VarRef ref) { - if (ref.getVariable() instanceof Mode && ref.getModifier() != null && !ModeTransitionType.KEYWORDS.contains(ref.getModifier())) { - error(String.format("Illegal mode transition modifier! Only %s is allowed.", - String.join("/", ModeTransitionType.KEYWORDS)), Literals.VAR_REF__MODIFIER); - } - } @Check(CheckType.FAST) public void checkInitialMode(Reactor reactor) { diff --git a/test/C/src/modal_models/ModalStartup.lf b/test/C/src/modal_models/ModalStartup.lf deleted file mode 100644 index 5ea7f31567..0000000000 --- a/test/C/src/modal_models/ModalStartup.lf +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Modal Reactor Test. - * Test startup reactions in modes. - */ -target C { - fast: false, - timeout: 4100 msec -}; - -import TraceTesting from "util/TraceTesting.lf" - -reactor Modal { - input next:bool - - output mode_switch:int - output startup1:int - output startup2:int - output startup3:int - - initial mode One { - reaction(startup) -> startup1 {= - printf("Startup 1 at (%lld, %u).\n", lf_time_logical_elapsed(), lf_tag().microstep); - lf_set(startup1, 1); - =} - - reaction(next) -> reset(Two), mode_switch {= - printf("Transitioning to mode Two (reset)\n"); - lf_set(mode_switch, 1); - lf_set_mode(Two); - =} - } - mode Two { - reaction(startup) -> startup2 {= - printf("Startup 2 at (%lld, %u).\n", lf_time_logical_elapsed(), lf_tag().microstep); - lf_set(startup2, 1); - =} - - reaction(next) -> continue(Three), mode_switch {= - printf("Transitioning to mode Three (continue)\n"); - lf_set(mode_switch, 1); - lf_set_mode(Three); - =} - } - - mode Three { - reaction(startup) -> startup3 {= - printf("Startup 3 at (%lld, %u).\n", lf_time_logical_elapsed(), lf_tag().microstep); - lf_set(startup3, 1); - =} - - reaction(next) -> continue(One), mode_switch {= - printf("Transitioning to mode One (continue)\n"); - lf_set(mode_switch, 1); - lf_set_mode(One); - =} - } -} - -main reactor { - timer stepper(1sec, 1sec) - - modal = new Modal() - test = new TraceTesting( - events_size = 4, - trace_size = 72, - trace = ( - 0,0,0,1,1,0,0,0,0, - 1000000000,1,1,0,1, - 0,0,0,0,0,0,1,0,1,1, - 1,0,0,1000000000,1, - 1,0,1,0,1,0,0,0,0,1, - 0,1,0,1,1,1,1000000000, - 1,1,0,1,0,1,0,1, - 1000000000,1,1,0,1,0, - 1,0,1,0,0,1,0,1,1,1,0,1 - ), - training = false - ) - - // Trigger mode change - reaction(stepper) -> modal.next {= - lf_set(modal.next, true); - =} - - modal.mode_switch, - modal.startup1, - modal.startup2, - modal.startup3 - -> test.events -} diff --git a/test/C/src/modal_models/ModalStartupShutdown.lf b/test/C/src/modal_models/ModalStartupShutdown.lf new file mode 100644 index 0000000000..73d6a65604 --- /dev/null +++ b/test/C/src/modal_models/ModalStartupShutdown.lf @@ -0,0 +1,152 @@ +/* + * Modal Reactor Test. + * Test startup/shutdown reactions in modes. + */ +target C { + fast: false, + timeout: 3000 msec +}; + +import TraceTesting from "util/TraceTesting.lf" + +reactor Modal { + input next:bool + + output mode_switch:int + output startup1:int + output startup2:int + output shutdown2:int + output shutdown3:int + output startup4:int + output reset4:int + output shutdown4:int + output startup5:int + output reset5:int + output shutdown5:int + + initial mode One { + reaction(startup) -> startup1 {= + printf("Startup 1 at (%lld, %u).\n", lf_time_logical_elapsed(), lf_tag().microstep); + lf_set(startup1, 1); + =} + + reaction(next) -> Two, mode_switch {= + printf("Transitioning to mode 1\n"); + lf_set(mode_switch, 2); + lf_set_mode(Two); + =} + } + mode Two { + reaction(startup) -> startup2 {= + printf("Startup 2 at (%lld, %u).\n", lf_time_logical_elapsed(), lf_tag().microstep); + lf_set(startup2, 1); + =} + + reaction(next) -> Three, mode_switch {= + printf("Transitioning to mode 3\n"); + lf_set(mode_switch, 3); + lf_set_mode(Three); + =} + + reaction(shutdown) -> shutdown2 {= + printf("Shutdown 2 at (%lld, %u).\n", lf_time_logical_elapsed(), lf_tag().microstep); + lf_set(shutdown2, 1); + =} + } + + mode Three { + reaction(next) -> Four, mode_switch {= + printf("Transitioning to mode 4\n"); + lf_set(mode_switch, 4); + lf_set_mode(Four); + =} + + reaction(shutdown) -> shutdown3 {= + printf("Shutdown 3 at (%lld, %u).\n", lf_time_logical_elapsed(), lf_tag().microstep); + lf_set(shutdown3, 1); + =} + } + + mode Four { + reaction(startup) -> startup4 {= + printf("Startup 4 at (%lld, %u).\n", lf_time_logical_elapsed(), lf_tag().microstep); + lf_set(startup4, 1); + =} + + reaction(reset) -> reset4 {= + printf("Reset 4 at (%lld, %u).\n", lf_time_logical_elapsed(), lf_tag().microstep); + lf_set(reset4, 1); + =} + + reaction(next) -> Four, mode_switch {= + printf("Transitioning to mode 4\n"); + lf_set(mode_switch, 4); + lf_set_mode(Four); + =} + + reaction(shutdown) -> shutdown4 {= + printf("Shutdown 4 at (%lld, %u).\n", lf_time_logical_elapsed(), lf_tag().microstep); + lf_set(shutdown4, 1); + =} + } + + // Unreachable! + mode Five { + reaction(startup) -> startup5 {= + printf("Startup 5 at (%lld, %u).\n", lf_time_logical_elapsed(), lf_tag().microstep); + lf_set(startup5, 1); + =} + + reaction(reset) -> reset5 {= + printf("Reset 5 at (%lld, %u).\n", lf_time_logical_elapsed(), lf_tag().microstep); + lf_set(reset5, 1); + =} + + reaction(shutdown) -> shutdown5 {= + printf("Shutdown 5 at (%lld, %u).\n", lf_time_logical_elapsed(), lf_tag().microstep); + lf_set(shutdown5, 1); + =} + } +} + +main reactor { + timer stepper(500msec, 500msec) + + modal = new Modal() + test = new TraceTesting( + events_size = 11, + trace_size = 253, + trace = ( + 0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 500000000,1,2,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,2,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 500000000,1,3,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 500000000,1,4,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,4,0,1,0,1,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0, + 500000000,1,4,0,1,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0, + 0,0,4,0,1,0,1,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0, + 500000000,1,4,0,1,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0, + 0,0,4,0,1,0,1,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0, + 500000000,1,4,0,1,0,1,1,1,1,1,0,1,0,1,1,1,0,0,0,0,0,0 + ), + training = false + ) + + // Trigger mode change + reaction(stepper) -> modal.next {= + lf_set(modal.next, true); + =} + + modal.mode_switch, + modal.startup1, + modal.startup2, + modal.shutdown2, + modal.shutdown3, + modal.startup4, + modal.reset4, + modal.shutdown4, + modal.startup5, + modal.reset5, + modal.shutdown5 + -> test.events +} diff --git a/test/C/src/modal_models/ModalStateReset.lf b/test/C/src/modal_models/ModalStateReset.lf index ecc7847229..7449eb2b58 100644 --- a/test/C/src/modal_models/ModalStateReset.lf +++ b/test/C/src/modal_models/ModalStateReset.lf @@ -28,6 +28,10 @@ reactor Modal { state counter1:int(0); timer T1(0msec, 250msec); + reaction(reset) {= + self->counter1 = 0; + =} + reaction(T1) -> count1 {= printf("Counter1: %d\n", self->counter1); lf_set(count1, self->counter1++); @@ -43,6 +47,10 @@ reactor Modal { state counter2:int(-2); timer T2(0msec, 250msec); + reaction(reset) {= + self->counter2 = -2; + =} + reaction(T2) -> count2 {= printf("Counter2: %d\n", self->counter2); lf_set(count2, self->counter2++); From 1c7ebbc9cd66e4aaf8b6ae130eb9685760b17c7b Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Fri, 13 May 2022 15:48:03 +0200 Subject: [PATCH 09/56] modes: Adjusted Kotlin files to new trigger API --- org.lflang/src/org/lflang/AstExtensions.kt | 3 +-- .../org/lflang/generator/cpp/CppActionGenerator.kt | 5 +++-- .../src/org/lflang/generator/cpp/CppExtensions.kt | 7 +++---- .../org/lflang/generator/cpp/CppReactionGenerator.kt | 4 ++-- .../src/org/lflang/generator/rust/RustModel.kt | 4 ++-- .../org/lflang/generator/ts/TSReactionGenerator.kt | 12 +++++++----- 6 files changed, 18 insertions(+), 17 deletions(-) diff --git a/org.lflang/src/org/lflang/AstExtensions.kt b/org.lflang/src/org/lflang/AstExtensions.kt index 8549818c81..93faf7bdfe 100644 --- a/org.lflang/src/org/lflang/AstExtensions.kt +++ b/org.lflang/src/org/lflang/AstExtensions.kt @@ -177,8 +177,7 @@ fun TriggerRef.toText(): String = when { this is VarRef && container != null -> "${container.name}.${variable.name}" this is VarRef -> variable.name - isStartup -> "startup" - isShutdown -> "shutdown" + this is BuiltinTriggerRef -> type.literal else -> throw UnsupportedOperationException("What's this ref: $this") } diff --git a/org.lflang/src/org/lflang/generator/cpp/CppActionGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppActionGenerator.kt index 80cebc30a1..d51a9e51e2 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppActionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppActionGenerator.kt @@ -31,6 +31,7 @@ import org.lflang.isLogical import org.lflang.lf.Action import org.lflang.lf.LfPackage import org.lflang.lf.Reactor +import org.lflang.lf.BuiltinTrigger /** A C++ code generator for actions */ class CppActionGenerator(private val reactor: Reactor, private val errorReporter: ErrorReporter) { @@ -43,8 +44,8 @@ class CppActionGenerator(private val reactor: Reactor, private val errorReporter else "reactor::PhysicalAction<$dataType>" } - val startupName: String = LfPackage.Literals.TRIGGER_REF__STARTUP.name - val shutdownName: String = LfPackage.Literals.TRIGGER_REF__SHUTDOWN.name + val startupName: String = BuiltinTrigger.STARTUP.literal + val shutdownName: String = BuiltinTrigger.SHUTDOWN.literal } private fun generateDeclaration(action: Action) = "${action.cppType} ${action.name};" diff --git a/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt b/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt index 31eb1356af..57b9e555a2 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt @@ -110,10 +110,9 @@ val VarRef.name: String /** Get a C++ code representation of the given trigger */ val TriggerRef.name: String get() = when { - this is VarRef -> this.name - this.isShutdown -> LfPackage.Literals.TRIGGER_REF__SHUTDOWN.name - this.isStartup -> LfPackage.Literals.TRIGGER_REF__STARTUP.name - else -> unreachable() + this is VarRef -> this.name + this is BuiltinTriggerRef -> type.literal + else -> unreachable() } /** Return a comment to be inserted at the top of generated files. */ diff --git a/org.lflang/src/org/lflang/generator/cpp/CppReactionGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppReactionGenerator.kt index f418255c35..87d469f4cc 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppReactionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppReactionGenerator.kt @@ -71,8 +71,8 @@ class CppReactionGenerator( private val TriggerRef.cppType get() = when { - this.isStartup -> "reactor::StartupAction" - this.isShutdown -> "reactor::ShutdownAction" + this is BuiltinTriggerRef && this.type == BuiltinTrigger.STARTUP -> "reactor::StartupAction" + this is BuiltinTriggerRef && this.type == BuiltinTrigger.SHUTDOWN -> "reactor::ShutdownAction" this is VarRef -> cppType else -> AssertionError("Unexpected trigger type") } diff --git a/org.lflang/src/org/lflang/generator/rust/RustModel.kt b/org.lflang/src/org/lflang/generator/rust/RustModel.kt index 12de9accfb..77d106a96d 100644 --- a/org.lflang/src/org/lflang/generator/rust/RustModel.kt +++ b/org.lflang/src/org/lflang/generator/rust/RustModel.kt @@ -551,8 +551,8 @@ object RustModelBuilder { this[DepKind.Effects] = makeDeps { effects } }, body = n.code.toText(), - isStartup = n.triggers.any { it.isStartup }, - isShutdown = n.triggers.any { it.isShutdown }, + isStartup = n.triggers.any { it is BuiltinTriggerRef && it.type == BuiltinTrigger.STARTUP }, + isShutdown = n.triggers.any { it is BuiltinTriggerRef && it.type == BuiltinTrigger.SHUTDOWN }, debugLabel = ASTUtils.label(n), loc = n.locationInfo().let { // remove code block diff --git a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt index 71b3294462..b072df9cd9 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt @@ -110,10 +110,12 @@ class TSReactionGenerator( for (trigger in reaction.triggers) { if (trigger is VarRef) { reactionTriggers.add(trigger.generateVarRef()) - } else if (trigger.isStartup) { - reactionTriggers.add("this.startup") - } else if (trigger.isShutdown) { - reactionTriggers.add("this.shutdown") + } else if (trigger is BuiltinTriggerRef) { + when (trigger.type) { + BuiltinTrigger.STARTUP -> reactionTriggers.add("this.startup") + BuiltinTrigger.SHUTDOWN -> reactionTriggers.add("this.shutdown") + else -> {} + } } } return with(PrependOperator) { @@ -249,7 +251,7 @@ class TSReactionGenerator( // so we can iterate over their union val triggersUnionSources = HashSet() for (trigger in reaction.triggers) { - if (!(trigger.isStartup || trigger.isShutdown)) { + if (!(trigger is BuiltinTriggerRef)) { triggersUnionSources.add(trigger as VarRef) } } From 341a8c4c0e4f2d757da54fb2df784d027d7fbb85 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Mon, 16 May 2022 19:08:06 +0200 Subject: [PATCH 10/56] modes: Added support for enlisting state variables for automatic reset --- org.lflang/src/org/lflang/LinguaFranca.xtext | 2 +- .../org/lflang/generator/c/CGenerator.java | 8 +- .../org/lflang/validation/LFValidator.java | 70 +++++++++++++ .../C/src/modal_models/ModalStateResetAuto.lf | 98 +++++++++++++++++++ .../MultipleOutputFeeder_2Connections.lf | 2 +- ...ultipleOutputFeeder_ReactionConnections.lf | 2 +- 6 files changed, 175 insertions(+), 7 deletions(-) create mode 100644 test/C/src/modal_models/ModalStateResetAuto.lf diff --git a/org.lflang/src/org/lflang/LinguaFranca.xtext b/org.lflang/src/org/lflang/LinguaFranca.xtext index 57074e2a67..3ea25f31cc 100644 --- a/org.lflang/src/org/lflang/LinguaFranca.xtext +++ b/org.lflang/src/org/lflang/LinguaFranca.xtext @@ -134,7 +134,7 @@ TargetDecl: ((parens+='(' (init+=Value (',' init+=Value)*)? parens+=')') | (braces+='{' (init+=Value (',' init+=Value)*)? braces+='}') )? - ) ';'? + ) (reset?='reset')? ';'? ; Method: diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.java b/org.lflang/src/org/lflang/generator/c/CGenerator.java index f55321dda8..4483e9e6ea 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.java @@ -2048,10 +2048,10 @@ public void generateStateVariableInitializations(ReactorInstance instance) { instance.lookupModeInstance((Mode) stateVar.eContainer()) : instance.getMode(false); // In the current concept state variables are not automatically reset. - // Instead they need to be manually reset using a reset triggered reaction. - // Yet, the code to generate an automatic reset is kept present but inactive to prepare for changes to this - // semantics and allow e.g. for a convenience annotation that enables reset for a specific variable/type. - mode = null; + // Instead they need to be manually reset using a reset triggered reaction or marked as reset. + if (!stateVar.isReset()) { + mode = null; // Treat as if outside of mode + } initializeTriggerObjects.pr(CStateGenerator.generateInitializer( instance, selfRef, diff --git a/org.lflang/src/org/lflang/validation/LFValidator.java b/org.lflang/src/org/lflang/validation/LFValidator.java index 204c477079..6a7c1968ea 100644 --- a/org.lflang/src/org/lflang/validation/LFValidator.java +++ b/org.lflang/src/org/lflang/validation/LFValidator.java @@ -41,6 +41,7 @@ import java.util.Arrays; import java.util.HashSet; import java.util.LinkedHashSet; +import java.util.LinkedList; import java.util.List; import java.util.Optional; import java.util.Set; @@ -65,6 +66,8 @@ import org.lflang.lf.Action; import org.lflang.lf.ActionOrigin; import org.lflang.lf.Assignment; +import org.lflang.lf.BuiltinTrigger; +import org.lflang.lf.BuiltinTriggerRef; import org.lflang.lf.Connection; import org.lflang.lf.Deadline; import org.lflang.lf.Host; @@ -78,6 +81,7 @@ import org.lflang.lf.KeyValuePairs; import org.lflang.lf.LfPackage.Literals; import org.lflang.lf.Mode; +import org.lflang.lf.ModeTransition; import org.lflang.lf.Model; import org.lflang.lf.NamedHost; import org.lflang.lf.Output; @@ -1337,6 +1341,72 @@ public void checkModeInstanceNamespace(Reactor reactor) { } } + @Check(CheckType.FAST) + public void checkMissingStateResetInMode(Reactor reactor) { + if (!reactor.getModes().isEmpty()) { + var resetModes = new HashSet(); + // Collect all modes that may be reset + for (var m : reactor.getModes()) { + for (var r : m.getReactions()) { + for (var e : r.getEffects()) { + if (e.getVariable() instanceof Mode && e.getTransition() != ModeTransition.HISTORY) { + resetModes.add((Mode) e.getVariable()); + } + } + } + } + for (var m : resetModes) { + // Check state variables in this mode + if (!m.getStateVars().isEmpty()) { + var hasResetReaction = m.getReactions().stream().anyMatch( + r -> r.getTriggers().stream().anyMatch( + t -> (t instanceof BuiltinTriggerRef && + ((BuiltinTriggerRef) t).getType() == BuiltinTrigger.RESET))); + if (!hasResetReaction) { + for (var s : m.getStateVars()) { + if (!s.isReset()) { + error("State variable is not reset upon mode entry. It is neither marked for automatic reset nor is there a reset reaction.", + m, Literals.MODE__STATE_VARS, m.getStateVars().indexOf(s)); + } + } + } + } + // Check state variables in instantiated reactors + if (!m.getInstantiations().isEmpty()) { + for (var i : m.getInstantiations()) { + var checked = new HashSet(); + var toCheck = new LinkedList(); + toCheck.add((Reactor) i.getReactorClass()); + while (!toCheck.isEmpty()) { + var check = toCheck.pop(); + checked.add(check); + if (!check.getStateVars().isEmpty()) { + var hasResetReaction = check.getReactions().stream().anyMatch( + r -> r.getTriggers().stream().anyMatch( + t -> (t instanceof BuiltinTriggerRef && + ((BuiltinTriggerRef) t).getType() == BuiltinTrigger.RESET))); + if (!hasResetReaction && check.getStateVars().stream().anyMatch(s -> !s.isReset())) { + error("This reactor contains state variables that are not reset upon mode entry. " + + "The instatiated reactor (or any inner reactor) neither marks its state variables for automatic reset nor defines a reset reaction. " + + "It is usafe to instatiate this reactor inside a mode.", + m, Literals.MODE__INSTANTIATIONS, m.getStateVars().indexOf(i)); + break; + } + } + // continue with inner + for (var innerInstance : check.getInstantiations()) { + var next = (Reactor) innerInstance.getReactorClass(); + if (!checked.contains(next)) { + toCheck.push(next); + } + } + } + } + } + } + } + } + ////////////////////////////////////////////////////////////// //// Public methods. diff --git a/test/C/src/modal_models/ModalStateResetAuto.lf b/test/C/src/modal_models/ModalStateResetAuto.lf new file mode 100644 index 0000000000..ab10c79d52 --- /dev/null +++ b/test/C/src/modal_models/ModalStateResetAuto.lf @@ -0,0 +1,98 @@ +/* + * Modal Reactor Test. + * Tests reset of state variables in modes. + */ +target C { + fast: false, + timeout: 4 sec +}; + +import TraceTesting from "util/TraceTesting.lf" + +reactor Modal { + input next:bool; + + output mode_switch:int + output count0:int; + output count1:int; + output count2:int; + + state counter0:int(0); + + reaction(next) -> count0 {= + printf("Counter0: %d\n", self->counter0); + lf_set(count0, self->counter0++); + =} + + initial mode One { + state counter1:int(0); + timer T1(0msec, 250msec); + + reaction(T1) -> count1 {= + printf("Counter1: %d\n", self->counter1); + lf_set(count1, self->counter1++); + =} + + reaction(next) -> reset(Two), mode_switch {= + printf("Transitioning to mode Two (reset)\n"); + lf_set(mode_switch, 1); + lf_set_mode(Two); + =} + } + mode Two { + state counter2:int(-2) reset; + timer T2(0msec, 250msec); + + reaction(T2) -> count2 {= + printf("Counter2: %d\n", self->counter2); + lf_set(count2, self->counter2++); + =} + + reaction(next) -> continue(One), mode_switch {= + printf("Transitioning to mode One (continue)\n"); + lf_set(mode_switch, 1); + lf_set_mode(One); + =} + } +} + +main reactor { + timer stepper(1sec, 1sec) + + modal = new Modal() + test = new TraceTesting( + events_size = 4, + trace_size = 171, + trace = ( + 0,0,0,0,0,1,0,0,0, + 250000000,0,0,0,0,1,1,0,0, + 250000000,0,0,0,0,1,2,0,0, + 250000000,0,0,0,0,1,3,0,0, + 250000000,1,1,1,0,1,4,0,0, + 0,0,1,0,0,0,4,1,-2, + 250000000,0,1,0,0,0,4,1,-1, + 250000000,0,1,0,0,0,4,1,0, + 250000000,0,1,0,0,0,4,1,1, + 250000000,1,1,1,1,0,4,1,2, + 250000000,0,1,0,1,1,5,0,2, + 250000000,0,1,0,1,1,6,0,2, + 250000000,0,1,0,1,1,7,0,2, + 250000000,1,1,1,2,1,8,0,2, + 0,0,1,0,2,0,8,1,-2, + 250000000,0,1,0,2,0,8,1,-1, + 250000000,0,1,0,2,0,8,1,0, + 250000000,0,1,0,2,0,8,1,1, + 250000000,1,1,1,3,0,8,1,2 + ), training = false) + + // Trigger mode change + reaction(stepper) -> modal.next {= + lf_set(modal.next, true); + =} + + modal.mode_switch, + modal.count0, + modal.count1, + modal.count2 + -> test.events +} \ No newline at end of file diff --git a/test/C/src/modal_models/MultipleOutputFeeder_2Connections.lf b/test/C/src/modal_models/MultipleOutputFeeder_2Connections.lf index 2fea77b1cc..366b461c48 100644 --- a/test/C/src/modal_models/MultipleOutputFeeder_2Connections.lf +++ b/test/C/src/modal_models/MultipleOutputFeeder_2Connections.lf @@ -36,7 +36,7 @@ reactor Counter(period:time(1sec)) { output value:int timer t(0, period) - state curval:int(0) + state curval:int(0) reset reaction(t) -> value {= lf_set(value, self->curval++); diff --git a/test/C/src/modal_models/MultipleOutputFeeder_ReactionConnections.lf b/test/C/src/modal_models/MultipleOutputFeeder_ReactionConnections.lf index 1c182c49cb..e72fd76356 100644 --- a/test/C/src/modal_models/MultipleOutputFeeder_ReactionConnections.lf +++ b/test/C/src/modal_models/MultipleOutputFeeder_ReactionConnections.lf @@ -39,7 +39,7 @@ reactor Counter(period:time(1sec)) { output value:int timer t(0, period) - state curval:int(0) + state curval:int(0) reset reaction(t) -> value {= lf_set(value, self->curval++); From 85ee3319072f9d96d324db64022a48f393f35553 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Thu, 19 May 2022 12:56:38 +0200 Subject: [PATCH 11/56] modes: Fixed state variable reset in python target --- .../org/lflang/generator/GeneratorBase.java | 11 +- .../generator/python/PythonGenerator.java | 4 +- .../generator/python/PythonModeGenerator.java | 31 ++-- test/Python/src/modal_models/ModalStartup.lf | 88 ---------- .../src/modal_models/ModalStartupShutdown.lf | 151 ++++++++++++++++++ .../src/modal_models/ModalStateReset.lf | 8 + .../MultipleOutputFeeder_2Connections.lf | 2 +- ...ultipleOutputFeeder_ReactionConnections.lf | 2 +- 8 files changed, 181 insertions(+), 116 deletions(-) delete mode 100644 test/Python/src/modal_models/ModalStartup.lf create mode 100644 test/Python/src/modal_models/ModalStartupShutdown.lf diff --git a/org.lflang/src/org/lflang/generator/GeneratorBase.java b/org.lflang/src/org/lflang/generator/GeneratorBase.java index 3bb1524aeb..b67324145a 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorBase.java +++ b/org.lflang/src/org/lflang/generator/GeneratorBase.java @@ -373,7 +373,7 @@ public void doGenerate(Resource resource, LFGeneratorContext context) { // Check for existence and support of modes hasModalReactors = IterableExtensions.exists(reactors, it -> !it.getModes().isEmpty()); checkModalReactorSupport(false); - generateStartupReactionsInModesIfNeeded(); + additionalPostProcessingForModes(); enableSupportForSerializationIfApplicable(context.getCancelIndicator()); } @@ -638,14 +638,9 @@ protected String getConflictingConnectionsInModalReactorsBody(String source, Str } /** - * Generate startup reactions in modes. - * - * Startup reactions (reactions that have startup in their list of triggers) - * will be triggered when the mode is entered for the first time and on each subsequent - * reset transition to that mode. These reactions could be useful for targets - * to perform cleanups, for example, to reset state variables. + * Hook for additional post-processing of the model. */ - protected void generateStartupReactionsInModesIfNeeded() { + protected void additionalPostProcessingForModes() { // Do nothing } diff --git a/org.lflang/src/org/lflang/generator/python/PythonGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonGenerator.java index 9c9f048121..85de11124c 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonGenerator.java @@ -852,11 +852,11 @@ protected void setUpGeneralParameters() { } @Override - protected void generateStartupReactionsInModesIfNeeded() { + protected void additionalPostProcessingForModes() { if (!hasModalReactors) { return; } - PythonModeGenerator.generateStartupReactionsInModesIfNeeded(reactors); + PythonModeGenerator.generateResetReactionsIfNeeded(reactors); } /** diff --git a/org.lflang/src/org/lflang/generator/python/PythonModeGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonModeGenerator.java index 026963abbc..c5e559e32e 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonModeGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonModeGenerator.java @@ -20,23 +20,18 @@ */ public class PythonModeGenerator { /** - * Generate startup reactions in modes. - * - * Startup reactions (reactions that have startup in their list of triggers) - * will be triggered when the mode is entered for the first time and on each subsequent - * reset transition to that mode. These reactions could be useful for targets - * to perform cleanups, for example, to reset state variables. + * Generate reset reactions in modes to reset state variables. * * @param reactors A list of reactors in the program, some of which could contain modes. */ - public static void generateStartupReactionsInModesIfNeeded(List reactors) { + public static void generateResetReactionsIfNeeded(List reactors) { for (Reactor reactor : reactors) { generateStartupReactionsInReactor(reactor); } } /** - * Generate startup reactions that reset state variables in + * Generate reset reactions that reset state variables in * - the reactor, and, * - the modes within the reactor. * @@ -44,20 +39,22 @@ public static void generateStartupReactionsInModesIfNeeded(List reactor */ private static void generateStartupReactionsInReactor(Reactor reactor) { - // Create a reaction with a startup trigger - BuiltinTriggerRef startupTrigger = LfFactory.eINSTANCE.createBuiltinTriggerRef(); - startupTrigger.setType(BuiltinTrigger.STARTUP); + // Create a reaction with a reset trigger + BuiltinTriggerRef resetTrigger = LfFactory.eINSTANCE.createBuiltinTriggerRef(); + resetTrigger.setType(BuiltinTrigger.RESET); Reaction baseReaction = LfFactory.eINSTANCE.createReaction(); - baseReaction.getTriggers().add(startupTrigger); + baseReaction.getTriggers().add(resetTrigger); - if (!reactor.getStateVars().isEmpty()) { + if (!reactor.getStateVars().isEmpty() && reactor.getStateVars().stream().anyMatch(s -> s.isReset())) { // Create a reaction body that resets all state variables (that are not in a mode) // to their initial value. var reactionBody = LfFactory.eINSTANCE.createCode(); CodeBuilder code = new CodeBuilder(); code.pr("# Reset the following state variables to their initial value."); for (var state: reactor.getStateVars()) { - code.pr("self."+state.getName()+" = "+PythonStateGenerator.generatePythonInitializer(state)); + if (state.isReset()) { + code.pr("self."+state.getName()+" = "+PythonStateGenerator.generatePythonInitializer(state)); + } } reactionBody.setBody(code.toString()); baseReaction.setCode(reactionBody); @@ -69,7 +66,7 @@ private static void generateStartupReactionsInReactor(Reactor reactor) { var reactorModes = reactor.getModes(); if (!reactorModes.isEmpty()) { for (Mode mode : reactorModes) { - if (mode.getStateVars().isEmpty()) { + if (mode.getStateVars().isEmpty() || mode.getStateVars().stream().allMatch(s -> !s.isReset())) { continue; } Reaction reaction = EcoreUtil.copy(baseReaction); @@ -79,7 +76,9 @@ private static void generateStartupReactionsInReactor(Reactor reactor) { CodeBuilder code = new CodeBuilder(); code.pr("# Reset the following state variables to their initial value."); for (var state: mode.getStateVars()) { - code.pr("self."+state.getName()+" = "+PythonStateGenerator.generatePythonInitializer(state)); + if (state.isReset()) { + code.pr("self."+state.getName()+" = "+PythonStateGenerator.generatePythonInitializer(state)); + } } reactionBody.setBody(code.toString()); reaction.setCode(reactionBody); diff --git a/test/Python/src/modal_models/ModalStartup.lf b/test/Python/src/modal_models/ModalStartup.lf deleted file mode 100644 index 99059da840..0000000000 --- a/test/Python/src/modal_models/ModalStartup.lf +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Modal Reactor Test. - * Test startup reactions in modes. - */ -target Python { - fast: false, - timeout: 4100 msec -}; - -import TraceTesting from "util/TraceTesting.lf" - -reactor Modal { - input next - - output mode_switch - output startup1 - output startup2 - output startup3 - - initial mode One { - reaction(startup) -> startup1 {= - print(f"Startup 1 at ({lf.time.logical_elapsed()}, {get_microstep()}).") - startup1.set(1) - =} - - reaction(next) -> reset(Two), mode_switch {= - print("Transitioning to mode Two (reset)") - mode_switch.set(1) - Two.set() - =} - } - mode Two { - reaction(startup) -> startup2 {= - print(f"Startup 2 at at ({lf.time.logical_elapsed()}, {get_microstep()}).") - startup2.set(1) - =} - - reaction(next) -> continue(Three), mode_switch {= - print("Transitioning to mode Three (continue)") - mode_switch.set(1) - Three.set() - =} - } - - mode Three { - reaction(startup) -> startup3 {= - print(f"Startup 3 at at ({lf.time.logical_elapsed()}, {get_microstep()}).") - startup3.set(1) - =} - - reaction(next) -> continue(One), mode_switch {= - print("Transitioning to mode One (continue)") - mode_switch.set(1) - One.set() - =} - } -} - -main reactor { - timer stepper(1sec, 1sec) - - modal = new Modal() - test = new TraceTesting( - events_size = 4, - trace = ( - 0,0,0,1,1,0,0,0,0, - 1000000000,1,1,0,1, - 0,0,0,0,0,0,1,0,1,1, - 1,0,0,1000000000,1, - 1,0,1,0,1,0,0,0,0,1, - 0,1,0,1,1,1,1000000000, - 1,1,0,1,0,1,0,1, - 1000000000,1,1,0,1,0, - 1,0,1,0,0,1,0,1,1,1,0,1 - ), training = False - ) - - // Trigger mode change - reaction(stepper) -> modal.next {= - modal.next.set(True) - =} - - modal.mode_switch, - modal.startup1, - modal.startup2, - modal.startup3 - -> test.events -} diff --git a/test/Python/src/modal_models/ModalStartupShutdown.lf b/test/Python/src/modal_models/ModalStartupShutdown.lf new file mode 100644 index 0000000000..18508446e8 --- /dev/null +++ b/test/Python/src/modal_models/ModalStartupShutdown.lf @@ -0,0 +1,151 @@ +/* + * Modal Reactor Test. + * Test startup/shutdown reactions in modes. + */ +target Python { + fast: false, + timeout: 3000 msec +}; + +import TraceTesting from "util/TraceTesting.lf" + +reactor Modal { + input next + + output mode_switch + output startup1 + output startup2 + output shutdown2 + output shutdown3 + output startup4 + output reset4 + output shutdown4 + output startup5 + output reset5 + output shutdown5 + + initial mode One { + reaction(startup) -> startup1 {= + print(f"Startup 1 at ({lf.time.logical_elapsed()}, {get_microstep()}).") + startup1.set(1) + =} + + reaction(next) -> Two, mode_switch {= + print("Transitioning to mode 2") + mode_switch.set(2) + Two.set() + =} + } + mode Two { + reaction(startup) -> startup2 {= + print(f"Startup 2 at ({lf.time.logical_elapsed()}, {get_microstep()}).") + startup2.set(1) + =} + + reaction(next) -> Three, mode_switch {= + print("Transitioning to mode 3") + mode_switch.set(3) + Three.set() + =} + + reaction(shutdown) -> shutdown2 {= + print(f"Shutdown 2 at ({lf.time.logical_elapsed()}, {get_microstep()}).") + shutdown2.set(1) + =} + } + + mode Three { + reaction(next) -> Four, mode_switch {= + print("Transitioning to mode 4") + mode_switch.set(4) + Four.set() + =} + + reaction(shutdown) -> shutdown3 {= + print(f"Shutdown 3 at ({lf.time.logical_elapsed()}, {get_microstep()}).") + shutdown3.set(1) + =} + } + + mode Four { + reaction(startup) -> startup4 {= + print(f"Startup 4 at ({lf.time.logical_elapsed()}, {get_microstep()}).") + startup4.set(1) + =} + + reaction(reset) -> reset4 {= + print(f"Reset 4 at ({lf.time.logical_elapsed()}, {get_microstep()}).") + reset4.set(1) + =} + + reaction(next) -> Four, mode_switch {= + print("Transitioning to mode 4") + mode_switch.set(4) + Four.set() + =} + + reaction(shutdown) -> shutdown4 {= + print(f"Shutdown 4 at ({lf.time.logical_elapsed()}, {get_microstep()}).") + shutdown4.set(1) + =} + } + + // Unreachable! + mode Five { + reaction(startup) -> startup5 {= + print(f"Startup 5 at ({lf.time.logical_elapsed()}, {get_microstep()}).") + startup5.set(1) + =} + + reaction(reset) -> reset5 {= + print(f"Reset 5 at ({lf.time.logical_elapsed()}, {get_microstep()}).") + reset5.set(1) + =} + + reaction(shutdown) -> shutdown5 {= + print(f"Shutdown 5 at ({lf.time.logical_elapsed()}, {get_microstep()}).") + shutdown5.set(1) + =} + } +} + +main reactor { + timer stepper(500msec, 500msec) + + modal = new Modal() + test = new TraceTesting( + events_size = 11, + trace = ( + 0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 500000000,1,2,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,2,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 500000000,1,3,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 500000000,1,4,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,4,0,1,0,1,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0, + 500000000,1,4,0,1,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0, + 0,0,4,0,1,0,1,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0, + 500000000,1,4,0,1,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0, + 0,0,4,0,1,0,1,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0, + 500000000,1,4,0,1,0,1,1,1,1,1,0,1,0,1,1,1,0,0,0,0,0,0 + ), + training = False + ) + + // Trigger mode change + reaction(stepper) -> modal.next {= + modal.next.set(True) + =} + + modal.mode_switch, + modal.startup1, + modal.startup2, + modal.shutdown2, + modal.shutdown3, + modal.startup4, + modal.reset4, + modal.shutdown4, + modal.startup5, + modal.reset5, + modal.shutdown5 + -> test.events +} diff --git a/test/Python/src/modal_models/ModalStateReset.lf b/test/Python/src/modal_models/ModalStateReset.lf index 3ee796508e..849f9fffa6 100644 --- a/test/Python/src/modal_models/ModalStateReset.lf +++ b/test/Python/src/modal_models/ModalStateReset.lf @@ -29,6 +29,10 @@ reactor Modal { state counter1(0) timer T1(0msec, 250msec) + reaction(reset) {= + self.counter1 = 0 + =} + reaction(T1) -> count1 {= print(f"Counter1: {self.counter1}") count1.set(self.counter1) @@ -45,6 +49,10 @@ reactor Modal { state counter2(-2) timer T2(0msec, 250msec) + reaction(reset) {= + self.counter2 = -2 + =} + reaction(T2) -> count2 {= print(f"Counter2: {self.counter2}") count2.set(self.counter2) diff --git a/test/Python/src/modal_models/MultipleOutputFeeder_2Connections.lf b/test/Python/src/modal_models/MultipleOutputFeeder_2Connections.lf index ab4d032b91..ad61154a62 100644 --- a/test/Python/src/modal_models/MultipleOutputFeeder_2Connections.lf +++ b/test/Python/src/modal_models/MultipleOutputFeeder_2Connections.lf @@ -36,7 +36,7 @@ reactor Counter(period(1sec)) { output value timer t(0, period) - state curval(0) + state curval(0) reset reaction(t) -> value {= value.set(self.curval) diff --git a/test/Python/src/modal_models/MultipleOutputFeeder_ReactionConnections.lf b/test/Python/src/modal_models/MultipleOutputFeeder_ReactionConnections.lf index 5bb5d18fdc..461cef6b03 100644 --- a/test/Python/src/modal_models/MultipleOutputFeeder_ReactionConnections.lf +++ b/test/Python/src/modal_models/MultipleOutputFeeder_ReactionConnections.lf @@ -39,7 +39,7 @@ reactor Counter(period(1sec)) { output value timer t(0, period) - state curval(0) + state curval(0) reset reaction(t) -> value {= value.set(self.curval) From f7101b102419a17d4068ef054d21646511d801bf Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Mon, 23 May 2022 16:32:07 -0500 Subject: [PATCH 12/56] Added failing test due to deadlock --- test/C/src/federated/FeedbackDelay.lf | 62 +++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 test/C/src/federated/FeedbackDelay.lf diff --git a/test/C/src/federated/FeedbackDelay.lf b/test/C/src/federated/FeedbackDelay.lf new file mode 100644 index 0000000000..4e8324db12 --- /dev/null +++ b/test/C/src/federated/FeedbackDelay.lf @@ -0,0 +1,62 @@ +target C { + timeout: 1 sec, + logging: DEBUG +} +reactor PhysicalPlant { + input control:double; + output sensor:double; + timer t(0, 33 msec); + state last_sensor_time:time(0); + state previous_sensor_time:time(0); + reaction(t) -> sensor {= + SET(sensor, 42); + self->previous_sensor_time = self->last_sensor_time; + self->last_sensor_time = get_physical_time(); + =} + reaction(control) {= + instant_t control_time = get_physical_time(); + info_print("Latency %lld.", control_time - self->previous_sensor_time); + info_print("Logical time: %lld.", get_elapsed_logical_time()); + =} STP(33 msec) {= + warning_print("STP violation."); + =} +} +reactor Controller { + input sensor:double; + output control:double; + + state latest_control:double(0.0); + state first:bool(true); + + output request_for_planning:double; + input planning:double; + + reaction(planning) {= + self->latest_control = planning->value; + =} + reaction(sensor) -> control, request_for_planning {= + if (!self->first) { + SET(control, self->latest_control); + } + self->first = false; + SET(request_for_planning, sensor->value); + =} +} +reactor Planner { + input request:double; + output response: double; + reaction(request) -> response {= + lf_nanosleep(MSEC(10)); + SET(response, request->value); + =} +} +federated reactor { + p = new PhysicalPlant(); + c = new Controller(); + pl = new Planner(); + + p.sensor -> c.sensor; + c.request_for_planning -> pl.request; + pl.response -> c.planning after 0; + c.control -> p.control; +} From 0318863d4168aae0a42facf23687e1a9788f1960 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Wed, 25 May 2022 10:42:15 +0200 Subject: [PATCH 13/56] Fixed merge error --- org.lflang/src/org/lflang/AstExtensions.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/org.lflang/src/org/lflang/AstExtensions.kt b/org.lflang/src/org/lflang/AstExtensions.kt index 1158fde881..eb53d40f3d 100644 --- a/org.lflang/src/org/lflang/AstExtensions.kt +++ b/org.lflang/src/org/lflang/AstExtensions.kt @@ -301,7 +301,6 @@ val Reaction.containingReactor get() = this.eContainer() as Reactor val Port.isInput get() = this is Input val Assignment.isInitWithBraces get() = braces.isNotEmpty() -val StateVar.isInitWithBraces get() = braces.isNotEmpty() val Parameter.isInitWithBraces get() = braces.isNotEmpty() /** From bad2201ba997f98fcaf684db63a39490ce1cc00c Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Wed, 25 May 2022 10:44:28 +0200 Subject: [PATCH 14/56] Fixed missing import --- org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt b/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt index b2aca89f61..2c6f23338b 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt @@ -8,6 +8,7 @@ import org.lflang.indexInContainer import org.lflang.isBank import org.lflang.isGeneric import org.lflang.isMultiport +import org.lflang.lf.BuiltinTriggerRef import org.lflang.lf.Expression import org.lflang.lf.LfPackage import org.lflang.lf.ParameterReference From 335c7527d888a0e2c9e0b4495ec5dbee1bf6c77d Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Wed, 25 May 2022 16:59:00 +0200 Subject: [PATCH 15/56] Updated reactor-c --- org.lflang/src/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index b5410990b7..e4918c9f0a 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit b5410990b7ae88f472c5dcf903d70c9e98967648 +Subproject commit e4918c9f0a4a643c7b88537fc99148ca4ec14b53 From 0607ed8d5313379622917da51ba15c0c5ddc6253 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Fri, 27 May 2022 09:38:18 -0500 Subject: [PATCH 16/56] Update reactor-c ref --- org.lflang/src/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index 5ac67c4913..e59095bfc9 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 5ac67c4913120026f3659afb60d480b6dd543399 +Subproject commit e59095bfc978290d9e74bacea81cf2b54466afb8 From b5d8e983df869409c39a68848f473f4e807d8fc7 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Fri, 27 May 2022 10:35:47 -0500 Subject: [PATCH 17/56] Updated reference to reactor-c --- org.lflang/src/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index e59095bfc9..29c3f042c4 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit e59095bfc978290d9e74bacea81cf2b54466afb8 +Subproject commit 29c3f042c474e3c7f3adaf8db228696c21bd83ad From c75271c9f2674245a795c0fffc5c8660833553a0 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Sat, 28 May 2022 09:59:54 +0200 Subject: [PATCH 18/56] modes: Added validation rule to check for reset state variables without initial value --- org.lflang/src/org/lflang/validation/LFValidator.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/org.lflang/src/org/lflang/validation/LFValidator.java b/org.lflang/src/org/lflang/validation/LFValidator.java index c3f6df370c..d3a66e1fa0 100644 --- a/org.lflang/src/org/lflang/validation/LFValidator.java +++ b/org.lflang/src/org/lflang/validation/LFValidator.java @@ -1374,6 +1374,13 @@ public void checkMissingStateResetInMode(Reactor reactor) { } } + @Check(CheckType.FAST) + public void checkStateResetWithoutInitialValue(StateVar state) { + if (state.isReset() && (state.getInit() == null) || state.getInit().isEmpty()) { + error("The state variable can not be automatically reset without an initial value.", state, Literals.STATE_VAR__RESET); + } + } + ////////////////////////////////////////////////////////////// //// Public methods. From 1666681479afedc3d2aae9985a8b6e7635a3b240 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Sat, 28 May 2022 10:03:05 +0200 Subject: [PATCH 19/56] modes: Moved reset modifier in front of state keyword in state variable declaration. Also adjusted test models to new grammar. --- org.lflang/src/org/lflang/LinguaFranca.xtext | 4 ++-- test/C/src/modal_models/ModalStateResetAuto.lf | 2 +- test/C/src/modal_models/MultipleOutputFeeder_2Connections.lf | 2 +- .../modal_models/MultipleOutputFeeder_ReactionConnections.lf | 2 +- .../src/modal_models/MultipleOutputFeeder_2Connections.lf | 2 +- .../modal_models/MultipleOutputFeeder_ReactionConnections.lf | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/org.lflang/src/org/lflang/LinguaFranca.xtext b/org.lflang/src/org/lflang/LinguaFranca.xtext index da5745384e..e5ece03e62 100644 --- a/org.lflang/src/org/lflang/LinguaFranca.xtext +++ b/org.lflang/src/org/lflang/LinguaFranca.xtext @@ -129,12 +129,12 @@ TargetDecl: * must be given, or a literal or code that denotes zero. */ StateVar: - 'state' name=ID ( + (reset?='reset')? 'state' name=ID ( (':' (type=Type))? ((parens+='(' (init+=Expression (',' init+=Expression)*)? parens+=')') | (braces+='{' (init+=Expression (',' init+=Expression)*)? braces+='}') )? - ) (reset?='reset')? ';'? + ) ';'? ; Method: diff --git a/test/C/src/modal_models/ModalStateResetAuto.lf b/test/C/src/modal_models/ModalStateResetAuto.lf index ab10c79d52..652590f673 100644 --- a/test/C/src/modal_models/ModalStateResetAuto.lf +++ b/test/C/src/modal_models/ModalStateResetAuto.lf @@ -40,7 +40,7 @@ reactor Modal { =} } mode Two { - state counter2:int(-2) reset; + reset state counter2:int(-2); timer T2(0msec, 250msec); reaction(T2) -> count2 {= diff --git a/test/C/src/modal_models/MultipleOutputFeeder_2Connections.lf b/test/C/src/modal_models/MultipleOutputFeeder_2Connections.lf index 85c0061b3e..3bb002c855 100644 --- a/test/C/src/modal_models/MultipleOutputFeeder_2Connections.lf +++ b/test/C/src/modal_models/MultipleOutputFeeder_2Connections.lf @@ -36,7 +36,7 @@ reactor Counter(period:time(1sec)) { output value:int timer t(0, period) - state curval:int(0) reset + reset state curval:int(0) reaction(t) -> value {= lf_set(value, self->curval++); diff --git a/test/C/src/modal_models/MultipleOutputFeeder_ReactionConnections.lf b/test/C/src/modal_models/MultipleOutputFeeder_ReactionConnections.lf index 47c8af1eee..c012e2c60c 100644 --- a/test/C/src/modal_models/MultipleOutputFeeder_ReactionConnections.lf +++ b/test/C/src/modal_models/MultipleOutputFeeder_ReactionConnections.lf @@ -39,7 +39,7 @@ reactor Counter(period:time(1sec)) { output value:int timer t(0, period) - state curval:int(0) reset + reset state curval:int(0) reaction(t) -> value {= lf_set(value, self->curval++); diff --git a/test/Python/src/modal_models/MultipleOutputFeeder_2Connections.lf b/test/Python/src/modal_models/MultipleOutputFeeder_2Connections.lf index ee22fa9946..f7c96dd861 100644 --- a/test/Python/src/modal_models/MultipleOutputFeeder_2Connections.lf +++ b/test/Python/src/modal_models/MultipleOutputFeeder_2Connections.lf @@ -36,7 +36,7 @@ reactor Counter(period(1sec)) { output value timer t(0, period) - state curval(0) reset + reset state curval(0) reaction(t) -> value {= value.set(self.curval) diff --git a/test/Python/src/modal_models/MultipleOutputFeeder_ReactionConnections.lf b/test/Python/src/modal_models/MultipleOutputFeeder_ReactionConnections.lf index 36e105e892..cc97c6da47 100644 --- a/test/Python/src/modal_models/MultipleOutputFeeder_ReactionConnections.lf +++ b/test/Python/src/modal_models/MultipleOutputFeeder_ReactionConnections.lf @@ -39,7 +39,7 @@ reactor Counter(period(1sec)) { output value timer t(0, period) - state curval(0) reset + reset state curval(0) reaction(t) -> value {= value.set(self.curval) From 330528cc1a9b978b3b00b17bf32d4067d1d22535 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Sat, 28 May 2022 10:38:11 +0200 Subject: [PATCH 20/56] modes: Fixed state reset value validation rule --- org.lflang/src/org/lflang/validation/LFValidator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/org/lflang/validation/LFValidator.java b/org.lflang/src/org/lflang/validation/LFValidator.java index d3a66e1fa0..95df225ddc 100644 --- a/org.lflang/src/org/lflang/validation/LFValidator.java +++ b/org.lflang/src/org/lflang/validation/LFValidator.java @@ -1376,7 +1376,7 @@ public void checkMissingStateResetInMode(Reactor reactor) { @Check(CheckType.FAST) public void checkStateResetWithoutInitialValue(StateVar state) { - if (state.isReset() && (state.getInit() == null) || state.getInit().isEmpty()) { + if (state.isReset() && (state.getInit() == null || state.getInit().isEmpty())) { error("The state variable can not be automatically reset without an initial value.", state, Literals.STATE_VAR__RESET); } } From a9187217afccd4ec8ced76d3c947964ced05a4bf Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Sat, 28 May 2022 12:22:16 -0500 Subject: [PATCH 21/56] Added more slack to the test --- .../src/federated/DistributedPhysicalActionUpstream.lf | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/C/src/federated/DistributedPhysicalActionUpstream.lf b/test/C/src/federated/DistributedPhysicalActionUpstream.lf index c7771d5f35..27b2a9e65d 100644 --- a/test/C/src/federated/DistributedPhysicalActionUpstream.lf +++ b/test/C/src/federated/DistributedPhysicalActionUpstream.lf @@ -1,5 +1,5 @@ target C { - timeout: 4 secs, + timeout: 10 secs, coordination-options: {advance-message-interval: 30 msec} }; @@ -13,7 +13,7 @@ preamble {= } // Simulate time passing before a callback occurs. void* take_time(void* a) { - while (_counter < 150) { + while (_counter < 15) { instant_t sleep_time = MSEC(10); lf_nanosleep(sleep_time); callback(a); @@ -39,7 +39,9 @@ reactor WithPhysicalAction { federated reactor { a = new WithPhysicalAction(); m1 = new PassThrough(); - test = new TestCount(num_inputs=149); + m2 = new PassThrough(); + test = new TestCount(num_inputs=14); a.out -> m1.in; - m1.out -> test.in; + m1.out -> m2.in; + m2.out -> test.in; } From 5aec6a6ce1b502f71869e183e39f75c3d99c3c53 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Sat, 28 May 2022 12:56:29 -0500 Subject: [PATCH 22/56] Updated CI to use the RTI in this branch --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2d9ae662d4..1dad55e8eb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,12 +54,12 @@ jobs: # Run the C integration tests. c-tests: - uses: lf-lang/lingua-franca/.github/workflows/c-tests.yml@master + uses: lf-lang/lingua-franca/.github/workflows/c-tests.yml@hotfix-C-TAN needs: cancel # Run the CCpp integration tests. ccpp-tests: - uses: lf-lang/lingua-franca/.github/workflows/c-tests.yml@master + uses: lf-lang/lingua-franca/.github/workflows/c-tests.yml@hotfix-C-TAN with: use-cpp: true needs: cancel @@ -83,7 +83,7 @@ jobs: # Run the Python integration tests. py-tests: - uses: lf-lang/lingua-franca/.github/workflows/py-tests.yml@master + uses: lf-lang/lingua-franca/.github/workflows/py-tests.yml@hotfix-C-TAN needs: cancel # Run the Rust integration tests. @@ -105,5 +105,5 @@ jobs: # Run the serialization tests serialization-tests: - uses: lf-lang/lingua-franca/.github/workflows/serialization-tests.yml@master + uses: lf-lang/lingua-franca/.github/workflows/serialization-tests.yml@hotfix-C-TAN needs: cancel From 2348162fc3facfaaa90824d60a394e4db73099a3 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Sun, 29 May 2022 17:28:47 -0500 Subject: [PATCH 23/56] Added test that uses a logical action, but still fails --- .../DistributedLogicalActionUpstreamLong.lf | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 test/C/src/federated/DistributedLogicalActionUpstreamLong.lf diff --git a/test/C/src/federated/DistributedLogicalActionUpstreamLong.lf b/test/C/src/federated/DistributedLogicalActionUpstreamLong.lf new file mode 100644 index 0000000000..6d275e65d8 --- /dev/null +++ b/test/C/src/federated/DistributedLogicalActionUpstreamLong.lf @@ -0,0 +1,50 @@ +target C { + timeout: 1 msec +}; + +import PassThrough from "../lib/PassThrough.lf" +import TestCount from "../lib/TestCount.lf" + +reactor WithLogicalAction { + output out:int; + state thread_id:lf_thread_t(0); + state counter:int(1); + logical action act(0):int; + + reaction(startup) -> act {= + schedule_int(act, 0, self->counter++); + =} + + reaction(act) -> out {= + SET(out, act->value); + schedule_int(act, USEC(50), self->counter++); + =} +} + +federated reactor { + a = new WithLogicalAction(); + test = new TestCount(num_inputs=29999); + + passThroughs1 = new PassThrough() + passThroughs2 = new PassThrough() + passThroughs3 = new PassThrough() + passThroughs4 = new PassThrough() + passThroughs5 = new PassThrough() + passThroughs6 = new PassThrough() + passThroughs7 = new PassThrough() + passThroughs8 = new PassThrough() + passThroughs9 = new PassThrough() + passThroughs10 = new PassThrough() + + a.out, passThroughs1.out, + passThroughs2.out, passThroughs3.out, + passThroughs4.out, passThroughs5.out, + passThroughs6.out, passThroughs7.out, + passThroughs8.out, passThroughs9.out, + passThroughs10.out -> passThroughs1.in, + passThroughs2.in, passThroughs3.in, + passThroughs4.in, passThroughs5.in, + passThroughs6.in, passThroughs7.in, + passThroughs8.in, passThroughs9.in, + passThroughs10.in, test.in +} From 57b113ea286a5b7684615fd84e5dc939a3064b52 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Mon, 30 May 2022 09:30:55 -0500 Subject: [PATCH 24/56] Updated pointer to reactor-c --- org.lflang/src/lib/c/reactor-c | 2 +- test/C/src/federated/DistributedLogicalActionUpstreamLong.lf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index 29c3f042c4..24c2691465 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 29c3f042c474e3c7f3adaf8db228696c21bd83ad +Subproject commit 24c26914656fcfb26407d5ced920d4d230b2afc6 diff --git a/test/C/src/federated/DistributedLogicalActionUpstreamLong.lf b/test/C/src/federated/DistributedLogicalActionUpstreamLong.lf index 6d275e65d8..9755a30b5c 100644 --- a/test/C/src/federated/DistributedLogicalActionUpstreamLong.lf +++ b/test/C/src/federated/DistributedLogicalActionUpstreamLong.lf @@ -23,7 +23,7 @@ reactor WithLogicalAction { federated reactor { a = new WithLogicalAction(); - test = new TestCount(num_inputs=29999); + test = new TestCount(num_inputs=21); passThroughs1 = new PassThrough() passThroughs2 = new PassThrough() From 265af368fc902a04801eecb511b872addfc04a82 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Mon, 30 May 2022 10:35:08 -0500 Subject: [PATCH 25/56] Simplified test --- .../src/federated/DistributedLogicalActionUpstreamLong.lf | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/test/C/src/federated/DistributedLogicalActionUpstreamLong.lf b/test/C/src/federated/DistributedLogicalActionUpstreamLong.lf index 9755a30b5c..3b3c2f1218 100644 --- a/test/C/src/federated/DistributedLogicalActionUpstreamLong.lf +++ b/test/C/src/federated/DistributedLogicalActionUpstreamLong.lf @@ -11,12 +11,8 @@ reactor WithLogicalAction { state counter:int(1); logical action act(0):int; - reaction(startup) -> act {= - schedule_int(act, 0, self->counter++); - =} - - reaction(act) -> out {= - SET(out, act->value); + reaction(startup, act) -> act, out {= + SET(out, self->counter); schedule_int(act, USEC(50), self->counter++); =} } From ac4ad97d142799d410e7654ae274115a2a5b4dd5 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Mon, 30 May 2022 10:35:20 -0500 Subject: [PATCH 26/56] Updated pointer to reactor-c --- org.lflang/src/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index 24c2691465..b0aace2e7f 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 24c26914656fcfb26407d5ced920d4d230b2afc6 +Subproject commit b0aace2e7f428f3e86182a5129847e23aec5ca9f From 982eecca8d040de4c9043507067d5662805512b1 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Mon, 30 May 2022 14:26:43 -0500 Subject: [PATCH 27/56] Update reference to reactor-c --- org.lflang/src/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index b0aace2e7f..5aaafb00d4 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit b0aace2e7f428f3e86182a5129847e23aec5ca9f +Subproject commit 5aaafb00d4bb270b3e26babda5985f53fa1cd36f From 9e12bc5a2dea2dac988aeb7a0fce3ec4e07c5e81 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Mon, 30 May 2022 23:11:14 -0500 Subject: [PATCH 28/56] Update reactor-c --- org.lflang/src/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index 5aaafb00d4..44dc5a1c34 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 5aaafb00d4bb270b3e26babda5985f53fa1cd36f +Subproject commit 44dc5a1c341733575412f06628f4941da590795b From b7bccaf76f4e6f6af1d214ddf1211368a8806384 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Tue, 31 May 2022 17:52:19 -0500 Subject: [PATCH 29/56] Updated pointer to reactor-c --- org.lflang/src/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index 44dc5a1c34..15b2f435e2 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 44dc5a1c341733575412f06628f4941da590795b +Subproject commit 15b2f435e27cce41a05a2c65b42cfe622fdb6c86 From 63e61c263e7de68bbf51ad1d846924c4e9e67133 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Wed, 1 Jun 2022 13:34:48 -0500 Subject: [PATCH 30/56] Updated pointer to reactor-c --- org.lflang/src/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index 44dc5a1c34..15b2f435e2 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 44dc5a1c341733575412f06628f4941da590795b +Subproject commit 15b2f435e27cce41a05a2c65b42cfe622fdb6c86 From c035868bd3f523804b02f5c40e30119847768d25 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Wed, 1 Jun 2022 13:59:17 -0500 Subject: [PATCH 31/56] Don't generate control reactions for connections that have delays --- .../src/org/lflang/federated/FedASTUtils.java | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/org.lflang/src/org/lflang/federated/FedASTUtils.java b/org.lflang/src/org/lflang/federated/FedASTUtils.java index a8e185846e..0f6738d198 100644 --- a/org.lflang/src/org/lflang/federated/FedASTUtils.java +++ b/org.lflang/src/org/lflang/federated/FedASTUtils.java @@ -280,6 +280,7 @@ private static void addNetworkInputControlReaction( FederateInstance instance, GeneratorBase generator ) { + LfFactory factory = LfFactory.eINSTANCE; Reaction reaction = factory.createReaction(); VarRef destRef = factory.createVarRef(); @@ -312,19 +313,17 @@ private static void addNetworkInputControlReaction( // Add the appropriate triggers to the list of triggers of the reaction reaction.getTriggers().add(newTriggerForControlReaction); - if (!connection.isPhysical() && connection.getDelay() == null) { - // If the connection is not physical and there is no delay, - // add the original output port of the source federate - // as a trigger to keep the overall dependency structure. - // This is useful when assigning levels. - VarRef sourceRef = factory.createVarRef(); - - sourceRef.setContainer(source.getParent().getDefinition()); - sourceRef.setVariable(source.getDefinition()); - reaction.getTriggers().add(sourceRef); - // Add this trigger to the list of disconnected network reaction triggers - instance.remoteNetworkReactionTriggers.add(sourceRef); - } + + // Add the original output port of the source federate + // as a trigger to keep the overall dependency structure. + // This is useful when assigning levels. + VarRef sourceRef = factory.createVarRef(); + + sourceRef.setContainer(source.getParent().getDefinition()); + sourceRef.setVariable(source.getDefinition()); + reaction.getTriggers().add(sourceRef); + // Add this trigger to the list of disconnected network reaction triggers + instance.remoteNetworkReactionTriggers.add(sourceRef); // Add the destination port as an effect of the reaction reaction.getEffects().add(destRef); @@ -714,7 +713,11 @@ public static void makeCommunication( serializer ); - if (!connection.isPhysical()) { + // Next, generate control reactions + if ( + !connection.isPhysical() && // Connections that are physical don't need control reactions + connection.getDelay() == null // Connections that have delays don't need control reactions + ) { // The ID of the receiving port (rightPort) is the position // of the networkAction (see below) in this list. From 1bfeac557d8059f053782eba06c86382f2272826 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Fri, 3 Jun 2022 13:13:00 +0200 Subject: [PATCH 32/56] modes: Added newline in test model --- test/C/src/modal_models/ModalStateResetAuto.lf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/C/src/modal_models/ModalStateResetAuto.lf b/test/C/src/modal_models/ModalStateResetAuto.lf index 652590f673..511a77787a 100644 --- a/test/C/src/modal_models/ModalStateResetAuto.lf +++ b/test/C/src/modal_models/ModalStateResetAuto.lf @@ -95,4 +95,4 @@ main reactor { modal.count1, modal.count2 -> test.events -} \ No newline at end of file +} From bb9bf334d4e6733f1c278d77cfe9c03249c0c111 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Fri, 3 Jun 2022 13:53:32 +0200 Subject: [PATCH 33/56] modes: Added unit tests for mode related validation rules. Also fixed bugs in tested rules. --- .../compiler/LinguaFrancaValidationTest.java | 149 +++++++++++++++++- .../org/lflang/validation/LFValidator.java | 16 +- 2 files changed, 156 insertions(+), 9 deletions(-) diff --git a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.java b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.java index 86f872f406..722617c779 100644 --- a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.java +++ b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.java @@ -67,6 +67,7 @@ * @author{Marten Lohstroh } * @author{Matt Weber } * @author(Christian Menard } + * @author{Alexander Schulz-Rosengarten } */ public class LinguaFrancaValidationTest { @Inject @@ -2221,7 +2222,153 @@ public void testUnrecognizedTarget() throws Exception { "Unrecognized target: Pjthon"); } - + @Test + public void testInitialMode() throws Exception { + String testCase = """ + target C; + main reactor { + mode M {} + } + """; + validator.assertError(parseWithoutError(testCase), LfPackage.eINSTANCE.getReactor(), null, + "Every modal reactor requires one initial mode."); + } + + @Test + public void testInitialModes() throws Exception { + String testCase = """ + target C; + main reactor { + initial mode IM1 {} + initial mode IM2 {} + } + """; + validator.assertError(parseWithoutError(testCase), LfPackage.eINSTANCE.getReactor(), null, + "A modal reactor can only have one initial mode."); + } + + @Test + public void testModeStateNamespace() throws Exception { + String testCase = """ + target C; + main reactor { + initial mode IM { + state s:int; + } + mode M { + state s:int; + } + } + """; + validator.assertError(parseWithoutError(testCase), LfPackage.eINSTANCE.getStateVar(), null, + "Duplicate state variable 's'. (State variables are currently scoped on reactor level not modes)"); + } + + @Test + public void testModeTimerNamespace() throws Exception { + String testCase = """ + target C; + main reactor { + initial mode IM { + timer t; + } + mode M { + timer t; + } + } + """; + validator.assertError(parseWithoutError(testCase), LfPackage.eINSTANCE.getTimer(), null, + "Duplicate Timer 't'. (Timers are currently scoped on reactor level not modes)"); + } + + @Test + public void testModeActionNamespace() throws Exception { + String testCase = """ + target C; + main reactor { + initial mode IM { + logical action a; + } + mode M { + logical action a; + } + } + """; + validator.assertError(parseWithoutError(testCase), LfPackage.eINSTANCE.getAction(), null, + "Duplicate Action 'a'. (Actions are currently scoped on reactor level not modes)"); + } + + @Test + public void testModeInstanceNamespace() throws Exception { + String testCase = """ + target C; + reactor R {} + main reactor { + initial mode IM { + r = new R(); + } + mode M { + r = new R(); + } + } + """; + validator.assertError(parseWithoutError(testCase), LfPackage.eINSTANCE.getInstantiation(), null, + "Duplicate Instantiation 'r'. (Instantiations are currently scoped on reactor level not modes)"); + } + + @Test + public void testMissingModeStateReset() throws Exception { + String testCase = """ + target C; + main reactor { + initial mode IM { + reaction(startup) -> M {==} + } + mode M { + state s:int(0); + } + } + """; + validator.assertError(parseWithoutError(testCase), LfPackage.eINSTANCE.getMode(), null, + "State variable is not reset upon mode entry. It is neither marked for automatic reset nor is there a reset reaction."); + } + + @Test + public void testMissingModeStateResetInstance() throws Exception { + String testCase = """ + target C; + reactor R { + state s:int(0); + } + main reactor { + initial mode IM { + reaction(startup) -> M {==} + } + mode M { + r = new R(); + } + } + """; + validator.assertError(parseWithoutError(testCase), LfPackage.eINSTANCE.getMode(), null, + "This reactor contains state variables that are not reset upon mode entry. " + + "The instatiated reactor (or any inner reactor) neither marks its state variables for automatic reset nor defines a reset reaction. " + + "It is usafe to instatiate this reactor inside a mode."); + } + + @Test + public void testModeStateResetWithoutInitialValue() throws Exception { + String testCase = """ + target C; + main reactor { + initial mode IM { + reset state s:int; + } + } + """; + validator.assertError(parseWithoutError(testCase), LfPackage.eINSTANCE.getStateVar(), null, + "The state variable can not be automatically reset without an initial value."); + } + } diff --git a/org.lflang/src/org/lflang/validation/LFValidator.java b/org.lflang/src/org/lflang/validation/LFValidator.java index 95df225ddc..7ad8a6bb23 100644 --- a/org.lflang/src/org/lflang/validation/LFValidator.java +++ b/org.lflang/src/org/lflang/validation/LFValidator.java @@ -1248,8 +1248,8 @@ public void checkModeStateNamespace(Reactor reactor) { for (var mode : reactor.getModes()) { for (var stateVar : mode.getStateVars()) { if (names.contains(stateVar.getName())) { - error(String.format("Duplicate StateVar '%s' in Reactor '%s'. (State variables are currently scoped on reactor level not modes)", - stateVar.getName(), reactor.getName()), stateVar, Literals.STATE_VAR__NAME); + error(String.format("Duplicate state variable '%s'. (State variables are currently scoped on reactor level not modes)", + stateVar.getName()), stateVar, Literals.STATE_VAR__NAME); } names.add(stateVar.getName()); } @@ -1265,8 +1265,8 @@ public void checkModeTimerNamespace(Reactor reactor) { for (var mode : reactor.getModes()) { for (var timer : mode.getTimers()) { if (names.contains(timer.getName())) { - error(String.format("Duplicate Timer '%s' in Reactor '%s'. (Timers are currently scoped on reactor level not modes)", - timer.getName(), timer.getName()), timer, Literals.STATE_VAR__NAME); + error(String.format("Duplicate Timer '%s'. (Timers are currently scoped on reactor level not modes)", + timer.getName()), timer, Literals.VARIABLE__NAME); } names.add(timer.getName()); } @@ -1282,8 +1282,8 @@ public void checkModeActionNamespace(Reactor reactor) { for (var mode : reactor.getModes()) { for (var action : mode.getActions()) { if (names.contains(action.getName())) { - error(String.format("Duplicate Action '%s' in Reactor '%s'. (Actions are currently scoped on reactor level not modes)", - action.getName(), action.getName()), action, Literals.STATE_VAR__NAME); + error(String.format("Duplicate Action '%s'. (Actions are currently scoped on reactor level not modes)", + action.getName()), action, Literals.VARIABLE__NAME); } names.add(action.getName()); } @@ -1299,8 +1299,8 @@ public void checkModeInstanceNamespace(Reactor reactor) { for (var mode : reactor.getModes()) { for (var instantiation : mode.getInstantiations()) { if (names.contains(instantiation.getName())) { - error(String.format("Duplicate Instantiation '%s' in Reactor '%s'. (Instantiations are currently scoped on reactor level not modes)", - instantiation.getName(), instantiation.getName()), instantiation, Literals.STATE_VAR__NAME); + error(String.format("Duplicate Instantiation '%s'. (Instantiations are currently scoped on reactor level not modes)", + instantiation.getName()), instantiation, Literals.INSTANTIATION__NAME); } names.add(instantiation.getName()); } From a6109d42118c6da35d8d485a766fc3e3e3521f8a Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Fri, 3 Jun 2022 15:40:45 -0500 Subject: [PATCH 34/56] Updated pointer to reactor-c --- org.lflang/src/lib/c/reactor-c | 2 +- test/C/src/federated/DistributedLogicalActionUpstreamLong.lf | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index 15b2f435e2..f257a3bfd5 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 15b2f435e27cce41a05a2c65b42cfe622fdb6c86 +Subproject commit f257a3bfd534a0e5037d8748da7d751f9ab77320 diff --git a/test/C/src/federated/DistributedLogicalActionUpstreamLong.lf b/test/C/src/federated/DistributedLogicalActionUpstreamLong.lf index 3b3c2f1218..ff76ac6df7 100644 --- a/test/C/src/federated/DistributedLogicalActionUpstreamLong.lf +++ b/test/C/src/federated/DistributedLogicalActionUpstreamLong.lf @@ -1,5 +1,6 @@ target C { - timeout: 1 msec + timeout: 1 msec, + logging: DEBUG }; import PassThrough from "../lib/PassThrough.lf" From 61d49a9c0d58c11ceb0c9e443a7d3b663aa4d797 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Fri, 3 Jun 2022 16:31:01 -0500 Subject: [PATCH 35/56] Enable debug for test --- org.lflang/src/lib/c/reactor-c | 2 +- test/C/src/federated/BroadcastFeedback.lf | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index f257a3bfd5..a228fb3507 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit f257a3bfd534a0e5037d8748da7d751f9ab77320 +Subproject commit a228fb3507c51066c2a3de9991d0f869cb6dd075 diff --git a/test/C/src/federated/BroadcastFeedback.lf b/test/C/src/federated/BroadcastFeedback.lf index c0618bfd17..f7817bf178 100644 --- a/test/C/src/federated/BroadcastFeedback.lf +++ b/test/C/src/federated/BroadcastFeedback.lf @@ -3,7 +3,8 @@ */ target C { timeout: 1 sec, - build-type: RelWithDebInfo + build-type: RelWithDebInfo, + logging: DEBUG }; reactor SenderAndReceiver { output out:int; From c6ef9c4de93ef6a9b2f8716fc4695d90ce5bb999 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Fri, 3 Jun 2022 16:41:23 -0500 Subject: [PATCH 36/56] Updated ref to reactor-c --- org.lflang/src/lib/c/reactor-c | 2 +- test/C/src/federated/DistributedLogicalActionUpstreamLong.lf | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index a228fb3507..eaec0e6e14 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit a228fb3507c51066c2a3de9991d0f869cb6dd075 +Subproject commit eaec0e6e1417531bb575f8aaf61aa6eba2909d01 diff --git a/test/C/src/federated/DistributedLogicalActionUpstreamLong.lf b/test/C/src/federated/DistributedLogicalActionUpstreamLong.lf index ff76ac6df7..3b3c2f1218 100644 --- a/test/C/src/federated/DistributedLogicalActionUpstreamLong.lf +++ b/test/C/src/federated/DistributedLogicalActionUpstreamLong.lf @@ -1,6 +1,5 @@ target C { - timeout: 1 msec, - logging: DEBUG + timeout: 1 msec }; import PassThrough from "../lib/PassThrough.lf" From bbc0e0a5d227b5f03f1ef502c48678ccb0f3ab32 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Sat, 4 Jun 2022 12:49:34 -0500 Subject: [PATCH 37/56] Added comments --- .../DistributedLogicalActionUpstreamLong.lf | 4 ++ .../DistributedPhysicalActionUpstream.lf | 4 ++ .../DistributedPhysicalActionUpstreamLong.lf | 69 +++++++++++++++++++ test/C/src/lib/PassThrough.lf | 3 + 4 files changed, 80 insertions(+) create mode 100644 test/C/src/federated/DistributedPhysicalActionUpstreamLong.lf diff --git a/test/C/src/federated/DistributedLogicalActionUpstreamLong.lf b/test/C/src/federated/DistributedLogicalActionUpstreamLong.lf index 3b3c2f1218..80bf89b93d 100644 --- a/test/C/src/federated/DistributedLogicalActionUpstreamLong.lf +++ b/test/C/src/federated/DistributedLogicalActionUpstreamLong.lf @@ -1,3 +1,7 @@ +/** + * Test that a rapidly produced logical action in an upstream federate can be + * properly handled in a long chain of federates. + */ target C { timeout: 1 msec }; diff --git a/test/C/src/federated/DistributedPhysicalActionUpstream.lf b/test/C/src/federated/DistributedPhysicalActionUpstream.lf index 27b2a9e65d..3e06b9a7df 100644 --- a/test/C/src/federated/DistributedPhysicalActionUpstream.lf +++ b/test/C/src/federated/DistributedPhysicalActionUpstream.lf @@ -1,3 +1,7 @@ +/** + * Test that a rapidly produced physical action in an upstream federate can be + * properly handled in federated execution. + */ target C { timeout: 10 secs, coordination-options: {advance-message-interval: 30 msec} diff --git a/test/C/src/federated/DistributedPhysicalActionUpstreamLong.lf b/test/C/src/federated/DistributedPhysicalActionUpstreamLong.lf new file mode 100644 index 0000000000..089fb0f82f --- /dev/null +++ b/test/C/src/federated/DistributedPhysicalActionUpstreamLong.lf @@ -0,0 +1,69 @@ +/** + * Test that a rapidly produced physical action in an upstream federate can be + * properly handled in a long chain of federates. + */ +target C { + timeout: 1 sec, + coordination-options: {advance-message-interval: 50 usec} +}; + +import PassThrough from "../lib/PassThrough.lf" +import TestCount from "../lib/TestCount.lf" + +preamble {= + int _counter = 1; + void callback(void *a) { + schedule_int(a, 0, _counter++); + } + // Simulate time passing before a callback occurs. + void* take_time(void* a) { + while (_counter < 20) { + instant_t sleep_time = USEC(50); + lf_nanosleep(sleep_time); + callback(a); + } + return NULL; + } +=} + +reactor WithPhysicalAction { + output out:int; + state thread_id:lf_thread_t(0); + physical action act(0):int; + reaction(startup) -> act {= + // start new thread, provide callback + lf_thread_create(&self->thread_id, &take_time, act); + =} + + reaction(act) -> out {= + SET(out, act->value); + =} +} + +federated reactor { + a = new WithPhysicalAction(); + test = new TestCount(num_inputs=19); + + passThroughs1 = new PassThrough() + passThroughs2 = new PassThrough() + passThroughs3 = new PassThrough() + passThroughs4 = new PassThrough() + passThroughs5 = new PassThrough() + passThroughs6 = new PassThrough() + passThroughs7 = new PassThrough() + passThroughs8 = new PassThrough() + passThroughs9 = new PassThrough() + passThroughs10 = new PassThrough() + + a.out, passThroughs1.out, + passThroughs2.out, passThroughs3.out, + passThroughs4.out, passThroughs5.out, + passThroughs6.out, passThroughs7.out, + passThroughs8.out, passThroughs9.out, + passThroughs10.out -> passThroughs1.in, + passThroughs2.in, passThroughs3.in, + passThroughs4.in, passThroughs5.in, + passThroughs6.in, passThroughs7.in, + passThroughs8.in, passThroughs9.in, + passThroughs10.in, test.in +} diff --git a/test/C/src/lib/PassThrough.lf b/test/C/src/lib/PassThrough.lf index c7ebcf6f3c..8add840b79 100644 --- a/test/C/src/lib/PassThrough.lf +++ b/test/C/src/lib/PassThrough.lf @@ -1,3 +1,6 @@ +/** + * Forward the integer input on `in` to the output port `out`. + */ target C; reactor PassThrough { From c7661b794074a2e8dc130e78ff61e6c3972c565c Mon Sep 17 00:00:00 2001 From: eal Date: Mon, 6 Jun 2022 11:34:58 +0200 Subject: [PATCH 38/56] First version of methods for the C target --- org.lflang/src/org/lflang/ASTUtils.java | 11 ++- .../org/lflang/generator/c/CGenerator.java | 40 +++++++++-- .../lflang/generator/c/CMethodGenerator.java | 72 +++++++++++++++++++ .../generator/c/CReactionGenerator.java | 5 +- test/C/src/target/Methods.lf | 28 ++++++++ 5 files changed, 146 insertions(+), 10 deletions(-) create mode 100644 org.lflang/src/org/lflang/generator/c/CMethodGenerator.java create mode 100644 test/C/src/target/Methods.lf diff --git a/org.lflang/src/org/lflang/ASTUtils.java b/org.lflang/src/org/lflang/ASTUtils.java index 404fbf1b0e..0f39e701a5 100644 --- a/org.lflang/src/org/lflang/ASTUtils.java +++ b/org.lflang/src/org/lflang/ASTUtils.java @@ -55,7 +55,6 @@ import org.eclipse.xtext.xbase.lib.IterableExtensions; import org.eclipse.xtext.xbase.lib.IteratorExtensions; import org.eclipse.xtext.xbase.lib.StringExtensions; - import org.lflang.ast.ToText; import org.lflang.generator.CodeMap; import org.lflang.generator.GeneratorBase; @@ -73,6 +72,7 @@ import org.lflang.lf.LfFactory; import org.lflang.lf.LfPackage; import org.lflang.lf.Literal; +import org.lflang.lf.Method; import org.lflang.lf.Mode; import org.lflang.lf.Model; import org.lflang.lf.Output; @@ -635,6 +635,15 @@ public static List allInstantiations(Reactor definition) { return ASTUtils.collectElements(definition, featurePackage.getReactor_Instantiations()); } + /** + * Given a reactor class, return a list of all its methods, + * which includes methods of base classes that it extends. + * @param definition Reactor class definition. + */ + public static List allMethods(Reactor definition) { + return ASTUtils.collectElements(definition, featurePackage.getReactor_Methods()); + } + /** * Given a reactor class, return a list of all its outputs, * which includes outputs of base classes that it extends. diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.java b/org.lflang/src/org/lflang/generator/c/CGenerator.java index 60311b5794..62a7afd75c 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.java @@ -25,6 +25,19 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ***************/ package org.lflang.generator.c; +import static org.lflang.ASTUtils.allActions; +import static org.lflang.ASTUtils.allInputs; +import static org.lflang.ASTUtils.allMethods; +import static org.lflang.ASTUtils.allOutputs; +import static org.lflang.ASTUtils.allReactions; +import static org.lflang.ASTUtils.allStateVars; +import static org.lflang.ASTUtils.convertToEmptyListIfNull; +import static org.lflang.ASTUtils.getInferredType; +import static org.lflang.ASTUtils.isInitialized; +import static org.lflang.ASTUtils.toDefinition; +import static org.lflang.ASTUtils.toText; +import static org.lflang.util.StringUtil.addDoubleQuotes; + import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -34,18 +47,17 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; -import com.google.common.base.Objects; -import com.google.common.collect.Iterables; + import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.xtext.util.CancelIndicator; import org.eclipse.xtext.xbase.lib.Exceptions; import org.eclipse.xtext.xbase.lib.IterableExtensions; import org.eclipse.xtext.xbase.lib.IteratorExtensions; import org.eclipse.xtext.xbase.lib.StringExtensions; +import org.lflang.ASTUtils; import org.lflang.ErrorReporter; import org.lflang.FileConfig; import org.lflang.InferredType; -import org.lflang.ASTUtils; import org.lflang.Target; import org.lflang.TargetConfig; import org.lflang.TargetProperty; @@ -60,10 +72,9 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY import org.lflang.generator.ActionInstance; import org.lflang.generator.CodeBuilder; import org.lflang.generator.GeneratorBase; -import org.lflang.generator.DockerGeneratorBase; import org.lflang.generator.GeneratorResult; -import org.lflang.generator.IntegratedBuilder; import org.lflang.generator.GeneratorUtils; +import org.lflang.generator.IntegratedBuilder; import org.lflang.generator.LFGeneratorContext; import org.lflang.generator.LFResource; import org.lflang.generator.ParameterInstance; @@ -79,6 +90,7 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY import org.lflang.lf.Expression; import org.lflang.lf.Input; import org.lflang.lf.Instantiation; +import org.lflang.lf.Method; import org.lflang.lf.Mode; import org.lflang.lf.Model; import org.lflang.lf.Output; @@ -92,8 +104,9 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY import org.lflang.lf.Variable; import org.lflang.lf.WidthTerm; import org.lflang.util.FileUtil; -import static org.lflang.ASTUtils.*; -import static org.lflang.util.StringUtil.*; + +import com.google.common.base.Objects; +import com.google.common.collect.Iterables; /** * Generator for C target. This class generates C code defining each reactor @@ -1183,6 +1196,7 @@ private void generateReactorClass(ReactorDecl reactor) { var constructorCode = new CodeBuilder(); generateAuxiliaryStructs(reactor); generateSelfStruct(reactor, constructorCode); + generateMethods(reactor); generateReactions(reactor, currentFederate); generateConstructor(reactor, currentFederate, constructorCode); @@ -1477,6 +1491,18 @@ private void generateInteractingContainedReactors( } } + /** Generate method functions definition for a reactor. + * These functions have a first argument that is a void* pointing to + * the self struct. + * @param reactor The reactor. + */ + public void generateMethods(ReactorDecl decl) { + var reactor = ASTUtils.toDefinition(decl); + for (Method method : allMethods(reactor)) { + code.pr(CMethodGenerator.generateMethod(method, decl, types)); + } + } + /** * This function is provided to allow extensions of the CGenerator to append the structure of the self struct * @param body The body of the self struct diff --git a/org.lflang/src/org/lflang/generator/c/CMethodGenerator.java b/org.lflang/src/org/lflang/generator/c/CMethodGenerator.java new file mode 100644 index 0000000000..7eb23f0dae --- /dev/null +++ b/org.lflang/src/org/lflang/generator/c/CMethodGenerator.java @@ -0,0 +1,72 @@ +package org.lflang.generator.c; + +import org.lflang.ASTUtils; +import org.lflang.InferredType; +import org.lflang.generator.CodeBuilder; +import org.lflang.lf.Method; +import org.lflang.lf.ReactorDecl; + +/** + * Generates C code to declare and initialize methods. + * + * @author {Edward A. Lee } + */ +public class CMethodGenerator { + /** + * Generate a method function definition for a reactor. + * This function will have a first argument that is a void* pointing to + * the self struct, followed by any arguments given in its definition. + * @param method The method. + * @param decl The reactor declaration. + * @param types The C-specific type conversion functions. + */ + public static String generateMethod( + Method method, + ReactorDecl decl, + CTypes types + ) { + var code = new CodeBuilder(); + var body = ASTUtils.toText(method.getCode()); + + StringBuilder header = new StringBuilder(); + if (method.getReturn() != null) { + header.append(types.getTargetType(InferredType.fromAST(method.getReturn()))); + header.append(" "); + } else { + header.append("void "); + } + header.append(method.getName()); + header.append("(void* instance_args"); + if (method.getArguments() != null) { + for (var arg : method.getArguments()) { + header.append(", "); + header.append(types.getTargetType(InferredType.fromAST(arg.getType()))); + header.append(" "); + header.append(arg.getName()); + } + } + header.append(") {"); + + code.prSourceLineNumber(method); + code.pr(header); + code.indent(); + + // Define the "self" struct. + String structType = CUtil.selfType(decl); + // A null structType means there are no inputs, state, + // or anything else. No need to declare it. + if (structType != null) { + code.pr(String.join("\n", + "#pragma GCC diagnostic push", + "#pragma GCC diagnostic ignored \"-Wunused-variable\"", + structType+"* self = ("+structType+"*)instance_args;" + )); + } + + code.prSourceLineNumber(method.getCode()); + code.pr(body); + code.unindent(); + code.pr("}"); + return code.toString(); + } +} \ No newline at end of file diff --git a/org.lflang/src/org/lflang/generator/c/CReactionGenerator.java b/org.lflang/src/org/lflang/generator/c/CReactionGenerator.java index 384b15fec6..a81e90efc8 100644 --- a/org.lflang/src/org/lflang/generator/c/CReactionGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CReactionGenerator.java @@ -1,6 +1,7 @@ package org.lflang.generator.c; import static org.lflang.generator.c.CUtil.generateWidthVariable; +import static org.lflang.util.StringUtil.addDoubleQuotes; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -8,6 +9,7 @@ import java.util.List; import java.util.Map; import java.util.Set; + import org.lflang.ASTUtils; import org.lflang.ErrorReporter; import org.lflang.InferredType; @@ -20,6 +22,7 @@ import org.lflang.lf.Code; import org.lflang.lf.Input; import org.lflang.lf.Instantiation; +import org.lflang.lf.Method; import org.lflang.lf.Mode; import org.lflang.lf.Output; import org.lflang.lf.Port; @@ -32,8 +35,6 @@ import org.lflang.lf.Variable; import org.lflang.util.StringUtil; -import static org.lflang.util.StringUtil.addDoubleQuotes; - public class CReactionGenerator { protected static String DISABLE_REACTION_INITIALIZATION_MARKER = "// **** Do not include initialization code in this reaction."; diff --git a/test/C/src/target/Methods.lf b/test/C/src/target/Methods.lf new file mode 100644 index 0000000000..3459ca4efb --- /dev/null +++ b/test/C/src/target/Methods.lf @@ -0,0 +1,28 @@ +target C; + +main reactor { + + state foo:int(2); + + method getFoo(): int {= + return self->foo; + =} + + method add(x:int) {= + self->foo += x; + =} + + reaction(startup){= + lf_print("Foo is initialized to %d", getFoo(self)); + if (getFoo(self) != 2) { + lf_print_error_and_exit("Expected 2!"); + } + + add(self, 40); + int a = getFoo(self); + lf_print("2 + 40 = %d", a); + if (a != 42) { + lf_print_error_and_exit("Expected 42!"); + } + =} +} From 99af2436400dfb3382bfccc33da94c52fb8aca52 Mon Sep 17 00:00:00 2001 From: eal Date: Mon, 6 Jun 2022 12:12:26 +0200 Subject: [PATCH 39/56] Refactor so that reactors can have the same method names. --- .../org/lflang/generator/c/CGenerator.java | 3 + .../lflang/generator/c/CMethodGenerator.java | 59 ++++++++++++++++++- test/C/src/target/Methods.lf | 8 +-- test/C/src/target/MethodsSameName.lf | 38 ++++++++++++ 4 files changed, 103 insertions(+), 5 deletions(-) create mode 100644 test/C/src/target/MethodsSameName.lf diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.java b/org.lflang/src/org/lflang/generator/c/CGenerator.java index 62a7afd75c..cd66928f42 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.java @@ -1346,6 +1346,9 @@ private void generateSelfStruct(ReactorDecl decl, CodeBuilder constructorCode) { // Next, generate fields for modes CModesGenerator.generateDeclarations(reactor, body, constructorCode); + + // Finally, generate fields for methods. + CMethodGenerator.generateDeclarations(reactor, body, constructorCode, types); // The first field has to always be a pointer to the list of // of allocated memory that must be freed when the reactor is freed. diff --git a/org.lflang/src/org/lflang/generator/c/CMethodGenerator.java b/org.lflang/src/org/lflang/generator/c/CMethodGenerator.java index 7eb23f0dae..089126f969 100644 --- a/org.lflang/src/org/lflang/generator/c/CMethodGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CMethodGenerator.java @@ -1,9 +1,12 @@ package org.lflang.generator.c; +import static org.lflang.ASTUtils.allMethods; + import org.lflang.ASTUtils; import org.lflang.InferredType; import org.lflang.generator.CodeBuilder; import org.lflang.lf.Method; +import org.lflang.lf.Reactor; import org.lflang.lf.ReactorDecl; /** @@ -12,6 +15,49 @@ * @author {Edward A. Lee } */ public class CMethodGenerator { + /** + * Generate fields in the self struct for method functions and + * initialize them in the constructor. + * + * @param reactor The reactor. + * @param body The place to put the struct declarations. + * @param constructorCode The place to put the constructor code. + * @param types The C-specific type conversion functions. + */ + public static void generateDeclarations( + Reactor reactor, + CodeBuilder body, + CodeBuilder constructorCode, + CTypes types + ) { + for (Method method : allMethods(reactor)) { + + var functionName = methodFunctionName(reactor, method); + + // Construct function type signature. + StringBuilder typeSignature = new StringBuilder(); + if (method.getReturn() != null) { + typeSignature.append(types.getTargetType(InferredType.fromAST(method.getReturn()))); + } else { + typeSignature.append("void"); + } + typeSignature.append("(*"); + typeSignature.append(method.getName()); + typeSignature.append(")(void*"); + if (method.getArguments() != null) { + for (var arg : method.getArguments()) { + typeSignature.append(", "); + typeSignature.append(types.getTargetType(InferredType.fromAST(arg.getType()))); + } + } + typeSignature.append(");"); + body.pr(typeSignature); + + constructorCode.pr("self->"+method.getName()+" = "+functionName+";"); + } + + } + /** * Generate a method function definition for a reactor. * This function will have a first argument that is a void* pointing to @@ -27,6 +73,7 @@ public static String generateMethod( ) { var code = new CodeBuilder(); var body = ASTUtils.toText(method.getCode()); + var functionName = methodFunctionName(decl, method); StringBuilder header = new StringBuilder(); if (method.getReturn() != null) { @@ -35,7 +82,7 @@ public static String generateMethod( } else { header.append("void "); } - header.append(method.getName()); + header.append(functionName); header.append("(void* instance_args"); if (method.getArguments() != null) { for (var arg : method.getArguments()) { @@ -69,4 +116,14 @@ public static String generateMethod( code.pr("}"); return code.toString(); } + + /** + * Return the function name for specified method of the specified reactor. + * @param reactor The reactor + * @param method The method. + * @return The function name for the method. + */ + private static String methodFunctionName(ReactorDecl reactor, Method method) { + return reactor.getName().toLowerCase() + "_method_" + method.getName(); + } } \ No newline at end of file diff --git a/test/C/src/target/Methods.lf b/test/C/src/target/Methods.lf index 3459ca4efb..53a78239fe 100644 --- a/test/C/src/target/Methods.lf +++ b/test/C/src/target/Methods.lf @@ -13,13 +13,13 @@ main reactor { =} reaction(startup){= - lf_print("Foo is initialized to %d", getFoo(self)); - if (getFoo(self) != 2) { + lf_print("Foo is initialized to %d", self->getFoo(self)); + if (self->getFoo(self) != 2) { lf_print_error_and_exit("Expected 2!"); } - add(self, 40); - int a = getFoo(self); + self->add(self, 40); + int a = self->getFoo(self); lf_print("2 + 40 = %d", a); if (a != 42) { lf_print_error_and_exit("Expected 42!"); diff --git a/test/C/src/target/MethodsSameName.lf b/test/C/src/target/MethodsSameName.lf new file mode 100644 index 0000000000..c026ea31c6 --- /dev/null +++ b/test/C/src/target/MethodsSameName.lf @@ -0,0 +1,38 @@ +/** This tests that reactors can have methods with the same names. */ +target C; + +reactor Foo { + + state foo:int(2); + + method add(x:int) {= + self->foo += x; + =} + + reaction(startup){= + self->add(self, 40); + lf_print("Foo: 2 + 40 = %d", self->foo); + if (self->foo != 42) { + lf_print_error_and_exit("Expected 42!"); + } + =} +} + +main reactor { + + state foo:int(2); + + a = new Foo(); + + method add(x:int) {= + self->foo += x; + =} + + reaction(startup){= + self->add(self, 40); + lf_print("Main: 2 + 40 = %d", self->foo); + if (self->foo != 42) { + lf_print_error_and_exit("Expected 42!"); + } + =} +} From fb3c1347bdcdfa912500a21ade110ada482fd7a2 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Wed, 8 Jun 2022 14:56:11 +0200 Subject: [PATCH 40/56] diagrams: Added graphical representation of reset triggers --- .../synthesis/LinguaFrancaSynthesis.java | 23 +++++++++++++++++ .../styles/LinguaFrancaShapeExtensions.java | 25 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/org.lflang.diagram/src/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java b/org.lflang.diagram/src/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java index 7131da51cd..4f7a475d19 100644 --- a/org.lflang.diagram/src/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java +++ b/org.lflang.diagram/src/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java @@ -735,6 +735,8 @@ private Collection transformReactorNetwork( boolean startupUsed = false; KNode shutdownNode = _kNodeExtensions.createNode(); boolean shutdownUsed = false; + KNode resetNode = _kNodeExtensions.createNode(); + boolean resetUsed = false; // Transform instances int index = 0; @@ -809,6 +811,11 @@ private Collection transformReactorNetwork( shutdownNode, port); shutdownUsed = true; + } else if (trigger.isReset()) { + connect(createDependencyEdge(((TriggerInstance.BuiltinTriggerVariable) trigger.getDefinition()).definition), + resetNode, + port); + resetUsed = true; } else if (trigger instanceof ActionInstance) { actionDestinations.put(((ActionInstance) trigger), port); } else if (trigger instanceof PortInstance) { @@ -1027,6 +1034,22 @@ private Collection transformReactorNetwork( }); } } + if (resetUsed) { + _linguaFrancaShapeExtensions.addResetFigure(resetNode); + _utilityExtensions.setID(resetNode, reactorInstance.uniqueID() + "_reset"); + resetNode.setProperty(REACTION_SPECIAL_TRIGGER, true); + nodes.add(startupUsed ? 1 : 0, resetNode); // after startup + // try to order with reactions vertically if in one layer + setLayoutOption(resetNode, LayeredOptions.POSITION, new KVector(0, 0.5)); + setLayoutOption(resetNode, LayeredOptions.LAYERING_LAYER_CONSTRAINT, LayerConstraint.FIRST); + + if (getBooleanValue(REACTIONS_USE_HYPEREDGES)) { // connect all edges to one port + KPort port = addInvisiblePort(resetNode); + resetNode.getOutgoingEdges().forEach(it -> { + it.setSourcePort(port); + }); + } + } // Postprocess timer nodes if (getBooleanValue(REACTIONS_USE_HYPEREDGES)) { // connect all edges to one port diff --git a/org.lflang.diagram/src/org/lflang/diagram/synthesis/styles/LinguaFrancaShapeExtensions.java b/org.lflang.diagram/src/org/lflang/diagram/synthesis/styles/LinguaFrancaShapeExtensions.java index a7f13df7f6..3ad1064275 100644 --- a/org.lflang.diagram/src/org/lflang/diagram/synthesis/styles/LinguaFrancaShapeExtensions.java +++ b/org.lflang.diagram/src/org/lflang/diagram/synthesis/styles/LinguaFrancaShapeExtensions.java @@ -584,6 +584,31 @@ public KPolygon addShutdownFigure(KNode node) { return figure; } + /** + * Creates the visual representation of a shutdown trigger. + */ + public KEllipse addResetFigure(KNode node) { + _kNodeExtensions.setMinimalNodeSize(node, 18, 18); + KEllipse figure = _kRenderingExtensions.addEllipse(node); + _kRenderingExtensions.setLineWidth(figure, 1); + _kRenderingExtensions.setBackground(figure, Colors.WHITE); + _linguaFrancaStyleExtensions.noSelectionStyle(figure); + _linguaFrancaStyleExtensions.boldLineSelectionStyle(figure); + + KEllipse inner = _kContainerRenderingExtensions.addEllipse(figure); + _kRenderingExtensions.setSurroundingSpace(inner, 2.5f, 0); + _kRenderingExtensions.setLineWidth(inner, 1); + _kRenderingExtensions.setBackground(inner, Colors.WHITE); + _linguaFrancaStyleExtensions.noSelectionStyle(inner); + + KText text = _kContainerRenderingExtensions.addText(inner, "R"); + _kRenderingExtensions.setFontSize(text, 6); + _kRenderingExtensions.setFontBold(text, true); + _linguaFrancaStyleExtensions.boldTextSelectionStyle(text); + + return figure; + } + /** * Creates the visual representation of a reactor port. */ From 065baa8785bf6dc678130b6cfc44fba12b94026d Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Wed, 8 Jun 2022 15:16:39 +0200 Subject: [PATCH 41/56] diagrams: Added graphical representation for reset state variables. Also corrected display of mode-local state variables. --- .../AbstractSynthesisExtensions.java | 5 ++++ .../synthesis/LinguaFrancaSynthesis.java | 23 ++++++++++---- .../diagram/synthesis/util/ModeDiagrams.java | 30 +++++++++++++++++++ 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/org.lflang.diagram/src/org/lflang/diagram/synthesis/AbstractSynthesisExtensions.java b/org.lflang.diagram/src/org/lflang/diagram/synthesis/AbstractSynthesisExtensions.java index 52e6b92ef3..e95472b07d 100644 --- a/org.lflang.diagram/src/org/lflang/diagram/synthesis/AbstractSynthesisExtensions.java +++ b/org.lflang.diagram/src/org/lflang/diagram/synthesis/AbstractSynthesisExtensions.java @@ -46,4 +46,9 @@ public boolean getBooleanValue(SynthesisOption option) { public T associateWith(T derived, Object source) { return delegate.associateWith(derived, source); } + + @SuppressWarnings("unchecked") + public > T getRootSynthesis() { + return (T) delegate; + } } diff --git a/org.lflang.diagram/src/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java b/org.lflang.diagram/src/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java index 4f7a475d19..c7b3fd8346 100644 --- a/org.lflang.diagram/src/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java +++ b/org.lflang.diagram/src/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java @@ -48,6 +48,7 @@ import org.eclipse.elk.core.math.ElkPadding; import org.eclipse.elk.core.math.KVector; import org.eclipse.elk.core.options.BoxLayouterOptions; +import org.eclipse.elk.core.options.ContentAlignment; import org.eclipse.elk.core.options.CoreOptions; import org.eclipse.elk.core.options.Direction; import org.eclipse.elk.core.options.PortConstraints; @@ -91,6 +92,7 @@ import org.lflang.generator.TimerInstance; import org.lflang.generator.TriggerInstance; import org.lflang.lf.Connection; +import org.lflang.lf.LfPackage; import org.lflang.lf.Model; import org.lflang.lf.Reactor; import org.lflang.lf.StateVar; @@ -359,7 +361,7 @@ private Collection createReactorNode( } if (getBooleanValue(SHOW_STATE_VARIABLES)) { - var variables = ASTUtils.allStateVars(reactor); + var variables = ASTUtils.collectElements(reactor, LfPackage.eINSTANCE.getReactor_StateVars(), true, false); if (!variables.isEmpty()) { KRectangle rectangle = _kContainerRenderingExtensions.addRectangle(figure); _kRenderingExtensions.setInvisible(rectangle, true); @@ -452,7 +454,7 @@ private Collection createReactorNode( } if (getBooleanValue(SHOW_STATE_VARIABLES)) { - var variables = ASTUtils.allStateVars(reactor); + var variables = ASTUtils.collectElements(reactor, LfPackage.eINSTANCE.getReactor_StateVars(), true, false); if (!variables.isEmpty()) { KRectangle rectangle = _kContainerRenderingExtensions.addRectangle(comps.getReactor()); _kRenderingExtensions.setInvisible(rectangle, true); @@ -584,6 +586,11 @@ private Collection createReactorNode( } private KNode configureReactorNodeLayout(KNode node) { + // Direction + setLayoutOption(node, CoreOptions.DIRECTION, Direction.RIGHT); + // Center free floating children + setLayoutOption(node, CoreOptions.CONTENT_ALIGNMENT, ContentAlignment.centerCenter()); + // Do not shrink nodes below content setLayoutOption(node, CoreOptions.NODE_SIZE_CONSTRAINTS, SizeConstraint.minimumSizeWithPorts()); // Allows to freely shuffle ports on each side setLayoutOption(node, CoreOptions.PORT_CONSTRAINTS, PortConstraints.FIXED_SIDE); @@ -1141,7 +1148,7 @@ private void addParameterList(KContainerRendering container, List variables) { + public void addStateVariableList(KContainerRendering container, List variables) { int cols = 1; try { cols = getIntValue(REACTOR_BODY_TABLE_COLS); @@ -1177,7 +1184,13 @@ private void addStateVariableList(KContainerRendering container, List private String createStateVariableLabel(StateVar variable, boolean bullet) { StringBuilder b = new StringBuilder(); if (bullet) { - b.append("\u229a "); + b.append("\u229a"); + // Reset marker + if (variable.isReset()) { + b.append("\u1d63\u2009"); + } else { + b.append("\u2009 "); + } } b.append(variable.getName()); if (variable.getType() != null) { diff --git a/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ModeDiagrams.java b/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ModeDiagrams.java index 7e6e1f20b3..4b5eda11ac 100644 --- a/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ModeDiagrams.java +++ b/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ModeDiagrams.java @@ -70,6 +70,7 @@ import de.cau.cs.kieler.klighd.kgraph.KNode; import de.cau.cs.kieler.klighd.kgraph.KPort; import de.cau.cs.kieler.klighd.krendering.Colors; +import de.cau.cs.kieler.klighd.krendering.HorizontalAlignment; import de.cau.cs.kieler.klighd.krendering.KContainerRendering; import de.cau.cs.kieler.klighd.krendering.KDecoratorPlacementData; import de.cau.cs.kieler.klighd.krendering.KEllipse; @@ -144,6 +145,7 @@ public void handleModes(List nodes, ReactorInstance reactor) { DiagramSyntheses.setLayoutOption(node, LayeredOptions.LAYERING_LAYER_CONSTRAINT, LayerConstraint.FIRST); } DiagramSyntheses.setLayoutOption(node, LayeredOptions.CROSSING_MINIMIZATION_SEMI_INTERACTIVE, true); + DiagramSyntheses.setLayoutOption(node, CoreOptions.DIRECTION, Direction.RIGHT); var expansionState = MemorizingExpandCollapseAction.getExpansionState(mode); DiagramSyntheses.setLayoutOption(node, KlighdProperties.EXPAND, @@ -165,6 +167,34 @@ public void handleModes(List nodes, ReactorInstance reactor) { _kRenderingExtensions.addDoubleClickAction(textButton, MemorizingExpandCollapseAction.ID); } + if (getBooleanValue(LinguaFrancaSynthesis.SHOW_STATE_VARIABLES)) { + // Add mode-local state variables + var variables = mode.getDefinition().getStateVars(); + if (!variables.isEmpty()) { + KRectangle rectangle = _kContainerRenderingExtensions.addRectangle(expandFigure); + _kRenderingExtensions.setInvisible(rectangle, true); + if (!getBooleanValue(LinguaFrancaSynthesis.SHOW_HYPERLINKS)) { + _kRenderingExtensions.to( + _kRenderingExtensions.from( + _kRenderingExtensions.setGridPlacementData(rectangle), + _kRenderingExtensions.LEFT, 6, 0, + _kRenderingExtensions.TOP, 0, 0), + _kRenderingExtensions.RIGHT, 6, 0, + _kRenderingExtensions.BOTTOM, 4, 0); + } else { + _kRenderingExtensions.to( + _kRenderingExtensions.from( + _kRenderingExtensions.setGridPlacementData(rectangle), + _kRenderingExtensions.LEFT, 6, 0, + _kRenderingExtensions.TOP, 4, 0), + _kRenderingExtensions.RIGHT, 6, 0, + _kRenderingExtensions.BOTTOM, 0, 0); + } + _kRenderingExtensions.setHorizontalAlignment(rectangle, HorizontalAlignment.LEFT); + this.getRootSynthesis().addStateVariableList(rectangle, variables); + } + } + _kContainerRenderingExtensions.addChildArea(expandFigure); // Collapse Rectangle From 5b84ae7228f4b8da49bbad37c494c63ab3efe0c9 Mon Sep 17 00:00:00 2001 From: eal Date: Wed, 8 Jun 2022 17:53:50 +0200 Subject: [PATCH 42/56] Reference methods as simply function calls by name. --- .../org/lflang/generator/c/CGenerator.java | 19 +-- .../lflang/generator/c/CMethodGenerator.java | 159 ++++++++++++------ .../generator/c/CReactionGenerator.java | 3 +- test/C/src/target/Methods.lf | 8 +- test/C/src/target/MethodsRecursive.lf | 23 +++ test/C/src/target/MethodsSameName.lf | 4 +- 6 files changed, 135 insertions(+), 81 deletions(-) create mode 100644 test/C/src/target/MethodsRecursive.lf diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.java b/org.lflang/src/org/lflang/generator/c/CGenerator.java index cd66928f42..f7fb41c696 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.java @@ -27,7 +27,6 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY package org.lflang.generator.c; import static org.lflang.ASTUtils.allActions; import static org.lflang.ASTUtils.allInputs; -import static org.lflang.ASTUtils.allMethods; import static org.lflang.ASTUtils.allOutputs; import static org.lflang.ASTUtils.allReactions; import static org.lflang.ASTUtils.allStateVars; @@ -90,7 +89,6 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY import org.lflang.lf.Expression; import org.lflang.lf.Input; import org.lflang.lf.Instantiation; -import org.lflang.lf.Method; import org.lflang.lf.Mode; import org.lflang.lf.Model; import org.lflang.lf.Output; @@ -1196,7 +1194,7 @@ private void generateReactorClass(ReactorDecl reactor) { var constructorCode = new CodeBuilder(); generateAuxiliaryStructs(reactor); generateSelfStruct(reactor, constructorCode); - generateMethods(reactor); + CMethodGenerator.generateMethods(reactor, code, types); generateReactions(reactor, currentFederate); generateConstructor(reactor, currentFederate, constructorCode); @@ -1347,9 +1345,6 @@ private void generateSelfStruct(ReactorDecl decl, CodeBuilder constructorCode) { // Next, generate fields for modes CModesGenerator.generateDeclarations(reactor, body, constructorCode); - // Finally, generate fields for methods. - CMethodGenerator.generateDeclarations(reactor, body, constructorCode, types); - // The first field has to always be a pointer to the list of // of allocated memory that must be freed when the reactor is freed. // This means that the struct can be safely cast to self_base_t. @@ -1494,18 +1489,6 @@ private void generateInteractingContainedReactors( } } - /** Generate method functions definition for a reactor. - * These functions have a first argument that is a void* pointing to - * the self struct. - * @param reactor The reactor. - */ - public void generateMethods(ReactorDecl decl) { - var reactor = ASTUtils.toDefinition(decl); - for (Method method : allMethods(reactor)) { - code.pr(CMethodGenerator.generateMethod(method, decl, types)); - } - } - /** * This function is provided to allow extensions of the CGenerator to append the structure of the self struct * @param body The body of the self struct diff --git a/org.lflang/src/org/lflang/generator/c/CMethodGenerator.java b/org.lflang/src/org/lflang/generator/c/CMethodGenerator.java index 089126f969..aace2ea646 100644 --- a/org.lflang/src/org/lflang/generator/c/CMethodGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CMethodGenerator.java @@ -10,54 +10,41 @@ import org.lflang.lf.ReactorDecl; /** - * Generates C code to declare and initialize methods. + * Collection of functios to generate C code to declare methods. * * @author {Edward A. Lee } */ public class CMethodGenerator { + /** - * Generate fields in the self struct for method functions and - * initialize them in the constructor. - * + * Generate macro definitions for methods. * @param reactor The reactor. - * @param body The place to put the struct declarations. - * @param constructorCode The place to put the constructor code. - * @param types The C-specific type conversion functions. + * @param body The place to put the macro definitions. */ - public static void generateDeclarations( + public static void generateMacrosForMethods( Reactor reactor, - CodeBuilder body, - CodeBuilder constructorCode, - CTypes types + CodeBuilder body ) { for (Method method : allMethods(reactor)) { - var functionName = methodFunctionName(reactor, method); - - // Construct function type signature. - StringBuilder typeSignature = new StringBuilder(); - if (method.getReturn() != null) { - typeSignature.append(types.getTargetType(InferredType.fromAST(method.getReturn()))); - } else { - typeSignature.append("void"); - } - typeSignature.append("(*"); - typeSignature.append(method.getName()); - typeSignature.append(")(void*"); - if (method.getArguments() != null) { - for (var arg : method.getArguments()) { - typeSignature.append(", "); - typeSignature.append(types.getTargetType(InferredType.fromAST(arg.getType()))); - } - } - typeSignature.append(");"); - body.pr(typeSignature); - - constructorCode.pr("self->"+method.getName()+" = "+functionName+";"); + body.pr("#define "+method.getName()+"(...) "+functionName+"(self, ##__VA_ARGS__)"); } + } + /** + * Generate macro undefinitions for methods. + * @param reactor The reactor. + * @param body The place to put the macro definitions. + */ + public static void generateMacroUndefsForMethods( + Reactor reactor, + CodeBuilder body + ) { + for (Method method : allMethods(reactor)) { + body.pr("#undef "+method.getName()); + } } - + /** * Generate a method function definition for a reactor. * This function will have a first argument that is a void* pointing to @@ -73,29 +60,10 @@ public static String generateMethod( ) { var code = new CodeBuilder(); var body = ASTUtils.toText(method.getCode()); - var functionName = methodFunctionName(decl, method); - - StringBuilder header = new StringBuilder(); - if (method.getReturn() != null) { - header.append(types.getTargetType(InferredType.fromAST(method.getReturn()))); - header.append(" "); - } else { - header.append("void "); - } - header.append(functionName); - header.append("(void* instance_args"); - if (method.getArguments() != null) { - for (var arg : method.getArguments()) { - header.append(", "); - header.append(types.getTargetType(InferredType.fromAST(arg.getType()))); - header.append(" "); - header.append(arg.getName()); - } - } - header.append(") {"); - + code.prSourceLineNumber(method); - code.pr(header); + code.prComment("Implementation of method "+method.getName()+"()"); + code.pr(generateMethodSignature(method, decl, types) + " {"); code.indent(); // Define the "self" struct. @@ -117,6 +85,49 @@ public static String generateMethod( return code.toString(); } + /** + * Generate method functions definition for a reactor. + * These functions have a first argument that is a void* pointing to + * the self struct. + * @param reactor The reactor. + * @param code The place to put the code. + * @param types The C-specific type conversion functions. + */ + public static void generateMethods( + ReactorDecl decl, + CodeBuilder code, + CTypes types + ) { + var reactor = ASTUtils.toDefinition(decl); + code.prComment("***** Start of method declarations."); + signatures(decl, code, types); + generateMacrosForMethods(reactor, code); + for (Method method : allMethods(reactor)) { + code.pr(CMethodGenerator.generateMethod(method, decl, types)); + } + generateMacroUndefsForMethods(reactor, code); + code.prComment("***** End of method declarations."); + } + + /** + * Generate function signatures for methods. + * This can be used to declare all the methods with signatures only + * before giving the full definition so that methods may call each other + * (and themselves) regardless of the order of definition. + * @param decl The reactor declaration. + * @param types The C-specific type conversion functions. + */ + public static void signatures( + ReactorDecl decl, + CodeBuilder body, + CTypes types + ) { + Reactor reactor = ASTUtils.toDefinition(decl); + for (Method method : allMethods(reactor)) { + body.pr(generateMethodSignature(method, decl, types) + ";"); + } + } + /** * Return the function name for specified method of the specified reactor. * @param reactor The reactor @@ -126,4 +137,40 @@ public static String generateMethod( private static String methodFunctionName(ReactorDecl reactor, Method method) { return reactor.getName().toLowerCase() + "_method_" + method.getName(); } + + /** + * Generate a method function signature for a reactor. + * This function will have a first argument that is a void* pointing to + * the self struct, followed by any arguments given in its definition. + * @param method The method. + * @param decl The reactor declaration. + * @param types The C-specific type conversion functions. + */ + public static String generateMethodSignature( + Method method, + ReactorDecl decl, + CTypes types + ) { + var functionName = methodFunctionName(decl, method); + + StringBuilder result = new StringBuilder(); + if (method.getReturn() != null) { + result.append(types.getTargetType(InferredType.fromAST(method.getReturn()))); + result.append(" "); + } else { + result.append("void "); + } + result.append(functionName); + result.append("(void* instance_args"); + if (method.getArguments() != null) { + for (var arg : method.getArguments()) { + result.append(", "); + result.append(types.getTargetType(InferredType.fromAST(arg.getType()))); + result.append(" "); + result.append(arg.getName()); + } + } + result.append(")"); + return result.toString(); + } } \ No newline at end of file diff --git a/org.lflang/src/org/lflang/generator/c/CReactionGenerator.java b/org.lflang/src/org/lflang/generator/c/CReactionGenerator.java index a81e90efc8..3b6180829b 100644 --- a/org.lflang/src/org/lflang/generator/c/CReactionGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CReactionGenerator.java @@ -22,7 +22,6 @@ import org.lflang.lf.Code; import org.lflang.lf.Input; import org.lflang.lf.Instantiation; -import org.lflang.lf.Method; import org.lflang.lf.Mode; import org.lflang.lf.Output; import org.lflang.lf.Port; @@ -1190,6 +1189,7 @@ public static String generateReaction( code.pr( "#include " + StringUtil.addDoubleQuotes( CCoreFilesUtils.getCTargetSetHeader())); + CMethodGenerator.generateMacrosForMethods(ASTUtils.toDefinition(decl), code); code.pr(generateFunction( generateReactionFunctionHeader(decl, reactionIndex), init, reaction.getCode() @@ -1210,6 +1210,7 @@ public static String generateReaction( generateDeadlineFunctionHeader(decl, reactionIndex), init, reaction.getDeadline().getCode())); } + CMethodGenerator.generateMacroUndefsForMethods(ASTUtils.toDefinition(decl), code); code.pr( "#include " + StringUtil.addDoubleQuotes( CCoreFilesUtils.getCTargetSetUndefHeader())); diff --git a/test/C/src/target/Methods.lf b/test/C/src/target/Methods.lf index 53a78239fe..99cdfe99f5 100644 --- a/test/C/src/target/Methods.lf +++ b/test/C/src/target/Methods.lf @@ -13,13 +13,13 @@ main reactor { =} reaction(startup){= - lf_print("Foo is initialized to %d", self->getFoo(self)); - if (self->getFoo(self) != 2) { + lf_print("Foo is initialized to %d", getFoo()); + if (getFoo() != 2) { lf_print_error_and_exit("Expected 2!"); } - self->add(self, 40); - int a = self->getFoo(self); + add(40); + int a = getFoo(); lf_print("2 + 40 = %d", a); if (a != 42) { lf_print_error_and_exit("Expected 42!"); diff --git a/test/C/src/target/MethodsRecursive.lf b/test/C/src/target/MethodsRecursive.lf new file mode 100644 index 0000000000..7b8a13a54d --- /dev/null +++ b/test/C/src/target/MethodsRecursive.lf @@ -0,0 +1,23 @@ +// Test ability of methods to call each other and (recursively) themselves. +target C; + +main reactor { + + state foo:int(2); + + // Return the n-th Fibonacci number. + method fib(n:int): int {= + if (n <= 1) return 1; + return add(fib(n-1), fib(n-2)); + =} + + method add(x:int, y:int): int {= + return x + y; + =} + + reaction(startup){= + for (int n = 1; n < 10; n++) { + lf_print("%d-th Fibonacci number is %d", n, fib(n)); + } + =} +} diff --git a/test/C/src/target/MethodsSameName.lf b/test/C/src/target/MethodsSameName.lf index c026ea31c6..2633ca2a8e 100644 --- a/test/C/src/target/MethodsSameName.lf +++ b/test/C/src/target/MethodsSameName.lf @@ -10,7 +10,7 @@ reactor Foo { =} reaction(startup){= - self->add(self, 40); + add(40); lf_print("Foo: 2 + 40 = %d", self->foo); if (self->foo != 42) { lf_print_error_and_exit("Expected 42!"); @@ -29,7 +29,7 @@ main reactor { =} reaction(startup){= - self->add(self, 40); + add(40); lf_print("Main: 2 + 40 = %d", self->foo); if (self->foo != 42) { lf_print_error_and_exit("Expected 42!"); From 4aa9170659aa0597c28bc5d930a17df9b9943733 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Wed, 8 Jun 2022 18:16:01 +0200 Subject: [PATCH 43/56] diagrams: Removed code correspondence tags from state variable types. The basic toText functions include code correspondence tags that are irrelevant and unappealing to the diagram. --- .../synthesis/LinguaFrancaSynthesis.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/org.lflang.diagram/src/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java b/org.lflang.diagram/src/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java index c7b3fd8346..00f3dd0bc8 100644 --- a/org.lflang.diagram/src/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java +++ b/org.lflang.diagram/src/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java @@ -1151,13 +1151,13 @@ private String createParameterLabel(ParameterInstance param, boolean bullet) { b.append("\u2009\u2219\u2009 "); // aligned spacing with state variables } b.append(param.getName()); - String t = param.type.toText(); - if (!StringExtensions.isNullOrEmpty(t)) { - b.append(":").append(t); + String typeName = param.type.astType != null ? ASTUtils.toOriginalText(param.type.astType) : param.type.toText(); + if (!typeName.isEmpty()) { + b.append(":").append(typeName); } if (!IterableExtensions.isNullOrEmpty(param.getInitialValue())) { b.append("("); - b.append(IterableExtensions.join(param.getInitialValue(), ", ", ASTUtils::toText)); + b.append(IterableExtensions.join(param.getInitialValue(), ", ", ASTUtils::toOriginalText)); b.append(")"); } return b.toString(); @@ -1194,12 +1194,15 @@ private String createStateVariableLabel(StateVar variable, boolean bullet) { } b.append(variable.getName()); if (variable.getType() != null) { - var t = InferredType.fromAST(variable.getType()); - b.append(":").append(t.toText()); + var inferred = InferredType.fromAST(variable.getType()); + var typeName = inferred.astType != null ? ASTUtils.toOriginalText(inferred.astType) : inferred.toText(); + if (!typeName.isEmpty()) { + b.append(":").append(typeName); + } } if (!IterableExtensions.isNullOrEmpty(variable.getInit())) { b.append("("); - b.append(IterableExtensions.join(variable.getInit(), ", ", ASTUtils::toText)); + b.append(IterableExtensions.join(variable.getInit(), ", ", ASTUtils::toOriginalText)); b.append(")"); } return b.toString(); From 5b2fd416af1f9bc6a7b40ed630fb36b6f50aad89 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Wed, 8 Jun 2022 18:42:27 +0200 Subject: [PATCH 44/56] diagrams: Adjusted symbol spacing for state variables --- .../org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/org.lflang.diagram/src/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java b/org.lflang.diagram/src/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java index 81c2b0da26..3976443181 100644 --- a/org.lflang.diagram/src/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java +++ b/org.lflang.diagram/src/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java @@ -1148,7 +1148,7 @@ private void addParameterList(KContainerRendering container, List Date: Thu, 9 Jun 2022 15:25:24 -0700 Subject: [PATCH 45/56] generate code to set negative deadline if there is no deadline specified --- org.lflang/src/lib/c/reactor-c | 2 +- .../org/lflang/generator/c/CGenerator.java | 12 ++++---- test/C/src/DeadlineZero.lf | 29 +++++++++++++++++++ 3 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 test/C/src/DeadlineZero.lf diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index da27154663..a04be222d1 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit da27154663ceb4c877d896d3009df869759bb8a9 +Subproject commit a04be222d118982130757ce87cb4a94d3e55262c diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.java b/org.lflang/src/org/lflang/generator/c/CGenerator.java index 60311b5794..867aee48db 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.java @@ -2048,12 +2048,14 @@ public void generateStateVariableInitializations(ReactorInstance instance) { */ private void generateSetDeadline(ReactorInstance instance) { for (ReactionInstance reaction : instance.reactions) { - if (reaction.declaredDeadline != null - && currentFederate.contains(reaction.getDefinition()) - ) { - var deadline = reaction.declaredDeadline.maxDelay; + if (currentFederate.contains(reaction.getDefinition())) { var selfRef = CUtil.reactorRef(reaction.getParent())+"->_lf__reaction_"+reaction.index; - initializeTriggerObjects.pr(selfRef+".deadline = "+GeneratorBase.timeInTargetLanguage(deadline)+";"); + if (reaction.declaredDeadline != null) { + var deadline = reaction.declaredDeadline.maxDelay; + initializeTriggerObjects.pr(selfRef+".deadline = "+GeneratorBase.timeInTargetLanguage(deadline)+";"); + } else { // No deadline. + initializeTriggerObjects.pr(selfRef+".deadline = NEVER;"); + } } } } diff --git a/test/C/src/DeadlineZero.lf b/test/C/src/DeadlineZero.lf new file mode 100644 index 0000000000..35e08908ca --- /dev/null +++ b/test/C/src/DeadlineZero.lf @@ -0,0 +1,29 @@ +// Tests that specified deadlines with zero duration are always violated. +target C { + timeout: 1 sec +}; + +reactor Detector { + input trigger:int; + state cnt:int(0); + reaction(trigger) {= + printf("ERROR: failed to detect zero-duration deadline at iteration %d.\n", self->cnt); + exit(1); + =} deadline(0 msec) {= + self->cnt++; + =} +} + +reactor Generator { + output pulse:int; + timer t(0, 100 msec); + reaction(t) -> pulse {= + lf_set(pulse, 0); + =} +} + +main reactor { + g = new Generator(); + d = new Detector(); + g.pulse -> d.trigger; +} From d70bc48df07bc533dd7d10210e77a2c116c056a0 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Fri, 10 Jun 2022 12:36:32 -0500 Subject: [PATCH 46/56] Update ref to reactor-c --- org.lflang/src/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index eaec0e6e14..f05be58b83 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit eaec0e6e1417531bb575f8aaf61aa6eba2909d01 +Subproject commit f05be58b838141ab2398b1f855fd8dd165a42e89 From 426495e0b9c585df0b01d70a6a6cc9d1dfd32011 Mon Sep 17 00:00:00 2001 From: eal Date: Fri, 10 Jun 2022 10:39:28 -0700 Subject: [PATCH 47/56] Updated reactor-c version --- org.lflang/src/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index da27154663..ba7d4dd949 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit da27154663ceb4c877d896d3009df869759bb8a9 +Subproject commit ba7d4dd9494afa156deb1c2a21e0903b104aada1 From 94cc4c4e88b4948f94988fdb807293c729041aea Mon Sep 17 00:00:00 2001 From: eal Date: Fri, 10 Jun 2022 12:22:06 -0700 Subject: [PATCH 48/56] Revert "Updated reactor-c version" This reverts commit 426495e0b9c585df0b01d70a6a6cc9d1dfd32011. --- org.lflang/src/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index ba7d4dd949..da27154663 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit ba7d4dd9494afa156deb1c2a21e0903b104aada1 +Subproject commit da27154663ceb4c877d896d3009df869759bb8a9 From 641787d12fe3b8b24ae5634007a11d239b219b4c Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Fri, 10 Jun 2022 14:15:59 -0700 Subject: [PATCH 49/56] Update reactor-c submodule pointer --- org.lflang/src/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index caea176947..ba7d4dd949 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit caea176947964f445f999d04564cc8c667d25b0b +Subproject commit ba7d4dd9494afa156deb1c2a21e0903b104aada1 From 49040c968dbd0bdf01128f614dd575bc4e071ea4 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Fri, 10 Jun 2022 16:20:58 -0500 Subject: [PATCH 50/56] Updated ref to reactor-c --- org.lflang/src/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index f05be58b83..2b1d3ecb90 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit f05be58b838141ab2398b1f855fd8dd165a42e89 +Subproject commit 2b1d3ecb90365b4e0ee6522babbb2a0176299532 From 1924b8072404e0015f931e9d62bbff75decdc2e3 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Fri, 10 Jun 2022 16:22:10 -0500 Subject: [PATCH 51/56] Updated reference to reactor-c --- org.lflang/src/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index 2b1d3ecb90..445b2c8195 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 2b1d3ecb90365b4e0ee6522babbb2a0176299532 +Subproject commit 445b2c8195543e1126c2a40bc2cfdccbc9a86138 From 49fa145ab05bc58047170051409225698a8aa57e Mon Sep 17 00:00:00 2001 From: Billy Bao Date: Fri, 10 Jun 2022 14:47:49 -0700 Subject: [PATCH 52/56] update reactor-c refs --- org.lflang/src/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index c8d6cb0cef..4ea5e26770 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit c8d6cb0cefd90bb27e331bc7c53f72f440e80357 +Subproject commit 4ea5e26770ad3b21a1feee6223f4c8b694617f3e From ee6de732b839c410b0a0e9e9f0799b5fd4345930 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Fri, 10 Jun 2022 19:08:56 -0500 Subject: [PATCH 53/56] Updated ref to reactor-c --- org.lflang/src/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index 445b2c8195..47c70ed3e8 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 445b2c8195543e1126c2a40bc2cfdccbc9a86138 +Subproject commit 47c70ed3e835a8363a1b97d05fc1ea66c9e66b43 From 8e6ffa46292ab69b926c28dff05931e823de0b7c Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Sat, 11 Jun 2022 00:30:59 -0500 Subject: [PATCH 54/56] Updated branch pointers in the CI --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1dad55e8eb..2d9ae662d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,12 +54,12 @@ jobs: # Run the C integration tests. c-tests: - uses: lf-lang/lingua-franca/.github/workflows/c-tests.yml@hotfix-C-TAN + uses: lf-lang/lingua-franca/.github/workflows/c-tests.yml@master needs: cancel # Run the CCpp integration tests. ccpp-tests: - uses: lf-lang/lingua-franca/.github/workflows/c-tests.yml@hotfix-C-TAN + uses: lf-lang/lingua-franca/.github/workflows/c-tests.yml@master with: use-cpp: true needs: cancel @@ -83,7 +83,7 @@ jobs: # Run the Python integration tests. py-tests: - uses: lf-lang/lingua-franca/.github/workflows/py-tests.yml@hotfix-C-TAN + uses: lf-lang/lingua-franca/.github/workflows/py-tests.yml@master needs: cancel # Run the Rust integration tests. @@ -105,5 +105,5 @@ jobs: # Run the serialization tests serialization-tests: - uses: lf-lang/lingua-franca/.github/workflows/serialization-tests.yml@hotfix-C-TAN + uses: lf-lang/lingua-franca/.github/workflows/serialization-tests.yml@master needs: cancel From dc4781a076c57513af957d66fcf62705e9be436a Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Sat, 11 Jun 2022 13:39:15 -0700 Subject: [PATCH 55/56] Machine-applicable changes. --- .../LinguaFrancaDependencyAnalysisTest.java | 44 +++---- .../compiler/LinguaFrancaScopingTest.java | 114 +++++------------- .../src/org/lflang/generator/CodeBuilder.java | 68 ++++------- .../org/lflang/generator/GeneratorBase.java | 74 ++++++------ .../lflang/generator/ReactionInstance.java | 33 +++-- .../generator/ReactionInstanceGraph.java | 7 +- .../org/lflang/generator/ReactorInstance.java | 20 ++- .../org/lflang/generator/RuntimeRange.java | 34 +++--- .../src/org/lflang/generator/SendRange.java | 13 +- .../lflang/generator/c/CActionGenerator.java | 2 +- .../lflang/generator/c/CCmakeCompiler.java | 18 +-- .../lflang/generator/c/CCmakeGenerator.java | 15 +-- .../src/org/lflang/generator/c/CCompiler.java | 4 +- .../org/lflang/generator/c/CGenerator.java | 45 +++---- .../lflang/generator/c/CMethodGenerator.java | 4 +- .../lflang/generator/c/CNetworkGenerator.java | 2 +- .../generator/c/CParameterGenerator.java | 1 - .../lflang/generator/c/CPortGenerator.java | 2 +- .../generator/c/CPreambleGenerator.java | 11 +- .../generator/c/CReactionGenerator.java | 47 +++----- .../lflang/generator/c/CStateGenerator.java | 1 - .../lflang/generator/c/CTimerGenerator.java | 17 ++- .../generator/c/CTriggerObjectsGenerator.java | 18 +-- .../src/org/lflang/generator/c/CTypes.java | 2 +- .../src/org/lflang/generator/c/CUtil.java | 72 +++++------ .../c/InteractingContainedReactors.java | 22 +--- 26 files changed, 274 insertions(+), 416 deletions(-) diff --git a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaDependencyAnalysisTest.java b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaDependencyAnalysisTest.java index 28ab59d749..6b56c5c108 100644 --- a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaDependencyAnalysisTest.java +++ b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaDependencyAnalysisTest.java @@ -154,36 +154,20 @@ public void cyclicDependency() throws Exception { */ @Test public void circularInstantiation() throws Exception { -// Java 17: -// String testCase = """ -// target C; -// -// reactor X { -// reaction() {= -// // -// =} -// p = new Y(); -// } -// -// reactor Y { -// q = new X(); -// } -// """ -// Java 11: - String testCase = String.join(System.getProperty("line.separator"), - "target C;", - "", - "reactor X {", - " reaction() {=", - " //", - " =}", - " p = new Y();", - "}", - "", - "reactor Y {", - " q = new X();", - "}" - ); + String testCase = """ + target C; + + reactor X { + reaction() {= + // + =} + p = new Y(); + } + + reactor Y { + q = new X(); + } + """; Model model = parser.parse(testCase); Assertions.assertNotNull(model); diff --git a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaScopingTest.java b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaScopingTest.java index 70c3880a49..8587675479 100644 --- a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaScopingTest.java +++ b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaScopingTest.java @@ -123,40 +123,21 @@ public void unresolvedReactorReference() throws Exception { */ @Test public void unresolvedHierarchicalPortReference() throws Exception { -// Java 17: -// Model model = """ -// target C; -// reactor From { -// output y:int; -// } -// reactor To { -// input x:int; -// } -// -// main reactor { -// a = new From(); -// d = new To(); -// a.x -> d.y; -// } -// """; -// Java 11: - - Model model = parser.parse(String.join( - System.getProperty("line.separator"), - "target C;", - "reactor From {", - " output y:int;", - "}", - "reactor To {", - " input x:int;", - "}", - "main reactor {", - " a = new From();", - " d = new To();", - " a.x -> d.y;", - "}" - )); - + Model model = parser.parse(""" + target C; + reactor From { + output y:int; + } + reactor To { + input x:int; + } + + main reactor { + a = new From(); + d = new To(); + a.x -> d.y; + } + """); Assertions.assertNotNull(model); Assertions.assertTrue(model.eResource().getErrors().isEmpty(), @@ -171,21 +152,12 @@ public void unresolvedHierarchicalPortReference() throws Exception { @Test public void unresolvedReferenceInTriggerClause() throws Exception { -// Java 17: -// Model model = """ -// target C; -// main reactor { -// reaction(unknown) {==} -// } -// """ -// Java 11: - Model model = parser.parse(String.join( - System.getProperty("line.separator"), - "target C;", - "main reactor {", - " reaction(unknown) {==}", - "}" - )); + Model model = parser.parse(""" + target C; + main reactor { + reaction(unknown) {==} + } + """); validator.assertError(model, LfPackage.eINSTANCE.getVarRef(), XtextLinkingDiagnostic.LINKING_DIAGNOSTIC, @@ -194,21 +166,12 @@ public void unresolvedReferenceInTriggerClause() throws Exception { @Test public void unresolvedReferenceInUseClause() throws Exception { -// Java 17: -// Model model = """ -// target C; -// main reactor { -// reaction() unknown {==} -// } -// """ -// Java 11: - Model model = parser.parse(String.join( - System.getProperty("line.separator"), - "target C;", - "main reactor {", - " reaction() unknown {==}", - "}" - )); + Model model = parser.parse(""" + target C; + main reactor { + reaction() unknown {==} + } + """); validator.assertError(model, LfPackage.eINSTANCE.getVarRef(), @@ -218,24 +181,13 @@ public void unresolvedReferenceInUseClause() throws Exception { @Test public void unresolvedReferenceInEffectsClause() throws Exception { -// Java 17: -// Model model = """ -// target C; -// main reactor { -// reaction() -> unknown {==} -// } -// """ -// Java 11: - - Model model = parser.parse(String.join( - System.getProperty("line.separator"), - "target C;", - "main reactor {", - " reaction() -> unknown {==}", - "}" - )); + Model model = parser.parse(""" + target C; + main reactor { + reaction() -> unknown {==} + } + """); - validator.assertError(model, LfPackage.eINSTANCE.getVarRef(), XtextLinkingDiagnostic.LINKING_DIAGNOSTIC, "Couldn't resolve reference to Variable 'unknown'."); diff --git a/org.lflang/src/org/lflang/generator/CodeBuilder.java b/org.lflang/src/org/lflang/generator/CodeBuilder.java index 52d2cc9f83..ba7717c53c 100644 --- a/org.lflang/src/org/lflang/generator/CodeBuilder.java +++ b/org.lflang/src/org/lflang/generator/CodeBuilder.java @@ -1,12 +1,7 @@ package org.lflang.generator; import java.nio.file.Path; -import java.io.BufferedWriter; -import java.io.FileWriter; import java.io.IOException; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.eclipse.emf.common.CommonPlugin; import org.eclipse.emf.common.util.URI; @@ -41,7 +36,7 @@ public CodeBuilder() {} */ public CodeBuilder(CodeBuilder model) { indentation = model.indentation; - code.append(model.toString()); + code.append(model); } ///////////////////////////////////////////// @@ -174,8 +169,7 @@ public CodeBuilder removeLines(String prefix) { /** * Start a scoped block, which is a section of code * surrounded by curley braces and indented. - * This must be followed by an {@link endScopedBlock(StringBuilder)}. - * @param builder The code emitter into which to write. + * This must be followed by an {@link #endScopedBlock()}. */ public void startScopedBlock() { pr("{"); @@ -186,7 +180,7 @@ public void startScopedBlock() { * Start a scoped block for the specified reactor. * If the reactor is a bank, then this starts a for loop * that iterates over the bank members using a standard index - * variable whose name is that returned by {@link CUtil.bankIndex(ReactorInstance)}. + * variable whose name is that returned by {@link CUtil#bankIndex(ReactorInstance)}. * If the reactor is null or is not a bank, then this simply * starts a scoped block by printing an opening curly brace. * This also adds a declaration of a pointer to the self @@ -197,9 +191,8 @@ public void startScopedBlock() { * This ensures that all (possibly nested) bank index variables * are defined within the block. * - * This must be followed by an {@link endScopedBlock(StringBuilder)}. - * - * @param builder The place to write the code. + * This must be followed by an {@link #endScopedBlock()}. + * * @param reactor The reactor instance. * @param restrict For federated execution only, if this is true, then * skip iterations where the topmost bank member is not in the federate. @@ -229,10 +222,9 @@ public void startScopedBlock( /** * If the specified port is a multiport, then start a specified iteration * over the channels of the multiport using as the channel index the - * variable name returned by {@link CUtil.channelIndex(PortInstance)}. + * variable name returned by {@link CUtil#channelIndex(PortInstance)}. * If the port is not a multiport, do nothing. - * This is required to be followed by {@link endChannelIteration(StringBuilder, PortInstance}. - * @param builder Where to write the code. + * This is required to be followed by {@link #endChannelIteration(PortInstance)}. * @param port The port. */ public void startChannelIteration(PortInstance port) { @@ -246,17 +238,16 @@ public void startChannelIteration(PortInstance port) { /** * Start a scoped block to iterate over bank members and - * channels for the specified port with a a variable with + * channels for the specified port with a variable with * the name given by count counting the iterations. * If this port is a multiport, then the channel index - * variable name is that returned by {@link CUtil.channelIndex(PortInstance)}. + * variable name is that returned by {@link CUtil#channelIndex(PortInstance)}. * * This block is intended to be nested, where each block is * put within a similar block for the reactor's parent. * * This is required to be followed by a call to - * {@link endScopedBankChannelIteration(StringBuilder, PortInstance, String)}. - * @param builder Where to write the code. + * {@link #endScopedBankChannelIteration(PortInstance, String)}. * @param port The port. * @param count The variable name to use for the counter, or * null to not provide a counter. @@ -277,24 +268,23 @@ public void startScopedBankChannelIteration( * Start a scoped block that iterates over the specified range of port channels. * * This must be followed by a call to - * {@link #endScopedRangeBlock(StringBuilder, RuntimeRange)}. + * {@link #endScopedRangeBlock(RuntimeRange, boolean)}. * * This block should NOT be nested, where each block is * put within a similar block for the reactor's parent. * Within the created block, every use of - * {@link CUtil.reactorRef(ReactorInstance, String)} + * {@link CUtil#reactorRef(ReactorInstance, String)} * must provide the second argument, a runtime index variable name, * that must match the runtimeIndex parameter given here. - * - * @param builder Where to write the code. + * * @param range The range of port channels. * @param runtimeIndex A variable name to use to index the runtime instance of * either port's parent or the port's parent's parent (if nested is true), or * null to use the default, "runtime_index". * @param bankIndex A variable name to use to index the bank of the port's parent or null to use the - * default, the string returned by {@link CUtil.bankIndexName(ReactorInstance)}. + * default, the string returned by {@link CUtil#bankIndexName(ReactorInstance)}. * @param channelIndex A variable name to use to index the channel or null to - * use the default, the string returned by {@link CUtil.channelIndexName(PortInstance)}. + * use the default, the string returned by {@link CUtil#channelIndexName(PortInstance)}. * @param nested If true, then the runtimeIndex variable will be set * to the bank index of the port's parent's parent rather than the * port's parent. @@ -398,13 +388,12 @@ public void startScopedRangeBlock( * This block should NOT be nested, where each block is * put within a similar block for the reactor's parent. * Within the created block, every use of - * {@link CUtil.reactorRef(ReactorInstance, String, String)} + * {@link CUtil#reactorRef(ReactorInstance, String)} * and related functions must provide the above variable names. * * This must be followed by a call to - * {@link #endScopedRangeBlock(StringBuilder, SendRange, RuntimeRange)}. - * - * @param builder Where to write the code. + * {@link #endScopedRangeBlock(SendRange, RuntimeRange, boolean)}. + * * @param srcRange The send range. * @param dstRange The destination range. */ @@ -419,7 +408,7 @@ public void startScopedRangeBlock( var srcNestedLevel = (srcRange.instance.isInput()) ? 2 : 1; var dstNested = dstRange.instance.isOutput(); - pr("// Iterate over ranges "+srcRange.toString()+" and "+dstRange.toString()+"."); + pr("// Iterate over ranges "+srcRange+" and "+dstRange+"."); if (isFederated && srcRange.width == 1) { // Skip this whole block if the src is not in the federate. @@ -474,13 +463,7 @@ public void startScopedRangeBlock( } } - - /** - * End a scoped block. - * @param builder The place to write the code. - */ public void endScopedBlock() { - // NOTE: This is protected because it is used by the PythonGenerator. unindent(); pr("}"); } @@ -488,10 +471,8 @@ public void endScopedBlock() { /** * If the specified port is a multiport, then start a specified iteration * over the channels of the multiport using as the channel index the - * variable name returned by {@link CUtil.channelIndex(PortInstance)}. + * variable name returned by {@link CUtil#channelIndex(PortInstance)}. * If the port is not a multiport, do nothing. - * This is required to be followed by {@link endChannelIteration(StringBuilder, PortInstance}. - * @param builder Where to write the code. * @param port The port. */ public void endChannelIteration(PortInstance port) { @@ -503,9 +484,8 @@ public void endChannelIteration(PortInstance port) { /** * End a scoped block to iterate over bank members and - * channels for the specified port with a a variable with + * channels for the specified port with a variable with * the name given by count counting the iterations. - * @param builder Where to write the code. * @param port The port. * @param count The variable name to use for the counter, or * null to not provide a counter. @@ -525,7 +505,6 @@ public void endScopedBankChannelIteration( /** * End a scoped block for the specified range. - * @param builder Where to write the code. * @param range The send range. */ public void endScopedRangeBlock( @@ -545,8 +524,7 @@ public void endScopedRangeBlock( /** * End a scoped block that iterates over the specified pair of ranges. - * - * @param builder Where to write the code. + * * @param srcRange The send range. * @param dstRange The destination range. */ @@ -613,7 +591,7 @@ public CodeMap writeToFile(String path) throws IOException { //// Private fields. /** Place to store the code. */ - private StringBuilder code = new StringBuilder(); + private final StringBuilder code = new StringBuilder(); /** Current indentation. */ private String indentation = ""; diff --git a/org.lflang/src/org/lflang/generator/GeneratorBase.java b/org.lflang/src/org/lflang/generator/GeneratorBase.java index 1c882b2671..3f2bca26af 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorBase.java +++ b/org.lflang/src/org/lflang/generator/GeneratorBase.java @@ -36,7 +36,6 @@ import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; import org.eclipse.core.resources.IMarker; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; @@ -102,7 +101,7 @@ public abstract class GeneratorBase extends AbstractLFValidator { */ public ReactorInstance main; - /** A error reporter for reporting any errors or warnings during the code generation */ + /** An error reporter for reporting any errors or warnings during the code generation */ public ErrorReporter errorReporter; //////////////////////////////////////////// @@ -248,7 +247,7 @@ public GeneratorBase(FileConfig fileConfig, ErrorReporter errorReporter) { /** * Store the given reactor in the collection of generated delay classes - * and insert it in the AST under the top-level reactors node. + * and insert it in the AST under the top-level reactor's node. */ public void addDelayClass(Reactor generatedDelay) { // Record this class, so it can be reused. @@ -275,7 +274,7 @@ private void createMainInstantiation() { Iterable nodes = IteratorExtensions.toIterable(fileConfig.resource.getAllContents()); for (Reactor reactor : Iterables.filter(nodes, Reactor.class)) { if (reactor.isMain() || reactor.isFederated()) { - // Creating an definition for the main reactor because there isn't one. + // Creating a definition for the main reactor because there isn't one. this.mainDef = LfFactory.eINSTANCE.createInstantiation(); this.mainDef.setName(reactor.getName()); this.mainDef.setReactorClass(reactor); @@ -289,7 +288,7 @@ private void createMainInstantiation() { * This is the main entry point for code generation. This base class finds all * reactor class definitions, including any reactors defined in imported .lf files * (except any main reactors in those imported files), and adds them to the - * {@link #GeneratorBase.reactors reactors} list. If errors occur during + * {@link GeneratorBase#reactors reactors} list. If errors occur during * generation, then a subsequent call to errorsOccurred() will return true. * @param resource The resource containing the source code. * @param context Context relating to invocation of the code generator. @@ -346,7 +345,7 @@ public void doGenerate(Resource resource, LFGeneratorContext context) { resources.addAll(allResources.stream() // FIXME: This filter reproduces the behavior of the method it replaces. But why must it be so complicated? Why are we worried about weird corner cases like this? .filter(it -> !Objects.equal(it, fileConfig.resource) || mainDef != null && it == mainDef.getReactorClass().eResource()) .map(it -> GeneratorUtils.getLFResource(it, fileConfig.getSrcGenBasePath(), context, errorReporter)) - .collect(Collectors.toList()) + .toList() ); GeneratorUtils.accommodatePhysicalActionsIfPresent( allResources, @@ -457,16 +456,16 @@ public boolean errorsOccurred() { /** * Generate code for the body of a reaction that takes an input and * schedules an action with the value of that input. - * @param the action to schedule - * @param the port to read from + * @param action the action to schedule + * @param port the port to read from */ public abstract String generateDelayBody(Action action, VarRef port); /** * Generate code for the body of a reaction that is triggered by the * given action and writes its value to the given port. - * @param the action that triggers the reaction - * @param the port to write to + * @param action the action that triggers the reaction + * @param port the port to write to */ public abstract String generateForwardBody(Action action, VarRef port); @@ -489,7 +488,7 @@ public boolean errorsOccurred() { * @return True if the reaction has been marked unordered. */ public boolean isUnordered(Reaction reaction) { - return unorderedReactions != null ? unorderedReactions.contains(reaction) : false; + return unorderedReactions != null && unorderedReactions.contains(reaction); } /** @@ -506,7 +505,7 @@ public boolean isUnordered(Reaction reaction) { */ public void makeUnordered(Reaction reaction) { if (unorderedReactions == null) { - unorderedReactions = new LinkedHashSet(); + unorderedReactions = new LinkedHashSet<>(); } unorderedReactions.add(reaction); } @@ -517,7 +516,7 @@ public void makeUnordered(Reaction reaction) { * a specific bank index as an effect or trigger. Reactions that * send messages between federates, including absent messages, * need to be specific to a bank member. - * @param The reaction. + * @param reaction The reaction. * @param bankIndex The bank index, or -1 if there is no bank. */ public void setReactionBankIndex(Reaction reaction, int bankIndex) { @@ -525,15 +524,15 @@ public void setReactionBankIndex(Reaction reaction, int bankIndex) { return; } if (reactionBankIndices == null) { - reactionBankIndices = new LinkedHashMap(); + reactionBankIndices = new LinkedHashMap<>(); } reactionBankIndices.put(reaction, bankIndex); } /** * Return the reaction bank index. - * @see setReactionBankIndex(Reaction reaction, int bankIndex) - * @param The reaction. + * @see #setReactionBankIndex(Reaction reaction, int bankIndex) + * @param reaction The reaction. * @return The reaction bank index, if one has been set, and -1 otherwise. */ public int getReactionBankIndex(Reaction reaction) { @@ -565,7 +564,7 @@ public static String timeInTargetLanguage(TimeValue time) { } // note that this is moved out by #544 - public static final String cMacroName(TimeUnit unit) { + public static String cMacroName(TimeUnit unit) { return unit.getCanonicalName().toUpperCase(); } @@ -626,7 +625,7 @@ private void transformConflictingConnectionsInModalReactors() { * Return target code for forwarding reactions iff the connections have the * same destination as other connections or reaction in mutually exclusive modes. * - * This methods needs to be overridden in target specific code generators that + * This method needs to be overridden in target specific code generators that * support modal reactors. */ protected String getConflictingConnectionsInModalReactorsBody(String source, String dest) { @@ -711,7 +710,7 @@ public String generateNetworkSenderBody( * Generate code for the body of a reaction that waits long enough so that the status * of the trigger for the given port becomes known for the current logical time. * - * @param port The port to generate the control reaction for + * @param receivingPortID port The port to generate the control reaction for * @param maxSTP The maximum value of STP is assigned to reactions (if any) * that have port as their trigger or source */ @@ -961,7 +960,7 @@ protected void removeRemoteFederateConnectionPorts(ReactorInstance instance) { * Set the RTI hostname, port and username if given as compiler arguments */ private void setFederationRTIProperties(LFGeneratorContext context) { - String rtiAddr = context.getArgs().getProperty("rti").toString(); + String rtiAddr = context.getArgs().getProperty("rti"); Pattern pattern = Pattern.compile("([a-zA-Z0-9]+@)?([a-zA-Z0-9]+\\.?[a-z]{2,}|[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+):?([0-9]+)?"); Matcher matcher = pattern.matcher(rtiAddr); @@ -1075,15 +1074,15 @@ private void analyzeFederates(LFGeneratorContext context) { * federateInstance.dir = instantiation.getHost().dir */ if (federateInstance.host != null && - federateInstance.host != "localhost" && - federateInstance.host != "0.0.0.0" + !federateInstance.host.equals("localhost") && + !federateInstance.host.equals("0.0.0.0") ) { federateInstance.isRemote = true; } } } if (federatesByInstantiation == null) { - federatesByInstantiation = new LinkedHashMap>(); + federatesByInstantiation = new LinkedHashMap<>(); } federatesByInstantiation.put(instantiation, federateInstances); } @@ -1131,7 +1130,6 @@ private void replaceFederateConnectionsWithActions() { /** * Replace the connections from the specified output port for the specified federate reactor. * @param output The output port instance. - * @param srcFederate The federate for which this port is an output. * @param federateReactor The reactor instance for that federate. * @param mainInstance The main reactor instance. */ @@ -1168,16 +1166,15 @@ private void replaceConnectionFromFederate( errorReporter.reportError(output.definition, "Unexpected error. Cannot find output connection for port"); } else { - if (!connection.isPhysical() - && targetConfig.coordination != CoordinationType.DECENTRALIZED) { + if ( + !connection.isPhysical() + && targetConfig.coordination != CoordinationType.DECENTRALIZED + ) { // Map the delays on connections between federates. - // First see if the cache has been created. - Set dependsOnDelays = dstFederate.dependsOn.get(srcFederate); - if (dependsOnDelays == null) { - // If not, create it. - dependsOnDelays = new LinkedHashSet(); - dstFederate.dependsOn.put(srcFederate, dependsOnDelays); - } + Set dependsOnDelays = dstFederate.dependsOn.computeIfAbsent( + srcFederate, + k -> new LinkedHashSet<>() + ); // Put the delay on the cache. if (connection.getDelay() != null) { dependsOnDelays.add(connection.getDelay()); @@ -1186,11 +1183,10 @@ private void replaceConnectionFromFederate( dependsOnDelays.add(null); } // Map the connections between federates. - Set sendsToDelays = srcFederate.sendsTo.get(dstFederate); - if (sendsToDelays == null) { - sendsToDelays = new LinkedHashSet(); - srcFederate.sendsTo.put(dstFederate, sendsToDelays); - } + Set sendsToDelays = srcFederate.sendsTo.computeIfAbsent( + dstFederate, + k -> new LinkedHashSet<>() + ); if (connection.getDelay() != null) { sendsToDelays.add(connection.getDelay()); } else { @@ -1238,7 +1234,7 @@ public void printInfo(LFGeneratorContext.Mode mode) { * Indicates whether delay banks generated from after delays should have a variable length width. * * If this is true, any delay reactors that are inserted for after delays on multiport connections - * will have a unspecified variable length width. The code generator is then responsible for inferring the + * will have an unspecified variable length width. The code generator is then responsible for inferring the * correct width of the delay bank, which is only possible if the precise connection width is known at compile time. * * If this is false, the width specification of the generated bank will list all the ports listed on the right diff --git a/org.lflang/src/org/lflang/generator/ReactionInstance.java b/org.lflang/src/org/lflang/generator/ReactionInstance.java index 11c84983d6..6465a74a18 100644 --- a/org.lflang/src/org/lflang/generator/ReactionInstance.java +++ b/org.lflang/src/org/lflang/generator/ReactionInstance.java @@ -50,7 +50,7 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * is a bank of reactors, then there will be more than one runtime instance * corresponding to this compile-time instance. The {@link #getRuntimeInstances()} * method returns a list of these runtime instances, each an instance of the - * inner class {@link #Runtime}. Each runtime instance has a "level", which is + * inner class {@link ReactionInstance.Runtime}. Each runtime instance has a "level", which is * its depth an acyclic precedence graph representing the dependencies between * reactions at a tag. * @@ -207,18 +207,16 @@ public ReactionInstance( /** * The ports or actions that this reaction may write to. */ - public Set> effects - = new LinkedHashSet>(); + public Set> effects = new LinkedHashSet<>(); /** * The ports, actions, or timers that this reaction is triggered by or uses. */ - public Set> sources - = new LinkedHashSet>(); + public Set> sources = new LinkedHashSet<>(); // FIXME: Above sources is misnamed because in the grammar, // "sources" are only the inputs a reaction reads without being // triggered by them. The name "reads" used here would be a better - // choice in the grammer. + // choice in the grammar. /** * Deadline for this reaction instance, if declared. @@ -253,15 +251,13 @@ public ReactionInstance( /** * The ports that this reaction reads but that do not trigger it. */ - public Set> reads - = new LinkedHashSet>(); + public Set> reads = new LinkedHashSet<>(); /** * The trigger instances (input ports, timers, and actions * that trigger reactions) that trigger this reaction. */ - public Set> triggers - = new LinkedHashSet>(); + public Set> triggers = new LinkedHashSet<>(); ////////////////////////////////////////////////////// //// Public methods. @@ -282,14 +278,14 @@ public void clearCaches(boolean includingRuntimes) { /** * Return the set of immediate downstream reactions, which are reactions - * that receive data produced produced by this reaction plus + * that receive data produced by this reaction plus * at most one reaction in the same reactor whose definition * lexically follows this one (unless this reaction is unordered). */ public Set dependentReactions() { // Cache the result. if (dependentReactionsCache != null) return dependentReactionsCache; - dependentReactionsCache = new LinkedHashSet(); + dependentReactionsCache = new LinkedHashSet<>(); // First, add the next lexical reaction, if appropriate. if (!isUnordered && parent.reactions.size() > index + 1) { @@ -322,7 +318,7 @@ public Set dependentReactions() { public Set dependsOnReactions() { // Cache the result. if (dependsOnReactionsCache != null) return dependsOnReactionsCache; - dependsOnReactionsCache = new LinkedHashSet(); + dependsOnReactionsCache = new LinkedHashSet<>(); // First, add the previous lexical reaction, if appropriate. if (!isUnordered && index > 0) { @@ -358,7 +354,7 @@ public Set dependsOnReactions() { * a bank and its dependencies on other reactions pass through multiports. */ public Set getLevels() { - Set result = new LinkedHashSet(); + Set result = new LinkedHashSet<>(); // Force calculation of levels if it has not been done. parent.assignLevels(); for (Runtime runtime : runtimeInstances) { @@ -374,7 +370,7 @@ public Set getLevels() { * a bank and its dependencies on other reactions pass through multiports. */ public List getLevelsList() { - List result = new LinkedList(); + List result = new LinkedList<>(); // Force calculation of levels if it has not been done. parent.assignLevels(); for (Runtime runtime : runtimeInstances) { @@ -418,7 +414,7 @@ public List getRuntimeInstances() { int size = parent.getTotalWidth(); // If the width cannot be determined, assume there is only one instance. if (size < 0) size = 1; - runtimeInstances = new ArrayList(size); + runtimeInstances = new ArrayList<>(size); for (int i = 0; i < size; i++) { Runtime r = new Runtime(); r.id = i; @@ -492,10 +488,9 @@ public ReactionInstance getReaction() { } @Override public String toString() { - String result = ReactionInstance.this.toString() - + "(level: " + level; + String result = ReactionInstance.this + "(level: " + level; if (deadline != null && deadline != TimeValue.MAX_VALUE) { - result += ", deadline: " + deadline.toString(); + result += ", deadline: " + deadline; } if (dominating != null) { result += ", dominating: " + dominating.getReaction(); diff --git a/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java b/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java index 76ed5bd633..4b76c29ff8 100644 --- a/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java +++ b/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java @@ -243,8 +243,7 @@ protected void addNodesAndEdges(ReactorInstance reactor) { * Number of reactions per level, represented as a list of * integers where the indices are the levels. */ - private List numReactionsPerLevel = new ArrayList<>( - List.of(Integer.valueOf(0))); + private List numReactionsPerLevel = new ArrayList<>(List.of(0)); /////////////////////////////////////////////////////////// //// Private methods @@ -260,7 +259,7 @@ protected void addNodesAndEdges(ReactorInstance reactor) { * the levels of the reactions it depends on. */ private void assignLevels() { - List start = new ArrayList(rootNodes()); + List start = new ArrayList<>(rootNodes()); // All root nodes start with level 0. for (Runtime origin : start) { @@ -271,7 +270,7 @@ private void assignLevels() { // the graph must be cyclic. while (!start.isEmpty()) { Runtime origin = start.remove(0); - Set toRemove = new LinkedHashSet(); + Set toRemove = new LinkedHashSet<>(); Set downstreamAdjacentNodes = getDownstreamAdjacentNodes(origin); // Visit effect nodes. diff --git a/org.lflang/src/org/lflang/generator/ReactorInstance.java b/org.lflang/src/org/lflang/generator/ReactorInstance.java index 48c3decf0b..c7d169ab23 100644 --- a/org.lflang/src/org/lflang/generator/ReactorInstance.java +++ b/org.lflang/src/org/lflang/generator/ReactorInstance.java @@ -54,9 +54,7 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY import org.lflang.lf.Reaction; import org.lflang.lf.Reactor; import org.lflang.lf.ReactorDecl; -import org.lflang.lf.Time; import org.lflang.lf.Timer; -import org.lflang.lf.TriggerRef; import org.lflang.lf.VarRef; import org.lflang.lf.Variable; import org.lflang.lf.WidthSpec; @@ -246,13 +244,13 @@ public void clearCaches(boolean includingRuntimes) { public Set> getCycles() { if (depth != 0) return root().getCycles(); if (cachedCycles != null) return cachedCycles; - Set reactions = new LinkedHashSet(); + Set reactions = new LinkedHashSet<>(); ReactionInstanceGraph reactionRuntimes = assignLevels(); for (ReactionInstance.Runtime runtime : reactionRuntimes.nodes()) { reactions.add(runtime.getReaction()); } - Set ports = new LinkedHashSet(); + Set ports = new LinkedHashSet<>(); // Need to figure out which ports are involved in the cycles. // It may not be all ports that depend on this reaction. for (ReactionInstance r : reactions) { @@ -263,7 +261,7 @@ public Set> getCycles() { } } - cachedCycles = new LinkedHashSet>(); + cachedCycles = new LinkedHashSet<>(); cachedCycles.addAll(reactions); cachedCycles.addAll(ports); return cachedCycles; @@ -400,7 +398,7 @@ public Set> getTriggersAndReads() { * Return true if the top-level parent of this reactor has causality cycles. */ public boolean hasCycles() { - return (assignLevels().nodeCount() != 0); + return assignLevels().nodeCount() != 0; } /** @@ -467,7 +465,7 @@ public List instantiations() { * @return true if a reactor is a bank, false otherwise */ public boolean isBank() { - return (definition.getWidthSpec() != null); + return definition.getWidthSpec() != null; } /** @@ -995,8 +993,8 @@ private boolean findPaths( private List> listPortInstances( List references, Connection connection ) { - List> result = new ArrayList>(); - List> tails = new LinkedList>(); + List> result = new ArrayList<>(); + List> tails = new LinkedList<>(); int count = 0; for (VarRef portRef : references) { // Simple error checking first. @@ -1017,7 +1015,7 @@ private List> listPortInstances( PortInstance portInstance = reactor.lookupPortInstance( (Port) portRef.getVariable()); - Set interleaved = new LinkedHashSet(); + Set interleaved = new LinkedHashSet<>(); if (portRef.isInterleaved()) { // NOTE: Here, we are assuming that the interleaved() // keyword is only allowed on the multiports contained by @@ -1059,7 +1057,7 @@ private List> listPortInstances( } // Iterate over the tails. while(tails.size() > 0) { - List> moreTails = new LinkedList>(); + List> moreTails = new LinkedList<>(); count = 0; for (RuntimeRange tail : tails) { if (count < tails.size() - 1) { diff --git a/org.lflang/src/org/lflang/generator/RuntimeRange.java b/org.lflang/src/org/lflang/generator/RuntimeRange.java index d865cc9680..fcf2582ed9 100644 --- a/org.lflang/src/org/lflang/generator/RuntimeRange.java +++ b/org.lflang/src/org/lflang/generator/RuntimeRange.java @@ -47,7 +47,7 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * in the graphical rendition). Equivalently, each CIG node has a unique * full name, even though it may represent many runtime instances. * The CIG is represented by - * {@link NamedInstance} and its derived classes. + * {@link NamedInstance} and its derived classes. * In the RIG, each bank is expanded so each bank member and * each port channel is represented. * @@ -251,13 +251,7 @@ public int compareTo(RuntimeRange o) { if (start < o.start) { return -1; } else if (start == o.start) { - if (width < o.width) { - return -1; - } else if (width == o.width) { - return 0; - } else { - return 1; - } + return Integer.compare(width, o.width); } else { return 1; } @@ -274,7 +268,7 @@ public int compareTo(RuntimeRange o) { public RuntimeRange head(int newWidth) { if (newWidth >= width) return this; if (newWidth <= 0) return null; - return new RuntimeRange(instance, start, newWidth, _interleaved); + return new RuntimeRange<>(instance, start, newWidth, _interleaved); } /** @@ -288,7 +282,7 @@ public RuntimeRange head(int newWidth) { * in which the runtime instances should be iterated. */ public List instances() { - List result = new ArrayList(width); + List result = new ArrayList<>(width); MixedRadixInt mr = startMR(); int count = 0; while (count++ < width) { @@ -309,7 +303,7 @@ public List instances() { * shallower). */ public List> iterationOrder() { - ArrayList> result = new ArrayList>(); + ArrayList> result = new ArrayList<>(); result.add(instance); ReactorInstance parent = instance.parent; while (parent.depth > 0) { @@ -341,7 +335,7 @@ public RuntimeRange overlap(RuntimeRange range) { */ public boolean overlaps(RuntimeRange range) { if (!instance.equals(range.instance)) return false; - return (start < range.start + range.width && start + width > range.start); + return start < range.start + range.width && start + width > range.start; } /** @@ -373,7 +367,7 @@ public boolean overlaps(RuntimeRange range) { * * * The remaining digits will be bank indices of containers up to but not * including the top-level reactor (there is no point in including the top-level - * reactor because it is never a bank. + * reactor because it is never a bank). * * Each index that is returned can be used as an index into an array of * runtime instances that is assumed to be in a **natural order**. @@ -382,7 +376,7 @@ public boolean overlaps(RuntimeRange range) { * less than the depth of this RuntimeRange's instance or an exception will be thrown. */ public Set parentInstances(int n) { - Set result = new LinkedHashSet(width); + Set result = new LinkedHashSet<>(width); MixedRadixInt mr = startMR(); int count = 0; while (count++ < width) { @@ -411,7 +405,7 @@ public ReactorInstance parentReactor() { * be incremented. */ public List permutation() { - List result = new ArrayList(instance.depth); + List result = new ArrayList<>(instance.depth); // Populate the result with default zeros. for (int i = 0; i < instance.depth; i++) { result.add(0); @@ -429,7 +423,7 @@ public List permutation() { * has radix 1 and value 0. */ public List radixes() { - List result = new ArrayList(instance.depth); + List result = new ArrayList<>(instance.depth); int width = instance.width; // If the width cannot be determined, assume 1. if (width < 0) width = 1; @@ -466,7 +460,7 @@ public MixedRadixInt startMR() { public RuntimeRange tail(int offset) { if (offset == 0) return this; if (offset >= width) return null; - return new RuntimeRange(instance, start + offset, width - offset, _interleaved); + return new RuntimeRange<>(instance, start + offset, width - offset, _interleaved); } /** @@ -477,13 +471,13 @@ public RuntimeRange tail(int offset) { * @param reactor The parent reactor at which to toggle interleaving. */ public RuntimeRange toggleInterleaved(ReactorInstance reactor) { - Set newInterleaved = new HashSet(_interleaved); + Set newInterleaved = new HashSet<>(_interleaved); if (_interleaved.contains(reactor)) { newInterleaved.remove(reactor); } else { newInterleaved.add(reactor); } - return new RuntimeRange(instance, start, width, newInterleaved); + return new RuntimeRange<>(instance, start, width, newInterleaved); } @Override @@ -495,7 +489,7 @@ public String toString() { //// Protected variables /** Record of which levels are interleaved. */ - Set _interleaved = new HashSet(); + Set _interleaved = new HashSet<>(); ////////////////////////////////////////////////////////// //// Public inner classes diff --git a/org.lflang/src/org/lflang/generator/SendRange.java b/org.lflang/src/org/lflang/generator/SendRange.java index 62cc418079..734e26e1b2 100644 --- a/org.lflang/src/org/lflang/generator/SendRange.java +++ b/org.lflang/src/org/lflang/generator/SendRange.java @@ -96,8 +96,7 @@ public SendRange( public final Connection connection; /** The list of destination ranges to which this broadcasts. */ - public final List> destinations - = new ArrayList>(); + public final List> destinations = new ArrayList<>(); ////////////////////////////////////////////////////////// //// Public methods @@ -148,7 +147,7 @@ public int getNumberOfDestinationReactors() { if (_numberOfDestinationReactors < 0) { // Has not been calculated before. Calculate now. _numberOfDestinationReactors = 0; - Map> result = new HashMap>(); + Map> result = new HashMap<>(); for (RuntimeRange destination : this.destinations) { // The following set contains unique identifiers the parent reactors // of destination ports. @@ -251,7 +250,7 @@ public String toString() { StringBuilder result = new StringBuilder(); result.append(super.toString()); result.append("->["); - List dsts = new LinkedList(); + List dsts = new LinkedList<>(); for (RuntimeRange dst : destinations) { dsts.add(dst.toString()); } @@ -291,7 +290,7 @@ protected SendRange newSendRange(SendRange srcRange, int srcRangeOffset) { // then assume srcRange is multicasting via this. int newWidth = Math.min(width, srcRange.width); // The interleaving of the result is the union of the two interleavings. - Set interleaving = new LinkedHashSet(); + Set interleaving = new LinkedHashSet<>(); interleaving.addAll(_interleaved); interleaving.addAll(srcRange._interleaved); SendRange result = new SendRange( @@ -307,8 +306,8 @@ protected SendRange newSendRange(SendRange srcRange, int srcRangeOffset) { } } throw new IllegalArgumentException( - "Expected this SendRange " + this.toString() - + " to be completely contained by a destination of " + srcRange.toString()); + "Expected this SendRange " + this + + " to be completely contained by a destination of " + srcRange); } ////////////////////////////////////////////////////////// diff --git a/org.lflang/src/org/lflang/generator/c/CActionGenerator.java b/org.lflang/src/org/lflang/generator/c/CActionGenerator.java index 1b2e18102c..4218658e1c 100644 --- a/org.lflang/src/org/lflang/generator/c/CActionGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CActionGenerator.java @@ -174,7 +174,7 @@ private static String valueDeclaration( } // Do not convert to lf_token_t* using lfTypeToTokenType because there // will be a separate field pointing to the token. - return action.getType() == null && target.requiresTypes == true ? + return action.getType() == null && target.requiresTypes ? "" : types.getTargetType(action) + " value;"; } diff --git a/org.lflang/src/org/lflang/generator/c/CCmakeCompiler.java b/org.lflang/src/org/lflang/generator/c/CCmakeCompiler.java index 2f47bed528..20aa39b383 100644 --- a/org.lflang/src/org/lflang/generator/c/CCmakeCompiler.java +++ b/org.lflang/src/org/lflang/generator/c/CCmakeCompiler.java @@ -187,15 +187,15 @@ public LFCommand compileCmakeCommand( // Set the build directory to be "build" Path buildPath = fileConfig.getSrcGenPath().resolve("build"); - List arguments = new ArrayList(); - arguments.addAll(List.of("-DCMAKE_INSTALL_PREFIX="+ FileUtil.toUnixString(fileConfig.getOutPath()), - "-DCMAKE_INSTALL_BINDIR="+ FileUtil.toUnixString( - fileConfig.getOutPath().relativize( - fileConfig.binPath - ) - ), - FileUtil.toUnixString(fileConfig.getSrcGenPath()) - )); + List arguments = new ArrayList<>(List.of( + "-DCMAKE_INSTALL_PREFIX=" + FileUtil.toUnixString(fileConfig.getOutPath()), + "-DCMAKE_INSTALL_BINDIR=" + FileUtil.toUnixString( + fileConfig.getOutPath().relativize( + fileConfig.binPath + ) + ), + FileUtil.toUnixString(fileConfig.getSrcGenPath()) + )); if (GeneratorUtils.isHostWindows()) { arguments.add("-DCMAKE_SYSTEM_VERSION=\"10.0\""); diff --git a/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java b/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java index 71644af8e5..5ad0becc90 100644 --- a/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java @@ -81,7 +81,7 @@ CodeBuilder generateCMakeCode( String cMakeExtras) { CodeBuilder cMakeCode = new CodeBuilder(); - List additionalSources = new ArrayList(); + List additionalSources = new ArrayList<>(); for (String file: targetConfig.compileAdditionalSources) { var relativePath = fileConfig.getSrcGenPath().relativize( fileConfig.getSrcGenPath().resolve(Paths.get(file))); @@ -139,15 +139,15 @@ CodeBuilder generateCMakeCode( } cMakeCode.indent(); cMakeCode.pr("${LF_MAIN_TARGET}"); - sources.forEach(source -> {cMakeCode.pr(source);}); + sources.forEach(cMakeCode::pr); cMakeCode.pr("${CoreLib}/platform/${LF_PLATFORM_FILE}"); - additionalSources.forEach(source -> {cMakeCode.pr(source);}); + additionalSources.forEach(cMakeCode::pr); cMakeCode.unindent(); cMakeCode.pr(")"); cMakeCode.newLine(); if (targetConfig.threading || targetConfig.tracing != null) { - // If threaded computation is requested, add a the threads option. + // If threaded computation is requested, add the threads option. cMakeCode.pr("# Find threads and link to it"); cMakeCode.pr("find_package(Threads REQUIRED)"); cMakeCode.pr("target_link_libraries( ${LF_MAIN_TARGET} Threads::Threads)"); @@ -203,9 +203,10 @@ CodeBuilder generateCMakeCode( case "-lprotobuf-c": cMakeCode.pr("include(FindPackageHandleStandardArgs)"); cMakeCode.pr("FIND_PATH( PROTOBUF_INCLUDE_DIR protobuf-c/protobuf-c.h)"); - cMakeCode.pr("find_library(PROTOBUF_LIBRARY \n"+ - "NAMES libprotobuf-c.a libprotobuf-c.so libprotobuf-c.dylib protobuf-c.lib protobuf-c.dll\n"+ - ")"); + cMakeCode.pr(""" + find_library(PROTOBUF_LIBRARY\s + NAMES libprotobuf-c.a libprotobuf-c.so libprotobuf-c.dylib protobuf-c.lib protobuf-c.dll + )"""); cMakeCode.pr("find_package_handle_standard_args(libprotobuf-c DEFAULT_MSG PROTOBUF_INCLUDE_DIR PROTOBUF_LIBRARY)"); cMakeCode.pr("target_include_directories( ${LF_MAIN_TARGET} PUBLIC ${PROTOBUF_INCLUDE_DIR} )"); cMakeCode.pr("target_link_libraries( ${LF_MAIN_TARGET} ${PROTOBUF_LIBRARY})"); diff --git a/org.lflang/src/org/lflang/generator/c/CCompiler.java b/org.lflang/src/org/lflang/generator/c/CCompiler.java index 36bc7c2bbd..4c99470a28 100644 --- a/org.lflang/src/org/lflang/generator/c/CCompiler.java +++ b/org.lflang/src/org/lflang/generator/c/CCompiler.java @@ -143,7 +143,7 @@ public boolean runCCompiler( System.out.println("SUCCESS: Compiling generated code for "+ fileConfig.name +" finished with no errors."); } - return (returnCode == 0); + return returnCode == 0; } /** @@ -176,7 +176,7 @@ public LFCommand compileCCommand( relBinPathString += ".o"; } - ArrayList compileArgs = new ArrayList(); + ArrayList compileArgs = new ArrayList<>(); compileArgs.add(relSrcPathString); for (String file: targetConfig.compileAdditionalSources) { var relativePath = fileConfig.getOutPath().relativize( diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.java b/org.lflang/src/org/lflang/generator/c/CGenerator.java index e64f417903..bcfae665d2 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.java @@ -475,7 +475,7 @@ protected boolean isOSCompatible() { // Visual Studio compiler is extensive. return false; } - if (targetConfig.useCmake == false) { + if (!targetConfig.useCmake) { errorReporter.reportError( "Only CMake is supported as the build system on Windows. "+ "Use `cmake: true` in the target properties. Exiting code generation." @@ -834,7 +834,7 @@ private void generateCodeForCurrentFederate( )); if (isFederated) { - code.pr(CFederateGenerator.generateFederateNeighborStructure(currentFederate).toString()); + code.pr(CFederateGenerator.generateFederateNeighborStructure(currentFederate)); } // Generate function to schedule shutdown reactions if any @@ -947,7 +947,7 @@ private void inspectReactorEResource(ReactorDecl reactor) { } // Extract the contents of the imported file for the preambles var contents = toDefinition(reactor).eResource().getContents(); - var model = (Model) contents.get(0);; + var model = (Model) contents.get(0); // Add the preambles from the imported .lf file toDefinition(reactor).getPreambles().addAll(model.getPreambles()); } @@ -1081,19 +1081,19 @@ private void generateReactorChildren( private void pickCompilePlatform() { var OS = System.getProperty("os.name").toLowerCase(); // FIXME: allow for cross-compiling - if ((OS.indexOf("mac") >= 0) || (OS.indexOf("darwin") >= 0)) { + if (OS.contains("mac") || OS.contains("darwin")) { if (mainDef != null && !targetConfig.useCmake) { targetConfig.compileAdditionalSources.add( "core" + File.separator + "platform" + File.separator + "lf_macos_support.c" ); } - } else if (OS.indexOf("win") >= 0) { + } else if (OS.contains("win")) { if (mainDef != null && !targetConfig.useCmake) { targetConfig.compileAdditionalSources.add( "core" + File.separator + "platform" + File.separator + "lf_windows_support.c" ); } - } else if (OS.indexOf("nux") >= 0) { + } else if (OS.contains("nux")) { if (mainDef != null && !targetConfig.useCmake) { targetConfig.compileAdditionalSources.add( "core" + File.separator + "platform" + File.separator + "lf_linux_support.c" @@ -1104,12 +1104,7 @@ private void pickCompilePlatform() { } } - /** - * Create a launcher script that executes all the federates and the RTI. - * - * @param coreFiles The files from the core directory that must be - * copied to the remote machines. - */ + /** Create a launcher script that executes all the federates and the RTI. */ public void createFederatedLauncher() throws IOException{ var launcher = new FedCLauncher( targetConfig, @@ -1132,7 +1127,7 @@ protected boolean clockSyncIsOn() { * Initialize clock synchronization (if enabled) and its related options for a given federate. * * Clock synchronization can be enabled using the clock-sync target property. - * @see https://github.com/icyphy/lingua-franca/wiki/Distributed-Execution#clock-synchronization + * @see Documentation */ protected void initializeClockSynchronization() { // Check if clock synchronization should be enabled for this federate in the first place @@ -1251,7 +1246,7 @@ protected void generateConstructor( /** * Generate the struct type definitions for inputs, outputs, and * actions of the specified reactor. - * @param reactor The parsed reactor data structure. + * @param decl The parsed reactor data structure. */ protected void generateAuxiliaryStructs(ReactorDecl decl) { var reactor = ASTUtils.toDefinition(decl); @@ -1311,7 +1306,7 @@ protected void generateAuxiliaryStructs(ReactorDecl decl) { /** * Generate the self struct type definition for the specified reactor * in the specified federate. - * @param reactor The parsed reactor data structure. + * @param decl The parsed reactor data structure. * @param constructorCode Place to put lines of code that need to * go into the constructor. */ @@ -1523,7 +1518,7 @@ public void generateSelfStructExtension( * These functions have a single argument that is a void* pointing to * a struct that contains parameters, state variables, inputs (triggering or not), * actions (triggering or produced), and outputs. - * @param reactor The reactor. + * @param decl The reactor. * @param federate The federate, or null if this is not * federated or not the main reactor and reactions should be * unconditionally generated. @@ -1546,7 +1541,7 @@ public void generateReactions(ReactorDecl decl, FederateInstance federate) { * a struct that contains parameters, state variables, inputs (triggering or not), * actions (triggering or produced), and outputs. * @param reaction The reaction. - * @param reactor The reactor. + * @param decl The reactor. * @param reactionIndex The position of the reaction within the reactor. */ public void generateReaction(Reaction reaction, ReactorDecl decl, int reactionIndex) { @@ -1844,7 +1839,7 @@ public void processProtoFile(String filename, CancelIndicator cancelIndicator) { * Construct a unique type for the struct of the specified * typed variable (port or action) of the specified reactor class. * This is required to be the same as the type name returned by - * {@link variableStructType(TriggerInstance)}. + * {@link #variableStructType(TriggerInstance)}. * @param variable The variable. * @param reactor The reactor class. * @return The name of the self struct. @@ -1857,7 +1852,7 @@ public static String variableStructType(Variable variable, ReactorDecl reactor) * Construct a unique type for the struct of the specified * instance (port or action). * This is required to be the same as the type name returned by - * {@link variableStructType(Variable, ReactorDecl)}. + * {@link #variableStructType(Variable, ReactorDecl)}. * @param portOrAction The port or action instance. * @return The name of the self struct. */ @@ -1901,8 +1896,6 @@ private void generateTraceTableEntries(ReactorInstance instance) { * Generate code to instantiate the specified reactor instance and * initialize it. * @param instance A reactor instance. - * @param federate A federate instance to conditionally generate code by - * contained reactors or null if there are no federates. */ public void generateReactorInstance(ReactorInstance instance) { var reactorClass = instance.getDefinition().getReactorClass(); @@ -1966,7 +1959,7 @@ public void generateReactorInstance(ReactorInstance instance) { if (targetConfig.coordinationOptions.advance_message_interval == null) { errorReporter.reportWarning(outputFound, String.join("\n", "Found a path from a physical action to output for reactor "+addDoubleQuotes(instance.getName())+". ", - "The amount of delay is "+minDelay.toString()+".", + "The amount of delay is "+minDelay+".", "With centralized coordination, this can result in a large number of messages to the RTI.", "Consider refactoring the code so that the output does not depend on the physical action,", "or consider using decentralized coordination. To silence this warning, set the target", @@ -2037,7 +2030,6 @@ private void generateInitializeActionToken(ReactorInstance reactor) { * but for the top-level of a federate, will be a subset of reactions that * is relevant to the federate. * @param instance The reactor instance. - * @param reactions The reactions of this instance. */ public void generateReactorInstanceExtension(ReactorInstance instance) { // Do nothing @@ -2048,7 +2040,6 @@ public void generateReactorInstanceExtension(ReactorInstance instance) { * Unlike parameters, state variables are uniformly initialized for all instances * of the same reactor. * @param instance The reactor class instance - * @return Initialization code fore state variables of instance */ public void generateStateVariableInitializations(ReactorInstance instance) { var reactorClass = instance.getDefinition().getReactorClass(); @@ -2059,7 +2050,7 @@ public void generateStateVariableInitializations(ReactorInstance instance) { instance.lookupModeInstance((Mode) stateVar.eContainer()) : instance.getMode(false); // In the current concept state variables are not automatically reset. - // Instead they need to be manually reset using a reset triggered reaction or marked as reset. + // Instead, they need to be manually reset using a reset triggered reaction or marked as reset. if (!stateVar.isReset()) { mode = null; // Treat as if outside of mode } @@ -2450,7 +2441,7 @@ public String generateNetworkSenderBody( * input port "port" or has it in its sources. If there are only connections to contained * reactors, in the top-level reactor. * - * @param port The port to generate the control reaction for + * @param receivingPortID The port to generate the control reaction for * @param maxSTP The maximum value of STP is assigned to reactions (if any) * that have port as their trigger or source */ @@ -2531,7 +2522,7 @@ public void enableSupportForSerializationIfApplicable(CancelIndicator cancelIndi "To use the ROS 2 serializer, please use the CCpp target." ); } - if (targetConfig.useCmake == false) { + if (!targetConfig.useCmake) { throw new UnsupportedOperationException( "Invalid target property \"cmake: false\"" + "To use the ROS 2 serializer, please use the CMake build system (default)" diff --git a/org.lflang/src/org/lflang/generator/c/CMethodGenerator.java b/org.lflang/src/org/lflang/generator/c/CMethodGenerator.java index aace2ea646..f82a95d557 100644 --- a/org.lflang/src/org/lflang/generator/c/CMethodGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CMethodGenerator.java @@ -10,7 +10,7 @@ import org.lflang.lf.ReactorDecl; /** - * Collection of functios to generate C code to declare methods. + * Collection of functions to generate C code to declare methods. * * @author {Edward A. Lee } */ @@ -89,7 +89,7 @@ public static String generateMethod( * Generate method functions definition for a reactor. * These functions have a first argument that is a void* pointing to * the self struct. - * @param reactor The reactor. + * @param decl The reactor. * @param code The place to put the code. * @param types The C-specific type conversion functions. */ diff --git a/org.lflang/src/org/lflang/generator/c/CNetworkGenerator.java b/org.lflang/src/org/lflang/generator/c/CNetworkGenerator.java index 5409340016..d807f66477 100644 --- a/org.lflang/src/org/lflang/generator/c/CNetworkGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CNetworkGenerator.java @@ -291,7 +291,7 @@ public static String generateNetworkSenderBody( * input port "port" or has it in its sources. If there are only connections to contained * reactors, in the top-level reactor. * - * @param port The port to generate the control reaction for + * @param receivingPortID The port to generate the control reaction for * @param maxSTP The maximum value of STP is assigned to reactions (if any) * that have port as their trigger or source */ diff --git a/org.lflang/src/org/lflang/generator/c/CParameterGenerator.java b/org.lflang/src/org/lflang/generator/c/CParameterGenerator.java index 7261182a75..121ebf93e4 100644 --- a/org.lflang/src/org/lflang/generator/c/CParameterGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CParameterGenerator.java @@ -78,7 +78,6 @@ public static String getInitializer(ParameterInstance p) { * Generate code for parameters variables of a reactor in the form "parameter.type parameter.name;" * @param reactor The reactor * @param types A helper class for types - * @return */ public static String generateDeclarations(Reactor reactor, CTypes types) { CodeBuilder code = new CodeBuilder(); diff --git a/org.lflang/src/org/lflang/generator/c/CPortGenerator.java b/org.lflang/src/org/lflang/generator/c/CPortGenerator.java index aca1d448ff..13571015db 100644 --- a/org.lflang/src/org/lflang/generator/c/CPortGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CPortGenerator.java @@ -187,7 +187,7 @@ private static String valueDeclaration( ErrorReporter errorReporter, CTypes types ) { - if (port.getType() == null && target.requiresTypes == true) { + if (port.getType() == null && target.requiresTypes) { // This should have been caught by the validator. errorReporter.reportError(port, "Port is required to have a type: " + port.getName()); return ""; diff --git a/org.lflang/src/org/lflang/generator/c/CPreambleGenerator.java b/org.lflang/src/org/lflang/generator/c/CPreambleGenerator.java index c68eb57798..a254a234e0 100644 --- a/org.lflang/src/org/lflang/generator/c/CPreambleGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CPreambleGenerator.java @@ -127,18 +127,17 @@ private static String generateTracingDefineDirective( * Initialize clock synchronization (if enabled) and its related options for a given federate. * * Clock synchronization can be enabled using the clock-sync target property. - * @see https://github.com/icyphy/lingua-franca/wiki/Distributed-Execution#clock-synchronization + * @see Documentation */ private static String generateClockSyncDefineDirective( ClockSyncMode mode, ClockSyncOptions options ) { - List code = new ArrayList<>(); - code.addAll(List.of( + List code = new ArrayList<>(List.of( "#define _LF_CLOCK_SYNC_INITIAL", - "#define _LF_CLOCK_SYNC_PERIOD_NS "+GeneratorBase.timeInTargetLanguage(options.period), - "#define _LF_CLOCK_SYNC_EXCHANGES_PER_INTERVAL "+options.trials, - "#define _LF_CLOCK_SYNC_ATTENUATION "+options.attenuation + "#define _LF_CLOCK_SYNC_PERIOD_NS " + GeneratorBase.timeInTargetLanguage(options.period), + "#define _LF_CLOCK_SYNC_EXCHANGES_PER_INTERVAL " + options.trials, + "#define _LF_CLOCK_SYNC_ATTENUATION " + options.attenuation )); if (mode == ClockSyncMode.ON) { code.add("#define _LF_CLOCK_SYNC_ON"); diff --git a/org.lflang/src/org/lflang/generator/c/CReactionGenerator.java b/org.lflang/src/org/lflang/generator/c/CReactionGenerator.java index 8db622aa98..6cf3770575 100644 --- a/org.lflang/src/org/lflang/generator/c/CReactionGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CReactionGenerator.java @@ -109,8 +109,7 @@ public static String generateInitializationForReaction(String body, // port is named 'out', then c.out->value c.out->is_present are // defined so that they can be used in the verbatim code. for (TriggerRef trigger : ASTUtils.convertToEmptyListIfNull(reaction.getTriggers())) { - if (trigger instanceof VarRef) { - VarRef triggerAsVarRef = (VarRef) trigger; + if (trigger instanceof VarRef triggerAsVarRef) { if (triggerAsVarRef.getVariable() instanceof Port) { generatePortVariablesInReaction( reactionInitialization, @@ -346,14 +345,12 @@ public static String generateIntendedTagInheritence(String body, Reaction reacti // Go through every trigger of the reaction and check the // value of intended_tag to choose the minimum. for (TriggerRef inputTrigger : ASTUtils.convertToEmptyListIfNull(reaction.getTriggers())) { - if (inputTrigger instanceof VarRef) { - VarRef inputTriggerAsVarRef = (VarRef) inputTrigger; + if (inputTrigger instanceof VarRef inputTriggerAsVarRef) { Variable variable = inputTriggerAsVarRef.getVariable(); String variableName = inputTriggerAsVarRef.getVariable().getName(); - if (variable instanceof Output) { + if (variable instanceof Output outputPort) { // Output from a contained reactor String containerName = inputTriggerAsVarRef.getContainer().getName(); - Output outputPort = (Output) variable; if (ASTUtils.isMultiport(outputPort)) { intendedTagInheritenceCode.pr(String.join("\n", "for (int i=0; i < "+containerName+"."+generateWidthVariable(variableName)+"; i++) {", @@ -370,9 +367,8 @@ public static String generateIntendedTagInheritence(String body, Reaction reacti " inherited_min_intended_tag = "+containerName+"."+variableName+"->intended_tag;", "}" )); - } else if (variable instanceof Port) { + } else if (variable instanceof Port inputPort) { // Input port - Port inputPort = (Port) variable; if (ASTUtils.isMultiport(inputPort)) { intendedTagInheritenceCode.pr(String.join("\n", "for (int i=0; i < "+generateWidthVariable(variableName)+"; i++) {", @@ -463,8 +459,7 @@ public static String generateIntendedTagInheritence(String body, Reaction reacti /** * Generate code for the body of a reaction that takes an input and * schedules an action with the value of that input. - * @param action The action to schedule - * @param port The port to read from + * @param actionName The action to schedule */ public static String generateDelayBody(String ref, String actionName, boolean isTokenType) { // Note that the action.type set by the base class is actually @@ -517,7 +512,7 @@ private static void generateVariablesForSendingToContainedReactors( structBuilder = new CodeBuilder(); structs.put(definition, structBuilder); } - String inputStructType = CGenerator.variableStructType(input, definition.getReactorClass()).toString(); + String inputStructType = CGenerator.variableStructType(input, definition.getReactorClass()); String defName = definition.getName(); String defWidth = generateWidthVariable(defName); String inputName = input.getName(); @@ -572,7 +567,7 @@ private static void generateVariablesForSendingToContainedReactors( * @param structs A map from reactor instantiations to a place to write * struct fields. * @param port The port. - * @param reactor The reactor or import statement. + * @param decl The reactor or import statement. */ private static void generatePortVariablesInReaction( CodeBuilder builder, @@ -586,7 +581,7 @@ private static void generatePortVariablesInReaction( } else { // port is an output of a contained reactor. Output output = (Output) port.getVariable(); - String portStructType = CGenerator.variableStructType(output, port.getContainer().getReactorClass()).toString(); + String portStructType = CGenerator.variableStructType(output, port.getContainer().getReactorClass()); CodeBuilder structBuilder = structs.get(port.getContainer()); if (structBuilder == null) { @@ -636,16 +631,15 @@ private static void generatePortVariablesInReaction( } /** Generate action variables for a reaction. - * @param builder Where to write the code. * @param action The action. - * @param reactor The reactor. + * @param decl The reactor. */ private static String generateActionVariablesInReaction( Action action, ReactorDecl decl, CTypes types ) { - String structType = CGenerator.variableStructType(action, decl).toString(); + String structType = CGenerator.variableStructType(action, decl); // If the action has a type, create variables for accessing the value. InferredType type = ASTUtils.getInferredType(action); // Pointer to the lf_token_t sent as the payload in the trigger. @@ -681,16 +675,15 @@ private static String generateActionVariablesInReaction( /** Generate into the specified string builder the code to * initialize local variables for the specified input port * in a reaction function from the "self" struct. - * @param builder The string builder. * @param input The input statement from the AST. - * @param reactor The reactor. + * @param decl The reactor. */ private static String generateInputVariablesInReaction( Input input, ReactorDecl decl, CTypes types ) { - String structType = CGenerator.variableStructType(input, decl).toString(); + String structType = CGenerator.variableStructType(input, decl); InferredType inputType = ASTUtils.getInferredType(input); CodeBuilder builder = new CodeBuilder(); String inputName = input.getName(); @@ -825,9 +818,9 @@ public static String generateOutputVariablesInReaction( // The container of the output may be a contained reactor or // the reactor containing the reaction. String outputStructType = (effect.getContainer() == null) ? - CGenerator.variableStructType(output, decl).toString() + CGenerator.variableStructType(output, decl) : - CGenerator.variableStructType(output, effect.getContainer().getReactorClass()).toString(); + CGenerator.variableStructType(output, effect.getContainer().getReactorClass()); if (!ASTUtils.isMultiport(output)) { // Output port is not a multiport. return outputStructType+"* "+outputName+" = &self->_lf_"+outputName+";"; @@ -849,7 +842,7 @@ public static String generateOutputVariablesInReaction( * specified reactor and a trigger_t struct for each trigger (input, action, * timer, or output of a contained reactor). * @param body The place to put the code for the self struct. - * @param reactor The reactor. + * @param decl The reactor. * @param constructorCode The place to put the constructor code. */ public static void generateReactionAndTriggerStructs( @@ -887,7 +880,7 @@ public static void generateReactionAndTriggerStructs( var triggerAsVarRef = (VarRef) trigger; var reactionList = triggerMap.get(triggerAsVarRef.getVariable()); if (reactionList == null) { - reactionList = new LinkedList(); + reactionList = new LinkedList<>(); triggerMap.put(triggerAsVarRef.getVariable(), reactionList); } reactionList.add(reactionCount); @@ -1075,7 +1068,7 @@ private static void createTriggerT( // self->_lf__"+input.name+".drop = false; // If the input type is 'void', we need to avoid generating the code // 'sizeof(void)', which some compilers reject. - var size = (rootType == "void") ? "0" : "sizeof("+rootType+")"; + var size = (rootType.equals("void")) ? "0" : "sizeof("+rootType+")"; constructorCode.pr("self->_lf__"+varName+".element_size = "+size+";"); if (isFederated) { body.pr( @@ -1233,7 +1226,7 @@ public static String generateLfModeTriggeredReactions( * a struct that contains parameters, state variables, inputs (triggering or not), * actions (triggering or produced), and outputs. * @param reaction The reaction. - * @param reactor The reactor. + * @param decl The reactor. * @param reactionIndex The position of the reaction within the reactor. */ public static String generateReaction( @@ -1299,7 +1292,7 @@ public static String generateFunction(String header, String init, Code code) { /** * Returns the name of the deadline function for reaction. * @param decl The reactor with the deadline - * @param index The number assigned to this reaction deadline + * @param reactionIndex The number assigned to this reaction deadline */ public static String generateDeadlineFunctionName(ReactorDecl decl, int reactionIndex) { return decl.getName().toLowerCase() + "_deadline_function" + reactionIndex; @@ -1319,7 +1312,7 @@ public static String generateReactionFunctionName(ReactorDecl reactor, int react /** * Returns the name of the stp function for reaction. * @param decl The reactor with the stp - * @param index The number assigned to this reaction deadline + * @param reactionIndex The number assigned to this reaction deadline */ public static String generateStpFunctionName(ReactorDecl decl, int reactionIndex) { return decl.getName().toLowerCase() + "_STP_function" + reactionIndex; diff --git a/org.lflang/src/org/lflang/generator/c/CStateGenerator.java b/org.lflang/src/org/lflang/generator/c/CStateGenerator.java index 970b2bc0d4..7cb022508c 100644 --- a/org.lflang/src/org/lflang/generator/c/CStateGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CStateGenerator.java @@ -17,7 +17,6 @@ public class CStateGenerator { * Generate code for state variables of a reactor in the form "stateVar.type stateVar.name;" * @param reactor The reactor * @param types A helper object for types - * @return */ public static String generateDeclarations(Reactor reactor, CTypes types) { CodeBuilder code = new CodeBuilder(); diff --git a/org.lflang/src/org/lflang/generator/c/CTimerGenerator.java b/org.lflang/src/org/lflang/generator/c/CTimerGenerator.java index 73055b47bd..58cc03069c 100644 --- a/org.lflang/src/org/lflang/generator/c/CTimerGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CTimerGenerator.java @@ -57,15 +57,14 @@ public static String generateDeclarations(int timerCount) { public static String generateLfInitializeTimer(int timerCount) { return String.join("\n", "void _lf_initialize_timers() {", - (timerCount > 0 ? - String.join("\n", - " for (int i = 0; i < _lf_timer_triggers_size; i++) {", - " if (_lf_timer_triggers[i] != NULL) {", - " _lf_initialize_timer(_lf_timer_triggers[i]);", - " }", - " }" - ) : - ""), + timerCount > 0 ? + """ + for (int i = 0; i < _lf_timer_triggers_size; i++) { + if (_lf_timer_triggers[i] != NULL) { + _lf_initialize_timer(_lf_timer_triggers[i]); + } + }""".indent(4) : + "", "}" ); } diff --git a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java index 988ff0d572..d55bfb514d 100644 --- a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java @@ -363,7 +363,7 @@ private static boolean setReactionPriorities( } var temp = new CodeBuilder(); - temp.pr("// Set reaction priorities for " + reactor.toString()); + temp.pr("// Set reaction priorities for " + reactor); temp.startScopedBlock(reactor, currentFederate, isFederated, true); for (ReactionInstance r : reactor.reactions) { if (currentFederate.contains(r.getDefinition())) { @@ -454,7 +454,7 @@ private static String deferredConnectInputsToOutputs( * to by a reaction belonging to the parent of the port's parent. * If it is an output, then it is being written to by a reaction belonging * to the port's parent. - * @param port A port that is written to by reactions. + * @param src A port that is written to by reactions. */ private static String connectPortToEventualDestinations( FederateInstance currentFederate, @@ -474,7 +474,7 @@ private static String connectPortToEventualDestinations( // really necessary. if (currentFederate.contains(dst.getParent())) { var mod = (dst.isMultiport() || (src.isInput() && src.isMultiport()))? "" : "&"; - code.pr("// Connect "+srcRange.toString()+" to port "+dstRange.toString()); + code.pr("// Connect "+srcRange+" to port "+dstRange); code.startScopedRangeBlock(currentFederate, srcRange, dstRange, isFederated); if (src.isInput()) { // Source port is written to by reaction in port's parent's parent @@ -500,7 +500,7 @@ private static String connectPortToEventualDestinations( * to the single dominating upstream reaction if there is one, or to be * NULL if there is none. * - * @param reactor The reactor. + * @param r The reactor. */ private static String deferredOptimizeForSingleDominatingReaction( FederateInstance currentFederate, @@ -870,7 +870,7 @@ private static String deferredOutputNumDestinations( * so each function it calls must handle its own iteration * over all runtime instance. * @param reactor The container. - * @param federate The federate (used to determine whether a + * @param currentFederate The federate (used to determine whether a * reaction belongs to the federate). */ private static String deferredInitializeNonNested( @@ -929,7 +929,7 @@ private static String deferredInitializeNonNested( /** * For each output of the specified reactor that has a token type * (type* or type[]), create a default token and put it on the self struct. - * @param parent The reactor. + * @param reactor The reactor. */ private static String deferredCreateDefaultTokens( ReactorInstance reactor, @@ -961,7 +961,7 @@ private static String deferredCreateDefaultTokens( * produced. The port may be an output of the reaction's parent * or an input to a reactor contained by the parent. * - * @param The reaction instance. + * @param reaction The reaction instance. */ private static String deferredReactionOutputs( FederateInstance currentFederate, @@ -1034,7 +1034,7 @@ private static String deferredReactionOutputs( init.endScopedBlock(); code.pr(String.join("\n", "// Total number of outputs (single ports and multiport channels)", - "// produced by "+reaction.toString()+".", + "// produced by "+reaction+".", CUtil.reactionRef(reaction)+".num_outputs = "+outputCount+";" )); if (outputCount > 0) { @@ -1170,7 +1170,7 @@ private static String deferredAllocationForEffectsOnInputs( * all reactor runtime instances have been created. * This function creates nested loops over nested banks. * @param reactor The container. - * @param federate The federate (used to determine whether a + * @param currentFederate The federate (used to determine whether a * reaction belongs to the federate). */ private static String deferredInitialize( diff --git a/org.lflang/src/org/lflang/generator/c/CTypes.java b/org.lflang/src/org/lflang/generator/c/CTypes.java index fc09ad6ad5..803d9e8432 100644 --- a/org.lflang/src/org/lflang/generator/c/CTypes.java +++ b/org.lflang/src/org/lflang/generator/c/CTypes.java @@ -65,7 +65,7 @@ public String getTargetUndefinedType() { * as a type, and {@code int*} must be used instead, except when initializing * a variable using a static initializer, as in {@code int[] foo = {1, 2, 3};}. * When initializing a variable using a static initializer, use - * {@link #getVariableDeclaration(InferredType, String)} instead. + * {@link #getVariableDeclaration(InferredType, String, boolean)} instead. * @param type The type. */ @Override diff --git a/org.lflang/src/org/lflang/generator/c/CUtil.java b/org.lflang/src/org/lflang/generator/c/CUtil.java index bc89fef73b..9ecd016a2a 100644 --- a/org.lflang/src/org/lflang/generator/c/CUtil.java +++ b/org.lflang/src/org/lflang/generator/c/CUtil.java @@ -90,7 +90,7 @@ public class CUtil { * If the instance is not a bank, return "0". * @param instance A reactor instance. */ - static public String bankIndex(ReactorInstance instance) { + public static String bankIndex(ReactorInstance instance) { if (!instance.isBank()) return "0"; return bankIndexName(instance); } @@ -102,19 +102,18 @@ static public String bankIndex(ReactorInstance instance) { * from the ID of any other instance in the program. * @param instance A reactor instance. */ - static public String bankIndexName(ReactorInstance instance) { + public static String bankIndexName(ReactorInstance instance) { return instance.uniqueID() + "_i"; } /** * Return a default name of a variable to refer to the channel index of a port - * in a bank. This is has the form uniqueID_c where uniqueID + * in a bank. This has the form uniqueID_c where uniqueID * is an identifier for the instance that is guaranteed to be different * from the ID of any other instance in the program. * If the port is not a multiport, then return the string "0". - * @param instance A reactor instance. */ - static public String channelIndex(PortInstance port) { + public static String channelIndex(PortInstance port) { if (!port.isMultiport()) return "0"; return channelIndexName(port); } @@ -124,9 +123,8 @@ static public String channelIndex(PortInstance port) { * in a bank. This is has the form uniqueID_c where uniqueID * is an identifier for the instance that is guaranteed to be different * from the ID of any other instance in the program. - * @param instance A reactor instance. */ - static public String channelIndexName(PortInstance port) { + public static String channelIndexName(PortInstance port) { return port.uniqueID() + "_c"; } @@ -163,7 +161,7 @@ static public String channelIndexName(PortInstance port) { * @param channelIndex A variable name to use to index the channel or null to * use the default, the string returned by {@link CUtil#channelIndex(PortInstance)}. */ - static public String portRef( + public static String portRef( PortInstance port, boolean isNested, boolean includeChannelIndex, @@ -192,7 +190,7 @@ static public String portRef( * This is equivalent to calling `portRef(port, false, true, null, null)`. * @param port An instance of the port to be referenced. */ - static public String portRef(PortInstance port) { + public static String portRef(PortInstance port) { return portRef(port, false, true, null, null, null); } @@ -211,7 +209,7 @@ static public String portRef(PortInstance port) { * @param channelIndex A variable name to use to index the channel or null to * use the default, the string returned by {@link CUtil#channelIndex(PortInstance)}. */ - static public String portRef( + public static String portRef( PortInstance port, String runtimeIndex, String bankIndex, String channelIndex ) { return portRef(port, false, true, runtimeIndex, bankIndex, channelIndex); @@ -222,7 +220,7 @@ static public String portRef( * This is useful for deriving a reference to the _width variable. * @param port An instance of the port to be referenced. */ - static public String portRefName(PortInstance port) { + public static String portRefName(PortInstance port) { return portRef(port, false, false, null, null, null); } @@ -238,7 +236,7 @@ static public String portRefName(PortInstance port) { * @param channelIndex A variable name to use to index the channel or null to * use the default, the string returned by {@link CUtil#channelIndex(PortInstance)}. */ - static public String portRefName( + public static String portRefName( PortInstance port, String runtimeIndex, String bankIndex, String channelIndex ) { return portRef(port, false, false, runtimeIndex, bankIndex, channelIndex); @@ -254,7 +252,7 @@ static public String portRefName( * * @param port The port. */ - static public String portRefNested(PortInstance port) { + public static String portRefNested(PortInstance port) { return portRef(port, true, true, null, null, null); } @@ -274,7 +272,7 @@ static public String portRefNested(PortInstance port) { * @param channelIndex A variable name to use to index the channel or null to * use the default, the string returned by {@link CUtil#channelIndex(PortInstance)}. */ - static public String portRefNested( + public static String portRefNested( PortInstance port, String runtimeIndex, String bankIndex, String channelIndex ) { return portRef(port, true, true, runtimeIndex, bankIndex, channelIndex); @@ -291,7 +289,7 @@ static public String portRefNested( * * @param port The port. */ - static public String portRefNestedName(PortInstance port) { + public static String portRefNestedName(PortInstance port) { return portRef(port, true, false, null, null, null); } @@ -312,7 +310,7 @@ static public String portRefNestedName(PortInstance port) { * @param channelIndex A variable name to use to index the channel or null to * use the default, the string returned by {@link CUtil#channelIndex(PortInstance)}. */ - static public String portRefNestedName( + public static String portRefNestedName( PortInstance port, String runtimeIndex, String bankIndex, String channelIndex ) { return portRef(port, true, false, runtimeIndex, bankIndex, channelIndex); @@ -350,7 +348,7 @@ public static String portRefInReaction(VarRef reference, Integer bankIndex, Inte * of the parent of the specified reaction. * @param reaction The reaction. */ - static public String reactionRef(ReactionInstance reaction) { + public static String reactionRef(ReactionInstance reaction) { return reactionRef(reaction, null); } @@ -360,7 +358,7 @@ static public String reactionRef(ReactionInstance reaction) { * @param reaction The reaction. * @param runtimeIndex An index into the array of self structs for the parent. */ - static public String reactionRef(ReactionInstance reaction, String runtimeIndex) { + public static String reactionRef(ReactionInstance reaction, String runtimeIndex) { return reactorRef(reaction.getParent(), runtimeIndex) + "->_lf__reaction_" + reaction.index; } @@ -373,7 +371,7 @@ static public String reactionRef(ReactionInstance reaction, String runtimeIndex) * by {@link #runtimeIndex(ReactorInstance)} or 0 if there are no banks. * @param instance The reactor instance. */ - static public String reactorRef(ReactorInstance instance) { + public static String reactorRef(ReactorInstance instance) { return reactorRef(instance, null); } @@ -383,7 +381,7 @@ static public String reactorRef(ReactorInstance instance) { * except that it does not index into the array. * @param instance The reactor instance. */ - static public String reactorRefName(ReactorInstance instance) { + public static String reactorRefName(ReactorInstance instance) { return instance.uniqueID() + "_self"; } @@ -393,13 +391,13 @@ static public String reactorRefName(ReactorInstance instance) { * self[runtimeIndex], where self is the name of the array of self structs * for this reactor instance. If runtimeIndex is null, then it is replaced by * the expression returned - * by {@link runtimeIndex(ReactorInstance)} or 0 if there are no banks. + * by {@link #runtimeIndex(ReactorInstance)} or 0 if there are no banks. * @param instance The reactor instance. * @param runtimeIndex An optional expression to use to address bank members. * If this is null, the expression used will be that returned by * {@link #runtimeIndex(ReactorInstance)}. */ - static public String reactorRef(ReactorInstance instance, String runtimeIndex) { + public static String reactorRef(ReactorInstance instance, String runtimeIndex) { if (runtimeIndex == null) runtimeIndex = runtimeIndex(instance); return reactorRefName(instance) + "[" + runtimeIndex + "]"; } @@ -412,11 +410,11 @@ static public String reactorRef(ReactorInstance instance, String runtimeIndex) { * a struct with fields corresponding to those inputs and outputs. * This method returns a reference to that struct or array of structs. * Note that the returned reference is not to the self struct of the - * contained reactor. Use {@link reactorRef(ReactorInstance)} for that. + * contained reactor. Use {@link #reactorRef(ReactorInstance)} for that. * * @param reactor The contained reactor. */ - static public String reactorRefNested(ReactorInstance reactor) { + public static String reactorRefNested(ReactorInstance reactor) { return reactorRefNested(reactor, null, null); } @@ -436,7 +434,7 @@ static public String reactorRefNested(ReactorInstance reactor) { * @param bankIndex A variable name to use to index the bank or null to use the * default, the string returned by {@link CUtil#bankIndex(ReactorInstance)}. */ - static public String reactorRefNested(ReactorInstance reactor, String runtimeIndex, String bankIndex) { + public static String reactorRefNested(ReactorInstance reactor, String runtimeIndex, String bankIndex) { String result = reactorRef(reactor.getParent(), runtimeIndex) + "->_lf_" + reactor.getName(); if (reactor.isBank()) { // Need the bank index not the runtimeIndex. @@ -463,7 +461,7 @@ static public String reactorRefNested(ReactorInstance reactor, String runtimeInd * * @param reactor The reactor. */ - static public String runtimeIndex(ReactorInstance reactor) { + public static String runtimeIndex(ReactorInstance reactor) { StringBuilder result = new StringBuilder(); int width = 0; int parens = 0; @@ -491,17 +489,12 @@ static public String runtimeIndex(ReactorInstance reactor) { * @param reactor The reactor class. * @return The type of a self struct for the specified reactor class. */ - static public String selfType(ReactorDecl reactor) { + public static String selfType(ReactorDecl reactor) { return reactor.getName().toLowerCase() + "_self_t"; } - /** - * Construct a unique type for the "self" struct of the specified - * reactor class from the reactor class. - * @param reactor The reactor class. - * @return The name of the self struct. - */ - static public String selfType(ReactorInstance instance) { + /** Construct a unique type for the "self" struct of the class of the given reactor. */ + public static String selfType(ReactorInstance instance) { return selfType(instance.getDefinition().getReactorClass()); } @@ -511,7 +504,7 @@ static public String selfType(ReactorInstance instance) { * is on the self struct. * @param instance The port or action instance. */ - static public String triggerRef(TriggerInstance instance) { + public static String triggerRef(TriggerInstance instance) { return triggerRef(instance, null); } @@ -522,7 +515,7 @@ static public String triggerRef(TriggerInstance instance) { * @param instance The port or action instance. * @param runtimeIndex An optional index variable name to use to address runtime instances. */ - static public String triggerRef(TriggerInstance instance, String runtimeIndex) { + public static String triggerRef(TriggerInstance instance, String runtimeIndex) { return reactorRef(instance.getParent(), runtimeIndex) + "->_lf__" + instance.getName(); @@ -533,7 +526,7 @@ static public String triggerRef(TriggerInstance instance, St * port of a contained reactor. * @param port The output port of a contained reactor. */ - static public String triggerRefNested(PortInstance port) { + public static String triggerRefNested(PortInstance port) { return triggerRefNested(port, null, null); } @@ -548,7 +541,7 @@ static public String triggerRefNested(PortInstance port) { * the the bank of the port's parent, or null to get the default returned by * {@link CUtil#bankIndex(ReactorInstance)}. */ - static public String triggerRefNested(PortInstance port, String runtimeIndex, String bankIndex) { + public static String triggerRefNested(PortInstance port, String runtimeIndex, String bankIndex) { return reactorRefNested(port.getParent(), runtimeIndex, bankIndex) + "." + port.getName() + "_trigger"; } @@ -735,8 +728,7 @@ public static void deleteBinFiles(FileConfig fileConfig) { String[] files = fileConfig.binPath.toFile().list(); List federateNames = new LinkedList<>(); // FIXME: put this in ASTUtils? fileConfig.resource.getAllContents().forEachRemaining(node -> { - if (node instanceof Reactor) { - Reactor r = (Reactor) node; + if (node instanceof Reactor r) { if (r.isFederated()) { r.getInstantiations().forEach(inst -> federateNames .add(inst.getName())); diff --git a/org.lflang/src/org/lflang/generator/c/InteractingContainedReactors.java b/org.lflang/src/org/lflang/generator/c/InteractingContainedReactors.java index a6429e44ef..e2ce9bef19 100644 --- a/org.lflang/src/org/lflang/generator/c/InteractingContainedReactors.java +++ b/org.lflang/src/org/lflang/generator/c/InteractingContainedReactors.java @@ -34,9 +34,6 @@ public class InteractingContainedReactors { * For each port, this provides a list of reaction indices that * are triggered by the port, or an empty list if there are no * reactions triggered by the port. - * @param reactor The container. - * @param federate The federate (used to determine whether a - * reaction belongs to the federate). */ // This horrible data structure is a collection, indexed by instantiation // of a contained reactor, of lists, indexed by ports of the contained reactor @@ -72,10 +69,9 @@ public InteractingContainedReactors(Reactor reactor, FederateInstance federate) // Second, handle reactions that are triggered by outputs // of contained reactors. for (TriggerRef trigger : ASTUtils.convertToEmptyListIfNull(reaction.getTriggers())) { - if (trigger instanceof VarRef) { + if (trigger instanceof VarRef triggerAsVarRef) { // If an trigger is an output, then it must be an output // of a contained reactor. - VarRef triggerAsVarRef = (VarRef) trigger; if (triggerAsVarRef.getVariable() instanceof Output) { var list = addPort(triggerAsVarRef.getContainer(), (Output) triggerAsVarRef.getVariable()); list.add(reactionCount); @@ -108,18 +104,12 @@ public InteractingContainedReactors(Reactor reactor, FederateInstance federate) */ private List addPort(Instantiation containedReactor, Port port) { // Get or create the entry for the containedReactor. - var containedReactorEntry = portsByContainedReactor.get(containedReactor); - if (containedReactorEntry == null) { - containedReactorEntry = new LinkedHashMap<>(); - portsByContainedReactor.put(containedReactor, containedReactorEntry); - } + var containedReactorEntry = portsByContainedReactor.computeIfAbsent( + containedReactor, + k -> new LinkedHashMap<>() + ); // Get or create the entry for the port. - var portEntry = containedReactorEntry.get(port); - if (portEntry == null) { - portEntry = new LinkedList<>(); - containedReactorEntry.put(port, portEntry); - } - return portEntry; + return containedReactorEntry.computeIfAbsent(port, k -> new LinkedList<>()); } /** From 10e5fb15d35ce3931249aeb084d4d2f28052ec83 Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Sat, 11 Jun 2022 16:04:03 -0700 Subject: [PATCH 56/56] Address comment from code review. --- org.lflang/src/org/lflang/generator/c/CGenerator.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.java b/org.lflang/src/org/lflang/generator/c/CGenerator.java index bcfae665d2..bc2a536137 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.java @@ -1079,28 +1079,28 @@ private void generateReactorChildren( * will detect and use the appropriate platform file based on the platform that cmake is invoked on. */ private void pickCompilePlatform() { - var OS = System.getProperty("os.name").toLowerCase(); + var osName = System.getProperty("os.name").toLowerCase(); // FIXME: allow for cross-compiling - if (OS.contains("mac") || OS.contains("darwin")) { + if (osName.contains("mac") || osName.contains("darwin")) { if (mainDef != null && !targetConfig.useCmake) { targetConfig.compileAdditionalSources.add( "core" + File.separator + "platform" + File.separator + "lf_macos_support.c" ); } - } else if (OS.contains("win")) { + } else if (osName.contains("win")) { if (mainDef != null && !targetConfig.useCmake) { targetConfig.compileAdditionalSources.add( "core" + File.separator + "platform" + File.separator + "lf_windows_support.c" ); } - } else if (OS.contains("nux")) { + } else if (osName.contains("nux")) { if (mainDef != null && !targetConfig.useCmake) { targetConfig.compileAdditionalSources.add( "core" + File.separator + "platform" + File.separator + "lf_linux_support.c" ); } } else { - errorReporter.reportError("Platform " + OS + " is not supported"); + errorReporter.reportError("Platform " + osName + " is not supported"); } }