diff --git a/.github/workflows/ts-tests.yml b/.github/workflows/ts-tests.yml index 344698ba66..68330269b5 100644 --- a/.github/workflows/ts-tests.yml +++ b/.github/workflows/ts-tests.yml @@ -24,9 +24,9 @@ jobs: - name: Prepare build environment uses: ./.github/actions/prepare-build-env - name: Setup Node.js environment - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: - node-version: 18 + node-version: 22 - name: Install pnpm run: npm i -g pnpm - name: Install coreutils (macOS) diff --git a/core/src/main/java/org/lflang/ast/ASTUtils.java b/core/src/main/java/org/lflang/ast/ASTUtils.java index 7e55e3cabc..ef66d24cd1 100644 --- a/core/src/main/java/org/lflang/ast/ASTUtils.java +++ b/core/src/main/java/org/lflang/ast/ASTUtils.java @@ -623,7 +623,7 @@ public static ReactorInstance createMainReactorInstance( messageReporter .nowhere() .error("Main reactor has causality cycles. Skipping code generation."); - return null; + return main; // Avoid NPE. } // Inform the run-time of the breadth/parallelism of the reaction graph var breadth = reactionInstanceGraph.getBreadth(); diff --git a/core/src/main/java/org/lflang/generator/GeneratorBase.java b/core/src/main/java/org/lflang/generator/GeneratorBase.java index 0713b863be..83eb540775 100644 --- a/core/src/main/java/org/lflang/generator/GeneratorBase.java +++ b/core/src/main/java/org/lflang/generator/GeneratorBase.java @@ -60,6 +60,7 @@ import org.lflang.lf.Mode; import org.lflang.lf.Reaction; import org.lflang.lf.Reactor; +import org.lflang.lf.VarRef; import org.lflang.target.Target; import org.lflang.target.TargetConfig; import org.lflang.target.property.FilesProperty; @@ -441,13 +442,7 @@ private void transformConflictingConnectionsInModalReactors(Set resour reaction.getEffects().add(destRef); var code = factory.createCode(); - var source = - (sourceRef.getContainer() != null ? sourceRef.getContainer().getName() + "." : "") - + sourceRef.getVariable().getName(); - var dest = - (destRef.getContainer() != null ? destRef.getContainer().getName() + "." : "") - + destRef.getVariable().getName(); - code.setBody(getConflictingConnectionsInModalReactorsBody(source, dest)); + code.setBody(getConflictingConnectionsInModalReactorsBody(sourceRef, destRef)); reaction.setCode(code); EcoreUtil.remove(connection); @@ -464,7 +459,7 @@ private void transformConflictingConnectionsInModalReactors(Set resour *

This method needs to be overridden in target specific code generators that support modal * reactors. */ - protected String getConflictingConnectionsInModalReactorsBody(String source, String dest) { + protected String getConflictingConnectionsInModalReactorsBody(VarRef source, VarRef dest) { messageReporter .nowhere() .error( diff --git a/core/src/main/java/org/lflang/generator/c/CGenerator.java b/core/src/main/java/org/lflang/generator/c/CGenerator.java index f6ee943085..dda5c06d89 100644 --- a/core/src/main/java/org/lflang/generator/c/CGenerator.java +++ b/core/src/main/java/org/lflang/generator/c/CGenerator.java @@ -81,7 +81,9 @@ import org.lflang.lf.Reactor; import org.lflang.lf.ReactorDecl; import org.lflang.lf.StateVar; +import org.lflang.lf.VarRef; import org.lflang.lf.Variable; +import org.lflang.lf.WidthSpec; import org.lflang.target.Target; import org.lflang.target.TargetConfig; import org.lflang.target.property.BuildCommandsProperty; @@ -408,7 +410,10 @@ public void doGenerate(Resource resource, LFGeneratorContext context) { if (!isOSCompatible()) return; // Incompatible OS and configuration // Perform set up that does not generate code - setUpGeneralParameters(); + if (!setUpGeneralParameters()) { + // Failure. + return; + } FileUtil.createDirectoryIfDoesNotExist(fileConfig.getSrcGenPath().toFile()); FileUtil.createDirectoryIfDoesNotExist(fileConfig.binPath.toFile()); @@ -659,12 +664,81 @@ public void checkModalReactorSupport(boolean __) { } @Override - protected String getConflictingConnectionsInModalReactorsBody(String source, String dest) { - return String.join( - "\n", - "// Generated forwarding reaction for connections with the same destination", - "// but located in mutually exclusive modes.", - "lf_set(" + dest + ", " + source + "->value);"); + protected String getConflictingConnectionsInModalReactorsBody(VarRef sourceRef, VarRef destRef) { + Instantiation sourceContainer = sourceRef.getContainer(); + Instantiation destContainer = destRef.getContainer(); + Port sourceAsPort = (Port) sourceRef.getVariable(); + Port destAsPort = (Port) destRef.getVariable(); + WidthSpec sourceWidth = sourceAsPort.getWidthSpec(); + WidthSpec destWidth = destAsPort.getWidthSpec(); + + // NOTE: Have to be careful with naming count variables because if the name matches + // that of a port, the program will fail to compile. + + // If the source or dest is a port of a bank, we need to iterate over it. + var isBank = false; + Instantiation bank = null; + var sourceContainerRef = ""; + if (sourceContainer != null) { + sourceContainerRef = sourceContainer.getName() + "."; + bank = sourceContainer; + if (bank.getWidthSpec() != null) { + isBank = true; + sourceContainerRef = sourceContainer.getName() + "[_lf_j]."; + } + } + var sourceIndex = isBank ? "_lf_i" : "_lf_c"; + var source = + sourceContainerRef + + sourceAsPort.getName() + + ((sourceWidth != null) ? "[" + sourceIndex + "]" : ""); + var destContainerRef = ""; + var destIndex = "_lf_c"; + if (destContainer != null) { + destIndex = "_lf_i"; + destContainerRef = destContainer.getName() + "."; + if (bank == null) { + bank = destContainer; + if (bank.getWidthSpec() != null) { + isBank = true; + destContainerRef = destContainer.getName() + "[_lf_j]."; + } + } + } + var dest = + destContainerRef + + destAsPort.getName() + + ((destWidth != null) ? "[" + destIndex + "]" : ""); + var result = new StringBuilder(); + result.append("{ int _lf_c = 0; SUPPRESS_UNUSED_WARNING(_lf_c); "); + // If either side is a bank (only one side should be), iterate over it. + if (isBank) { + var width = new StringBuilder(); + for (var term : bank.getWidthSpec().getTerms()) { + if (!width.isEmpty()) width.append(" + "); + if (term.getCode() != null) width.append(term.getCode().getBody()); + else if (term.getParameter() != null) + width.append("self->" + term.getParameter().getName()); + else width.append(term.getWidth()); + } + result.append("for(int _lf_j = 0; _lf_j < " + width.toString() + "; _lf_j++) { "); + } + // If either side is a multiport, iterate. + // Note that one side could be a multiport of width 1 and the other an ordinary port. + if (sourceWidth != null || destWidth != null) { + var width = + (sourceAsPort.getWidthSpec() != null) + ? sourceContainerRef + sourceAsPort.getName() + : destContainerRef + destAsPort.getName(); + result.append("for(int _lf_i = 0; _lf_i < " + width + "_width; _lf_i++) { "); + } + result.append("lf_set(" + dest + ", " + source + "->value); _lf_c++; "); + if (sourceWidth != null || destAsPort.getWidthSpec() != null) { + result.append(" }"); + } + if (isBank) result.append(" }"); + result.append(" }"); + return result.toString(); } /** Set the scheduler type in the target config as needed. */ @@ -1939,8 +2013,8 @@ protected DockerGenerator getDockerGenerator(LFGeneratorContext context) { // ////////////////////////////////////////// // // Protected methods. - // Perform set up that does not generate code - protected void setUpGeneralParameters() { + // Perform set up that does not generate code. Return false on failure. + protected boolean setUpGeneralParameters() { accommodatePhysicalActionsIfPresent(); CompileDefinitionsProperty.INSTANCE.update( targetConfig, @@ -1950,6 +2024,10 @@ protected void setUpGeneralParameters() { // Create the main reactor instance if there is a main reactor. this.main = ASTUtils.createMainReactorInstance(mainDef, reactors, messageReporter, targetConfig); + if (this.main == null) { + // Something went wrong (causality cycle?). Stop. + return false; + } if (hasModalReactors) { // So that each separate compile knows about modal reactors, do this: CompileDefinitionsProperty.INSTANCE.update(targetConfig, Map.of("MODAL_REACTORS", "TRUE")); @@ -2002,6 +2080,7 @@ protected void setUpGeneralParameters() { } pickCompilePlatform(); } + return true; } protected void handleProtoFiles() { diff --git a/core/src/main/java/org/lflang/generator/c/CReactionGenerator.java b/core/src/main/java/org/lflang/generator/c/CReactionGenerator.java index 151d2a47fb..a2f2a93f7d 100644 --- a/core/src/main/java/org/lflang/generator/c/CReactionGenerator.java +++ b/core/src/main/java/org/lflang/generator/c/CReactionGenerator.java @@ -279,7 +279,10 @@ public static int maxContainedReactorBankWidth( nestedBreadcrumbs.add(mainDef); } int result = max; - Reactor parent = (Reactor) containedReactor.eContainer(); + Reactor parent = + containedReactor.eContainer() instanceof Mode + ? (Reactor) containedReactor.eContainer().eContainer() + : (Reactor) containedReactor.eContainer(); if (parent == ASTUtils.toDefinition(mainDef.getReactorClass())) { // The parent is main, so there can't be any other instantiations of it. return ASTUtils.width(containedReactor.getWidthSpec(), null); diff --git a/core/src/main/java/org/lflang/generator/python/PythonGenerator.java b/core/src/main/java/org/lflang/generator/python/PythonGenerator.java index 854c8f108a..f486c64a47 100644 --- a/core/src/main/java/org/lflang/generator/python/PythonGenerator.java +++ b/core/src/main/java/org/lflang/generator/python/PythonGenerator.java @@ -54,17 +54,19 @@ import org.lflang.generator.docker.PythonDockerGenerator; import org.lflang.lf.Action; import org.lflang.lf.Input; +import org.lflang.lf.Instantiation; import org.lflang.lf.Model; import org.lflang.lf.Output; import org.lflang.lf.Port; import org.lflang.lf.Reaction; import org.lflang.lf.Reactor; +import org.lflang.lf.VarRef; +import org.lflang.lf.WidthSpec; import org.lflang.target.Target; import org.lflang.target.property.DockerProperty; import org.lflang.target.property.ProtobufsProperty; import org.lflang.util.FileUtil; import org.lflang.util.LFCommand; -import org.lflang.util.StringUtil; /** * Generator for Python target. This class generates Python code defining each reactor class given @@ -539,22 +541,94 @@ protected void generateSelfStructExtension( } @Override - protected String getConflictingConnectionsInModalReactorsBody(String source, String dest) { - // NOTE: Strangely, a newline is needed at the beginning or indentation - // gets swallowed. - return String.join( - "\n", - "\n# Generated forwarding reaction for connections with the same destination", - "# but located in mutually exclusive modes.", - dest + ".set(" + source + ".value)\n"); + protected String getConflictingConnectionsInModalReactorsBody(VarRef sourceRef, VarRef destRef) { + Instantiation sourceContainer = sourceRef.getContainer(); + Instantiation destContainer = destRef.getContainer(); + Port sourceAsPort = (Port) sourceRef.getVariable(); + Port destAsPort = (Port) destRef.getVariable(); + WidthSpec sourceWidth = sourceAsPort.getWidthSpec(); + WidthSpec destWidth = destAsPort.getWidthSpec(); + + // NOTE: Have to be careful with naming count variables because if the name matches + // that of a port, the program will fail to compile. + + // If the source or dest is a port of a bank, we need to iterate over it. + var isBank = false; + Instantiation bank = null; + var sourceContainerRef = ""; + if (sourceContainer != null) { + sourceContainerRef = sourceContainer.getName() + "."; + bank = sourceContainer; + if (bank.getWidthSpec() != null) { + isBank = true; + sourceContainerRef = sourceContainer.getName() + "[_lf_j]."; + } + } + var sourceIndex = isBank ? "_lf_i" : "_lf_c"; + var source = + sourceContainerRef + + sourceAsPort.getName() + + ((sourceWidth != null) ? "[" + sourceIndex + "]" : ""); + var destContainerRef = ""; + var destIndex = "_lf_c"; + if (destContainer != null) { + destIndex = "_lf_i"; + destContainerRef = destContainer.getName() + "."; + if (bank == null) { + bank = destContainer; + if (bank.getWidthSpec() != null) { + isBank = true; + destContainerRef = destContainer.getName() + "[_lf_j]."; + } + } + } + var dest = + destContainerRef + + destAsPort.getName() + + ((destWidth != null) ? "[" + destIndex + "]" : ""); + var result = new CodeBuilder(); + // If either side is a bank (only one side should be), iterate over it. + result.pr("_lf_c = 0"); // Counter variable over nested loop if there is a bank and multiport. + if (isBank) { + var width = new StringBuilder(); + for (var term : bank.getWidthSpec().getTerms()) { + if (!width.isEmpty()) width.append(" + "); + if (term.getCode() != null) width.append(term.getCode().getBody()); + else if (term.getParameter() != null) width.append("self." + term.getParameter().getName()); + else width.append(term.getWidth()); + } + result.pr("for _lf_j in range(" + width + "):"); + result.indent(); + } + // If either side is a multiport, iterate. + // Note that one side could be a multiport of width 1 and the other an ordinary port. + if (sourceWidth != null || destWidth != null) { + var width = + (sourceAsPort.getWidthSpec() != null) + ? sourceContainerRef + sourceAsPort.getName() + : destContainerRef + destAsPort.getName(); + result.pr("for _lf_i in range(" + width + ".width):"); + result.indent(); + } + result.pr(dest + ".set(" + source + ".value)"); + result.pr("_lf_c += 1"); // Increment the count. + result.unindent(); + if (isBank) { + result.unindent(); + } + return result.toString(); } @Override - protected void setUpGeneralParameters() { - super.setUpGeneralParameters(); - if (hasModalReactors) { - targetConfig.compileAdditionalSources.add("lib/modal_models/impl.c"); + protected boolean setUpGeneralParameters() { + boolean result = super.setUpGeneralParameters(); + if (result) { + if (hasModalReactors) { + targetConfig.compileAdditionalSources.add("lib/modal_models/impl.c"); + } + return true; } + return false; } @Override @@ -622,18 +696,6 @@ private static String generateCmakeInstall(FileConfig fileConfig) { .replace("", pyMainName); } - /** - * Generate a ({@code key}, {@code val}) tuple pair for the {@code define_macros} field of the - * Extension class constructor from setuptools. - * - * @param key The key of the macro entry - * @param val The value of the macro entry - * @return A ({@code key}, {@code val}) tuple pair as String - */ - private static String generateMacroEntry(String key, String val) { - return "(" + StringUtil.addDoubleQuotes(key) + ", " + StringUtil.addDoubleQuotes(val) + ")"; - } - /** * Generate the name of the python module. * diff --git a/core/src/main/java/org/lflang/generator/python/PythonReactionGenerator.java b/core/src/main/java/org/lflang/generator/python/PythonReactionGenerator.java index 528ec85bf8..d0a1bffac7 100644 --- a/core/src/main/java/org/lflang/generator/python/PythonReactionGenerator.java +++ b/core/src/main/java/org/lflang/generator/python/PythonReactionGenerator.java @@ -118,8 +118,7 @@ private static String generateCPythonFunctionCaller( + " code again", " }", " " + PyUtil.generateGILReleaseCode(), - " Py_FinalizeEx();", - " exit(1);", + " exit(1);", // NOTE: Used to call Py_FinalizeEx() before exit, but it segfaults. "}", "", "/* Release the thread. No Python API allowed beyond this point. */", diff --git a/core/src/main/java/org/lflang/util/ImportUtil.java b/core/src/main/java/org/lflang/util/ImportUtil.java index b7de0a16d4..432f70574b 100644 --- a/core/src/main/java/org/lflang/util/ImportUtil.java +++ b/core/src/main/java/org/lflang/util/ImportUtil.java @@ -25,8 +25,8 @@ public class ImportUtil { * names. */ public static String buildPackageURI(String uriStr, Resource resource) { - Path rootPath = Paths.get(resource.getURI().toString()).toAbsolutePath(); + Path rootPath = FileUtil.toPath(resource); Path uriPath = Paths.get(uriStr.trim()); if (uriPath.getNameCount() < 2) { diff --git a/core/src/main/kotlin/org/lflang/generator/ts/TSFileConfig.kt b/core/src/main/kotlin/org/lflang/generator/ts/TSFileConfig.kt index e04040f9b3..5b5820e045 100644 --- a/core/src/main/kotlin/org/lflang/generator/ts/TSFileConfig.kt +++ b/core/src/main/kotlin/org/lflang/generator/ts/TSFileConfig.kt @@ -27,6 +27,7 @@ package org.lflang.generator.ts import org.eclipse.emf.ecore.resource.Resource import org.lflang.FileConfig +import org.lflang.generator.GeneratorUtils import org.lflang.util.FileUtil import org.lflang.util.LFCommand import java.io.IOException @@ -53,4 +54,8 @@ class TSFileConfig( super.doClean() FileUtil.deleteDirectory(srcGenPath) } + + override fun getExecutableExtension(): String { + return if (GeneratorUtils.isHostWindows()) ".bat" else "" + } } diff --git a/core/src/main/kotlin/org/lflang/generator/ts/TSGenerator.kt b/core/src/main/kotlin/org/lflang/generator/ts/TSGenerator.kt index 91db8810e1..c3bb156d97 100644 --- a/core/src/main/kotlin/org/lflang/generator/ts/TSGenerator.kt +++ b/core/src/main/kotlin/org/lflang/generator/ts/TSGenerator.kt @@ -451,7 +451,7 @@ class TSGenerator( context.unsuccessfulFinish() } else { context.finish(GeneratorResult.Status.COMPILED, codeMaps) - val shScriptPath = fileConfig.binPath.resolve(fileConfig.name) + val shScriptPath = fileConfig.executable val jsPath = fileConfig.srcGenPath.resolve("dist").resolve("${fileConfig.name}.js") FileUtil.writeToFile("#!/bin/sh\nnode $jsPath", shScriptPath) shScriptPath.toFile().setExecutable(true) diff --git a/core/src/main/resources/lib/c/reactor-c b/core/src/main/resources/lib/c/reactor-c index da80702620..05280d0f54 160000 --- a/core/src/main/resources/lib/c/reactor-c +++ b/core/src/main/resources/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit da807026204818cc38c1f39cff167a1c0da9b789 +Subproject commit 05280d0f5488058693e18863725bcd1e5fe68a29 diff --git a/core/src/main/resources/lib/ts/package.json b/core/src/main/resources/lib/ts/package.json index f77ff84e31..eec471ab6d 100644 --- a/core/src/main/resources/lib/ts/package.json +++ b/core/src/main/resources/lib/ts/package.json @@ -11,13 +11,12 @@ "@types/command-line-usage": "^5.0.2", "@types/google-protobuf": "^3.7.4", "@types/microtime": "^2.1.0", - "@types/node": "^18.14.2", - "@typescript-eslint/eslint-plugin": "5.33.0", - "@typescript-eslint/parser": "^5.8.1", - "eslint": "^8.5.0", - "typescript": "~4.8.2", + "@types/node": "^22.5.0", + "eslint-config-prettier": "^9.0.0", + "eslint-config-standard-with-typescript": "^43.0.1", + "typescript": "^5.1.6", "ts-protoc-gen": "^0.15.0", - "rimraf": "^3.0.2" + "rimraf": "^6.0.1" }, "scripts": { "build": "npx rimraf dist && npx tsc --outDir dist" diff --git a/core/src/main/resources/lib/ts/tsconfig.json b/core/src/main/resources/lib/ts/tsconfig.json index ae7bb7804a..bd1f323b67 100644 --- a/core/src/main/resources/lib/ts/tsconfig.json +++ b/core/src/main/resources/lib/ts/tsconfig.json @@ -4,7 +4,6 @@ "target": "esnext", "module": "CommonJS", "types": ["node", "@lf-lang/reactor-ts", "microtime", "command-line-args", "command-line-usage"], - "typeRoots": ["./node_modules/@types/", "./node_modules/@lf-lang/reactor-ts/src/core/@types/"], "esModuleInterop": true, "isolatedModules": true, "lib": ["esnext", "dom"], diff --git a/test/C/src/Mutable.lf b/test/C/src/Mutable.lf index 575473986f..220ff1afbd 100644 --- a/test/C/src/Mutable.lf +++ b/test/C/src/Mutable.lf @@ -11,7 +11,9 @@ reactor S(width: int = 4) { } reactor R(width: int = 4) { - mutable input[width] in: int + // Pathetically, the Windows C compiler doesn't support arrays on the stack with a variable size. + // So we have to give a constant here instead of the parameter width. + mutable input[2] in: int output[width] out: int reaction(in) -> out {= diff --git a/test/C/src/modal_models/ModalMultiport.lf b/test/C/src/modal_models/ModalMultiport.lf new file mode 100644 index 0000000000..e6714ec024 --- /dev/null +++ b/test/C/src/modal_models/ModalMultiport.lf @@ -0,0 +1,67 @@ +target C { + timeout: 1 ms +} + +reactor Destination(n_inputs: int = 2) { + input[n_inputs] req: int + output[n_inputs] rsp: int + + reaction(req) -> rsp {= + for (int i = 0; i < self->n_inputs; ++i) { + if (req[i]->is_present) { + lf_set (rsp[i], req[i]->value); + } + } + =} +} + +reactor Source(n_ports: int = 2) { + output[n_ports] req: int + input[n_ports] rsp: int + timer t(0, 1 ms) + + reaction(t) -> req {= + for (int i = 0; i < self->n_ports; ++i) { + lf_set (req[i], i); + } + =} + + reaction(rsp) {= + for (int i = 0; i < self->n_ports; ++i) { + lf_print("Received response:%d", rsp[i]->value); + if (rsp[i]->value != i) { + lf_print_error_and_exit("Expected %d", i); + } + } + =} +} + +reactor Selector(n_ports: int = 2) { + input[n_ports] in_req: int + output[n_ports] out_rsp: int + + initial mode DST_1 { + dst1 = new Destination() + + in_req -> dst1.req + dst1.rsp -> out_rsp + reaction(startup) -> reset(DST_2) {= + lf_set_mode(DST_2); + =} + } + + mode DST_2 { + dst2 = new Destination() + + in_req -> dst2.req + dst2.rsp -> out_rsp + } +} + +main reactor { + src = new Source() + sel = new Selector() + + src.req -> sel.in_req + sel.out_rsp -> src.rsp +} diff --git a/test/C/src/modal_models/ModalMultiportBank.lf b/test/C/src/modal_models/ModalMultiportBank.lf new file mode 100644 index 0000000000..9ab4213fd5 --- /dev/null +++ b/test/C/src/modal_models/ModalMultiportBank.lf @@ -0,0 +1,67 @@ +target C { + timeout: 1 ms +} + +reactor Destination(n_inputs: int = 2) { + input[n_inputs] req: int + output[n_inputs] rsp: int + + reaction(req) -> rsp {= + for (int i = 0; i < self->n_inputs; ++i) { + if (req[i]->is_present) { + lf_set (rsp[i], req[i]->value); + } + } + =} +} + +reactor Source(n_ports: int = 4) { + output[n_ports] req: int + input[n_ports] rsp: int + timer t(0, 1 ms) + + reaction(t) -> req {= + for (int i = 0; i < self->n_ports; ++i) { + lf_set (req[i], i); + } + =} + + reaction(rsp) {= + for (int i = 0; i < self->n_ports; ++i) { + lf_print("Received response:%d", rsp[i]->value); + if (rsp[i]->value != i) { + lf_print_error_and_exit("Expected %d", i); + } + } + =} +} + +reactor Selector(n_ports: int = 4) { + input[n_ports] in_req: int + output[n_ports] out_rsp: int + + initial mode DST_1 { + dst1 = new[2] Destination() + + in_req -> dst1.req + dst1.rsp -> out_rsp + reaction(startup) -> reset(DST_2) {= + lf_set_mode(DST_2); + =} + } + + mode DST_2 { + dst2 = new[2] Destination() + + in_req -> dst2.req + dst2.rsp -> out_rsp + } +} + +main reactor { + src = new Source() + sel = new Selector() + + src.req -> sel.in_req + sel.out_rsp -> src.rsp +} diff --git a/test/C/src/token/MutableToken.lf b/test/C/src/token/MutableToken.lf index 724b8a6579..33d86be4b0 100644 --- a/test/C/src/token/MutableToken.lf +++ b/test/C/src/token/MutableToken.lf @@ -10,7 +10,9 @@ preamble {= =} reactor R(width: int = 4) { - mutable input[width] in: int_array_t* + // Pathetically, the Windows C compiler doesn't support arrays on the stack with a variable size. + // So we have to give a constant here instead of the parameter width. + mutable input[2] in: int_array_t* output[width] out: int_array_t* reaction(in) -> out {= diff --git a/test/Python/src/concurrent/ConcurrentAction.lf b/test/Python/src/concurrent/failing/ConcurrentAction.lf similarity index 100% rename from test/Python/src/concurrent/ConcurrentAction.lf rename to test/Python/src/concurrent/failing/ConcurrentAction.lf diff --git a/test/Python/src/LingoFederatedImport.lf b/test/Python/src/federated/LingoFederatedImport.lf similarity index 89% rename from test/Python/src/LingoFederatedImport.lf rename to test/Python/src/federated/LingoFederatedImport.lf index 5b3a4fadd6..6c642bae6f 100644 --- a/test/Python/src/LingoFederatedImport.lf +++ b/test/Python/src/federated/LingoFederatedImport.lf @@ -1,5 +1,4 @@ # Test the new import statement for Lingo downloaded packages with the import path enclosed in angle brackets -# Version 1: The LF file is located in "src". target Python { timeout: 2 sec } diff --git a/test/Python/src/lingo_imports/FederatedTestImportPackages.lf b/test/Python/src/lingo_imports/FederatedTestImportPackages.lf deleted file mode 100644 index 4081aa29ba..0000000000 --- a/test/Python/src/lingo_imports/FederatedTestImportPackages.lf +++ /dev/null @@ -1,21 +0,0 @@ -# Test the new import statement for Lingo downloaded packages with the import path enclosed in angle brackets -# Version 2: The LF file is now located in a subdirectory under "src". -target Python { - timeout: 2 sec -} - -import Count from - -reactor Actuator { - input results - - reaction(results) {= - print(f"Count: {results.value}") - =} -} - -federated reactor { - count = new Count() - act = new Actuator() - count.out -> act.results -} diff --git a/test/Python/src/modal_models/ModalMultiport.lf b/test/Python/src/modal_models/ModalMultiport.lf new file mode 100644 index 0000000000..38790a7e80 --- /dev/null +++ b/test/Python/src/modal_models/ModalMultiport.lf @@ -0,0 +1,63 @@ +target Python { + timeout: 1 ms +} + +reactor Destination(n_inputs=2) { + input[n_inputs] req + output[n_inputs] rsp + + reaction(req) -> rsp {= + for n in range(self.n_inputs): + if req[n].is_present: + rsp[n].set(req[n].value) + =} +} + +reactor Source(n_ports=2) { + output[n_ports] req + input[n_ports] rsp + timer t(0, 1 ms) + + reaction(t) -> req {= + for n in range(self.n_ports): + req[n].set(n) + =} + + reaction(rsp) {= + for n in range(self.n_ports): + print("Received response: ", rsp[n].value); + if rsp[n].value != n: + sys.stderr.write("ERROR: Expected {:d}\n".format(n)) + exit(1) + =} +} + +reactor Selector(n_ports=2) { + input[n_ports] in_req + output[n_ports] out_rsp + + initial mode DST_1 { + dst1 = new Destination() + + in_req -> dst1.req + dst1.rsp -> out_rsp + reaction(startup) -> reset(DST_2) {= + DST_2.set() + =} + } + + mode DST_2 { + dst2 = new Destination() + + in_req -> dst2.req + dst2.rsp -> out_rsp + } +} + +main reactor { + src = new Source() + sel = new Selector() + + src.req -> sel.in_req + sel.out_rsp -> src.rsp +} diff --git a/test/Python/src/modal_models/ModalMultiportBank.lf b/test/Python/src/modal_models/ModalMultiportBank.lf new file mode 100644 index 0000000000..5521a8d2e3 --- /dev/null +++ b/test/Python/src/modal_models/ModalMultiportBank.lf @@ -0,0 +1,63 @@ +target Python { + timeout: 1 ms +} + +reactor Destination(n_inputs=2) { + input[n_inputs] req + output[n_inputs] rsp + + reaction(req) -> rsp {= + for n in range(self.n_inputs): + if req[n].is_present: + rsp[n].set(req[n].value) + =} +} + +reactor Source(n_ports=4) { + output[n_ports] req + input[n_ports] rsp + timer t(0, 1 ms) + + reaction(t) -> req {= + for n in range(self.n_ports): + req[n].set(n) + =} + + reaction(rsp) {= + for n in range(self.n_ports): + print("Received {:d} response: {:d}".format(n, rsp[n].value)) + if rsp[n].value != n: + sys.stderr.write("ERROR: Expected {:d}\n".format(n)) + exit(1) + =} +} + +reactor Selector(n_ports=4, n_dest=2) { + input[n_ports] in_req + output[n_ports] out_rsp + + initial mode DST_1 { + dst1 = new[n_dest] Destination() + + in_req -> dst1.req + dst1.rsp -> out_rsp + reaction(startup) -> reset(DST_2) {= + DST_2.set() + =} + } + + mode DST_2 { + dst2 = new[n_dest] Destination() + + in_req -> dst2.req + dst2.rsp -> out_rsp + } +} + +main reactor { + src = new Source() + sel = new Selector() + + src.req -> sel.in_req + sel.out_rsp -> src.rsp +}