From 7e3c696e7a68a4daa7856e08392f8773cfc2a19a Mon Sep 17 00:00:00 2001 From: erlingrj Date: Sun, 8 Dec 2024 00:09:05 -0800 Subject: [PATCH 1/4] Add support for C arrays on ports and state variables --- include/reactor-uc/macros.h | 26 +++++++++++++++- .../org/lflang/generator/TargetTypes.java | 4 +-- .../main/java/org/lflang/target/Target.java | 2 +- .../lflang/generator/uc/UcPortGenerator.kt | 22 +++++++++++-- .../lflang/generator/uc/UcStateGenerator.kt | 25 +++++++++++++-- test/lf/src/ArrayPorts.lf | 30 ++++++++++++++++++ test/lf/src/ArrayState.lf | 14 +++++++++ test/lf/src/StringPorts.lf | 31 +++++++++++++++++++ 8 files changed, 146 insertions(+), 8 deletions(-) create mode 100644 test/lf/src/ArrayPorts.lf create mode 100644 test/lf/src/ArrayState.lf create mode 100644 test/lf/src/StringPorts.lf diff --git a/include/reactor-uc/macros.h b/include/reactor-uc/macros.h index 2ca83b78..e02ed1f5 100644 --- a/include/reactor-uc/macros.h +++ b/include/reactor-uc/macros.h @@ -11,6 +11,13 @@ _port->set(_port, &__val); \ } while (0) +// Sets an output port, copies data and triggers all downstream reactions. +#define lf_set_array(port, array) \ + do { \ + Port *_port = (Port *)(port); \ + _port->set(_port, array); \ + } while (0) + /** * @brief Retreive the value of a trigger and cast it to the expected type */ @@ -211,7 +218,14 @@ typedef struct { \ Port super; \ Reaction *sources[(SourceSize)]; \ - BufferType value; \ + BufferType value; \ + } ReactorName##_##PortName; + +#define LF_DEFINE_OUTPUT_ARRAY_STRUCT(ReactorName, PortName, SourceSize, BufferType, ArrayLen) \ + typedef struct { \ + Port super; \ + Reaction *sources[(SourceSize)]; \ + BufferType value[(ArrayLen)]; \ } ReactorName##_##PortName; #define LF_DEFINE_OUTPUT_CTOR(ReactorName, PortName, SourceSize) \ @@ -249,6 +263,16 @@ Connection *conns_out[(NumConnsOut)]; \ } ReactorName##_##PortName; +#define LF_DEFINE_INPUT_ARRAY_STRUCT(ReactorName, PortName, EffectSize, ObserversSize, BufferType, ArrayLen, \ + NumConnsOut) \ + typedef struct { \ + Port super; \ + Reaction *effects[(EffectSize)]; \ + Reaction *observers[(ObserversSize)]; \ + BufferType value[(ArrayLen)]; \ + Connection *conns_out[(NumConnsOut)]; \ + } ReactorName##_##PortName; + #define LF_DEFINE_INPUT_CTOR(ReactorName, PortName, EffectSize, ObserverSize, BufferType, NumConnsOut) \ void ReactorName##_##PortName##_ctor(ReactorName##_##PortName *self, Reactor *parent, \ InputExternalCtorArgs external) { \ diff --git a/lfc/core/src/main/java/org/lflang/generator/TargetTypes.java b/lfc/core/src/main/java/org/lflang/generator/TargetTypes.java index c5df6992..4a0c4a0c 100644 --- a/lfc/core/src/main/java/org/lflang/generator/TargetTypes.java +++ b/lfc/core/src/main/java/org/lflang/generator/TargetTypes.java @@ -184,8 +184,8 @@ default String getTargetExpr(Expression expr, InferredType type) { return ASTUtils.addZeroToLeadingDot(((Literal) expr).getLiteral()); // here we don't escape } else if (expr instanceof CodeExpr) { return ASTUtils.toText(((CodeExpr) expr).getCode()); -// } else if (expr instanceof BracedListExpression) { -// return getTargetBracedListExpr((BracedListExpression) expr, type); + } else if (expr instanceof BracedListExpression) { + return getTargetBracedListExpr((BracedListExpression) expr, type); // } else if (expr instanceof BracketListExpression) { // return getTargetBracketListExpr((BracketListExpression) expr, type); // } else if (expr instanceof ParenthesisListExpression) { diff --git a/lfc/core/src/main/java/org/lflang/target/Target.java b/lfc/core/src/main/java/org/lflang/target/Target.java index 71aff70f..7f504cfd 100644 --- a/lfc/core/src/main/java/org/lflang/target/Target.java +++ b/lfc/core/src/main/java/org/lflang/target/Target.java @@ -468,7 +468,7 @@ public boolean mandatesEqualsInitializers() { /** Allow expressions of the form {@code {a, b, c}}. */ public boolean allowsBracedListExpressions() { - return false; + return true; } /** Allow expressions of the form {@code [a, b, c]}. */ diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt index 9b73aaf0..4cf5e187 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt @@ -36,14 +36,32 @@ import org.lflang.lf.* class UcPortGenerator(private val reactor: Reactor, private val connections: UcConnectionGenerator) { val Port.external_args get(): String = "_${name}_external" + val Port.isArray + get(): Boolean = type.cStyleArraySpec != null + val Port.arrayLength + get(): Int = type.cStyleArraySpec.length + companion object { val Port.width get(): Int = widthSpec?.getWidth()?:1 } - private fun generateSelfStruct(input: Input) = "LF_DEFINE_INPUT_STRUCT(${reactor.codeType}, ${input.name}, ${reactor.getEffects(input).size}, ${reactor.getObservers(input).size}, ${input.type.toText()}, ${connections.getNumConnectionsFromPort(null, input as Port)});" + private fun generateSelfStruct(input: Input): String { + if (input.isArray) { + return "LF_DEFINE_INPUT_ARRAY_STRUCT(${reactor.codeType}, ${input.name}, ${reactor.getEffects(input).size}, ${reactor.getObservers(input).size}, ${input.type.id}, ${input.arrayLength}, ${connections.getNumConnectionsFromPort(null, input as Port)});" + } else { + return "LF_DEFINE_INPUT_STRUCT(${reactor.codeType}, ${input.name}, ${reactor.getEffects(input).size}, ${reactor.getObservers(input).size}, ${input.type.toText()}, ${connections.getNumConnectionsFromPort(null, input as Port)});" + } + } private fun generateInputCtor(input: Input) = "LF_DEFINE_INPUT_CTOR(${reactor.codeType}, ${input.name}, ${reactor.getEffects(input).size}, ${reactor.getObservers(input).size}, ${input.type.toText()}, ${connections.getNumConnectionsFromPort(null, input as Port)});" - private fun generateSelfStruct(output: Output) = "LF_DEFINE_OUTPUT_STRUCT(${reactor.codeType}, ${output.name}, ${reactor.getSources(output).size}, ${output.type.toText()});" + private fun generateSelfStruct(output: Output): String { + if (output.isArray) { + return "LF_DEFINE_OUTPUT_ARRAY_STRUCT(${reactor.codeType}, ${output.name}, ${reactor.getSources(output).size}, ${output.type.id}, ${output.arrayLength});" + } else { + return "LF_DEFINE_OUTPUT_STRUCT(${reactor.codeType}, ${output.name}, ${reactor.getSources(output).size}, ${output.type.toText()});" + } + } + private fun generateOutputCtor(output: Output) = "LF_DEFINE_OUTPUT_CTOR(${reactor.codeType}, ${output.name}, ${reactor.getSources(output).size});" fun generateSelfStructs() = reactor.allInputs.plus(reactor.allOutputs).joinToString(prefix = "// Port structs\n", separator = "\n", postfix = "\n") { diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStateGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStateGenerator.kt index 041e3bc8..a8685d37 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStateGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStateGenerator.kt @@ -3,12 +3,33 @@ package org.lflang.generator.uc import org.lflang.allStateVars import org.lflang.isInitialized import org.lflang.lf.Reactor +import org.lflang.lf.StateVar import org.lflang.toText class UcStateGenerator(private val reactor: Reactor) { + private val StateVar.isArray + get(): Boolean = type.cStyleArraySpec != null + + private val StateVar.arrayLength + get(): Int = type.cStyleArraySpec.length + fun generateReactorStructFields() = - reactor.allStateVars.joinToString(prefix = "// State variables \n", separator = "\n") { "${it.type.toText()} ${it.name};" } + reactor.allStateVars.joinToString(prefix = "// State variables \n", separator = "\n") { + if (it.isArray) { + "${it.type.id} ${it.name}[${it.arrayLength}];" + } else { + "${it.type.toText()} ${it.name};" + } + } fun generateInitializeStateVars() = - reactor.allStateVars.filter{it.isInitialized}.joinToString(prefix = "// Initialize State variables \n", separator = "\n") { "self->${it.name} = ${it.init.expr.toCCode()};" } + reactor.allStateVars.filter{it.isInitialized}.joinToString(prefix = "// Initialize State variables \n", separator = "\n") { + if (it.isArray) { + """|${it.type.id} _${it.name}_init[${it.arrayLength}] = ${it.init.expr.toCCode()}; + |memcpy(&self->${it.name}, &_${it.name}_init, sizeof(_${it.name}_init)); + """.trimMargin() + } else { + "self->${it.name} = ${it.init.expr.toCCode()};" + } + } } diff --git a/test/lf/src/ArrayPorts.lf b/test/lf/src/ArrayPorts.lf new file mode 100644 index 00000000..6559b05d --- /dev/null +++ b/test/lf/src/ArrayPorts.lf @@ -0,0 +1,30 @@ +target uC { + platform: Native +} + + +reactor Src { + output out: int[4] + + reaction(startup) -> out {= + int arr[] = {1, 2, 3, 4}; + lf_set_array(out, arr); + =} +} + +reactor Sink { + input in: int[4] + + reaction(in) {= + for (int i = 0; i < 4; i++) { + printf("%d\n", in->value[i]); + validate(in->value[i] == i+1); + } + =} +} + +main reactor { + src = new Src() + sink = new Sink() + src.out -> sink.in +} \ No newline at end of file diff --git a/test/lf/src/ArrayState.lf b/test/lf/src/ArrayState.lf new file mode 100644 index 00000000..948abed4 --- /dev/null +++ b/test/lf/src/ArrayState.lf @@ -0,0 +1,14 @@ +target uC { + platform: Native +} + +main reactor { + state s: int[4] = {1, 2, 3, 4} + + reaction(startup) {= + for (int i = 0; i < 4; i++) { + printf("%d\n", self->s[i]); + validate(self->s[i] == i+1); + } + =} +} \ No newline at end of file diff --git a/test/lf/src/StringPorts.lf b/test/lf/src/StringPorts.lf new file mode 100644 index 00000000..d799eda5 --- /dev/null +++ b/test/lf/src/StringPorts.lf @@ -0,0 +1,31 @@ +target uC { + platform: Native +} + + +reactor Src { + output out: char[12] + + reaction(startup) -> out {= + lf_set_array(out, "Hello World"); + =} +} + +reactor Sink { + input in: char[12] + + reaction(in) {= + const char *expected = "Hello World"; + for (int i = 0; i < 12; i++) { + printf("%c", in->value[i]); + validate(in->value[i] == expected[i]); + } + printf("\n"); + =} +} + +main reactor { + src = new Src() + sink = new Sink() + src.out -> sink.in +} \ No newline at end of file From 643d4b35568e451ae78d7b8cfd6eb715bcf864d7 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Sun, 8 Dec 2024 00:09:35 -0800 Subject: [PATCH 2/4] Formatter --- include/reactor-uc/macros.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/reactor-uc/macros.h b/include/reactor-uc/macros.h index e02ed1f5..c3e5784b 100644 --- a/include/reactor-uc/macros.h +++ b/include/reactor-uc/macros.h @@ -12,10 +12,10 @@ } while (0) // Sets an output port, copies data and triggers all downstream reactions. -#define lf_set_array(port, array) \ +#define lf_set_array(port, array) \ do { \ Port *_port = (Port *)(port); \ - _port->set(_port, array); \ + _port->set(_port, array); \ } while (0) /** @@ -218,7 +218,7 @@ typedef struct { \ Port super; \ Reaction *sources[(SourceSize)]; \ - BufferType value; \ + BufferType value; \ } ReactorName##_##PortName; #define LF_DEFINE_OUTPUT_ARRAY_STRUCT(ReactorName, PortName, SourceSize, BufferType, ArrayLen) \ From 714c00cc3629a792f674fdf1e188595249fd7e26 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Sun, 8 Dec 2024 08:28:07 -0800 Subject: [PATCH 3/4] Refactor --- .../org/lflang/generator/uc/UcPortGenerator.kt | 15 ++++++++------- .../org/lflang/generator/uc/UcStateGenerator.kt | 12 ++++-------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt index 4cf5e187..a5fdc8e4 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt @@ -36,18 +36,19 @@ import org.lflang.lf.* class UcPortGenerator(private val reactor: Reactor, private val connections: UcConnectionGenerator) { val Port.external_args get(): String = "_${name}_external" - val Port.isArray - get(): Boolean = type.cStyleArraySpec != null - val Port.arrayLength - get(): Int = type.cStyleArraySpec.length companion object { val Port.width get(): Int = widthSpec?.getWidth()?:1 + val Type.isArray + get(): Boolean = cStyleArraySpec != null + val Type.arrayLength + get(): Int = cStyleArraySpec.length + } private fun generateSelfStruct(input: Input): String { - if (input.isArray) { + if (input.type.isArray) { return "LF_DEFINE_INPUT_ARRAY_STRUCT(${reactor.codeType}, ${input.name}, ${reactor.getEffects(input).size}, ${reactor.getObservers(input).size}, ${input.type.id}, ${input.arrayLength}, ${connections.getNumConnectionsFromPort(null, input as Port)});" } else { return "LF_DEFINE_INPUT_STRUCT(${reactor.codeType}, ${input.name}, ${reactor.getEffects(input).size}, ${reactor.getObservers(input).size}, ${input.type.toText()}, ${connections.getNumConnectionsFromPort(null, input as Port)});" @@ -55,8 +56,8 @@ class UcPortGenerator(private val reactor: Reactor, private val connections: UcC } private fun generateInputCtor(input: Input) = "LF_DEFINE_INPUT_CTOR(${reactor.codeType}, ${input.name}, ${reactor.getEffects(input).size}, ${reactor.getObservers(input).size}, ${input.type.toText()}, ${connections.getNumConnectionsFromPort(null, input as Port)});" private fun generateSelfStruct(output: Output): String { - if (output.isArray) { - return "LF_DEFINE_OUTPUT_ARRAY_STRUCT(${reactor.codeType}, ${output.name}, ${reactor.getSources(output).size}, ${output.type.id}, ${output.arrayLength});" + if (output.type.isArray) { + return "LF_DEFINE_OUTPUT_ARRAY_STRUCT(${reactor.codeType}, ${output.name}, ${reactor.getSources(output).size}, ${output.type.id}, ${output.type.arrayLength});" } else { return "LF_DEFINE_OUTPUT_STRUCT(${reactor.codeType}, ${output.name}, ${reactor.getSources(output).size}, ${output.type.toText()});" } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStateGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStateGenerator.kt index a8685d37..2773cd9d 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStateGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStateGenerator.kt @@ -1,18 +1,14 @@ package org.lflang.generator.uc import org.lflang.allStateVars +import org.lflang.generator.uc.UcPortGenerator.Companion.arrayLength +import org.lflang.generator.uc.UcPortGenerator.Companion.isArray import org.lflang.isInitialized import org.lflang.lf.Reactor import org.lflang.lf.StateVar import org.lflang.toText class UcStateGenerator(private val reactor: Reactor) { - private val StateVar.isArray - get(): Boolean = type.cStyleArraySpec != null - - private val StateVar.arrayLength - get(): Int = type.cStyleArraySpec.length - fun generateReactorStructFields() = reactor.allStateVars.joinToString(prefix = "// State variables \n", separator = "\n") { if (it.isArray) { @@ -24,8 +20,8 @@ class UcStateGenerator(private val reactor: Reactor) { fun generateInitializeStateVars() = reactor.allStateVars.filter{it.isInitialized}.joinToString(prefix = "// Initialize State variables \n", separator = "\n") { - if (it.isArray) { - """|${it.type.id} _${it.name}_init[${it.arrayLength}] = ${it.init.expr.toCCode()}; + if (it.type.isArray) { + """|${it.type.id} _${it.name}_init[${it.type.arrayLength}] = ${it.init.expr.toCCode()}; |memcpy(&self->${it.name}, &_${it.name}_init, sizeof(_${it.name}_init)); """.trimMargin() } else { From 0f26b2e320a9a23e98c1c8a78810ddf6f61090ae Mon Sep 17 00:00:00 2001 From: erlingrj Date: Sun, 8 Dec 2024 09:08:25 -0800 Subject: [PATCH 4/4] Typo --- .../main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt | 2 +- .../main/kotlin/org/lflang/generator/uc/UcStateGenerator.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt index a5fdc8e4..5824313b 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt @@ -49,7 +49,7 @@ class UcPortGenerator(private val reactor: Reactor, private val connections: UcC private fun generateSelfStruct(input: Input): String { if (input.type.isArray) { - return "LF_DEFINE_INPUT_ARRAY_STRUCT(${reactor.codeType}, ${input.name}, ${reactor.getEffects(input).size}, ${reactor.getObservers(input).size}, ${input.type.id}, ${input.arrayLength}, ${connections.getNumConnectionsFromPort(null, input as Port)});" + return "LF_DEFINE_INPUT_ARRAY_STRUCT(${reactor.codeType}, ${input.name}, ${reactor.getEffects(input).size}, ${reactor.getObservers(input).size}, ${input.type.id}, ${input.type.arrayLength}, ${connections.getNumConnectionsFromPort(null, input as Port)});" } else { return "LF_DEFINE_INPUT_STRUCT(${reactor.codeType}, ${input.name}, ${reactor.getEffects(input).size}, ${reactor.getObservers(input).size}, ${input.type.toText()}, ${connections.getNumConnectionsFromPort(null, input as Port)});" } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStateGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStateGenerator.kt index 2773cd9d..95ee58e4 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStateGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStateGenerator.kt @@ -11,8 +11,8 @@ import org.lflang.toText class UcStateGenerator(private val reactor: Reactor) { fun generateReactorStructFields() = reactor.allStateVars.joinToString(prefix = "// State variables \n", separator = "\n") { - if (it.isArray) { - "${it.type.id} ${it.name}[${it.arrayLength}];" + if (it.type.isArray) { + "${it.type.id} ${it.name}[${it.type.arrayLength}];" } else { "${it.type.toText()} ${it.name};" }