From e65c44b992ac0efe573a4be7f0a09e1cd945366f Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Sat, 24 Sep 2022 15:59:09 -0700 Subject: [PATCH 01/90] add --build-type cli option to lfc --- org.lflang.cli/src/org/lflang/cli/Lfc.java | 1 + org.lflang/src/org/lflang/generator/GeneratorUtils.java | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/org.lflang.cli/src/org/lflang/cli/Lfc.java b/org.lflang.cli/src/org/lflang/cli/Lfc.java index 77bfba5056..927cc24ef4 100644 --- a/org.lflang.cli/src/org/lflang/cli/Lfc.java +++ b/org.lflang.cli/src/org/lflang/cli/Lfc.java @@ -65,6 +65,7 @@ public class Lfc extends CliBase { * @author Marten Lohstroh */ enum CLIOption { + BUILD_TYPE(null, "build-type", true, false, "The build type to use", true), CLEAN("c", "clean", false, false, "Clean before building.", true), COMPILER(null, "target-compiler", true, false, "Target compiler to invoke.", true), EXTERNAL_RUNTIME_PATH(null, "external-runtime-path", true, false, "Specify an external runtime library to be used by the compiled binary.", true), diff --git a/org.lflang/src/org/lflang/generator/GeneratorUtils.java b/org.lflang/src/org/lflang/generator/GeneratorUtils.java index de21ecf281..a554bc1ca8 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorUtils.java +++ b/org.lflang/src/org/lflang/generator/GeneratorUtils.java @@ -23,6 +23,7 @@ import org.lflang.FileConfig; import org.lflang.Target; import org.lflang.TargetConfig; +import org.lflang.TargetProperty.BuildType; import org.lflang.generator.LFGeneratorContext.Mode; import org.lflang.TargetProperty; import org.lflang.TargetProperty.SchedulerOption; @@ -80,6 +81,9 @@ public static void setTargetConfig( if (context.getArgs().containsKey("no-compile")) { targetConfig.noCompile = true; } + if (context.getArgs().containsKey("build-type")) { + targetConfig.cmakeBuildType = BuildType.valueOf(context.getArgs().getProperty("build-type")); + } if (context.getArgs().containsKey("workers")) { targetConfig.workers = Integer.parseInt(context.getArgs().getProperty("workers")); } From a139513370ca1d3a7f7f0bbf34b90c34702c924d Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Sat, 24 Sep 2022 16:00:40 -0700 Subject: [PATCH 02/90] add a test build type --- org.lflang/src/org/lflang/TargetProperty.java | 3 ++- org.lflang/src/org/lflang/generator/GeneratorUtils.java | 2 +- org.lflang/src/org/lflang/generator/rust/RustModel.kt | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/org.lflang/src/org/lflang/TargetProperty.java b/org.lflang/src/org/lflang/TargetProperty.java index 05a1801900..80f53eff53 100644 --- a/org.lflang/src/org/lflang/TargetProperty.java +++ b/org.lflang/src/org/lflang/TargetProperty.java @@ -1000,7 +1000,8 @@ public String toString() { */ public enum BuildType { RELEASE("Release"), - DEBUG("Debug"), + DEBUG("Debug"), + TEST("Test"), REL_WITH_DEB_INFO("RelWithDebInfo"), MIN_SIZE_REL("MinSizeRel"); diff --git a/org.lflang/src/org/lflang/generator/GeneratorUtils.java b/org.lflang/src/org/lflang/generator/GeneratorUtils.java index a554bc1ca8..40bb6ad6b3 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorUtils.java +++ b/org.lflang/src/org/lflang/generator/GeneratorUtils.java @@ -82,7 +82,7 @@ public static void setTargetConfig( targetConfig.noCompile = true; } if (context.getArgs().containsKey("build-type")) { - targetConfig.cmakeBuildType = BuildType.valueOf(context.getArgs().getProperty("build-type")); + targetConfig.cmakeBuildType = BuildType.valueOf(context.getArgs().getProperty("build-type").toUpperCase()); } if (context.getArgs().containsKey("workers")) { targetConfig.workers = Integer.parseInt(context.getArgs().getProperty("workers")); diff --git a/org.lflang/src/org/lflang/generator/rust/RustModel.kt b/org.lflang/src/org/lflang/generator/rust/RustModel.kt index 9de454ddcd..d05ebe03c5 100644 --- a/org.lflang/src/org/lflang/generator/rust/RustModel.kt +++ b/org.lflang/src/org/lflang/generator/rust/RustModel.kt @@ -694,6 +694,7 @@ private val TypeParm.identifier: String val BuildType.cargoProfileName: String get() = when (this) { BuildType.DEBUG -> "debug" + BuildType.TEST -> "test" BuildType.RELEASE -> "release" BuildType.REL_WITH_DEB_INFO -> "release-with-debug-info" BuildType.MIN_SIZE_REL -> "release-with-min-size" From 4d263704ce2382e5484b1e777ab9f29abbbb395f Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Sat, 24 Sep 2022 18:02:16 -0700 Subject: [PATCH 03/90] generate coverage information for Test builds in C++ --- .../generator/cpp/CppStandaloneCmakeGenerator.kt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/org.lflang/src/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt index af8aa4cd03..d20c8ac874 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt @@ -46,6 +46,17 @@ class CppStandaloneCmakeGenerator(private val targetConfig: TargetConfig, privat |cmake_minimum_required(VERSION 3.5) |project($projectName VERSION 0.0.0 LANGUAGES CXX) | + |# The Test build type is the Debug type plus coverage generation + |if(CMAKE_BUILD_TYPE STREQUAL "Test") + | set(CMAKE_BUILD_TYPE "Debug") + | + | if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + | set(CMAKE_CXX_FLAGS "$S{CMAKE_CXX_FLAGS} --coverage -fprofile-arcs -ftest-coverage") + | else() + | message("Not producing code coverage information since the selected compiler is no gcc") + | endif() + |endif() + | |# require C++ 17 |set(CMAKE_CXX_STANDARD 17 CACHE STRING "The C++ standard is cached for visibility in external tools." FORCE) |set(CMAKE_CXX_STANDARD_REQUIRED ON) From bc2f184144bd151a9f85fd2d77303e4cd5ebace3 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Sat, 24 Sep 2022 18:02:39 -0700 Subject: [PATCH 04/90] make all tests run with the "Test" build type --- org.lflang.tests/src/org/lflang/tests/TestBase.java | 1 + .../src/org/lflang/tests/runtime/RustTest.java | 12 ------------ 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/org.lflang.tests/src/org/lflang/tests/TestBase.java b/org.lflang.tests/src/org/lflang/tests/TestBase.java index fe1b1f26a6..4da730289e 100644 --- a/org.lflang.tests/src/org/lflang/tests/TestBase.java +++ b/org.lflang.tests/src/org/lflang/tests/TestBase.java @@ -444,6 +444,7 @@ private void validate(LFTest test, IGeneratorContext context) { * Override to add some LFC arguments to all runs of this test class. */ protected void addExtraLfcArgs(Properties args) { + args.setProperty("build-type", "Test"); // to be overridden } diff --git a/org.lflang.tests/src/org/lflang/tests/runtime/RustTest.java b/org.lflang.tests/src/org/lflang/tests/runtime/RustTest.java index a064a8e8d2..2601fe79d0 100644 --- a/org.lflang.tests/src/org/lflang/tests/runtime/RustTest.java +++ b/org.lflang.tests/src/org/lflang/tests/runtime/RustTest.java @@ -38,18 +38,6 @@ public RustTest() { super(Target.Rust); } - - @Override - protected void addExtraLfcArgs(Properties args) { - // Set this environment variable if you develop the crate locally, - // it's more convenient. You'll have to delete test/Rust/src-gen/* - // to make a change (and checkout the Cargo.toml back). - String path = System.getenv("LOCAL_RUST_REACTOR_RT"); - if (path != null) { - args.setProperty("external-runtime-path", path); - } - } - @Override protected boolean supportsGenericTypes() { return true; From fe64ecdc52869ae929f8e85fefbcf57e32b4f3f9 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Sat, 24 Sep 2022 18:10:41 -0700 Subject: [PATCH 05/90] configure CI to generate a coverage report for reactor-cpp --- .github/workflows/ci.yml | 2 +- .github/workflows/cpp-tests.yml | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 62eea2ce5e..0e1b746e49 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -74,7 +74,7 @@ jobs: # Run the C++ integration tests. cpp-tests: - uses: lf-lang/lingua-franca/.github/workflows/cpp-tests.yml@master + uses: lf-lang/lingua-franca/.github/workflows/cpp-tests.yml@cpp-code-cov needs: cancel # Run the C++ integration tests on ROS2. diff --git a/.github/workflows/cpp-tests.yml b/.github/workflows/cpp-tests.yml index 08bcf52243..c3919528f6 100644 --- a/.github/workflows/cpp-tests.yml +++ b/.github/workflows/cpp-tests.yml @@ -27,10 +27,11 @@ jobs: - name: Install Dependencies OS X run: brew install coreutils if: runner.os == 'macOS' - - name: Install clang-tidy on Ubuntu + - name: Install clang-tidy and lcov on Ubuntu run: | sudo apt-get update sudo apt-get install -y clang-tidy + sudo apt-get install -y lcov if: matrix.platform == 'ubuntu-latest' - name: Check out specific ref of reactor-cpp uses: actions/checkout@v2 @@ -49,3 +50,15 @@ jobs: fail_ci_if_error: false verbose: true if: ${{ !inputs.runtime-ref }} # i.e., if this is part of the main repo's CI + - name: Collect reactor-cpp coverage data + run: | + lcov --capture --directory test/Cpp/build/reactor-cpp-default --output-file coverage.info + lcov --extract coverage.info ${GITHUB_WORKSPACE}/test/Cpp/src-gen/reactor-cpp-default/\* --output-file reactor-cpp.info + genhtml reactor-cpp.info --output-directory reactor-cpp.coverage + if: matrix.platform == 'ubuntu-latest' + - name: Upload coverage report + uses: actions/upload-artifact@v3 + with: + name: reactor-cpp.coverage + path: reactor-cpp.coverage + if: matrix.platform == 'ubuntu-latest' From 40a6333ed83bbe7018ac7423cdd6af23cb8f1976 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Sun, 25 Sep 2022 14:03:44 -0700 Subject: [PATCH 06/90] generate code coverage for C --- .../src/org/lflang/generator/c/CCmakeCompiler.java | 1 + .../src/org/lflang/generator/c/CCmakeGenerator.java | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/org.lflang/src/org/lflang/generator/c/CCmakeCompiler.java b/org.lflang/src/org/lflang/generator/c/CCmakeCompiler.java index 69347ff84e..4fcd1044d7 100644 --- a/org.lflang/src/org/lflang/generator/c/CCmakeCompiler.java +++ b/org.lflang/src/org/lflang/generator/c/CCmakeCompiler.java @@ -190,6 +190,7 @@ public LFCommand compileCmakeCommand( Path buildPath = fileConfig.getSrcGenPath().resolve("build"); List arguments = new ArrayList<>(List.of( + "-DCMAKE_BUILD_TYPE=" + ((targetConfig.cmakeBuildType!=null) ? targetConfig.cmakeBuildType.toString() : "Release"), "-DCMAKE_INSTALL_PREFIX=" + FileUtil.toUnixString(fileConfig.getOutPath()), "-DCMAKE_INSTALL_BINDIR=" + FileUtil.toUnixString( fileConfig.getOutPath().relativize( diff --git a/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java b/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java index e1eb841fb4..7e4a142b91 100644 --- a/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java @@ -92,8 +92,18 @@ CodeBuilder generateCMakeCode( cMakeCode.pr("cmake_minimum_required(VERSION 3.13)"); cMakeCode.pr("project("+executableName+" LANGUAGES C)"); - cMakeCode.newLine(); + + // The Test build type is the Debug type plus coverage generation + cMakeCode.pr("if(CMAKE_BUILD_TYPE STREQUAL \"Test\")"); + cMakeCode.pr(" set(CMAKE_BUILD_TYPE \"Debug\")"); + cMakeCode.pr(" if(CMAKE_C_COMPILER_ID STREQUAL \"GNU\")"); + cMakeCode.pr(" set(CMAKE_C_FLAGS \"${CMAKE_CXX_FLAGS} --coverage -fprofile-arcs -ftest-coverage\")"); + cMakeCode.pr(" else()"); + cMakeCode.pr(" message(\"Not producing code coverage information since the selected compiler is no gcc\")"); + cMakeCode.pr(" endif()"); + cMakeCode.pr("endif()"); + cMakeCode.pr("# Require C11"); cMakeCode.pr("set(CMAKE_C_STANDARD 11)"); cMakeCode.pr("set(CMAKE_C_STANDARD_REQUIRED ON)"); From adcaa099e82e671ad497b41c7e21e3651372b4af Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Sun, 25 Sep 2022 16:45:49 -0700 Subject: [PATCH 07/90] avoid using an unsupported cmake profile --- .../org/lflang/generator/c/CCmakeCompiler.java | 18 +++++++++++++++++- .../generator/cpp/CppStandaloneGenerator.kt | 9 ++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/c/CCmakeCompiler.java b/org.lflang/src/org/lflang/generator/c/CCmakeCompiler.java index 4fcd1044d7..16f01e8dc4 100644 --- a/org.lflang/src/org/lflang/generator/c/CCmakeCompiler.java +++ b/org.lflang/src/org/lflang/generator/c/CCmakeCompiler.java @@ -36,6 +36,7 @@ import org.lflang.ErrorReporter; import org.lflang.FileConfig; import org.lflang.TargetConfig; +import org.lflang.TargetProperty; import org.lflang.generator.GeneratorBase; import org.lflang.generator.GeneratorUtils; import org.lflang.generator.LFGeneratorContext; @@ -223,6 +224,21 @@ public LFCommand compileCmakeCommand( } + /** + * Return the cmake config name correspnding to a given build type. + */ + private String buildTypeToCmakeConfig(TargetProperty.BuildType type) { + if (type == null) { + return "Release"; + } + switch (type) { + case TEST: + return "Debug"; + default: + return type.toString(); + } + } + /** * Return a command to build the specified C file using CMake. * This produces a C-specific build command. @@ -244,7 +260,7 @@ public LFCommand buildCmakeCommand( LFCommand command = commandFactory.createCommand( "cmake", List.of( "--build", ".", "--target", "install", "--parallel", cores, "--config", - ((targetConfig.cmakeBuildType!=null) ? targetConfig.cmakeBuildType.toString() : "Release") + buildTypeToCmakeConfig(targetConfig.cmakeBuildType) ), buildPath); if (command == null) { diff --git a/org.lflang/src/org/lflang/generator/cpp/CppStandaloneGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppStandaloneGenerator.kt index f81db86228..c6899f5388 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppStandaloneGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppStandaloneGenerator.kt @@ -1,5 +1,6 @@ package org.lflang.generator.cpp +import org.lflang.TargetProperty import org.lflang.generator.CodeMap import org.lflang.generator.LFGeneratorContext import org.lflang.toUnixString @@ -122,6 +123,12 @@ class CppStandaloneGenerator(generator: CppGenerator) : return 0 } + private fun buildTypeToCmakeConfig(type: TargetProperty.BuildType?) = when (type) { + null -> "Release" + TargetProperty.BuildType.TEST -> "Debug" + else -> type.toString() + } + private fun createMakeCommand(buildPath: Path, version: String, target: String): LFCommand { val makeArgs: List if (version.compareVersion("3.12.0") < 0) { @@ -138,7 +145,7 @@ class CppStandaloneGenerator(generator: CppGenerator) : "--parallel", cores.toString(), "--config", - targetConfig.cmakeBuildType?.toString() ?: "Release" + buildTypeToCmakeConfig(targetConfig.cmakeBuildType) ) } From 28b46e15e40a2996deb6269b513cd9a4f0b13e92 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Sun, 25 Sep 2022 16:46:59 -0700 Subject: [PATCH 08/90] only produce coverage information if lcov is installed --- .../src/org/lflang/generator/c/CCmakeGenerator.java | 9 +++++++-- .../lflang/generator/cpp/CppStandaloneCmakeGenerator.kt | 7 ++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java b/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java index 7e4a142b91..5a41ac52ec 100644 --- a/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java @@ -98,9 +98,14 @@ CodeBuilder generateCMakeCode( cMakeCode.pr("if(CMAKE_BUILD_TYPE STREQUAL \"Test\")"); cMakeCode.pr(" set(CMAKE_BUILD_TYPE \"Debug\")"); cMakeCode.pr(" if(CMAKE_C_COMPILER_ID STREQUAL \"GNU\")"); - cMakeCode.pr(" set(CMAKE_C_FLAGS \"${CMAKE_CXX_FLAGS} --coverage -fprofile-arcs -ftest-coverage\")"); + cMakeCode.pr(" find_program(LCOV_BIN lcov)"); + cMakeCode.pr(" if(LCOV_BIN MATCHES \"lcov$\")"); + cMakeCode.pr(" set(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} --coverage -fprofile-arcs -ftest-coverage\")"); + cMakeCode.pr(" else()"); + cMakeCode.pr(" message(\"Not producing code coverage information since lcov was not found\")"); + cMakeCode.pr(" endif()"); cMakeCode.pr(" else()"); - cMakeCode.pr(" message(\"Not producing code coverage information since the selected compiler is no gcc\")"); + cMakeCode.pr(" message(\"Not producing code coverage information since the selected compiler is no gcc\")"); cMakeCode.pr(" endif()"); cMakeCode.pr("endif()"); diff --git a/org.lflang/src/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt index d20c8ac874..729201cf46 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt @@ -51,7 +51,12 @@ class CppStandaloneCmakeGenerator(private val targetConfig: TargetConfig, privat | set(CMAKE_BUILD_TYPE "Debug") | | if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - | set(CMAKE_CXX_FLAGS "$S{CMAKE_CXX_FLAGS} --coverage -fprofile-arcs -ftest-coverage") + | find_program(LCOV_BIN lcov) + | if(LCOV_BIN MATCHES "lcov$S") + | set(CMAKE_CXX_FLAGS "$S{CMAKE_CXX_FLAGS} --coverage -fprofile-arcs -ftest-coverage") + | else() + | message("Not producing code coverage information since lcov was not found") + | endif() | else() | message("Not producing code coverage information since the selected compiler is no gcc") | endif() From 791655d317459f00775624ee76af16ccad279abe Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Sun, 25 Sep 2022 23:11:17 -0700 Subject: [PATCH 09/90] collect coverage data from all generated sources --- .github/workflows/cpp-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cpp-tests.yml b/.github/workflows/cpp-tests.yml index 08fac0d1ba..6b550c41d7 100644 --- a/.github/workflows/cpp-tests.yml +++ b/.github/workflows/cpp-tests.yml @@ -53,7 +53,7 @@ jobs: if: ${{ !inputs.runtime-ref }} # i.e., if this is part of the main repo's CI - name: Collect reactor-cpp coverage data run: | - lcov --capture --directory test/Cpp/build/reactor-cpp-default --output-file coverage.info + lcov --capture --directory test/Cpp --output-file coverage.info lcov --extract coverage.info ${GITHUB_WORKSPACE}/test/Cpp/src-gen/reactor-cpp-default/\* --output-file reactor-cpp.info genhtml reactor-cpp.info --output-directory reactor-cpp.coverage if: matrix.platform == 'ubuntu-latest' From f7edcfe7cfdb27c7d8e20f2a787e3584710910cf Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Fri, 30 Sep 2022 11:53:15 -0700 Subject: [PATCH 10/90] update reactor-cpp --- org.lflang/src/lib/cpp/reactor-cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/cpp/reactor-cpp b/org.lflang/src/lib/cpp/reactor-cpp index 39d53bc613..8e04e2a15e 160000 --- a/org.lflang/src/lib/cpp/reactor-cpp +++ b/org.lflang/src/lib/cpp/reactor-cpp @@ -1 +1 @@ -Subproject commit 39d53bc613872f0dd86ea198ed8365f5c86e3def +Subproject commit 8e04e2a15e94343d005270a9c5dfa5007797336e From f4da6a94415e399ecf89986f69190a91ded58bf6 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Fri, 30 Sep 2022 16:15:36 -0700 Subject: [PATCH 11/90] fix syntax after merge --- org.lflang/src/org/lflang/generator/GeneratorUtils.java | 1 + 1 file changed, 1 insertion(+) diff --git a/org.lflang/src/org/lflang/generator/GeneratorUtils.java b/org.lflang/src/org/lflang/generator/GeneratorUtils.java index 854f9de1be..2b87eecf74 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorUtils.java +++ b/org.lflang/src/org/lflang/generator/GeneratorUtils.java @@ -84,6 +84,7 @@ public static void setTargetConfig( } if (context.getArgs().containsKey("build-type")) { targetConfig.cmakeBuildType = BuildType.valueOf(context.getArgs().getProperty("build-type").toUpperCase()); + } if (context.getArgs().containsKey("logging")) { targetConfig.logLevel = LogLevel.valueOf(context.getArgs().getProperty("logging").toUpperCase()); } From 548a310334c83fe5bcf2a91781813f02fb587a78 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Fri, 30 Sep 2022 17:06:16 -0700 Subject: [PATCH 12/90] Upload C++ coverage information to codecov --- .github/workflows/cpp-tests.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cpp-tests.yml b/.github/workflows/cpp-tests.yml index 6b550c41d7..7a8fdc11ef 100644 --- a/.github/workflows/cpp-tests.yml +++ b/.github/workflows/cpp-tests.yml @@ -44,7 +44,7 @@ jobs: - name: Run C++ tests; run: | ./gradlew test --tests org.lflang.tests.runtime.CppTest.* - - name: Report to CodeCov + - name: Report Java coverage to CodeCov uses: codecov/codecov-action@v2.1.0 with: file: org.lflang.tests/build/reports/xml/jacoco @@ -63,3 +63,10 @@ jobs: name: reactor-cpp.coverage path: reactor-cpp.coverage if: matrix.platform == 'ubuntu-latest' + - name: Report C++ coverage to CodeCov + uses: codecov/codecov-action@v2.1.0 + with: + file: coverage.info + fail_ci_if_error: false + verbose: true + if: matrix.platform == 'ubuntu-latest' From 167600ad50603ffe8d8be9a08f38ed668dd9c760 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Fri, 30 Sep 2022 17:39:11 -0700 Subject: [PATCH 13/90] fix file name --- .github/workflows/cpp-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cpp-tests.yml b/.github/workflows/cpp-tests.yml index 7a8fdc11ef..77b1049925 100644 --- a/.github/workflows/cpp-tests.yml +++ b/.github/workflows/cpp-tests.yml @@ -66,7 +66,7 @@ jobs: - name: Report C++ coverage to CodeCov uses: codecov/codecov-action@v2.1.0 with: - file: coverage.info + file: reactor-cpp.info fail_ci_if_error: false verbose: true if: matrix.platform == 'ubuntu-latest' From 12c7b3cfea305a5e7b4f29d506b42737e7b3a6d9 Mon Sep 17 00:00:00 2001 From: Erling Rennemo Jellum Date: Wed, 12 Oct 2022 16:53:57 -0700 Subject: [PATCH 14/90] C: Add compiler flag to distinguish between multi-threaded and single-threaded runtime --- .../src/org/lflang/generator/c/CCmakeGenerator.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java b/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java index 5a6e574955..d53b10f7aa 100644 --- a/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java @@ -160,10 +160,21 @@ CodeBuilder generateCMakeCode( // If the LF program itself is threaded or if tracing is enabled, we need to define // NUMBER_OF_WORKERS so that platform-specific C files will contain the appropriate functions - cMakeCode.pr("# Set the number of workers to enable threading"); + cMakeCode.pr("# Set the number of workers to enable threading/tracing"); cMakeCode.pr("target_compile_definitions( ${LF_MAIN_TARGET} PUBLIC NUMBER_OF_WORKERS="+targetConfig.workers+")"); + cMakeCode.newLine(); } + + // Add additional flags so runtime can distinguish between multi-threaded and single-threaded mode + if (targetConfig.threading) { + cMakeCode.pr("# Set flag to indicate a multi-threaded runtime"); + cMakeCode.pr("target_compile_definitions( ${LF_MAIN_TARGET} PUBLIC LF_MULTI_THREADED)"); + } else { + cMakeCode.pr("# Set flag to indicate a single-threaded runtime"); + cMakeCode.pr("target_compile_definitions( ${LF_MAIN_TARGET} PUBLIC LF_SINGLE_THREADED)"); + } + cMakeCode.newLine(); cMakeCode.pr("# Target definitions\n"); targetConfig.compileDefinitions.forEach( (key, value) -> { From 9b21c9435d367a2bf016e699dcb9107b7f738bc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 21 Sep 2022 15:21:58 +0200 Subject: [PATCH 15/90] Bump rust runtime Support lf-lang/reactor-rs#29 --- org.lflang/src/lib/rs/reactor-rs | 2 +- .../generator/rust/RustReactorEmitter.kt | 28 ++++++------------- test/Rust/src/CompositionWithPorts.lf | 2 +- .../multiport/ConnectionToSelfMultiport.lf | 2 +- test/Rust/src/multiport/CycledLhs_SelfLoop.lf | 2 +- test/Rust/src/multiport/CycledLhs_Single.lf | 12 ++++---- test/Rust/src/multiport/FullyConnected.lf | 2 +- .../multiport/FullyConnectedAddressable.lf | 4 +-- test/Rust/src/multiport/MultiportFromBank.lf | 4 +-- .../src/multiport/MultiportFromHierarchy.lf | 6 ++-- test/Rust/src/multiport/MultiportIn.lf | 2 +- test/Rust/src/multiport/MultiportOut.lf | 4 +-- .../src/multiport/MultiportToMultiport.lf | 4 +-- .../src/multiport/MultiportToMultiport2.lf | 12 ++++---- 14 files changed, 36 insertions(+), 50 deletions(-) diff --git a/org.lflang/src/lib/rs/reactor-rs b/org.lflang/src/lib/rs/reactor-rs index 868c834f0d..530fec8f91 160000 --- a/org.lflang/src/lib/rs/reactor-rs +++ b/org.lflang/src/lib/rs/reactor-rs @@ -1 +1 @@ -Subproject commit 868c834f0de46b2cd633892ccafb7768357d3bd9 +Subproject commit 530fec8f911ad9a03297b1c17e4faeb339979b1c diff --git a/org.lflang/src/org/lflang/generator/rust/RustReactorEmitter.kt b/org.lflang/src/org/lflang/generator/rust/RustReactorEmitter.kt index ab21972bd1..7526fdc5e1 100644 --- a/org.lflang/src/org/lflang/generator/rust/RustReactorEmitter.kt +++ b/org.lflang/src/org/lflang/generator/rust/RustReactorEmitter.kt @@ -382,15 +382,9 @@ ${" | "..declareChildConnections()} /** The type of the parameter injected into a reaction for the given dependency. */ private fun ReactorComponent.toBorrowedType(kind: DepKind): TargetCode = - when (this) { - is PortLike -> when { - kind == DepKind.Effects && isMultiport -> "$rsRuntime::WritablePortBank<$dataType>" // note: owned - kind == DepKind.Effects -> "$rsRuntime::WritablePort<$dataType>" // note: owned - isMultiport -> "$rsRuntime::ReadablePortBank<$dataType>" // note: owned - else -> "&$rsRuntime::ReadablePort<$dataType>" // note: a reference - } - is TimerData -> "&${toType()}" - is ActionData -> if (kind == DepKind.Effects) "&mut ${toType()}" else "&${toType()}" + when (kind) { + DepKind.Effects -> "&mut ${toType()}" + else -> "&${toType()}" } /** @@ -398,15 +392,9 @@ ${" | "..declareChildConnections()} * into a reaction. This conceptually just borrows the field. */ private fun ReactorComponent.toBorrow(kind: DepKind): TargetCode = - when (this) { - is PortLike -> when { - kind == DepKind.Effects && isMultiport -> "$rsRuntime::WritablePortBank::new(&mut self.$rustFieldName)" // note: owned - kind == DepKind.Effects -> "$rsRuntime::WritablePort::new(&mut self.$rustFieldName)" // note: owned - isMultiport -> "$rsRuntime::ReadablePortBank::new(&self.$rustFieldName)" // note: owned - else -> "&$rsRuntime::ReadablePort::new(&self.$rustFieldName)" // note: a reference - } - is ActionData -> if (kind == DepKind.Effects) "&mut self.$rustFieldName" else "&self.$rustFieldName" - is TimerData -> "&self.$rustFieldName" + when (kind) { + DepKind.Effects -> "&mut self.$rustFieldName" + else -> "&self.$rustFieldName" } private fun ReactorComponent.isNotInjectedInReaction(depKind: DepKind, n: ReactionInfo): Boolean = @@ -419,11 +407,11 @@ ${" | "..declareChildConnections()} depKind != DepKind.Effects && this in n.effects private fun ReactorComponent.isInjectedAsMut(depKind: DepKind): Boolean = - depKind == DepKind.Effects && (this is PortData || this is ActionData) + depKind == DepKind.Effects && this is ActionData // todo where is this useful? The action already has &mut /** * Whether this component may be unused in a reaction. - * Eg. actions on which we have just a trigger dependency + * E.g. actions on which we have just a trigger dependency * are fine to ignore. */ private fun ReactorComponent.mayBeUnusedInReaction(depKind: DepKind): Boolean = diff --git a/test/Rust/src/CompositionWithPorts.lf b/test/Rust/src/CompositionWithPorts.lf index 90e33db180..d91e8f3370 100644 --- a/test/Rust/src/CompositionWithPorts.lf +++ b/test/Rust/src/CompositionWithPorts.lf @@ -3,7 +3,7 @@ target Rust reactor Source { output out: i32 - reaction(startup) -> out {= ctx.set(&mut out, 76600) =} + reaction(startup) -> out {= ctx.set(out, 76600) =} } reactor Sink { diff --git a/test/Rust/src/multiport/ConnectionToSelfMultiport.lf b/test/Rust/src/multiport/ConnectionToSelfMultiport.lf index 3ec52163a1..3618f7c881 100644 --- a/test/Rust/src/multiport/ConnectionToSelfMultiport.lf +++ b/test/Rust/src/multiport/ConnectionToSelfMultiport.lf @@ -13,7 +13,7 @@ reactor Node(num_nodes: usize(4)) { =} reaction(in) {= - let count = r#in.into_iter().filter(|p| ctx.is_present(p)).count(); + let count = r#in.iter().filter(|p| ctx.is_present(p)).count(); assert_eq!(count, self.num_nodes); println!("success") =} diff --git a/test/Rust/src/multiport/CycledLhs_SelfLoop.lf b/test/Rust/src/multiport/CycledLhs_SelfLoop.lf index c377e4ba31..dd6724df39 100644 --- a/test/Rust/src/multiport/CycledLhs_SelfLoop.lf +++ b/test/Rust/src/multiport/CycledLhs_SelfLoop.lf @@ -17,7 +17,7 @@ reactor Test { reaction(act) -> out {= ctx.set_opt(out, ctx.get(act)); =} reaction(in) -> act {= - let sum: u32 = r#in.into_iter().map(|p| ctx.get(&p).unwrap()).sum(); + let sum: u32 = r#in.iter().map(|p| ctx.get(p).unwrap()).sum(); println!("Sum of received: {}", sum); assert_eq!(self.last * 2, sum); self.last = sum; diff --git a/test/Rust/src/multiport/CycledLhs_Single.lf b/test/Rust/src/multiport/CycledLhs_Single.lf index 8ff2760220..dddf53dd47 100644 --- a/test/Rust/src/multiport/CycledLhs_Single.lf +++ b/test/Rust/src/multiport/CycledLhs_Single.lf @@ -16,18 +16,18 @@ reactor Test { reaction(act) -> out {= let (a, b) = ctx.get(act).unwrap(); - ctx.set(out.get(0), a); - ctx.set(out.get(1), b); + ctx.set(out[0], a); + ctx.set(out[1], b); =} reaction(in) -> act {= // compute the nth fibonacci number // r#in is [fib(n-2), fib(n-1), fib(n-2), fib(n-1)] - let fib_n_1 = ctx.get(&r#in.get(1)).unwrap(); - let fib_n_2 = ctx.get(&r#in.get(0)).unwrap(); + let fib_n_1 = ctx.get(&r#in[1]).unwrap(); + let fib_n_2 = ctx.get(&r#in[0]).unwrap(); let fib_n: u32 = fib_n_2 + fib_n_1; - assert_eq!(ctx.get(&r#in.get(2)).unwrap(), fib_n_2); - assert_eq!(ctx.get(&r#in.get(3)).unwrap(), fib_n_1); + assert_eq!(ctx.get(&r#in[2]).unwrap(), fib_n_2); + assert_eq!(ctx.get(&r#in[3]).unwrap(), fib_n_1); println!("{} + {} = {}", fib_n_2, fib_n_1, fib_n); self.last = fib_n; // send fib(n-1) and fib(n) diff --git a/test/Rust/src/multiport/FullyConnected.lf b/test/Rust/src/multiport/FullyConnected.lf index 58122779ca..06dee6a309 100644 --- a/test/Rust/src/multiport/FullyConnected.lf +++ b/test/Rust/src/multiport/FullyConnected.lf @@ -13,7 +13,7 @@ reactor Right(bank_index: usize(0), num_nodes: usize(4)) { state num_nodes(num_nodes) reaction(in) {= - let count = r#in.into_iter().filter(|p| ctx.is_present(p)).count(); + let count = r#in.iter().filter(|p| ctx.is_present(p)).count(); assert_eq!(count, self.num_nodes); println!("success") =} diff --git a/test/Rust/src/multiport/FullyConnectedAddressable.lf b/test/Rust/src/multiport/FullyConnectedAddressable.lf index 8664f9bca5..63aed7b840 100644 --- a/test/Rust/src/multiport/FullyConnectedAddressable.lf +++ b/test/Rust/src/multiport/FullyConnectedAddressable.lf @@ -13,7 +13,7 @@ reactor Node(bank_index: usize(0), num_nodes: usize(4)) { reaction(startup) -> out {= println!("Hello from node {}!", self.bank_index); // send my ID only to my right neighbour - ctx.set(out.get((self.bank_index + 1) % self.num_nodes), self.bank_index); + ctx.set(&mut out[(self.bank_index + 1) % self.num_nodes], self.bank_index); =} reaction(inpt) {= @@ -22,7 +22,7 @@ reactor Node(bank_index: usize(0), num_nodes: usize(4)) { let mut count = 0; let mut result = 0; for port in inpt { - if let Some(v) = ctx.get(&port) { + if let Some(v) = ctx.get(port) { count += 1; result = v; print!("{}, ", result); diff --git a/test/Rust/src/multiport/MultiportFromBank.lf b/test/Rust/src/multiport/MultiportFromBank.lf index 4d2370d914..f17a4ffa8d 100644 --- a/test/Rust/src/multiport/MultiportFromBank.lf +++ b/test/Rust/src/multiport/MultiportFromBank.lf @@ -14,8 +14,8 @@ reactor Destination(port_width: usize(2)) { input[port_width] in: usize reaction(in) {= - for (i, port) in r#in.into_iter().enumerate() { - assert_eq!(Some(i), ctx.get(&port), "Failed for input in[{}]", i); + for (i, port) in r#in.enumerate_set() { + assert_eq!(Some(i), ctx.get(port), "Failed for input in[{}]", i); } println!("Success"); =} diff --git a/test/Rust/src/multiport/MultiportFromHierarchy.lf b/test/Rust/src/multiport/MultiportFromHierarchy.lf index 632b7c460f..92238bd8e0 100644 --- a/test/Rust/src/multiport/MultiportFromHierarchy.lf +++ b/test/Rust/src/multiport/MultiportFromHierarchy.lf @@ -10,8 +10,8 @@ reactor Source { state s: u32(0) reaction(t) -> out {= - for mut chan in out { - ctx.set(&mut chan, self.s); + for chan in out { + ctx.set(chan, self.s); self.s += 1; } =} @@ -22,7 +22,7 @@ reactor Destination { input[4] in: u32 reaction(in) {= - let sum: u32 = r#in.into_iter().map(|p| ctx.get(&p).unwrap()).sum(); + let sum: u32 = r#in.iter().map(|p| ctx.get(p).unwrap()).sum(); println!("Sum of received: {}", sum); assert_eq!(sum, self.s); self.s += 16; diff --git a/test/Rust/src/multiport/MultiportIn.lf b/test/Rust/src/multiport/MultiportIn.lf index 309c3821d1..fd884acbaa 100644 --- a/test/Rust/src/multiport/MultiportIn.lf +++ b/test/Rust/src/multiport/MultiportIn.lf @@ -30,7 +30,7 @@ reactor Destination { input[4] in: u32 reaction(in) {= - let sum: u32 = r#in.into_iter().map(|p| ctx.get(&p).unwrap()).sum(); + let sum: u32 = r#in.enumerate_values(ctx).map(|a| a.1).sum(); println!("Sum of received: {}", sum); assert_eq!(sum, self.s); self.s += 4; diff --git a/test/Rust/src/multiport/MultiportOut.lf b/test/Rust/src/multiport/MultiportOut.lf index 00ab0bf3a2..010b3a9603 100644 --- a/test/Rust/src/multiport/MultiportOut.lf +++ b/test/Rust/src/multiport/MultiportOut.lf @@ -10,7 +10,7 @@ reactor Source { reaction(t) -> out {= for i in 0..out.len() { - ctx.set(out.get(i), self.s); + ctx.set(&mut out[i], self.s); } self.s += 1; =} @@ -36,7 +36,7 @@ reactor Destination { reaction(in) {= let mut sum = 0; for channel in r#in { - if let Some(ci) = ctx.get(&channel) { + if let Some(ci) = ctx.get(channel) { sum += ci; } } diff --git a/test/Rust/src/multiport/MultiportToMultiport.lf b/test/Rust/src/multiport/MultiportToMultiport.lf index 1ca42c44fa..d1325ba0e9 100644 --- a/test/Rust/src/multiport/MultiportToMultiport.lf +++ b/test/Rust/src/multiport/MultiportToMultiport.lf @@ -15,8 +15,8 @@ reactor Sink { input[4] in: usize reaction(in) {= - for (i, port) in r#in.into_iter().enumerate() { - assert_eq!(Some(i), ctx.get(&port), "Failed for input in[{}]", i); + for (i, port) in r#in.iter().enumerate() { + assert_eq!(Some(i), ctx.get(port), "Failed for input in[{}]", i); } println!("Success"); =} diff --git a/test/Rust/src/multiport/MultiportToMultiport2.lf b/test/Rust/src/multiport/MultiportToMultiport2.lf index 334f559ee6..0f9563556f 100644 --- a/test/Rust/src/multiport/MultiportToMultiport2.lf +++ b/test/Rust/src/multiport/MultiportToMultiport2.lf @@ -6,7 +6,7 @@ reactor Source(width: usize(2)) { output[width] out: usize reaction(startup) -> out {= - for (i, out) in out.into_iter().enumerate() { + for (i, out) in out.iter_mut().enumerate() { ctx.set(out, i) } =} @@ -16,12 +16,10 @@ reactor Destination(width: usize(2)) { input[width] in: usize reaction(in) {= - for (i, port) in r#in.into_iter().enumerate() { - if let Some(v) = ctx.get(&port) { - // NOTE: For testing purposes, this assumes the specific - // widths instantiated below. - assert_eq!(v, i % 3, "Failed for input in[{}]", i); - } + for (i, v) in r#in.enumerate_values(ctx) { + // NOTE: For testing purposes, this assumes the specific + // widths instantiated below. + assert_eq!(v, i % 3, "Failed for input in[{}]", i); } println!("Success"); =} From b921553e16fc9e74bba82de4b1950d99cc87c627 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Tue, 1 Nov 2022 13:19:42 +0100 Subject: [PATCH 16/90] pass build type to cmake in the C target --- org.lflang/src/org/lflang/generator/c/CCompiler.java | 1 + 1 file changed, 1 insertion(+) diff --git a/org.lflang/src/org/lflang/generator/c/CCompiler.java b/org.lflang/src/org/lflang/generator/c/CCompiler.java index 1c0145cd3d..e0b31a5376 100644 --- a/org.lflang/src/org/lflang/generator/c/CCompiler.java +++ b/org.lflang/src/org/lflang/generator/c/CCompiler.java @@ -210,6 +210,7 @@ private static List cmakeOptions(TargetConfig targetConfig, FileConfig f List arguments = new ArrayList<>(); cmakeCompileDefinitions(targetConfig).forEachOrdered(arguments::add); arguments.addAll(List.of( + "-DCMAKE_BUILD_TYPE=" + ((targetConfig.cmakeBuildType!=null) ? targetConfig.cmakeBuildType.toString() : "Release"), "-DCMAKE_INSTALL_PREFIX=" + FileUtil.toUnixString(fileConfig.getOutPath()), "-DCMAKE_INSTALL_BINDIR=" + FileUtil.toUnixString( fileConfig.getOutPath().relativize( From ed21e672ce3e8f39f460d5ea2c5f03d5338003a4 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Tue, 1 Nov 2022 14:33:40 +0100 Subject: [PATCH 17/90] avoid using an unsupported cmake profile --- .../src/org/lflang/generator/c/CCompiler.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/c/CCompiler.java b/org.lflang/src/org/lflang/generator/c/CCompiler.java index e0b31a5376..3765ef1fb7 100644 --- a/org.lflang/src/org/lflang/generator/c/CCompiler.java +++ b/org.lflang/src/org/lflang/generator/c/CCompiler.java @@ -36,6 +36,7 @@ import org.lflang.ErrorReporter; import org.lflang.FileConfig; import org.lflang.TargetConfig; +import org.lflang.TargetProperty; import org.lflang.TargetProperty.Platform; import org.lflang.generator.GeneratorBase; import org.lflang.generator.GeneratorCommandFactory; @@ -232,6 +233,20 @@ private static List cmakeOptions(TargetConfig targetConfig, FileConfig f return arguments; } + /** + * Return the cmake config name correspnding to a given build type. + */ + private String buildTypeToCmakeConfig(TargetProperty.BuildType type) { + if (type == null) { + return "Release"; + } + switch (type) { + case TEST: + return "Debug"; + default: + return type.toString(); + } + } /** * Return a command to build the specified C file using CMake. @@ -247,8 +262,7 @@ public LFCommand buildCmakeCommand() { LFCommand command = commandFactory.createCommand( "cmake", List.of( "--build", ".", "--target", "install", "--parallel", cores, "--config", - targetConfig.cmakeBuildType != null ? - targetConfig.cmakeBuildType.toString() : "Release" + buildTypeToCmakeConfig(targetConfig.cmakeBuildType) ), buildPath); if (command == null) { From 81a770e08577311cba2353adc3636ac973608001 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Tue, 1 Nov 2022 15:27:14 +0100 Subject: [PATCH 18/90] update reactor-cpp --- org.lflang/src/lib/cpp/reactor-cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/cpp/reactor-cpp b/org.lflang/src/lib/cpp/reactor-cpp index b31bd68d66..79422a7b1a 160000 --- a/org.lflang/src/lib/cpp/reactor-cpp +++ b/org.lflang/src/lib/cpp/reactor-cpp @@ -1 +1 @@ -Subproject commit b31bd68d666b65e7496133c9187fed8b3989e91f +Subproject commit 79422a7b1ad246935a0165d971cedfac88904071 From e7b635256f6ec45b939ff3fe67be2f84a696eca5 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Wed, 2 Nov 2022 16:14:20 +0100 Subject: [PATCH 19/90] run the serialization without generating c code coverage --- .../tests/serialization/SerializationTest.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/org.lflang.tests/src/org/lflang/tests/serialization/SerializationTest.java b/org.lflang.tests/src/org/lflang/tests/serialization/SerializationTest.java index ad6c33eb84..c62bc3cf15 100644 --- a/org.lflang.tests/src/org/lflang/tests/serialization/SerializationTest.java +++ b/org.lflang.tests/src/org/lflang/tests/serialization/SerializationTest.java @@ -1,5 +1,7 @@ package org.lflang.tests.serialization; +import java.util.Properties; + import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; import org.lflang.Target; @@ -12,6 +14,17 @@ public class SerializationTest extends TestBase { protected SerializationTest() { super(Target.ALL); } + + + /** + * Override to add some LFC arguments to all runs of this test class. + */ + @Override + protected void addExtraLfcArgs(Properties args) { + super.addExtraLfcArgs(args); + // Use the Debug build type as coverage generation does not work for the serialization tests + args.setProperty("build-type", "Debug"); + } @Test public void runSerializationTestsWithThreadingOff() { From 1394fd5e1dcf2692b376fb6190e1232d5f2df876 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Wed, 2 Nov 2022 20:44:51 +0100 Subject: [PATCH 20/90] Fix TS CI on Windows --- .github/workflows/ts-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ts-tests.yml b/.github/workflows/ts-tests.yml index ddb1848093..de2b2be406 100644 --- a/.github/workflows/ts-tests.yml +++ b/.github/workflows/ts-tests.yml @@ -7,7 +7,7 @@ jobs: run: strategy: matrix: - platform: [ubuntu-latest, macos-latest, windows] + platform: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} steps: - name: Check out lingua-franca repository From 5c7b74730ef122d73ab9380ffcde9ef18acaa432 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Wed, 2 Nov 2022 20:46:04 +0100 Subject: [PATCH 21/90] Update ci.yaml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f339b9aa9e..25c293eb2a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -100,7 +100,7 @@ jobs: # Run the TypeScript integration tests. ts-tests: - uses: lf-lang/lingua-franca/.github/workflows/ts-tests.yml@master + uses: lf-lang/lingua-franca/.github/workflows/ts-tests.yml@fix-ts-ci needs: cancel # Run the serialization tests From edd2fe45fa141ba7ff9015b0d1073284baa6eb69 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Wed, 2 Nov 2022 12:54:47 -0700 Subject: [PATCH 22/90] Update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 25c293eb2a..f339b9aa9e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -100,7 +100,7 @@ jobs: # Run the TypeScript integration tests. ts-tests: - uses: lf-lang/lingua-franca/.github/workflows/ts-tests.yml@fix-ts-ci + uses: lf-lang/lingua-franca/.github/workflows/ts-tests.yml@master needs: cancel # Run the serialization tests From d9604195fb1be5abefd715c8a064b8489dd92500 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Thu, 3 Nov 2022 09:33:11 +0100 Subject: [PATCH 23/90] CI: use Python 3.10 --- .github/workflows/py-tests.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/py-tests.yml b/.github/workflows/py-tests.yml index 66ba232393..6119825e91 100644 --- a/.github/workflows/py-tests.yml +++ b/.github/workflows/py-tests.yml @@ -30,7 +30,9 @@ jobs: - name: Prepare build environment uses: ./.github/actions/prepare-build-env - name: Setup Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 + with: + python-version: '3.10' if: ${{ runner.os == 'Windows' }} - name: Install dependencies OS X run: | From 3a463fb2d3cdcbba3dab3416d51018cfa06e9a8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 2 Nov 2022 15:37:26 +0100 Subject: [PATCH 24/90] Cleanup TS generator --- org.lflang/src/org/lflang/CommonExtensions.kt | 6 + .../lflang/generator/ExpressionGenerator.java | 164 --------- .../org/lflang/generator/GeneratorUtils.kt | 46 ++- .../src/org/lflang/generator/TargetTypes.java | 9 +- .../cpp/CppAssembleMethodGenerator.kt | 5 +- .../generator/cpp/CppInstanceGenerator.kt | 2 +- .../lflang/generator/cpp/CppPortGenerator.kt | 5 +- .../generator/cpp/CppReactionGenerator.kt | 7 +- .../generator/cpp/CppRos2PackageGenerator.kt | 9 +- .../cpp/CppStandaloneCmakeGenerator.kt | 5 +- .../generator/rust/RustCargoTomlEmitter.kt | 3 +- .../org/lflang/generator/rust/RustEmitter.kt | 3 +- .../generator/rust/RustMainFileEmitter.kt | 5 +- .../generator/rust/RustReactorEmitter.kt | 2 +- .../lflang/generator/ts/TSActionGenerator.kt | 23 +- .../generator/ts/TSConstructorGenerator.kt | 57 +--- .../org/lflang/generator/ts/TSExtensions.kt | 38 +-- .../org/lflang/generator/ts/TSGenerator.kt | 21 +- .../generator/ts/TSImportPreambleGenerator.kt | 27 +- .../generator/ts/TSInstanceGenerator.kt | 40 +-- .../generator/ts/TSParameterGenerator.kt | 28 +- .../ts/TSParameterPreambleGenerator.kt | 301 ++++++++--------- .../lflang/generator/ts/TSPortGenerator.kt | 21 +- .../generator/ts/TSReactionGenerator.kt | 313 ++++++++---------- .../lflang/generator/ts/TSReactorGenerator.kt | 12 +- .../lflang/generator/ts/TSStateGenerator.kt | 17 +- .../lflang/generator/ts/TSTimerGenerator.kt | 11 +- .../src/org/lflang/generator/ts/TSTypes.kt | 12 +- 28 files changed, 469 insertions(+), 723 deletions(-) delete mode 100644 org.lflang/src/org/lflang/generator/ExpressionGenerator.java diff --git a/org.lflang/src/org/lflang/CommonExtensions.kt b/org.lflang/src/org/lflang/CommonExtensions.kt index 64e08288d4..f27570b7db 100644 --- a/org.lflang/src/org/lflang/CommonExtensions.kt +++ b/org.lflang/src/org/lflang/CommonExtensions.kt @@ -158,6 +158,12 @@ fun Iterable.joinWithCommasLn( transform: (T) -> CharSequence = { it.toString() } ): String = joinWithCommas(prefix, postfix, skipLines = true, trailing, transform) +fun Iterable.joinWithLn( + prefix: CharSequence = "", + postfix: CharSequence = "", + transform: (T) -> CharSequence +): String = joinToString(separator = "\n", prefix = prefix, postfix = postfix, transform = transform) + /** * Join this list with commas, surrounding it with angled brackets (`<...>`). * If this list is empty, returns an empty string. diff --git a/org.lflang/src/org/lflang/generator/ExpressionGenerator.java b/org.lflang/src/org/lflang/generator/ExpressionGenerator.java deleted file mode 100644 index df89b1b2cf..0000000000 --- a/org.lflang/src/org/lflang/generator/ExpressionGenerator.java +++ /dev/null @@ -1,164 +0,0 @@ -package org.lflang.generator; - -import java.util.List; -import java.util.ArrayList; -import java.util.stream.Collectors; - -import org.lflang.ASTUtils; -import org.lflang.TimeValue; -import org.lflang.lf.Assignment; -import org.lflang.lf.Expression; -import org.lflang.lf.Instantiation; -import org.lflang.lf.Parameter; -import org.lflang.lf.ParameterReference; -import org.lflang.lf.StateVar; -import org.lflang.lf.Time; -import org.lflang.TimeUnit; - -/** - * Encapsulates logic for representing {@code Value}s in a - * target language. - */ -public final class ExpressionGenerator { - - /** - * A {@code TimeInTargetLanguage} is a - * target-language-specific time representation - * strategy. - */ - public interface TimeInTargetLanguage { - String apply(TimeValue t); - } - - /** - * A {@code GetTargetReference} instance is a - * target-language-specific function. It provides the - * target language code that refers to the given - * parameter {@code param}. - */ - public interface GetTargetReference { - String apply(Parameter param); - } - - private final TimeInTargetLanguage timeInTargetLanguage; - private final GetTargetReference getTargetReference; - - /** - * Instantiates a target-language-specific - * ExpressionGenerator parameterized by {@code f}. - * @param f a time representation strategy - */ - public ExpressionGenerator(TimeInTargetLanguage f, GetTargetReference g) { - this.timeInTargetLanguage = f; - this.getTargetReference = g; - } - - /** - * Create a list of state initializers in target code. - * - * @param state The state variable to create initializers for - * @return A list of initializers in target code - */ - public List getInitializerList(StateVar state) { - List list = new ArrayList<>(); - // FIXME: Previously, we returned null if it was not initialized, which would have caused an - // NPE in TSStateGenerator. Is this the desired behavior? - if (!ASTUtils.isInitialized(state)) return list; - for (Expression expr : state.getInit()) { - if (expr instanceof ParameterReference) { - list.add(getTargetReference.apply(((ParameterReference)expr).getParameter())); - } else { - list.add(getTargetValue(expr, ASTUtils.isOfTimeType(state))); - } - } - return list; - } - - /** - * Create a list of default parameter initializers in target code. - * - * @param param The parameter to create initializers for - * @return A list of initializers in target code - */ - public List getInitializerList(Parameter param) { - List list = new ArrayList<>(); - if (param == null) return list; - for (Expression expr : param.getInit()) - list.add(getTargetValue(expr, ASTUtils.isOfTimeType(param))); - return list; - } - - /** - * Create a list of parameter initializers in target code in the context - * of an reactor instantiation. - * - * This respects the parameter assignments given in the reactor - * instantiation and falls back to the reactors default initializers - * if no value is assigned to it. - * - * @param param The parameter to create initializers for - * @return A list of initializers in target code - */ - public List getInitializerList(Parameter param, Instantiation i) { - List assignments = i.getParameters().stream() - .filter(it -> it.getLhs() == param) - .collect(Collectors.toList()); - if (assignments.isEmpty()) // Case 0: The parameter was not overwritten in the instantiation - return getInitializerList(param); - // Case 1: The parameter was overwritten in the instantiation - List list = new ArrayList<>(); - if (assignments.get(0) == null) return list; - for (Expression expr : assignments.get(0).getRhs()) - list.add(getTargetValue(expr, ASTUtils.isOfTimeType(param))); - return list; - } - - /** - * Return the time specified by {@code t}, expressed as - * code that is valid for some target languages. - */ - public String getTargetTime(TimeValue t) { - return timeInTargetLanguage.apply(t); - } - - /** - * Return the time specified by {@code t}, expressed as - * code that is valid for some target languages. - */ - public String getTargetTime(Time t) { - return timeInTargetLanguage.apply(new TimeValue(t.getInterval(), TimeUnit.fromName(t.getUnit()))); - } - - /** - * Return the time specified by {@code v}, expressed as - * code that is valid for some target languages. - */ - public String getTargetTime(Expression expr) { - return getTargetValue(expr, true); - } - - /** - * Get textual representation of an expression in the target language. - * - * If the value evaluates to 0, it is interpreted as a literal. - * - * @param expr A time AST node - * @return A time string in the target language - */ - public String getTargetValue(Expression expr) { - return ASTUtils.toText(expr); - } - - /** - * Get textual representation of an expression in the target language. - * - * @param expr A time AST node - * @param isTime Whether {@code v} is expected to be a time - * @return A time string in the target language - */ - public String getTargetValue(Expression expr, boolean isTime) { - if (expr instanceof Time) return getTargetTime((Time)expr); - if (isTime && ASTUtils.isZero(expr)) return timeInTargetLanguage.apply(TimeValue.ZERO); - return ASTUtils.toText(expr); - } -} diff --git a/org.lflang/src/org/lflang/generator/GeneratorUtils.kt b/org.lflang/src/org/lflang/generator/GeneratorUtils.kt index e124080f82..b64e29913f 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorUtils.kt +++ b/org.lflang/src/org/lflang/generator/GeneratorUtils.kt @@ -2,11 +2,16 @@ package org.lflang.generator import org.eclipse.emf.ecore.EObject import org.eclipse.xtext.nodemodel.util.NodeModelUtils -import org.lflang.ErrorReporter +import org.lflang.InferredType +import org.lflang.isInitWithBraces +import org.lflang.lf.Expression +import org.lflang.lf.Instantiation +import org.lflang.lf.LfFactory +import org.lflang.lf.Parameter +import org.lflang.lf.StateVar import org.lflang.toPath -import org.lflang.toUnixString import org.lflang.toTextTokenBased -import org.lflang.lf.Instantiation +import org.lflang.toUnixString /** A transparent type alias to document when a string contains target code. */ typealias TargetCode = String @@ -42,3 +47,38 @@ fun EObject.locationInfo(): LocationInfo { lfText = toTextTokenBased() ?: "" ) } + + +fun TargetTypes.getTargetInitializer(sv: StateVar): TargetCode = + this.getTargetInitializer(sv.init, sv.type, sv.braces.isNotEmpty()) + +fun TargetTypes.getTargetInitializer(sv: Parameter): TargetCode = + this.getTargetInitializer(sv.init, sv.type, sv.isInitWithBraces) + +/** + * Returns the target code for the [getActualValue] of the + * param for this instantiation. + */ +fun TargetTypes.getTargetInitializer(param: Parameter, inst: Instantiation): TargetCode { + val init = inst.getActualValue(param) + return getTargetInitializer(init, param.type, param.isInitWithBraces) +} + +/** + * Returns the actual value of a parameter for the given instantiation. + * The value is defaulted to the default value for the parameter if + * there is no explicit assignment. If there is no default value, the + * source code is invalid (param is required) + */ +fun Instantiation.getActualValue(param: Parameter): List = + parameters.firstOrNull { it.lhs == param }?.rhs + ?: param.init + ?: throw InvalidLfSourceException(this, "No value for parameter ${param.name}") + + +fun TargetTypes.getTargetTimeExpr(v: Expression): TargetCode = + this.getTargetExpr(v, InferredType.time()) + +/** If this is null, returns the literal 0. */ +fun Expression?.orZero(): Expression = + this ?: LfFactory.eINSTANCE.createLiteral().apply { literal = "0" } diff --git a/org.lflang/src/org/lflang/generator/TargetTypes.java b/org.lflang/src/org/lflang/generator/TargetTypes.java index 8e72c2a3f0..38d918d72a 100644 --- a/org.lflang/src/org/lflang/generator/TargetTypes.java +++ b/org.lflang/src/org/lflang/generator/TargetTypes.java @@ -6,7 +6,6 @@ import org.lflang.ASTUtils; import org.lflang.InferredType; -import org.lflang.TimeUnit; import org.lflang.TimeValue; import org.lflang.lf.Action; import org.lflang.lf.Code; @@ -64,6 +63,10 @@ public interface TargetTypes { String getTargetVariableSizeListType(String baseType); + default String getTargetParamRef(ParameterReference expr, InferredType typeOrNull) { + return escapeIdentifier(expr.getParameter().getName()); + } + /** * Return an "undefined" type which is used as a default * when a type cannot be inferred. @@ -232,7 +235,7 @@ default String getTargetExpr(Expression expr, InferredType type) { if (ASTUtils.isZero(expr) && type != null && type.isTime) { return getTargetTimeExpr(TimeValue.ZERO); } else if (expr instanceof ParameterReference) { - return escapeIdentifier(((ParameterReference) expr).getParameter().getName()); + return getTargetParamRef((ParameterReference) expr, type); } else if (expr instanceof Time) { return getTargetTimeExpr((Time) expr); } else if (expr instanceof Literal) { @@ -249,6 +252,6 @@ default String getTargetExpr(Expression expr, InferredType type) { * target code. */ default String getTargetTimeExpr(Time t) { - return getTargetTimeExpr(new TimeValue(t.getInterval(), TimeUnit.fromName(t.getUnit()))); + return getTargetTimeExpr(ASTUtils.toTimeValue(t)); } } diff --git a/org.lflang/src/org/lflang/generator/cpp/CppAssembleMethodGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppAssembleMethodGenerator.kt index 32ba3c27d5..4ef12ee201 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppAssembleMethodGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppAssembleMethodGenerator.kt @@ -28,6 +28,7 @@ import org.lflang.generator.PrependOperator import org.lflang.isBank import org.lflang.isMultiport import org.lflang.hasMultipleConnections +import org.lflang.joinWithLn import org.lflang.lf.* /** @@ -175,9 +176,9 @@ class CppAssembleMethodGenerator(private val reactor: Reactor) { """ |// connection $idx |std::vector<$portType> __lf_left_ports_$idx; - ${" |"..c.leftPorts.joinToString("\n") { addAllPortsToVector(it, "__lf_left_ports_$idx") }} + ${" |"..c.leftPorts.joinWithLn { addAllPortsToVector(it, "__lf_left_ports_$idx") }} |std::vector<$portType> __lf_right_ports_$idx; - ${" |"..c.rightPorts.joinToString("\n") { addAllPortsToVector(it, "__lf_right_ports_$idx") }} + ${" |"..c.rightPorts.joinWithLn { addAllPortsToVector(it, "__lf_right_ports_$idx") }} |lfutil::bind_multiple_ports(__lf_left_ports_$idx, __lf_right_ports_$idx, ${c.isIterated}); """.trimMargin() } diff --git a/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt index a8968559ec..a7f6b6c920 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt @@ -132,7 +132,7 @@ class CppInstanceGenerator( } fun generateConstructorInitializers() = - reactor.instantiations.filter { it.isBank }.joinToString("\n") { generateConstructorInitializer(it) } + reactor.instantiations.filter { it.isBank }.joinWithLn { generateConstructorInitializer(it) } /** Generate constructor initializers for all reactor instantiations */ fun generateInitializers(): String = diff --git a/org.lflang/src/org/lflang/generator/cpp/CppPortGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppPortGenerator.kt index b2cb8aba3a..99f55655cd 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppPortGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppPortGenerator.kt @@ -26,6 +26,7 @@ package org.lflang.generator.cpp import org.lflang.inferredType import org.lflang.isMultiport +import org.lflang.joinWithLn import org.lflang.lf.Input import org.lflang.lf.Output import org.lflang.lf.Port @@ -89,8 +90,8 @@ class CppPortGenerator(private val reactor: Reactor) { } fun generateConstructorInitializers() = - reactor.inputs.filter { it.isMultiport }.joinToString("\n") { generateConstructorInitializer(it) } + - reactor.outputs.filter { it.isMultiport }.joinToString("\n") { generateConstructorInitializer(it) } + reactor.inputs.filter { it.isMultiport }.joinWithLn { generateConstructorInitializer(it) } + + reactor.outputs.filter { it.isMultiport }.joinWithLn { generateConstructorInitializer(it) } fun generateDeclarations() = reactor.inputs.joinToString("\n", "// input ports\n", postfix = "\n") { generateDeclaration(it) } + diff --git a/org.lflang/src/org/lflang/generator/cpp/CppReactionGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppReactionGenerator.kt index 8e2513dc71..ef58a5d2be 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppReactionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppReactionGenerator.kt @@ -26,6 +26,7 @@ package org.lflang.generator.cpp import org.lflang.generator.PrependOperator import org.lflang.isBank +import org.lflang.joinWithLn import org.lflang.label import org.lflang.lf.* import org.lflang.priority @@ -193,15 +194,15 @@ class CppReactionGenerator( } private fun generateViews(r: Reaction) = - r.allReferencedContainers.joinToString("\n") { generateViewForContainer(r, it) } + r.allReferencedContainers.joinWithLn { generateViewForContainer(r, it) } private fun generateViewInitializers(r: Reaction) = r.allReferencedContainers.filterNot { it.isBank } - .joinToString("\n") { ", ${r.getViewInstanceName(it)}(${it.name}.get()) " } + .joinWithLn { ", ${r.getViewInstanceName(it)}(${it.name}.get()) " } private fun generateViewConstructorInitializers(r: Reaction) = r.allReferencedContainers.filter { it.isBank } - .joinToString("\n") { + .joinWithLn { val viewInstance = r.getViewInstanceName(it) """ $viewInstance.reserve(${it.name}.size()); diff --git a/org.lflang/src/org/lflang/generator/cpp/CppRos2PackageGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppRos2PackageGenerator.kt index ff5fa22982..f15adfcde2 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppRos2PackageGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppRos2PackageGenerator.kt @@ -1,6 +1,7 @@ package org.lflang.generator.cpp import org.lflang.generator.PrependOperator +import org.lflang.joinWithLn import org.lflang.toUnixString import java.nio.file.Path @@ -30,7 +31,7 @@ class CppRos2PackageGenerator(generator: CppGenerator, private val nodeName: Str | ament_cmake | ament_cmake_auto | - ${" |"..dependencies.joinToString("\n") { "$it" } } + ${" |"..dependencies.joinWithLn { "$it" } } | | ament_lint_auto | ament_lint_common @@ -71,7 +72,7 @@ class CppRos2PackageGenerator(generator: CppGenerator, private val nodeName: Str | |ament_auto_add_library($S{LF_MAIN_TARGET} SHARED | src/$nodeName.cc - ${" | "..sources.joinToString("\n") { "src/$it" }} + ${" | "..sources.joinWithLn { "src/$it" }} |) |ament_target_dependencies($S{LF_MAIN_TARGET} ${dependencies.joinToString(" ")}) |target_include_directories($S{LF_MAIN_TARGET} PUBLIC @@ -94,7 +95,7 @@ class CppRos2PackageGenerator(generator: CppGenerator, private val nodeName: Str | |ament_auto_package() | - ${" |"..(includeFiles?.joinToString("\n") { "include(\"$it\")" } ?: "")} + ${" |"..(includeFiles?.joinWithLn { "include(\"$it\")" } ?: "")} """.trimMargin() } } @@ -109,4 +110,4 @@ class CppRos2PackageGenerator(generator: CppGenerator, private val nodeName: Str |ros2 run ${fileConfig.name} ${fileConfig.name}_exe """.trimMargin() } -} \ No newline at end of file +} diff --git a/org.lflang/src/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt index af8aa4cd03..cc5f2a661e 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt @@ -26,6 +26,7 @@ package org.lflang.generator.cpp import org.lflang.TargetConfig import org.lflang.generator.PrependOperator +import org.lflang.joinWithLn import org.lflang.toUnixString import java.nio.file.Path @@ -128,7 +129,7 @@ class CppStandaloneCmakeGenerator(private val targetConfig: TargetConfig, privat |set(LF_MAIN_TARGET ${fileConfig.name}) | |add_executable($S{LF_MAIN_TARGET} - ${" | "..sources.joinToString("\n") { it.toUnixString() }} + ${" | "..sources.joinWithLn { it.toUnixString() }} |) |target_include_directories($S{LF_MAIN_TARGET} PUBLIC | "$S{LF_SRC_PKG_PATH}/src" @@ -156,7 +157,7 @@ class CppStandaloneCmakeGenerator(private val targetConfig: TargetConfig, privat |set(${includesVarName(fileConfig.name)} $S{TARGET_INCLUDE_DIRECTORIES} CACHE STRING "Directories included in the main target." FORCE) |set($compilerIdName $S{CMAKE_CXX_COMPILER_ID} CACHE STRING "The name of the C++ compiler." FORCE) | - ${" |"..(includeFiles?.joinToString("\n") { "include(\"$it\")" } ?: "")} + ${" |"..(includeFiles?.joinWithLn { "include(\"$it\")" } ?: "")} """.trimMargin() } } diff --git a/org.lflang/src/org/lflang/generator/rust/RustCargoTomlEmitter.kt b/org.lflang/src/org/lflang/generator/rust/RustCargoTomlEmitter.kt index c4ef1b8a16..a4b9ba0548 100644 --- a/org.lflang/src/org/lflang/generator/rust/RustCargoTomlEmitter.kt +++ b/org.lflang/src/org/lflang/generator/rust/RustCargoTomlEmitter.kt @@ -28,6 +28,7 @@ import org.lflang.TargetProperty.BuildType.* import org.lflang.escapeStringLiteral import org.lflang.generator.PrependOperator.rangeTo import org.lflang.joinWithCommas +import org.lflang.joinWithLn import org.lflang.withDQuotes import java.nio.file.Paths @@ -52,7 +53,7 @@ object RustCargoTomlEmitter : RustEmitterBase() { |env_logger = "0.9" |log = { version = "0.4", features = ["release_max_level_info"] } |clap = { version = "3.1.8", features = ["derive", "env"], optional = true } -${" |"..crate.dependencies.asIterable().joinToString("\n") { (name, spec) -> name + " = " + spec.toToml() }} +${" |"..crate.dependencies.asIterable().joinWithLn { (name, spec) -> name + " = " + spec.toToml() }} | |[[bin]] |name = "${gen.executableName}" diff --git a/org.lflang/src/org/lflang/generator/rust/RustEmitter.kt b/org.lflang/src/org/lflang/generator/rust/RustEmitter.kt index a801367116..7a36bce23e 100644 --- a/org.lflang/src/org/lflang/generator/rust/RustEmitter.kt +++ b/org.lflang/src/org/lflang/generator/rust/RustEmitter.kt @@ -27,6 +27,7 @@ package org.lflang.generator.rust import org.lflang.generator.CodeMap import org.lflang.generator.PrependOperator import org.lflang.generator.rust.RustEmitter.generateRustProject +import org.lflang.joinWithLn import org.lflang.util.FileUtil import java.nio.file.Files import java.nio.file.Path @@ -95,7 +96,7 @@ object RustEmitter : RustEmitterBase() { """ |${generatedByComment("//")} | -${" |"..gen.reactors.joinToString("\n") { it.modDecl() }} +${" |"..gen.reactors.joinWithLn { it.modDecl() }} | """.trimMargin() } diff --git a/org.lflang/src/org/lflang/generator/rust/RustMainFileEmitter.kt b/org.lflang/src/org/lflang/generator/rust/RustMainFileEmitter.kt index 70d106f878..fb54556a2c 100644 --- a/org.lflang/src/org/lflang/generator/rust/RustMainFileEmitter.kt +++ b/org.lflang/src/org/lflang/generator/rust/RustMainFileEmitter.kt @@ -30,6 +30,7 @@ import org.lflang.generator.PrependOperator import org.lflang.generator.PrependOperator.rangeTo import org.lflang.generator.UnsupportedGeneratorFeatureException import org.lflang.joinWithCommasLn +import org.lflang.joinWithLn import org.lflang.withoutQuotes @@ -54,10 +55,10 @@ object RustMainFileEmitter : RustEmitterBase() { |extern crate log; | |// user dependencies -${" |"..gen.crate.dependencies.keys.joinToString("\n") { "extern crate ${it.replace('-', '_')};" }} +${" |"..gen.crate.dependencies.keys.joinWithLn { "extern crate ${it.replace('-', '_')};" }} | |// user-defined modules -${" |"..gen.crate.modulesToIncludeInMain.joinToString("\n") { "mod ${it.fileName.toString().removeSuffix(".rs")};" }} +${" |"..gen.crate.modulesToIncludeInMain.joinWithLn { "mod ${it.fileName.toString().removeSuffix(".rs")};" }} | |use $rsRuntime::*; |use log::LevelFilter; diff --git a/org.lflang/src/org/lflang/generator/rust/RustReactorEmitter.kt b/org.lflang/src/org/lflang/generator/rust/RustReactorEmitter.kt index ab21972bd1..2fb80b341f 100644 --- a/org.lflang/src/org/lflang/generator/rust/RustReactorEmitter.kt +++ b/org.lflang/src/org/lflang/generator/rust/RustReactorEmitter.kt @@ -104,7 +104,7 @@ ${" | "..otherComponents.joinWithCommasLn { it.toStructField() }} | | let __impl = { | // declare them all here so that they are visible to the initializers of state vars declared later -${" | "..reactor.stateVars.joinToString("\n") { "let ${it.lfName} = ${it.init};" }} +${" | "..reactor.stateVars.joinWithLn { "let ${it.lfName} = ${it.init};" }} | | $structName { | __phantom: std::marker::PhantomData, diff --git a/org.lflang/src/org/lflang/generator/ts/TSActionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSActionGenerator.kt index 73fec6a7f9..e162c16c26 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSActionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSActionGenerator.kt @@ -1,23 +1,18 @@ package org.lflang.generator.ts import org.lflang.federated.FederateInstance +import org.lflang.generator.getTargetTimeExpr import org.lflang.lf.Action -import org.lflang.lf.Expression import org.lflang.lf.ParameterReference -import org.lflang.lf.Type import java.util.* /** * Generator for actions in TypeScript target. */ -class TSActionGenerator ( - // TODO(hokeun): Remove dependency on TSGenerator. - private val tsGenerator: TSGenerator, +class TSActionGenerator( private val actions: List, private val federate: FederateInstance ) { - private fun Expression.getTargetValue(): String = tsGenerator.getTargetValueW(this) - private fun Type.getTargetType(): String = tsGenerator.getTargetTypeW(this) fun generateClassProperties(): String { val stateClassProperties = LinkedList() @@ -27,7 +22,7 @@ class TSActionGenerator ( // duplicate action if we included the one generated // by LF. if (action.name != "shutdown") { - stateClassProperties.add("${action.name}: __Action<${getActionType(action)}>;") + stateClassProperties.add("${action.name}: __Action<${action.tsActionType}>;") } } return stateClassProperties.joinToString("\n") @@ -45,21 +40,21 @@ class TSActionGenerator ( if (action.minDelay != null) { // Actions in the TypeScript target are constructed // with an optional minDelay argument which defaults to 0. - if (action.minDelay is ParameterReference) { - actionArgs+= ", " + (action.minDelay as ParameterReference).parameter.name + actionArgs += if (action.minDelay is ParameterReference) { + ", " + (action.minDelay as ParameterReference).parameter.name } else { - actionArgs+= ", " + action.minDelay.getTargetValue() + ", " + action.minDelay.toTsTime() } } if (action in networkMessageActions){ actionInstantiations.add( - "this.${action.name} = new __FederatePortAction<${getActionType(action)}>($actionArgs);") + "this.${action.name} = new __FederatePortAction<${action.tsActionType}>($actionArgs);") } else { actionInstantiations.add( - "this.${action.name} = new __Action<${getActionType(action)}>($actionArgs);") + "this.${action.name} = new __Action<${action.tsActionType}>($actionArgs);") } } } return actionInstantiations.joinToString("\n") } -} \ No newline at end of file +} diff --git a/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt index 070e6434d6..35ab8df514 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt @@ -4,6 +4,8 @@ import org.lflang.ErrorReporter import org.lflang.TargetConfig import org.lflang.federated.FederateInstance import org.lflang.generator.PrependOperator +import org.lflang.generator.getTargetInitializer +import org.lflang.joinWithLn import org.lflang.lf.Action import org.lflang.lf.Parameter import org.lflang.lf.Reactor @@ -24,26 +26,9 @@ class TSConstructorGenerator ( private val federate: FederateInstance, private val targetConfig: TargetConfig ) { - private fun getInitializerList(param: Parameter): List = - tsGenerator.getInitializerListW(param) - // Initializer functions - private fun getTargetInitializerHelper(param: Parameter, - list: List): String { - return if (list.size == 0) { - errorReporter.reportError(param, "Parameters must have a default value!") - } else if (list.size == 1) { - list[0] - } else { - list.joinToString(", ", "[", "]") - } - } - private fun getTargetInitializer(param: Parameter): String { - return getTargetInitializerHelper(param, getInitializerList(param)) - } - private fun initializeParameter(p: Parameter): String { - return """${p.name}: ${p.getTargetType()} = ${getTargetInitializer(p)}""" - } + private fun initializeParameter(p: Parameter): String = + "${p.name}: ${TSTypes.getTargetType(p)} = ${TSTypes.getTargetInitializer(p)}" private fun generateConstructorArguments(reactor: Reactor): String { val arguments = LinkedList() @@ -83,7 +68,7 @@ class TSConstructorGenerator ( port = 15045 } return """ - super(federationID, ${federate.id}, ${port}, + super(federationID, ${federate.id}, $port, "${federationRTIProperties()["host"]}", timeout, keepAlive, fast, success, fail); """ @@ -94,29 +79,17 @@ class TSConstructorGenerator ( // If the app is federated, register its // networkMessageActions with the RTIClient - private fun generateFederatePortActionRegistrations(networkMessageActions: List): String { - var fedPortID = 0; - val connectionInstantiations = LinkedList() - for (nAction in networkMessageActions) { - val registration = """ - this.registerFederatePortAction(${fedPortID}, this.${nAction.name}); - """ - connectionInstantiations.add(registration) - fedPortID++ + private fun generateFederatePortActionRegistrations(networkMessageActions: List): String = + networkMessageActions.withIndex().joinWithLn { (fedPortID, nAction) -> + "this.registerFederatePortAction($fedPortID, this.${nAction.name});" } - return connectionInstantiations.joinToString("\n") - } // Generate code for setting target configurations. - private fun generateTargetConfigurations(): String { - val targetConfigurations = LinkedList() - if ((reactor.isMain || reactor.isFederated) && - targetConfig.coordinationOptions.advance_message_interval != null) { - targetConfigurations.add( - "this.setAdvanceMessageInterval(${timeInTargetLanguage(targetConfig.coordinationOptions.advance_message_interval)})") - } - return targetConfigurations.joinToString("\n") - } + private fun generateTargetConfigurations(): String = + if ((reactor.isMain || reactor.isFederated) + && targetConfig.coordinationOptions.advance_message_interval != null + ) "this.setAdvanceMessageInterval(${targetConfig.coordinationOptions.advance_message_interval.toTsTime()})" + else "" // Generate code for registering Fed IDs that are connected to // this federate via ports in the TypeScript's FederatedApp. @@ -145,7 +118,7 @@ class TSConstructorGenerator ( ports: TSPortGenerator ): String { val connections = TSConnectionGenerator(reactor.connections, errorReporter) - val reactions = TSReactionGenerator(tsGenerator, errorReporter, reactor, federate) + val reactions = TSReactionGenerator(errorReporter, reactor, federate) return with(PrependOperator) { """ @@ -168,4 +141,4 @@ class TSConstructorGenerator ( """.trimMargin() } } -} \ No newline at end of file +} diff --git a/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt b/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt index 2e557db545..a01a01f4b3 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt @@ -1,12 +1,12 @@ package org.lflang.generator.ts import org.lflang.TimeValue +import org.lflang.generator.getTargetTimeExpr import org.lflang.isBank import org.lflang.isMultiport import org.lflang.lf.Action -import org.lflang.lf.Parameter +import org.lflang.lf.Expression import org.lflang.lf.Port -import org.lflang.lf.Type import org.lflang.lf.WidthSpec import org.lflang.toText @@ -32,45 +32,23 @@ fun WidthSpec.toTSCode(): String = terms.joinToString(" + ") { } } -private fun Type.getTargetType(): String = TSTypes.getTargetType(this) - /** * Return a TS type for the specified port. * If the type has not been specified, return * "Present" which is the base type for ports. - * @param port The port * @return The TS type. */ -fun getPortType(port: Port): String { - if (port.type != null) { - return port.type.getTargetType() - } else { - return "Present" - } -} - -fun Parameter.getTargetType(): String = TSTypes.getTargetType(this) +val Port.tsPortType: String + get() = type?.let { TSTypes.getTargetType(it) } ?: "Present" /** * 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. */ -fun getActionType(action: Action): String { - if (action.type != null) { - return action.type.getTargetType() - } else { - return "Present" - } -} +val Action.tsActionType: String + get() = type?.let { TSTypes.getTargetType(it) } ?: "Present" -fun timeInTargetLanguage(value: TimeValue): String { - return if (value.unit != null) { - "TimeValue.${value.unit.canonicalName}(${value.time})" - } else { - // The value must be zero. - "TimeValue.zero()" - } -} +fun Expression.toTsTime(): String = TSTypes.getTargetTimeExpr(this) +fun TimeValue.toTsTime(): String = TSTypes.getTargetTimeExpr(this) diff --git a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt index 82cd08c616..04f00cc788 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt @@ -36,7 +36,6 @@ import org.lflang.federated.FederateInstance import org.lflang.federated.launcher.FedTSLauncher import org.lflang.federated.serialization.SupportedSerializers import org.lflang.generator.CodeMap -import org.lflang.generator.ExpressionGenerator import org.lflang.generator.GeneratorBase import org.lflang.generator.GeneratorResult import org.lflang.generator.GeneratorUtils @@ -47,13 +46,8 @@ import org.lflang.generator.PrependOperator import org.lflang.generator.ReactorInstance import org.lflang.generator.SubContext import org.lflang.generator.TargetTypes -import org.lflang.inferredType import org.lflang.lf.Action import org.lflang.lf.Expression -import org.lflang.lf.Instantiation -import org.lflang.lf.Parameter -import org.lflang.lf.StateVar -import org.lflang.lf.Type import org.lflang.lf.VarRef import org.lflang.scoping.LFGlobalScopeProvider import org.lflang.util.FileUtil @@ -91,9 +85,6 @@ class TSGenerator( */ val CONFIG_FILES = arrayOf("package.json", "tsconfig.json", "babel.config.js", ".eslintrc.json") - private val VG = - ExpressionGenerator(::timeInTargetLanguage) { param -> "this.${param.name}.get()" } - fun timeInTargetLanguage(value: TimeValue): String { return if (value.unit != null) { "TimeValue.${value.unit.canonicalName}(${value.magnitude})" @@ -120,16 +111,6 @@ class TSGenerator( // Wrappers to expose GeneratorBase methods. fun federationRTIPropertiesW() = federationRTIProperties - fun getTargetValueW(expr: Expression): String = VG.getTargetValue(expr, false) - fun getTargetTypeW(p: Parameter): String = TSTypes.getTargetType(p.inferredType) - fun getTargetTypeW(state: StateVar): String = TSTypes.getTargetType(state) - fun getTargetTypeW(t: Type): String = TSTypes.getTargetType(t) - - fun getInitializerListW(state: StateVar): List = VG.getInitializerList(state) - fun getInitializerListW(param: Parameter): List = VG.getInitializerList(param) - fun getInitializerListW(param: Parameter, i: Instantiation): List = - VG.getInitializerList(param, i) - /** Generate TypeScript code from the Lingua Franca model contained by the * specified resource. This is the main entry point for code * generation. @@ -312,7 +293,7 @@ class TSGenerator( targetConfig.protoFiles) tsCode.append(preambleGenerator.generatePreamble()) - val parameterGenerator = TSParameterPreambleGenerator(this, fileConfig, targetConfig, reactors) + val parameterGenerator = TSParameterPreambleGenerator(fileConfig, targetConfig, reactors) val (mainParameters, parameterCode) = parameterGenerator.generateParameters() tsCode.append(parameterCode) diff --git a/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt index e623d2e05d..3a261186fe 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt @@ -67,36 +67,29 @@ class TSImportPreambleGenerator( } private fun generateDefaultImports(): String { - return with(PrependOperator) { - """ - |// Code generated by the Lingua Franca compiler from: - |// file:/${filePath.toString()} - $DEFAULT_IMPORTS - | - """.trimMargin() - } + return """ + |// Code generated by the Lingua Franca compiler from: + |// file:/$filePath + $DEFAULT_IMPORTS + | + """.trimMargin() } private fun generateProtoPreamble(): String { val protoFileImports = StringJoiner("\n") for (file in protoFiles) { - var name = file // Remove any extension the file name may have. - val dot = name.lastIndexOf('.') - if (dot > 0) { - name = name.substring(0, dot) - } + val name = file.substringBeforeLast('.') protoFileImports.add(""" - |import * as ${name} from "./${name}_pb" + |import * as $name from "./${name}_pb" """.trimMargin()) } - return with(PrependOperator) {""" + return """ |// Imports for protocol buffers - |${protoFileImports} + |$protoFileImports | | """.trimMargin() - } } fun generatePreamble(): String { diff --git a/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt index 065b0dc320..2c349ebd19 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt @@ -2,7 +2,9 @@ package org.lflang.generator.ts import org.lflang.ErrorReporter import org.lflang.federated.FederateInstance +import org.lflang.generator.getTargetInitializer import org.lflang.isBank +import org.lflang.joinWithLn import org.lflang.lf.Instantiation import org.lflang.lf.Parameter import org.lflang.lf.Reactor @@ -15,11 +17,8 @@ import java.util.* /** * Generator for child reactor instantiations in TypeScript target. */ -class TSInstanceGenerator ( - // TODO(hokeun): Remove dependency on TSGenerator. - private val tsGenerator: TSGenerator, +class TSInstanceGenerator( private val errorReporter: ErrorReporter, - private val tsReactorGenerator: TSReactorGenerator, reactor: Reactor, federate: FederateInstance ) { @@ -37,36 +36,25 @@ class TSInstanceGenerator ( } } - private fun getInitializerList(param: Parameter, i: Instantiation): List = - tsGenerator.getInitializerListW(param, i) - - private fun getTargetInitializer(param: Parameter, i: Instantiation): String { - return tsReactorGenerator.getTargetInitializerHelper(param, getInitializerList(param, i)) - } - private fun getTypeParams(typeParms: List): String = - if (typeParms.isEmpty()) {""} else { - typeParms.joinToString(", ", "<", ">") { it.toText() }} + if (typeParms.isEmpty()) "" + else typeParms.joinToString(", ", "<", ">") { it.toText() } private fun getReactorParameterList(parameters: List): String = - if (parameters.isEmpty()) { "[__Reactor]" } else { - parameters.joinToString(", ", "[__Reactor, ", "]") { it.getTargetType() }} + parameters.joinToString(", ", "[__Reactor, ", "]") { TSTypes.getTargetType(it) } - fun generateClassProperties(): String { - val childReactorClassProperties = LinkedList() - for (childReactor in childReactors) { + fun generateClassProperties(): String = + childReactors.joinWithLn { childReactor -> if (childReactor.isBank) { - childReactorClassProperties.add("${childReactor.name}: " + + "${childReactor.name}: " + "__Bank<${childReactor.reactorClass.name}${getTypeParams(childReactor.typeParms)}, " + - "${getReactorParameterList(childReactor.reactor.parameters)}>") + "${getReactorParameterList(childReactor.reactor.parameters)}>" } else { - childReactorClassProperties.add("${childReactor.name}: " + - "${childReactor.reactorClass.name}${getTypeParams(childReactor.typeParms)}") + "${childReactor.name}: " + + "${childReactor.reactorClass.name}${getTypeParams(childReactor.typeParms)}" } } - return childReactorClassProperties.joinToString("\n") - } fun generateInstantiations(): String { val childReactorInstantiations = LinkedList() @@ -75,7 +63,7 @@ class TSInstanceGenerator ( childReactorArguments.add("this") for (parameter in childReactor.reactorClass.toDefinition().parameters) { - childReactorArguments.add(getTargetInitializer(parameter, childReactor)) + childReactorArguments.add(TSTypes.getTargetInitializer(parameter, childReactor)) } if (childReactor.isBank) { childReactorInstantiations.add( @@ -93,4 +81,4 @@ class TSInstanceGenerator ( } return childReactorInstantiations.joinToString("\n") } -} \ No newline at end of file +} diff --git a/org.lflang/src/org/lflang/generator/ts/TSParameterGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSParameterGenerator.kt index 1cd987b815..5ded5814b0 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSParameterGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSParameterGenerator.kt @@ -1,32 +1,22 @@ package org.lflang.generator.ts -import org.lflang.generator.PrependOperator +import org.lflang.joinWithLn import org.lflang.lf.Parameter -import java.util.* /** * Generate parameters for TypeScript target. */ -class TSParameterGenerator ( - // TODO(hokeun): Remove dependency on TSGenerator. - private val tsGenerator: TSGenerator, +class TSParameterGenerator( private val parameters: List ) { - private fun Parameter.getTargetType(): String = tsGenerator.getTargetTypeW(this) - fun generateClassProperties(): String { - val paramClassProperties = LinkedList() - for (param in parameters) { - paramClassProperties.add("${param.name}: __Parameter<${param.getTargetType()}>;") + fun generateClassProperties(): String = + parameters.joinWithLn { + "${it.name}: __Parameter<${TSTypes.getTargetType(it)}>;" } - return paramClassProperties.joinToString("\n") - } - fun generateInstantiations(): String { - val paramInstantiations = LinkedList() - for (param in parameters) { - paramInstantiations.add("this.${param.name} = new __Parameter(${param.name});") + fun generateInstantiations(): String = + parameters.joinWithLn { + "this.${it.name} = new __Parameter(${it.name});" } - return paramInstantiations.joinToString("\n") - } -} \ No newline at end of file +} diff --git a/org.lflang/src/org/lflang/generator/ts/TSParameterPreambleGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSParameterPreambleGenerator.kt index 02380fa9fd..7ef44319ef 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSParameterPreambleGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSParameterPreambleGenerator.kt @@ -27,8 +27,8 @@ package org.lflang.generator.ts import org.lflang.FileConfig import org.lflang.TargetConfig -import org.lflang.TimeValue -import org.lflang.generator.PrependOperator +import org.lflang.joinWithCommasLn +import org.lflang.joinWithLn import org.lflang.lf.Parameter import org.lflang.lf.Reactor import java.util.StringJoiner @@ -44,20 +44,13 @@ import java.util.StringJoiner */ class TSParameterPreambleGenerator( - private val tsGenerator: TSGenerator, private val fileConfig: FileConfig, private val targetConfig: TargetConfig, private val reactors: MutableList ) { - private fun getTargetType(p: Parameter): String = tsGenerator.getTargetTypeW(p) - private fun getTimeoutTimeValue(): String { - return if (targetConfig.timeout != null) { - timeInTargetLanguage(targetConfig.timeout) - } else { - "undefined" - } - } + private fun getTimeoutTimeValue(): String = + targetConfig.timeout?.toTsTime() ?: "undefined" private fun getParameters(): List { var mainReactor: Reactor? = null @@ -72,11 +65,11 @@ class TSParameterPreambleGenerator( /** * Assign results of parsing custom command line arguments */ - private fun assignCustomCLArgs(mainParameters: HashSet): String { - val code = StringJoiner("\n") - for (parameter in mainParameters) { - code.add(""" - |let __CL${parameter.name}: ${getTargetType(parameter)} | undefined = undefined; + private fun assignCustomCLArgs(mainParameters: HashSet): String = + mainParameters.joinWithLn { parameter -> + + """ + |let __CL${parameter.name}: ${TSTypes.getTargetType(parameter)} | undefined = undefined; |if (__processedCLArgs.${parameter.name} !== undefined) { | if (__processedCLArgs.${parameter.name} !== null) { | __CL${parameter.name} = __processedCLArgs.${parameter.name}; @@ -85,30 +78,25 @@ class TSParameterPreambleGenerator( | throw new Error("Custom '${parameter.name}' command line argument is malformed."); | } |} - """) + """ } - return code.toString() - } /** * Generate code for extracting custom command line arguments * from the object returned from commandLineArgs */ - private fun logCustomCLArgs(mainParameters: Set): String { - val code = StringJoiner("\n") - for (parameter in mainParameters) { + private fun logCustomCLArgs(mainParameters: Set): String = + mainParameters.joinWithLn { parameter -> // We can't allow the programmer's parameter names // to cause the generation of variables with a "__" prefix // because they could collide with other variables. // So prefix variables created here with __CL - code.add(""" + """ |if (__processedCLArgs.${parameter.name} !== undefined && __processedCLArgs.${parameter.name} !== null | && !__noStart) { | Log.global.info("'${parameter.name}' property overridden by command line argument."); - |}""") + |}""" } - return code.toString() - } fun generateParameters(): Pair, String> { /** @@ -126,7 +114,7 @@ class TSParameterPreambleGenerator( var customArgType: String? = null var customTypeLabel: String? = null - val paramType = getTargetType(parameter) + val paramType = TSTypes.getTargetType(parameter) if (paramType == "string") { mainParameters.add(parameter) customArgType = "String"; @@ -147,20 +135,24 @@ class TSParameterPreambleGenerator( if (customArgType != null) { clTypeExtension.add(parameter.name + ": " + paramType) if (customTypeLabel != null) { - customArgs.add(with(PrependOperator) {""" + customArgs.add( + """ |{ | name: '${parameter.name}', - | type: ${customArgType}, - | typeLabel: "{underline ${customTypeLabel}}", + | type: $customArgType, + | typeLabel: "{underline $customTypeLabel}", | description: 'Custom argument. Refer to ${fileConfig.srcFile} for documentation.' - |}""".trimMargin()}) + |}""".trimMargin() + ) } else { - customArgs.add(with(PrependOperator) {""" + customArgs.add( + """ |{ | name: '${parameter.name}', - | type: ${customArgType}, + | type: $customArgType, | description: 'Custom argument. Refer to ${fileConfig.srcFile} for documentation.' - |}""".trimMargin()}) + |}""".trimMargin() + ) } } } @@ -168,127 +160,126 @@ class TSParameterPreambleGenerator( val customArgsList = "[\n$customArgs]" val clTypeExtensionDef = "{$clTypeExtension}" - val codeText = with(PrependOperator) {""" - |// ************* App Parameters - |let __timeout: TimeValue | undefined = ${getTimeoutTimeValue()}; - |let __keepAlive: boolean = ${targetConfig.keepalive}; - |let __fast: boolean = ${targetConfig.fastMode}; - |let __federationID: string = 'Unidentified Federation' - | - |let __noStart = false; // If set to true, don't start the app. - | - |// ************* Custom Command Line Arguments - |let __additionalCommandLineArgs : __CommandLineOptionSpec = ${customArgsList}; - |let __customCommandLineArgs = __CommandLineOptionDefs.concat(__additionalCommandLineArgs); - |let __customCommandLineUsageDefs = __CommandLineUsageDefs; - |type __customCLTypeExtension = ${clTypeExtensionDef}; - |__customCommandLineUsageDefs[1].optionList = __customCommandLineArgs; - |const __clUsage = commandLineUsage(__customCommandLineUsageDefs); - | - |// Set App parameters using values from the constructor or command line args. - |// Command line args have precedence over values from the constructor - |let __processedCLArgs: __ProcessedCommandLineArgs & __customCLTypeExtension; - |try { - | __processedCLArgs = commandLineArgs(__customCommandLineArgs) as __ProcessedCommandLineArgs & __customCLTypeExtension; - |} catch (e){ - | Log.global.error(__clUsage); - | throw new Error("Command line argument parsing failed with: " + e); - |} - | - |// Fast Parameter - |if (__processedCLArgs.fast !== undefined) { - | if (__processedCLArgs.fast !== null) { - | __fast = __processedCLArgs.fast; - | } else { - | Log.global.error(__clUsage); - | throw new Error("'fast' command line argument is malformed."); - | } - |} - | - |// federationID Parameter - |if (__processedCLArgs.id !== undefined) { - | if (__processedCLArgs.id !== null) { - | __federationID = __processedCLArgs.id; - | } else { - | Log.global.error(__clUsage); - | throw new Error("'id (federationID)' command line argument is malformed."); - | } - |} - | - |// KeepAlive parameter - |if (__processedCLArgs.keepalive !== undefined) { - | if (__processedCLArgs.keepalive !== null) { - | __keepAlive = __processedCLArgs.keepalive; - | } else { - | Log.global.error(__clUsage); - | throw new Error("'keepalive' command line argument is malformed."); - | } - |} - | - |// Timeout parameter - |if (__processedCLArgs.timeout !== undefined) { - | if (__processedCLArgs.timeout !== null) { - | __timeout = __processedCLArgs.timeout; - | } else { - | Log.global.error(__clUsage); - | throw new Error("'timeout' command line argument is malformed."); - | } - |} - | - |// Logging parameter (not a constructor parameter, but a command line option) - |if (__processedCLArgs.logging !== undefined) { - | if (__processedCLArgs.logging !== null) { - | Log.global.level = __processedCLArgs.logging; - | } else { - | Log.global.error(__clUsage); - | throw new Error("'logging' command line argument is malformed."); - | } - |} else { - | Log.global.level = Log.levels.${targetConfig.logLevel.name}; // Default from target property. - |} - | - |// Help parameter (not a constructor parameter, but a command line option) - |// NOTE: this arg has to be checked after logging, because the help mode should - |// suppress debug statements from it changes logging - |if (__processedCLArgs.help === true) { - | Log.global.error(__clUsage); - | __noStart = true; - | // Don't execute the app if the help flag is given. - |} - | - |// Now the logging property has been set to its final value, - |// log information about how command line arguments were set, - |// but only if not in help mode. - | - |// Runtime command line arguments - |if (__processedCLArgs.fast !== undefined && __processedCLArgs.fast !== null - | && !__noStart) { - | Log.global.info("'fast' property overridden by command line argument."); - |} - |if (__processedCLArgs.id !== undefined && __processedCLArgs.id !== null - | && !__noStart) { - | Log.global.info("'id (federationID)' property overridden by command line argument."); - |} - |if (__processedCLArgs.keepalive !== undefined && __processedCLArgs.keepalive !== null - | && !__noStart) { - | Log.global.info("'keepalive' property overridden by command line argument."); - |} - |if (__processedCLArgs.timeout !== undefined && __processedCLArgs.timeout !== null - | && !__noStart) { - | Log.global.info("'timeout' property overridden by command line argument."); - |} - |if (__processedCLArgs.logging !== undefined && __processedCLArgs.logging !== null - | && !__noStart) { - | Log.global.info("'logging' property overridden by command line argument."); - |} - | - |// Custom command line arguments - |${logCustomCLArgs(mainParameters)} - |// Assign custom command line arguments - |${assignCustomCLArgs(mainParameters)} - | - """.trimMargin() - } + val codeText = """ + |// ************* App Parameters + |let __timeout: TimeValue | undefined = ${getTimeoutTimeValue()}; + |let __keepAlive: boolean = ${targetConfig.keepalive}; + |let __fast: boolean = ${targetConfig.fastMode}; + |let __federationID: string = 'Unidentified Federation' + | + |let __noStart = false; // If set to true, don't start the app. + | + |// ************* Custom Command Line Arguments + |let __additionalCommandLineArgs : __CommandLineOptionSpec = $customArgsList; + |let __customCommandLineArgs = __CommandLineOptionDefs.concat(__additionalCommandLineArgs); + |let __customCommandLineUsageDefs = __CommandLineUsageDefs; + |type __customCLTypeExtension = $clTypeExtensionDef; + |__customCommandLineUsageDefs[1].optionList = __customCommandLineArgs; + |const __clUsage = commandLineUsage(__customCommandLineUsageDefs); + | + |// Set App parameters using values from the constructor or command line args. + |// Command line args have precedence over values from the constructor + |let __processedCLArgs: __ProcessedCommandLineArgs & __customCLTypeExtension; + |try { + | __processedCLArgs = commandLineArgs(__customCommandLineArgs) as __ProcessedCommandLineArgs & __customCLTypeExtension; + |} catch (e){ + | Log.global.error(__clUsage); + | throw new Error("Command line argument parsing failed with: " + e); + |} + | + |// Fast Parameter + |if (__processedCLArgs.fast !== undefined) { + | if (__processedCLArgs.fast !== null) { + | __fast = __processedCLArgs.fast; + | } else { + | Log.global.error(__clUsage); + | throw new Error("'fast' command line argument is malformed."); + | } + |} + | + |// federationID Parameter + |if (__processedCLArgs.id !== undefined) { + | if (__processedCLArgs.id !== null) { + | __federationID = __processedCLArgs.id; + | } else { + | Log.global.error(__clUsage); + | throw new Error("'id (federationID)' command line argument is malformed."); + | } + |} + | + |// KeepAlive parameter + |if (__processedCLArgs.keepalive !== undefined) { + | if (__processedCLArgs.keepalive !== null) { + | __keepAlive = __processedCLArgs.keepalive; + | } else { + | Log.global.error(__clUsage); + | throw new Error("'keepalive' command line argument is malformed."); + | } + |} + | + |// Timeout parameter + |if (__processedCLArgs.timeout !== undefined) { + | if (__processedCLArgs.timeout !== null) { + | __timeout = __processedCLArgs.timeout; + | } else { + | Log.global.error(__clUsage); + | throw new Error("'timeout' command line argument is malformed."); + | } + |} + | + |// Logging parameter (not a constructor parameter, but a command line option) + |if (__processedCLArgs.logging !== undefined) { + | if (__processedCLArgs.logging !== null) { + | Log.global.level = __processedCLArgs.logging; + | } else { + | Log.global.error(__clUsage); + | throw new Error("'logging' command line argument is malformed."); + | } + |} else { + | Log.global.level = Log.levels.${targetConfig.logLevel.name}; // Default from target property. + |} + | + |// Help parameter (not a constructor parameter, but a command line option) + |// NOTE: this arg has to be checked after logging, because the help mode should + |// suppress debug statements from it changes logging + |if (__processedCLArgs.help === true) { + | Log.global.error(__clUsage); + | __noStart = true; + | // Don't execute the app if the help flag is given. + |} + | + |// Now the logging property has been set to its final value, + |// log information about how command line arguments were set, + |// but only if not in help mode. + | + |// Runtime command line arguments + |if (__processedCLArgs.fast !== undefined && __processedCLArgs.fast !== null + | && !__noStart) { + | Log.global.info("'fast' property overridden by command line argument."); + |} + |if (__processedCLArgs.id !== undefined && __processedCLArgs.id !== null + | && !__noStart) { + | Log.global.info("'id (federationID)' property overridden by command line argument."); + |} + |if (__processedCLArgs.keepalive !== undefined && __processedCLArgs.keepalive !== null + | && !__noStart) { + | Log.global.info("'keepalive' property overridden by command line argument."); + |} + |if (__processedCLArgs.timeout !== undefined && __processedCLArgs.timeout !== null + | && !__noStart) { + | Log.global.info("'timeout' property overridden by command line argument."); + |} + |if (__processedCLArgs.logging !== undefined && __processedCLArgs.logging !== null + | && !__noStart) { + | Log.global.info("'logging' property overridden by command line argument."); + |} + | + |// Custom command line arguments + |${logCustomCLArgs(mainParameters)} + |// Assign custom command line arguments + |${assignCustomCLArgs(mainParameters)} + | + """.trimMargin() return Pair(mainParameters, codeText) } diff --git a/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt index 10e2a4a644..338ef9600d 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt @@ -3,15 +3,12 @@ package org.lflang.generator.ts import org.lflang.isMultiport import org.lflang.lf.Input import org.lflang.lf.Output -import org.lflang.lf.Port -import org.lflang.lf.Type import java.util.* /** * Generate input and output ports for TypeScript target. */ class TSPortGenerator ( - // TODO(hokeun): Remove dependency on TSGenerator. private val inputs: List, private val outputs: List ) { @@ -20,16 +17,16 @@ class TSPortGenerator ( val portClassProperties = LinkedList() for (input in inputs) { if (input.isMultiport) { - portClassProperties.add("${input.name}: __InMultiPort<${getPortType(input)}>;") + portClassProperties.add("${input.name}: __InMultiPort<${input.tsPortType}>;") } else { - portClassProperties.add("${input.name}: __InPort<${getPortType(input)}>;") + portClassProperties.add("${input.name}: __InPort<${input.tsPortType}>;") } } for (output in outputs) { if (output.isMultiport) { - portClassProperties.add("${output.name}: __OutMultiPort<${getPortType(output)}>;") + portClassProperties.add("${output.name}: __OutMultiPort<${output.tsPortType}>;") } else { - portClassProperties.add("${output.name}: __OutPort<${getPortType(output)}>;") + portClassProperties.add("${output.name}: __OutPort<${output.tsPortType}>;") } } return portClassProperties.joinToString("\n") @@ -40,19 +37,19 @@ class TSPortGenerator ( for (input in inputs) { if (input.isMultiport) { porInstantiations.add( - "this.${input.name} = new __InMultiPort<${getPortType(input)}>(this, ${input.widthSpec.toTSCode()});") + "this.${input.name} = new __InMultiPort<${input.tsPortType}>(this, ${input.widthSpec.toTSCode()});") } else { - porInstantiations.add("this.${input.name} = new __InPort<${getPortType(input)}>(this);") + porInstantiations.add("this.${input.name} = new __InPort<${input.tsPortType}>(this);") } } for (output in outputs) { if (output.isMultiport) { porInstantiations.add( - "this.${output.name} = new __OutMultiPort<${getPortType(output)}>(this, ${output.widthSpec.toTSCode()});") + "this.${output.name} = new __OutMultiPort<${output.tsPortType}>(this, ${output.widthSpec.toTSCode()});") } else { - porInstantiations.add("this.${output.name} = new __OutPort<${getPortType(output)}>(this);") + porInstantiations.add("this.${output.name} = new __OutPort<${output.tsPortType}>(this);") } } return porInstantiations.joinToString("\n") } -} \ No newline at end of file +} diff --git a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt index e18c1656a4..620861aee4 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt @@ -4,8 +4,10 @@ import org.lflang.ErrorReporter import org.lflang.ASTUtils import org.lflang.federated.FederateInstance import org.lflang.generator.PrependOperator +import org.lflang.generator.getTargetTimeExpr import org.lflang.isBank import org.lflang.isMultiport +import org.lflang.joinWithCommas import org.lflang.lf.* import org.lflang.lf.Timer import org.lflang.toText @@ -24,16 +26,10 @@ import java.util.LinkedList * @author {Hokeun Kim } */ class TSReactionGenerator( - // TODO(hokeun): Remove dependency on TSGenerator. - private val tsGenerator: TSGenerator, private val errorReporter: ErrorReporter, - private val reactor : Reactor, + private val reactor: Reactor, private val federate: FederateInstance ) { - private fun Expression.getTargetExpression(): String = tsGenerator.getTargetValueW(this) - private fun Parameter.getTargetType(): String = tsGenerator.getTargetTypeW(this) - private fun StateVar.getTargetType(): String = tsGenerator.getTargetTypeW(this) - private fun Type.getTargetType(): String = tsGenerator.getTargetTypeW(this) private fun VarRef.generateVarRef(): String { return if (this.container != null && this.container.isBank && this.variable is Port) { @@ -43,21 +39,6 @@ class TSReactionGenerator( } } - /** - * Return a TS type for the specified port. - * If the type has not been specified, return - * "Present" which is the base type for ports. - * @param port The port - * @return The TS type. - */ - private fun getPortType(port: Port): String { - if (port.type != null) { - return port.type.getTargetType() - } else { - return "Present" - } - } - private fun generateArg(v: VarRef): String { return if (v.container != null) { "__${v.container.name}_${v.variable.name}" @@ -74,10 +55,10 @@ class TSReactionGenerator( ): String { var deadlineArgs = "" val delay = reaction.deadline.delay - if (delay is ParameterReference) { - deadlineArgs += "this.${delay.parameter.name}.get()"; + deadlineArgs += if (delay is ParameterReference) { + "this.${delay.parameter.name}.get()" } else { - deadlineArgs += delay.getTargetExpression() + delay.toTsTime() } return with(PrependOperator) { @@ -118,7 +99,7 @@ class TSReactionGenerator( when (trigger.type) { BuiltinTrigger.STARTUP -> reactionTriggers.add("this.startup") BuiltinTrigger.SHUTDOWN -> reactionTriggers.add("this.shutdown") - else -> {} + else -> {} } } } @@ -126,7 +107,7 @@ class TSReactionGenerator( return with(PrependOperator) { """ | - |this.add${if (reaction.isMutation()) "Mutation" else "Reaction"}( + |this.add${if (reaction.isMutation) "Mutation" else "Reaction"}( | new __Triggers($reactionTriggers), | new __Args($reactFuncArgs), | function ($reactSignature) { @@ -140,27 +121,31 @@ class TSReactionGenerator( ${" | "..reactEpilogue} | // =============== END react epilogue | } - ${" | "..if (reaction.deadline != null) generateDeadlineHandler(reaction, reactPrologue, reactEpilogue, reactSignature) else "}"} + ${ + " | "..if (reaction.deadline != null) generateDeadlineHandler( + reaction, + reactPrologue, + reactEpilogue, + reactSignature + ) else "}" + } |); |""".trimMargin() - } + } } private fun generateReactionSignatureForTrigger(trigOrSource: VarRef): String { - var reactSignatureElementType = if (trigOrSource.variable is Timer) { - "__Tag" - } else if (trigOrSource.variable is Action) { - getActionType(trigOrSource.variable as Action) - } else if (trigOrSource.variable is Port) { - getPortType(trigOrSource.variable as Port) - } else { - errorReporter.reportError("Invalid trigger: ${trigOrSource.variable.name}") + val reactSignatureElementType = when (trigOrSource.variable) { + is Timer -> "__Tag" + is Action -> (trigOrSource.variable as Action).tsActionType + is Port -> (trigOrSource.variable as Port).tsPortType + else -> errorReporter.reportError("Invalid trigger: ${trigOrSource.variable.name}") } val portClassType = if (trigOrSource.variable.isMultiport) { - "__InMultiPort<${reactSignatureElementType}>" + "__InMultiPort<$reactSignatureElementType>" } else { - "Read<${reactSignatureElementType}>" + "Read<$reactSignatureElementType>" } return if (trigOrSource.container != null && trigOrSource.container.isBank) { "${generateArg(trigOrSource)}: Array<$portClassType>" @@ -172,13 +157,13 @@ class TSReactionGenerator( private fun generateReactionSignatureElementForPortEffect(effect: VarRef, isMutation: Boolean): String { val outputPort = effect.variable as Port val portClassType = if (outputPort.isMultiport) { - (if (isMutation) "__WritableMultiPort" else "MultiReadWrite") + "<${getPortType(effect.variable as Port)}>" + (if (isMutation) "__WritableMultiPort" else "MultiReadWrite") + "<${(effect.variable as Port).tsPortType}>" } else { - (if (isMutation) "__WritablePort" else "ReadWrite") + "<${getPortType(effect.variable as Port)}>" + (if (isMutation) "__WritablePort" else "ReadWrite") + "<${(effect.variable as Port).tsPortType}>" } return if (effect.container != null && effect.container.isBank) { - "Array<${portClassType}>" + "Array<$portClassType>" } else { portClassType } @@ -186,51 +171,53 @@ class TSReactionGenerator( private fun generateReactionEpilogueForPortEffect(effect: VarRef): String { val portEffect = effect.variable as Port - if (effect.container == null) { + val effectName = portEffect.name + return if (effect.container == null) { if (portEffect.isMultiport) { - return """ - |${portEffect.name}.forEach((__element, __index) => { - | if (__element !== undefined) { - | __${portEffect.name}.set(__index, __element); - | } - |});""".trimMargin() + """ + |$effectName.forEach((__element, __index) => { + | if (__element !== undefined) { + | __$effectName.set(__index, __element); + | } + |});""".trimMargin() } else { - return """ - |if (${portEffect.name} !== undefined) { - | __${portEffect.name}.set(${portEffect.name}); - |}""".trimMargin() + """ + |if ($effectName !== undefined) { + | __$effectName.set($effectName); + |}""".trimMargin() } } else { + val containerName = effect.container.name if (effect.container.isBank) { if (portEffect.isMultiport) { - return """ - |${effect.container.name}.forEach((__reactor, __reactorIndex) => { - | __reactor.${portEffect.name}.forEach((__element, __index) => { + """ + |$containerName.forEach((__reactor, __reactorIndex) => { + | __reactor.$effectName.forEach((__element, __index) => { | if (__element !== undefined) { - | __${effect.container.name}_${portEffect.name}[__reactorIndex].set(__index, __element) + | __${containerName}_$effectName[__reactorIndex].set(__index, __element) | } | }) |});""".trimMargin() } else { - return """ - |${effect.container.name}.forEach((__reactor, __reactorIndex) => { - | if (__reactor.${portEffect.name} !== undefined) { - | __${effect.container.name}_${portEffect.name}[__reactorIndex].set(__reactor.${portEffect.name}) + """ + |$containerName.forEach((__reactor, __reactorIndex) => { + | if (__reactor.$effectName !== undefined) { + | __${containerName}_$effectName[__reactorIndex].set(__reactor.$effectName) | } |});""".trimMargin() } } else { if (portEffect.isMultiport) { - return """ - |${effect.container.name}.${portEffect.name}.forEach((__element, __index) => { + """ + |$containerName.$effectName.forEach((__element, __index) => { | if (__element !== undefined) { - | __${effect.container.name}_${portEffect.name}.set(__index, __element) + | __${containerName}_$effectName.set(__index, __element) | } |});""".trimMargin() } else { - return """ - |if (${effect.container.name}.${portEffect.name} !== undefined) { - | __${effect.container.name}_${portEffect.name}.set(${effect.container.name}.${portEffect.name}) + """ + |if ($containerName.$effectName !== undefined) { + | __${containerName}_$effectName.set($containerName.$effectName) |}""".trimMargin() } } @@ -238,7 +225,7 @@ class TSReactionGenerator( } // TODO(hokeun): Decompose this function further. - private fun generateSingleReaction(reactor : Reactor, reaction: Reaction): String { + private fun generateSingleReaction(reactor: Reactor, reaction: Reaction): String { // Determine signature of the react function val reactSignature = StringJoiner(", ") reactSignature.add("this") @@ -254,14 +241,9 @@ class TSReactionGenerator( val reactFunctArgs = StringJoiner(", ") // Combine triggers and sources into a set // so we can iterate over their union - val triggersUnionSources = HashSet() - for (trigger in reaction.triggers) { - if (!(trigger is BuiltinTriggerRef)) { - triggersUnionSources.add(trigger as VarRef) - } - } - for (source in reaction.sources) { - triggersUnionSources.add(source) + val triggersUnionSources = mutableSetOf().also { + it.addAll(reaction.triggers.filterIsInstance()) + it.addAll(reaction.sources) } // Create a set of effect names so actions that appear @@ -272,112 +254,94 @@ class TSReactionGenerator( // unequal. // The key of the pair is the effect's container's name, // The effect of the pair is the effect's name - val effectSet = HashSet>() + val effectSet = reaction.effects.map { + val key = it.container?.name ?: "" // The container + val value = it.variable.name // The name of the effect + key to value + }.toMutableSet() - for (effect in reaction.effects) { - var key = ""; // The container, defaults to an empty string - val value = effect.variable.name; // The name of the effect - if (effect.container != null) { - key = effect.container.name - } - effectSet.add(Pair(key, value)) - } // The prologue to the react function writes state // and parameters to local variables of the same name - val reactPrologue = LinkedList() + val reactPrologue = mutableListOf() reactPrologue.add("const util = this.util;") // Add triggers and sources to the react function - val containerToArgs = HashMap>(); + val containerToArgs = mutableMapOf>() for (trigOrSource in triggersUnionSources) { // Actions that are both read and scheduled should only // appear once as a schedulable effect - var trigOrSourceKey = "" // The default for no container - val trigOrSourceValue = trigOrSource.variable.name - if (trigOrSource.container != null) { - trigOrSourceKey = trigOrSource.container.name - } - val trigOrSourcePair = Pair(trigOrSourceKey, trigOrSourceValue) + val trigOrSourceKey = trigOrSource.container?.name.orEmpty() + val triggerName = trigOrSource.variable.name + val trigOrSourcePair = trigOrSourceKey to triggerName - if (!effectSet.contains(trigOrSourcePair)) { + if (trigOrSourcePair !in effectSet) { reactSignature.add(generateReactionSignatureForTrigger(trigOrSource)) reactFunctArgs.add(trigOrSource.generateVarRef()) if (trigOrSource.container == null) { if (trigOrSource.variable.isMultiport) { - val inputPort = trigOrSource.variable as Port - reactPrologue.add( - "let ${inputPort.name} = ${generateArg(trigOrSource)}.values();") + reactPrologue.add("let $triggerName = ${generateArg(trigOrSource)}.values();") } else { - reactPrologue.add("let ${trigOrSource.variable.name} = ${generateArg(trigOrSource)}.get();") + reactPrologue.add("let $triggerName = ${generateArg(trigOrSource)}.get();") } } else { - var args = containerToArgs.get(trigOrSource.container) - if (args == null) { - // Create the HashSet for the container - // and handle it later. - args = HashSet() - containerToArgs.put(trigOrSource.container, args) - } + val args = containerToArgs.computeIfAbsent(trigOrSource.container) { mutableSetOf() } args.add(trigOrSource.variable) } } } - val schedActionSet = HashSet() + val schedActionSet = mutableSetOf() // The epilogue to the react function writes local // state variables back to the state - val reactEpilogue = LinkedList() + val reactEpilogue = mutableListOf() for (effect in reaction.effects) { - var reactSignatureElement = generateArg(effect) - if (effect.variable is Timer) { - errorReporter.reportError("A timer cannot be an effect of a reaction") - } else if (effect.variable is Action){ - reactSignatureElement += ": Sched<" + getActionType(effect.variable as Action) + ">" - schedActionSet.add(effect.variable as Action) - } else if (effect.variable is Port){ - reactSignatureElement += ": ${generateReactionSignatureElementForPortEffect(effect, reaction.isMutation())}" - reactEpilogue.add(generateReactionEpilogueForPortEffect(effect)) - } + val reactSignatureElement = generateArg(effect) + val functArg = effect.generateVarRef() + when (val effectVar = effect.variable) { + is Timer -> { + errorReporter.reportError("A timer cannot be an effect of a reaction") + } - reactSignature.add(reactSignatureElement) + is Action -> { + reactSignature.add("$reactSignatureElement: Sched<${effectVar.tsActionType}>") + schedActionSet.add(effectVar) + reactFunctArgs.add("this.schedulable($functArg)") + } - var functArg = effect.generateVarRef() - if (effect.variable is Action){ - reactFunctArgs.add("this.schedulable($functArg)") - } else if (effect.variable is Port) { - val port = effect.variable as Port - if (port.isMultiport) { - if (effect.container != null && effect.container.isBank) { - reactFunctArgs.add("this.${effect.container.name}.allWritable($functArg)") - } else { - reactFunctArgs.add("this.allWritable($functArg)") - } - } else { - if (effect.container != null && effect.container.isBank) { - reactFunctArgs.add("this.${effect.container.name}.writable($functArg)") + is Port -> { + val type = generateReactionSignatureElementForPortEffect(effect, reaction.isMutation) + reactSignature.add("$reactSignatureElement: $type") + reactEpilogue.add(generateReactionEpilogueForPortEffect(effect)) + val funcArgs = if (effectVar.isMultiport) { + if (effect.container?.isBank == true) { + "this.${effect.container.name}.allWritable($functArg)" + } else { + "this.allWritable($functArg)" + } } else { - reactFunctArgs.add("this.writable($functArg)") + if (effect.container?.isBank == true) { + "this.${effect.container.name}.writable($functArg)" + } else { + "this.writable($functArg)" + } } + reactFunctArgs.add(funcArgs) } } if (effect.container == null) { if (effect.variable.isMultiport) { val port = effect.variable as Port - reactPrologue.add("let ${port.name} = new Array<${getPortType(port)}>(__${port.name}.width());") + reactPrologue.add("let ${port.name} = new Array<${port.tsPortType}>(__${port.name}.width());") } else { reactPrologue.add("let ${effect.variable.name} = __${effect.variable.name}.get();") } } else { // Hierarchical references are handled later because there // could be references to other members of the same reactor. - var args = containerToArgs.get(effect.container) - if (args == null) { - args = HashSet(); - containerToArgs.put(effect.container, args) - } + val args = containerToArgs.computeIfAbsent(effect.container) { HashSet() } args.add(effect.variable) } } @@ -385,10 +349,8 @@ class TSReactionGenerator( // Iterate through the actions to handle the prologue's // "actions" object if (schedActionSet.size > 0) { - val prologueActionObjectBody = StringJoiner(", ") - for (act in schedActionSet) { - prologueActionObjectBody.add("${act.name}: __${act.name}") - } + val prologueActionObjectBody = + schedActionSet.joinWithCommas { "${it.name}: __${it.name}" } reactPrologue.add("let actions = {$prologueActionObjectBody};") } @@ -396,45 +358,47 @@ class TSReactionGenerator( for (param in reactor.parameters) { // Underscores are added to parameter names to prevent conflict with prologue - reactSignature.add("__${param.name}: __Parameter<${param.getTargetType()}>") - reactFunctArgs.add("this.${param.name}") - - reactPrologue.add("let ${param.name} = __${param.name}.get();") + val name = param.name + reactSignature.add("__$name: __Parameter<${TSTypes.getTargetType(param)}>") + reactFunctArgs.add("this.$name") + reactPrologue.add("let $name = __$name.get();") } // Add state to the react function for (state in reactor.stateVars) { // Underscores are added to state names to prevent conflict with prologue - reactSignature.add("__${state.name}: __State<${state.getTargetType()}>") - reactFunctArgs.add("this.${state.name}") - - reactPrologue.add("let ${state.name} = __${state.name}.get();") - reactEpilogue.add(with(PrependOperator) {""" - |if (${state.name} !== undefined) { - | __${state.name}.set(${state.name}); - |}""".trimMargin()}) + val name = state.name + reactSignature.add("__$name: __State<${TSTypes.getTargetType(state)}>") + reactFunctArgs.add("this.$name") + reactPrologue.add("let $name = __$name.get();") + reactEpilogue.add( + """ + |if ($name !== undefined) { + | __$name.set($name); + |}""".trimMargin() + ) } // Initialize objects to enable hierarchical references. - for (entry in containerToArgs.entries) { - val initializer = StringJoiner(", ") - for (variable in entry.value) { - initializer.add("${variable.name}: __${entry.key.name}_${variable.name}" + + for ((container, args) in containerToArgs.entries) { + val containerName = container.name + val initializer = args.joinWithCommas { variable -> + "${variable.name}: __${containerName}_${variable.name}" + // The parentheses are needed below to separate two if-else statements. - (if (entry.key.isBank) "[i]" else "") + - if (variable.isMultiport) ".values()" else ".get()") + (if (container.isBank) "[i]" else "") + + if (variable.isMultiport) ".values()" else ".get()" } - if (entry.key.isBank) { - reactPrologue.add( + val prologuePart = + if (container.isBank) { """ - |let ${entry.key.name} = [] - |for (let i = 0; i < ${entry.key.widthSpec.toTSCode()}; i++) { - | ${entry.key.name}.push({${initializer}}) - |}""".trimMargin() - ) - } else { - reactPrologue.add("let ${entry.key.name} = {${initializer}}") - } + |let $containerName = [] + |for (let i = 0; i < ${container.widthSpec.toTSCode()}; i++) { + | $containerName.push({$initializer}) + |}""".trimMargin() + } else { + "let $containerName = {$initializer}" + } + reactPrologue.add(prologuePart) } // Generate reaction as a formatted string. @@ -460,8 +424,9 @@ class TSReactionGenerator( // Do not add reactions created by generateNetworkOutputControlReactionBody // or generateNetworkInputControlReactionBody. if (reaction.code.toText().contains("generateNetworkOutputControlReactionBody") - || reaction.code.toText().contains("generateNetworkInputControlReactionBody")) { - continue; + || reaction.code.toText().contains("generateNetworkInputControlReactionBody") + ) { + continue } if (federate.contains(reaction)) { generatedReactions.add(reaction) @@ -479,4 +444,4 @@ class TSReactionGenerator( } return reactionCodes.joinToString("\n") } -} \ No newline at end of file +} diff --git a/org.lflang/src/org/lflang/generator/ts/TSReactorGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSReactorGenerator.kt index e599bd28c8..3574d76310 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSReactorGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSReactorGenerator.kt @@ -153,11 +153,11 @@ class TSReactorGenerator( "export class $reactorName extends __Reactor {" } - val instanceGenerator = TSInstanceGenerator(tsGenerator, errorReporter, this, reactor, federate) - val timerGenerator = TSTimerGenerator(tsGenerator, reactor.timers) - val parameterGenerator = TSParameterGenerator(tsGenerator, reactor.parameters) - val stateGenerator = TSStateGenerator(tsGenerator, reactor.stateVars) - val actionGenerator = TSActionGenerator(tsGenerator, reactor.actions, federate) + val instanceGenerator = TSInstanceGenerator(errorReporter, reactor, federate) + val timerGenerator = TSTimerGenerator(reactor.timers) + val parameterGenerator = TSParameterGenerator(reactor.parameters) + val stateGenerator = TSStateGenerator(reactor.stateVars) + val actionGenerator = TSActionGenerator(reactor.actions, federate) val portGenerator = TSPortGenerator(reactor.inputs, reactor.outputs) val constructorGenerator = TSConstructorGenerator(tsGenerator, errorReporter, reactor, federate, targetConfig) @@ -196,4 +196,4 @@ class TSReactorGenerator( """ }.trimMargin() } -} \ No newline at end of file +} diff --git a/org.lflang/src/org/lflang/generator/ts/TSStateGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSStateGenerator.kt index 58ed7d4eee..091da1d958 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSStateGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSStateGenerator.kt @@ -1,42 +1,35 @@ package org.lflang.generator.ts import org.lflang.ASTUtils +import org.lflang.generator.getTargetInitializer import org.lflang.lf.StateVar import java.util.* /** * Generator for state variables in TypeScript target. */ -class TSStateGenerator ( - private val tsGenerator: TSGenerator, +class TSStateGenerator( private val stateVars: List ) { - private fun StateVar.getTargetType(): String = tsGenerator.getTargetTypeW(this) fun generateClassProperties(): String { val stateClassProperties = LinkedList() for (stateVar in stateVars) { - stateClassProperties.add("${stateVar.name}: __State<${stateVar.getTargetType()}>;"); + stateClassProperties.add("${stateVar.name}: __State<${TSTypes.getTargetType(stateVar)}>;"); } return stateClassProperties.joinToString("\n") } - private fun getInitializerList(state: StateVar): List = - tsGenerator.getInitializerListW(state) - - private fun getTargetInitializer(state: StateVar): String { - return getInitializerList(state).joinToString(",") - } fun generateInstantiations(): String { val stateInstantiations = LinkedList() // Next handle states. for (stateVar in stateVars) { if (ASTUtils.isInitialized(stateVar)) { - stateInstantiations.add("this.${stateVar.name} = new __State(${getTargetInitializer(stateVar)});"); + stateInstantiations.add("this.${stateVar.name} = new __State(${TSTypes.getTargetInitializer(stateVar)});"); } else { stateInstantiations.add("this.${stateVar.name} = new __State(undefined);"); } } return stateInstantiations.joinToString("\n") } -} \ No newline at end of file +} diff --git a/org.lflang/src/org/lflang/generator/ts/TSTimerGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSTimerGenerator.kt index 0d47872142..2cb1f37894 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSTimerGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSTimerGenerator.kt @@ -1,5 +1,7 @@ package org.lflang.generator.ts +import org.lflang.generator.getTargetTimeExpr +import org.lflang.generator.orZero import org.lflang.lf.Expression import org.lflang.lf.Timer import java.util.* @@ -7,12 +9,9 @@ import java.util.* /** * Generator timers for TypeScript target. */ -class TSTimerGenerator ( - // TODO(hokeun): Remove dependency on TSGenerator. - private val tsGenerator: TSGenerator, +class TSTimerGenerator( private val timers: List ) { - private fun Expression.getTargetValue(): String = tsGenerator.getTargetValueW(this) fun generateClassProperties(): String { val timerClassProperties = LinkedList() @@ -25,8 +24,8 @@ class TSTimerGenerator ( fun generateInstantiations(): String { val timerInstantiations = LinkedList() for (timer in timers) { - val timerPeriod: String = timer.period?.getTargetValue() ?: "0" - val timerOffset: String = timer.offset?.getTargetValue() ?: "0" + val timerPeriod: String = timer.period.orZero().toTsTime() + val timerOffset: String = timer.offset.orZero().toTsTime() timerInstantiations.add("this.${timer.name} = new __Timer(this, $timerOffset, $timerPeriod);") } diff --git a/org.lflang/src/org/lflang/generator/ts/TSTypes.kt b/org.lflang/src/org/lflang/generator/ts/TSTypes.kt index d996f5638e..21c0d32fa7 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSTypes.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSTypes.kt @@ -1,6 +1,7 @@ package org.lflang.generator.ts import org.lflang.ASTUtils +import org.lflang.TimeValue import org.lflang.generator.TargetTypes import org.lflang.lf.StateVar @@ -31,6 +32,15 @@ object TSTypes : TargetTypes { return "Present" } + override fun getTargetTimeExpr(value: TimeValue): String { + return if (value.unit != null) { + "TimeValue.${value.unit.canonicalName}(${value.time})" + } else { + // The value must be zero. + "TimeValue.zero()" + } + } + override fun getTargetFixedSizeListType(baseType: String, size: Int): String { return "Array($size)<$baseType>" } @@ -38,4 +48,4 @@ object TSTypes : TargetTypes { override fun getTargetVariableSizeListType(baseType: String): String { return "Array<$baseType>" } -} \ No newline at end of file +} From ba9b5a04aa0ace46a95cf232c978824d8ed7c026 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Thu, 3 Nov 2022 09:19:06 +0100 Subject: [PATCH 25/90] run Python tests on Windows using the RelWithDebInfo build type --- .../src/org/lflang/tests/runtime/PythonTest.java | 11 +++++++++++ .../lflang/tests/serialization/SerializationTest.java | 4 ---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/org.lflang.tests/src/org/lflang/tests/runtime/PythonTest.java b/org.lflang.tests/src/org/lflang/tests/runtime/PythonTest.java index 93cca1beda..8bc93b35c3 100644 --- a/org.lflang.tests/src/org/lflang/tests/runtime/PythonTest.java +++ b/org.lflang.tests/src/org/lflang/tests/runtime/PythonTest.java @@ -24,6 +24,8 @@ ***************/ package org.lflang.tests.runtime; +import java.util.Properties; + import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -47,6 +49,15 @@ public PythonTest() { super(Target.Python); } + @Override + protected void addExtraLfcArgs(Properties args) { + super.addExtraLfcArgs(args); + if (System.getProperty("os.name").startsWith("Windows")) { + // Use the RelWithDebInfo build type on Windows as the Debug/Test build type produces linker Errors in CI + args.setProperty("build-type", "RelWithDebInfo"); + } + } + @Override protected boolean supportsFederatedExecution() { return true; diff --git a/org.lflang.tests/src/org/lflang/tests/serialization/SerializationTest.java b/org.lflang.tests/src/org/lflang/tests/serialization/SerializationTest.java index c62bc3cf15..0fab08cef9 100644 --- a/org.lflang.tests/src/org/lflang/tests/serialization/SerializationTest.java +++ b/org.lflang.tests/src/org/lflang/tests/serialization/SerializationTest.java @@ -15,10 +15,6 @@ protected SerializationTest() { super(Target.ALL); } - - /** - * Override to add some LFC arguments to all runs of this test class. - */ @Override protected void addExtraLfcArgs(Properties args) { super.addExtraLfcArgs(args); From 98db015b536ce0bc58d43c46a16cff99d859cd20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 3 Nov 2022 16:19:35 +0100 Subject: [PATCH 26/90] Implement missing method in TsTypes Fixed-size lists are now explicitly disallowed, previously they would have generated malformed code. --- org.lflang/src/org/lflang/generator/ts/TSTypes.kt | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/ts/TSTypes.kt b/org.lflang/src/org/lflang/generator/ts/TSTypes.kt index 21c0d32fa7..9b7b66f62f 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSTypes.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSTypes.kt @@ -3,6 +3,8 @@ package org.lflang.generator.ts import org.lflang.ASTUtils import org.lflang.TimeValue import org.lflang.generator.TargetTypes +import org.lflang.generator.UnsupportedGeneratorFeatureException +import org.lflang.joinWithCommas import org.lflang.lf.StateVar object TSTypes : TargetTypes { @@ -41,11 +43,15 @@ object TSTypes : TargetTypes { } } - override fun getTargetFixedSizeListType(baseType: String, size: Int): String { - return "Array($size)<$baseType>" + override fun getTargetFixedSizeListType(baseType: String?, size: Int): String { + throw UnsupportedGeneratorFeatureException("TypeScript does not support fixed-size array types.") } override fun getTargetVariableSizeListType(baseType: String): String { - return "Array<$baseType>" + return "Array<$baseType>" // same as "$baseType[]" + } + + override fun getVariableSizeListInitExpression(contents: MutableList, withBraces: Boolean): String { + return contents.joinWithCommas("[", "]") } } From dae6685da494d969f782077e11384f9345b70a7e Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Thu, 3 Nov 2022 11:25:32 +0100 Subject: [PATCH 27/90] CI: install Python 3.10 on all platforms --- .github/workflows/ci.yml | 2 +- .github/workflows/py-tests.yml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f339b9aa9e..843e6a7e5f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -83,7 +83,7 @@ jobs: # Run the Python integration tests. py-tests: - uses: lf-lang/lingua-franca/.github/workflows/py-tests.yml@master + uses: lf-lang/lingua-franca/.github/workflows/py-tests.yml@fix-python-tests needs: cancel # Run the Rust integration tests. diff --git a/.github/workflows/py-tests.yml b/.github/workflows/py-tests.yml index 6119825e91..7102b9f542 100644 --- a/.github/workflows/py-tests.yml +++ b/.github/workflows/py-tests.yml @@ -33,7 +33,6 @@ jobs: uses: actions/setup-python@v4 with: python-version: '3.10' - if: ${{ runner.os == 'Windows' }} - name: Install dependencies OS X run: | brew install coreutils From c3be8f1a615a8edb2e695f4c442909b331ebb80f Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Thu, 3 Nov 2022 16:53:26 +0100 Subject: [PATCH 28/90] specify range of python versions to look for in cmake --- org.lflang/src/org/lflang/generator/python/PythonGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/org/lflang/generator/python/PythonGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonGenerator.java index 1b0cd44d98..e61144a26b 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonGenerator.java @@ -799,7 +799,7 @@ private static String setUpMainTarget(boolean hasMain, String executableName, St add_subdirectory(core) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}) set(LF_MAIN_TARGET ) - find_package(Python COMPONENTS Interpreter Development) + find_package(Python 3.7.0...<3.11.0 COMPONENTS Interpreter Development) Python_add_library( ${LF_MAIN_TARGET} MODULE From 7bb431b67a91ccccca0ac1c881ba80873ad18adf Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Fri, 4 Nov 2022 13:23:39 +0100 Subject: [PATCH 29/90] update minimum required cmake version --- org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java | 4 +++- org.lflang/src/org/lflang/generator/c/CCompiler.java | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java b/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java index 8b572f807c..07696a72df 100644 --- a/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java @@ -54,6 +54,8 @@ public class CCmakeGenerator { ) """; + public static final String MIN_CMAKE_VERSION = "3.19"; + private final FileConfig fileConfig; private final List additionalSources; private final SetUpMainTarget setUpMainTarget; @@ -115,7 +117,7 @@ CodeBuilder generateCMakeCode( additionalSources.addAll(this.additionalSources); cMakeCode.newLine(); - cMakeCode.pr("cmake_minimum_required(VERSION 3.13)"); + cMakeCode.pr("cmake_minimum_required(VERSION " + MIN_CMAKE_VERSION + ")"); cMakeCode.pr("project("+executableName+" LANGUAGES C)"); cMakeCode.newLine(); diff --git a/org.lflang/src/org/lflang/generator/c/CCompiler.java b/org.lflang/src/org/lflang/generator/c/CCompiler.java index 1c0145cd3d..504b8ec878 100644 --- a/org.lflang/src/org/lflang/generator/c/CCompiler.java +++ b/org.lflang/src/org/lflang/generator/c/CCompiler.java @@ -195,8 +195,9 @@ public LFCommand compileCmakeCommand() { buildPath); if (command == null) { errorReporter.reportError( - "The C/CCpp target requires CMAKE >= 3.5 to compile the generated code. " + - "Auto-compiling can be disabled using the \"no-compile: true\" target property."); + "The C/CCpp target requires CMAKE >= " + CCmakeGenerator.MIN_CMAKE_VERSION + + "3.5 to compile the generated code. " + + "Auto-compiling can be disabled using the \"no-compile: true\" target property."); } return command; } From 488021b9767ce04f4524989b7ef52947bfd81a05 Mon Sep 17 00:00:00 2001 From: "Edward A. Lee" Date: Sat, 5 Nov 2022 14:45:13 -0700 Subject: [PATCH 30/90] Make second deadline miss much less likely --- test/C/src/concurrent/DeadlineThreaded.lf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/C/src/concurrent/DeadlineThreaded.lf b/test/C/src/concurrent/DeadlineThreaded.lf index 158e955aff..03230fd2e0 100644 --- a/test/C/src/concurrent/DeadlineThreaded.lf +++ b/test/C/src/concurrent/DeadlineThreaded.lf @@ -14,7 +14,7 @@ reactor Source(period: time(1500 msec)) { if (2 * (self->count / 2) != self->count) { // The count variable is odd. // Take time to cause a deadline violation. - lf_nanosleep(MSEC(400)); + lf_nanosleep(MSEC(210)); } printf("Source sends: %d.\n", self->count); lf_set(y, self->count); @@ -38,7 +38,7 @@ reactor Destination(timeout: time(1 sec)) { printf("Destination deadline handler receives: %d\n", x->value); if (2 * (self->count / 2) == self->count) { // The count variable is even, so the deadline should not have been violated. - printf("ERROR: Deadline miss handler invoked without deadline violation.\n"); + printf("ERROR: Unexpected deadline violation.\n"); exit(2); } (self->count)++; From 8eec246ad0b46feb5e30c54d4e638e71a3e6dd40 Mon Sep 17 00:00:00 2001 From: "Edward A. Lee" Date: Sat, 5 Nov 2022 14:52:01 -0700 Subject: [PATCH 31/90] Increased period to make unexpected deadline violations even less likely --- test/C/src/concurrent/DeadlineThreaded.lf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/C/src/concurrent/DeadlineThreaded.lf b/test/C/src/concurrent/DeadlineThreaded.lf index 03230fd2e0..fccb60001b 100644 --- a/test/C/src/concurrent/DeadlineThreaded.lf +++ b/test/C/src/concurrent/DeadlineThreaded.lf @@ -2,10 +2,10 @@ // the Source immediately, whereas odd numbers are sent after a big enough delay // to violate the deadline. target C { - timeout: 3 sec + timeout: 6 sec } -reactor Source(period: time(1500 msec)) { +reactor Source(period: time(3000 msec)) { output y: int timer t(0, period) state count: int(0) From 9a5e318191341fc15328dac7ebac3d9f53bec3c5 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Sun, 6 Nov 2022 12:04:16 +0100 Subject: [PATCH 32/90] Fix error message Co-authored-by: Peter Donovan <33707478+petervdonovan@users.noreply.github.com> --- org.lflang/src/org/lflang/generator/c/CCompiler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/org/lflang/generator/c/CCompiler.java b/org.lflang/src/org/lflang/generator/c/CCompiler.java index 504b8ec878..ce5ab5f4e8 100644 --- a/org.lflang/src/org/lflang/generator/c/CCompiler.java +++ b/org.lflang/src/org/lflang/generator/c/CCompiler.java @@ -196,7 +196,7 @@ public LFCommand compileCmakeCommand() { if (command == null) { errorReporter.reportError( "The C/CCpp target requires CMAKE >= " + CCmakeGenerator.MIN_CMAKE_VERSION - + "3.5 to compile the generated code. " + + + " to compile the generated code. " + "Auto-compiling can be disabled using the \"no-compile: true\" target property."); } return command; From 586c441c74bd3f076d8eef0e4a886cfcff40e6ed Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Mon, 7 Nov 2022 14:02:26 +0100 Subject: [PATCH 33/90] Install correct Python version in LSP tests --- .github/workflows/lsp-tests.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/lsp-tests.yml b/.github/workflows/lsp-tests.yml index ba048dea77..fafdbedc81 100644 --- a/.github/workflows/lsp-tests.yml +++ b/.github/workflows/lsp-tests.yml @@ -59,6 +59,10 @@ jobs: brew install protobuf brew install protobuf-c if: ${{ runner.os == 'macOS' }} + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: '3.10' - name: Run language server Python tests without PyLint run: ./gradlew test --tests org.lflang.tests.lsp.LspTests.pythonValidationTestSyntaxOnly - name: Report to CodeCov From 8feb6ef5e3b05488c88ec792312edd725be18684 Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Mon, 7 Nov 2022 17:42:42 -0800 Subject: [PATCH 34/90] Try again to pass LSP tests. --- .github/workflows/README.md | 6 ++++++ .github/workflows/lsp-tests.yml | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/.github/workflows/README.md b/.github/workflows/README.md index f88a535ce9..d11093c691 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -44,3 +44,9 @@ If a repository has a text file that stores a ref (e.g., a SHA1 hash of a commit After workflow execution, the value of the output `ref` will be equal to the first line in the given `file`. ## Nightly Build See [nightly-build.yml](https://github.com/lf-lang/lingua-franca/blob/master/.github/workflows/nightly-build.yml). + +## Debugging tests + +To debug test failures that are difficult to reproduce locally, it can be useful +to add a step such as [this one](https://github.com/marketplace/actions/debugging-with-ssh) to SSH into the GitHub Actions runner. Such a debugging step +should not be included in the `master` version of the workflow file. diff --git a/.github/workflows/lsp-tests.yml b/.github/workflows/lsp-tests.yml index fafdbedc81..183db879f7 100644 --- a/.github/workflows/lsp-tests.yml +++ b/.github/workflows/lsp-tests.yml @@ -73,6 +73,10 @@ jobs: verbose: true - name: Install pylint run: python3 -m pip install pylint + if: ${{ runner.os != 'macOS' }} + - name: Install pylint macOS + run: brew install pylint + if: ${{ runner.os == 'macOS' }} - name: Run language server tests run: ./gradlew clean test --tests org.lflang.tests.lsp.LspTests.*ValidationTest - name: Report to CodeCov From afa0970dc6cff5671c3e4f9f7aa9341624481297 Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Mon, 7 Nov 2022 18:27:30 -0800 Subject: [PATCH 35/90] Update ci.yml. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 843e6a7e5f..4be7c81c42 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,7 +49,7 @@ jobs: # Run language server tests. lsp-tests: - uses: lf-lang/lingua-franca/.github/workflows/lsp-tests.yml@master + uses: lf-lang/lingua-franca/.github/workflows/lsp-tests.yml@fix-python-tests needs: cancel # Run the C integration tests. From 1cd63150b5a12672d9b6b963b54c02b5073dcb3b Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Tue, 8 Nov 2022 08:48:53 +0100 Subject: [PATCH 36/90] Update ci.yml --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4be7c81c42..f339b9aa9e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,7 +49,7 @@ jobs: # Run language server tests. lsp-tests: - uses: lf-lang/lingua-franca/.github/workflows/lsp-tests.yml@fix-python-tests + uses: lf-lang/lingua-franca/.github/workflows/lsp-tests.yml@master needs: cancel # Run the C integration tests. @@ -83,7 +83,7 @@ jobs: # Run the Python integration tests. py-tests: - uses: lf-lang/lingua-franca/.github/workflows/py-tests.yml@fix-python-tests + uses: lf-lang/lingua-franca/.github/workflows/py-tests.yml@master needs: cancel # Run the Rust integration tests. From 4cd389dcc4e483dd373b86ca5d366a1abf1a84ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 8 Nov 2022 12:15:48 +0100 Subject: [PATCH 37/90] Address review comments --- org.lflang/src/org/lflang/CommonExtensions.kt | 4 ++ .../org/lflang/generator/GeneratorUtils.kt | 14 ++++- .../generator/ts/TSImportPreambleGenerator.kt | 57 ++++++++++--------- 3 files changed, 45 insertions(+), 30 deletions(-) diff --git a/org.lflang/src/org/lflang/CommonExtensions.kt b/org.lflang/src/org/lflang/CommonExtensions.kt index f27570b7db..3e098d791c 100644 --- a/org.lflang/src/org/lflang/CommonExtensions.kt +++ b/org.lflang/src/org/lflang/CommonExtensions.kt @@ -158,6 +158,10 @@ fun Iterable.joinWithCommasLn( transform: (T) -> CharSequence = { it.toString() } ): String = joinWithCommas(prefix, postfix, skipLines = true, trailing, transform) +/** + * Join the elements of [this] sequence with newlines. The + * [prefix] and [postfix] are added even if this iterable is empty. + */ fun Iterable.joinWithLn( prefix: CharSequence = "", postfix: CharSequence = "", diff --git a/org.lflang/src/org/lflang/generator/GeneratorUtils.kt b/org.lflang/src/org/lflang/generator/GeneratorUtils.kt index b64e29913f..e734467e10 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorUtils.kt +++ b/org.lflang/src/org/lflang/generator/GeneratorUtils.kt @@ -49,11 +49,17 @@ fun EObject.locationInfo(): LocationInfo { } +/** + * Returns the target code for the initial value of [sv]. + */ fun TargetTypes.getTargetInitializer(sv: StateVar): TargetCode = this.getTargetInitializer(sv.init, sv.type, sv.braces.isNotEmpty()) -fun TargetTypes.getTargetInitializer(sv: Parameter): TargetCode = - this.getTargetInitializer(sv.init, sv.type, sv.isInitWithBraces) +/** + * Returns the target code for the default value of the [param]. + */ +fun TargetTypes.getTargetInitializer(param: Parameter): TargetCode = + this.getTargetInitializer(param.init, param.type, param.isInitWithBraces) /** * Returns the target code for the [getActualValue] of the @@ -76,6 +82,10 @@ fun Instantiation.getActualValue(param: Parameter): List = ?: throw InvalidLfSourceException(this, "No value for parameter ${param.name}") +/** + * Returns the target code for the given expression, given + * that it's a time expression. + */ fun TargetTypes.getTargetTimeExpr(v: Expression): TargetCode = this.getTargetExpr(v, InferredType.time()) diff --git a/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt index 3a261186fe..3b34214a78 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt @@ -26,8 +26,8 @@ package org.lflang.generator.ts import org.lflang.generator.PrependOperator +import org.lflang.joinWithLn import java.nio.file.Path -import java.util.* /** * Preamble generator for imports in TypeScript target. @@ -48,48 +48,49 @@ class TSImportPreambleGenerator( * Default imports for importing all the core classes and helper classes * for CLI argument handling. */ - const val DEFAULT_IMPORTS = """ - |import commandLineArgs from 'command-line-args' - |import commandLineUsage from 'command-line-usage' - |import {Parameter as __Parameter, Timer as __Timer, Reactor as __Reactor, App as __App} from '@lf-lang/reactor-ts' - |import {Action as __Action, Startup as __Startup, FederatePortAction as __FederatePortAction} from '@lf-lang/reactor-ts' - |import {Bank as __Bank} from '@lf-lang/reactor-ts' - |import {FederatedApp as __FederatedApp} from '@lf-lang/reactor-ts' - |import {InPort as __InPort, OutPort as __OutPort, Port as __Port, WritablePort as __WritablePort, WritableMultiPort as __WritableMultiPort} from '@lf-lang/reactor-ts' - |import {InMultiPort as __InMultiPort, OutMultiPort as __OutMultiPort} from '@lf-lang/reactor-ts' - |import {Reaction as __Reaction} from '@lf-lang/reactor-ts' - |import {State as __State} from '@lf-lang/reactor-ts' - |import {TimeUnit, TimeValue, Tag as __Tag, Origin as __Origin} from '@lf-lang/reactor-ts' - |import {Args as __Args, Variable as __Variable, Triggers as __Triggers, Present, Read, Write, ReadWrite, MultiReadWrite, Sched} from '@lf-lang/reactor-ts' - |import {Log} from '@lf-lang/reactor-ts' - |import {ProcessedCommandLineArgs as __ProcessedCommandLineArgs, CommandLineOptionDefs as __CommandLineOptionDefs, CommandLineUsageDefs as __CommandLineUsageDefs, CommandLineOptionSpec as __CommandLineOptionSpec, unitBasedTimeValueCLAType as __unitBasedTimeValueCLAType, booleanCLAType as __booleanCLAType} from '@lf-lang/reactor-ts' - |""" + const val DEFAULT_IMPORTS = """ + import commandLineArgs from 'command-line-args' + import commandLineUsage from 'command-line-usage' + import {Parameter as __Parameter, Timer as __Timer, Reactor as __Reactor, App as __App} from '@lf-lang/reactor-ts' + import {Action as __Action, Startup as __Startup, FederatePortAction as __FederatePortAction} from '@lf-lang/reactor-ts' + import {Bank as __Bank} from '@lf-lang/reactor-ts' + import {FederatedApp as __FederatedApp} from '@lf-lang/reactor-ts' + import {InPort as __InPort, OutPort as __OutPort, Port as __Port, WritablePort as __WritablePort, WritableMultiPort as __WritableMultiPort} from '@lf-lang/reactor-ts' + import {InMultiPort as __InMultiPort, OutMultiPort as __OutMultiPort} from '@lf-lang/reactor-ts' + import {Reaction as __Reaction} from '@lf-lang/reactor-ts' + import {State as __State} from '@lf-lang/reactor-ts' + import {TimeUnit, TimeValue, Tag as __Tag, Origin as __Origin} from '@lf-lang/reactor-ts' + import {Args as __Args, Variable as __Variable, Triggers as __Triggers, Present, Read, Write, ReadWrite, MultiReadWrite, Sched} from '@lf-lang/reactor-ts' + import {Log} from '@lf-lang/reactor-ts' + import {ProcessedCommandLineArgs as __ProcessedCommandLineArgs, CommandLineOptionDefs as __CommandLineOptionDefs, CommandLineUsageDefs as __CommandLineUsageDefs, CommandLineOptionSpec as __CommandLineOptionSpec, unitBasedTimeValueCLAType as __unitBasedTimeValueCLAType, booleanCLAType as __booleanCLAType} from '@lf-lang/reactor-ts' + """ } private fun generateDefaultImports(): String { - return """ + return with(PrependOperator) { + """ |// Code generated by the Lingua Franca compiler from: |// file:/$filePath - $DEFAULT_IMPORTS +${" |"..DEFAULT_IMPORTS} | - """.trimMargin() + """.trimMargin() + } } private fun generateProtoPreamble(): String { - val protoFileImports = StringJoiner("\n") - for (file in protoFiles) { + val protoFileImports = protoFiles.joinWithLn { file -> // Remove any extension the file name may have. val name = file.substringBeforeLast('.') - protoFileImports.add(""" - |import * as $name from "./${name}_pb" - """.trimMargin()) + "import * as $name from \"./${name}_pb\"" } - return """ + return with(PrependOperator) { + """ |// Imports for protocol buffers - |$protoFileImports +${" |"..protoFileImports} | | - """.trimMargin() + """.trimMargin() + } } fun generatePreamble(): String { From 19f1b69ce11c3a92a1ce32175491fdd5b44e214a Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Tue, 8 Nov 2022 17:28:26 +0100 Subject: [PATCH 38/90] fix setting build types from the command line --- org.lflang/src/org/lflang/generator/GeneratorUtils.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/org.lflang/src/org/lflang/generator/GeneratorUtils.java b/org.lflang/src/org/lflang/generator/GeneratorUtils.java index 2b87eecf74..d0f6978010 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorUtils.java +++ b/org.lflang/src/org/lflang/generator/GeneratorUtils.java @@ -19,12 +19,15 @@ import org.eclipse.xtext.validation.CheckMode; import org.eclipse.xtext.validation.IResourceValidator; import org.eclipse.xtext.validation.Issue; + +import org.lflang.ASTUtils; import org.lflang.ErrorReporter; import org.lflang.FileConfig; import org.lflang.Target; import org.lflang.TargetConfig; import org.lflang.TargetProperty.BuildType; import org.lflang.TargetProperty.LogLevel; +import org.lflang.TargetProperty.UnionType; import org.lflang.generator.LFGeneratorContext.Mode; import org.lflang.TargetProperty; import org.lflang.TargetProperty.SchedulerOption; @@ -83,7 +86,7 @@ public static void setTargetConfig( targetConfig.noCompile = true; } if (context.getArgs().containsKey("build-type")) { - targetConfig.cmakeBuildType = BuildType.valueOf(context.getArgs().getProperty("build-type").toUpperCase()); + targetConfig.cmakeBuildType = (BuildType) UnionType.BUILD_TYPE_UNION.forName(context.getArgs().getProperty("build-type")); } if (context.getArgs().containsKey("logging")) { targetConfig.logLevel = LogLevel.valueOf(context.getArgs().getProperty("logging").toUpperCase()); From 2978aa2f89021561db37e4df3963e5184066b946 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Tue, 8 Nov 2022 11:34:38 -0800 Subject: [PATCH 39/90] Apply suggestions from code review --- org.lflang/src/org/lflang/generator/GeneratorUtils.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/GeneratorUtils.kt b/org.lflang/src/org/lflang/generator/GeneratorUtils.kt index e734467e10..6d11501b80 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorUtils.kt +++ b/org.lflang/src/org/lflang/generator/GeneratorUtils.kt @@ -71,7 +71,7 @@ fun TargetTypes.getTargetInitializer(param: Parameter, inst: Instantiation): Tar } /** - * Returns the actual value of a parameter for the given instantiation. + * Return the actual value of a parameter for the given instantiation. * The value is defaulted to the default value for the parameter if * there is no explicit assignment. If there is no default value, the * source code is invalid (param is required) @@ -83,12 +83,12 @@ fun Instantiation.getActualValue(param: Parameter): List = /** - * Returns the target code for the given expression, given + * Return the target code for the given expression, given * that it's a time expression. */ fun TargetTypes.getTargetTimeExpr(v: Expression): TargetCode = this.getTargetExpr(v, InferredType.time()) -/** If this is null, returns the literal 0. */ +/** If this is null, return the literal 0. */ fun Expression?.orZero(): Expression = this ?: LfFactory.eINSTANCE.createLiteral().apply { literal = "0" } From c6ea5be923e358598c61e3104465d9636eba2f08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 9 Nov 2022 16:20:34 +0100 Subject: [PATCH 40/90] Bump runtime, fix tests --- org.lflang/src/lib/rs/reactor-rs | 2 +- test/Rust/src/multiport/ConnectionToSelfBank.lf | 2 +- test/Rust/src/multiport/ConnectionToSelfMultiport.lf | 2 +- test/Rust/src/multiport/CycledLhs_Single.lf | 4 ++-- test/Rust/src/multiport/FullyConnected.lf | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/org.lflang/src/lib/rs/reactor-rs b/org.lflang/src/lib/rs/reactor-rs index 530fec8f91..8b0a8a0448 160000 --- a/org.lflang/src/lib/rs/reactor-rs +++ b/org.lflang/src/lib/rs/reactor-rs @@ -1 +1 @@ -Subproject commit 530fec8f911ad9a03297b1c17e4faeb339979b1c +Subproject commit 8b0a8a0448fe70dcf75f8bb9b43c60f5c37a9fac diff --git a/test/Rust/src/multiport/ConnectionToSelfBank.lf b/test/Rust/src/multiport/ConnectionToSelfBank.lf index 6467fa445a..d1741368fb 100644 --- a/test/Rust/src/multiport/ConnectionToSelfBank.lf +++ b/test/Rust/src/multiport/ConnectionToSelfBank.lf @@ -9,7 +9,7 @@ reactor Node(bank_index: usize(0), num_nodes: usize(4)) { reaction(startup) -> out {= ctx.set(out, self.bank_index); =} reaction(in) {= - let count = r#in.into_iter().filter(|p| ctx.is_present(p)).count(); + let count = r#in.into_iter().filter(|&p| ctx.is_present(p)).count(); assert_eq!(count, self.num_nodes); println!("success") =} diff --git a/test/Rust/src/multiport/ConnectionToSelfMultiport.lf b/test/Rust/src/multiport/ConnectionToSelfMultiport.lf index 3618f7c881..126e3f9ad4 100644 --- a/test/Rust/src/multiport/ConnectionToSelfMultiport.lf +++ b/test/Rust/src/multiport/ConnectionToSelfMultiport.lf @@ -13,7 +13,7 @@ reactor Node(num_nodes: usize(4)) { =} reaction(in) {= - let count = r#in.iter().filter(|p| ctx.is_present(p)).count(); + let count = r#in.iter().filter(|&p| ctx.is_present(p)).count(); assert_eq!(count, self.num_nodes); println!("success") =} diff --git a/test/Rust/src/multiport/CycledLhs_Single.lf b/test/Rust/src/multiport/CycledLhs_Single.lf index dddf53dd47..289bd85159 100644 --- a/test/Rust/src/multiport/CycledLhs_Single.lf +++ b/test/Rust/src/multiport/CycledLhs_Single.lf @@ -16,8 +16,8 @@ reactor Test { reaction(act) -> out {= let (a, b) = ctx.get(act).unwrap(); - ctx.set(out[0], a); - ctx.set(out[1], b); + ctx.set(&mut out[0], a); + ctx.set(&mut out[1], b); =} reaction(in) -> act {= diff --git a/test/Rust/src/multiport/FullyConnected.lf b/test/Rust/src/multiport/FullyConnected.lf index 06dee6a309..3a97feacf9 100644 --- a/test/Rust/src/multiport/FullyConnected.lf +++ b/test/Rust/src/multiport/FullyConnected.lf @@ -13,7 +13,7 @@ reactor Right(bank_index: usize(0), num_nodes: usize(4)) { state num_nodes(num_nodes) reaction(in) {= - let count = r#in.iter().filter(|p| ctx.is_present(p)).count(); + let count = r#in.iter().filter(|&p| ctx.is_present(p)).count(); assert_eq!(count, self.num_nodes); println!("success") =} From 971bf35f1d65cc5aa61955b2c12d00f1c9d5df3c Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Wed, 9 Nov 2022 12:26:47 +0100 Subject: [PATCH 41/90] update reactor-cpp --- org.lflang/src/lib/cpp/reactor-cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/cpp/reactor-cpp b/org.lflang/src/lib/cpp/reactor-cpp index 79422a7b1a..196ef2cc4b 160000 --- a/org.lflang/src/lib/cpp/reactor-cpp +++ b/org.lflang/src/lib/cpp/reactor-cpp @@ -1 +1 @@ -Subproject commit 79422a7b1ad246935a0165d971cedfac88904071 +Subproject commit 196ef2cc4b69a6e28f10b0d2674fddcc391a69bd From 8032bc1ac9c14af2b5b23b8857757b7ca7b50e18 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Wed, 9 Nov 2022 19:52:54 +0100 Subject: [PATCH 42/90] Added Gradle build support for language server --- gradle.properties | 1 + org.lflang.diagram/build.gradle | 108 ++++++++++++++++++++++++++++++++ org.lflang.ide/build.gradle | 47 +------------- settings.gradle | 2 + 4 files changed, 112 insertions(+), 46 deletions(-) create mode 100644 org.lflang.diagram/build.gradle diff --git a/gradle.properties b/gradle.properties index ed77fa4c11..4006c4b3ed 100644 --- a/gradle.properties +++ b/gradle.properties @@ -20,6 +20,7 @@ shadowJarVersion=7.1.2 spotlessVersion=6.11.0 xtextGradleVersion=3.0.0 xtextVersion=2.28.0 +klighdVersion=2.2.1-SNAPSHOT [manifestPropertyNames] org.eclipse.xtext=xtextVersion diff --git a/org.lflang.diagram/build.gradle b/org.lflang.diagram/build.gradle new file mode 100644 index 0000000000..bd14ca4a2c --- /dev/null +++ b/org.lflang.diagram/build.gradle @@ -0,0 +1,108 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + +repositories { + mavenCentral() + // TODO Remove this in-official maven repository as soon as Klighd is released to maven central in the future. + maven { + url "https://rtsys.informatik.uni-kiel.de/~kieler/files/repo/" + } +} +dependencies { + implementation project(':org.lflang') + implementation project(':org.lflang.ide') + implementation "org.eclipse.xtext:org.eclipse.xtext.ide:${xtextVersion}" + implementation ("de.cau.cs.kieler.klighd:de.cau.cs.kieler.klighd.lsp:${klighdVersion}") { + exclude group: 'org.eclipse.platform', module: 'org.eclipse.swt.*' + exclude group: 'org.eclipse.platform', module: 'org.eclipse.swt' + } + implementation ("de.cau.cs.kieler.klighd:de.cau.cs.kieler.klighd.standalone:${klighdVersion}") { + exclude group: 'org.eclipse.platform', module: 'org.eclipse.swt.*' + exclude group: 'org.eclipse.platform', module: 'org.eclipse.swt' + } +} + +apply plugin: 'application' +apply plugin: 'com.github.johnrengelman.shadow' + +task generateLanguageDiagramServer { + description 'Creates a jar that implements a language server with diagram support for LF.' + + apply plugin: 'java' + apply plugin: 'application' + apply plugin: 'com.github.johnrengelman.shadow' + + mainClassName = "org.lflang.diagram.lsp.LanguageDiagramServer" + + compileJava { + options.compilerArgs << '-Xlint:unchecked' + } + + shadowJar { + classifier = 'lds' + + // Handling of service loader registrations via META-INF/services/* + mergeServiceFiles() + + // Merge properties + transform(com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer){ + resource = 'plugin.properties' + } + + // Exclude files that are known to be dispensable for a language server + exclude( + '*._trace', + '*.ecore', + '*.ecorediag', + '*.g', + '*.genmodel', + '*.html', + '*.mwe2', + '*.profile', + '*.xtext', + '*readme.txt', + '.api_description', + '.options', + 'about.*', + 'about_*', + 'about_files/*', + 'ant_tasks/*', + 'cheatsheets/*', + 'com/*/*.java', + 'de/*/*.java', + 'docs/*', + 'log4j.properties', + 'META-INF/*.DSA', + 'META-INF/*.RSA', + 'META-INF/*.SF', + 'META-INF/changelog.txt', + 'META-INF/DEPENDENCIES', + 'META-INF/eclipse.inf', + 'META-INF/INDEX.LIST', + 'META-INF/maven/*', + 'META-INF/NOTICE', + 'META-INF/NOTICE.txt', + 'META-INF/p2.inf', + 'META-INF/versions/*/module-info.class', + 'modeling32.png', + 'module-info.class', + 'org/*/*.java', + 'OSGI-INF/l10n/bundle.properties', + 'plugin.xml', + 'profile.list', + 'schema/*', + 'systembundle.properties', + 'xtend-gen/*', + 'xtext32.png', + ) + + // Minimizing should be enabled with caution because some classes are only + // loaded via services (e.g. diagram syntheses and some xtext aspects) + // and would be removed when minimized + // minimize() { + // exclude(dependency('log4j:log4j:.*')) + // exclude(dependency('org.eclipse.xtext:.*ide:.*')) + // } + } +} + +generateLanguageDiagramServer.finalizedBy shadowJar diff --git a/org.lflang.ide/build.gradle b/org.lflang.ide/build.gradle index 8e89890fcf..cdfb70a334 100644 --- a/org.lflang.ide/build.gradle +++ b/org.lflang.ide/build.gradle @@ -1,52 +1,7 @@ -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar dependencies { implementation project(':org.lflang') implementation "org.eclipse.xtext:org.eclipse.xtext.ide:${xtextVersion}" implementation "org.eclipse.xtext:org.eclipse.xtext.xbase.ide:${xtextVersion}" } -apply plugin: 'application' -apply plugin: 'com.github.johnrengelman.shadow' - -task generateLanguageServer { - description 'Creates a jar file that implements a language server.' - - apply plugin: 'java' - apply plugin: 'application' - apply plugin: 'com.github.johnrengelman.shadow' - mainClassName = "org.eclipse.xtext.ide.server.ServerLauncher" - - shadowJar { - classifier = 'ls' - minimize() { - exclude(dependency('log4j:log4j:.*')) - exclude(dependency('org.eclipse.xtext:.*ide:.*')) - } - - exclude('META-INF/INDEX.LIST', 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA', 'schema/*', - '.options', '.api_description', '*.profile', '*.html', 'about.*', 'about_files/*', 'ant_tasks/*', - 'plugin.xml', 'modeling32.png', 'xtext32.png', 'systembundle.properties', 'profile.list') - - transform(com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer){ - resource = 'plugin.properties' - } - } - - compileJava { - options.compilerArgs << '-Xlint:unchecked' - } -} - -generateLanguageServer.finalizedBy shadowJar - -task testJar(type: ShadowJar) { - mainClassName = "org.eclipse.xtext.ide.server.ServerLauncher" - //minimize() // Minimizing might cause problems because it could delete classes that are not referenced but loaded by name (service loader) - from(project.convention.getPlugin(JavaPluginConvention).sourceSets.main.output) - configurations = [project.configurations.runtime] - exclude('META-INF/INDEX.LIST', 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA','schema/*', - '.options', '.api_description', '*.profile', '*.html', 'about.*', 'about_files/*', - 'plugin.xml', 'modeling32.png', 'systembundle.properties', 'profile.list') - classifier = 'ls' - append('plugin.properties') -} +apply plugin: 'application' \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 3e48c953bb..a6d734cb8f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,5 @@ include 'org.lflang' +include 'org.lflang.ide' +include 'org.lflang.diagram' include 'org.lflang.cli' include 'org.lflang.tests' From 7ad7028400ca778b6978adb96796fe12dcf47908 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Wed, 9 Nov 2022 19:55:14 +0100 Subject: [PATCH 43/90] diagrams: Disabled reactor icons functionality Klighd currently does not support user images in a language diagram server setting. --- .../diagram/synthesis/util/ReactorIcons.java | 131 +++++++++--------- 1 file changed, 68 insertions(+), 63 deletions(-) diff --git a/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ReactorIcons.java b/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ReactorIcons.java index 71f30e491b..297abb4dcc 100644 --- a/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ReactorIcons.java +++ b/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ReactorIcons.java @@ -27,8 +27,8 @@ import java.io.InputStream; import java.util.HashMap; -import org.eclipse.swt.graphics.ImageData; -import org.eclipse.swt.graphics.ImageLoader; +//import org.eclipse.swt.graphics.ImageData; +//import org.eclipse.swt.graphics.ImageLoader; import org.eclipse.xtext.xbase.lib.Extension; import org.lflang.ASTUtils; import org.lflang.AttributeUtils; @@ -57,10 +57,10 @@ public class ReactorIcons extends AbstractSynthesisExtensions { @Inject @Extension private KRenderingExtensions _kRenderingExtensions; @Inject @Extension private KContainerRenderingExtensions _kContainerRenderingExtensions; - private static final ImageLoader LOADER = new ImageLoader(); +// private static final ImageLoader LOADER = new ImageLoader(); // Image cache during synthesis - private final HashMap cache = new HashMap<>(); +// private final HashMap cache = new HashMap<>(); // Error message private String error = null; @@ -83,68 +83,73 @@ public void handleIcon(KContainerRendering rendering, ReactorDecl reactor, boole if (iconLocation == null) { error = "Cannot find given icon file."; } else { - ImageData data = loadImage(iconLocation); - if (data != null) { - KRectangle figure = _kContainerRenderingExtensions.addRectangle(rendering); - _kRenderingExtensions.setInvisible(figure, true); - KGridPlacementData figurePlacement = _kRenderingExtensions.setGridPlacementData(figure, data.width, data.height); - _kRenderingExtensions.to( - _kRenderingExtensions.from( - figurePlacement, - _kRenderingExtensions.LEFT, 3, 0, - _kRenderingExtensions.TOP, 0, 0), - _kRenderingExtensions.RIGHT, 3, 0, - _kRenderingExtensions.BOTTOM, 3, 0); - - KRectangle icon = _kContainerRenderingExtensions.addRectangle(figure); - _kRenderingExtensions.setInvisible(icon, true); - _kContainerRenderingExtensions.addImage(icon, data); - _kRenderingExtensions.setPointPlacementData(icon, - _kRenderingExtensions.createKPosition( - _kRenderingExtensions.LEFT, 0, 0.5f, - _kRenderingExtensions.TOP, 0, 0.5f), - _kRenderingExtensions.H_CENTRAL, _kRenderingExtensions.V_CENTRAL, 0, - 0, data.width, data.height); - } - if (error != null) { - var errorText = _kContainerRenderingExtensions.addText(rendering, "Icon not found!\n"+error); - _kRenderingExtensions.setForeground(errorText, Colors.RED); - _kRenderingExtensions.setFontBold(errorText, true); - _kRenderingExtensions.setSurroundingSpaceGrid(errorText, 8, 0); - } + /* + * This code was disabled because it cannot be compiled for the language server with Gradle. + * As soon as the Klighd API is extended to support URI-based images in both Eclipse and VSCode, + * this code should be reactivated and adapted. + */ +// ImageData data = loadImage(iconLocation); +// if (data != null) { +// KRectangle figure = _kContainerRenderingExtensions.addRectangle(rendering); +// _kRenderingExtensions.setInvisible(figure, true); +// KGridPlacementData figurePlacement = _kRenderingExtensions.setGridPlacementData(figure, data.width, data.height); +// _kRenderingExtensions.to( +// _kRenderingExtensions.from( +// figurePlacement, +// _kRenderingExtensions.LEFT, 3, 0, +// _kRenderingExtensions.TOP, 0, 0), +// _kRenderingExtensions.RIGHT, 3, 0, +// _kRenderingExtensions.BOTTOM, 3, 0); +// +// KRectangle icon = _kContainerRenderingExtensions.addRectangle(figure); +// _kRenderingExtensions.setInvisible(icon, true); +// _kContainerRenderingExtensions.addImage(icon, data); +// _kRenderingExtensions.setPointPlacementData(icon, +// _kRenderingExtensions.createKPosition( +// _kRenderingExtensions.LEFT, 0, 0.5f, +// _kRenderingExtensions.TOP, 0, 0.5f), +// _kRenderingExtensions.H_CENTRAL, _kRenderingExtensions.V_CENTRAL, 0, +// 0, data.width, data.height); +// } +// if (error != null) { +// var errorText = _kContainerRenderingExtensions.addText(rendering, "Icon not found!\n"+error); +// _kRenderingExtensions.setForeground(errorText, Colors.RED); +// _kRenderingExtensions.setFontBold(errorText, true); +// _kRenderingExtensions.setSurroundingSpaceGrid(errorText, 8, 0); +// } } } } - private ImageData loadImage(final java.net.URI uri) { - try { - if (cache.containsKey(uri)) { - return cache.get(uri); - } - synchronized (LOADER) { - InputStream inStream = null; - try { - inStream = uri.toURL().openStream(); - ImageData[] data = LOADER.load(inStream); - if (data != null && data.length > 0) { - ImageData img = data[0]; - cache.put(uri, img); - return img; - } else { - error = "Could not load icon image."; - return null; - } - } finally { - if (inStream != null) { - inStream.close(); - } - } - } - } catch (Exception ex) { - ex.printStackTrace(); - error = "Could not load icon image."; - return null; - } - } +// private ImageData loadImage(final java.net.URI uri) { +// try { +// if (cache.containsKey(uri)) { +// return cache.get(uri); +// } +// synchronized (LOADER) { +// InputStream inStream = null; +// try { +// inStream = uri.toURL().openStream(); +// ImageData[] data = LOADER.load(inStream); +// if (data != null && data.length > 0) { +// ImageData img = data[0]; +// cache.put(uri, img); +// return img; +// } else { +// error = "Could not load icon image."; +// return null; +// } +// } finally { +// if (inStream != null) { +// inStream.close(); +// } +// } +// } +// } catch (Exception ex) { +// ex.printStackTrace(); +// error = "Could not load icon image."; +// return null; +// } +// } } From bb6138fd5c53750a52c3b3cc48a60cb4bac7f74c Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Wed, 9 Nov 2022 19:56:23 +0100 Subject: [PATCH 44/90] diagrams: Moved service loader registration into source folder to work with new Gradle build --- .../META-INF/services/de.cau.cs.kieler.klighd.IKlighdStartupHook | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename org.lflang.diagram/{ => src}/META-INF/services/de.cau.cs.kieler.klighd.IKlighdStartupHook (100%) diff --git a/org.lflang.diagram/META-INF/services/de.cau.cs.kieler.klighd.IKlighdStartupHook b/org.lflang.diagram/src/META-INF/services/de.cau.cs.kieler.klighd.IKlighdStartupHook similarity index 100% rename from org.lflang.diagram/META-INF/services/de.cau.cs.kieler.klighd.IKlighdStartupHook rename to org.lflang.diagram/src/META-INF/services/de.cau.cs.kieler.klighd.IKlighdStartupHook From 803777fd5e1cdde84e52609e657e3d0a027f8118 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Wed, 9 Nov 2022 19:57:26 +0100 Subject: [PATCH 45/90] diagrams: Added workaround for Klighd/Xtext injection error in LDS. --- org.lflang.diagram/META-INF/MANIFEST.MF | 1 + .../diagram/lsp/LanguageDiagramServer.java | 20 ++++++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/org.lflang.diagram/META-INF/MANIFEST.MF b/org.lflang.diagram/META-INF/MANIFEST.MF index ac3c71aa3e..b11524c688 100644 --- a/org.lflang.diagram/META-INF/MANIFEST.MF +++ b/org.lflang.diagram/META-INF/MANIFEST.MF @@ -9,6 +9,7 @@ Bundle-ActivationPolicy: lazy Require-Bundle: de.cau.cs.kieler.klighd;bundle-version="2.2.0", de.cau.cs.kieler.klighd.krendering.extensions;bundle-version="2.2.0", de.cau.cs.kieler.klighd.lsp;bundle-version="2.2.0", + de.cau.cs.kieler.kgraph.text;bundle-version="2.2.0", com.google.guava, org.eclipse.xtext, org.eclipse.xtext.xbase.lib, diff --git a/org.lflang.diagram/src/org/lflang/diagram/lsp/LanguageDiagramServer.java b/org.lflang.diagram/src/org/lflang/diagram/lsp/LanguageDiagramServer.java index 439f290b7c..cdb3da5db0 100644 --- a/org.lflang.diagram/src/org/lflang/diagram/lsp/LanguageDiagramServer.java +++ b/org.lflang.diagram/src/org/lflang/diagram/lsp/LanguageDiagramServer.java @@ -2,11 +2,20 @@ import java.util.List; -import org.eclipse.xtext.ide.server.LanguageServerImpl; +import org.eclipse.xtext.Constants; +import org.eclipse.xtext.IGrammarAccess; import org.eclipse.xtext.ide.server.ILanguageServerExtension; +import org.eclipse.xtext.ide.server.LanguageServerImpl; import org.eclipse.xtext.service.AbstractGenericModule; +import org.eclipse.xtext.util.Modules2; +import org.lflang.generator.LanguageServerErrorReporter; +import org.lflang.ide.LFIdeSetup; + import com.google.inject.Module; +import com.google.inject.name.Names; import com.google.inject.util.Modules; + +import de.cau.cs.kieler.kgraph.text.services.KGraphGrammarAccess; import de.cau.cs.kieler.klighd.lsp.KGraphLanguageClient; import de.cau.cs.kieler.klighd.lsp.interactive.layered.LayeredInteractiveLanguageServerExtension; import de.cau.cs.kieler.klighd.lsp.interactive.rectpacking.RectpackingInteractiveLanguageServerExtension; @@ -16,9 +25,6 @@ import de.cau.cs.kieler.klighd.lsp.launch.ILanguageRegistration; import de.cau.cs.kieler.klighd.lsp.launch.Language; -import org.lflang.ide.LFIdeSetup; -import org.lflang.generator.LanguageServerErrorReporter; - /** * Language server with extended diagram communication. * @@ -30,10 +36,14 @@ private static class LFLsCreator extends AbstractLsCreator { @Override public Module createLSModules(boolean socket) { - return Modules.override(super.createLSModules(socket)).with(new AbstractGenericModule() { + return Modules2.mixin(Modules.override(super.createLSModules(socket)).with(new AbstractGenericModule() { public Class bindLanguageServerImpl() { return LFLanguageServer.class; } + }), it -> { + // Temporary fix for an issue of Klighd with Xtext 2.28 (https://github.com/kieler/KLighD/issues/144) + it.bind(IGrammarAccess.class).to(KGraphGrammarAccess.class); + it.bind(String.class).annotatedWith(Names.named(Constants.LANGUAGE_NAME)).toInstance("de.cau.cs.kieler.kgraph.text.KGraph"); }); } From f80bd16728ba040e74853bc03f6c04bad4798390 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 10 Nov 2022 16:39:01 +0100 Subject: [PATCH 46/90] Refactor entry point for testability --- .../src/org/lflang/tests/cli/LfcCliTest.java | 11 +++ org.lflang/src/org/lflang/cli/Lfc.java | 80 +++++++++++++------ .../src/org/lflang/cli/ReportingUtil.kt | 49 +++++++++++- 3 files changed, 111 insertions(+), 29 deletions(-) create mode 100644 org.lflang.tests/src/org/lflang/tests/cli/LfcCliTest.java diff --git a/org.lflang.tests/src/org/lflang/tests/cli/LfcCliTest.java b/org.lflang.tests/src/org/lflang/tests/cli/LfcCliTest.java new file mode 100644 index 0000000000..65ff739ad5 --- /dev/null +++ b/org.lflang.tests/src/org/lflang/tests/cli/LfcCliTest.java @@ -0,0 +1,11 @@ +package org.lflang.tests.cli; + +/** + * @author Clément Fournier + */ +public class LfcCliTest { + + + + +} diff --git a/org.lflang/src/org/lflang/cli/Lfc.java b/org.lflang/src/org/lflang/cli/Lfc.java index 4487954aab..8f1f8031a7 100644 --- a/org.lflang/src/org/lflang/cli/Lfc.java +++ b/org.lflang/src/org/lflang/cli/Lfc.java @@ -4,6 +4,8 @@ package org.lflang.cli; +import java.io.PrintStream; +import java.io.PrintWriter; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -132,7 +134,7 @@ public static Options getOptions() { public static List