From 6e5c37b92fbef6e03d90fd24fbc95bd52441aee4 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Fri, 16 Dec 2022 17:14:32 +0100 Subject: [PATCH 01/13] allow registering transformation and automatically apply them --- .../src/org/lflang/ast/ITransformation.java | 17 ++++++++++++++++ .../org/lflang/generator/GeneratorBase.java | 20 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 org.lflang/src/org/lflang/ast/ITransformation.java diff --git a/org.lflang/src/org/lflang/ast/ITransformation.java b/org.lflang/src/org/lflang/ast/ITransformation.java new file mode 100644 index 0000000000..50ae6a3cf1 --- /dev/null +++ b/org.lflang/src/org/lflang/ast/ITransformation.java @@ -0,0 +1,17 @@ +package org.lflang.ast; + +import java.util.List; + +import org.lflang.generator.LFResource; +import org.lflang.lf.Reactor; + +/** + * Interface for AST Transfomations + */ +public interface ITransformation { + + /** + * Apply the AST transformation to all given reactors. + */ + void applyTransformation(List reactors); +} diff --git a/org.lflang/src/org/lflang/generator/GeneratorBase.java b/org.lflang/src/org/lflang/generator/GeneratorBase.java index debfcd8334..2eb4704595 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorBase.java +++ b/org.lflang/src/org/lflang/generator/GeneratorBase.java @@ -31,6 +31,7 @@ import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -55,6 +56,7 @@ import org.lflang.TargetProperty.CoordinationType; import org.lflang.TimeUnit; import org.lflang.TimeValue; +import org.lflang.ast.ITransformation; import org.lflang.federated.FedASTUtils; import org.lflang.federated.FederateInstance; import org.lflang.federated.serialization.SupportedSerializers; @@ -233,6 +235,11 @@ public abstract class GeneratorBase extends AbstractLFValidator { // ////////////////////////////////////////// // // Private fields. + /** + * A list ot AST transformations to apply before code generation + */ + private List astTransformations = new LinkedList(); + /** * Create a new GeneratorBase object. */ @@ -242,6 +249,15 @@ public GeneratorBase(FileConfig fileConfig, ErrorReporter errorReporter) { this.commandFactory = new GeneratorCommandFactory(errorReporter, fileConfig); } + /** + * Register an AST transformation to be applied to the AST. + * + * The transformations will be applied in the order that they are registered in. + */ + protected void registerTransformation(ITransformation transformation) { + astTransformations.add(transformation); + } + // ////////////////////////////////////////// // // Code generation functions to override for a concrete code generator. @@ -356,6 +372,10 @@ public void doGenerate(Resource resource, LFGeneratorContext context) { // FIXME: Should the GeneratorBase pull in `files` from imported // resources? + for (ITransformation transformation : astTransformations) { + transformation.applyTransformation(reactors); + } + // Reroute connections that have delays associated with them via // generated delay reactors. transformDelays(); From ad122018717093fdd364a4605d2783a3e769cd22 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Fri, 16 Dec 2022 17:39:03 +0100 Subject: [PATCH 02/13] Factor out delay code generation in a separate interface --- .../org/lflang/generator/GeneratorBase.java | 40 +------------------ .../lflang/generator/IDelayBodyGenerator.java | 29 ++++++++++++++ 2 files changed, 30 insertions(+), 39 deletions(-) create mode 100644 org.lflang/src/org/lflang/generator/IDelayBodyGenerator.java diff --git a/org.lflang/src/org/lflang/generator/GeneratorBase.java b/org.lflang/src/org/lflang/generator/GeneratorBase.java index 2eb4704595..4b81c0d1b0 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorBase.java +++ b/org.lflang/src/org/lflang/generator/GeneratorBase.java @@ -88,7 +88,7 @@ * @author{Matt Weber } * @author{Soroush Bateni } */ -public abstract class GeneratorBase extends AbstractLFValidator { +public abstract class GeneratorBase extends AbstractLFValidator implements IDelayBodyGenerator { //////////////////////////////////////////// //// Public fields. @@ -473,44 +473,6 @@ public boolean errorsOccurred() { */ public abstract TargetTypes getTargetTypes(); - /** - * 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 - */ - 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 action the action that triggers the reaction - * @param port the port to write to - */ - public abstract String generateForwardBody(Action action, VarRef port); - - /** - * Generate code for the generic type to be used in the class definition - * of a generated delay reactor. - */ - public abstract String generateDelayGeneric(); - - /** - * Return true if the reaction is unordered. An unordered reaction is one - * that does not have any dependency on other reactions in the containing - * reactor, and where no other reaction in the containing reactor depends - * on it. There is currently no way in the syntax of LF to make a reaction - * unordered, deliberately, because it can introduce unexpected - * nondeterminacy. However, certain automatically generated reactions are - * known to be safe to be unordered because they do not interact with the - * state of the containing reactor. To make a reaction unordered, when - * the Reaction instance is created, add that instance to this set. - * @return True if the reaction has been marked unordered. - */ - public boolean isUnordered(Reaction reaction) { - return unorderedReactions != null && unorderedReactions.contains(reaction); - } - /** * Mark the reaction unordered. An unordered reaction is one that does not * have any dependency on other reactions in the containing reactor, and diff --git a/org.lflang/src/org/lflang/generator/IDelayBodyGenerator.java b/org.lflang/src/org/lflang/generator/IDelayBodyGenerator.java new file mode 100644 index 0000000000..2922475d74 --- /dev/null +++ b/org.lflang/src/org/lflang/generator/IDelayBodyGenerator.java @@ -0,0 +1,29 @@ +package org.lflang.generator; + +import org.lflang.lf.Action; +import org.lflang.lf.VarRef; + +public interface IDelayBodyGenerator { + + /** + * 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 + */ + 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 action the action that triggers the reaction + * @param port the port to write to + */ + String generateForwardBody(Action action, VarRef port); + + /** + * Generate code for the generic type to be used in the class definition + * of a generated delay reactor. + */ + String generateDelayGeneric(); +} From 2fe4488bb2828bd0fa1780f50fc1c6a8a2bf6d56 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Mon, 19 Dec 2022 09:53:03 +0100 Subject: [PATCH 03/13] implement basic after delay transformation --- org.lflang/src/org/lflang/ASTUtils.java | 18 +++++---- .../lflang/ast/AfterDelayTransformation.java | 34 +++++++++++++++++ .../org/lflang/generator/GeneratorBase.java | 19 +--------- .../lflang/generator/IDelayBodyGenerator.java | 38 ++++++++++++++++++- .../org/lflang/generator/c/CGenerator.java | 4 ++ .../org/lflang/generator/cpp/CppGenerator.kt | 4 ++ .../lflang/generator/rust/RustGenerator.kt | 5 +++ .../org/lflang/generator/ts/TSGenerator.kt | 5 +++ 8 files changed, 100 insertions(+), 27 deletions(-) create mode 100644 org.lflang/src/org/lflang/ast/AfterDelayTransformation.java diff --git a/org.lflang/src/org/lflang/ASTUtils.java b/org.lflang/src/org/lflang/ASTUtils.java index dc15877879..7bd6513157 100644 --- a/org.lflang/src/org/lflang/ASTUtils.java +++ b/org.lflang/src/org/lflang/ASTUtils.java @@ -58,7 +58,9 @@ import org.lflang.ast.ToText; import org.lflang.generator.CodeMap; import org.lflang.generator.GeneratorBase; +import org.lflang.generator.IDelayBodyGenerator; import org.lflang.generator.InvalidSourceException; +import org.lflang.generator.TargetTypes; import org.lflang.lf.Action; import org.lflang.lf.ActionOrigin; import org.lflang.lf.Assignment; @@ -150,7 +152,7 @@ public static Iterable getAllReactors(Resource resource) { * @param resource The AST. * @param generator A code generator. */ - public static void insertGeneratedDelays(Resource resource, GeneratorBase generator) { + public static void insertGeneratedDelays(List reactors, IDelayBodyGenerator generator, TargetTypes targetTypes) { // The resulting changes to the AST are performed _after_ iterating // in order to avoid concurrent modification problems. List oldConnections = new ArrayList<>(); @@ -158,14 +160,14 @@ public static void insertGeneratedDelays(Resource resource, GeneratorBase genera Map> delayInstances = new LinkedHashMap<>(); // Iterate over the connections in the tree. - for (Reactor container : getAllReactors(resource)) { + for (Reactor container : reactors) { for (Connection connection : allConnections(container)) { if (connection.getDelay() != null) { EObject parent = connection.eContainer(); // Assume all the types are the same, so just use the first on the right. Type type = ((Port) connection.getRightPorts().get(0).getVariable()).getType(); - Reactor delayClass = getDelayClass(type, generator); - String generic = generator.getTargetTypes().supportsGenerics() ? generator.getTargetTypes().getTargetType(InferredType.fromAST(type)) : ""; + Reactor delayClass = getDelayClass(type, generator, targetTypes); + String generic = targetTypes.supportsGenerics() ? targetTypes.getTargetType(InferredType.fromAST(type)) : ""; Instantiation delayInstance = getDelayInstance(delayClass, connection, generic, !generator.generateAfterDelaysWithVariableWidth()); @@ -455,9 +457,9 @@ private static Instantiation getDelayInstance(Reactor delayClass, * @param type The type the delay class must be compatible with. * @param generator A code generator. */ - private static Reactor getDelayClass(Type type, GeneratorBase generator) { + private static Reactor getDelayClass(Type type, IDelayBodyGenerator generator, TargetTypes targetTypes) { String className; - if (generator.getTargetTypes().supportsGenerics()) { + if (targetTypes.supportsGenerics()) { className = GeneratorBase.GEN_DELAY_CLASS_NAME; } else { String id = Integer.toHexString(InferredType.fromAST(type).toText().hashCode()); @@ -499,7 +501,7 @@ private static Reactor getDelayClass(Type type, GeneratorBase generator) { action.setMinDelay(paramRef); action.setOrigin(ActionOrigin.LOGICAL); - if (generator.getTargetTypes().supportsGenerics()) { + if (targetTypes.supportsGenerics()) { action.setType(factory.createType()); action.getType().setId("T"); } else { @@ -544,7 +546,7 @@ private static Reactor getDelayClass(Type type, GeneratorBase generator) { delayClass.getReactions().add(r1); // Add a type parameter if the target supports it. - if (generator.getTargetTypes().supportsGenerics()) { + if (targetTypes.supportsGenerics()) { TypeParm parm = factory.createTypeParm(); parm.setLiteral(generator.generateDelayGeneric()); delayClass.getTypeParms().add(parm); diff --git a/org.lflang/src/org/lflang/ast/AfterDelayTransformation.java b/org.lflang/src/org/lflang/ast/AfterDelayTransformation.java new file mode 100644 index 0000000000..b14292eb7b --- /dev/null +++ b/org.lflang/src/org/lflang/ast/AfterDelayTransformation.java @@ -0,0 +1,34 @@ +package org.lflang.ast; + +import java.util.List; + +import org.lflang.ASTUtils; +import org.lflang.generator.IDelayBodyGenerator; +import org.lflang.generator.TargetTypes; +import org.lflang.lf.Reactor; + +public class AfterDelayTransformation implements ITransformation { + + /** + * A code generator used to insert reaction bodies for the generated delay reactors. + */ + private IDelayBodyGenerator delayCodeGenerator; + + /** + * A target type instance that is used during the transformation to manage target specific types + */ + private TargetTypes targetTypes; + + public AfterDelayTransformation(IDelayBodyGenerator generator, TargetTypes targetTypes) { + this.delayCodeGenerator = generator; + this.targetTypes = targetTypes; + } + + /** + * Transform all after delay connections by inserting generated delay reactors. + */ + @Override + public void applyTransformation(List reactors) { + ASTUtils.insertGeneratedDelays(reactors, delayCodeGenerator, targetTypes); + } +} diff --git a/org.lflang/src/org/lflang/generator/GeneratorBase.java b/org.lflang/src/org/lflang/generator/GeneratorBase.java index 4b81c0d1b0..69ce672bd4 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorBase.java +++ b/org.lflang/src/org/lflang/generator/GeneratorBase.java @@ -93,11 +93,6 @@ public abstract class GeneratorBase extends AbstractLFValidator implements IDela //////////////////////////////////////////// //// Public fields. - /** - * Constant that specifies how to name generated delay reactors. - */ - public static String GEN_DELAY_CLASS_NAME = "_lf_GenDelay"; - /** * The main (top-level) reactor instance. */ @@ -376,10 +371,6 @@ public void doGenerate(Resource resource, LFGeneratorContext context) { transformation.applyTransformation(reactors); } - // Reroute connections that have delays associated with them via - // generated delay reactors. - transformDelays(); - // Transform connections that reside in mutually exclusive modes and are otherwise conflicting // This should be done before creating the instantiation graph transformConflictingConnectionsInModalReactors(); @@ -440,15 +431,6 @@ protected void setReactorsAndInstantiationGraph(LFGeneratorContext.Mode mode) { } } - /** - * For each involved resource, replace connections with delays with generated delay reactors. - */ - private void transformDelays() { - for (LFResource r : resources) { - ASTUtils.insertGeneratedDelays(r.eResource, this); - } - } - /** * Copy user specific files to the src-gen folder. * @@ -1223,6 +1205,7 @@ public void printInfo(LFGeneratorContext.Mode mode) { * side of the connection. This gives the code generator the information needed to infer the correct width at * runtime. */ + @Override public boolean generateAfterDelaysWithVariableWidth() { return true; } /** diff --git a/org.lflang/src/org/lflang/generator/IDelayBodyGenerator.java b/org.lflang/src/org/lflang/generator/IDelayBodyGenerator.java index 2922475d74..85bcab0566 100644 --- a/org.lflang/src/org/lflang/generator/IDelayBodyGenerator.java +++ b/org.lflang/src/org/lflang/generator/IDelayBodyGenerator.java @@ -1,13 +1,24 @@ package org.lflang.generator; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.xtext.xbase.lib.IteratorExtensions; + import org.lflang.lf.Action; +import org.lflang.lf.Model; +import org.lflang.lf.Reactor; import org.lflang.lf.VarRef; public interface IDelayBodyGenerator { + /** + * Constant that specifies how to name generated delay reactors. + */ + String GEN_DELAY_CLASS_NAME = "_lf_GenDelay"; + /** * 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 */ @@ -16,14 +27,39 @@ public interface IDelayBodyGenerator { /** * Generate code for the body of a reaction that is triggered by the * given action and writes its value to the given port. + * * @param action the action that triggers the reaction * @param port the port to write to */ - String generateForwardBody(Action action, VarRef port); + String generateForwardBody(Action action, VarRef port); /** * Generate code for the generic type to be used in the class definition * of a generated delay reactor. */ String generateDelayGeneric(); + + /** + * 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 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 + * side of the connection. This gives the code generator the information needed to infer the + * correct width at + * runtime. + */ + boolean generateAfterDelaysWithVariableWidth(); + + void addDelayClass(Reactor generatedDelay); + + Reactor findDelayClass(String className); + } diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.java b/org.lflang/src/org/lflang/generator/c/CGenerator.java index ec2b7986c3..7db7f084f4 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.java @@ -63,6 +63,7 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY import org.lflang.TargetProperty.CoordinationType; import org.lflang.TargetProperty.Platform; import org.lflang.TimeValue; +import org.lflang.ast.AfterDelayTransformation; import org.lflang.federated.FedFileConfig; import org.lflang.federated.FederateInstance; import org.lflang.federated.launcher.FedCLauncher; @@ -395,6 +396,9 @@ protected CGenerator( this.CCppMode = CCppMode; this.types = types; this.cmakeGenerator = cmakeGenerator; + + // Register the after delay transformation to be applied by GeneratorBase. + registerTransformation(new AfterDelayTransformation(this, types)); } public CGenerator(FileConfig fileConfig, ErrorReporter errorReporter, boolean CCppMode) { diff --git a/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt index f04fd70ee3..4bfaf75f50 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt @@ -31,6 +31,7 @@ import org.lflang.ErrorReporter import org.lflang.Target import org.lflang.TimeUnit import org.lflang.TimeValue +import org.lflang.ast.AfterDelayTransformation import org.lflang.generator.CodeMap import org.lflang.generator.GeneratorBase import org.lflang.generator.GeneratorResult @@ -65,6 +66,9 @@ class CppGenerator( } override fun doGenerate(resource: Resource, context: LFGeneratorContext) { + // Register the after delay transformation to be applied by GeneratorBase. + registerTransformation(AfterDelayTransformation(this, CppTypes)) + super.doGenerate(resource, context) if (!canGenerate(errorsOccurred(), mainDef, errorReporter, context)) return diff --git a/org.lflang/src/org/lflang/generator/rust/RustGenerator.kt b/org.lflang/src/org/lflang/generator/rust/RustGenerator.kt index 804b49fe56..df50b267c5 100644 --- a/org.lflang/src/org/lflang/generator/rust/RustGenerator.kt +++ b/org.lflang/src/org/lflang/generator/rust/RustGenerator.kt @@ -28,6 +28,7 @@ import org.eclipse.emf.ecore.resource.Resource import org.lflang.ErrorReporter import org.lflang.Target import org.lflang.TargetProperty.BuildType +import org.lflang.ast.AfterDelayTransformation import org.lflang.generator.GeneratorUtils.canGenerate import org.lflang.generator.CodeMap import org.lflang.generator.GeneratorBase @@ -35,6 +36,7 @@ import org.lflang.generator.GeneratorResult import org.lflang.generator.IntegratedBuilder import org.lflang.generator.LFGeneratorContext import org.lflang.generator.TargetTypes +import org.lflang.generator.cpp.CppTypes import org.lflang.joinWithCommas import org.lflang.lf.Action import org.lflang.lf.VarRef @@ -70,6 +72,9 @@ class RustGenerator( } override fun doGenerate(resource: Resource, context: LFGeneratorContext) { + // Register the after delay transformation to be applied by GeneratorBase. + registerTransformation(AfterDelayTransformation(this, CppTypes)) + super.doGenerate(resource, context) if (!canGenerate(errorsOccurred(), mainDef, errorReporter, context)) return diff --git a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt index 04f00cc788..76a37c527f 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt @@ -32,6 +32,7 @@ import org.lflang.ErrorReporter import org.lflang.InferredType import org.lflang.Target import org.lflang.TimeValue +import org.lflang.ast.AfterDelayTransformation import org.lflang.federated.FederateInstance import org.lflang.federated.launcher.FedTSLauncher import org.lflang.federated.serialization.SupportedSerializers @@ -46,6 +47,7 @@ import org.lflang.generator.PrependOperator import org.lflang.generator.ReactorInstance import org.lflang.generator.SubContext import org.lflang.generator.TargetTypes +import org.lflang.generator.cpp.CppTypes import org.lflang.lf.Action import org.lflang.lf.Expression import org.lflang.lf.VarRef @@ -118,6 +120,9 @@ class TSGenerator( * @param context The context of this build. */ override fun doGenerate(resource: Resource, context: LFGeneratorContext) { + // Register the after delay transformation to be applied by GeneratorBase. + registerTransformation(AfterDelayTransformation(this, CppTypes)) + super.doGenerate(resource, context) if (!canGenerate(errorsOccurred(), mainDef, errorReporter, context)) return From 5fca59783d2ded3dcefa6214279012f68eb4946f Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Mon, 19 Dec 2022 10:06:57 +0100 Subject: [PATCH 04/13] Move the after delay transformation code --- .../tests/compiler/LetInferenceTests.java | 4 +- org.lflang/src/org/lflang/ASTUtils.java | 276 ---------------- .../lflang/ast/AfterDelayTransformation.java | 307 +++++++++++++++++- 3 files changed, 306 insertions(+), 281 deletions(-) diff --git a/org.lflang.tests/src/org/lflang/tests/compiler/LetInferenceTests.java b/org.lflang.tests/src/org/lflang/tests/compiler/LetInferenceTests.java index e97916d3c9..6dc8554101 100644 --- a/org.lflang.tests/src/org/lflang/tests/compiler/LetInferenceTests.java +++ b/org.lflang.tests/src/org/lflang/tests/compiler/LetInferenceTests.java @@ -37,11 +37,11 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.lflang.ASTUtils; import org.lflang.DefaultErrorReporter; import org.lflang.FileConfig; import org.lflang.TimeUnit; import org.lflang.TimeValue; +import org.lflang.ast.AfterDelayTransformation; import org.lflang.generator.ReactionInstance; import org.lflang.generator.ReactorInstance; import org.lflang.generator.c.CGenerator; @@ -99,7 +99,7 @@ public void testLet() throws Exception { )); Assertions.assertNotNull(model); - ASTUtils.insertGeneratedDelays(model.eResource(), new CGenerator(new FileConfig(model.eResource(), Path.of("./a/"), true), new DefaultErrorReporter())); + AfterDelayTransformation.insertGeneratedDelays(model.eResource(), new CGenerator(new FileConfig(model.eResource(), Path.of("./a/"), true), new DefaultErrorReporter())); Assertions.assertTrue(model.eResource().getErrors().isEmpty(), "Encountered unexpected error while parsing: " + model.eResource().getErrors()); diff --git a/org.lflang/src/org/lflang/ASTUtils.java b/org.lflang/src/org/lflang/ASTUtils.java index 7bd6513157..7f4cdab33f 100644 --- a/org.lflang/src/org/lflang/ASTUtils.java +++ b/org.lflang/src/org/lflang/ASTUtils.java @@ -28,7 +28,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -44,7 +43,6 @@ import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.resource.Resource; -import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.xtext.TerminalRule; import org.eclipse.xtext.nodemodel.ICompositeNode; import org.eclipse.xtext.nodemodel.INode; @@ -57,12 +55,8 @@ import org.eclipse.xtext.xbase.lib.StringExtensions; import org.lflang.ast.ToText; import org.lflang.generator.CodeMap; -import org.lflang.generator.GeneratorBase; -import org.lflang.generator.IDelayBodyGenerator; import org.lflang.generator.InvalidSourceException; -import org.lflang.generator.TargetTypes; import org.lflang.lf.Action; -import org.lflang.lf.ActionOrigin; import org.lflang.lf.Assignment; import org.lflang.lf.Code; import org.lflang.lf.Connection; @@ -89,7 +83,6 @@ import org.lflang.lf.Time; import org.lflang.lf.Timer; import org.lflang.lf.Type; -import org.lflang.lf.TypeParm; import org.lflang.lf.VarRef; import org.lflang.lf.Variable; import org.lflang.lf.WidthSpec; @@ -146,76 +139,6 @@ public static Iterable getAllReactors(Resource resource) { .collect(Collectors.toList()); } - /** - * Find connections in the given resource that have a delay associated with them, - * and reroute them via a generated delay reactor. - * @param resource The AST. - * @param generator A code generator. - */ - public static void insertGeneratedDelays(List reactors, IDelayBodyGenerator generator, TargetTypes targetTypes) { - // The resulting changes to the AST are performed _after_ iterating - // in order to avoid concurrent modification problems. - List oldConnections = new ArrayList<>(); - Map> newConnections = new LinkedHashMap<>(); - Map> delayInstances = new LinkedHashMap<>(); - - // Iterate over the connections in the tree. - for (Reactor container : reactors) { - for (Connection connection : allConnections(container)) { - if (connection.getDelay() != null) { - EObject parent = connection.eContainer(); - // Assume all the types are the same, so just use the first on the right. - Type type = ((Port) connection.getRightPorts().get(0).getVariable()).getType(); - Reactor delayClass = getDelayClass(type, generator, targetTypes); - String generic = targetTypes.supportsGenerics() ? targetTypes.getTargetType(InferredType.fromAST(type)) : ""; - Instantiation delayInstance = getDelayInstance(delayClass, connection, generic, - !generator.generateAfterDelaysWithVariableWidth()); - - // Stage the new connections for insertion into the tree. - List connections = convertToEmptyListIfNull(newConnections.get(parent)); - connections.addAll(rerouteViaDelay(connection, delayInstance)); - newConnections.put(parent, connections); - // Stage the original connection for deletion from the tree. - oldConnections.add(connection); - - // Stage the newly created delay reactor instance for insertion - List instances = convertToEmptyListIfNull(delayInstances.get(parent)); - instances.add(delayInstance); - delayInstances.put(parent, instances); - } - } - } - - // Remove old connections; insert new ones. - oldConnections.forEach(connection -> { - var container = connection.eContainer(); - if (container instanceof Reactor) { - ((Reactor) container).getConnections().remove(connection); - } else if (container instanceof Mode) { - ((Mode) container).getConnections().remove(connection); - } - }); - newConnections.forEach((container, connections) -> { - if (container instanceof Reactor) { - ((Reactor) container).getConnections().addAll(connections); - } else if (container instanceof Mode) { - ((Mode) container).getConnections().addAll(connections); - } - }); - // Finally, insert the instances and, before doing so, assign them a unique name. - delayInstances.forEach((container, instantiations) -> - instantiations.forEach(instantiation -> { - if (container instanceof Reactor) { - instantiation.setName(getUniqueIdentifier((Reactor) container, "delay")); - ((Reactor) container).getInstantiations().add(instantiation); - } else if (container instanceof Mode) { - instantiation.setName(getUniqueIdentifier((Reactor) container.eContainer(), "delay")); - ((Mode) container).getInstantiations().add(instantiation); - } - }) - ); - } - /** * Find connections in the given resource that would be conflicting writes if they were not located in mutually * exclusive modes. @@ -358,206 +281,7 @@ public static boolean hasMultipleConnections(Connection connection) { || rightPortAsPort.getWidthSpec() != null || rightContainer != null && rightContainer.getWidthSpec() != null; } - - /** - * Take a connection and reroute it via an instance of a generated delay - * reactor. This method returns a list to new connections to substitute - * the original one. - * @param connection The connection to reroute. - * @param delayInstance The delay instance to route the connection through. - */ - private static List rerouteViaDelay(Connection connection, - Instantiation delayInstance) { - List connections = new ArrayList<>(); - Connection upstream = factory.createConnection(); - Connection downstream = factory.createConnection(); - VarRef input = factory.createVarRef(); - VarRef output = factory.createVarRef(); - - Reactor delayClass = toDefinition(delayInstance.getReactorClass()); - - // Establish references to the involved ports. - input.setContainer(delayInstance); - input.setVariable(delayClass.getInputs().get(0)); - output.setContainer(delayInstance); - output.setVariable(delayClass.getOutputs().get(0)); - upstream.getLeftPorts().addAll(connection.getLeftPorts()); - upstream.getRightPorts().add(input); - downstream.getLeftPorts().add(output); - downstream.getRightPorts().addAll(connection.getRightPorts()); - downstream.setIterated(connection.isIterated()); - connections.add(upstream); - connections.add(downstream); - return connections; - } - - /** - * Create a new instance delay instances using the given reactor class. - * The supplied time value is used to override the default interval (which - * is zero). - * If the target supports parametric polymorphism, then a single class may - * be used for each instantiation, in which case a non-empty string must - * be supplied to parameterize the instance. - * A default name ("delay") is assigned to the instantiation, but this - * name must be overridden at the call site, where checks can be done to - * avoid name collisions in the container in which the instantiation is - * to be placed. Such checks (or modifications of the AST) are not - * performed in this method in order to avoid causing concurrent - * modification exceptions. - * @param delayClass The class to create an instantiation for - * @param connection The connection to create a delay instantiation foe - * @param generic A string that denotes the appropriate type parameter, - * which should be null or empty if the target does not support generics. - * @param defineWidthFromConnection If this is true and if the connection - * is a wide connection, then instantiate a bank of delays where the width - * is given by ports involved in the connection. Otherwise, the width will - * be unspecified indicating a variable length. - */ - private static Instantiation getDelayInstance(Reactor delayClass, - Connection connection, String generic, Boolean defineWidthFromConnection) { - Expression delay = connection.getDelay(); - Instantiation delayInstance = factory.createInstantiation(); - delayInstance.setReactorClass(delayClass); - if (!StringExtensions.isNullOrEmpty(generic)) { - TypeParm typeParm = factory.createTypeParm(); - typeParm.setLiteral(generic); - delayInstance.getTypeParms().add(typeParm); - } - if (hasMultipleConnections(connection)) { - WidthSpec widthSpec = factory.createWidthSpec(); - if (defineWidthFromConnection) { - // Add all left ports of the connection to the WidthSpec of the generated delay instance. - // This allows the code generator to later infer the width from the involved ports. - // We only consider the left ports here, as they could be part of a broadcast. In this case, we want - // to delay the ports first, and then broadcast the output of the delays. - for (VarRef port : connection.getLeftPorts()) { - WidthTerm term = factory.createWidthTerm(); - term.setPort(EcoreUtil.copy(port)); - widthSpec.getTerms().add(term); - } - } else { - widthSpec.setOfVariableLength(true); - } - delayInstance.setWidthSpec(widthSpec); - } - Assignment assignment = factory.createAssignment(); - assignment.setLhs(delayClass.getParameters().get(0)); - assignment.getRhs().add(delay); - delayInstance.getParameters().add(assignment); - delayInstance.setName("delay"); // This has to be overridden. - return delayInstance; - } - - /** - * Return a synthesized AST node that represents the definition of a delay - * reactor. Depending on whether the target supports generics, either this - * method will synthesize a generic definition and keep returning it upon - * subsequent calls, or otherwise, it will synthesize a new definition for - * each new type it hasn't yet created a compatible delay reactor for. - * @param type The type the delay class must be compatible with. - * @param generator A code generator. - */ - private static Reactor getDelayClass(Type type, IDelayBodyGenerator generator, TargetTypes targetTypes) { - String className; - if (targetTypes.supportsGenerics()) { - className = GeneratorBase.GEN_DELAY_CLASS_NAME; - } else { - String id = Integer.toHexString(InferredType.fromAST(type).toText().hashCode()); - className = String.format("%s_%s", GeneratorBase.GEN_DELAY_CLASS_NAME, id); - } - // Only add class definition if it is not already there. - Reactor classDef = generator.findDelayClass(className); - if (classDef != null) { - return classDef; - } - - Reactor delayClass = factory.createReactor(); - Parameter delayParameter = factory.createParameter(); - Action action = factory.createAction(); - VarRef triggerRef = factory.createVarRef(); - VarRef effectRef = factory.createVarRef(); - Input input = factory.createInput(); - Output output = factory.createOutput(); - VarRef inRef = factory.createVarRef(); - VarRef outRef = factory.createVarRef(); - - Reaction r1 = factory.createReaction(); - Reaction r2 = factory.createReaction(); - - delayParameter.setName("delay"); - delayParameter.setType(factory.createType()); - delayParameter.getType().setId("time"); - delayParameter.getType().setTime(true); - Time defaultTime = factory.createTime(); - defaultTime.setUnit(null); - defaultTime.setInterval(0); - delayParameter.getInit().add(defaultTime); - - // Name the newly created action; set its delay and type. - action.setName("act"); - var paramRef = factory.createParameterReference(); - paramRef.setParameter(delayParameter); - action.setMinDelay(paramRef); - action.setOrigin(ActionOrigin.LOGICAL); - - if (targetTypes.supportsGenerics()) { - action.setType(factory.createType()); - action.getType().setId("T"); - } else { - action.setType(EcoreUtil.copy(type)); - } - - input.setName("inp"); - input.setType(EcoreUtil.copy(action.getType())); - - output.setName("out"); - output.setType(EcoreUtil.copy(action.getType())); - - // Establish references to the involved ports. - inRef.setVariable(input); - outRef.setVariable(output); - - // Establish references to the action. - triggerRef.setVariable(action); - effectRef.setVariable(action); - - // Add the action to the reactor. - delayClass.setName(className); - delayClass.getActions().add(action); - - // Configure the second reaction, which reads the input. - r1.getTriggers().add(inRef); - r1.getEffects().add(effectRef); - r1.setCode(factory.createCode()); - r1.getCode().setBody(generator.generateDelayBody(action, inRef)); - - // Configure the first reaction, which produces the output. - r2.getTriggers().add(triggerRef); - r2.getEffects().add(outRef); - r2.setCode(factory.createCode()); - r2.getCode().setBody(generator.generateForwardBody(action, outRef)); - - // Add the reactions to the newly created reactor class. - // These need to go in the opposite order in case - // a new input arrives at the same time the delayed - // output is delivered! - delayClass.getReactions().add(r2); - delayClass.getReactions().add(r1); - - // Add a type parameter if the target supports it. - if (targetTypes.supportsGenerics()) { - TypeParm parm = factory.createTypeParm(); - parm.setLiteral(generator.generateDelayGeneric()); - delayClass.getTypeParms().add(parm); - } - delayClass.getInputs().add(input); - delayClass.getOutputs().add(output); - delayClass.getParameters().add(delayParameter); - generator.addDelayClass(delayClass); - return delayClass; - } - /** * Produce a unique identifier within a reactor based on a * given based name. If the name does not exists, it is returned; diff --git a/org.lflang/src/org/lflang/ast/AfterDelayTransformation.java b/org.lflang/src/org/lflang/ast/AfterDelayTransformation.java index b14292eb7b..72db2c50c6 100644 --- a/org.lflang/src/org/lflang/ast/AfterDelayTransformation.java +++ b/org.lflang/src/org/lflang/ast/AfterDelayTransformation.java @@ -1,18 +1,51 @@ package org.lflang.ast; +import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.xtext.xbase.lib.StringExtensions; import org.lflang.ASTUtils; +import org.lflang.InferredType; +import org.lflang.generator.GeneratorBase; import org.lflang.generator.IDelayBodyGenerator; import org.lflang.generator.TargetTypes; +import org.lflang.lf.Action; +import org.lflang.lf.ActionOrigin; +import org.lflang.lf.Assignment; +import org.lflang.lf.Connection; +import org.lflang.lf.Expression; +import org.lflang.lf.Input; +import org.lflang.lf.Instantiation; +import org.lflang.lf.LfFactory; +import org.lflang.lf.Mode; +import org.lflang.lf.Output; +import org.lflang.lf.Parameter; +import org.lflang.lf.Port; +import org.lflang.lf.Reaction; import org.lflang.lf.Reactor; +import org.lflang.lf.Time; +import org.lflang.lf.Type; +import org.lflang.lf.TypeParm; +import org.lflang.lf.VarRef; +import org.lflang.lf.WidthSpec; +import org.lflang.lf.WidthTerm; public class AfterDelayTransformation implements ITransformation { + /** + * The Lingua Franca factory for creating new AST nodes. + */ + public static final LfFactory factory = ASTUtils.factory; + /** * A code generator used to insert reaction bodies for the generated delay reactors. */ - private IDelayBodyGenerator delayCodeGenerator; + private IDelayBodyGenerator generator; /** * A target type instance that is used during the transformation to manage target specific types @@ -20,7 +53,7 @@ public class AfterDelayTransformation implements ITransformation { private TargetTypes targetTypes; public AfterDelayTransformation(IDelayBodyGenerator generator, TargetTypes targetTypes) { - this.delayCodeGenerator = generator; + this.generator = generator; this.targetTypes = targetTypes; } @@ -29,6 +62,274 @@ public AfterDelayTransformation(IDelayBodyGenerator generator, TargetTypes targe */ @Override public void applyTransformation(List reactors) { - ASTUtils.insertGeneratedDelays(reactors, delayCodeGenerator, targetTypes); + insertGeneratedDelays(reactors); + } + + /** + * Find connections in the given resource that have a delay associated with them, + * and reroute them via a generated delay reactor. + * @param resource The AST. + * @param generator A code generator. + */ + private void insertGeneratedDelays(List reactors) { + // The resulting changes to the AST are performed _after_ iterating + // in order to avoid concurrent modification problems. + List oldConnections = new ArrayList<>(); + Map> newConnections = new LinkedHashMap<>(); + Map> delayInstances = new LinkedHashMap<>(); + + // Iterate over the connections in the tree. + for (Reactor container : reactors) { + for (Connection connection : ASTUtils.allConnections(container)) { + if (connection.getDelay() != null) { + EObject parent = connection.eContainer(); + // Assume all the types are the same, so just use the first on the right. + Type type = ((Port) connection.getRightPorts().get(0).getVariable()).getType(); + Reactor delayClass = getDelayClass(type); + String generic = targetTypes.supportsGenerics() ? targetTypes.getTargetType(InferredType.fromAST(type)) : ""; + Instantiation delayInstance = getDelayInstance(delayClass, connection, generic, + !generator.generateAfterDelaysWithVariableWidth()); + + // Stage the new connections for insertion into the tree. + List connections = ASTUtils.convertToEmptyListIfNull(newConnections.get(parent)); + connections.addAll(rerouteViaDelay(connection, delayInstance)); + newConnections.put(parent, connections); + // Stage the original connection for deletion from the tree. + oldConnections.add(connection); + + // Stage the newly created delay reactor instance for insertion + List instances = ASTUtils.convertToEmptyListIfNull(delayInstances.get(parent)); + instances.add(delayInstance); + delayInstances.put(parent, instances); + } + } + } + + // Remove old connections; insert new ones. + oldConnections.forEach(connection -> { + var container = connection.eContainer(); + if (container instanceof Reactor) { + ((Reactor) container).getConnections().remove(connection); + } else if (container instanceof Mode) { + ((Mode) container).getConnections().remove(connection); + } + }); + newConnections.forEach((container, connections) -> { + if (container instanceof Reactor) { + ((Reactor) container).getConnections().addAll(connections); + } else if (container instanceof Mode) { + ((Mode) container).getConnections().addAll(connections); + } + }); + // Finally, insert the instances and, before doing so, assign them a unique name. + delayInstances.forEach((container, instantiations) -> + instantiations.forEach(instantiation -> { + if (container instanceof Reactor) { + instantiation.setName(ASTUtils.getUniqueIdentifier((Reactor) container, "delay")); + ((Reactor) container).getInstantiations().add(instantiation); + } else if (container instanceof Mode) { + instantiation.setName(ASTUtils.getUniqueIdentifier((Reactor) container.eContainer(), "delay")); + ((Mode) container).getInstantiations().add(instantiation); + } + }) + ); + } + + /** + * Take a connection and reroute it via an instance of a generated delay + * reactor. This method returns a list to new connections to substitute + * the original one. + * @param connection The connection to reroute. + * @param delayInstance The delay instance to route the connection through. + */ + private static List rerouteViaDelay(Connection connection, + Instantiation delayInstance) { + List connections = new ArrayList<>(); + Connection upstream = factory.createConnection(); + Connection downstream = factory.createConnection(); + VarRef input = factory.createVarRef(); + VarRef output = factory.createVarRef(); + + Reactor delayClass = ASTUtils.toDefinition(delayInstance.getReactorClass()); + + // Establish references to the involved ports. + input.setContainer(delayInstance); + input.setVariable(delayClass.getInputs().get(0)); + output.setContainer(delayInstance); + output.setVariable(delayClass.getOutputs().get(0)); + upstream.getLeftPorts().addAll(connection.getLeftPorts()); + upstream.getRightPorts().add(input); + downstream.getLeftPorts().add(output); + downstream.getRightPorts().addAll(connection.getRightPorts()); + downstream.setIterated(connection.isIterated()); + connections.add(upstream); + connections.add(downstream); + return connections; + } + + /** + * Create a new instance delay instances using the given reactor class. + * The supplied time value is used to override the default interval (which + * is zero). + * If the target supports parametric polymorphism, then a single class may + * be used for each instantiation, in which case a non-empty string must + * be supplied to parameterize the instance. + * A default name ("delay") is assigned to the instantiation, but this + * name must be overridden at the call site, where checks can be done to + * avoid name collisions in the container in which the instantiation is + * to be placed. Such checks (or modifications of the AST) are not + * performed in this method in order to avoid causing concurrent + * modification exceptions. + * @param delayClass The class to create an instantiation for + * @param connection The connection to create a delay instantiation foe + * @param generic A string that denotes the appropriate type parameter, + * which should be null or empty if the target does not support generics. + * @param defineWidthFromConnection If this is true and if the connection + * is a wide connection, then instantiate a bank of delays where the width + * is given by ports involved in the connection. Otherwise, the width will + * be unspecified indicating a variable length. + */ + private static Instantiation getDelayInstance(Reactor delayClass, + Connection connection, String generic, Boolean defineWidthFromConnection) { + Expression delay = connection.getDelay(); + Instantiation delayInstance = factory.createInstantiation(); + delayInstance.setReactorClass(delayClass); + if (!StringExtensions.isNullOrEmpty(generic)) { + TypeParm typeParm = factory.createTypeParm(); + typeParm.setLiteral(generic); + delayInstance.getTypeParms().add(typeParm); + } + if (ASTUtils.hasMultipleConnections(connection)) { + WidthSpec widthSpec = factory.createWidthSpec(); + if (defineWidthFromConnection) { + // Add all left ports of the connection to the WidthSpec of the generated delay instance. + // This allows the code generator to later infer the width from the involved ports. + // We only consider the left ports here, as they could be part of a broadcast. In this case, we want + // to delay the ports first, and then broadcast the output of the delays. + for (VarRef port : connection.getLeftPorts()) { + WidthTerm term = factory.createWidthTerm(); + term.setPort(EcoreUtil.copy(port)); + widthSpec.getTerms().add(term); + } + } else { + widthSpec.setOfVariableLength(true); + } + delayInstance.setWidthSpec(widthSpec); + } + Assignment assignment = factory.createAssignment(); + assignment.setLhs(delayClass.getParameters().get(0)); + assignment.getRhs().add(delay); + delayInstance.getParameters().add(assignment); + delayInstance.setName("delay"); // This has to be overridden. + return delayInstance; + } + + /** + * Return a synthesized AST node that represents the definition of a delay + * reactor. Depending on whether the target supports generics, either this + * method will synthesize a generic definition and keep returning it upon + * subsequent calls, or otherwise, it will synthesize a new definition for + * each new type it hasn't yet created a compatible delay reactor for. + * @param type The type the delay class must be compatible with. + */ + private Reactor getDelayClass(Type type) { + String className; + if (targetTypes.supportsGenerics()) { + className = GeneratorBase.GEN_DELAY_CLASS_NAME; + } else { + String id = Integer.toHexString(InferredType.fromAST(type).toText().hashCode()); + className = String.format("%s_%s", GeneratorBase.GEN_DELAY_CLASS_NAME, id); + } + + // Only add class definition if it is not already there. + Reactor classDef = generator.findDelayClass(className); + if (classDef != null) { + return classDef; + } + + Reactor delayClass = factory.createReactor(); + Parameter delayParameter = factory.createParameter(); + Action action = factory.createAction(); + VarRef triggerRef = factory.createVarRef(); + VarRef effectRef = factory.createVarRef(); + Input input = factory.createInput(); + Output output = factory.createOutput(); + VarRef inRef = factory.createVarRef(); + VarRef outRef = factory.createVarRef(); + + Reaction r1 = factory.createReaction(); + Reaction r2 = factory.createReaction(); + + delayParameter.setName("delay"); + delayParameter.setType(factory.createType()); + delayParameter.getType().setId("time"); + delayParameter.getType().setTime(true); + Time defaultTime = factory.createTime(); + defaultTime.setUnit(null); + defaultTime.setInterval(0); + delayParameter.getInit().add(defaultTime); + + // Name the newly created action; set its delay and type. + action.setName("act"); + var paramRef = factory.createParameterReference(); + paramRef.setParameter(delayParameter); + action.setMinDelay(paramRef); + action.setOrigin(ActionOrigin.LOGICAL); + + if (targetTypes.supportsGenerics()) { + action.setType(factory.createType()); + action.getType().setId("T"); + } else { + action.setType(EcoreUtil.copy(type)); + } + + input.setName("inp"); + input.setType(EcoreUtil.copy(action.getType())); + + output.setName("out"); + output.setType(EcoreUtil.copy(action.getType())); + + // Establish references to the involved ports. + inRef.setVariable(input); + outRef.setVariable(output); + + // Establish references to the action. + triggerRef.setVariable(action); + effectRef.setVariable(action); + + // Add the action to the reactor. + delayClass.setName(className); + delayClass.getActions().add(action); + + // Configure the second reaction, which reads the input. + r1.getTriggers().add(inRef); + r1.getEffects().add(effectRef); + r1.setCode(factory.createCode()); + r1.getCode().setBody(generator.generateDelayBody(action, inRef)); + + // Configure the first reaction, which produces the output. + r2.getTriggers().add(triggerRef); + r2.getEffects().add(outRef); + r2.setCode(factory.createCode()); + r2.getCode().setBody(generator.generateForwardBody(action, outRef)); + + // Add the reactions to the newly created reactor class. + // These need to go in the opposite order in case + // a new input arrives at the same time the delayed + // output is delivered! + delayClass.getReactions().add(r2); + delayClass.getReactions().add(r1); + + // Add a type parameter if the target supports it. + if (targetTypes.supportsGenerics()) { + TypeParm parm = factory.createTypeParm(); + parm.setLiteral(generator.generateDelayGeneric()); + delayClass.getTypeParms().add(parm); + } + delayClass.getInputs().add(input); + delayClass.getOutputs().add(output); + delayClass.getParameters().add(delayParameter); + generator.addDelayClass(delayClass); + return delayClass; } } From d781d38bbe91125ebefb93986e0e5e6dba6ad1fd Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Mon, 19 Dec 2022 10:36:06 +0100 Subject: [PATCH 05/13] keep track of the inserted classes in the transformation class --- .../lflang/ast/AfterDelayTransformation.java | 45 ++++++++++++++++--- .../org/lflang/generator/GeneratorBase.java | 26 ----------- .../lflang/generator/IDelayBodyGenerator.java | 4 -- .../org/lflang/generator/c/CGenerator.java | 6 +-- .../org/lflang/generator/cpp/CppGenerator.kt | 2 +- .../lflang/generator/rust/RustGenerator.kt | 2 +- .../org/lflang/generator/ts/TSGenerator.kt | 2 +- 7 files changed, 46 insertions(+), 41 deletions(-) diff --git a/org.lflang/src/org/lflang/ast/AfterDelayTransformation.java b/org.lflang/src/org/lflang/ast/AfterDelayTransformation.java index 72db2c50c6..5594ed2611 100644 --- a/org.lflang/src/org/lflang/ast/AfterDelayTransformation.java +++ b/org.lflang/src/org/lflang/ast/AfterDelayTransformation.java @@ -2,11 +2,15 @@ import java.util.ArrayList; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.util.EcoreUtil; +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; @@ -23,6 +27,7 @@ import org.lflang.lf.Instantiation; import org.lflang.lf.LfFactory; import org.lflang.lf.Mode; +import org.lflang.lf.Model; import org.lflang.lf.Output; import org.lflang.lf.Parameter; import org.lflang.lf.Port; @@ -52,9 +57,20 @@ public class AfterDelayTransformation implements ITransformation { */ private TargetTypes targetTypes; - public AfterDelayTransformation(IDelayBodyGenerator generator, TargetTypes targetTypes) { + /** + * The Eclipse eCore view of the main LF file. + */ + private Resource mainResource; + + /** + * Collection of generated delay classes. + */ + private LinkedHashSet delayClasses = new LinkedHashSet<>(); + + public AfterDelayTransformation(IDelayBodyGenerator generator, TargetTypes targetTypes, Resource mainResource) { this.generator = generator; this.targetTypes = targetTypes; + this.mainResource = mainResource; } /** @@ -68,8 +84,7 @@ public void applyTransformation(List reactors) { /** * Find connections in the given resource that have a delay associated with them, * and reroute them via a generated delay reactor. - * @param resource The AST. - * @param generator A code generator. + * @param reactors A list of reactors to apply the transformation to. */ private void insertGeneratedDelays(List reactors) { // The resulting changes to the AST are performed _after_ iterating @@ -242,7 +257,7 @@ private Reactor getDelayClass(Type type) { } // Only add class definition if it is not already there. - Reactor classDef = generator.findDelayClass(className); + Reactor classDef = findDelayClass(className); if (classDef != null) { return classDef; } @@ -329,7 +344,27 @@ private Reactor getDelayClass(Type type) { delayClass.getInputs().add(input); delayClass.getOutputs().add(output); delayClass.getParameters().add(delayParameter); - generator.addDelayClass(delayClass); + addDelayClass(delayClass); return delayClass; } + + /** + * Store the given reactor in the collection of generated delay classes + * and insert it in the AST under the top-level reactor's node. + */ + private void addDelayClass(Reactor generatedDelay) { + // Record this class, so it can be reused. + delayClasses.add(generatedDelay); + // And hook it into the AST. + EObject node = IteratorExtensions.findFirst(mainResource.getAllContents(), Model.class::isInstance); + ((Model) node).getReactors().add(generatedDelay); + } + + /** + * Return the generated delay reactor that corresponds to the given class + * name if it had been created already, `null` otherwise. + */ + private Reactor findDelayClass(String className) { + return IterableExtensions.findFirst(delayClasses, it -> it.getName().equals(className)); + } } diff --git a/org.lflang/src/org/lflang/generator/GeneratorBase.java b/org.lflang/src/org/lflang/generator/GeneratorBase.java index 69ce672bd4..0f89399c6f 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorBase.java +++ b/org.lflang/src/org/lflang/generator/GeneratorBase.java @@ -67,7 +67,6 @@ import org.lflang.lf.Instantiation; import org.lflang.lf.LfFactory; import org.lflang.lf.Mode; -import org.lflang.lf.Model; import org.lflang.lf.Parameter; import org.lflang.lf.Reaction; import org.lflang.lf.Reactor; @@ -123,11 +122,6 @@ public abstract class GeneratorBase extends AbstractLFValidator implements IDela public GeneratorCommandFactory getCommandFactory() { return commandFactory; } - /** - * Collection of generated delay classes. - */ - private LinkedHashSet delayClasses = new LinkedHashSet<>(); - /** * Definition of the main (top-level) reactor. * This is an automatically generated AST node for the top-level @@ -256,26 +250,6 @@ protected void registerTransformation(ITransformation transformation) { // ////////////////////////////////////////// // // Code generation functions to override for a concrete code generator. - /** - * Store the given reactor in the collection of generated delay classes - * 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. - delayClasses.add(generatedDelay); - // And hook it into the AST. - EObject node = IteratorExtensions.findFirst(fileConfig.resource.getAllContents(), Model.class::isInstance); - ((Model) node).getReactors().add(generatedDelay); - } - - /** - * Return the generated delay reactor that corresponds to the given class - * name if it had been created already, `null` otherwise. - */ - public Reactor findDelayClass(String className) { - return IterableExtensions.findFirst(delayClasses, it -> it.getName().equals(className)); - } - /** * If there is a main or federated reactor, then create a synthetic Instantiation * for that top-level reactor and set the field mainDef to refer to it. diff --git a/org.lflang/src/org/lflang/generator/IDelayBodyGenerator.java b/org.lflang/src/org/lflang/generator/IDelayBodyGenerator.java index 85bcab0566..835b368303 100644 --- a/org.lflang/src/org/lflang/generator/IDelayBodyGenerator.java +++ b/org.lflang/src/org/lflang/generator/IDelayBodyGenerator.java @@ -58,8 +58,4 @@ public interface IDelayBodyGenerator { */ boolean generateAfterDelaysWithVariableWidth(); - void addDelayClass(Reactor generatedDelay); - - Reactor findDelayClass(String className); - } diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.java b/org.lflang/src/org/lflang/generator/c/CGenerator.java index 7db7f084f4..0671fd39cb 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.java @@ -396,9 +396,6 @@ protected CGenerator( this.CCppMode = CCppMode; this.types = types; this.cmakeGenerator = cmakeGenerator; - - // Register the after delay transformation to be applied by GeneratorBase. - registerTransformation(new AfterDelayTransformation(this, types)); } public CGenerator(FileConfig fileConfig, ErrorReporter errorReporter, boolean CCppMode) { @@ -498,6 +495,9 @@ protected boolean isOSCompatible() { */ @Override public void doGenerate(Resource resource, LFGeneratorContext context) { + // Register the after delay transformation to be applied by GeneratorBase. + registerTransformation(new AfterDelayTransformation(this, types, resource)); + super.doGenerate(resource, context); if (!GeneratorUtils.canGenerate(errorsOccurred(), mainDef, errorReporter, context)) return; if (!isOSCompatible()) return; // Incompatible OS and configuration diff --git a/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt index 4bfaf75f50..d46ffe8953 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt @@ -67,7 +67,7 @@ class CppGenerator( override fun doGenerate(resource: Resource, context: LFGeneratorContext) { // Register the after delay transformation to be applied by GeneratorBase. - registerTransformation(AfterDelayTransformation(this, CppTypes)) + registerTransformation(AfterDelayTransformation(this, CppTypes, resource)) super.doGenerate(resource, context) diff --git a/org.lflang/src/org/lflang/generator/rust/RustGenerator.kt b/org.lflang/src/org/lflang/generator/rust/RustGenerator.kt index df50b267c5..deb2aa1a7f 100644 --- a/org.lflang/src/org/lflang/generator/rust/RustGenerator.kt +++ b/org.lflang/src/org/lflang/generator/rust/RustGenerator.kt @@ -73,7 +73,7 @@ class RustGenerator( override fun doGenerate(resource: Resource, context: LFGeneratorContext) { // Register the after delay transformation to be applied by GeneratorBase. - registerTransformation(AfterDelayTransformation(this, CppTypes)) + registerTransformation(AfterDelayTransformation(this, CppTypes, resource)) super.doGenerate(resource, context) diff --git a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt index 76a37c527f..cc40d2169d 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt @@ -121,7 +121,7 @@ class TSGenerator( */ override fun doGenerate(resource: Resource, context: LFGeneratorContext) { // Register the after delay transformation to be applied by GeneratorBase. - registerTransformation(AfterDelayTransformation(this, CppTypes)) + registerTransformation(AfterDelayTransformation(this, CppTypes, resource)) super.doGenerate(resource, context) From 4c6860bac735dbc87c83ff2c5ad53755fc62b169 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Mon, 19 Dec 2022 10:53:17 +0100 Subject: [PATCH 06/13] factor out target code generation for delays into separate classes --- .../tests/compiler/LetInferenceTests.java | 2 +- .../lflang/ast/AfterDelayTransformation.java | 13 ++-- .../org/lflang/generator/GeneratorBase.java | 16 +--- .../lflang/generator/IDelayBodyGenerator.java | 5 -- .../org/lflang/generator/ReactorInstance.java | 2 +- .../generator/c/CDelayBodyGenerator.java | 62 ++++++++++++++++ .../org/lflang/generator/c/CGenerator.java | 60 +++------------ .../generator/cpp/CppDelayBodyGenerator.kt | 46 ++++++++++++ .../org/lflang/generator/cpp/CppGenerator.kt | 42 ----------- .../python/PythonDelayBodyGenerator.java | 74 +++++++++++++++++++ .../generator/python/PythonGenerator.java | 34 +-------- .../python/PythonReactionGenerator.java | 43 +---------- .../python/PythonReactorGenerator.java | 5 +- .../lflang/generator/rust/RustGenerator.kt | 15 ---- .../generator/ts/TSDelayBodyGenerator.kt | 37 ++++++++++ .../org/lflang/generator/ts/TSGenerator.kt | 32 +------- 16 files changed, 249 insertions(+), 239 deletions(-) create mode 100644 org.lflang/src/org/lflang/generator/c/CDelayBodyGenerator.java create mode 100644 org.lflang/src/org/lflang/generator/cpp/CppDelayBodyGenerator.kt create mode 100644 org.lflang/src/org/lflang/generator/python/PythonDelayBodyGenerator.java create mode 100644 org.lflang/src/org/lflang/generator/ts/TSDelayBodyGenerator.kt diff --git a/org.lflang.tests/src/org/lflang/tests/compiler/LetInferenceTests.java b/org.lflang.tests/src/org/lflang/tests/compiler/LetInferenceTests.java index 6dc8554101..370268299a 100644 --- a/org.lflang.tests/src/org/lflang/tests/compiler/LetInferenceTests.java +++ b/org.lflang.tests/src/org/lflang/tests/compiler/LetInferenceTests.java @@ -99,7 +99,7 @@ public void testLet() throws Exception { )); Assertions.assertNotNull(model); - AfterDelayTransformation.insertGeneratedDelays(model.eResource(), new CGenerator(new FileConfig(model.eResource(), Path.of("./a/"), true), new DefaultErrorReporter())); + AfterDelayTransformation.insertGeneratedDelays(model.eResource(), new CGenerator(new FileConfig(model.eResource(), Path.of("./a/"), true), new DefaultErrorReporter(), false)); Assertions.assertTrue(model.eResource().getErrors().isEmpty(), "Encountered unexpected error while parsing: " + model.eResource().getErrors()); diff --git a/org.lflang/src/org/lflang/ast/AfterDelayTransformation.java b/org.lflang/src/org/lflang/ast/AfterDelayTransformation.java index 5594ed2611..69367af018 100644 --- a/org.lflang/src/org/lflang/ast/AfterDelayTransformation.java +++ b/org.lflang/src/org/lflang/ast/AfterDelayTransformation.java @@ -15,7 +15,6 @@ import org.lflang.ASTUtils; import org.lflang.InferredType; -import org.lflang.generator.GeneratorBase; import org.lflang.generator.IDelayBodyGenerator; import org.lflang.generator.TargetTypes; import org.lflang.lf.Action; @@ -50,22 +49,22 @@ public class AfterDelayTransformation implements ITransformation { /** * A code generator used to insert reaction bodies for the generated delay reactors. */ - private IDelayBodyGenerator generator; + private final IDelayBodyGenerator generator; /** * A target type instance that is used during the transformation to manage target specific types */ - private TargetTypes targetTypes; + private final TargetTypes targetTypes; /** * The Eclipse eCore view of the main LF file. */ - private Resource mainResource; + private final Resource mainResource; /** * Collection of generated delay classes. */ - private LinkedHashSet delayClasses = new LinkedHashSet<>(); + private final LinkedHashSet delayClasses = new LinkedHashSet<>(); public AfterDelayTransformation(IDelayBodyGenerator generator, TargetTypes targetTypes, Resource mainResource) { this.generator = generator; @@ -250,10 +249,10 @@ private static Instantiation getDelayInstance(Reactor delayClass, private Reactor getDelayClass(Type type) { String className; if (targetTypes.supportsGenerics()) { - className = GeneratorBase.GEN_DELAY_CLASS_NAME; + className = IDelayBodyGenerator.GEN_DELAY_CLASS_NAME; } else { String id = Integer.toHexString(InferredType.fromAST(type).toText().hashCode()); - className = String.format("%s_%s", GeneratorBase.GEN_DELAY_CLASS_NAME, id); + className = String.format("%s_%s", IDelayBodyGenerator.GEN_DELAY_CLASS_NAME, id); } // Only add class definition if it is not already there. diff --git a/org.lflang/src/org/lflang/generator/GeneratorBase.java b/org.lflang/src/org/lflang/generator/GeneratorBase.java index 0f89399c6f..97f82c68b1 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorBase.java +++ b/org.lflang/src/org/lflang/generator/GeneratorBase.java @@ -87,7 +87,7 @@ * @author{Matt Weber } * @author{Soroush Bateni } */ -public abstract class GeneratorBase extends AbstractLFValidator implements IDelayBodyGenerator { +public abstract class GeneratorBase extends AbstractLFValidator { //////////////////////////////////////////// //// Public fields. @@ -1168,20 +1168,6 @@ public void printInfo(LFGeneratorContext.Mode mode) { System.out.println("******** generated sources: " + fileConfig.getSrcGenPath()); } - /** - * 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 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 - * side of the connection. This gives the code generator the information needed to infer the correct width at - * runtime. - */ - @Override - public boolean generateAfterDelaysWithVariableWidth() { return true; } - /** * Get the buffer type used for network messages */ diff --git a/org.lflang/src/org/lflang/generator/IDelayBodyGenerator.java b/org.lflang/src/org/lflang/generator/IDelayBodyGenerator.java index 835b368303..50a55d8501 100644 --- a/org.lflang/src/org/lflang/generator/IDelayBodyGenerator.java +++ b/org.lflang/src/org/lflang/generator/IDelayBodyGenerator.java @@ -1,11 +1,6 @@ package org.lflang.generator; -import org.eclipse.emf.ecore.EObject; -import org.eclipse.xtext.xbase.lib.IteratorExtensions; - import org.lflang.lf.Action; -import org.lflang.lf.Model; -import org.lflang.lf.Reactor; import org.lflang.lf.VarRef; public interface IDelayBodyGenerator { diff --git a/org.lflang/src/org/lflang/generator/ReactorInstance.java b/org.lflang/src/org/lflang/generator/ReactorInstance.java index ae953e9370..3f27c95822 100644 --- a/org.lflang/src/org/lflang/generator/ReactorInstance.java +++ b/org.lflang/src/org/lflang/generator/ReactorInstance.java @@ -1124,7 +1124,7 @@ private void setInitialWidth() { * @return True if this is a generated delay, false otherwise. */ public boolean isGeneratedDelay() { - if (this.definition.getReactorClass().getName().contains(GeneratorBase.GEN_DELAY_CLASS_NAME)) { + if (this.definition.getReactorClass().getName().contains(IDelayBodyGenerator.GEN_DELAY_CLASS_NAME)) { return true; } return false; diff --git a/org.lflang/src/org/lflang/generator/c/CDelayBodyGenerator.java b/org.lflang/src/org/lflang/generator/c/CDelayBodyGenerator.java new file mode 100644 index 0000000000..2fb600a4de --- /dev/null +++ b/org.lflang/src/org/lflang/generator/c/CDelayBodyGenerator.java @@ -0,0 +1,62 @@ +package org.lflang.generator.c; + +import static org.lflang.ASTUtils.getInferredType; + +import org.lflang.ASTUtils; +import org.lflang.generator.IDelayBodyGenerator; +import org.lflang.lf.Action; +import org.lflang.lf.VarRef; + +public class CDelayBodyGenerator implements IDelayBodyGenerator { + + protected CTypes types; + + public CDelayBodyGenerator(CTypes types) { + this.types = types; + } + + /** + * 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 + */ + @Override + public String generateDelayBody(Action action, VarRef port) { + var ref = ASTUtils.generateVarRef(port); + return CReactionGenerator.generateDelayBody( + ref, + action.getName(), + CUtil.isTokenType(getInferredType(action), types) + ); + } + + /** + * Generate code for the body of a reaction that is triggered by the + * given action and writes its value to the given port. This realizes + * the receiving end of a logical delay specified with the 'after' + * keyword. + * @param action The action that triggers the reaction + * @param port The port to write to. + */ + @Override + public String generateForwardBody(Action action, VarRef port) { + var outputName = ASTUtils.generateVarRef(port); + return CReactionGenerator.generateForwardBody( + outputName, + types.getTargetType(action), + action.getName(), + CUtil.isTokenType(getInferredType(action), types) + ); + } + + @Override + public String generateDelayGeneric() { + throw new UnsupportedOperationException("TODO: auto-generated method stub"); + } + + @Override + public boolean generateAfterDelaysWithVariableWidth() { + return false; + } +} diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.java b/org.lflang/src/org/lflang/generator/c/CGenerator.java index 0671fd39cb..7ca9a9261a 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.java @@ -74,6 +74,7 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY import org.lflang.generator.GeneratorBase; import org.lflang.generator.GeneratorResult; import org.lflang.generator.GeneratorUtils; +import org.lflang.generator.IDelayBodyGenerator; import org.lflang.generator.IntegratedBuilder; import org.lflang.generator.LFGeneratorContext; import org.lflang.generator.LFResource; @@ -390,26 +391,31 @@ protected CGenerator( ErrorReporter errorReporter, boolean CCppMode, CTypes types, - CCmakeGenerator cmakeGenerator + CCmakeGenerator cmakeGenerator, + IDelayBodyGenerator delayBodyGenerator ) { super(fileConfig, errorReporter); this.CCppMode = CCppMode; this.types = types; this.cmakeGenerator = cmakeGenerator; + + // Register the after delay transformation to be applied by GeneratorBase. + registerTransformation(new AfterDelayTransformation(delayBodyGenerator, types, fileConfig.resource)); } - public CGenerator(FileConfig fileConfig, ErrorReporter errorReporter, boolean CCppMode) { + public CGenerator(FileConfig fileConfig, ErrorReporter errorReporter, boolean CCppMode, CTypes types) { this( fileConfig, errorReporter, CCppMode, - new CTypes(errorReporter), - new CCmakeGenerator(fileConfig, List.of()) + types, + new CCmakeGenerator(fileConfig, List.of()), + new CDelayBodyGenerator(types) ); } - public CGenerator(FileConfig fileConfig, ErrorReporter errorReporter) { - this(fileConfig, errorReporter, false); + public CGenerator(FileConfig fileConfig, ErrorReporter errorReporter, boolean CCppMode) { + this(fileConfig, errorReporter, CCppMode, new CTypes(errorReporter)); } //////////////////////////////////////////// @@ -495,9 +501,6 @@ protected boolean isOSCompatible() { */ @Override public void doGenerate(Resource resource, LFGeneratorContext context) { - // Register the after delay transformation to be applied by GeneratorBase. - registerTransformation(new AfterDelayTransformation(this, types, resource)); - super.doGenerate(resource, context); if (!GeneratorUtils.canGenerate(errorsOccurred(), mainDef, errorReporter, context)) return; if (!isOSCompatible()) return; // Incompatible OS and configuration @@ -2238,40 +2241,7 @@ protected void setUpFederateSpecificParameters(FederateInstance federate, CodeBu } } - /** - * 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 - */ - @Override - public String generateDelayBody(Action action, VarRef port) { - var ref = ASTUtils.generateVarRef(port); - return CReactionGenerator.generateDelayBody( - ref, - action.getName(), - CUtil.isTokenType(getInferredType(action), types) - ); - } - /** - * Generate code for the body of a reaction that is triggered by the - * given action and writes its value to the given port. This realizes - * the receiving end of a logical delay specified with the 'after' - * keyword. - * @param action The action that triggers the reaction - * @param port The port to write to. - */ - @Override - public String generateForwardBody(Action action, VarRef port) { - var outputName = ASTUtils.generateVarRef(port); - return CReactionGenerator.generateForwardBody( - outputName, - types.getTargetType(action), - action.getName(), - CUtil.isTokenType(getInferredType(action), types) - ); - } /** * Generate code for the body of a reaction that handles the @@ -2548,12 +2518,6 @@ public String getNetworkBufferType() { return "uint8_t*"; } - - @Override - public String generateDelayGeneric() { - throw new UnsupportedOperationException("TODO: auto-generated method stub"); - } - //////////////////////////////////////////////////////////// //// Private methods diff --git a/org.lflang/src/org/lflang/generator/cpp/CppDelayBodyGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppDelayBodyGenerator.kt new file mode 100644 index 0000000000..377bc73400 --- /dev/null +++ b/org.lflang/src/org/lflang/generator/cpp/CppDelayBodyGenerator.kt @@ -0,0 +1,46 @@ +package org.lflang.generator.cpp + +import org.lflang.generator.IDelayBodyGenerator +import org.lflang.lf.Action +import org.lflang.lf.VarRef + + +class CppDelayBodyGenerator : IDelayBodyGenerator { + + /** + * 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 + */ + override fun generateDelayBody(action: Action, port: VarRef): String { + // Since we cannot easily decide whether a given type evaluates + // to void, we leave this job to the target compiler, by calling + // the template function below. + return """ + // delay body for ${action.name} + lfutil::after_delay(&${action.name}, &${port.name}); + """.trimIndent() + } + + /** + * Generate code for the body of a reaction that is triggered by the + * given action and writes its value to the given port. + * @param action the action that triggers the reaction + * @param port the port to write to + */ + override fun generateForwardBody(action: Action, port: VarRef): String { + // Since we cannot easily decide whether a given type evaluates + // to void, we leave this job to the target compiler, by calling + // the template function below. + return """ + // forward body for ${action.name} + lfutil::after_forward(&${action.name}, &${port.name}); + """.trimIndent() + } + + override fun generateDelayGeneric() = "T" + + override fun generateAfterDelaysWithVariableWidth() = false + +} \ No newline at end of file diff --git a/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt index d46ffe8953..af0a6a6a7c 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt @@ -31,7 +31,6 @@ import org.lflang.ErrorReporter import org.lflang.Target import org.lflang.TimeUnit import org.lflang.TimeValue -import org.lflang.ast.AfterDelayTransformation import org.lflang.generator.CodeMap import org.lflang.generator.GeneratorBase import org.lflang.generator.GeneratorResult @@ -41,8 +40,6 @@ import org.lflang.generator.LFGeneratorContext.Mode import org.lflang.generator.TargetTypes import org.lflang.generator.GeneratorUtils.canGenerate import org.lflang.isGeneric -import org.lflang.lf.Action -import org.lflang.lf.VarRef import org.lflang.scoping.LFGlobalScopeProvider import org.lflang.util.FileUtil import java.nio.file.Files @@ -66,9 +63,6 @@ class CppGenerator( } override fun doGenerate(resource: Resource, context: LFGeneratorContext) { - // Register the after delay transformation to be applied by GeneratorBase. - registerTransformation(AfterDelayTransformation(this, CppTypes, resource)) - super.doGenerate(resource, context) if (!canGenerate(errorsOccurred(), mainDef, errorReporter, context)) return @@ -183,42 +177,6 @@ class CppGenerator( } } - /** - * 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 - */ - override fun generateDelayBody(action: Action, port: VarRef): String { - // Since we cannot easily decide whether a given type evaluates - // to void, we leave this job to the target compiler, by calling - // the template function below. - return """ - // delay body for ${action.name} - lfutil::after_delay(&${action.name}, &${port.name}); - """.trimIndent() - } - - /** - * Generate code for the body of a reaction that is triggered by the - * given action and writes its value to the given port. - * @param action the action that triggers the reaction - * @param port the port to write to - */ - override fun generateForwardBody(action: Action, port: VarRef): String { - // Since we cannot easily decide whether a given type evaluates - // to void, we leave this job to the target compiler, by calling - // the template function below. - return """ - // forward body for ${action.name} - lfutil::after_forward(&${action.name}, &${port.name}); - """.trimIndent() - } - - override fun generateDelayGeneric() = "T" - - override fun generateAfterDelaysWithVariableWidth() = false - override fun getTarget() = Target.CPP override fun getTargetTypes(): TargetTypes = CppTypes diff --git a/org.lflang/src/org/lflang/generator/python/PythonDelayBodyGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonDelayBodyGenerator.java new file mode 100644 index 0000000000..472a3b8769 --- /dev/null +++ b/org.lflang/src/org/lflang/generator/python/PythonDelayBodyGenerator.java @@ -0,0 +1,74 @@ +package org.lflang.generator.python; + + +import org.lflang.ASTUtils; +import org.lflang.generator.c.CDelayBodyGenerator; +import org.lflang.generator.c.CUtil; +import org.lflang.lf.Action; +import org.lflang.lf.VarRef; + +public class PythonDelayBodyGenerator extends CDelayBodyGenerator { + + public PythonDelayBodyGenerator(PythonTypes types) { + super(types); + } + + /** + * 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 + */ + @Override + public String generateDelayBody(Action action, VarRef port) { + boolean isTokenType = CUtil.isTokenType(ASTUtils.getInferredType(action), types); + String ref = ASTUtils.generateVarRef(port); + // Note that the action.type set by the base class is actually + // the port type. + if (isTokenType) { + return String.join("\n", + "if ("+ref+"->is_present) {", + " // Put the whole token on the event queue, not just the payload.", + " // This way, the length and element_size are transported.", + " lf_schedule_token("+action.getName()+", 0, "+ref+"->token);", + "}" + ); + } else { + return String.join("\n", + "// Create a token.", + "#if NUMBER_OF_WORKERS > 0", + "// Need to lock the mutex first.", + "lf_mutex_lock(&mutex);", + "#endif", + "lf_token_t* t = create_token(sizeof(PyObject*));", + "#if NUMBER_OF_WORKERS > 0", + "lf_mutex_unlock(&mutex);", + "#endif", + "t->value = self->_lf_"+ref+"->value;", + "t->length = 1; // Length is 1", + "", + "// Pass the token along", + "lf_schedule_token("+action.getName()+", 0, t);" + ); + } + } + + /** + * Generate code for the body of a reaction that is triggered by the + * given action and writes its value to the given port. This realizes + * the receiving end of a logical delay specified with the 'after' + * keyword. + * @param action The action that triggers the reaction + * @param port The port to write to. + */ + @Override + public String generateForwardBody(Action action, VarRef port) { + String outputName = ASTUtils.generateVarRef(port); + if (CUtil.isTokenType(ASTUtils.getInferredType(action), types)) { + return super.generateForwardBody(action, port); + } else { + return "lf_set("+outputName+", "+action.getName()+"->token->value);"; + } + } + +} diff --git a/org.lflang/src/org/lflang/generator/python/PythonGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonGenerator.java index e61144a26b..a6a88cf0eb 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonGenerator.java @@ -58,6 +58,7 @@ import org.lflang.generator.CodeMap; import org.lflang.generator.GeneratorResult; import org.lflang.generator.GeneratorUtils; +import org.lflang.generator.IDelayBodyGenerator; import org.lflang.generator.IntegratedBuilder; import org.lflang.generator.LFGeneratorContext; import org.lflang.generator.ReactorInstance; @@ -124,7 +125,7 @@ public PythonGenerator(FileConfig fileConfig, ErrorReporter errorReporter) { } private PythonGenerator(FileConfig fileConfig, ErrorReporter errorReporter, PythonTypes types, CCmakeGenerator cmakeGenerator) { - super(fileConfig, errorReporter, false, types, cmakeGenerator); + super(fileConfig, errorReporter, false, types, cmakeGenerator, new PythonDelayBodyGenerator(types)); this.targetConfig.compiler = "gcc"; this.targetConfig.compilerFlags = new ArrayList<>(); this.targetConfig.linkerFlags = ""; @@ -638,35 +639,6 @@ protected PythonDockerGenerator getDockerGenerator() { return new PythonDockerGenerator(isFederated, targetConfig); } - /** - * 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 - */ - @Override - public String generateDelayBody(Action action, VarRef port) { - return PythonReactionGenerator.generateCDelayBody(action, port, CUtil.isTokenType(ASTUtils.getInferredType(action), types)); - } - - /** - * Generate code for the body of a reaction that is triggered by the - * given action and writes its value to the given port. This realizes - * the receiving end of a logical delay specified with the 'after' - * keyword. - * @param action The action that triggers the reaction - * @param port The port to write to. - */ - @Override - public String generateForwardBody(Action action, VarRef port) { - String outputName = ASTUtils.generateVarRef(port); - if (CUtil.isTokenType(ASTUtils.getInferredType(action), types)) { - return super.generateForwardBody(action, port); - } else { - return "lf_set("+outputName+", "+action.getName()+"->token->value);"; - } - } - /** Generate a reaction function definition for a reactor. * This function has a single argument that is a void* pointing to * a struct that contains parameters, state variables, inputs (triggering or not), @@ -680,7 +652,7 @@ protected void generateReaction(Reaction reaction, ReactorDecl decl, int reactio Reactor reactor = ASTUtils.toDefinition(decl); // Delay reactors and top-level reactions used in the top-level reactor(s) in federated execution are generated in C - if (reactor.getName().contains(GEN_DELAY_CLASS_NAME) || + if (reactor.getName().contains(IDelayBodyGenerator.GEN_DELAY_CLASS_NAME) || mainDef != null && decl == mainDef.getReactorClass() && reactor.isFederated()) { super.generateReaction(reaction, decl, reactionIndex); return; diff --git a/org.lflang/src/org/lflang/generator/python/PythonReactionGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonReactionGenerator.java index 076082a141..c4ae8c49e5 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonReactionGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonReactionGenerator.java @@ -7,6 +7,7 @@ import org.lflang.ASTUtils; import org.lflang.ErrorReporter; import org.lflang.Target; +import org.lflang.generator.IDelayBodyGenerator; import org.lflang.generator.c.CReactionGenerator; import org.lflang.lf.ReactorDecl; import org.lflang.lf.Reaction; @@ -24,7 +25,6 @@ import org.lflang.generator.c.CTypes; import org.lflang.generator.c.CUtil; import org.lflang.generator.CodeBuilder; -import org.lflang.generator.GeneratorBase; import org.lflang.generator.ReactionInstance; import org.lflang.generator.ReactorInstance; import org.lflang.lf.Mode; @@ -367,48 +367,9 @@ private static String generateVariableToSendPythonReaction(VarRef varRef, return ""; } - /** - * 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 - */ - public static String generateCDelayBody(Action action, VarRef port, boolean isTokenType) { - String ref = ASTUtils.generateVarRef(port); - // Note that the action.type set by the base class is actually - // the port type. - if (isTokenType) { - return String.join("\n", - "if ("+ref+"->is_present) {", - " // Put the whole token on the event queue, not just the payload.", - " // This way, the length and element_size are transported.", - " lf_schedule_token("+action.getName()+", 0, "+ref+"->token);", - "}" - ); - } else { - return String.join("\n", - "// Create a token.", - "#if NUMBER_OF_WORKERS > 0", - "// Need to lock the mutex first.", - "lf_mutex_lock(&mutex);", - "#endif", - "lf_token_t* t = create_token(sizeof(PyObject*));", - "#if NUMBER_OF_WORKERS > 0", - "lf_mutex_unlock(&mutex);", - "#endif", - "t->value = self->_lf_"+ref+"->value;", - "t->length = 1; // Length is 1", - "", - "// Pass the token along", - "lf_schedule_token("+action.getName()+", 0, t);" - ); - } - } - /** * Generate Python code to link cpython functions to python functions for each reaction. * @param instance The reactor instance. - * @param reactions The reactions of this instance. * @param mainDef The definition of the main reactor */ public static String generateCPythonReactionLinkers( @@ -420,7 +381,7 @@ public static String generateCPythonReactionLinkers( CodeBuilder code = new CodeBuilder(); // Delay reactors and top-level reactions used in the top-level reactor(s) in federated execution are generated in C - if (reactor.getName().contains(GeneratorBase.GEN_DELAY_CLASS_NAME) || + if (reactor.getName().contains(IDelayBodyGenerator.GEN_DELAY_CLASS_NAME) || instance.getDefinition().getReactorClass() == (mainDef != null ? mainDef.getReactorClass() : null) && reactor.isFederated()) { return ""; diff --git a/org.lflang/src/org/lflang/generator/python/PythonReactorGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonReactorGenerator.java index f4ff648161..76ad81d5c8 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonReactorGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonReactorGenerator.java @@ -6,6 +6,7 @@ import org.lflang.federated.FederateInstance; import org.lflang.generator.CodeBuilder; import org.lflang.generator.GeneratorBase; +import org.lflang.generator.IDelayBodyGenerator; import org.lflang.generator.ParameterInstance; import org.lflang.generator.ReactorInstance; import org.lflang.lf.Reaction; @@ -43,7 +44,7 @@ public static String generatePythonClass(ReactorInstance instance, FederateInsta if (instance != main && !federate.contains(instance) || instantiatedClasses == null || // Do not generate code for delay reactors in Python - className.contains(GeneratorBase.GEN_DELAY_CLASS_NAME)) { + className.contains(IDelayBodyGenerator.GEN_DELAY_CLASS_NAME)) { return ""; } @@ -138,7 +139,7 @@ public static String generatePythonClassInstantiations(ReactorInstance instance, } String className = instance.getDefinition().getReactorClass().getName(); // Do not instantiate delay reactors in Python - if (className.contains(GeneratorBase.GEN_DELAY_CLASS_NAME)) { + if (className.contains(IDelayBodyGenerator.GEN_DELAY_CLASS_NAME)) { return ""; } diff --git a/org.lflang/src/org/lflang/generator/rust/RustGenerator.kt b/org.lflang/src/org/lflang/generator/rust/RustGenerator.kt index deb2aa1a7f..3cc3a2448f 100644 --- a/org.lflang/src/org/lflang/generator/rust/RustGenerator.kt +++ b/org.lflang/src/org/lflang/generator/rust/RustGenerator.kt @@ -72,9 +72,6 @@ class RustGenerator( } override fun doGenerate(resource: Resource, context: LFGeneratorContext) { - // Register the after delay transformation to be applied by GeneratorBase. - registerTransformation(AfterDelayTransformation(this, CppTypes, resource)) - super.doGenerate(resource, context) if (!canGenerate(errorsOccurred(), mainDef, errorReporter, context)) return @@ -169,16 +166,4 @@ class RustGenerator( override fun getTargetTypes(): TargetTypes = RustTypes - override fun generateDelayBody(action: Action, port: VarRef): String { - TODO("Not yet implemented") - } - - override fun generateForwardBody(action: Action, port: VarRef): String { - TODO("Not yet implemented") - } - - override fun generateDelayGeneric(): String { - TODO("Not yet implemented") - } - } diff --git a/org.lflang/src/org/lflang/generator/ts/TSDelayBodyGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSDelayBodyGenerator.kt new file mode 100644 index 0000000000..33add8daee --- /dev/null +++ b/org.lflang/src/org/lflang/generator/ts/TSDelayBodyGenerator.kt @@ -0,0 +1,37 @@ +package org.lflang.generator.ts + +import org.lflang.ASTUtils +import org.lflang.generator.IDelayBodyGenerator +import org.lflang.lf.Action +import org.lflang.lf.VarRef + +class TSDelayBodyGenerator : IDelayBodyGenerator { + /** + * Return a TS type for the specified action. + * If the type has not been specified, return + * "Present" which is the base type for Actions. + * @param action The action + * @return The TS type. + */ + private fun getActionType(action: Action): String { + return if (action.type != null) { + TSTypes.getTargetType(action.type) + } else { + "Present" + } + } + + override fun generateDelayBody(action: Action, port: VarRef): String { + return "actions.${action.name}.schedule(0, ${ASTUtils.generateVarRef(port)} as ${getActionType(action)});" + } + + override fun generateForwardBody(action: Action, port: VarRef): String { + return "${ASTUtils.generateVarRef(port)} = ${action.name} as ${getActionType(action)};" + } + + override fun generateDelayGeneric(): String { + return "T extends Present" + } + + override fun generateAfterDelaysWithVariableWidth() = false +} \ No newline at end of file diff --git a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt index cc40d2169d..6c9d05fcfa 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt @@ -121,7 +121,7 @@ class TSGenerator( */ override fun doGenerate(resource: Resource, context: LFGeneratorContext) { // Register the after delay transformation to be applied by GeneratorBase. - registerTransformation(AfterDelayTransformation(this, CppTypes, resource)) + registerTransformation(AfterDelayTransformation(TSDelayBodyGenerator(), CppTypes, resource)) super.doGenerate(resource, context) @@ -517,21 +517,6 @@ class TSGenerator( override fun getTargetTypes(): TargetTypes = TSTypes - /** - * Return a TS type for the specified action. - * If the type has not been specified, return - * "Present" which is the base type for Actions. - * @param action The action - * @return The TS type. - */ - private fun getActionType(action: Action): String { - return if (action.type != null) { - TSTypes.getTargetType(action.type) - } else { - "Present" - } - } - /** * Generate code for the body of a reaction that handles the * action that is triggered by receiving a message from a remote @@ -660,22 +645,7 @@ class TSGenerator( } } - // Virtual methods. - override fun generateDelayBody(action: Action, port: VarRef): String { - return "actions.${action.name}.schedule(0, ${ASTUtils.generateVarRef(port)} as ${getActionType(action)});" - } - - override fun generateForwardBody(action: Action, port: VarRef): String { - return "${ASTUtils.generateVarRef(port)} = ${action.name} as ${getActionType(action)};" - } - - override fun generateDelayGeneric(): String { - return "T extends Present" - } - override fun getTarget(): Target { return Target.TS } - - override fun generateAfterDelaysWithVariableWidth() = false } From 889bc9cdc270d3a5d4d4a095386fc0909762ccfd Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Mon, 19 Dec 2022 11:34:22 +0100 Subject: [PATCH 07/13] remove unused code --- .../lflang/ast/AfterDelayTransformation.java | 24 +++---------------- .../lflang/generator/IDelayBodyGenerator.java | 19 --------------- .../generator/c/CDelayBodyGenerator.java | 4 ---- .../generator/cpp/CppDelayBodyGenerator.kt | 2 -- .../generator/ts/TSDelayBodyGenerator.kt | 2 -- 5 files changed, 3 insertions(+), 48 deletions(-) diff --git a/org.lflang/src/org/lflang/ast/AfterDelayTransformation.java b/org.lflang/src/org/lflang/ast/AfterDelayTransformation.java index 69367af018..0451a192c8 100644 --- a/org.lflang/src/org/lflang/ast/AfterDelayTransformation.java +++ b/org.lflang/src/org/lflang/ast/AfterDelayTransformation.java @@ -37,7 +37,6 @@ import org.lflang.lf.TypeParm; import org.lflang.lf.VarRef; import org.lflang.lf.WidthSpec; -import org.lflang.lf.WidthTerm; public class AfterDelayTransformation implements ITransformation { @@ -101,8 +100,7 @@ private void insertGeneratedDelays(List reactors) { Type type = ((Port) connection.getRightPorts().get(0).getVariable()).getType(); Reactor delayClass = getDelayClass(type); String generic = targetTypes.supportsGenerics() ? targetTypes.getTargetType(InferredType.fromAST(type)) : ""; - Instantiation delayInstance = getDelayInstance(delayClass, connection, generic, - !generator.generateAfterDelaysWithVariableWidth()); + Instantiation delayInstance = getDelayInstance(delayClass, connection, generic); // Stage the new connections for insertion into the tree. List connections = ASTUtils.convertToEmptyListIfNull(newConnections.get(parent)); @@ -198,13 +196,9 @@ private static List rerouteViaDelay(Connection connection, * @param connection The connection to create a delay instantiation foe * @param generic A string that denotes the appropriate type parameter, * which should be null or empty if the target does not support generics. - * @param defineWidthFromConnection If this is true and if the connection - * is a wide connection, then instantiate a bank of delays where the width - * is given by ports involved in the connection. Otherwise, the width will - * be unspecified indicating a variable length. */ private static Instantiation getDelayInstance(Reactor delayClass, - Connection connection, String generic, Boolean defineWidthFromConnection) { + Connection connection, String generic) { Expression delay = connection.getDelay(); Instantiation delayInstance = factory.createInstantiation(); delayInstance.setReactorClass(delayClass); @@ -215,19 +209,7 @@ private static Instantiation getDelayInstance(Reactor delayClass, } if (ASTUtils.hasMultipleConnections(connection)) { WidthSpec widthSpec = factory.createWidthSpec(); - if (defineWidthFromConnection) { - // Add all left ports of the connection to the WidthSpec of the generated delay instance. - // This allows the code generator to later infer the width from the involved ports. - // We only consider the left ports here, as they could be part of a broadcast. In this case, we want - // to delay the ports first, and then broadcast the output of the delays. - for (VarRef port : connection.getLeftPorts()) { - WidthTerm term = factory.createWidthTerm(); - term.setPort(EcoreUtil.copy(port)); - widthSpec.getTerms().add(term); - } - } else { - widthSpec.setOfVariableLength(true); - } + widthSpec.setOfVariableLength(true); delayInstance.setWidthSpec(widthSpec); } Assignment assignment = factory.createAssignment(); diff --git a/org.lflang/src/org/lflang/generator/IDelayBodyGenerator.java b/org.lflang/src/org/lflang/generator/IDelayBodyGenerator.java index 50a55d8501..fecdc8e0a4 100644 --- a/org.lflang/src/org/lflang/generator/IDelayBodyGenerator.java +++ b/org.lflang/src/org/lflang/generator/IDelayBodyGenerator.java @@ -34,23 +34,4 @@ public interface IDelayBodyGenerator { */ String generateDelayGeneric(); - /** - * 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 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 - * side of the connection. This gives the code generator the information needed to infer the - * correct width at - * runtime. - */ - boolean generateAfterDelaysWithVariableWidth(); - } diff --git a/org.lflang/src/org/lflang/generator/c/CDelayBodyGenerator.java b/org.lflang/src/org/lflang/generator/c/CDelayBodyGenerator.java index 2fb600a4de..5d540122b8 100644 --- a/org.lflang/src/org/lflang/generator/c/CDelayBodyGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CDelayBodyGenerator.java @@ -55,8 +55,4 @@ public String generateDelayGeneric() { throw new UnsupportedOperationException("TODO: auto-generated method stub"); } - @Override - public boolean generateAfterDelaysWithVariableWidth() { - return false; - } } diff --git a/org.lflang/src/org/lflang/generator/cpp/CppDelayBodyGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppDelayBodyGenerator.kt index 377bc73400..827d6d2902 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppDelayBodyGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppDelayBodyGenerator.kt @@ -41,6 +41,4 @@ class CppDelayBodyGenerator : IDelayBodyGenerator { override fun generateDelayGeneric() = "T" - override fun generateAfterDelaysWithVariableWidth() = false - } \ No newline at end of file diff --git a/org.lflang/src/org/lflang/generator/ts/TSDelayBodyGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSDelayBodyGenerator.kt index 33add8daee..948be78ca5 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSDelayBodyGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSDelayBodyGenerator.kt @@ -32,6 +32,4 @@ class TSDelayBodyGenerator : IDelayBodyGenerator { override fun generateDelayGeneric(): String { return "T extends Present" } - - override fun generateAfterDelaysWithVariableWidth() = false } \ No newline at end of file From 40fdb38cbad936e24e2fb13d8ddfca08982040cb Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Mon, 19 Dec 2022 12:03:44 +0100 Subject: [PATCH 08/13] fix test --- .../src/org/lflang/tests/compiler/LetInferenceTests.java | 9 ++++++++- org.lflang/src/org/lflang/ASTUtils.java | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/org.lflang.tests/src/org/lflang/tests/compiler/LetInferenceTests.java b/org.lflang.tests/src/org/lflang/tests/compiler/LetInferenceTests.java index 370268299a..3023e9c317 100644 --- a/org.lflang.tests/src/org/lflang/tests/compiler/LetInferenceTests.java +++ b/org.lflang.tests/src/org/lflang/tests/compiler/LetInferenceTests.java @@ -37,6 +37,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.lflang.ASTUtils; import org.lflang.DefaultErrorReporter; import org.lflang.FileConfig; import org.lflang.TimeUnit; @@ -44,7 +45,9 @@ import org.lflang.ast.AfterDelayTransformation; import org.lflang.generator.ReactionInstance; import org.lflang.generator.ReactorInstance; +import org.lflang.generator.c.CDelayBodyGenerator; import org.lflang.generator.c.CGenerator; +import org.lflang.generator.c.CTypes; import org.lflang.lf.Instantiation; import org.lflang.lf.LfFactory; import org.lflang.lf.Model; @@ -99,7 +102,11 @@ public void testLet() throws Exception { )); Assertions.assertNotNull(model); - AfterDelayTransformation.insertGeneratedDelays(model.eResource(), new CGenerator(new FileConfig(model.eResource(), Path.of("./a/"), true), new DefaultErrorReporter(), false)); + final var ctypes = new CTypes(new DefaultErrorReporter()); + final var resource = model.eResource(); + final var transformation = new AfterDelayTransformation(new CDelayBodyGenerator(ctypes), ctypes, resource); + transformation.applyTransformation(ASTUtils.getAllReactors(resource)); + Assertions.assertTrue(model.eResource().getErrors().isEmpty(), "Encountered unexpected error while parsing: " + model.eResource().getErrors()); diff --git a/org.lflang/src/org/lflang/ASTUtils.java b/org.lflang/src/org/lflang/ASTUtils.java index 7f4cdab33f..34e6af0498 100644 --- a/org.lflang/src/org/lflang/ASTUtils.java +++ b/org.lflang/src/org/lflang/ASTUtils.java @@ -132,7 +132,7 @@ public class ASTUtils { * @param resource the resource to extract reactors from * @return An iterable over all reactors found in the resource */ - public static Iterable getAllReactors(Resource resource) { + public static List getAllReactors(Resource resource) { return StreamSupport.stream(IteratorExtensions.toIterable(resource.getAllContents()).spliterator(), false) .filter(Reactor.class::isInstance) .map(Reactor.class::cast) From 061d07509fa2afd72436c25db7bddc58099a6850 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Mon, 19 Dec 2022 13:34:03 +0100 Subject: [PATCH 09/13] fix TS and C++ transformations --- org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt | 3 +++ org.lflang/src/org/lflang/generator/ts/TSGenerator.kt | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt index af0a6a6a7c..710fccf8b2 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt @@ -31,6 +31,7 @@ import org.lflang.ErrorReporter import org.lflang.Target import org.lflang.TimeUnit import org.lflang.TimeValue +import org.lflang.ast.AfterDelayTransformation import org.lflang.generator.CodeMap import org.lflang.generator.GeneratorBase import org.lflang.generator.GeneratorResult @@ -63,6 +64,8 @@ class CppGenerator( } override fun doGenerate(resource: Resource, context: LFGeneratorContext) { + // Register the after delay transformation to be applied by GeneratorBase. + registerTransformation(AfterDelayTransformation(CppDelayBodyGenerator(), CppTypes, resource)) super.doGenerate(resource, context) if (!canGenerate(errorsOccurred(), mainDef, errorReporter, context)) return diff --git a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt index 6c9d05fcfa..f654bf4975 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt @@ -121,7 +121,7 @@ class TSGenerator( */ override fun doGenerate(resource: Resource, context: LFGeneratorContext) { // Register the after delay transformation to be applied by GeneratorBase. - registerTransformation(AfterDelayTransformation(TSDelayBodyGenerator(), CppTypes, resource)) + registerTransformation(AfterDelayTransformation(TSDelayBodyGenerator(), targetTypes, resource)) super.doGenerate(resource, context) From 25e103fccefd7d3490232c96ba116bda2dcde319 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Mon, 19 Dec 2022 14:05:02 +0100 Subject: [PATCH 10/13] address code review comments --- .../src/org/lflang/ast/AfterDelayTransformation.java | 12 ++++++------ .../{ITransformation.java => AstTransformation.java} | 3 +-- ...layBodyGenerator.java => DelayBodyGenerator.java} | 2 +- .../src/org/lflang/generator/GeneratorBase.java | 8 ++++---- .../src/org/lflang/generator/ReactorInstance.java | 2 +- .../org/lflang/generator/c/CDelayBodyGenerator.java | 4 ++-- .../src/org/lflang/generator/c/CGenerator.java | 4 ++-- .../lflang/generator/cpp/CppDelayBodyGenerator.kt | 4 ++-- .../src/org/lflang/generator/cpp/CppGenerator.kt | 2 +- .../org/lflang/generator/python/PythonGenerator.java | 4 ++-- .../generator/python/PythonReactionGenerator.java | 4 ++-- .../generator/python/PythonReactorGenerator.java | 7 +++---- .../org/lflang/generator/ts/TSDelayBodyGenerator.kt | 4 ++-- .../src/org/lflang/generator/ts/TSGenerator.kt | 2 +- 14 files changed, 30 insertions(+), 32 deletions(-) rename org.lflang/src/org/lflang/ast/{ITransformation.java => AstTransformation.java} (77%) rename org.lflang/src/org/lflang/generator/{IDelayBodyGenerator.java => DelayBodyGenerator.java} (96%) diff --git a/org.lflang/src/org/lflang/ast/AfterDelayTransformation.java b/org.lflang/src/org/lflang/ast/AfterDelayTransformation.java index 0451a192c8..b6edf1969f 100644 --- a/org.lflang/src/org/lflang/ast/AfterDelayTransformation.java +++ b/org.lflang/src/org/lflang/ast/AfterDelayTransformation.java @@ -15,7 +15,7 @@ import org.lflang.ASTUtils; import org.lflang.InferredType; -import org.lflang.generator.IDelayBodyGenerator; +import org.lflang.generator.DelayBodyGenerator; import org.lflang.generator.TargetTypes; import org.lflang.lf.Action; import org.lflang.lf.ActionOrigin; @@ -38,7 +38,7 @@ import org.lflang.lf.VarRef; import org.lflang.lf.WidthSpec; -public class AfterDelayTransformation implements ITransformation { +public class AfterDelayTransformation implements AstTransformation { /** * The Lingua Franca factory for creating new AST nodes. @@ -48,7 +48,7 @@ public class AfterDelayTransformation implements ITransformation { /** * A code generator used to insert reaction bodies for the generated delay reactors. */ - private final IDelayBodyGenerator generator; + private final DelayBodyGenerator generator; /** * A target type instance that is used during the transformation to manage target specific types @@ -65,7 +65,7 @@ public class AfterDelayTransformation implements ITransformation { */ private final LinkedHashSet delayClasses = new LinkedHashSet<>(); - public AfterDelayTransformation(IDelayBodyGenerator generator, TargetTypes targetTypes, Resource mainResource) { + public AfterDelayTransformation(DelayBodyGenerator generator, TargetTypes targetTypes, Resource mainResource) { this.generator = generator; this.targetTypes = targetTypes; this.mainResource = mainResource; @@ -231,10 +231,10 @@ private static Instantiation getDelayInstance(Reactor delayClass, private Reactor getDelayClass(Type type) { String className; if (targetTypes.supportsGenerics()) { - className = IDelayBodyGenerator.GEN_DELAY_CLASS_NAME; + className = DelayBodyGenerator.GEN_DELAY_CLASS_NAME; } else { String id = Integer.toHexString(InferredType.fromAST(type).toText().hashCode()); - className = String.format("%s_%s", IDelayBodyGenerator.GEN_DELAY_CLASS_NAME, id); + className = String.format("%s_%s", DelayBodyGenerator.GEN_DELAY_CLASS_NAME, id); } // Only add class definition if it is not already there. diff --git a/org.lflang/src/org/lflang/ast/ITransformation.java b/org.lflang/src/org/lflang/ast/AstTransformation.java similarity index 77% rename from org.lflang/src/org/lflang/ast/ITransformation.java rename to org.lflang/src/org/lflang/ast/AstTransformation.java index 50ae6a3cf1..80a93da4b9 100644 --- a/org.lflang/src/org/lflang/ast/ITransformation.java +++ b/org.lflang/src/org/lflang/ast/AstTransformation.java @@ -2,13 +2,12 @@ import java.util.List; -import org.lflang.generator.LFResource; import org.lflang.lf.Reactor; /** * Interface for AST Transfomations */ -public interface ITransformation { +public interface AstTransformation { /** * Apply the AST transformation to all given reactors. diff --git a/org.lflang/src/org/lflang/generator/IDelayBodyGenerator.java b/org.lflang/src/org/lflang/generator/DelayBodyGenerator.java similarity index 96% rename from org.lflang/src/org/lflang/generator/IDelayBodyGenerator.java rename to org.lflang/src/org/lflang/generator/DelayBodyGenerator.java index fecdc8e0a4..d48e146237 100644 --- a/org.lflang/src/org/lflang/generator/IDelayBodyGenerator.java +++ b/org.lflang/src/org/lflang/generator/DelayBodyGenerator.java @@ -3,7 +3,7 @@ import org.lflang.lf.Action; import org.lflang.lf.VarRef; -public interface IDelayBodyGenerator { +public interface DelayBodyGenerator { /** * Constant that specifies how to name generated delay reactors. diff --git a/org.lflang/src/org/lflang/generator/GeneratorBase.java b/org.lflang/src/org/lflang/generator/GeneratorBase.java index 97f82c68b1..97fd28e919 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorBase.java +++ b/org.lflang/src/org/lflang/generator/GeneratorBase.java @@ -56,7 +56,7 @@ import org.lflang.TargetProperty.CoordinationType; import org.lflang.TimeUnit; import org.lflang.TimeValue; -import org.lflang.ast.ITransformation; +import org.lflang.ast.AstTransformation; import org.lflang.federated.FedASTUtils; import org.lflang.federated.FederateInstance; import org.lflang.federated.serialization.SupportedSerializers; @@ -227,7 +227,7 @@ public abstract class GeneratorBase extends AbstractLFValidator { /** * A list ot AST transformations to apply before code generation */ - private List astTransformations = new LinkedList(); + private List astTransformations = new ArrayList(); /** * Create a new GeneratorBase object. @@ -243,7 +243,7 @@ public GeneratorBase(FileConfig fileConfig, ErrorReporter errorReporter) { * * The transformations will be applied in the order that they are registered in. */ - protected void registerTransformation(ITransformation transformation) { + protected void registerTransformation(AstTransformation transformation) { astTransformations.add(transformation); } @@ -341,7 +341,7 @@ public void doGenerate(Resource resource, LFGeneratorContext context) { // FIXME: Should the GeneratorBase pull in `files` from imported // resources? - for (ITransformation transformation : astTransformations) { + for (AstTransformation transformation : astTransformations) { transformation.applyTransformation(reactors); } diff --git a/org.lflang/src/org/lflang/generator/ReactorInstance.java b/org.lflang/src/org/lflang/generator/ReactorInstance.java index 3f27c95822..336fb5ade5 100644 --- a/org.lflang/src/org/lflang/generator/ReactorInstance.java +++ b/org.lflang/src/org/lflang/generator/ReactorInstance.java @@ -1124,7 +1124,7 @@ private void setInitialWidth() { * @return True if this is a generated delay, false otherwise. */ public boolean isGeneratedDelay() { - if (this.definition.getReactorClass().getName().contains(IDelayBodyGenerator.GEN_DELAY_CLASS_NAME)) { + if (this.definition.getReactorClass().getName().contains(DelayBodyGenerator.GEN_DELAY_CLASS_NAME)) { return true; } return false; diff --git a/org.lflang/src/org/lflang/generator/c/CDelayBodyGenerator.java b/org.lflang/src/org/lflang/generator/c/CDelayBodyGenerator.java index 5d540122b8..5bf362c625 100644 --- a/org.lflang/src/org/lflang/generator/c/CDelayBodyGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CDelayBodyGenerator.java @@ -3,11 +3,11 @@ import static org.lflang.ASTUtils.getInferredType; import org.lflang.ASTUtils; -import org.lflang.generator.IDelayBodyGenerator; +import org.lflang.generator.DelayBodyGenerator; import org.lflang.lf.Action; import org.lflang.lf.VarRef; -public class CDelayBodyGenerator implements IDelayBodyGenerator { +public class CDelayBodyGenerator implements DelayBodyGenerator { protected CTypes types; diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.java b/org.lflang/src/org/lflang/generator/c/CGenerator.java index 7ca9a9261a..abfa1c3216 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.java @@ -74,7 +74,7 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY import org.lflang.generator.GeneratorBase; import org.lflang.generator.GeneratorResult; import org.lflang.generator.GeneratorUtils; -import org.lflang.generator.IDelayBodyGenerator; +import org.lflang.generator.DelayBodyGenerator; import org.lflang.generator.IntegratedBuilder; import org.lflang.generator.LFGeneratorContext; import org.lflang.generator.LFResource; @@ -392,7 +392,7 @@ protected CGenerator( boolean CCppMode, CTypes types, CCmakeGenerator cmakeGenerator, - IDelayBodyGenerator delayBodyGenerator + DelayBodyGenerator delayBodyGenerator ) { super(fileConfig, errorReporter); this.CCppMode = CCppMode; diff --git a/org.lflang/src/org/lflang/generator/cpp/CppDelayBodyGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppDelayBodyGenerator.kt index 827d6d2902..87d63399d7 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppDelayBodyGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppDelayBodyGenerator.kt @@ -1,11 +1,11 @@ package org.lflang.generator.cpp -import org.lflang.generator.IDelayBodyGenerator +import org.lflang.generator.DelayBodyGenerator import org.lflang.lf.Action import org.lflang.lf.VarRef -class CppDelayBodyGenerator : IDelayBodyGenerator { +object CppDelayBodyGenerator : DelayBodyGenerator { /** * Generate code for the body of a reaction that takes an input and diff --git a/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt index 710fccf8b2..bdf31e1f67 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt @@ -65,7 +65,7 @@ class CppGenerator( override fun doGenerate(resource: Resource, context: LFGeneratorContext) { // Register the after delay transformation to be applied by GeneratorBase. - registerTransformation(AfterDelayTransformation(CppDelayBodyGenerator(), CppTypes, resource)) + registerTransformation(AfterDelayTransformation(CppDelayBodyGenerator, CppTypes, resource)) super.doGenerate(resource, context) if (!canGenerate(errorsOccurred(), mainDef, errorReporter, context)) return diff --git a/org.lflang/src/org/lflang/generator/python/PythonGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonGenerator.java index a6a88cf0eb..a8111d9eb0 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonGenerator.java @@ -56,9 +56,9 @@ import org.lflang.federated.serialization.SupportedSerializers; import org.lflang.generator.CodeBuilder; import org.lflang.generator.CodeMap; +import org.lflang.generator.DelayBodyGenerator; import org.lflang.generator.GeneratorResult; import org.lflang.generator.GeneratorUtils; -import org.lflang.generator.IDelayBodyGenerator; import org.lflang.generator.IntegratedBuilder; import org.lflang.generator.LFGeneratorContext; import org.lflang.generator.ReactorInstance; @@ -652,7 +652,7 @@ protected void generateReaction(Reaction reaction, ReactorDecl decl, int reactio Reactor reactor = ASTUtils.toDefinition(decl); // Delay reactors and top-level reactions used in the top-level reactor(s) in federated execution are generated in C - if (reactor.getName().contains(IDelayBodyGenerator.GEN_DELAY_CLASS_NAME) || + if (reactor.getName().contains(DelayBodyGenerator.GEN_DELAY_CLASS_NAME) || mainDef != null && decl == mainDef.getReactorClass() && reactor.isFederated()) { super.generateReaction(reaction, decl, reactionIndex); return; diff --git a/org.lflang/src/org/lflang/generator/python/PythonReactionGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonReactionGenerator.java index c4ae8c49e5..8c30feec93 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonReactionGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonReactionGenerator.java @@ -7,7 +7,7 @@ import org.lflang.ASTUtils; import org.lflang.ErrorReporter; import org.lflang.Target; -import org.lflang.generator.IDelayBodyGenerator; +import org.lflang.generator.DelayBodyGenerator; import org.lflang.generator.c.CReactionGenerator; import org.lflang.lf.ReactorDecl; import org.lflang.lf.Reaction; @@ -381,7 +381,7 @@ public static String generateCPythonReactionLinkers( CodeBuilder code = new CodeBuilder(); // Delay reactors and top-level reactions used in the top-level reactor(s) in federated execution are generated in C - if (reactor.getName().contains(IDelayBodyGenerator.GEN_DELAY_CLASS_NAME) || + if (reactor.getName().contains(DelayBodyGenerator.GEN_DELAY_CLASS_NAME) || instance.getDefinition().getReactorClass() == (mainDef != null ? mainDef.getReactorClass() : null) && reactor.isFederated()) { return ""; diff --git a/org.lflang/src/org/lflang/generator/python/PythonReactorGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonReactorGenerator.java index 76ad81d5c8..b6d449b8b5 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonReactorGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonReactorGenerator.java @@ -5,8 +5,7 @@ import org.lflang.ASTUtils; import org.lflang.federated.FederateInstance; import org.lflang.generator.CodeBuilder; -import org.lflang.generator.GeneratorBase; -import org.lflang.generator.IDelayBodyGenerator; +import org.lflang.generator.DelayBodyGenerator; import org.lflang.generator.ParameterInstance; import org.lflang.generator.ReactorInstance; import org.lflang.lf.Reaction; @@ -44,7 +43,7 @@ public static String generatePythonClass(ReactorInstance instance, FederateInsta if (instance != main && !federate.contains(instance) || instantiatedClasses == null || // Do not generate code for delay reactors in Python - className.contains(IDelayBodyGenerator.GEN_DELAY_CLASS_NAME)) { + className.contains(DelayBodyGenerator.GEN_DELAY_CLASS_NAME)) { return ""; } @@ -139,7 +138,7 @@ public static String generatePythonClassInstantiations(ReactorInstance instance, } String className = instance.getDefinition().getReactorClass().getName(); // Do not instantiate delay reactors in Python - if (className.contains(IDelayBodyGenerator.GEN_DELAY_CLASS_NAME)) { + if (className.contains(DelayBodyGenerator.GEN_DELAY_CLASS_NAME)) { return ""; } diff --git a/org.lflang/src/org/lflang/generator/ts/TSDelayBodyGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSDelayBodyGenerator.kt index 948be78ca5..a876287cd2 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSDelayBodyGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSDelayBodyGenerator.kt @@ -1,11 +1,11 @@ package org.lflang.generator.ts import org.lflang.ASTUtils -import org.lflang.generator.IDelayBodyGenerator +import org.lflang.generator.DelayBodyGenerator import org.lflang.lf.Action import org.lflang.lf.VarRef -class TSDelayBodyGenerator : IDelayBodyGenerator { +object TSDelayBodyGenerator : DelayBodyGenerator { /** * Return a TS type for the specified action. * If the type has not been specified, return diff --git a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt index f654bf4975..dfcf27a30b 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt @@ -121,7 +121,7 @@ class TSGenerator( */ override fun doGenerate(resource: Resource, context: LFGeneratorContext) { // Register the after delay transformation to be applied by GeneratorBase. - registerTransformation(AfterDelayTransformation(TSDelayBodyGenerator(), targetTypes, resource)) + registerTransformation(AfterDelayTransformation(TSDelayBodyGenerator, targetTypes, resource)) super.doGenerate(resource, context) From 7aa0a15e4f9f06f21714dfad6e680a0eb04fd4a3 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Mon, 19 Dec 2022 14:11:00 +0100 Subject: [PATCH 11/13] Revert "remove unused code" This reverts commit 889bc9cdc270d3a5d4d4a095386fc0909762ccfd. --- .../lflang/ast/AfterDelayTransformation.java | 24 ++++++++++++++++--- .../lflang/generator/DelayBodyGenerator.java | 19 +++++++++++++++ .../generator/c/CDelayBodyGenerator.java | 4 ++++ .../generator/cpp/CppDelayBodyGenerator.kt | 2 ++ .../generator/ts/TSDelayBodyGenerator.kt | 2 ++ 5 files changed, 48 insertions(+), 3 deletions(-) diff --git a/org.lflang/src/org/lflang/ast/AfterDelayTransformation.java b/org.lflang/src/org/lflang/ast/AfterDelayTransformation.java index b6edf1969f..1faa2e38fb 100644 --- a/org.lflang/src/org/lflang/ast/AfterDelayTransformation.java +++ b/org.lflang/src/org/lflang/ast/AfterDelayTransformation.java @@ -37,6 +37,7 @@ import org.lflang.lf.TypeParm; import org.lflang.lf.VarRef; import org.lflang.lf.WidthSpec; +import org.lflang.lf.WidthTerm; public class AfterDelayTransformation implements AstTransformation { @@ -100,7 +101,8 @@ private void insertGeneratedDelays(List reactors) { Type type = ((Port) connection.getRightPorts().get(0).getVariable()).getType(); Reactor delayClass = getDelayClass(type); String generic = targetTypes.supportsGenerics() ? targetTypes.getTargetType(InferredType.fromAST(type)) : ""; - Instantiation delayInstance = getDelayInstance(delayClass, connection, generic); + Instantiation delayInstance = getDelayInstance(delayClass, connection, generic, + !generator.generateAfterDelaysWithVariableWidth()); // Stage the new connections for insertion into the tree. List connections = ASTUtils.convertToEmptyListIfNull(newConnections.get(parent)); @@ -196,9 +198,13 @@ private static List rerouteViaDelay(Connection connection, * @param connection The connection to create a delay instantiation foe * @param generic A string that denotes the appropriate type parameter, * which should be null or empty if the target does not support generics. + * @param defineWidthFromConnection If this is true and if the connection + * is a wide connection, then instantiate a bank of delays where the width + * is given by ports involved in the connection. Otherwise, the width will + * be unspecified indicating a variable length. */ private static Instantiation getDelayInstance(Reactor delayClass, - Connection connection, String generic) { + Connection connection, String generic, Boolean defineWidthFromConnection) { Expression delay = connection.getDelay(); Instantiation delayInstance = factory.createInstantiation(); delayInstance.setReactorClass(delayClass); @@ -209,7 +215,19 @@ private static Instantiation getDelayInstance(Reactor delayClass, } if (ASTUtils.hasMultipleConnections(connection)) { WidthSpec widthSpec = factory.createWidthSpec(); - widthSpec.setOfVariableLength(true); + if (defineWidthFromConnection) { + // Add all left ports of the connection to the WidthSpec of the generated delay instance. + // This allows the code generator to later infer the width from the involved ports. + // We only consider the left ports here, as they could be part of a broadcast. In this case, we want + // to delay the ports first, and then broadcast the output of the delays. + for (VarRef port : connection.getLeftPorts()) { + WidthTerm term = factory.createWidthTerm(); + term.setPort(EcoreUtil.copy(port)); + widthSpec.getTerms().add(term); + } + } else { + widthSpec.setOfVariableLength(true); + } delayInstance.setWidthSpec(widthSpec); } Assignment assignment = factory.createAssignment(); diff --git a/org.lflang/src/org/lflang/generator/DelayBodyGenerator.java b/org.lflang/src/org/lflang/generator/DelayBodyGenerator.java index d48e146237..a26d002ae5 100644 --- a/org.lflang/src/org/lflang/generator/DelayBodyGenerator.java +++ b/org.lflang/src/org/lflang/generator/DelayBodyGenerator.java @@ -34,4 +34,23 @@ public interface DelayBodyGenerator { */ String generateDelayGeneric(); + /** + * 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 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 + * side of the connection. This gives the code generator the information needed to infer the + * correct width at + * runtime. + */ + boolean generateAfterDelaysWithVariableWidth(); + } diff --git a/org.lflang/src/org/lflang/generator/c/CDelayBodyGenerator.java b/org.lflang/src/org/lflang/generator/c/CDelayBodyGenerator.java index 5bf362c625..c3895e89d4 100644 --- a/org.lflang/src/org/lflang/generator/c/CDelayBodyGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CDelayBodyGenerator.java @@ -55,4 +55,8 @@ public String generateDelayGeneric() { throw new UnsupportedOperationException("TODO: auto-generated method stub"); } + @Override + public boolean generateAfterDelaysWithVariableWidth() { + return false; + } } diff --git a/org.lflang/src/org/lflang/generator/cpp/CppDelayBodyGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppDelayBodyGenerator.kt index 87d63399d7..e2c41930db 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppDelayBodyGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppDelayBodyGenerator.kt @@ -41,4 +41,6 @@ object CppDelayBodyGenerator : DelayBodyGenerator { override fun generateDelayGeneric() = "T" + override fun generateAfterDelaysWithVariableWidth() = false + } \ No newline at end of file diff --git a/org.lflang/src/org/lflang/generator/ts/TSDelayBodyGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSDelayBodyGenerator.kt index a876287cd2..326d4a65a5 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSDelayBodyGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSDelayBodyGenerator.kt @@ -32,4 +32,6 @@ object TSDelayBodyGenerator : DelayBodyGenerator { override fun generateDelayGeneric(): String { return "T extends Present" } + + override fun generateAfterDelaysWithVariableWidth() = false } \ No newline at end of file From cfacbd41be6ccf16b04ac3fe5fb2c859ae924d9d Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Mon, 19 Dec 2022 14:12:14 +0100 Subject: [PATCH 12/13] the C generator can infer widths --- org.lflang/src/org/lflang/generator/c/CDelayBodyGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/org/lflang/generator/c/CDelayBodyGenerator.java b/org.lflang/src/org/lflang/generator/c/CDelayBodyGenerator.java index c3895e89d4..72d4939714 100644 --- a/org.lflang/src/org/lflang/generator/c/CDelayBodyGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CDelayBodyGenerator.java @@ -57,6 +57,6 @@ public String generateDelayGeneric() { @Override public boolean generateAfterDelaysWithVariableWidth() { - return false; + return true; } } From 39675354364eaf34e220f7212fe9b2a90a8c5355 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Tue, 20 Dec 2022 08:17:22 +0100 Subject: [PATCH 13/13] formatting Co-authored-by: Marten Lohstroh --- .../src/org/lflang/generator/cpp/CppDelayBodyGenerator.kt | 2 +- org.lflang/src/org/lflang/generator/ts/TSDelayBodyGenerator.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/cpp/CppDelayBodyGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppDelayBodyGenerator.kt index e2c41930db..3a03f889c7 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppDelayBodyGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppDelayBodyGenerator.kt @@ -43,4 +43,4 @@ object CppDelayBodyGenerator : DelayBodyGenerator { override fun generateAfterDelaysWithVariableWidth() = false -} \ No newline at end of file +} diff --git a/org.lflang/src/org/lflang/generator/ts/TSDelayBodyGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSDelayBodyGenerator.kt index 326d4a65a5..6d612dba7b 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSDelayBodyGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSDelayBodyGenerator.kt @@ -34,4 +34,4 @@ object TSDelayBodyGenerator : DelayBodyGenerator { } override fun generateAfterDelaysWithVariableWidth() = false -} \ No newline at end of file +}