From 5d1be6e9c9d9ec037968a51d8c26df535d91a60e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 26 Sep 2021 14:56:40 +0200 Subject: [PATCH 01/68] Support list literals in LF syntax --- .../compiler/LinguaFrancaValidationTest.xtend | 24 ++++++++++++++++++- org.lflang/src/org/lflang/LinguaFranca.xtext | 7 +++++- org.lflang/src/org/lflang/Target.java | 8 +++++++ .../lflang/validation/LFValidatorImpl.xtend | 8 +++++++ 4 files changed, 45 insertions(+), 2 deletions(-) diff --git a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend index 349285fe30..7056a008e0 100644 --- a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend +++ b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend @@ -611,7 +611,29 @@ class LinguaFrancaValidationTest { } } } - + + @Test + def void forbidListLiterals() { + parseWithoutError(''' + target C; + reactor Contained { + state x: int[]([1, 2]); + } + ''').assertError(LfPackage::eINSTANCE.listLiteral, + null, 'Target C does not support LF list literals') + } + + + @Test + def void allowListLiteralsInPython() { + parseWithoutError(''' + target Python; + reactor Contained { + state x([1, 2]); + } + ''').assertNoErrors() + } + /** * Tests for state and parameter declarations, including native lists. diff --git a/org.lflang/src/org/lflang/LinguaFranca.xtext b/org.lflang/src/org/lflang/LinguaFranca.xtext index 4edfbe551c..e7f12f77d9 100644 --- a/org.lflang/src/org/lflang/LinguaFranca.xtext +++ b/org.lflang/src/org/lflang/LinguaFranca.xtext @@ -323,7 +323,7 @@ Expr : // If it is a constant, the validator should check that if the value // is non-zero, it is accompanied by a unit. Value: - (parameter=[Parameter] | time=Time | literal=Literal | code=Code); + (parameter=[Parameter] | time=Time | literal=Literal | list=ListLiteral | code=Code); Time: @@ -385,6 +385,11 @@ SignedInt: INT | NEGINT ; +// list literals support even empty lists, and a trailing comma. +ListLiteral: + '[' ( items+=Value (',' items+=Value)* )? ','? ']' +; + Literal: STRING | CHAR_LIT | SignedFloat | SignedInt | Boolean; diff --git a/org.lflang/src/org/lflang/Target.java b/org.lflang/src/org/lflang/Target.java index 51bc0a60ac..cb9254efa7 100644 --- a/org.lflang/src/org/lflang/Target.java +++ b/org.lflang/src/org/lflang/Target.java @@ -397,6 +397,14 @@ public enum Target { this(description, requiresTypes, "N/A", "N/A", keywords); } + /** + * Returns true if the target supports list literals in LF syntax. + * If not, list literals produce validator errors. + */ + public boolean supportsLfListLiterals() { + return this == Python; + } + /** * Return the target that matches the given string. diff --git a/org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend b/org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend index d631805b9f..f4c190b804 100644 --- a/org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend +++ b/org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend @@ -64,6 +64,7 @@ import org.lflang.lf.Instantiation import org.lflang.lf.KeyValuePair import org.lflang.lf.KeyValuePairs import org.lflang.lf.LfPackage.Literals +import org.lflang.lf.ListLiteral import org.lflang.lf.Model import org.lflang.lf.NamedHost import org.lflang.lf.Output @@ -371,6 +372,13 @@ class LFValidatorImpl extends AbstractLFValidator { } } + @Check(FAST) + def checkListLiteral(ListLiteral list) { + if (!target.supportsLfListLiterals()) { + error("Target " + target + " does not support LF list literals", Literals.LIST_LITERAL__ITEMS) + } + } + @Check(FAST) def checkConnection(Connection connection) { From 328285a2f08f8582a9acffd4a3ef64e5c534c8f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 26 Sep 2021 15:10:37 +0200 Subject: [PATCH 02/68] turn [] token into two separate tokens --- org.lflang/src/org/lflang/LinguaFranca.xtext | 6 +++--- test/Python/src/ListLiterals.lf | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 test/Python/src/ListLiterals.lf diff --git a/org.lflang/src/org/lflang/LinguaFranca.xtext b/org.lflang/src/org/lflang/LinguaFranca.xtext index e7f12f77d9..3af2661cc7 100644 --- a/org.lflang/src/org/lflang/LinguaFranca.xtext +++ b/org.lflang/src/org/lflang/LinguaFranca.xtext @@ -341,10 +341,10 @@ Type: ; ArraySpec: - ofVariableLength?='[]' | '[' length=INT ']'; + '[' ( ofVariableLength?=']' | length=INT ']' ); WidthSpec: - ofVariableLength?='[]' | '[' (terms+=WidthTerm) ('+' terms+=WidthTerm)* ']'; + '[' ( ofVariableLength?=']' | (terms+=WidthTerm) ('+' terms+=WidthTerm)* ']' ); WidthTerm: width=INT @@ -527,7 +527,7 @@ Token: // Braces '(' | ')' | '{' | '}' | // Brackets - '[' | ']' | '<' | '>' | '[]' | + '[' | ']' | '<' | '>' | // Punctuation ':' | ';' | ',' | '.' | '::' | // Slashes diff --git a/test/Python/src/ListLiterals.lf b/test/Python/src/ListLiterals.lf new file mode 100644 index 0000000000..3760f6a86e --- /dev/null +++ b/test/Python/src/ListLiterals.lf @@ -0,0 +1,16 @@ +// This example illustrates state variables and parameters on the wiki. +// For this test, success is just compiling and running. +target Python; + +main reactor { + state l12([1,2]); + state empty([]); + state empty2([,]); + + reaction(startup) {= + assert self.empty == [] + assert self.empty2 == [] + assert self.l12 == [1, 2] + print("Success") + =} +} From 419cc0de2c516ad9d41c249fe661131f2ccbb29c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 26 Sep 2021 15:26:56 +0200 Subject: [PATCH 03/68] Update python generator --- org.lflang/src/org/lflang/LinguaFranca.xtext | 9 +- .../lflang/generator/PythonGenerator.xtend | 133 ++++-------------- test/Python/src/MovingAverage.lf | 4 +- test/Python/src/NativeListsAndTimes.lf | 8 +- 4 files changed, 40 insertions(+), 114 deletions(-) diff --git a/org.lflang/src/org/lflang/LinguaFranca.xtext b/org.lflang/src/org/lflang/LinguaFranca.xtext index 3af2661cc7..93e9d95083 100644 --- a/org.lflang/src/org/lflang/LinguaFranca.xtext +++ b/org.lflang/src/org/lflang/LinguaFranca.xtext @@ -385,9 +385,14 @@ SignedInt: INT | NEGINT ; -// list literals support even empty lists, and a trailing comma. +// List literals support even empty lists, and a trailing comma. + +// Note: this expansion is weirdly written, but without the +// emptyList property, Xtext fails to push a ListLiteral node +// when the list is empty... Another bug of Xtext. ListLiteral: - '[' ( items+=Value (',' items+=Value)* )? ','? ']' + '[' (items+=Value (',' items+=Value)* ','? ']' + | ','? emptyList?=']') ; Literal: diff --git a/org.lflang/src/org/lflang/generator/PythonGenerator.xtend b/org.lflang/src/org/lflang/generator/PythonGenerator.xtend index 0086433344..9213c191ce 100644 --- a/org.lflang/src/org/lflang/generator/PythonGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/PythonGenerator.xtend @@ -29,7 +29,6 @@ package org.lflang.generator import java.io.File import java.util.ArrayList import java.util.LinkedHashSet -import java.util.LinkedList import java.util.List import java.util.regex.Pattern import org.eclipse.emf.ecore.resource.Resource @@ -47,12 +46,10 @@ import org.lflang.lf.Input import org.lflang.lf.Instantiation import org.lflang.lf.Model import org.lflang.lf.Output -import org.lflang.lf.Parameter import org.lflang.lf.Port import org.lflang.lf.Reaction import org.lflang.lf.Reactor import org.lflang.lf.ReactorDecl -import org.lflang.lf.StateVar import org.lflang.lf.TriggerRef import org.lflang.lf.Value import org.lflang.lf.VarRef @@ -181,99 +178,35 @@ class PythonGenerator extends CGenerator { * @param v A value * @return A value string in the target language */ - private def getPythonTargetValue(Value v) { - var String returnValue = ""; - switch(v.toText) { - case "false": returnValue = "False" - case "true": returnValue = "True" - default: returnValue = super.getTargetValue(v) - } - + protected def String getPythonTargetValue(Value v) { // Parameters in Python are always prepended with a 'self.' // predicate. Therefore, we need to append the returned value // if it is a parameter. if (v.parameter !== null) { - returnValue = "self." + returnValue; - } - - return returnValue; - } - - /** - * Create a list of state initializers in target code. - * - * @param state The state variable to create initializers for - * @return A list of initializers in target code - */ - protected def List getPythonInitializerList(StateVar state) { - if (!state.isInitialized) { - return null + return "self." + super.getTargetValue(v); + } else if (v.list !== null) { + return "[" + v.list.items.join(', ', [it.pythonTargetValue]) + "]" } - var list = new LinkedList(); - - for (i : state?.init) { - if (i.parameter !== null) { - list.add(i.parameter.targetReference) - } else if (state.isOfTimeType) { - list.add(i.targetTime) - } else { - list.add(i.pythonTargetValue) - } + switch(v.toText) { + case "false": return "False" + case "true": return "True" + default: return super.getTargetValue(v) } - return list - } - - /** - * Create a Python tuple for parameter initialization in target code. - * - * @param p The parameter instance to create initializers for - * @return Initialization code - */ - protected def String getPythonInitializer(StateVar state) throws Exception { - if (state.init.size > 1) { - // state variables are initialized as mutable lists - return state.init.join('[', ', ', ']', [it.pythonTargetValue]) - } else if (state.isInitialized) { - return state.init.get(0).getPythonTargetValue - } else { - return "None" - } - - } - - /** - * Create a Python list for parameter initialization in target code. - * - * @param p The parameter instance to create initializers for - * @return Initialization code - */ - protected def String getPythonInitializer(ParameterInstance p) { - if (p.init.size > 1) { - // parameters are initialized as immutable tuples - return p.init.join('(', ', ', ')', [it.pythonTargetValue]) - } else { - return p.init.get(0).getPythonTargetValue - } - } - - /** - * Create a Python list for parameter initialization in target code. - * - * @param p The parameter to create initializers for - * @return Initialization code - */ - protected def String getPythonInitializer(Parameter p) { - if (p.init.size > 1) { - // parameters are initialized as immutable tuples - return p.init.join('(', ', ', ')', [it.pythonTargetValue]) - } else { - return p.init.get(0).pythonTargetValue - } - + + /** Returns the python initializer corresponding to the given LF init list. */ + def String getPythonInitializer(List init) { + if (init === null) { + return "None" + } if (init.size > 1) { + // corresponds to a tuple + return init.join('(', ', ', ')', [it.pythonTargetValue]) + } else { + return init.get(0).getPythonTargetValue + } } - + /** * Generate parameters and their respective initialization code for a reaction function * The initialization code is put at the beginning of the reaction before user code @@ -388,20 +321,6 @@ class PythonGenerator extends CGenerator { } - /** - * Handle initialization for state variable - * @param state a state variable - */ - def String getTargetInitializer(StateVar state) { - if(!state.isInitialized) - { - return '''None''' - } - - '''«FOR init : state.pythonInitializerList SEPARATOR ", "»«init»«ENDFOR»''' - } - - /** * Wrapper function for the more elaborate generatePythonReactorClass that keeps track * of visited reactors to avoid duplicate generation @@ -521,11 +440,11 @@ class PythonGenerator extends CGenerator { if (!param.inferredType.targetType.equals("PyObject*")) { // If type is given, use it temporary_code. - append(''' self._«param.name»:«param.inferredType.pythonType» = «param.pythonInitializer» + append(''' self._«param.name»:«param.inferredType.pythonType» = «param.init.pythonInitializer» ''') } else { // If type is not given, just pass along the initialization - temporary_code.append(''' self._«param.name» = «param.pythonInitializer» + temporary_code.append(''' self._«param.name» = «param.init.pythonInitializer» ''') } @@ -546,11 +465,11 @@ class PythonGenerator extends CGenerator { if (!stateVar.inferredType.targetType.equals("PyObject*")) { // If type is given, use it temporary_code. - append(''' self.«stateVar.name»:«stateVar.inferredType.pythonType» = «stateVar.pythonInitializer» + append(''' self.«stateVar.name»:«stateVar.inferredType.pythonType» = «stateVar.init.pythonInitializer» ''') } else if (stateVar.isInitialized) { // If type is not given, pass along the initialization directly if it is present - temporary_code.append(''' self.«stateVar.name» = «stateVar.pythonInitializer» + temporary_code.append(''' self.«stateVar.name» = «stateVar.init.pythonInitializer» ''') } else { // If neither the type nor the initialization is given, use None @@ -664,14 +583,14 @@ class PythonGenerator extends CGenerator { «instance.uniqueID»_lf = \ [«FOR member : instance.bankMembers SEPARATOR ", \\\n"»\ _«className»(bank_index = «member.bankIndex/* bank_index is specially assigned by us*/»,\ - «FOR param : member.parameters SEPARATOR ", "»_«param.name»=«param.pythonInitializer»«ENDFOR»)«ENDFOR»] + «FOR param : member.parameters SEPARATOR ", "»_«param.name»=«param.init.pythonInitializer»«ENDFOR»)«ENDFOR»] ''') return } else if (instance.bankIndex === -1 && !instance.definition.reactorClass.toDefinition.allReactions.isEmpty) { pythonClassesInstantiation.append(''' «instance.uniqueID»_lf = \ [_«className»(bank_index = 0«/* bank_index is specially assigned by us*/», \ - «FOR param : instance.parameters SEPARATOR ", \\\n"»_«param.name»=«param.pythonInitializer»«ENDFOR»)] + «FOR param : instance.parameters SEPARATOR ", \\\n"»_«param.name»=«param.init.pythonInitializer»«ENDFOR»)] ''') } diff --git a/test/Python/src/MovingAverage.lf b/test/Python/src/MovingAverage.lf index 38d8e3ef44..96220d544f 100644 --- a/test/Python/src/MovingAverage.lf +++ b/test/Python/src/MovingAverage.lf @@ -18,7 +18,7 @@ reactor MASource { =} } reactor MovingAverageImpl { - state delay_line(0.0, 0.0, 0.0); + state delay_line([0.0, 0.0, 0.0]); state index(0); input m_in; output out; @@ -46,4 +46,4 @@ main reactor MovingAverage { p = new TestDouble(expected=(0.0, 0.25, 0.75, 1.5, 2.5, 3.5)); s.out -> m.m_in; m.out -> p.t_in; -} \ No newline at end of file +} diff --git a/test/Python/src/NativeListsAndTimes.lf b/test/Python/src/NativeListsAndTimes.lf index 8f88e07ac7..b1ad63d42b 100644 --- a/test/Python/src/NativeListsAndTimes.lf +++ b/test/Python/src/NativeListsAndTimes.lf @@ -19,9 +19,11 @@ main reactor(x(0), timer toe(z); // Implicit type time state baz(p); // Implicit type int[] state period(z); // Implicit type time - state bar(1 msec, 2 msec, 3 msec); // list of time values - state notype(1, 2, 3, 4); + state bar(1 msec, 2 msec, 3 msec); // tuple of time values + state bar([1 msec, 2 msec, 3 msec]); // list of time values + state notype(1, 2, 3, 4); // tuple + state notype([1, 2, 3, 4]); // list reaction(tick) {= # Target code =} -} \ No newline at end of file +} From 32e33853d2b62049f6414ab32a3ab3d22fe286e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 26 Sep 2021 16:34:45 +0200 Subject: [PATCH 04/68] Fix python test --- test/Python/src/ListLiterals.lf | 9 ++++++++ test/Python/src/NativeListsAndTimes.lf | 29 +++++++++++++++++++------- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/test/Python/src/ListLiterals.lf b/test/Python/src/ListLiterals.lf index 3760f6a86e..88a126949c 100644 --- a/test/Python/src/ListLiterals.lf +++ b/test/Python/src/ListLiterals.lf @@ -2,11 +2,20 @@ // For this test, success is just compiling and running. target Python; + +reactor Sub(seq([1,2])) { + +} + main reactor { state l12([1,2]); state empty([]); state empty2([,]); + sub = new Sub(seq = [1, 2]); + sub2 = new Sub(seq = (1, 2)); + sub3 = new Sub(seq = ([1, 2])); + reaction(startup) {= assert self.empty == [] assert self.empty2 == [] diff --git a/test/Python/src/NativeListsAndTimes.lf b/test/Python/src/NativeListsAndTimes.lf index b1ad63d42b..19c9b3d353 100644 --- a/test/Python/src/NativeListsAndTimes.lf +++ b/test/Python/src/NativeListsAndTimes.lf @@ -5,10 +5,11 @@ main reactor(x(0), y(0), // Units are missing but not required z(1 msec), // Type is missing but not required - p(1, 2, 3, 4), // List of integers - q(1 msec, 2 msec, 3 msec), // list of time values - r({=0=}), // Zero-valued target code also is a valid time - g(1 msec, 2 msec) // List of time values + p(1, 2, 3, 4), // tuple of integers + pList([1, 2, 3, 4]), // List of integers + q(1 msec, 2 msec, 3 msec), // tuple of time values + qList([1 msec, 2 msec, 3 msec]), // list of time values + r({=0=}) // Zero-valued target code also is a valid time ) { state s(y); // Reference to explicitly typed time parameter state t(z); // Reference to implicitly typed time parameter @@ -18,12 +19,24 @@ main reactor(x(0), timer tock(1 sec); // Implicit type time timer toe(z); // Implicit type time state baz(p); // Implicit type int[] + state bazList(pList); state period(z); // Implicit type time state bar(1 msec, 2 msec, 3 msec); // tuple of time values - state bar([1 msec, 2 msec, 3 msec]); // list of time values + state barList([1 msec, 2 msec, 3 msec]); // list of time values state notype(1, 2, 3, 4); // tuple - state notype([1, 2, 3, 4]); // list - reaction(tick) {= - # Target code + state notypeList([1, 2, 3, 4]); // list + + reaction(startup) {= + assert self.notype == (1,2,3,4) + assert self.p == (1,2,3,4) + assert self.pList == [1,2,3,4] + assert self.pList != (1,2,3,4) + assert self.notypeList != (1,2,3,4) + assert self.notypeList == [1,2,3,4] + assert self.baz == (1,2,3,4) + assert self.bazList == [1,2,3,4] + + assert self.bar == (MSEC(1),MSEC(2),MSEC(3)) + assert self.barList == [MSEC(1),MSEC(2),MSEC(3)] =} } From 0fa8c209d0032d4b1c9766335820af9a31d44e49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 4 Oct 2021 17:27:04 +0200 Subject: [PATCH 05/68] Update some test code --- test/Python/src/ArrayAsParameter.lf | 6 +++--- test/Python/src/{ArrayAsType.lf => TuplePrint.lf} | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) rename test/Python/src/{ArrayAsType.lf => TuplePrint.lf} (82%) diff --git a/test/Python/src/ArrayAsParameter.lf b/test/Python/src/ArrayAsParameter.lf index 8c08da4ea1..d23116ede3 100644 --- a/test/Python/src/ArrayAsParameter.lf +++ b/test/Python/src/ArrayAsParameter.lf @@ -1,7 +1,7 @@ // Source has an array as a parameter, the elements of which it passes // to Print. target Python; -reactor Source(sequence(0, 1, 2)) { +reactor Source(sequence([0, 1, 2])) { output out; state count(0); logical action next; @@ -31,7 +31,7 @@ reactor Print { =} } main reactor ArrayAsParameter { - s = new Source(sequence = (1, 2, 3, 4)); + s = new Source(sequence = [1, 2, 3, 4]); p = new Print(); s.out -> p._in; -} \ No newline at end of file +} diff --git a/test/Python/src/ArrayAsType.lf b/test/Python/src/TuplePrint.lf similarity index 82% rename from test/Python/src/ArrayAsType.lf rename to test/Python/src/TuplePrint.lf index ea2cc5c2c2..e4a7cb6412 100644 --- a/test/Python/src/ArrayAsType.lf +++ b/test/Python/src/TuplePrint.lf @@ -1,5 +1,5 @@ -// Source produces a statically allocated array, which it passes -// to Print. The destination references the array directly. +// Source produces a tuple, which it passes to Print. +// The destination references the array directly. target Python; reactor Source { output out; From 7784b3afd20ee371c7902101f79838ba8a4d66c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 4 Oct 2021 20:25:27 +0200 Subject: [PATCH 06/68] Rename TuplePrint -> ArrayAsType --- test/Python/src/{TuplePrint.lf => ArrayAsType.lf} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/Python/src/{TuplePrint.lf => ArrayAsType.lf} (100%) diff --git a/test/Python/src/TuplePrint.lf b/test/Python/src/ArrayAsType.lf similarity index 100% rename from test/Python/src/TuplePrint.lf rename to test/Python/src/ArrayAsType.lf From f08bd72e998d55394ce0512d10cce3fb9e7db977 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 4 Oct 2021 20:33:56 +0200 Subject: [PATCH 07/68] Forbid writing [,] as list literal --- org.lflang.tests/src/org/lflang/tests/LFParsingTest.java | 9 ++++++++- org.lflang/src/org/lflang/LinguaFranca.xtext | 6 +----- test/Python/src/ListLiterals.lf | 5 +++-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/org.lflang.tests/src/org/lflang/tests/LFParsingTest.java b/org.lflang.tests/src/org/lflang/tests/LFParsingTest.java index 487c3b21e5..a58dec3e12 100644 --- a/org.lflang.tests/src/org/lflang/tests/LFParsingTest.java +++ b/org.lflang.tests/src/org/lflang/tests/LFParsingTest.java @@ -27,7 +27,7 @@ public class LFParsingTest { @Test - public void testLexingEmptyTargetProperties() throws Exception { + public void testParsingEmptyTargetProperties() throws Exception { assertNoParsingErrorsIn("target C { }; \nreactor Foo {}"); assertNoParsingErrorsIn("target C {a:b,}; \nreactor Foo {}"); expectParsingErrorIn("target C {,}; \nreactor Foo {}"); @@ -38,6 +38,13 @@ public void testLexingEmptyTargetProperties() throws Exception { // assertNoParsingErrorsIn("target C {x:[,]}; \nreactor Foo {}"); } + @Test + public void testParsingListLiterals() throws Exception { + assertNoParsingErrorsIn("target Python; \nreactor Foo(p([1,])) {}"); + // [,] is not an ok list literal + expectParsingErrorIn("target Python; \nreactor Foo(p([,])) {}"); + } + @Test public void testLexingLifetimeAnnots() throws Exception { assertNoParsingErrorsIn(makeLfTargetCode("Rust", diff --git a/org.lflang/src/org/lflang/LinguaFranca.xtext b/org.lflang/src/org/lflang/LinguaFranca.xtext index c0c718ff39..cc293ec564 100644 --- a/org.lflang/src/org/lflang/LinguaFranca.xtext +++ b/org.lflang/src/org/lflang/LinguaFranca.xtext @@ -387,12 +387,8 @@ SignedInt: // List literals support even empty lists, and a trailing comma. -// Note: this expansion is weirdly written, but without the -// emptyList property, Xtext fails to push a ListLiteral node -// when the list is empty... Another bug of Xtext. ListLiteral: - '[' (items+=Value (',' items+=Value)* ','? ']' - | ','? emptyList?=']') + {ListLiteral} '[' ( items+=Value (',' items+=Value)* ','? )? ']' ; Literal: diff --git a/test/Python/src/ListLiterals.lf b/test/Python/src/ListLiterals.lf index 88a126949c..9af1583a3a 100644 --- a/test/Python/src/ListLiterals.lf +++ b/test/Python/src/ListLiterals.lf @@ -9,8 +9,9 @@ reactor Sub(seq([1,2])) { main reactor { state l12([1,2]); + state l12_trailing([1,2,]); state empty([]); - state empty2([,]); + // doesn't parse state empty2([,]); sub = new Sub(seq = [1, 2]); sub2 = new Sub(seq = (1, 2)); @@ -18,8 +19,8 @@ main reactor { reaction(startup) {= assert self.empty == [] - assert self.empty2 == [] assert self.l12 == [1, 2] + assert self.l12 == self.l12_trailing print("Success") =} } From adc20a5e84b511426eee02e7a0f95585104f4bb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 14 Oct 2021 14:45:51 +0200 Subject: [PATCH 08/68] Refactor to use a new Initializer production --- org.lflang/src/org/lflang/ASTUtils.xtend | 111 ++++++++++-------- org.lflang/src/org/lflang/AstExtensions.kt | 9 +- org.lflang/src/org/lflang/LinguaFranca.xtext | 31 ++--- org.lflang/src/org/lflang/ModelInfo.java | 6 +- org.lflang/src/org/lflang/TimeValue.java | 5 + .../lflang/federated/CGeneratorExtension.java | 39 ------ .../src/org/lflang/federated/FedASTUtils.java | 66 +++++++---- .../org/lflang/generator/ActionInstance.xtend | 41 ++----- .../lflang/generator/DeadlineInstance.xtend | 23 +--- .../org/lflang/generator/GeneratorBase.xtend | 88 ++++---------- .../lflang/generator/ParameterInstance.xtend | 28 ++--- .../lflang/generator/PythonGenerator.xtend | 22 +++- .../lflang/generator/ReactorInstance.xtend | 17 ++- .../org/lflang/generator/TimerInstance.xtend | 29 +---- .../org/lflang/generator/c/CGenerator.xtend | 44 +++---- .../generator/cpp/CppInstanceGenerator.kt | 25 ++-- .../generator/cpp/CppParameterGenerator.kt | 6 +- .../lflang/generator/cpp/CppStateGenerator.kt | 9 +- .../org/lflang/generator/ts/TSGenerator.kt | 14 +-- .../lflang/generator/ts/TSStateGenerator.kt | 4 +- .../lflang/validation/LFValidatorImpl.xtend | 36 +++--- 21 files changed, 266 insertions(+), 387 deletions(-) diff --git a/org.lflang/src/org/lflang/ASTUtils.xtend b/org.lflang/src/org/lflang/ASTUtils.xtend index e28f4b8341..af470e58f7 100644 --- a/org.lflang/src/org/lflang/ASTUtils.xtend +++ b/org.lflang/src/org/lflang/ASTUtils.xtend @@ -31,7 +31,6 @@ import java.util.LinkedHashMap import java.util.LinkedHashSet import java.util.LinkedList import java.util.List -import org.eclipse.emf.common.util.EList import org.eclipse.emf.ecore.EObject import org.eclipse.emf.ecore.resource.Resource import org.eclipse.xtext.TerminalRule @@ -51,6 +50,7 @@ import org.lflang.lf.Delay import org.lflang.lf.Element import org.lflang.lf.ImportedReactor import org.lflang.lf.Input +import org.lflang.lf.Initializer import org.lflang.lf.Instantiation import org.lflang.lf.LfFactory import org.lflang.lf.Model @@ -280,11 +280,12 @@ class ASTUtils { if (delay.parameter !== null) { value.parameter = delay.parameter } else { - value.time = factory.createTime - value.time.interval = delay.interval - value.time.unit = delay.unit + value.time = delay.time } - assignment.rhs.add(value) + val rhs = factory.createInitializer + rhs.exprs.add(value) + rhs.assign = true + assignment.rhs = rhs delayInstance.parameters.add(assignment) delayInstance.name = "delay" // This has to be overridden. @@ -335,9 +336,12 @@ class ASTUtils { val defaultTime = factory.createTime defaultTime.unit = TimeUnit.NONE defaultTime.interval = 0 + + val init = factory.createInitializer val defaultValue = factory.createValue defaultValue.time = defaultTime - delayParameter.init.add(defaultValue) + delayParameter.init = init + init.exprs.add(defaultValue) // Name the newly created action; set its delay and type. action.name = "act" @@ -729,6 +733,11 @@ class ASTUtils { def static toTimeValue(Element e) { return new TimeValue(e.time, e.unit) } + + /** Returns the time value represented by the given AST node. */ + def static TimeValue toTimeValue(Time e) { + return new TimeValue(e.interval, e.unit) + } /** * Return a boolean based on the given element. @@ -775,7 +784,7 @@ class ASTUtils { if (d.parameter !== null) { return d.parameter.name } - '''«d.interval» «d.unit.toString»''' + d.time.toText } /** @@ -997,7 +1006,7 @@ class ASTUtils { if (p !== null) { if (p.type !== null && p.type.isTime && p.type.arraySpec !== null) { return true - } else if (p.init !== null && p.init.size > 1 && p.init.forall [ + } else if (p.init !== null && p.init.exprs.size > 1 && p.init.exprs.forall [ it.isValidTime ]) { return true @@ -1021,12 +1030,7 @@ class ASTUtils { return true } // Or it has to be initialized as a proper time with units. - if (p.init !== null && p.init.size == 1) { - val time = p.init.get(0).time - if (time !== null && time.unit != TimeUnit.NONE) { - return true - } - } + return p.init?.asSingleValue?.time?.unit !== TimeUnit.NONE // In other words, one can write: // - `x:time(0)` -OR- // - `x:(0 msec)`, `x:(0 sec)`, etc. @@ -1045,11 +1049,10 @@ class ASTUtils { if (s.type !== null) return s.type.isTime // Or the it has to be initialized as a time except zero. - if (s.init !== null && s.init.size == 1) { - val init = s.init.get(0) - if (init.isValidTime && !init.isZero) - return true - } + val init = s.init.asSingleValue + if (init !== null && init.isValidTime && !init.isZero) + return true + // In other words, one can write: // - `x:time(0)` -OR- // - `x:(0 msec)`, `x:(0 sec)`, etc. -OR- @@ -1057,6 +1060,25 @@ class ASTUtils { } return false } + + /** + * If the initializer contains exactly one expression, + * return it. Otherwise return null. + */ + def static Value asSingleValue(Initializer init) { + if (init.exprs.size == 1) + return init.exprs.get(0) + return null + } + + /** + * If the initializer contains exactly one expression, + * return it. Otherwise return null. + */ + def static boolean isList(Initializer init) { + return (init.isBraces || init.isParens) && init.exprs.size != 1 + // || init.isAssign && init.asSingleValue instanceof ListLiteral + } /** * Assuming that the given parameter is of time type, return @@ -1066,10 +1088,10 @@ class ASTUtils { */ def static TimeValue getInitialTimeValue(Parameter p) { if (p !== null && p.isOfTimeType) { - val init = p.init.get(0) - if (init.time !== null) { + val init = p.init.asSingleValue + if (init?.time !== null) { return new TimeValue(init.time.interval, init.time.unit) - } else if (init.parameter !== null) { + } else if (init?.parameter !== null) { // Parameter value refers to another parameter. return getInitialTimeValue(init.parameter) } else { @@ -1194,7 +1216,7 @@ class ASTUtils { if (lastAssignment !== null) { // Right hand side can be a list. Collect the entries. val result = new LinkedList() - for (value: lastAssignment.rhs) { + for (value: lastAssignment.rhs.exprs) { if (value.parameter !== null) { if (instantiations.size() > 1 && instantiation.eContainer !== instantiations.get(1).reactorClass @@ -1218,7 +1240,7 @@ class ASTUtils { // If we reach here, then either no instantiation was supplied or // there was no assignment in the instantiation. So just use the // parameter's initial value. - return parameter.init; + return parameter.init.exprs; } /** @@ -1477,24 +1499,18 @@ class ASTUtils { * @return True if the variable was initialized, false otherwise. */ def static boolean isInitialized(StateVar v) { - if (v !== null && (v.parens.size == 2 || v.braces.size == 2)) { - return true - } - return false + return v !== null && v.init !== null } /** - * Report whether the given time state variable is initialized using a - * parameter or not. + * Return whether the given time state variable is initialized using a + * single parameter or not. * @param s A state variable. * @return True if the argument is initialized using a parameter, false * otherwise. */ def static boolean isParameterized(StateVar s) { - if (s.init !== null && s.init.exists[it.parameter !== null]) { - return true - } - return false + return s?.init?.asSingleValue?.parameter !== null } /** @@ -1505,32 +1521,23 @@ class ASTUtils { * state variable. * @return The inferred type, or "undefined" if none could be inferred. */ - protected static def InferredType getInferredType(EList initList) { - if (initList.size == 1) { + static def InferredType getInferredType(Initializer init) { + val single = init.asSingleValue + if (single !== null) { // If there is a single element in the list, and it is a proper // time value with units, we infer the type "time". - val init = initList.get(0) - if (init.parameter !== null) { - return init.parameter.getInferredType - } else if (init.isValidTime && !init.isZero) { + if (single.parameter !== null) { + return single.parameter.getInferredType + } else if (single.isValidTime && !single.isZero) { return InferredType.time; } - } else if (initList.size > 1) { + } else if (init.exprs.size > 1) { // If there are multiple elements in the list, and there is at // least one proper time value with units, and all other elements // are valid times (including zero without units), we infer the // type "time list". - var allValidTime = true - var foundNonZero = false - - for (init : initList) { - if (!init.isValidTime) { - allValidTime = false; - } - if (!init.isZero) { - foundNonZero = true - } - } + var allValidTime = init.exprs.forall[ it.isValidTime ] + var foundNonZero = init.exprs.exists[ !it.isZero ] if (allValidTime && foundNonZero) { // Conservatively, no bounds are inferred; the returned type diff --git a/org.lflang/src/org/lflang/AstExtensions.kt b/org.lflang/src/org/lflang/AstExtensions.kt index f7e264d393..6a9aab0218 100644 --- a/org.lflang/src/org/lflang/AstExtensions.kt +++ b/org.lflang/src/org/lflang/AstExtensions.kt @@ -153,8 +153,7 @@ fun Element.toText(): String = literal?.withoutQuotes()?.trim() ?: id ?: "" -fun Delay.toText(): String = - parameter?.name ?: "$interval $unit" +fun Delay.toText(): String = parameter?.name ?: time.toText() /** @@ -296,14 +295,14 @@ fun List.tail() = subList(1, size) fun List.headAndTail() = Pair(first(), tail()) /** - * Given an initialization list, return an inferred type. Only two types + * Given an initializer, return an inferred type. Only two types * can be inferred: "time" and "timeList". Return the "undefined" type if * neither can be inferred. * * @see ASTUtils.getInferredType * @return The inferred type, or "undefined" if none could be inferred. */ -val EList.inferredType: InferredType get() = ASTUtils.getInferredType(this) +val Initializer.inferredType: InferredType get() = ASTUtils.getInferredType(this) /** * Given a parameter, return an inferred type. Only two types can be @@ -349,7 +348,7 @@ val Port.inferredType: InferredType get() = ASTUtils.getInferredType(this) * @receiver The state variable to be checked. * @return True if the variable was initialized, false otherwise. */ -val StateVar.isInitialized: Boolean get() = (this.parens.size == 2 || this.braces.size == 2) +val StateVar.isInitialized: Boolean get() = init != null /** * Given the width specification of port or instantiation diff --git a/org.lflang/src/org/lflang/LinguaFranca.xtext b/org.lflang/src/org/lflang/LinguaFranca.xtext index cc293ec564..85bd614b48 100644 --- a/org.lflang/src/org/lflang/LinguaFranca.xtext +++ b/org.lflang/src/org/lflang/LinguaFranca.xtext @@ -139,12 +139,13 @@ TargetDecl: * must be given, or a literal or code that denotes zero. */ StateVar: - 'state' name=ID ( - (':' (type=Type))? - ((parens+='(' (init+=Value (',' init+=Value)*)? parens+=')') - | (braces+='{' (init+=Value (',' init+=Value)*)? braces+='}') - )? - ) ';'? + 'state' name=ID (':' type=Type)? init=Initializer? ';'? +; + +Initializer: + parens?='(' (exprs+=Value (',' exprs+=Value)* (trailingComma?=',')?)? ')' + | braces?='{' (exprs+=Value (',' exprs+=Value)* (trailingComma?=',')?)? '}' + | assign?='=' exprs+=Value ; Method: @@ -239,8 +240,8 @@ Connection: // a literal value. If no units are given (which implies that the time interval // must be zero), the clause must be ended with a semicolon. If units are given, // the semicolon is optional. -Delay: - 'after' (parameter=[Parameter] | (interval=INT ((unit=TimeUnit)))) +Delay: // todo replace this with Value, add validator check to constrain expression to be a Time + 'after' (parameter=[Parameter] | time=Time) ; // Chooses the serializer to use for the connection @@ -285,23 +286,13 @@ VarRef: ; Assignment: - (lhs=[Parameter] ( - (equals='=' rhs+=Value) - | ((equals='=')? ( - parens+='(' (rhs+=Value (',' rhs+=Value)*)? parens+=')' - | braces+='{' (rhs+=Value (',' rhs+=Value)*)? braces+='}')) - )); + lhs=[Parameter] rhs=Initializer; /** * Parameter declaration with optional type and mandatory initialization. */ Parameter: - name=ID (':' (type=Type))? - // FIXME: rename Value to Expr - ((parens+='(' (init+=Value (',' init+=Value)*)? parens+=')') - | (braces+='{' (init+=Value (',' init+=Value)*)? braces+='}') - )? -; + name=ID (':' type=Type)? init=Initializer?; Sum : terms+=Difference ('+' terms+=Difference)*; diff --git a/org.lflang/src/org/lflang/ModelInfo.java b/org.lflang/src/org/lflang/ModelInfo.java index bcded78e8f..5e783fbe9a 100644 --- a/org.lflang/src/org/lflang/ModelInfo.java +++ b/org.lflang/src/org/lflang/ModelInfo.java @@ -38,6 +38,7 @@ import org.lflang.graph.TopologyGraph; import org.lflang.lf.Assignment; import org.lflang.lf.Deadline; +import org.lflang.lf.Initializer; import org.lflang.lf.Instantiation; import org.lflang.lf.Model; import org.lflang.lf.Parameter; @@ -192,14 +193,15 @@ private boolean detectOverflow(Set visited, Parameter current) { // Find assignments that override the current parameter. for (var assignment : instantiation.getParameters()) { if (assignment.getLhs().equals(current)) { - Parameter parameter = assignment.getRhs().get(0).getParameter(); + Initializer init = assignment.getRhs(); + Parameter parameter = init.getExprs().get(0).getParameter(); if (parameter != null) { // Check for overflow in the referenced parameter. overflow = detectOverflow(visited, parameter) || overflow; } else { // The right-hand side of the assignment is a // constant; check whether it is too large. - if (isTooLarge(ASTUtils.getTimeValue(assignment.getRhs().get(0)))) { + if (isTooLarge(ASTUtils.getTimeValue(init.getExprs().get(0)))) { this.overflowingAssignments.add(assignment); overflow = true; } diff --git a/org.lflang/src/org/lflang/TimeValue.java b/org.lflang/src/org/lflang/TimeValue.java index 0bfa237843..ba9544ed11 100644 --- a/org.lflang/src/org/lflang/TimeValue.java +++ b/org.lflang/src/org/lflang/TimeValue.java @@ -39,6 +39,11 @@ public final class TimeValue implements Comparable { */ public static final TimeValue MAX_VALUE = new TimeValue(Long.MAX_VALUE, TimeUnit.NSECS); + /** + * A time value equal to zero. + */ + public static final TimeValue ZERO = new TimeValue(0, TimeUnit.NONE); + /** * Primitive numerical representation of this time value, * to be interpreted in terms the associated time unit. diff --git a/org.lflang/src/org/lflang/federated/CGeneratorExtension.java b/org.lflang/src/org/lflang/federated/CGeneratorExtension.java index 82604068fa..59a806b265 100644 --- a/org.lflang/src/org/lflang/federated/CGeneratorExtension.java +++ b/org.lflang/src/org/lflang/federated/CGeneratorExtension.java @@ -207,44 +207,5 @@ public static String createPortStatusFieldForInput(Input input, } return builder.toString(); } - - /** - * Given a connection 'delay' predicate, return a string that represents the - * interval_t value of the additional delay that needs to be applied to the - * outgoing message. - * - * The returned additional delay in absence of after on network connection - * (i.e., if delay is passed as a null) is NEVER. This has a special - * meaning in C library functions that send network messages that carry - * timestamps (@see send_timed_message and send_port_absent_to_federate - * in lib/core/federate.c). In this case, the sender will send its current - * tag as the timestamp of the outgoing message without adding a microstep delay. - * If the user has assigned an after delay to the network connection (that - * can be zero) either as a time value (e.g., 200 msec) or as a literal - * (e.g., a parameter), that delay in nsec will be returned. - * - * @param delay - * @param generator - * @return - */ - public static String getNetworkDelayLiteral(Delay delay, CGenerator generator) { - String additionalDelayString = "NEVER"; - if (delay != null) { - if (delay.getParameter() != null) { - // The parameter has to be parameter of the main reactor. - // And that value has to be a Time. - Value value = delay.getParameter().getInit().get(0); - if (value.getTime() != null) { - additionalDelayString = Long.toString(ASTUtils.getTimeValue(value).toNanoSeconds()); - } else if (value.getLiteral() != null) { - // If no units are given, e.g. "0", then use the literal. - additionalDelayString = value.getLiteral(); - } - } else { - additionalDelayString = Long.toString(new TimeValue(delay.getInterval(), delay.getUnit()).toNanoSeconds()); - } - } - return additionalDelayString; - } } diff --git a/org.lflang/src/org/lflang/federated/FedASTUtils.java b/org.lflang/src/org/lflang/federated/FedASTUtils.java index 2c1060f34b..ddd6169ae0 100644 --- a/org.lflang/src/org/lflang/federated/FedASTUtils.java +++ b/org.lflang/src/org/lflang/federated/FedASTUtils.java @@ -40,6 +40,7 @@ import org.lflang.TimeValue; import org.lflang.generator.GeneratorBase; import org.lflang.generator.PortInstance; +import org.lflang.generator.ReactorInstance; import org.lflang.lf.Action; import org.lflang.lf.ActionOrigin; import org.lflang.lf.Connection; @@ -47,6 +48,7 @@ import org.lflang.lf.Input; import org.lflang.lf.Instantiation; import org.lflang.lf.LfFactory; +import org.lflang.lf.LfPackage; import org.lflang.lf.Parameter; import org.lflang.lf.Reaction; import org.lflang.lf.Reactor; @@ -293,14 +295,14 @@ private static TimeValue findMaxSTP(Variable port, * @param delay The delay value imposed on the connection using after */ private static void addNetworkOutputControlReaction( - PortInstance source, - FederateInstance instance, - int receivingPortID, - int bankIndex, - int channelIndex, - int receivingFedID, - GeneratorBase generator, - Delay delay + PortInstance source, + FederateInstance instance, + int receivingPortID, + int bankIndex, + int channelIndex, + int receivingFedID, + GeneratorBase generator, + TimeValue delay ) { LfFactory factory = LfFactory.eINSTANCE; Reaction reaction = factory.createReaction(); @@ -393,17 +395,18 @@ private static void addNetworkOutputControlReaction( * @param coordination One of CoordinationType.DECENTRALIZED or CoordinationType.CENTRALIZED. */ public static void makeCommunication( - PortInstance source, - PortInstance destination, - Connection connection, - FederateInstance leftFederate, - int leftBankIndex, - int leftChannelIndex, - FederateInstance rightFederate, - int rightBankIndex, - int rightChannelIndex, - GeneratorBase generator, - CoordinationType coordination + PortInstance source, + PortInstance destination, + Connection connection, + FederateInstance leftFederate, + int leftBankIndex, + int leftChannelIndex, + FederateInstance rightFederate, + int rightBankIndex, + int rightChannelIndex, + GeneratorBase generator, + CoordinationType coordination, + ReactorInstance mainInstance ) { LfFactory factory = LfFactory.eINSTANCE; // Assume all the types are the same, so just use the first on the right. @@ -445,7 +448,9 @@ public static void makeCommunication( action_type.setId(generator.getNetworkBufferType()); action.setType(action_type); } - + + TimeValue delayValue = mainInstance.resolveTimeValue(delayAsValue(connection.getDelay())); + // The connection is 'physical' if it uses the ~> notation. if (connection.isPhysical()) { leftFederate.outboundP2PConnections.add(rightFederate); @@ -458,8 +463,8 @@ public static void makeCommunication( if (connection.getDelay() != null) { action.setMinDelay(factory.createValue()); action.getMinDelay().setTime(factory.createTime()); - action.getMinDelay().getTime().setInterval(connection.getDelay().getInterval()); - action.getMinDelay().getTime().setUnit(connection.getDelay().getUnit()); + action.getMinDelay().getTime().setInterval((int) delayValue.time); + action.getMinDelay().getTime().setUnit(delayValue.unit); } } else { // If the connection is logical but coordination @@ -503,7 +508,7 @@ public static void makeCommunication( rightFederate, InferredType.fromAST(type), connection.isPhysical(), - connection.getDelay(), + delayValue, serializer )); @@ -520,7 +525,7 @@ public static void makeCommunication( leftChannelIndex, rightFederate.id, generator, - connection.getDelay() + delayValue ); // Add the network input control reaction to the parent @@ -551,8 +556,19 @@ public static void makeCommunication( connection.isPhysical(), serializer )); - + // Add the receiver reaction to the parent parent.getReactions().add(r2); } + + private static Value delayAsValue(Delay delay) { + Value value = LfPackage.eINSTANCE.getLfFactory().createValue(); + if (delay.getParameter() != null) { + value.setParameter(delay.getParameter()); + } else { + value.setTime(delay.getTime()); + } + return value; + } + } diff --git a/org.lflang/src/org/lflang/generator/ActionInstance.xtend b/org.lflang/src/org/lflang/generator/ActionInstance.xtend index 75d5de0323..a98ffadc3f 100644 --- a/org.lflang/src/org/lflang/generator/ActionInstance.xtend +++ b/org.lflang/src/org/lflang/generator/ActionInstance.xtend @@ -30,9 +30,6 @@ import org.eclipse.xtend.lib.annotations.Accessors import org.lflang.TimeValue import org.lflang.lf.Action import org.lflang.lf.ActionOrigin -import org.lflang.lf.TimeUnit - -import static extension org.lflang.ASTUtils.* /** * Instance of an action. @@ -42,17 +39,16 @@ import static extension org.lflang.ASTUtils.* class ActionInstance extends TriggerInstance { /** The constant default for a minimum delay. */ - public static val DEFAULT_MIN_DELAY = new TimeValue(0, TimeUnit.NONE) + public static val DEFAULT_MIN_DELAY = TimeValue.ZERO @Accessors(PUBLIC_GETTER) - TimeValue minDelay = DEFAULT_MIN_DELAY + TimeValue minDelay - // TODO introduce default value? @Accessors(PUBLIC_GETTER) - TimeValue minSpacing = null; + TimeValue minSpacing @Accessors(PUBLIC_GETTER) - String policy = null; + String policy @Accessors(PUBLIC_GETTER) boolean isPhysical; @@ -68,27 +64,12 @@ class ActionInstance extends TriggerInstance { if (parent === null) { throw new Exception('Cannot create an ActionInstance with no parent.') } - if (definition !== null) { - if (definition.minDelay !== null) { - if (definition.minDelay.parameter !== null) { - val parm = definition.minDelay.parameter - this.minDelay = parent.lookupParameterInstance(parm).init.get(0).getTimeValue - } else { - this.minDelay = definition.minDelay.timeValue - } - } - if (definition.minSpacing !== null) { - if (definition.minSpacing.parameter !== null) { - val parm = definition.minSpacing.parameter - this.minSpacing = parent.lookupParameterInstance(parm).init.get(0).getTimeValue - } else { - this.minSpacing = definition.minSpacing.timeValue - } - } - if (definition.origin === ActionOrigin.PHYSICAL) { - isPhysical = true - } - policy = definition.policy + this.minDelay = parent.resolveTimeValue(definition?.minDelay) ?: DEFAULT_MIN_DELAY + // TODO introduce default value? + this.minSpacing = parent.resolveTimeValue(definition?.minSpacing) + if (definition?.origin === ActionOrigin.PHYSICAL) { + isPhysical = true } + policy = definition?.policy } -} \ No newline at end of file +} diff --git a/org.lflang/src/org/lflang/generator/DeadlineInstance.xtend b/org.lflang/src/org/lflang/generator/DeadlineInstance.xtend index c9eadaa408..fb9e135a98 100644 --- a/org.lflang/src/org/lflang/generator/DeadlineInstance.xtend +++ b/org.lflang/src/org/lflang/generator/DeadlineInstance.xtend @@ -29,9 +29,6 @@ package org.lflang.generator import org.lflang.TimeValue import org.lflang.lf.Deadline -import org.lflang.lf.TimeUnit - -import static extension org.lflang.ASTUtils.* /** * Instance of a deadline. Upon creation the actual delay is converted into @@ -42,30 +39,22 @@ import static extension org.lflang.ASTUtils.* * @author{Edward A. Lee } */ class DeadlineInstance { - + /** * The delay D associated with this deadline. If physical time T < logical * time t + D, the deadline is met, otherwise, it is violated. */ - public TimeValue maxDelay = new TimeValue(0, TimeUnit.NONE) - + public val TimeValue maxDelay + /** * Create a new deadline instance associated with the given reaction * instance. */ new(Deadline definition, ReactionInstance reaction) { - if (definition.delay !== null) { - val parm = definition.delay.parameter - if (parm !== null) { - this.maxDelay = reaction.parent.lookupParameterInstance(parm).init. - get(0).getTimeValue - } else { - this.maxDelay = definition.delay.timeValue - } - } + this.maxDelay = reaction.parent.resolveTimeValue(definition.delay) ?: TimeValue.ZERO } - + override toString() { "DeadlineInstance " + maxDelay.toString } -} \ No newline at end of file +} diff --git a/org.lflang/src/org/lflang/generator/GeneratorBase.xtend b/org.lflang/src/org/lflang/generator/GeneratorBase.xtend index 366052b25c..d44b301cd5 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorBase.xtend +++ b/org.lflang/src/org/lflang/generator/GeneratorBase.xtend @@ -68,6 +68,7 @@ import org.lflang.lf.Action import org.lflang.lf.ActionOrigin import org.lflang.lf.Code import org.lflang.lf.Delay +import org.lflang.lf.Initializer import org.lflang.lf.Instantiation import org.lflang.lf.LfFactory import org.lflang.lf.Model @@ -873,7 +874,7 @@ abstract class GeneratorBase extends AbstractLFValidator { FederateInstance receivingFed, InferredType type, boolean isPhysical, - Delay delay, + TimeValue delay, SupportedSerializers serializer ) { throw new UnsupportedOperationException("This target does not support network connections between federates.") @@ -911,7 +912,7 @@ abstract class GeneratorBase extends AbstractLFValidator { int receivingFederateID, int sendingBankIndex, int sendingChannelIndex, - Delay delay + TimeValue delay ) { throw new UnsupportedOperationException("This target does not support network connections between federates.") } @@ -1302,58 +1303,36 @@ abstract class GeneratorBase extends AbstractLFValidator { } } - /** - * Create a list of default parameter initializers in target code. - * - * @param param The parameter to create initializers for - * @return A list of initializers in target code - */ - protected def getInitializerList(Parameter param) { - var list = new LinkedList(); - - for (i : param?.init) { - if (param.isOfTimeType) { - list.add(i.targetTime) - } else { - list.add(i.targetValue) - } - } - return list - } - /** * Create a list of state initializers in target code. * * @param state The state variable to create initializers for * @return A list of initializers in target code */ - protected def List getInitializerList(StateVar state) { - if (!state.isInitialized) { + protected def List getInitializerList(Initializer init, InferredType type) { + if (init === null) { return null } - var list = new LinkedList(); - - for (i : state?.init) { + return init?.exprs.map[i| if (i.parameter !== null) { - list.add(i.parameter.targetReference) - } else if (state.isOfTimeType) { - list.add(i.targetTime) + i.parameter.targetReference + } else if (type.isTime) { + i.targetTime } else { - list.add(i.targetValue) + i.targetValue } - } - return list + ] } /** * Create a list of parameter initializers in target code in the context * of an reactor instantiation. - * + * * This respects the parameter assignments given in the reactor * instantiation and falls back to the reactors default initializers - * if no value is assigned to it. - * + * if no value is assigned to it. + * * @param param The parameter to create initializers for * @return A list of initializers in target code */ @@ -1363,22 +1342,9 @@ abstract class GeneratorBase extends AbstractLFValidator { } val assignments = i.parameters.filter[p|p.lhs === param] + val actualValue = assignments.size === 0 ? param.init : assignments.get(0).rhs - if (assignments.size == 0) { - // the parameter was not overwritten in the instantiation - return param.initializerList - } else { - // the parameter was overwritten in the instantiation - var list = new LinkedList(); - for (init : assignments.get(0)?.rhs) { - if (param.isOfTimeType) { - list.add(init.targetTime) - } else { - list.add(init.targetValue) - } - } - return list - } + return getInitializerList(actualValue, param.inferredType) } /** @@ -1459,18 +1425,11 @@ abstract class GeneratorBase extends AbstractLFValidator { * @return An RTI-compatible (ie. C target) time string */ protected def getRTITime(Delay d) { - var TimeValue time if (d.parameter !== null) { return d.toText } - time = new TimeValue(d.interval, d.unit) - - if (time.unit != TimeUnit.NONE) { - return time.unit.name() + '(' + time.time + ')' - } else { - return time.time.toString() - } + return d.time.toTimeValue.timeInTargetLanguage } /** Analyze the resource (the .lf file) that is being parsed @@ -1697,7 +1656,8 @@ abstract class GeneratorBase extends AbstractLFValidator { destination.parent.bankIndex, destination.index, this, - targetConfig.coordination + targetConfig.coordination, + mainInstance ) } } @@ -1834,20 +1794,14 @@ abstract class GeneratorBase extends AbstractLFValidator { * @return A time string in the target language */ protected def getTargetTime(Value v) { - if (v.time !== null) { - return v.time.targetTime - } else if (v.isZero) { - val value = new TimeValue(0, TimeUnit.NONE) - return value.timeInTargetLanguage - } - return v.toText + v.timeValue?.timeInTargetLanguage ?: v.toText } protected def getTargetTime(Delay d) { if (d.parameter !== null) { return d.toText } else { - return new TimeValue(d.interval, d.unit).timeInTargetLanguage + return d.time.toTimeValue.timeInTargetLanguage } } diff --git a/org.lflang/src/org/lflang/generator/ParameterInstance.xtend b/org.lflang/src/org/lflang/generator/ParameterInstance.xtend index dd77dfe61f..ce469e44bf 100644 --- a/org.lflang/src/org/lflang/generator/ParameterInstance.xtend +++ b/org.lflang/src/org/lflang/generator/ParameterInstance.xtend @@ -27,12 +27,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package org.lflang.generator -import java.util.LinkedList -import java.util.List import org.lflang.InferredType import org.lflang.lf.LfFactory +import org.lflang.lf.Initializer import org.lflang.lf.Parameter -import org.lflang.lf.Value import static extension org.lflang.ASTUtils.* @@ -46,7 +44,11 @@ import static extension org.lflang.ASTUtils.* * @author{Edward A. Lee } */ class ParameterInstance extends NamedInstance { - + + public Initializer init + + public InferredType type + /** * Create a runtime instance from the specified definition * and with the specified parent that instantiated it. @@ -68,7 +70,7 @@ class ParameterInstance extends NamedInstance { while (assignment !== null) { // NOTE: we only allow a reference to single a parameter or // a list of ordinary values. - val ref = assignment.rhs.get(0).parameter + val ref = assignment.rhs.asSingleValue?.parameter if (ref !== null) { // Get the value from the parameter instance, not the parameter // so that overrides like bank_index work. @@ -89,7 +91,7 @@ class ParameterInstance extends NamedInstance { this.init = ref.init } } else { - this.init = assignment.rhs + this.init = assignment.rhs } if (parent.parent !== null) { assignment = parent.parent.definition.parameters.findFirst[it.lhs === ref] @@ -103,19 +105,13 @@ class ParameterInstance extends NamedInstance { if (parent.bankIndex >= 0 && definition.name.equals("bank_index")) { val value = LfFactory.eINSTANCE.createValue value.literal = "" + parent.bankIndex - val list = new LinkedList() - list.add(value) - this.init = list + + this.init = LfFactory.eINSTANCE.createInitializer + this.init.assign = true + this.init.exprs.add(value) } } - ///////////////////////////////////////////// - //// Public Fields - - public List init - - public InferredType type - ///////////////////////////////////////////// //// Public Methods diff --git a/org.lflang/src/org/lflang/generator/PythonGenerator.xtend b/org.lflang/src/org/lflang/generator/PythonGenerator.xtend index bad299f0ee..20757bae21 100644 --- a/org.lflang/src/org/lflang/generator/PythonGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/PythonGenerator.xtend @@ -29,7 +29,6 @@ package org.lflang.generator import java.io.File import java.util.ArrayList import java.util.LinkedHashSet -import java.util.List import java.util.regex.Pattern import org.eclipse.emf.ecore.resource.Resource import org.eclipse.xtext.generator.IFileSystemAccess2 @@ -44,6 +43,7 @@ import org.lflang.federated.SupportedSerializers import org.lflang.generator.c.CGenerator import org.lflang.lf.Action import org.lflang.lf.Input +import org.lflang.lf.Initializer import org.lflang.lf.Instantiation import org.lflang.lf.Model import org.lflang.lf.Output @@ -198,14 +198,24 @@ class PythonGenerator extends CGenerator { } /** Returns the python initializer corresponding to the given LF init list. */ - def String getPythonInitializer(List init) { + def String getPythonInitializer(Initializer init) { if (init === null) { return "None" - } if (init.size > 1) { - // corresponds to a tuple - return init.join('(', ', ', ')', [it.pythonTargetValue]) + } if (init.isParens && (init.exprs.size != 1 || init.isTrailingComma)) { + // corresponds to a tuple: + // state foo(1,2) # tuple w/ 2 components + // state foo(1,) # tuple w/ 1 component + // state foo() # tuple w/ 0 component + if (init.exprs.size == 1) { + // python tuple of size 1 + return "(" + init.asSingleValue.pythonTargetValue + ",)" + } + // regular python tuple (may also have size zero) + return init.exprs.join('(', ', ', ')', [it.pythonTargetValue]) + } else if ((init.isAssign || init.isParens) && init.exprs.size == 1) { + return init.asSingleValue.getPythonTargetValue } else { - return init.get(0).getPythonTargetValue + throw new AssertionError("invalid expression form: " + init) } } diff --git a/org.lflang/src/org/lflang/generator/ReactorInstance.xtend b/org.lflang/src/org/lflang/generator/ReactorInstance.xtend index 56e09ebd7d..40d1a7a4fb 100644 --- a/org.lflang/src/org/lflang/generator/ReactorInstance.xtend +++ b/org.lflang/src/org/lflang/generator/ReactorInstance.xtend @@ -35,6 +35,7 @@ import java.util.Set import org.eclipse.xtend.lib.annotations.Accessors import org.lflang.ASTUtils import org.lflang.ErrorReporter +import org.lflang.TimeValue import org.lflang.generator.TriggerInstance.BuiltinTriggerVariable import org.lflang.lf.Action import org.lflang.lf.Connection @@ -323,7 +324,21 @@ class ReactorInstance extends NamedInstance { } return null } - + + /** + * Returns the time value of the parameter, possibly looking up a parameter + * value in this instance. Result may be null if param is + * null or code is invalid. + */ + def TimeValue resolveTimeValue(Value v) { + val parm = v?.parameter + if (parm !== null) { + return this.lookupParameterInstance(parm).init.asSingleValue?.getTimeValue + } else { + return v?.timeValue + } + } + /** * Override the base class to return the uniqueID of the bank rather * than this member of the bank, if this is a member of a bank of reactors. diff --git a/org.lflang/src/org/lflang/generator/TimerInstance.xtend b/org.lflang/src/org/lflang/generator/TimerInstance.xtend index 2aaca21200..3481b8edf9 100644 --- a/org.lflang/src/org/lflang/generator/TimerInstance.xtend +++ b/org.lflang/src/org/lflang/generator/TimerInstance.xtend @@ -28,11 +28,8 @@ package org.lflang.generator import org.eclipse.xtend.lib.annotations.Accessors import org.lflang.TimeValue -import org.lflang.lf.TimeUnit import org.lflang.lf.Timer -import static extension org.lflang.ASTUtils.* - /** * Instance of a timer. * @@ -42,9 +39,9 @@ import static extension org.lflang.ASTUtils.* class TimerInstance extends TriggerInstance { /** The global default for offset. */ - public static val DEFAULT_OFFSET = new TimeValue(0, TimeUnit.NONE) + public static val DEFAULT_OFFSET = TimeValue.ZERO /** The global default for period. */ - public static val DEFAULT_PERIOD = new TimeValue(0, TimeUnit.NONE) + public static val DEFAULT_PERIOD = TimeValue.ZERO @Accessors(PUBLIC_GETTER) protected TimeValue offset = DEFAULT_OFFSET @@ -62,23 +59,7 @@ class TimerInstance extends TriggerInstance { if (parent === null) { throw new Exception('Cannot create an TimerInstance with no parent.') } - if (definition !== null) { - if (definition.offset !== null) { - if (definition.offset.parameter !== null) { - val parm = definition.offset.parameter - this.offset = parent.lookupParameterInstance(parm).init.get(0).getTimeValue - } else { - this.offset = definition.offset.timeValue - } - } - if (definition.period !== null) { - if (definition.period.parameter !== null) { - val parm = definition.period.parameter - this.period = parent.lookupParameterInstance(parm).init.get(0).getTimeValue - } else { - this.period = definition.period.timeValue - } - } - } + this.offset = parent.resolveTimeValue(definition?.offset) ?: DEFAULT_OFFSET + this.period = parent.resolveTimeValue(definition?.period) ?: DEFAULT_PERIOD } -} \ No newline at end of file +} diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend index aeeab66d3e..1d6ffe72fc 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend @@ -72,8 +72,8 @@ import org.lflang.generator.TriggerInstance import org.lflang.lf.Action import org.lflang.lf.ActionOrigin import org.lflang.lf.Code -import org.lflang.lf.Delay import org.lflang.lf.Input +import org.lflang.lf.Initializer import org.lflang.lf.Instantiation import org.lflang.lf.Model import org.lflang.lf.Output @@ -81,7 +81,6 @@ import org.lflang.lf.Port import org.lflang.lf.Reaction import org.lflang.lf.Reactor import org.lflang.lf.ReactorDecl -import org.lflang.lf.StateVar import org.lflang.lf.Timer import org.lflang.lf.TriggerRef import org.lflang.lf.TypedVariable @@ -1345,7 +1344,7 @@ class CGenerator extends GeneratorBase { val reactorInstance = main.getChildReactorInstance(federate.instantiation) for (param : reactorInstance.parameters) { if (param.name.equalsIgnoreCase("STP_offset") && param.type.isTime) { - val stp = param.init.get(0).getTimeValue + val stp = param.init.asSingleValue?.getTimeValue if (stp !== null) { pr(''' set_stp_offset(«stp.timeInTargetLanguage»); @@ -4237,7 +4236,7 @@ class CGenerator extends GeneratorBase { val nameOfSelfStruct = selfStructName(instance) for (stateVar : reactorClass.toDefinition.stateVars) { - val initializer = getInitializer(stateVar, instance) + val initializer = getInitializer(stateVar.init, stateVar.inferredType, instance) if (stateVar.initialized) { if (stateVar.isOfTimeType) { pr(initializeTriggerObjects, nameOfSelfStruct + "->" + stateVar.name + " = " + initializer + ";") @@ -4247,7 +4246,7 @@ class CGenerator extends GeneratorBase { // static initializers for arrays and structs have to be handled // this way, and there is no way to tell whether the type of the array // is a struct. - if (stateVar.isParameterized && stateVar.init.size > 0) { + if (stateVar.isParameterized) { pr(initializeTriggerObjects, nameOfSelfStruct + "->" + stateVar.name + " = " + initializer + ";") } else { @@ -4380,13 +4379,16 @@ class CGenerator extends GeneratorBase { return result.toString } - protected def getInitializer(StateVar state, ReactorInstance parent) { + protected def getInitializer(Initializer init, InferredType t, ReactorInstance parent) { + if (init === null) + return "{}" + var list = new LinkedList(); - for (i : state?.init) { + for (i : init.exprs) { if (i.parameter !== null) { list.add(parent.selfStructName + "->" + i.parameter.name) - } else if (state.isOfTimeType) { + } else if (t.isTime) { list.add(i.targetTime) } else { list.add(i.targetValue) @@ -4633,7 +4635,7 @@ class CGenerator extends GeneratorBase { FederateInstance receivingFed, InferredType type, boolean isPhysical, - Delay delay, + TimeValue delay, SupportedSerializers serializer ) { var sendRef = generatePortRef(sendingPort, sendingBankIndex, sendingChannelIndex); @@ -4650,12 +4652,8 @@ class CGenerator extends GeneratorBase { var String next_destination_name = '''"federate «receivingFed.id»"''' // Get the delay literal - var String additionalDelayString = - CGeneratorExtension.getNetworkDelayLiteral( - delay, - this - ); - + var String additionalDelayString = delay?.timeInTargetLanguage ?: "NEVER"; + if (isPhysical) { messageType = "MSG_TYPE_P2P_MESSAGE" } else if (targetConfig.coordination === CoordinationType.DECENTRALIZED) { @@ -4798,18 +4796,14 @@ class CGenerator extends GeneratorBase { int receivingFederateID, int sendingBankIndex, int sendingChannelIndex, - Delay delay + TimeValue delay ) { // Store the code val result = new StringBuilder(); var sendRef = generatePortRef(port, sendingBankIndex, sendingChannelIndex); // Get the delay literal - var String additionalDelayString = - CGeneratorExtension.getNetworkDelayLiteral( - delay, - this - ); + var String additionalDelayString = delay?.timeInTargetLanguage ?: "NEVER" result.append(''' // If the output port has not been SET for the current logical time, @@ -5901,13 +5895,7 @@ class CGenerator extends GeneratorBase { override getNetworkBufferType() '''uint8_t*''' protected def String getInitializer(ParameterInstance p) { - - if (p.type.isList && p.init.size > 1) { - return p.init.join('{', ', ', '}', [it.targetValue]) - } else { - return p.init.get(0).targetValue - } - + return getInitializer(p.init, p.type, p.parent) } override supportsGenerics() { diff --git a/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt index fde384e847..f76beafd56 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt @@ -62,24 +62,15 @@ class CppInstanceGenerator( // If no assignment was found, then the parameter is not overwritten and we assign the // default value with(CppParameterGenerator) { param.defaultValue } + } else if (assignment.rhs.isAssign) { + assert(assignment.rhs.exprs.size == 1) + assignment.rhs.exprs.single().toCode() + } else if (assignment.rhs.isBraces) { + "{${assignment.rhs.exprs.joinToString(", ") { it.toCode() }}}" + } else if (assignment.rhs.isParens) { + "(${assignment.rhs.exprs.joinToString(", ") { it.toCode() }})" } else { - // Otherwise, we use the assigned value. - if (assignment.equals == "=") { - if (!assignment.braces.isNullOrEmpty()) { - "{${assignment.rhs.joinToString(", ") { it.toCode() }}}" - } else if (!assignment.parens.isNullOrEmpty()) { - "(${assignment.rhs.joinToString(", ") { it.toCode() }})" - } else { - assert(assignment.rhs.size == 1) - assignment.rhs[0].toCode() - } - } else { - if (!assignment.braces.isNullOrEmpty()) { - "${param.targetType}{${assignment.rhs.joinToString(", ") { it.toCode() }}}" - } else { - "${param.targetType}(${assignment.rhs.joinToString(", ") { it.toCode() }})" - } - } + throw AssertionError("unreachable") } } diff --git a/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt index 8611621ecd..ded99fa6df 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt @@ -39,7 +39,7 @@ class CppParameterGenerator(private val reactor: Reactor) { * * TODO This is redundant to GeneratorBase.getInitializerList */ - private fun Parameter.getInitializerList() = init.map { + private fun Parameter.getInitializerList() = init.exprs.map { if (isOfTimeType) it.toTime() else it.toCode() } @@ -50,7 +50,7 @@ class CppParameterGenerator(private val reactor: Reactor) { /** Get the default value of the receiver parameter in C++ code */ val Parameter.defaultValue: String get() = - if (braces?.size == 2) "$targetType{${getInitializerList().joinToString(", ")}}" + if (init?.isBraces == true) "$targetType{${getInitializerList().joinToString(", ")}}" else "$targetType(${getInitializerList().joinToString(", ")})" /** Get a C++ type that is a const reference to the parameter type */ @@ -70,4 +70,4 @@ class CppParameterGenerator(private val reactor: Reactor) { reactor.parameters.joinToString("\n", "// parameters\n", "\n") { ", ${it.name}(${it.name})" } -} \ No newline at end of file +} diff --git a/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt index 96bf27afb3..d4babcdf71 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt @@ -24,7 +24,6 @@ package org.lflang.generator.cpp -import org.lflang.isInitialized import org.lflang.isOfTimeType import org.lflang.lf.Reactor import org.lflang.lf.StateVar @@ -37,7 +36,7 @@ class CppStateGenerator(private val reactor: Reactor) { * * TODO This is redundant to GeneratorBase.getInitializerList */ - private fun getInitializerList(state: StateVar) = state.init.map { + private fun getInitializerList(state: StateVar) = state.init.exprs.map { when { it.parameter != null -> it.parameter.name state.isOfTimeType -> it.toTime() @@ -46,7 +45,7 @@ class CppStateGenerator(private val reactor: Reactor) { } private fun generateInitializer(state: StateVar): String = - if (state.parens.isNullOrEmpty()) + if (state.init.isBraces) "${state.name}{${getInitializerList(state).joinToString(separator = ", ")}}" else "${state.name}(${getInitializerList(state).joinToString(separator = ", ")})" @@ -57,6 +56,6 @@ class CppStateGenerator(private val reactor: Reactor) { reactor.stateVars.joinToString("\n", "// state variable\n", "\n") { "${it.targetType} ${it.name};" } /** Get all timer initializers */ - fun generateInitializers(): String = reactor.stateVars.filter { it.isInitialized } + fun generateInitializers(): String = reactor.stateVars.filter { it.init != null } .joinToString(separator = "\n", prefix = "// state variables\n") { ", ${generateInitializer(it)}" } -} \ No newline at end of file +} diff --git a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt index 6dc50484d7..31ee53607e 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt @@ -29,7 +29,6 @@ import org.eclipse.emf.ecore.resource.Resource import org.eclipse.xtext.generator.IFileSystemAccess2 import org.eclipse.xtext.generator.IGeneratorContext import org.lflang.* -import org.lflang.ASTUtils.isInitialized import org.lflang.Target import org.lflang.federated.FedTSLauncher import org.lflang.federated.FederateInstance @@ -90,10 +89,9 @@ class TSGenerator( fun getTargetTypeW(state: StateVar): String = getTargetType(state) fun getTargetTypeW(t: Type): String = getTargetType(t) - fun getInitializerListW(state: StateVar): List = getInitializerList(state) - fun getInitializerListW(param: Parameter): List = getInitializerList(param) - fun getInitializerListW(param: Parameter, i: Instantiation): List = - getInitializerList(param, i) + fun getInitializerListW(state: StateVar): List = getInitializerList(state.init, state.inferredType) + fun getInitializerListW(param: Parameter): List = getInitializerList(param.init, param.inferredType) + fun getInitializerListW(param: Parameter, i: Instantiation): List = getInitializerList(param, i) /** Generate TypeScript code from the Lingua Franca model contained by the * specified resource. This is the main entry point for code @@ -352,7 +350,7 @@ class TSGenerator( override fun getTargetType(s: StateVar): String { val type = super.getTargetType(s) - return if (!isInitialized(s)) { + return if (s.init == null) { "$type | undefined" } else { type @@ -428,7 +426,7 @@ class TSGenerator( receivingFed: FederateInstance, type: InferredType, isPhysical: Boolean, - delay: Delay?, + delay: TimeValue?, serializer: SupportedSerializers ): String { return with(PrependOperator) {""" @@ -459,7 +457,7 @@ class TSGenerator( receivingFederateID: Int, sendingBankIndex: Int, sendingChannelIndex: Int, - delay: Delay? + delay: TimeValue? ): String? { return with(PrependOperator) {""" |// TODO(hokeun): Figure out what to do for generateNetworkOutputControlReactionBody diff --git a/org.lflang/src/org/lflang/generator/ts/TSStateGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSStateGenerator.kt index e662aeb799..bc92e5d889 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSStateGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSStateGenerator.kt @@ -32,7 +32,7 @@ class TSStateGenerator ( val stateInstantiations = LinkedList() // Next handle states. for (stateVar in stateVars) { - if (ASTUtils.isInitialized(stateVar)) { + if (stateVar.init != null) { stateInstantiations.add("this.${stateVar.name} = new __State(${getTargetInitializer(stateVar)});"); } else { stateInstantiations.add("this.${stateVar.name} = new __State(undefined);"); @@ -40,4 +40,4 @@ class TSStateGenerator ( } return stateInstantiations.joinToString("\n") } -} \ No newline at end of file +} diff --git a/org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend b/org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend index f4c190b804..ed6f27b7e0 100644 --- a/org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend +++ b/org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend @@ -60,6 +60,7 @@ import org.lflang.lf.IPV6Host import org.lflang.lf.Import import org.lflang.lf.ImportedReactor import org.lflang.lf.Input +import org.lflang.lf.Initializer import org.lflang.lf.Instantiation import org.lflang.lf.KeyValuePair import org.lflang.lf.KeyValuePairs @@ -302,10 +303,10 @@ class LFValidatorImpl extends AbstractLFValidator { def checkAssignment(Assignment assignment) { // If the left-hand side is a time parameter, make sure the assignment has units if (assignment.lhs.isOfTimeType) { - if (assignment.rhs.size > 1) { + if (assignment.rhs.isList) { error("Incompatible type.", Literals.ASSIGNMENT__RHS) } else { - val v = assignment.rhs.get(0) + val v = assignment.rhs.asSingleValue if (!v.isValidTime) { if (v.parameter === null) { // This is a value. Check that units are present. @@ -336,10 +337,6 @@ class LFValidatorImpl extends AbstractLFValidator { } } - if(!assignment.braces.isNullOrEmpty() && this.target != Target.CPP) { - error("Brace initializers are only supported for the C++ target", Literals.ASSIGNMENT__BRACES) - } - // FIXME: lhs is list => rhs is list // lhs is fixed with size n => rhs is fixed with size n // FIXME": similar checks for decl/init @@ -750,13 +747,13 @@ class LFValidatorImpl extends AbstractLFValidator { def checkParameter(Parameter param) { checkName(param.name, Literals.PARAMETER__NAME) - if (param.init.exists[it.parameter !== null]) { + if (param.init.exprs.exists[it.parameter !== null]) { // Initialization using parameters is forbidden. error("Parameter cannot be initialized using parameter.", Literals.PARAMETER__INIT) } - if (param.init === null || param.init.size == 0) { + if (param.init === null || param.init.exprs.size == 0) { // All parameters must be initialized. error("Uninitialized parameter.", Literals.PARAMETER__INIT) } else if (param.isOfTimeType) { @@ -765,12 +762,12 @@ class LFValidatorImpl extends AbstractLFValidator { // If the parameter is not a list, cannot be initialized // using a one. - if (param.init.size > 1 && param.type.arraySpec === null) { + if (param.init.exprs.size > 1 && param.type.arraySpec === null) { error("Time parameter cannot be initialized using a list.", Literals.PARAMETER__INIT) } else { // The parameter is a singleton time. - val init = param.init.get(0) + val init = param.init.exprs.get(0) if (init.time === null) { if (init !== null && !init.isZero) { if (init.isInteger) { @@ -800,10 +797,13 @@ class LFValidatorImpl extends AbstractLFValidator { Literals.PARAMETER__INIT) } - if(!param.braces.isNullOrEmpty && this.target != Target.CPP) { - error("Brace initializers are only supported for the C++ target", Literals.PARAMETER__BRACES) + } + + @Check(FAST) + def checkInitializer(Initializer init) { + if (init.isBraces && this.target != Target.CPP) { + error("Brace initializers are only supported for the C++ target", Literals.INITIALIZER__BRACES) } - } @Check(FAST) @@ -1160,7 +1160,7 @@ class LFValidatorImpl extends AbstractLFValidator { // If the state is declared to be a time, // make sure that it is initialized correctly. if (stateVar.init !== null) { - for (init : stateVar.init) { + for (init : stateVar.init.exprs) { if (stateVar.type !== null && stateVar.type.isTime && !init.isValidTime) { if (stateVar.isParameterized) { @@ -1189,18 +1189,14 @@ class LFValidatorImpl extends AbstractLFValidator { error("State must have a type.", Literals.STATE_VAR__TYPE) } - if (isCBasedTarget && stateVar.init.size > 1) { + if (isCBasedTarget && stateVar.init.isList) { // In C, if initialization is done with a list, elements cannot // refer to parameters. - if (stateVar.init.exists[it.parameter !== null]) { + if (stateVar.init.exprs.exists[it.parameter !== null]) { error("List items cannot refer to a parameter.", Literals.STATE_VAR__INIT) } } - - if(!stateVar.braces.isNullOrEmpty && this.target != Target.CPP) { - error("Brace initializers are only supported for the C++ target", Literals.STATE_VAR__BRACES) - } } @Check(FAST) From 599627c44bea30d5957d2239c9520e213eb795ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 14 Oct 2021 17:10:03 +0200 Subject: [PATCH 09/68] Add tests --- .../src/org/lflang/tests/LFParsingTest.java | 38 +++++++++++++++++++ org.lflang/src/org/lflang/LinguaFranca.xtext | 17 ++++++++- org.lflang/src/org/lflang/Target.java | 21 ++++++++++ .../lflang/validation/LFValidatorImpl.xtend | 14 +++++++ 4 files changed, 89 insertions(+), 1 deletion(-) diff --git a/org.lflang.tests/src/org/lflang/tests/LFParsingTest.java b/org.lflang.tests/src/org/lflang/tests/LFParsingTest.java index a58dec3e12..20d69c4169 100644 --- a/org.lflang.tests/src/org/lflang/tests/LFParsingTest.java +++ b/org.lflang.tests/src/org/lflang/tests/LFParsingTest.java @@ -45,6 +45,44 @@ public void testParsingListLiterals() throws Exception { expectParsingErrorIn("target Python; \nreactor Foo(p([,])) {}"); } + @Test + public void testParsingInitializers() throws Exception { + assertNoParsingErrorsIn("target Python; \nreactor Foo(p = [1,]) {}"); + assertNoParsingErrorsIn("target Python; \nreactor Foo(p = (1,)) {}"); + assertNoParsingErrorsIn("target Python; \nreactor Foo(p = ()) {}"); + assertNoParsingErrorsIn("target Python; \nreactor Foo(p = []) {}"); + assertNoParsingErrorsIn("target Python; \nreactor Foo(p = 1) {}"); + + assertNoParsingErrorsIn("target Python; \nreactor Foo{ state p = [1,] }"); + assertNoParsingErrorsIn("target Python; \nreactor Foo{ state p = (1,) }"); + assertNoParsingErrorsIn("target Python; \nreactor Foo{ state p = () }"); + assertNoParsingErrorsIn("target Python; \nreactor Foo{ state p = [] }"); + assertNoParsingErrorsIn("target Python; \nreactor Foo{ state p = 1 }"); + + + // [,] is not an ok list literal + expectParsingErrorIn("target Python; \nreactor Foo(p = [,]) {}"); + } + + @Test + public void testParsingParenInitializers() throws Exception { + assertNoParsingErrorsIn("target Python; \nreactor Foo(p ([1,])) {}"); + assertNoParsingErrorsIn("target Python; \nreactor Foo(p ((1,))) {}"); + assertNoParsingErrorsIn("target Python; \nreactor Foo(p (())) {}"); + assertNoParsingErrorsIn("target Python; \nreactor Foo(p ([])) {}"); + assertNoParsingErrorsIn("target Python; \nreactor Foo(p (1)) {}"); + + assertNoParsingErrorsIn("target Python; \nreactor Foo{ state p([1,]) }"); + assertNoParsingErrorsIn("target Python; \nreactor Foo{ state p((1,)) }"); + assertNoParsingErrorsIn("target Python; \nreactor Foo{ state p(()) }"); + assertNoParsingErrorsIn("target Python; \nreactor Foo{ state p([]) }"); + assertNoParsingErrorsIn("target Python; \nreactor Foo{ state p(1) }"); + + + // [,] is not an ok list literal + expectParsingErrorIn("target Python; \nreactor Foo(p([,])) {}"); + } + @Test public void testLexingLifetimeAnnots() throws Exception { assertNoParsingErrorsIn(makeLfTargetCode("Rust", diff --git a/org.lflang/src/org/lflang/LinguaFranca.xtext b/org.lflang/src/org/lflang/LinguaFranca.xtext index 85bd614b48..22f9e96571 100644 --- a/org.lflang/src/org/lflang/LinguaFranca.xtext +++ b/org.lflang/src/org/lflang/LinguaFranca.xtext @@ -314,8 +314,23 @@ Expr : // If it is a constant, the validator should check that if the value // is non-zero, it is accompanied by a unit. Value: - (parameter=[Parameter] | time=Time | literal=Literal | list=ListLiteral | code=Code); + parameter=[Parameter] + | time=Time + | literal=Literal + | list=ListLiteral + | parenthesizedExpr=ParenthesizedExpr + | code=Code +; +// This translates to a tuple in Python & Rust (at least). +// If it's just a single expression without trailing comma, +// it's equivalent to the wrapped expression (in all targets). +// (expr) = expr +// (expr,) = tuple of 1 component +// () = tuple of zero components (in Rust/Caml, unit) +ParenthesizedExpr: + {ParenthesizedExpr} '(' ( items+=Value (',' items+=Value)* )? ')' +; Time: (interval=INT unit=TimeUnit) diff --git a/org.lflang/src/org/lflang/Target.java b/org.lflang/src/org/lflang/Target.java index cb9254efa7..3fc49e3e14 100644 --- a/org.lflang/src/org/lflang/Target.java +++ b/org.lflang/src/org/lflang/Target.java @@ -405,6 +405,27 @@ public boolean supportsLfListLiterals() { return this == Python; } + /** + * Returns true if the target supports tuple literals in LF syntax. + * If not, they produce validator errors. + * + *

Tuple literals use the production {@link org.lflang.lf.ParenthesizedExpr}, + * they're only interpreted as a tuple if they match: + *

    + *
  • The empty tuple: {@code ()} + *
  • A one element tuple: eg {@code (expr,)} note that the trailing comma is required + *
  • A 2+ element tuple: eg {@code ( expr, expr )} note that the trailing comma is NOT required but allowed. + *
+ * + *

In other words, writing {@code (expr)} is never + * interpreted as a tuple, but always as equivalent to + * the contained {@code expr}. Hence, that syntax is allowed + * in all targets and this method cannot switch it off. + */ + public boolean supportsLfTupleLiterals() { + return this == Python; + } + /** * Return the target that matches the given string. diff --git a/org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend b/org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend index ed6f27b7e0..db9a7edc11 100644 --- a/org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend +++ b/org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend @@ -376,6 +376,20 @@ class LFValidatorImpl extends AbstractLFValidator { } } + @Check(FAST) + def checkTupleLiteral(ParenthesizedExpr expr) { + if (expr.items.size == 1 && !expr.isTrailingComma) { + // this is allowed in all targets + return; + } + if (!target.supportsLfTupleLiterals()) { + if (expr.items.size == 1) + error("Target " + target + " does not support tuple literals. You might want to remove the trailing comma.", Literals.PARENTHESIZED_EXPR__ITEMS) + else + error("Target " + target + " does not support tuple literals.", Literals.PARENTHESIZED_EXPR__ITEMS) + } + } + @Check(FAST) def checkConnection(Connection connection) { From 03fcc57f579deb0658bb99662b92bbd1e884dbe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 14 Oct 2021 17:40:25 +0200 Subject: [PATCH 10/68] Fix grammar Replace Value members with different node types that implement the interface Value. Uniformize treatment of Value everywhere in grammar. This is still going to need some work to streamline the generators. I feel like everything around values has been duplicated 10 times everywhere... The Rust branch introduces a couple of things that may be used to refactor other generators more gradually and purge GeneratorBase, so let's wait for that. --- .../synthesis/LinguaFrancaSynthesis.xtend | 4 +- .../synthesis/util/UtilityExtensions.xtend | 21 +-- .../src/org/lflang/tests/LFParsingTest.java | 10 + .../compiler/LinguaFrancaASTUtilsTest.xtend | 29 +-- .../compiler/LinguaFrancaValidationTest.xtend | 39 ++-- org.lflang/src/org/lflang/ASTUtils.xtend | 153 ++++++++------- org.lflang/src/org/lflang/AstExtensions.kt | 18 +- org.lflang/src/org/lflang/LinguaFranca.xtext | 95 +++++----- org.lflang/src/org/lflang/ModelInfo.java | 19 +- .../src/org/lflang/federated/FedASTUtils.java | 55 ++---- .../org/lflang/generator/GeneratorBase.xtend | 25 +-- .../lflang/generator/ParameterInstance.xtend | 13 +- .../lflang/generator/PythonGenerator.xtend | 10 +- .../lflang/generator/ReactorInstance.xtend | 6 +- .../org/lflang/generator/c/CGenerator.xtend | 10 +- .../org/lflang/generator/cpp/CppExtensions.kt | 10 +- .../lflang/generator/cpp/CppStateGenerator.kt | 7 +- .../lflang/generator/ts/TSActionGenerator.kt | 9 +- .../generator/ts/TSReactionGenerator.kt | 9 +- .../lflang/validation/LFValidatorImpl.xtend | 175 ++++++++---------- 20 files changed, 334 insertions(+), 383 deletions(-) diff --git a/org.lflang.diagram/src/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.xtend b/org.lflang.diagram/src/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.xtend index 5be8029772..4fb00bafdb 100644 --- a/org.lflang.diagram/src/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.xtend +++ b/org.lflang.diagram/src/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.xtend @@ -915,9 +915,7 @@ class LinguaFrancaSynthesis extends AbstractDiagramSynthesis { if (!t.nullOrEmpty) { b.append(":").append(t) } - if (!param.init.nullOrEmpty) { - b.append("(").append(param.init.join(", ", [it.toText])).append(")") - } + b.append(ASTUtils.toText(param.init)) return b.toString() } diff --git a/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/UtilityExtensions.xtend b/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/UtilityExtensions.xtend index b8bbb369b9..4d52f5b726 100644 --- a/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/UtilityExtensions.xtend +++ b/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/UtilityExtensions.xtend @@ -28,26 +28,7 @@ import org.lflang.lf.Value class UtilityExtensions extends AbstractSynthesisExtensions { extension KGraphFactory = KGraphFactory.eINSTANCE - - /** - * Converts a timing value into readable text - */ - def String toText(Value value) { - if (value !== null) { - if (value.parameter !== null) { - return value.parameter.name - } else if (value.time !== null) { - return value.time.interval + - value.time.unit.toString - } else if (value.literal !== null) { - return value.literal - } else if (value.code !== null) { - ASTUtils.toText(value.code) - } - } - return "" - } - + /** * Converts a host value into readable text */ diff --git a/org.lflang.tests/src/org/lflang/tests/LFParsingTest.java b/org.lflang.tests/src/org/lflang/tests/LFParsingTest.java index 20d69c4169..f077913eba 100644 --- a/org.lflang.tests/src/org/lflang/tests/LFParsingTest.java +++ b/org.lflang.tests/src/org/lflang/tests/LFParsingTest.java @@ -83,6 +83,16 @@ public void testParsingParenInitializers() throws Exception { expectParsingErrorIn("target Python; \nreactor Foo(p([,])) {}"); } + @Test + public void testParsingParentheses() throws Exception { + // unnecessary parentheses are allowed + assertNoParsingErrorsIn("target Python; \nreactor Foo{ state p = ((1)) }"); + assertNoParsingErrorsIn("target Python; \nreactor Foo{ state p = (1) }"); + assertNoParsingErrorsIn("target Python; \nreactor Foo{ state p = (1,) }"); + assertNoParsingErrorsIn("target Python; \nreactor Foo{ state p = (1,2) }"); + assertNoParsingErrorsIn("target Python; \nreactor Foo{ state p = (1,2,) }"); + } + @Test public void testLexingLifetimeAnnots() throws Exception { assertNoParsingErrorsIn(makeLfTargetCode("Rust", diff --git a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaASTUtilsTest.xtend b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaASTUtilsTest.xtend index 4e44d14bc4..5639e359c7 100644 --- a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaASTUtilsTest.xtend +++ b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaASTUtilsTest.xtend @@ -37,6 +37,8 @@ import org.lflang.lf.Instantiation import org.lflang.lf.Model import org.lflang.lf.Parameter import org.lflang.lf.StateVar +import org.lflang.lf.Value +import org.lflang.lf.Literal import org.lflang.tests.LFInjectorProvider import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test @@ -183,28 +185,28 @@ class LinguaFrancaASTUtilsTest { for (parameter : model.eAllContents.filter(Parameter).toList) { if (parameter.name == 'x') { var values = ASTUtils.initialValue(parameter, null); - Assertions.assertEquals(values.get(0).literal, "1"); + assertIsLiteral(values.get(0), "1"); values = ASTUtils.initialValue(parameter, list_a1); - Assertions.assertEquals(values.get(0).literal, "2"); + assertIsLiteral(values.get(0), "2"); values = ASTUtils.initialValue(parameter, list_a2); - Assertions.assertEquals(values.get(0).literal, "-1"); + assertIsLiteral(values.get(0), "-1"); values = ASTUtils.initialValue(parameter, list_a1b1); - Assertions.assertEquals(values.get(0).literal, "3"); + assertIsLiteral(values.get(0), "3"); values = ASTUtils.initialValue(parameter, list_a2b1); - Assertions.assertEquals(values.get(0).literal, "-1"); + assertIsLiteral(values.get(0), "-1"); values = ASTUtils.initialValue(parameter, list_a1b2); - Assertions.assertEquals(values.get(0).literal, "-2"); + assertIsLiteral(values.get(0), "-2"); values = ASTUtils.initialValue(parameter, list_a2b2); - Assertions.assertEquals(values.get(0).literal, "-1"); + assertIsLiteral(values.get(0), "-1"); } else if (parameter.name == 'y') { var values = ASTUtils.initialValue(parameter, null); - Assertions.assertEquals(values.get(0).literal, "2"); + assertIsLiteral(values.get(0), "2"); try { values = ASTUtils.initialValue(parameter, list_a1); @@ -213,11 +215,16 @@ class LinguaFrancaASTUtilsTest { } values = ASTUtils.initialValue(parameter, list_b1); - Assertions.assertEquals(values.get(0).literal, "3"); + assertIsLiteral(values.get(0), "3"); values = ASTUtils.initialValue(parameter, list_b2); - Assertions.assertEquals(values.get(0).literal, "-2"); + assertIsLiteral(values.get(0), "-2"); } } } -} \ No newline at end of file + + def static assertIsLiteral(Value v, String literalValue) { + Assertions.assertTrue(v instanceof Literal, "Expected a literal, got " + v); + Assertions.assertEquals((v as Literal).literal, literalValue); + } +} diff --git a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend index e0be4634fd..eecd3ab6d3 100644 --- a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend +++ b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend @@ -283,7 +283,7 @@ class LinguaFrancaValidationTest { reactor Foo { output out:int; } - main reactor Bar { + main reactor { output out:int; x = new Foo(); x.out -> out; @@ -495,7 +495,7 @@ class LinguaFrancaValidationTest { printf("Hello World.\n"); =} } - ''').assertError(LfPackage::eINSTANCE.value, + ''').assertError(LfPackage::eINSTANCE.timer, null, "Missing time units. Should be one of " + TimeUnit.VALUES.filter[it != TimeUnit.NONE]) } @@ -513,8 +513,8 @@ class LinguaFrancaValidationTest { printf("Hello World.\n"); =} } - ''').assertError(LfPackage::eINSTANCE.value, - null, 'Parameter is not of time type') + ''').assertError(LfPackage::eINSTANCE.timer, + null, 'Referenced parameter does not have time type.') } @@ -531,7 +531,7 @@ class LinguaFrancaValidationTest { printf("Hello World.\n"); =} } - ''').assertError(LfPackage::eINSTANCE.value, + ''').assertError(LfPackage::eINSTANCE.timer, null, 'Invalid time literal') } @@ -713,26 +713,15 @@ class LinguaFrancaValidationTest { } ''') - model.assertError(LfPackage::eINSTANCE.parameter, null, - "Type declaration missing.") - model.assertError(LfPackage::eINSTANCE.parameter, null, - "Missing time units. Should be one of " + - TimeUnit.VALUES.filter[it != TimeUnit.NONE]) - model.assertError(LfPackage::eINSTANCE.parameter, null, - "Invalid time literal.") - model.assertError(LfPackage::eINSTANCE.parameter, null, - "Time parameter cannot be initialized using a list.") - model.assertError(LfPackage::eINSTANCE.parameter, null, - "Parameter cannot be initialized using parameter.") - model.assertError(LfPackage::eINSTANCE.stateVar, null, - "Referenced parameter does not denote a time.") - model.assertError(LfPackage::eINSTANCE.stateVar, null, - "Invalid time literal.") - model.assertError(LfPackage::eINSTANCE.parameter, null, - "Uninitialized parameter.") - model.assertError(LfPackage::eINSTANCE.value, null, - "Missing time units. Should be one of " + - TimeUnit.VALUES.filter[it != TimeUnit.NONE]) + model.assertError(LfPackage::eINSTANCE.parameter, null, "Type declaration missing.") + model.assertError(LfPackage::eINSTANCE.parameter, null, "Missing time units. Should be one of " + TimeUnit.VALUES.filter[it != TimeUnit.NONE]) + model.assertError(LfPackage::eINSTANCE.parameter, null, "Invalid time literal.") + model.assertError(LfPackage::eINSTANCE.parameter, null, "Expected exactly one time value.") + model.assertError(LfPackage::eINSTANCE.parameter, null, "Parameter cannot be initialized using parameter.") + model.assertError(LfPackage::eINSTANCE.stateVar, null, "Referenced parameter does not have time type.") + model.assertError(LfPackage::eINSTANCE.stateVar, null, "Invalid time literal.") + // model.assertError(LfPackage::eINSTANCE.parameter, null, "Uninitialized parameter.") + model.assertError(LfPackage::eINSTANCE.timer, null, "Missing time units. Should be one of " + TimeUnit.VALUES.filter[it != TimeUnit.NONE]) } diff --git a/org.lflang/src/org/lflang/ASTUtils.xtend b/org.lflang/src/org/lflang/ASTUtils.xtend index af470e58f7..5ee64f72e1 100644 --- a/org.lflang/src/org/lflang/ASTUtils.xtend +++ b/org.lflang/src/org/lflang/ASTUtils.xtend @@ -45,6 +45,7 @@ import org.lflang.lf.ActionOrigin import org.lflang.lf.ArraySpec import org.lflang.lf.Assignment import org.lflang.lf.Code +import org.lflang.lf.CodeExpr import org.lflang.lf.Connection import org.lflang.lf.Delay import org.lflang.lf.Element @@ -53,9 +54,12 @@ import org.lflang.lf.Input import org.lflang.lf.Initializer import org.lflang.lf.Instantiation import org.lflang.lf.LfFactory +import org.lflang.lf.Literal +import org.lflang.lf.ListLiteral import org.lflang.lf.Model import org.lflang.lf.Output import org.lflang.lf.Parameter +import org.lflang.lf.ParamRef import org.lflang.lf.Port import org.lflang.lf.Reaction import org.lflang.lf.Reactor @@ -65,6 +69,7 @@ import org.lflang.lf.TargetDecl import org.lflang.lf.Time import org.lflang.lf.TimeUnit import org.lflang.lf.Timer +import org.lflang.lf.TupleExpr import org.lflang.lf.Type import org.lflang.lf.TypeParm import org.lflang.lf.Value @@ -276,14 +281,8 @@ class ASTUtils { } val assignment = factory.createAssignment assignment.lhs = delayClass.parameters.get(0) - val value = factory.createValue - if (delay.parameter !== null) { - value.parameter = delay.parameter - } else { - value.time = delay.time - } val rhs = factory.createInitializer - rhs.exprs.add(value) + rhs.exprs.add(delay.value) rhs.assign = true assignment.rhs = rhs delayInstance.parameters.add(assignment) @@ -338,15 +337,14 @@ class ASTUtils { defaultTime.interval = 0 val init = factory.createInitializer - val defaultValue = factory.createValue - defaultValue.time = defaultTime delayParameter.init = init - init.exprs.add(defaultValue) + init.exprs.add(defaultTime) // Name the newly created action; set its delay and type. action.name = "act" - action.minDelay = factory.createValue - action.minDelay.parameter = delayParameter + val p = factory.createParamRef + p.parameter = delayParameter + action.minDelay = p action.origin = ActionOrigin.LOGICAL if (generator.supportsGenerics) { @@ -616,7 +614,19 @@ class ASTUtils { t.code.toText } } - + + /** Format the initializer as it would appear in LF. */ + def static toText(Initializer init) { + if (init.isBraces) + init.exprs.join(", ", "{", "}", [it.toText]) + else if (init.isParens) + init.exprs.join(", ", "(", ")", [it.toText]) + else if (init.isAssign) + "= " + init.asSingleValue.toText + else + "" // no initializer + } + /** * Intelligently trim the white space in a code block. * @@ -765,28 +775,36 @@ class ASTUtils { * @return A textual representation */ def static String toText(Value v) { - if (v.parameter !== null) { - return v.parameter.name - } - if (v.time !== null) { - return v.time.toText - } - if (v.literal !== null) { - return v.literal - } - if (v.code !== null) { - return v.code.toText + return switch (v) { + ParamRef: v.parameter.name + Time: v.toText + Literal: v.literal + ListLiteral: v.items.join(',', '[', ']', [ it.toText ]) + TupleExpr: { + val end = v.isTrailingComma ? ",)" : ")" + v.items.join(',', '(', end, [ it.toText ]) + } + CodeExpr: v.code.toText + default: throw new AssertionError("unsupported " + v) } - "" } def static String toText(Delay d) { - if (d.parameter !== null) { - return d.parameter.name + d.value.toText + } + + /** Remove parentheses around a single expression. */ + def static Value peelParens(Value value) { + // fixme remove this + var v = value + while (v instanceof TupleExpr + && (v as TupleExpr).items.size == 1 + && !(v as TupleExpr).isTrailingComma) { + throw new AssertionError("grammar should prevent this") } - d.time.toText + v } - + /** * Return a string of the form either "name" or "container.name" depending * on in which form the variable reference was given. @@ -912,9 +930,9 @@ class ASTUtils { * @return True if the given value denotes the constant `0`, false otherwise. */ def static boolean isZero(Value value) { - if (value.literal !== null) { + if (value instanceof Literal) { return value.literal.isZero - } else if (value.code !== null) { + } else if (value instanceof CodeExpr) { return value.code.isZero } return false @@ -950,9 +968,9 @@ class ASTUtils { * @return True if the given value is an integer, false otherwise. */ def static boolean isInteger(Value value) { - if (value.literal !== null) { + if (value instanceof Literal) { return value.literal.isInteger - } else if (value.code !== null) { + } else if (value instanceof CodeExpr) { return value.code.isInteger } return false @@ -964,22 +982,14 @@ class ASTUtils { * @return True if the argument denotes a valid time, false otherwise. */ def static boolean isValidTime(Value value) { - if (value !== null) { - if (value.parameter !== null) { - if (value.parameter.isOfTimeType) { - return true - } - } else if (value.time !== null) { - return isValidTime(value.time) - } else if (value.literal !== null) { - if (value.literal.isZero) { - return true - } - } else if (value.code !== null) { - if (value.code.isZero) { - return true - } - } + if (value instanceof ParamRef) { + return value.parameter.isOfTimeType + } else if (value instanceof Time) { + return isValidTime(value) + } else if (value instanceof Literal) { + return value.isZero + } else if (value instanceof CodeExpr) { + return value.code.isZero } return false } @@ -1030,10 +1040,12 @@ class ASTUtils { return true } // Or it has to be initialized as a proper time with units. - return p.init?.asSingleValue?.time?.unit !== TimeUnit.NONE // In other words, one can write: - // - `x:time(0)` -OR- - // - `x:(0 msec)`, `x:(0 sec)`, etc. + // - `x:time(0)` -OR- + // - `x:(0 msec)`, `x:(0 sec)`, etc. + + val init = p.init?.asSingleValue + return init instanceof Time && (init as Time).unit !== TimeUnit.NONE } return false } @@ -1089,13 +1101,12 @@ class ASTUtils { def static TimeValue getInitialTimeValue(Parameter p) { if (p !== null && p.isOfTimeType) { val init = p.init.asSingleValue - if (init?.time !== null) { - return new TimeValue(init.time.interval, init.time.unit) - } else if (init?.parameter !== null) { - // Parameter value refers to another parameter. - return getInitialTimeValue(init.parameter) + if (init instanceof Time) { + return init.toTimeValue + } else if (init instanceof ParamRef) { + return getInitialTimeValue(init.parameter) } else { - return new TimeValue(0, TimeUnit.NONE) + return TimeValue.ZERO } } return null @@ -1107,19 +1118,19 @@ class ASTUtils { * @return A time value based on the given parameter's initial value. */ def static TimeValue getTimeValue(Value v) { - if (v.parameter !== null) { + if (v instanceof ParamRef) { return ASTUtils.getInitialTimeValue(v.parameter) - } else if (v.time !== null) { - return new TimeValue(v.time.interval, v.time.unit) + } else if (v instanceof Time) { + return v.toTimeValue } else { - return new TimeValue(0, TimeUnit.NONE) + return TimeValue.ZERO //fixme doesn't look right. } } /** * Given a parameter, return its initial value. - * The initial value is a list of instances of Value, where each - * Value is either an instance of Time, Literal, or Code. + * The initial value is a list of instances of Expr, where each + * Expr is either an instance of Time, Literal, or Code. * * If the instantiations argument is null or an empty list, then the * value returned is simply the default value given when the parameter @@ -1217,7 +1228,7 @@ class ASTUtils { // Right hand side can be a list. Collect the entries. val result = new LinkedList() for (value: lastAssignment.rhs.exprs) { - if (value.parameter !== null) { + if (value instanceof ParamRef) { if (instantiations.size() > 1 && instantiation.eContainer !== instantiations.get(1).reactorClass ) { @@ -1228,7 +1239,7 @@ class ASTUtils { + "." ); } - result.addAll(initialValue(value.parameter, + result.addAll(initialValue(value.parameter, instantiations.subList(1, instantiations.size()))); } else { result.add(value) @@ -1262,11 +1273,11 @@ class ASTUtils { */ def static Integer initialValueInt(Parameter parameter, List instantiations) { val values = initialValue(parameter, instantiations); - var result = 0; + var result = 0; // why does this sum arguments? for (value: values) { - if (value.literal === null) return null; + if (!(value instanceof Literal)) return null; try { - result += Integer.decode(value.literal); + result += Integer.decode((value as Literal).literal); } catch (NumberFormatException ex) { return null; } @@ -1510,7 +1521,7 @@ class ASTUtils { * otherwise. */ def static boolean isParameterized(StateVar s) { - return s?.init?.asSingleValue?.parameter !== null + return s?.init?.asSingleValue instanceof ParamRef } /** @@ -1526,7 +1537,7 @@ class ASTUtils { if (single !== null) { // If there is a single element in the list, and it is a proper // time value with units, we infer the type "time". - if (single.parameter !== null) { + if (single instanceof ParamRef) { return single.parameter.getInferredType } else if (single.isValidTime && !single.isZero) { return InferredType.time; diff --git a/org.lflang/src/org/lflang/AstExtensions.kt b/org.lflang/src/org/lflang/AstExtensions.kt index 6a9aab0218..4222988505 100644 --- a/org.lflang/src/org/lflang/AstExtensions.kt +++ b/org.lflang/src/org/lflang/AstExtensions.kt @@ -153,7 +153,7 @@ fun Element.toText(): String = literal?.withoutQuotes()?.trim() ?: id ?: "" -fun Delay.toText(): String = parameter?.name ?: time.toText() +fun Delay.toText(): String = value.toText() /** @@ -182,12 +182,7 @@ fun VarRef.toText(): String = * @receiver The value to be converted * @return A textual representation */ -fun Value.toText(): String = - parameter?.name - ?: time?.toText() - ?: literal - ?: code?.toText() - ?: "" +fun Value.toText(): String = ASTUtils.toText(this) /** @@ -258,10 +253,11 @@ val Code.isZero: Boolean get() = this.toText().isZero * @return True if the given value denotes the constant `0`, false otherwise. */ val Value.isZero: Boolean - get() = - this.literal?.isZero - ?: this.code?.isZero - ?: false + get() = when (this) { + is Literal -> literal.isZero + is CodeExpr -> code.isZero + else -> false + } /** * Parse and return an integer from this string, much diff --git a/org.lflang/src/org/lflang/LinguaFranca.xtext b/org.lflang/src/org/lflang/LinguaFranca.xtext index 22f9e96571..87addbdd66 100644 --- a/org.lflang/src/org/lflang/LinguaFranca.xtext +++ b/org.lflang/src/org/lflang/LinguaFranca.xtext @@ -143,9 +143,9 @@ TargetDecl: ; Initializer: - parens?='(' (exprs+=Value (',' exprs+=Value)* (trailingComma?=',')?)? ')' - | braces?='{' (exprs+=Value (',' exprs+=Value)* (trailingComma?=',')?)? '}' - | assign?='=' exprs+=Value + parens?='(' (exprs+=Expr (',' exprs+=Expr)* (trailingComma?=',')?)? ')' + | braces?='{' (exprs+=Expr (',' exprs+=Expr)* (trailingComma?=',')?)? '}' + | assign?='=' exprs+=Expr ; Method: @@ -171,11 +171,7 @@ Output: // E.g. (0) or (NOW) or (NOW, ONCE) or (100, 1000) // The latter means fire with period 1000, offset 100. Timer: - 'timer' name=ID ('(' offset=Value (',' period=Value)? ')')? ';'?; - -Boolean: - TRUE | FALSE -; + 'timer' name=ID ('(' offset=Expr (',' period=Expr)? ')')? ';'?; // Action that has either a physical or logical origin. // @@ -189,7 +185,7 @@ Boolean: // the tags of two subsequently scheduled events. Action: (origin=ActionOrigin)? 'action' name=ID - ('(' minDelay=Value (',' minSpacing=Value (',' policy=STRING)? )? ')')? + ('(' minDelay=Expr (',' minSpacing=Expr (',' policy=STRING)? )? ')')? (':' type=Type)? ';'?; Reaction: @@ -205,10 +201,10 @@ TriggerRef: VarRef | startup?='startup' | shutdown?='shutdown'; Deadline: - 'deadline' '(' delay=Value ')' code=Code; + 'deadline' '(' delay=Expr ')' code=Code; STP: - 'STP' '(' value=Value ')' code=Code; + 'STP' '(' value=Expr ')' code=Code; Mutation: ('mutation') @@ -236,12 +232,8 @@ Connection: ';'? ; -// After clause with a delay that can either be specified as a parameter or as -// a literal value. If no units are given (which implies that the time interval -// must be zero), the clause must be ended with a semicolon. If units are given, -// the semicolon is optional. -Delay: // todo replace this with Value, add validator check to constrain expression to be a Time - 'after' (parameter=[Parameter] | time=Time) +Delay: + 'after' value=Expr ; // Chooses the serializer to use for the connection @@ -294,44 +286,54 @@ Assignment: Parameter: name=ID (':' type=Type)? init=Initializer?; -Sum : - terms+=Difference ('+' terms+=Difference)*; -Difference: - terms+=Product ('-' terms +=Product)*; +// fake rule used to generate an interface that Sum, Product, Atom implement +Value: Sum | Product | Atom; -Product: - terms+=Quotient ('*' terms+=Quotient)*; +Expr returns Value: Sum; -Quotient: - terms+=Expr ('/' terms += Expr)*; +Sum returns Value: + Product ({AddExpr.left=current} op=('+'|'-') right=Product)* +; -Expr : - Value | '(' Sum ')'; +Product returns Value: + Atom ({MulExpr.left=current} op=('*'|'/') right=Atom)* +; -// Time is either a reference to a parameter or an integer value, -// a number followed by a unit specification (unless the value is zero). -// If it is a constant, the validator should check that if the value -// is non-zero, it is accompanied by a unit. -Value: - parameter=[Parameter] - | time=Time - | literal=Literal - | list=ListLiteral - | parenthesizedExpr=ParenthesizedExpr - | code=Code +Atom returns Value: + ParamRef + | Time + | {Literal} literal=Literal + | ListLiteral + | TupleExpr + | {CodeExpr} code=Code ; +ParamRef: parameter=[Parameter]; + // This translates to a tuple in Python & Rust (at least). // If it's just a single expression without trailing comma, -// it's equivalent to the wrapped expression (in all targets). +// it's equivalent to the wrapped expression (in all targets) +// and no node is pushed. // (expr) = expr // (expr,) = tuple of 1 component -// () = tuple of zero components (in Rust/Caml, unit) -ParenthesizedExpr: - {ParenthesizedExpr} '(' ( items+=Value (',' items+=Value)* )? ')' +// (expr,...,expr) = tuple of n component +// () = tuple of zero components (in Rust and Caml, this is called "unit") +TupleExpr returns Value: + '(' ( + {TupleExpr} ')' // empty tuple + | Expr ( ')' // regular parenthesized expr + | {TupleExpr.items+=current} trailingComma?=',' ')' + | {TupleExpr.items+=current} (',' items+=Expr)+ trailingComma?=','? ')' + ) + ) ; + +// Time is either a reference to a parameter or an integer value, +// a number followed by a unit specification (unless the value is zero). +// If it is a constant, the validator should check that if the value +// is non-zero, it is accompanied by a unit. Time: (interval=INT unit=TimeUnit) ; @@ -394,11 +396,16 @@ SignedInt: // List literals support even empty lists, and a trailing comma. ListLiteral: - {ListLiteral} '[' ( items+=Value (',' items+=Value)* ','? )? ']' + {ListLiteral} '[' ( items+=Expr (',' items+=Expr)* ','? )? ']' ; Literal: - STRING | CHAR_LIT | SignedFloat | SignedInt | Boolean; + STRING | CHAR_LIT | SignedFloat | SignedInt | Boolean +; + +Boolean: + TRUE | FALSE +; /////////// Elementary components diff --git a/org.lflang/src/org/lflang/ModelInfo.java b/org.lflang/src/org/lflang/ModelInfo.java index 5e783fbe9a..8e8c48a7e9 100644 --- a/org.lflang/src/org/lflang/ModelInfo.java +++ b/org.lflang/src/org/lflang/ModelInfo.java @@ -41,8 +41,10 @@ import org.lflang.lf.Initializer; import org.lflang.lf.Instantiation; import org.lflang.lf.Model; +import org.lflang.lf.ParamRef; import org.lflang.lf.Parameter; import org.lflang.lf.Reactor; +import org.lflang.lf.Value; /** @@ -150,9 +152,11 @@ private void collectOverflowingNodes() { this.overflowingDeadlines.add(deadline); } - // If any of the upstream parameters overflow, report this deadline. - if (detectOverflow(new HashSet<>(), deadline.getDelay().getParameter())) { - this.overflowingDeadlines.add(deadline); + if (deadline.getDelay() instanceof ParamRef) { + // If any of the upstream parameters overflow, report this deadline. + if (detectOverflow(new HashSet<>(), ((ParamRef) deadline.getDelay()).getParameter())) { + this.overflowingDeadlines.add(deadline); + } } } } @@ -193,15 +197,14 @@ private boolean detectOverflow(Set visited, Parameter current) { // Find assignments that override the current parameter. for (var assignment : instantiation.getParameters()) { if (assignment.getLhs().equals(current)) { - Initializer init = assignment.getRhs(); - Parameter parameter = init.getExprs().get(0).getParameter(); - if (parameter != null) { + Value init = assignment.getRhs().getExprs().get(0); + if (init instanceof ParamRef) { // Check for overflow in the referenced parameter. - overflow = detectOverflow(visited, parameter) || overflow; + overflow = detectOverflow(visited, ((ParamRef) init).getParameter()) || overflow; } else { // The right-hand side of the assignment is a // constant; check whether it is too large. - if (isTooLarge(ASTUtils.getTimeValue(init.getExprs().get(0)))) { + if (isTooLarge(ASTUtils.getTimeValue(init))) { this.overflowingAssignments.add(assignment); overflow = true; } diff --git a/org.lflang/src/org/lflang/federated/FedASTUtils.java b/org.lflang/src/org/lflang/federated/FedASTUtils.java index ddd6169ae0..9210c0cca7 100644 --- a/org.lflang/src/org/lflang/federated/FedASTUtils.java +++ b/org.lflang/src/org/lflang/federated/FedASTUtils.java @@ -44,14 +44,14 @@ import org.lflang.lf.Action; import org.lflang.lf.ActionOrigin; import org.lflang.lf.Connection; -import org.lflang.lf.Delay; import org.lflang.lf.Input; import org.lflang.lf.Instantiation; import org.lflang.lf.LfFactory; -import org.lflang.lf.LfPackage; +import org.lflang.lf.ParamRef; import org.lflang.lf.Parameter; import org.lflang.lf.Reaction; import org.lflang.lf.Reactor; +import org.lflang.lf.Time; import org.lflang.lf.TimeUnit; import org.lflang.lf.Type; import org.lflang.lf.Value; @@ -86,12 +86,9 @@ public static List safe(List list) { * * @note Used in federated execution * - * @param portRef The network input port - * @param receivingPortID The ID of the receiving port * @param bankIndex The bank index of the receiving federate, or -1 if not in a bank. * @param instance The federate instance is used to keep track of all * network input ports globally - * @param parent The federated reactor * @param generator The GeneratorBase instance used to identify certain * target properties */ @@ -212,13 +209,7 @@ private static TimeValue findMaxSTP(Variable port, // If STP offset is determined, add it // If not, assume it is zero if (r.getStp() != null) { - if (r.getStp().getValue().getParameter() != null) { - List instantList = new ArrayList(); - instantList.add(instance.instantiation); - STPList.addAll(ASTUtils.initialValue((Parameter)r.getStp().getValue().getParameter(), instantList)); - } else { - STPList.add(r.getStp().getValue()); - } + addStp(STPList, r, instance.instantiation); } } // Check the children for STPs as well @@ -253,13 +244,7 @@ private static TimeValue findMaxSTP(Variable port, // If STP offset is determined, add it // If not, assume it is zero if (r.getStp() != null) { - if (r.getStp().getValue() instanceof Parameter) { - List instantList = new ArrayList(); - instantList.add(childPort.getContainer()); - STPList.addAll(ASTUtils.initialValue((Parameter)r.getStp().getValue().getParameter(), instantList)); - } else { - STPList.add(r.getStp().getValue()); - } + addStp(STPList, r, childPort.getContainer()); } } } @@ -276,6 +261,17 @@ private static TimeValue findMaxSTP(Variable port, return maxSTP; } + private static void addStp(List STPList, Reaction r, Instantiation instant) { + Value stpValue = r.getStp().getValue(); + if (stpValue instanceof ParamRef) { + List instantList = new ArrayList<>(); + instantList.add(instant); + STPList.addAll(ASTUtils.initialValue(((ParamRef) stpValue).getParameter(), instantList)); + } else { + STPList.add(stpValue); + } + } + /** * Add a network control reaction for a given output port "portRef" to the * reactions of the container reactor. This reaction will send a port absent @@ -283,7 +279,6 @@ private static TimeValue findMaxSTP(Variable port, * * @note Used in federated execution * - * @param portRef The output port * @param instance The federate instance is used to keep track of all * network input ports globally * @param receivingPortID The ID of the receiving port @@ -449,7 +444,7 @@ public static void makeCommunication( action.setType(action_type); } - TimeValue delayValue = mainInstance.resolveTimeValue(delayAsValue(connection.getDelay())); + TimeValue delayValue = mainInstance.resolveTimeValue(connection.getDelay().getValue()); // The connection is 'physical' if it uses the ~> notation. if (connection.isPhysical()) { @@ -461,10 +456,10 @@ public static void makeCommunication( // provided using after is enforced by setting // the minDelay. if (connection.getDelay() != null) { - action.setMinDelay(factory.createValue()); - action.getMinDelay().setTime(factory.createTime()); - action.getMinDelay().getTime().setInterval((int) delayValue.time); - action.getMinDelay().getTime().setUnit(delayValue.unit); + Time time = factory.createTime(); + time.setInterval((int) delayValue.time); + time.setUnit(delayValue.unit); + action.setMinDelay(time); } } else { // If the connection is logical but coordination @@ -561,14 +556,4 @@ public static void makeCommunication( parent.getReactions().add(r2); } - private static Value delayAsValue(Delay delay) { - Value value = LfPackage.eINSTANCE.getLfFactory().createValue(); - if (delay.getParameter() != null) { - value.setParameter(delay.getParameter()); - } else { - value.setTime(delay.getTime()); - } - return value; - } - } diff --git a/org.lflang/src/org/lflang/generator/GeneratorBase.xtend b/org.lflang/src/org/lflang/generator/GeneratorBase.xtend index d44b301cd5..711452d757 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorBase.xtend +++ b/org.lflang/src/org/lflang/generator/GeneratorBase.xtend @@ -73,6 +73,7 @@ import org.lflang.lf.Instantiation import org.lflang.lf.LfFactory import org.lflang.lf.Model import org.lflang.lf.Parameter +import org.lflang.lf.ParamRef import org.lflang.lf.Port import org.lflang.lf.Reaction import org.lflang.lf.Reactor @@ -1315,7 +1316,7 @@ abstract class GeneratorBase extends AbstractLFValidator { } return init?.exprs.map[i| - if (i.parameter !== null) { + if (i instanceof ParamRef) { i.parameter.targetReference } else if (type.isTime) { i.targetTime @@ -1425,11 +1426,7 @@ abstract class GeneratorBase extends AbstractLFValidator { * @return An RTI-compatible (ie. C target) time string */ protected def getRTITime(Delay d) { - if (d.parameter !== null) { - return d.toText - } - - return d.time.toTimeValue.timeInTargetLanguage + return d.value.targetTime } /** Analyze the resource (the .lf file) that is being parsed @@ -1779,8 +1776,8 @@ abstract class GeneratorBase extends AbstractLFValidator { * @return A time string in the target language */ protected def getTargetValue(Value v) { - if (v.time !== null) { - return v.time.targetTime + if (v instanceof Time) { + return v.targetTime } return v.toText } @@ -1793,16 +1790,12 @@ abstract class GeneratorBase extends AbstractLFValidator { * @param v A time AST node * @return A time string in the target language */ - protected def getTargetTime(Value v) { - v.timeValue?.timeInTargetLanguage ?: v.toText + protected def String getTargetTime(Value v) { + v.timeValue.timeInTargetLanguage } - protected def getTargetTime(Delay d) { - if (d.parameter !== null) { - return d.toText - } else { - return d.time.toTimeValue.timeInTargetLanguage - } + protected def String getTargetTime(Delay d) { + d.value.targetTime } /** diff --git a/org.lflang/src/org/lflang/generator/ParameterInstance.xtend b/org.lflang/src/org/lflang/generator/ParameterInstance.xtend index ce469e44bf..6f9a08d75d 100644 --- a/org.lflang/src/org/lflang/generator/ParameterInstance.xtend +++ b/org.lflang/src/org/lflang/generator/ParameterInstance.xtend @@ -31,6 +31,7 @@ import org.lflang.InferredType import org.lflang.lf.LfFactory import org.lflang.lf.Initializer import org.lflang.lf.Parameter +import org.lflang.lf.ParamRef import static extension org.lflang.ASTUtils.* @@ -70,25 +71,25 @@ class ParameterInstance extends NamedInstance { while (assignment !== null) { // NOTE: we only allow a reference to single a parameter or // a list of ordinary values. - val ref = assignment.rhs.asSingleValue?.parameter - if (ref !== null) { + val ref = assignment.rhs.asSingleValue + if (ref instanceof ParamRef) { // Get the value from the parameter instance, not the parameter // so that overrides like bank_index work. // parent.parent will be non-null or the rhs parameter reference // would not have passed validation. Nevertheless, we check. val parentsParent = parent.parent; if (parentsParent !== null) { - val parameterInstance = parentsParent.parameters.findFirst[it.name.equals(ref.name)] + val parameterInstance = parentsParent.parameters.findFirst[it.name.equals(ref.parameter.name)] // Again, this result should be non-null, but we check. if (parameterInstance !== null) { this.init = parameterInstance.init } else { // Fall back on reference. - this.init = ref.init + this.init = ref.parameter.init } } else { // Fall back on reference. - this.init = ref.init + this.init = ref.parameter.init } } else { this.init = assignment.rhs @@ -103,7 +104,7 @@ class ParameterInstance extends NamedInstance { // If the parent is in a bank and the parameter name is "bank_index", then // override the default value provided to make it equal to the bank index. if (parent.bankIndex >= 0 && definition.name.equals("bank_index")) { - val value = LfFactory.eINSTANCE.createValue + val value = LfFactory.eINSTANCE.createLiteral value.literal = "" + parent.bankIndex this.init = LfFactory.eINSTANCE.createInitializer diff --git a/org.lflang/src/org/lflang/generator/PythonGenerator.xtend b/org.lflang/src/org/lflang/generator/PythonGenerator.xtend index 20757bae21..f3c915ec16 100644 --- a/org.lflang/src/org/lflang/generator/PythonGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/PythonGenerator.xtend @@ -45,9 +45,11 @@ import org.lflang.lf.Action import org.lflang.lf.Input import org.lflang.lf.Initializer import org.lflang.lf.Instantiation +import org.lflang.lf.ListLiteral import org.lflang.lf.Model import org.lflang.lf.Output import org.lflang.lf.Port +import org.lflang.lf.ParamRef import org.lflang.lf.Reaction import org.lflang.lf.Reactor import org.lflang.lf.ReactorDecl @@ -184,10 +186,10 @@ class PythonGenerator extends CGenerator { // Parameters in Python are always prepended with a 'self.' // predicate. Therefore, we need to append the returned value // if it is a parameter. - if (v.parameter !== null) { - return "self." + super.getTargetValue(v); - } else if (v.list !== null) { - return "[" + v.list.items.join(', ', [it.pythonTargetValue]) + "]" + if (v instanceof ParamRef) { + return "self." + v.parameter.name; + } else if (v instanceof ListLiteral) { + return "[" + v.items.join(', ', [it.pythonTargetValue]) + "]" } switch(v.toText) { diff --git a/org.lflang/src/org/lflang/generator/ReactorInstance.xtend b/org.lflang/src/org/lflang/generator/ReactorInstance.xtend index 40d1a7a4fb..d3d5fee5af 100644 --- a/org.lflang/src/org/lflang/generator/ReactorInstance.xtend +++ b/org.lflang/src/org/lflang/generator/ReactorInstance.xtend @@ -43,6 +43,7 @@ import org.lflang.lf.Input import org.lflang.lf.Instantiation import org.lflang.lf.Output import org.lflang.lf.Parameter +import org.lflang.lf.ParamRef import org.lflang.lf.Port import org.lflang.lf.Reaction import org.lflang.lf.Reactor @@ -331,9 +332,8 @@ class ReactorInstance extends NamedInstance { * null or code is invalid. */ def TimeValue resolveTimeValue(Value v) { - val parm = v?.parameter - if (parm !== null) { - return this.lookupParameterInstance(parm).init.asSingleValue?.getTimeValue + if (v instanceof ParamRef) { + return this.lookupParameterInstance(v.parameter).init.asSingleValue?.getTimeValue } else { return v?.timeValue } diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend index 1d6ffe72fc..0581a2f4ca 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend @@ -78,6 +78,8 @@ import org.lflang.lf.Instantiation import org.lflang.lf.Model import org.lflang.lf.Output import org.lflang.lf.Port +import org.lflang.lf.ParamRef +import org.lflang.lf.Delay import org.lflang.lf.Reaction import org.lflang.lf.Reactor import org.lflang.lf.ReactorDecl @@ -1511,7 +1513,7 @@ class CGenerator extends GeneratorBase { pr(rtiCode, ''' candidate_tmp = FOREVER; ''') - for (delay : delays) { + for (Delay delay : delays) { if (delay === null) { // Use NEVER to encode no delay at all. pr(rtiCode, ''' @@ -1519,10 +1521,6 @@ class CGenerator extends GeneratorBase { ''') } else { var delayTime = delay.getTargetTime - if (delay.parameter !== null) { - // The delay is given as a parameter reference. Find its value. - delayTime = ASTUtils.getInitialTimeValue(delay.parameter).timeInTargetLanguage - } pr(rtiCode, ''' if («delayTime» < candidate_tmp) { candidate_tmp = «delayTime»; @@ -4386,7 +4384,7 @@ class CGenerator extends GeneratorBase { var list = new LinkedList(); for (i : init.exprs) { - if (i.parameter !== null) { + if (i instanceof ParamRef) { list.add(parent.selfStructName + "->" + i.parameter.name) } else if (t.isTime) { list.add(i.targetTime) diff --git a/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt b/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt index 0386463303..657e986b0f 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt @@ -94,10 +94,10 @@ fun Time.toCode() = TimeValue(this.interval.toLong(), this.unit).toCode() * This should be set to false if called from code generators for the inner class. */ fun Value.toTime(outerContext: Boolean = false): String = when { - this.time != null -> this.time.toCode() - this.isZero -> TimeValue(0, TimeUnit.NONE).toCode() - outerContext && this.parameter != null -> "__lf_inner.${parameter.name}" - else -> this.toText() + this is Time -> this.toCode() + this.isZero -> TimeValue.ZERO.toCode() + outerContext && this is ParamRef -> "__lf_inner.${parameter.name}" + else -> this.toText() } /** @@ -106,7 +106,7 @@ fun Value.toTime(outerContext: Boolean = false): String = when { * If the value evaluates to 0, it is interpreted as a normal value. * FIXME this is redundant to GeneratorBase.getTargetValue */ -fun Value.toCode(): String = this.time?.toCode() ?: this.toText() +fun Value.toCode(): String = (this as? Time)?.toCode() ?: this.toText() /** Get the textual representation of a width in C++ code */ fun WidthSpec.toCode(): String = terms.joinToString(" + ") { diff --git a/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt index d4babcdf71..7cb597463f 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt @@ -25,6 +25,7 @@ package org.lflang.generator.cpp import org.lflang.isOfTimeType +import org.lflang.lf.ParamRef import org.lflang.lf.Reactor import org.lflang.lf.StateVar @@ -38,9 +39,9 @@ class CppStateGenerator(private val reactor: Reactor) { */ private fun getInitializerList(state: StateVar) = state.init.exprs.map { when { - it.parameter != null -> it.parameter.name - state.isOfTimeType -> it.toTime() - else -> it.toCode() + it is ParamRef -> it.parameter.name + state.isOfTimeType -> it.toTime() + else -> it.toCode() } } diff --git a/org.lflang/src/org/lflang/generator/ts/TSActionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSActionGenerator.kt index d3bb1a2f0a..8e5e670d76 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSActionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSActionGenerator.kt @@ -1,6 +1,7 @@ package org.lflang.generator.ts import org.lflang.lf.Action +import org.lflang.lf.ParamRef import org.lflang.lf.Type import org.lflang.lf.Value import java.util.* @@ -63,11 +64,7 @@ class TSActionGenerator ( if (action.minDelay != null) { // Actions in the TypeScript target are constructed // with an optional minDelay argument which defaults to 0. - if (action.minDelay.parameter != null) { - actionArgs+= ", " + action.minDelay.parameter.name - } else { - actionArgs+= ", " + action.minDelay.getTargetValue() - } + actionArgs += ", " + action.minDelay.getTargetValue() } actionInstantiations.add( "this.${action.name} = new __Action<${getActionType(action)}>($actionArgs);") @@ -75,4 +72,4 @@ class TSActionGenerator ( } return actionInstantiations.joinToString("\n") } -} \ No newline at end of file +} diff --git a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt index 1e73e390f9..aa50b65e73 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt @@ -77,10 +77,11 @@ class TSReactionGenerator( reactSignature: StringJoiner ): String { var deadlineArgs = "" - if (reaction.deadline.delay.parameter != null) { - deadlineArgs += "this.${reaction.deadline.delay.parameter.name}.get()"; + val delay = reaction.deadline.delay + if (delay is ParamRef) { + deadlineArgs += "this.${delay.parameter.name}.get()" } else { - deadlineArgs += reaction.deadline.delay.getTargetValue() + deadlineArgs += delay.getTargetValue() } return with(PrependOperator) { @@ -378,4 +379,4 @@ class TSReactionGenerator( } return reactionCodes.joinToString("\n") } -} \ No newline at end of file +} diff --git a/org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend b/org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend index db9a7edc11..5c0a85e2d6 100644 --- a/org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend +++ b/org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend @@ -53,6 +53,7 @@ import org.lflang.lf.Action import org.lflang.lf.ActionOrigin import org.lflang.lf.Assignment import org.lflang.lf.Connection +import org.lflang.lf.CodeExpr import org.lflang.lf.Deadline import org.lflang.lf.Host import org.lflang.lf.IPV4Host @@ -65,6 +66,7 @@ import org.lflang.lf.Instantiation import org.lflang.lf.KeyValuePair import org.lflang.lf.KeyValuePairs import org.lflang.lf.LfPackage.Literals +import org.lflang.lf.Literal import org.lflang.lf.ListLiteral import org.lflang.lf.Model import org.lflang.lf.NamedHost @@ -72,6 +74,7 @@ import org.lflang.lf.Output import org.lflang.lf.Parameter import org.lflang.lf.Port import org.lflang.lf.Preamble +import org.lflang.lf.ParamRef import org.lflang.lf.Reaction import org.lflang.lf.Reactor import org.lflang.lf.Serializer @@ -80,6 +83,7 @@ import org.lflang.lf.StateVar import org.lflang.lf.TargetDecl import org.lflang.lf.TimeUnit import org.lflang.lf.Timer +import org.lflang.lf.TupleExpr import org.lflang.lf.Type import org.lflang.lf.TypedVariable import org.lflang.lf.Value @@ -283,6 +287,9 @@ class LFValidatorImpl extends AbstractLFValidator { @Check(FAST) def checkAction(Action action) { checkName(action.name, Literals.VARIABLE__NAME) + checkValueIsTime(action.minDelay, Literals.ACTION__MIN_DELAY) + checkValueIsTime(action.minSpacing, Literals.ACTION__MIN_SPACING) + if (action.origin == ActionOrigin.NONE) { error( "Action must have modifier `logical` or `physical`.", @@ -308,21 +315,18 @@ class LFValidatorImpl extends AbstractLFValidator { } else { val v = assignment.rhs.asSingleValue if (!v.isValidTime) { - if (v.parameter === null) { + if (v instanceof ParamRef) { + // This is a reference to another parameter. Report problem. + error("Cannot assign parameter: " + v.parameter.name + + " to " + assignment.lhs.name + + ". The latter is a time parameter, but the former is not.", + Literals.ASSIGNMENT__RHS) + } else { // This is a value. Check that units are present. - error( - "Invalid time units: " + assignment.rhs + - ". Should be one of " + TimeUnit.VALUES.filter [ + error("Invalid time units: " + assignment.rhs + + ". Should be one of " + TimeUnit.VALUES.filter [ it != TimeUnit.NONE ], Literals.ASSIGNMENT__RHS) - } else { - // This is a reference to another parameter. Report problem. - error( - "Cannot assign parameter: " + - v.parameter.name + " to " + - assignment.lhs.name + - ". The latter is a time parameter, but the former is not.", - Literals.ASSIGNMENT__RHS) } } } @@ -377,21 +381,22 @@ class LFValidatorImpl extends AbstractLFValidator { } @Check(FAST) - def checkTupleLiteral(ParenthesizedExpr expr) { + def checkTupleLiteral(TupleExpr expr) { if (expr.items.size == 1 && !expr.isTrailingComma) { // this is allowed in all targets return; } if (!target.supportsLfTupleLiterals()) { if (expr.items.size == 1) - error("Target " + target + " does not support tuple literals. You might want to remove the trailing comma.", Literals.PARENTHESIZED_EXPR__ITEMS) + error("Target " + target + " does not support tuple literals. You might want to remove the trailing comma.", Literals.TUPLE_EXPR__ITEMS) else - error("Target " + target + " does not support tuple literals.", Literals.PARENTHESIZED_EXPR__ITEMS) + error("Target " + target + " does not support tuple literals.", Literals.TUPLE_EXPR__ITEMS) } } @Check(FAST) def checkConnection(Connection connection) { + checkValueIsTime(connection.delay?.value, Literals.CONNECTION__DELAY) // Report if connection is part of a cycle. for (cycle : this.info.topologyGraph.cycles) { @@ -575,6 +580,8 @@ class LFValidatorImpl extends AbstractLFValidator { @Check(FAST) def checkDeadline(Deadline deadline) { + checkValueIsTime(deadline.delay, Literals.DEADLINE__DELAY) + if (isCBasedTarget && this.info.overflowingDeadlines.contains(deadline)) { error( @@ -583,8 +590,11 @@ class LFValidatorImpl extends AbstractLFValidator { Literals.DEADLINE__DELAY) } } -@Check(FAST) + + @Check(FAST) def checkSTPOffset(STP stp) { + checkValueIsTime(stp.value, Literals.STP__VALUE) + if (isCBasedTarget && this.info.overflowingDeadlines.contains(stp)) { error( @@ -761,7 +771,7 @@ class LFValidatorImpl extends AbstractLFValidator { def checkParameter(Parameter param) { checkName(param.name, Literals.PARAMETER__NAME) - if (param.init.exprs.exists[it.parameter !== null]) { + if (param.init.exprs.exists[it instanceof ParamRef]) { // Initialization using parameters is forbidden. error("Parameter cannot be initialized using parameter.", Literals.PARAMETER__INIT) @@ -771,32 +781,10 @@ class LFValidatorImpl extends AbstractLFValidator { // All parameters must be initialized. error("Uninitialized parameter.", Literals.PARAMETER__INIT) } else if (param.isOfTimeType) { - // We do additional checks on types because we can make stronger - // assumptions about them. - - // If the parameter is not a list, cannot be initialized - // using a one. - if (param.init.exprs.size > 1 && param.type.arraySpec === null) { - error("Time parameter cannot be initialized using a list.", - Literals.PARAMETER__INIT) - } else { - // The parameter is a singleton time. - val init = param.init.exprs.get(0) - if (init.time === null) { - if (init !== null && !init.isZero) { - if (init.isInteger) { - error("Missing time units. Should be one of " + - TimeUnit.VALUES.filter [ - it != TimeUnit.NONE - ], Literals.PARAMETER__INIT) - } else { - error("Invalid time literal.", - Literals.PARAMETER__INIT) - } - } - } // If time is not null, we know that a unit is also specified. - } - } else if (this.target.requiresTypes) { + checkValueIsTime(param.init, Literals.PARAMETER__INIT) + } + + if (this.target.requiresTypes) { // Report missing target type. if (param.inferredType.isUndefined()) { error("Type declaration missing.", Literals.PARAMETER__TYPE) @@ -1173,31 +1161,7 @@ class LFValidatorImpl extends AbstractLFValidator { if (stateVar.isOfTimeType) { // If the state is declared to be a time, // make sure that it is initialized correctly. - if (stateVar.init !== null) { - for (init : stateVar.init.exprs) { - if (stateVar.type !== null && stateVar.type.isTime && - !init.isValidTime) { - if (stateVar.isParameterized) { - error( - "Referenced parameter does not denote a time.", - Literals.STATE_VAR__INIT) - } else { - if (init !== null && !init.isZero) { - if (init.isInteger) { - error( - "Missing time units. Should be one of " + - TimeUnit.VALUES.filter [ - it != TimeUnit.NONE - ], Literals.STATE_VAR__INIT) - } else { - error("Invalid time literal.", - Literals.STATE_VAR__INIT) - } - } - } - } - } - } + checkValueIsTime(stateVar.init, Literals.STATE_VAR__INIT) } else if (this.target.requiresTypes && stateVar.inferredType.isUndefined) { // Report if a type is missing error("State must have a type.", Literals.STATE_VAR__TYPE) @@ -1206,7 +1170,7 @@ class LFValidatorImpl extends AbstractLFValidator { if (isCBasedTarget && stateVar.init.isList) { // In C, if initialization is done with a list, elements cannot // refer to parameters. - if (stateVar.init.exprs.exists[it.parameter !== null]) { + if (stateVar.init.exprs.exists[it instanceof ParamRef]) { error("List items cannot refer to a parameter.", Literals.STATE_VAR__INIT) } @@ -1293,41 +1257,46 @@ class LFValidatorImpl extends AbstractLFValidator { } } - @Check(FAST) - def checkValueAsTime(Value value) { - val container = value.eContainer + def void checkValueIsTime(Initializer init, EStructuralFeature feature) { + if (init === null) return; - if (container instanceof Timer || container instanceof Action || - container instanceof Connection || container instanceof Deadline) { + if (init.exprs.size != 1) { + error("Expected exactly one time value.", feature) + } else { + checkValueIsTime(init.asSingleValue, feature) + } + } - // If parameter is referenced, check that it is of the correct type. - if (value.parameter !== null) { - if (!value.parameter.isOfTimeType && target.requiresTypes === true) { - error("Parameter is not of time type", - Literals.VALUE__PARAMETER) - } - } else if (value.time === null) { - if (value.literal !== null && !value.literal.isZero) { - if (value.literal.isInteger) { - error("Missing time units. Should be one of " + - TimeUnit.VALUES.filter [ - it != TimeUnit.NONE - ], Literals.VALUE__LITERAL) - } else { - error("Invalid time literal.", - Literals.VALUE__LITERAL) - } - } else if (value.code !== null && !value.code.isZero) { - if (value.code.isInteger) { - error("Missing time units. Should be one of " + - TimeUnit.VALUES.filter [ - it != TimeUnit.NONE - ], Literals.VALUE__CODE) - } else { - error("Invalid time literal.", - Literals.VALUE__CODE) - } - } + def void checkValueIsTime(Value v, EStructuralFeature feature) { + if (v === null) return; + + val value = v.peelParens + + if (value instanceof ParamRef) { + if (!value.parameter.isOfTimeType && target.requiresTypes) { + error("Referenced parameter does not have time type.", feature) + } + } else if (value instanceof Literal) { + if (value.literal.isZero) return; + + if (value.literal.isInteger) { + error("Missing time units. Should be one of " + + TimeUnit.VALUES.filter [ + it != TimeUnit.NONE + ], feature) + } else { + error("Invalid time literal.", feature) + } + } else if (value instanceof CodeExpr) { + if (value.code.isZero) return; + + if (value.code.isInteger) { + error("Missing time units. Should be one of " + + TimeUnit.VALUES.filter [ + it != TimeUnit.NONE + ], feature) + } else { + error("Invalid time literal.", feature) } } } @@ -1335,6 +1304,8 @@ class LFValidatorImpl extends AbstractLFValidator { @Check(FAST) def checkTimer(Timer timer) { checkName(timer.name, Literals.VARIABLE__NAME) + checkValueIsTime(timer.offset, Literals.TIMER__OFFSET) + checkValueIsTime(timer.period, Literals.TIMER__PERIOD) } @Check(FAST) From ec1d7d80b17e6c11ac9c29c93b74aab293ffd054 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 14 Oct 2021 22:57:07 +0200 Subject: [PATCH 11/68] Add tests --- .../compiler/LinguaFrancaValidationTest.xtend | 17 +++++++- org.lflang/src/org/lflang/ASTUtils.xtend | 6 +-- org.lflang/src/org/lflang/LinguaFranca.xtext | 6 +-- .../lflang/generator/PythonGenerator.xtend | 24 ++++++----- .../lflang/validation/LFValidatorImpl.xtend | 31 +++++--------- test/Python/src/NativeExpressions.lf | 40 +++++++++++++++++++ 6 files changed, 87 insertions(+), 37 deletions(-) create mode 100644 test/Python/src/NativeExpressions.lf diff --git a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend index eecd3ab6d3..80c7fa656c 100644 --- a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend +++ b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend @@ -674,9 +674,22 @@ class LinguaFrancaValidationTest { target C; reactor Contained { state x: int[]([1, 2]); + state y: int[] = [1, 2]; } - ''').assertError(LfPackage::eINSTANCE.listLiteral, - null, 'Target C does not support LF list literals') + ''').assertError(LfPackage::eINSTANCE.listExpr, + null, 'Target C does not support list literals.') + } + + + @Test + def void forbidTupleLiterals() { + parseWithoutError(''' + target C; // in C + reactor Contained { + state x: int[] = (1, 2); + } + ''').assertError(LfPackage::eINSTANCE.tupleExpr, + null, 'Target C does not support tuple literals.') } diff --git a/org.lflang/src/org/lflang/ASTUtils.xtend b/org.lflang/src/org/lflang/ASTUtils.xtend index 5ee64f72e1..cadab5fef8 100644 --- a/org.lflang/src/org/lflang/ASTUtils.xtend +++ b/org.lflang/src/org/lflang/ASTUtils.xtend @@ -55,7 +55,7 @@ import org.lflang.lf.Initializer import org.lflang.lf.Instantiation import org.lflang.lf.LfFactory import org.lflang.lf.Literal -import org.lflang.lf.ListLiteral +import org.lflang.lf.ListExpr import org.lflang.lf.Model import org.lflang.lf.Output import org.lflang.lf.Parameter @@ -779,7 +779,7 @@ class ASTUtils { ParamRef: v.parameter.name Time: v.toText Literal: v.literal - ListLiteral: v.items.join(',', '[', ']', [ it.toText ]) + ListExpr: v.items.join(',', '[', ']', [ it.toText ]) TupleExpr: { val end = v.isTrailingComma ? ",)" : ")" v.items.join(',', '(', end, [ it.toText ]) @@ -1089,7 +1089,7 @@ class ASTUtils { */ def static boolean isList(Initializer init) { return (init.isBraces || init.isParens) && init.exprs.size != 1 - // || init.isAssign && init.asSingleValue instanceof ListLiteral + // || init.isAssign && init.asSingleValue instanceof ListExpr } /** diff --git a/org.lflang/src/org/lflang/LinguaFranca.xtext b/org.lflang/src/org/lflang/LinguaFranca.xtext index 87addbdd66..a9e46c93bb 100644 --- a/org.lflang/src/org/lflang/LinguaFranca.xtext +++ b/org.lflang/src/org/lflang/LinguaFranca.xtext @@ -304,7 +304,7 @@ Atom returns Value: ParamRef | Time | {Literal} literal=Literal - | ListLiteral + | ListExpr | TupleExpr | {CodeExpr} code=Code ; @@ -395,8 +395,8 @@ SignedInt: // List literals support even empty lists, and a trailing comma. -ListLiteral: - {ListLiteral} '[' ( items+=Expr (',' items+=Expr)* ','? )? ']' +ListExpr: + {ListExpr} '[' ( items+=Expr (',' items+=Expr)* ','? )? ']' ; Literal: diff --git a/org.lflang/src/org/lflang/generator/PythonGenerator.xtend b/org.lflang/src/org/lflang/generator/PythonGenerator.xtend index f3c915ec16..474087fcd6 100644 --- a/org.lflang/src/org/lflang/generator/PythonGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/PythonGenerator.xtend @@ -45,7 +45,7 @@ import org.lflang.lf.Action import org.lflang.lf.Input import org.lflang.lf.Initializer import org.lflang.lf.Instantiation -import org.lflang.lf.ListLiteral +import org.lflang.lf.ListExpr import org.lflang.lf.Model import org.lflang.lf.Output import org.lflang.lf.Port @@ -54,6 +54,7 @@ import org.lflang.lf.Reaction import org.lflang.lf.Reactor import org.lflang.lf.ReactorDecl import org.lflang.lf.TriggerRef +import org.lflang.lf.TupleExpr import org.lflang.lf.Value import org.lflang.lf.VarRef @@ -188,8 +189,14 @@ class PythonGenerator extends CGenerator { // if it is a parameter. if (v instanceof ParamRef) { return "self." + v.parameter.name; - } else if (v instanceof ListLiteral) { - return "[" + v.items.join(', ', [it.pythonTargetValue]) + "]" + } else if (v instanceof ListExpr) { + return v.items.join("[", ", ", "]", [it.pythonTargetValue]) + } else if (v instanceof TupleExpr) { + if (v.items.isEmpty) + return "()" + else + // notice the closing delimiter is ",)", + return v.items.join("(", ", ", ",)", [it.pythonTargetValue]) } switch(v.toText) { @@ -204,16 +211,15 @@ class PythonGenerator extends CGenerator { if (init === null) { return "None" } if (init.isParens && (init.exprs.size != 1 || init.isTrailingComma)) { + // corresponds to a tuple: // state foo(1,2) # tuple w/ 2 components // state foo(1,) # tuple w/ 1 component // state foo() # tuple w/ 0 component - if (init.exprs.size == 1) { - // python tuple of size 1 - return "(" + init.asSingleValue.pythonTargetValue + ",)" - } - // regular python tuple (may also have size zero) - return init.exprs.join('(', ', ', ')', [it.pythonTargetValue]) + return if (init.exprs.size == 0) "()" + else if (init.exprs.size == 1) "(" + init.asSingleValue.pythonTargetValue + ",)" + else init.exprs.join('(', ', ', ')', [it.pythonTargetValue]) + } else if ((init.isAssign || init.isParens) && init.exprs.size == 1) { return init.asSingleValue.getPythonTargetValue } else { diff --git a/org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend b/org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend index 5c0a85e2d6..96995eaf92 100644 --- a/org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend +++ b/org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend @@ -67,7 +67,7 @@ import org.lflang.lf.KeyValuePair import org.lflang.lf.KeyValuePairs import org.lflang.lf.LfPackage.Literals import org.lflang.lf.Literal -import org.lflang.lf.ListLiteral +import org.lflang.lf.ListExpr import org.lflang.lf.Model import org.lflang.lf.NamedHost import org.lflang.lf.Output @@ -82,6 +82,7 @@ import org.lflang.lf.STP import org.lflang.lf.StateVar import org.lflang.lf.TargetDecl import org.lflang.lf.TimeUnit +import org.lflang.lf.Time import org.lflang.lf.Timer import org.lflang.lf.TupleExpr import org.lflang.lf.Type @@ -374,17 +375,16 @@ class LFValidatorImpl extends AbstractLFValidator { } @Check(FAST) - def checkListLiteral(ListLiteral list) { + def checkListExpr(ListExpr list) { if (!target.supportsLfListLiterals()) { - error("Target " + target + " does not support LF list literals", Literals.LIST_LITERAL__ITEMS) + error("Target " + target + " does not support list literals.", Literals.LIST_EXPR__ITEMS) } } @Check(FAST) def checkTupleLiteral(TupleExpr expr) { if (expr.items.size == 1 && !expr.isTrailingComma) { - // this is allowed in all targets - return; + throw new AssertionError("this parenthesized expr should not have been parsed as a tuple") } if (!target.supportsLfTupleLiterals()) { if (expr.items.size == 1) @@ -1276,29 +1276,20 @@ class LFValidatorImpl extends AbstractLFValidator { if (!value.parameter.isOfTimeType && target.requiresTypes) { error("Referenced parameter does not have time type.", feature) } + return; } else if (value instanceof Literal) { if (value.literal.isZero) return; if (value.literal.isInteger) { - error("Missing time units. Should be one of " + - TimeUnit.VALUES.filter [ - it != TimeUnit.NONE - ], feature) - } else { - error("Invalid time literal.", feature) + error("Missing time units. Should be one of " + TimeUnit.VALUES.filter[it != TimeUnit.NONE], feature) } } else if (value instanceof CodeExpr) { if (value.code.isZero) return; - - if (value.code.isInteger) { - error("Missing time units. Should be one of " + - TimeUnit.VALUES.filter [ - it != TimeUnit.NONE - ], feature) - } else { - error("Invalid time literal.", feature) - } + error("Invalid time literal.", feature) } + + if (!(value instanceof Time)) + error("Invalid time literal.", feature) } @Check(FAST) diff --git a/test/Python/src/NativeExpressions.lf b/test/Python/src/NativeExpressions.lf new file mode 100644 index 0000000000..d1d5e1d328 --- /dev/null +++ b/test/Python/src/NativeExpressions.lf @@ -0,0 +1,40 @@ +// This example illustrates state variables and parameters on the wiki. +// For this test, success is just compiling and running. +target Python; + + +reactor Sub(seq([1,2])) { + +} + +main reactor { + state t0 = (); // empty tuple + state t02(); // empty tuple + + state t1 = (1,); // tuple of 1 + state t12(1,); // tuple of 1 + + state t2 = (1,2); // tuple of 2 + state t22(1,2); // tuple of 2 + + state justin = (1); // just 1 + state justin2(1); // just 1 + + sub = new Sub(seq = ()); + sub2 = new Sub(seq = (1,)); + sub3 = new Sub(seq = (1, 2)); + sub4 = new Sub(seq((1, 2,))); + + reaction(startup) {= + assert self.t0 == () + assert self.t02 == () + assert self.t1 == (1,) + assert self.t12 == (1,) + assert self.t2 == (1,2) + assert self.t22 == (1,2) + assert self.justin == 1 + assert self.justin2 == 1 + + print("Success") + =} +} From 9a8b6e7c4fb02d76a3255d65e92d9f2d3ffceaea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 14 Oct 2021 23:09:20 +0200 Subject: [PATCH 12/68] Remove peelParens --- org.lflang/src/org/lflang/ASTUtils.xtend | 12 ------------ .../src/org/lflang/validation/LFValidatorImpl.xtend | 6 ++---- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/org.lflang/src/org/lflang/ASTUtils.xtend b/org.lflang/src/org/lflang/ASTUtils.xtend index cadab5fef8..0b372fe5c4 100644 --- a/org.lflang/src/org/lflang/ASTUtils.xtend +++ b/org.lflang/src/org/lflang/ASTUtils.xtend @@ -793,18 +793,6 @@ class ASTUtils { d.value.toText } - /** Remove parentheses around a single expression. */ - def static Value peelParens(Value value) { - // fixme remove this - var v = value - while (v instanceof TupleExpr - && (v as TupleExpr).items.size == 1 - && !(v as TupleExpr).isTrailingComma) { - throw new AssertionError("grammar should prevent this") - } - v - } - /** * Return a string of the form either "name" or "container.name" depending * on in which form the variable reference was given. diff --git a/org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend b/org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend index 96995eaf92..ae72f48a64 100644 --- a/org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend +++ b/org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend @@ -1267,10 +1267,8 @@ class LFValidatorImpl extends AbstractLFValidator { } } - def void checkValueIsTime(Value v, EStructuralFeature feature) { - if (v === null) return; - - val value = v.peelParens + def void checkValueIsTime(Value value, EStructuralFeature feature) { + if (value === null) return; if (value instanceof ParamRef) { if (!value.parameter.isOfTimeType && target.requiresTypes) { From a8cf467aa57bc0726b9089984291a784b149594e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 14 Oct 2021 23:31:28 +0200 Subject: [PATCH 13/68] Fix checking of types --- org.lflang/src/org/lflang/ASTUtils.xtend | 15 ++++++++ .../lflang/validation/LFValidatorImpl.xtend | 35 +++++++++++++++++-- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/org.lflang/src/org/lflang/ASTUtils.xtend b/org.lflang/src/org/lflang/ASTUtils.xtend index 0b372fe5c4..ce7aed32a1 100644 --- a/org.lflang/src/org/lflang/ASTUtils.xtend +++ b/org.lflang/src/org/lflang/ASTUtils.xtend @@ -1114,6 +1114,21 @@ class ASTUtils { return TimeValue.ZERO //fixme doesn't look right. } } + + /** + * Given an initializer that is known to be of a list type + * (because of a type annotation), return the components of + * the list. Return null if the initializer is not a list. + */ + def static List initializerAsList(Initializer init) { + return if (init.isAssign) { + val list = init.asSingleValue + if (list instanceof ListExpr) list.items + else null + } else { + init.exprs + } + } /** * Given a parameter, return its initial value. diff --git a/org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend b/org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend index ae72f48a64..09457a84f4 100644 --- a/org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend +++ b/org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend @@ -45,6 +45,7 @@ import org.eclipse.xtext.validation.ValidationMessageAcceptor; import org.lflang.ErrorReporter import org.lflang.FileConfig +import org.lflang.InferredType import org.lflang.ModelInfo import org.lflang.Target import org.lflang.TargetProperty @@ -780,8 +781,8 @@ class LFValidatorImpl extends AbstractLFValidator { if (param.init === null || param.init.exprs.size == 0) { // All parameters must be initialized. error("Uninitialized parameter.", Literals.PARAMETER__INIT) - } else if (param.isOfTimeType) { - checkValueIsTime(param.init, Literals.PARAMETER__INIT) + } else { + checkType(param.init, param.inferredType, Literals.PARAMETER__INIT) } if (this.target.requiresTypes) { @@ -1257,6 +1258,36 @@ class LFValidatorImpl extends AbstractLFValidator { } } + def void checkType(Initializer init, InferredType type, EStructuralFeature feature) { + if (init === null) return; + + if (type.isTime) { + if (type.isList) { + // list of times + val exprs = if (init.isAssign) { + val list = init.asSingleValue + if (list instanceof ListExpr) list.items + else if (list instanceof CodeExpr) return // cannot check it + else { + error("Expected a list of time values.", feature) + return + } + } else { + init.exprs + } + for (component: exprs) { + checkValueIsTime(component, feature) + } + } else { + if (init.exprs.size != 1) { + error("Expected exactly one time value.", feature) + } else { + checkValueIsTime(init.asSingleValue, feature) + } + } + } + } + def void checkValueIsTime(Initializer init, EStructuralFeature feature) { if (init === null) return; From 48040d3a500433adf6f6a4207328551837cd27f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 14 Oct 2021 23:40:37 +0200 Subject: [PATCH 14/68] Fix C++ param initializer --- .../generator/cpp/CppInstanceGenerator.kt | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt index f76beafd56..36a3c3964e 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt @@ -52,23 +52,22 @@ class CppInstanceGenerator( } private fun Instantiation.getParameterValue(param: Parameter, isBankInstantiation: Boolean = false): String { - val assignment = this.parameters.firstOrNull { it.lhs === param } + val rhs = this.parameters.firstOrNull { it.lhs === param }?.rhs return if (isBankInstantiation && param.name == "bank_index") { // If we are in a bank instantiation (instanceId != null), then assign the instanceId // to the parameter named "bank_index" """__lf_idx""" - } else if (assignment == null) { + } else if (rhs == null) { // If no assignment was found, then the parameter is not overwritten and we assign the // default value with(CppParameterGenerator) { param.defaultValue } - } else if (assignment.rhs.isAssign) { - assert(assignment.rhs.exprs.size == 1) - assignment.rhs.exprs.single().toCode() - } else if (assignment.rhs.isBraces) { - "{${assignment.rhs.exprs.joinToString(", ") { it.toCode() }}}" - } else if (assignment.rhs.isParens) { - "(${assignment.rhs.exprs.joinToString(", ") { it.toCode() }})" + } else if (rhs.isAssign) { + rhs.exprs.single().toCode() + } else if (rhs.isBraces) { + "${param.targetType}{${rhs.exprs.joinToString(", ") { it.toCode() }}}" + } else if (rhs.isParens) { + "${param.targetType}(${rhs.exprs.joinToString(", ") { it.toCode() }})" } else { throw AssertionError("unreachable") } From 451e928703d058dd6cef6f6e865514227a664fe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 15 Oct 2021 00:16:38 +0200 Subject: [PATCH 15/68] Fix C tests Note that now, the syntax to assign a parameter (in instantiations) is the same as the syntax for any initializer. This is the case in the entire grammar and in all targets. Hence in reactor Foo(p(...)) the `...` mean exactly the same as in new Foo(p(...)) Notably, this is not necessarily the same thing as reactor Foo(p = ...) where again, the `...` mean the same as in new Foo(p = ...) Hence in LF-C, since the syntax to initialize a list is `name: type(a,b,c)`, the syntax to override a list parameter is `new Foo(name(a,b,c))`. And since in LF-C, `name: type = (a,b,c)` is forbidden (it's a tuple literal), it stands to reason that `new Foo(name = (a,b,c))` is now forbidden. To alleviate this we could allow `(a,b,c)` to be interpreted as an array initializer in LF-C instead of forbidding it entirely. But I think this is not satisfactory as that syntax means something else in other targets, and C uses braces to denote array initializers... Maybe we should replace the tuple syntax in C with brace initializers. --- test/C/src/ArrayAsParameter.lf | 4 ++-- test/C/src/MovingAverage.lf | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/C/src/ArrayAsParameter.lf b/test/C/src/ArrayAsParameter.lf index 48c84be597..3e2f448db5 100644 --- a/test/C/src/ArrayAsParameter.lf +++ b/test/C/src/ArrayAsParameter.lf @@ -35,7 +35,7 @@ reactor Print { =} } main reactor ArrayAsParameter { - s = new Source(sequence = (1, 2, 3, 4), n_sequence=4); + s = new Source(sequence(1, 2, 3, 4), n_sequence=4); p = new Print(); s.out -> p.in; -} \ No newline at end of file +} diff --git a/test/C/src/MovingAverage.lf b/test/C/src/MovingAverage.lf index 2adbf63925..d5cbc1f924 100644 --- a/test/C/src/MovingAverage.lf +++ b/test/C/src/MovingAverage.lf @@ -45,7 +45,7 @@ reactor MovingAverageImpl { main reactor MovingAverage { s = new MASource(); m = new MovingAverageImpl(); - p = new TestDouble(expected=(0.0, 0.25, 0.75, 1.5, 2.5, 3.5)); + p = new TestDouble(expected(0.0, 0.25, 0.75, 1.5, 2.5, 3.5)); s.out -> m.in; m.out -> p.in; -} \ No newline at end of file +} From 43fe613d9178ccbac3222fbdd80beec9a4d56ec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 15 Oct 2021 00:31:02 +0200 Subject: [PATCH 16/68] Fix TS tests --- org.lflang/src/org/lflang/federated/FedASTUtils.java | 7 +++++-- .../src/org/lflang/generator/ts/TSConstructorGenerator.kt | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/org.lflang/src/org/lflang/federated/FedASTUtils.java b/org.lflang/src/org/lflang/federated/FedASTUtils.java index 9210c0cca7..c05a8c2c37 100644 --- a/org.lflang/src/org/lflang/federated/FedASTUtils.java +++ b/org.lflang/src/org/lflang/federated/FedASTUtils.java @@ -44,6 +44,7 @@ import org.lflang.lf.Action; import org.lflang.lf.ActionOrigin; import org.lflang.lf.Connection; +import org.lflang.lf.Delay; import org.lflang.lf.Input; import org.lflang.lf.Instantiation; import org.lflang.lf.LfFactory; @@ -444,7 +445,9 @@ public static void makeCommunication( action.setType(action_type); } - TimeValue delayValue = mainInstance.resolveTimeValue(connection.getDelay().getValue()); + Delay delay = connection.getDelay(); + TimeValue delayValue = delay == null ? null + : mainInstance.resolveTimeValue(delay.getValue()); // The connection is 'physical' if it uses the ~> notation. if (connection.isPhysical()) { @@ -455,7 +458,7 @@ public static void makeCommunication( // carry a timestamp, or a delay. The delay // provided using after is enforced by setting // the minDelay. - if (connection.getDelay() != null) { + if (delay != null) { Time time = factory.createTime(); time.setInterval((int) delayValue.time); time.setUnit(delayValue.unit); diff --git a/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt index 300951c66f..543907b81d 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt @@ -143,9 +143,9 @@ class TSConstructorGenerator ( |) { ${" | "..generateSuperConstructorCall(reactor, federate)} ${" | "..generateFederateConfigurations()} + ${" | "..parameters.generateInstantiations()} // generate this first so that later expressions can use them ${" | "..instances.generateInstantiations()} ${" | "..timers.generateInstantiations()} - ${" | "..parameters.generateInstantiations()} ${" | "..states.generateInstantiations()} ${" | "..actions.generateInstantiations()} ${" | "..ports.generateInstantiations()} @@ -156,4 +156,4 @@ class TSConstructorGenerator ( """.trimMargin() } } -} \ No newline at end of file +} From 4bb0a66a028b37bfda46ec2e4798313fef0faf76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 15 Oct 2021 00:42:33 +0200 Subject: [PATCH 17/68] Enrich c++ initializer test --- .../src/target/BraceAndParenInitialization.lf | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/test/Cpp/src/target/BraceAndParenInitialization.lf b/test/Cpp/src/target/BraceAndParenInitialization.lf index 46df6e86ee..35942baf86 100644 --- a/test/Cpp/src/target/BraceAndParenInitialization.lf +++ b/test/Cpp/src/target/BraceAndParenInitialization.lf @@ -4,19 +4,33 @@ reactor Foo( param_list_1: std::vector(4, 2), // list containing [2,2,2,2] param_list_2: std::vector{4, 2}, // list containing [4,2] param_list_3: std::vector(4, 2), // list containing [2,2,2,2] - param_list_4: std::vector{4, 2} // list containing [4,2] + param_list_4: std::vector{4, 2}, // list containing [4,2] + + param_list_5: std::vector = {= std::vector(4, 2) =} // list containing [2,2,2,2] ) { state state_list_1: std::vector(6,42); // list containing [42,42,42,42,42,42] state state_list_2: std::vector{6,42}; // list containing [6,42] + state state_list_eq: std::vector = {= std::vector(4, 2) =}; // list containing [2,2,2,2] + + state state_int: int = 22; + state state_str: std::string = "-----"; + state state_str_paren: std::string("-----"); + reaction(startup) {= - std::cerr << "Hello!\n"; + std::cerr << "Hello!\n"; if (param_list_1.size() != 4 || param_list_1[0] != 2 || param_list_2.size() != 2 || param_list_2[0] != 4 || param_list_3.size() != 3 || param_list_3[0] != 5 || param_list_4.size() != 2 || param_list_4[0] != 3 || + param_list_5.size() != 4 || param_list_5[0] != 2 || state_list_1.size() != 6 || state_list_1[0] != 42 || - state_list_2.size() != 2 || state_list_2[0] != 6) { + state_list_2.size() != 2 || state_list_2[0] != 6 + state_list_eq.size() != 4 || state_list_eq[0] != 2 || + state_int != 22 || + state_str != "-----" || + state_str_paren != "-----" + ) { std::cerr << "Error!\n"; exit(1); } From f500a34c9b29875b12ce8c836943d8878e9d9fa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 15 Oct 2021 01:05:43 +0200 Subject: [PATCH 18/68] Fix python empty lists --- .../lflang/generator/PythonGenerator.xtend | 40 +++++++++---------- test/Python/src/ListLiterals.lf | 4 +- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/PythonGenerator.xtend b/org.lflang/src/org/lflang/generator/PythonGenerator.xtend index 474087fcd6..404bb877ec 100644 --- a/org.lflang/src/org/lflang/generator/PythonGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/PythonGenerator.xtend @@ -187,41 +187,39 @@ class PythonGenerator extends CGenerator { // Parameters in Python are always prepended with a 'self.' // predicate. Therefore, we need to append the returned value // if it is a parameter. - if (v instanceof ParamRef) { - return "self." + v.parameter.name; + return if (v instanceof ParamRef) { + "self." + v.parameter.name; } else if (v instanceof ListExpr) { - return v.items.join("[", ", ", "]", [it.pythonTargetValue]) + if (v.items.isEmpty) "[]" + else v.items.join("[", ", ", "]", [it.pythonTargetValue]) } else if (v instanceof TupleExpr) { - if (v.items.isEmpty) - return "()" - else - // notice the closing delimiter is ",)", - return v.items.join("(", ", ", ",)", [it.pythonTargetValue]) - } - - switch(v.toText) { - case "false": return "False" - case "true": return "True" - default: return super.getTargetValue(v) + if (v.items.isEmpty) "()" + else return v.items.join("(", ", ", ",)", [it.pythonTargetValue]) + } else { + switch(v.toText) { + case "false": "False" + case "true": "True" + default: super.getTargetValue(v) + } } } /** Returns the python initializer corresponding to the given LF init list. */ def String getPythonInitializer(Initializer init) { - if (init === null) { - return "None" - } if (init.isParens && (init.exprs.size != 1 || init.isTrailingComma)) { + return if (init === null) { + "None" + } else if (init.isParens && (init.exprs.size != 1 || init.isTrailingComma)) { // corresponds to a tuple: // state foo(1,2) # tuple w/ 2 components // state foo(1,) # tuple w/ 1 component // state foo() # tuple w/ 0 component - return if (init.exprs.size == 0) "()" - else if (init.exprs.size == 1) "(" + init.asSingleValue.pythonTargetValue + ",)" - else init.exprs.join('(', ', ', ')', [it.pythonTargetValue]) + + if (init.exprs.isEmpty) "()" + else init.exprs.join("(", ", ", ",)", [it.pythonTargetValue]) } else if ((init.isAssign || init.isParens) && init.exprs.size == 1) { - return init.asSingleValue.getPythonTargetValue + init.asSingleValue.getPythonTargetValue } else { throw new AssertionError("invalid expression form: " + init) } diff --git a/test/Python/src/ListLiterals.lf b/test/Python/src/ListLiterals.lf index 9af1583a3a..28bd69968b 100644 --- a/test/Python/src/ListLiterals.lf +++ b/test/Python/src/ListLiterals.lf @@ -11,7 +11,8 @@ main reactor { state l12([1,2]); state l12_trailing([1,2,]); state empty([]); - // doesn't parse state empty2([,]); + state empty2 = []; + // doesn't parse // state empty2([,]); sub = new Sub(seq = [1, 2]); sub2 = new Sub(seq = (1, 2)); @@ -19,6 +20,7 @@ main reactor { reaction(startup) {= assert self.empty == [] + assert self.empty2 == [] assert self.l12 == [1, 2] assert self.l12 == self.l12_trailing print("Success") From 57585df9690041bcdb840c38f78985aa4363c549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 15 Oct 2021 02:07:26 +0200 Subject: [PATCH 19/68] Fix typo --- test/Cpp/src/target/BraceAndParenInitialization.lf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Cpp/src/target/BraceAndParenInitialization.lf b/test/Cpp/src/target/BraceAndParenInitialization.lf index 35942baf86..3ba606d58e 100644 --- a/test/Cpp/src/target/BraceAndParenInitialization.lf +++ b/test/Cpp/src/target/BraceAndParenInitialization.lf @@ -25,7 +25,7 @@ reactor Foo( param_list_4.size() != 2 || param_list_4[0] != 3 || param_list_5.size() != 4 || param_list_5[0] != 2 || state_list_1.size() != 6 || state_list_1[0] != 42 || - state_list_2.size() != 2 || state_list_2[0] != 6 + state_list_2.size() != 2 || state_list_2[0] != 6 || state_list_eq.size() != 4 || state_list_eq[0] != 2 || state_int != 22 || state_str != "-----" || From 30bd55a6832549e17a7f336fd11feced9f241f72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 15 Oct 2021 14:32:56 +0200 Subject: [PATCH 20/68] 1 class support for arithmetic exprs in more contexts --- .../compiler/LinguaFrancaValidationTest.xtend | 46 +++++++++++++++++- org.lflang/src/org/lflang/ASTUtils.xtend | 14 ++++-- .../org/lflang/validation/LFValidator.xtend | 48 ++++++++++++++++++- .../src/target/BraceAndParenInitialization.lf | 2 + 4 files changed, 103 insertions(+), 7 deletions(-) diff --git a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend index 80c7fa656c..1c93e1c44d 100644 --- a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend +++ b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend @@ -703,7 +703,51 @@ class LinguaFrancaValidationTest { ''').assertNoErrors() } - + + @Test + def void forbidMalformedArithmetic() { + val model = parseWithoutError(''' + target Python; + reactor Contained(p = 2) { + state x0 = 1 + 2 sec; // int + time + state x1 = 1 + (2,); // int + tuple + state x2 = 1 + [2]; // int + list + + // following should probs be ok, currently aren't + + state x3 = [2] + [3]; // list + list + } + ''') + + model.assertError(LfPackage::eINSTANCE.addExpr, null, 57, 1, 'Unexpected operand in arithmetic expression.') + model.assertError(LfPackage::eINSTANCE.addExpr, null, 97, 1, 'Unexpected operand in arithmetic expression.') + model.assertError(LfPackage::eINSTANCE.addExpr, null, 138, 1, 'Unexpected operand in arithmetic expression.') + model.assertError(LfPackage::eINSTANCE.addExpr, null, 234, 3, 'Unexpected operand in arithmetic expression.') + model.assertError(LfPackage::eINSTANCE.addExpr, null, 234, 3, 'Unexpected operand in arithmetic expression.') + } + + + @Test + def void wellFormedArithmetic() { + parseWithoutError(''' + target Python; + reactor Contained(p = 2) { + state x1 = 1 * 2 + state x2 = 1 + 2 + state x3 = 1 + p + state x4 = 1 * p + state x5 = "a" + "b" + state x6 = "a" + p + state x7 = p + "a" + state x8 = p * "a" + + // following should probs not be ok, currently are + state x9 = "a" * "b"; + } + ''').assertNoErrors() + } + + /** * Tests for state and parameter declarations, including native lists. */ diff --git a/org.lflang/src/org/lflang/ASTUtils.xtend b/org.lflang/src/org/lflang/ASTUtils.xtend index ce7aed32a1..e6e1746b08 100644 --- a/org.lflang/src/org/lflang/ASTUtils.xtend +++ b/org.lflang/src/org/lflang/ASTUtils.xtend @@ -43,6 +43,7 @@ import org.lflang.generator.GeneratorBase import org.lflang.lf.Action import org.lflang.lf.ActionOrigin import org.lflang.lf.ArraySpec +import org.lflang.lf.AddExpr import org.lflang.lf.Assignment import org.lflang.lf.Code import org.lflang.lf.CodeExpr @@ -57,6 +58,7 @@ import org.lflang.lf.LfFactory import org.lflang.lf.Literal import org.lflang.lf.ListExpr import org.lflang.lf.Model +import org.lflang.lf.MulExpr import org.lflang.lf.Output import org.lflang.lf.Parameter import org.lflang.lf.ParamRef @@ -779,11 +781,15 @@ class ASTUtils { ParamRef: v.parameter.name Time: v.toText Literal: v.literal + AddExpr: "(" + v.left.toText + " " + v.op + " " + v.right.toText + ")" + MulExpr: "(" + v.left.toText + " " + v.op + " " + v.right.toText + ")" ListExpr: v.items.join(',', '[', ']', [ it.toText ]) - TupleExpr: { - val end = v.isTrailingComma ? ",)" : ")" - v.items.join(',', '(', end, [ it.toText ]) - } + TupleExpr: + if (v.items.isEmpty) "()" + else { + val end = v.isTrailingComma ? ",)" : ")" + v.items.join(',', '(', end, [ it.toText ]) + } CodeExpr: v.code.toText default: throw new AssertionError("unsupported " + v) } diff --git a/org.lflang/src/org/lflang/validation/LFValidator.xtend b/org.lflang/src/org/lflang/validation/LFValidator.xtend index 9ce627e96f..0ed2dc9ea6 100644 --- a/org.lflang/src/org/lflang/validation/LFValidator.xtend +++ b/org.lflang/src/org/lflang/validation/LFValidator.xtend @@ -47,6 +47,7 @@ import org.lflang.TargetProperty import org.lflang.TimeValue import org.lflang.lf.Action import org.lflang.lf.ActionOrigin +import org.lflang.lf.AddExpr import org.lflang.lf.Assignment import org.lflang.lf.Connection import org.lflang.lf.CodeExpr @@ -65,6 +66,7 @@ import org.lflang.lf.LfPackage.Literals import org.lflang.lf.Literal import org.lflang.lf.ListExpr import org.lflang.lf.Model +import org.lflang.lf.MulExpr import org.lflang.lf.NamedHost import org.lflang.lf.Output import org.lflang.lf.Parameter @@ -754,7 +756,7 @@ class LFValidator extends BaseLFValidator { // All parameters must be initialized. error("Uninitialized parameter.", Literals.PARAMETER__INIT) } else { - checkType(param.init, param.inferredType, Literals.PARAMETER__INIT) + typeCheck(param.init, param.inferredType, Literals.PARAMETER__INIT) } if (this.target.requiresTypes) { @@ -1230,7 +1232,49 @@ class LFValidator extends BaseLFValidator { } } - def void checkType(Initializer init, InferredType type, EStructuralFeature feature) { + @Check(FAST) + def checkAddExpr(AddExpr e) { + checkValidArithmeticExpr(e, Literals.ADD_EXPR__LEFT) + } + + @Check(FAST) + def checkMulExpr(MulExpr e) { + checkValidArithmeticExpr(e, Literals.MUL_EXPR__LEFT) + } + + /** + * Check the form of an arithmetic expression. + * todo would be nice to allow `time + time` and `int * time`. + * But we'd need to build up our typing infrastructure. + */ + def void checkValidArithmeticExpr(Value e, EStructuralFeature feature) { + switch (e) { + AddExpr: { + checkValidArithmeticExpr(e.left, feature) + checkValidArithmeticExpr(e.right, feature) + } + MulExpr: { + checkValidArithmeticExpr(e.left, feature) + checkValidArithmeticExpr(e.right, feature) + } + Literal, CodeExpr, ParamRef: { + // Assume it's ok, language will check validity later. + // Eg in Python, the following is fine: + // 2 * "a" + // or in C/Java/C++: + // c - 'a' + } + default: + error("Unexpected operand in arithmetic expression.", feature) + } + } + + /** + * Check that the initializer is compatible with the type of value. + * Note that if the type is inferred it will necessarily be compatible + * so this method is not harmful. + */ + def void typeCheck(Initializer init, InferredType type, EStructuralFeature feature) { if (init === null) return; if (type.isTime) { diff --git a/test/Cpp/src/target/BraceAndParenInitialization.lf b/test/Cpp/src/target/BraceAndParenInitialization.lf index 3ba606d58e..4d0c457344 100644 --- a/test/Cpp/src/target/BraceAndParenInitialization.lf +++ b/test/Cpp/src/target/BraceAndParenInitialization.lf @@ -14,6 +14,7 @@ reactor Foo( state state_list_eq: std::vector = {= std::vector(4, 2) =}; // list containing [2,2,2,2] state state_int: int = 22; + state state_int_mul: int = 22 * 5; state state_str: std::string = "-----"; state state_str_paren: std::string("-----"); @@ -28,6 +29,7 @@ reactor Foo( state_list_2.size() != 2 || state_list_2[0] != 6 || state_list_eq.size() != 4 || state_list_eq[0] != 2 || state_int != 22 || + state_int_mul != 22 * 5 || state_str != "-----" || state_str_paren != "-----" ) { From 1f7838289271fa1e3408c09321d8a24391a4a19f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 15 Oct 2021 15:05:16 +0200 Subject: [PATCH 21/68] Test type inference with eq assignment --- .../tests/compiler/LinguaFrancaValidationTest.xtend | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend index 1c93e1c44d..a036039820 100644 --- a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend +++ b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend @@ -747,6 +747,15 @@ class LinguaFrancaValidationTest { ''').assertNoErrors() } + @Test + def void typeInferenceWithEqAssignment() { + parseWithoutError(''' + target C; + reactor Contained(p = 4 sec) { + state x1 = p; + } + ''').assertNoErrors() + } /** * Tests for state and parameter declarations, including native lists. From 6cb60fef6445f5aa120cd568b67295b9a07f218e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 15 Oct 2021 15:54:31 +0200 Subject: [PATCH 22/68] Remove offset checks Of course Xtext doesn't normalize newlines so that these offsets are only valid on linux/macos but not on windows................. --- .../tests/compiler/LinguaFrancaValidationTest.xtend | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend index a036039820..d2b03f23d6 100644 --- a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend +++ b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend @@ -719,11 +719,11 @@ class LinguaFrancaValidationTest { } ''') - model.assertError(LfPackage::eINSTANCE.addExpr, null, 57, 1, 'Unexpected operand in arithmetic expression.') - model.assertError(LfPackage::eINSTANCE.addExpr, null, 97, 1, 'Unexpected operand in arithmetic expression.') - model.assertError(LfPackage::eINSTANCE.addExpr, null, 138, 1, 'Unexpected operand in arithmetic expression.') - model.assertError(LfPackage::eINSTANCE.addExpr, null, 234, 3, 'Unexpected operand in arithmetic expression.') - model.assertError(LfPackage::eINSTANCE.addExpr, null, 234, 3, 'Unexpected operand in arithmetic expression.') + model.assertError(LfPackage::eINSTANCE.addExpr, null, 'Unexpected operand in arithmetic expression.') + model.assertError(LfPackage::eINSTANCE.addExpr, null, 'Unexpected operand in arithmetic expression.') + model.assertError(LfPackage::eINSTANCE.addExpr, null, 'Unexpected operand in arithmetic expression.') + model.assertError(LfPackage::eINSTANCE.addExpr, null, 'Unexpected operand in arithmetic expression.') + model.assertError(LfPackage::eINSTANCE.addExpr, null, 'Unexpected operand in arithmetic expression.') } From 0434933ed92bdd5380306d4475512ed920ec30a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 15 Oct 2021 19:03:23 +0200 Subject: [PATCH 23/68] Add common test for expressions --- org.lflang/src/org/lflang/LinguaFranca.xtext | 6 ++- test/C/src/Initializers.lf | 37 ++++++++++++++++++ test/Cpp/src/Initializers.lf | 36 +++++++++++++++++ test/Python/src/Initializers.lf | 41 ++++++++++++++++++++ 4 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 test/C/src/Initializers.lf create mode 100644 test/Cpp/src/Initializers.lf create mode 100644 test/Python/src/Initializers.lf diff --git a/org.lflang/src/org/lflang/LinguaFranca.xtext b/org.lflang/src/org/lflang/LinguaFranca.xtext index a9e46c93bb..9c693c27d4 100644 --- a/org.lflang/src/org/lflang/LinguaFranca.xtext +++ b/org.lflang/src/org/lflang/LinguaFranca.xtext @@ -353,8 +353,10 @@ ArraySpec: WidthSpec: '[' ( ofVariableLength?=']' | (terms+=WidthTerm) ('+' terms+=WidthTerm)* ']' ); - -WidthTerm: + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + // todo replace this with just Expr. + +WidthTerm: // todo remove this. width=INT | parameter=[Parameter] | 'widthof(' port=VarRef ')' diff --git a/test/C/src/Initializers.lf b/test/C/src/Initializers.lf new file mode 100644 index 0000000000..8ba604b5bc --- /dev/null +++ b/test/C/src/Initializers.lf @@ -0,0 +1,37 @@ +target C; + +reactor Foo( + p1: int[](4, 2), + p2: int[](4, 2), + + p4: string("a") +) { + state s0: int = 2 - 2 - 2; state s1: int(2 - 2 - 2); + state s2: string = "a"; state s4: string("a"); + state s3: char(2 + 'a'); state s5: char = 2 + 'a'; + + preamble{= +#include "assert.h" + =} + + reaction(startup) {= + assert(p1[0] == 4); assert(p1[1] == 2); + assert(p2[0] == 3); assert(p2[1] == 5); + + assert(s0 == -2); + assert(s1 == -2); + + assert(strcmp(s2, "a")); + assert(strcmp(s4, "a")); + assert(strcmp(p4, "a")); + + assert(s3 == 'c'); + assert(s5 == 'c'); + + printf("Success\n"); + =} +} + +main reactor { + foo = new Foo(p2(3,5)); +} diff --git a/test/Cpp/src/Initializers.lf b/test/Cpp/src/Initializers.lf new file mode 100644 index 0000000000..625aec99f5 --- /dev/null +++ b/test/Cpp/src/Initializers.lf @@ -0,0 +1,36 @@ +target Cpp; + +reactor Foo( + p1: std::vector(4, 2), // list containing [2,2,2,2] + p2: std::vector{4, 2}, // list containing [4,2] + + p3: std::vector(4, 2), // list containing [2,2,2,2] + p4: std::vector{4, 2} // list containing [4,2] + + // p5: std::vector = (4, 2) // disallowed +) { + state s0: int = 2 - 2 - 2; state s1: int(2 - 2 - 2); + state s2: std::string = "a"; state s3: char(2 + 'a'); + + private preamble{= +#include + =} + + reaction(startup) {= + assert(p1 == std::vector(4, 2)); + assert(p2 == (std::vector { 4, 2 })); + assert(p3 == std::vector(3, 5)); + assert(p4 == (std::vector { 3, 5 })); + + assert(s0 == -2); + assert(s1 == -2); + assert(s2 == "a"); + assert(s3 == 'c'); + + std::cerr << "Success\n"; + =} +} + +main reactor { + foo = new Foo(p3(3,5), p4{3,5}); +} diff --git a/test/Python/src/Initializers.lf b/test/Python/src/Initializers.lf new file mode 100644 index 0000000000..b1b0837911 --- /dev/null +++ b/test/Python/src/Initializers.lf @@ -0,0 +1,41 @@ +target Python; + +reactor Foo( + p1(4, 2), // tuple + p2 = (4, 2), // tuple + + p3([4, 2]), // list + p4 = [4, 2], // list + + p5("a"), + p6 = "a", + + p12 = 2 +) { + state s0 = 2 - 2 - 2; state s1(2 - 2 - 2); + # todo # state s0 = 2 - 2 - p12; state s1(2 - p12 - 2); + state s2 = "a"; state s4("a"); + # state s3(2 + 'a'); state s5 = 2 + 'a'; + + reaction(startup) {= + assert self.p1 == (4, 2); + assert self.p2 == (3, 5); + assert self.p3 == [4, 2]; + assert self.p4 == [4, 2]; + + assert self.s0 == -2; + assert self.s1 == -2; + + assert self.s2 == "a"; + assert self.s4 == "a"; + assert self.p5 == "a"; + assert self.p6 == "a"; + + if __debug__: # otherwise asserts are noop + print("Success\n") + =} +} + +main reactor { + foo = new Foo(p2(3,5)); +} From 6c0f65f402831f1dce74f73a5c9add9601f3b0b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 15 Oct 2021 19:23:38 +0200 Subject: [PATCH 24/68] Move test --- test/Python/src/Initializers.lf | 31 +++++++++++++++ test/Python/src/NativeExpressions.lf | 40 -------------------- test/Python/src/{ => target}/ListLiterals.lf | 0 3 files changed, 31 insertions(+), 40 deletions(-) delete mode 100644 test/Python/src/NativeExpressions.lf rename test/Python/src/{ => target}/ListLiterals.lf (100%) diff --git a/test/Python/src/Initializers.lf b/test/Python/src/Initializers.lf index b1b0837911..947a97c86d 100644 --- a/test/Python/src/Initializers.lf +++ b/test/Python/src/Initializers.lf @@ -1,5 +1,7 @@ target Python; +reactor Sub(seq([1,2])) {} + reactor Foo( p1(4, 2), // tuple p2 = (4, 2), // tuple @@ -31,9 +33,38 @@ reactor Foo( assert self.p5 == "a"; assert self.p6 == "a"; + assert self.t0 == () + assert self.t02 == () + assert self.t1 == (1,) + assert self.t12 == (1,) + assert self.t2 == (1,2) + assert self.t22 == (1,2) + assert self.justin == 1 + assert self.justin2 == 1 + if __debug__: # otherwise asserts are noop print("Success\n") =} + + + state t0 = (); // empty tuple + state t02(); // empty tuple + + state t1 = (1,); // tuple of 1 + state t12(1,); // tuple of 1 + + state t2 = (1,2); // tuple of 2 + state t22(1,2); // tuple of 2 + + state justin = (1); // just 1 + state justin2(1); // just 1 + + sub = new Sub(seq = ()); + sub2 = new Sub(seq = (1,)); + sub3 = new Sub(seq = (1, 2)); + sub4 = new Sub(seq((1, 2,))); + + } main reactor { diff --git a/test/Python/src/NativeExpressions.lf b/test/Python/src/NativeExpressions.lf deleted file mode 100644 index d1d5e1d328..0000000000 --- a/test/Python/src/NativeExpressions.lf +++ /dev/null @@ -1,40 +0,0 @@ -// This example illustrates state variables and parameters on the wiki. -// For this test, success is just compiling and running. -target Python; - - -reactor Sub(seq([1,2])) { - -} - -main reactor { - state t0 = (); // empty tuple - state t02(); // empty tuple - - state t1 = (1,); // tuple of 1 - state t12(1,); // tuple of 1 - - state t2 = (1,2); // tuple of 2 - state t22(1,2); // tuple of 2 - - state justin = (1); // just 1 - state justin2(1); // just 1 - - sub = new Sub(seq = ()); - sub2 = new Sub(seq = (1,)); - sub3 = new Sub(seq = (1, 2)); - sub4 = new Sub(seq((1, 2,))); - - reaction(startup) {= - assert self.t0 == () - assert self.t02 == () - assert self.t1 == (1,) - assert self.t12 == (1,) - assert self.t2 == (1,2) - assert self.t22 == (1,2) - assert self.justin == 1 - assert self.justin2 == 1 - - print("Success") - =} -} diff --git a/test/Python/src/ListLiterals.lf b/test/Python/src/target/ListLiterals.lf similarity index 100% rename from test/Python/src/ListLiterals.lf rename to test/Python/src/target/ListLiterals.lf From 8a3e49a7e7adb5621d119b72de29eac5d3f3eb8b Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Tue, 19 Oct 2021 00:45:26 -0700 Subject: [PATCH 25/68] Apply suggestions from code review Co-authored-by: Soroush Bateni --- org.lflang/src/org/lflang/federated/FedASTUtils.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/org.lflang/src/org/lflang/federated/FedASTUtils.java b/org.lflang/src/org/lflang/federated/FedASTUtils.java index c05a8c2c37..1a9f7fb502 100644 --- a/org.lflang/src/org/lflang/federated/FedASTUtils.java +++ b/org.lflang/src/org/lflang/federated/FedASTUtils.java @@ -87,6 +87,8 @@ public static List safe(List list) { * * @note Used in federated execution * + * @param destination The receiving network input port at the destination federate + * @param receivingPortID The ID of the receiving port * @param bankIndex The bank index of the receiving federate, or -1 if not in a bank. * @param instance The federate instance is used to keep track of all * network input ports globally @@ -280,6 +282,7 @@ private static void addStp(List STPList, Reaction r, Instantiation instan * * @note Used in federated execution * + * @param source The output port at the source federate * @param instance The federate instance is used to keep track of all * network input ports globally * @param receivingPortID The ID of the receiving port From f1924ad848d4728fc9d13b687b658f01225ce3fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 20 Oct 2021 19:02:53 +0200 Subject: [PATCH 26/68] Fix TargetTypes --- .../src/org/lflang/generator/TargetTypes.java | 109 +++++++++++++++--- .../org/lflang/generator/cpp/CppExtensions.kt | 2 +- .../org/lflang/generator/cpp/CppGenerator.kt | 2 +- .../org/lflang/generator/rust/RustModel.kt | 14 +-- .../org/lflang/generator/rust/RustTypes.kt | 2 +- 5 files changed, 105 insertions(+), 24 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/TargetTypes.java b/org.lflang/src/org/lflang/generator/TargetTypes.java index 2e7c65eb00..00eeb5ff72 100644 --- a/org.lflang/src/org/lflang/generator/TargetTypes.java +++ b/org.lflang/src/org/lflang/generator/TargetTypes.java @@ -1,3 +1,28 @@ +/* + * Copyright (c) 2021, The Authors of this file and their respective Institutions. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * 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 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 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + package org.lflang.generator; import java.util.List; @@ -7,13 +32,17 @@ import org.lflang.ASTUtils; import org.lflang.InferredType; import org.lflang.JavaAstUtils; +import org.lflang.Target; import org.lflang.TimeValue; +import org.lflang.lf.Code; import org.lflang.lf.CodeExpr; import org.lflang.lf.Initializer; +import org.lflang.lf.ListExpr; import org.lflang.lf.Literal; -import org.lflang.lf.Parameter; +import org.lflang.lf.ParamRef; import org.lflang.lf.Time; import org.lflang.lf.TimeUnit; +import org.lflang.lf.TupleExpr; import org.lflang.lf.Type; import org.lflang.lf.Value; @@ -22,16 +51,18 @@ * utilities to convert LF expressions and types to the target * language. Each code generator is expected to use at least one * language-specific instance of this interface. - * + * * TODO currently, {@link GeneratorBase} implements this interface, * it should instead contain an instance. + * + * @author Clément Fournier - TU Dresden, INSA Rennes */ public interface TargetTypes { /** - * Return true if the target supports generics (i.e., parametric - * polymorphism), false otherwise. + * Return true if the target supports generic types + * (i.e., parametric polymorphism), false otherwise. */ boolean supportsGenerics(); @@ -80,13 +111,57 @@ default String escapeIdentifier(String ident) { * to a time value ({@link #getTargetTimeType()}), with the given * magnitude and unit. The unit may not be null (use {@link TimeUnit#NONE}). */ - default String getTargetTimeExpression(long magnitude, TimeUnit unit) { + default String getTargetTimeExpr(long magnitude, TimeUnit unit) { // todo make non-default when we reuse this for all generators, // all targets should support this. Objects.requireNonNull(unit); throw new UnsupportedGeneratorFeatureException("Time expressions"); } + /** + * Returns an expression in the target language that is the translation + * of the given parameter reference. All targets should support this. + * The default returns the simple name of the parameter. + */ + default String getTargetParamRef(ParamRef expr, InferredType type) { + return escapeIdentifier(expr.getParameter().getName()); + } + + /** + * Returns an expression in the target language that is + * the translation of the given literal. All targets + * should support this. + * The default returns the literal converted to a string. + */ + default String getTargetLiteral(Literal expr, InferredType type) { + if (ASTUtils.isZero(expr) && type != null && type.isTime) { + return getTargetTimeExpr(0, TimeUnit.NONE); + } + return expr.getLiteral(); // unescaped + } + + /** + * Returns an expression in the target language that is the translation + * of the given tuple expression. To support tuple expressions, a target + * must also register this capability in {@link Target#supportsLfTupleLiterals()}. + * + * @throws UnsupportedGeneratorFeatureException If the target does not support this + */ + default String getTargetTupleExpr(TupleExpr expr, InferredType type) { + throw new UnsupportedGeneratorFeatureException("Tuple expressions lists"); + } + + /** + * Returns an expression in the target language that is the translation + * of the given list expression. To support list expressions, a target + * must also register this capability in {@link Target#supportsLfListLiterals()}. + * + * @throws UnsupportedGeneratorFeatureException If the target does not support this + */ + default String getTargetListExpr(ListExpr expr, InferredType type) { + throw new UnsupportedGeneratorFeatureException("Tuple expressions lists"); + } + /** * Returns an expression in the target language that corresponds * to a variable-size list expression. @@ -194,16 +269,24 @@ default String getTargetInitializer(Initializer init, Type type) { * The given type, if non-null, may inform the code generation. */ default String getTargetExpr(Value value, InferredType type) { - if (ASTUtils.isZero(value) && type != null && type.isTime) { - return getTargetTimeExpression(0, TimeUnit.NONE); - } else if (value instanceof Parameter) { - return escapeIdentifier(((Parameter) value).getName()); + if (value instanceof ParamRef) { + return getTargetParamRef((ParamRef) value, type); } else if (value instanceof Time) { return getTargetTimeExpr((Time) value); } else if (value instanceof Literal) { - return ((Literal) value).getLiteral();// here we don't escape + return getTargetLiteral((Literal) value, type); } else if (value instanceof CodeExpr) { - return ASTUtils.toText(((CodeExpr) value).getCode()); + Code code = ((CodeExpr) value).getCode(); + if (ASTUtils.isZero(code) && type != null && type.isTime) { + // todo remove this branch, this is not useful + return getTargetTimeExpr(TimeValue.ZERO); + } else { + return ASTUtils.toText(code); + } + } else if (value instanceof TupleExpr) { + return getTargetTupleExpr((TupleExpr) value, type); + } else if (value instanceof ListExpr) { + return getTargetListExpr((ListExpr) value, type); } else { throw new IllegalStateException("Invalid value " + value); } @@ -215,7 +298,7 @@ default String getTargetExpr(Value value, InferredType type) { * target code. */ default String getTargetTimeExpr(TimeValue tv) { - return getTargetTimeExpression(tv.time, tv.unit); + return getTargetTimeExpr(tv.time, tv.unit); } /** @@ -223,6 +306,6 @@ default String getTargetTimeExpr(TimeValue tv) { * target code. */ default String getTargetTimeExpr(Time t) { - return getTargetTimeExpression(t.getInterval(), t.getUnit()); + return getTargetTimeExpr(t.getInterval(), t.getUnit()); } } diff --git a/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt b/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt index eb352b2194..18734e090f 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt @@ -49,7 +49,7 @@ val Reaction.name /** Convert a LF time value to a representation in C++ code */ -fun TimeValue.toCode() = CppTypes.getTargetTimeExpression(time, unit) +fun TimeValue.toCode() = CppTypes.getTargetTimeExpr(time, unit) /** Convert a value to a time representation in C++ code* * diff --git a/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt index 13f11f2204..70f44115c3 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt @@ -239,7 +239,7 @@ object CppTypes : TargetTypes { override fun getTargetUndefinedType() = "void" - override fun getTargetTimeExpression(magnitude: Long, unit: TimeUnit): String = + override fun getTargetTimeExpr(magnitude: Long, unit: TimeUnit): String = if (magnitude == 0L) "reactor::Duration::zero()" else magnitude.toString() + unit.cppUnit diff --git a/org.lflang/src/org/lflang/generator/rust/RustModel.kt b/org.lflang/src/org/lflang/generator/rust/RustModel.kt index 2362ebe521..775dae5960 100644 --- a/org.lflang/src/org/lflang/generator/rust/RustModel.kt +++ b/org.lflang/src/org/lflang/generator/rust/RustModel.kt @@ -370,7 +370,7 @@ private fun TimeValue.toRustTimeExpr(): TargetCode = toRustTimeExpr(time, unit) private fun Time.toRustTimeExpr(): TargetCode = toRustTimeExpr(interval.toLong(), unit) private fun toRustTimeExpr(interval: Long, unit: TimeUnit): TargetCode = - RustTypes.getTargetTimeExpression(interval, unit) + RustTypes.getTargetTimeExpr(interval, unit) /** Regex to match a target code block, captures the insides as $1. */ private val TARGET_BLOCK_R = Regex("\\{=(.*)=}", RegexOption.DOT_MATCHES_ALL) @@ -490,7 +490,7 @@ object RustModelBuilder { StateVarInfo( lfName = it.name, type = RustTypes.getTargetType(it.type, it.init), - init = RustTypes.getTargetInitializer(it.init, it.type) + init = it.init?.let { init -> RustTypes.getTargetInitializer(init, it.type) } ) }, nestedInstances = reactor.instantiations.map { it.toModel() }, @@ -499,7 +499,7 @@ object RustModelBuilder { CtorParamInfo( lfName = it.name, type = RustTypes.getTargetType(it.type, it.init), - defaultValue = RustTypes.getTargetInitializer(it.init, it.type) + defaultValue = it.init?.let { init -> RustTypes.getTargetInitializer(init, it.type) } ) } ) @@ -508,12 +508,10 @@ object RustModelBuilder { private fun Instantiation.toModel(): NestedReactorInstance { val byName = parameters.associateBy { it.lhs.name } - val args = reactor.parameters.associate { ithParam -> + val args = reactor.parameters.associate { ithParam: Parameter -> // use provided argument - val value = byName[ithParam.name]?.let { - RustTypes.getTargetInitializer(it.rhs, ithParam.type) - } - ?: ithParam?.let { RustTypes.getTargetInitializer(it.init, it.type) } + val value = byName[ithParam.name]?.let { RustTypes.getTargetInitializer(it.rhs, ithParam.type) } + ?: ithParam.init?.let { RustTypes.getTargetInitializer(it, ithParam.type) } ?: throw InvalidLfSourceException("Cannot find value of parameter ${ithParam.name}", this) ithParam.name to value } diff --git a/org.lflang/src/org/lflang/generator/rust/RustTypes.kt b/org.lflang/src/org/lflang/generator/rust/RustTypes.kt index e4145411a2..4525358a6d 100644 --- a/org.lflang/src/org/lflang/generator/rust/RustTypes.kt +++ b/org.lflang/src/org/lflang/generator/rust/RustTypes.kt @@ -48,7 +48,7 @@ object RustTypes : TargetTypes { if (ident in RustKeywords) "r#$ident" else ident - override fun getTargetTimeExpression(magnitude: Long, unit: TimeUnit): TargetCode = when (unit) { + override fun getTargetTimeExpr(magnitude: Long, unit: TimeUnit): TargetCode = when (unit) { TimeUnit.NSEC, TimeUnit.NSECS -> "Duration::from_nanos($magnitude)" TimeUnit.USEC, From 1605becd65cec26ce38f2ac89c7b4245ca36a843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 20 Oct 2021 22:26:58 +0200 Subject: [PATCH 27/68] Fix TargetTypes w/ arithmetic --- org.lflang/src/org/lflang/generator/TargetTypes.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/org.lflang/src/org/lflang/generator/TargetTypes.java b/org.lflang/src/org/lflang/generator/TargetTypes.java index 00eeb5ff72..8655e75da7 100644 --- a/org.lflang/src/org/lflang/generator/TargetTypes.java +++ b/org.lflang/src/org/lflang/generator/TargetTypes.java @@ -34,11 +34,13 @@ import org.lflang.JavaAstUtils; import org.lflang.Target; import org.lflang.TimeValue; +import org.lflang.lf.AddExpr; import org.lflang.lf.Code; import org.lflang.lf.CodeExpr; import org.lflang.lf.Initializer; import org.lflang.lf.ListExpr; import org.lflang.lf.Literal; +import org.lflang.lf.MulExpr; import org.lflang.lf.ParamRef; import org.lflang.lf.Time; import org.lflang.lf.TimeUnit; @@ -287,6 +289,16 @@ default String getTargetExpr(Value value, InferredType type) { return getTargetTupleExpr((TupleExpr) value, type); } else if (value instanceof ListExpr) { return getTargetListExpr((ListExpr) value, type); + } else if (value instanceof MulExpr) { + MulExpr e = (MulExpr) value; + return getTargetExpr(e.getLeft(), null) + + " " + e.getOp() + + " " + getTargetExpr(e.getRight(), null); + } else if (value instanceof AddExpr) { + AddExpr e = (AddExpr) value; + return getTargetExpr(e.getLeft(), null) + + " " + e.getOp() + + " " + getTargetExpr(e.getRight(), null); } else { throw new IllegalStateException("Invalid value " + value); } From 84d92a92254bd6efc9c9a9d347deaa851da73a4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 20 Oct 2021 22:44:53 +0200 Subject: [PATCH 28/68] Simplify C++ generator --- org.lflang/src/org/lflang/AstExtensions.kt | 3 +++ .../org/lflang/generator/GeneratorUtils.kt | 21 ++++++++++++---- .../generator/cpp/CppActionGenerator.kt | 2 +- .../cpp/CppAssembleMethodGenerator.kt | 4 ++-- .../org/lflang/generator/cpp/CppExtensions.kt | 19 ++++----------- .../org/lflang/generator/cpp/CppGenerator.kt | 14 +++++++++++ .../generator/cpp/CppInstanceGenerator.kt | 12 +--------- .../lflang/generator/cpp/CppMainGenerator.kt | 8 +++---- .../generator/cpp/CppMethodGenerator.kt | 2 +- .../generator/cpp/CppParameterGenerator.kt | 14 +---------- .../lflang/generator/cpp/CppStateGenerator.kt | 24 ++----------------- .../lflang/generator/cpp/CppTimerGenerator.kt | 6 ++--- 12 files changed, 52 insertions(+), 77 deletions(-) diff --git a/org.lflang/src/org/lflang/AstExtensions.kt b/org.lflang/src/org/lflang/AstExtensions.kt index 5d04b22574..b5b60c6c44 100644 --- a/org.lflang/src/org/lflang/AstExtensions.kt +++ b/org.lflang/src/org/lflang/AstExtensions.kt @@ -339,6 +339,9 @@ val StateVar.isInitialized: Boolean get() = init != null */ fun WidthSpec.getWidth(instantiations: List? = null) = ASTUtils.width(this, instantiations) +/** Returns a non-null time value, defaulting null to zero. */ +fun TimeValue?.orZero(): TimeValue = this ?: TimeValue.ZERO + /** Get the LF Model of a resource */ val Resource.model: Model get() = this.allContents.asSequence().filterIsInstance().first() diff --git a/org.lflang/src/org/lflang/generator/GeneratorUtils.kt b/org.lflang/src/org/lflang/generator/GeneratorUtils.kt index cd01702da4..608a1cf2f6 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorUtils.kt +++ b/org.lflang/src/org/lflang/generator/GeneratorUtils.kt @@ -2,12 +2,23 @@ package org.lflang.generator import org.eclipse.emf.ecore.EObject import org.eclipse.xtext.nodemodel.util.NodeModelUtils -import org.lflang.* -import org.lflang.JavaAstUtils.getInferredType -import org.lflang.lf.Time -import org.lflang.lf.TimeUnit -import org.lflang.lf.Type +import org.lflang.InferredType +import org.lflang.lf.Parameter +import org.lflang.lf.StateVar import org.lflang.lf.Value +import org.lflang.toPath +import org.lflang.toTextTokenBased +import org.lflang.toUnixString + + +fun TargetTypes.getTargetInitializer(sv: StateVar): TargetCode = + this.getTargetInitializer(sv.init, sv.type) + +fun TargetTypes.getTargetInitializer(sv: Parameter): TargetCode = + this.getTargetInitializer(sv.init, sv.type) + +fun TargetTypes.getTargetTimeExpr(v: Value): TargetCode = + this.getTargetExpr(v, InferredType.time()) /** A transparent type alias to document when a string contains target code. */ diff --git a/org.lflang/src/org/lflang/generator/cpp/CppActionGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppActionGenerator.kt index 80cebc30a1..d1ac02dab5 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppActionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppActionGenerator.kt @@ -59,7 +59,7 @@ class CppActionGenerator(private val reactor: Reactor, private val errorReporter "minSpacing and spacing violation policies are not yet supported for logical actions in reactor-ccp!" ) } else { - val time = action.minDelay?.toTime() ?: "reactor::Duration::zero()" + val time = action.minDelay.toCppTime() """, ${action.name}{"${action.name}", this, $time}""" } } diff --git a/org.lflang/src/org/lflang/generator/cpp/CppAssembleMethodGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppAssembleMethodGenerator.kt index b9ac6cc00e..3ce9ce995d 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppAssembleMethodGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppAssembleMethodGenerator.kt @@ -120,7 +120,7 @@ class CppAssembleMethodGenerator(private val reactor: Reactor) { } private fun setDeadline(reaction: Reaction): String = - "${reaction.name}.set_deadline(${reaction.deadline.delay.toTime(true)}, [this]() { ${reaction.name}_deadline_handler(); });" + "${reaction.name}.set_deadline(${reaction.deadline.delay.toCppTime(true)}, [this]() { ${reaction.name}_deadline_handler(); });" private fun assembleReaction(reaction: Reaction) = with(PrependOperator) { """ @@ -201,4 +201,4 @@ class CppAssembleMethodGenerator(private val reactor: Reactor) { |} """.trimMargin() } -} \ No newline at end of file +} diff --git a/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt b/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt index 18734e090f..a11e5ecd2f 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt @@ -2,6 +2,7 @@ package org.lflang.generator.cpp import org.eclipse.emf.ecore.resource.Resource import org.lflang.* +import org.lflang.generator.getTargetTimeExpr import org.lflang.lf.* import java.time.LocalDateTime import java.time.format.DateTimeFormatter @@ -48,9 +49,6 @@ val Reaction.name // corresponding generator classes. See for instance the CppParameterGenerator -/** Convert a LF time value to a representation in C++ code */ -fun TimeValue.toCode() = CppTypes.getTargetTimeExpr(time, unit) - /** Convert a value to a time representation in C++ code* * * If the value evaluates to 0, it is interpreted as a time. @@ -58,17 +56,10 @@ fun TimeValue.toCode() = CppTypes.getTargetTimeExpr(time, unit) * @param outerContext A flag indicating whether to generate code for the scope of the outer reactor class. * This should be set to false if called from code generators for the inner class. */ -fun Value.toTime(outerContext: Boolean = false): String = - if (outerContext && this is ParamRef) "__lf_inner.${parameter.name}" - else CppTypes.getTargetExpr(this, InferredType.time()) - -/** - * Get textual representation of a value in C++ code - * - * If the value evaluates to 0, it is interpreted as a normal value. - * FIXME this is redundant to GeneratorBase.getTargetValue - */ -fun Value.toCode(): String = CppTypes.getTargetExpr(this, null) +fun Value?.toCppTime(outerContext: Boolean = false): String = + if (this == null) "reactor::Duration::zero()" + else if (outerContext) CppOuterTypes.getTargetTimeExpr(this) + else CppTypes.getTargetTimeExpr(this) /** Get the textual representation of a width in C++ code */ fun WidthSpec.toCode(): String = terms.joinToString(" + ") { diff --git a/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt index 70f44115c3..0e7f7229a4 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt @@ -34,6 +34,7 @@ import org.lflang.Target import org.lflang.generator.GeneratorBase import org.lflang.generator.TargetTypes import org.lflang.lf.Action +import org.lflang.lf.ParamRef import org.lflang.lf.TimeUnit import org.lflang.lf.VarRef import org.lflang.scoping.LFGlobalScopeProvider @@ -244,6 +245,19 @@ object CppTypes : TargetTypes { else magnitude.toString() + unit.cppUnit } + +/** + * This object generates types in the context of the outer class, + * where parameter references need special handling. + */ +object CppOuterTypes : TargetTypes by CppTypes { + + override fun getTargetParamRef(expr: ParamRef, type: InferredType?): String { + return "__lf_inner.${expr.parameter.name}" + } + +} + /** Get a C++ representation of a LF unit. */ val TimeUnit.cppUnit get() = when (this) { diff --git a/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt index 36a3c3964e..e15e6913c6 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt @@ -58,18 +58,8 @@ class CppInstanceGenerator( // If we are in a bank instantiation (instanceId != null), then assign the instanceId // to the parameter named "bank_index" """__lf_idx""" - } else if (rhs == null) { - // If no assignment was found, then the parameter is not overwritten and we assign the - // default value - with(CppParameterGenerator) { param.defaultValue } - } else if (rhs.isAssign) { - rhs.exprs.single().toCode() - } else if (rhs.isBraces) { - "${param.targetType}{${rhs.exprs.joinToString(", ") { it.toCode() }}}" - } else if (rhs.isParens) { - "${param.targetType}(${rhs.exprs.joinToString(", ") { it.toCode() }})" } else { - throw AssertionError("unreachable") + CppTypes.getTargetInitializer(rhs ?: param.init, param.type) } } diff --git a/org.lflang/src/org/lflang/generator/cpp/CppMainGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppMainGenerator.kt index 5f198c3bcc..b7a0766271 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppMainGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppMainGenerator.kt @@ -1,11 +1,9 @@ package org.lflang.generator.cpp -import org.lflang.TargetConfig +import org.lflang.* import org.lflang.generator.PrependOperator -import org.lflang.inferredType import org.lflang.lf.Parameter import org.lflang.lf.Reactor -import org.lflang.toUnixString /** C++ code generator responsible for generating the main file including the main() function */ class CppMainGenerator( @@ -81,7 +79,7 @@ class CppMainGenerator( | unsigned threads = ${if (targetConfig.threads != 0) targetConfig.threads else "std::thread::hardware_concurrency()"}; | app.add_option("-t,--threads", threads, "the number of worker threads used by the scheduler", true); | - | reactor::Duration timeout = ${targetConfig.timeout?.toCode() ?: "reactor::Duration::zero()"}; + | reactor::Duration timeout = ${CppTypes.getTargetTimeExpr(targetConfig.timeout.orZero())}; | auto opt_timeout = app.add_option ("-o,--timeout", timeout, "Time after which the execution is aborted."); | | opt_timeout->check([](const std::string& val ){ return validate_time_string(val); }); @@ -120,4 +118,4 @@ class CppMainGenerator( |} """.trimMargin() } -} \ No newline at end of file +} diff --git a/org.lflang/src/org/lflang/generator/cpp/CppMethodGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppMethodGenerator.kt index 17aa30032c..9686b254da 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppMethodGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppMethodGenerator.kt @@ -34,7 +34,7 @@ import org.lflang.toText /** A C++ code generator for state variables */ class CppMethodGenerator(private val reactor: Reactor) { - private val Method.targetType: String get() = if (`return` != null) InferredType.fromAST(`return`).cppType else "void" + private val Method.targetType: String get() = InferredType.fromAST(`return`).cppType private val MethodArgument.targetType: String get() = InferredType.fromAST(type).cppType private val Method.cppArgs get() = this.arguments.map { "${it.targetType} ${it.name}" } diff --git a/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt index 2946bd7bfc..8125eeabfe 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt @@ -34,24 +34,12 @@ class CppParameterGenerator(private val reactor: Reactor) { companion object { - /** - * Create a list of initializers for the given parameter - * - * TODO This is redundant to GeneratorBase.getInitializerList - */ - private fun Parameter.getInitializerList() = init.exprs.map { - if (isOfTimeType) it.toTime() - else it.toCode() - } - /** Type of the parameter in C++ code */ val Parameter.targetType get(): String = this.inferredType.cppType /** Get the default value of the receiver parameter in C++ code */ val Parameter.defaultValue: String - get() = - if (init?.isBraces == true) "$targetType{${getInitializerList().joinToString(", ")}}" - else "$targetType(${getInitializerList().joinToString(", ")})" + get() = CppTypes.getTargetInitializer(init, type) /** Get a C++ type that is a const reference to the parameter type */ val Parameter.constRefType: String diff --git a/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt index f75ea29766..0d3b60d8f9 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt @@ -24,8 +24,8 @@ package org.lflang.generator.cpp +import org.lflang.generator.getTargetInitializer import org.lflang.inferredType -import org.lflang.isInitialized import org.lflang.isOfTimeType import org.lflang.lf.ParamRef import org.lflang.lf.Reactor @@ -34,31 +34,11 @@ import org.lflang.lf.StateVar /** A C++ code generator for state variables */ class CppStateGenerator(private val reactor: Reactor) { - /** - * Create a list of state initializers in target code. - * - * TODO This is redundant to GeneratorBase.getInitializerList - */ - private fun getInitializerList(state: StateVar) = state.init.exprs.map { - when { - it is ParamRef -> it.parameter.name - state.isOfTimeType -> it.toTime() - else -> it.toCode() - } - } - - private fun generateInitializer(state: StateVar): String = - if (state.init.isBraces) - "${state.name}{${getInitializerList(state).joinToString(separator = ", ")}}" - else - "${state.name}(${getInitializerList(state).joinToString(separator = ", ")})" - - /** Get all state declarations */ fun generateDeclarations() = reactor.stateVars.joinToString("\n", "// state variable\n", "\n") { "${it.inferredType.cppType} ${it.name};" } /** Get all timer initializers */ fun generateInitializers(): String = reactor.stateVars.filter { it.init != null } - .joinToString(separator = "\n", prefix = "// state variables\n") { ", ${generateInitializer(it)}" } + .joinToString(separator = "\n", prefix = "// state variables\n") { ", ${CppTypes.getTargetInitializer(it)}" } } diff --git a/org.lflang/src/org/lflang/generator/cpp/CppTimerGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppTimerGenerator.kt index 578d87e76d..61648e4a5e 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppTimerGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppTimerGenerator.kt @@ -31,8 +31,8 @@ import org.lflang.lf.Timer class CppTimerGenerator(private val reactor: Reactor) { private fun generateInitializer(timer: Timer): String { - val offset = timer.offset?.toTime() ?: "reactor::Duration::zero()" - val period = timer.period?.toTime() ?: "reactor::Duration::zero()" + val offset = timer.offset.toCppTime() + val period = timer.period.toCppTime() return """${timer.name}{"${timer.name}", this, $period, $offset}""" } @@ -43,4 +43,4 @@ class CppTimerGenerator(private val reactor: Reactor) { /** Get all timer initializers */ fun generateInitializers() = reactor.timers.joinToString(separator = "\n", prefix = "// timers\n") { ", ${generateInitializer(it)}" } -} \ No newline at end of file +} From 0b5f021be9b71574e92b842754fcddc9295c6677 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 21 Oct 2021 12:57:38 +0200 Subject: [PATCH 29/68] Fix cpp state initializers --- .../src/org/lflang/generator/TargetTypes.java | 4 +++- .../org/lflang/generator/cpp/CppExtensions.kt | 17 +++++++++++++++++ .../org/lflang/generator/cpp/CppGenerator.kt | 5 +---- .../generator/cpp/CppParameterGenerator.kt | 2 +- .../lflang/generator/cpp/CppStateGenerator.kt | 8 ++++---- 5 files changed, 26 insertions(+), 10 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/TargetTypes.java b/org.lflang/src/org/lflang/generator/TargetTypes.java index 8655e75da7..27266f2f1a 100644 --- a/org.lflang/src/org/lflang/generator/TargetTypes.java +++ b/org.lflang/src/org/lflang/generator/TargetTypes.java @@ -249,7 +249,9 @@ default String getTargetType(InferredType type) { * @param type Declared type of the expression (nullable) */ default String getTargetInitializer(Initializer init, Type type) { - Objects.requireNonNull(init); + if (init == null) { + return getMissingExpr(); + } var inferredType = JavaAstUtils.getInferredType(type, init); Value single = JavaAstUtils.asSingleValue(init); if (single != null) { diff --git a/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt b/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt index a11e5ecd2f..ed1e25d1f0 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt @@ -120,3 +120,20 @@ fun fileComment(r: Resource) = """ val InferredType.cppType: String get() = CppTypes.getTargetType(this) + + +fun CppTypes.getCppInitializerList(init: Initializer?, type: Type?): String { + val items = if (init == null) { + missingExpr + } else { + val inferredType = JavaAstUtils.getInferredType(type, init) + val single = JavaAstUtils.asSingleValue(init) + single?.let { getTargetExpr(it, inferredType) } + ?: init.exprs.joinToString { + val itemType = if (type?.isTime == true) InferredType.time() else null + getTargetExpr(it, itemType) + } + } + + return if (init?.isBraces == true) "{$items}" else "($items)" +} diff --git a/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt index 0e7f7229a4..eaa762ac82 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt @@ -33,10 +33,7 @@ import org.lflang.* import org.lflang.Target import org.lflang.generator.GeneratorBase import org.lflang.generator.TargetTypes -import org.lflang.lf.Action -import org.lflang.lf.ParamRef -import org.lflang.lf.TimeUnit -import org.lflang.lf.VarRef +import org.lflang.lf.* import org.lflang.scoping.LFGlobalScopeProvider import java.nio.file.Files import java.nio.file.Path diff --git a/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt index 8125eeabfe..78e82f5099 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt @@ -39,7 +39,7 @@ class CppParameterGenerator(private val reactor: Reactor) { /** Get the default value of the receiver parameter in C++ code */ val Parameter.defaultValue: String - get() = CppTypes.getTargetInitializer(init, type) + get() = targetType + CppTypes.getCppInitializerList(init, type) /** Get a C++ type that is a const reference to the parameter type */ val Parameter.constRefType: String diff --git a/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt index 0d3b60d8f9..1a0d83ba75 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt @@ -24,21 +24,21 @@ package org.lflang.generator.cpp -import org.lflang.generator.getTargetInitializer import org.lflang.inferredType -import org.lflang.isOfTimeType -import org.lflang.lf.ParamRef import org.lflang.lf.Reactor import org.lflang.lf.StateVar /** A C++ code generator for state variables */ class CppStateGenerator(private val reactor: Reactor) { + private fun generateInitializer(state: StateVar): String = + state.name + CppTypes.getCppInitializerList(state.init, state.type) + /** Get all state declarations */ fun generateDeclarations() = reactor.stateVars.joinToString("\n", "// state variable\n", "\n") { "${it.inferredType.cppType} ${it.name};" } /** Get all timer initializers */ fun generateInitializers(): String = reactor.stateVars.filter { it.init != null } - .joinToString(separator = "\n", prefix = "// state variables\n") { ", ${CppTypes.getTargetInitializer(it)}" } + .joinToString(separator = "\n", prefix = "// state variables\n") { ", ${generateInitializer(it)}" } } From c2f7490b259385b8f7b124a4cc2fb1ce49349c38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 22 Oct 2021 12:15:43 +0200 Subject: [PATCH 30/68] Really fix C++ --- .../src/org/lflang/generator/TargetTypes.java | 34 ++---- .../org/lflang/generator/cpp/CppGenerator.kt | 57 ---------- .../generator/cpp/CppInstanceGenerator.kt | 5 +- .../src/org/lflang/generator/cpp/CppTypes.kt | 100 ++++++++++++++++++ .../org/lflang/generator/rust/RustTypes.kt | 15 ++- 5 files changed, 118 insertions(+), 93 deletions(-) create mode 100644 org.lflang/src/org/lflang/generator/cpp/CppTypes.kt diff --git a/org.lflang/src/org/lflang/generator/TargetTypes.java b/org.lflang/src/org/lflang/generator/TargetTypes.java index 27266f2f1a..ef1e7803bc 100644 --- a/org.lflang/src/org/lflang/generator/TargetTypes.java +++ b/org.lflang/src/org/lflang/generator/TargetTypes.java @@ -117,7 +117,7 @@ default String getTargetTimeExpr(long magnitude, TimeUnit unit) { // todo make non-default when we reuse this for all generators, // all targets should support this. Objects.requireNonNull(unit); - throw new UnsupportedGeneratorFeatureException("Time expressions"); + throw new AssertionError("Unimplemented"); // todo make non-default } /** @@ -166,22 +166,12 @@ default String getTargetListExpr(ListExpr expr, InferredType type) { /** * Returns an expression in the target language that corresponds - * to a variable-size list expression. - * - * @throws UnsupportedGeneratorFeatureException If the target does not support this - */ - default String getVariableSizeListInitExpression(List contents, boolean withBraces) { - throw new UnsupportedGeneratorFeatureException("Variable size lists"); - } - - /** - * Returns an expression in the target language that corresponds - * to a fixed-size list expression. - * - * @throws UnsupportedGeneratorFeatureException If the target does not support this + * to a variable or fixed-size list expression, depending on the + * parameter type. The inferred type may be opaque, or undefined, + * but is non-null. The contents list has size != 1. */ - default String getFixedSizeListInitExpression(List contents, int listSize, boolean withBraces) { - throw new UnsupportedGeneratorFeatureException("Fixed size lists"); + default String getDefaultInitExpression(List contents, InferredType type, boolean withBraces) { + throw new AssertionError("Unimplemented"); // todo make non-default } @@ -252,19 +242,13 @@ default String getTargetInitializer(Initializer init, Type type) { if (init == null) { return getMissingExpr(); } - var inferredType = JavaAstUtils.getInferredType(type, init); + InferredType inferredType = JavaAstUtils.getInferredType(type, init); Value single = JavaAstUtils.asSingleValue(init); if (single != null) { return getTargetExpr(single, inferredType); } - var targetValues = init.getExprs().stream().map(it -> getTargetExpr(it, inferredType)).collect(Collectors.toList()); - if (inferredType.isFixedSizeList) { - return getFixedSizeListInitExpression(targetValues, inferredType.listSize, init.isBraces()); - } else if (inferredType.isVariableSizeList) { - return getVariableSizeListInitExpression(targetValues, init.isBraces()); - } else { - return getMissingExpr(); - } + var targetValues = init.getExprs().stream().map(it -> getTargetExpr(it, null)).collect(Collectors.toList()); + return getDefaultInitExpression(targetValues, inferredType, init.isBraces()); } diff --git a/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt index eaa762ac82..c409192e3d 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt @@ -224,60 +224,3 @@ class CppGenerator( override fun getTarget() = Target.CPP } - -object CppTypes : TargetTypes { - - override fun supportsGenerics() = true - - override fun getTargetTimeType() = "reactor::Duration" - override fun getTargetTagType() = "reactor::Tag" - - override fun getTargetFixedSizeListType(baseType: String, size: Int) = "std::array<$baseType, $size>" - override fun getTargetVariableSizeListType(baseType: String) = "std::vector<$baseType>" - - override fun getTargetUndefinedType() = "void" - - override fun getTargetTimeExpr(magnitude: Long, unit: TimeUnit): String = - if (magnitude == 0L) "reactor::Duration::zero()" - else magnitude.toString() + unit.cppUnit - -} - -/** - * This object generates types in the context of the outer class, - * where parameter references need special handling. - */ -object CppOuterTypes : TargetTypes by CppTypes { - - override fun getTargetParamRef(expr: ParamRef, type: InferredType?): String { - return "__lf_inner.${expr.parameter.name}" - } - -} - -/** Get a C++ representation of a LF unit. */ -val TimeUnit.cppUnit - get() = when (this) { - TimeUnit.NSEC -> "ns" - TimeUnit.NSECS -> "ns" - TimeUnit.USEC -> "us" - TimeUnit.USECS -> "us" - TimeUnit.MSEC -> "ms" - TimeUnit.MSECS -> "ms" - TimeUnit.SEC -> "s" - TimeUnit.SECS -> "s" - TimeUnit.SECOND -> "s" - TimeUnit.SECONDS -> "s" - TimeUnit.MIN -> "min" - TimeUnit.MINS -> "min" - TimeUnit.MINUTE -> "min" - TimeUnit.MINUTES -> "min" - TimeUnit.HOUR -> "h" - TimeUnit.HOURS -> "h" - TimeUnit.DAY -> "d" - TimeUnit.DAYS -> "d" - TimeUnit.WEEK -> "d*7" - TimeUnit.WEEKS -> "d*7" - TimeUnit.NONE -> "" - else -> "" - } diff --git a/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt index e15e6913c6..ba2e4cc0c2 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt @@ -52,14 +52,15 @@ class CppInstanceGenerator( } private fun Instantiation.getParameterValue(param: Parameter, isBankInstantiation: Boolean = false): String { - val rhs = this.parameters.firstOrNull { it.lhs === param }?.rhs + val rhs = this.parameters.firstOrNull { it.lhs === param }?.rhs ?: param.init + println(param.name + " : " + CppTypes.getTargetType(param.inferredType) + " = " + rhs) return if (isBankInstantiation && param.name == "bank_index") { // If we are in a bank instantiation (instanceId != null), then assign the instanceId // to the parameter named "bank_index" """__lf_idx""" } else { - CppTypes.getTargetInitializer(rhs ?: param.init, param.type) + CppTypes.getTargetInitializer(rhs, param.type) } } diff --git a/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt b/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt new file mode 100644 index 0000000000..1a16bd2239 --- /dev/null +++ b/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2021, TU Dresden. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * 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 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 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.lflang.generator.cpp + +import org.lflang.InferredType +import org.lflang.generator.TargetTypes +import org.lflang.joinWithCommas +import org.lflang.lf.ParamRef +import org.lflang.lf.TimeUnit + +/** + * Implementation of [TargetTypes] for C++. + * + * @author Clément Fournier + */ +object CppTypes : TargetTypes { + + override fun supportsGenerics() = true + + override fun getTargetTimeType() = "reactor::Duration" + override fun getTargetTagType() = "reactor::Tag" + + override fun getTargetFixedSizeListType(baseType: String, size: Int) = "std::array<$baseType, $size>" + override fun getTargetVariableSizeListType(baseType: String) = "std::vector<$baseType>" + + override fun getDefaultInitExpression(contents: List, type: InferredType, withBraces: Boolean): String = + buildString { + this.append(getTargetType(type)) + val (prefix, postfix) = if (withBraces) Pair("{", "}") else Pair("(", ")") + contents.joinTo(this, ", ", prefix, postfix) + } + + + override fun getTargetUndefinedType() = "void" + + override fun getTargetTimeExpr(magnitude: Long, unit: TimeUnit): String = + if (magnitude == 0L) "reactor::Duration::zero()" + else magnitude.toString() + unit.cppUnit + +} + +/** + * This object generates types in the context of the outer class, + * where parameter references need special handling. + */ +object CppOuterTypes : TargetTypes by CppTypes { + + override fun getTargetParamRef(expr: ParamRef, type: InferredType?): String { + return "__lf_inner.${expr.parameter.name}" + } + +} + +/** Get a C++ representation of a LF unit. */ +val TimeUnit.cppUnit + get() = when (this) { + TimeUnit.NSEC -> "ns" + TimeUnit.NSECS -> "ns" + TimeUnit.USEC -> "us" + TimeUnit.USECS -> "us" + TimeUnit.MSEC -> "ms" + TimeUnit.MSECS -> "ms" + TimeUnit.SEC -> "s" + TimeUnit.SECS -> "s" + TimeUnit.SECOND -> "s" + TimeUnit.SECONDS -> "s" + TimeUnit.MIN -> "min" + TimeUnit.MINS -> "min" + TimeUnit.MINUTE -> "min" + TimeUnit.MINUTES -> "min" + TimeUnit.HOUR -> "h" + TimeUnit.HOURS -> "h" + TimeUnit.DAY -> "d" + TimeUnit.DAYS -> "d" + TimeUnit.WEEK -> "d*7" + TimeUnit.WEEKS -> "d*7" + else -> "" + } diff --git a/org.lflang/src/org/lflang/generator/rust/RustTypes.kt b/org.lflang/src/org/lflang/generator/rust/RustTypes.kt index 4525358a6d..fc17f2e5b9 100644 --- a/org.lflang/src/org/lflang/generator/rust/RustTypes.kt +++ b/org.lflang/src/org/lflang/generator/rust/RustTypes.kt @@ -24,6 +24,7 @@ package org.lflang.generator.rust +import org.lflang.InferredType import org.lflang.generator.TargetCode import org.lflang.generator.TargetTypes import org.lflang.lf.TimeUnit @@ -67,15 +68,11 @@ object RustTypes : TargetTypes { TimeUnit.SECOND, TimeUnit.SECONDS -> "Duration::from_secs($magnitude)" } - override fun getFixedSizeListInitExpression( - contents: List, - listSize: Int, - withBraces: Boolean - ): String = - contents.joinToString(", ", "[", "]") - - override fun getVariableSizeListInitExpression(contents: List, withBraces: Boolean): String = - contents.joinToString(", ", "vec![", "]") + override fun getDefaultInitExpression(contents: List, type: InferredType, withBraces: Boolean): String = + if (type.isFixedSizeList) + contents.joinToString(", ", "[", "]") + else + contents.joinToString(", ", "vec![", "]") override fun getMissingExpr(): String = "Default::default()" From b5bcd3d9138bb61e5a0461a45bfc25156db5125f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 22 Oct 2021 13:09:13 +0200 Subject: [PATCH 31/68] Cleanups Remove getDefaultInitExpression - was public, we dont want to call that by accident --- org.lflang/src/org/lflang/JavaAstUtils.java | 4 +--- .../src/org/lflang/generator/TargetTypes.java | 21 +++++++----------- .../src/org/lflang/generator/cpp/CppTypes.kt | 22 ++++++++++++------- .../org/lflang/generator/rust/RustTypes.kt | 20 ++++++++++++----- 4 files changed, 38 insertions(+), 29 deletions(-) diff --git a/org.lflang/src/org/lflang/JavaAstUtils.java b/org.lflang/src/org/lflang/JavaAstUtils.java index bd31a32bd3..6a704428d4 100644 --- a/org.lflang/src/org/lflang/JavaAstUtils.java +++ b/org.lflang/src/org/lflang/JavaAstUtils.java @@ -27,8 +27,6 @@ import java.util.List; import java.util.stream.Collectors; -import org.eclipse.emf.common.util.EList; - import org.lflang.lf.Action; import org.lflang.lf.Initializer; import org.lflang.lf.ParamRef; @@ -156,7 +154,7 @@ public static InferredType getInferredType(Port p) { * return it. Otherwise return null. */ public static Value asSingleValue(Initializer init) { - EList exprs = init.getExprs(); + List exprs = init.getExprs(); return exprs.size() == 1 ? exprs.get(0) : null; } diff --git a/org.lflang/src/org/lflang/generator/TargetTypes.java b/org.lflang/src/org/lflang/generator/TargetTypes.java index ef1e7803bc..66ad2a84cd 100644 --- a/org.lflang/src/org/lflang/generator/TargetTypes.java +++ b/org.lflang/src/org/lflang/generator/TargetTypes.java @@ -164,16 +164,6 @@ default String getTargetListExpr(ListExpr expr, InferredType type) { throw new UnsupportedGeneratorFeatureException("Tuple expressions lists"); } - /** - * Returns an expression in the target language that corresponds - * to a variable or fixed-size list expression, depending on the - * parameter type. The inferred type may be opaque, or undefined, - * but is non-null. The contents list has size != 1. - */ - default String getDefaultInitExpression(List contents, InferredType type, boolean withBraces) { - throw new AssertionError("Unimplemented"); // todo make non-default - } - /** * Returns the expression that is used to replace a @@ -235,7 +225,12 @@ default String getTargetType(InferredType type) { * expression in target code. The given type, if non-null, * may inform the code generation. * - * @param init Initializer list (non-null) + *

By default, only initializers with exactly one expression + * are handled. If you want to provide semantics for other kinds + * of initializers, for instance, braced initializer lists, override + * this method. + * + * @param init Initializer list (nullable) * @param type Declared type of the expression (nullable) */ default String getTargetInitializer(Initializer init, Type type) { @@ -247,8 +242,8 @@ default String getTargetInitializer(Initializer init, Type type) { if (single != null) { return getTargetExpr(single, inferredType); } - var targetValues = init.getExprs().stream().map(it -> getTargetExpr(it, null)).collect(Collectors.toList()); - return getDefaultInitExpression(targetValues, inferredType, init.isBraces()); + // override this method to give semantics to this + throw new UnsupportedGeneratorFeatureException(init, "Initializers with != 1 value"); } diff --git a/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt b/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt index 1a16bd2239..0aae1dd691 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt @@ -25,10 +25,9 @@ package org.lflang.generator.cpp import org.lflang.InferredType +import org.lflang.JavaAstUtils import org.lflang.generator.TargetTypes -import org.lflang.joinWithCommas -import org.lflang.lf.ParamRef -import org.lflang.lf.TimeUnit +import org.lflang.lf.* /** * Implementation of [TargetTypes] for C++. @@ -45,12 +44,19 @@ object CppTypes : TargetTypes { override fun getTargetFixedSizeListType(baseType: String, size: Int) = "std::array<$baseType, $size>" override fun getTargetVariableSizeListType(baseType: String) = "std::vector<$baseType>" - override fun getDefaultInitExpression(contents: List, type: InferredType, withBraces: Boolean): String = - buildString { - this.append(getTargetType(type)) - val (prefix, postfix) = if (withBraces) Pair("{", "}") else Pair("(", ")") - contents.joinTo(this, ", ", prefix, postfix) + override fun getTargetInitializer(init: Initializer?, type: Type?): String { + if (init == null) { + return missingExpr } + val inferredType = JavaAstUtils.getInferredType(type, init) + return init.exprs.singleOrNull()?.let { getTargetExpr(it, inferredType) } + ?: buildString { + // != 1 expr + this.append(getTargetType(type)) + val (prefix, postfix) = if (init.isBraces) Pair("{", "}") else Pair("(", ")") + init.exprs.joinTo(this, ", ", prefix, postfix) { getTargetExpr(it, null) } + } + } override fun getTargetUndefinedType() = "void" diff --git a/org.lflang/src/org/lflang/generator/rust/RustTypes.kt b/org.lflang/src/org/lflang/generator/rust/RustTypes.kt index fc17f2e5b9..477f8e3bfa 100644 --- a/org.lflang/src/org/lflang/generator/rust/RustTypes.kt +++ b/org.lflang/src/org/lflang/generator/rust/RustTypes.kt @@ -25,9 +25,12 @@ package org.lflang.generator.rust import org.lflang.InferredType +import org.lflang.JavaAstUtils import org.lflang.generator.TargetCode import org.lflang.generator.TargetTypes +import org.lflang.lf.Initializer import org.lflang.lf.TimeUnit +import org.lflang.lf.Type object RustTypes : TargetTypes { @@ -68,11 +71,18 @@ object RustTypes : TargetTypes { TimeUnit.SECOND, TimeUnit.SECONDS -> "Duration::from_secs($magnitude)" } - override fun getDefaultInitExpression(contents: List, type: InferredType, withBraces: Boolean): String = - if (type.isFixedSizeList) - contents.joinToString(", ", "[", "]") - else - contents.joinToString(", ", "vec![", "]") + override fun getTargetInitializer(init: Initializer?, type: Type?): String { + if (init == null) { + return missingExpr + } + val inferredType: InferredType = JavaAstUtils.getInferredType(type, init) + return init.exprs.singleOrNull()?.let { getTargetExpr(it, inferredType) } + ?: if (inferredType.isFixedSizeList) + init.exprs.joinToString(", ", "[", "]") { getTargetExpr(it, null) } + else + init.exprs.joinToString(", ", "vec![", "].into()") { getTargetExpr(it, null) } + + } override fun getMissingExpr(): String = "Default::default()" From b701c427ec463e3cc794f0074d4ae34925a586dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 27 Oct 2021 13:28:58 +0200 Subject: [PATCH 32/68] Fix merge --- .../src/org/lflang/generator/TargetTypes.java | 6 ++---- .../org/lflang/generator/cpp/CppExtensions.kt | 18 ++---------------- .../generator/cpp/CppParameterGenerator.kt | 2 +- .../lflang/generator/cpp/CppStateGenerator.kt | 4 +++- .../src/org/lflang/generator/cpp/CppTypes.kt | 2 +- .../src/org/lflang/generator/rust/RustModel.kt | 2 +- .../src/org/lflang/generator/rust/RustTypes.kt | 8 +++++--- test/Cpp/src/NativeListsAndTimes.lf | 1 + 8 files changed, 16 insertions(+), 27 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/TargetTypes.java b/org.lflang/src/org/lflang/generator/TargetTypes.java index 833c5e12b5..4df4c2753b 100644 --- a/org.lflang/src/org/lflang/generator/TargetTypes.java +++ b/org.lflang/src/org/lflang/generator/TargetTypes.java @@ -25,9 +25,7 @@ package org.lflang.generator; -import java.util.List; import java.util.Objects; -import java.util.stream.Collectors; import org.lflang.ASTUtils; import org.lflang.InferredType; @@ -174,7 +172,7 @@ default String getTargetListExpr(ListExpr expr, InferredType type) { * * @throws UnsupportedGeneratorFeatureException If the target does not support this */ - default String getMissingExpr(InferredType type) { + default String getMissingExpr() { throw new UnsupportedGeneratorFeatureException("Missing initializers"); } @@ -236,7 +234,7 @@ default String getTargetType(InferredType type) { default String getTargetInitializer(Initializer init, Type type) { InferredType inferredType = JavaAstUtils.getInferredType(type, init); if (init == null) { - return getMissingExpr(inferredType); + return getMissingExpr(); } Value single = JavaAstUtils.asSingleValue(init); if (single != null) { diff --git a/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt b/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt index a73a4e0b6d..1515532e45 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt @@ -70,7 +70,7 @@ fun WidthSpec.toCppCode(): String = terms.joinToString(" + ") { if ((variable as Port).isMultiport) "(${container.name}.size() * ${container.name}[0]->${variable.name}.size())" else "${container.name}.size()" } else { - if ((variable as Port).isMultiport) "${name}.size()" + if ((variable as Port).isMultiport) "$name.size()" else "1" } } @@ -122,18 +122,4 @@ val InferredType.cppType: String get() = CppTypes.getTargetType(this) -fun CppTypes.getCppInitializerList(init: Initializer?, type: Type?): String { - val items = if (init == null) { - missingExpr - } else { - val inferredType = JavaAstUtils.getInferredType(type, init) - val single = JavaAstUtils.asSingleValue(init) - single?.let { getTargetExpr(it, inferredType) } - ?: init.exprs.joinToString { - val itemType = if (type?.isTime == true) InferredType.time() else null - getTargetExpr(it, itemType) - } - } - - return if (init?.isBraces == true) "{$items}" else "($items)" -} +fun CppTypes.getCppInitializerList(init: Initializer?, type: Type?): String = getTargetInitializer(init, type) diff --git a/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt index 78e82f5099..5b24824c5f 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt @@ -39,7 +39,7 @@ class CppParameterGenerator(private val reactor: Reactor) { /** Get the default value of the receiver parameter in C++ code */ val Parameter.defaultValue: String - get() = targetType + CppTypes.getCppInitializerList(init, type) + get() = CppTypes.getCppInitializerList(init, type) /** Get a C++ type that is a const reference to the parameter type */ val Parameter.constRefType: String diff --git a/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt index 1a0d83ba75..4dc63a12a0 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt @@ -32,7 +32,9 @@ import org.lflang.lf.StateVar class CppStateGenerator(private val reactor: Reactor) { private fun generateInitializer(state: StateVar): String = - state.name + CppTypes.getCppInitializerList(state.init, state.type) + state.name + CppTypes.getCppInitializerList(state.init, state.type).let { + if (!it.startsWith("(")) "($it)" else it + } /** Get all state declarations */ fun generateDeclarations() = diff --git a/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt b/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt index 0aae1dd691..857eb3fdc1 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt @@ -45,10 +45,10 @@ object CppTypes : TargetTypes { override fun getTargetVariableSizeListType(baseType: String) = "std::vector<$baseType>" override fun getTargetInitializer(init: Initializer?, type: Type?): String { + val inferredType = JavaAstUtils.getInferredType(type, init) if (init == null) { return missingExpr } - val inferredType = JavaAstUtils.getInferredType(type, init) return init.exprs.singleOrNull()?.let { getTargetExpr(it, inferredType) } ?: buildString { // != 1 expr diff --git a/org.lflang/src/org/lflang/generator/rust/RustModel.kt b/org.lflang/src/org/lflang/generator/rust/RustModel.kt index 242836f4e2..6a0263336f 100644 --- a/org.lflang/src/org/lflang/generator/rust/RustModel.kt +++ b/org.lflang/src/org/lflang/generator/rust/RustModel.kt @@ -552,7 +552,7 @@ object RustModelBuilder { StateVarInfo( lfName = it.name, type = RustTypes.getTargetType(it.type, it.init), - init = it.init?.let { init -> RustTypes.getTargetInitializer(init, it.type) } + init = RustTypes.getTargetInitializer(it.init, it.type), ) }, nestedInstances = reactor.instantiations.map { it.toModel() }, diff --git a/org.lflang/src/org/lflang/generator/rust/RustTypes.kt b/org.lflang/src/org/lflang/generator/rust/RustTypes.kt index a2134d9a1a..d28b289fba 100644 --- a/org.lflang/src/org/lflang/generator/rust/RustTypes.kt +++ b/org.lflang/src/org/lflang/generator/rust/RustTypes.kt @@ -72,10 +72,10 @@ object RustTypes : TargetTypes { } override fun getTargetInitializer(init: Initializer?, type: Type?): String { + val inferredType: InferredType = JavaAstUtils.getInferredType(type, init) if (init == null) { return missingExpr } - val inferredType: InferredType = JavaAstUtils.getInferredType(type, init) return init.exprs.singleOrNull()?.let { getTargetExpr(it, inferredType) } ?: if (inferredType.isFixedSizeList) init.exprs.joinToString(", ", "[", "]") { getTargetExpr(it, null) } @@ -84,10 +84,12 @@ object RustTypes : TargetTypes { } - override fun getMissingExpr(type: InferredType): String = - "<${getTargetType(type)} as Default>::default()" + override fun getMissingExpr(): String = + "Default::default()" + } + val RustKeywords = setOf( // https://doc.rust-lang.org/reference/keywords.html "as", "break", "const", "continue", "crate", "else", diff --git a/test/Cpp/src/NativeListsAndTimes.lf b/test/Cpp/src/NativeListsAndTimes.lf index 205b8d6c34..ad5f1bd6ca 100644 --- a/test/Cpp/src/NativeListsAndTimes.lf +++ b/test/Cpp/src/NativeListsAndTimes.lf @@ -19,6 +19,7 @@ reactor Foo(x:int(0), timer toe(z); // Implicit type time state baz(p); // Implicit type int[] state period(z); // Implicit type time + state period2 = z; // Implicit type time state times: std::vector>{q, g}; // a list of lists reaction(tick) {= // Target code From 6252667d5979974f8392f7e2b4fee22628a985f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 7 Nov 2021 14:34:32 +0100 Subject: [PATCH 33/68] Add support for array initializer literals in C Rename ListExpr to BracketExpr --- .../LinguaFrancaDependencyAnalysisTest.xtend | 2 +- .../compiler/LinguaFrancaValidationTest.xtend | 18 +++++++++++-- org.lflang/src/org/lflang/ASTUtils.xtend | 7 +++--- org.lflang/src/org/lflang/JavaAstUtils.java | 2 +- org.lflang/src/org/lflang/LinguaFranca.xtext | 15 ++++++++--- org.lflang/src/org/lflang/Target.java | 16 +++++++++--- .../src/org/lflang/generator/TargetTypes.java | 25 ++++++++++++++----- .../generator/python/PythonGenerator.xtend | 4 +-- .../org/lflang/validation/LFValidator.xtend | 18 +++++++++---- 9 files changed, 79 insertions(+), 28 deletions(-) diff --git a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaDependencyAnalysisTest.xtend b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaDependencyAnalysisTest.xtend index 5de81cb1b2..891dd5ab97 100644 --- a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaDependencyAnalysisTest.xtend +++ b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaDependencyAnalysisTest.xtend @@ -109,7 +109,7 @@ class LinguaFrancaDependencyAnalysisTest { new ReactionInstanceGraph(instance) Assertions.fail("No cycle detected") } catch(InvalidSourceException e) { - Assertions.assertTrue(e.message != null && e.message.contains("Reactions form a cycle!"), + Assertions.assertTrue(e.message !== null && e.message.contains("Reactions form a cycle!"), "Should be a message about cycles: " + e.message) } } diff --git a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend index dbe755c810..85019bbb85 100644 --- a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend +++ b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend @@ -674,9 +674,23 @@ class LinguaFrancaValidationTest { reactor Contained { state x: int[]([1, 2]); state y: int[] = [1, 2]; + state y: int[] = {1, 2}; // fine } - ''').assertError(LfPackage::eINSTANCE.listExpr, - null, 'Target C does not support list literals.') + ''').assertError(LfPackage::eINSTANCE.bracketExpr, + null, 'Target C does not support this expression form.') + } + + @Test + def void forbidArrayInitializersInPython() { + parseWithoutError(''' + target Python; + reactor Contained { + state x: int[]([1, 2]); + state y: int[] = [1, 2]; + state y: int[] = {1, 2}; // wrong + } + ''').assertError(LfPackage::eINSTANCE.braceExpr, + null, 'Target C does not support this expression form.') } diff --git a/org.lflang/src/org/lflang/ASTUtils.xtend b/org.lflang/src/org/lflang/ASTUtils.xtend index 9e626aa285..6fef80d6a8 100644 --- a/org.lflang/src/org/lflang/ASTUtils.xtend +++ b/org.lflang/src/org/lflang/ASTUtils.xtend @@ -57,7 +57,7 @@ import org.lflang.lf.Initializer import org.lflang.lf.Instantiation import org.lflang.lf.LfFactory import org.lflang.lf.Literal -import org.lflang.lf.ListExpr +import org.lflang.lf.BracketExpr import org.lflang.lf.Model import org.lflang.lf.MulExpr import org.lflang.lf.Output @@ -768,7 +768,8 @@ class ASTUtils { Literal: v.literal AddExpr: "(" + v.left.toText + " " + v.op + " " + v.right.toText + ")" MulExpr: "(" + v.left.toText + " " + v.op + " " + v.right.toText + ")" - ListExpr: v.items.join(',', '[', ']', [ it.toText ]) + BracketExpr: v.items.isEmpty() ? "[]" : v.items.join('[', ',', ']', [ it.toText ]) + BraceExpr: v.items.isEmpty() ? "{}" : v.items.join('{', ',', '}', [ it.toText ]) TupleExpr: if (v.items.isEmpty) "()" else { @@ -1096,7 +1097,7 @@ class ASTUtils { def static List initializerAsList(Initializer init) { return if (init.isAssign) { val list = init.asSingleValue - if (list instanceof ListExpr) list.items + if (list instanceof BracketExpr) list.items else null } else { init.exprs diff --git a/org.lflang/src/org/lflang/JavaAstUtils.java b/org.lflang/src/org/lflang/JavaAstUtils.java index 8276a83a68..c775b5e850 100644 --- a/org.lflang/src/org/lflang/JavaAstUtils.java +++ b/org.lflang/src/org/lflang/JavaAstUtils.java @@ -166,7 +166,7 @@ public static Value asSingleValue(Initializer init) { */ public static boolean isList(Initializer init) { return (init.isBraces() || init.isParens()) && init.getExprs().size() != 1; - // || init.isAssign && init.asSingleValue instanceof ListExpr + // || init.isAssign && init.asSingleValue instanceof BracketExpr } /** diff --git a/org.lflang/src/org/lflang/LinguaFranca.xtext b/org.lflang/src/org/lflang/LinguaFranca.xtext index 18cfb05de4..7a591994ed 100644 --- a/org.lflang/src/org/lflang/LinguaFranca.xtext +++ b/org.lflang/src/org/lflang/LinguaFranca.xtext @@ -304,7 +304,8 @@ Atom returns Value: ParamRef | Time | {Literal} literal=Literal - | ListExpr + | BracketExpr + | BraceExpr | TupleExpr | {CodeExpr} code=Code ; @@ -395,10 +396,16 @@ SignedInt: INT | NEGINT ; -// List literals support even empty lists, and a trailing comma. +// corresponds to a python list +BracketExpr: + {BracketExpr} '[' ( items+=Expr (',' items+=Expr)* ','? )? ']' +; -ListExpr: - {ListExpr} '[' ( items+=Expr (',' items+=Expr)* ','? )? ']' +// corresponds to an array initializer for C/C++. For Python, +// we may improve this production to parse dictionaries. A "dictionary" +// production would also help with Rust struct initializers. +BraceExpr: + {BraceExpr} '{' ( items+=Expr (',' items+=Expr)* ','? )? '}' ; Literal: diff --git a/org.lflang/src/org/lflang/Target.java b/org.lflang/src/org/lflang/Target.java index d4b1f274a1..c098165bed 100644 --- a/org.lflang/src/org/lflang/Target.java +++ b/org.lflang/src/org/lflang/Target.java @@ -409,18 +409,26 @@ public enum Target { } /** - * Returns true if the target supports list literals in LF syntax. - * If not, list literals produce validator errors. + * Returns true if the target supports bracketed list literals in LF syntax. + * If not, they produce validator errors. */ - public boolean supportsLfListLiterals() { + public boolean supportsLfBracketListExpressions() { return this == Python; } + /** + * Returns true if the target supports braced list literals in LF syntax. + * If not, they produce validator errors. + */ + public boolean supportsLfBraceListExpressions() { + return this == C || this == CCPP || this == CPP; + } + /** * Returns true if the target supports tuple literals in LF syntax. * If not, they produce validator errors. * - *

Tuple literals use the production {@link org.lflang.lf.ParenthesizedExpr}, + *

Tuple literals use the production {@link org.lflang.lf.TupleExpr}, * they're only interpreted as a tuple if they match: *

    *
  • The empty tuple: {@code ()} diff --git a/org.lflang/src/org/lflang/generator/TargetTypes.java b/org.lflang/src/org/lflang/generator/TargetTypes.java index 4df4c2753b..8a0ceaa94b 100644 --- a/org.lflang/src/org/lflang/generator/TargetTypes.java +++ b/org.lflang/src/org/lflang/generator/TargetTypes.java @@ -33,10 +33,11 @@ import org.lflang.Target; import org.lflang.TimeValue; import org.lflang.lf.AddExpr; +import org.lflang.lf.BraceExpr; import org.lflang.lf.Code; import org.lflang.lf.CodeExpr; import org.lflang.lf.Initializer; -import org.lflang.lf.ListExpr; +import org.lflang.lf.BracketExpr; import org.lflang.lf.Literal; import org.lflang.lf.MulExpr; import org.lflang.lf.ParamRef; @@ -154,14 +155,24 @@ default String getTargetTupleExpr(TupleExpr expr, InferredType type) { /** * Returns an expression in the target language that is the translation * of the given list expression. To support list expressions, a target - * must also register this capability in {@link Target#supportsLfListLiterals()}. + * must also register this capability in {@link Target#supportsLfBracketListExpressions()}. * * @throws UnsupportedGeneratorFeatureException If the target does not support this */ - default String getTargetListExpr(ListExpr expr, InferredType type) { - throw new UnsupportedGeneratorFeatureException("Tuple expressions lists"); + default String getTargetBracketExpr(BracketExpr expr, InferredType type) { + throw new UnsupportedGeneratorFeatureException("Bracket-delimited list expressions"); } + /** + * Returns an expression in the target language that is the translation + * of the given list expression. To support list expressions, a target + * must also register this capability in {@link Target#supportsLfBraceListExpressions()}. + * + * @throws UnsupportedGeneratorFeatureException If the target does not support this + */ + default String getTargetBraceExpr(BraceExpr expr, InferredType type) { + throw new UnsupportedGeneratorFeatureException("Brace-delimited list expressions"); + } /** * Returns the expression that is used to replace a @@ -266,8 +277,10 @@ default String getTargetExpr(Value value, InferredType type) { } } else if (value instanceof TupleExpr) { return getTargetTupleExpr((TupleExpr) value, type); - } else if (value instanceof ListExpr) { - return getTargetListExpr((ListExpr) value, type); + } else if (value instanceof BracketExpr) { + return getTargetBracketExpr((BracketExpr) value, type); + } else if (value instanceof BraceExpr) { + return getTargetBraceExpr((BraceExpr) value, type); } else if (value instanceof MulExpr) { MulExpr e = (MulExpr) value; return getTargetExpr(e.getLeft(), null) diff --git a/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend b/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend index d48295c468..b1068540fe 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend @@ -54,7 +54,7 @@ import org.lflang.lf.Action import org.lflang.lf.Input import org.lflang.lf.Initializer import org.lflang.lf.Instantiation -import org.lflang.lf.ListExpr +import org.lflang.lf.BracketExpr import org.lflang.lf.Model import org.lflang.lf.Output import org.lflang.lf.Port @@ -229,7 +229,7 @@ class PythonGenerator extends CGenerator { // if it is a parameter. return if (v instanceof ParamRef) { "self." + v.parameter.name; - } else if (v instanceof ListExpr) { + } else if (v instanceof BracketExpr) { if (v.items.isEmpty) "[]" else v.items.join("[", ", ", "]", [it.pythonTargetValue]) } else if (v instanceof TupleExpr) { diff --git a/org.lflang/src/org/lflang/validation/LFValidator.xtend b/org.lflang/src/org/lflang/validation/LFValidator.xtend index 3e9c6b8e85..3f26fa9b9f 100644 --- a/org.lflang/src/org/lflang/validation/LFValidator.xtend +++ b/org.lflang/src/org/lflang/validation/LFValidator.xtend @@ -49,6 +49,8 @@ import org.lflang.lf.Action import org.lflang.lf.ActionOrigin import org.lflang.lf.AddExpr import org.lflang.lf.Assignment +import org.lflang.lf.BracketExpr +import org.lflang.lf.BraceExpr import org.lflang.lf.Connection import org.lflang.lf.CodeExpr import org.lflang.lf.Deadline @@ -64,7 +66,6 @@ import org.lflang.lf.KeyValuePair import org.lflang.lf.KeyValuePairs import org.lflang.lf.LfPackage.Literals import org.lflang.lf.Literal -import org.lflang.lf.ListExpr import org.lflang.lf.Model import org.lflang.lf.MulExpr import org.lflang.lf.NamedHost @@ -375,9 +376,16 @@ class LFValidator extends BaseLFValidator { } @Check(FAST) - def checkListExpr(ListExpr list) { - if (!target.supportsLfListLiterals()) { - error("Target " + target + " does not support list literals.", Literals.LIST_EXPR__ITEMS) + def checkBracketExpr(BracketExpr list) { + if (!target.supportsLfBracketListExpressions()) { + error("Target " + target + " does not support this expression form.", Literals.BRACKET_EXPR__ITEMS) + } + } + + @Check(FAST) + def checkBraceExpr(BraceExpr list) { + if (!target.supportsLfBraceListExpressions()) { + error("Target " + target + " does not support this expression form.", Literals.BRACE_EXPR__ITEMS) } } @@ -1284,7 +1292,7 @@ class LFValidator extends BaseLFValidator { // list of times val exprs = if (init.isAssign) { val list = init.asSingleValue - if (list instanceof ListExpr) list.items + if (list instanceof BracketExpr) list.items else if (list instanceof CodeExpr) return // cannot check it else { error("Expected a list of time values.", feature) From 640a04865f32c833fb08d999aee5aa6688c8419a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 7 Nov 2021 15:16:38 +0100 Subject: [PATCH 34/68] Refactor GeneratorBase --- org.lflang/src/org/lflang/ASTUtils.xtend | 7 +- .../org/lflang/generator/GeneratorBase.xtend | 44 ++----------- .../org/lflang/generator/c/CGenerator.xtend | 38 ++++++----- .../org/lflang/generator/ts/TSGenerator.kt | 44 ++----------- .../src/org/lflang/generator/ts/TsTypes.kt | 65 +++++++++++++++++++ .../org/lflang/validation/LFValidator.xtend | 1 + test/C/src/NativeListsAndTimes.lf | 7 +- 7 files changed, 106 insertions(+), 100 deletions(-) create mode 100644 org.lflang/src/org/lflang/generator/ts/TsTypes.kt diff --git a/org.lflang/src/org/lflang/ASTUtils.xtend b/org.lflang/src/org/lflang/ASTUtils.xtend index 6fef80d6a8..c83eaefbfe 100644 --- a/org.lflang/src/org/lflang/ASTUtils.xtend +++ b/org.lflang/src/org/lflang/ASTUtils.xtend @@ -46,6 +46,8 @@ import org.lflang.lf.ActionOrigin import org.lflang.lf.ArraySpec import org.lflang.lf.AddExpr import org.lflang.lf.Assignment +import org.lflang.lf.BraceExpr +import org.lflang.lf.BracketExpr import org.lflang.lf.Code import org.lflang.lf.CodeExpr import org.lflang.lf.Connection @@ -57,7 +59,6 @@ import org.lflang.lf.Initializer import org.lflang.lf.Instantiation import org.lflang.lf.LfFactory import org.lflang.lf.Literal -import org.lflang.lf.BracketExpr import org.lflang.lf.Model import org.lflang.lf.MulExpr import org.lflang.lf.Output @@ -768,8 +769,8 @@ class ASTUtils { Literal: v.literal AddExpr: "(" + v.left.toText + " " + v.op + " " + v.right.toText + ")" MulExpr: "(" + v.left.toText + " " + v.op + " " + v.right.toText + ")" - BracketExpr: v.items.isEmpty() ? "[]" : v.items.join('[', ',', ']', [ it.toText ]) - BraceExpr: v.items.isEmpty() ? "{}" : v.items.join('{', ',', '}', [ it.toText ]) + BracketExpr: v.items.isEmpty() ? "[]" : v.items.join('[', ', ', ']', [ it.toText ]) + BraceExpr: v.items.isEmpty() ? "{}" : v.items.join('{', ', ', '}', [ it.toText ]) TupleExpr: if (v.items.isEmpty) "()" else { diff --git a/org.lflang/src/org/lflang/generator/GeneratorBase.xtend b/org.lflang/src/org/lflang/generator/GeneratorBase.xtend index 926bbcec51..dccf068fc0 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorBase.xtend +++ b/org.lflang/src/org/lflang/generator/GeneratorBase.xtend @@ -80,7 +80,6 @@ import org.lflang.lf.Reactor import org.lflang.lf.StateVar import org.lflang.lf.TargetDecl import org.lflang.lf.Time -import org.lflang.lf.TimeUnit import org.lflang.lf.Value import org.lflang.lf.VarRef import org.lflang.lf.Variable @@ -727,28 +726,6 @@ abstract class GeneratorBase extends AbstractLFValidator implements TargetTypes if (reactionBankIndices.get(reaction) === null) return -1 return reactionBankIndices.get(reaction) } - - /** - * Given a representation of time that may possibly include units, return - * a string that the target language can recognize as a value. In this base - * class, if units are given, e.g. "msec", then we convert the units to upper - * case and return an expression of the form "MSEC(value)". Particular target - * generators will need to either define functions or macros for each possible - * time unit or override this method to return something acceptable to the - * target language. - * @param time A TimeValue that represents a time. - * @return A string, such as "MSEC(100)" for 100 milliseconds. - */ - def String timeInTargetLanguage(TimeValue time) { - if (time !== null) { - if (time.unit != TimeUnit.NONE) { - return time.unit.name() + '(' + time.time + ')' - } else { - return time.time.toString() - } - } - return "0" // FIXME: do this or throw exception? - } /** * Run the custom build command specified with the "build" parameter. @@ -1354,8 +1331,6 @@ abstract class GeneratorBase extends AbstractLFValidator implements TargetTypes return init?.exprs.map[i| if (i instanceof ParamRef) { i.parameter.targetReference - } else if (type.isTime) { - i.targetTime } else { i.targetValue } @@ -1462,7 +1437,7 @@ abstract class GeneratorBase extends AbstractLFValidator implements TargetTypes * @return An RTI-compatible (ie. C target) time string */ protected def getRTITime(Delay d) { - return d.value.targetTime + return d.value.targetValue } @@ -1791,23 +1766,14 @@ abstract class GeneratorBase extends AbstractLFValidator implements TargetTypes * @return A time string in the target language */ protected def getTargetTime(Time t) { - val value = new TimeValue(t.interval, t.unit) - return value.timeInTargetLanguage + return t.targetValue } /** - * Get textual representation of a value in the target language. - * - * If the value evaluates to 0, it is interpreted as a normal value. - * - * @param v A time AST node - * @return A time string in the target language + * Returns the textual representation of a value in the target language. */ protected def getTargetValue(Value v) { - if (v instanceof Time) { - return v.targetTime - } - return v.toText + return getTargetExpr(v, null) } /** @@ -1819,7 +1785,7 @@ abstract class GeneratorBase extends AbstractLFValidator implements TargetTypes * @return A time string in the target language */ protected def String getTargetTime(Value v) { - v.timeValue.timeInTargetLanguage + v.timeValue.targetTimeExpr } protected def String getTargetTime(Delay d) { diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend index 3a86789e2a..243fd7812d 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend @@ -35,7 +35,6 @@ import java.util.LinkedHashSet import java.util.LinkedList import java.util.Set import java.util.concurrent.Executors -import java.util.concurrent.TimeUnit import java.util.regex.Pattern import org.eclipse.emf.common.CommonPlugin import org.eclipse.emf.ecore.EObject @@ -87,6 +86,7 @@ import org.lflang.lf.Reaction import org.lflang.lf.Reactor import org.lflang.lf.ReactorDecl import org.lflang.lf.Timer +import org.lflang.lf.TimeUnit import org.lflang.lf.TriggerRef import org.lflang.lf.TypedVariable import org.lflang.lf.VarRef @@ -898,7 +898,7 @@ class CGenerator extends GeneratorBase { compileThreadPool.shutdown(); // Wait for all compile threads to finish (FIXME: Can block forever) - compileThreadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); + compileThreadPool.awaitTermination(Long.MAX_VALUE, java.util.concurrent.TimeUnit.NANOSECONDS); // Restore the base filename. topLevelName = baseFilename @@ -1381,7 +1381,7 @@ class CGenerator extends GeneratorBase { // Insert the #defines at the beginning code.insert(0, ''' #define _LF_CLOCK_SYNC_INITIAL - #define _LF_CLOCK_SYNC_PERIOD_NS «targetConfig.clockSyncOptions.period.timeInTargetLanguage» + #define _LF_CLOCK_SYNC_PERIOD_NS «targetConfig.clockSyncOptions.period.targetTimeExpr» #define _LF_CLOCK_SYNC_EXCHANGES_PER_INTERVAL «targetConfig.clockSyncOptions.trials» #define _LF_CLOCK_SYNC_ATTENUATION «targetConfig.clockSyncOptions.attenuation» ''') @@ -1433,7 +1433,7 @@ class CGenerator extends GeneratorBase { val stp = param.init.asSingleValue?.getTimeValue if (stp !== null) { pr(''' - set_stp_offset(«stp.timeInTargetLanguage»); + set_stp_offset(«stp.targetTimeExpr»); ''') } } @@ -3464,8 +3464,8 @@ class CGenerator extends GeneratorBase { var trigger = triggerInstance.definition var triggerStructName = triggerStructName(triggerInstance) if (trigger instanceof Timer && !triggerInstance.isStartup) { - val offset = timeInTargetLanguage((triggerInstance as TimerInstance).offset) - val period = timeInTargetLanguage((triggerInstance as TimerInstance).period) + val offset = (triggerInstance as TimerInstance).offset.targetTimeExpr + val period = (triggerInstance as TimerInstance).period.targetTimeExpr pr(initializeTriggerObjects, ''' «triggerStructName».offset = «offset»; «triggerStructName».period = «period»; @@ -3478,9 +3478,9 @@ class CGenerator extends GeneratorBase { var minDelay = (triggerInstance as ActionInstance).minDelay var minSpacing = (triggerInstance as ActionInstance).minSpacing pr(initializeTriggerObjects, ''' - «triggerStructName».offset = «timeInTargetLanguage(minDelay)»; + «triggerStructName».offset = «minDelay.targetTimeExpr»; «IF minSpacing !== null» - «triggerStructName».period = «timeInTargetLanguage(minSpacing)»; + «triggerStructName».period = «minSpacing.targetTimeExpr»; «ELSE» «triggerStructName».period = «CGenerator.UNDEFINED_MIN_SPACING»; «ENDIF» @@ -4094,7 +4094,7 @@ class CGenerator extends GeneratorBase { var deadline = reaction.declaredDeadline.maxDelay val reactionStructName = '''«selfStructName(reaction.parent)»->_lf__reaction_«reactionCount»''' pr(initializeTriggerObjects, ''' - «reactionStructName».deadline = «timeInTargetLanguage(deadline)»; + «reactionStructName».deadline = «deadline.targetTimeExpr»; ''') } } @@ -4134,7 +4134,7 @@ class CGenerator extends GeneratorBase { parameter cooridiation-options with a value like {advance-message-interval: 10 msec}"''') } pr(initializeTriggerObjects, ''' - _fed.min_delay_from_physical_action_to_federate_output = «minDelay.timeInTargetLanguage»; + _fed.min_delay_from_physical_action_to_federate_output = «minDelay.targetTimeExpr»; ''') } } @@ -4461,6 +4461,14 @@ class CGenerator extends GeneratorBase { } return result.toString } + + override String getTargetTimeExpr(long magnitude, TimeUnit unit) { + if (unit != TimeUnit.NONE) { + return unit.name() + '(' + magnitude + ')' + } else { + return magnitude.toString() + } + } protected def getInitializer(Initializer init, InferredType t, ReactorInstance parent) { if (init === null) @@ -4471,8 +4479,6 @@ class CGenerator extends GeneratorBase { for (i : init.exprs) { if (i instanceof ParamRef) { list.add(parent.selfStructName + "->" + i.parameter.name) - } else if (t.isTime) { - list.add(i.targetTime) } else { list.add(i.targetValue) } @@ -4735,7 +4741,7 @@ class CGenerator extends GeneratorBase { var String next_destination_name = '''"federate «receivingFed.id»"''' // Get the delay literal - var String additionalDelayString = delay?.timeInTargetLanguage ?: "NEVER"; + var String additionalDelayString = delay?.targetTimeExpr ?: "NEVER"; if (isPhysical) { messageType = "MSG_TYPE_P2P_MESSAGE" @@ -4850,7 +4856,7 @@ class CGenerator extends GeneratorBase { // Find the maximum STP for decentralized coordination if(isFederatedAndDecentralized) { result.append(''' - max_STP = «maxSTP.timeInTargetLanguage»; + max_STP = «maxSTP.targetTimeExpr»; ''') } @@ -4886,7 +4892,7 @@ class CGenerator extends GeneratorBase { var sendRef = generatePortRef(port, sendingBankIndex, sendingChannelIndex); // Get the delay literal - var String additionalDelayString = delay?.timeInTargetLanguage ?: "NEVER" + var String additionalDelayString = delay?.targetTimeExpr ?: "NEVER" result.append(''' // If the output port has not been SET for the current logical time, @@ -4999,7 +5005,7 @@ class CGenerator extends GeneratorBase { pr('#define TARGET_FILES_DIRECTORY "' + fileConfig.srcGenPath + '"'); if (targetConfig.coordinationOptions.advance_message_interval !== null) { - pr('#define ADVANCE_MESSAGE_INTERVAL ' + targetConfig.coordinationOptions.advance_message_interval.timeInTargetLanguage) + pr('#define ADVANCE_MESSAGE_INTERVAL ' + targetConfig.coordinationOptions.advance_message_interval.targetTimeExpr) } includeTargetLanguageSourceFiles() diff --git a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt index 4c26250da0..e2fde19ded 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt @@ -39,6 +39,7 @@ import org.lflang.scoping.LFGlobalScopeProvider import java.nio.file.Files import java.util.* import org.lflang.federated.serialization.SupportedSerializers +import org.lflang.generator.TargetTypes /** * Generator for TypeScript target. @@ -53,7 +54,8 @@ class TSGenerator( private val tsFileConfig: TSFileConfig, errorReporter: ErrorReporter, private val scopeProvider: LFGlobalScopeProvider -) : GeneratorBase(tsFileConfig, errorReporter) { +) : GeneratorBase(tsFileConfig, errorReporter), + TargetTypes by TsTypes { companion object { /** Path to the Cpp lib directory (relative to class path) */ @@ -342,22 +344,8 @@ class TSGenerator( } } - /** Given a representation of time that may possibly include units, - * return a string that TypeScript can recognize as a value. - * @param value Literal that represents a time value. - * @return A string, as "[ timeLiteral, TimeUnit.unit]" . - */ - override fun timeInTargetLanguage(value: TimeValue): String { - return if (value.unit != TimeUnit.NONE) { - "TimeValue.${value.unit}(${value.time})" - } else { - // The value must be zero. - "TimeValue.zero()" - } - } - override fun getTargetType(s: StateVar): String { - val type = super.getTargetType(s) + val type = super.getTargetType(s) return if (s.init == null) { "$type | undefined" } else { @@ -499,30 +487,6 @@ class TSGenerator( return "T extends Present" } - override fun supportsGenerics(): Boolean { - return true - } - - override fun getTargetTimeType(): String { - return "TimeValue" - } - - override fun getTargetTagType(): String { - return "TimeValue" - } - - override fun getTargetUndefinedType(): String { - return "Present" - } - - override fun getTargetFixedSizeListType(baseType: String, size: Int): String { - return "Array($size)<$baseType>" - } - - override fun getTargetVariableSizeListType(baseType: String): String { - return "Array<$baseType>" - } - override fun getTarget(): Target { return Target.TS } diff --git a/org.lflang/src/org/lflang/generator/ts/TsTypes.kt b/org.lflang/src/org/lflang/generator/ts/TsTypes.kt new file mode 100644 index 0000000000..315d23bae2 --- /dev/null +++ b/org.lflang/src/org/lflang/generator/ts/TsTypes.kt @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2021, TU Dresden. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * 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 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 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.lflang.generator.ts + +import org.lflang.generator.TargetTypes +import org.lflang.lf.TimeUnit + +/** + * [TargetTypes] implementation for [TSGenerator]. + * + * @author Clément Fournier + * @author Hokeun Kim + */ +object TsTypes : TargetTypes { + override fun supportsGenerics(): Boolean = true + + override fun getTargetTimeType(): String { + return "TimeValue" + } + + override fun getTargetTagType(): String { + return "TimeValue" + } + + override fun getTargetUndefinedType(): String { + return "Present" + } + + override fun getTargetFixedSizeListType(baseType: String, size: Int): String { + return "Array($size)<$baseType>" + } + + override fun getTargetVariableSizeListType(baseType: String): String { + return "Array<$baseType>" + } + + + override fun getTargetTimeExpr(magnitude: Long, unit: TimeUnit): String = + if (unit != TimeUnit.NONE) "TimeValue.$unit($magnitude)" + // The value must be zero. + else "TimeValue.zero()" + +} diff --git a/org.lflang/src/org/lflang/validation/LFValidator.xtend b/org.lflang/src/org/lflang/validation/LFValidator.xtend index 3f26fa9b9f..bafb8f3a7f 100644 --- a/org.lflang/src/org/lflang/validation/LFValidator.xtend +++ b/org.lflang/src/org/lflang/validation/LFValidator.xtend @@ -1293,6 +1293,7 @@ class LFValidator extends BaseLFValidator { val exprs = if (init.isAssign) { val list = init.asSingleValue if (list instanceof BracketExpr) list.items + if (list instanceof BraceExpr) list.items else if (list instanceof CodeExpr) return // cannot check it else { error("Expected a list of time values.", feature) diff --git a/test/C/src/NativeListsAndTimes.lf b/test/C/src/NativeListsAndTimes.lf index d75b511779..83ab42783f 100644 --- a/test/C/src/NativeListsAndTimes.lf +++ b/test/C/src/NativeListsAndTimes.lf @@ -6,9 +6,12 @@ main reactor(x:int(0), y:time(0), // Units are missing but not required z(1 msec), // Type is missing but not required p:int[](1, 2, 3, 4), // List of integers + p2:int[] = { 1, 2, 3, 4 }, // List of integers q:interval_t[](1 msec, 2 msec, 3 msec), // list of time values + q2:interval_t[] = { 1 msec, 2 msec, 3 msec }, // list of time values r:time({=0=}), // Zero-valued target code also is a valid time - g:time[](1 msec, 2 msec) // List of time values + g:time[](1 msec, 2 msec), // List of time values + g2:time[] = {1 msec, 2 msec} // List of time values ) { state s:time(y); // Reference to explicitly typed time parameter state t:time(z); // Reference to implicitly typed time parameter @@ -22,4 +25,4 @@ main reactor(x:int(0), reaction(tick) {= // Target code =} -} \ No newline at end of file +} From 08ed64309d32cf31273352d9975fa51c55d14a8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 7 Nov 2021 15:23:53 +0100 Subject: [PATCH 35/68] More cleanups for TS --- .../lflang/generator/ts/TSActionGenerator.kt | 18 ++++++------------ .../src/org/lflang/generator/ts/TSGenerator.kt | 4 ---- .../lflang/generator/ts/TSReactorGenerator.kt | 4 ++-- .../src/org/lflang/generator/ts/TsTypes.kt | 5 +++++ 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/ts/TSActionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSActionGenerator.kt index 8e5e670d76..e58a32b7bd 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSActionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSActionGenerator.kt @@ -1,5 +1,6 @@ package org.lflang.generator.ts +import org.lflang.InferredType import org.lflang.lf.Action import org.lflang.lf.ParamRef import org.lflang.lf.Type @@ -9,13 +10,9 @@ import java.util.* /** * Generator for actions in TypeScript target. */ -class TSActionGenerator ( - // TODO(hokeun): Remove dependency on TSGenerator. - private val tsGenerator: TSGenerator, +class TSActionGenerator( private val actions: List ) { - private fun Value.getTargetValue(): String = tsGenerator.getTargetValueW(this) - private fun Type.getTargetType(): String = tsGenerator.getTargetTypeW(this) /** * Return a TS type for the specified action. @@ -24,19 +21,16 @@ class TSActionGenerator ( * @param action The action * @return The TS type. */ - private fun getActionType(action: Action): String { + private fun getActionType(action: Action): String = // Special handling for the networkMessage action created by // FedASTUtils.makeCommunication(), by assigning TypeScript // Buffer type for the action. Action is used as // FederatePortAction in federation.ts. if (action.name == "networkMessage") { - return "Buffer" - } else if (action.type != null) { - return action.type.getTargetType() + "Buffer" } else { - return "Present" + TsTypes.getTargetType(action.type) } - } fun generateClassProperties(): String { val stateClassProperties = LinkedList() @@ -64,7 +58,7 @@ class TSActionGenerator ( if (action.minDelay != null) { // Actions in the TypeScript target are constructed // with an optional minDelay argument which defaults to 0. - actionArgs += ", " + action.minDelay.getTargetValue() + actionArgs += ", " + TsTypes.getTargetExpr(action.minDelay, InferredType.time()) } actionInstantiations.add( "this.${action.name} = new __Action<${getActionType(action)}>($actionArgs);") diff --git a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt index e2fde19ded..5f2084c03f 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt @@ -353,10 +353,6 @@ class TSGenerator( } } - override fun getTargetReference(param: Parameter): String { - return "this.${param.name}.get()" - } - /** * Generate code for the body of a reaction that handles the * action that is triggered by receiving a message from a remote diff --git a/org.lflang/src/org/lflang/generator/ts/TSReactorGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSReactorGenerator.kt index d4a8b2b9cb..5dba363ae0 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSReactorGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSReactorGenerator.kt @@ -117,7 +117,7 @@ class TSReactorGenerator( val timerGenerator = TSTimerGenerator(tsGenerator, reactor.timers) val parameterGenerator = TSParameterGenerator(tsGenerator, reactor.parameters) val stateGenerator = TSStateGenerator(tsGenerator, reactor.stateVars) - val actionGenerator = TSActionGenerator(tsGenerator, reactor.actions) + val actionGenerator = TSActionGenerator(reactor.actions) val portGenerator = TSPortGenerator(tsGenerator, reactor.inputs, reactor.outputs) val constructorGenerator = TSConstructorGenerator(tsGenerator, errorReporter, reactor, federate) @@ -151,4 +151,4 @@ class TSReactorGenerator( """ }.trimMargin() } -} \ No newline at end of file +} diff --git a/org.lflang/src/org/lflang/generator/ts/TsTypes.kt b/org.lflang/src/org/lflang/generator/ts/TsTypes.kt index 315d23bae2..c5d6fadaa7 100644 --- a/org.lflang/src/org/lflang/generator/ts/TsTypes.kt +++ b/org.lflang/src/org/lflang/generator/ts/TsTypes.kt @@ -24,7 +24,10 @@ package org.lflang.generator.ts +import org.lflang.InferredType import org.lflang.generator.TargetTypes +import org.lflang.lf.ParamRef +import org.lflang.lf.Parameter import org.lflang.lf.TimeUnit /** @@ -62,4 +65,6 @@ object TsTypes : TargetTypes { // The value must be zero. else "TimeValue.zero()" + override fun getTargetParamRef(expr: ParamRef, type: InferredType?): String = + "this.${expr.parameter.name}.get()" } From 8ebc6e6038fddf3419fd74cd197738a9c0a32e26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 7 Nov 2021 15:41:03 +0100 Subject: [PATCH 36/68] Cleanup TS further --- .../org/lflang/generator/GeneratorUtils.kt | 14 ++- .../generator/ts/TSConstructorGenerator.kt | 15 ++- .../org/lflang/generator/ts/TSGenerator.kt | 33 +------ .../generator/ts/TSParameterGenerator.kt | 13 +-- .../ts/TSParameterPreambleGenerator.kt | 32 +++--- .../lflang/generator/ts/TSPortGenerator.kt | 37 ++----- .../generator/ts/TSReactionGenerator.kt | 98 +++++++------------ .../lflang/generator/ts/TSReactorGenerator.kt | 6 +- .../lflang/generator/ts/TSStateGenerator.kt | 7 +- .../lflang/generator/ts/TSTimerGenerator.kt | 15 ++- .../src/org/lflang/generator/ts/TsTypes.kt | 16 +++ 11 files changed, 126 insertions(+), 160 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/GeneratorUtils.kt b/org.lflang/src/org/lflang/generator/GeneratorUtils.kt index 72c501c000..4c0aac2066 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorUtils.kt +++ b/org.lflang/src/org/lflang/generator/GeneratorUtils.kt @@ -2,8 +2,14 @@ package org.lflang.generator import org.eclipse.emf.ecore.EObject import org.eclipse.xtext.nodemodel.util.NodeModelUtils -import org.lflang.* -import org.lflang.lf.* +import org.lflang.InferredType +import org.lflang.lf.LfFactory +import org.lflang.lf.Parameter +import org.lflang.lf.StateVar +import org.lflang.lf.Value +import org.lflang.toPath +import org.lflang.toTextTokenBased +import org.lflang.toUnixString fun TargetTypes.getTargetInitializer(sv: StateVar): TargetCode = @@ -15,6 +21,10 @@ fun TargetTypes.getTargetInitializer(sv: Parameter): TargetCode = fun TargetTypes.getTargetTimeExpr(v: Value): TargetCode = this.getTargetExpr(v, InferredType.time()) +/** If this is null, returns the literal 0. */ +fun Value?.orZero(): Value = + this ?: LfFactory.eINSTANCE.createLiteral().apply { literal = "0" } + /** A transparent type alias to document when a string contains target code. */ typealias TargetCode = String diff --git a/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt index 543907b81d..f95fa76266 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt @@ -3,6 +3,7 @@ package org.lflang.generator.ts import org.lflang.ErrorReporter import org.lflang.federated.FederateInstance import org.lflang.generator.PrependOperator +import org.lflang.inferredType import org.lflang.lf.Action import org.lflang.lf.Parameter import org.lflang.lf.Reactor @@ -16,20 +17,22 @@ import java.util.* * and code to register reactions. This generator also generates federate port action * registrations. */ -class TSConstructorGenerator ( +class TSConstructorGenerator( private val tsGenerator: TSGenerator, private val errorReporter: ErrorReporter, - private val reactor : Reactor, + private val reactor: Reactor, private val federate: FederateInstance ) { private fun getInitializerList(param: Parameter): List = tsGenerator.getInitializerListW(param) - private fun Parameter.getTargetType(): String = tsGenerator.getTargetTypeW(this) + private fun Parameter.getTargetType(): String = TsTypes.getTargetType(this.inferredType) // Initializer functions - private fun getTargetInitializerHelper(param: Parameter, - list: List): String { + private fun getTargetInitializerHelper( + param: Parameter, + list: List + ): String { return if (list.size == 0) { errorReporter.reportError(param, "Parameters must have a default value!") } else if (list.size == 1) { @@ -38,9 +41,11 @@ class TSConstructorGenerator ( list.joinToString(", ", "[", "]") } } + private fun getTargetInitializer(param: Parameter): String { return getTargetInitializerHelper(param, getInitializerList(param)) } + private fun initializeParameter(p: Parameter): String { return """${p.name}: ${p.getTargetType()} = ${getTargetInitializer(p)}""" } diff --git a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt index 5f2084c03f..8ca9e5bcd4 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt @@ -86,11 +86,7 @@ class TSGenerator( // Wrappers to expose GeneratorBase methods. fun federationRTIPropertiesW() = federationRTIProperties - fun getTargetValueW(v: Value): String = getTargetValue(v) - fun getTargetTypeW(p: Parameter): String = getTargetType(p.inferredType) - fun getTargetTypeW(state: StateVar): String = getTargetType(state) - fun getTargetTypeW(t: Type): String = getTargetType(t) - + // todo(clément): get rid of those fun getInitializerListW(state: StateVar): List = getInitializerList(state.init, state.inferredType) fun getInitializerListW(param: Parameter): List = getInitializerList(param.init, param.inferredType) fun getInitializerListW(param: Parameter, i: Instantiation): List = getInitializerList(param, i) @@ -329,29 +325,6 @@ class TSGenerator( // } } - /** - * Return a TS type for the specified action. - * If the type has not been specified, return - * "Present" which is the base type for Actions. - * @param action The action - * @return The TS type. - */ - private fun getActionType(action: Action): String { - return if (action.type != null) { - getTargetType(action.type) - } else { - "Present" - } - } - - override fun getTargetType(s: StateVar): String { - val type = super.getTargetType(s) - return if (s.init == null) { - "$type | undefined" - } else { - type - } - } /** * Generate code for the body of a reaction that handles the @@ -472,11 +445,11 @@ class TSGenerator( // Virtual methods. override fun generateDelayBody(action: Action, port: VarRef): String { - return "actions.${action.name}.schedule(0, ${generateVarRef(port)} as ${getActionType(action)});" + return "actions.${action.name}.schedule(0, ${generateVarRef(port)} as ${getTargetType(action.type)});" } override fun generateForwardBody(action: Action, port: VarRef): String { - return "${generateVarRef(port)} = ${action.name} as ${getActionType(action)};" + return "${generateVarRef(port)} = ${action.name} as ${getTargetType(action.type)};" } override fun generateDelayGeneric(): String { diff --git a/org.lflang/src/org/lflang/generator/ts/TSParameterGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSParameterGenerator.kt index 1cd987b815..c8dbbc2c96 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSParameterGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSParameterGenerator.kt @@ -1,23 +1,20 @@ package org.lflang.generator.ts -import org.lflang.generator.PrependOperator +import org.lflang.inferredType import org.lflang.lf.Parameter import java.util.* /** * Generate parameters for TypeScript target. */ -class TSParameterGenerator ( - // TODO(hokeun): Remove dependency on TSGenerator. - private val tsGenerator: TSGenerator, +class TSParameterGenerator( private val parameters: List - ) { - private fun Parameter.getTargetType(): String = tsGenerator.getTargetTypeW(this) +) { fun generateClassProperties(): String { val paramClassProperties = LinkedList() for (param in parameters) { - paramClassProperties.add("${param.name}: __Parameter<${param.getTargetType()}>;") + paramClassProperties.add("${param.name}: __Parameter<${TsTypes.getTargetType(param.inferredType)}>;") } return paramClassProperties.joinToString("\n") } @@ -29,4 +26,4 @@ class TSParameterGenerator ( } return paramInstantiations.joinToString("\n") } -} \ No newline at end of file +} diff --git a/org.lflang/src/org/lflang/generator/ts/TSParameterPreambleGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSParameterPreambleGenerator.kt index a29e6d6c83..ebb6f00b12 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSParameterPreambleGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSParameterPreambleGenerator.kt @@ -29,6 +29,7 @@ import org.lflang.FileConfig import org.lflang.TargetConfig import org.lflang.TimeValue import org.lflang.generator.PrependOperator +import org.lflang.inferredType import org.lflang.lf.Parameter import org.lflang.lf.Reactor import org.lflang.lf.TimeUnit @@ -50,7 +51,7 @@ class TSParameterPreambleGenerator( private val targetConfig: TargetConfig, private val reactors: MutableList ) { - private fun getTargetType(p: Parameter): String = tsGenerator.getTargetTypeW(p) + private fun getTargetType(p: Parameter): String = TsTypes.getTargetType(p.inferredType) private fun getTimeoutTimeValue(): String { return if (targetConfig.timeout != null) { @@ -85,7 +86,8 @@ class TSParameterPreambleGenerator( private fun assignCustomCLArgs(mainParameters: HashSet): String { val code = StringJoiner("\n") for (parameter in mainParameters) { - code.add(""" + code.add( + """ |let __CL${parameter.name}: ${getTargetType(parameter)} | undefined = undefined; |if (__processedCLArgs.${parameter.name} !== undefined) { | if (__processedCLArgs.${parameter.name} !== null) { @@ -95,7 +97,8 @@ class TSParameterPreambleGenerator( | throw new Error("Custom '${parameter.name}' command line argument is malformed."); | } |} - """) + """ + ) } return code.toString() } @@ -111,11 +114,13 @@ class TSParameterPreambleGenerator( // to cause the generation of variables with a "__" prefix // because they could collide with other variables. // So prefix variables created here with __CL - code.add(""" + code.add( + """ |if (__processedCLArgs.${parameter.name} !== undefined && __processedCLArgs.${parameter.name} !== null | && !__noStart) { | Log.global.info("'${parameter.name}' property overridden by command line argument."); - |}""") + |}""" + ) } return code.toString() } @@ -157,20 +162,24 @@ class TSParameterPreambleGenerator( if (customArgType != null) { clTypeExtension.add(parameter.name + ": " + paramType) if (customTypeLabel != null) { - customArgs.add(with(PrependOperator) {""" + customArgs.add(with(PrependOperator) { + """ |{ | name: '${parameter.name}', | type: ${customArgType}, | typeLabel: "{underline ${customTypeLabel}}", | description: 'Custom argument. Refer to ${fileConfig.srcFile} for documentation.' - |}""".trimMargin()}) + |}""".trimMargin() + }) } else { - customArgs.add(with(PrependOperator) {""" + customArgs.add(with(PrependOperator) { + """ |{ | name: '${parameter.name}', | type: ${customArgType}, | description: 'Custom argument. Refer to ${fileConfig.srcFile} for documentation.' - |}""".trimMargin()}) + |}""".trimMargin() + }) } } } @@ -178,7 +187,8 @@ class TSParameterPreambleGenerator( val customArgsList = "[\n$customArgs]" val clTypeExtensionDef = "{$clTypeExtension}" - val codeText = with(PrependOperator) {""" + val codeText = with(PrependOperator) { + """ |// ************* App Parameters |let __timeout: TimeValue | undefined = ${getTimeoutTimeValue()}; |let __keepAlive: boolean = ${targetConfig.keepalive}; @@ -302,4 +312,4 @@ class TSParameterPreambleGenerator( return Pair(mainParameters, codeText) } -} \ No newline at end of file +} diff --git a/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt index 35bcfa42df..2f843ef6f4 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt @@ -1,56 +1,37 @@ package org.lflang.generator.ts +import org.lflang.generator.ts.TsTypes.getTargetType import org.lflang.lf.Input import org.lflang.lf.Output -import org.lflang.lf.Port -import org.lflang.lf.Type import java.util.* /** * Generate input and output ports for TypeScript target. */ -class TSPortGenerator ( - // TODO(hokeun): Remove dependency on TSGenerator. - private val tsGenerator: TSGenerator, +class TSPortGenerator( private val inputs: List, private val outputs: List ) { - private fun Type.getTargetType(): String = tsGenerator.getTargetTypeW(this) - - /** - * Return a TS type for the specified port. - * If the type has not been specified, return - * "Present" which is the base type for ports. - * @param port The port - * @return The TS type. - */ - private fun getPortType(port: Port): String { - if (port.type != null) { - return port.type.getTargetType() - } else { - return "Present" - } - } fun generateClassProperties(): String { - val portClassProperties = LinkedList() + val portClassProperties = mutableListOf() for (input in inputs) { - portClassProperties.add("${input.name}: __InPort<${getPortType(input)}>;") + portClassProperties.add("${input.name}: __InPort<${getTargetType(input.type)}>;") } for (output in outputs) { - portClassProperties.add("${output.name}: __OutPort<${getPortType(output)}>;") + portClassProperties.add("${output.name}: __OutPort<${getTargetType(output.type)}>;") } return portClassProperties.joinToString("\n") } fun generateInstantiations(): String { - val porInstantiations = LinkedList() + val porInstantiations = mutableListOf() for (input in inputs) { - porInstantiations.add("this.${input.name} = new __InPort<${getPortType(input)}>(this);") + porInstantiations.add("this.${input.name} = new __InPort<${getTargetType(input.type)}>(this);") } for (output in outputs) { - porInstantiations.add("this.${output.name} = new __OutPort<${getPortType(output)}>(this);") + porInstantiations.add("this.${output.name} = new __OutPort<${getTargetType(output.type)}>(this);") } return porInstantiations.joinToString("\n") } -} \ No newline at end of file +} diff --git a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt index aa50b65e73..19305a0d2f 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt @@ -3,6 +3,9 @@ package org.lflang.generator.ts import org.lflang.ErrorReporter import org.lflang.federated.FederateInstance import org.lflang.generator.PrependOperator +import org.lflang.generator.getTargetTimeExpr +import org.lflang.generator.orZero +import org.lflang.inferredType import org.lflang.lf.* import org.lflang.lf.Timer import org.lflang.toText @@ -22,45 +25,12 @@ class TSReactionGenerator( // TODO(hokeun): Remove dependency on TSGenerator. private val tsGenerator: TSGenerator, private val errorReporter: ErrorReporter, - private val reactor : Reactor, + private val reactor: Reactor, private val federate: FederateInstance ) { - private fun Value.getTargetValue(): String = tsGenerator.getTargetValueW(this) - private fun Parameter.getTargetType(): String = tsGenerator.getTargetTypeW(this) - private fun StateVar.getTargetType(): String = tsGenerator.getTargetTypeW(this) - private fun Type.getTargetType(): String = tsGenerator.getTargetTypeW(this) private fun VarRef.generateVarRef(): String = tsGenerator.generateVarRef(this) - /** - * Return a TS type for the specified action. - * If the type has not been specified, return - * "Present" which is the base type for Actions. - * @param action The action - * @return The TS type. - */ - private fun getActionType(action: Action): String { - if (action.type != null) { - return action.type.getTargetType() - } else { - return "Present" - } - } - - /** - * Return a TS type for the specified port. - * If the type has not been specified, return - * "Present" which is the base type for ports. - * @param port The port - * @return The TS type. - */ - private fun getPortType(port: Port): String { - if (port.type != null) { - return port.type.getTargetType() - } else { - return "Present" - } - } private fun generateArg(v: VarRef): String { return if (v.container != null) { @@ -76,13 +46,7 @@ class TSReactionGenerator( reactEpilogue: String, reactSignature: StringJoiner ): String { - var deadlineArgs = "" - val delay = reaction.deadline.delay - if (delay is ParamRef) { - deadlineArgs += "this.${delay.parameter.name}.get()" - } else { - deadlineArgs += delay.getTargetValue() - } + val deadlineArgs = TsTypes.getTargetTimeExpr(reaction.deadline.delay.orZero()) return with(PrependOperator) { """ @@ -140,14 +104,21 @@ class TSReactionGenerator( ${" | "..reactEpilogue} | // =============== END react epilogue | } - ${" | "..if (reaction.deadline != null) generateDeadlineHandler(reaction, reactPrologue, reactEpilogue, reactSignature) else "}"} + ${ + " | "..if (reaction.deadline != null) generateDeadlineHandler( + reaction, + reactPrologue, + reactEpilogue, + reactSignature + ) else "}" + } |); """.trimMargin() } } // TODO(hokeun): Decompose this function further. - private fun generateSingleReaction(reactor : Reactor, reaction: Reaction): String { + private fun generateSingleReaction(reactor: Reactor, reaction: Reaction): String { // Determine signature of the react function val reactSignature = StringJoiner(", ") reactSignature.add("this") @@ -222,12 +193,12 @@ class TSReactionGenerator( } else if (trigOrSource.variable is Timer) { reactSignatureElementType = "__Tag" } else if (trigOrSource.variable is Action) { - reactSignatureElementType = getActionType(trigOrSource.variable as Action) + reactSignatureElementType = TsTypes.getTargetType((trigOrSource.variable as Action).inferredType) } else if (trigOrSource.variable is Port) { - reactSignatureElementType = getPortType(trigOrSource.variable as Port) + reactSignatureElementType = TsTypes.getTargetType((trigOrSource.variable as Port).inferredType) } - reactSignature.add("${generateArg(trigOrSource)}: Read<${reactSignatureElementType}>") + reactSignature.add("${generateArg(trigOrSource)}: Read<$reactSignatureElementType>") reactFunctArgs.add("this.${trigOrSource.generateVarRef()}") if (trigOrSource.container == null) { reactPrologue.add("let ${trigOrSource.variable.name} = ${generateArg(trigOrSource)}.get();") @@ -253,23 +224,25 @@ class TSReactionGenerator( var reactSignatureElement = generateArg(effect) if (effect.variable is Timer) { errorReporter.reportError("A timer cannot be an effect of a reaction") - } else if (effect.variable is Action){ - reactSignatureElement += ": Sched<" + getActionType(effect.variable as Action) + ">" + } else if (effect.variable is Action) { + reactSignatureElement += ": Sched<" + TsTypes.getTargetType((effect.variable as Action).inferredType) + ">" schedActionSet.add(effect.variable as Action) - } else if (effect.variable is Port){ - reactSignatureElement += ": ReadWrite<" + getPortType(effect.variable as Port) + ">" + } else if (effect.variable is Port) { + reactSignatureElement += ": ReadWrite<" + TsTypes.getTargetType((effect.variable as Port).inferredType) + ">" if (effect.container == null) { - reactEpilogue.add(with(PrependOperator) {""" + reactEpilogue.add(with(PrependOperator) { + """ |if (${effect.variable.name} !== undefined) { | __${effect.variable.name}.set(${effect.variable.name}); - |}""".trimMargin()}) + |}""".trimMargin() + }) } } reactSignature.add(reactSignatureElement) functArg = "this." + effect.generateVarRef() - if (effect.variable is Action){ + if (effect.variable is Action) { reactFunctArgs.add("this.schedulable($functArg)") } else if (effect.variable is Port) { reactFunctArgs.add("this.writable($functArg)") @@ -303,7 +276,7 @@ class TSReactionGenerator( for (param in reactor.parameters) { // Underscores are added to parameter names to prevent conflict with prologue - reactSignature.add("__${param.name}: __Parameter<${param.getTargetType()}>") + reactSignature.add("__${param.name}: __Parameter<${TsTypes.getTargetType(param.inferredType)}>") reactFunctArgs.add("this.${param.name}") reactPrologue.add("let ${param.name} = __${param.name}.get();") @@ -312,14 +285,16 @@ class TSReactionGenerator( // Add state to the react function for (state in reactor.stateVars) { // Underscores are added to state names to prevent conflict with prologue - reactSignature.add("__${state.name}: __State<${state.getTargetType()}>") + reactSignature.add("__${state.name}: __State<${TsTypes.getTargetType(state)}>") reactFunctArgs.add("this.${state.name}") reactPrologue.add("let ${state.name} = __${state.name}.get();") - reactEpilogue.add(with(PrependOperator) {""" + reactEpilogue.add(with(PrependOperator) { + """ |if (${state.name} !== undefined) { | __${state.name}.set(${state.name}); - |}""".trimMargin()}) + |}""".trimMargin() + }) } // Initialize objects to enable hierarchical references. @@ -328,10 +303,12 @@ class TSReactionGenerator( for (variable in entry.value) { initializer.add("${variable.name}: __${entry.key.name}_${variable.name}.get()") if (variable is Input) { - reactEpilogue.add(with(PrependOperator) {""" + reactEpilogue.add(with(PrependOperator) { + """ |if (${entry.key.name}.${variable.name} !== undefined) { | __${entry.key.name}_${variable.name}.set(${entry.key.name}.${variable.name}) - |}""".trimMargin()}) + |}""".trimMargin() + }) } } reactPrologue.add("let ${entry.key.name} = {${initializer}}") @@ -360,7 +337,8 @@ class TSReactionGenerator( // Do not add reactions created by generateNetworkOutputControlReactionBody // or generateNetworkInputControlReactionBody. if (reaction.code.toText().contains("generateNetworkOutputControlReactionBody") - || reaction.code.toText().contains("generateNetworkInputControlReactionBody")) { + || reaction.code.toText().contains("generateNetworkInputControlReactionBody") + ) { continue; } if (federate.containsReaction(reaction)) { diff --git a/org.lflang/src/org/lflang/generator/ts/TSReactorGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSReactorGenerator.kt index 5dba363ae0..e639e62b86 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSReactorGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSReactorGenerator.kt @@ -114,11 +114,11 @@ class TSReactorGenerator( } val instanceGenerator = TSInstanceGenerator(tsGenerator, this, reactor, federate) - val timerGenerator = TSTimerGenerator(tsGenerator, reactor.timers) - val parameterGenerator = TSParameterGenerator(tsGenerator, reactor.parameters) + val timerGenerator = TSTimerGenerator(reactor.timers) + val parameterGenerator = TSParameterGenerator(reactor.parameters) val stateGenerator = TSStateGenerator(tsGenerator, reactor.stateVars) val actionGenerator = TSActionGenerator(reactor.actions) - val portGenerator = TSPortGenerator(tsGenerator, reactor.inputs, reactor.outputs) + val portGenerator = TSPortGenerator(reactor.inputs, reactor.outputs) val constructorGenerator = TSConstructorGenerator(tsGenerator, errorReporter, reactor, federate) return with(PrependOperator) { diff --git a/org.lflang/src/org/lflang/generator/ts/TSStateGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSStateGenerator.kt index bc92e5d889..d5db8436bf 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSStateGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSStateGenerator.kt @@ -1,18 +1,16 @@ package org.lflang.generator.ts -import org.lflang.ASTUtils -import org.lflang.generator.PrependOperator import org.lflang.lf.StateVar import java.util.* /** * Generator for state variables in TypeScript target. */ -class TSStateGenerator ( +class TSStateGenerator( private val tsGenerator: TSGenerator, private val stateVars: List ) { - private fun StateVar.getTargetType(): String = tsGenerator.getTargetTypeW(this) + private fun StateVar.getTargetType(): String = TsTypes.getTargetType(this) fun generateClassProperties(): String { val stateClassProperties = LinkedList() @@ -28,6 +26,7 @@ class TSStateGenerator ( private fun getTargetInitializer(state: StateVar): String { return getInitializerList(state).joinToString(",") } + fun generateInstantiations(): String { val stateInstantiations = LinkedList() // Next handle states. diff --git a/org.lflang/src/org/lflang/generator/ts/TSTimerGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSTimerGenerator.kt index 4039716297..3edde60655 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSTimerGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSTimerGenerator.kt @@ -1,22 +1,19 @@ package org.lflang.generator.ts; -import org.lflang.generator.PrependOperator +import org.lflang.generator.getTargetTimeExpr +import org.lflang.generator.orZero import org.lflang.lf.Timer -import org.lflang.lf.Value import java.util.* /** * Generator timers for TypeScript target. */ class TSTimerGenerator ( - // TODO(hokeun): Remove dependency on TSGenerator. - private val tsGenerator: TSGenerator, private val timers: List ) { - private fun Value.getTargetValue(): String = tsGenerator.getTargetValueW(this) fun generateClassProperties(): String { - val timerClassProperties = LinkedList() + val timerClassProperties = mutableListOf() for (timer in timers) { timerClassProperties.add("${timer.name}: __Timer;") } @@ -24,10 +21,10 @@ class TSTimerGenerator ( } fun generateInstantiations(): String { - val timerInstantiations = LinkedList() + val timerInstantiations = mutableListOf() for (timer in timers) { - val timerPeriod: String = timer.period?.getTargetValue() ?: "0" - val timerOffset: String = timer.offset?.getTargetValue() ?: "0" + val timerPeriod: String = TsTypes.getTargetTimeExpr(timer.period.orZero()) + val timerOffset: String = TsTypes.getTargetTimeExpr(timer.offset.orZero()) timerInstantiations.add("this.${timer.name} = new __Timer(this, $timerOffset, $timerPeriod);") } diff --git a/org.lflang/src/org/lflang/generator/ts/TsTypes.kt b/org.lflang/src/org/lflang/generator/ts/TsTypes.kt index c5d6fadaa7..8a564f3567 100644 --- a/org.lflang/src/org/lflang/generator/ts/TsTypes.kt +++ b/org.lflang/src/org/lflang/generator/ts/TsTypes.kt @@ -26,8 +26,10 @@ package org.lflang.generator.ts import org.lflang.InferredType import org.lflang.generator.TargetTypes +import org.lflang.inferredType import org.lflang.lf.ParamRef import org.lflang.lf.Parameter +import org.lflang.lf.StateVar import org.lflang.lf.TimeUnit /** @@ -67,4 +69,18 @@ object TsTypes : TargetTypes { override fun getTargetParamRef(expr: ParamRef, type: InferredType?): String = "this.${expr.parameter.name}.get()" + + + /** + * Returns a type for the state variable. Note that this is TS-specific + * and not equivalent to `s.type.targetType`. + */ + fun getTargetType(s: StateVar): String { + val type = getTargetType(s.inferredType) + return if (s.init == null) { + "$type | undefined" + } else { + type + } + } } From ff2e42ef7d0d766d946327c6ef41223da85d4d35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 7 Nov 2021 15:57:09 +0100 Subject: [PATCH 37/68] Make all generators use TargetTypes default methods --- org.lflang/src/org/lflang/InferredType.java | 14 +++++ .../src/org/lflang/generator/TargetTypes.java | 32 +++++----- .../src/org/lflang/generator/cpp/CppTypes.kt | 3 +- .../generator/python/PythonGenerator.xtend | 59 ++++--------------- test/Cpp/src/NativeListsAndTimes.lf | 4 +- 5 files changed, 47 insertions(+), 65 deletions(-) diff --git a/org.lflang/src/org/lflang/InferredType.java b/org.lflang/src/org/lflang/InferredType.java index 4a55519345..479380dced 100644 --- a/org.lflang/src/org/lflang/InferredType.java +++ b/org.lflang/src/org/lflang/InferredType.java @@ -177,4 +177,18 @@ public static InferredType timeList(Integer size) { public static InferredType timeList() { return timeList(null); } + + /** + * Returns the component type, if this is a first-class + * list type. Eg returns {@code time} for the type {@code time[]}. + * If this is not a first-class list type, returns null. + * + *

    Todo this does not return int for int[], we need to + * make the parser production left-recursive. OTOH only + * time and time[] are first class in our framework so + * this doesn't contradict the contract of this method. + */ + public InferredType getComponentType() { + return isTime && isList ? time() : null; + } } diff --git a/org.lflang/src/org/lflang/generator/TargetTypes.java b/org.lflang/src/org/lflang/generator/TargetTypes.java index 8a0ceaa94b..c9387961f8 100644 --- a/org.lflang/src/org/lflang/generator/TargetTypes.java +++ b/org.lflang/src/org/lflang/generator/TargetTypes.java @@ -26,6 +26,7 @@ package org.lflang.generator; import java.util.Objects; +import java.util.stream.Collectors; import org.lflang.ASTUtils; import org.lflang.InferredType; @@ -112,12 +113,7 @@ default String escapeIdentifier(String ident) { * to a time value ({@link #getTargetTimeType()}), with the given * magnitude and unit. The unit may not be null (use {@link TimeUnit#NONE}). */ - default String getTargetTimeExpr(long magnitude, TimeUnit unit) { - // todo make non-default when we reuse this for all generators, - // all targets should support this. - Objects.requireNonNull(unit); - throw new AssertionError("Unimplemented"); // todo make non-default - } + String getTargetTimeExpr(long magnitude, TimeUnit unit); /** * Returns an expression in the target language that is the translation @@ -145,33 +141,39 @@ default String getTargetLiteral(Literal expr, InferredType type) { * Returns an expression in the target language that is the translation * of the given tuple expression. To support tuple expressions, a target * must also register this capability in {@link Target#supportsLfTupleLiterals()}. - * - * @throws UnsupportedGeneratorFeatureException If the target does not support this */ default String getTargetTupleExpr(TupleExpr expr, InferredType type) { - throw new UnsupportedGeneratorFeatureException("Tuple expressions lists"); + if (expr.getItems().isEmpty()) { + return "()"; + } + InferredType compType = type == null ? null : type.getComponentType(); + return expr.getItems().stream() + .map(it -> getTargetExpr(it, compType)) + .collect(Collectors.joining(", ", "(", ",)")); } /** * Returns an expression in the target language that is the translation * of the given list expression. To support list expressions, a target * must also register this capability in {@link Target#supportsLfBracketListExpressions()}. - * - * @throws UnsupportedGeneratorFeatureException If the target does not support this */ default String getTargetBracketExpr(BracketExpr expr, InferredType type) { - throw new UnsupportedGeneratorFeatureException("Bracket-delimited list expressions"); + InferredType compType = type == null ? null : type.getComponentType(); + return expr.getItems().stream() + .map(it -> getTargetExpr(it, compType)) + .collect(Collectors.joining(", ", "[", "]")); } /** * Returns an expression in the target language that is the translation * of the given list expression. To support list expressions, a target * must also register this capability in {@link Target#supportsLfBraceListExpressions()}. - * - * @throws UnsupportedGeneratorFeatureException If the target does not support this */ default String getTargetBraceExpr(BraceExpr expr, InferredType type) { - throw new UnsupportedGeneratorFeatureException("Brace-delimited list expressions"); + InferredType compType = type == null ? null : type.getComponentType(); + return expr.getItems().stream() + .map(it -> getTargetExpr(it, compType)) + .collect(Collectors.joining(", ", "{", "}")); } /** diff --git a/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt b/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt index 857eb3fdc1..ba6885c69d 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt @@ -54,11 +54,10 @@ object CppTypes : TargetTypes { // != 1 expr this.append(getTargetType(type)) val (prefix, postfix) = if (init.isBraces) Pair("{", "}") else Pair("(", ")") - init.exprs.joinTo(this, ", ", prefix, postfix) { getTargetExpr(it, null) } + init.exprs.joinTo(this, ", ", prefix, postfix) { getTargetExpr(it, inferredType?.componentType) } } } - override fun getTargetUndefinedType() = "void" override fun getTargetTimeExpr(magnitude: Long, unit: TimeUnit): String = diff --git a/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend b/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend index b1068540fe..d744877f3e 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend @@ -54,7 +54,7 @@ import org.lflang.lf.Action import org.lflang.lf.Input import org.lflang.lf.Initializer import org.lflang.lf.Instantiation -import org.lflang.lf.BracketExpr +import org.lflang.lf.Literal import org.lflang.lf.Model import org.lflang.lf.Output import org.lflang.lf.Port @@ -63,8 +63,6 @@ import org.lflang.lf.Reaction import org.lflang.lf.Reactor import org.lflang.lf.ReactorDecl import org.lflang.lf.TriggerRef -import org.lflang.lf.TupleExpr -import org.lflang.lf.Value import org.lflang.lf.VarRef import static extension org.lflang.ASTUtils.* @@ -214,55 +212,22 @@ class PythonGenerator extends CGenerator { //////////////////////////////////////////// //// Protected methods - - /** - * Override to convert some C types to their - * Python equivalent. - * Examples: - * true/false -> True/False - * @param v A value - * @return A value string in the target language - */ - protected def String getPythonTargetValue(Value v) { - // Parameters in Python are always prepended with a 'self.' - // predicate. Therefore, we need to append the returned value - // if it is a parameter. - return if (v instanceof ParamRef) { - "self." + v.parameter.name; - } else if (v instanceof BracketExpr) { - if (v.items.isEmpty) "[]" - else v.items.join("[", ", ", "]", [it.pythonTargetValue]) - } else if (v instanceof TupleExpr) { - if (v.items.isEmpty) "()" - else return v.items.join("(", ", ", ",)", [it.pythonTargetValue]) - } else { - switch(v.toText) { - case "false": "False" - case "true": "True" - default: super.getTargetValue(v) - } + + override String getTargetParamRef(ParamRef expr, InferredType type) { + return "self." + expr.parameter.name + } + + override String getTargetLiteral(Literal expr, InferredType type) { + switch(expr.toText) { + case "false": "False" + case "true": "True" + default: super.getTargetLiteral(expr, type) } } /** Returns the python initializer corresponding to the given LF init list. */ def String getPythonInitializer(Initializer init) { - return if (init === null) { - "None" - } else if (init.isParens && (init.exprs.size != 1 || init.isTrailingComma)) { - - // corresponds to a tuple: - // state foo(1,2) # tuple w/ 2 components - // state foo(1,) # tuple w/ 1 component - // state foo() # tuple w/ 0 component - - if (init.exprs.isEmpty) "()" - else init.exprs.join("(", ", ", ",)", [it.pythonTargetValue]) - - } else if ((init.isAssign || init.isParens) && init.exprs.size == 1) { - init.asSingleValue.getPythonTargetValue - } else { - throw new AssertionError("invalid expression form: " + init) - } + return getTargetInitializer(init, null) } /** diff --git a/test/Cpp/src/NativeListsAndTimes.lf b/test/Cpp/src/NativeListsAndTimes.lf index ad5f1bd6ca..6629778994 100644 --- a/test/Cpp/src/NativeListsAndTimes.lf +++ b/test/Cpp/src/NativeListsAndTimes.lf @@ -6,9 +6,11 @@ reactor Foo(x:int(0), y:time(0), // Units are missing but not required z(1 msec), // Type is missing but not required p:int[]{1, 2, 3, 4}, // List of integers + p2:int[] = {1, 2, 3, 4}, // List of integers q:{=std::vector=}{1 msec, 2 msec, 3 msec}, // list of time values r:time({=0=}), // Zero-valued target code also is a valid time - g:time[]{1 msec, 2 msec} // List of time values + g:time[]{1 msec, 2 msec}, // List of time values + g2:time[] = {1 msec, 2 msec} // List of time values ) { state s:time(y); // Reference to explicitly typed time parameter state t:time(z); // Reference to implicitly typed time parameter From bc08e8307f57333ca8e8675c833fec906995f2cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 7 Nov 2021 16:07:40 +0100 Subject: [PATCH 38/68] Fix C++ parameters --- org.lflang/src/org/lflang/generator/cpp/CppTypes.kt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt b/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt index ba6885c69d..8d2a889896 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt @@ -58,6 +58,17 @@ object CppTypes : TargetTypes { } } + override fun getTargetBraceExpr(expr: BraceExpr, type: InferredType?): String { + val braceInitializer = super.getTargetBraceExpr(expr, type) + return if (type?.isList == true) { + if (type.isTime) + getTargetVariableSizeListType(targetTimeType) + braceInitializer + else + getTargetVariableSizeListType(type.baseType()) + braceInitializer + } else + braceInitializer + } + override fun getTargetUndefinedType() = "void" override fun getTargetTimeExpr(magnitude: Long, unit: TimeUnit): String = From b19695dd33c988da3cf8c8edb49d24b1cf89ff59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 7 Nov 2021 16:20:39 +0100 Subject: [PATCH 39/68] Fix validation of state vars --- org.lflang/src/org/lflang/LinguaFranca.xtext | 2 +- .../src/org/lflang/validation/LFValidator.xtend | 13 +++---------- test/C/src/NativeListsAndTimes.lf | 3 +++ test/Cpp/src/NativeListsAndTimes.lf | 4 ++++ 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/org.lflang/src/org/lflang/LinguaFranca.xtext b/org.lflang/src/org/lflang/LinguaFranca.xtext index 7a591994ed..69ff16e907 100644 --- a/org.lflang/src/org/lflang/LinguaFranca.xtext +++ b/org.lflang/src/org/lflang/LinguaFranca.xtext @@ -344,7 +344,7 @@ Port: // A type is in the target language, hence either an ID or target code. Type: - time?='time' (arraySpec=ArraySpec)? + time?='time' (stars+='*')* (arraySpec=ArraySpec)? | id=DottedName ('<' typeParms+=Type (',' typeParms+=Type)* '>')? (stars+='*')* (arraySpec=ArraySpec)? | code=Code ; diff --git a/org.lflang/src/org/lflang/validation/LFValidator.xtend b/org.lflang/src/org/lflang/validation/LFValidator.xtend index bafb8f3a7f..e8a86de756 100644 --- a/org.lflang/src/org/lflang/validation/LFValidator.xtend +++ b/org.lflang/src/org/lflang/validation/LFValidator.xtend @@ -1142,12 +1142,9 @@ class LFValidator extends BaseLFValidator { @Check(FAST) def checkState(StateVar stateVar) { checkName(stateVar.name, Literals.STATE_VAR__NAME) + typeCheck(stateVar.init, stateVar.inferredType, Literals.STATE_VAR__INIT) - if (stateVar.isOfTimeType) { - // If the state is declared to be a time, - // make sure that it is initialized correctly. - checkValueIsTime(stateVar.init, Literals.STATE_VAR__INIT) - } else if (this.target.requiresTypes && stateVar.inferredType.isUndefined) { + if (this.target.requiresTypes && stateVar.inferredType.isUndefined) { // Report if a type is missing error("State must have a type.", Literals.STATE_VAR__TYPE) } @@ -1306,11 +1303,7 @@ class LFValidator extends BaseLFValidator { checkValueIsTime(component, feature) } } else { - if (init.exprs.size != 1) { - error("Expected exactly one time value.", feature) - } else { - checkValueIsTime(init.asSingleValue, feature) - } + checkValueIsTime(init, feature) } } } diff --git a/test/C/src/NativeListsAndTimes.lf b/test/C/src/NativeListsAndTimes.lf index 83ab42783f..e835c27d9c 100644 --- a/test/C/src/NativeListsAndTimes.lf +++ b/test/C/src/NativeListsAndTimes.lf @@ -22,6 +22,9 @@ main reactor(x:int(0), timer toe(z); // Implicit type time state baz(p); // Implicit type int[] state period(z); // Implicit type time + + state intArrayState: int[] = {1, 2}; // Array of int values + reaction(tick) {= // Target code =} diff --git a/test/Cpp/src/NativeListsAndTimes.lf b/test/Cpp/src/NativeListsAndTimes.lf index 6629778994..22b4c8c4bd 100644 --- a/test/Cpp/src/NativeListsAndTimes.lf +++ b/test/Cpp/src/NativeListsAndTimes.lf @@ -22,6 +22,10 @@ reactor Foo(x:int(0), state baz(p); // Implicit type int[] state period(z); // Implicit type time state period2 = z; // Implicit type time + + state timeListState: time[] = {1 msec, 2 msec}; // Vector of time values + state intArrayState: int[] = {1, 2}; // Array of int values + state times: std::vector>{q, g}; // a list of lists reaction(tick) {= // Target code From e2b009a3e1fcef05b264bf40ca0f98d294cc3a26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 7 Nov 2021 16:57:45 +0100 Subject: [PATCH 40/68] Fix validation tests --- .../org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend index 85019bbb85..c54c2773b0 100644 --- a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend +++ b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.xtend @@ -690,7 +690,7 @@ class LinguaFrancaValidationTest { state y: int[] = {1, 2}; // wrong } ''').assertError(LfPackage::eINSTANCE.braceExpr, - null, 'Target C does not support this expression form.') + null, 'Target Python does not support this expression form.') } From 30a0028bf4518bacf062a3ec1f5e50c4e4a20bf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 7 Nov 2021 17:13:13 +0100 Subject: [PATCH 41/68] Fix TS state initializers --- .../lflang/tests/runtime/TypeScriptTest.java | 2 +- .../lflang/generator/ts/TSReactorGenerator.kt | 2 +- .../lflang/generator/ts/TSStateGenerator.kt | 34 +++++-------------- 3 files changed, 10 insertions(+), 28 deletions(-) diff --git a/org.lflang.tests/src/org/lflang/tests/runtime/TypeScriptTest.java b/org.lflang.tests/src/org/lflang/tests/runtime/TypeScriptTest.java index e5e07e0b68..182f98b6a6 100644 --- a/org.lflang.tests/src/org/lflang/tests/runtime/TypeScriptTest.java +++ b/org.lflang.tests/src/org/lflang/tests/runtime/TypeScriptTest.java @@ -16,7 +16,7 @@ * @author Marten Lohstroh */ public class TypeScriptTest extends AbstractTest { - TypeScriptTest() { + public TypeScriptTest() { super(Target.TS); } diff --git a/org.lflang/src/org/lflang/generator/ts/TSReactorGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSReactorGenerator.kt index e639e62b86..235c3cf65c 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSReactorGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSReactorGenerator.kt @@ -116,7 +116,7 @@ class TSReactorGenerator( val instanceGenerator = TSInstanceGenerator(tsGenerator, this, reactor, federate) val timerGenerator = TSTimerGenerator(reactor.timers) val parameterGenerator = TSParameterGenerator(reactor.parameters) - val stateGenerator = TSStateGenerator(tsGenerator, reactor.stateVars) + val stateGenerator = TSStateGenerator(reactor.stateVars) val actionGenerator = TSActionGenerator(reactor.actions) val portGenerator = TSPortGenerator(reactor.inputs, reactor.outputs) diff --git a/org.lflang/src/org/lflang/generator/ts/TSStateGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSStateGenerator.kt index d5db8436bf..e6c6aa5939 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSStateGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSStateGenerator.kt @@ -1,5 +1,6 @@ package org.lflang.generator.ts +import org.lflang.generator.getTargetInitializer import org.lflang.lf.StateVar import java.util.* @@ -7,36 +8,17 @@ import java.util.* * Generator for state variables in TypeScript target. */ class TSStateGenerator( - private val tsGenerator: TSGenerator, private val stateVars: List ) { - private fun StateVar.getTargetType(): String = TsTypes.getTargetType(this) - fun generateClassProperties(): String { - val stateClassProperties = LinkedList() - for (stateVar in stateVars) { - stateClassProperties.add("${stateVar.name}: __State<${stateVar.getTargetType()}>;"); + fun generateClassProperties(): String = + stateVars.joinToString("\n") { + "${it.name}: __State<${TsTypes.getTargetType(it)}>;" } - return stateClassProperties.joinToString("\n") - } - private fun getInitializerList(state: StateVar): List = - tsGenerator.getInitializerListW(state) - - private fun getTargetInitializer(state: StateVar): String { - return getInitializerList(state).joinToString(",") - } - - fun generateInstantiations(): String { - val stateInstantiations = LinkedList() - // Next handle states. - for (stateVar in stateVars) { - if (stateVar.init != null) { - stateInstantiations.add("this.${stateVar.name} = new __State(${getTargetInitializer(stateVar)});"); - } else { - stateInstantiations.add("this.${stateVar.name} = new __State(undefined);"); - } + fun generateInstantiations(): String = + stateVars.joinToString("\n") { + val init = if (it.init != null) TsTypes.getTargetInitializer(it) else "undefined" + "this.${it.name} = new __State($init);" } - return stateInstantiations.joinToString("\n") - } } From 39cbe2dff0ac1ca307a0683bf2b5b92e0a58d8f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 7 Nov 2021 17:34:17 +0100 Subject: [PATCH 42/68] Fix TS ctor generation --- .../org/lflang/generator/GeneratorUtils.kt | 24 +++++-- .../generator/ts/TSConstructorGenerator.kt | 65 +++++-------------- .../org/lflang/generator/ts/TSGenerator.kt | 7 +- .../generator/ts/TSInstanceGenerator.kt | 20 ++---- .../ts/TSParameterPreambleGenerator.kt | 1 - .../lflang/generator/ts/TSReactorGenerator.kt | 2 +- .../src/org/lflang/generator/ts/TsTypes.kt | 14 ++-- 7 files changed, 55 insertions(+), 78 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/GeneratorUtils.kt b/org.lflang/src/org/lflang/generator/GeneratorUtils.kt index 4c0aac2066..acb81851ac 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorUtils.kt +++ b/org.lflang/src/org/lflang/generator/GeneratorUtils.kt @@ -3,10 +3,7 @@ package org.lflang.generator import org.eclipse.emf.ecore.EObject import org.eclipse.xtext.nodemodel.util.NodeModelUtils import org.lflang.InferredType -import org.lflang.lf.LfFactory -import org.lflang.lf.Parameter -import org.lflang.lf.StateVar -import org.lflang.lf.Value +import org.lflang.lf.* import org.lflang.toPath import org.lflang.toTextTokenBased import org.lflang.toUnixString @@ -18,6 +15,25 @@ fun TargetTypes.getTargetInitializer(sv: StateVar): TargetCode = fun TargetTypes.getTargetInitializer(sv: Parameter): TargetCode = this.getTargetInitializer(sv.init, sv.type) +/** + * Returns the target code for the initial value of a parameter + * for the given instantiation. The value is defaulted to the + * default value for the parameter. + */ +fun TargetTypes.getTargetInitializer(sv: Parameter, inst: Instantiation): TargetCode { + val delegated = object : TargetTypes by this { + override fun getTargetParamRef(expr: ParamRef, type: InferredType?): String { + val init = inst.parameters.firstOrNull { it.lhs == expr.parameter }?.rhs + ?: expr.parameter.init + ?: throw InvalidLfSourceException(inst, "No value for parameter ${sv.name}") + + return super.getTargetInitializer(init, sv.type) + } + } + return delegated.getTargetInitializer(sv) +} + + fun TargetTypes.getTargetTimeExpr(v: Value): TargetCode = this.getTargetExpr(v, InferredType.time()) diff --git a/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt index f95fa76266..0c9a91e366 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt @@ -23,35 +23,15 @@ class TSConstructorGenerator( private val reactor: Reactor, private val federate: FederateInstance ) { - private fun getInitializerList(param: Parameter): List = - tsGenerator.getInitializerListW(param) - - private fun Parameter.getTargetType(): String = TsTypes.getTargetType(this.inferredType) - - // Initializer functions - private fun getTargetInitializerHelper( - param: Parameter, - list: List - ): String { - return if (list.size == 0) { - errorReporter.reportError(param, "Parameters must have a default value!") - } else if (list.size == 1) { - list[0] - } else { - list.joinToString(", ", "[", "]") - } - } - - private fun getTargetInitializer(param: Parameter): String { - return getTargetInitializerHelper(param, getInitializerList(param)) - } private fun initializeParameter(p: Parameter): String { - return """${p.name}: ${p.getTargetType()} = ${getTargetInitializer(p)}""" + val type = TsTypes.getTargetType(p.inferredType) + val init = TsTypes.getTargetInitializer(p.init, p.type) + return "${p.name}: $type = $init" } private fun generateConstructorArguments(reactor: Reactor): String { - val arguments = LinkedList() + val arguments = mutableListOf() if (reactor.isMain || reactor.isFederated) { arguments.add("timeout: TimeValue | undefined = undefined") arguments.add("keepAlive: boolean = false") @@ -74,7 +54,7 @@ class TSConstructorGenerator( return arguments.joinToString(", \n") } - private fun federationRTIProperties(): LinkedHashMap { + private fun federationRTIProperties(): Map { return tsGenerator.federationRTIPropertiesW() } @@ -99,36 +79,27 @@ class TSConstructorGenerator( // If the app is federated, register its // networkMessageActions with the RTIClient - private fun generateFederatePortActionRegistrations(networkMessageActions: List): String { - var fedPortID = 0; - val connectionInstantiations = LinkedList() - for (nAction in networkMessageActions) { - val registration = """ - this.registerFederatePortAction(${fedPortID}, this.${nAction.name}); - """ - connectionInstantiations.add(registration) - fedPortID++ + private fun generateFederatePortActionRegistrations(networkMessageActions: List): String = + networkMessageActions.withIndex().joinToString("\n") { (fedPortID, nAction) -> + "this.registerFederatePortAction($fedPortID, this.${nAction.name});" } - return connectionInstantiations.joinToString("\n") - } // Generate code for registering Fed IDs that are connected to // this federate via ports in the TypeScript's FederatedApp. // These Fed IDs are used to let the RTI know about the connections // between federates during the initialization with the RTI. - fun generateFederateConfigurations(): String { - val federateConfigurations = LinkedList() - if (reactor.isFederated) { - for ((key, _) in federate.dependsOn) { - // FIXME: Get delay properly considering the unit instead of hardcoded BigInt(0). - federateConfigurations.add("this.addUpstreamFederate(${key.id}, BigInt(0));") - } - for ((key, _) in federate.sendsTo) { - federateConfigurations.add("this.addDownstreamFederate(${key.id});") + fun generateFederateConfigurations(): String = + buildString { + if (reactor.isFederated) { + for ((key, _) in federate.dependsOn) { + // FIXME: Get delay properly considering the unit instead of hardcoded BigInt(0). + this.appendLine("this.addUpstreamFederate(${key.id}, BigInt(0));") + } + for ((key, _) in federate.sendsTo) { + this.appendLine("this.addDownstreamFederate(${key.id});") + } } } - return federateConfigurations.joinToString("\n") - } fun generateConstructor( instances: TSInstanceGenerator, diff --git a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt index 8ca9e5bcd4..2cff039275 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt @@ -86,11 +86,6 @@ class TSGenerator( // Wrappers to expose GeneratorBase methods. fun federationRTIPropertiesW() = federationRTIProperties - // todo(clément): get rid of those - fun getInitializerListW(state: StateVar): List = getInitializerList(state.init, state.inferredType) - fun getInitializerListW(param: Parameter): List = getInitializerList(param.init, param.inferredType) - fun getInitializerListW(param: Parameter, i: Instantiation): List = getInitializerList(param, i) - /** Generate TypeScript code from the Lingua Franca model contained by the * specified resource. This is the main entry point for code * generation. @@ -164,7 +159,7 @@ class TSGenerator( targetConfig.protoFiles) tsCode.append(preambleGenerator.generatePreamble()) - val parameterGenerator = TSParameterPreambleGenerator(this, fileConfig, targetConfig, reactors) + val parameterGenerator = TSParameterPreambleGenerator(fileConfig, targetConfig, reactors) val (mainParameters, parameterCode) = parameterGenerator.generateParameters() tsCode.append(parameterCode) diff --git a/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt index dc400c4293..08880cdec6 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt @@ -1,8 +1,8 @@ package org.lflang.generator.ts import org.lflang.federated.FederateInstance +import org.lflang.generator.getTargetInitializer import org.lflang.lf.Instantiation -import org.lflang.lf.Parameter import org.lflang.lf.Reactor import org.lflang.toDefinition import org.lflang.toText @@ -11,10 +11,7 @@ import java.util.* /** * Generator for child reactor instantiations in TypeScript target. */ -class TSInstanceGenerator ( - // TODO(hokeun): Remove dependency on TSGenerator. - private val tsGenerator: TSGenerator, - private val tsReactorGenerator: TSReactorGenerator, +class TSInstanceGenerator( reactor: Reactor, federate: FederateInstance ) { @@ -32,13 +29,6 @@ class TSInstanceGenerator ( } } - private fun getInitializerList(param: Parameter, i: Instantiation): List = - tsGenerator.getInitializerListW(param, i) - - private fun getTargetInitializer(param: Parameter, i: Instantiation): String { - return tsReactorGenerator.getTargetInitializerHelper(param, getInitializerList(param, i)) - } - fun generateClassProperties(): String { val childReactorClassProperties = LinkedList() for (childReactor in childReactors) { @@ -52,7 +42,7 @@ class TSInstanceGenerator ( fun generateInstantiations(): String { val childReactorInstantiations = LinkedList() for (childReactor in childReactors) { - val childReactorArguments = StringJoiner(", "); + val childReactorArguments = StringJoiner(", ") childReactorArguments.add("this") // Iterate through parameters in the order they appear in the @@ -60,7 +50,7 @@ class TSInstanceGenerator ( // the reactor instance, and write the corresponding parameter // value as an argument for the TypeScript constructor for (parameter in childReactor.reactorClass.toDefinition().parameters) { - childReactorArguments.add(getTargetInitializer(parameter, childReactor)) + childReactorArguments.add(TsTypes.getTargetInitializer(parameter, childReactor)) } childReactorInstantiations.add( @@ -68,4 +58,4 @@ class TSInstanceGenerator ( } return childReactorInstantiations.joinToString("\n") } -} \ No newline at end of file +} diff --git a/org.lflang/src/org/lflang/generator/ts/TSParameterPreambleGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSParameterPreambleGenerator.kt index ebb6f00b12..4825f9e1be 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSParameterPreambleGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSParameterPreambleGenerator.kt @@ -46,7 +46,6 @@ import java.util.StringJoiner */ class TSParameterPreambleGenerator( - private val tsGenerator: TSGenerator, private val fileConfig: FileConfig, private val targetConfig: TargetConfig, private val reactors: MutableList diff --git a/org.lflang/src/org/lflang/generator/ts/TSReactorGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSReactorGenerator.kt index 235c3cf65c..be760646a7 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSReactorGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSReactorGenerator.kt @@ -113,7 +113,7 @@ class TSReactorGenerator( "export class $reactorName extends __Reactor {" } - val instanceGenerator = TSInstanceGenerator(tsGenerator, this, reactor, federate) + val instanceGenerator = TSInstanceGenerator(reactor, federate) val timerGenerator = TSTimerGenerator(reactor.timers) val parameterGenerator = TSParameterGenerator(reactor.parameters) val stateGenerator = TSStateGenerator(reactor.stateVars) diff --git a/org.lflang/src/org/lflang/generator/ts/TsTypes.kt b/org.lflang/src/org/lflang/generator/ts/TsTypes.kt index 8a564f3567..a36f17e9a6 100644 --- a/org.lflang/src/org/lflang/generator/ts/TsTypes.kt +++ b/org.lflang/src/org/lflang/generator/ts/TsTypes.kt @@ -25,12 +25,10 @@ package org.lflang.generator.ts import org.lflang.InferredType +import org.lflang.JavaAstUtils import org.lflang.generator.TargetTypes import org.lflang.inferredType -import org.lflang.lf.ParamRef -import org.lflang.lf.Parameter -import org.lflang.lf.StateVar -import org.lflang.lf.TimeUnit +import org.lflang.lf.* /** * [TargetTypes] implementation for [TSGenerator]. @@ -70,6 +68,14 @@ object TsTypes : TargetTypes { override fun getTargetParamRef(expr: ParamRef, type: InferredType?): String = "this.${expr.parameter.name}.get()" + override fun getTargetInitializer(init: Initializer?, type: Type?): String { + if (init != null && init.exprs.singleOrNull() == null) { + // support initializing lists with a(1,2,3) syntax + val componentType = InferredType.fromAST(type).componentType + return init.exprs.joinToString(", ", "[", "]") { getTargetExpr(it, componentType) } + } + return super.getTargetInitializer(init, type) + } /** * Returns a type for the state variable. Note that this is TS-specific From 8348a1b5dd6d17c96b5b2fa443593260d32981d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 7 Nov 2021 17:39:30 +0100 Subject: [PATCH 43/68] Remove dead code --- .../org/lflang/generator/GeneratorBase.xtend | 42 ------------------- 1 file changed, 42 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/GeneratorBase.xtend b/org.lflang/src/org/lflang/generator/GeneratorBase.xtend index dccf068fc0..573a1c871c 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorBase.xtend +++ b/org.lflang/src/org/lflang/generator/GeneratorBase.xtend @@ -1317,48 +1317,6 @@ abstract class GeneratorBase extends AbstractLFValidator implements TargetTypes } } - /** - * Create a list of state initializers in target code. - * - * @param state The state variable to create initializers for - * @return A list of initializers in target code - */ - protected def List getInitializerList(Initializer init, InferredType type) { - if (init === null) { - return null - } - - return init?.exprs.map[i| - if (i instanceof ParamRef) { - i.parameter.targetReference - } else { - i.targetValue - } - ] - } - - /** - * Create a list of parameter initializers in target code in the context - * of an reactor instantiation. - * - * This respects the parameter assignments given in the reactor - * instantiation and falls back to the reactors default initializers - * if no value is assigned to it. - * - * @param param The parameter to create initializers for - * @return A list of initializers in target code - */ - protected def getInitializerList(Parameter param, Instantiation i) { - if (i === null || param === null) { - return null - } - - val assignments = i.parameters.filter[p|p.lhs === param] - val actualValue = assignments.size === 0 ? param.init : assignments.get(0).rhs - - return getInitializerList(actualValue, param.inferredType) - } - /** * Generate target code for a parameter reference. * From 27b94cb760848d22e095597ba6eadaa8df0dbaf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 7 Nov 2021 20:27:30 +0100 Subject: [PATCH 44/68] Fix C++ initializers --- .../org/lflang/generator/cpp/CppExtensions.kt | 1 - .../generator/cpp/CppInstanceGenerator.kt | 1 - .../generator/cpp/CppParameterGenerator.kt | 3 +- .../lflang/generator/cpp/CppStateGenerator.kt | 4 +- .../src/org/lflang/generator/cpp/CppTypes.kt | 43 +++++++++++++------ test/Cpp/src/NativeListsAndTimes.lf | 2 +- 6 files changed, 34 insertions(+), 20 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt b/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt index 1515532e45..270f0ca6a7 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt @@ -122,4 +122,3 @@ val InferredType.cppType: String get() = CppTypes.getTargetType(this) -fun CppTypes.getCppInitializerList(init: Initializer?, type: Type?): String = getTargetInitializer(init, type) diff --git a/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt index 81c695c9d8..f6351e7cf2 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt @@ -53,7 +53,6 @@ class CppInstanceGenerator( private fun Instantiation.getParameterValue(param: Parameter, isBankInstantiation: Boolean = false): String { val rhs = this.parameters.firstOrNull { it.lhs === param }?.rhs ?: param.init - println(param.name + " : " + CppTypes.getTargetType(param.inferredType) + " = " + rhs) return if (isBankInstantiation && param.name == "bank_index") { // If we are in a bank instantiation (instanceId != null), then assign the instanceId diff --git a/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt index 5b24824c5f..aabdd330a0 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt @@ -25,7 +25,6 @@ package org.lflang.generator.cpp import org.lflang.inferredType -import org.lflang.isOfTimeType import org.lflang.lf.Parameter import org.lflang.lf.Reactor @@ -39,7 +38,7 @@ class CppParameterGenerator(private val reactor: Reactor) { /** Get the default value of the receiver parameter in C++ code */ val Parameter.defaultValue: String - get() = CppTypes.getCppInitializerList(init, type) + get() = CppTypes.getCppInitializerWithTypePrefix(init, type) /** Get a C++ type that is a const reference to the parameter type */ val Parameter.constRefType: String diff --git a/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt index 4dc63a12a0..02b35c9fa4 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt @@ -32,9 +32,7 @@ import org.lflang.lf.StateVar class CppStateGenerator(private val reactor: Reactor) { private fun generateInitializer(state: StateVar): String = - state.name + CppTypes.getCppInitializerList(state.init, state.type).let { - if (!it.startsWith("(")) "($it)" else it - } + state.name + CppTypes.getCppInitializerWithoutTypePrefix(state.init, state.type) /** Get all state declarations */ fun generateDeclarations() = diff --git a/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt b/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt index 8d2a889896..17f09e2a55 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt @@ -45,28 +45,20 @@ object CppTypes : TargetTypes { override fun getTargetVariableSizeListType(baseType: String) = "std::vector<$baseType>" override fun getTargetInitializer(init: Initializer?, type: Type?): String { - val inferredType = JavaAstUtils.getInferredType(type, init) - if (init == null) { - return missingExpr - } - return init.exprs.singleOrNull()?.let { getTargetExpr(it, inferredType) } - ?: buildString { - // != 1 expr - this.append(getTargetType(type)) - val (prefix, postfix) = if (init.isBraces) Pair("{", "}") else Pair("(", ")") - init.exprs.joinTo(this, ", ", prefix, postfix) { getTargetExpr(it, inferredType?.componentType) } - } + return getCppInitializerWithTypePrefix(init, type) } override fun getTargetBraceExpr(expr: BraceExpr, type: InferredType?): String { val braceInitializer = super.getTargetBraceExpr(expr, type) return if (type?.isList == true) { + // prefix the {...} with std::vector<_> if (type.isTime) getTargetVariableSizeListType(targetTimeType) + braceInitializer else getTargetVariableSizeListType(type.baseType()) + braceInitializer - } else + } else { braceInitializer + } } override fun getTargetUndefinedType() = "void" @@ -77,6 +69,33 @@ object CppTypes : TargetTypes { } +fun CppTypes.getCppInitializerWithTypePrefix(init: Initializer?, type: Type?): String = + if (init == null) missingExpr + else getTargetType(JavaAstUtils.getInferredType(type, init)) + getCppInitializerWithoutTypePrefix(init, type) + +fun CppTypes.getCppInitializerWithoutTypePrefix(init: Initializer?, type: Type?): String { + val inferredType = JavaAstUtils.getInferredType(type, init) + if (init == null) { + return missingExpr + } + val singleExpr = init.exprs.singleOrNull() + return if (init.isAssign && singleExpr is BraceExpr) + singleExpr.items.joinToString(", ", "{", "}") { + getTargetExpr(it, inferredType?.componentType) + } + else buildString { + if (init.isAssign) { + val expr = init.exprs.single() + append("(").append(getTargetExpr(expr, inferredType)).append(")") + } else { + val (prefix, postfix) = if (init.isBraces) Pair("{", "}") else Pair("(", ")") + init.exprs.joinTo(this, ", ", prefix, postfix) { + getTargetExpr(it, inferredType?.componentType) + } + } + } +} + /** * This object generates types in the context of the outer class, * where parameter references need special handling. diff --git a/test/Cpp/src/NativeListsAndTimes.lf b/test/Cpp/src/NativeListsAndTimes.lf index 22b4c8c4bd..69bafc8242 100644 --- a/test/Cpp/src/NativeListsAndTimes.lf +++ b/test/Cpp/src/NativeListsAndTimes.lf @@ -24,7 +24,7 @@ reactor Foo(x:int(0), state period2 = z; // Implicit type time state timeListState: time[] = {1 msec, 2 msec}; // Vector of time values - state intArrayState: int[] = {1, 2}; // Array of int values + state intArrayState: int[] = {1, 2}; // Vector of int values state times: std::vector>{q, g}; // a list of lists reaction(tick) {= From 2924bf8cd891ba689a8d953ba9b6a110906df59b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 7 Nov 2021 21:07:35 +0100 Subject: [PATCH 45/68] Fix Python tuples --- .../src/org/lflang/generator/TargetTypes.java | 21 ++++- .../org/lflang/generator/c/CGenerator.xtend | 29 ++---- .../src/org/lflang/generator/c/CTypes.java | 90 +++++++++++++++++++ .../generator/cpp/CppParameterGenerator.kt | 2 +- .../lflang/generator/cpp/CppStateGenerator.kt | 2 +- .../src/org/lflang/generator/cpp/CppTypes.kt | 17 ++-- .../generator/python/PythonGenerator.xtend | 22 ++++- .../org/lflang/generator/rust/RustTypes.kt | 17 ++-- .../src/org/lflang/generator/ts/TsTypes.kt | 10 +-- 9 files changed, 157 insertions(+), 53 deletions(-) create mode 100644 org.lflang/src/org/lflang/generator/c/CTypes.java diff --git a/org.lflang/src/org/lflang/generator/TargetTypes.java b/org.lflang/src/org/lflang/generator/TargetTypes.java index c9387961f8..8c417fdc29 100644 --- a/org.lflang/src/org/lflang/generator/TargetTypes.java +++ b/org.lflang/src/org/lflang/generator/TargetTypes.java @@ -246,13 +246,28 @@ default String getTargetType(InferredType type) { */ default String getTargetInitializer(Initializer init, Type type) { InferredType inferredType = JavaAstUtils.getInferredType(type, init); + return getTargetInitializer(init, inferredType); + } + + default String getTargetInitializer(Initializer init, InferredType inferredType) { if (init == null) { return getMissingExpr(); } Value single = JavaAstUtils.asSingleValue(init); - if (single != null) { - return getTargetExpr(single, inferredType); - } + return single != null ? getTargetExpr(single, inferredType) + : getTargetInitializerWithNotExactlyOneValue(init, inferredType); + } + + /** + * Returns the representation of the given initializer if + * it has not exactly one value. This is an internal implementation + * detail of {@link #getTargetInitializer(Initializer, Type)}, + * used to support the syntax {@code a(1,2,3)}. + * + * @param init Initializer, non-null + * @param type Inferred type, non-null + */ + default String getTargetInitializerWithNotExactlyOneValue(Initializer init, InferredType type) { // override this method to give semantics to this throw new UnsupportedGeneratorFeatureException(init, "Initializers with != 1 value"); } diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend index 243fd7812d..419e5f6439 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend @@ -4463,31 +4463,20 @@ class CGenerator extends GeneratorBase { } override String getTargetTimeExpr(long magnitude, TimeUnit unit) { - if (unit != TimeUnit.NONE) { - return unit.name() + '(' + magnitude + ')' - } else { - return magnitude.toString() - } + return CTypes.INSTANCE.getTargetTimeExpr(magnitude, unit) } - - protected def getInitializer(Initializer init, InferredType t, ReactorInstance parent) { - if (init === null) - return "{}" - var list = new LinkedList(); + override String getTargetInitializerWithNotExactlyOneValue(Initializer init, InferredType type) { + return CTypes.INSTANCE.getTargetInitializerWithNotExactlyOneValue(init, type) + } - for (i : init.exprs) { - if (i instanceof ParamRef) { - list.add(parent.selfStructName + "->" + i.parameter.name) - } else { - list.add(i.targetValue) + protected def getInitializer(Initializer init, InferredType t, ReactorInstance parent) { + val customExprMaker = new CTypes() { + override String getTargetParamRef(ParamRef expr, InferredType t) { + return parent.selfStructName + "->" + expr.parameter.name } } - - if (list.size == 1) - return list.get(0) - else - return list.join('{', ', ', '}', [it]) + return customExprMaker.getTargetInitializer(init, t) } /** diff --git a/org.lflang/src/org/lflang/generator/c/CTypes.java b/org.lflang/src/org/lflang/generator/c/CTypes.java new file mode 100644 index 0000000000..67ca08e743 --- /dev/null +++ b/org.lflang/src/org/lflang/generator/c/CTypes.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2021, TU Dresden. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * 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 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 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.lflang.generator.c; + +import java.util.stream.Collectors; + +import org.lflang.InferredType; +import org.lflang.generator.TargetTypes; +import org.lflang.generator.UnsupportedGeneratorFeatureException; +import org.lflang.lf.Initializer; +import org.lflang.lf.TimeUnit; + +/** + * {@link TargetTypes} impl for {@link CGenerator}. + * + * @author Clément Fournier + */ +public class CTypes implements TargetTypes { + + public static final CTypes INSTANCE = new CTypes(); + + @Override + public boolean supportsGenerics() { + return false; + } + + @Override + public String getTargetTimeType() { + return "interval_t"; + } + + @Override + public String getTargetTagType() { + return "tag_t"; + } + + @Override + public String getTargetFixedSizeListType(String baseType, int size) { + return baseType + "[" + size + "]"; + } + + @Override + public String getTargetVariableSizeListType(String baseType) { + return baseType + "[]"; + } + + @Override + public String getTargetUndefinedType() { + // todo C used to insert a marker in the code + throw new UnsupportedGeneratorFeatureException("Undefined type"); + } + + @Override + public String getTargetTimeExpr(long magnitude, TimeUnit unit) { + if (unit != TimeUnit.NONE) { + return unit.name() + '(' + magnitude + ')'; + } else { + return Long.toString(magnitude); + } + } + + @Override + public String getTargetInitializerWithNotExactlyOneValue(Initializer init, InferredType type) { + return init.getExprs().stream() + .map(it -> getTargetExpr(it, type.getComponentType())) + .collect(Collectors.joining(", ", "{", "}")); + } +} diff --git a/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt index aabdd330a0..92ee451e54 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt @@ -38,7 +38,7 @@ class CppParameterGenerator(private val reactor: Reactor) { /** Get the default value of the receiver parameter in C++ code */ val Parameter.defaultValue: String - get() = CppTypes.getCppInitializerWithTypePrefix(init, type) + get() = CppTypes.getCppInitializerWithTypePrefix(init, inferredType) /** Get a C++ type that is a const reference to the parameter type */ val Parameter.constRefType: String diff --git a/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt index 02b35c9fa4..9c5b3e1049 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt @@ -32,7 +32,7 @@ import org.lflang.lf.StateVar class CppStateGenerator(private val reactor: Reactor) { private fun generateInitializer(state: StateVar): String = - state.name + CppTypes.getCppInitializerWithoutTypePrefix(state.init, state.type) + state.name + CppTypes.getCppInitializerWithoutTypePrefix(state.init, state.inferredType) /** Get all state declarations */ fun generateDeclarations() = diff --git a/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt b/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt index 17f09e2a55..9414eb2410 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt @@ -25,9 +25,11 @@ package org.lflang.generator.cpp import org.lflang.InferredType -import org.lflang.JavaAstUtils import org.lflang.generator.TargetTypes -import org.lflang.lf.* +import org.lflang.lf.BraceExpr +import org.lflang.lf.Initializer +import org.lflang.lf.ParamRef +import org.lflang.lf.TimeUnit /** * Implementation of [TargetTypes] for C++. @@ -44,8 +46,8 @@ object CppTypes : TargetTypes { override fun getTargetFixedSizeListType(baseType: String, size: Int) = "std::array<$baseType, $size>" override fun getTargetVariableSizeListType(baseType: String) = "std::vector<$baseType>" - override fun getTargetInitializer(init: Initializer?, type: Type?): String { - return getCppInitializerWithTypePrefix(init, type) + override fun getTargetInitializer(init: Initializer?, inferredType: InferredType): String { + return getCppInitializerWithTypePrefix(init, inferredType) } override fun getTargetBraceExpr(expr: BraceExpr, type: InferredType?): String { @@ -69,12 +71,11 @@ object CppTypes : TargetTypes { } -fun CppTypes.getCppInitializerWithTypePrefix(init: Initializer?, type: Type?): String = +fun CppTypes.getCppInitializerWithTypePrefix(init: Initializer?, type: InferredType): String = if (init == null) missingExpr - else getTargetType(JavaAstUtils.getInferredType(type, init)) + getCppInitializerWithoutTypePrefix(init, type) + else getTargetType(type) + getCppInitializerWithoutTypePrefix(init, type) -fun CppTypes.getCppInitializerWithoutTypePrefix(init: Initializer?, type: Type?): String { - val inferredType = JavaAstUtils.getInferredType(type, init) +fun CppTypes.getCppInitializerWithoutTypePrefix(init: Initializer?, inferredType: InferredType?): String { if (init == null) { return missingExpr } diff --git a/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend b/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend index d744877f3e..c270e01264 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend @@ -227,9 +227,29 @@ class PythonGenerator extends CGenerator { /** Returns the python initializer corresponding to the given LF init list. */ def String getPythonInitializer(Initializer init) { - return getTargetInitializer(init, null) + return getTargetInitializer(init, InferredType.undefined()) } + override String getTargetInitializerWithNotExactlyOneValue(Initializer init, InferredType type) { + if (init.exprs.isEmpty) return "()" + else return init.exprs.join("(", ", ", ")", [getTargetExpr(it, type.componentType)]) + } + + override String getTargetInitializer(Initializer init, InferredType type) { + if (init.isParens) { + // tuple expr, but only if size != 1 or trailing comma + if (init.exprs.isEmpty) { + return "()" + } else if (init.exprs.size == 1 && !init.isTrailingComma) { + return getTargetExpr(init.asSingleValue, type) + } else { + return init.exprs.join("(", ", ", ",)", [getTargetExpr(it, type.componentType)]) + } + } + return super.getTargetInitializer(init, type) + } + + /** * Generate parameters and their respective initialization code for a reaction function * The initialization code is put at the beginning of the reaction before user code diff --git a/org.lflang/src/org/lflang/generator/rust/RustTypes.kt b/org.lflang/src/org/lflang/generator/rust/RustTypes.kt index d28b289fba..17c847bd7c 100644 --- a/org.lflang/src/org/lflang/generator/rust/RustTypes.kt +++ b/org.lflang/src/org/lflang/generator/rust/RustTypes.kt @@ -71,18 +71,11 @@ object RustTypes : TargetTypes { TimeUnit.SECOND, TimeUnit.SECONDS -> "Duration::from_secs($magnitude)" } - override fun getTargetInitializer(init: Initializer?, type: Type?): String { - val inferredType: InferredType = JavaAstUtils.getInferredType(type, init) - if (init == null) { - return missingExpr - } - return init.exprs.singleOrNull()?.let { getTargetExpr(it, inferredType) } - ?: if (inferredType.isFixedSizeList) - init.exprs.joinToString(", ", "[", "]") { getTargetExpr(it, null) } - else - init.exprs.joinToString(", ", "vec![", "].into()") { getTargetExpr(it, null) } - - } + override fun getTargetInitializerWithNotExactlyOneValue(init: Initializer, type: InferredType): String = + if (type.isFixedSizeList) + init.exprs.joinToString(", ", "[", "]") { getTargetExpr(it, type.componentType) } + else + init.exprs.joinToString(", ", "vec![", "].into()") { getTargetExpr(it, type.componentType) } override fun getMissingExpr(): String = "Default::default()" diff --git a/org.lflang/src/org/lflang/generator/ts/TsTypes.kt b/org.lflang/src/org/lflang/generator/ts/TsTypes.kt index a36f17e9a6..7adb9a1ec0 100644 --- a/org.lflang/src/org/lflang/generator/ts/TsTypes.kt +++ b/org.lflang/src/org/lflang/generator/ts/TsTypes.kt @@ -68,14 +68,10 @@ object TsTypes : TargetTypes { override fun getTargetParamRef(expr: ParamRef, type: InferredType?): String = "this.${expr.parameter.name}.get()" - override fun getTargetInitializer(init: Initializer?, type: Type?): String { - if (init != null && init.exprs.singleOrNull() == null) { - // support initializing lists with a(1,2,3) syntax - val componentType = InferredType.fromAST(type).componentType - return init.exprs.joinToString(", ", "[", "]") { getTargetExpr(it, componentType) } + override fun getTargetInitializerWithNotExactlyOneValue(init: Initializer, type: InferredType): String = + init.exprs.joinToString(", ", "[", "]") { + getTargetExpr(it, type.componentType) } - return super.getTargetInitializer(init, type) - } /** * Returns a type for the state variable. Note that this is TS-specific From f2207db6b0790f9d3cb6f3035f9e2086103a0d3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 7 Nov 2021 21:52:06 +0100 Subject: [PATCH 46/68] Support missing initializer in C --- org.lflang/src/org/lflang/generator/TargetTypes.java | 5 ++--- org.lflang/src/org/lflang/generator/c/CGenerator.xtend | 4 ++++ org.lflang/src/org/lflang/generator/c/CTypes.java | 5 +++++ org.lflang/src/org/lflang/generator/cpp/CppTypes.kt | 4 ++-- .../src/org/lflang/generator/python/PythonGenerator.xtend | 4 ++++ org.lflang/src/org/lflang/generator/rust/RustTypes.kt | 4 +--- 6 files changed, 18 insertions(+), 8 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/TargetTypes.java b/org.lflang/src/org/lflang/generator/TargetTypes.java index 8c417fdc29..b48013b28c 100644 --- a/org.lflang/src/org/lflang/generator/TargetTypes.java +++ b/org.lflang/src/org/lflang/generator/TargetTypes.java @@ -25,7 +25,6 @@ package org.lflang.generator; -import java.util.Objects; import java.util.stream.Collectors; import org.lflang.ASTUtils; @@ -185,7 +184,7 @@ default String getTargetBraceExpr(BraceExpr expr, InferredType type) { * * @throws UnsupportedGeneratorFeatureException If the target does not support this */ - default String getMissingExpr() { + default String getMissingExpr(InferredType type) { throw new UnsupportedGeneratorFeatureException("Missing initializers"); } @@ -251,7 +250,7 @@ default String getTargetInitializer(Initializer init, Type type) { default String getTargetInitializer(Initializer init, InferredType inferredType) { if (init == null) { - return getMissingExpr(); + return getMissingExpr(inferredType); } Value single = JavaAstUtils.asSingleValue(init); return single != null ? getTargetExpr(single, inferredType) diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend index 419e5f6439..95d7ff2627 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend @@ -4470,6 +4470,10 @@ class CGenerator extends GeneratorBase { return CTypes.INSTANCE.getTargetInitializerWithNotExactlyOneValue(init, type) } + override String getMissingExpr(InferredType type) { + return "0" + } + protected def getInitializer(Initializer init, InferredType t, ReactorInstance parent) { val customExprMaker = new CTypes() { override String getTargetParamRef(ParamRef expr, InferredType t) { diff --git a/org.lflang/src/org/lflang/generator/c/CTypes.java b/org.lflang/src/org/lflang/generator/c/CTypes.java index 67ca08e743..0b0363fe77 100644 --- a/org.lflang/src/org/lflang/generator/c/CTypes.java +++ b/org.lflang/src/org/lflang/generator/c/CTypes.java @@ -81,6 +81,11 @@ public String getTargetTimeExpr(long magnitude, TimeUnit unit) { } } + @Override + public String getMissingExpr(InferredType type) { + return "0"; + } + @Override public String getTargetInitializerWithNotExactlyOneValue(Initializer init, InferredType type) { return init.getExprs().stream() diff --git a/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt b/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt index 9414eb2410..59fc630976 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt @@ -72,12 +72,12 @@ object CppTypes : TargetTypes { } fun CppTypes.getCppInitializerWithTypePrefix(init: Initializer?, type: InferredType): String = - if (init == null) missingExpr + if (init == null) getMissingExpr(type) else getTargetType(type) + getCppInitializerWithoutTypePrefix(init, type) fun CppTypes.getCppInitializerWithoutTypePrefix(init: Initializer?, inferredType: InferredType?): String { if (init == null) { - return missingExpr + return getMissingExpr(inferredType) } val singleExpr = init.exprs.singleOrNull() return if (init.isAssign && singleExpr is BraceExpr) diff --git a/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend b/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend index c270e01264..67c1c1104e 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend @@ -235,6 +235,10 @@ class PythonGenerator extends CGenerator { else return init.exprs.join("(", ", ", ")", [getTargetExpr(it, type.componentType)]) } + override String getMissingExpr(InferredType type) { + return "None" + } + override String getTargetInitializer(Initializer init, InferredType type) { if (init.isParens) { // tuple expr, but only if size != 1 or trailing comma diff --git a/org.lflang/src/org/lflang/generator/rust/RustTypes.kt b/org.lflang/src/org/lflang/generator/rust/RustTypes.kt index 17c847bd7c..c784e32ec8 100644 --- a/org.lflang/src/org/lflang/generator/rust/RustTypes.kt +++ b/org.lflang/src/org/lflang/generator/rust/RustTypes.kt @@ -25,12 +25,10 @@ package org.lflang.generator.rust import org.lflang.InferredType -import org.lflang.JavaAstUtils import org.lflang.generator.TargetCode import org.lflang.generator.TargetTypes import org.lflang.lf.Initializer import org.lflang.lf.TimeUnit -import org.lflang.lf.Type object RustTypes : TargetTypes { @@ -77,7 +75,7 @@ object RustTypes : TargetTypes { else init.exprs.joinToString(", ", "vec![", "].into()") { getTargetExpr(it, type.componentType) } - override fun getMissingExpr(): String = + override fun getMissingExpr(type: InferredType): String = "Default::default()" } From ed3fde92061e00c253672d7d39af148f19a92b94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 8 Nov 2021 12:17:59 +0100 Subject: [PATCH 47/68] Convert some stuff from AstUtils to java --- org.lflang/src/org/lflang/ASTUtils.xtend | 98 ------------------- org.lflang/src/org/lflang/AstExtensions.kt | 9 +- org.lflang/src/org/lflang/JavaAstUtils.java | 74 +++++++++++++- org.lflang/src/org/lflang/ModelInfo.java | 6 +- org.lflang/src/org/lflang/TimeValue.java | 5 + .../src/org/lflang/federated/FedASTUtils.java | 14 +-- .../org/lflang/generator/rust/RustModel.kt | 2 +- 7 files changed, 91 insertions(+), 117 deletions(-) diff --git a/org.lflang/src/org/lflang/ASTUtils.xtend b/org.lflang/src/org/lflang/ASTUtils.xtend index c83eaefbfe..8a6b1cb4f7 100644 --- a/org.lflang/src/org/lflang/ASTUtils.xtend +++ b/org.lflang/src/org/lflang/ASTUtils.xtend @@ -1006,105 +1006,7 @@ class ASTUtils { return true } - /** - * Report whether the given parameter has been declared a type or has been - * inferred to be a type. Note that if the parameter was declared to be a - * time, its initialization may still be faulty (assigning a value that is - * not actually a valid time). - * @param A parameter - * @return True if the argument denotes a time, false otherwise. - */ - def static boolean isOfTimeType(Parameter p) { - if (p !== null) { - // Either the type has to be declared as a time. - if (p.type !== null && p.type.isTime) { - return true - } - // Or it has to be initialized as a proper time with units. - // In other words, one can write: - // - `x:time(0)` -OR- - // - `x:(0 msec)`, `x:(0 sec)`, etc. - - val init = p.init?.asSingleValue - return init instanceof Time && (init as Time).unit !== TimeUnit.NONE - } - return false - } - - /** - * Report whether the given state variable denotes a time or not. - * @param A state variable - * @return True if the argument denotes a time, false otherwise. - */ - def static boolean isOfTimeType(StateVar s) { - if (s !== null) { - // Either the type has to be declared as a time. - if (s.type !== null) - return s.type.isTime - // Or the it has to be initialized as a time except zero. - val init = s.init.asSingleValue - if (init !== null && init.isValidTime && !init.isZero) - return true - - // In other words, one can write: - // - `x:time(0)` -OR- - // - `x:(0 msec)`, `x:(0 sec)`, etc. -OR- - // - `x:(p)` where p is defined as above. - } - return false - } - - /** - * Assuming that the given parameter is of time type, return - * its initial value. - * @param p The AST node to inspect. - * @return A time value based on the given parameter's initial value. - */ - def static TimeValue getInitialTimeValue(Parameter p) { - if (p !== null && p.isOfTimeType) { - val init = p.init.asSingleValue - if (init instanceof Time) { - return init.toTimeValue - } else if (init instanceof ParamRef) { - return getInitialTimeValue(init.parameter) - } else { - return TimeValue.ZERO - } - } - return null - } - - /** - * Assuming that the given value denotes a valid time, return a time value. - * @param p The AST node to inspect. - * @return A time value based on the given parameter's initial value. - */ - def static TimeValue getTimeValue(Value v) { - if (v instanceof ParamRef) { - return ASTUtils.getInitialTimeValue(v.parameter) - } else if (v instanceof Time) { - return v.toTimeValue - } else { - return TimeValue.ZERO //fixme doesn't look right. - } - } - - /** - * Given an initializer that is known to be of a list type - * (because of a type annotation), return the components of - * the list. Return null if the initializer is not a list. - */ - def static List initializerAsList(Initializer init) { - return if (init.isAssign) { - val list = init.asSingleValue - if (list instanceof BracketExpr) list.items - else null - } else { - init.exprs - } - } - /** * Given a parameter, return its initial value. * The initial value is a list of instances of Expr, where each diff --git a/org.lflang/src/org/lflang/AstExtensions.kt b/org.lflang/src/org/lflang/AstExtensions.kt index b5b60c6c44..06858e4d1c 100644 --- a/org.lflang/src/org/lflang/AstExtensions.kt +++ b/org.lflang/src/org/lflang/AstExtensions.kt @@ -125,17 +125,18 @@ val Reactor.isGeneric get() = ASTUtils.isGeneric(toDefinition()) * inferred to be a type. Note that if the parameter was declared to be a * time, its initialization may still be faulty (assigning a value that is * not actually a valid time). - * @see ASTUtils.isOfTimeType + * + * @see JavaAstUtils.isOfTimeType * @return True if the receiver denotes a time, false otherwise. */ -val Parameter.isOfTimeType: Boolean get() = ASTUtils.isOfTimeType(this) +val Parameter.isOfTimeType: Boolean get() = JavaAstUtils.isOfTimeType(this) /** * Report whether the given state variable denotes a time or not. - * @see ASTUtils.isOfTimeType + * @see JavaAstUtils.isOfTimeType * @return True if the receiver denotes a time, false otherwise. */ -val StateVar.isOfTimeType: Boolean get() = ASTUtils.isOfTimeType(this) +val StateVar.isOfTimeType: Boolean get() = JavaAstUtils.isOfTimeType(this) /** * Translate this code element into its textual representation. diff --git a/org.lflang/src/org/lflang/JavaAstUtils.java b/org.lflang/src/org/lflang/JavaAstUtils.java index c775b5e850..623e27484f 100644 --- a/org.lflang/src/org/lflang/JavaAstUtils.java +++ b/org.lflang/src/org/lflang/JavaAstUtils.java @@ -30,6 +30,8 @@ import java.util.stream.Collectors; import org.lflang.lf.Action; +import org.lflang.lf.BraceExpr; +import org.lflang.lf.BracketExpr; import org.lflang.lf.Initializer; import org.lflang.lf.ParamRef; import org.lflang.lf.Parameter; @@ -204,7 +206,77 @@ public static TimeValue toTimeValue(Time e) { */ public static String addZeroToLeadingDot(String literal) { Matcher m = ABBREVIATED_FLOAT.matcher(literal); - if (m.matches()) return literal.replace(".", "0."); + if (m.matches()) { + return literal.replace(".", "0."); + } return literal; } + + /** + * Assuming that the given value denotes a valid time, + * return a time value. + */ + public static TimeValue getTimeValue(Value v) { + if (v instanceof ParamRef) { + return getDefaultAsTimeValue(((ParamRef) v).getParameter()); + } else if (v instanceof Time) { + return toTimeValue((Time) v); + } else { + return null; + } + } + + /** + * If the parameter is of time type, return its default value. + * Otherwise, return null. + */ + public static TimeValue getDefaultAsTimeValue(Parameter p) { + if (isOfTimeType(p)) { + var init = asSingleValue(p.getInit()); + if (init != null) { + return getTimeValue(init); + } + } + return null; + } + + /** + * Return whether the given state variable is inferred + * to a time type. + */ + public static boolean isOfTimeType(StateVar state) { + InferredType t = getInferredType(state); + return t.isTime && !t.isList; + } + + /** + * Return whether the given parameter is inferred + * to a time type. + */ + public static boolean isOfTimeType(Parameter param) { + InferredType t = getInferredType(param); + return t.isTime && !t.isList; + } + + /** + * Given an initializer that is known to be of a list type + * (because of a type annotation), return the components of + * the list. Return null if the initializer is not a list. + */ + public static List initializerAsList(Initializer init) { + if (init.isAssign()) { + var list = asSingleValue(init); + if (list instanceof BracketExpr) { + return ((BracketExpr) list).getItems(); + } else if (list instanceof BraceExpr) { + return ((BraceExpr) list).getItems(); + } else { + return null; + } + } else { + return init.getExprs(); + } + } + + } diff --git a/org.lflang/src/org/lflang/ModelInfo.java b/org.lflang/src/org/lflang/ModelInfo.java index 3fa9b169f0..542aa77dca 100644 --- a/org.lflang/src/org/lflang/ModelInfo.java +++ b/org.lflang/src/org/lflang/ModelInfo.java @@ -158,7 +158,7 @@ private void collectOverflowingNodes() { // Visit all deadlines in the model; detect possible overflow. for (var deadline : filter(toIterable(model.eAllContents()), Deadline.class)) { // If the time value overflows, mark this deadline as overflowing. - if (isTooLarge(ASTUtils.getTimeValue(deadline.getDelay()))) { + if (isTooLarge(JavaAstUtils.getTimeValue(deadline.getDelay()))) { this.overflowingDeadlines.add(deadline); } @@ -192,7 +192,7 @@ private boolean detectOverflow(Set visited, Parameter current) { var overflow = false; // Determine whether the parameter's default value overflows or not. - if (isTooLarge(ASTUtils.getInitialTimeValue(current))) { + if (isTooLarge(JavaAstUtils.getDefaultAsTimeValue(current))) { this.overflowingParameters.add(current); overflow = true; } @@ -214,7 +214,7 @@ private boolean detectOverflow(Set visited, Parameter current) { } else { // The right-hand side of the assignment is a // constant; check whether it is too large. - if (isTooLarge(ASTUtils.getTimeValue(init))) { + if (isTooLarge(JavaAstUtils.getTimeValue(init))) { this.overflowingAssignments.add(assignment); overflow = true; } diff --git a/org.lflang/src/org/lflang/TimeValue.java b/org.lflang/src/org/lflang/TimeValue.java index ba9544ed11..9a55bb0323 100644 --- a/org.lflang/src/org/lflang/TimeValue.java +++ b/org.lflang/src/org/lflang/TimeValue.java @@ -140,6 +140,11 @@ public String toString() { : Long.toString(time); } + /** Return the latest of both values. */ + public static TimeValue max(TimeValue t1, TimeValue t2) { + return t1.isEarlierThan(t2) ? t2 : t1; + } + /** * Return the sum of this duration and the one represented by b. *

    diff --git a/org.lflang/src/org/lflang/federated/FedASTUtils.java b/org.lflang/src/org/lflang/federated/FedASTUtils.java index 1568d4c888..49d3d9fcce 100644 --- a/org.lflang/src/org/lflang/federated/FedASTUtils.java +++ b/org.lflang/src/org/lflang/federated/FedASTUtils.java @@ -455,16 +455,10 @@ private static TimeValue findMaxSTP(Variable port, } } } - - TimeValue maxSTP = new TimeValue(0, TimeUnit.NONE); - for (Value value : safe(STPList)) { - TimeValue tValue = ASTUtils.getTimeValue(value); - if(maxSTP.isEarlierThan(tValue)) { - maxSTP = tValue; - } - } - - return maxSTP; + + return STPList.stream() + .map(JavaAstUtils::getTimeValue) + .reduce(TimeValue.ZERO, TimeValue::max); } private static void addStp(List STPList, Reaction r, Instantiation instant) { diff --git a/org.lflang/src/org/lflang/generator/rust/RustModel.kt b/org.lflang/src/org/lflang/generator/rust/RustModel.kt index 6a0263336f..54276dc352 100644 --- a/org.lflang/src/org/lflang/generator/rust/RustModel.kt +++ b/org.lflang/src/org/lflang/generator/rust/RustModel.kt @@ -565,7 +565,7 @@ object RustModelBuilder { documentation = null, // todo isTime = it.inferredType.isTime, isList = it.inferredType.isList, - defaultValueAsTimeValue = ASTUtils.getInitialTimeValue(it), + defaultValueAsTimeValue = JavaAstUtils.getDefaultAsTimeValue(it), ) } ) From 006ec71e2d8b0999c3ced6d5084ed7e28e01f343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 8 Nov 2021 12:35:48 +0100 Subject: [PATCH 48/68] Fix C++ tests Also fix a corner case failing on master - added a test --- .../lflang/generator/cpp/CppInstanceGenerator.kt | 2 +- .../src/target/GenericParameterDefaultValue.lf | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 test/Cpp/src/target/GenericParameterDefaultValue.lf diff --git a/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt index f6351e7cf2..5f03c30560 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt @@ -59,7 +59,7 @@ class CppInstanceGenerator( // to the parameter named "bank_index" """__lf_idx""" } else { - CppTypes.getTargetInitializer(rhs, param.type) + CppTypes.getCppInitializerWithoutTypePrefix(rhs, param.inferredType) } } diff --git a/test/Cpp/src/target/GenericParameterDefaultValue.lf b/test/Cpp/src/target/GenericParameterDefaultValue.lf new file mode 100644 index 0000000000..860564e064 --- /dev/null +++ b/test/Cpp/src/target/GenericParameterDefaultValue.lf @@ -0,0 +1,15 @@ +target Cpp; + +reactor Foo (bar:T(0)) { + + reaction (startup) {= + if (bar != 0) { + std::cerr << "ERROR: Expected bar=0 but got bar=" << bar << '\n'; + exit(1); + } + =} +} + +main reactor { + foo = new Foo() // bar takes a default value +} From 7093364c412a0fffa2cb73ffd6408af0da05c747 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 8 Nov 2021 13:02:57 +0100 Subject: [PATCH 49/68] Missing ctor in typescript test --- org.lflang.tests/src/org/lflang/tests/TestBase.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/org.lflang.tests/src/org/lflang/tests/TestBase.java b/org.lflang.tests/src/org/lflang/tests/TestBase.java index 58ad3d1bed..37bf39906f 100644 --- a/org.lflang.tests/src/org/lflang/tests/TestBase.java +++ b/org.lflang.tests/src/org/lflang/tests/TestBase.java @@ -267,8 +267,12 @@ public static void runSingleTestAndPrintResults(LFTest test, Class[] ctors = testClass.getConstructors(); + if (ctors.length == 0) { + throw new IllegalStateException("No public constructor in " + testClass); + } @SuppressWarnings("unchecked") - Constructor constructor = (Constructor) testClass.getConstructors()[0]; + Constructor constructor = (Constructor) ctors[0]; runner = constructor.newInstance(); } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { throw new IllegalStateException(e); From 42130448ddf7dd86e204aa03223c2720a1a58d1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 8 Nov 2021 13:24:09 +0100 Subject: [PATCH 50/68] Fix TS tests --- .../org/lflang/generator/GeneratorUtils.kt | 27 +++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/GeneratorUtils.kt b/org.lflang/src/org/lflang/generator/GeneratorUtils.kt index acb81851ac..8dff2cc6c5 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorUtils.kt +++ b/org.lflang/src/org/lflang/generator/GeneratorUtils.kt @@ -2,11 +2,8 @@ package org.lflang.generator import org.eclipse.emf.ecore.EObject import org.eclipse.xtext.nodemodel.util.NodeModelUtils -import org.lflang.InferredType +import org.lflang.* import org.lflang.lf.* -import org.lflang.toPath -import org.lflang.toTextTokenBased -import org.lflang.toUnixString fun TargetTypes.getTargetInitializer(sv: StateVar): TargetCode = @@ -16,21 +13,17 @@ fun TargetTypes.getTargetInitializer(sv: Parameter): TargetCode = this.getTargetInitializer(sv.init, sv.type) /** - * Returns the target code for the initial value of a parameter + * Returns the target code for the actual value of a parameter * for the given instantiation. The value is defaulted to the - * default value for the parameter. + * default value for the parameter if there is no explicit + * assignment. */ -fun TargetTypes.getTargetInitializer(sv: Parameter, inst: Instantiation): TargetCode { - val delegated = object : TargetTypes by this { - override fun getTargetParamRef(expr: ParamRef, type: InferredType?): String { - val init = inst.parameters.firstOrNull { it.lhs == expr.parameter }?.rhs - ?: expr.parameter.init - ?: throw InvalidLfSourceException(inst, "No value for parameter ${sv.name}") - - return super.getTargetInitializer(init, sv.type) - } - } - return delegated.getTargetInitializer(sv) +fun TargetTypes.getTargetInitializer(param: Parameter, inst: Instantiation): TargetCode { + val init = inst.parameters.firstOrNull { it.lhs == param }?.rhs + ?: param.init + ?: throw InvalidLfSourceException(inst, "No value for parameter ${param.name}") + + return getTargetInitializer(init, param.inferredType) } From b1cd66aaa957c011e31cf73fad1c60d666c19f41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 8 Nov 2021 13:54:09 +0100 Subject: [PATCH 51/68] Guard against null time values --- org.lflang/src/org/lflang/federated/FedASTUtils.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/org.lflang/src/org/lflang/federated/FedASTUtils.java b/org.lflang/src/org/lflang/federated/FedASTUtils.java index 49d3d9fcce..49f73657ff 100644 --- a/org.lflang/src/org/lflang/federated/FedASTUtils.java +++ b/org.lflang/src/org/lflang/federated/FedASTUtils.java @@ -31,6 +31,7 @@ import java.util.Collections; import java.util.LinkedList; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; @@ -458,6 +459,7 @@ private static TimeValue findMaxSTP(Variable port, return STPList.stream() .map(JavaAstUtils::getTimeValue) + .filter(Objects::nonNull) .reduce(TimeValue.ZERO, TimeValue::max); } From e268913b46b3e927a94b9043d6f7e80059fbd863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 8 Nov 2021 16:18:19 +0100 Subject: [PATCH 52/68] Hopefully fix C++ for good --- .../org/lflang/generator/GeneratorUtils.kt | 22 ++++++---- .../generator/cpp/CppInstanceGenerator.kt | 12 +++--- .../generator/cpp/CppParameterGenerator.kt | 2 +- .../lflang/generator/cpp/CppStateGenerator.kt | 2 +- .../src/org/lflang/generator/cpp/CppTypes.kt | 43 +++++++++++-------- test/Cpp/src/Initializers.lf | 9 ++++ 6 files changed, 54 insertions(+), 36 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/GeneratorUtils.kt b/org.lflang/src/org/lflang/generator/GeneratorUtils.kt index 8dff2cc6c5..4d36a9895f 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorUtils.kt +++ b/org.lflang/src/org/lflang/generator/GeneratorUtils.kt @@ -13,19 +13,25 @@ fun TargetTypes.getTargetInitializer(sv: Parameter): TargetCode = this.getTargetInitializer(sv.init, sv.type) /** - * Returns the target code for the actual value of a parameter - * for the given instantiation. The value is defaulted to the - * default value for the parameter if there is no explicit - * assignment. + * Returns the target code for the [getActualValue] of the + * param for this instantiation. */ fun TargetTypes.getTargetInitializer(param: Parameter, inst: Instantiation): TargetCode { - val init = inst.parameters.firstOrNull { it.lhs == param }?.rhs - ?: param.init - ?: throw InvalidLfSourceException(inst, "No value for parameter ${param.name}") - + val init = inst.getActualValue(param) return getTargetInitializer(init, param.inferredType) } +/** + * Returns the actual value of a parameter for the given instantiation. + * The value is defaulted to the default value for the parameter if + * there is no explicit assignment. If there is no default value, the + * source code is invalid (param is required) + */ +fun Instantiation.getActualValue(param: Parameter): Initializer = + parameters.firstOrNull { it.lhs == param }?.rhs + ?: param.init + ?: throw InvalidLfSourceException(this, "No value for parameter ${param.name}") + fun TargetTypes.getTargetTimeExpr(v: Value): TargetCode = this.getTargetExpr(v, InferredType.time()) diff --git a/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt index 5f03c30560..db5c531942 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt @@ -25,7 +25,7 @@ package org.lflang.generator.cpp import org.lflang.* -import org.lflang.generator.cpp.CppParameterGenerator.Companion.targetType +import org.lflang.generator.getActualValue import org.lflang.lf.Instantiation import org.lflang.lf.Parameter import org.lflang.lf.Reactor @@ -51,17 +51,15 @@ class CppInstanceGenerator( "std::unique_ptr<$cppType> $name;" } - private fun Instantiation.getParameterValue(param: Parameter, isBankInstantiation: Boolean = false): String { - val rhs = this.parameters.firstOrNull { it.lhs === param }?.rhs ?: param.init - - return if (isBankInstantiation && param.name == "bank_index") { + private fun Instantiation.getParameterValue(param: Parameter, isBankInstantiation: Boolean = false): String = + if (isBankInstantiation && param.name == "bank_index") { // If we are in a bank instantiation (instanceId != null), then assign the instanceId // to the parameter named "bank_index" """__lf_idx""" } else { - CppTypes.getCppInitializerWithoutTypePrefix(rhs, param.inferredType) + val value = this.getActualValue(param) + CppTypes.getCppStandaloneInitializer(value, param.inferredType) } - } private fun generateInitializer(inst: Instantiation): String { assert(!inst.isBank) diff --git a/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt index 92ee451e54..3fd3f9df2f 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt @@ -38,7 +38,7 @@ class CppParameterGenerator(private val reactor: Reactor) { /** Get the default value of the receiver parameter in C++ code */ val Parameter.defaultValue: String - get() = CppTypes.getCppInitializerWithTypePrefix(init, inferredType) + get() = CppTypes.getCppStandaloneInitializer(init, inferredType) /** Get a C++ type that is a const reference to the parameter type */ val Parameter.constRefType: String diff --git a/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt index 9c5b3e1049..3a68e2aacb 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt @@ -32,7 +32,7 @@ import org.lflang.lf.StateVar class CppStateGenerator(private val reactor: Reactor) { private fun generateInitializer(state: StateVar): String = - state.name + CppTypes.getCppInitializerWithoutTypePrefix(state.init, state.inferredType) + state.name + CppTypes.getCppInitializerList(state.init, state.inferredType) /** Get all state declarations */ fun generateDeclarations() = diff --git a/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt b/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt index 59fc630976..2d409e484c 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt @@ -47,20 +47,7 @@ object CppTypes : TargetTypes { override fun getTargetVariableSizeListType(baseType: String) = "std::vector<$baseType>" override fun getTargetInitializer(init: Initializer?, inferredType: InferredType): String { - return getCppInitializerWithTypePrefix(init, inferredType) - } - - override fun getTargetBraceExpr(expr: BraceExpr, type: InferredType?): String { - val braceInitializer = super.getTargetBraceExpr(expr, type) - return if (type?.isList == true) { - // prefix the {...} with std::vector<_> - if (type.isTime) - getTargetVariableSizeListType(targetTimeType) + braceInitializer - else - getTargetVariableSizeListType(type.baseType()) + braceInitializer - } else { - braceInitializer - } + return getCppStandaloneInitializer(init, inferredType) } override fun getTargetUndefinedType() = "void" @@ -71,11 +58,10 @@ object CppTypes : TargetTypes { } -fun CppTypes.getCppInitializerWithTypePrefix(init: Initializer?, type: InferredType): String = - if (init == null) getMissingExpr(type) - else getTargetType(type) + getCppInitializerWithoutTypePrefix(init, type) - -fun CppTypes.getCppInitializerWithoutTypePrefix(init: Initializer?, inferredType: InferredType?): String { +/** + * Returns the initializer list used in direct initialization in ctor definition. + */ +fun CppTypes.getCppInitializerList(init: Initializer?, inferredType: InferredType?): String { if (init == null) { return getMissingExpr(inferredType) } @@ -97,6 +83,25 @@ fun CppTypes.getCppInitializerWithoutTypePrefix(init: Initializer?, inferredType } } +fun CppTypes.getCppStandaloneInitializer(init: Initializer?, inferredType: InferredType?): String { + if (init == null) { + return getMissingExpr(inferredType) + } + + return buildString { + if (init.exprs.size == 1) { // also the case for = assignment + append(getTargetExpr(init.exprs.single(), inferredType)) + } else { + append(getTargetType(inferredType)) // treat as ctor call + val (prefix, postfix) = if (init.isBraces) Pair("{", "}") else Pair("(", ")") + init.exprs.joinTo(this, ", ", prefix, postfix) { + getTargetExpr(it, inferredType?.componentType) + } + } + } +} + + /** * This object generates types in the context of the outer class, * where parameter references need special handling. diff --git a/test/Cpp/src/Initializers.lf b/test/Cpp/src/Initializers.lf index 625aec99f5..31c47bd523 100644 --- a/test/Cpp/src/Initializers.lf +++ b/test/Cpp/src/Initializers.lf @@ -12,10 +12,17 @@ reactor Foo( state s0: int = 2 - 2 - 2; state s1: int(2 - 2 - 2); state s2: std::string = "a"; state s3: char(2 + 'a'); + state s4: Point{1, 2}; + state s4p: Point = {1, 2}; + private preamble{= #include =} + public preamble {= +struct Point { int x; int y; }; + =} + reaction(startup) {= assert(p1 == std::vector(4, 2)); assert(p2 == (std::vector { 4, 2 })); @@ -26,6 +33,8 @@ reactor Foo( assert(s1 == -2); assert(s2 == "a"); assert(s3 == 'c'); + assert(s4 == (Point {1, 2})); + assert(s4p == (Point {1, 2})); std::cerr << "Success\n"; =} From 35729b834e656d95d52782e4ae0a9b8a1da0a6a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 23 Jan 2022 20:07:45 +0100 Subject: [PATCH 53/68] Fix some conflicts still wip --- org.lflang/src/org/lflang/JavaAstUtils.java | 15 +- org.lflang/src/org/lflang/TimeValue.java | 6 - .../org/lflang/generator/ActionInstance.xtend | 75 -- .../org/lflang/generator/GeneratorBase.xtend | 14 +- .../lflang/generator/ParameterInstance.xtend | 138 -- .../lflang/generator/ReactorInstance.xtend | 1107 ----------------- .../org/lflang/generator/c/CGenerator.xtend | 15 +- .../src/org/lflang/generator/c/CTypes.java | 17 +- .../org/lflang/validation/LFValidator.xtend | 4 +- 9 files changed, 39 insertions(+), 1352 deletions(-) delete mode 100644 org.lflang/src/org/lflang/generator/ActionInstance.xtend delete mode 100644 org.lflang/src/org/lflang/generator/ParameterInstance.xtend delete mode 100644 org.lflang/src/org/lflang/generator/ReactorInstance.xtend diff --git a/org.lflang/src/org/lflang/JavaAstUtils.java b/org.lflang/src/org/lflang/JavaAstUtils.java index 7473276718..8c95032e43 100644 --- a/org.lflang/src/org/lflang/JavaAstUtils.java +++ b/org.lflang/src/org/lflang/JavaAstUtils.java @@ -33,6 +33,7 @@ import org.lflang.lf.BraceExpr; import org.lflang.lf.BracketExpr; import org.lflang.lf.Initializer; +import org.lflang.lf.Literal; import org.lflang.lf.ParamRef; import org.lflang.lf.Parameter; import org.lflang.lf.Port; @@ -224,10 +225,20 @@ public static String addZeroToLeadingDot(String literal) { * Assuming that the given value denotes a valid time literal, * return a time value. */ - public static TimeValue getLiteralTimeValue(Value v) {; + public static TimeValue getLiteralTimeValue(Value v) { if (v instanceof Time) { return toTimeValue((Time) v); - } else if (v instanceof Literal) { + } else if (v instanceof Literal && ASTUtils.isZero(v)) { + return TimeValue.ZERO; + } else { + return null; + } + } + + public static TimeValue getTimeValue(Value v) { + if (v instanceof Time) { + return toTimeValue((Time) v); + } else if (v instanceof Literal && ASTUtils.isZero(v)) { return TimeValue.ZERO; } else { return null; diff --git a/org.lflang/src/org/lflang/TimeValue.java b/org.lflang/src/org/lflang/TimeValue.java index 768566c052..3775d09e64 100644 --- a/org.lflang/src/org/lflang/TimeValue.java +++ b/org.lflang/src/org/lflang/TimeValue.java @@ -42,12 +42,6 @@ public final class TimeValue implements Comparable { */ public static final TimeValue ZERO = new TimeValue(0, null); - - /** - * A time value equal to zero. - */ - public static final TimeValue ZERO = new TimeValue(0, TimeUnit.NONE); - /** * Primitive numerical representation of this time value, * to be interpreted in terms the associated time unit. diff --git a/org.lflang/src/org/lflang/generator/ActionInstance.xtend b/org.lflang/src/org/lflang/generator/ActionInstance.xtend deleted file mode 100644 index 9edd00d7d1..0000000000 --- a/org.lflang/src/org/lflang/generator/ActionInstance.xtend +++ /dev/null @@ -1,75 +0,0 @@ -/* Instance of an action. */ - -/************* -Copyright (c) 2019, The University of California at Berkeley. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - 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 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 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************/ - -package org.lflang.generator - -import org.eclipse.xtend.lib.annotations.Accessors -import org.lflang.TimeValue -import org.lflang.lf.Action -import org.lflang.lf.ActionOrigin - -/** - * Instance of an action. - * @author{Edward A. Lee } - * @author{Marten Lohstroh } - */ -class ActionInstance extends TriggerInstance { - - /** The constant default for a minimum delay. */ - public static val DEFAULT_MIN_DELAY = TimeValue.ZERO - - @Accessors(PUBLIC_GETTER) - TimeValue minDelay - - @Accessors(PUBLIC_GETTER) - TimeValue minSpacing - - @Accessors(PUBLIC_GETTER) - String policy - - @Accessors(PUBLIC_GETTER) - boolean isPhysical; - - /** - * Create a new timer instance. - * If the definition is null, then this is a shutdown action. - * @param definition The AST definition, or null for startup. - * @param parent The parent reactor. - */ - new(Action definition, ReactorInstance parent) { - super(definition, parent) - if (parent === null) { - throw new InvalidSourceException('Cannot create an ActionInstance with no parent.') - } - this.minDelay = parent.resolveTimeValue(definition?.minDelay) ?: DEFAULT_MIN_DELAY - // TODO introduce default value? - this.minSpacing = parent.resolveTimeValue(definition?.minSpacing) - if (definition?.origin === ActionOrigin.PHYSICAL) { - isPhysical = true - } - policy = definition?.policy - } -} diff --git a/org.lflang/src/org/lflang/generator/GeneratorBase.xtend b/org.lflang/src/org/lflang/generator/GeneratorBase.xtend index 8784a9f1c9..64803769be 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorBase.xtend +++ b/org.lflang/src/org/lflang/generator/GeneratorBase.xtend @@ -1756,11 +1756,11 @@ abstract class GeneratorBase extends AbstractLFValidator implements TargetTypes * @param v A time AST node * @return A time string in the target language */ - protected def String getTargetTime(Value v) { - v.timeValue.targetTimeExpr - } - - protected def String getTargetTime(Delay d) { - d.value.targetTime - } +// protected def String getTargetTime(Value v) { +// v.timeValue.targetTimeExpr +// } +// +// protected def String getTargetTime(Delay d) { +// d.value.targetTime +// } } diff --git a/org.lflang/src/org/lflang/generator/ParameterInstance.xtend b/org.lflang/src/org/lflang/generator/ParameterInstance.xtend deleted file mode 100644 index 5c210dd00d..0000000000 --- a/org.lflang/src/org/lflang/generator/ParameterInstance.xtend +++ /dev/null @@ -1,138 +0,0 @@ -/** A data structure for a parameter instance. */ - -/************* -Copyright (c) 2019, The University of California at Berkeley. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - 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 -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 -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************/ - -package org.lflang.generator - -import org.lflang.InferredType -import org.lflang.lf.LfFactory -import org.lflang.lf.Initializer -import org.lflang.lf.Parameter -import org.lflang.lf.ParamRef - -import static extension org.lflang.JavaAstUtils.* - -/** - * Representation of a runtime instance of a parameter. - * Upon creation, it is checked whether this parameter is overridden by an - * assignment in the instantiation that this parameter instance is a result of. - * If it is overridden, the parameter gets initialized using the value looked up - * in the instantiation hierarchy. - * @author{Marten Lohstroh } - * @author{Edward A. Lee } - */ -class ParameterInstance extends NamedInstance { - - public Initializer init - - public InferredType type - - /** - * Create a runtime instance from the specified definition - * and with the specified parent that instantiated it. - * @param instance The Instance statement in the AST. - * @param parent The reactor instance this parameter is a part of. - */ - new(Parameter definition, ReactorInstance parent) { - super(definition, parent) - if (parent === null) { - throw new InvalidSourceException('Cannot create a ParameterInstance with no parent.') - } - - this.type = definition.inferredType - this.init = definition.init - - // Check for an override. - var assignment = parent.definition.parameters.findFirst[it.lhs === definition] - - while (assignment !== null) { - // NOTE: we only allow a reference to single a parameter or - // a list of ordinary values. - val ref = assignment.rhs.asSingleValue - if (ref instanceof ParamRef) { - // Get the value from the parameter instance, not the parameter - // so that overrides like bank_index work. - // parent.parent will be non-null or the rhs parameter reference - // would not have passed validation. Nevertheless, we check. - val parentsParent = parent.parent; - if (parentsParent !== null) { - val parameterInstance = parentsParent.parameters.findFirst[it.name.equals(ref.parameter.name)] - // Again, this result should be non-null, but we check. - if (parameterInstance !== null) { - this.init = parameterInstance.init - } else { - // Fall back on reference. - this.init = ref.parameter.init - } - } else { - // Fall back on reference. - this.init = ref.parameter.init - } - } else { - this.init = assignment.rhs - } - if (parent.parent !== null) { - assignment = parent.parent.definition.parameters.findFirst[it.lhs === ref] - } else { - assignment = null - } - } - - // If the parent is in a bank and the parameter name is "bank_index", then - // override the default value provided to make it equal to the bank index. - if (parent.bankIndex >= 0 && definition.name.equals("bank_index")) { - val value = LfFactory.eINSTANCE.createLiteral - value.literal = "" + parent.bankIndex - - this.init = LfFactory.eINSTANCE.createInitializer - this.init.assign = true - this.init.exprs.add(value) - } - } - - ///////////////////////////////////////////// - //// Public Methods - - /** - * Return the name of this parameter. - * @return The name of this parameter. - */ - override String getName() { - this.definition.name - } - - /** - * {@inheritDoc} - */ - override ReactorInstance root() { - parent.root() - } - - /** Return a descriptive string. */ - override toString() { - "ParameterInstance " + getFullName - } -} diff --git a/org.lflang/src/org/lflang/generator/ReactorInstance.xtend b/org.lflang/src/org/lflang/generator/ReactorInstance.xtend deleted file mode 100644 index cb47b53a9e..0000000000 --- a/org.lflang/src/org/lflang/generator/ReactorInstance.xtend +++ /dev/null @@ -1,1107 +0,0 @@ -/** A data structure for a reactor instance. */ - -/************* -Copyright (c) 2019, The University of California at Berkeley. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - 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 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 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***************/ - -package org.lflang.generator - -import java.util.ArrayList -import java.util.LinkedHashMap -import java.util.LinkedHashSet -import java.util.List -import java.util.Set -import java.util.Map -import org.eclipse.xtend.lib.annotations.Accessors -import org.lflang.ASTUtils -import org.lflang.util.CollectionUtil -import org.lflang.ErrorReporter -import org.lflang.TimeValue -import org.lflang.generator.TriggerInstance.BuiltinTriggerVariable -import org.lflang.lf.Action -import org.lflang.lf.Connection -import org.lflang.lf.Input -import org.lflang.lf.Instantiation -import org.lflang.lf.Output -import org.lflang.lf.Parameter -import org.lflang.lf.ParamRef -import org.lflang.lf.Port -import org.lflang.lf.Reaction -import org.lflang.lf.Reactor -import org.lflang.lf.Timer -import org.lflang.lf.TriggerRef -import org.lflang.lf.Value -import org.lflang.lf.VarRef -import org.lflang.lf.Variable -import org.lflang.lf.WidthSpec - -import static extension org.lflang.ASTUtils.* -import static extension org.lflang.JavaAstUtils.* - -/** - * Representation of a runtime instance of a reactor. - * For the main reactor, which has no parent, once constructed, - * this object represents the entire Lingua Franca program. - * The constructor analyzes the graph of dependencies between - * reactions and throws exception if this graph is cyclic. - * - * @author{Marten Lohstroh } - * @author{Edward A. Lee } - */ -class ReactorInstance extends NamedInstance { - /** The action instances belonging to this reactor instance. */ - public val List actions = new ArrayList() - - /** - * The contained reactor instances, in order of declaration. - * For banks of reactors, this includes both the bank definition - * Reactor (which has bankIndex == -2) followed by each of the - * bank members (which have bankIndex >= 0). - */ - public val List children = new ArrayList() - - /** A map from sources to destinations as specified by the connections - * of this reactor instance. Note that this is redundant, because the same - * information is available in the port instances of this reactor and its - * children, but it is sometimes convenient to have it collected here. - */ - public val Map> destinations = new LinkedHashMap(); - - /** The input port instances belonging to this reactor instance. */ - public val List inputs = new ArrayList() - - /** The output port instances belonging to this reactor instance. */ - public val List outputs = new ArrayList() - - /** The parameters of this instance. */ - public val List parameters = new ArrayList() - - /** List of reaction instances for this reactor instance. */ - public val List reactions = new ArrayList(); - - /** The timer instances belonging to this reactor instance. */ - public val List timers = new ArrayList() - - /** The reactor definition in the AST. */ - public final Reactor reactorDefinition - - public final boolean recursive - - /** - * The LF syntax does not currently support declaring reactions unordered, - * but unordered reactions are created in the AST transformations handling - * federated communication and after delays. Unordered reactions can execute - * in any order and concurrently even though they are in the same reactor. - * FIXME: Remove this when the language provides syntax. - */ - protected var Set unorderedReactions = new LinkedHashSet() - - /** - * If this reactor is in a bank of reactors, then this member - * refers to the reactor instance defining the bank. - */ - @Accessors(PUBLIC_GETTER) - protected ReactorInstance bank = null - - /** - * If this reactor instance is a placeholder for a bank of reactors, - * as created by the new[width] ReactorClass() syntax, then this - * list will be non-null and will contain the reactor instances in - * the bank. - */ - protected List bankMembers = null - - /** - * If this reactor is in a bank of reactors, its index, otherwise, -1 - * for an ordinary reactor and -2 for a placeholder for a bank of reactors. - */ - @Accessors(PUBLIC_GETTER) - protected int bankIndex = -1 - - /** The generator that created this reactor instance. */ - protected ErrorReporter reporter // FIXME: This accumulates a lot of redundant references - - /** The startup trigger. Null if not used in any reaction. */ - @Accessors(PUBLIC_GETTER) - protected var TriggerInstance startupTrigger = null - - /** The startup trigger. Null if not used in any reaction. */ - @Accessors(PUBLIC_GETTER) - protected var TriggerInstance shutdownTrigger = null - - - /** Table recording which connection created a link between a source and destination. */ - var Map> connectionTable = new LinkedHashMap() - - /** The nested list of instantiations that created this reactor instance. */ - var List _instantiations; - - /** - * The depth in the hierarchy of this reactor instance. - * This is 0 for main or federated, 1 for the reactors immediately contained, etc. - */ - var int depth = 0; - - /** - * Data structure that maps connections to their connections as they appear - * in a visualization of the program. For each connection, there is map - * from source ports (single ports and multiports) on the left side of the - * connection to a set of destination ports (single ports and multiports) - * on the right side of the connection. The ports contained by the multiports - * are not represented. - */ - @Accessors(PUBLIC_GETTER) - val Map>> connections = new LinkedHashMap() - - /** - * Create a new instantiation hierarchy that starts with the given top-level reactor. - * @param reactor The top-level reactor. - * @param reporter The error reporter. - */ - new(Reactor reactor, ErrorReporter reporter) { - this(ASTUtils.createInstantiation(reactor), null, reporter, -1, null) - } - - /** - * Create a new instantiation hierarchy that starts with the given top-level reactor - * but only creates contained reactors up to the specified depth. - * @param reactor The top-level reactor. - * @param reporter The error reporter. - * @param desiredDepth The depth to which to go, or -1 to construct the full hierarchy. - */ - new(Reactor reactor, ErrorReporter reporter, int desiredDepth) { - this(ASTUtils.createInstantiation(reactor), null, reporter, desiredDepth, null) - } - - /** - * Create a new instantiation hierarchy that starts with the given reactor. - * @param reactor The top-level reactor. - * @param reporter The error reporter. - * @param unorderedReactions A list of reactions that should be treated as unordered. - */ - new(Reactor reactor, ErrorReporter reporter, Set unorderedReactions) { - this(ASTUtils.createInstantiation(reactor), null, reporter, -1, unorderedReactions) - } - - - /** - * Populate destinations map and the connectivity information in the port instances. - * Note that this can only happen _after_ the children and port instances have been created. - * Unfortunately, we have to do some complicated things here - * to support multiport-to-multiport, multiport-to-bank, - * and bank-to-multiport communication. The principle being followed is: - * in each connection statement, for each port instance on the left, - * as obtained by the nextPort() function, connect to the next available - * port on the right, as obtained by the nextPort() function. - */ - def establishPortConnections() { - for (connection : reactorDefinition.allConnections) { - val leftPortInstances = listPortInstances(connection.leftPorts) - val rightPortInstances = listPortInstances(connection.rightPorts) - - // Check widths. - if (leftPortInstances.size > rightPortInstances.size) { - reporter.reportWarning(connection, "Source is wider than the destination. Outputs will be lost.") - } else if (leftPortInstances.size < rightPortInstances.size && !connection.isIterated) { - reporter.reportWarning(connection, "Destination is wider than the source. Inputs will be missing.") - } - - var leftPortIndex = 0 - for (rightPortInstance : rightPortInstances) { - if (leftPortIndex < leftPortInstances.size) { - val leftPortInstance = leftPortInstances.get(leftPortIndex) - connectPortInstances(connection, leftPortInstance, rightPortInstance) - } - leftPortIndex++ - if (connection.isIterated && leftPortIndex >= leftPortInstances.size) { - leftPortIndex = 0 - } - } - } - } - - /** - * Given a list of port references, as found on either side of a connection, - * return a list of the port instances referenced. If the port is a multiport, - * the list will contain the individual port instance, not the multiport instance. - * If the port reference has the form `c.x`, where `c` is a bank of reactors, - * then the list will contain the port instances belonging to each bank member. - * - * If a given port reference `b.m`, where `b` is a bank and `m` is a multiport, - * is unqualified, this function iterates over bank members first, then ports. - * E.g., if `b` and `m` have width 2, it returns `[b0.m0, b0.m1, b1.m0, b1.m1]`. - * - * If a given port reference has the form `interleaved(b.m)`, where `b` is a - * bank and `m` is a multiport, this function iterates over ports first, - * then bank members. E.g., if `b` and `m` have width 2, it returns - * `[b0.m0, b1.m0, b0.m1, b1.m1]`. - */ - private def List listPortInstances(List references) { - val result = new ArrayList(); - for (portRef : references) { - // Simple error checking first. - if (!(portRef.variable instanceof Port)) { - reporter.reportError(portRef, "Not a port."); - return result; - } - // First, figure out which reactor we are dealing with. - // The reactor we want is the container of the port. - // If the port reference has no container, then the reactor is this one, - // or if this one is a bank, the next available bank member. - var reactor = this - if (portRef.container !== null) { - reactor = getChildReactorInstance(portRef.container); - } - // The reactor be null only if there is an error in the code. - // Skip this portRef so that diagram synthesis can complete. - if (reactor !== null) { - if (reactor.bankMembers !== null) { - // Reactor is a bank. - if (!portRef.isInterleaved) { - // Not a cross connection. - for (memberReactor: reactor.bankMembers) { - val portInstance = memberReactor.lookupPortInstance(portRef.variable as Port) - if (portInstance instanceof MultiportInstance) { - // Multiport within a bank. - result.addAll(portInstance.instances) - } else { - // Not multiport. - result.add(portInstance) - } - } - } else { - // It is a cross connection. - var width = 1 - for (var i = 0; i < width; i++) { - for (memberReactor: reactor.bankMembers) { - val portInstance = memberReactor.lookupPortInstance(portRef.variable as Port) - if (portInstance instanceof MultiportInstance) { - // Multiport, bank, cross connection. - // Assume all have the same width. - // Otherwise, stop collecting ports. - width = portInstance.getWidth() - if (i < width) { - result.add(portInstance.getInstance(i)) - } - } else { - // Not multiport, bank, cross is irrelevant. - result.add(portInstance) - } - } - } - } - } else { - // Reactor is not a bank. - val portInstance = reactor.lookupPortInstance(portRef.variable as Port) - if (portInstance instanceof MultiportInstance) { - // Multiport, not bank. - result.addAll(portInstance.instances) - } else { - // Not bank, not multiport. - result.add(portInstance) - } - } - } - } - return result; - } - - /** - * Return true if the specified variable reference is a source of data. - * It is a source of data if it is an output port of a contained reactor - * or an input port of the current reactor. - */ - def isSource(VarRef portReference) { - (portReference.variable instanceof Output && portReference.container !== null) - || (portReference.variable instanceof Input && portReference.container === null) - } - - /** - * Connect the given left port instance to the given right port instance. - * These are both assumed to be single ports, not multiports. - * @param connection The connection statement creating this connection. - * @param srcInstance The source instance (the left port). - * @param dstInstance The destination instance (the right port). - */ - def connectPortInstances(Connection connection, PortInstance srcInstance, PortInstance dstInstance) { - srcInstance.addDependentPort(dstInstance) - if (dstInstance.dependsOnPort !== null && dstInstance.dependsOnPort !== srcInstance) { - reporter.reportError( - connection, - "dstInstance port " + dstInstance.getFullName + " is already connected to " + - dstInstance.dependsOnPort.getFullName - ) - return - } - dstInstance.dependsOnPort = srcInstance - this.destinations.compute(srcInstance, [key, set| CollectionUtil.plus(set, dstInstance)]) - this.connectionTable.compute(srcInstance, [key, map| CollectionUtil.plus(map, dstInstance, connection)]) - - // The diagram package needs to know, for each single port - // or multiport (not the ports within the multiport), which - // other single ports or multiports they are connected to. - // Record that here. - - var src = srcInstance.multiport ?: srcInstance; - var dst = dstInstance.multiport ?: dstInstance; - - // The following is support for the diagram visualization. - // The source may be at a bank index greater than 0. - // For visualization, this needs to be converted to the source - // at bank 0, because only that one is rendered. - // We want the rendering to represent all connections. - if (src.isOutput && src.parent.bankIndex > 0) { - // Replace the source with the corresponding port instance - // at bank index 0. - val newParent = src.parent.bankMaster.bankMembers.get(0) - val name = src.name - src = newParent.outputs.findFirst [ it.name.equals(name) ] - } - // The destination may be at a bank index greater than 0. - // For visualization, this needs to be converted to the destination - // at bank 0, because only that one is rendered. - // We want the rendering to represent all connections. - if (dst.isInput && dst.parent.bankIndex > 0) { - // Replace the destination with the corresponding port instance - // at bank index 0. - val newParent = dst.parent.bankMaster.bankMembers.get(0) - val name = dst.name - dst = newParent.inputs.findFirst [ it.name.equals(name) ] - } - - val src2 = src - val dst2 = dst - this.connections.compute(connection, [_, links| { - CollectionUtil.compute(links, src2, [_2, destinations| CollectionUtil.plus(destinations, dst2)]) - }]) - } - - /** - * Return the Connection that created the link between the specified source - * and destination, or null if there is no such link. - */ - def Connection getConnection(PortInstance source, PortInstance destination) { - var table = connectionTable.get(source) - if (table !== null) { - return table.get(destination) - } - return null - } - - /** - * Returns the time value of the parameter, possibly looking up a parameter - * value in this instance. Result may be null if param is - * null or code is invalid. - */ - def TimeValue resolveTimeValue(Value v) { - if (v instanceof ParamRef) { - return this.lookupParameterInstance(v.parameter).init.asSingleValue?.getTimeValue - } else { - return v?.timeValue - } - } - - /** - * Override the base class to return the uniqueID of the bank rather - * than this member of the bank, if this is a member of a bank of reactors. - * - * @return An identifier for this instance that is guaranteed to be - * unique within the top-level parent. - */ - override uniqueID() { - if (this.bank !== null) { - return this.bank.uniqueID - } - return super.uniqueID - } - - - // //////////////////////////////////////////////////// - // // Public methods. - - /** - * Override the base class to append [index] if this reactpr - * is in a bank of reactors. - * - * @return The full name of this instance. - */ - override String getName() { - var result = this.definition.name - if (this.bankIndex >= 0) { - result += "[" + this.bankIndex + "]" - } - if (result === null) return ""; - result - } - - /** - * 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). - * - * @return The instance of the child reactor or null if there is no - * such "new" statement. - */ - def getChildReactorInstance(Instantiation definition) { - for (child : this.children) { - if (child.definition === definition) { - return child - } - } - null - } - - /** - * Return the trigger instances (input ports, timers, and actions - * that trigger reactions) belonging to this reactor instance. - * - * @return The trigger instances belonging to this reactor instance. - */ - def getTriggers() { - var triggers = new LinkedHashSet> - for (reaction : this.reactions) { - triggers.addAll(reaction.triggers) - } - 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. - */ - def getTriggersAndReads() { - var triggers = new LinkedHashSet> - for (reaction : this.reactions) { - triggers.addAll(reaction.triggers) - triggers.addAll(reaction.reads) - } - return triggers - } - - /** - * Given a parameter definition for this reactor, return the initial 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. - * - * The returned list of Value objects is such that each element is an - * instance of Time, String, or Code, never Parameter. - * For most uses, this list has only one element, but parameter - * values can be lists of elements, so the returned value is a list. - * - * @param parameter The parameter definition (a syntactic object in the AST). - * - * @return A list of Value objects, or null if the parameter is not found. - * Return an empty list if no initial value is given. - * Each value is an instance of Literal if a literal value is given, - * a Time if a time value was given, or a Code, if a code value was - * given (text in the target language delimited by {= ... =} - */ - def List initialParameterValue(Parameter parameter) { - return ASTUtils.initialValue(parameter, instantiations()); - } - - /** - * 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. - */ - def Integer initialIntParameterValue(Parameter parameter) { - return ASTUtils.initialValueInt(parameter, instantiations()); - } - - /** - * Return a list of Instantiation objects such that the first object - * is the AST instantiation that created this reactor instance, the - * second is the AST instantiation that created the containing - * reactor instance, and so on until there are no more containing - * reactor instances. This will return an empty list if this - * reactor instance is at the top level (is main). - */ - def List instantiations() { - if (_instantiations === null) { - _instantiations = new ArrayList(); - if (definition !== null) { - _instantiations.add(definition); - if (parent !== null) { - _instantiations.addAll(parent.instantiations()); - } - } - } - return _instantiations; - } - - /** - * {@inheritDoc} - */ - override ReactorInstance root() { - if (parent !== null) { - return parent.root() - } else { - return this - } - } - - /** - * Returns whether this is a main or federated reactor. - * @return true if reactor definition is marked as main or federated, false otherwise. - */ - def boolean isMainOrFederated() { - val defn = this.reactorDefinition - return defn !== null && (defn.isMain || defn.isFederated) - } - - /** Return a descriptive string. */ - override toString() { - "ReactorInstance " + getFullName - } - - /** - * Return the set of all ports that receive data from the - * specified source. This includes inputs and outputs at the same level - * of hierarchy and input ports deeper in the hierarchy. - * It does not include inputs or outputs up the hierarchy (i.e., ones - * that are reached via any output port that it does return). - * If the argument is an input port, then it is included in the result. - * No port will appear more than once in the result. - * - * @param source An output or input port. - */ - def transitiveClosure(PortInstance source) { - var result = new LinkedHashSet(); - transitiveClosure(source, result); - result - } - - - /** - * Returns the startup trigger or create a new one if none exists. - */ - def getOrCreateStartup(TriggerRef trigger) { - if (startupTrigger === null) { - startupTrigger = new TriggerInstance(TriggerInstance.BuiltinTrigger.STARTUP, trigger, this) - } - return startupTrigger - } - - /** - * Returns the shutdown trigger or create a new one if none exists. - */ - def getOrCreateShutdown(TriggerRef trigger) { - if (shutdownTrigger === null) { - shutdownTrigger = new TriggerInstance(TriggerInstance.BuiltinTrigger.SHUTDOWN, trigger, this) - } - return shutdownTrigger - } - - /////////////////////////////////////////////////// - //// Methods for finding instances in this reactor given an AST node. - - /** 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 - * action does not belong to this reactor. - */ - def ActionInstance lookupActionInstance(Action action) { - for (actionInstance : actions) { - if (actionInstance.name.equals(action.name)) { - return actionInstance - } - } - } - - /** - * Given a parameter definition, return the parameter instance - * corresponding to that definition, or null if there is - * no such instance. - * @param parameter The parameter definition (a syntactic object in the AST). - * @return A parameter instance, or null if there is none. - */ - def ParameterInstance lookupParameterInstance(Parameter parameter) { - return this.parameters.findFirst [ - it.definition === parameter - ] - } - - /** - * Given a port definition, return the port instance - * corresponding to that definition, or null if there is - * no such instance. - * @param port The port definition (a syntactic object in the AST). - * @return A port instance, or null if there is none. - */ - def PortInstance lookupPortInstance(Port port) { - // Search one of the inputs and outputs sets. - var List ports = null - if (port instanceof Input) { - ports = this.inputs - } else if (port instanceof Output) { - ports = this.outputs - } - for (portInstance : ports) { - if (portInstance.definition === port) { - return portInstance - } - } - null - } - - /** Given a reference to a port belonging to this reactor - * instance, return the port instance. - * Return null if there is no such instance. - * @param reference The port reference. - * @return A port instance, or null if there is none. - */ - def lookupPortInstance(VarRef reference) { - if (!(reference.variable instanceof Port)) { - // Trying to resolve something that is not a port - return null - } - if (reference.container === null) { - // Handle local reference - return lookupPortInstance(reference.variable as Port) - } else { - // Handle hierarchical reference - var containerInstance = this. - getChildReactorInstance(reference.container) - if (containerInstance === null) return null - return containerInstance.lookupPortInstance(reference.variable as Port) - } - } - - /** 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 - * reaction does not belong to this reactor. - */ - def lookupReactionInstance(Reaction reaction) { - for (reactionInstance : reactions) { - if (reactionInstance.definition === reaction) { - return reactionInstance - } - } - } - - /** - * Return the reactor instance within this reactor - * that has the specified instantiation. Note that this - * may be a bank of reactors. - */ - def lookupReactorInstance(Instantiation instantiation) { - for (reactorInstance : children) { - if (reactorInstance.definition === instantiation) { - return reactorInstance - } - } - } - - /** 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 - * timer does not belong to this reactor. - */ - def lookupTimerInstance(Timer timer) { - for (timerInstance : timers) { - if (timerInstance.name.equals(timer.name)) { - return timerInstance - } - } - } - - - /////////////////////////////////////////////////// - //// Methods for getting widths of ports and banks - - /** - * For the specified width specification, return the width. - * This may be for a bank of reactors within this reactor instance or - * for a port of this reactor instance. If the argument is null, there - * is no width specification, so return 1. Otherwise, evaluate the - * width value by determining the value of any referenced parameters. - * - * @param widthSpec The width specification. - * - * @return The width, or -1 if it cannot be determined. - */ - def width(WidthSpec widthSpec) { - if (widthSpec.eContainer instanceof Instantiation && parent !== null) { - // We need the instantiations list of the containing reactor, - // not this one. - return ASTUtils.width(widthSpec, parent.instantiations()); - } - return ASTUtils.width(widthSpec, instantiations()); - } - - /** - * Returns true if this is a bank of reactors. - * @return true if a reactor is a bank, false otherwise - */ - def isBank() { - // FIXME magic number - return bankIndex == -2 - } - - /** - * If this reactor is in a bank of reactors, return the reactor instance - * representing the bank. Otherwise, return null. - */ - def getBankMaster() { - return bank; - } - - /** - * Return the size of this bank. - * @return actual bank size or -1 if this is not a bank master. - */ - def int getBankSize() { - if (bankMembers !== null) { - return bankMembers.size - } - return -1 - } - - /** - * Return the index of this reactor within a bank, or -1 if it - * it is not within a bank, or -2 if it is itself the placeholder - * for a bank. - */ - def getBankIndex() { - bankIndex - } - - /** - * Return the members of this bank, or null if there are none. - * @return actual bank size or -1 if this is not a bank master. - */ - def getBankMembers() { - bankMembers - } - - /** - * Return a parameter matching the specified name if the reactor has one - * and otherwise return null. - * @param name The parameter name. - */ - def ParameterInstance getParameter(String name) { - for (parameter: parameters) { - if (parameter.name.equals(name)) { - return parameter; - } - } - return null; - } - - ////////////////////////////////////////////////////// - //// Protected fields. - - // //////////////////////////////////////////////////// - // // Protected methods - - /** Create all the reaction instances of this reactor instance - * and record the dependencies and antidependencies - * between ports, actions, and timers and reactions. - * This also records the dependencies between reactions - * that follows from the order in which they are defined. - */ - protected def createReactionInstances() { - var reactions = this.reactorDefinition.allReactions - if (this.reactorDefinition.reactions !== null) { - - var count = 0 - - // Check for startup and shutdown triggers. - for (Reaction reaction : reactions) { - // 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); - } - } - } - - /** Add to the specified destinations set all ports that receive data from the - * specified source. This includes inputs and outputs at the same level - * of hierarchy and input ports deeper in the hierarchy. - * It does not include inputs or outputs up the hierarchy (i.e., ones - * that are reached via any output port that it does return). - * @param source A port belonging to this reaction instance or one - * of its children. - * @param destinations The set of destinations to populate. - */ - protected def void transitiveClosure( - PortInstance source, - LinkedHashSet destinations - ) { - // Check that the specified port belongs to this reactor or one of its children. - // The following assumes that the main reactor has no ports, or else - // a NPE will occur. - if (source.parent !== this && source.parent.parent !== this) { - throw new InvalidSourceException( - "Internal error: port " + source + " does not belong to " + - this + " nor any of its children." - ) - } - // If the port is a multiport, then iterate over its contained ordinary ports instead. - if (source instanceof MultiportInstance) { - for (port : source.instances) { - transitiveClosure(port, destinations) - } - } else { - // The port is not a multiport. - // If the port is an input port, then include it in the result. - if (source.isInput) { - destinations.add(source) - } - var localDestinations = this.destinations.get(source) - - if (localDestinations !== null) { - for (destination : localDestinations) { - if (destination instanceof MultiportInstance) { - destinations.addAll(destination.instances) - } else { - destinations.add(destination) - if (destination.isInput) { - // Destination may have further destinations lower in the hierarchy. - destination.parent.transitiveClosure(destination, destinations) - } else if (destination.parent.parent !== null) { - // Destination may have further destinations higher in the hierarchy. - destination.parent.parent.transitiveClosure(destination, destinations) - } - } - } - } - } - } - - /** Collect all reactions that have not been assigned a level and - * return the list. - * @param reactor The reactor for which to check reactions. - * @param result The list to add reactions to. - * @return The list of reactions without levels. - */ - protected def List reactionsWithoutLevels( - ReactorInstance reactor, - List result - ) { - for (reaction : reactor.reactions) { - if (reaction.level < 0) { - result.add(reaction) - } - } - for (child : reactor.children) { - reactionsWithoutLevels(child, result) - } - result - } - - //////////////////////////////////////// - //// Private constructors - - /** - * Create reactor instance resulting from the specified top-level instantiation. - * @param instance The Instance statement in the AST. - * @param parent The parent, or null for the main rector. - * @param reporter The error reporter. - * @param desiredDepth The depth to which to expand the hierarchy. - * @param unorderedReactions A list of reactions that should be treated as unordered. - */ - private new( - Instantiation definition, - ReactorInstance parent, - ErrorReporter reporter, - int desiredDepth, - Set unorderedReactions - ) { - // If the reactor is being instantiated with new[width], then pass -2 - // to the constructor, otherwise pass -1. - this( - definition, - parent, - reporter, - (definition.widthSpec !== null)? -2 : -1, - 0, - desiredDepth, - unorderedReactions - ) - } - - /** - * Create a runtime instance from the specified definition - * and with the specified parent that instantiated it. - * @param instance The Instance statement in the AST. - * @param parent The parent, or null for the main rector. - * @param generator The generator (for error reporting). - * @param reactorIndex -1 for an ordinary reactor, -2 for a - * placeholder for a bank of reactors, or the index of the - * reactor in a bank of reactors otherwise. - * @param depth The depth of this reactor in the hierarchy. - * @param desiredDepth The depth to which to expand the hierarchy. - * @param unorderedReactions A list of reactions that should be treated as unordered. - * It can be passed as null. - */ - private new( - Instantiation definition, - ReactorInstance parent, - ErrorReporter reporter, - int reactorIndex, - int depth, - int desiredDepth, - Set unorderedReactions) { - super(definition, parent) - this.reporter = reporter - this.bankIndex = reactorIndex - this.reactorDefinition = definition.reactorClass.toDefinition - this.depth = depth - if (unorderedReactions !== null) { - this.unorderedReactions = unorderedReactions; - } - - // check for recursive instantiation - var currentParent = parent - var foundSelfAsParent = false - do { - if (currentParent !== null) { - if (currentParent.reactorDefinition === this.reactorDefinition) { - foundSelfAsParent = true - currentParent = null // break - } else { - currentParent = currentParent.parent - } - } - } while(currentParent !== null) - this.recursive = foundSelfAsParent - if (recursive) { - reporter.reportError(definition, "Recursive reactor instantiation.") - } - - // If this reactor is actually a bank of reactors, then instantiate - // each individual reactor in the bank and skip the rest of the - // initialization for this reactor instance. - if (reactorIndex === -2) { - // If the bank width is variable, then we have to wait until the first connection - // before instantiating the children. - var width = width(definition.widthSpec) - if (width > 0) { - this.bankMembers = new ArrayList(width) - for (var index = 0; index < width; index++) { - var childInstance = new ReactorInstance( - definition, parent, reporter, index, depth, desiredDepth, this.unorderedReactions - ) - this.bankMembers.add(childInstance) - childInstance.bank = this - childInstance.bankIndex = index - } - } else { - reporter.reportWarning(definition, "Cannot infer width.") - } - return - } - - // 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 - } - - // Apply overrides and instantiate parameters for this reactor instance. - for (parameter : reactorDefinition.allParameters) { - this.parameters.add(new ParameterInstance(parameter, this)) - } - - // Instantiate inputs for this reactor instance - for (inputDecl : reactorDefinition.allInputs) { - if (inputDecl.widthSpec === null) { - this.inputs.add(new PortInstance(inputDecl, this)) - } else { - this.inputs.add(new MultiportInstance(inputDecl, this, reporter)) - } - } - - // Instantiate outputs for this reactor instance - for (outputDecl : reactorDefinition.allOutputs) { - if (outputDecl.widthSpec === null) { - this.outputs.add(new PortInstance(outputDecl, this)) - } else { - this.outputs.add(new MultiportInstance(outputDecl, this, reporter)) - } - } - - // Do not process content (except interface above) if recursive - if (!recursive && (desiredDepth < 0 || this.depth < desiredDepth)) { - // Instantiate children for this reactor instance - for (child : reactorDefinition.allInstantiations) { - var childInstance = new ReactorInstance( - child, - this, - reporter, - (child.widthSpec !== null)? -2 : -1, - depth + 1, - desiredDepth, - this.unorderedReactions - ) - this.children.add(childInstance) - // If the child is a bank of instances, add all the bank instances. - // These must be added after the bank itself. - if (childInstance.bankMembers !== null) { - this.children.addAll(childInstance.bankMembers) - } - } - - // Instantiate timers for this reactor instance - for (timerDecl : reactorDefinition.allTimers) { - this.timers.add(new TimerInstance(timerDecl, this)) - } - - // Instantiate actions for this reactor instance - for (actionDecl : reactorDefinition.allActions) { - this.actions.add(new ActionInstance(actionDecl, this)) - } - - establishPortConnections() - - // Create the reaction instances in this reactor instance. - // This also establishes all the implied dependencies. - // Note that this can only happen _after_ the children, - // port, action, and timer instances have been created. - createReactionInstances() - } - } - -} diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend index acf3a6b036..aa3418daaf 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend @@ -86,9 +86,6 @@ import org.lflang.lf.Delay import org.lflang.lf.Reaction import org.lflang.lf.Reactor import org.lflang.lf.ReactorDecl -import org.lflang.lf.StateVar -import org.lflang.lf.Timer -import org.lflang.lf.TimeUnit import org.lflang.lf.TriggerRef import org.lflang.lf.TypedVariable import org.lflang.lf.VarRef @@ -3530,9 +3527,9 @@ class CGenerator extends GeneratorBase { var minDelay = action.minDelay var minSpacing = action.minSpacing pr(initializeTriggerObjects, ''' - «triggerStructName».offset = «timeInTargetLanguage(minDelay)»; + «triggerStructName».offset = «minDelay.targetTimeExpr»; «IF minSpacing !== null» - «triggerStructName».period = «timeInTargetLanguage(minSpacing)»; + «triggerStructName».period = «minSpacing.targetTimeExpr»; «ELSE» «triggerStructName».period = «CGenerator.UNDEFINED_MIN_SPACING»; «ENDIF» @@ -4050,7 +4047,7 @@ class CGenerator extends GeneratorBase { parameter coordination-options with a value like {advance-message-interval: 10 msec}"''') } pr(initializeTriggerObjects, ''' - _fed.min_delay_from_physical_action_to_federate_output = «minDelay.timeInTargetLanguage»; + _fed.min_delay_from_physical_action_to_federate_output = «minDelay.targetTimeExpr»; ''') } } @@ -4482,7 +4479,7 @@ class CGenerator extends GeneratorBase { var deadline = reaction.declaredDeadline.maxDelay val reactionStructName = '''«selfStructName(reaction.parent)»->_lf__reaction_«reaction.index»''' pr(initializeTriggerObjects, ''' - «reactionStructName».deadline = «timeInTargetLanguage(deadline)»; + «reactionStructName».deadline = «deadline.targetTimeExpr»; ''') } } @@ -4580,8 +4577,8 @@ class CGenerator extends GeneratorBase { return result.toString } - override String getTargetTimeExpr(long magnitude, TimeUnit unit) { - return CTypes.INSTANCE.getTargetTimeExpr(magnitude, unit) + override String getTargetTimeExpr(TimeValue value) { + return CTypes.INSTANCE.getTargetTimeExpr(value) } override String getTargetInitializerWithNotExactlyOneValue(Initializer init, InferredType type) { diff --git a/org.lflang/src/org/lflang/generator/c/CTypes.java b/org.lflang/src/org/lflang/generator/c/CTypes.java index 0b0363fe77..67e4fb7036 100644 --- a/org.lflang/src/org/lflang/generator/c/CTypes.java +++ b/org.lflang/src/org/lflang/generator/c/CTypes.java @@ -24,13 +24,15 @@ package org.lflang.generator.c; +import java.util.Locale; import java.util.stream.Collectors; import org.lflang.InferredType; +import org.lflang.TimeUnit; +import org.lflang.TimeValue; import org.lflang.generator.TargetTypes; import org.lflang.generator.UnsupportedGeneratorFeatureException; import org.lflang.lf.Initializer; -import org.lflang.lf.TimeUnit; /** * {@link TargetTypes} impl for {@link CGenerator}. @@ -73,12 +75,15 @@ public String getTargetUndefinedType() { } @Override - public String getTargetTimeExpr(long magnitude, TimeUnit unit) { - if (unit != TimeUnit.NONE) { - return unit.name() + '(' + magnitude + ')'; - } else { - return Long.toString(magnitude); + public String getTargetTimeExpr(TimeValue timeValue) { + if (timeValue.equals(TimeValue.ZERO)) { + return "0"; } + return cMacroName(timeValue.getUnit()) + '(' + timeValue.getMagnitude() + ')'; + } + + private static String cMacroName(TimeUnit unit) { + return unit.getCanonicalName().toUpperCase(Locale.ROOT); } @Override diff --git a/org.lflang/src/org/lflang/validation/LFValidator.xtend b/org.lflang/src/org/lflang/validation/LFValidator.xtend index dcfe1ec625..ef623ed80b 100644 --- a/org.lflang/src/org/lflang/validation/LFValidator.xtend +++ b/org.lflang/src/org/lflang/validation/LFValidator.xtend @@ -44,6 +44,7 @@ import org.lflang.InferredType import org.lflang.ModelInfo import org.lflang.Target import org.lflang.TargetProperty +import org.lflang.TimeUnit import org.lflang.TimeValue import org.lflang.lf.Action import org.lflang.lf.ActionOrigin @@ -80,7 +81,6 @@ import org.lflang.lf.Serializer import org.lflang.lf.STP import org.lflang.lf.StateVar import org.lflang.lf.TargetDecl -import org.lflang.lf.TimeUnit import org.lflang.lf.Time import org.lflang.lf.Timer import org.lflang.lf.TupleExpr @@ -1327,7 +1327,7 @@ class LFValidator extends BaseLFValidator { if (value.literal.isZero) return; if (value.literal.isInteger) { - error("Missing time units. Should be one of " + TimeUnit.VALUES.filter[it != TimeUnit.NONE], feature) + error("Missing time unit.", feature) } } else if (value instanceof CodeExpr) { if (value.code.isZero) return; From f02fa399f964f4d99ff11257456fd8758d24467b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 29 Jan 2022 14:24:09 +0100 Subject: [PATCH 54/68] Fix conflicts... --- .../org/lflang/generator/GeneratorBase.xtend | 21 +++---- .../org/lflang/generator/c/CGenerator.xtend | 51 +++------------ .../src/org/lflang/generator/c/CTypes.java | 19 ++++-- .../generator/python/PythonGenerator.xtend | 63 +++---------------- .../lflang/generator/python/PythonTypes.java | 32 +++++++--- 5 files changed, 61 insertions(+), 125 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/GeneratorBase.xtend b/org.lflang/src/org/lflang/generator/GeneratorBase.xtend index b6f3d9dee1..b0601e79e2 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorBase.xtend +++ b/org.lflang/src/org/lflang/generator/GeneratorBase.xtend @@ -1213,7 +1213,11 @@ abstract class GeneratorBase extends AbstractLFValidator { * @return A time string in the target language */ protected def getTargetTime(Time t) { - return t.targetValue + return targetTypes.getTargetTimeExpr(t) + } + + protected def getTargetInitializer(Initializer init) { + return targetTypes.getTargetInitializer(init) } /** @@ -1232,20 +1236,11 @@ abstract class GeneratorBase extends AbstractLFValidator { * @return A time string in the target language */ protected def getTargetTime(Value v) { - if (v.time !== null) { - return v.time.targetTime + if (v instanceof Time) { + return v.targetTime } else if (v.isZero) { - val value = TimeValue.ZERO - return value.timeInTargetLanguage + return TimeValue.ZERO.targetTime } return v.toText } - - protected def getTargetTime(Delay d) { - if (d.parameter !== null) { - return d.toText - } else { - return d.time.targetTime - } - } } diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend index cd66763b97..d26a496dc2 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend @@ -75,9 +75,7 @@ import org.lflang.generator.TimerInstance import org.lflang.generator.TriggerInstance import org.lflang.lf.Action import org.lflang.lf.ActionOrigin -import org.lflang.lf.Assignment import org.lflang.lf.Delay -import org.lflang.lf.Code import org.lflang.lf.Input import org.lflang.lf.Initializer import org.lflang.lf.Instantiation @@ -85,10 +83,10 @@ import org.lflang.lf.Model import org.lflang.lf.Output import org.lflang.lf.Port import org.lflang.lf.ParamRef -import org.lflang.lf.Delay import org.lflang.lf.Reaction import org.lflang.lf.Reactor import org.lflang.lf.ReactorDecl +import org.lflang.lf.StateVar import org.lflang.lf.TriggerRef import org.lflang.lf.VarRef import org.lflang.lf.Variable @@ -328,14 +326,9 @@ import static extension org.lflang.JavaAstUtils.* class CGenerator extends GeneratorBase { - protected new(FileConfig fileConfig, ErrorReporter errorReporter, boolean CCppMode, CTypes types) { + protected new(FileConfig fileConfig, ErrorReporter errorReporter, boolean CCppMode) { super(fileConfig, errorReporter) this.CCppMode = CCppMode; - this.types = types - } - - new(FileConfig fileConfig, ErrorReporter errorReporter, boolean CCppMode) { - this(fileConfig, errorReporter, CCppMode, new CTypes(errorReporter)) } new(FileConfig fileConfig, ErrorReporter errorReporter) { @@ -1619,7 +1612,7 @@ class CGenerator extends GeneratorBase { ''') } else { var delayTime = delay.getTargetTime - pr(rtiCode, ''' + rtiCode.pr(''' if («delayTime» < candidate_tmp) { candidate_tmp = «delayTime»; } @@ -3947,22 +3940,14 @@ class CGenerator extends GeneratorBase { return result.toString } - override String getTargetTimeExpr(TimeValue value) { + protected def String getTargetTimeExpr(TimeValue value) { return CTypes.INSTANCE.getTargetTimeExpr(value) } - override String getTargetInitializerWithNotExactlyOneValue(Initializer init, InferredType type) { - return CTypes.INSTANCE.getTargetInitializerWithNotExactlyOneValue(init, type) - } - - override String getMissingExpr(InferredType type) { - return "0" - } - protected def getInitializer(Initializer init, InferredType t, ReactorInstance parent) { val customExprMaker = new CTypes() { override String getTargetParamRef(ParamRef expr, InferredType t) { - return parent.selfStructName + "->" + expr.parameter.name + return CUtil.reactorRef(parent) + "->" + expr.parameter.name } } return customExprMaker.getTargetInitializer(init, t) @@ -5564,23 +5549,7 @@ class CGenerator extends GeneratorBase { * references are replaced with accesses to the self struct of the parent. */ protected def String getInitializer(StateVar state, ReactorInstance parent) { - var list = new LinkedList(); - - for (i : state?.init) { - if (i.parameter !== null) { - list.add(CUtil.reactorRef(parent) + "->" + i.parameter.name) - } else if (state.isOfTimeType) { - list.add(i.targetTime) - } else { - list.add(i.targetTime) - } - } - - if (list.size == 1) { - return list.get(0) - } else { - return list.join('{', ', ', '}', [it]) - } + return getInitializer(state.init, state.inferredType, parent) } /** @@ -5592,11 +5561,7 @@ class CGenerator extends GeneratorBase { protected def String getInitializer(ParameterInstance p) { return getInitializer(p.init, p.type, p.parent) } - - override supportsGenerics() { - return false - } - + override generateDelayGeneric() { throw new UnsupportedOperationException("TODO: auto-generated method stub") } @@ -6478,7 +6443,7 @@ class CGenerator extends GeneratorBase { // Indicate whether the generator is in Cpp mode or not var boolean CCppMode = false; - var CTypes types; + val CTypes types = CTypes.INSTANCE; // todo remove /** The current federate for which we are generating code. */ var currentFederate = null as FederateInstance; diff --git a/org.lflang/src/org/lflang/generator/c/CTypes.java b/org.lflang/src/org/lflang/generator/c/CTypes.java index 0b35a2d980..446a8796e7 100644 --- a/org.lflang/src/org/lflang/generator/c/CTypes.java +++ b/org.lflang/src/org/lflang/generator/c/CTypes.java @@ -25,6 +25,8 @@ package org.lflang.generator.c; import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; import org.lflang.InferredType; @@ -41,8 +43,17 @@ */ public class CTypes implements TargetTypes { + // Regular expression pattern for array types. + // For example, for "foo[10]", the first match will be "foo" and the second "[10]". + // For "foo[]", the first match will be "foo" and the second "". + private static final Pattern arrayPattern = Pattern.compile("^\\s*(?:/\\*.*?\\*/)?\\s*(\\w+)\\s*\\[([0-9]*)]\\s*$"); + + public static final CTypes INSTANCE = new CTypes(); + protected CTypes() { + } + @Override public boolean supportsGenerics() { return false; @@ -71,6 +82,7 @@ public String getTargetVariableSizeListType(String baseType) { @Override public String getTargetUndefinedType() { // todo C used to insert a marker in the code + // return String.format("/* %s */", errorReporter.reportError("undefined type")); throw new UnsupportedGeneratorFeatureException("Undefined type"); } @@ -98,18 +110,13 @@ public String getTargetInitializerWithNotExactlyOneValue(Initializer init, Infer .collect(Collectors.joining(", ", "{", "}")); } - @Override - public String getTargetUndefinedType() { - return String.format("/* %s */", errorReporter.reportError("undefined type")); - } - /** * Given a type, return a C representation of the type. Note that * C types are very idiosyncratic. For example, {@code int[]} is not always accepted * as a type, and {@code int*} must be used instead, except when initializing * a variable using a static initializer, as in {@code int[] foo = {1, 2, 3};}. * When initializing a variable using a static initializer, use - * {@link #getVariableDeclaration(InferredType, String)} instead. + * {@link #getVariableDeclaration(InferredType, String, boolean)} instead. * @param type The type. */ @Override diff --git a/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend b/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend index 1ffeb0f378..d97c09b8c3 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend @@ -32,9 +32,6 @@ import java.util.ArrayList import java.util.HashMap import java.util.HashSet import java.util.LinkedHashSet -import java.util.LinkedList -import java.util.List -import java.util.regex.Pattern import org.eclipse.emf.ecore.resource.Resource import org.eclipse.xtext.generator.IFileSystemAccess2 import org.eclipse.xtext.util.CancelIndicator @@ -66,12 +63,9 @@ import org.lflang.generator.SubContext import org.lflang.generator.c.CGenerator import org.lflang.generator.c.CUtil import org.lflang.lf.Action -import org.lflang.lf.Assignment -import org.lflang.lf.Delay import org.lflang.lf.Input import org.lflang.lf.Initializer import org.lflang.lf.Instantiation -import org.lflang.lf.Literal import org.lflang.lf.Model import org.lflang.lf.Output import org.lflang.lf.Port @@ -107,17 +101,13 @@ class PythonGenerator extends CGenerator { var PythonTypes types; - new(FileConfig fileConfig, ErrorReporter errorReporter) { - this(fileConfig, errorReporter, new PythonTypes(errorReporter)) - } - - private new(FileConfig fileConfig, ErrorReporter errorReporter, PythonTypes types) { + private new(FileConfig fileConfig, ErrorReporter errorReporter) { super(fileConfig, errorReporter, false, types) // set defaults targetConfig.compiler = "gcc" targetConfig.compilerFlags = newArrayList // -Wall -Wconversion" targetConfig.linkerFlags = "" - this.types = types + this.types = PythonTypes.INSTANCE } /** @@ -174,6 +164,10 @@ class PythonGenerator extends CGenerator { return Target.Python } + override PythonTypes getTargetTypes() { + return PythonTypes.INSTANCE + } + val protoNames = new HashSet() // ////////////////////////////////////////// @@ -238,43 +232,6 @@ class PythonGenerator extends CGenerator { return "self." + expr.parameter.name } - override String getTargetLiteral(Literal expr, InferredType type) { - switch(expr.toText) { - case "false": "False" - case "true": "True" - default: super.getTargetLiteral(expr, type) - } - } - - /** Returns the python initializer corresponding to the given LF init list. */ - def String getPythonInitializer(Initializer init) { - return getTargetInitializer(init, InferredType.undefined()) - } - - override String getTargetInitializerWithNotExactlyOneValue(Initializer init, InferredType type) { - if (init.exprs.isEmpty) return "()" - else return init.exprs.join("(", ", ", ")", [getTargetExpr(it, type.componentType)]) - } - - override String getMissingExpr(InferredType type) { - return "None" - } - - override String getTargetInitializer(Initializer init, InferredType type) { - if (init.isParens) { - // tuple expr, but only if size != 1 or trailing comma - if (init.exprs.isEmpty) { - return "()" - } else if (init.exprs.size == 1 && !init.isTrailingComma) { - return getTargetExpr(init.asSingleValue, type) - } else { - return init.exprs.join("(", ", ", ",)", [getTargetExpr(it, type.componentType)]) - } - } - return super.getTargetInitializer(init, type) - } - - /** * Generate parameters and their respective initialization code for a reaction function * The initialization code is put at the beginning of the reaction before user code @@ -521,11 +478,11 @@ class PythonGenerator extends CGenerator { if (!types.getTargetType(param).equals("PyObject*")) { // If type is given, use it temporary_code. - pr('''self._«param.name»:«types.getPythonType(param.inferredType)» = «param.init.pythonInitializer» + pr('''self._«param.name»:«types.getPythonType(param.inferredType)» = «param.init.targetInitializer» ''') } else { // If type is not given, just pass along the initialization - temporary_code.pr('''self._«param.name» = «param.init.pythonInitializer» + temporary_code.pr('''self._«param.name» = «param.init.targetInitializer» ''') } @@ -544,7 +501,7 @@ class PythonGenerator extends CGenerator { for (stateVar : reactor.allStateVars) { if (stateVar.isInitialized) { // If initialized, pass along the initialization directly if it is present - temporary_code.pr('''self.«stateVar.name» = «stateVar.pythonInitializer» + temporary_code.pr('''self.«stateVar.name» = «stateVar.targetInitializer» ''') } else { // If neither the type nor the initialization is given, use None @@ -651,7 +608,7 @@ class PythonGenerator extends CGenerator { _bank_index = «PyUtil.bankIndex(instance)», «FOR param : instance.parameters» «IF !param.name.equals("bank_index")» - _«param.name»=«param.pythonInitializer», + _«param.name»=«param.targetInitializer», «ENDIF»«ENDFOR» ) ''') diff --git a/org.lflang/src/org/lflang/generator/python/PythonTypes.java b/org.lflang/src/org/lflang/generator/python/PythonTypes.java index c066494047..422044593f 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonTypes.java +++ b/org.lflang/src/org/lflang/generator/python/PythonTypes.java @@ -5,22 +5,18 @@ import org.lflang.ErrorReporter; import org.lflang.InferredType; import org.lflang.generator.c.CTypes; +import org.lflang.lf.Initializer; +import org.lflang.lf.Literal; +import org.lflang.lf.ParamRef; +import org.lflang.lf.Type; public class PythonTypes extends CTypes { // Regular expression pattern for pointer types. The star at the end has to be visible. static final Pattern pointerPatternVariable = Pattern.compile("^\\s*+(\\w+)\\s*\\*\\s*$"); + public static final PythonTypes INSTANCE = new PythonTypes(); - /** - * Initializes a {@code CTargetTypes} with the given - * error reporter. - * - * @param errorReporter The error reporter for any - * errors raised in the code - * generation process. - */ - public PythonTypes(ErrorReporter errorReporter) { - super(errorReporter); + protected PythonTypes() { } @Override @@ -28,6 +24,22 @@ public String getTargetUndefinedType() { return "PyObject*"; } + @Override + public String getTargetLiteral(Literal expr, InferredType type) { + switch (expr.getLiteral()) { + case "False": + case "True": + return expr.getLiteral(); + default: + return super.getTargetLiteral(expr, type); + } + } + + @Override + public String getTargetParamRef(ParamRef expr, InferredType type) { + return "self." + expr.getParameter().getName(); + } + /** * This generator inherits types from the CGenerator. * This function reverts them back to Python types From f073ec66a1cf548ec5addf417d4f23e297fdbf6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 29 Jan 2022 14:40:46 +0100 Subject: [PATCH 55/68] Fix conflicts... --- org.lflang/src/org/lflang/ASTUtils.xtend | 1 - org.lflang/src/org/lflang/JavaAstUtils.java | 11 +++++++++++ .../src/org/lflang/generator/GeneratorBase.xtend | 12 +++--------- .../org/lflang/generator/ParameterInstance.java | 13 ++++++++++++- .../src/org/lflang/generator/TargetTypes.java | 2 +- .../src/org/lflang/generator/c/CGenerator.xtend | 6 +++--- .../lflang/generator/python/PythonGenerator.xtend | 15 +++++++-------- 7 files changed, 37 insertions(+), 23 deletions(-) diff --git a/org.lflang/src/org/lflang/ASTUtils.xtend b/org.lflang/src/org/lflang/ASTUtils.xtend index 9335f2cb4a..329de1719f 100644 --- a/org.lflang/src/org/lflang/ASTUtils.xtend +++ b/org.lflang/src/org/lflang/ASTUtils.xtend @@ -57,7 +57,6 @@ import org.lflang.lf.Delay import org.lflang.lf.Element import org.lflang.lf.ImportedReactor import org.lflang.lf.Input -import org.lflang.lf.Initializer import org.lflang.lf.Instantiation import org.lflang.lf.LfFactory import org.lflang.lf.Literal diff --git a/org.lflang/src/org/lflang/JavaAstUtils.java b/org.lflang/src/org/lflang/JavaAstUtils.java index b21c4f1396..3a1067a3c5 100644 --- a/org.lflang/src/org/lflang/JavaAstUtils.java +++ b/org.lflang/src/org/lflang/JavaAstUtils.java @@ -33,6 +33,7 @@ import org.lflang.lf.BraceExpr; import org.lflang.lf.BracketExpr; import org.lflang.lf.Initializer; +import org.lflang.lf.LfFactory; import org.lflang.lf.Literal; import org.lflang.lf.ParamRef; import org.lflang.lf.Parameter; @@ -43,6 +44,8 @@ import org.lflang.lf.Value; import org.lflang.lf.VarRef; +import kotlin.reflect.jvm.internal.ReflectProperties.Val; + /** * Helper class to manipulate the LF AST. This is partly * converted from {@link ASTUtils}. @@ -333,5 +336,13 @@ public static List initializerAsList(Initializer init) { } } + public static Initializer listAsInitializer(List values) { + // for compatibility with ParameterInstance + Initializer initializer = LfFactory.eINSTANCE.createInitializer(); + initializer.setParens(true); + initializer.getExprs().addAll(values); + return initializer; + } + } diff --git a/org.lflang/src/org/lflang/generator/GeneratorBase.xtend b/org.lflang/src/org/lflang/generator/GeneratorBase.xtend index b0601e79e2..cb92a782f4 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorBase.xtend +++ b/org.lflang/src/org/lflang/generator/GeneratorBase.xtend @@ -62,8 +62,6 @@ import org.lflang.lf.Instantiation import org.lflang.lf.LfFactory import org.lflang.lf.Model import org.lflang.lf.Parameter -import org.lflang.lf.ParamRef -import org.lflang.lf.Port import org.lflang.lf.Reaction import org.lflang.lf.Reactor import org.lflang.lf.Time @@ -1216,15 +1214,11 @@ abstract class GeneratorBase extends AbstractLFValidator { return targetTypes.getTargetTimeExpr(t) } - protected def getTargetInitializer(Initializer init) { - return targetTypes.getTargetInitializer(init) - } - /** * Returns the textual representation of a value in the target language. */ protected def getTargetValue(Value v) { - return getTargetExpr(v, null) + return targetTypes.getTargetExpr(v, null) } /** @@ -1237,9 +1231,9 @@ abstract class GeneratorBase extends AbstractLFValidator { */ protected def getTargetTime(Value v) { if (v instanceof Time) { - return v.targetTime + return targetTypes.getTargetTimeExpr(v) } else if (v.isZero) { - return TimeValue.ZERO.targetTime + return targetTypes.getTargetTimeExpr(TimeValue.ZERO) } return v.toText } diff --git a/org.lflang/src/org/lflang/generator/ParameterInstance.java b/org.lflang/src/org/lflang/generator/ParameterInstance.java index ee2d044e6e..eb49d85cd4 100644 --- a/org.lflang/src/org/lflang/generator/ParameterInstance.java +++ b/org.lflang/src/org/lflang/generator/ParameterInstance.java @@ -33,6 +33,7 @@ import org.lflang.InferredType; import org.lflang.JavaAstUtils; import org.lflang.lf.Assignment; +import org.lflang.lf.Initializer; import org.lflang.lf.Parameter; import org.lflang.lf.Value; @@ -65,7 +66,7 @@ public ParameterInstance(Parameter definition, ReactorInstance parent) { ///////////////////////////////////////////// //// Public Fields - public InferredType type; + public final InferredType type; ///////////////////////////////////////////// //// Public Methods @@ -79,6 +80,16 @@ public ParameterInstance(Parameter definition, ReactorInstance parent) { public List getInitialValue() { return parent.initialParameterValue(this.definition); } + + /** + * Return a view of the initial value as an initializer. + * TODO there might be problems with it, it is used for + * compatibility with the C generator. Most other generators + * do not use the instance tree anyway (C++, Rust, TS) + */ + public Initializer getInit() { + return JavaAstUtils.listAsInitializer(getInitialValue()); + } /** * Return the name of this parameter. diff --git a/org.lflang/src/org/lflang/generator/TargetTypes.java b/org.lflang/src/org/lflang/generator/TargetTypes.java index f603371eac..b5ef117b51 100644 --- a/org.lflang/src/org/lflang/generator/TargetTypes.java +++ b/org.lflang/src/org/lflang/generator/TargetTypes.java @@ -135,7 +135,7 @@ default String getTargetParamRef(ParamRef expr, InferredType type) { */ default String getTargetLiteral(Literal expr, InferredType type) { if (ASTUtils.isZero(expr) && type != null && type.isTime) { - return getTargetTimeExpr(0, TimeUnit.NONE); + return getTargetTimeExpr(TimeValue.ZERO); } return JavaAstUtils.addZeroToLeadingDot(expr.getLiteral()); // unescaped } diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend index d26a496dc2..622996bed0 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend @@ -1437,7 +1437,7 @@ class CGenerator extends GeneratorBase { val reactorInstance = main.getChildReactorInstance(federate.instantiation) for (param : reactorInstance.parameters) { if (param.name.equalsIgnoreCase("STP_offset") && param.type.isTime) { - val stp = param.getInitialValue().asSingleValue?.getLiteralTimeValue + val stp = param.init.asSingleValue?.getLiteralTimeValue if (stp !== null) { code.pr(''' set_stp_offset(«stp.targetTimeExpr»); @@ -1611,7 +1611,7 @@ class CGenerator extends GeneratorBase { candidate_tmp = NEVER; ''') } else { - var delayTime = delay.getTargetTime + var delayTime = delay.value.targetTime rtiCode.pr(''' if («delayTime» < candidate_tmp) { candidate_tmp = «delayTime»; @@ -5559,7 +5559,7 @@ class CGenerator extends GeneratorBase { * accesses to the self struct of the parents of those parameters. */ protected def String getInitializer(ParameterInstance p) { - return getInitializer(p.init, p.type, p.parent) + return targetTypes.getTargetInitializer(p.init, p.type) } override generateDelayGeneric() { diff --git a/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend b/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend index d97c09b8c3..0882c72a02 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend @@ -69,7 +69,6 @@ import org.lflang.lf.Instantiation import org.lflang.lf.Model import org.lflang.lf.Output import org.lflang.lf.Port -import org.lflang.lf.ParamRef import org.lflang.lf.Reaction import org.lflang.lf.Reactor import org.lflang.lf.ReactorDecl @@ -102,7 +101,7 @@ class PythonGenerator extends CGenerator { var PythonTypes types; private new(FileConfig fileConfig, ErrorReporter errorReporter) { - super(fileConfig, errorReporter, false, types) + super(fileConfig, errorReporter, false) // set defaults targetConfig.compiler = "gcc" targetConfig.compilerFlags = newArrayList // -Wall -Wconversion" @@ -168,6 +167,10 @@ class PythonGenerator extends CGenerator { return PythonTypes.INSTANCE } + protected def String getTargetInitializer(Initializer init) { + return targetTypes.getTargetInitializer(init, InferredType.undefined()) + } + val protoNames = new HashSet() // ////////////////////////////////////////// @@ -228,10 +231,6 @@ class PythonGenerator extends CGenerator { //////////////////////////////////////////// //// Protected methods - override String getTargetParamRef(ParamRef expr, InferredType type) { - return "self." + expr.parameter.name - } - /** * Generate parameters and their respective initialization code for a reaction function * The initialization code is put at the beginning of the reaction before user code @@ -501,7 +500,7 @@ class PythonGenerator extends CGenerator { for (stateVar : reactor.allStateVars) { if (stateVar.isInitialized) { // If initialized, pass along the initialization directly if it is present - temporary_code.pr('''self.«stateVar.name» = «stateVar.targetInitializer» + temporary_code.pr('''self.«stateVar.name» = «stateVar.init.targetInitializer» ''') } else { // If neither the type nor the initialization is given, use None @@ -608,7 +607,7 @@ class PythonGenerator extends CGenerator { _bank_index = «PyUtil.bankIndex(instance)», «FOR param : instance.parameters» «IF !param.name.equals("bank_index")» - _«param.name»=«param.targetInitializer», + _«param.name»=«param.initializer», «ENDIF»«ENDFOR» ) ''') From bfb524b8b4267a8a8f477827b744aad90bc5152d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 29 Jan 2022 14:52:35 +0100 Subject: [PATCH 56/68] Fix conflicts... --- .../lflang/generator/DeadlineInstance.java | 4 - .../src/org/lflang/generator/cpp/CppTypes.kt | 26 ++---- .../org/lflang/generator/rust/RustModel.kt | 20 ++--- .../org/lflang/generator/rust/RustTypes.kt | 15 ++-- .../lflang/generator/ts/TSActionGenerator.kt | 4 +- .../generator/ts/TSConstructorGenerator.kt | 4 +- .../org/lflang/generator/ts/TSGenerator.kt | 40 +++------ .../generator/ts/TSInstanceGenerator.kt | 2 +- .../generator/ts/TSParameterGenerator.kt | 2 +- .../ts/TSParameterPreambleGenerator.kt | 2 +- .../lflang/generator/ts/TSPortGenerator.kt | 2 +- .../generator/ts/TSReactionGenerator.kt | 14 +-- .../lflang/generator/ts/TSStateGenerator.kt | 4 +- .../lflang/generator/ts/TSTimerGenerator.kt | 4 +- .../src/org/lflang/generator/ts/TSTypes.kt | 41 ++++++++- .../src/org/lflang/generator/ts/TsTypes.kt | 88 ------------------- 16 files changed, 93 insertions(+), 179 deletions(-) delete mode 100644 org.lflang/src/org/lflang/generator/ts/TsTypes.kt diff --git a/org.lflang/src/org/lflang/generator/DeadlineInstance.java b/org.lflang/src/org/lflang/generator/DeadlineInstance.java index 47ae3c1b61..ca70584d05 100644 --- a/org.lflang/src/org/lflang/generator/DeadlineInstance.java +++ b/org.lflang/src/org/lflang/generator/DeadlineInstance.java @@ -27,12 +27,8 @@ package org.lflang.generator; -import org.lflang.TimeValue -import org.lflang.lf.Deadline -import org.lflang.JavaAstUtils; import org.lflang.TimeValue; import org.lflang.lf.Deadline; -import org.lflang.lf.Parameter; /** * Instance of a deadline. Upon creation the actual delay is converted into diff --git a/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt b/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt index 2d409e484c..129b076edf 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt @@ -25,11 +25,12 @@ package org.lflang.generator.cpp import org.lflang.InferredType +import org.lflang.TimeUnit +import org.lflang.TimeValue import org.lflang.generator.TargetTypes import org.lflang.lf.BraceExpr import org.lflang.lf.Initializer import org.lflang.lf.ParamRef -import org.lflang.lf.TimeUnit /** * Implementation of [TargetTypes] for C++. @@ -52,9 +53,11 @@ object CppTypes : TargetTypes { override fun getTargetUndefinedType() = "void" - override fun getTargetTimeExpr(magnitude: Long, unit: TimeUnit): String = - if (magnitude == 0L) "reactor::Duration::zero()" - else magnitude.toString() + unit.cppUnit + override fun getTargetTimeExpr(value: TimeValue): String = + with (value) { + if (magnitude == 0L) "reactor::Duration::zero()" + else magnitude.toString() + unit.cppUnit + } } @@ -117,25 +120,10 @@ object CppOuterTypes : TargetTypes by CppTypes { /** Get a C++ representation of a LF unit. */ val TimeUnit.cppUnit get() = when (this) { - TimeUnit.NSEC -> "ns" - TimeUnit.NSECS -> "ns" - TimeUnit.USEC -> "us" - TimeUnit.USECS -> "us" - TimeUnit.MSEC -> "ms" - TimeUnit.MSECS -> "ms" - TimeUnit.SEC -> "s" - TimeUnit.SECS -> "s" TimeUnit.SECOND -> "s" - TimeUnit.SECONDS -> "s" - TimeUnit.MIN -> "min" - TimeUnit.MINS -> "min" TimeUnit.MINUTE -> "min" - TimeUnit.MINUTES -> "min" TimeUnit.HOUR -> "h" - TimeUnit.HOURS -> "h" TimeUnit.DAY -> "d" - TimeUnit.DAYS -> "d" TimeUnit.WEEK -> "d*7" - TimeUnit.WEEKS -> "d*7" else -> "" } diff --git a/org.lflang/src/org/lflang/generator/rust/RustModel.kt b/org.lflang/src/org/lflang/generator/rust/RustModel.kt index d86d71d905..970a76c60d 100644 --- a/org.lflang/src/org/lflang/generator/rust/RustModel.kt +++ b/org.lflang/src/org/lflang/generator/rust/RustModel.kt @@ -396,10 +396,7 @@ fun WidthSpec.toRustExpr(): String = terms.joinToString(" + ") { } fun TimeValue.toRustTimeExpr(): TargetCode = RustTypes.getTargetTimeExpr(this) -private fun Time.toRustTimeExpr(): TargetCode = this.toTimeValue().toRustTimeExpr() - -private fun toRustTimeExpr(interval: Long, unit: TimeUnit): TargetCode = - RustTypes.getTargetTimeExpr(interval, unit) +private fun Time.toRustTimeExpr(): TargetCode = JavaAstUtils.toTimeValue(this).toRustTimeExpr() /** Regex to match a target code block, captures the insides as $1. */ private val TARGET_BLOCK_R = Regex("\\{=(.*)=}", RegexOption.DOT_MATCHES_ALL) @@ -592,13 +589,14 @@ object RustModelBuilder { val byName = parameters.associateBy { it.lhs.name } val args = reactor.parameters.associate { ithParam: Parameter -> // use provided argument - val value = byName[ithParam.name]?.let { RustTypes.getTargetInitializer(it.rhs, ithParam.type, it.isInitWithBraces) } - ?: if (ithParam.name == "bank_index" && this.isBank) "bank_index" else null // special value - ?: ithParam.init?.let { RustTypes.getTargetInitializer(it, ithParam.type) } - ?: throw InvalidLfSourceException( - "Cannot find value of parameter ${ithParam.name}", - this - ) + val value: String = + byName[ithParam.name]?.let { RustTypes.getTargetInitializer(it.rhs, ithParam.type) } + ?: if (ithParam.name == "bank_index" && this.isBank) "bank_index" else null // special value + ?: ithParam.init?.let { RustTypes.getTargetInitializer(it, ithParam.type) } + ?: throw InvalidLfSourceException( + "Cannot find value of parameter ${ithParam.name}", + this + ) ithParam.name to value } diff --git a/org.lflang/src/org/lflang/generator/rust/RustTypes.kt b/org.lflang/src/org/lflang/generator/rust/RustTypes.kt index 7c6292be89..01318d38bc 100644 --- a/org.lflang/src/org/lflang/generator/rust/RustTypes.kt +++ b/org.lflang/src/org/lflang/generator/rust/RustTypes.kt @@ -29,10 +29,10 @@ import org.lflang.TimeValue import org.lflang.generator.TargetCode import org.lflang.generator.TargetTypes import org.lflang.inBlock +import org.lflang.lf.CodeExpr +import org.lflang.lf.Initializer import org.lflang.lf.Value import org.lflang.toText -import org.lflang.lf.Initializer -import org.lflang.lf.TimeUnit object RustTypes : TargetTypes { @@ -54,11 +54,12 @@ object RustTypes : TargetTypes { if (ident in RustKeywords) "r#$ident" else ident - override fun getTargetExpr(value: Value, type: InferredType?): String = when { - // wrap in a block to enable writing several statements - value.code != null -> value.code.toText().inBlock() - else -> super.getTargetExpr(value, type) - } + override fun getTargetExpr(value: Value, type: InferredType?): String = + when (value) { + // wrap in a block to enable writing several statements + is CodeExpr -> value.code.toText().inBlock() + else -> super.getTargetExpr(value, type) + } override fun getTargetTimeExpr(timeValue: TimeValue): TargetCode = with(timeValue) { val unit = unit?.canonicalName.orEmpty() diff --git a/org.lflang/src/org/lflang/generator/ts/TSActionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSActionGenerator.kt index e58a32b7bd..91581a0298 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSActionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSActionGenerator.kt @@ -29,7 +29,7 @@ class TSActionGenerator( if (action.name == "networkMessage") { "Buffer" } else { - TsTypes.getTargetType(action.type) + TSTypes.getTargetType(action.type) } fun generateClassProperties(): String { @@ -58,7 +58,7 @@ class TSActionGenerator( if (action.minDelay != null) { // Actions in the TypeScript target are constructed // with an optional minDelay argument which defaults to 0. - actionArgs += ", " + TsTypes.getTargetExpr(action.minDelay, InferredType.time()) + actionArgs += ", " + TSTypes.getTargetExpr(action.minDelay, InferredType.time()) } actionInstantiations.add( "this.${action.name} = new __Action<${getActionType(action)}>($actionArgs);") diff --git a/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt index 0c9a91e366..bbe5a5b98f 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt @@ -25,8 +25,8 @@ class TSConstructorGenerator( ) { private fun initializeParameter(p: Parameter): String { - val type = TsTypes.getTargetType(p.inferredType) - val init = TsTypes.getTargetInitializer(p.init, p.type) + val type = TSTypes.getTargetType(p.inferredType) + val init = TSTypes.getTargetInitializer(p.init, p.type) return "${p.name}: $type = $init" } diff --git a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt index 64a3b3fb4d..164b6a1c69 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt @@ -28,31 +28,12 @@ package org.lflang.generator.ts import org.eclipse.emf.ecore.resource.Resource import org.eclipse.xtext.generator.IFileSystemAccess2 import org.eclipse.xtext.util.CancelIndicator -import org.lflang.ErrorReporter -import org.lflang.inferredType -import org.lflang.InferredType -import org.lflang.TimeValue -import org.lflang.JavaAstUtils -import org.lflang.ASTUtils.isInitialized -import org.eclipse.xtext.generator.IGeneratorContext import org.lflang.* import org.lflang.Target import org.lflang.TargetConfig.Mode -import org.lflang.federated.launcher.FedTSLauncher import org.lflang.federated.FederateInstance -import org.lflang.lf.Action -import org.lflang.lf.Delay -import org.lflang.lf.Instantiation -import org.lflang.lf.Parameter -import org.lflang.lf.StateVar -import org.lflang.lf.Type -import org.lflang.lf.Value -import org.lflang.lf.VarRef -import org.lflang.scoping.LFGlobalScopeProvider -import java.nio.file.Files -import java.util.LinkedList +import org.lflang.federated.launcher.FedTSLauncher import org.lflang.federated.serialization.SupportedSerializers -import org.lflang.generator.canGenerate import org.lflang.generator.CodeMap import org.lflang.generator.GeneratorBase import org.lflang.generator.GeneratorResult @@ -60,13 +41,17 @@ import org.lflang.generator.IntegratedBuilder import org.lflang.generator.JavaGeneratorUtils import org.lflang.generator.LFGeneratorContext import org.lflang.generator.PrependOperator +import org.lflang.generator.SubContext import org.lflang.generator.TargetTypes import org.lflang.generator.ValueGenerator -import org.lflang.generator.SubContext +import org.lflang.generator.canGenerate +import org.lflang.lf.Action +import org.lflang.lf.VarRef +import org.lflang.scoping.LFGlobalScopeProvider +import java.nio.file.Files import java.nio.file.Path import java.nio.file.StandardCopyOption -import kotlin.collections.HashMap -import org.lflang.generator.TargetTypes +import java.util.* private const val NO_NPM_MESSAGE = "The TypeScript target requires npm >= 6.14.4. " + "For installation instructions, see: https://www.npmjs.com/get-npm. \n" + @@ -85,8 +70,9 @@ class TSGenerator( private val tsFileConfig: TSFileConfig, errorReporter: ErrorReporter, private val scopeProvider: LFGlobalScopeProvider -) : GeneratorBase(tsFileConfig, errorReporter), - TargetTypes by TsTypes { +) : GeneratorBase(tsFileConfig, errorReporter) { + + override fun getTargetTypes(): TargetTypes = TSTypes companion object { /** Path to the Cpp lib directory (relative to class path) */ @@ -617,11 +603,11 @@ class TSGenerator( // Virtual methods. override fun generateDelayBody(action: Action, port: VarRef): String { - return "actions.${action.name}.schedule(0, ${JavaAstUtils.generateVarRef(port)} as ${getTargetType(action.type)});" + return "actions.${action.name}.schedule(0, ${JavaAstUtils.generateVarRef(port)} as ${targetTypes.getTargetType(action.type)});" } override fun generateForwardBody(action: Action, port: VarRef): String { - return "${JavaAstUtils.generateVarRef(port)} = ${action.name} as ${getTargetType(action.type)};" + return "${JavaAstUtils.generateVarRef(port)} = ${action.name} as ${targetTypes.getTargetType(action.type)};" } override fun generateDelayGeneric(): String { diff --git a/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt index 08880cdec6..c38eda7b7d 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt @@ -50,7 +50,7 @@ class TSInstanceGenerator( // the reactor instance, and write the corresponding parameter // value as an argument for the TypeScript constructor for (parameter in childReactor.reactorClass.toDefinition().parameters) { - childReactorArguments.add(TsTypes.getTargetInitializer(parameter, childReactor)) + childReactorArguments.add(TSTypes.getTargetInitializer(parameter, childReactor)) } childReactorInstantiations.add( diff --git a/org.lflang/src/org/lflang/generator/ts/TSParameterGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSParameterGenerator.kt index c8dbbc2c96..f5d1a13684 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSParameterGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSParameterGenerator.kt @@ -14,7 +14,7 @@ class TSParameterGenerator( fun generateClassProperties(): String { val paramClassProperties = LinkedList() for (param in parameters) { - paramClassProperties.add("${param.name}: __Parameter<${TsTypes.getTargetType(param.inferredType)}>;") + paramClassProperties.add("${param.name}: __Parameter<${TSTypes.getTargetType(param.inferredType)}>;") } return paramClassProperties.joinToString("\n") } diff --git a/org.lflang/src/org/lflang/generator/ts/TSParameterPreambleGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSParameterPreambleGenerator.kt index 6bdcad33f7..ad1040744f 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSParameterPreambleGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSParameterPreambleGenerator.kt @@ -50,7 +50,7 @@ class TSParameterPreambleGenerator( private val targetConfig: TargetConfig, private val reactors: MutableList ) { - private fun getTargetType(p: Parameter): String = TsTypes.getTargetType(p.inferredType) + private fun getTargetType(p: Parameter): String = TSTypes.getTargetType(p.inferredType) private fun getTimeoutTimeValue(): String { return if (targetConfig.timeout != null) { diff --git a/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt index 2f843ef6f4..d4de160c11 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt @@ -1,6 +1,6 @@ package org.lflang.generator.ts -import org.lflang.generator.ts.TsTypes.getTargetType +import org.lflang.generator.ts.TSTypes.getTargetType import org.lflang.lf.Input import org.lflang.lf.Output import java.util.* diff --git a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt index ac2f6d7a68..f60062dee4 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt @@ -47,7 +47,7 @@ class TSReactionGenerator( reactEpilogue: String, reactSignature: StringJoiner ): String { - val deadlineArgs = TsTypes.getTargetTimeExpr(reaction.deadline.delay.orZero()) + val deadlineArgs = TSTypes.getTargetTimeExpr(reaction.deadline.delay.orZero()) return with(PrependOperator) { """ @@ -194,9 +194,9 @@ class TSReactionGenerator( } else if (trigOrSource.variable is Timer) { reactSignatureElementType = "__Tag" } else if (trigOrSource.variable is Action) { - reactSignatureElementType = TsTypes.getTargetType((trigOrSource.variable as Action).inferredType) + reactSignatureElementType = TSTypes.getTargetType((trigOrSource.variable as Action).inferredType) } else if (trigOrSource.variable is Port) { - reactSignatureElementType = TsTypes.getTargetType((trigOrSource.variable as Port).inferredType) + reactSignatureElementType = TSTypes.getTargetType((trigOrSource.variable as Port).inferredType) } reactSignature.add("${generateArg(trigOrSource)}: Read<$reactSignatureElementType>") @@ -226,10 +226,10 @@ class TSReactionGenerator( if (effect.variable is Timer) { errorReporter.reportError("A timer cannot be an effect of a reaction") } else if (effect.variable is Action) { - reactSignatureElement += ": Sched<" + TsTypes.getTargetType((effect.variable as Action).inferredType) + ">" + reactSignatureElement += ": Sched<" + TSTypes.getTargetType((effect.variable as Action).inferredType) + ">" schedActionSet.add(effect.variable as Action) } else if (effect.variable is Port) { - reactSignatureElement += ": ReadWrite<" + TsTypes.getTargetType((effect.variable as Port).inferredType) + ">" + reactSignatureElement += ": ReadWrite<" + TSTypes.getTargetType((effect.variable as Port).inferredType) + ">" if (effect.container == null) { reactEpilogue.add(with(PrependOperator) { """ @@ -277,7 +277,7 @@ class TSReactionGenerator( for (param in reactor.parameters) { // Underscores are added to parameter names to prevent conflict with prologue - reactSignature.add("__${param.name}: __Parameter<${TsTypes.getTargetType(param.inferredType)}>") + reactSignature.add("__${param.name}: __Parameter<${TSTypes.getTargetType(param.inferredType)}>") reactFunctArgs.add("this.${param.name}") reactPrologue.add("let ${param.name} = __${param.name}.get();") @@ -286,7 +286,7 @@ class TSReactionGenerator( // Add state to the react function for (state in reactor.stateVars) { // Underscores are added to state names to prevent conflict with prologue - reactSignature.add("__${state.name}: __State<${TsTypes.getTargetType(state)}>") + reactSignature.add("__${state.name}: __State<${TSTypes.getTargetType(state)}>") reactFunctArgs.add("this.${state.name}") reactPrologue.add("let ${state.name} = __${state.name}.get();") diff --git a/org.lflang/src/org/lflang/generator/ts/TSStateGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSStateGenerator.kt index e6c6aa5939..9010baa4ae 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSStateGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSStateGenerator.kt @@ -13,12 +13,12 @@ class TSStateGenerator( fun generateClassProperties(): String = stateVars.joinToString("\n") { - "${it.name}: __State<${TsTypes.getTargetType(it)}>;" + "${it.name}: __State<${TSTypes.getTargetType(it)}>;" } fun generateInstantiations(): String = stateVars.joinToString("\n") { - val init = if (it.init != null) TsTypes.getTargetInitializer(it) else "undefined" + val init = if (it.init != null) TSTypes.getTargetInitializer(it) else "undefined" "this.${it.name} = new __State($init);" } } diff --git a/org.lflang/src/org/lflang/generator/ts/TSTimerGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSTimerGenerator.kt index 3edde60655..1d7118128d 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSTimerGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSTimerGenerator.kt @@ -23,8 +23,8 @@ class TSTimerGenerator ( fun generateInstantiations(): String { val timerInstantiations = mutableListOf() for (timer in timers) { - val timerPeriod: String = TsTypes.getTargetTimeExpr(timer.period.orZero()) - val timerOffset: String = TsTypes.getTargetTimeExpr(timer.offset.orZero()) + val timerPeriod: String = TSTypes.getTargetTimeExpr(timer.period.orZero()) + val timerOffset: String = TSTypes.getTargetTimeExpr(timer.offset.orZero()) timerInstantiations.add("this.${timer.name} = new __Timer(this, $timerOffset, $timerPeriod);") } diff --git a/org.lflang/src/org/lflang/generator/ts/TSTypes.kt b/org.lflang/src/org/lflang/generator/ts/TSTypes.kt index d996f5638e..8fa5b61fbd 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSTypes.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSTypes.kt @@ -1,14 +1,30 @@ package org.lflang.generator.ts -import org.lflang.ASTUtils +import org.lflang.InferredType +import org.lflang.TimeValue import org.lflang.generator.TargetTypes +import org.lflang.inferredType +import org.lflang.lf.Initializer +import org.lflang.lf.ParamRef import org.lflang.lf.StateVar +/** + * [TargetTypes] implementation for [TSGenerator]. + * + * @author Peter Donovan + * @author Clément Fournier + * @author Hokeun Kim + */ + object TSTypes : TargetTypes { + /** + * Returns a type for the state variable. Note that this is TS-specific + * and not equivalent to `s.type.targetType`. + */ override fun getTargetType(s: StateVar): String { - val type = super.getTargetType(s) - return if (!ASTUtils.isInitialized(s)) { + val type = getTargetType(s.inferredType) + return if (s.init == null) { "$type | undefined" } else { type @@ -38,4 +54,21 @@ object TSTypes : TargetTypes { override fun getTargetVariableSizeListType(baseType: String): String { return "Array<$baseType>" } -} \ No newline at end of file + + + override fun getTargetTimeExpr(value: TimeValue): String = + with(value) { + if (unit != null) "TimeValue.${unit.canonicalName}($magnitude)" + // The value must be zero. + else "TimeValue.zero()" + } + + override fun getTargetParamRef(expr: ParamRef, type: InferredType?): String = + "this.${expr.parameter.name}.get()" + + override fun getTargetInitializerWithNotExactlyOneValue(init: Initializer, type: InferredType): String = + init.exprs.joinToString(", ", "[", "]") { + getTargetExpr(it, type.componentType) + } + +} diff --git a/org.lflang/src/org/lflang/generator/ts/TsTypes.kt b/org.lflang/src/org/lflang/generator/ts/TsTypes.kt deleted file mode 100644 index 7adb9a1ec0..0000000000 --- a/org.lflang/src/org/lflang/generator/ts/TsTypes.kt +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2021, TU Dresden. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * 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 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 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.lflang.generator.ts - -import org.lflang.InferredType -import org.lflang.JavaAstUtils -import org.lflang.generator.TargetTypes -import org.lflang.inferredType -import org.lflang.lf.* - -/** - * [TargetTypes] implementation for [TSGenerator]. - * - * @author Clément Fournier - * @author Hokeun Kim - */ -object TsTypes : TargetTypes { - override fun supportsGenerics(): Boolean = true - - override fun getTargetTimeType(): String { - return "TimeValue" - } - - override fun getTargetTagType(): String { - return "TimeValue" - } - - override fun getTargetUndefinedType(): String { - return "Present" - } - - override fun getTargetFixedSizeListType(baseType: String, size: Int): String { - return "Array($size)<$baseType>" - } - - override fun getTargetVariableSizeListType(baseType: String): String { - return "Array<$baseType>" - } - - - override fun getTargetTimeExpr(magnitude: Long, unit: TimeUnit): String = - if (unit != TimeUnit.NONE) "TimeValue.$unit($magnitude)" - // The value must be zero. - else "TimeValue.zero()" - - override fun getTargetParamRef(expr: ParamRef, type: InferredType?): String = - "this.${expr.parameter.name}.get()" - - override fun getTargetInitializerWithNotExactlyOneValue(init: Initializer, type: InferredType): String = - init.exprs.joinToString(", ", "[", "]") { - getTargetExpr(it, type.componentType) - } - - /** - * Returns a type for the state variable. Note that this is TS-specific - * and not equivalent to `s.type.targetType`. - */ - fun getTargetType(s: StateVar): String { - val type = getTargetType(s.inferredType) - return if (s.init == null) { - "$type | undefined" - } else { - type - } - } -} From 80437245bd5ea928be5ab3b65fc2c55934e7ecd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 29 Jan 2022 15:01:49 +0100 Subject: [PATCH 57/68] Fix conflicts... --- .../src/org/lflang/federated/FedASTUtils.java | 11 +- .../org/lflang/generator/ReactorInstance.java | 23 +-- .../src/org/lflang/generator/TargetTypes.java | 10 +- .../org/lflang/generator/TimerInstance.java | 22 +-- .../org/lflang/generator/ValueGenerator.java | 175 ------------------ .../org/lflang/generator/c/CGenerator.xtend | 2 +- .../generator/python/PythonGenerator.xtend | 2 +- .../org/lflang/generator/ts/TSGenerator.kt | 2 - 8 files changed, 24 insertions(+), 223 deletions(-) delete mode 100644 org.lflang/src/org/lflang/generator/ValueGenerator.java diff --git a/org.lflang/src/org/lflang/federated/FedASTUtils.java b/org.lflang/src/org/lflang/federated/FedASTUtils.java index 2c9668cbcc..bfe8d5ab17 100644 --- a/org.lflang/src/org/lflang/federated/FedASTUtils.java +++ b/org.lflang/src/org/lflang/federated/FedASTUtils.java @@ -27,7 +27,6 @@ package org.lflang.federated; -import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -36,12 +35,13 @@ import java.util.stream.Collectors; import org.eclipse.emf.ecore.util.EcoreUtil; + import org.lflang.ASTUtils; import org.lflang.InferredType; import org.lflang.JavaAstUtils; import org.lflang.TargetProperty.CoordinationType; -import org.lflang.federated.serialization.SupportedSerializers; import org.lflang.TimeValue; +import org.lflang.federated.serialization.SupportedSerializers; import org.lflang.generator.GeneratorBase; import org.lflang.generator.PortInstance; import org.lflang.generator.ReactorInstance; @@ -50,14 +50,11 @@ import org.lflang.lf.Connection; import org.lflang.lf.Delay; import org.lflang.lf.Input; -import org.lflang.lf.Instantiation; import org.lflang.lf.LfFactory; import org.lflang.lf.ParamRef; import org.lflang.lf.Parameter; import org.lflang.lf.Reaction; import org.lflang.lf.Reactor; -import org.lflang.lf.Time; -import org.lflang.lf.TimeUnit; import org.lflang.lf.Type; import org.lflang.lf.Value; import org.lflang.lf.VarRef; @@ -536,7 +533,7 @@ private static void addNetworkSenderReaction( // Configure the sending reaction. Delay delay = connection.getDelay(); - TimeValue delayValue = mainInstance.resolveTimeValue(delay == null ? null : delay.getValue()); + TimeValue delayValue = mainInstance.getTimeValue(delay == null ? null : delay.getValue()); networkSenderReaction.getTriggers().add(sourceRef); @@ -730,7 +727,7 @@ public static void makeCommunication( int receivingPortID = destinationFederate.networkMessageActions.size(); Delay delay = connection.getDelay(); - TimeValue delayValue = mainInstance.resolveTimeValue(delay == null ? null : delay.getValue()); + TimeValue delayValue = mainInstance.getTimeValue(delay == null ? null : delay.getValue()); // Add the network output control reaction to the parent FedASTUtils.addNetworkOutputControlReaction( diff --git a/org.lflang/src/org/lflang/generator/ReactorInstance.java b/org.lflang/src/org/lflang/generator/ReactorInstance.java index 09cc8c2c96..6792bcccc4 100644 --- a/org.lflang/src/org/lflang/generator/ReactorInstance.java +++ b/org.lflang/src/org/lflang/generator/ReactorInstance.java @@ -40,10 +40,10 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY import org.lflang.generator.TriggerInstance.BuiltinTriggerVariable; import org.lflang.lf.Action; import org.lflang.lf.Connection; -import org.lflang.lf.Delay; import org.lflang.lf.Input; import org.lflang.lf.Instantiation; import org.lflang.lf.Output; +import org.lflang.lf.ParamRef; import org.lflang.lf.Parameter; import org.lflang.lf.Port; import org.lflang.lf.Reaction; @@ -628,29 +628,14 @@ public String toString() { * precise time value assigned to this reactor instance. */ public TimeValue getTimeValue(Value v) { - Parameter p = v.getParameter(); - if (p != null) { - return JavaAstUtils.getLiteralTimeValue(lookupParameterInstance(p).getInitialValue().get(0)); + if (v instanceof ParamRef) { + ParameterInstance instance = lookupParameterInstance(((ParamRef) v).getParameter()); + return JavaAstUtils.getLiteralTimeValue(instance.getInitialValue().get(0)); } else { return JavaAstUtils.getLiteralTimeValue(v); } } - /** - * Assuming that the given delay denotes a valid time, return a time value. - * - * If the delay is given as a parameter reference, this will look up the - * precise time value assigned to this reactor instance. - */ - public TimeValue getTimeValue(Delay d) { - Parameter p = d.getParameter(); - if (p != null) { - return JavaAstUtils.getLiteralTimeValue(lookupParameterInstance(p).getInitialValue().get(0)); - } else { - return JavaAstUtils.toTimeValue(d.getTime()); - } - } - ////////////////////////////////////////////////////// //// Protected fields. diff --git a/org.lflang/src/org/lflang/generator/TargetTypes.java b/org.lflang/src/org/lflang/generator/TargetTypes.java index b5ef117b51..1a22b46b97 100644 --- a/org.lflang/src/org/lflang/generator/TargetTypes.java +++ b/org.lflang/src/org/lflang/generator/TargetTypes.java @@ -30,24 +30,22 @@ import org.lflang.ASTUtils; import org.lflang.InferredType; import org.lflang.JavaAstUtils; -import org.lflang.TimeUnit; import org.lflang.Target; import org.lflang.TimeValue; import org.lflang.lf.Action; -import org.lflang.lf.Parameter; -import org.lflang.lf.Port; -import org.lflang.lf.StateVar; import org.lflang.lf.AddExpr; import org.lflang.lf.BraceExpr; +import org.lflang.lf.BracketExpr; import org.lflang.lf.Code; import org.lflang.lf.CodeExpr; import org.lflang.lf.Initializer; -import org.lflang.lf.BracketExpr; import org.lflang.lf.Literal; import org.lflang.lf.MulExpr; import org.lflang.lf.ParamRef; +import org.lflang.lf.Parameter; +import org.lflang.lf.Port; +import org.lflang.lf.StateVar; import org.lflang.lf.Time; -import org.lflang.lf.TimeUnit; import org.lflang.lf.TupleExpr; import org.lflang.lf.Type; import org.lflang.lf.Value; diff --git a/org.lflang/src/org/lflang/generator/TimerInstance.java b/org.lflang/src/org/lflang/generator/TimerInstance.java index ed734a55d0..8e87756117 100644 --- a/org.lflang/src/org/lflang/generator/TimerInstance.java +++ b/org.lflang/src/org/lflang/generator/TimerInstance.java @@ -56,21 +56,19 @@ public class TimerInstance extends TriggerInstance { * @param parent The parent reactor. */ public TimerInstance(Timer definition, ReactorInstance parent) { - super(definition, parent); + super(definition, parent); if (parent == null) { throw new InvalidSourceException("Cannot create an TimerInstance with no parent."); } - if (definition != null) { - if (definition.getOffset() != null) { - this.offset = parent.getTimeValue(definition.getOffset()); - } else { - this.offset = DEFAULT_OFFSET; - } - if (definition.getPeriod() != null) { - this.period = parent.getTimeValue(definition.getPeriod()); - } else { - this.offset = DEFAULT_PERIOD; - } + if (definition != null && definition.getOffset() != null) { + this.offset = parent.getTimeValue(definition.getOffset()); + } else { + this.offset = DEFAULT_OFFSET; + } + if (definition != null && definition.getPeriod() != null) { + this.period = parent.getTimeValue(definition.getPeriod()); + } else { + this.period = DEFAULT_PERIOD; } } diff --git a/org.lflang/src/org/lflang/generator/ValueGenerator.java b/org.lflang/src/org/lflang/generator/ValueGenerator.java deleted file mode 100644 index 190777f764..0000000000 --- a/org.lflang/src/org/lflang/generator/ValueGenerator.java +++ /dev/null @@ -1,175 +0,0 @@ -package org.lflang.generator; - -import java.util.List; -import java.util.ArrayList; -import java.util.stream.Collectors; - -import org.lflang.ASTUtils; -import org.lflang.JavaAstUtils; -import org.lflang.TimeValue; -import org.lflang.lf.Assignment; -import org.lflang.lf.Delay; -import org.lflang.lf.Instantiation; -import org.lflang.lf.Parameter; -import org.lflang.lf.StateVar; -import org.lflang.lf.Time; -import org.lflang.TimeUnit; -import org.lflang.lf.Value; - -/** - * Encapsulates logic for representing {@code Value}s in a - * target language. - */ -public final class ValueGenerator { - - /** - * A {@code TimeInTargetLanguage} is a - * target-language-specific time representation - * strategy. - */ - public interface TimeInTargetLanguage { - String apply(TimeValue t); - } - - /** - * A {@code GetTargetReference} instance is a - * target-language-specific function. It provides the - * target language code that refers to the given - * parameter {@code param}. - */ - public interface GetTargetReference { - String apply(Parameter param); - } - - private final TimeInTargetLanguage timeInTargetLanguage; - private final GetTargetReference getTargetReference; - - /** - * Instantiates a target-language-specific - * ValueGenerator parameterized by {@code f}. - * @param f a time representation strategy - */ - public ValueGenerator(TimeInTargetLanguage f, GetTargetReference g) { - this.timeInTargetLanguage = f; - this.getTargetReference = g; - } - - /** - * Create a list of state initializers in target code. - * - * @param state The state variable to create initializers for - * @return A list of initializers in target code - */ - public List getInitializerList(StateVar state) { - List list = new ArrayList<>(); - // FIXME: Previously, we returned null if it was not initialized, which would have caused an - // NPE in TSStateGenerator. Is this the desired behavior? - if (!ASTUtils.isInitialized(state)) return list; - for (Value v : state.getInit()) { - if (v.getParameter() != null) { - list.add(getTargetReference.apply(v.getParameter())); - } else { - list.add(getTargetValue(v, JavaAstUtils.isOfTimeType(state))); - } - } - return list; - } - - /** - * Create a list of default parameter initializers in target code. - * - * @param param The parameter to create initializers for - * @return A list of initializers in target code - */ - public List getInitializerList(Parameter param) { - List list = new ArrayList<>(); - if (param == null) return list; - for (Value v : param.getInit()) - list.add(getTargetValue(v, JavaAstUtils.isOfTimeType(param))); - return list; - } - - /** - * Create a list of parameter initializers in target code in the context - * of an reactor instantiation. - * - * This respects the parameter assignments given in the reactor - * instantiation and falls back to the reactors default initializers - * if no value is assigned to it. - * - * @param param The parameter to create initializers for - * @return A list of initializers in target code - */ - public List getInitializerList(Parameter param, Instantiation i) { - List assignments = i.getParameters().stream() - .filter(it -> it.getLhs() == param) - .collect(Collectors.toList()); - if (assignments.isEmpty()) // Case 0: The parameter was not overwritten in the instantiation - return getInitializerList(param); - // Case 1: The parameter was overwritten in the instantiation - List list = new ArrayList<>(); - if (assignments.get(0) == null) return list; - for (Value init : assignments.get(0).getRhs()) - list.add(getTargetValue(init, JavaAstUtils.isOfTimeType(param))); - return list; - } - - /** - * Return the time specified by {@code t}, expressed as - * code that is valid for some target languages. - */ - public String getTargetTime(TimeValue t) { - return timeInTargetLanguage.apply(t); - } - - /** - * Return the time specified by {@code t}, expressed as - * code that is valid for some target languages. - */ - public String getTargetTime(Time t) { - return timeInTargetLanguage.apply(new TimeValue(t.getInterval(), TimeUnit.fromName(t.getUnit()))); - } - - /** - * Return the time specified by {@code d}, expressed as - * code that is valid for some target languages. - */ - public String getTargetTime(Delay d) { - return d.getParameter() != null ? ASTUtils.toText(d) : timeInTargetLanguage.apply( - JavaAstUtils.toTimeValue(d.getTime()) // The time is given as a parameter reference. - ); - } - - /** - * Return the time specified by {@code v}, expressed as - * code that is valid for some target languages. - */ - public String getTargetTime(Value v) { - return getTargetValue(v, true); - } - - /** - * Get textual representation of a value in the target language. - * - * If the value evaluates to 0, it is interpreted as a normal value. - * - * @param v A time AST node - * @return A time string in the target language - */ - public String getTargetValue(Value v) { - return ASTUtils.toText(v); - } - - /** - * Get textual representation of a value in the target language. - * - * @param v A time AST node - * @param isTime Whether {@code v} is expected to be a time - * @return A time string in the target language - */ - public String getTargetValue(Value v, boolean isTime) { - if (v.getTime() != null) return getTargetTime(v.getTime()); - if (isTime && ASTUtils.isZero(v)) return timeInTargetLanguage.apply(TimeValue.ZERO); - return ASTUtils.toText(v); - } -} diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend index 622996bed0..871013228c 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend @@ -326,7 +326,7 @@ import static extension org.lflang.JavaAstUtils.* class CGenerator extends GeneratorBase { - protected new(FileConfig fileConfig, ErrorReporter errorReporter, boolean CCppMode) { + new(FileConfig fileConfig, ErrorReporter errorReporter, boolean CCppMode) { super(fileConfig, errorReporter) this.CCppMode = CCppMode; } diff --git a/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend b/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend index 0882c72a02..194f8ad3d5 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend @@ -100,7 +100,7 @@ class PythonGenerator extends CGenerator { var PythonTypes types; - private new(FileConfig fileConfig, ErrorReporter errorReporter) { + new(FileConfig fileConfig, ErrorReporter errorReporter) { super(fileConfig, errorReporter, false) // set defaults targetConfig.compiler = "gcc" diff --git a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt index 164b6a1c69..0ccabb004d 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt @@ -43,7 +43,6 @@ import org.lflang.generator.LFGeneratorContext import org.lflang.generator.PrependOperator import org.lflang.generator.SubContext import org.lflang.generator.TargetTypes -import org.lflang.generator.ValueGenerator import org.lflang.generator.canGenerate import org.lflang.lf.Action import org.lflang.lf.VarRef @@ -93,7 +92,6 @@ class TSGenerator( "reactor.ts", "microtime.d.ts", "nanotimer.d.ts", "time.ts", "ulog.d.ts", "util.ts") - private val VG = ValueGenerator(::timeInTargetLanguage) { param -> "this.${param.name}.get()" } private fun timeInTargetLanguage(value: TimeValue): String { return if (value.unit != null) { From c618d57621d71c42e5e4d0fbf7c4f6a469a9f266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 29 Jan 2022 15:03:41 +0100 Subject: [PATCH 58/68] Fix conflicts... --- .../compiler/LinguaFrancaASTUtilsTest.java | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaASTUtilsTest.java b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaASTUtilsTest.java index b7ccf6f592..13af250ee4 100644 --- a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaASTUtilsTest.java +++ b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaASTUtilsTest.java @@ -44,9 +44,11 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.lflang.ASTUtils; import org.lflang.lf.Instantiation; +import org.lflang.lf.Literal; import org.lflang.lf.Model; import org.lflang.lf.Parameter; import org.lflang.lf.StateVar; +import org.lflang.lf.Value; import org.lflang.tests.LFInjectorProvider; /** @@ -222,34 +224,34 @@ public void initialValue() throws Exception { Parameter parameter = (Parameter)obj; if (parameter.getName() == "x") { var values = ASTUtils.initialValue(parameter, null); - Assertions.assertEquals(values.get(0).getLiteral(), "1"); + Assertions.assertEquals(asLiteral(values), "1"); values = ASTUtils.initialValue(parameter, List.of(map.get("a1"))); - Assertions.assertEquals(values.get(0).getLiteral(), "2"); + Assertions.assertEquals(asLiteral(values), "2"); values = ASTUtils.initialValue(parameter, List.of(map.get("a2"))); - Assertions.assertEquals(values.get(0).getLiteral(), "-1"); + Assertions.assertEquals(asLiteral(values), "-1"); values = ASTUtils.initialValue(parameter, List.of(map.get("a1"), map.get("b1"))); - Assertions.assertEquals(values.get(0).getLiteral(), "3"); + Assertions.assertEquals(asLiteral(values), "3"); values = ASTUtils.initialValue(parameter, List.of(map.get("a2"), map.get("b1"))); - Assertions.assertEquals(values.get(0).getLiteral(), "-1"); + Assertions.assertEquals(asLiteral(values), "-1"); values = ASTUtils.initialValue(parameter, List.of(map.get("a1"), map.get("b2"))); - Assertions.assertEquals(values.get(0).getLiteral(), "-2"); + Assertions.assertEquals(asLiteral(values), "-2"); values = ASTUtils.initialValue(parameter, List.of(map.get("a2"), map.get("b2"))); - Assertions.assertEquals(values.get(0).getLiteral(), "-1"); + Assertions.assertEquals(asLiteral(values), "-1"); } else if (parameter.getName() == "y") { var values = ASTUtils.initialValue(parameter, null); - Assertions.assertEquals(values.get(0).getLiteral(), "2"); + Assertions.assertEquals(asLiteral(values), "2"); try { values = ASTUtils.initialValue(parameter, @@ -261,13 +263,17 @@ public void initialValue() throws Exception { values = ASTUtils.initialValue(parameter, List.of(map.get("b1"))); - Assertions.assertEquals(values.get(0).getLiteral(), "3"); + Assertions.assertEquals(asLiteral(values), "3"); values = ASTUtils.initialValue(parameter, List.of(map.get("b2"))); - Assertions.assertEquals(values.get(0).getLiteral(), "-2"); + Assertions.assertEquals(asLiteral(values), "-2"); } } }); } + + private Object asLiteral(List values) { + return ((Literal) values.get(0)).getLiteral(); + } } From 5dd07de1885f15fbac6e622a9115d07c9e8d83e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 5 Feb 2022 15:16:17 +0100 Subject: [PATCH 59/68] Update kotlin version --- build.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.gradle b/build.gradle index f11be29183..f3451a2536 100644 --- a/build.gradle +++ b/build.gradle @@ -12,6 +12,7 @@ plugins { id "com.github.johnrengelman.shadow" version "${shadowJarVersion}" id 'java' id 'jacoco' + id 'org.jetbrains.kotlin.jvm' version '1.5.31' } subprojects { @@ -26,6 +27,7 @@ subprojects { jvmTarget = kotlinJvmTarget } } + kotlinVersion = '1.5.31' // sync with version in plugin block above dependencies { implementation platform("org.eclipse.xtext:xtext-dev-bom:${xtextVersion}") From a675fbd3f796c2adb359e8763bf1a4d2f5fcc05d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 5 Feb 2022 16:14:35 +0100 Subject: [PATCH 60/68] Fix validator tests --- .../compiler/LinguaFrancaValidationTest.java | 29 ++++++++--------- org.lflang/src/org/lflang/ASTUtils.java | 7 ++-- .../org/lflang/validation/LFValidator.java | 32 +++++++++++-------- 3 files changed, 37 insertions(+), 31 deletions(-) diff --git a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.java b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.java index 75d49d89f8..25bb4c79ed 100644 --- a/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.java +++ b/org.lflang.tests/src/org/lflang/tests/compiler/LinguaFrancaValidationTest.java @@ -866,7 +866,7 @@ public void nonzeroAfterMustHaveUnits() throws Exception { " b = new X()", " a.y -> b.x after 1", "}"); - validator.assertError(parseWithoutError(testCase), LfPackage.eINSTANCE.getTime(), + validator.assertError(parseWithoutError(testCase), LfPackage.eINSTANCE.getConnection(), null, "Missing time unit."); } @@ -896,7 +896,7 @@ public void nonZeroTimeValueWithoutUnits() throws Exception { " printf(\"Hello World.\\n\");", " =}", "}"); - validator.assertError(parseWithoutError(testCase), LfPackage.eINSTANCE.getValue(), null, "Missing time unit."); + validator.assertError(parseWithoutError(testCase), LfPackage.eINSTANCE.getTimer(), null, "Missing time unit."); } /** @@ -923,8 +923,8 @@ public void parameterTypeMismatch() throws Exception { " printf(\"Hello World.\\n\");", " =}", "}"); - validator.assertError(parseWithoutError(testCase), LfPackage.eINSTANCE.getValue(), - null, "Parameter is not of time type"); + validator.assertError(parseWithoutError(testCase), LfPackage.eINSTANCE.getTimer(), + null, "Referenced parameter does not have time type."); } /** @@ -951,7 +951,7 @@ public void targetCodeInTimeArgument() throws Exception { " printf(\"Hello World.\\n\");", " =}", "}"); - validator.assertError(parseWithoutError(testCase), LfPackage.eINSTANCE.getValue(), + validator.assertError(parseWithoutError(testCase), LfPackage.eINSTANCE.getTimer(), null, "Invalid time literal"); } @@ -1055,7 +1055,7 @@ public void overflowingAssignmentC() throws Exception { validator.assertError(parseWithoutError(testCase), LfPackage.eINSTANCE.getAssignment(), null, "Time value used to specify a deadline exceeds the maximum of " + TimeValue.MAX_LONG_DEADLINE + " nanoseconds."); - } + } /** * Report missing trigger. @@ -1202,20 +1202,19 @@ public void stateAndParameterDeclarationsInC() throws Exception { validator.assertError(model, LfPackage.eINSTANCE.getParameter(), null, "Type declaration missing."); validator.assertError(model, LfPackage.eINSTANCE.getParameter(), null, - "Missing time unit."); + "Uninitialized parameter."); validator.assertError(model, LfPackage.eINSTANCE.getParameter(), null, - "Invalid time literal."); + "Missing time unit."); validator.assertError(model, LfPackage.eINSTANCE.getParameter(), null, - "Time parameter cannot be initialized using a list."); + "Expected exactly one time value."); + validator.assertError(model, LfPackage.eINSTANCE.getStateVar(), null, + "Missing time unit."); validator.assertError(model, LfPackage.eINSTANCE.getParameter(), null, "Parameter cannot be initialized using parameter."); + validator.assertError(model, LfPackage.eINSTANCE.getStateVar(), null, - "Referenced parameter does not denote a time."); - validator.assertError(model, LfPackage.eINSTANCE.getStateVar(), null, - "Invalid time literal."); - validator.assertError(model, LfPackage.eINSTANCE.getParameter(), null, - "Uninitialized parameter."); - validator.assertError(model, LfPackage.eINSTANCE.getValue(), null, + "Referenced parameter does not have time type."); + validator.assertError(model, LfPackage.eINSTANCE.getTimer(), null, "Missing time unit."); } diff --git a/org.lflang/src/org/lflang/ASTUtils.java b/org.lflang/src/org/lflang/ASTUtils.java index dfcf2e0cfe..b41ba69d48 100644 --- a/org.lflang/src/org/lflang/ASTUtils.java +++ b/org.lflang/src/org/lflang/ASTUtils.java @@ -1034,6 +1034,7 @@ public static boolean isZero(String literal) { return false; } + // todo #657: remove public static boolean isZero(Code code) { if (code != null && isZero(toUntaggedText(code))) { return true; @@ -1107,10 +1108,12 @@ public static boolean isValidTime(Value value) { if (value instanceof ParamRef) { return JavaAstUtils.isOfTimeType(((ParamRef) value).getParameter()); } else if (value instanceof Time) { - return isValidTime(value); + return ((Time) value).getInterval() != 0 + || ((Time) value).getUnit() != null; } else if (value instanceof Literal) { - return isZero(((Literal) value)); + return isZero(value); } else if (value instanceof CodeExpr) { + // todo #657: remove return isZero(((CodeExpr) value).getCode()); } return false; diff --git a/org.lflang/src/org/lflang/validation/LFValidator.java b/org.lflang/src/org/lflang/validation/LFValidator.java index ddcbf97f41..927b3b3064 100644 --- a/org.lflang/src/org/lflang/validation/LFValidator.java +++ b/org.lflang/src/org/lflang/validation/LFValidator.java @@ -799,17 +799,16 @@ public void updateModelInfo(Model model) { public void checkParameter(Parameter param) { checkName(param.getName(), Literals.PARAMETER__NAME); - if (param.getInit().getExprs().stream() - .anyMatch(it -> it instanceof ParamRef)) { - error("Parameter cannot be initialized using parameter.", - Literals.PARAMETER__INIT); - } - if (param.getInit() == null || param.getInit().getExprs().size() == 0) { // All parameters must be initialized. error("Uninitialized parameter.", Literals.PARAMETER__INIT); + return; + } else if (param.getInit().getExprs().stream() + .anyMatch(it -> it instanceof ParamRef)) { + error("Parameter cannot be initialized using parameter.", + Literals.PARAMETER__INIT); } else { - typeCheck(param.getInit(), JavaAstUtils.getInferredType(param), Literals.PARAMETER__INIT); + typeCheck(param.getInit(), JavaAstUtils.getInferredType(param), Literals.PARAMETER__INIT); } if (this.target.requiresTypes) { @@ -818,8 +817,8 @@ public void checkParameter(Parameter param) { error("Type declaration missing.", Literals.PARAMETER__TYPE); } } - - if(this.target == Target.CPP) { + + if (this.target == Target.CPP) { EObject container = param.eContainer(); Reactor reactor = (Reactor) container; if(reactor.isMain()){ @@ -1257,8 +1256,12 @@ public void checkSerializer(Serializer serializer) { public void checkState(StateVar stateVar) { checkName(stateVar.getName(), Literals.STATE_VAR__NAME); + if (stateVar.getInit() != null && stateVar.getInit().getExprs().size() != 0) { + typeCheck(stateVar.getInit(), JavaAstUtils.getInferredType(stateVar), Literals.STATE_VAR__INIT); + } + if (this.target.requiresTypes - && (JavaAstUtils.getInferredType(stateVar)).isUndefined()) { + && JavaAstUtils.getInferredType(stateVar).isUndefined()) { // Report if a type is missing error("State must have a type.", Literals.STATE_VAR__TYPE); } @@ -1443,12 +1446,13 @@ public void checkValueIsTime(Value value, EStructuralFeature feature) { error("Missing time unit.", feature); } } else if (value instanceof CodeExpr) { - if (ASTUtils.isZero(((CodeExpr)value).getCode())) return; + if (ASTUtils.isZero(((CodeExpr) value).getCode())) { + return; + } error("Invalid time literal.", feature); - } - - if (!(value instanceof Time)) + } else if (!(value instanceof Time)) { error("Invalid time literal.", feature); + } } From 30b71576a3f0efe51dc87d69aa15e98c5264224d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 5 Feb 2022 16:34:27 +0100 Subject: [PATCH 61/68] Fix some other tests --- .../org/lflang/tests/lsp/ErrorInserter.java | 26 +++++++++++++++++++ .../src/org/lflang/tests/lsp/LspTests.java | 3 +++ .../org/lflang/generator/ReactorInstance.java | 6 ++++- .../src/org/lflang/generator/c/CTypes.java | 2 +- test/Python/src/CompositionGain.lf | 4 +-- 5 files changed, 37 insertions(+), 4 deletions(-) diff --git a/org.lflang.tests/src/org/lflang/tests/lsp/ErrorInserter.java b/org.lflang.tests/src/org/lflang/tests/lsp/ErrorInserter.java index 0ddb149ccb..0e48a43f10 100644 --- a/org.lflang.tests/src/org/lflang/tests/lsp/ErrorInserter.java +++ b/org.lflang.tests/src/org/lflang/tests/lsp/ErrorInserter.java @@ -1,5 +1,8 @@ package org.lflang.tests.lsp; +import static java.lang.Math.max; +import static java.lang.Math.min; + import java.io.Closeable; import java.io.IOException; import java.io.PrintWriter; @@ -14,6 +17,7 @@ import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Predicate; +import java.util.stream.Collectors; import java.util.stream.Stream; import com.google.common.collect.ImmutableList; @@ -164,8 +168,30 @@ public void insert(String line, Random random) { }); } + /** + * Get the text of the test around the given line, + * for error reporting. + */ + public String getLinesAround(int line) { + final int LINES_AROUND = 2; + int minLine = max(0, line - LINES_AROUND); + List linesAround = + lines.stream().skip(minLine).limit(2 * LINES_AROUND + 1).collect(Collectors.toList()); + // prefix each line with its line number + ListIterator iter = linesAround.listIterator(); + int i = 0; + while (iter.hasNext()) { + String next = iter.next(); + int lineNo = minLine + i; + iter.set(lineNo + "| " + next); + i++; + } + return String.join("\n", linesAround); + } + /** * Alter the content of this test. + * * @param alterer A function whose first argument is an iterator over the lines of {@code this}, whose second * argument is the line most recently returned by that iterator, and whose return value is * whether an alteration was successfully performed. This function is only applied within diff --git a/org.lflang.tests/src/org/lflang/tests/lsp/LspTests.java b/org.lflang.tests/src/org/lflang/tests/lsp/LspTests.java index bcf176b6bc..a46d2915db 100644 --- a/org.lflang.tests/src/org/lflang/tests/lsp/LspTests.java +++ b/org.lflang.tests/src/org/lflang/tests/lsp/LspTests.java @@ -104,6 +104,9 @@ private void targetLanguageValidationTest(Target target, ErrorInserter errorInse diagnostic -> diagnostic.getRange().getStart().getLine() == badLine ); System.out.println(result ? " Success." : " but the expected error could not be found."); + if (!result) { + System.err.println(alteredTest.getLinesAround(badLine)); + } return result; } )), diff --git a/org.lflang/src/org/lflang/generator/ReactorInstance.java b/org.lflang/src/org/lflang/generator/ReactorInstance.java index 6792bcccc4..960d83663a 100644 --- a/org.lflang/src/org/lflang/generator/ReactorInstance.java +++ b/org.lflang/src/org/lflang/generator/ReactorInstance.java @@ -630,7 +630,11 @@ public String toString() { public TimeValue getTimeValue(Value v) { if (v instanceof ParamRef) { ParameterInstance instance = lookupParameterInstance(((ParamRef) v).getParameter()); - return JavaAstUtils.getLiteralTimeValue(instance.getInitialValue().get(0)); + List initialValue = instance.getInitialValue(); + if (!initialValue.isEmpty()) { + return JavaAstUtils.getLiteralTimeValue(initialValue.get(0)); + } + return TimeValue.ZERO; //fixme } else { return JavaAstUtils.getLiteralTimeValue(v); } diff --git a/org.lflang/src/org/lflang/generator/c/CTypes.java b/org.lflang/src/org/lflang/generator/c/CTypes.java index 446a8796e7..2b5308a3c5 100644 --- a/org.lflang/src/org/lflang/generator/c/CTypes.java +++ b/org.lflang/src/org/lflang/generator/c/CTypes.java @@ -83,7 +83,7 @@ public String getTargetVariableSizeListType(String baseType) { public String getTargetUndefinedType() { // todo C used to insert a marker in the code // return String.format("/* %s */", errorReporter.reportError("undefined type")); - throw new UnsupportedGeneratorFeatureException("Undefined type"); + return "/* error! */"; } @Override diff --git a/test/Python/src/CompositionGain.lf b/test/Python/src/CompositionGain.lf index 8d223b1c12..5c7615de30 100644 --- a/test/Python/src/CompositionGain.lf +++ b/test/Python/src/CompositionGain.lf @@ -4,7 +4,7 @@ reactor Gain { input gainin; output y; reaction(gainin) -> y {= - print("Gain received " + str(gainin.value)) + undefined_name15291838("Gain received " + str(gainin.value)) y.set(gainin.value * 2) =} } @@ -26,4 +26,4 @@ main reactor { sys.stderr.write("ERROR: Received value should have been ", str(42 * 2)) exit(2) =} -} \ No newline at end of file +} From 5b93e157bfc56fa82ac62f11326a2a674d1c472a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 5 Feb 2022 16:59:34 +0100 Subject: [PATCH 62/68] Try to fix bugs in CGenerator --- .../src/org/lflang/generator/c/CGenerator.xtend | 16 +++++++++++++--- .../src/org/lflang/generator/c/CTypes.java | 3 +-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend index fb8f71fa1b..4651f25bd3 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend @@ -319,6 +319,11 @@ class CGenerator extends GeneratorBase { new(FileConfig fileConfig, ErrorReporter errorReporter, boolean CCppMode) { super(fileConfig, errorReporter) this.CCppMode = CCppMode; + this.types = new CTypes() { + override String getTargetUndefinedType() { + return String.format("/* %s */", errorReporter.reportError("undefined type")); + } + } } new(FileConfig fileConfig, ErrorReporter errorReporter) { @@ -3851,7 +3856,7 @@ class CGenerator extends GeneratorBase { } protected def String getTargetTimeExpr(TimeValue value) { - return CTypes.INSTANCE.getTargetTimeExpr(value) + return targetTypes.getTargetTimeExpr(value) } protected def getInitializer(Initializer init, InferredType t, ReactorInstance parent) { @@ -4861,6 +4866,7 @@ class CGenerator extends GeneratorBase { } endScopedBlock(builder); } + /** Standardized name for channel index variable for a source. */ static val sc = "src_channel"; /** Standardized name for bank index variable for a source. */ @@ -5492,7 +5498,11 @@ class CGenerator extends GeneratorBase { * accesses to the self struct of the parents of those parameters. */ protected def String getInitializer(ParameterInstance p) { - return targetTypes.getTargetInitializer(p.init, p.type) + // Handle the bank_index parameter. + if (p.name.equals("bank_index")) { + return CUtil.bankIndex(p.parent); + } + return getInitializer(p.init, p.type, p.parent) } override generateDelayGeneric() { @@ -6398,7 +6408,7 @@ class CGenerator extends GeneratorBase { // Indicate whether the generator is in Cpp mode or not var boolean CCppMode = false; - val CTypes types = CTypes.INSTANCE; // todo remove + val CTypes types; /** The current federate for which we are generating code. */ var currentFederate = null as FederateInstance; diff --git a/org.lflang/src/org/lflang/generator/c/CTypes.java b/org.lflang/src/org/lflang/generator/c/CTypes.java index 2b5308a3c5..1bce0342bc 100644 --- a/org.lflang/src/org/lflang/generator/c/CTypes.java +++ b/org.lflang/src/org/lflang/generator/c/CTypes.java @@ -35,6 +35,7 @@ import org.lflang.generator.TargetTypes; import org.lflang.generator.UnsupportedGeneratorFeatureException; import org.lflang.lf.Initializer; +import org.lflang.lf.ParamRef; /** * {@link TargetTypes} impl for {@link CGenerator}. @@ -49,8 +50,6 @@ public class CTypes implements TargetTypes { private static final Pattern arrayPattern = Pattern.compile("^\\s*(?:/\\*.*?\\*/)?\\s*(\\w+)\\s*\\[([0-9]*)]\\s*$"); - public static final CTypes INSTANCE = new CTypes(); - protected CTypes() { } From 3c195d51dae8aa7ac0d4b18abb84f674c5ffbadb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 6 Feb 2022 12:27:03 +0100 Subject: [PATCH 63/68] Update reactor-c to master version --- org.lflang/src/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index a98c93b614..df7aef6ebb 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit a98c93b614bd894d3b7febe68ab010345caabdfb +Subproject commit df7aef6ebbf790305db8eb9ba0c26f32d6faa47c From 14fe4c0bc9ae0f8197a39381f67be0fd4d8f5f03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 6 Feb 2022 16:37:17 +0100 Subject: [PATCH 64/68] Fix TS delay tests --- org.lflang/src/org/lflang/ASTUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/org/lflang/ASTUtils.java b/org.lflang/src/org/lflang/ASTUtils.java index b41ba69d48..2efbcbe427 100644 --- a/org.lflang/src/org/lflang/ASTUtils.java +++ b/org.lflang/src/org/lflang/ASTUtils.java @@ -334,7 +334,7 @@ private static Instantiation getDelayInstance(Reactor delayClass, var rhs = factory.createInitializer(); rhs.getExprs().add(delay.getValue()); rhs.setAssign(true); - assignment.setRhs(factory.createInitializer()); + assignment.setRhs(rhs); delayInstance.getParameters().add(assignment); delayInstance.setName("delay"); // This has to be overridden. return delayInstance; From 8aa7ab9137ba1581b17b1b2feb7f691247eff43e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 6 Feb 2022 17:22:53 +0100 Subject: [PATCH 65/68] Fix python errors --- .../src/org/lflang/generator/c/CGenerator.xtend | 10 +++++++--- .../org/lflang/generator/python/PythonGenerator.xtend | 11 ++++------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend index 4651f25bd3..c93b8348ff 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend @@ -316,14 +316,18 @@ import static extension org.lflang.JavaAstUtils.* class CGenerator extends GeneratorBase { - new(FileConfig fileConfig, ErrorReporter errorReporter, boolean CCppMode) { + new(FileConfig fileConfig, ErrorReporter errorReporter, boolean CCppMode, CTypes ctypes) { super(fileConfig, errorReporter) this.CCppMode = CCppMode; - this.types = new CTypes() { + this.types = ctypes; + } + + new(FileConfig fileConfig, ErrorReporter errorReporter, boolean CCppMode) { + this(fileConfig, errorReporter, CCppMode, new CTypes() { override String getTargetUndefinedType() { return String.format("/* %s */", errorReporter.reportError("undefined type")); } - } + }) } new(FileConfig fileConfig, ErrorReporter errorReporter) { diff --git a/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend b/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend index d69d21e71c..eef5b92119 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend @@ -97,15 +97,12 @@ class PythonGenerator extends CGenerator { // Used to add module requirements to setup.py (delimited with ,) var pythonRequiredModules = new StringBuilder() - var PythonTypes types; - new(FileConfig fileConfig, ErrorReporter errorReporter) { - super(fileConfig, errorReporter, false) + super(fileConfig, errorReporter, false, PythonTypes.INSTANCE) // set defaults targetConfig.compiler = "gcc" targetConfig.compilerFlags = newArrayList // -Wall -Wconversion" targetConfig.linkerFlags = "" - this.types = PythonTypes.INSTANCE } /** @@ -163,7 +160,7 @@ class PythonGenerator extends CGenerator { } override PythonTypes getTargetTypes() { - return PythonTypes.INSTANCE + return super.getTargetTypes() as PythonTypes } protected def String getTargetInitializer(Initializer init) { @@ -473,10 +470,10 @@ class PythonGenerator extends CGenerator { ''') for (param : decl.toDefinition.allParameters) { - if (!types.getTargetType(param).equals("PyObject*")) { + if (!targetTypes.getTargetType(param).equals("PyObject*")) { // If type is given, use it temporary_code. - pr('''self._«param.name»:«types.getPythonType(param.inferredType)» = «param.init.targetInitializer» + pr('''self._«param.name»:«targetTypes.getPythonType(param.inferredType)» = «param.init.targetInitializer» ''') } else { // If type is not given, just pass along the initialization From 7e7f21544f97ff5f86f64f261578a69e83a6d5cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 8 Feb 2022 19:56:51 +0100 Subject: [PATCH 66/68] Revert "Update kotlin version" This reverts commit 5dd07de1885f15fbac6e622a9115d07c9e8d83e2. --- build.gradle | 2 -- 1 file changed, 2 deletions(-) diff --git a/build.gradle b/build.gradle index f3451a2536..f11be29183 100644 --- a/build.gradle +++ b/build.gradle @@ -12,7 +12,6 @@ plugins { id "com.github.johnrengelman.shadow" version "${shadowJarVersion}" id 'java' id 'jacoco' - id 'org.jetbrains.kotlin.jvm' version '1.5.31' } subprojects { @@ -27,7 +26,6 @@ subprojects { jvmTarget = kotlinJvmTarget } } - kotlinVersion = '1.5.31' // sync with version in plugin block above dependencies { implementation platform("org.eclipse.xtext:xtext-dev-bom:${xtextVersion}") From a4b7fe82e4b9e796424f0556cdfaedd57549f3b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 22 Feb 2022 02:01:39 +0100 Subject: [PATCH 67/68] fix merge... --- .idea/inspectionProfiles/Project_Default.xml | 1 + build.gradle | 1 + gradle.properties | 2 +- org.lflang/src/org/lflang/ASTUtils.java | 457 +++++++++++++----- org.lflang/src/org/lflang/AstExtensions.kt | 27 +- org.lflang/src/org/lflang/JavaAstUtils.java | 127 ----- .../src/org/lflang/generator/TargetTypes.java | 6 +- .../org/lflang/validation/LFValidator.java | 55 +-- 8 files changed, 370 insertions(+), 306 deletions(-) diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 8a4c13d6e2..ca5d3d2e0a 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -32,6 +32,7 @@