diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 48110ac926..28d049634a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,7 @@ jobs: # Run the unit tests. unit-tests: - uses: lf-lang/lingua-franca/.github/workflows/unit-tests.yml@master + uses: lf-lang/lingua-franca/.github/workflows/unit-tests.yml@bodyless-reactions needs: cancel # Run tests for the standalone compiler. @@ -51,6 +51,8 @@ jobs: uses: lf-lang/benchmarks-lingua-franca/.github/workflows/benchmark-tests.yml@main with: target: 'C' + benchmarks-ref: 'c-separate-gen-files' + compiler-ref: bodyless-reactions needs: cancel # Run language server tests. diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 6c2704629e..1caec5c1ac 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -12,6 +12,7 @@ jobs: steps: - uses: actions/checkout@v3 with: + submodules: true fetch-depth: 0 - name: Prepare build environment uses: ./.github/actions/prepare-build-env diff --git a/org.lflang.tests/resources/org/lflang/tests/cli/issue490.stderr b/org.lflang.tests/resources/org/lflang/tests/cli/issue490.stderr index edf3254755..fdaa4f8e0e 100644 --- a/org.lflang.tests/resources/org/lflang/tests/cli/issue490.stderr +++ b/org.lflang.tests/resources/org/lflang/tests/cli/issue490.stderr @@ -6,23 +6,19 @@ lfc: error: Name of main reactor must match the file name (or be omitted). | ^ Name of main reactor must match the file name (or be omitted). | 5 | state liss(2, 3); - -lfc: error: missing '{=' at '{' +lfc: error: no viable alternative at input '{' --> %%%PATH.lf%%%:6:22 | 5 | state liss(2, 3); 6 | reaction (startup) { - | ^ missing '{=' at '{' + | ^ no viable alternative at input '{' | 7 | print(self.liss) - -lfc: error: mismatched input '' expecting '=}' - --> %%%PATH.lf%%%:9:2 - | - 8 | } - | >>>>>>>>>>>>>> - 9 | } -10 | - | < mismatched input '' expecting '=}' -11 | - +lfc: error: no viable alternative at input '(' +--> %%%PATH.lf%%%:7:5 + | +6 | reaction (startup) { +7 | print(self.liss) + | ^^^^^ no viable alternative at input '(' + | +8 | } diff --git a/org.lflang.tests/src/org/lflang/tests/TestRegistry.java b/org.lflang.tests/src/org/lflang/tests/TestRegistry.java index 232e735641..10fa68acaf 100644 --- a/org.lflang.tests/src/org/lflang/tests/TestRegistry.java +++ b/org.lflang.tests/src/org/lflang/tests/TestRegistry.java @@ -32,14 +32,14 @@ /** * A registry to retrieve tests from, organized by target and category. - * + * * @author Marten Lohstroh */ public class TestRegistry { - + static class TestMap { /** - * Registry that maps targets to maps from categories to sets of tests. + * Registry that maps targets to maps from categories to sets of tests. */ protected final Map>> map = new HashMap<>(); @@ -57,7 +57,7 @@ public TestMap() { map.put(target, categories); } } - + /** * Return a set of tests given a target and test category. * @param t The target. @@ -68,26 +68,26 @@ public Set getTests(Target t, TestCategory c) { return this.map.get(t).get(c); } } - + /** * List of directories that should be skipped when indexing test files. Any * test file that has a directory in its path that matches an entry in this * array will not be discovered. */ public static final String[] IGNORED_DIRECTORIES = {"failing", "knownfailed", "failed", "fed-gen"}; - + /** * Path to the root of the repository. */ public static final Path LF_REPO_PATH = Paths.get("").toAbsolutePath(); - + /** * Path to the test directory in the repository. */ public static final Path LF_TEST_PATH = LF_REPO_PATH.resolve("test"); /** - * Internal data structure that stores registered tests. + * Internal data structure that stores registered tests. */ protected static final TestMap registered = new TestMap(); @@ -96,31 +96,31 @@ public Set getTests(Target t, TestCategory c) { * source files with no main reactor are indexed here. */ protected static final TestMap ignored = new TestMap(); - + /** * A map from each test category to a set of tests that is the union of * all registered tests in that category across all targets. */ protected static final Map> allTargets = new HashMap<>(); - + /** * Enumeration of test categories, used to map tests to categories. The * nearest containing directory that matches any of the categories will * determine the category that the test is mapped to. Matching is case * insensitive. - * + * * For example, the following files will all map to THREADED: * - C/threaded/Foo.lf - * - C/THREADED/Foo.lf + * - C/THREADED/Foo.lf * - C/Threaded/Foo.lf - * - C/foo/threaded/Bar.lf - * - C/foo/bar/threaded/Threaded.lf + * - C/foo/threaded/Bar.lf + * - C/foo/bar/threaded/Threaded.lf * - C/federated/threaded/bar.lf - * but the following will not: + * but the following will not: * - C/Foo.lf (maps to COMMON) * - C/Threaded.lf (maps to COMMON) * - C/threaded/federated/foo.lf (maps to FEDERATED) - * + * * @author Marten Lohstroh */ public enum TestCategory { @@ -140,7 +140,7 @@ public enum TestCategory { PROPERTIES(true), /** Tests concerning modal reactors */ MODAL_MODELS(true), - + NO_INLINING(false), // non-shared tests DOCKER(true), DOCKER_FEDERATED(true, "docker" + File.separator + "federated"), @@ -155,7 +155,7 @@ public enum TestCategory { public final boolean isCommon; public final String path; public final TestLevel level ; - + /** * Create a new test category. */ @@ -189,14 +189,14 @@ public String getPath() { /** * Return a header associated with the category. - * + * * @return A header to print in the test report. */ public String getHeader() { return TestBase.THICK_LINE + "Category: " + this.name(); } } - + // Static code that performs the file system traversal and discovers // all .lf files to be included in the registry. static { @@ -220,7 +220,7 @@ public String getHeader() { } else { System.out.println("WARNING: No test directory for target " + target + "\n"); } - + } catch (IOException e) { System.err.println( "ERROR: Caught exception while indexing tests for target " + target); @@ -231,7 +231,7 @@ public String getHeader() { c -> allTargets.get(c).addAll(getRegisteredTests(target, c, false))); } } - + /** * Calling this function forces the lazy initialization of the static code * that indexes all files. It is advisable to do this prior to executing @@ -239,10 +239,10 @@ public String getHeader() { * printed while indexing are printed first. */ public static void initialize() {} - + /** * Return the tests that were indexed for a given target and category. - * + * * @param target The target to get indexed tests for. * @param category The category of tests to include in the returned tests. * @param copy Whether to return copies of the indexed tests instead of the indexed tests themselves. @@ -260,7 +260,7 @@ public static Set getRegisteredTests(Target target, return registered.getTests(target, category); } } - + /** * Return the test that were found but not indexed because they did not * have a main reactor. @@ -275,11 +275,11 @@ public static String getCoverageReport(Target target, TestCategory category) { s.append(TestBase.THIN_LINE); s.append("Ignored: ").append(ignored.size()).append("\n"); s.append(TestBase.THIN_LINE); - + for (LFTest test : ignored) { s.append("No main reactor in: ").append(test).append("\n"); } - + Set own = getRegisteredTests(target, category, false); if (category.isCommon) { Set all = allTargets.get(category); @@ -301,17 +301,17 @@ public static String getCoverageReport(Target target, TestCategory category) { /** * FileVisitor implementation that maintains a stack to map found tests to - * the appropriate category and excludes directories that are listed as + * the appropriate category and excludes directories that are listed as * "ignored" from walks. - * + * * Specifically, when a directory is encountered that matches a category, * this category is pushed onto the stack. Similarly, when the DFS leaves * such a directory, its corresponding category is popped from the stack. * Any test (*.lf) file that is encountered will be mapped to the category - * that is on top of the stack. Initially, the stack has one element that + * that is on top of the stack. Initially, the stack has one element that * is TestCategory.COMMON, meaning that test files in the top-level test * directory for a given target will be mapped to that category. - * + * * @author Marten Lohstroh */ public static class TestDirVisitor extends SimpleFileVisitor { @@ -325,7 +325,7 @@ public static class TestDirVisitor extends SimpleFileVisitor { * The target that all encountered tests belong to. */ protected Target target; - + protected ResourceSet rs; protected Path srcBasePath; @@ -342,7 +342,7 @@ public TestDirVisitor(ResourceSet rs, Target target, Path srcBasePath) { this.target = target; this.srcBasePath = srcBasePath; } - + /** * Push categories onto the stack as appropriate and skip directories * that should be ignored. @@ -363,7 +363,7 @@ public FileVisitResult preVisitDirectory(Path dir, } return CONTINUE; } - + /** * Pop categories from the stack as appropriate. */ @@ -377,7 +377,7 @@ public FileVisitResult postVisitDirectory(Path dir, IOException exc) { } return CONTINUE; } - + /** * Add test files to the registry if they end with ".lf", but only if they have a main reactor. */ diff --git a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.java b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.java index c5ead40cb7..847a364193 100644 --- a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.java +++ b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.java @@ -1150,7 +1150,7 @@ private List synthesizeExamples(UnionType type, boolean correct) { */ private List synthesizeExamples(DictionaryType type, boolean correct) { List examples = new LinkedList<>(); - // Produce a set of singleton dictionaries. + // Produce a set of singleton dictionaries. // If incorrect examples are wanted, garble the key. for (DictionaryElement option : type.options) { synthesizeExamples(option.getType(), correct).forEach(it -> examples.add( @@ -1629,7 +1629,7 @@ public void testMainReactorHasHost() throws Exception { """; // TODO: Uncomment and fix test // List issues = validator.validate(parseWithoutError(testCase)); - // Assertions.assertTrue(issues.size() == 1 && + // Assertions.assertTrue(issues.size() == 1 && // issues.get(0).getMessage().contains("Cannot assign a host to reactor '") && // issues.get(0).getMessage().contains("' because it is not federated.")); } @@ -1810,7 +1810,7 @@ public void testMissingModeStateResetInstance() throws Exception { "This reactor contains state variables that are not reset upon mode entry: " + "s in R" + ".\nThe state variables are neither marked for automatic reset nor have a dedicated reset reaction. " - + "It is usafe to instatiate this reactor inside a mode entered with reset."); + + "It is unsafe to instantiate this reactor inside a mode entered with reset."); } @Test @@ -1841,11 +1841,9 @@ public void testUnspecifiedTransitionType() throws Exception { } """; validator.assertWarning(parseWithoutError(testCase), LfPackage.eINSTANCE.getReaction(), null, - "You should specifiy a transition type! " + "You should specify a transition type! " + "Reset and history transitions have different effects on this target mode. " + "Currently, a reset type is implicitly assumed."); } } - - diff --git a/org.lflang.tests/src/org/lflang/tests/compiler/RoundTripTests.java b/org.lflang.tests/src/org/lflang/tests/compiler/RoundTripTests.java index 24cb3554ff..801211be23 100644 --- a/org.lflang.tests/src/org/lflang/tests/compiler/RoundTripTests.java +++ b/org.lflang.tests/src/org/lflang/tests/compiler/RoundTripTests.java @@ -45,6 +45,7 @@ public void roundTripTest() { private void run(Path file) throws Exception { Model originalModel = LfParsingUtil.parse(file); + System.out.println(file); assertThat(originalModel.eResource().getErrors(), equalTo(emptyList())); // TODO: Check that the output is a fixed point final int smallLineLength = 20; diff --git a/org.lflang.tests/src/org/lflang/tests/lsp/MockReportProgress.java b/org.lflang.tests/src/org/lflang/tests/lsp/MockReportProgress.java index 901f0e6154..014fcc7276 100644 --- a/org.lflang.tests/src/org/lflang/tests/lsp/MockReportProgress.java +++ b/org.lflang.tests/src/org/lflang/tests/lsp/MockReportProgress.java @@ -17,7 +17,7 @@ public MockReportProgress() { @Override public void apply(String message, Integer percentage) { - System.out.printf("%s [%d -> %d]%n", message, previousPercentProgress, percentage); + System.out.printf("MockReportProgress: %s [%d -> %d]%n", message, previousPercentProgress, percentage); if (percentage == null) return; if (percentage < previousPercentProgress || percentage < 0 || percentage > 100) failed = true; previousPercentProgress = percentage; diff --git a/org.lflang.tests/src/org/lflang/tests/runtime/CCppTest.java b/org.lflang.tests/src/org/lflang/tests/runtime/CCppTest.java index db0ee3431f..17fd386ad8 100644 --- a/org.lflang.tests/src/org/lflang/tests/runtime/CCppTest.java +++ b/org.lflang.tests/src/org/lflang/tests/runtime/CCppTest.java @@ -46,6 +46,7 @@ private static boolean isExcludedFromCCpp(TestCategory category) { excluded |= isMac() && (category == TestCategory.DOCKER_FEDERATED || category == TestCategory.DOCKER); excluded |= category == TestCategory.ZEPHYR; excluded |= category == TestCategory.ARDUINO; + excluded |= category == TestCategory.NO_INLINING; return !excluded; } } diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index b48bf1fc59..667a5d702c 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit b48bf1fc59665ccf22e9f6fb7da93a31d046d090 +Subproject commit 667a5d702cbcd0288328417d8c632e6b5af19c04 diff --git a/org.lflang/src/lib/py/reactor-c-py b/org.lflang/src/lib/py/reactor-c-py index 14146b2f7b..5bdd8b836c 160000 --- a/org.lflang/src/lib/py/reactor-c-py +++ b/org.lflang/src/lib/py/reactor-c-py @@ -1 +1 @@ -Subproject commit 14146b2f7be6db8261b6a45724e18e0693f1f822 +Subproject commit 5bdd8b836c984457d8f4a830fa340f2f1d22cfa6 diff --git a/org.lflang/src/org/lflang/ASTUtils.java b/org.lflang/src/org/lflang/ASTUtils.java index 2be5f2970f..afda11bb46 100644 --- a/org.lflang/src/org/lflang/ASTUtils.java +++ b/org.lflang/src/org/lflang/ASTUtils.java @@ -58,6 +58,7 @@ import org.lflang.ast.ToText; import org.lflang.generator.CodeMap; import org.lflang.generator.InvalidSourceException; +import org.lflang.generator.ReactorInstance; import org.lflang.lf.Action; import org.lflang.lf.Assignment; import org.lflang.lf.AttrParm; @@ -371,6 +372,11 @@ public static List allInputs(Reactor definition) { return ASTUtils.collectElements(definition, featurePackage.getReactor_Inputs()); } + /** A list of all ports of {@code definition}, in an unspecified order. */ + public static List allPorts(Reactor definition) { + return Stream.concat(ASTUtils.allInputs(definition).stream(), ASTUtils.allOutputs(definition).stream()).toList(); + } + /** * Given a reactor class, return a list of all its instantiations, * which includes instantiations of base classes that it extends. @@ -382,6 +388,12 @@ public static List allInstantiations(Reactor definition) { return ASTUtils.collectElements(definition, featurePackage.getReactor_Instantiations()); } + public static Stream allNestedClasses(Reactor definition) { + return new HashSet<>(ASTUtils.allInstantiations(definition)).stream() + .map(Instantiation::getReactorClass) + .map(ASTUtils::toDefinition); + } + /** * Given a reactor class, return a list of all its methods, * which includes methods of base classes that it extends. @@ -451,6 +463,16 @@ public static List allModes(Reactor definition) { return ASTUtils.collectElements(definition, featurePackage.getReactor_Modes()); } + /** A list of all reactors instantiated, transitively or intransitively, by {@code r}. */ + public static List recursiveChildren(ReactorInstance r) { + List ret = new ArrayList<>(); + ret.add(r.reactorDefinition); + for (var child: r.children) { + ret.addAll(recursiveChildren(child)); + } + return ret; + } + /** * Return all the superclasses of the specified reactor * in deepest-first order. For example, if A extends B and C, and diff --git a/org.lflang/src/org/lflang/LinguaFranca.xtext b/org.lflang/src/org/lflang/LinguaFranca.xtext index 8aa7bf99b0..1ce6656e96 100644 --- a/org.lflang/src/org/lflang/LinguaFranca.xtext +++ b/org.lflang/src/org/lflang/LinguaFranca.xtext @@ -13,22 +13,22 @@ are permitted provided that the following conditions are met: this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************/ -/** +/** * Grammar for Lingua Franca. * A note of caution: extending this grammar with productions that introduce - * new terminals (i.e., anything written between quotes), will require those + * new terminals (i.e., anything written between quotes), will require those * terminals to also be added to the Token production at the bottom of this * file. Failing to do so will cause a parse error whenever a terminal not * listed in the Token production is featured in a segment of target-language @@ -44,7 +44,7 @@ import "http://www.eclipse.org/emf/2002/Ecore" as ecore // Use the package name "lf" for generated files defining the classes // in the metamodel (i.e., the classes representing nodes in the -// abstract syntax tree (AST), i.e., the eCore model). +// abstract syntax tree (AST), i.e., the eCore model). generate lf "https://lf-lang.org" /////////// Overall file @@ -105,7 +105,7 @@ TypeExpr: /** * Specification of the target language. Target properties can be specified in - * YAML format to pass on configuration details to the runtime environment. + * YAML format to pass on configuration details to the runtime environment. */ TargetDecl: 'target' name=ID (config=KeyValuePairs)? ';'?; @@ -114,11 +114,11 @@ TargetDecl: /////////// Statements /** - * Declaration of a state variable. Types are optional, but may be required - * during validation (depending on the target language). Initialization is also + * Declaration of a state variable. Types are optional, but may be required + * during validation (depending on the target language). Initialization is also * optional. A state variable can be initialized by assigning a `Expression` or list * of these. Note that a `Expression` may also be a reference to a parameter. - * The following checks must be carried out during validation: + * The following checks must be carried out during validation: * - if the list of initialization expressions has more than one element in it, a * type must be specified; * - if the `time` type is specified, there can only be a single initialization @@ -180,10 +180,10 @@ Mode: )* '}'; // Action that has either a physical or logical origin. -// +// // If the origin is logical, the minDelay is a minimum logical delay // after the logical time at which schedule() is called that the -// action will occur. If the origin is physical, then the +// action will occur. If the origin is physical, then the // minDelay is a minimum logical delay after the physical time // at which schedule() is called that the action will occur. // @@ -198,12 +198,11 @@ Action: Reaction: (attributes+=Attribute)* (('reaction') | mutation ?= 'mutation') - ('(' (triggers+=TriggerRef (',' triggers+=TriggerRef)*)? ')')? + ('(' (triggers+=TriggerRef (',' triggers+=TriggerRef)*)? ')') (sources+=VarRef (',' sources+=VarRef)*)? ('->' effects+=VarRefOrModeTransition (',' effects+=VarRefOrModeTransition)*)? - code=Code - (stp=STP)? - (deadline=Deadline)?; + ((('named' name=ID)? code=Code) | 'named' name=ID)(stp=STP)?(deadline=Deadline)? + ; TriggerRef: BuiltinTriggerRef | VarRef; @@ -213,7 +212,7 @@ BuiltinTriggerRef: Deadline: 'deadline' '(' delay=Expression ')' code=Code; - + STP: 'STP' '(' value=Expression ')' code=Code; @@ -230,7 +229,7 @@ Instantiation: Connection: ((leftPorts += VarRef (',' leftPorts += VarRef)*) | ( '(' leftPorts += VarRef (',' leftPorts += VarRef)* ')' iterated ?= '+'?)) - ('->' | physical?='~>') + ('->' | physical?='~>') rightPorts += VarRef (',' rightPorts += VarRef)* ('after' delay=Expression)? (serializer=Serializer)? @@ -264,7 +263,7 @@ Array: // todo allow empty array in grammar, replace with validator error Element: keyvalue=KeyValuePairs | array=Array - | literal=Literal + | literal=Literal | (time=INT unit=TimeUnit) | id=Path; @@ -277,8 +276,8 @@ Variable: TypedVariable | Timer | Mode; VarRef: - variable=[Variable] | container=[Instantiation] '.' variable=[Variable] - | interleaved?='interleaved' '(' (variable=[Variable] | container=[Instantiation] '.' variable=[Variable]) ')' + (variable=[Variable] | container=[Instantiation] '.' variable=[Variable] + | interleaved?='interleaved' '(' (variable=[Variable] | container=[Instantiation] '.' variable=[Variable]) ')') ('as' (alias=ID))? ; VarRefOrModeTransition returns VarRef: VarRef | transition=ModeTransition '(' variable=[Mode] ')'; @@ -331,7 +330,7 @@ Time: Port: Input | Output; - + // A type is in the target language, hence either an ID or target code. Type: time?='time' (arraySpec=ArraySpec)? @@ -428,24 +427,24 @@ IPV4Addr: ; IPV6Seg: - // NOTE: This rule is too permissive by design. + // NOTE: This rule is too permissive by design. // Further checking is done during validation. (INT | (INT? ID)) ; IPV6Addr: - // NOTE: This rule is too permissive by design. - // Further checking is done during validation. + // NOTE: This rule is too permissive by design. + // Further checking is done during validation. // IPV6 with truncation. - '::' | ('::' (IPV6Seg (':'))* IPV6Seg) | ((IPV6Seg (':'|'::'))+ IPV6Seg?) | - + '::' | ('::' (IPV6Seg (':'))* IPV6Seg) | ((IPV6Seg (':'|'::'))+ IPV6Seg?) | + // (Link-local IPv6 addresses with zone index) "fe80::7:8%1" (ID '::' IPV6Seg (':' IPV6Seg)* '%' (INT | ID)+) | // IPv4-mapped IPv6 addresses and IPv4-translated addresses ('::' IPV4Addr) | ('::' ID ':' (INT ':')? IPV4Addr) | - + // IPv4-Embedded IPv6 Address (((IPV6Seg (':' IPV6Seg)* '::') | (IPV6Seg (':' IPV6Seg)*) ':') IPV4Addr) ; @@ -462,7 +461,7 @@ Code: {Code} '{=' body=Body '=}' ; -FSName: +FSName: (ID | '.' | '_')+ ; // Absolute or relative directory path in Windows, Linux, or MacOS. @@ -501,11 +500,11 @@ Token: // Non-constant terminals ID | INT | FLOAT_EXP_SUFFIX | LT_ANNOT | STRING | CHAR_LIT | ML_COMMENT | SL_COMMENT | WS | ANY_OTHER | // Keywords - 'target' | 'import' | 'main' | 'realtime' | 'reactor' | 'state' | 'time' | - 'mutable' | 'input' | 'output' | 'timer' | 'action' | 'reaction' | + 'target' | 'import' | 'main' | 'realtime' | 'reactor' | 'state' | 'time' | + 'mutable' | 'input' | 'output' | 'timer' | 'action' | 'reaction' | 'startup' | 'shutdown' | 'after' | 'deadline' | 'mutation' | 'preamble' | 'new' | 'federated' | 'at' | 'as' | 'from' | 'widthof' | 'const' | 'method' | - 'interleaved' | 'mode' | 'initial' | 'reset' | 'history' | + 'interleaved' | 'mode' | 'initial' | 'reset' | 'history' | 'named' | // Other terminals NEGINT | TRUE | FALSE | // Action origins @@ -525,7 +524,7 @@ Token: // Underscore '_' | // Arrow - '->' | + '->' | // Assignment '=' | // Percentage diff --git a/org.lflang/src/org/lflang/ast/IsEqual.java b/org.lflang/src/org/lflang/ast/IsEqual.java index 33733a3314..7f2dd072ea 100644 --- a/org.lflang/src/org/lflang/ast/IsEqual.java +++ b/org.lflang/src/org/lflang/ast/IsEqual.java @@ -284,6 +284,7 @@ public Boolean caseReaction(Reaction object) { .listsEquivalent(Reaction::getSources) .listsEquivalent(Reaction::getEffects) .equalAsObjects(Reaction::isMutation) + .equalAsObjects(Reaction::getName) .equivalent(Reaction::getCode) .equivalent(Reaction::getStp) .equivalent(Reaction::getDeadline) diff --git a/org.lflang/src/org/lflang/ast/ToLf.java b/org.lflang/src/org/lflang/ast/ToLf.java index 5c1deba96e..52a0d302f2 100644 --- a/org.lflang/src/org/lflang/ast/ToLf.java +++ b/org.lflang/src/org/lflang/ast/ToLf.java @@ -595,13 +595,12 @@ public MalleableString caseAction(Action object) { @Override public MalleableString caseReaction(Reaction object) { - // ('reaction') - // ('(' (triggers+=TriggerRef (',' triggers+=TriggerRef)*)? ')')? + // (attributes+=Attribute)* + // (('reaction') | mutation ?= 'mutation') + // ('(' (triggers+=TriggerRef (',' triggers+=TriggerRef)*)? ')') // (sources+=VarRef (',' sources+=VarRef)*)? // ('->' effects+=VarRefOrModeTransition (',' effects+=VarRefOrModeTransition)*)? - // code=Code - // (stp=STP)? - // (deadline=Deadline)? + // ((('named' name=ID)? code=Code) | 'named' name=ID)(stp=STP)?(deadline=Deadline)? Builder msb = new Builder(); addAttributes(msb, object::getAttributes); if (object.isMutation()) { @@ -630,7 +629,8 @@ public MalleableString caseReaction(Reaction object) { : doSwitch(varRef)) .collect(new Joiner(", "))); } - msb.append(" ").append(doSwitch(object.getCode())); + if (object.getName() != null) msb.append(" named ").append(object.getName()); + if (object.getCode() != null) msb.append(" ").append(doSwitch(object.getCode())); if (object.getStp() != null) msb.append(" ").append(doSwitch(object.getStp())); if (object.getDeadline() != null) msb.append(" ").append(doSwitch(object.getDeadline())); return msb.get(); diff --git a/org.lflang/src/org/lflang/federated/extensions/CExtension.java b/org.lflang/src/org/lflang/federated/extensions/CExtension.java index a2ac48216f..4698c2eb84 100644 --- a/org.lflang/src/org/lflang/federated/extensions/CExtension.java +++ b/org.lflang/src/org/lflang/federated/extensions/CExtension.java @@ -38,6 +38,7 @@ import org.lflang.ASTUtils; import org.lflang.ErrorReporter; import org.lflang.InferredType; +import org.lflang.Target; import org.lflang.TargetProperty; import org.lflang.TargetProperty.CoordinationType; import org.lflang.TimeValue; @@ -99,7 +100,7 @@ public void initializeTargetConfig( federate.targetConfig.setByUser.add(TargetProperty.THREADING); // Include the fed setup file for this federate in the target property - String relPath = "include" + File.separator + "_" + federate.name + "_preamble.h"; + String relPath = getPreamblePath(federate); federate.targetConfig.fedSetupPreamble = relPath; federate.targetConfig.setByUser.add(TargetProperty.FED_SETUP); } @@ -471,7 +472,7 @@ public String getNetworkBufferType() { } /** - * Add preamble to a separate file `include/_federateName_preamble.h` to set up federated execution. + * Add preamble to a separate file to set up federated execution. * Return an empty string since no code generated needs to go in the source. */ @Override @@ -483,14 +484,29 @@ public String generatePreamble( ) throws IOException { // Put the C preamble in a `include/_federate.name + _preamble.h` file String cPreamble = makePreamble(federate, fileConfig, rtiConfig, errorReporter); - String relPath = "include" + File.separator + "_" + federate.name + "_preamble.h"; + String relPath = getPreamblePath(federate); Path fedPreamblePath = fileConfig.getSrcPath().resolve(relPath); Files.createDirectories(fedPreamblePath.getParent()); try (var writer = Files.newBufferedWriter(fedPreamblePath)) { writer.write(cPreamble); } + var includes = new CodeBuilder(); + if (federate.targetConfig.target != Target.Python) { + includes.pr("#ifdef __cplusplus\n" + + "extern \"C\" {\n" + + "#endif"); + includes.pr("#include \"core/federated/federate.h\""); + includes.pr("#include \"core/federated/net_common.h\""); + includes.pr("#include \"core/federated/net_util.h\""); + includes.pr("#include \"core/threaded/reactor_threaded.h\""); + includes.pr("#include \"core/utils/util.h\""); + includes.pr("extern federate_instance_t _fed;"); + includes.pr("#ifdef __cplusplus\n" + + "}\n" + + "#endif"); + } - return ""; + return includes.toString(); } /** @@ -558,7 +574,7 @@ private String generateInitializeTriggers(FederateInstance federate, ErrorReport code.pr(CExtensionUtils.initializeTriggersForNetworkActions(federate, main)); code.pr(CExtensionUtils.initializeTriggerForControlReactions(main, main, federate)); federatedReactor.setName(oldFederatedReactorName); - + return """ #define initialize_triggers_for_federate() \\ do { \\ @@ -744,5 +760,7 @@ private String generateCodeForPhysicalActions(FederateInstance federate, ErrorRe } return code.getCode(); } - + private String getPreamblePath(FederateInstance f) { + return "include" + File.separator + "_" + f.name + "_preamble.h"; + } } diff --git a/org.lflang/src/org/lflang/federated/extensions/TSExtension.java b/org.lflang/src/org/lflang/federated/extensions/TSExtension.java index e438b1b866..77d6203a93 100644 --- a/org.lflang/src/org/lflang/federated/extensions/TSExtension.java +++ b/org.lflang/src/org/lflang/federated/extensions/TSExtension.java @@ -96,22 +96,21 @@ public String generatePreamble(FederateInstance federate, FedFileConfig fileConf var upstreamConnectionDelays = getUpstreamConnectionDelays(federate); return """ - preamble {= - const defaultFederateConfig: __FederateConfig = { - dependsOn: [%s], - executionTimeout: undefined, - fast: false, - federateID: %d, - federationID: "Unidentified Federation", - keepAlive: false, - minOutputDelay: %s, - networkMessageActions: [%s], - rtiHost: "%s", - rtiPort: %d, - sendsTo: [%s], - upstreamConnectionDelays: [%s] - } - =}""".formatted( + const defaultFederateConfig: __FederateConfig = { + dependsOn: [%s], + executionTimeout: undefined, + fast: false, + federateID: %d, + federationID: "Unidentified Federation", + keepAlive: false, + minOutputDelay: %s, + networkMessageActions: [%s], + rtiHost: "%s", + rtiPort: %d, + sendsTo: [%s], + upstreamConnectionDelays: [%s] + } + """.formatted( federate.dependsOn.keySet().stream() .map(e->String.valueOf(e.id)) .collect(Collectors.joining(",")), diff --git a/org.lflang/src/org/lflang/federated/generator/FedGenerator.java b/org.lflang/src/org/lflang/federated/generator/FedGenerator.java index 18d5770165..cd9c2e95d0 100644 --- a/org.lflang/src/org/lflang/federated/generator/FedGenerator.java +++ b/org.lflang/src/org/lflang/federated/generator/FedGenerator.java @@ -288,7 +288,7 @@ private Map compileFederates( Resource res = rs.getResource(URI.createFileURI( fileConfig.getSrcPath().resolve(fed.name + ".lf").toAbsolutePath().toString() ), true); - FileConfig subFileConfig = LFGenerator.createFileConfig(res, fileConfig.getSrcGenPath(), false); + FileConfig subFileConfig = LFGenerator.createFileConfig(res, fileConfig.getSrcGenPath(), true); ErrorReporter subContextErrorReporter = new LineAdjustingErrorReporter(threadSafeErrorReporter, lf2lfCodeMapMap); var props = new Properties(); diff --git a/org.lflang/src/org/lflang/federated/generator/FedPreambleEmitter.java b/org.lflang/src/org/lflang/federated/generator/FedPreambleEmitter.java index 20771e7848..354cd33044 100644 --- a/org.lflang/src/org/lflang/federated/generator/FedPreambleEmitter.java +++ b/org.lflang/src/org/lflang/federated/generator/FedPreambleEmitter.java @@ -39,8 +39,13 @@ String generatePreamble(FederateInstance federate, FedFileConfig fileConfig, Rti )); } - preambleCode.pr(FedTargetExtensionFactory.getExtension(federate.targetConfig.target).generatePreamble( - federate, fileConfig, rtiConfig, errorReporter)); + preambleCode.pr(""" + preamble {= + %s + =}""".formatted(FedTargetExtensionFactory.getExtension(federate.targetConfig.target).generatePreamble( + federate, fileConfig, rtiConfig, errorReporter + )) + ); return preambleCode.getCode(); } diff --git a/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java b/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java index 116cbf3046..3422ee0d50 100644 --- a/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java +++ b/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java @@ -44,23 +44,23 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * In the worst case, of these runtime instances may have distinct dependencies, * and hence distinct levels in the graph. Moreover, some of these instances * may be involved in cycles while others are not. - * + * * Upon construction of this class, the runtime instances are created if necessary, * stored each ReactionInstance, and assigned levels (maximum number of * upstream reaction instances), deadlines, and single dominating reactions. - * + * * After creation, the resulting graph will be empty unless there are causality * cycles, in which case, the resulting graph is a graph of runtime reaction * instances that form cycles. - * + * * @author Marten Lohstroh * @author Edward A. Lee */ public class ReactionInstanceGraph extends PrecedenceGraph { - + /** - * Create a new graph by traversing the maps in the named instances - * embedded in the hierarchy of the program. + * Create a new graph by traversing the maps in the named instances + * embedded in the hierarchy of the program. */ public ReactionInstanceGraph(ReactorInstance main) { this.main = main; @@ -74,12 +74,12 @@ public ReactionInstanceGraph(ReactorInstance main) { * The main reactor instance that this graph is associated with. */ public final ReactorInstance main; - + /////////////////////////////////////////////////////////// //// Public methods - + /** - * Rebuild this graph by clearing and repeating the traversal that + * Rebuild this graph by clearing and repeating the traversal that * adds all the nodes and edges. */ public void rebuild() { @@ -89,7 +89,7 @@ public void rebuild() { // FIXME: Use {@link TargetProperty#EXPORT_DEPENDENCY_GRAPH}. // System.out.println(toDOT()); - // Assign a level to each reaction. + // Assign a level to each reaction. // If there are cycles present in the graph, it will be detected here. assignLevels(); if (nodeCount() != 0) { @@ -109,9 +109,9 @@ public void rebuildAndAssignDeadlines() { assignInferredDeadlines(); this.clear(); } - + /* - * Get an array of non-negative integers representing the number of reactions + * Get an array of non-negative integers representing the number of reactions * per each level, where levels are indices of the array. */ public Integer[] getNumReactionsPerLevel() { @@ -133,7 +133,7 @@ public int getBreadth() { /////////////////////////////////////////////////////////// //// Protected methods - + /** * Add to the graph edges between the given reaction and all the reactions * that depend on the specified port. @@ -144,19 +144,19 @@ protected void addDownstreamReactions(PortInstance port, ReactionInstance reacti // Use mixed-radix numbers to increment over the ranges. List srcRuntimes = reaction.getRuntimeInstances(); List eventualDestinations = port.eventualDestinations(); - + int srcDepth = (port.isInput())? 2 : 1; - + for (SendRange sendRange : eventualDestinations) { for (RuntimeRange dstRange : sendRange.destinations) { - + int dstDepth = (dstRange.instance.isOutput())? 2 : 1; MixedRadixInt dstRangePosition = dstRange.startMR(); int dstRangeCount = 0; MixedRadixInt sendRangePosition = sendRange.startMR(); int sendRangeCount = 0; - + while (dstRangeCount++ < dstRange.width) { int srcIndex = sendRangePosition.get(srcDepth); int dstIndex = dstRangePosition.get(dstDepth); @@ -177,7 +177,7 @@ protected void addDownstreamReactions(PortInstance port, ReactionInstance reacti if (srcRuntime.deadline.compareTo(dstRuntime.deadline) > 0) { srcRuntime.deadline = dstRuntime.deadline; } - + // If this seems to be a single dominating reaction, set it. // If another upstream reaction shows up, then this will be // reset to null. @@ -203,7 +203,7 @@ protected void addDownstreamReactions(PortInstance port, ReactionInstance reacti } /** - * Build the graph by adding nodes and edges based on the given reactor + * Build the graph by adding nodes and edges based on the given reactor * instance. * @param reactor The reactor on the basis of which to add nodes and edges. */ @@ -211,12 +211,12 @@ protected void addNodesAndEdges(ReactorInstance reactor) { ReactionInstance previousReaction = null; for (ReactionInstance reaction : reactor.reactions) { List runtimes = reaction.getRuntimeInstances(); - + // Add reactions of this reactor. for (Runtime runtime : runtimes) { - this.addNode(runtime); + this.addNode(runtime); } - + // If this is not an unordered reaction, then create a dependency // on any previously defined reaction. if (!reaction.isUnordered) { @@ -251,12 +251,12 @@ protected void addNodesAndEdges(ReactorInstance reactor) { addNodesAndEdges(child); } } - + /////////////////////////////////////////////////////////// //// Private fields - + /** - * Number of reactions per level, represented as a list of + * Number of reactions per level, represented as a list of * integers where the indices are the levels. */ private List numReactionsPerLevel = new ArrayList<>(List.of(0)); @@ -268,7 +268,7 @@ protected void addNodesAndEdges(ReactorInstance reactor) { * Analyze the dependencies between reactions and assign each reaction * instance a level. This method removes nodes from this graph as it * assigns levels. Any remaining nodes are part of causality cycles. - * + * * This procedure is based on Kahn's algorithm for topological sorting. * Rather than establishing a total order, we establish a partial order. * In this order, the level of each reaction is the least upper bound of @@ -276,13 +276,13 @@ protected void addNodesAndEdges(ReactorInstance reactor) { */ private void assignLevels() { List start = new ArrayList<>(rootNodes()); - + // All root nodes start with level 0. for (Runtime origin : start) { origin.level = 0; } - // No need to do any of this if there are no root nodes; + // No need to do any of this if there are no root nodes; // the graph must be cyclic. while (!start.isEmpty()) { Runtime origin = start.remove(0); @@ -293,7 +293,7 @@ private void assignLevels() { for (Runtime effect : downstreamAdjacentNodes) { // Stage edge between origin and effect for removal. toRemove.add(effect); - + // Update level of downstream node. effect.level = origin.level + 1; } @@ -306,7 +306,7 @@ private void assignLevels() { start.add(effect); } } - + // Remove visited origin. removeNode(origin); @@ -314,16 +314,16 @@ private void assignLevels() { adjustNumReactionsPerLevel(origin.level, 1); } } - + /** * This function assigns inferred deadlines to all the reactions in the graph. * It is modeled after `assignLevels` but it starts at the leaf nodes and uses * Kahns algorithm to build a reverse topologically sorted graph - * + * */ private void assignInferredDeadlines() { List start = new ArrayList<>(leafNodes()); - + // All leaf nodes have deadline initialized to their declared deadline or MAX_VALUE while (!start.isEmpty()) { Runtime origin = start.remove(0); @@ -334,7 +334,7 @@ private void assignInferredDeadlines() { for (Runtime upstream : upstreamAdjacentNodes) { // Stage edge between origin and upstream for removal. toRemove.add(upstream); - + // Update deadline of upstream node if origins deadline is earlier. if (origin.deadline.isEarlierThan(upstream.deadline)) { upstream.deadline = origin.deadline; @@ -349,12 +349,12 @@ private void assignInferredDeadlines() { start.add(upstream); } } - + // Remove visited origin. removeNode(origin); } } - + /** * Adjust {@link #numReactionsPerLevel} at index level by * adding to the previously recorded number valueToAdd. @@ -411,7 +411,7 @@ public String toDOT() { var currentLevelNodes = groupedNodes.get(level); for (var node: currentLevelNodes) { // Draw the node - var label = CUtil.getName(node.getReaction().getParent().reactorDeclaration) + "." + node.getReaction().getName(); + var label = CUtil.getName(node.getReaction().getParent().reactorDefinition) + "." + node.getReaction().getName(); // Need a positive number to name the nodes in GraphViz var labelHashCode = label.hashCode() & 0xfffffff; dotRepresentation.pr(" node_" + labelHashCode + " [label=\""+ label +"\"];"); @@ -419,7 +419,7 @@ public String toDOT() { // Draw the edges var downstreamNodes = getDownstreamAdjacentNodes(node); for (var downstreamNode: downstreamNodes) { - var downstreamLabel = CUtil.getName(downstreamNode.getReaction().getParent().reactorDeclaration) + "." + downstreamNode.getReaction().getName(); + var downstreamLabel = CUtil.getName(downstreamNode.getReaction().getParent().reactorDefinition) + "." + downstreamNode.getReaction().getName(); edges.append(" node_" + labelHashCode + " -> node_" + (downstreamLabel.hashCode() & 0xfffffff) + ";\n" ); diff --git a/org.lflang/src/org/lflang/generator/ReactorInstance.java b/org.lflang/src/org/lflang/generator/ReactorInstance.java index 709c4ac012..eb5163ade6 100644 --- a/org.lflang/src/org/lflang/generator/ReactorInstance.java +++ b/org.lflang/src/org/lflang/generator/ReactorInstance.java @@ -80,7 +80,7 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * instance and the bank widths of all of its parents. * There is exactly one instance of this ReactorInstance class for each * graphical rendition of a reactor in the diagram view. - * + * * For the main reactor, which has no parent, once constructed, * this object represents the entire Lingua Franca program. * If the program has causality loops (a programming error), then @@ -137,7 +137,7 @@ public ReactorInstance(Reactor reactor, ReactorInstance parent, ErrorReporter re * bank members (which have bankIndex >= 0). */ public final List children = new ArrayList<>(); - + /** The input port instances belonging to this reactor instance. */ public final List inputs = new ArrayList<>(); @@ -152,7 +152,7 @@ public ReactorInstance(Reactor reactor, ReactorInstance parent, ErrorReporter re /** The timer instances belonging to this reactor instance. */ public final List timers = new ArrayList<>(); - + /** The mode instances belonging to this reactor instance. */ public final List modes = new ArrayList<>(); @@ -167,18 +167,18 @@ public ReactorInstance(Reactor reactor, ReactorInstance parent, ErrorReporter re ////////////////////////////////////////////////////// //// Public methods. - + /** * Assign levels to all reactions within the same root as this * reactor. The level of a reaction r is equal to the length of the * longest chain of reactions that must have the opportunity to * execute before r at each logical tag. This fails and returns * false if a causality cycle exists. - * + * * This method uses a variant of Kahn's algorithm, which is linear * in V + E, where V is the number of vertices (reactions) and E * is the number of edges (dependencies between reactions). - * + * * @return An empty graph if successful and otherwise a graph * with runtime reaction instances that form cycles. */ @@ -205,8 +205,8 @@ public ReactionInstanceGraph assignDeadlines() { cachedReactionLoopGraph.rebuildAndAssignDeadlines(); return cachedReactionLoopGraph; } - - /** + + /** * Return the instance of a child rector created by the specified * definition or null if there is none. * @param definition The definition of the child reactor ("new" statement). @@ -219,7 +219,7 @@ public ReactorInstance getChildReactorInstance(Instantiation definition) { } return null; } - + /** * Clear any cached data in this reactor and its children. * This is useful if a mutation has been realized. @@ -251,7 +251,7 @@ public void clearCaches(boolean includingRuntimes) { } cachedCycles = null; } - + /** * Return the set of ReactionInstance and PortInstance that form causality * loops in the topmost parent reactor in the instantiation hierarchy. This will return an @@ -261,7 +261,7 @@ public Set> getCycles() { if (depth != 0) return root().getCycles(); if (cachedCycles != null) return cachedCycles; cachedCycles = new LinkedHashSet<>(); - + ReactionInstanceGraph reactionRuntimes = assignLevels(); if (reactionRuntimes.nodes().size() > 0) { Set reactions = new LinkedHashSet<>(); @@ -287,7 +287,7 @@ public Set> getCycles() { cachedCycles.addAll(reactions); cachedCycles.addAll(ports); } - + return cachedCycles; } @@ -303,8 +303,8 @@ public PortInstance getInput(String name) { } return null; } - - /** + + /** * Override the base class to append [i_d], where d is the depth, * if this reactor is in a bank of reactors. * @return The name of this instance. @@ -340,7 +340,7 @@ public PortInstance getOutput(String name) { } return null; } - + /** * Return a parameter matching the specified name if the reactor has one * and otherwise return null. @@ -354,7 +354,7 @@ public ParameterInstance getParameter(String name) { } return null; } - + /** * Return the startup trigger or null if not used in any reaction. */ @@ -368,7 +368,7 @@ public TriggerInstance getStartupTrigger() { public TriggerInstance getShutdownTrigger() { return builtinTriggers.get(BuiltinTrigger.SHUTDOWN); } - + /** * If this reactor is a bank or any of its parents is a bank, * return the total number of runtime instances, which is the product @@ -378,7 +378,7 @@ public TriggerInstance getShutdownTrigger() { public int getTotalWidth() { return getTotalWidth(0); } - + /** * If this reactor is a bank or any of its parents is a bank, * return the total number of runtime instances, which is the product @@ -387,7 +387,7 @@ public int getTotalWidth() { * @param atDepth The depth at which to determine the width. * Use 0 to get the total number of instances. * Use 1 to get the number of instances within a single top-level - * bank member (this is useful for federates). + * bank member (this is useful for federates). */ public int getTotalWidth(int atDepth) { if (width <= 0) return -1; @@ -402,7 +402,7 @@ public int getTotalWidth(int atDepth) { return result; } - /** + /** * Return the trigger instances (input ports, timers, and actions * that trigger reactions) belonging to this reactor instance. */ @@ -415,11 +415,11 @@ public Set> getTriggers() { return triggers; } - /** + /** * Return the trigger instances (input ports, timers, and actions * that trigger reactions) together the ports that the reaction reads * but that don't trigger it. - * + * * @return The trigger instances belonging to this reactor instance. */ public Set> getTriggersAndReads() { @@ -431,23 +431,23 @@ public Set> getTriggersAndReads() { } return triggers; } - + /** * Return true if the top-level parent of this reactor has causality cycles. */ public boolean hasCycles() { return assignLevels().nodeCount() != 0; } - + /** * Given a parameter definition for this reactor, return the initial integer * value of the parameter. If the parameter is overridden when instantiating * this reactor or any of its containing reactors, use that value. * Otherwise, use the default value in the reactor definition. * If the parameter cannot be found or its value is not an integer, return null. - * + * * @param parameter The parameter definition (a syntactic object in the AST). - * + * * @return An integer value or null. */ public Integer initialIntParameterValue(Parameter parameter) { @@ -532,10 +532,10 @@ public boolean isBank() { * @return true if reactor definition is marked as main or federated, false otherwise. */ public boolean isMainOrFederated() { - return reactorDefinition != null + return reactorDefinition != null && (reactorDefinition.isMain() || reactorDefinition.isFederated()); } - + /** * Return true if the specified reactor instance is either equal to this * reactor instance or a parent of it. @@ -549,12 +549,12 @@ public boolean isParent(ReactorInstance r) { } return false; } - + /////////////////////////////////////////////////// //// Methods for finding instances in this reactor given an AST node. - - /** - * Return the action instance within this reactor + + /** + * Return the action instance within this reactor * instance corresponding to the specified action reference. * @param action The action as an AST node. * @return The corresponding action instance or null if the @@ -569,7 +569,7 @@ public ActionInstance lookupActionInstance(Action action) { return null; } - /** + /** * Given a parameter definition, return the parameter instance * corresponding to that definition, or null if there is * no such instance. @@ -584,8 +584,8 @@ public ParameterInstance lookupParameterInstance(Parameter parameter) { } return null; } - - /** + + /** * Given a port definition, return the port instance * corresponding to that definition, or null if there is * no such instance. @@ -608,7 +608,7 @@ public PortInstance lookupPortInstance(Port port) { return null; } - /** + /** * Given a reference to a port belonging to this reactor * instance, return the port instance. * Return null if there is no such instance. @@ -631,8 +631,8 @@ public PortInstance lookupPortInstance(VarRef reference) { } } - /** - * Return the reaction instance within this reactor + /** + * Return the reaction instance within this reactor * instance corresponding to the specified reaction. * @param reaction The reaction as an AST node. * @return The corresponding reaction instance or null if the @@ -646,7 +646,7 @@ public ReactionInstance lookupReactionInstance(Reaction reaction) { } return null; } - + /** * Return the reactor instance within this reactor * that has the specified instantiation. Note that this @@ -661,9 +661,9 @@ public ReactorInstance lookupReactorInstance(Instantiation instantiation) { } return null; } - - /** - * Return the timer instance within this reactor + + /** + * Return the timer instance within this reactor * instance corresponding to the specified timer reference. * @param timer The timer as an AST node. * @return The corresponding timer instance or null if the @@ -677,8 +677,8 @@ public TimerInstance lookupTimerInstance(Timer timer) { } return null; } - - /** Returns the mode instance within this reactor + + /** Returns the mode instance within this reactor * instance corresponding to the specified mode reference. * @param mode The mode as an AST node. * @return The corresponding mode instance or null if the @@ -693,14 +693,14 @@ public ModeInstance lookupModeInstance(Mode mode) { return null; } - /** + /** * Return a descriptive string. */ @Override public String toString() { return "ReactorInstance " + getFullName(); } - + /** * Assuming that the given expression denotes a valid time, return a time value. * @@ -711,7 +711,7 @@ public TimeValue getTimeValue(Expression expr) { Expression resolved = resolveParameters(expr); return getLiteralTimeValue(resolved); } - + ////////////////////////////////////////////////////// //// Protected fields. @@ -735,8 +735,8 @@ public TimeValue getTimeValue(Expression expr) { ////////////////////////////////////////////////////// //// Protected methods. - - /** + + /** * Create all the reaction instances of this reactor instance * and record the dependencies and antidependencies * between ports, actions, and timers and reactions. @@ -756,7 +756,7 @@ protected void createReactionInstances() { // Create the reaction instance. var reactionInstance = new ReactionInstance(reaction, this, unorderedReactions.contains(reaction), count++); - + // Add the reaction instance to the map of reactions for this // reactor. this.reactions.add(reactionInstance); @@ -770,10 +770,10 @@ protected void createReactionInstances() { protected TriggerInstance getOrCreateBuiltinTrigger(BuiltinTriggerRef trigger) { return builtinTriggers.computeIfAbsent(trigger.getType(), ref -> TriggerInstance.builtinTrigger(trigger, this)); } - + //////////////////////////////////////// //// Private constructors - + /** * Create a runtime instance from the specified definition * and with the specified parent that instantiated it. @@ -783,7 +783,7 @@ protected TriggerInstance getOrCreateBuiltinTrigger(Buil * @param desiredDepth The depth to which to expand the hierarchy. */ private ReactorInstance( - Instantiation definition, + Instantiation definition, ReactorInstance parent, ErrorReporter reporter, int desiredDepth) { @@ -791,7 +791,7 @@ private ReactorInstance( this.reporter = reporter; this.reactorDeclaration = definition.getReactorClass(); this.reactorDefinition = ASTUtils.toDefinition(reactorDeclaration); - + // check for recursive instantiation var currentParent = parent; var foundSelfAsParent = false; @@ -805,21 +805,21 @@ private ReactorInstance( } } } while(currentParent != null); - + this.recursive = foundSelfAsParent; if (recursive) { reporter.reportError(definition, "Recursive reactor instantiation."); } - + // If the reactor definition is null, give up here. Otherwise, diagram generation // will fail an NPE. if (reactorDefinition == null) { reporter.reportError(definition, "Reactor instantiation has no matching reactor definition."); return; } - + setInitialWidth(); - + // Apply overrides and instantiate parameters for this reactor instance. for (Parameter parameter : ASTUtils.allParameters(reactorDefinition)) { this.parameters.add(new ParameterInstance(parameter, this)); @@ -841,9 +841,9 @@ private ReactorInstance( // While doing this, assign an index offset to each. for (Instantiation child : ASTUtils.allInstantiations(reactorDefinition)) { var childInstance = new ReactorInstance( - child, - this, - reporter, + child, + this, + reporter, desiredDepth ); this.children.add(childInstance); @@ -863,10 +863,10 @@ private ReactorInstance( // Create the reaction instances in this reactor instance. // This also establishes all the implied dependencies. - // Note that this can only happen _after_ the children, + // Note that this can only happen _after_ the children, // port, action, and timer instances have been created. createReactionInstances(); - + // Instantiate modes for this reactor instance // This must come after the child elements (reactions, etc) of this reactor // are created in order to allow their association with modes @@ -878,17 +878,17 @@ private ReactorInstance( } } } - + ////////////////////////////////////////////////////// //// Private methods. /** * Connect the given left port range to the given right port range. - * + * * NOTE: This method is public to enable its use in unit tests. * Otherwise, it should be private. This is why it is defined here, * in the section labeled "Private methods." - * + * * @param src The source range. * @param dst The destination range. * @param connection The connection establishing this relationship. @@ -918,7 +918,7 @@ private void establishPortConnections() { Iterator> srcRanges = leftPorts.iterator(); List> rightPorts = listPortInstances(connection.getRightPorts(), connection); Iterator> dstRanges = rightPorts.iterator(); - + // Check for empty lists. if (!srcRanges.hasNext()) { if (dstRanges.hasNext()) { @@ -929,7 +929,7 @@ private void establishPortConnections() { reporter.reportWarning(connection, "No destination. Outputs will be lost."); return; } - + RuntimeRange src = srcRanges.next(); RuntimeRange dst = dstRanges.next(); @@ -939,7 +939,7 @@ private void establishPortConnections() { if (!dstRanges.hasNext()) { if (srcRanges.hasNext()) { // Should not happen (checked by the validator). - reporter.reportWarning(connection, + reporter.reportWarning(connection, "Source is wider than the destination. Outputs will be lost."); } break; @@ -950,7 +950,7 @@ private void establishPortConnections() { } else { if (dstRanges.hasNext()) { // Should not happen (checked by the validator). - reporter.reportWarning(connection, + reporter.reportWarning(connection, "Destination is wider than the source. Inputs will be missing."); } break; @@ -964,7 +964,7 @@ private void establishPortConnections() { src = src.tail(dst.width); if (!dstRanges.hasNext()) { // Should not happen (checked by the validator). - reporter.reportWarning(connection, + reporter.reportWarning(connection, "Source is wider than the destination. Outputs will be lost."); break; } @@ -977,7 +977,7 @@ private void establishPortConnections() { if (connection.isIterated()) { srcRanges = leftPorts.iterator(); } else { - reporter.reportWarning(connection, + reporter.reportWarning(connection, "Destination is wider than the source. Inputs will be missing."); break; } @@ -987,7 +987,7 @@ private void establishPortConnections() { } } } - + /** * If path exists from the specified port to any reaction in the specified * set of reactions, then add the specified port and all ports along the path @@ -995,7 +995,7 @@ private void establishPortConnections() { * @return True if the specified port was added. */ private boolean findPaths( - PortInstance port, + PortInstance port, Set reactions, Set ports ) { @@ -1017,22 +1017,22 @@ private boolean findPaths( } return result; } - + /** * Given a list of port references, as found on either side of a connection, * return a list of the port instance ranges referenced. These may be multiports, * and may be ports of a contained bank (a port representing ports of the bank * members) so the returned list includes ranges of banks and channels. - * + * * If a given port reference has the form `interleaved(b.m)`, where `b` is * a bank and `m` is a multiport, then the corresponding range in the returned * list is marked interleaved. - * + * * For example, if `b` and `m` have width 2, without the interleaved keyword, * the returned range represents the sequence `[b0.m0, b0.m1, b1.m0, b1.m1]`. * With the interleaved marking, the returned range represents the sequence * `[b0.m0, b1.m0, b0.m1, b1.m1]`. Both ranges will have width 4. - * + * * @param references The variable references on one side of the connection. * @param connection The connection. */ @@ -1060,7 +1060,7 @@ private List> listPortInstances( if (reactor != null) { PortInstance portInstance = reactor.lookupPortInstance( (Port) portRef.getVariable()); - + Set interleaved = new LinkedHashSet<>(); if (portRef.isInterleaved()) { // NOTE: Here, we are assuming that the interleaved() @@ -1087,11 +1087,11 @@ private List> listPortInstances( portParentWidth = 1; } int widthBound = portWidth * portParentWidth; - + // If either of these widths cannot be determined, assume infinite. if (portWidth < 0) widthBound = Integer.MAX_VALUE; if (portParentWidth < 0) widthBound = Integer.MAX_VALUE; - + if (widthBound < range.width) { // Need to split the range. tails.add(range.tail(widthBound)); @@ -1113,7 +1113,7 @@ private List> listPortInstances( } // If the width cannot be determined, assume infinite. if (widthBound < 0) widthBound = Integer.MAX_VALUE; - + if (widthBound < tail.width) { // Need to split the range again moreTails.add(tail.tail(widthBound)); @@ -1139,7 +1139,7 @@ private void setInitialWidth() { width = ASTUtils.width(widthSpec, parent.instantiations()); } } - + ////////////////////////////////////////////////////// //// Private fields. @@ -1152,14 +1152,15 @@ private void setInitialWidth() { * Cached reaction graph containing reactions that form a causality loop. */ private ReactionInstanceGraph cachedReactionLoopGraph = null; - + /** * Return true if this is a generated delay reactor that originates from * an "after" delay on a connection. - * + * * @return True if this is a generated delay, false otherwise. */ public boolean isGeneratedDelay() { + // FIXME: hacky string matching again... if (this.definition.getReactorClass().getName().contains(DelayBodyGenerator.GEN_DELAY_CLASS_NAME)) { return true; } diff --git a/org.lflang/src/org/lflang/generator/Validator.java b/org.lflang/src/org/lflang/generator/Validator.java index bc11061f0d..7a9b85c453 100644 --- a/org.lflang/src/org/lflang/generator/Validator.java +++ b/org.lflang/src/org/lflang/generator/Validator.java @@ -161,7 +161,7 @@ private List> getValidationStrategies() { */ private Pair getValidationStrategy(Path generatedFile) { List sorted = getPossibleStrategies().stream() - .sorted(Comparator.comparingInt(vs -> -vs.getPriority())).collect(Collectors.toList()); + .sorted(Comparator.comparingInt(vs -> -vs.getPriority())).toList(); for (ValidationStrategy strategy : sorted) { LFCommand validateCommand = strategy.getCommand(generatedFile); if (validateCommand != null) { diff --git a/org.lflang/src/org/lflang/generator/c/CActionGenerator.java b/org.lflang/src/org/lflang/generator/c/CActionGenerator.java index 1c4a1b45e8..af41440641 100644 --- a/org.lflang/src/org/lflang/generator/c/CActionGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CActionGenerator.java @@ -97,13 +97,12 @@ public static String generateTokenInitializer( */ public static void generateDeclarations( Reactor reactor, - ReactorDecl decl, CodeBuilder body, CodeBuilder constructorCode ) { for (Action action : ASTUtils.allActions(reactor)) { var actionName = action.getName(); - body.pr(action, CGenerator.variableStructType(action, decl)+" _lf_"+actionName+";"); + body.pr(action, CGenerator.variableStructType(action, reactor, false)+" _lf_"+actionName+";"); // Initialize the trigger pointer in the action. constructorCode.pr(action, "self->_lf_"+actionName+".trigger = &self->_lf__"+actionName+";"); } @@ -121,11 +120,12 @@ public static void generateDeclarations( * @return The auxiliary struct for the port as a string */ public static String generateAuxiliaryStruct( - ReactorDecl decl, + Reactor r, Action action, Target target, CTypes types, - CodeBuilder federatedExtension + CodeBuilder federatedExtension, + boolean userFacing ) { var code = new CodeBuilder(); code.pr("typedef struct {"); @@ -145,7 +145,7 @@ public static String generateAuxiliaryStruct( code.pr(valueDeclaration(action, target, types)); code.pr(federatedExtension.toString()); code.unindent(); - code.pr("} " + variableStructType(action, decl) + ";"); + code.pr("} " + variableStructType(action, r, userFacing) + ";"); return code.toString(); } diff --git a/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java b/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java index 6710a0eb68..3f5eb4a5b7 100644 --- a/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java @@ -212,6 +212,7 @@ CodeBuilder generateCMakeCode( cMakeCode.pr("target_link_libraries(${LF_MAIN_TARGET} PRIVATE core)"); + cMakeCode.pr("target_include_directories(${LF_MAIN_TARGET} PUBLIC .)"); cMakeCode.pr("target_include_directories(${LF_MAIN_TARGET} PUBLIC include/)"); cMakeCode.pr("target_include_directories(${LF_MAIN_TARGET} PUBLIC include/api)"); cMakeCode.pr("target_include_directories(${LF_MAIN_TARGET} PUBLIC include/core)"); diff --git a/org.lflang/src/org/lflang/generator/c/CConstructorGenerator.java b/org.lflang/src/org/lflang/generator/c/CConstructorGenerator.java index 0cc2199cae..8944fd0796 100644 --- a/org.lflang/src/org/lflang/generator/c/CConstructorGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CConstructorGenerator.java @@ -1,8 +1,7 @@ package org.lflang.generator.c; -import org.lflang.federated.generator.FederateInstance; import org.lflang.generator.CodeBuilder; -import org.lflang.lf.ReactorDecl; +import org.lflang.lf.Reactor; /** * Generates C constructor code for a reactor. @@ -16,12 +15,12 @@ public class CConstructorGenerator { * go into the constructor. */ public static String generateConstructor( - ReactorDecl reactor, + Reactor reactor, String constructorCode ) { var structType = CUtil.selfType(reactor); var code = new CodeBuilder(); - code.pr(structType+"* new_"+reactor.getName()+"() {"); + code.pr(structType+"* new_"+CUtil.getName(reactor)+"() {"); code.indent(); code.pr(structType+"* self = ("+structType+"*)_lf_new_reactor(sizeof("+structType+"));"); code.pr(constructorCode); @@ -30,4 +29,8 @@ public static String generateConstructor( code.pr("}"); return code.toString(); } + + public static String generateConstructorPrototype(Reactor reactor) { + return CUtil.selfType(reactor)+"* new_"+CUtil.getName(reactor)+"();"; + } } diff --git a/org.lflang/src/org/lflang/generator/c/CFileConfig.java b/org.lflang/src/org/lflang/generator/c/CFileConfig.java index 8bd70b74c3..362b8645b5 100644 --- a/org.lflang/src/org/lflang/generator/c/CFileConfig.java +++ b/org.lflang/src/org/lflang/generator/c/CFileConfig.java @@ -8,12 +8,18 @@ import org.lflang.FileConfig; public class CFileConfig extends FileConfig { + private final Path includePath; public CFileConfig(Resource resource, Path srcGenBasePath, boolean useHierarchicalBin) throws IOException { super(resource, srcGenBasePath, useHierarchicalBin); + var includeDir = getOutPath().resolve("include"); + includePath = !useHierarchicalBin ? includeDir : includeDir.resolve(getOutPath().relativize(srcPath)).resolve(srcFile.getFileName().toString().split("\\.")[0]); } -} - - - + public Path getIncludePath() { + return includePath; + } + public String getRuntimeIncludePath() { + return "/lib/c/reactor-c/include"; + } +} diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.java b/org.lflang/src/org/lflang/generator/c/CGenerator.java index 34a7118135..f598219ddd 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.java @@ -25,8 +25,7 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY package org.lflang.generator.c; import static org.lflang.ASTUtils.allActions; -import static org.lflang.ASTUtils.allInputs; -import static org.lflang.ASTUtils.allOutputs; +import static org.lflang.ASTUtils.allPorts; import static org.lflang.ASTUtils.allReactions; import static org.lflang.ASTUtils.allStateVars; import static org.lflang.ASTUtils.convertToEmptyListIfNull; @@ -36,12 +35,11 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY import static org.lflang.ASTUtils.toText; import static org.lflang.util.StringUtil.addDoubleQuotes; -import java.io.BufferedWriter; import java.io.File; -import java.io.FileWriter; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.regex.Pattern; @@ -89,7 +87,6 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY import org.lflang.lf.Instantiation; import org.lflang.lf.Mode; import org.lflang.lf.Model; -import org.lflang.lf.Output; import org.lflang.lf.Port; import org.lflang.lf.Preamble; import org.lflang.lf.Reaction; @@ -99,7 +96,6 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY import org.lflang.lf.Variable; import org.lflang.util.ArduinoUtil; import org.lflang.util.FileUtil; -import org.lflang.util.LFCommand; import com.google.common.base.Objects; import com.google.common.collect.Iterables; @@ -458,110 +454,85 @@ public void doGenerate(Resource resource, LFGeneratorContext context) { FileUtil.createDirectoryIfDoesNotExist(fileConfig.getSrcGenPath().toFile()); FileUtil.createDirectoryIfDoesNotExist(fileConfig.binPath.toFile()); + FileUtil.createDirectoryIfDoesNotExist(fileConfig.getIncludePath().toFile()); handleProtoFiles(); - var lfModuleName = fileConfig.name; - generateCodeFor(lfModuleName); - // Derive target filename from the .lf filename. + var lfModuleName = fileConfig.name; var cFilename = CCompiler.getTargetFileName(lfModuleName, this.CCppMode, targetConfig); var targetFile = fileConfig.getSrcGenPath() + File.separator + cFilename; - try { + generateCodeFor(lfModuleName); + copyTargetFiles(); + generateHeaders(); + code.writeToFile(targetFile); + } catch (IOException e) { + //noinspection ThrowableNotThrown,ResultOfMethodCallIgnored + Exceptions.sneakyThrow(e); + } - String srcPrefix = targetConfig.platformOptions.platform == Platform.ARDUINO ? "src/" : ""; - - // Copy the core lib - FileUtil.copyDirectoryFromClassPath( - "/lib/c/reactor-c/core", - fileConfig.getSrcGenPath().resolve(srcPrefix + "core"), - true - ); - // Copy the C target files - copyTargetFiles(); - - // For the Zephyr target, copy default config and board files. - if (targetConfig.platformOptions.platform == Platform.ZEPHYR) { - FileUtil.copyDirectoryFromClassPath( - "/lib/platform/zephyr/boards", - fileConfig.getSrcGenPath().resolve("boards"), - false - ); - FileUtil.copyFileFromClassPath( - "/lib/platform/zephyr/prj_lf.conf", - fileConfig.getSrcGenPath().resolve("prj_lf.conf"), - true - ); - - FileUtil.copyFileFromClassPath( - "/lib/platform/zephyr/Kconfig", - fileConfig.getSrcGenPath().resolve("Kconfig"), - true - ); - } + // Create docker file. + if (targetConfig.dockerOptions != null && mainDef != null) { + try { + var dockerData = getDockerGenerator(context).generateDockerData(); + dockerData.writeDockerFile(); + (new DockerComposeGenerator(context)).writeDockerComposeFile(List.of(dockerData)); + } catch (IOException e) { + throw new RuntimeException("Error while writing Docker files", e); + } + } - // Write the generated code - code.writeToFile(targetFile); + // If cmake is requested, generate the CMakeLists.txt + if (targetConfig.platformOptions.platform != Platform.ARDUINO) { + var cmakeFile = fileConfig.getSrcGenPath() + File.separator + "CMakeLists.txt"; + var sources = new HashSet<>(ASTUtils.recursiveChildren(main)).stream() + .map(CUtil::getName).map(it -> it + (CCppMode ? ".cpp" : ".c")) + .collect(Collectors.toList()); + sources.add(cFilename); + var cmakeCode = cmakeGenerator.generateCMakeCode( + sources, + lfModuleName, + errorReporter, + CCppMode, + mainDef != null, + cMakeExtras, + targetConfig + ); + try { + cmakeCode.writeToFile(cmakeFile); } catch (IOException e) { //noinspection ThrowableNotThrown,ResultOfMethodCallIgnored Exceptions.sneakyThrow(e); } - - // Create docker file. - if (targetConfig.dockerOptions != null && mainDef != null) { - try { - var dockerData = getDockerGenerator(context).generateDockerData(); - dockerData.writeDockerFile(); - (new DockerComposeGenerator(context)).writeDockerComposeFile(List.of(dockerData)); - } catch (IOException e) { - throw new RuntimeException("Error while writing Docker files", e); - } + } else { + try { + Path include = fileConfig.getSrcGenPath().resolve("include/"); + Path src = fileConfig.getSrcGenPath().resolve("src/"); + FileUtil.arduinoDeleteHelper(src, targetConfig.threading); + FileUtil.relativeIncludeHelper(src, include); + FileUtil.relativeIncludeHelper(include, include); + } catch (IOException e) { + //noinspection ThrowableNotThrown,ResultOfMethodCallIgnored + Exceptions.sneakyThrow(e); } - // If cmake is requested, generate the CMakeLists.txt - if (targetConfig.platformOptions.platform != Platform.ARDUINO) { - var cmakeFile = fileConfig.getSrcGenPath() + File.separator + "CMakeLists.txt"; - var cmakeCode = cmakeGenerator.generateCMakeCode( - List.of(cFilename), - lfModuleName, - errorReporter, - CCppMode, - mainDef != null, - cMakeExtras, - targetConfig + if (!targetConfig.noCompile) { + ArduinoUtil arduinoUtil = new ArduinoUtil(context, commandFactory, errorReporter); + arduinoUtil.buildArduino(fileConfig, targetConfig); + context.finish( + GeneratorResult.Status.COMPILED, null ); - try { - cmakeCode.writeToFile(cmakeFile); - } catch (IOException e) { - //noinspection ThrowableNotThrown,ResultOfMethodCallIgnored - Exceptions.sneakyThrow(e); - } } else { - try { - FileUtil.arduinoDeleteHelper(fileConfig.getSrcGenPath().resolve("src/"), targetConfig.threading); - FileUtil.relativeIncludeHelper(fileConfig.getSrcGenPath().resolve("src/")); - } catch (IOException e) { - //noinspection ThrowableNotThrown,ResultOfMethodCallIgnored - Exceptions.sneakyThrow(e); - } - - if (!targetConfig.noCompile) { - ArduinoUtil arduinoUtil = new ArduinoUtil(context, commandFactory, errorReporter); - arduinoUtil.buildArduino(fileConfig, targetConfig); - context.finish( - GeneratorResult.Status.COMPILED, null - ); - } else { - System.out.println("********"); - System.out.println("To compile your program, run the following command to see information about the board you plugged in:\n\n\tarduino-cli board list\n\nGrab the FQBN and PORT from the command and run the following command in the generated sources directory:\n\n\tarduino-cli compile -b --build-property compiler.c.extra_flags='-DLF_UNTHREADED -DPLATFORM_ARDUINO -DINITIAL_EVENT_QUEUE_SIZE=10 -DINITIAL_REACT_QUEUE_SIZE=10' --build-property compiler.cpp.extra_flags='-DLF_UNTHREADED -DPLATFORM_ARDUINO -DINITIAL_EVENT_QUEUE_SIZE=10 -DINITIAL_REACT_QUEUE_SIZE=10' .\n\nTo flash/upload your generated sketch to the board, run the following command in the generated sources directory:\n\n\tarduino-cli upload -b -p \n"); - // System.out.println("For a list of all boards installed on your computer, you can use the following command:\n\n\tarduino-cli board listall\n"); - context.finish( - GeneratorResult.GENERATED_NO_EXECUTABLE.apply(context, null) - ); - } - GeneratorUtils.refreshProject(resource, context.getMode()); - return; + System.out.println("********"); + System.out.println("To compile your program, run the following command to see information about the board you plugged in:\n\n\tarduino-cli board list\n\nGrab the FQBN and PORT from the command and run the following command in the generated sources directory:\n\n\tarduino-cli compile -b --build-property compiler.c.extra_flags='-DLF_UNTHREADED -DPLATFORM_ARDUINO -DINITIAL_EVENT_QUEUE_SIZE=10 -DINITIAL_REACT_QUEUE_SIZE=10' --build-property compiler.cpp.extra_flags='-DLF_UNTHREADED -DPLATFORM_ARDUINO -DINITIAL_EVENT_QUEUE_SIZE=10 -DINITIAL_REACT_QUEUE_SIZE=10' .\n\nTo flash/upload your generated sketch to the board, run the following command in the generated sources directory:\n\n\tarduino-cli upload -b -p \n"); + // System.out.println("For a list of all boards installed on your computer, you can use the following command:\n\n\tarduino-cli board listall\n"); + context.finish( + GeneratorResult.GENERATED_NO_EXECUTABLE.apply(context, null) + ); } + GeneratorUtils.refreshProject(resource, context.getMode()); + return; + } // Dump the additional compile definitions to a file to keep the generated project // self-contained. In this way, third-party build tools like PlatformIO, west, arduino-cli can @@ -650,10 +621,9 @@ public void doGenerate(Resource resource, LFGeneratorContext context) { private void generateCodeFor( String lfModuleName - ) { + ) throws IOException { startTimeStepIsPresentCount = 0; code.pr(generateDirectives()); - code.pr(generateTopLevelPreambles()); code.pr(new CMainFunctionGenerator(targetConfig).generateCode()); // Generate code for each reactor. generateReactorDefinitions(); @@ -685,6 +655,12 @@ private void generateCodeFor( generateSelfStructs(main); generateReactorInstance(main); + if (targetConfig.fedSetupPreamble != null) { + if (targetLanguageIsCpp()) code.pr("extern \"C\" {"); + code.pr("#include \"" + targetConfig.fedSetupPreamble + "\""); + if (targetLanguageIsCpp()) code.pr("}"); + } + // If there are timers, create a table of timers to be initialized. code.pr(CTimerGenerator.generateDeclarations(timerCount)); @@ -831,11 +807,6 @@ private void inspectReactorEResource(ReactorDecl reactor) { if (lfResource != null) { copyUserFiles(lfResource.getTargetConfig(), lfResource.getFileConfig()); } - // Extract the contents of the imported file for the preambles - var contents = toDefinition(reactor).eResource().getContents(); - var model = (Model) contents.get(0); - // Add the preambles from the imported .lf file - toDefinition(reactor).getPreambles().addAll(model.getPreambles()); } } @@ -914,14 +885,14 @@ public void copyUserFiles(TargetConfig targetConfig, FileConfig fileConfig) { * of cmake-include files. * - If there are any preambles, add them to the preambles of the reactor. */ - private void generateReactorDefinitions() { - var generatedReactorDecls = new LinkedHashSet(); + private void generateReactorDefinitions() throws IOException { + var generatedReactors = new LinkedHashSet(); if (this.main != null) { - generateReactorChildren(this.main, generatedReactorDecls); + generateReactorChildren(this.main, generatedReactors); } if (this.mainDef != null) { - generateReactorClass(this.mainDef.getReactorClass()); + generateReactorClass(ASTUtils.toDefinition(this.mainDef.getReactorClass())); } if (mainDef == null) { @@ -940,6 +911,33 @@ private void generateReactorDefinitions() { } } + /** Generate user-visible header files for all reactors instantiated. */ + private void generateHeaders() throws IOException { + FileUtil.deleteDirectory(fileConfig.getIncludePath()); + FileUtil.copyDirectoryFromClassPath( + fileConfig.getRuntimeIncludePath(), + fileConfig.getIncludePath(), + false + ); + for (Reactor r : reactors) { + CReactorHeaderFileGenerator.doGenerate( + types, r, fileConfig, + (builder, rr, userFacing) -> { + generateAuxiliaryStructs(builder, rr, userFacing); + if (userFacing) { + ASTUtils.allInstantiations(r).stream().map(Instantiation::getReactorClass).collect(Collectors.toSet()).forEach(it -> { + ASTUtils.allPorts(ASTUtils.toDefinition(it)) + .forEach(p -> builder.pr(CPortGenerator.generateAuxiliaryStruct( + ASTUtils.toDefinition(it), p, getTarget(), errorReporter, types, new CodeBuilder(), true, it + ))); + }); + } + }, + this::generateTopLevelPreambles); + } + FileUtil.copyDirectory(fileConfig.getIncludePath(), fileConfig.getSrcGenPath().resolve("include"), false); + } + /** * Generate code for the children of 'reactor' that belong to 'federate'. * Duplicates are avoided. @@ -954,15 +952,15 @@ private void generateReactorDefinitions() { */ private void generateReactorChildren( ReactorInstance reactor, - LinkedHashSet generatedReactorDecls - ) { + LinkedHashSet generatedReactors + ) throws IOException { for (ReactorInstance r : reactor.children) { if (r.reactorDeclaration != null && - !generatedReactorDecls.contains(r.reactorDeclaration)) { - generatedReactorDecls.add(r.reactorDeclaration); - generateReactorChildren(r, generatedReactorDecls); + !generatedReactors.contains(r.reactorDefinition)) { + generatedReactors.add(r.reactorDefinition); + generateReactorChildren(r, generatedReactors); inspectReactorEResource(r.reactorDeclaration); - generateReactorClass(r.reactorDeclaration); + generateReactorClass(r.reactorDefinition); } } } @@ -988,19 +986,44 @@ private void pickCompilePlatform() { * Copy target-specific header file to the src-gen directory. */ protected void copyTargetFiles() throws IOException { + // Copy the core lib + String coreLib = LFGeneratorContext.BuildParm.EXTERNAL_RUNTIME_PATH.getValue(context); + Path dest = fileConfig.getSrcGenPath(); + if (targetConfig.platformOptions.platform == Platform.ARDUINO) dest = dest.resolve("src"); + if (coreLib != null) { + FileUtil.copyDirectory(Path.of(coreLib), dest, true); + } else { + FileUtil.copyDirectoryFromClassPath( + "/lib/c/reactor-c/core", + dest.resolve("core"), + true + ); + FileUtil.copyDirectoryFromClassPath( + "/lib/c/reactor-c/lib", + dest.resolve("lib"), + true + ); + } - String srcPrefix = targetConfig.platformOptions.platform == Platform.ARDUINO ? "src/" : ""; + // For the Zephyr target, copy default config and board files. + if (targetConfig.platformOptions.platform == Platform.ZEPHYR) { + FileUtil.copyDirectoryFromClassPath( + "/lib/platform/zephyr/boards", + fileConfig.getSrcGenPath().resolve("boards"), + false + ); + FileUtil.copyFileFromClassPath( + "/lib/platform/zephyr/prj_lf.conf", + fileConfig.getSrcGenPath().resolve("prj_lf.conf"), + true + ); - FileUtil.copyDirectoryFromClassPath( - "/lib/c/reactor-c/include", - fileConfig.getSrcGenPath().resolve(srcPrefix + "include"), - false - ); - FileUtil.copyDirectoryFromClassPath( - "/lib/c/reactor-c/lib", - fileConfig.getSrcGenPath().resolve(srcPrefix + "lib"), - false - ); + FileUtil.copyFileFromClassPath( + "/lib/platform/zephyr/Kconfig", + fileConfig.getSrcGenPath().resolve("Kconfig"), + true + ); + } } //////////////////////////////////////////// @@ -1022,50 +1045,73 @@ protected void copyTargetFiles() throws IOException { * data to contained reactors that are not in the federate. * @param reactor The parsed reactor data structure. */ - private void generateReactorClass(ReactorDecl reactor) { + private void generateReactorClass(Reactor reactor) throws IOException { // FIXME: Currently we're not reusing definitions for declarations that point to the same definition. + CodeBuilder header = new CodeBuilder(); + CodeBuilder src = new CodeBuilder(); + final String headerName = CUtil.getName(reactor) + ".h"; + var guardMacro = headerName.toUpperCase().replace(".", "_"); + header.pr("#ifndef " + guardMacro); + header.pr("#define " + guardMacro); + generateReactorClassHeaders(reactor, headerName, header, src); + header.pr(generateTopLevelPreambles(reactor)); + generateUserPreamblesForReactor(reactor, src); + generateReactorClassBody(reactor, header, src); + header.pr("#endif // " + guardMacro); + FileUtil.writeToFile(header.toString(), fileConfig.getSrcGenPath().resolve(headerName), true); + var extension = targetConfig.platformOptions.platform == Platform.ARDUINO ? ".ino" : + CCppMode ? ".cpp" : ".c"; + FileUtil.writeToFile(src.toString(), fileConfig.getSrcGenPath().resolve(CUtil.getName(reactor) + extension), true); + } - Reactor defn = ASTUtils.toDefinition(reactor); - - if (reactor instanceof Reactor) { - code.pr("// =============== START reactor class " + reactor.getName()); - } else { - code.pr("// =============== START reactor class " + defn.getName() + " as " + reactor.getName()); + protected void generateReactorClassHeaders(Reactor reactor, String headerName, CodeBuilder header, CodeBuilder src) { + if (CCppMode) { + src.pr("extern \"C\" {"); + header.pr("extern \"C\" {"); } + header.pr("#include \"include/core/reactor.h\""); + src.pr("#include \"include/api/api.h\""); + src.pr("#include \"include/api/set.h\""); + generateIncludes(reactor); + if (CCppMode) { + src.pr("}"); + header.pr("}"); + } + src.pr("#include \"include/" + CReactorHeaderFileGenerator.outputPath(reactor) + "\""); + src.pr("#include \"" + headerName + "\""); + ASTUtils.allNestedClasses(reactor).map(CUtil::getName) + .map(name -> "#include \"" + name + ".h\"") + .forEach(header::pr); + } - // Preamble code contains state declarations with static initializers. - generateUserPreamblesForReactor(defn); - + private void generateReactorClassBody(Reactor reactor, CodeBuilder header, CodeBuilder src) { // Some of the following methods create lines of code that need to // go into the constructor. Collect those lines of code here: var constructorCode = new CodeBuilder(); - generateAuxiliaryStructs(reactor); - generateSelfStruct(reactor, constructorCode); - generateMethods(reactor); - generateReactions(reactor); - generateConstructor(reactor, constructorCode); - - code.pr("// =============== END reactor class " + reactor.getName()); - code.pr(""); + generateAuxiliaryStructs(header, reactor, false); + generateSelfStruct(header, reactor, constructorCode); + generateMethods(src, reactor); + generateReactions(src, reactor); + generateConstructor(src, header, reactor, constructorCode); } /** * Generate methods for {@code reactor}. */ - protected void generateMethods(ReactorDecl reactor) { - CMethodGenerator.generateMethods(reactor, code, types); + protected void generateMethods(CodeBuilder src, ReactorDecl reactor) { + CMethodGenerator.generateMethods(reactor, src, types); } /** * Generates preambles defined by user for a given reactor * @param reactor The given reactor */ - protected void generateUserPreamblesForReactor(Reactor reactor) { + protected void generateUserPreamblesForReactor(Reactor reactor, CodeBuilder src) { for (Preamble p : convertToEmptyListIfNull(reactor.getPreambles())) { - code.pr("// *********** From the preamble, verbatim:"); - code.prSourceLineNumber(p.getCode()); - code.pr(toText(p.getCode())); - code.pr("\n// *********** End of preamble."); + src.pr("// *********** From the preamble, verbatim:"); + src.prSourceLineNumber(p.getCode()); + src.pr(toText(p.getCode())); + src.pr("\n// *********** End of preamble."); } } @@ -1076,21 +1122,24 @@ protected void generateUserPreamblesForReactor(Reactor reactor) { * go into the constructor. */ protected void generateConstructor( - ReactorDecl reactor, CodeBuilder constructorCode + CodeBuilder src, CodeBuilder header, Reactor reactor, CodeBuilder constructorCode ) { - code.pr(CConstructorGenerator.generateConstructor( + header.pr(CConstructorGenerator.generateConstructorPrototype(reactor)); + src.pr(CConstructorGenerator.generateConstructor( reactor, constructorCode.toString() )); } + protected void generateIncludes(Reactor r) { + code.pr("#include \"" + CUtil.getName(r) + ".h\""); + } + /** * Generate the struct type definitions for inputs, outputs, and * actions of the specified reactor. - * @param decl The parsed reactor data structure. */ - protected void generateAuxiliaryStructs(ReactorDecl decl) { - var reactor = ASTUtils.toDefinition(decl); + protected void generateAuxiliaryStructs(CodeBuilder builder, Reactor r, boolean userFacing) { // In the case where there are incoming // p2p logical connections in decentralized // federated execution, there will be an @@ -1108,39 +1157,29 @@ protected void generateAuxiliaryStructs(ReactorDecl decl) { #endif """.formatted(types.getTargetTagType(), types.getTargetTimeType()) ); - // First, handle inputs. - for (Input input : allInputs(reactor)) { - code.pr(CPortGenerator.generateAuxiliaryStruct( - decl, - input, - getTarget(), - errorReporter, - types, - federatedExtension - )); - } - // Next, handle outputs. - for (Output output : allOutputs(reactor)) { - code.pr(CPortGenerator.generateAuxiliaryStruct( - decl, - output, + for (Port p : allPorts(r)) { + builder.pr(CPortGenerator.generateAuxiliaryStruct( + r, + p, getTarget(), errorReporter, types, - federatedExtension + federatedExtension, + userFacing, + null )); } - // Finally, handle actions. // The very first item on this struct needs to be // a trigger_t* because the struct will be cast to (trigger_t*) // by the lf_schedule() functions to get to the trigger. - for (Action action : allActions(reactor)) { - code.pr(CActionGenerator.generateAuxiliaryStruct( - decl, + for (Action action : allActions(r)) { + builder.pr(CActionGenerator.generateAuxiliaryStruct( + r, action, getTarget(), types, - federatedExtension + federatedExtension, + userFacing )); } } @@ -1152,9 +1191,9 @@ protected void generateAuxiliaryStructs(ReactorDecl decl) { * @param constructorCode Place to put lines of code that need to * go into the constructor. */ - private void generateSelfStruct(ReactorDecl decl, CodeBuilder constructorCode) { + private void generateSelfStruct(CodeBuilder builder, ReactorDecl decl, CodeBuilder constructorCode) { var reactor = toDefinition(decl); - var selfType = CUtil.selfType(decl); + var selfType = CUtil.selfType(ASTUtils.toDefinition(decl)); // Construct the typedef for the "self" struct. // Create a type name for the self struct. @@ -1170,7 +1209,7 @@ private void generateSelfStruct(ReactorDecl decl, CodeBuilder constructorCode) { body.pr(CStateGenerator.generateDeclarations(reactor, types)); // Next handle actions. - CActionGenerator.generateDeclarations(reactor, decl, body, constructorCode); + CActionGenerator.generateDeclarations(reactor, body, constructorCode); // Next handle inputs and outputs. CPortGenerator.generateDeclarations(reactor, decl, body, constructorCode); @@ -1187,7 +1226,7 @@ private void generateSelfStruct(ReactorDecl decl, CodeBuilder constructorCode) { // Next, generate the fields needed for each reaction. CReactionGenerator.generateReactionAndTriggerStructs( body, - decl, + reactor, constructorCode, types ); @@ -1198,12 +1237,12 @@ private void generateSelfStruct(ReactorDecl decl, CodeBuilder constructorCode) { // The first field has to always be a pointer to the list of // of allocated memory that must be freed when the reactor is freed. // This means that the struct can be safely cast to self_base_t. - code.pr("typedef struct {"); - code.indent(); - code.pr("struct self_base_t base;"); - code.pr(body.toString()); - code.unindent(); - code.pr("} " + selfType + ";"); + builder.pr("typedef struct {"); + builder.indent(); + builder.pr("struct self_base_t base;"); + builder.pr(body.toString()); + builder.unindent(); + builder.pr("} " + selfType + ";"); } /** @@ -1232,6 +1271,7 @@ private void generateInteractingContainedReactors( var contained = new InteractingContainedReactors(reactor); // Next generate the relevant code. for (Instantiation containedReactor : contained.containedReactors()) { + Reactor containedReactorType = ASTUtils.toDefinition(containedReactor.getReactorClass()); // First define an _width variable in case it is a bank. var array = ""; var width = -2; @@ -1259,12 +1299,12 @@ private void generateInteractingContainedReactors( // to be malloc'd at initialization. if (!ASTUtils.isMultiport(port)) { // Not a multiport. - body.pr(port, variableStructType(port, containedReactor.getReactorClass())+" "+port.getName()+";"); + body.pr(port, variableStructType(port, containedReactorType, false)+" "+port.getName()+";"); } else { // Is a multiport. // Memory will be malloc'd in initialization. body.pr(port, String.join("\n", - variableStructType(port, containedReactor.getReactorClass())+"** "+port.getName()+";", + variableStructType(port, containedReactorType, false)+"** "+port.getName()+";", "int "+port.getName()+"_width;" )); } @@ -1274,13 +1314,13 @@ private void generateInteractingContainedReactors( // self struct of the container. if (!ASTUtils.isMultiport(port)) { // Not a multiport. - body.pr(port, variableStructType(port, containedReactor.getReactorClass())+"* "+port.getName()+";"); + body.pr(port, variableStructType(port, containedReactorType, false)+"* "+port.getName()+";"); } else { // Is a multiport. // Here, we will use an array of pointers. // Memory will be malloc'd in initialization. body.pr(port, String.join("\n", - variableStructType(port, containedReactor.getReactorClass())+"** "+port.getName()+";", + variableStructType(port, containedReactorType, false)+"** "+port.getName()+";", "int "+port.getName()+"_width;" )); } @@ -1360,13 +1400,13 @@ protected void generateSelfStructExtension( * These functions have a single argument that is a void* pointing to * a struct that contains parameters, state variables, inputs (triggering or not), * actions (triggering or produced), and outputs. - * @param decl The reactor. + * @param r The reactor. */ - public void generateReactions(ReactorDecl decl) { + public void generateReactions(CodeBuilder src, Reactor r) { var reactionIndex = 0; - var reactor = ASTUtils.toDefinition(decl); + var reactor = ASTUtils.toDefinition(r); for (Reaction reaction : allReactions(reactor)) { - generateReaction(reaction, decl, reactionIndex); + generateReaction(src, reaction, r, reactionIndex); // Increment reaction index even if the reaction is not in the federate // so that across federates, the reaction indices are consistent. reactionIndex++; @@ -1378,14 +1418,13 @@ public void generateReactions(ReactorDecl decl) { * a struct that contains parameters, state variables, inputs (triggering or not), * actions (triggering or produced), and outputs. * @param reaction The reaction. - * @param decl The reactor. + * @param r The reactor. * @param reactionIndex The position of the reaction within the reactor. */ - protected void generateReaction(Reaction reaction, ReactorDecl decl, int reactionIndex) { - - code.pr(CReactionGenerator.generateReaction( + protected void generateReaction(CodeBuilder src, Reaction reaction, Reactor r, int reactionIndex) { + src.pr(CReactionGenerator.generateReaction( reaction, - decl, + r, reactionIndex, mainDef, errorReporter, @@ -1629,24 +1668,21 @@ public void processProtoFile(String filename) { * typed variable (port or action) of the specified reactor class. * This is required to be the same as the type name returned by * {@link #variableStructType(TriggerInstance)}. - * @param variable The variable. - * @param reactor The reactor class. - * @return The name of the self struct. */ - public static String variableStructType(Variable variable, ReactorDecl reactor) { - return reactor.getName().toLowerCase()+"_"+variable.getName()+"_t"; + public static String variableStructType(Variable variable, Reactor reactor, boolean userFacing) { + return (userFacing ? reactor.getName().toLowerCase() : CUtil.getName(reactor)) +"_"+variable.getName()+"_t"; } /** * Construct a unique type for the struct of the specified * instance (port or action). * This is required to be the same as the type name returned by - * {@link #variableStructType(Variable, ReactorDecl)}. + * {@link #variableStructType(Variable, Reactor, boolean)}. * @param portOrAction The port or action instance. * @return The name of the self struct. */ public static String variableStructType(TriggerInstance portOrAction) { - return portOrAction.getParent().reactorDeclaration.getName().toLowerCase()+"_"+portOrAction.getName()+"_t"; + return CUtil.getName(portOrAction.getParent().reactorDefinition)+"_"+portOrAction.getName()+"_t"; } /** @@ -1669,13 +1705,13 @@ private void generateTraceTableEntries(ReactorInstance instance) { * @param instance A reactor instance. */ public void generateReactorInstance(ReactorInstance instance) { - var reactorClass = instance.getDefinition().getReactorClass(); + var reactorClass = ASTUtils.toDefinition(instance.getDefinition().getReactorClass()); var fullName = instance.getFullName(); initializeTriggerObjects.pr( "// ***** Start initializing " + fullName + " of class " + reactorClass.getName()); // Generate the instance self struct containing parameters, state variables, // and outputs (the "self" struct). - initializeTriggerObjects.pr(CUtil.reactorRefName(instance)+"["+CUtil.runtimeIndex(instance)+"] = new_"+reactorClass.getName()+"();"); + initializeTriggerObjects.pr(CUtil.reactorRefName(instance)+"["+CUtil.runtimeIndex(instance)+"] = new_"+CUtil.getName(reactorClass)+"();"); // Generate code to initialize the "self" struct in the // _lf_initialize_trigger_objects function. generateTraceTableEntries(instance); @@ -1941,58 +1977,10 @@ protected void setUpGeneralParameters() { pickCompilePlatform(); } - -// // Perform set up that does not generate code -// protected void setUpFederateSpecificParameters(FederateInstance federate, CodeBuilder commonCode) { -// currentFederate = federate; -// if (isFederated) { -// // Reset the cmake-includes and files, to be repopulated for each federate individually. -// // This is done to enable support for separately -// // adding cmake-includes/files for different federates to prevent linking and mixing -// // all federates' supporting libraries/files together. -// targetConfig.cmakeIncludes.clear(); -// targetConfig.cmakeIncludesWithoutPath.clear(); -// targetConfig.fileNames.clear(); -// targetConfig.filesNamesWithoutPath.clear(); -// -// // Re-apply the cmake-include target property of the main .lf file. -// var target = GeneratorUtils.findTarget(mainDef.getReactorClass().eResource()); -// if (target.getConfig() != null) { -// // Update the cmake-include -// TargetProperty.updateOne( -// this.targetConfig, -// TargetProperty.CMAKE_INCLUDE, -// convertToEmptyListIfNull(target.getConfig().getPairs()), -// errorReporter -// ); -// // Update the files -// TargetProperty.updateOne( -// this.targetConfig, -// TargetProperty.FILES, -// convertToEmptyListIfNull(target.getConfig().getPairs()), -// errorReporter -// ); -// } -// // Clear out previously generated code. -// code = new CodeBuilder(commonCode); -// initializeTriggerObjects = new CodeBuilder(); -// // Enable clock synchronization if the federate -// // is not local and clock-sync is enabled -// initializeClockSynchronization(); -// startTimeStep = new CodeBuilder(); -// } -// } - protected void handleProtoFiles() { // Handle .proto files. for (String file : targetConfig.protoFiles) { this.processProtoFile(file); - var dotIndex = file.lastIndexOf("."); - var rootFilename = file; - if (dotIndex > 0) { - rootFilename = file.substring(0, dotIndex); - } - code.pr("#include " + addDoubleQuotes(rootFilename + ".pb-c.h")); } } @@ -2019,24 +2007,26 @@ public String generateDirectives() { /** * Generate top-level preamble code. */ - protected String generateTopLevelPreambles() { - CodeBuilder code = new CodeBuilder(); - - // preamble for federated execution setup - if (targetConfig.fedSetupPreamble != null) { - if (targetLanguageIsCpp()) code.pr("extern \"C\" {"); - code.pr("#include \"" + targetConfig.fedSetupPreamble + "\""); - if (targetLanguageIsCpp()) code.pr("}"); - } - - // user preambles - if (this.mainDef != null) { - var mainModel = (Model) toDefinition(mainDef.getReactorClass()).eContainer(); - for (Preamble p : mainModel.getPreambles()) { - code.pr(toText(p.getCode())); + protected String generateTopLevelPreambles(Reactor reactor) { + CodeBuilder builder = new CodeBuilder(); + var guard = "TOP_LEVEL_PREAMBLE_" + reactor.eContainer().hashCode() + "_H"; + builder.pr("#ifndef " + guard); + builder.pr("#define " + guard); + Stream.concat(Stream.of(reactor), ASTUtils.allNestedClasses(reactor)) + .flatMap(it -> ((Model) it.eContainer()).getPreambles().stream()) + .collect(Collectors.toSet()) + .forEach(it -> builder.pr(toText(it.getCode()))); + for (String file : targetConfig.protoFiles) { + var dotIndex = file.lastIndexOf("."); + var rootFilename = file; + if (dotIndex > 0) { + rootFilename = file.substring(0, dotIndex); } + code.pr("#include " + addDoubleQuotes(rootFilename + ".pb-c.h")); + builder.pr("#include " + addDoubleQuotes(rootFilename + ".pb-c.h")); } - return code.toString(); + builder.pr("#endif"); + return builder.toString(); } protected boolean targetLanguageIsCpp() { diff --git a/org.lflang/src/org/lflang/generator/c/CMethodGenerator.java b/org.lflang/src/org/lflang/generator/c/CMethodGenerator.java index febac3362c..8228ce320c 100644 --- a/org.lflang/src/org/lflang/generator/c/CMethodGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CMethodGenerator.java @@ -72,7 +72,7 @@ public static String generateMethod( code.indent(); // Define the "self" struct. - String structType = CUtil.selfType(decl); + String structType = CUtil.selfType(ASTUtils.toDefinition(decl)); // A null structType means there are no inputs, state, // or anything else. No need to declare it. if (structType != null) { @@ -139,7 +139,7 @@ public static void signatures( * @return The function name for the method. */ private static String methodFunctionName(ReactorDecl reactor, Method method) { - return reactor.getName().toLowerCase() + "_method_" + method.getName(); + return CUtil.getName(ASTUtils.toDefinition(reactor)) + "_method_" + method.getName(); } /** diff --git a/org.lflang/src/org/lflang/generator/c/CPortGenerator.java b/org.lflang/src/org/lflang/generator/c/CPortGenerator.java index 0db76a16ff..1b22b41f9a 100644 --- a/org.lflang/src/org/lflang/generator/c/CPortGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CPortGenerator.java @@ -44,22 +44,28 @@ public static void generateDeclarations( * Generate the struct type definitions for the port of the * reactor * - * @param decl The reactor declaration + * @param r The reactor * @param port The port to generate the struct * @param target The target of the code generation (C, CCpp or Python) * @param errorReporter The error reporter * @param types The helper object for types related stuff * @param federatedExtension The code needed to support federated execution + * @param userFacing Whether this struct is to be presented in a user-facing header + * @param decl The reactorDecl if this struct is for the header of this reactor's container; + * null otherwise * @return The auxiliary struct for the port as a string */ public static String generateAuxiliaryStruct( - ReactorDecl decl, + Reactor r, Port port, Target target, ErrorReporter errorReporter, CTypes types, - CodeBuilder federatedExtension + CodeBuilder federatedExtension, + boolean userFacing, + ReactorDecl decl ) { + assert decl == null || userFacing; var code = new CodeBuilder(); code.pr("typedef struct {"); code.indent(); @@ -79,10 +85,16 @@ public static String generateAuxiliaryStruct( code.pr(valueDeclaration(port, target, errorReporter, types)); code.pr(federatedExtension.toString()); code.unindent(); - code.pr("} "+variableStructType(port, decl)+";"); + var name = decl != null ? localPortName(decl, port.getName()) + : variableStructType(port, r, userFacing); + code.pr("} " + name + ";"); return code.toString(); } + public static String localPortName(ReactorDecl decl, String portName) { + return decl.getName().toLowerCase() + "_" + portName + "_t"; + } + /** * Allocate memory for the input port. * @param input The input port @@ -210,21 +222,21 @@ private static void generateInputDeclarations( if (ASTUtils.isMultiport(input)) { body.pr(input, String.join("\n", "// Multiport input array will be malloc'd later.", - variableStructType(input, decl)+"** _lf_"+inputName+";", + variableStructType(input, reactor, false)+"** _lf_"+inputName+";", "int _lf_"+inputName+"_width;", "// Default input (in case it does not get connected)", - variableStructType(input, decl)+" _lf_default__"+inputName+";", + variableStructType(input, reactor, false)+" _lf_default__"+inputName+";", "// Struct to support efficiently reading sparse inputs.", "lf_sparse_io_record_t* _lf_"+inputName+"__sparse;" )); } else { // input is not a multiport. body.pr(input, String.join("\n", - variableStructType(input, decl)+"* _lf_"+inputName+";", + variableStructType(input, reactor, false)+"* _lf_"+inputName+";", "// width of -2 indicates that it is not a multiport.", "int _lf_"+inputName+"_width;", "// Default input (in case it does not get connected)", - variableStructType(input, decl)+" _lf_default__"+inputName+";" + variableStructType(input, reactor, false)+" _lf_default__"+inputName+";" )); constructorCode.pr(input, String.join("\n", @@ -256,7 +268,7 @@ private static void generateOutputDeclarations( if (ASTUtils.isMultiport(output)) { body.pr(output, String.join("\n", "// Array of output ports.", - variableStructType(output, decl)+"* _lf_"+outputName+";", + variableStructType(output, reactor, false)+"* _lf_"+outputName+";", "int _lf_"+outputName+"_width;", "// An array of pointers to the individual ports. Useful", "// for the lf_set macros to work out-of-the-box for", @@ -264,11 +276,11 @@ private static void generateOutputDeclarations( "// value can be accessed via a -> operator (e.g.,foo[i]->value).", "// So we have to handle multiports specially here a construct that", "// array of pointers.", - variableStructType(output, decl)+"** _lf_"+outputName+"_pointers;" + variableStructType(output, reactor, false)+"** _lf_"+outputName+"_pointers;" )); } else { body.pr(output, String.join("\n", - variableStructType(output, decl)+" _lf_"+outputName+";", + variableStructType(output, reactor, false)+" _lf_"+outputName+";", "int _lf_"+outputName+"_width;" )); } diff --git a/org.lflang/src/org/lflang/generator/c/CPreambleGenerator.java b/org.lflang/src/org/lflang/generator/c/CPreambleGenerator.java index d78456e4ae..8ca0404d75 100644 --- a/org.lflang/src/org/lflang/generator/c/CPreambleGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CPreambleGenerator.java @@ -36,34 +36,26 @@ public static String generateIncludeStatements( if (cppMode || targetConfig.platformOptions.platform == Platform.ARDUINO) { code.pr("extern \"C\" {"); } - - String relPathHeader = ""; - if (targetConfig.platformOptions.platform == Platform.ARDUINO) { - relPathHeader = "src/include/"; - - CCoreFilesUtils.getCTargetHeader().forEach( - it -> code.pr("#include " + StringUtil.addDoubleQuotes("src/" + it)) - ); - } else { - CCoreFilesUtils.getCTargetHeader().forEach( - it -> code.pr("#include " + StringUtil.addDoubleQuotes(it)) - ); - } - code.pr("#include \"" + relPathHeader + "core/reactor.h\""); - code.pr("#include \"" + relPathHeader + "core/reactor_common.h\""); + code.pr("#include "); + code.pr("#include \"include/core/platform.h\""); + CCoreFilesUtils.getCTargetHeader().forEach( + it -> code.pr("#include " + StringUtil.addDoubleQuotes(it)) + ); + code.pr("#include \"include/core/reactor.h\""); + code.pr("#include \"include/core/reactor_common.h\""); if (targetConfig.threading) { - code.pr("#include \"" + relPathHeader + "core/threaded/scheduler.h\""); + code.pr("#include \"include/core/threaded/scheduler.h\""); } if (tracing != null) { - code.pr("#include \"" + relPathHeader + "core/trace.h\""); + code.pr("#include \"include/core/trace.h\""); } - code.pr("#include \"" + relPathHeader + "core/mixed_radix.h\""); - code.pr("#include \"" + relPathHeader + "core/port.h\""); + code.pr("#include \"include/core/mixed_radix.h\""); + code.pr("#include \"include/core/port.h\""); code.pr("int lf_reactor_c_main(int argc, const char* argv[]);"); if(targetConfig.fedSetupPreamble != null) { - code.pr("#include \"" + relPathHeader + "core/federated/federate.h\""); - code.pr("#include \"" + relPathHeader + "core/federated/net_common.h\""); + code.pr("#include \"include/core/federated/federate.h\""); + code.pr("#include \"include/core/federated/net_common.h\""); } if (cppMode || targetConfig.platformOptions.platform == Platform.ARDUINO) { code.pr("}"); diff --git a/org.lflang/src/org/lflang/generator/c/CReactionGenerator.java b/org.lflang/src/org/lflang/generator/c/CReactionGenerator.java index 5b5d26bbf3..100f3deaa4 100644 --- a/org.lflang/src/org/lflang/generator/c/CReactionGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CReactionGenerator.java @@ -14,7 +14,6 @@ import org.lflang.ErrorReporter; import org.lflang.InferredType; import org.lflang.TargetConfig; -import org.lflang.TargetProperty.Platform; import org.lflang.federated.extensions.CExtensionUtils; import org.lflang.generator.CodeBuilder; import org.lflang.lf.Action; @@ -23,6 +22,7 @@ import org.lflang.lf.Code; import org.lflang.lf.Input; import org.lflang.lf.Instantiation; +import org.lflang.lf.LfFactory; import org.lflang.lf.Mode; import org.lflang.lf.ModeTransition; import org.lflang.lf.Output; @@ -38,7 +38,7 @@ public class CReactionGenerator { protected static String DISABLE_REACTION_INITIALIZATION_MARKER - = "// **** Do not include initialization code in this reaction."; + = "// **** Do not include initialization code in this reaction."; // FIXME: Such markers should not exist (#1687) /** * Generate necessary initialization code inside the body of the reaction that belongs to reactor decl. @@ -49,7 +49,7 @@ public class CReactionGenerator { */ public static String generateInitializationForReaction(String body, Reaction reaction, - ReactorDecl decl, + Reactor decl, int reactionIndex, CTypes types, ErrorReporter errorReporter, @@ -159,7 +159,7 @@ public static String generateInitializationForReaction(String body, // It is an action, not an output. // If it has already appeared as trigger, do not redefine it. if (!actionsAsTriggers.contains(effect.getVariable())) { - reactionInitialization.pr(CGenerator.variableStructType(variable, decl)+"* "+variable.getName()+" = &self->_lf_"+variable.getName()+";"); + reactionInitialization.pr(CGenerator.variableStructType(variable, decl, false)+"* "+variable.getName()+" = &self->_lf_"+variable.getName()+";"); } } else if (effect.getVariable() instanceof Mode) { // Mode change effect @@ -354,7 +354,7 @@ private static void generateVariablesForSendingToContainedReactors( structBuilder = new CodeBuilder(); structs.put(definition, structBuilder); } - String inputStructType = CGenerator.variableStructType(input, definition.getReactorClass()); + String inputStructType = CGenerator.variableStructType(input, ASTUtils.toDefinition(definition.getReactorClass()), false); String defName = definition.getName(); String defWidth = generateWidthVariable(defName); String inputName = input.getName(); @@ -408,22 +408,20 @@ private static void generateVariablesForSendingToContainedReactors( * @param builder The place into which to write the code. * @param structs A map from reactor instantiations to a place to write * struct fields. - * @param port The port. - * @param decl The reactor or import statement. */ private static void generatePortVariablesInReaction( CodeBuilder builder, Map structs, VarRef port, - ReactorDecl decl, + Reactor r, CTypes types ) { if (port.getVariable() instanceof Input) { - builder.pr(generateInputVariablesInReaction((Input) port.getVariable(), decl, types)); + builder.pr(generateInputVariablesInReaction((Input) port.getVariable(), r, types)); } else { // port is an output of a contained reactor. Output output = (Output) port.getVariable(); - String portStructType = CGenerator.variableStructType(output, port.getContainer().getReactorClass()); + String portStructType = CGenerator.variableStructType(output, ASTUtils.toDefinition(port.getContainer().getReactorClass()), false); CodeBuilder structBuilder = structs.get(port.getContainer()); if (structBuilder == null) { @@ -474,14 +472,13 @@ private static void generatePortVariablesInReaction( /** Generate action variables for a reaction. * @param action The action. - * @param decl The reactor. */ private static String generateActionVariablesInReaction( Action action, - ReactorDecl decl, + Reactor r, CTypes types ) { - String structType = CGenerator.variableStructType(action, decl); + String structType = CGenerator.variableStructType(action, r, false); // If the action has a type, create variables for accessing the value. InferredType type = ASTUtils.getInferredType(action); // Pointer to the lf_token_t sent as the payload in the trigger. @@ -518,14 +515,14 @@ private static String generateActionVariablesInReaction( * initialize local variables for the specified input port * in a reaction function from the "self" struct. * @param input The input statement from the AST. - * @param decl The reactor. + * @param r The reactor. */ private static String generateInputVariablesInReaction( Input input, - ReactorDecl decl, + Reactor r, CTypes types ) { - String structType = CGenerator.variableStructType(input, decl); + String structType = CGenerator.variableStructType(input, r, false); InferredType inputType = ASTUtils.getInferredType(input); CodeBuilder builder = new CodeBuilder(); String inputName = input.getName(); @@ -626,11 +623,11 @@ private static String generateInputVariablesInReaction( * initialize local variables for outputs in a reaction function * from the "self" struct. * @param effect The effect declared by the reaction. This must refer to an output. - * @param decl The reactor containing the reaction or the import statement. + * @param r The reactor containing the reaction. */ public static String generateOutputVariablesInReaction( VarRef effect, - ReactorDecl decl, + Reactor r, ErrorReporter errorReporter, boolean requiresTypes ) { @@ -644,9 +641,9 @@ public static String generateOutputVariablesInReaction( // The container of the output may be a contained reactor or // the reactor containing the reaction. String outputStructType = (effect.getContainer() == null) ? - CGenerator.variableStructType(output, decl) + CGenerator.variableStructType(output, r, false) : - CGenerator.variableStructType(output, effect.getContainer().getReactorClass()); + CGenerator.variableStructType(output, ASTUtils.toDefinition(effect.getContainer().getReactorClass()), false); if (!ASTUtils.isMultiport(output)) { // Output port is not a multiport. return outputStructType+"* "+outputName+" = &self->_lf_"+outputName+";"; @@ -668,17 +665,16 @@ public static String generateOutputVariablesInReaction( * specified reactor and a trigger_t struct for each trigger (input, action, * timer, or output of a contained reactor). * @param body The place to put the code for the self struct. - * @param decl The reactor. + * @param reactor The reactor. * @param constructorCode The place to put the constructor code. */ public static void generateReactionAndTriggerStructs( CodeBuilder body, - ReactorDecl decl, + Reactor reactor, CodeBuilder constructorCode, CTypes types ) { var reactionCount = 0; - var reactor = ASTUtils.toDefinition(decl); // Iterate over reactions and create initialize the reaction_t struct // on the self struct. Also, collect a map from triggers to the reactions // that are triggered by that trigger. Also, collect a set of sources @@ -734,7 +730,7 @@ public static void generateReactionAndTriggerStructs( var deadlineFunctionPointer = "NULL"; if (reaction.getDeadline() != null) { // The following has to match the name chosen in generateReactions - var deadlineFunctionName = generateDeadlineFunctionName(decl, reactionCount); + var deadlineFunctionName = generateDeadlineFunctionName(reactor, reactionCount); deadlineFunctionPointer = "&" + deadlineFunctionName; } @@ -742,7 +738,7 @@ public static void generateReactionAndTriggerStructs( var STPFunctionPointer = "NULL"; if (reaction.getStp() != null) { // The following has to match the name chosen in generateReactions - var STPFunctionName = generateStpFunctionName(decl, reactionCount); + var STPFunctionName = generateStpFunctionName(reactor, reactionCount); STPFunctionPointer = "&" + STPFunctionName; } @@ -756,7 +752,7 @@ public static void generateReactionAndTriggerStructs( // self->_lf__reaction_"+reactionCount+".is_STP_violated = false; constructorCode.pr(reaction, String.join("\n", "self->_lf__reaction_"+reactionCount+".number = "+reactionCount+";", - "self->_lf__reaction_"+reactionCount+".function = "+CReactionGenerator.generateReactionFunctionName(decl, reactionCount)+";", + "self->_lf__reaction_"+reactionCount+".function = "+CReactionGenerator.generateReactionFunctionName(reactor, reactionCount)+";", "self->_lf__reaction_"+reactionCount+".self = self;", "self->_lf__reaction_"+reactionCount+".deadline_violation_handler = "+deadlineFunctionPointer+";", "self->_lf__reaction_"+reactionCount+".STP_handler = "+STPFunctionPointer+";", @@ -1034,12 +1030,12 @@ public static String generateLfModeTriggeredReactions( * a struct that contains parameters, state variables, inputs (triggering or not), * actions (triggering or produced), and outputs. * @param reaction The reaction. - * @param decl The reactor. + * @param r The reactor. * @param reactionIndex The position of the reaction within the reactor. */ public static String generateReaction( Reaction reaction, - ReactorDecl decl, + Reactor r, int reactionIndex, Instantiation mainDef, ErrorReporter errorReporter, @@ -1048,44 +1044,53 @@ public static String generateReaction( boolean requiresType ) { var code = new CodeBuilder(); - var body = ASTUtils.toText(reaction.getCode()); + var body = ASTUtils.toText(getCode(types, reaction, r)); String init = generateInitializationForReaction( - body, reaction, decl, reactionIndex, + body, reaction, ASTUtils.toDefinition(r), reactionIndex, types, errorReporter, mainDef, requiresType); - - String srcPrefix = targetConfig.platformOptions.platform == Platform.ARDUINO ? "src/" : ""; + code.pr( "#include " + StringUtil.addDoubleQuotes( - srcPrefix + CCoreFilesUtils.getCTargetSetHeader())); - - CMethodGenerator.generateMacrosForMethods(ASTUtils.toDefinition(decl), code); + CCoreFilesUtils.getCTargetSetHeader())); + + CMethodGenerator.generateMacrosForMethods(ASTUtils.toDefinition(r), code); code.pr(generateFunction( - generateReactionFunctionHeader(decl, reactionIndex), - init, reaction.getCode() + generateReactionFunctionHeader(r, reactionIndex), + init, getCode(types, reaction, r) )); // Now generate code for the late function, if there is one // Note that this function can only be defined on reactions // in federates that have inputs from a logical connection. if (reaction.getStp() != null) { code.pr(generateFunction( - generateStpFunctionHeader(decl, reactionIndex), + generateStpFunctionHeader(r, reactionIndex), init, reaction.getStp().getCode())); } // Now generate code for the deadline violation function, if there is one. if (reaction.getDeadline() != null) { code.pr(generateFunction( - generateDeadlineFunctionHeader(decl, reactionIndex), + generateDeadlineFunctionHeader(r, reactionIndex), init, reaction.getDeadline().getCode())); } - CMethodGenerator.generateMacroUndefsForMethods(ASTUtils.toDefinition(decl), code); + CMethodGenerator.generateMacroUndefsForMethods(ASTUtils.toDefinition(r), code); code.pr( "#include " + StringUtil.addDoubleQuotes( - srcPrefix + CCoreFilesUtils.getCTargetSetUndefHeader())); + CCoreFilesUtils.getCTargetSetUndefHeader())); return code.toString(); } + private static Code getCode(CTypes types, Reaction r, ReactorDecl container) { + if (r.getCode() != null) return r.getCode(); + Code ret = LfFactory.eINSTANCE.createCode(); + var reactor = ASTUtils.toDefinition(container); + ret.setBody( + CReactorHeaderFileGenerator.nonInlineInitialization(r, reactor) + "\n" + + r.getName() + "( " + CReactorHeaderFileGenerator.reactionArguments(r, reactor) + " );"); + return ret; + } + public static String generateFunction(String header, String init, Code code) { var function = new CodeBuilder(); function.pr(header + " {"); @@ -1100,11 +1105,11 @@ public static String generateFunction(String header, String init, Code code) { /** * Returns the name of the deadline function for reaction. - * @param decl The reactor with the deadline + * @param r The reactor with the deadline * @param reactionIndex The number assigned to this reaction deadline */ - public static String generateDeadlineFunctionName(ReactorDecl decl, int reactionIndex) { - return CUtil.getName(decl).toLowerCase() + "_deadline_function" + reactionIndex; + public static String generateDeadlineFunctionName(Reactor r, int reactionIndex) { + return CUtil.getName(r).toLowerCase() + "_deadline_function" + reactionIndex; } /** @@ -1114,44 +1119,44 @@ public static String generateDeadlineFunctionName(ReactorDecl decl, int reaction * @param reactionIndex The reaction index. * @return The function name for the reaction. */ - public static String generateReactionFunctionName(ReactorDecl reactor, int reactionIndex) { + public static String generateReactionFunctionName(Reactor reactor, int reactionIndex) { return CUtil.getName(reactor).toLowerCase() + "reaction_function_" + reactionIndex; } /** * Returns the name of the stp function for reaction. - * @param decl The reactor with the stp + * @param r The reactor with the stp * @param reactionIndex The number assigned to this reaction deadline */ - public static String generateStpFunctionName(ReactorDecl decl, int reactionIndex) { - return CUtil.getName(decl).toLowerCase() + "_STP_function" + reactionIndex; + public static String generateStpFunctionName(Reactor r, int reactionIndex) { + return CUtil.getName(r).toLowerCase() + "_STP_function" + reactionIndex; } - /** Return the top level C function header for the deadline function numbered "reactionIndex" in "decl" - * @param decl The reactor declaration + /** Return the top level C function header for the deadline function numbered "reactionIndex" in "r" + * @param r The reactor declaration * @param reactionIndex The reaction index. * @return The function name for the deadline function. */ - public static String generateDeadlineFunctionHeader(ReactorDecl decl, + public static String generateDeadlineFunctionHeader(Reactor r, int reactionIndex) { - String functionName = generateDeadlineFunctionName(decl, reactionIndex); + String functionName = generateDeadlineFunctionName(r, reactionIndex); return generateFunctionHeader(functionName); } - /** Return the top level C function header for the reaction numbered "reactionIndex" in "decl" - * @param decl The reactor declaration + /** Return the top level C function header for the reaction numbered "reactionIndex" in "r" + * @param r The reactor declaration * @param reactionIndex The reaction index. * @return The function name for the reaction. */ - public static String generateReactionFunctionHeader(ReactorDecl decl, + public static String generateReactionFunctionHeader(Reactor r, int reactionIndex) { - String functionName = generateReactionFunctionName(decl, reactionIndex); + String functionName = generateReactionFunctionName(r, reactionIndex); return generateFunctionHeader(functionName); } - public static String generateStpFunctionHeader(ReactorDecl decl, + public static String generateStpFunctionHeader(Reactor r, int reactionIndex) { - String functionName = generateStpFunctionName(decl, reactionIndex); + String functionName = generateStpFunctionName(r, reactionIndex); return generateFunctionHeader(functionName); } diff --git a/org.lflang/src/org/lflang/generator/c/CReactorHeaderFileGenerator.java b/org.lflang/src/org/lflang/generator/c/CReactorHeaderFileGenerator.java new file mode 100644 index 0000000000..68b05f656a --- /dev/null +++ b/org.lflang/src/org/lflang/generator/c/CReactorHeaderFileGenerator.java @@ -0,0 +1,208 @@ +package org.lflang.generator.c; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.lflang.ASTUtils; +import org.lflang.generator.CodeBuilder; +import org.lflang.lf.Instantiation; +import org.lflang.lf.LfFactory; +import org.lflang.lf.Parameter; +import org.lflang.lf.Reaction; +import org.lflang.lf.Reactor; +import org.lflang.lf.StateVar; +import org.lflang.lf.TriggerRef; +import org.lflang.lf.TypedVariable; +import org.lflang.lf.VarRef; +import org.lflang.util.FileUtil; + +/** Generate user-visible header files. */ +public class CReactorHeaderFileGenerator { + + /** Functional interface for generating auxiliary structs such as port structs. */ + public interface GenerateAuxiliaryStructs { + void generate(CodeBuilder b, Reactor r, boolean userFacing); + } + + /** Return the path to the user-visible header file that would be generated for {@code r}. */ + public static Path outputPath(Reactor r) { + return Path.of(Path.of(r.eResource().getURI().toFileString()) + .getFileName().toString().replaceFirst("[.][^.]+$", "")) + .resolve(r.getName() + ".h"); + } + + /** Generate the user-visible header file for {@code r}. */ + public static void doGenerate(CTypes types, Reactor r, CFileConfig fileConfig, GenerateAuxiliaryStructs generator, Function topLevelPreamble) throws IOException { + String contents = generateHeaderFile(types, r, generator, topLevelPreamble.apply(r)); + FileUtil.writeToFile(contents, fileConfig.getIncludePath().resolve(outputPath(r))); + } + private static String generateHeaderFile(CTypes types, Reactor r, GenerateAuxiliaryStructs generator, String topLevelPreamble) { + CodeBuilder builder = new CodeBuilder(); + appendIncludeGuard(builder, r); + builder.pr(topLevelPreamble); + appendPoundIncludes(builder); + appendSelfStruct(builder, types, r); + generator.generate(builder, r, true); + for (Reaction reaction : r.getReactions()) { + appendSignature(builder, reaction, r); + } + builder.pr("#endif"); + return builder.getCode(); + } + + private static void appendIncludeGuard(CodeBuilder builder, Reactor r) { + String macro = CUtil.getName(r) + "_H"; + builder.pr("#ifndef " + macro); + builder.pr("#define " + macro); + } + private static void appendPoundIncludes(CodeBuilder builder) { + builder.pr(""" + #ifdef __cplusplus + extern "C" { + #endif + #include "../include/api/api.h" + #include "../include/api/set.h" + #include "../include/core/reactor.h" + #ifdef __cplusplus + } + #endif + """); + } + + /** The type name of the user-facing version of the self struct. */ + private static String userFacingSelfType(Reactor r) { + return r.getName().toLowerCase() + "_self_t"; + } + + private static void appendSelfStruct(CodeBuilder builder, CTypes types, Reactor r) { + builder.pr("typedef struct " + userFacingSelfType(r) + "{"); + for (Parameter p : r.getParameters()) { + builder.pr(types.getTargetType(p) + " " + p.getName() + ";"); + } + for (StateVar s : r.getStateVars()) { + builder.pr(types.getTargetType(s) + " " + s.getName() + ";"); + } + builder.pr("int end[0]; // placeholder; MSVC does not compile empty structs"); + builder.pr("} " + userFacingSelfType(r) + ";"); + } + + /** Generate the signature of the reaction function of {@code r}. */ + private static void appendSignature(CodeBuilder builder, Reaction r, Reactor reactor) { + if (r.getName() != null) builder.pr("void " + r.getName() + "(" + reactionParameters(r, reactor) + ");"); + } + /** Return a string representation of the parameters of the reaction function of {@code r}. */ + private static String reactionParameters(Reaction r, Reactor reactor) { + return Stream.concat(Stream.of(userFacingSelfType(reactor) + "* self"), portVariableStream(r, reactor) + .map(it -> it.getType(true) + " " + it.getName())) + .collect(Collectors.joining(", ")); + } + + /** Generate initialization code that is needed if {@code r} is not inlined. */ + public static String nonInlineInitialization(Reaction r, Reactor reactor) { + var mainDef = LfFactory.eINSTANCE.createInstantiation(); + mainDef.setName(reactor.getName()); + mainDef.setReactorClass(ASTUtils.findMainReactor(reactor.eResource())); + return portVariableStream(r, reactor) + .map(it -> it.container == null ? "" : it.getWidth() == null ? + String.format("%s %s = (%s) %s;", it.getType(false), it.getAlias(), it.getType(false), it.getRvalue()) + : String.format(""" + %s %s[%s]; + for (int i = 0; i < %s; i++) { + %s[i] = (%s) self->_lf_%s[i].%s; + } + """, + it.getType(true).replaceFirst("\\*", ""), + it.getAlias(), + CReactionGenerator.maxContainedReactorBankWidth( + reactor.getInstantiations().stream() + .filter(instantiation -> ASTUtils.toDefinition(instantiation.getReactorClass()).equals(it.r)) + .findAny().orElseThrow(), + null, 0, mainDef), + "self->_lf_"+it.container.getName()+"_width", + it.getAlias(), + it.getType(true).replaceFirst("\\*", ""), + it.container.getName(), + it.getName())) + .collect(Collectors.joining("\n")); + } + + /** Return a string representation of the arguments passed to the function for {@code r}. */ + public static String reactionArguments(Reaction r, Reactor reactor) { + return Stream.concat(Stream.of(getApiSelfStruct(reactor)), portVariableStream(r, reactor) + .map(it -> String.format("((%s) %s)", it.getType(true), it.getAlias()))) + .collect(Collectors.joining(", ")); + } + + /** Return code for extracting the user-facing part of the self struct from the self struct. */ + private static String getApiSelfStruct(Reactor reactor) { + return "(" + userFacingSelfType(reactor) + "*) (((char*) self) + sizeof(self_base_t))"; + } + + /** Return a stream of all ports referenced by the signature of {@code r}. */ + private static Stream portVariableStream(Reaction r, Reactor defaultReactorClass) { + return varRefStream(r) + .map(it -> it.getVariable() instanceof TypedVariable tv ? + new PortVariable( + tv, + ASTUtils.toDefinition(it.getContainer() == null ? defaultReactorClass : it.getContainer().getReactorClass()), + it.getContainer()) + : null) + .filter(Objects::nonNull); + } + + /** + * A variable that refers to a port. + * @param tv The variable of the variable reference. + * @param r The reactor that contains the port. + * @param container The {@code Instantiation} referenced in the obtaining of {@code tv}, if + * applicable; {@code null} otherwise. + */ + private record PortVariable(TypedVariable tv, Reactor r, Instantiation container) { + String getType(boolean userFacing) { + var typeName = container == null ? + CGenerator.variableStructType(tv, r, userFacing) + : CPortGenerator.localPortName(container.getReactorClass(), getName()); + var isMultiport = ASTUtils.isMultiport(ASTUtils.allPorts(r).stream() + .filter(it -> it.getName().equals(tv.getName())) + .findAny().orElseThrow()); + return typeName + "*" + (getWidth() != null ? "*" : "") + (isMultiport ? "*" : ""); + } + /** The name of the variable as it appears in the LF source. */ + String getName() { + return tv.getName(); + } + /** The alias of the variable that should be used in code generation. */ + String getAlias() { + return getName(); // TODO: avoid naming conflicts + } + /** The width of the container, if applicable. */ + String getWidth() { + return container == null || container.getWidthSpec() == null ? null : "self->_lf_"+r.getName()+"_width"; + } + /** The representation of this port as used by the LF programmer. */ + String getRvalue() { + return container == null ? getName() : container.getName() + "." + getName(); + } + } + + private static Stream inputVarRefStream(Reaction reaction) { + return varRefStream(Stream.concat(reaction.getTriggers().stream(), reaction.getSources().stream())); + } + + private static Stream varRefStream(Stream toFilter) { + return toFilter.map(it -> it instanceof VarRef v ? v : null) + .filter(Objects::nonNull); + } + + private static Stream outputVarRefStream(Reaction reaction) { + return reaction.getEffects().stream(); + } + + private static Stream varRefStream(Reaction reaction) { + return Stream.concat(inputVarRefStream(reaction), outputVarRefStream(reaction)); + } +} diff --git a/org.lflang/src/org/lflang/generator/c/CUtil.java b/org.lflang/src/org/lflang/generator/c/CUtil.java index 411a0a464b..69e9ec30ae 100644 --- a/org.lflang/src/org/lflang/generator/c/CUtil.java +++ b/org.lflang/src/org/lflang/generator/c/CUtil.java @@ -38,6 +38,7 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY import java.util.Objects; import java.util.stream.Collectors; +import org.lflang.ASTUtils; import org.lflang.ErrorReporter; import org.lflang.FileConfig; import org.lflang.InferredType; @@ -146,11 +147,12 @@ public static String channelIndexName(PortInstance port) { * reactor is main (to allow for instantiations that have the same name as * the main reactor or the .lf file). */ - public static String getName(ReactorDecl reactor) { - if (reactor instanceof Reactor r && r.isMain()) { - return reactor.getName() + "_main"; + public static String getName(Reactor reactor) { + String name = reactor.getName().toLowerCase() + reactor.hashCode(); + if (reactor.isMain()) { + return name + "_main"; } - return reactor.getName(); + return name; } /** @@ -514,16 +516,16 @@ public static String runtimeIndex(ReactorInstance reactor) { * @param reactor The reactor class. * @return The type of a self struct for the specified reactor class. */ - public static String selfType(ReactorDecl reactor) { - if (reactor instanceof Reactor r && r.isMain()) { - return reactor.getName().toLowerCase() + "_main_self_t"; + public static String selfType(Reactor reactor) { + if (reactor.isMain()) { + return "_" + CUtil.getName(reactor) + "_main_self_t"; } - return reactor.getName().toLowerCase() + "_self_t"; + return "_" + CUtil.getName(reactor) + "_self_t"; } /** Construct a unique type for the "self" struct of the class of the given reactor. */ public static String selfType(ReactorInstance instance) { - return selfType(instance.getDefinition().getReactorClass()); + return selfType(ASTUtils.toDefinition(instance.getDefinition().getReactorClass())); } /** diff --git a/org.lflang/src/org/lflang/generator/cpp/CppAssembleMethodGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppAssembleMethodGenerator.kt index 45f6f10b0a..fb4f8605d2 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppAssembleMethodGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppAssembleMethodGenerator.kt @@ -100,41 +100,41 @@ class CppAssembleMethodGenerator(private val reactor: Reactor) { private fun declareTrigger(reaction: Reaction, trigger: TriggerRef): String = if (trigger is VarRef && trigger.variable is Port) { // if the trigger is a port, then it could be a multiport or contained in a bank - iterateOverAllPortsAndApply(trigger) { port: String -> "${reaction.name}.declare_trigger(&$port);" } + iterateOverAllPortsAndApply(trigger) { port: String -> "${reaction.codeName}.declare_trigger(&$port);" } } else { // treat as single trigger otherwise - "${reaction.name}.declare_trigger(&${trigger.name});" + "${reaction.codeName}.declare_trigger(&${trigger.name});" } private fun declareDependency(reaction: Reaction, dependency: VarRef): String { assert(dependency.variable is Port) // if the trigger is a port, then it could be a multiport or contained in a bank - return iterateOverAllPortsAndApply(dependency) { port: String -> "${reaction.name}.declare_dependency(&$port);" } + return iterateOverAllPortsAndApply(dependency) { port: String -> "${reaction.codeName}.declare_dependency(&$port);" } } private fun declareAntidependency(reaction: Reaction, antidependency: VarRef): String { val variable = antidependency.variable return if (variable is Port) { // if the trigger is a port, then it could be a multiport or contained in a bank - iterateOverAllPortsAndApply(antidependency) { port: String -> "${reaction.name}.declare_antidependency(&$port);" } + iterateOverAllPortsAndApply(antidependency) { port: String -> "${reaction.codeName}.declare_antidependency(&$port);" } } else { // treat as single antidependency otherwise - if (variable is Action) "${reaction.name}.declare_schedulable_action(&${antidependency.name});" - else "${reaction.name}.declare_antidependency(&${antidependency.name});" + if (variable is Action) "${reaction.codeName}.declare_schedulable_action(&${antidependency.name});" + else "${reaction.codeName}.declare_antidependency(&${antidependency.name});" } } private fun setDeadline(reaction: Reaction): String { val delay = reaction.deadline.delay val value = if (delay is ParameterReference) "__lf_inner.${delay.parameter.name}" else delay.toCppTime() - return "${reaction.name}.set_deadline($value, [this]() { ${reaction.name}_deadline_handler(); });" + return "${reaction.codeName}.set_deadline($value, [this]() { ${reaction.codeName}_deadline_handler(); });" } private fun assembleReaction(reaction: Reaction): String { val sources = reaction.sources.filter { it.variable is Port } return with(PrependOperator) { """ - |// ${reaction.name} + |// ${reaction.codeName} ${" |"..reaction.triggers.joinToString(separator = "\n") { declareTrigger(reaction, it) }} ${" |"..sources.joinToString(separator = "\n") { declareDependency(reaction, it) }} ${" |"..reaction.effects.joinToString(separator = "\n") { declareAntidependency(reaction, it) }} diff --git a/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt b/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt index f2386bf96a..2944cd3b67 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt @@ -45,7 +45,7 @@ import org.lflang.lf.WidthSpec */ /** Get the "name" a reaction is represented with in target code.*/ -val Reaction.name +val Reaction.codeName get(): String = "r$indexInContainer" /* ********************************************************************************************** diff --git a/org.lflang/src/org/lflang/generator/cpp/CppReactionGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppReactionGenerator.kt index c1e66f3632..de34478723 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppReactionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppReactionGenerator.kt @@ -67,8 +67,8 @@ class CppReactionGenerator( private fun Reaction.getAllReferencedVariablesForContainer(container: Instantiation) = allVariableReferences.filter { it.container == container }.distinct() - private fun Reaction.getViewClassName(container: Instantiation) = "__lf_view_of_${name}_on_${container.name}_t" - private fun Reaction.getViewInstanceName(container: Instantiation) = "__lf_view_of_${name}_on_${container.name}" + private fun Reaction.getViewClassName(container: Instantiation) = "__lf_view_of_${codeName}_on_${container.name}_t" + private fun Reaction.getViewInstanceName(container: Instantiation) = "__lf_view_of_${codeName}_on_${container.name}" private val VarRef.cppType get() = @@ -102,20 +102,20 @@ class CppReactionGenerator( allUncontainedSources.map { it.name } + allUncontainedEffects.map { it.name } + allReferencedContainers.map { getViewInstanceName(it) } - val body = "void ${name}_body() { __lf_inner.${name}_body(${parameters.joinToString(", ")}); }" + val body = "void ${codeName}_body() { __lf_inner.${codeName}_body(${parameters.joinToString(", ")}); }" val deadlineHandler = - "void ${name}_deadline_handler() { __lf_inner.${name}_deadline_handler(${parameters.joinToString(", ")}); }" + "void ${codeName}_deadline_handler() { __lf_inner.${codeName}_deadline_handler(${parameters.joinToString(", ")}); }" return if (deadline == null) """ $body - reactor::Reaction $name{"$label", $priority, this, [this]() { ${name}_body(); }}; + reactor::Reaction $codeName{"$label", $priority, this, [this]() { ${codeName}_body(); }}; """.trimIndent() else """ $body $deadlineHandler - reactor::Reaction $name{"$label", $priority, this, [this]() { ${name}_body(); }}; + reactor::Reaction $codeName{"$label", $priority, this, [this]() { ${codeName}_body(); }}; """.trimIndent() } } @@ -123,11 +123,11 @@ class CppReactionGenerator( private fun generateFunctionDeclaration(reaction: Reaction, postfix: String): String { val params = reaction.getBodyParameters() return when (params.size) { - 0 -> "void ${reaction.name}_$postfix();" - 1 -> "void ${reaction.name}_$postfix(${params[0]});" + 0 -> "void ${reaction.codeName}_$postfix();" + 1 -> "void ${reaction.codeName}_$postfix(${params[0]});" else -> with(PrependOperator) { """ - |void ${reaction.name}_$postfix( + |void ${reaction.codeName}_$postfix( ${" | "..params.joinToString(",\n")}); """.trimMargin() } @@ -137,11 +137,11 @@ class CppReactionGenerator( private fun getFunctionDefinitionSignature(reaction: Reaction, postfix: String): String { val params = reaction.getBodyParameters() return when (params.size) { - 0 -> "void ${reactor.templateName}::Inner::${reaction.name}_$postfix()" - 1 -> "void ${reactor.templateName}::Inner::${reaction.name}_$postfix(${params[0]})" + 0 -> "void ${reactor.templateName}::Inner::${reaction.codeName}_$postfix()" + 1 -> "void ${reactor.templateName}::Inner::${reaction.codeName}_$postfix(${params[0]})" else -> with(PrependOperator) { """ - |void ${reactor.templateName}::Inner::${reaction.name}_$postfix( + |void ${reactor.templateName}::Inner::${reaction.codeName}_$postfix( ${" | "..params.joinToString(",\n")}) """.trimMargin() } diff --git a/org.lflang/src/org/lflang/generator/python/PythonActionGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonActionGenerator.java index f1a095403f..a2a149f522 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonActionGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonActionGenerator.java @@ -1,13 +1,13 @@ package org.lflang.generator.python; import org.lflang.lf.Action; -import org.lflang.lf.ReactorDecl; +import org.lflang.lf.Reactor; import org.lflang.generator.c.CGenerator; public class PythonActionGenerator { - public static String generateAliasTypeDef(ReactorDecl decl, Action action, + public static String generateAliasTypeDef(Reactor r, Action action, String genericActionType) { - return "typedef "+genericActionType+" "+CGenerator.variableStructType(action, decl)+";"; + return "typedef "+genericActionType+" "+CGenerator.variableStructType(action, r, false)+";"; } } diff --git a/org.lflang/src/org/lflang/generator/python/PythonGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonGenerator.java index 3f82f20016..020954be80 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonGenerator.java @@ -35,6 +35,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.xtext.xbase.lib.Exceptions; @@ -55,6 +56,7 @@ import org.lflang.generator.c.CGenerator; import org.lflang.generator.c.CUtil; import org.lflang.lf.Action; +import org.lflang.lf.Code; import org.lflang.lf.Input; import org.lflang.lf.Model; import org.lflang.lf.Output; @@ -277,8 +279,6 @@ public String generateDirectives() { code.prComment("file:/" + FileUtil.toUnixString(fileConfig.srcFile)); code.pr(PythonPreambleGenerator.generateCDefineDirectives( targetConfig, fileConfig.getSrcGenPath(), hasModalReactors)); - code.pr(PythonPreambleGenerator.generateCIncludeStatements( - targetConfig, targetLanguageIsCpp(), hasModalReactors)); return code.toString(); } @@ -288,7 +288,7 @@ public String generateDirectives() { * execution setup preamble specified in the target config. */ @Override - protected String generateTopLevelPreambles() { + protected String generateTopLevelPreambles(Reactor ignored) { // user preambles Set models = new LinkedHashSet<>(); for (Reactor r : ASTUtils.convertToEmptyListIfNull(reactors)) { @@ -304,13 +304,7 @@ protected String generateTopLevelPreambles() { for (Model m : models) { pythonPreamble.pr(PythonPreambleGenerator.generatePythonPreambles(m.getPreambles())); } - - // C preamble for federated execution setup - String ret = ""; - if (targetConfig.fedSetupPreamble != null) { - ret = "#include \"" + targetConfig.fedSetupPreamble + "\""; - } - return ret; + return PythonPreambleGenerator.generateCIncludeStatements(targetConfig, targetLanguageIsCpp(), hasModalReactors); } @Override @@ -354,38 +348,34 @@ public void processProtoFile(String filename) { /** * Generate the aliases for inputs, outputs, and struct type definitions for * actions of the specified reactor in the specified federate. - * @param decl The parsed reactor data structure. + * @param r The parsed reactor data structure. */ @Override public void generateAuxiliaryStructs( - ReactorDecl decl + CodeBuilder builder, Reactor r, boolean userFacing ) { - Reactor reactor = ASTUtils.toDefinition(decl); - // First, handle inputs. - for (Input input : ASTUtils.allInputs(reactor)) { - generateAuxiliaryStructsForPort(decl, input); + for (Input input : ASTUtils.allInputs(r)) { + generateAuxiliaryStructsForPort(builder, r, input); } - // Next, handle outputs. - for (Output output : ASTUtils.allOutputs(reactor)) { - generateAuxiliaryStructsForPort(decl, output); + for (Output output : ASTUtils.allOutputs(r)) { + generateAuxiliaryStructsForPort(builder, r, output); } - // Finally, handle actions. - for (Action action : ASTUtils.allActions(reactor)) { - generateAuxiliaryStructsForAction(decl, action); + for (Action action : ASTUtils.allActions(r)) { + generateAuxiliaryStructsForAction(builder, r, action); } } - private void generateAuxiliaryStructsForPort(ReactorDecl decl, + private void generateAuxiliaryStructsForPort(CodeBuilder builder, Reactor r, Port port) { boolean isTokenType = CUtil.isTokenType(ASTUtils.getInferredType(port), types); - code.pr(port, - PythonPortGenerator.generateAliasTypeDef(decl, port, isTokenType, + builder.pr(port, + PythonPortGenerator.generateAliasTypeDef(r, port, isTokenType, genericPortType)); } - private void generateAuxiliaryStructsForAction(ReactorDecl decl, + private void generateAuxiliaryStructsForAction(CodeBuilder builder, Reactor r, Action action) { - code.pr(action, PythonActionGenerator.generateAliasTypeDef(decl, action, genericActionType)); + builder.pr(action, PythonActionGenerator.generateAliasTypeDef(r, action, genericActionType)); } /** @@ -413,6 +403,7 @@ public void doGenerate(Resource resource, LFGeneratorContext context) { targetConfig.threading = false; } int cGeneratedPercentProgress = (IntegratedBuilder.VALIDATED_PERCENT_PROGRESS + 100) / 2; + code.pr(PythonPreambleGenerator.generateCIncludeStatements(targetConfig, targetLanguageIsCpp(), hasModalReactors)); super.doGenerate(resource, new SubContext( context, IntegratedBuilder.VALIDATED_PERCENT_PROGRESS, @@ -461,19 +452,19 @@ protected PythonDockerGenerator getDockerGenerator(LFGeneratorContext context) { * a struct that contains parameters, state variables, inputs (triggering or not), * actions (triggering or produced), and outputs. * @param reaction The reaction. - * @param decl The reactor. + * @param r The reactor. * @param reactionIndex The position of the reaction within the reactor. */ @Override - protected void generateReaction(Reaction reaction, ReactorDecl decl, int reactionIndex) { - Reactor reactor = ASTUtils.toDefinition(decl); + protected void generateReaction(CodeBuilder src, Reaction reaction, Reactor r, int reactionIndex) { + Reactor reactor = ASTUtils.toDefinition(r); // Reactions marked with a `@_c_body` attribute are generated in C if (AttributeUtils.hasCBody(reaction)) { - super.generateReaction(reaction, decl, reactionIndex); + super.generateReaction(src, reaction, r, reactionIndex); return; } - code.pr(PythonReactionGenerator.generateCReaction(reaction, decl, reactionIndex, mainDef, errorReporter, types)); + src.pr(PythonReactionGenerator.generateCReaction(reaction, reactor, reactionIndex, mainDef, errorReporter, types)); } /** @@ -510,7 +501,7 @@ protected void generateParameterInitialization(ReactorInstance instance) { * @see PythonMethodGenerator */ @Override - protected void generateMethods(ReactorDecl reactor) { } + protected void generateMethods(CodeBuilder src, ReactorDecl reactor) { } /** * Generate C preambles defined by user for a given reactor @@ -520,7 +511,7 @@ protected void generateMethods(ReactorDecl reactor) { } * @param reactor The given reactor */ @Override - protected void generateUserPreamblesForReactor(Reactor reactor) { + protected void generateUserPreamblesForReactor(Reactor reactor, CodeBuilder src) { // Do nothing } diff --git a/org.lflang/src/org/lflang/generator/python/PythonPortGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonPortGenerator.java index 2c7512b499..9e0919b52c 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonPortGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonPortGenerator.java @@ -5,6 +5,7 @@ import org.lflang.lf.Output; import org.lflang.lf.Port; import org.lflang.lf.Action; +import org.lflang.lf.Reactor; import org.lflang.lf.ReactorDecl; import org.lflang.lf.VarRef; import java.util.List; @@ -194,8 +195,8 @@ public static String generatePythonListForContainedBank(String reactorName, Port ); } - public static String generateAliasTypeDef(ReactorDecl decl, Port port, boolean isTokenType, String genericPortType) { - return "typedef "+genericPortType+" "+CGenerator.variableStructType(port, decl)+";"; + public static String generateAliasTypeDef(Reactor r, Port port, boolean isTokenType, String genericPortType) { + return "typedef "+genericPortType+" "+CGenerator.variableStructType(port, r, false)+";"; } private static String generateConvertCPortToPy(String port) { diff --git a/org.lflang/src/org/lflang/generator/python/PythonPreambleGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonPreambleGenerator.java index 5fa8aff84f..e2fd7af86a 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonPreambleGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonPreambleGenerator.java @@ -55,7 +55,7 @@ public static String generateCIncludeStatements( code.pr(CPreambleGenerator.generateIncludeStatements(targetConfig, CCppMode)); code.pr("#include \"pythontarget.h\""); if (hasModalReactors) { - code.pr("#include \"modal_models/definitions.h\""); + code.pr("#include \"include/modal_models/definitions.h\""); } return code.toString(); } diff --git a/org.lflang/src/org/lflang/generator/python/PythonReactionGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonReactionGenerator.java index 77f7352f08..bcc1beeaed 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonReactionGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonReactionGenerator.java @@ -34,49 +34,46 @@ public class PythonReactionGenerator { /** - * Generate code to call reaction numbered "reactionIndex" in reactor "decl". - * @param decl The reactor containing the reaction + * Generate code to call reaction numbered "reactionIndex" in reactor "reactor". + * @param reactor The reactor containing the reaction * @param reactionIndex The index of the reaction - * @param pyObjectDescriptor CPython related descriptors for each object in "pyObjects". * @param pyObjects CPython related objects */ - public static String generateCPythonReactionCaller(ReactorDecl decl, + public static String generateCPythonReactionCaller(Reactor reactor, int reactionIndex, List pyObjects, String inits) { String pythonFunctionName = generatePythonReactionFunctionName(reactionIndex); String cpythonFunctionName = generateCPythonReactionFunctionName(reactionIndex); - return generateCPythonFunctionCaller(decl.getName(), pythonFunctionName, cpythonFunctionName, pyObjects, inits); + return generateCPythonFunctionCaller(CUtil.getName(reactor), pythonFunctionName, cpythonFunctionName, pyObjects, inits); } /** - * Generate code to call deadline function numbered "reactionIndex" in reactor "decl". - * @param decl The reactor containing the reaction + * Generate code to call deadline function numbered "reactionIndex" in reactor "r". + * @param r The reactor containing the reaction * @param reactionIndex The index of the reaction - * @param pyObjectDescriptor CPython related descriptors for each object in "pyObjects". * @param pyObjects CPython related objects */ - public static String generateCPythonDeadlineCaller(ReactorDecl decl, + public static String generateCPythonDeadlineCaller(Reactor r, int reactionIndex, List pyObjects) { String pythonFunctionName = generatePythonDeadlineFunctionName(reactionIndex); String cpythonFunctionName = generateCPythonDeadlineFunctionName(reactionIndex); - return generateCPythonFunctionCaller(decl.getName(), pythonFunctionName, cpythonFunctionName, pyObjects, ""); + return generateCPythonFunctionCaller(CUtil.getName(r), pythonFunctionName, cpythonFunctionName, pyObjects, ""); } /** - * Generate code to call deadline function numbered "reactionIndex" in reactor "decl". - * @param decl The reactor containing the reaction + * Generate code to call deadline function numbered "reactionIndex" in reactor "r". + * @param r The reactor containing the reaction * @param reactionIndex The index of the reaction - * @param pyObjectDescriptor CPython related descriptors for each object in "pyObjects". * @param pyObjects CPython related objects */ - public static String generateCPythonSTPCaller(ReactorDecl decl, + public static String generateCPythonSTPCaller(Reactor r, int reactionIndex, List pyObjects) { String pythonFunctionName = generatePythonSTPFunctionName(reactionIndex); String cpythonFunctionName = generateCPythonSTPFunctionName(reactionIndex); - return generateCPythonFunctionCaller(decl.getName(), pythonFunctionName, cpythonFunctionName, pyObjects, ""); + return generateCPythonFunctionCaller(CUtil.getName(r), pythonFunctionName, cpythonFunctionName, pyObjects, ""); } /** @@ -122,7 +119,7 @@ private static String generateCPythonFunctionCaller(String reactorDeclName, * Generate the reaction in the .c file, which calls the Python reaction through the CPython interface. * * @param reaction The reaction to generate Python-specific initialization for. - * @param decl The reactor to which reaction belongs to. + * @param r The reactor to which reaction belongs to. * @param reactionIndex The index number of the reaction in decl. * @param mainDef The main reactor. * @param errorReporter An error reporter. @@ -130,7 +127,7 @@ private static String generateCPythonFunctionCaller(String reactorDeclName, */ public static String generateCReaction( Reaction reaction, - ReactorDecl decl, + Reactor r, int reactionIndex, Instantiation mainDef, ErrorReporter errorReporter, @@ -140,34 +137,34 @@ public static String generateCReaction( // Each input must be cast to (PyObject *) (aka their descriptors for Py_BuildValue are "O") List pyObjects = new ArrayList<>(); CodeBuilder code = new CodeBuilder(); - String cPyInit = generateCPythonInitializers(reaction, decl, pyObjects, errorReporter); + String cPyInit = generateCPythonInitializers(reaction, r, pyObjects, errorReporter); String cInit = CReactionGenerator.generateInitializationForReaction( - "", reaction, decl, reactionIndex, + "", reaction, r, reactionIndex, types, errorReporter, mainDef, Target.Python.requiresTypes); code.pr( "#include " + StringUtil.addDoubleQuotes( CCoreFilesUtils.getCTargetSetHeader())); code.pr(generateFunction( - CReactionGenerator.generateReactionFunctionHeader(decl, reactionIndex), + CReactionGenerator.generateReactionFunctionHeader(r, reactionIndex), cInit, reaction.getCode(), - generateCPythonReactionCaller(decl, reactionIndex, pyObjects, cPyInit) + generateCPythonReactionCaller(r, reactionIndex, pyObjects, cPyInit) )); // Generate code for the STP violation handler, if there is one. if (reaction.getStp() != null) { code.pr(generateFunction( - CReactionGenerator.generateStpFunctionHeader(decl, reactionIndex), + CReactionGenerator.generateStpFunctionHeader(r, reactionIndex), cInit, reaction.getStp().getCode(), - generateCPythonSTPCaller(decl, reactionIndex, pyObjects) + generateCPythonSTPCaller(r, reactionIndex, pyObjects) )); } // Generate code for the deadline violation function, if there is one. if (reaction.getDeadline() != null) { code.pr(generateFunction( - CReactionGenerator.generateDeadlineFunctionHeader(decl, reactionIndex), + CReactionGenerator.generateDeadlineFunctionHeader(r, reactionIndex), cInit, reaction.getDeadline().getCode(), - generateCPythonDeadlineCaller(decl, reactionIndex, pyObjects) + generateCPythonDeadlineCaller(r, reactionIndex, pyObjects) )); } code.pr( diff --git a/org.lflang/src/org/lflang/generator/python/PythonReactorGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonReactorGenerator.java index 5778791f76..fd954ca956 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonReactorGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonReactorGenerator.java @@ -34,7 +34,7 @@ public static String generatePythonClass(ReactorInstance instance, CodeBuilder pythonClasses = new CodeBuilder(); ReactorDecl decl = instance.getDefinition().getReactorClass(); Reactor reactor = ASTUtils.toDefinition(decl); - String className = PyUtil.getName(decl); + String className = PyUtil.getName(reactor); if (instantiatedClasses == null) { return ""; } @@ -113,7 +113,7 @@ public static String generatePythonClassInstantiations(ReactorInstance instance, ReactorInstance main) { CodeBuilder code = new CodeBuilder(); - String className = PyUtil.getName(instance.reactorDeclaration); + String className = PyUtil.getName(instance.reactorDefinition); if (instance.getWidth() > 0) { // For each reactor instance, create a list regardless of whether it is a bank or not. diff --git a/org.lflang/src/org/lflang/util/FileUtil.java b/org.lflang/src/org/lflang/util/FileUtil.java index 61fa3d8816..d4d34c7b08 100644 --- a/org.lflang/src/org/lflang/util/FileUtil.java +++ b/org.lflang/src/org/lflang/util/FileUtil.java @@ -455,15 +455,20 @@ public static boolean isCFile(Path path) { * @param dir The folder to search for includes to change. * @throws IOException If the given set of files cannot be relativized. */ - public static void relativeIncludeHelper(Path dir) throws IOException { + public static void relativeIncludeHelper(Path dir, Path includePath) throws IOException { System.out.println("Relativizing all includes in " + dir.toString()); - List allPaths = Files.walk(dir) + List includePaths = Files.walk(includePath) + .filter(Files::isRegularFile) + .filter(FileUtil::isCFile) + .sorted(Comparator.reverseOrder()) + .collect(Collectors.toList()); + List srcPaths = Files.walk(dir) .filter(Files::isRegularFile) .filter(FileUtil::isCFile) .sorted(Comparator.reverseOrder()) .collect(Collectors.toList()); Map fileStringToFilePath = new HashMap(); - for (Path path : allPaths) { + for (Path path : includePaths) { String fileName = path.getFileName().toString(); if (path.getFileName().toString().contains("CMakeLists.txt")) continue; if (fileStringToFilePath.put(fileName, path) != null) { @@ -471,7 +476,7 @@ public static void relativeIncludeHelper(Path dir) throws IOException { } } Pattern regexExpression = Pattern.compile("#include\s+[\"]([^\"]+)*[\"]"); - for (Path path : allPaths) { + for (Path path : srcPaths) { String fileName = path.getFileName().toString(); String fileContents = Files.readString(path); Matcher matcher = regexExpression.matcher(fileContents); diff --git a/org.lflang/src/org/lflang/validation/LFValidator.java b/org.lflang/src/org/lflang/validation/LFValidator.java index b9db8d3a67..7f1aa5c808 100644 --- a/org.lflang/src/org/lflang/validation/LFValidator.java +++ b/org.lflang/src/org/lflang/validation/LFValidator.java @@ -224,9 +224,9 @@ public void checkConnection(Connection connection) { for (VarRef lp : connection.getLeftPorts()) { for (VarRef rp : connection.getRightPorts()) { boolean leftInCycle = false; - + for (NamedInstance it : cycles) { - if ((lp.getContainer() == null && it.getDefinition().equals(lp.getVariable())) + if ((lp.getContainer() == null && it.getDefinition().equals(lp.getVariable())) || (it.getDefinition().equals(lp.getVariable()) && it.getParent().equals(lp.getContainer()))) { leftInCycle = true; break; @@ -234,7 +234,7 @@ public void checkConnection(Connection connection) { } for (NamedInstance it : cycles) { - if ((rp.getContainer() == null && it.getDefinition().equals(rp.getVariable())) + if ((rp.getContainer() == null && it.getDefinition().equals(rp.getVariable())) || (it.getDefinition().equals(rp.getVariable()) && it.getParent().equals(rp.getContainer()))) { if (leftInCycle) { Reactor reactor = ASTUtils.getEnclosingReactor(connection); @@ -510,7 +510,7 @@ public void checkInstantiation(Instantiation instantiation) { for (Reactor r : cycle) { names.add(r.getName()); } - + error( "Instantiation is part of a cycle: " + String.join(", ", names) + ".", Literals.INSTANTIATION__REACTOR_CLASS @@ -563,12 +563,12 @@ public void checkKeyValuePair(KeyValuePair param) { // Report problem with the assigned value. prop.type.check(param.getValue(), param.getName(), this); } - + for (String it : targetPropertyErrors) { error(it, Literals.KEY_VALUE_PAIR__VALUE); } targetPropertyErrors.clear(); - + for (String it : targetPropertyWarnings) { error(it, Literals.KEY_VALUE_PAIR__VALUE); } @@ -664,7 +664,7 @@ public void checkParameter(Parameter param) { TimeValue.MAX_LONG_DEADLINE + " nanoseconds.", Literals.PARAMETER__INIT); } - + } @Check(CheckType.FAST) @@ -691,7 +691,7 @@ public void checkPreamble(Preamble preamble) { } } else if (preamble.getVisibility() != Visibility.NONE) { warning( - String.format("The %s qualifier has no meaning for the %s target. It should be removed.", + String.format("The %s qualifier has no meaning for the %s target. It should be removed.", preamble.getVisibility(), this.target.name()), Literals.PREAMBLE__VISIBILITY ); @@ -982,12 +982,12 @@ public void checkSerializer(Serializer serializer) { for (SupportedSerializers method : SupportedSerializers.values()) { if (method.name().equalsIgnoreCase(serializer.getType())){ isValidSerializer = true; - } + } } - + if (!isValidSerializer) { error( - "Serializer can be " + Arrays.asList(SupportedSerializers.values()), + "Serializer can be " + Arrays.asList(SupportedSerializers.values()), Literals.SERIALIZER__TYPE ); } @@ -1218,7 +1218,7 @@ public void checkVarRef(VarRef varRef) { /** * Check whether an attribute is supported * and the validity of the attribute. - * + * * @param attr The attribute being checked */ @Check(CheckType.FAST) @@ -1258,7 +1258,7 @@ public void checkWidthSpec(WidthSpec widthSpec) { } } } - + @Check(CheckType.FAST) public void checkReactorIconAttribute(Reactor reactor) { var path = AttributeUtils.getIconPath(reactor); @@ -1273,7 +1273,7 @@ public void checkReactorIconAttribute(Reactor reactor) { param, Literals.ATTR_PARM__VALUE); return; } - + // Check file location var iconLocation = FileUtil.locateFile(path, reactor.eResource()); if (iconLocation == null) { @@ -1387,7 +1387,7 @@ public void checkMissingStateResetInMode(Reactor reactor) { if (!m.getStateVars().isEmpty()) { var hasResetReaction = m.getReactions().stream().anyMatch( r -> r.getTriggers().stream().anyMatch( - t -> (t instanceof BuiltinTriggerRef && + t -> (t instanceof BuiltinTriggerRef && ((BuiltinTriggerRef) t).getType() == BuiltinTrigger.RESET))); if (!hasResetReaction) { for (var s : m.getStateVars()) { @@ -1411,7 +1411,7 @@ public void checkMissingStateResetInMode(Reactor reactor) { if (!check.getStateVars().isEmpty()) { var hasResetReaction = check.getReactions().stream().anyMatch( r -> r.getTriggers().stream().anyMatch( - t -> (t instanceof BuiltinTriggerRef && + t -> (t instanceof BuiltinTriggerRef && ((BuiltinTriggerRef) t).getType() == BuiltinTrigger.RESET))); if (!hasResetReaction) { // Add state vars that are not self-resetting to the error @@ -1428,10 +1428,10 @@ public void checkMissingStateResetInMode(Reactor reactor) { } if (!error.isEmpty()) { error("This reactor contains state variables that are not reset upon mode entry: " - + error.stream().map(e -> e.getName() + " in " + + error.stream().map(e -> e.getName() + " in " + ASTUtils.getEnclosingReactor(e).getName()).collect(Collectors.joining(", ")) + ".\nThe state variables are neither marked for automatic reset nor have a dedicated reset reaction. " - + "It is usafe to instatiate this reactor inside a mode entered with reset.", + + "It is unsafe to instantiate this reactor inside a mode entered with reset.", m, Literals.MODE__INSTANTIATIONS, m.getInstantiations().indexOf(i)); } @@ -1454,12 +1454,12 @@ public void checkUnspecifiedTransitionType(Reaction reaction) { var variable = effect.getVariable(); if (variable instanceof Mode) { // The transition type is always set to default by Xtext. - // Hence, check if there is an explicit node for the transition type in the AST. + // Hence, check if there is an explicit node for the transition type in the AST. var transitionAssignment = NodeModelUtils.findNodesForFeature((EObject) effect, Literals.VAR_REF__TRANSITION); if (transitionAssignment.isEmpty()) { // Transition type not explicitly specified. var mode = (Mode) variable; // Check if reset or history transition would make a difference. - var makesDifference = !mode.getStateVars().isEmpty() + var makesDifference = !mode.getStateVars().isEmpty() || !mode.getTimers().isEmpty() || !mode.getActions().isEmpty() || mode.getConnections().stream().anyMatch(c -> c.getDelay() != null); @@ -1472,13 +1472,13 @@ public void checkUnspecifiedTransitionType(Reaction reaction) { while (!toCheck.isEmpty() && !makesDifference) { var check = toCheck.pop(); checked.add(check); - + makesDifference |= !check.getModes().isEmpty() - || !ASTUtils.allStateVars(check).isEmpty() + || !ASTUtils.allStateVars(check).isEmpty() || !ASTUtils.allTimers(check).isEmpty() || !ASTUtils.allActions(check).isEmpty() || ASTUtils.allConnections(check).stream().anyMatch(c -> c.getDelay() != null); - + // continue with inner for (var innerInstance : check.getInstantiations()) { var next = (Reactor) innerInstance.getReactorClass(); @@ -1490,7 +1490,7 @@ public void checkUnspecifiedTransitionType(Reaction reaction) { } } if (makesDifference) { - warning("You should specifiy a transition type! " + warning("You should specify a transition type! " + "Reset and history transitions have different effects on this target mode. " + "Currently, a reset type is implicitly assumed.", reaction, Literals.REACTION__EFFECTS, reaction.getEffects().indexOf(effect)); @@ -1877,5 +1877,5 @@ private boolean sameType(Type type1, Type type2) { + "state, reactor definitions, and reactor instantiation) may not start with \"__\": "; private static String USERNAME_REGEX = "^[a-z_]([a-z0-9_-]{0,31}|[a-z0-9_-]{0,30}\\$)$"; - + } diff --git a/test/C/.gitignore b/test/C/.gitignore new file mode 100644 index 0000000000..08f514ebc5 --- /dev/null +++ b/test/C/.gitignore @@ -0,0 +1 @@ +include/ diff --git a/test/C/c/bank_multiport_to_reaction_no_inlining.c b/test/C/c/bank_multiport_to_reaction_no_inlining.c new file mode 100644 index 0000000000..ae54c84c2d --- /dev/null +++ b/test/C/c/bank_multiport_to_reaction_no_inlining.c @@ -0,0 +1,16 @@ +#include "../include/BankMultiportToReactionNoInlining/BankMultiportToReactionNoInlining.h" + +void check(bankmultiporttoreactionnoinlining_self_t* self, doublecount_out_t*** out) { + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + if (out[i][j]->is_present) { + lf_print("Received %d.", out[i][j]->value); + if (self->count != out[i][j]->value) { + lf_print_error_and_exit("Expected %d.", self->count); + } + self->received = true; + } + } + } + self->count++; +} diff --git a/test/C/c/bank_multiport_to_reaction_no_inlining.cmake b/test/C/c/bank_multiport_to_reaction_no_inlining.cmake new file mode 100644 index 0000000000..c433f89d7e --- /dev/null +++ b/test/C/c/bank_multiport_to_reaction_no_inlining.cmake @@ -0,0 +1 @@ +target_sources(${LF_MAIN_TARGET} PRIVATE c/bank_multiport_to_reaction_no_inlining.c) diff --git a/test/C/c/bank_to_reaction_no_inlining.c b/test/C/c/bank_to_reaction_no_inlining.c new file mode 100644 index 0000000000..9cf5b220b9 --- /dev/null +++ b/test/C/c/bank_to_reaction_no_inlining.c @@ -0,0 +1,11 @@ +#include "../include/BankToReactionNoInlining/BankToReactionNoInlining.h" + +void check(banktoreactionnoinlining_self_t* self, count_out_t** out) { + for (int i = 0; i < 2; i++) { + lf_print("Received %d.", out[i]->value); + if (self->count != out[i]->value) { + lf_print_error_and_exit("Expected %d but got %d.", self->count, out[i]->value); + } + } + self->count++; +} diff --git a/test/C/c/bank_to_reaction_no_inlining.cmake b/test/C/c/bank_to_reaction_no_inlining.cmake new file mode 100644 index 0000000000..a4f8d8dfa3 --- /dev/null +++ b/test/C/c/bank_to_reaction_no_inlining.cmake @@ -0,0 +1 @@ +target_sources(${LF_MAIN_TARGET} PRIVATE c/bank_to_reaction_no_inlining.c) diff --git a/test/C/c/count.c b/test/C/c/count.c new file mode 100644 index 0000000000..2bc5378b7d --- /dev/null +++ b/test/C/c/count.c @@ -0,0 +1,15 @@ +#include +#include "../include/Count/Count.h" + +void increment(count_self_t* self) { + printf("in increment, count=%d\n", self->count); + self->count++; +} + +void check_done(count_self_t* self) { + printf("in done, count=%d\n", self->count); + if (self->count > 10) { + printf("%s", "requesting stop\n"); + lf_request_stop(); + } +} diff --git a/test/C/c/count.cmake b/test/C/c/count.cmake new file mode 100644 index 0000000000..a9b364a871 --- /dev/null +++ b/test/C/c/count.cmake @@ -0,0 +1 @@ +target_sources(${LF_MAIN_TARGET} PRIVATE c/count.c) diff --git a/test/C/c/count_hierarchy.c b/test/C/c/count_hierarchy.c new file mode 100644 index 0000000000..999159aab8 --- /dev/null +++ b/test/C/c/count_hierarchy.c @@ -0,0 +1,15 @@ +#include +#include "../include/CountHierarchy/CountHierarchy.h" + +void increment(counthierarchy_self_t* self, timer_out_t* out) { + printf("in increment, count=%d\n", self->count); + self->count++; +} + +void check_done(counthierarchy_self_t* self, timer_out_t* out) { + printf("in done, count=%d\n", self->count); + if (self->count > 10) { + printf("%s", "requesting stop\n"); + lf_request_stop(); + } +} diff --git a/test/C/c/count_hierarchy.cmake b/test/C/c/count_hierarchy.cmake new file mode 100644 index 0000000000..955c07270a --- /dev/null +++ b/test/C/c/count_hierarchy.cmake @@ -0,0 +1 @@ +target_sources(${LF_MAIN_TARGET} PRIVATE c/count_hierarchy.c) diff --git a/test/C/c/multiport_to_reaction_no_inlining.c b/test/C/c/multiport_to_reaction_no_inlining.c new file mode 100644 index 0000000000..5036771cbd --- /dev/null +++ b/test/C/c/multiport_to_reaction_no_inlining.c @@ -0,0 +1,14 @@ +#include "../include/MultiportToReactionNoInlining/MultiportToReactionNoInlining.h" + +void check(multiporttoreactionnoinlining_self_t* self, source_out_t** out) { + int sum = 0; + for (int i = 0; i < 4; i++) { + if (out[i]->is_present) sum += out[i]->value; + } + printf("Sum of received: %d.\n", sum); + if (sum != self->s) { + printf("ERROR: Expected %d.\n", self->s); + exit(1); + } + self->s += 16; +} diff --git a/test/C/c/multiport_to_reaction_no_inlining.cmake b/test/C/c/multiport_to_reaction_no_inlining.cmake new file mode 100644 index 0000000000..8f4226aff2 --- /dev/null +++ b/test/C/c/multiport_to_reaction_no_inlining.cmake @@ -0,0 +1 @@ +target_sources(${LF_MAIN_TARGET} PRIVATE c/multiport_to_reaction_no_inlining.c) diff --git a/test/C/c/sendreceive.c b/test/C/c/sendreceive.c new file mode 100644 index 0000000000..95a08176e7 --- /dev/null +++ b/test/C/c/sendreceive.c @@ -0,0 +1,17 @@ +#include + +#include "../include/IntPrint/Print.h" +#include "../include/IntPrint/Check.h" +#include "../include/api/set.h" + +void send(print_self_t* self, print_out_t* out) { + lf_set(out, 42); +} + +void receive(check_self_t* self, check_in_t* in) { + printf("Received: %d\n", in->value); + if (in->value != self->expected) { + printf("ERROR: Expected value to be %d.\n", self->expected); + exit(1); + } +} diff --git a/test/C/c/sendreceive.cmake b/test/C/c/sendreceive.cmake new file mode 100644 index 0000000000..acd63f229c --- /dev/null +++ b/test/C/c/sendreceive.cmake @@ -0,0 +1 @@ +target_sources(${LF_MAIN_TARGET} PRIVATE c/sendreceive.c) diff --git a/test/C/src/Deadline.lf b/test/C/src/Deadline.lf index c72dc16dcf..21ea7df141 100644 --- a/test/C/src/Deadline.lf +++ b/test/C/src/Deadline.lf @@ -5,6 +5,16 @@ target C { timeout: 6 sec } +preamble {= + #ifdef __cplusplus + extern "C" { + #endif + #include "platform.h" + #ifdef __cplusplus + } + #endif +=} + reactor Source(period: time = 3 sec) { output y: int timer t(0, period) @@ -14,7 +24,7 @@ reactor Source(period: time = 3 sec) { if (2 * (self->count / 2) != self->count) { // The count variable is odd. // Take time to cause a deadline violation. - lf_nanosleep(MSEC(1500)); + lf_sleep(MSEC(1500)); } printf("Source sends: %d.\n", self->count); lf_set(y, self->count); diff --git a/test/C/src/DeadlineHandledAbove.lf b/test/C/src/DeadlineHandledAbove.lf index 92d7e9dac8..3b0dc49198 100644 --- a/test/C/src/DeadlineHandledAbove.lf +++ b/test/C/src/DeadlineHandledAbove.lf @@ -2,6 +2,16 @@ // container reacts to that output. target C +preamble {= + #ifdef __cplusplus + extern "C" { + #endif + #include "platform.h" + #ifdef __cplusplus + } + #endif +=} + reactor Deadline(threshold: time = 100 msec) { input x: int output deadline_violation: bool diff --git a/test/C/src/DeadlineInherited.lf b/test/C/src/DeadlineInherited.lf index eab2f56d9b..3d3c8ac96b 100644 --- a/test/C/src/DeadlineInherited.lf +++ b/test/C/src/DeadlineInherited.lf @@ -4,9 +4,10 @@ target C { threading: false } -preamble {= int global_cnt = 0; =} +preamble {= extern int global_cnt; =} reactor NoDeadline { + preamble {= int global_cnt = 0; =} timer t(0 msec, 100 msec) reaction(t) {= global_cnt++; =} diff --git a/test/C/src/DeadlinePriority.lf b/test/C/src/DeadlinePriority.lf index 723a29b754..2aa5c8136f 100644 --- a/test/C/src/DeadlinePriority.lf +++ b/test/C/src/DeadlinePriority.lf @@ -4,9 +4,10 @@ target C { threading: false } -preamble {= int global_cnt = 0; =} +preamble {= extern int global_cnt; =} reactor NoDeadline { + preamble {= int global_cnt = 0; =} timer t(0 msec, 100 msec) reaction(t) {= global_cnt++; =} diff --git a/test/C/src/DeadlineWithAfterDelay.lf b/test/C/src/DeadlineWithAfterDelay.lf index d1b87860ac..5e33e129e6 100644 --- a/test/C/src/DeadlineWithAfterDelay.lf +++ b/test/C/src/DeadlineWithAfterDelay.lf @@ -4,9 +4,11 @@ target C { threading: false } -preamble {= int global_cnt = 0; =} +preamble {= extern int global_cnt; =} reactor Source { + preamble {= int global_cnt = 0; =} + output out: int timer t(0 msec, 100 msec) diff --git a/test/C/src/DeadlineWithBanks.lf b/test/C/src/DeadlineWithBanks.lf index 18bc8c3682..7a9445ec8d 100644 --- a/test/C/src/DeadlineWithBanks.lf +++ b/test/C/src/DeadlineWithBanks.lf @@ -9,9 +9,11 @@ target C { build-type: Debug } -preamble {= volatile int global_cnt = 0; =} +preamble {= extern volatile int global_cnt; =} reactor Bank(bank_index: int = 0) { + preamble {= volatile int global_cnt = 0; =} + timer t(0, 100 msec) output out: int diff --git a/test/C/src/DelayString.lf b/test/C/src/DelayString.lf index b14e680d72..80b89020dd 100644 --- a/test/C/src/DelayString.lf +++ b/test/C/src/DelayString.lf @@ -2,6 +2,16 @@ // freed. target C +preamble {= + #ifdef __cplusplus + extern "C" { + #endif + #include + #ifdef __cplusplus + } + #endif +=} + reactor DelayString2(delay: time = 100 msec) { input in: string output out: string diff --git a/test/C/src/DelayStruct.lf b/test/C/src/DelayStruct.lf index 9cd201aa11..0a92171d35 100644 --- a/test/C/src/DelayStruct.lf +++ b/test/C/src/DelayStruct.lf @@ -5,6 +5,7 @@ target C { preamble {= #include "hello.h" + #include =} reactor DelayPointer(delay: time = 100 msec) { diff --git a/test/C/src/Hello.lf b/test/C/src/Hello.lf index ea37265a23..7784e2f86f 100644 --- a/test/C/src/Hello.lf +++ b/test/C/src/Hello.lf @@ -8,6 +8,10 @@ target C { fast: true } +preamble {= + #include +=} + reactor Reschedule(period: time = 2 sec, message: string = "Hello C") { state count: int = 0 state previous_time: time = 0 diff --git a/test/C/src/ScheduleValue.lf b/test/C/src/ScheduleValue.lf index 65e36fbd06..8481d47d61 100644 --- a/test/C/src/ScheduleValue.lf +++ b/test/C/src/ScheduleValue.lf @@ -3,6 +3,17 @@ target C { timeout: 3 sec } +preamble {= + #ifdef __cplusplus + extern "C" { + #endif + #include + #include + #ifdef __cplusplus + } + #endif +=} + main reactor ScheduleValue { logical action a: char* diff --git a/test/C/src/SimpleDeadline.lf b/test/C/src/SimpleDeadline.lf index 3e2d786eec..fba3e03cbc 100644 --- a/test/C/src/SimpleDeadline.lf +++ b/test/C/src/SimpleDeadline.lf @@ -3,6 +3,16 @@ // violation. target C +preamble {= + #ifdef __cplusplus + extern "C" { + #endif + #include "platform.h" + #ifdef __cplusplus + } + #endif +=} + reactor Deadline(threshold: time = 100 msec) { input x: int output deadlineViolation: bool diff --git a/test/C/src/StructAsState.lf b/test/C/src/StructAsState.lf index 59ae495853..119762c14c 100644 --- a/test/C/src/StructAsState.lf +++ b/test/C/src/StructAsState.lf @@ -2,13 +2,14 @@ // value. target C +preamble {= + typedef struct hello_t { + char* name; + int value; + } hello_t; +=} + main reactor StructAsState { - preamble {= - typedef struct hello_t { - char* name; - int value; - } hello_t; - =} // Notice that target code delimiters are no longer necessary. state s: hello_t = {"Earth", 42} diff --git a/test/C/src/TestForPreviousOutput.lf b/test/C/src/TestForPreviousOutput.lf index fec3bc54da..583c6cbb74 100644 --- a/test/C/src/TestForPreviousOutput.lf +++ b/test/C/src/TestForPreviousOutput.lf @@ -2,6 +2,16 @@ // a given output. The output should always be 42. target C +preamble {= + #ifdef __cplusplus + extern "C" { + #endif + #include + #ifdef __cplusplus + } + #endif +=} + reactor Source { output out: int diff --git a/test/C/src/concurrent/AsyncCallback.lf b/test/C/src/concurrent/AsyncCallback.lf index ec2beae766..58120446d4 100644 --- a/test/C/src/concurrent/AsyncCallback.lf +++ b/test/C/src/concurrent/AsyncCallback.lf @@ -9,9 +9,20 @@ */ target C { tracing: true, - timeout: 2 sec + timeout: 2 sec, + keepalive: true } +preamble {= + #ifdef __cplusplus + extern "C" { + #endif + #include "platform.h" + #ifdef __cplusplus + } + #endif +=} + main reactor AsyncCallback { preamble {= void callback(void* a) { diff --git a/test/C/src/concurrent/AsyncCallbackDrop.lf b/test/C/src/concurrent/AsyncCallbackDrop.lf index f123bcdbdf..b0c5c502f2 100644 --- a/test/C/src/concurrent/AsyncCallbackDrop.lf +++ b/test/C/src/concurrent/AsyncCallbackDrop.lf @@ -7,6 +7,16 @@ target C { timeout: 2 sec } +preamble {= + #ifdef __cplusplus + extern "C" { + #endif + #include "platform.h" + #ifdef __cplusplus + } + #endif +=} + main reactor { preamble {= void callback(void* a) { diff --git a/test/C/src/concurrent/AsyncCallbackReplace.lf b/test/C/src/concurrent/AsyncCallbackReplace.lf index b7afeb2363..7bac7496b5 100644 --- a/test/C/src/concurrent/AsyncCallbackReplace.lf +++ b/test/C/src/concurrent/AsyncCallbackReplace.lf @@ -7,6 +7,16 @@ target C { timeout: 2 sec } +preamble {= + #ifdef __cplusplus + extern "C" { + #endif + #include "platform.h" + #ifdef __cplusplus + } + #endif +=} + main reactor { preamble {= void callback(void* a) { diff --git a/test/C/src/concurrent/DeadlineHandledAboveThreaded.lf b/test/C/src/concurrent/DeadlineHandledAboveThreaded.lf index 545206de91..ad14879080 100644 --- a/test/C/src/concurrent/DeadlineHandledAboveThreaded.lf +++ b/test/C/src/concurrent/DeadlineHandledAboveThreaded.lf @@ -2,6 +2,16 @@ // container reacts to that output. target C +preamble {= + #ifdef __cplusplus + extern "C" { + #endif + #include "platform.h" + #ifdef __cplusplus + } + #endif +=} + reactor Deadline(threshold: time = 100 msec) { input x: int output deadline_violation: bool @@ -20,7 +30,7 @@ main reactor { d = new Deadline(threshold = 10 msec) reaction(startup) -> d.x {= - lf_nanosleep(MSEC(200)); + lf_sleep(MSEC(200)); lf_set(d.x, 42); =} diff --git a/test/C/src/concurrent/DeadlineThreaded.lf b/test/C/src/concurrent/DeadlineThreaded.lf index ac765fe6f7..ffc1be2701 100644 --- a/test/C/src/concurrent/DeadlineThreaded.lf +++ b/test/C/src/concurrent/DeadlineThreaded.lf @@ -5,6 +5,16 @@ target C { timeout: 6 sec } +preamble {= + #ifdef __cplusplus + extern "C" { + #endif + #include "platform.h" + #ifdef __cplusplus + } + #endif +=} + reactor Source(period: time = 3000 msec) { output y: int timer t(0, period) @@ -14,7 +24,7 @@ reactor Source(period: time = 3000 msec) { if (2 * (self->count / 2) != self->count) { // The count variable is odd. // Take time to cause a deadline violation. - lf_nanosleep(MSEC(210)); + lf_sleep(MSEC(210)); } printf("Source sends: %d.\n", self->count); lf_set(y, self->count); diff --git a/test/C/src/concurrent/HelloThreaded.lf b/test/C/src/concurrent/HelloThreaded.lf index a8ea34dd05..285811a17d 100644 --- a/test/C/src/concurrent/HelloThreaded.lf +++ b/test/C/src/concurrent/HelloThreaded.lf @@ -8,6 +8,10 @@ target C { fast: true } +preamble {= + #include +=} + reactor Reschedule(period: time = 2 sec, message: string = "Hello C") { state count: int = 0 state previous_time: time = 0 diff --git a/test/C/src/concurrent/ScheduleAt.lf b/test/C/src/concurrent/ScheduleAt.lf index e68b3956e8..2ef6af7af6 100644 --- a/test/C/src/concurrent/ScheduleAt.lf +++ b/test/C/src/concurrent/ScheduleAt.lf @@ -10,6 +10,15 @@ target C { } reactor Scheduler { + preamble {= + #ifdef __cplusplus + extern "C" { + #endif + #include "include/core/reactor_common.h" + #ifdef __cplusplus + } + #endif + =} logical action act // List of microsteps. Size = 16 state microstep_delay_list: uint32_t[] = { diff --git a/test/C/src/concurrent/Tracing.lf b/test/C/src/concurrent/Tracing.lf index 71da79c7ef..cbc4513732 100644 --- a/test/C/src/concurrent/Tracing.lf +++ b/test/C/src/concurrent/Tracing.lf @@ -7,6 +7,10 @@ target C { logging: DEBUG } +preamble {= + #include +=} + reactor Source { timer t(0, 200 msec) output out: int diff --git a/test/C/src/federated/DistributedNetworkOrder.lf b/test/C/src/federated/DistributedNetworkOrder.lf index e18ea4ac04..ef979a949d 100644 --- a/test/C/src/federated/DistributedNetworkOrder.lf +++ b/test/C/src/federated/DistributedNetworkOrder.lf @@ -13,6 +13,16 @@ target C { build-type: RelWithDebInfo // Release with debug info } +preamble {= + #ifdef __cplusplus + extern "C" { + #endif + #include "federate.h" + #ifdef __cplusplus + } + #endif +=} + reactor Sender { output out: int timer t(0, 1 msec) diff --git a/test/C/src/federated/DistributedPhysicalActionUpstream.lf b/test/C/src/federated/DistributedPhysicalActionUpstream.lf index 47280a24b8..520142b466 100644 --- a/test/C/src/federated/DistributedPhysicalActionUpstream.lf +++ b/test/C/src/federated/DistributedPhysicalActionUpstream.lf @@ -13,22 +13,28 @@ import PassThrough from "../lib/PassThrough.lf" import TestCount from "../lib/TestCount.lf" preamble {= - int _counter = 1; - void callback(void *a) { - lf_schedule_int(a, 0, _counter++); - } - // Simulate time passing before a callback occurs. - void* take_time(void* a) { - while (_counter < 15) { - instant_t sleep_time = MSEC(10); - lf_sleep(sleep_time); - callback(a); - } - return NULL; - } + extern int _counter; + void callback(void *a); + void* take_time(void* a); =} reactor WithPhysicalAction { + preamble {= + int _counter = 1; + void callback(void *a) { + lf_schedule_int(a, 0, _counter++); + } + // Simulate time passing before a callback occurs. + void* take_time(void* a) { + while (_counter < 15) { + instant_t sleep_time = MSEC(10); + lf_sleep(sleep_time); + callback(a); + } + return NULL; + } + =} + output out: int state thread_id: lf_thread_t = 0 physical action act(0): int diff --git a/test/C/src/federated/DistributedPhysicalActionUpstreamLong.lf b/test/C/src/federated/DistributedPhysicalActionUpstreamLong.lf index 66bbb964e1..5cee1171cc 100644 --- a/test/C/src/federated/DistributedPhysicalActionUpstreamLong.lf +++ b/test/C/src/federated/DistributedPhysicalActionUpstreamLong.lf @@ -13,22 +13,27 @@ import PassThrough from "../lib/PassThrough.lf" import TestCount from "../lib/TestCount.lf" preamble {= - int _counter = 1; - void callback(void *a) { - lf_schedule_int(a, 0, _counter++); - } - // Simulate time passing before a callback occurs. - void* take_time(void* a) { - while (_counter < 20) { - instant_t sleep_time = USEC(50); - lf_sleep(sleep_time); - callback(a); - } - return NULL; - } + extern int _counter; + void callback(void *a); + void* take_time(void* a); =} reactor WithPhysicalAction { + preamble {= + int _counter = 1; + void callback(void *a) { + lf_schedule_int(a, 0, _counter++); + } + // Simulate time passing before a callback occurs. + void* take_time(void* a) { + while (_counter < 20) { + instant_t sleep_time = USEC(50); + lf_sleep(sleep_time); + callback(a); + } + return NULL; + } + =} output out: int state thread_id: lf_thread_t = 0 physical action act(0): int diff --git a/test/C/src/federated/HelloDistributed.lf b/test/C/src/federated/HelloDistributed.lf index 6841bf91bc..909ce53b43 100644 --- a/test/C/src/federated/HelloDistributed.lf +++ b/test/C/src/federated/HelloDistributed.lf @@ -7,6 +7,10 @@ */ target C +preamble {= + #include +=} + reactor Source { output out: string diff --git a/test/C/src/federated/LoopDistributedCentralizedPhysicalAction.lf b/test/C/src/federated/LoopDistributedCentralizedPhysicalAction.lf index 44ce6e1681..b4501cd555 100644 --- a/test/C/src/federated/LoopDistributedCentralizedPhysicalAction.lf +++ b/test/C/src/federated/LoopDistributedCentralizedPhysicalAction.lf @@ -15,19 +15,23 @@ target C { preamble {= #include // Defines sleep() - bool stop = false; - // Thread to trigger an action once every second. - void* ping(void* actionref) { - while(!stop) { - lf_print("Scheduling action."); - lf_schedule(actionref, 0); - sleep(1); - } - return NULL; - } + extern bool stop; + void* ping(void* actionref); =} reactor Looper(incr: int = 1, delay: time = 0 msec) { + preamble {= + bool stop = false; + // Thread to trigger an action once every second. + void* ping(void* actionref) { + while(!stop) { + lf_print("Scheduling action."); + lf_schedule(actionref, 0); + sleep(1); + } + return NULL; + } + =} input in: int output out: int physical action a(delay) diff --git a/test/C/src/federated/LoopDistributedDecentralized.lf b/test/C/src/federated/LoopDistributedDecentralized.lf index ae64e6e71a..4350c984b3 100644 --- a/test/C/src/federated/LoopDistributedDecentralized.lf +++ b/test/C/src/federated/LoopDistributedDecentralized.lf @@ -11,19 +11,23 @@ target C { preamble {= #include // Defines sleep() - bool stop = false; - // Thread to trigger an action once every second. - void* ping(void* actionref) { - while(!stop) { - lf_print("Scheduling action."); - lf_schedule(actionref, 0); - sleep(1); - } - return NULL; - } + extern bool stop; + void* ping(void* actionref); =} reactor Looper(incr: int = 1, delay: time = 0 msec, stp_offset: time = 0) { + preamble {= + bool stop = false; + // Thread to trigger an action once every second. + void* ping(void* actionref) { + while(!stop) { + lf_print("Scheduling action."); + lf_schedule(actionref, 0); + sleep(1); + } + return NULL; + } + =} input in: int output out: int physical action a(stp_offset) diff --git a/test/C/src/federated/LoopDistributedDouble.lf b/test/C/src/federated/LoopDistributedDouble.lf index 31b7eb0f6e..28723650d7 100644 --- a/test/C/src/federated/LoopDistributedDouble.lf +++ b/test/C/src/federated/LoopDistributedDouble.lf @@ -15,19 +15,23 @@ target C { preamble {= #include // Defines sleep() - bool stop = false; - // Thread to trigger an action once every second. - void* ping(void* actionref) { - while(!stop) { - lf_print("Scheduling action."); - lf_schedule(actionref, 0); - sleep(1); - } - return NULL; - } + extern bool stop; + void* ping(void* actionref); =} reactor Looper(incr: int = 1, delay: time = 0 msec) { + preamble {= + bool stop = false; + // Thread to trigger an action once every second. + void* ping(void* actionref) { + while(!stop) { + lf_print("Scheduling action."); + lf_schedule(actionref, 0); + sleep(1); + } + return NULL; + } + =} input in: int input in2: int output out: int diff --git a/test/C/src/include/hello.h b/test/C/src/include/hello.h index 6ee45b62a7..7b290fba89 100644 --- a/test/C/src/include/hello.h +++ b/test/C/src/include/hello.h @@ -18,14 +18,14 @@ typedef struct hello_t { typedef int* int_pointer; -hello_t* hello_constructor(char* name, int value) { +static hello_t* hello_constructor(char* name, int value) { hello_t* val = (hello_t*) malloc(sizeof(hello_t)); val->name = name; val->value = value; return val; } -hello_t* hello_copy_constructor(hello_t v) { +static hello_t* hello_copy_constructor(hello_t v) { hello_t* val = (hello_t*) malloc(sizeof(hello_t)); val->name = v.name; val->value = v.value; diff --git a/test/C/src/modal_models/BanksCount3ModesComplex.lf b/test/C/src/modal_models/BanksCount3ModesComplex.lf index ad08cc7325..47805f1009 100644 --- a/test/C/src/modal_models/BanksCount3ModesComplex.lf +++ b/test/C/src/modal_models/BanksCount3ModesComplex.lf @@ -55,7 +55,7 @@ main reactor { test = new TraceTesting( // keep-format events_size = 16, trace_size = 429, - trace = ( + trace = { 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 250000000,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 250000000,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -69,7 +69,7 @@ main reactor { 250000000,1,2,1,2,1,2,1,2,0,2,0,2,0,2,0,2,1,2,1,2,1,2,1,2,0,0,0,0,0,0,0,0, 250000000,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,0,2,0,2,0,2,0,2,0,0,0,0,0,0,0,0, 250000000,1,1,1,1,1,1,1,1,0,3,0,3,0,3,0,3,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0 - ), training = false) + }, training = false) counters.always, counters.mode1, counters.mode2, counters.never -> test.events diff --git a/test/C/src/modal_models/BanksCount3ModesSimple.lf b/test/C/src/modal_models/BanksCount3ModesSimple.lf index c03fb7e562..40c2ba50a8 100644 --- a/test/C/src/modal_models/BanksCount3ModesSimple.lf +++ b/test/C/src/modal_models/BanksCount3ModesSimple.lf @@ -13,7 +13,7 @@ main reactor { test = new TraceTesting( events_size = 3, trace_size = 63, - trace = ( // keep-format + trace = { // keep-format 0,1,1,1,1,1,1, 250000000,1,2,1,2,1,2, 250000000,1,3,1,3,1,3, @@ -23,7 +23,7 @@ main reactor { 250000000,1,1,1,1,1,1, 250000000,1,2,1,2,1,2, 250000000,1,3,1,3,1,3 - ), + }, training = false ) diff --git a/test/C/src/modal_models/BanksModalStateReset.lf b/test/C/src/modal_models/BanksModalStateReset.lf index 1dde9c5b97..8e075715c8 100644 --- a/test/C/src/modal_models/BanksModalStateReset.lf +++ b/test/C/src/modal_models/BanksModalStateReset.lf @@ -16,7 +16,7 @@ main reactor { reset1 = new[2] ResetReaction() reset2 = new[2] AutoReset() - test = new TraceTesting(events_size = 16, trace_size = 627, trace = ( // keep-format + test = new TraceTesting(events_size = 16, trace_size = 627, trace = { // keep-format 0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0, 0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0, 250000000,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0, 0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0, 250000000,0,0,0,0,0,0,0,0,1,2,1,2,0,0,0,0, 0,0,0,0,0,0,0,0,1,2,1,2,0,0,0,0, @@ -36,7 +36,7 @@ main reactor { 250000000,0,1,0,1,0,2,0,2,0,8,0,8,1,0,1,0, 0,1,0,1,0,2,0,2,0,8,0,8,1,0,1,0, 250000000,0,1,0,1,0,2,0,2,0,8,0,8,1,1,1,1, 0,1,0,1,0,2,0,2,0,8,0,8,1,1,1,1, 250000000,1,1,1,1,1,3,1,3,0,8,0,8,1,2,1,2, 1,1,1,1,1,3,1,3,0,8,0,8,1,2,1,2 - ), training = false) + }, training = false) reset1.mode_switch, reset1.count0, diff --git a/test/C/src/modal_models/ConvertCaseTest.lf b/test/C/src/modal_models/ConvertCaseTest.lf index 5475edaa35..3f644eb133 100644 --- a/test/C/src/modal_models/ConvertCaseTest.lf +++ b/test/C/src/modal_models/ConvertCaseTest.lf @@ -50,10 +50,6 @@ reactor Converter { input raw: char output converted: int - preamble {= - #include - =} - initial mode Upper { reaction(raw) -> converted, reset(Lower) {= char c = raw->value; @@ -114,7 +110,7 @@ main reactor { test = new TraceTesting( events_size = 2, trace_size = 60, - trace = ( // keep-format + trace = { // keep-format 0,1,72,1,72, 250000000,1,69,1,69, 250000000,1,76,1,76, @@ -127,7 +123,7 @@ main reactor { 250000000,1,76,1,108, 250000000,1,68,1,100, 250000000,1,95,1,95 - ), + }, training = false ) diff --git a/test/C/src/modal_models/ModalActions.lf b/test/C/src/modal_models/ModalActions.lf index 1d8dff8cb4..e687d4bafd 100644 --- a/test/C/src/modal_models/ModalActions.lf +++ b/test/C/src/modal_models/ModalActions.lf @@ -68,7 +68,7 @@ main reactor { test = new TraceTesting( events_size = 5, trace_size = 165, - trace = ( // keep-format + trace = { // keep-format 0,0,0,1,1,0,0,0,0,0,0, 500000000,0,0,0,1,1,1,0,0,0,0, 250000000,0,0,1,1,0,1,0,0,0,0, @@ -84,7 +84,7 @@ main reactor { 500000000,0,1,0,1,0,1,0,1,1,1, 250000000,0,1,0,1,0,1,1,1,0,1, 250000000,1,1,0,1,0,1,0,1,0,1 - ), + }, training = false ) diff --git a/test/C/src/modal_models/ModalAfter.lf b/test/C/src/modal_models/ModalAfter.lf index 270fd88d05..f5cc8d229e 100644 --- a/test/C/src/modal_models/ModalAfter.lf +++ b/test/C/src/modal_models/ModalAfter.lf @@ -73,7 +73,7 @@ main reactor { test = new TraceTesting( events_size = 5, trace_size = 165, - trace = ( // keep-format + trace = { // keep-format 0,0,0,1,1,0,0,0,0,0,0, 500000000,0,0,0,1,1,1,0,0,0,0, 250000000,0,0,1,1,0,1,0,0,0,0, @@ -89,7 +89,7 @@ main reactor { 500000000,0,1,0,1,0,1,0,1,1,1, 250000000,0,1,0,1,0,1,1,1,0,1, 250000000,1,1,0,1,0,1,0,1,0,1 - ), + }, training = false ) diff --git a/test/C/src/modal_models/ModalCycleBreaker.lf b/test/C/src/modal_models/ModalCycleBreaker.lf index cee4fabfec..aae727b23d 100644 --- a/test/C/src/modal_models/ModalCycleBreaker.lf +++ b/test/C/src/modal_models/ModalCycleBreaker.lf @@ -57,7 +57,7 @@ main reactor { test = new TraceTesting( events_size = 1, trace_size = 27, - trace = ( // keep-format + trace = { // keep-format 0,1,0, 100000000,1,1, 100000000,1,2, @@ -67,7 +67,7 @@ main reactor { 100000000,1,7, 100000000,1,8, 100000000,1,9 - ), + }, training = false ) diff --git a/test/C/src/modal_models/ModalStartupShutdown.lf b/test/C/src/modal_models/ModalStartupShutdown.lf index fa1ebaacdf..d39117882a 100644 --- a/test/C/src/modal_models/ModalStartupShutdown.lf +++ b/test/C/src/modal_models/ModalStartupShutdown.lf @@ -113,7 +113,7 @@ main reactor { test = new TraceTesting( events_size = 11, trace_size = 253, - trace = ( // keep-format + trace = { // keep-format 0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 500000000,1,2,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,2,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -125,7 +125,7 @@ main reactor { 500000000,1,4,0,1,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0, 0,0,4,0,1,0,1,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0, 500000000,1,4,0,1,0,1,1,1,1,1,0,1,0,1,1,1,0,0,0,0,0,0 - ), + }, training = false ) diff --git a/test/C/src/modal_models/ModalStateReset.lf b/test/C/src/modal_models/ModalStateReset.lf index 61563162b6..c098f088d5 100644 --- a/test/C/src/modal_models/ModalStateReset.lf +++ b/test/C/src/modal_models/ModalStateReset.lf @@ -63,7 +63,7 @@ main reactor { test = new TraceTesting( events_size = 4, trace_size = 171, - trace = ( // keep-format + trace = { // keep-format 0,0,0,0,0,1,0,0,0, 250000000,0,0,0,0,1,1,0,0, 250000000,0,0,0,0,1,2,0,0, @@ -83,7 +83,7 @@ main reactor { 250000000,0,1,0,2,0,8,1,0, 250000000,0,1,0,2,0,8,1,1, 250000000,1,1,1,3,0,8,1,2 - ), + }, training = false ) diff --git a/test/C/src/modal_models/ModalStateResetAuto.lf b/test/C/src/modal_models/ModalStateResetAuto.lf index a2b6b105f0..a82fd18212 100644 --- a/test/C/src/modal_models/ModalStateResetAuto.lf +++ b/test/C/src/modal_models/ModalStateResetAuto.lf @@ -59,7 +59,7 @@ main reactor { test = new TraceTesting( events_size = 4, trace_size = 171, - trace = ( // keep-format + trace = { // keep-format 0,0,0,0,0,1,0,0,0, 250000000,0,0,0,0,1,1,0,0, 250000000,0,0,0,0,1,2,0,0, @@ -79,7 +79,7 @@ main reactor { 250000000,0,1,0,2,0,8,1,0, 250000000,0,1,0,2,0,8,1,1, 250000000,1,1,1,3,0,8,1,2 - ), + }, training = false ) diff --git a/test/C/src/modal_models/ModalTimers.lf b/test/C/src/modal_models/ModalTimers.lf index 47fcf5f9e4..954a677de4 100644 --- a/test/C/src/modal_models/ModalTimers.lf +++ b/test/C/src/modal_models/ModalTimers.lf @@ -49,7 +49,7 @@ main reactor { test = new TraceTesting( events_size = 3, trace_size = 77, - trace = ( // keep-format + trace = { // keep-format 0,0,0,1,1,0,0, 750000000,0,0,1,1,0,0, 250000000,1,1,0,1,0,0, @@ -61,7 +61,7 @@ main reactor { 0,0,1,0,1,1,1, 750000000,0,1,0,1,1,1, 250000000,1,1,0,1,0,1 - ), + }, training = false ) diff --git a/test/C/src/modal_models/MultipleOutputFeeder_2Connections.lf b/test/C/src/modal_models/MultipleOutputFeeder_2Connections.lf index 60fa76dd1e..31de4fe6bd 100644 --- a/test/C/src/modal_models/MultipleOutputFeeder_2Connections.lf +++ b/test/C/src/modal_models/MultipleOutputFeeder_2Connections.lf @@ -44,7 +44,7 @@ main reactor { test = new TraceTesting( events_size = 1, trace_size = 51, - trace = ( // keep-format + trace = { // keep-format 0,1,0, 250000000,1,1, 250000000,1,2, @@ -62,7 +62,7 @@ main reactor { 100000000,1,3, 100000000,1,4, 100000000,1,5 - ), + }, training = false ) diff --git a/test/C/src/modal_models/MultipleOutputFeeder_ReactionConnections.lf b/test/C/src/modal_models/MultipleOutputFeeder_ReactionConnections.lf index dff82c602f..f89073dfc1 100644 --- a/test/C/src/modal_models/MultipleOutputFeeder_ReactionConnections.lf +++ b/test/C/src/modal_models/MultipleOutputFeeder_ReactionConnections.lf @@ -47,7 +47,7 @@ main reactor { test = new TraceTesting( events_size = 1, trace_size = 51, - trace = ( // keep-format + trace = { // keep-format 0,1,0, 250000000,1,1, 250000000,1,2, @@ -65,7 +65,7 @@ main reactor { 100000000,1,30, 100000000,1,40, 100000000,1,50 - ), + }, training = false ) diff --git a/test/C/src/modal_models/util/TraceTesting.lf b/test/C/src/modal_models/util/TraceTesting.lf index 247b3f1def..c63de35f2a 100644 --- a/test/C/src/modal_models/util/TraceTesting.lf +++ b/test/C/src/modal_models/util/TraceTesting.lf @@ -1,10 +1,6 @@ /** Utility reactor to record and test execution traces. */ target C -preamble {= - #include -=} - reactor TraceTesting( events_size: int = 0, trace_size: int = 0, diff --git a/test/C/src/multiport/BankIndexInitializer.lf b/test/C/src/multiport/BankIndexInitializer.lf index d8a2c5766e..e6c52e8efe 100644 --- a/test/C/src/multiport/BankIndexInitializer.lf +++ b/test/C/src/multiport/BankIndexInitializer.lf @@ -1,9 +1,11 @@ // Test bank of reactors to multiport input with id parameter in the bank. target C -preamble {= int table[] = {4, 3, 2, 1}; =} +preamble {= extern int table[4]; =} reactor Source(bank_index: int = 0, value: int = 0) { + preamble {= int table[] = {4, 3, 2, 1}; =} + output out: int reaction(startup) -> out {= lf_set(out, self->value); =} diff --git a/test/C/src/no_inlining/BankMultiportToReactionNoInlining.lf b/test/C/src/no_inlining/BankMultiportToReactionNoInlining.lf new file mode 100644 index 0000000000..e69e0af0b4 --- /dev/null +++ b/test/C/src/no_inlining/BankMultiportToReactionNoInlining.lf @@ -0,0 +1,30 @@ +target C { + timeout: 5 sec, + fast: true, + cmake-include: ["../../c/bank_multiport_to_reaction_no_inlining.cmake"], + files: ["../../c"] +} + +import Count from "../lib/Count.lf" + +reactor DoubleCount { + output[2] out: int + c1 = new Count() + c2 = new Count() + c1.out, c2.out -> out +} + +main reactor { + state count: int = 1 + state received: bool = false + + s = new[2] DoubleCount() + + reaction(s.out) named check + + reaction(shutdown) {= + if (!self->received) { + lf_print_error_and_exit("No inputs present."); + } + =} +} diff --git a/test/C/src/no_inlining/BankToReactionNoInlining.lf b/test/C/src/no_inlining/BankToReactionNoInlining.lf new file mode 100644 index 0000000000..4956bf68cd --- /dev/null +++ b/test/C/src/no_inlining/BankToReactionNoInlining.lf @@ -0,0 +1,16 @@ +target C { + timeout: 5 sec, + fast: true, + cmake-include: ["../../c/bank_to_reaction_no_inlining.cmake"], + files: ["../../c"] +} + +import Count from "../lib/Count.lf" + +main reactor { + state count: int = 1 + + s = new[2] Count() + + reaction(s.out) named check +} diff --git a/test/C/src/no_inlining/Count.lf b/test/C/src/no_inlining/Count.lf new file mode 100644 index 0000000000..f8fd51bec7 --- /dev/null +++ b/test/C/src/no_inlining/Count.lf @@ -0,0 +1,16 @@ +target C { + cmake-include: ["../../c/count.cmake"], + files: ["../../c"] +} + +main reactor Count { + timer t(0, 1 msec) + + state count: int + + reaction(t) named increment + + reaction(t) named check_done + + reaction(shutdown) {= printf("%s", "shutting down\n"); =} +} diff --git a/test/C/src/no_inlining/CountHierarchy.lf b/test/C/src/no_inlining/CountHierarchy.lf new file mode 100644 index 0000000000..771222af2f --- /dev/null +++ b/test/C/src/no_inlining/CountHierarchy.lf @@ -0,0 +1,23 @@ +target C { + cmake-include: ["../../c/count_hierarchy.cmake"], + files: ["../../c"] +} + +reactor Timer(m: time = 0, n: time = 0) { + output out: int + timer t(m, n) + + reaction(t) -> out {= lf_set(out, 0); =} +} + +main reactor { + t = new Timer(m = 0, n = 1 msec) + + state count: int + + reaction(t.out) named increment + + reaction(t.out) named check_done + + reaction(shutdown) {= printf("%s", "shutting down\n"); =} +} diff --git a/test/C/src/no_inlining/IntPrint.lf b/test/C/src/no_inlining/IntPrint.lf new file mode 100644 index 0000000000..fa6ab8c757 --- /dev/null +++ b/test/C/src/no_inlining/IntPrint.lf @@ -0,0 +1,22 @@ +target C { + cmake-include: ["../../c/sendreceive.cmake"], + files: ["../../c"] +} + +reactor Print { + output out: int + + reaction(startup) -> out named send +} + +reactor Check(expected: int = 42) { // expected parameter is for testing. + input in: int + + reaction(in) named receive +} + +main reactor { + s = new Print() + p = new Check() + s.out -> p.in +} diff --git a/test/C/src/no_inlining/MultiportToReactionNoInlining.lf b/test/C/src/no_inlining/MultiportToReactionNoInlining.lf new file mode 100644 index 0000000000..3c5f18d62a --- /dev/null +++ b/test/C/src/no_inlining/MultiportToReactionNoInlining.lf @@ -0,0 +1,35 @@ +// Check reaction to multiport output of a contained reactor. +target C { + timeout: 2 sec, + fast: true, + cmake-include: ["../../c/multiport_to_reaction_no_inlining.cmake"], + files: ["../../c"] +} + +reactor Source(width: int = 1) { + timer t(0, 200 msec) + state s: int = 0 + output[width] out: int + + reaction(t) -> out {= + printf("Sending.\n"); + for(int i = 0; i < out_width; i++) { + lf_set(out[i], self->s++); + } + =} +} + +main reactor { + state s: int = 6 + b = new Source(width = 4) + + reaction(b.out) named check + + reaction(shutdown) {= + if (self->s <= 6) { + fprintf(stderr, "ERROR: Destination received no input!\n"); + exit(1); + } + printf("Success.\n"); + =} +} diff --git a/test/C/src/token/include/array.h b/test/C/src/token/include/array.h index c1fd1b4991..5ea8d5230b 100644 --- a/test/C/src/token/include/array.h +++ b/test/C/src/token/include/array.h @@ -21,7 +21,7 @@ typedef struct int_array_t { * @param length The length. * @return A pointer to the array struct. */ -int_array_t* int_array_constructor(size_t length) { +static inline int_array_t* int_array_constructor(size_t length) { int_array_t* result = (int_array_t*) malloc(sizeof(int_array_t)); result->data = (int*) calloc(length, sizeof(int)); result->length = length; @@ -36,7 +36,7 @@ int_array_t* int_array_constructor(size_t length) { * @param array The array to copy. * @return void* */ -void* int_array_copy_constructor(void* array) { +static inline void* int_array_copy_constructor(void* array) { int_array_t* source = (int_array_t*) array; int_array_t* copy = (int_array_t*) malloc(sizeof(int_array_t)); copy->data = (int*) calloc(source->length, sizeof(int)); @@ -54,7 +54,7 @@ void* int_array_copy_constructor(void* array) { * when their reference count decrements to zero. * @param array The array to free. */ -void int_array_destructor(void* array) { +static inline void int_array_destructor(void* array) { free(((int_array_t*) array)->data); free(array); } diff --git a/test/Python/.gitignore b/test/Python/.gitignore new file mode 100644 index 0000000000..08f514ebc5 --- /dev/null +++ b/test/Python/.gitignore @@ -0,0 +1 @@ +include/ diff --git a/util/tracing/.gitignore b/util/tracing/.gitignore new file mode 100644 index 0000000000..5fe4f5283d --- /dev/null +++ b/util/tracing/.gitignore @@ -0,0 +1,2 @@ +*.o +trace_to_csv diff --git a/util/tracing/trace_to_chrome.c b/util/tracing/trace_to_chrome.c index 92eeee71dd..5cf979227a 100644 --- a/util/tracing/trace_to_chrome.c +++ b/util/tracing/trace_to_chrome.c @@ -31,6 +31,7 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * point your chrome browser to chrome://tracing/ and the load the .json file. */ #define LF_TRACE +#include #include "reactor.h" #include "trace.h" #include "trace_util.h" @@ -92,7 +93,7 @@ size_t read_and_write_trace(FILE* trace_file, FILE* output_file) { if (reactor_name == NULL) { if (trace[i].event_type == worker_wait_starts || trace[i].event_type == worker_wait_ends) { reactor_name = "WAIT"; - } else if (trace[i].event_type == scheduler_advancing_time_starts + } else if (trace[i].event_type == scheduler_advancing_time_starts || trace[i].event_type == scheduler_advancing_time_starts) { reactor_name = "ADVANCE TIME"; } else { @@ -194,7 +195,7 @@ size_t read_and_write_trace(FILE* trace_file, FILE* output_file) { phase = "E"; break; default: - fprintf(stderr, "WARNING: Unrecognized event type %d: %s\n", + fprintf(stderr, "WARNING: Unrecognized event type %d: %s\n", trace[i].event_type, trace_event_names[trace[i].event_type]); pid = PID_FOR_UNKNOWN_EVENT; phase = "i"; @@ -275,7 +276,7 @@ void write_metadata_events(FILE* output_file) { "\"pid\": 0, " "\"tid\": 0, " "\"args\": {" - "\"name\": \"Main thread\"" + "\"name\": \"Main thread\"" "}},\n" ); @@ -328,7 +329,7 @@ void write_metadata_events(FILE* output_file) { ); } } - + // Write the reactor names for the logical timelines. for (int i = 0; i < object_table_size; i++) { if (object_table[i].type == trace_trigger) { @@ -341,10 +342,10 @@ void write_metadata_events(FILE* output_file) { "\"pid\": %d, " // the "process" to identify by reactor. "\"tid\": %d," // The "thread" to label with action or timer name. "\"args\": {" - "\"name\": \"Trigger %s\"" + "\"name\": \"Trigger %s\"" "}},\n", reactor_index + 1, // Offset of 1 prevents collision with Execution. - i, + i, object_table[i].description); } else if (object_table[i].type == trace_reactor) { fprintf(output_file, "{" @@ -352,7 +353,7 @@ void write_metadata_events(FILE* output_file) { "\"ph\": \"M\", " // mark as metadata. "\"pid\": %d, " // the "process" to label as reactor. "\"args\": {" - "\"name\": \"Reactor %s reactions, actions, and timers in logical time\"" + "\"name\": \"Reactor %s reactions, actions, and timers in logical time\"" "}},\n", i + 1, // Offset of 1 prevents collision with Execution. object_table[i].description); @@ -363,7 +364,7 @@ void write_metadata_events(FILE* output_file) { "\"pid\": %d, " // the "process" to label as reactor. "\"tid\": %d," // The "thread" to label with action or timer name. "\"args\": {" - "\"name\": \"%s\"" + "\"name\": \"%s\"" "}},\n", PID_FOR_USER_EVENT, i, // This is the index in the object table. @@ -376,7 +377,7 @@ void write_metadata_events(FILE* output_file) { "\"ph\": \"M\", " // mark as metadata. "\"pid\": 0, " // the "process" to label "Execution". "\"args\": {" - "\"name\": \"Execution of %s\"" + "\"name\": \"Execution of %s\"" "}},\n", top_level); // Name the "process" for "Worker Waiting" if the PID is not the main execution one. @@ -386,7 +387,7 @@ void write_metadata_events(FILE* output_file) { "\"ph\": \"M\", " // mark as metadata. "\"pid\": %d, " // the "process" to label "Workers waiting for reaction queue". "\"args\": {" - "\"name\": \"Workers waiting for reaction queue\"" + "\"name\": \"Workers waiting for reaction queue\"" "}},\n", PID_FOR_WORKER_WAIT); } @@ -397,7 +398,7 @@ void write_metadata_events(FILE* output_file) { "\"ph\": \"M\", " // mark as metadata. "\"pid\": %d, " // the "process" to label "Workers waiting for reaction queue". "\"args\": {" - "\"name\": \"Workers advancing time\"" + "\"name\": \"Workers advancing time\"" "}},\n", PID_FOR_WORKER_ADVANCING_TIME); } @@ -408,7 +409,7 @@ void write_metadata_events(FILE* output_file) { "\"ph\": \"M\", " // mark as metadata. "\"pid\": %d, " // the "process" to label "User events". "\"args\": {" - "\"name\": \"User events in %s, shown in physical time:\"" + "\"name\": \"User events in %s, shown in physical time:\"" "}}\n", PID_FOR_USER_EVENT, top_level); } diff --git a/util/tracing/trace_to_csv.c b/util/tracing/trace_to_csv.c index 707cc5f43b..1abf08c082 100644 --- a/util/tracing/trace_to_csv.c +++ b/util/tracing/trace_to_csv.c @@ -30,6 +30,8 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * text file. */ #define LF_TRACE +#include +#include #include "reactor.h" #include "trace.h" #include "trace_util.h" @@ -84,7 +86,7 @@ typedef struct summary_stats_t { reaction_stats_t reactions[MAX_NUM_REACTIONS]; } summary_stats_t; -/** +/** * Sumary stats array. This array has the same size as the * object table. Pointer in the array will be void if there * are no stats for the object table item. @@ -135,7 +137,7 @@ size_t read_and_write_trace() { if (trigger_instance >= 0 && summary_stats[NUM_EVENT_TYPES + trigger_instance] == NULL) { summary_stats[NUM_EVENT_TYPES + trigger_instance] = (summary_stats_t*)calloc(1, sizeof(summary_stats_t)); } - + summary_stats_t* stats = NULL; interval_t exec_time; reaction_stats_t* rstats; @@ -198,7 +200,7 @@ size_t read_and_write_trace() { // commandeer the first entry in the reactions array to track values. stats = summary_stats[NUM_EVENT_TYPES + object_instance]; stats->description = reactor_name; - rstats = &stats->reactions[0]; + rstats = &stats->reactions[0]; rstats->occurrences++; // User values are stored in the "extra_delay" field, which is an interval_t. interval_t value = trace[i].extra_delay; @@ -332,7 +334,7 @@ void write_summary_file() { first = true; for (int i = NUM_EVENT_TYPES; i < table_size; i++) { summary_stats_t* stats = summary_stats[i]; - if (stats != NULL + if (stats != NULL && (stats->event_type == user_event || stats->event_type == user_value) && stats->occurrences > 0) { if (first) { diff --git a/util/tracing/trace_to_influxdb.c b/util/tracing/trace_to_influxdb.c index a99ae003ec..aa50911e5e 100644 --- a/util/tracing/trace_to_influxdb.c +++ b/util/tracing/trace_to_influxdb.c @@ -27,13 +27,13 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * @section DESCRIPTION - * + * * Standalone program to send a Lingua Franca trace file to InfluxDB. * InfluxDB is a database server to which data can be posted using HTTP * or sent as a UDP datagram. - * + * * ## Compiling this Program - * + * * To compile this program, simply do this in this source directory: * ``` * make install @@ -41,13 +41,13 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * This will place an executable program `trace_to_influxdb` in the directory `lingua-franca/bin`. * I find it convenient to have this directory in my `PATH` (this is also where the * `lfc` command-line Lingua Franca compiler is located). - * + * * ## Setting up InfluxDB - * + * * To set up InfluxDB, see: - * + * * [https://docs.influxdata.com/influxdb/v2.0/get-started/](https://docs.influxdata.com/influxdb/v2.0/get-started/) - * + * * If you have previously installed InfluxDB and you want a fresh start, do this: * ```shell * rm -rf ~/.influxdbv2/ @@ -58,13 +58,13 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * kill -9 PID * ``` * where 'PID' is replaced with whatever process ID(s) are reported by the `ps` command above. - * + * * To start an InfluxDB server on localhost with port 8087: * ```shell * influxd --http-bind-address :8087 --reporting-disabled * ``` * The 'reporting-disabled' option simply disables notifications to the InfluxDB mother ship. - * + * * You then need to set up at least one user, organization, and bucket. You can do this by pointing your browser to * ``` * http://localhost:8087 @@ -77,9 +77,9 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ``` * The UI in the browser will then give you the options Quick Start or Advanced, either of which you can select. * If you select "Data" on the left, you can browse Buckets to verify that your test bucket was created. - * + * * ## Uploading Trace Data to InfluxDB - * + * * First, generate a trace file by setting a target parameter in a Lingua Franca program: * ``` * target C { @@ -87,7 +87,7 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * }; * ``` * Then, when you run this program, a binary file with extension `.lft` will be created. - * + * * In your browser, in the InfluxDB UI, select Data on the left, then select the Tokens tab. * Select a token and copy the token string to clipboard. It will looks something like this: * ``` @@ -100,15 +100,16 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ``` * where 'Filename' and the token are replaced with your values. * This will upload the trace data to InfluxDB. - * + * * You can also specify the following command-line options: * * -h, --host: The host name running InfluxDB. If not given, this defaults to "localhost". * * -p, --port: The port for accessing InfluxDB. This defaults to 8086. If you used 8087, as shown above, then you have to give this option. - * + * * The data can then be viewed in the InfluxDB browser, or you can configure an external * tool such as Grafana to visualize it (see https://grafana.com/docs/grafana/latest/datasources/influxdb/). */ #define LF_TRACE +#include #include "reactor.h" #include "trace.h" #include "trace_util.h" @@ -208,7 +209,7 @@ int main(int argc, char* argv[]) { influx_v2_client.token = NULL; influx_v2_client.host = "localhost"; influx_v2_client.port = 8086; - influx_v2_client.org = "iCyPhy"; + influx_v2_client.org = "iCyPhy"; influx_v2_client.bucket = "test"; char* filename = NULL; @@ -277,4 +278,4 @@ int main(int argc, char* argv[]) { printf("***** %zu records written to InfluxDB.\n", num_records); // File closing is handled by termination function. } -} \ No newline at end of file +} diff --git a/util/tracing/trace_util.c b/util/tracing/trace_util.c index 0f97b3bdb9..551ec94473 100644 --- a/util/tracing/trace_util.c +++ b/util/tracing/trace_util.c @@ -30,6 +30,9 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * text file. */ #define LF_TRACE +#include +#include +#include #include "reactor.h" #include "trace.h" #include "trace_util.h" @@ -187,12 +190,12 @@ void print_table() { } else { type = "unknown type"; } - printf("pointer = %p, trigger = %p, type = %s: %s\n", + printf("pointer = %p, trigger = %p, type = %s: %s\n", object_table[i].pointer, object_table[i].trigger, type, object_table[i].description); - } + } printf("-------\n"); }