From c8639d4f17f21bb78230dbe04d8e9ae5ae5f2a13 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Thu, 3 Feb 2022 16:46:56 -0800 Subject: [PATCH 01/64] Add a simple multiport test MultiportToPort.lf for TypeScript, to be passed when the output multiport is implemented. --- .../src/multiport/MultiportToPort.lf | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 test/TypeScript/src/multiport/MultiportToPort.lf diff --git a/test/TypeScript/src/multiport/MultiportToPort.lf b/test/TypeScript/src/multiport/MultiportToPort.lf new file mode 100644 index 0000000000..9c570147fc --- /dev/null +++ b/test/TypeScript/src/multiport/MultiportToPort.lf @@ -0,0 +1,40 @@ +// Check multiport output to multiport input. +// Destination port is wider than sending port. +target TypeScript { + timeout: 2 sec +}; +reactor Source { + output[2] out:number; + reaction(startup) -> out {= + //for(int i = 0; i < out.size(); i++) { + // std::cout << "Source sending " << i << ".\n"; + console.log("Source sending " + 0); + // out[i].set(i); + //} + =} +} +reactor Destination(expected:number(0)) { + input inp:number; + state received:boolean(false); + reaction(inp) {= + console.log("Received " + inp); + received = true; + if (inp != expected) { + util.requestErrorStop("FAILURE: Expected " + expected); + } + =} + reaction(shutdown) {= + if (!received) { + util.requestErrorStop("ERROR: Destination received no input!"); + } + console.log("Received " + inp); + console.log("Success."); + =} +} + +main reactor MultiportToPort { + a = new Source(); + b1 = new Destination(); + b2 = new Destination(expected = 1); + a.out -> b1.inp, b2.inp; +} \ No newline at end of file From aabe437aef81b222dbdc85eddad80220ac21c927 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Sat, 5 Feb 2022 18:44:18 -0800 Subject: [PATCH 02/64] Add TS as a target that supports multiports, fix error in MultiportToPort.lf test, and fix the checks in TSConnectionGenerator.kt --- org.lflang/src/org/lflang/Target.java | 1 + org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt | 2 +- test/TypeScript/src/multiport/MultiportToPort.lf | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/org.lflang/src/org/lflang/Target.java b/org.lflang/src/org/lflang/Target.java index 13d0e2b11e..9354b59635 100644 --- a/org.lflang/src/org/lflang/Target.java +++ b/org.lflang/src/org/lflang/Target.java @@ -473,6 +473,7 @@ public boolean supportsMultiports() { case CPP: case Python: case Rust: + case TS: return true; } return false; diff --git a/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt index 4a5e04300a..c14ec89bcc 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt @@ -28,7 +28,7 @@ class TSConnectionGenerator ( leftPortName += connection.leftPorts[0].variable.name } var rightPortName = "" - if (connection.leftPorts.size > 1) { + if (connection.rightPorts.size > 1) { errorReporter.reportError(connection, "Multiports are not yet supported in the TypeScript target.") } else { if (connection.rightPorts[0].container != null) { diff --git a/test/TypeScript/src/multiport/MultiportToPort.lf b/test/TypeScript/src/multiport/MultiportToPort.lf index 9c570147fc..480dbc7af7 100644 --- a/test/TypeScript/src/multiport/MultiportToPort.lf +++ b/test/TypeScript/src/multiport/MultiportToPort.lf @@ -27,7 +27,6 @@ reactor Destination(expected:number(0)) { if (!received) { util.requestErrorStop("ERROR: Destination received no input!"); } - console.log("Received " + inp); console.log("Success."); =} } From 8c703593f71368f14854c6a5926ac433fcee1bf0 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Sat, 5 Feb 2022 21:43:08 -0800 Subject: [PATCH 03/64] Update reactor-ts version to use _connectMultiplePorts and extended declaration of Variable to use multiports. --- org.lflang/src/lib/ts/reactor-ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/ts/reactor-ts b/org.lflang/src/lib/ts/reactor-ts index e4e1aca013..d10b50332b 160000 --- a/org.lflang/src/lib/ts/reactor-ts +++ b/org.lflang/src/lib/ts/reactor-ts @@ -1 +1 @@ -Subproject commit e4e1aca01335bd5220c90522ceec46031b9f92b5 +Subproject commit d10b50332b6532696be35e9fce0d6cc22a7d80ea From 913c67e8605344693ed1be6a9b4210419506c0a4 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Sat, 5 Feb 2022 21:46:16 -0800 Subject: [PATCH 04/64] Add code generation for multiport in TypeScript. --- .../generator/ts/TSConnectionGenerator.kt | 54 ++++++++++++++----- .../lflang/generator/ts/TSPortGenerator.kt | 31 +++++++++-- .../generator/ts/TSReactionGenerator.kt | 43 ++++++++++++--- 3 files changed, 105 insertions(+), 23 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt index c14ec89bcc..a6c96bbc30 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt @@ -1,7 +1,12 @@ package org.lflang.generator.ts import org.lflang.ErrorReporter +import org.lflang.getWidth +import org.lflang.hasMultipleConnections +import org.lflang.isMultiport import org.lflang.lf.Connection +import org.lflang.lf.Port +import org.lflang.lf.VarRef import java.util.* /** @@ -13,31 +18,56 @@ class TSConnectionGenerator ( private val errorReporter: ErrorReporter ) { // There is no generateClassProperties() for connections + + private fun getPortName(port: VarRef): String { + var portName = "" + if (port.container != null) { + portName += port.container.name + "." + } + portName += port.variable.name + return portName + } + + private fun getPortNames(ports: List): List { + var portNames = LinkedList() + for (varRef in ports) { + val port = varRef.variable as Port + if (port.isMultiport) { + repeat(port.widthSpec.getWidth()) { index -> + portNames.add("this.${getPortName(varRef)}[$index]") + } + } else { + portNames.add("this.${getPortName(varRef)}") + } + } + return portNames + } fun generateInstantiations(): String { val connectionInstantiations = LinkedList() for (connection in connections) { - var leftPortName = "" - // FIXME: Add support for multiports. - if (connection.leftPorts.size > 1) { - errorReporter.reportError(connection, "Multiports are not yet supported in the TypeScript target.") + if (connection.hasMultipleConnections) { + var leftPortNames = getPortNames(connection.leftPorts) + var rightPortNames = getPortNames(connection.rightPorts) + val leftPortName = leftPortNames.joinToString(prefix = "[", separator = ", ", postfix = "]") + val rightPortName = rightPortNames.joinToString(prefix = "[", separator = ", ", postfix = "]") + connectionInstantiations.add("this._connectMultiplePorts($leftPortName, $rightPortName, ${connection.isIterated});") } else { + var leftPortName = "" + if (connection.leftPorts[0].container != null) { leftPortName += connection.leftPorts[0].container.name + "." } leftPortName += connection.leftPorts[0].variable.name - } - var rightPortName = "" - if (connection.rightPorts.size > 1) { - errorReporter.reportError(connection, "Multiports are not yet supported in the TypeScript target.") - } else { + var rightPortName = "" + if (connection.rightPorts[0].container != null) { rightPortName += connection.rightPorts[0].container.name + "." } rightPortName += connection.rightPorts[0].variable.name - } - if (leftPortName != "" && rightPortName != "") { - connectionInstantiations.add("this._connect(this.$leftPortName, this.$rightPortName);") + if (leftPortName != "" && rightPortName != "") { + connectionInstantiations.add("this._connect(this.$leftPortName, this.$rightPortName);") + } } } return connectionInstantiations.joinToString("\n") diff --git a/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt index 35bcfa42df..b7ff9d122d 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt @@ -1,5 +1,7 @@ package org.lflang.generator.ts +import org.lflang.getWidth +import org.lflang.isMultiport import org.lflang.lf.Input import org.lflang.lf.Output import org.lflang.lf.Port @@ -35,10 +37,19 @@ class TSPortGenerator ( fun generateClassProperties(): String { val portClassProperties = LinkedList() for (input in inputs) { - portClassProperties.add("${input.name}: __InPort<${getPortType(input)}>;") + if (input.isMultiport) { + portClassProperties.add("${input.name}: Array<__InPort<${getPortType(input)}>>;") + } else { + portClassProperties.add("${input.name}: __InPort<${getPortType(input)}>;") + } } for (output in outputs) { - portClassProperties.add("${output.name}: __OutPort<${getPortType(output)}>;") + if (output.isMultiport) { + portClassProperties.add("${output.name}: Array<__OutPort<${getPortType(output)}>>;") + portClassProperties.add("__rw__${output.name}: Array>;") + } else { + portClassProperties.add("${output.name}: __OutPort<${getPortType(output)}>;") + } } return portClassProperties.joinToString("\n") } @@ -46,10 +57,22 @@ class TSPortGenerator ( fun generateInstantiations(): String { val porInstantiations = LinkedList() for (input in inputs) { - porInstantiations.add("this.${input.name} = new __InPort<${getPortType(input)}>(this);") + if (input.isMultiport) { + porInstantiations.add("this.${input.name} = [];") + porInstantiations.add("for (let i = 0; i< ${input.widthSpec.getWidth()}; i++) this.${input.name}.push(new __InPort<${getPortType(input)}>(this));") + } else { + porInstantiations.add("this.${input.name} = new __InPort<${getPortType(input)}>(this);") + } } for (output in outputs) { - porInstantiations.add("this.${output.name} = new __OutPort<${getPortType(output)}>(this);") + if (output.isMultiport) { + porInstantiations.add("this.${output.name} = [];") + porInstantiations.add("for (let i = 0; i< ${output.widthSpec.getWidth()}; i++) this.${output.name}.push(new __OutPort<${getPortType(output)}>(this));") + porInstantiations.add("this.__rw__${output.name} = [];") + porInstantiations.add("this.${output.name}.forEach(element => { this.__rw__${output.name}.push(this.writable(element)) });") + } else { + porInstantiations.add("this.${output.name} = this.writable(new __OutPort<${getPortType(output)}>(this));") + } } return porInstantiations.joinToString("\n") } diff --git a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt index e82f5693e7..c6d3a8e3a7 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt @@ -4,6 +4,7 @@ import org.lflang.ErrorReporter import org.lflang.JavaAstUtils import org.lflang.federated.FederateInstance import org.lflang.generator.PrependOperator +import org.lflang.isMultiport import org.lflang.lf.* import org.lflang.lf.Timer import org.lflang.toText @@ -257,12 +258,30 @@ class TSReactionGenerator( reactSignatureElement += ": Sched<" + getActionType(effect.variable as Action) + ">" schedActionSet.add(effect.variable as Action) } else if (effect.variable is Port){ - reactSignatureElement += ": ReadWrite<" + getPortType(effect.variable as Port) + ">" + val port = effect.variable as Port + if (port.isMultiport) { + reactSignatureElement += ": Array>" + } else { + reactSignatureElement += ": ReadWrite<" + getPortType(effect.variable as Port) + ">" + } if (effect.container == null) { - reactEpilogue.add(with(PrependOperator) {""" - |if (${effect.variable.name} !== undefined) { - | __${effect.variable.name}.set(${effect.variable.name}); - |}""".trimMargin()}) + if (port.isMultiport) { + reactEpilogue.add(with(PrependOperator) { + """ + |${port.name}.forEach((element, index) => { + | if (element !== undefined) { + | __${port.name}[index].set(element); + | } + |});""".trimMargin() + }) + } else { + reactEpilogue.add(with(PrependOperator) { + """ + |if (${port.name} !== undefined) { + | __${port.name}.set(${port.name}); + |}""".trimMargin() + }) + } } } @@ -272,11 +291,21 @@ class TSReactionGenerator( if (effect.variable is Action){ reactFunctArgs.add("this.schedulable($functArg)") } else if (effect.variable is Port) { - reactFunctArgs.add("this.writable($functArg)") + val port = effect.variable as Port + if (port.isMultiport) { + reactFunctArgs.add("this.__rw__" + effect.generateVarRef()) + } else { + reactFunctArgs.add("this.writable($functArg)") + } } if (effect.container == null) { - reactPrologue.add("let ${effect.variable.name} = __${effect.variable.name}.get();") + if (effect.variable is Port && (effect.variable as Port).isMultiport) { + val port = effect.variable as Port + reactPrologue.add("let ${port.name} = new Array<${getPortType(port)}>(__${port.name}.length);") + } else { + reactPrologue.add("let ${effect.variable.name} = __${effect.variable.name}.get();") + } } else { // Hierarchical references are handled later because there // could be references to other members of the same reactor. From 4fab0e4306bee1d398376c5dce9f379ca26de685 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Sat, 5 Feb 2022 21:47:27 -0800 Subject: [PATCH 05/64] Update multiport test to test writing values to output multiport. --- test/TypeScript/src/multiport/MultiportToPort.lf | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/test/TypeScript/src/multiport/MultiportToPort.lf b/test/TypeScript/src/multiport/MultiportToPort.lf index 480dbc7af7..5205b93d16 100644 --- a/test/TypeScript/src/multiport/MultiportToPort.lf +++ b/test/TypeScript/src/multiport/MultiportToPort.lf @@ -6,11 +6,10 @@ target TypeScript { reactor Source { output[2] out:number; reaction(startup) -> out {= - //for(int i = 0; i < out.size(); i++) { - // std::cout << "Source sending " << i << ".\n"; - console.log("Source sending " + 0); - // out[i].set(i); - //} + for(let i = 0; i < out.length; i++) { + console.log("Source sending " + i); + out[i] = i; + } =} } reactor Destination(expected:number(0)) { From 77e9f0dfccdcb3234edbc7b6138b79f01a0d87a7 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Sat, 5 Feb 2022 23:25:49 -0800 Subject: [PATCH 06/64] Fix error in non-multiport code generation. --- org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt index b7ff9d122d..3c320985f7 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt @@ -71,7 +71,7 @@ class TSPortGenerator ( porInstantiations.add("this.__rw__${output.name} = [];") porInstantiations.add("this.${output.name}.forEach(element => { this.__rw__${output.name}.push(this.writable(element)) });") } else { - porInstantiations.add("this.${output.name} = this.writable(new __OutPort<${getPortType(output)}>(this));") + porInstantiations.add("this.${output.name} = new __OutPort<${getPortType(output)}>(this);") } } return porInstantiations.joinToString("\n") From fdd48ec50745f647acf1c829359d9179159384c4 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Mon, 7 Feb 2022 19:08:58 -0800 Subject: [PATCH 07/64] Add MultiportToMultiport.lf test for TypeScript target and add support for input multiports. --- .../lflang/generator/ts/TSPortGenerator.kt | 2 +- .../generator/ts/TSReactionGenerator.kt | 33 ++++++++++------ .../src/multiport/MultiportToMultiport.lf | 38 +++++++++++++++++++ 3 files changed, 61 insertions(+), 12 deletions(-) create mode 100644 test/TypeScript/src/multiport/MultiportToMultiport.lf diff --git a/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt index 3c320985f7..48f4bee9f1 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt @@ -67,7 +67,7 @@ class TSPortGenerator ( for (output in outputs) { if (output.isMultiport) { porInstantiations.add("this.${output.name} = [];") - porInstantiations.add("for (let i = 0; i< ${output.widthSpec.getWidth()}; i++) this.${output.name}.push(new __OutPort<${getPortType(output)}>(this));") + porInstantiations.add("for (let i = 0; i < ${output.widthSpec.getWidth()}; i++) this.${output.name}.push(new __OutPort<${getPortType(output)}>(this));") porInstantiations.add("this.__rw__${output.name} = [];") porInstantiations.add("this.${output.name}.forEach(element => { this.__rw__${output.name}.push(this.writable(element)) });") } else { diff --git a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt index c6d3a8e3a7..00e9feb898 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt @@ -4,6 +4,7 @@ import org.lflang.ErrorReporter import org.lflang.JavaAstUtils import org.lflang.federated.FederateInstance import org.lflang.generator.PrependOperator +import org.lflang.getWidth import org.lflang.isMultiport import org.lflang.lf.* import org.lflang.lf.Timer @@ -228,23 +229,33 @@ class TSReactionGenerator( reactSignatureElementType = getPortType(trigOrSource.variable as Port) } - reactSignature.add("${generateArg(trigOrSource)}: Read<${reactSignatureElementType}>") + if (trigOrSource.variable is Port && (trigOrSource.variable as Port).isMultiport) { + reactSignature.add("${generateArg(trigOrSource)}: Array>") + } else { + reactSignature.add("${generateArg(trigOrSource)}: Read<${reactSignatureElementType}>") + } reactFunctArgs.add("this.${trigOrSource.generateVarRef()}") if (trigOrSource.container == null) { - reactPrologue.add("let ${trigOrSource.variable.name} = ${generateArg(trigOrSource)}.get();") + if (trigOrSource.variable is Port && (trigOrSource.variable as Port).isMultiport) { + val inputPort = trigOrSource.variable as Port + reactPrologue.add("let ${inputPort.name} = [];") + reactPrologue.add("for (let i = 0; i < ${inputPort.widthSpec.getWidth()}; i++) ${inputPort.name}.push(${generateArg(trigOrSource)}[i].get());") + } else { + reactPrologue.add("let ${trigOrSource.variable.name} = ${generateArg(trigOrSource)}.get();") + } } else { var args = containerToArgs.get(trigOrSource.container) if (args == null) { // Create the HashSet for the container // and handle it later. - args = HashSet(); + args = HashSet() containerToArgs.put(trigOrSource.container, args) } args.add(trigOrSource.variable) } } } - val schedActionSet = HashSet(); + val schedActionSet = HashSet() // The epilogue to the react function writes local // state variables back to the state @@ -258,27 +269,27 @@ class TSReactionGenerator( reactSignatureElement += ": Sched<" + getActionType(effect.variable as Action) + ">" schedActionSet.add(effect.variable as Action) } else if (effect.variable is Port){ - val port = effect.variable as Port - if (port.isMultiport) { + val outputPort = effect.variable as Port + if (outputPort.isMultiport) { reactSignatureElement += ": Array>" } else { reactSignatureElement += ": ReadWrite<" + getPortType(effect.variable as Port) + ">" } if (effect.container == null) { - if (port.isMultiport) { + if (outputPort.isMultiport) { reactEpilogue.add(with(PrependOperator) { """ - |${port.name}.forEach((element, index) => { + |${outputPort.name}.forEach((element, index) => { | if (element !== undefined) { - | __${port.name}[index].set(element); + | __${outputPort.name}[index].set(element); | } |});""".trimMargin() }) } else { reactEpilogue.add(with(PrependOperator) { """ - |if (${port.name} !== undefined) { - | __${port.name}.set(${port.name}); + |if (${outputPort.name} !== undefined) { + | __${outputPort.name}.set(${outputPort.name}); |}""".trimMargin() }) } diff --git a/test/TypeScript/src/multiport/MultiportToMultiport.lf b/test/TypeScript/src/multiport/MultiportToMultiport.lf new file mode 100644 index 0000000000..571a2fc331 --- /dev/null +++ b/test/TypeScript/src/multiport/MultiportToMultiport.lf @@ -0,0 +1,38 @@ +target TypeScript; + +reactor Source { + output[4] out:number; + + reaction (startup) -> out {= + for (let i = 0; i < out.length; i++) { + out[i] = i; + } + =} +} + +reactor Sink { + input[4] inp:number; + state received:boolean(false); + + reaction (inp) {= + for (let i = 0; i < inp.length; i++) { + console.log("Received " + inp[i]); + received = true; + if (inp[i] != i) { + util.requestErrorStop("FAILURE: Expected " + i + "!"); + } + } + =} + reaction (shutdown) {= + if (!received) { + util.requestErrorStop("ERROR: No data received!!"); + } + console.log("Success."); + =} +} + +main reactor MultiportToMultiport { + source = new Source(); + sink = new Sink(); + source.out -> sink.inp; +} \ No newline at end of file From bf6ac2676511023a87d649ab2ac6b8197c054786 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Mon, 7 Feb 2022 19:25:11 -0800 Subject: [PATCH 08/64] Make for loops and forEach in the generated TypeScript code "multi-line" to improve readability of the generated code. --- .../org/lflang/generator/ts/TSPortGenerator.kt | 15 ++++++++++++--- .../lflang/generator/ts/TSReactionGenerator.kt | 5 ++++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt index 48f4bee9f1..26d65189f8 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt @@ -59,7 +59,10 @@ class TSPortGenerator ( for (input in inputs) { if (input.isMultiport) { porInstantiations.add("this.${input.name} = [];") - porInstantiations.add("for (let i = 0; i< ${input.widthSpec.getWidth()}; i++) this.${input.name}.push(new __InPort<${getPortType(input)}>(this));") + porInstantiations.add(""" + |for (let i = 0; i< ${input.widthSpec.getWidth()}; i++) { + | this.${input.name}.push(new __InPort<${getPortType(input)}>(this)); + |}""".trimMargin()) } else { porInstantiations.add("this.${input.name} = new __InPort<${getPortType(input)}>(this);") } @@ -67,9 +70,15 @@ class TSPortGenerator ( for (output in outputs) { if (output.isMultiport) { porInstantiations.add("this.${output.name} = [];") - porInstantiations.add("for (let i = 0; i < ${output.widthSpec.getWidth()}; i++) this.${output.name}.push(new __OutPort<${getPortType(output)}>(this));") + porInstantiations.add(""" + |for (let i = 0; i < ${output.widthSpec.getWidth()}; i++) { + | this.${output.name}.push(new __OutPort<${getPortType(output)}>(this)); + |}""".trimMargin()) porInstantiations.add("this.__rw__${output.name} = [];") - porInstantiations.add("this.${output.name}.forEach(element => { this.__rw__${output.name}.push(this.writable(element)) });") + porInstantiations.add(""" + |this.${output.name}.forEach(element => { + | this.__rw__${output.name}.push(this.writable(element)) + |});""".trimMargin()) } else { porInstantiations.add("this.${output.name} = new __OutPort<${getPortType(output)}>(this);") } diff --git a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt index 00e9feb898..ba3b37fa0b 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt @@ -239,7 +239,10 @@ class TSReactionGenerator( if (trigOrSource.variable is Port && (trigOrSource.variable as Port).isMultiport) { val inputPort = trigOrSource.variable as Port reactPrologue.add("let ${inputPort.name} = [];") - reactPrologue.add("for (let i = 0; i < ${inputPort.widthSpec.getWidth()}; i++) ${inputPort.name}.push(${generateArg(trigOrSource)}[i].get());") + reactPrologue.add(""" + |for (let i = 0; i < ${inputPort.widthSpec.getWidth()}; i++) { + | ${inputPort.name}.push(${generateArg(trigOrSource)}[i].get()); + |}""".trimMargin()) } else { reactPrologue.add("let ${trigOrSource.variable.name} = ${generateArg(trigOrSource)}.get();") } From e86bc886350644baeab0b9f4077a581ac49e09f2 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Mon, 7 Feb 2022 21:28:24 -0800 Subject: [PATCH 09/64] Add multiport check at Variable level and simplify the multiport check code in TypeScript code generation. --- org.lflang/src/org/lflang/AstExtensions.kt | 5 +++++ .../src/org/lflang/generator/ts/TSReactionGenerator.kt | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/org.lflang/src/org/lflang/AstExtensions.kt b/org.lflang/src/org/lflang/AstExtensions.kt index 9821e36fa6..b2250d98f4 100644 --- a/org.lflang/src/org/lflang/AstExtensions.kt +++ b/org.lflang/src/org/lflang/AstExtensions.kt @@ -373,6 +373,11 @@ val Action.isPhysical get() = this.origin == ActionOrigin.PHYSICAL */ val Port.isMultiport get() = JavaAstUtils.isMultiport(this) +/** + * Return true if the receiving is a port and a multiport. + */ +val Variable.isMultiport get() = (this is Port) && this.isMultiport + /** Get the reactor that is instantiated in the receiving instantiation. */ val Instantiation.reactor get() = this.reactorClass.toDefinition() diff --git a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt index ba3b37fa0b..0683102ace 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt @@ -229,14 +229,14 @@ class TSReactionGenerator( reactSignatureElementType = getPortType(trigOrSource.variable as Port) } - if (trigOrSource.variable is Port && (trigOrSource.variable as Port).isMultiport) { + if (trigOrSource.variable.isMultiport) { reactSignature.add("${generateArg(trigOrSource)}: Array>") } else { reactSignature.add("${generateArg(trigOrSource)}: Read<${reactSignatureElementType}>") } reactFunctArgs.add("this.${trigOrSource.generateVarRef()}") if (trigOrSource.container == null) { - if (trigOrSource.variable is Port && (trigOrSource.variable as Port).isMultiport) { + if (trigOrSource.variable.isMultiport) { val inputPort = trigOrSource.variable as Port reactPrologue.add("let ${inputPort.name} = [];") reactPrologue.add(""" @@ -314,7 +314,7 @@ class TSReactionGenerator( } if (effect.container == null) { - if (effect.variable is Port && (effect.variable as Port).isMultiport) { + if (effect.variable.isMultiport) { val port = effect.variable as Port reactPrologue.add("let ${port.name} = new Array<${getPortType(port)}>(__${port.name}.length);") } else { From ca3566ab790528f16be4a6c33d25ca68c5adb65a Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Mon, 7 Feb 2022 21:55:18 -0800 Subject: [PATCH 10/64] Add multiport test MultiportIn.lf for TypeScript. --- test/TypeScript/src/multiport/MultiportIn.lf | 60 ++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 test/TypeScript/src/multiport/MultiportIn.lf diff --git a/test/TypeScript/src/multiport/MultiportIn.lf b/test/TypeScript/src/multiport/MultiportIn.lf new file mode 100644 index 0000000000..95bffd7bab --- /dev/null +++ b/test/TypeScript/src/multiport/MultiportIn.lf @@ -0,0 +1,60 @@ +// This is a version of the test that uses a multiport input at the destination. +// Its purpose is to test multiport inputs. +target TypeScript { + timeout: 2 sec +}; + +reactor Source { + timer t(0, 200 msec); + output out:number; + state s:number(0); + reaction(t) -> out {= + out = s++; + =} +} +reactor Computation { + input inp:number; + output out:number; + reaction(inp) -> out {= + out = inp; + =} +} +reactor Destination { + state s:number(0); + input[4] inp:number; + reaction(inp) {= + let sum = 0; + for (let i = 0; i < inp.length; i++) { + if (inp[i] === undefined) { + util.requestErrorStop("ERROR: input at [" + i + "] is missing."); + } else { + sum += inp[i]!; + } + } + console.log("Sum of received: " + sum + "."); + if (sum != s) { + util.requestErrorStop("ERROR: Expected " + s + "."); + } + s += 4; + =} + reaction(shutdown) {= + if (s == 0) { + util.requestErrorStop("ERROR: Destination received no input!"); + } + console.log("Success."); + =} +} + +main reactor MultiportIn { + a = new Source(); + t1 = new Computation(); + t2 = new Computation(); + t3 = new Computation(); + t4 = new Computation(); + b = new Destination(); + a.out -> t1.inp; + a.out -> t2.inp; + a.out -> t3.inp; + a.out -> t4.inp; + t1.out, t2.out, t3.out, t4.out -> b.inp; +} \ No newline at end of file From 6d3381f4ff85cf43c838b757c9b617494b30969b Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Mon, 7 Feb 2022 22:39:12 -0800 Subject: [PATCH 11/64] Initialize multiport (an array of ports) with the initial size, instead of using = [] and push() to the array. --- .../lflang/generator/ts/TSPortGenerator.kt | 21 +++++++++++-------- .../generator/ts/TSReactionGenerator.kt | 7 ++++--- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt index 26d65189f8..2d67a8c8a4 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt @@ -58,10 +58,11 @@ class TSPortGenerator ( val porInstantiations = LinkedList() for (input in inputs) { if (input.isMultiport) { - porInstantiations.add("this.${input.name} = [];") + porInstantiations.add( + "this.${input.name} = new Array<__InPort<${getPortType(input)}>>(${input.widthSpec.getWidth()});") porInstantiations.add(""" - |for (let i = 0; i< ${input.widthSpec.getWidth()}; i++) { - | this.${input.name}.push(new __InPort<${getPortType(input)}>(this)); + |for (let i = 0; i< this.${input.name}.length; i++) { + | this.${input.name}[i] = new __InPort<${getPortType(input)}>(this); |}""".trimMargin()) } else { porInstantiations.add("this.${input.name} = new __InPort<${getPortType(input)}>(this);") @@ -69,15 +70,17 @@ class TSPortGenerator ( } for (output in outputs) { if (output.isMultiport) { - porInstantiations.add("this.${output.name} = [];") + porInstantiations.add( + "this.${output.name} = new Array<__OutPort<${getPortType(output)}>>(${output.widthSpec.getWidth()});") porInstantiations.add(""" - |for (let i = 0; i < ${output.widthSpec.getWidth()}; i++) { - | this.${output.name}.push(new __OutPort<${getPortType(output)}>(this)); + |for (let i = 0; i < this.${output.name}.length; i++) { + | this.${output.name}[i] = new __OutPort<${getPortType(output)}>(this); |}""".trimMargin()) - porInstantiations.add("this.__rw__${output.name} = [];") + porInstantiations.add( + "this.__rw__${output.name} = new Array>(${output.widthSpec.getWidth()});") porInstantiations.add(""" - |this.${output.name}.forEach(element => { - | this.__rw__${output.name}.push(this.writable(element)) + |this.${output.name}.forEach((element, i) => { + | this.__rw__${output.name}[i] = this.writable(element) |});""".trimMargin()) } else { porInstantiations.add("this.${output.name} = new __OutPort<${getPortType(output)}>(this);") diff --git a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt index 0683102ace..59aac8f360 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt @@ -238,10 +238,11 @@ class TSReactionGenerator( if (trigOrSource.container == null) { if (trigOrSource.variable.isMultiport) { val inputPort = trigOrSource.variable as Port - reactPrologue.add("let ${inputPort.name} = [];") + reactPrologue.add( + "let ${inputPort.name} = new Array<${reactSignatureElementType} | undefined>(${inputPort.widthSpec.getWidth()});") reactPrologue.add(""" - |for (let i = 0; i < ${inputPort.widthSpec.getWidth()}; i++) { - | ${inputPort.name}.push(${generateArg(trigOrSource)}[i].get()); + |for (let i = 0; i < ${inputPort.name}.length; i++) { + | ${inputPort.name}[i] = ${generateArg(trigOrSource)}[i].get(); |}""".trimMargin()) } else { reactPrologue.add("let ${trigOrSource.variable.name} = ${generateArg(trigOrSource)}.get();") From 786476bae48ffd3bd12bd8d63b6f83bc460acd9a Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Mon, 7 Feb 2022 22:39:45 -0800 Subject: [PATCH 12/64] Add multiport test MultiportOut.lf for TypeScript. --- test/TypeScript/src/multiport/MultiportOut.lf | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 test/TypeScript/src/multiport/MultiportOut.lf diff --git a/test/TypeScript/src/multiport/MultiportOut.lf b/test/TypeScript/src/multiport/MultiportOut.lf new file mode 100644 index 0000000000..80e03e7cce --- /dev/null +++ b/test/TypeScript/src/multiport/MultiportOut.lf @@ -0,0 +1,55 @@ +// Check multiport capabilities on Outputs. +target TypeScript { + timeout: 2 sec, +}; +reactor Source { + timer t(0, 200 msec); + output[4] out:number; + state s:number(0); + reaction(t) -> out {= + for(let i = 0; i < 4; i++) { + out[i] = s; + } + s++; + =} +} +reactor Computation { + input inp:number; + output out:number; + reaction(inp) -> out {= + // No need to sleep for this test. + out = inp; + =} +} +reactor Destination { + state s:number(0); + input[4] inp:number; + reaction(inp) {= + let sum = 0; + for (let i = 0; i < inp.length; i++) { + if (inp[i] !== undefined) sum += inp[i]!; + } + console.log("Sum of received: " + sum + "."); + if (sum != s) { + util.requestErrorStop("ERROR: Expected " + s + "."); + } + s += 4; + =} + reaction(shutdown) {= + if (s == 0) { + util.requestErrorStop("ERROR: Destination received no input!"); + } + console.log("Success."); + =} +} + +main reactor { + a = new Source(); + t1 = new Computation(); + t2 = new Computation(); + t3 = new Computation(); + t4 = new Computation(); + b = new Destination(); + a.out -> t1.inp, t2.inp, t3.inp, t4.inp; + t1.out, t2.out, t3.out, t4.out -> b.inp; +} From 54dad15bf6b2a787c8b76f36f8c006c4a9a9ee0e Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Tue, 8 Feb 2022 00:52:16 -0800 Subject: [PATCH 13/64] Use spread operator (...) to expand an array of ports instead of hard-coding the multiport indices to allow flexible handling of multiport connection. --- .../src/org/lflang/generator/ts/TSConnectionGenerator.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt index a6c96bbc30..d4f415498d 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt @@ -33,9 +33,8 @@ class TSConnectionGenerator ( for (varRef in ports) { val port = varRef.variable as Port if (port.isMultiport) { - repeat(port.widthSpec.getWidth()) { index -> - portNames.add("this.${getPortName(varRef)}[$index]") - } + // Use spread operator (...) to expand an array of ports. + portNames.add("...this.${getPortName(varRef)}") } else { portNames.add("this.${getPortName(varRef)}") } From 23da4c18e943120a877f698db2d958b807741301 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Tue, 8 Feb 2022 10:17:17 -0800 Subject: [PATCH 14/64] Add support for parameterized width for multiports. Add and use WidthSpec.toTSCode() for generating code for multiport width. --- org.lflang/src/org/lflang/Target.java | 1 + .../generator/ts/TSConnectionGenerator.kt | 1 - .../src/org/lflang/generator/ts/TSExtensions.kt | 17 +++++++++++++++++ .../org/lflang/generator/ts/TSPortGenerator.kt | 7 +++---- .../lflang/generator/ts/TSReactionGenerator.kt | 3 +-- 5 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 org.lflang/src/org/lflang/generator/ts/TSExtensions.kt diff --git a/org.lflang/src/org/lflang/Target.java b/org.lflang/src/org/lflang/Target.java index 9354b59635..84a2711311 100644 --- a/org.lflang/src/org/lflang/Target.java +++ b/org.lflang/src/org/lflang/Target.java @@ -492,6 +492,7 @@ public boolean supportsParameterizedWidths() { case CPP: case Python: case Rust: + case TS: return true; } return false; diff --git a/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt index d4f415498d..5068742f29 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt @@ -1,7 +1,6 @@ package org.lflang.generator.ts import org.lflang.ErrorReporter -import org.lflang.getWidth import org.lflang.hasMultipleConnections import org.lflang.isMultiport import org.lflang.lf.Connection diff --git a/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt b/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt new file mode 100644 index 0000000000..100d44a862 --- /dev/null +++ b/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt @@ -0,0 +1,17 @@ +package org.lflang.generator.ts + +import org.lflang.lf.WidthSpec +import org.lflang.toText + +/** + * The following definition provide extension that are useful for TypeScript target. + * + * @author {Hokeun Kim } + */ +fun WidthSpec.toTSCode(): String = terms.joinToString(" + ") { + when { + it.parameter != null -> "${it.parameter.name}" + it.code != null -> it.code.toText() + else -> it.width.toString() + } +} \ 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 2d67a8c8a4..f30074a3d0 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt @@ -1,6 +1,5 @@ package org.lflang.generator.ts -import org.lflang.getWidth import org.lflang.isMultiport import org.lflang.lf.Input import org.lflang.lf.Output @@ -59,7 +58,7 @@ class TSPortGenerator ( for (input in inputs) { if (input.isMultiport) { porInstantiations.add( - "this.${input.name} = new Array<__InPort<${getPortType(input)}>>(${input.widthSpec.getWidth()});") + "this.${input.name} = new Array<__InPort<${getPortType(input)}>>(${input.widthSpec.toTSCode()});") porInstantiations.add(""" |for (let i = 0; i< this.${input.name}.length; i++) { | this.${input.name}[i] = new __InPort<${getPortType(input)}>(this); @@ -71,13 +70,13 @@ class TSPortGenerator ( for (output in outputs) { if (output.isMultiport) { porInstantiations.add( - "this.${output.name} = new Array<__OutPort<${getPortType(output)}>>(${output.widthSpec.getWidth()});") + "this.${output.name} = new Array<__OutPort<${getPortType(output)}>>(${output.widthSpec.toTSCode()});") porInstantiations.add(""" |for (let i = 0; i < this.${output.name}.length; i++) { | this.${output.name}[i] = new __OutPort<${getPortType(output)}>(this); |}""".trimMargin()) porInstantiations.add( - "this.__rw__${output.name} = new Array>(${output.widthSpec.getWidth()});") + "this.__rw__${output.name} = new Array>(this.${output.name}.length);") porInstantiations.add(""" |this.${output.name}.forEach((element, i) => { | this.__rw__${output.name}[i] = this.writable(element) diff --git a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt index 59aac8f360..87c62cd05d 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt @@ -4,7 +4,6 @@ import org.lflang.ErrorReporter import org.lflang.JavaAstUtils import org.lflang.federated.FederateInstance import org.lflang.generator.PrependOperator -import org.lflang.getWidth import org.lflang.isMultiport import org.lflang.lf.* import org.lflang.lf.Timer @@ -239,7 +238,7 @@ class TSReactionGenerator( if (trigOrSource.variable.isMultiport) { val inputPort = trigOrSource.variable as Port reactPrologue.add( - "let ${inputPort.name} = new Array<${reactSignatureElementType} | undefined>(${inputPort.widthSpec.getWidth()});") + "let ${inputPort.name} = new Array<${reactSignatureElementType} | undefined>(${generateArg(trigOrSource)}.length);") reactPrologue.add(""" |for (let i = 0; i < ${inputPort.name}.length; i++) { | ${inputPort.name}[i] = ${generateArg(trigOrSource)}[i].get(); From 8f091bbd6cd8cd90c668d1433642523b9141e594 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Tue, 8 Feb 2022 10:25:16 -0800 Subject: [PATCH 15/64] Add multiport test MultiportToMultiport2.lf for TypeScript to verify the parameterized width for multiports. --- .../src/multiport/MultiportToMultiport2.lf | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 test/TypeScript/src/multiport/MultiportToMultiport2.lf diff --git a/test/TypeScript/src/multiport/MultiportToMultiport2.lf b/test/TypeScript/src/multiport/MultiportToMultiport2.lf new file mode 100644 index 0000000000..73b26bb81f --- /dev/null +++ b/test/TypeScript/src/multiport/MultiportToMultiport2.lf @@ -0,0 +1,36 @@ +// Test multiport to multiport connections. +// See also MultiportToMultiport. +target TypeScript; + +reactor Source(width:number(2)) { + output[width] out:number; + reaction (startup) -> out {= + for (let i = 0; i < out.length; i++) { + out[i] = i; + } + =} +} + +reactor Destination(width:number(2)) { + input[width] inp:number; + reaction (inp) {= + for (let i = 0; i < inp.length; i++) { + if (inp[i] !== undefined) { + let value = inp[i]; + console.log("Received on channel " + i + ": " + value); + // NOTE: For testing purposes, this assumes the specific + // widths instantiated below. + if (value != i % 3) { + util.requestErrorStop("ERROR: expected " + i % 3); + } + } + } + =} +} + +main reactor MultiportToMultiport2 { + a1 = new Source(width = 3); + a2 = new Source(width = 2); + b = new Destination(width = 5); + a1.out, a2.out -> b.inp; +} \ No newline at end of file From 4b97ff2b70e51c6d3d069b833fb0134850c66b01 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Wed, 9 Feb 2022 19:56:27 -0800 Subject: [PATCH 16/64] Add multiport test MultiportToHierarchy.lf for TypeScript to verify multiports when reactors are nested. --- .../src/multiport/MultiportToHierarchy.lf | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 test/TypeScript/src/multiport/MultiportToHierarchy.lf diff --git a/test/TypeScript/src/multiport/MultiportToHierarchy.lf b/test/TypeScript/src/multiport/MultiportToHierarchy.lf new file mode 100644 index 0000000000..91c3a49786 --- /dev/null +++ b/test/TypeScript/src/multiport/MultiportToHierarchy.lf @@ -0,0 +1,47 @@ + // Check multiport output to multiport input, where the latter is a hierarchical reactor. + // Note that the destination reactor has width wider than the sender, so one input is dangling. +target TypeScript { + timeout: 2 sec, +}; +reactor Source(width:number(4)) { + timer t(0, 200 msec); + output[width] out:number; + state s:number(0); + reaction(t) -> out {= + for(let i = 0; i < 4; i++) { + out[i] = s++; + } + =} +} +reactor Destination(width:number(4)) { + state s:number(6); + input[width] inp:number; + reaction(inp) {= + let sum = 0; + for (let i = 0; i < inp.length; i++) { + if (inp[i] !== undefined) sum += inp[i]!; + } + console.log("Sum of received: " + sum + "."); + if (sum != s) { + util.requestErrorStop("ERROR: Expected " + s + "."); + } + s += 16; + =} + reaction(shutdown) {= + if (s <= 6) { + util.requestErrorStop("ERROR: Destination received no input!"); + } + console.log("Success."); + =} +} +reactor Container(width:number(4)) { + input[width] inp:number; + dst = new Destination(); + inp -> dst.inp; +} + +main reactor MultiportToHierarchy(width:number(4)) { + a = new Source(width=width); + b = new Container(width=width); + a.out -> b.inp; +} \ No newline at end of file From be2645392767d7637531fa923356743788f7ddcd Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Wed, 9 Feb 2022 21:39:49 -0800 Subject: [PATCH 17/64] Add multiport test MultiportToMultiportArray.lf for TypeScript to verify multiports of arrays. --- .../multiport/MultiportToMultiportArray.lf | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 test/TypeScript/src/multiport/MultiportToMultiportArray.lf diff --git a/test/TypeScript/src/multiport/MultiportToMultiportArray.lf b/test/TypeScript/src/multiport/MultiportToMultiportArray.lf new file mode 100644 index 0000000000..37d47a9a45 --- /dev/null +++ b/test/TypeScript/src/multiport/MultiportToMultiportArray.lf @@ -0,0 +1,55 @@ +// Check multiport output to multiport input. +// Destination port is wider than sending port. +target TypeScript { + timeout: 2 sec +}; +reactor Source { + timer t(0, 200 msec); + output[2] out:{=Array=}; + state s:number(0); + reaction(t) -> out {= + for(let i = 0; i < 2; i++) { + // Dynamically allocate a new output array + let a = new Array(3); + // initialize it + a[0] = s++; + a[1] = s++; + a[2] = s++; + // and send it + out[i] = a; + } + =} +} + +reactor Destination { + state s:number(15); + input[2] inp:{=Array=}; + reaction(inp) {= + let sum = 0; + for (let i = 0; i < inp.length; i++) { + if (inp[i] !== undefined) { + const a = inp[i]!; + for (let j = 0; j < a.length; j++) { + sum += a[j]; + } + } + } + console.log("Sum of received: " + sum); + if (sum != s) { + util.requestErrorStop("ERROR: Expected " + s); + } + s += 36; + =} + reaction(shutdown) {= + if (s <= 15) { + util.requestErrorStop("ERROR: Destination received no input!"); + } + console.log("Success."); + =} +} + +main reactor MultiportToMultiportArray { + a = new Source(); + b = new Destination(); + a.out -> b.inp; +} From fef26a7f79a83669d507e74b673ffa26b2c6930e Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Thu, 10 Feb 2022 14:27:23 -0800 Subject: [PATCH 18/64] Update org.lflang/src/org/lflang/AstExtensions.kt Co-authored-by: Edward A. Lee --- org.lflang/src/org/lflang/AstExtensions.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/org/lflang/AstExtensions.kt b/org.lflang/src/org/lflang/AstExtensions.kt index b2250d98f4..cc5e96af85 100644 --- a/org.lflang/src/org/lflang/AstExtensions.kt +++ b/org.lflang/src/org/lflang/AstExtensions.kt @@ -374,7 +374,7 @@ val Action.isPhysical get() = this.origin == ActionOrigin.PHYSICAL val Port.isMultiport get() = JavaAstUtils.isMultiport(this) /** - * Return true if the receiving is a port and a multiport. + * Return true if the receiving Variable is a port and a multiport. */ val Variable.isMultiport get() = (this is Port) && this.isMultiport From 8d5b52c89dc876e7b752128617d7c92d3d1b6db8 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Fri, 11 Feb 2022 02:13:27 -0800 Subject: [PATCH 19/64] Use InMultiPort and OutMultiPort instead of arrays of ports, and simplify generated code for multiports in TypeScript. (Move the burden of multiport handling from code generator to reactor-ts). --- org.lflang/src/lib/ts/reactor-ts | 2 +- .../generator/ts/TSConnectionGenerator.kt | 9 ++----- .../generator/ts/TSImportPreambleGenerator.kt | 2 +- .../lflang/generator/ts/TSPortGenerator.kt | 23 ++++-------------- .../generator/ts/TSReactionGenerator.kt | 24 +++++++++---------- 5 files changed, 20 insertions(+), 40 deletions(-) diff --git a/org.lflang/src/lib/ts/reactor-ts b/org.lflang/src/lib/ts/reactor-ts index d10b50332b..45fce1b60e 160000 --- a/org.lflang/src/lib/ts/reactor-ts +++ b/org.lflang/src/lib/ts/reactor-ts @@ -1 +1 @@ -Subproject commit d10b50332b6532696be35e9fce0d6cc22a7d80ea +Subproject commit 45fce1b60ef65ff2ce648cd5c2182d7259a2cbfd diff --git a/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt index 5068742f29..dca740cec0 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt @@ -31,12 +31,7 @@ class TSConnectionGenerator ( var portNames = LinkedList() for (varRef in ports) { val port = varRef.variable as Port - if (port.isMultiport) { - // Use spread operator (...) to expand an array of ports. - portNames.add("...this.${getPortName(varRef)}") - } else { - portNames.add("this.${getPortName(varRef)}") - } + portNames.add("this.${getPortName(varRef)}") } return portNames } @@ -49,7 +44,7 @@ class TSConnectionGenerator ( var rightPortNames = getPortNames(connection.rightPorts) val leftPortName = leftPortNames.joinToString(prefix = "[", separator = ", ", postfix = "]") val rightPortName = rightPortNames.joinToString(prefix = "[", separator = ", ", postfix = "]") - connectionInstantiations.add("this._connectMultiplePorts($leftPortName, $rightPortName, ${connection.isIterated});") + connectionInstantiations.add("this._connectMulti($leftPortName, $rightPortName, ${connection.isIterated});") } else { var leftPortName = "" diff --git a/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt index 337835f8ab..d96fe3a4eb 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt @@ -51,7 +51,7 @@ class TSImportPreambleGenerator( const val DEFAULT_IMPORTS = """ |import commandLineArgs from 'command-line-args' |import commandLineUsage from 'command-line-usage' - |import {Args as __Args, Present, Parameter as __Parameter, State as __State, Variable as __Variable, Read, Triggers as __Triggers, ReadWrite, Write, Action as __Action, Startup as __Startup, Sched, Timer as __Timer, Reactor as __Reactor, Port as __Port, OutPort as __OutPort, InPort as __InPort, App as __App} from './core/reactor' + |import {Args as __Args, Present, Parameter as __Parameter, State as __State, Variable as __Variable, Read, Triggers as __Triggers, ReadWrite, Write, Action as __Action, Startup as __Startup, Sched, Timer as __Timer, Reactor as __Reactor, Port as __Port, OutPort as __OutPort, InPort as __InPort, App as __App, InMultiPort, OutMultiPort} from './core/reactor' |import {Reaction as __Reaction} from './core/reaction' |import {FederatedApp as __FederatedApp} from './core/federation' |import {TimeUnit, TimeValue, Tag as __Tag, Origin as __Origin} from './core/time' diff --git a/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt index f30074a3d0..68443673b3 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt @@ -37,15 +37,14 @@ class TSPortGenerator ( val portClassProperties = LinkedList() for (input in inputs) { if (input.isMultiport) { - portClassProperties.add("${input.name}: Array<__InPort<${getPortType(input)}>>;") + portClassProperties.add("${input.name}: InMultiPort<${getPortType(input)}>;") } else { portClassProperties.add("${input.name}: __InPort<${getPortType(input)}>;") } } for (output in outputs) { if (output.isMultiport) { - portClassProperties.add("${output.name}: Array<__OutPort<${getPortType(output)}>>;") - portClassProperties.add("__rw__${output.name}: Array>;") + portClassProperties.add("${output.name}: OutMultiPort<${getPortType(output)}>;") } else { portClassProperties.add("${output.name}: __OutPort<${getPortType(output)}>;") } @@ -58,11 +57,7 @@ class TSPortGenerator ( for (input in inputs) { if (input.isMultiport) { porInstantiations.add( - "this.${input.name} = new Array<__InPort<${getPortType(input)}>>(${input.widthSpec.toTSCode()});") - porInstantiations.add(""" - |for (let i = 0; i< this.${input.name}.length; i++) { - | this.${input.name}[i] = new __InPort<${getPortType(input)}>(this); - |}""".trimMargin()) + "this.${input.name} = new InMultiPort<${getPortType(input)}>(this, ${input.widthSpec.toTSCode()});") } else { porInstantiations.add("this.${input.name} = new __InPort<${getPortType(input)}>(this);") } @@ -70,17 +65,7 @@ class TSPortGenerator ( for (output in outputs) { if (output.isMultiport) { porInstantiations.add( - "this.${output.name} = new Array<__OutPort<${getPortType(output)}>>(${output.widthSpec.toTSCode()});") - porInstantiations.add(""" - |for (let i = 0; i < this.${output.name}.length; i++) { - | this.${output.name}[i] = new __OutPort<${getPortType(output)}>(this); - |}""".trimMargin()) - porInstantiations.add( - "this.__rw__${output.name} = new Array>(this.${output.name}.length);") - porInstantiations.add(""" - |this.${output.name}.forEach((element, i) => { - | this.__rw__${output.name}[i] = this.writable(element) - |});""".trimMargin()) + "this.${output.name} = new OutMultiPort<${getPortType(output)}>(this, ${output.widthSpec.toTSCode()});") } else { porInstantiations.add("this.${output.name} = new __OutPort<${getPortType(output)}>(this);") } diff --git a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt index 87c62cd05d..f30a08ce7c 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt @@ -117,7 +117,11 @@ class TSReactionGenerator( val reactionTriggers = StringJoiner(",\n") for (trigger in reaction.triggers) { if (trigger is VarRef) { - reactionTriggers.add("this.${trigger.generateVarRef()}") + if (trigger.variable.isMultiport) { + reactionTriggers.add("this.${trigger.generateVarRef()}.ports") + } else { + reactionTriggers.add("this.${trigger.generateVarRef()}") + } } else if (trigger.isStartup) { reactionTriggers.add("this.startup") } else if (trigger.isShutdown) { @@ -229,7 +233,7 @@ class TSReactionGenerator( } if (trigOrSource.variable.isMultiport) { - reactSignature.add("${generateArg(trigOrSource)}: Array>") + reactSignature.add("${generateArg(trigOrSource)}: InMultiPort<${reactSignatureElementType}>") } else { reactSignature.add("${generateArg(trigOrSource)}: Read<${reactSignatureElementType}>") } @@ -238,11 +242,7 @@ class TSReactionGenerator( if (trigOrSource.variable.isMultiport) { val inputPort = trigOrSource.variable as Port reactPrologue.add( - "let ${inputPort.name} = new Array<${reactSignatureElementType} | undefined>(${generateArg(trigOrSource)}.length);") - reactPrologue.add(""" - |for (let i = 0; i < ${inputPort.name}.length; i++) { - | ${inputPort.name}[i] = ${generateArg(trigOrSource)}[i].get(); - |}""".trimMargin()) + "let ${inputPort.name} = ${generateArg(trigOrSource)}.getValueArray();") } else { reactPrologue.add("let ${trigOrSource.variable.name} = ${generateArg(trigOrSource)}.get();") } @@ -274,9 +274,9 @@ class TSReactionGenerator( } else if (effect.variable is Port){ val outputPort = effect.variable as Port if (outputPort.isMultiport) { - reactSignatureElement += ": Array>" + reactSignatureElement += ": OutMultiPort<${getPortType(effect.variable as Port)}>" } else { - reactSignatureElement += ": ReadWrite<" + getPortType(effect.variable as Port) + ">" + reactSignatureElement += ": ReadWrite<${getPortType(effect.variable as Port)}>" } if (effect.container == null) { if (outputPort.isMultiport) { @@ -284,7 +284,7 @@ class TSReactionGenerator( """ |${outputPort.name}.forEach((element, index) => { | if (element !== undefined) { - | __${outputPort.name}[index].set(element); + | __${outputPort.name}.writablePorts[index].set(element); | } |});""".trimMargin() }) @@ -307,7 +307,7 @@ class TSReactionGenerator( } else if (effect.variable is Port) { val port = effect.variable as Port if (port.isMultiport) { - reactFunctArgs.add("this.__rw__" + effect.generateVarRef()) + reactFunctArgs.add("this.${effect.generateVarRef()}") } else { reactFunctArgs.add("this.writable($functArg)") } @@ -316,7 +316,7 @@ class TSReactionGenerator( if (effect.container == null) { if (effect.variable.isMultiport) { val port = effect.variable as Port - reactPrologue.add("let ${port.name} = new Array<${getPortType(port)}>(__${port.name}.length);") + reactPrologue.add("let ${port.name} = new Array<${getPortType(port)}>(__${port.name}.width());") } else { reactPrologue.add("let ${effect.variable.name} = __${effect.variable.name}.get();") } From 628a0b7999ea4b0b804d63d8beebe7af44d34b94 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Fri, 11 Feb 2022 08:49:48 -0800 Subject: [PATCH 20/64] Fix typescript-eslint errors: - Test client received diagnostic at line 21: Forbidden non-null assertion - Test client received diagnostic at line 25: Mixed spaces and tabs. --- test/TypeScript/src/multiport/MultiportIn.lf | 5 ++- test/TypeScript/src/multiport/MultiportOut.lf | 3 +- .../src/multiport/MultiportToHierarchy.lf | 43 ++++++++++--------- .../multiport/MultiportToMultiportArray.lf | 4 +- 4 files changed, 29 insertions(+), 26 deletions(-) diff --git a/test/TypeScript/src/multiport/MultiportIn.lf b/test/TypeScript/src/multiport/MultiportIn.lf index 95bffd7bab..ff662557ae 100644 --- a/test/TypeScript/src/multiport/MultiportIn.lf +++ b/test/TypeScript/src/multiport/MultiportIn.lf @@ -25,10 +25,11 @@ reactor Destination { reaction(inp) {= let sum = 0; for (let i = 0; i < inp.length; i++) { - if (inp[i] === undefined) { + const val = inp[i]; + if (val === undefined) { util.requestErrorStop("ERROR: input at [" + i + "] is missing."); } else { - sum += inp[i]!; + sum += val; } } console.log("Sum of received: " + sum + "."); diff --git a/test/TypeScript/src/multiport/MultiportOut.lf b/test/TypeScript/src/multiport/MultiportOut.lf index 80e03e7cce..67c698adb8 100644 --- a/test/TypeScript/src/multiport/MultiportOut.lf +++ b/test/TypeScript/src/multiport/MultiportOut.lf @@ -27,7 +27,8 @@ reactor Destination { reaction(inp) {= let sum = 0; for (let i = 0; i < inp.length; i++) { - if (inp[i] !== undefined) sum += inp[i]!; + const val = inp[i] + if (val !== undefined) sum += val; } console.log("Sum of received: " + sum + "."); if (sum != s) { diff --git a/test/TypeScript/src/multiport/MultiportToHierarchy.lf b/test/TypeScript/src/multiport/MultiportToHierarchy.lf index 91c3a49786..e097148fb9 100644 --- a/test/TypeScript/src/multiport/MultiportToHierarchy.lf +++ b/test/TypeScript/src/multiport/MultiportToHierarchy.lf @@ -4,30 +4,31 @@ target TypeScript { timeout: 2 sec, }; reactor Source(width:number(4)) { - timer t(0, 200 msec); - output[width] out:number; - state s:number(0); - reaction(t) -> out {= + timer t(0, 200 msec); + output[width] out:number; + state s:number(0); + reaction(t) -> out {= for(let i = 0; i < 4; i++) { out[i] = s++; } - =} + =} } reactor Destination(width:number(4)) { - state s:number(6); - input[width] inp:number; - reaction(inp) {= - let sum = 0; - for (let i = 0; i < inp.length; i++) { - if (inp[i] !== undefined) sum += inp[i]!; + state s:number(6); + input[width] inp:number; + reaction(inp) {= + let sum = 0; + for (let i = 0; i < inp.length; i++) { + const val = inp[i] + if (val !== undefined) sum += val; } - console.log("Sum of received: " + sum + "."); - if (sum != s) { - util.requestErrorStop("ERROR: Expected " + s + "."); - } - s += 16; - =} - reaction(shutdown) {= + console.log("Sum of received: " + sum + "."); + if (sum != s) { + util.requestErrorStop("ERROR: Expected " + s + "."); + } + s += 16; + =} + reaction(shutdown) {= if (s <= 6) { util.requestErrorStop("ERROR: Destination received no input!"); } @@ -41,7 +42,7 @@ reactor Container(width:number(4)) { } main reactor MultiportToHierarchy(width:number(4)) { - a = new Source(width=width); - b = new Container(width=width); - a.out -> b.inp; + a = new Source(width=width); + b = new Container(width=width); + a.out -> b.inp; } \ No newline at end of file diff --git a/test/TypeScript/src/multiport/MultiportToMultiportArray.lf b/test/TypeScript/src/multiport/MultiportToMultiportArray.lf index 37d47a9a45..bb455d3715 100644 --- a/test/TypeScript/src/multiport/MultiportToMultiportArray.lf +++ b/test/TypeScript/src/multiport/MultiportToMultiportArray.lf @@ -27,8 +27,8 @@ reactor Destination { reaction(inp) {= let sum = 0; for (let i = 0; i < inp.length; i++) { - if (inp[i] !== undefined) { - const a = inp[i]!; + const a = inp[i] + if (a !== undefined) { for (let j = 0; j < a.length; j++) { sum += a[j]; } From dcbe092ccbf65598ee4f91f03b13c0b9a6900940 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Wed, 16 Feb 2022 14:22:48 -0800 Subject: [PATCH 21/64] Add BankToMultiport.lf and add code-generation support for Banks and bank_index parameter. --- org.lflang/src/lib/ts/reactor-ts | 2 +- .../generator/ts/TSConnectionGenerator.kt | 18 +++++++-- .../generator/ts/TSConstructorGenerator.kt | 2 - .../org/lflang/generator/ts/TSExtensions.kt | 24 +++++++++++- .../org/lflang/generator/ts/TSGenerator.kt | 2 +- .../generator/ts/TSImportPreambleGenerator.kt | 1 + .../generator/ts/TSInstanceGenerator.kt | 38 ++++++++++++++++--- .../lflang/generator/ts/TSPortGenerator.kt | 17 --------- .../lflang/generator/ts/TSReactorGenerator.kt | 4 +- .../src/multiport/BankToMultiport.lf | 35 +++++++++++++++++ 10 files changed, 109 insertions(+), 34 deletions(-) create mode 100644 test/TypeScript/src/multiport/BankToMultiport.lf diff --git a/org.lflang/src/lib/ts/reactor-ts b/org.lflang/src/lib/ts/reactor-ts index 45fce1b60e..102ac73d7f 160000 --- a/org.lflang/src/lib/ts/reactor-ts +++ b/org.lflang/src/lib/ts/reactor-ts @@ -1 +1 @@ -Subproject commit 45fce1b60ef65ff2ce648cd5c2182d7259a2cbfd +Subproject commit 102ac73d7fac8438622161088d38617a865d4cfe diff --git a/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt index dca740cec0..e97e9fe744 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt @@ -2,6 +2,8 @@ package org.lflang.generator.ts import org.lflang.ErrorReporter import org.lflang.hasMultipleConnections +import org.lflang.isBank +import org.lflang.isInput import org.lflang.isMultiport import org.lflang.lf.Connection import org.lflang.lf.Port @@ -17,21 +19,29 @@ class TSConnectionGenerator ( private val errorReporter: ErrorReporter ) { // There is no generateClassProperties() for connections - + + private fun getPortTypeName(port: Port) = + "${if(port.isInput) "__In" else "__Out"}${if(port.isMultiport) "Multi" else ""}Port<${getPortType(port)}>" + private fun getPortName(port: VarRef): String { var portName = "" if (port.container != null) { + if (port.container.isBank) { + portName += "...this.${port.container.name}.all().reduce(" + + "(__acc, __val) => __acc.concat(__val.${port.variable.name}), " + + "new Array<${getPortTypeName(port.variable as Port)}>(0))" + return portName + } portName += port.container.name + "." } portName += port.variable.name - return portName + return "this.$portName" } private fun getPortNames(ports: List): List { var portNames = LinkedList() for (varRef in ports) { - val port = varRef.variable as Port - portNames.add("this.${getPortName(varRef)}") + portNames.add(getPortName(varRef)) } return portNames } diff --git a/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt index 300951c66f..eab9dd9d56 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt @@ -25,8 +25,6 @@ class TSConstructorGenerator ( private fun getInitializerList(param: Parameter): List = tsGenerator.getInitializerListW(param) - private fun Parameter.getTargetType(): String = tsGenerator.getTargetTypeW(this) - // Initializer functions private fun getTargetInitializerHelper(param: Parameter, list: List): String { diff --git a/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt b/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt index 100d44a862..8ae64bb5f2 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt @@ -1,5 +1,8 @@ package org.lflang.generator.ts +import org.lflang.lf.Parameter +import org.lflang.lf.Port +import org.lflang.lf.Type import org.lflang.lf.WidthSpec import org.lflang.toText @@ -14,4 +17,23 @@ fun WidthSpec.toTSCode(): String = terms.joinToString(" + ") { it.code != null -> it.code.toText() else -> it.width.toString() } -} \ No newline at end of file +} + +private fun Type.getTargetType(): String = TSTypes.getTargetType(this) + +/** + * Return a TS type for the specified port. + * If the type has not been specified, return + * "Present" which is the base type for ports. + * @param port The port + * @return The TS type. + */ +fun getPortType(port: Port): String { + if (port.type != null) { + return port.type.getTargetType() + } else { + return "Present" + } +} + +fun Parameter.getTargetType(): String = this.type.getTargetType() diff --git a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt index 3fe2fd783d..5d546b32e4 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt @@ -96,7 +96,7 @@ class TSGenerator( * Files to be copied from the reactor-ts submodule into the generated * source directory. */ - val RUNTIME_FILES = arrayOf("cli.ts", "command-line-args.d.ts", + val RUNTIME_FILES = arrayOf("bank.ts", "cli.ts", "command-line-args.d.ts", "command-line-usage.d.ts", "component.ts", "federation.ts", "reaction.ts", "reactor.ts", "microtime.d.ts", "nanotimer.d.ts", "time.ts", "ulog.d.ts", "util.ts") diff --git a/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt index d96fe3a4eb..01ad3f0d25 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt @@ -52,6 +52,7 @@ class TSImportPreambleGenerator( |import commandLineArgs from 'command-line-args' |import commandLineUsage from 'command-line-usage' |import {Args as __Args, Present, Parameter as __Parameter, State as __State, Variable as __Variable, Read, Triggers as __Triggers, ReadWrite, Write, Action as __Action, Startup as __Startup, Sched, Timer as __Timer, Reactor as __Reactor, Port as __Port, OutPort as __OutPort, InPort as __InPort, App as __App, InMultiPort, OutMultiPort} from './core/reactor' + |import {Bank as __Bank} from './core/bank' |import {Reaction as __Reaction} from './core/reaction' |import {FederatedApp as __FederatedApp} from './core/federation' |import {TimeUnit, TimeValue, Tag as __Tag, Origin as __Origin} from './core/time' diff --git a/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt index dc400c4293..281dfd57d3 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt @@ -1,9 +1,12 @@ package org.lflang.generator.ts +import org.lflang.ErrorReporter import org.lflang.federated.FederateInstance +import org.lflang.isBank import org.lflang.lf.Instantiation import org.lflang.lf.Parameter import org.lflang.lf.Reactor +import org.lflang.reactor import org.lflang.toDefinition import org.lflang.toText import java.util.* @@ -14,6 +17,7 @@ import java.util.* class TSInstanceGenerator ( // TODO(hokeun): Remove dependency on TSGenerator. private val tsGenerator: TSGenerator, + private val errorReporter: ErrorReporter, private val tsReactorGenerator: TSReactorGenerator, reactor: Reactor, federate: FederateInstance @@ -42,9 +46,16 @@ class TSInstanceGenerator ( fun generateClassProperties(): String { val childReactorClassProperties = LinkedList() for (childReactor in childReactors) { - val childReactorParams = if (childReactor.typeParms.isEmpty()) {""} else { + val childReactorTypeParams = if (childReactor.typeParms.isEmpty()) {""} else { childReactor.typeParms.joinToString(", ", "<", ">") { it.toText() }} - childReactorClassProperties.add("${childReactor.name}: ${childReactor.reactorClass.name}$childReactorParams") + if (childReactor.isBank) { + val childReactorParamTypes = + childReactor.reactor.parameters.joinToString(", ", "[", "]") { it.type.toText() } + childReactorClassProperties.add("${childReactor.name}: __Bank<${childReactor.reactorClass.name}$childReactorTypeParams, " + + "$childReactorParamTypes>") + } else { + childReactorClassProperties.add("${childReactor.name}: ${childReactor.reactorClass.name}$childReactorTypeParams") + } } return childReactorClassProperties.joinToString("\n") } @@ -59,12 +70,27 @@ class TSInstanceGenerator ( // reactor class, find the matching parameter assignments in // the reactor instance, and write the corresponding parameter // value as an argument for the TypeScript constructor - for (parameter in childReactor.reactorClass.toDefinition().parameters) { + var bankIndexArgIndex: Int? = null + for ((index, parameter) in childReactor.reactorClass.toDefinition().parameters.withIndex()) { + if (parameter.name == "bank_index") { + if (parameter.getTargetType() != "number") { + errorReporter.reportError("Type of the reactor parameter 'bank_index' must be number!") + } + bankIndexArgIndex = index + } childReactorArguments.add(getTargetInitializer(parameter, childReactor)) } - - childReactorInstantiations.add( - "this.${childReactor.name} = new ${childReactor.reactorClass.name}($childReactorArguments)") + if (childReactor.isBank) { + childReactorInstantiations.add( + "this.${childReactor.name} = " + + "new __Bank" + + "(this, ${childReactor.widthSpec.toTSCode()}, ${childReactor.reactorClass.name}," + + "${bankIndexArgIndex ?: "undefined"}, $childReactorArguments)") + } else { + childReactorInstantiations.add( + "this.${childReactor.name} = " + + "new ${childReactor.reactorClass.name}($childReactorArguments)") + } } return childReactorInstantiations.joinToString("\n") } diff --git a/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt index 68443673b3..dec51b103b 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt @@ -12,26 +12,9 @@ import java.util.* */ class TSPortGenerator ( // TODO(hokeun): Remove dependency on TSGenerator. - private val tsGenerator: TSGenerator, 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() diff --git a/org.lflang/src/org/lflang/generator/ts/TSReactorGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSReactorGenerator.kt index d4a8b2b9cb..6a4004e4ad 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSReactorGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSReactorGenerator.kt @@ -113,12 +113,12 @@ class TSReactorGenerator( "export class $reactorName extends __Reactor {" } - val instanceGenerator = TSInstanceGenerator(tsGenerator, this, reactor, federate) + val instanceGenerator = TSInstanceGenerator(tsGenerator, errorReporter, this, reactor, federate) val timerGenerator = TSTimerGenerator(tsGenerator, reactor.timers) val parameterGenerator = TSParameterGenerator(tsGenerator, reactor.parameters) val stateGenerator = TSStateGenerator(tsGenerator, reactor.stateVars) val actionGenerator = TSActionGenerator(tsGenerator, reactor.actions) - 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/test/TypeScript/src/multiport/BankToMultiport.lf b/test/TypeScript/src/multiport/BankToMultiport.lf new file mode 100644 index 0000000000..8e87c81a45 --- /dev/null +++ b/test/TypeScript/src/multiport/BankToMultiport.lf @@ -0,0 +1,35 @@ +// Test bank of reactors to multiport input with id parameter in the bank. +target TypeScript; +reactor Source(bank_index:number(0)) { + output out:number; + + reaction (startup) -> out {= + out = bank_index; + =} +} + +reactor Sink { + input[4] inp:number; + state received:boolean(false); + + reaction (inp) {= + for (let i = 0; i < inp.length; i++) { + received = true; + console.log("Received " + inp[i]); + if (inp[i] != i) { + util.requestErrorStop("Error: expected " + i); + } + } + =} + reaction (shutdown) {= + if (!received) { + util.requestErrorStop("Error: received no input!"); + } + =} +} + +main reactor BankToMultiport { + source = new[4] Source(); + sink = new Sink(); + source.out -> sink.inp; +} From 3af6a47da927775d091417da4f627c7322d779f4 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Thu, 17 Feb 2022 09:43:40 -0800 Subject: [PATCH 22/64] Add MultiportToBank.lf test and fix minor issues including the non-typed parameter error. --- org.lflang/src/lib/ts/reactor-ts | 2 +- .../org/lflang/generator/ts/TSExtensions.kt | 2 +- .../generator/ts/TSInstanceGenerator.kt | 2 +- test/TypeScript/src/multiport/MultiportIn.lf | 2 +- .../src/multiport/MultiportToBank.lf | 28 +++++++++++++++++++ .../src/multiport/MultiportToHierarchy.lf | 2 +- .../src/multiport/MultiportToMultiport.lf | 2 +- .../src/multiport/MultiportToPort.lf | 2 +- 8 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 test/TypeScript/src/multiport/MultiportToBank.lf diff --git a/org.lflang/src/lib/ts/reactor-ts b/org.lflang/src/lib/ts/reactor-ts index 102ac73d7f..3cb1021ebb 160000 --- a/org.lflang/src/lib/ts/reactor-ts +++ b/org.lflang/src/lib/ts/reactor-ts @@ -1 +1 @@ -Subproject commit 102ac73d7fac8438622161088d38617a865d4cfe +Subproject commit 3cb1021ebb5b0c34b0d847d8929746ba76dd2eb3 diff --git a/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt b/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt index 8ae64bb5f2..ec9a5cd0f8 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt @@ -36,4 +36,4 @@ fun getPortType(port: Port): String { } } -fun Parameter.getTargetType(): String = this.type.getTargetType() +fun Parameter.getTargetType(): String = TSTypes.getTargetType(this) diff --git a/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt index 281dfd57d3..b9ef35cd83 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt @@ -84,7 +84,7 @@ class TSInstanceGenerator ( childReactorInstantiations.add( "this.${childReactor.name} = " + "new __Bank" + - "(this, ${childReactor.widthSpec.toTSCode()}, ${childReactor.reactorClass.name}," + + "(this, ${childReactor.widthSpec.toTSCode()}, ${childReactor.reactorClass.name}, " + "${bankIndexArgIndex ?: "undefined"}, $childReactorArguments)") } else { childReactorInstantiations.add( diff --git a/test/TypeScript/src/multiport/MultiportIn.lf b/test/TypeScript/src/multiport/MultiportIn.lf index ff662557ae..b774fb5666 100644 --- a/test/TypeScript/src/multiport/MultiportIn.lf +++ b/test/TypeScript/src/multiport/MultiportIn.lf @@ -58,4 +58,4 @@ main reactor MultiportIn { a.out -> t3.inp; a.out -> t4.inp; t1.out, t2.out, t3.out, t4.out -> b.inp; -} \ No newline at end of file +} diff --git a/test/TypeScript/src/multiport/MultiportToBank.lf b/test/TypeScript/src/multiport/MultiportToBank.lf new file mode 100644 index 0000000000..34f024053a --- /dev/null +++ b/test/TypeScript/src/multiport/MultiportToBank.lf @@ -0,0 +1,28 @@ +target TypeScript; + +reactor Source { + output[4] out:number; + + reaction (startup) -> out {= + for (let i = 0 ; i < out.length; i++) { + out[i] = i; + } + =} +} + +reactor Sink(bank_index:number(0)) { + input inp:number; + + reaction (inp) {= + console.log("Received " + inp); + if (inp != bank_index) { + util.requestErrorStop("Error: expected " + bank_index); + } + =} +} + +main reactor MultiportToBank { + source = new Source(); + sink = new[4] Sink(); + source.out -> sink.inp; +} diff --git a/test/TypeScript/src/multiport/MultiportToHierarchy.lf b/test/TypeScript/src/multiport/MultiportToHierarchy.lf index e097148fb9..132ba10120 100644 --- a/test/TypeScript/src/multiport/MultiportToHierarchy.lf +++ b/test/TypeScript/src/multiport/MultiportToHierarchy.lf @@ -45,4 +45,4 @@ main reactor MultiportToHierarchy(width:number(4)) { a = new Source(width=width); b = new Container(width=width); a.out -> b.inp; -} \ No newline at end of file +} diff --git a/test/TypeScript/src/multiport/MultiportToMultiport.lf b/test/TypeScript/src/multiport/MultiportToMultiport.lf index 571a2fc331..ac01b63b92 100644 --- a/test/TypeScript/src/multiport/MultiportToMultiport.lf +++ b/test/TypeScript/src/multiport/MultiportToMultiport.lf @@ -35,4 +35,4 @@ main reactor MultiportToMultiport { source = new Source(); sink = new Sink(); source.out -> sink.inp; -} \ No newline at end of file +} diff --git a/test/TypeScript/src/multiport/MultiportToPort.lf b/test/TypeScript/src/multiport/MultiportToPort.lf index 5205b93d16..49d0de0af7 100644 --- a/test/TypeScript/src/multiport/MultiportToPort.lf +++ b/test/TypeScript/src/multiport/MultiportToPort.lf @@ -35,4 +35,4 @@ main reactor MultiportToPort { b1 = new Destination(); b2 = new Destination(expected = 1); a.out -> b1.inp, b2.inp; -} \ No newline at end of file +} From 92466276e50d943c83e18cd99b413a55bbd61459 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Thu, 17 Feb 2022 13:47:13 -0800 Subject: [PATCH 23/64] Make generateAfterDelaysWithVariableWidth false for TypeScript target and add code-generation support to compute the bank with for _lf_GenDelay reactor. Also fix issue with the Reactor's generic type parameters. This is for support MultiportToMultiport2After.lf test, where multiple a Delay is involved in multiple connections (hasMultipleConnections). --- .../src/org/lflang/generator/ts/TSExtensions.kt | 11 +++++++++++ .../src/org/lflang/generator/ts/TSGenerator.kt | 2 ++ .../lflang/generator/ts/TSInstanceGenerator.kt | 16 ++++++++++------ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt b/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt index ec9a5cd0f8..968f958df3 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt @@ -1,5 +1,7 @@ package org.lflang.generator.ts +import org.lflang.isBank +import org.lflang.isMultiport import org.lflang.lf.Parameter import org.lflang.lf.Port import org.lflang.lf.Type @@ -14,6 +16,15 @@ import org.lflang.toText fun WidthSpec.toTSCode(): String = terms.joinToString(" + ") { when { it.parameter != null -> "${it.parameter.name}" + it.port != null -> with(it.port) { + if (container?.isBank == true) { + if ((variable as Port).isMultiport) "this.${container.name}.all().length * this.${container.name}.all()[0].${variable.name}.width())" + else "this.${container.name}.all().length" + } else { + if ((variable as Port).isMultiport) "this.${container.name}.${variable.name}.width()" + else "1" + } + } it.code != null -> it.code.toText() else -> it.width.toString() } diff --git a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt index 5d546b32e4..2bfe98575c 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt @@ -649,4 +649,6 @@ class TSGenerator( override fun getTarget(): Target { return Target.TS } + + override fun generateAfterDelaysWithVariableWidth() = false } diff --git a/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt index b9ef35cd83..96cdfaf572 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt @@ -43,18 +43,21 @@ class TSInstanceGenerator ( return tsReactorGenerator.getTargetInitializerHelper(param, getInitializerList(param, i)) } + private fun getChildReactorTypeParams(childReactor: Instantiation): String = + if (childReactor.typeParms.isEmpty()) {""} else { + childReactor.typeParms.joinToString(", ", "<", ">") { it.toText() }} + fun generateClassProperties(): String { val childReactorClassProperties = LinkedList() for (childReactor in childReactors) { - val childReactorTypeParams = if (childReactor.typeParms.isEmpty()) {""} else { - childReactor.typeParms.joinToString(", ", "<", ">") { it.toText() }} if (childReactor.isBank) { val childReactorParamTypes = - childReactor.reactor.parameters.joinToString(", ", "[", "]") { it.type.toText() } - childReactorClassProperties.add("${childReactor.name}: __Bank<${childReactor.reactorClass.name}$childReactorTypeParams, " + + childReactor.reactor.parameters.joinToString(", ", "[", "]") { it.getTargetType() } + childReactorClassProperties.add("${childReactor.name}: " + + "__Bank<${childReactor.reactorClass.name}${getChildReactorTypeParams(childReactor)}, " + "$childReactorParamTypes>") } else { - childReactorClassProperties.add("${childReactor.name}: ${childReactor.reactorClass.name}$childReactorTypeParams") + childReactorClassProperties.add("${childReactor.name}: ${childReactor.reactorClass.name}${getChildReactorTypeParams(childReactor)}") } } return childReactorClassProperties.joinToString("\n") @@ -84,7 +87,8 @@ class TSInstanceGenerator ( childReactorInstantiations.add( "this.${childReactor.name} = " + "new __Bank" + - "(this, ${childReactor.widthSpec.toTSCode()}, ${childReactor.reactorClass.name}, " + + "(this, ${childReactor.widthSpec.toTSCode()}, " + + "${childReactor.reactorClass.name}${getChildReactorTypeParams(childReactor)}, " + "${bankIndexArgIndex ?: "undefined"}, $childReactorArguments)") } else { childReactorInstantiations.add( From 3d991dfd807a51c03c27ecb3d40a7949005f7b70 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Thu, 17 Feb 2022 14:11:41 -0800 Subject: [PATCH 24/64] Use this.getBankIndex() instead of the bank_index parameter in TypeScript target. --- org.lflang/src/lib/ts/reactor-ts | 2 +- .../org/lflang/generator/ts/TSInstanceGenerator.kt | 13 +------------ test/TypeScript/src/multiport/BankToMultiport.lf | 4 ++-- test/TypeScript/src/multiport/MultiportToBank.lf | 6 +++--- 4 files changed, 7 insertions(+), 18 deletions(-) diff --git a/org.lflang/src/lib/ts/reactor-ts b/org.lflang/src/lib/ts/reactor-ts index 3cb1021ebb..13c3f64eb7 160000 --- a/org.lflang/src/lib/ts/reactor-ts +++ b/org.lflang/src/lib/ts/reactor-ts @@ -1 +1 @@ -Subproject commit 3cb1021ebb5b0c34b0d847d8929746ba76dd2eb3 +Subproject commit 13c3f64eb74a5400c46cbb59175ff0d574ed43d6 diff --git a/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt index 96cdfaf572..5c41e4a44e 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt @@ -69,18 +69,7 @@ class TSInstanceGenerator ( val childReactorArguments = StringJoiner(", "); childReactorArguments.add("this") - // Iterate through parameters in the order they appear in the - // reactor class, find the matching parameter assignments in - // the reactor instance, and write the corresponding parameter - // value as an argument for the TypeScript constructor - var bankIndexArgIndex: Int? = null for ((index, parameter) in childReactor.reactorClass.toDefinition().parameters.withIndex()) { - if (parameter.name == "bank_index") { - if (parameter.getTargetType() != "number") { - errorReporter.reportError("Type of the reactor parameter 'bank_index' must be number!") - } - bankIndexArgIndex = index - } childReactorArguments.add(getTargetInitializer(parameter, childReactor)) } if (childReactor.isBank) { @@ -89,7 +78,7 @@ class TSInstanceGenerator ( "new __Bank" + "(this, ${childReactor.widthSpec.toTSCode()}, " + "${childReactor.reactorClass.name}${getChildReactorTypeParams(childReactor)}, " + - "${bankIndexArgIndex ?: "undefined"}, $childReactorArguments)") + "$childReactorArguments)") } else { childReactorInstantiations.add( "this.${childReactor.name} = " + diff --git a/test/TypeScript/src/multiport/BankToMultiport.lf b/test/TypeScript/src/multiport/BankToMultiport.lf index 8e87c81a45..ef8b4abf83 100644 --- a/test/TypeScript/src/multiport/BankToMultiport.lf +++ b/test/TypeScript/src/multiport/BankToMultiport.lf @@ -1,10 +1,10 @@ // Test bank of reactors to multiport input with id parameter in the bank. target TypeScript; -reactor Source(bank_index:number(0)) { +reactor Source { output out:number; reaction (startup) -> out {= - out = bank_index; + out = this.getBankIndex(); =} } diff --git a/test/TypeScript/src/multiport/MultiportToBank.lf b/test/TypeScript/src/multiport/MultiportToBank.lf index 34f024053a..5cf903c36b 100644 --- a/test/TypeScript/src/multiport/MultiportToBank.lf +++ b/test/TypeScript/src/multiport/MultiportToBank.lf @@ -10,13 +10,13 @@ reactor Source { =} } -reactor Sink(bank_index:number(0)) { +reactor Sink { input inp:number; reaction (inp) {= console.log("Received " + inp); - if (inp != bank_index) { - util.requestErrorStop("Error: expected " + bank_index); + if (inp != this.getBankIndex()) { + util.requestErrorStop("Error: expected " + this.getBankIndex()); } =} } From 56b53ed3b284570024ac5969d6bfab152e3300de Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Thu, 17 Feb 2022 14:56:25 -0800 Subject: [PATCH 25/64] Add MultiportFromBank.lf test and remove unused code. --- org.lflang/src/lib/ts/reactor-ts | 2 +- .../generator/ts/TSInstanceGenerator.kt | 2 +- .../src/multiport/MultiportFromBank.lf | 36 +++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 test/TypeScript/src/multiport/MultiportFromBank.lf diff --git a/org.lflang/src/lib/ts/reactor-ts b/org.lflang/src/lib/ts/reactor-ts index 13c3f64eb7..5d2d00f795 160000 --- a/org.lflang/src/lib/ts/reactor-ts +++ b/org.lflang/src/lib/ts/reactor-ts @@ -1 +1 @@ -Subproject commit 13c3f64eb74a5400c46cbb59175ff0d574ed43d6 +Subproject commit 5d2d00f7953aa4ca3735548c45b8311472016d96 diff --git a/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt index 5c41e4a44e..00f6a8f37a 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt @@ -69,7 +69,7 @@ class TSInstanceGenerator ( val childReactorArguments = StringJoiner(", "); childReactorArguments.add("this") - for ((index, parameter) in childReactor.reactorClass.toDefinition().parameters.withIndex()) { + for (parameter in childReactor.reactorClass.toDefinition().parameters) { childReactorArguments.add(getTargetInitializer(parameter, childReactor)) } if (childReactor.isBank) { diff --git a/test/TypeScript/src/multiport/MultiportFromBank.lf b/test/TypeScript/src/multiport/MultiportFromBank.lf new file mode 100644 index 0000000000..5143df7849 --- /dev/null +++ b/test/TypeScript/src/multiport/MultiportFromBank.lf @@ -0,0 +1,36 @@ +// Check multiport output to bank of recipients. +// Here, the bank is smaller than the width of the sending port. +target TypeScript { + timeout: 2 sec +}; +reactor Source { + output out:number; + reaction(startup) -> out {= + out = this.getBankIndex(); + =} +} +reactor Destination(portWidth:number(2)) { + input[portWidth] inp:number; + state received:boolean(false); + reaction(inp) {= + for (let i = 0; i < inp.length; i++) { + console.log("Destination channel " + i + " received " + inp[i]); + if (i != inp[i]) { + util.requestErrorStop("ERROR: Expected " + i); + } + } + received = true; + =} + reaction(shutdown) {= + if (!received) { + util.requestErrorStop("ERROR: Destination received no input!"); + } + console.log("Success."); + =} +} + +main reactor (width:number(4)) { + a = new[width] Source(); + b = new Destination(portWidth = width); + a.out -> b.inp; +} From 2aef559e12bbf5352c13d66e754e20ebd44bdbc8 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Thu, 17 Feb 2022 16:20:33 -0800 Subject: [PATCH 26/64] Add 3 multiport tests Broadcast.lf, BankSelfBroadcast.lf, BankMulticast.lf for TypeScript target. Add TestCount.lf and fix Count.lf in test/TypeScript/src/lib. Also change imports for MultiPort classes. --- org.lflang/src/lib/ts/reactor-ts | 2 +- .../generator/ts/TSImportPreambleGenerator.kt | 2 +- .../lflang/generator/ts/TSPortGenerator.kt | 8 ++-- .../generator/ts/TSReactionGenerator.kt | 4 +- test/TypeScript/src/lib/Count.lf | 13 +++--- test/TypeScript/src/lib/TestCount.lf | 29 +++++++++++++ .../TypeScript/src/multiport/BankMulticast.lf | 37 ++++++++++++++++ .../src/multiport/BankSelfBroadcast.lf | 43 +++++++++++++++++++ test/TypeScript/src/multiport/Broadcast.lf | 26 +++++++++++ 9 files changed, 149 insertions(+), 15 deletions(-) create mode 100644 test/TypeScript/src/lib/TestCount.lf create mode 100644 test/TypeScript/src/multiport/BankMulticast.lf create mode 100644 test/TypeScript/src/multiport/BankSelfBroadcast.lf create mode 100644 test/TypeScript/src/multiport/Broadcast.lf diff --git a/org.lflang/src/lib/ts/reactor-ts b/org.lflang/src/lib/ts/reactor-ts index 5d2d00f795..6d2659c601 160000 --- a/org.lflang/src/lib/ts/reactor-ts +++ b/org.lflang/src/lib/ts/reactor-ts @@ -1 +1 @@ -Subproject commit 5d2d00f7953aa4ca3735548c45b8311472016d96 +Subproject commit 6d2659c601a4e1bba8c289d9c66919860b2399c8 diff --git a/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt index 01ad3f0d25..f6ccf5586e 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt @@ -51,7 +51,7 @@ class TSImportPreambleGenerator( const val DEFAULT_IMPORTS = """ |import commandLineArgs from 'command-line-args' |import commandLineUsage from 'command-line-usage' - |import {Args as __Args, Present, Parameter as __Parameter, State as __State, Variable as __Variable, Read, Triggers as __Triggers, ReadWrite, Write, Action as __Action, Startup as __Startup, Sched, Timer as __Timer, Reactor as __Reactor, Port as __Port, OutPort as __OutPort, InPort as __InPort, App as __App, InMultiPort, OutMultiPort} from './core/reactor' + |import {Args as __Args, Present, Parameter as __Parameter, State as __State, Variable as __Variable, Read, Triggers as __Triggers, ReadWrite, Write, Action as __Action, Startup as __Startup, Sched, Timer as __Timer, Reactor as __Reactor, Port as __Port, OutPort as __OutPort, InPort as __InPort, App as __App, InMultiPort as __InMultiPort, OutMultiPort as __OutMultiPort} from './core/reactor' |import {Bank as __Bank} from './core/bank' |import {Reaction as __Reaction} from './core/reaction' |import {FederatedApp as __FederatedApp} from './core/federation' diff --git a/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt index dec51b103b..10e2a4a644 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt @@ -20,14 +20,14 @@ class TSPortGenerator ( val portClassProperties = LinkedList() for (input in inputs) { if (input.isMultiport) { - portClassProperties.add("${input.name}: InMultiPort<${getPortType(input)}>;") + portClassProperties.add("${input.name}: __InMultiPort<${getPortType(input)}>;") } else { portClassProperties.add("${input.name}: __InPort<${getPortType(input)}>;") } } for (output in outputs) { if (output.isMultiport) { - portClassProperties.add("${output.name}: OutMultiPort<${getPortType(output)}>;") + portClassProperties.add("${output.name}: __OutMultiPort<${getPortType(output)}>;") } else { portClassProperties.add("${output.name}: __OutPort<${getPortType(output)}>;") } @@ -40,7 +40,7 @@ class TSPortGenerator ( for (input in inputs) { if (input.isMultiport) { porInstantiations.add( - "this.${input.name} = new InMultiPort<${getPortType(input)}>(this, ${input.widthSpec.toTSCode()});") + "this.${input.name} = new __InMultiPort<${getPortType(input)}>(this, ${input.widthSpec.toTSCode()});") } else { porInstantiations.add("this.${input.name} = new __InPort<${getPortType(input)}>(this);") } @@ -48,7 +48,7 @@ class TSPortGenerator ( for (output in outputs) { if (output.isMultiport) { porInstantiations.add( - "this.${output.name} = new OutMultiPort<${getPortType(output)}>(this, ${output.widthSpec.toTSCode()});") + "this.${output.name} = new __OutMultiPort<${getPortType(output)}>(this, ${output.widthSpec.toTSCode()});") } else { porInstantiations.add("this.${output.name} = new __OutPort<${getPortType(output)}>(this);") } diff --git a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt index f30a08ce7c..957702e4b2 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt @@ -233,7 +233,7 @@ class TSReactionGenerator( } if (trigOrSource.variable.isMultiport) { - reactSignature.add("${generateArg(trigOrSource)}: InMultiPort<${reactSignatureElementType}>") + reactSignature.add("${generateArg(trigOrSource)}: __InMultiPort<${reactSignatureElementType}>") } else { reactSignature.add("${generateArg(trigOrSource)}: Read<${reactSignatureElementType}>") } @@ -274,7 +274,7 @@ class TSReactionGenerator( } else if (effect.variable is Port){ val outputPort = effect.variable as Port if (outputPort.isMultiport) { - reactSignatureElement += ": OutMultiPort<${getPortType(effect.variable as Port)}>" + reactSignatureElement += ": __OutMultiPort<${getPortType(effect.variable as Port)}>" } else { reactSignatureElement += ": ReadWrite<${getPortType(effect.variable as Port)}>" } diff --git a/test/TypeScript/src/lib/Count.lf b/test/TypeScript/src/lib/Count.lf index a53775efad..a2c390eeae 100644 --- a/test/TypeScript/src/lib/Count.lf +++ b/test/TypeScript/src/lib/Count.lf @@ -1,11 +1,10 @@ target TypeScript; -reactor Count { - output c:number; - timer t(0, 1 sec); - state i:number(0); - reaction(t) -> c {= - i++; - c = i; +reactor Count(offset:time(0), period:time(1 sec)) { + output out:number; + timer t(offset, period); + state count:number(1); + reaction(t) -> out {= + out = count++; =} } \ No newline at end of file diff --git a/test/TypeScript/src/lib/TestCount.lf b/test/TypeScript/src/lib/TestCount.lf new file mode 100644 index 0000000000..6d75554872 --- /dev/null +++ b/test/TypeScript/src/lib/TestCount.lf @@ -0,0 +1,29 @@ +/** + * Test that a counting sequence of inputs starts with the specified start + * parameter value, increments by the specified stride, and receives the + * specified number of inputs. + * + * @param start The starting value for the expected inputs. Default is 1. + * @param stride The increment for the inputs. Default is 1. + * @param numInputs The number of inputs expected. Default is 1. + */ +target TypeScript; +reactor TestCount(start:number(1), stride:number(1), numInputs:number(1)) { + state count:number(start); + state inputsReceived:number(0); + input inp:number; + reaction(inp) {= + console.log("Received " + inp + "."); + if (inp != count) { + util.requestErrorStop("Expected " + count + "."); + } + count += stride; + inputsReceived++; + =} + reaction(shutdown) {= + console.log("Shutdown invoked."); + if (inputsReceived != numInputs) { + console.log("ERROR: Expected to receive " + numInputs + " inputs, but got " + inputsReceived + "."); + } + =} +} \ No newline at end of file diff --git a/test/TypeScript/src/multiport/BankMulticast.lf b/test/TypeScript/src/multiport/BankMulticast.lf new file mode 100644 index 0000000000..8e00efc69f --- /dev/null +++ b/test/TypeScript/src/multiport/BankMulticast.lf @@ -0,0 +1,37 @@ +/** + * This tests that a contained bank can send not only to a local connection, but also + * multicast via the container's output port. + */ +target TypeScript { + timeout: 3 sec +} + +import Count from "../lib/Count.lf" +import TestCount from "../lib/TestCount.lf" + +reactor Foo { + input inp:number; + output out:number; + + c = new Count(); + c.out -> out; + + d = new TestCount(numInputs = 4); + inp -> d.inp; +} + +reactor Bar { + output[4] out:number; + + foo = new[4] Foo(); + + foo.out -> foo.inp; + foo.out -> out; +} + +main reactor { + bar = new Bar(); + + d = new[4] TestCount(numInputs = 4); + bar.out -> d.inp; +} diff --git a/test/TypeScript/src/multiport/BankSelfBroadcast.lf b/test/TypeScript/src/multiport/BankSelfBroadcast.lf new file mode 100644 index 0000000000..7f50088749 --- /dev/null +++ b/test/TypeScript/src/multiport/BankSelfBroadcast.lf @@ -0,0 +1,43 @@ +/** + * Test a bank of reactors that broadcast a single output + * back to a multiport input of the same reactors in the bank + * so that each reactor in the bank receives the output + * produced by itself and each other reactor. + * + * @author Edward A. Lee + * @author Christian Menard + * @author Hokeun Kim + */ +target TypeScript; +reactor A { + input[4] inp:number; + output out:number; + state received:boolean(false); + reaction(startup) -> out {= + out = this.getBankIndex(); + =} + reaction(inp) {= + for (let i = 0; i < inp.length; i++) { + if (inp[i] !== undefined) { + console.log("Reactor " + this.getBankIndex() + " received " + + inp[i] + " on channel " + i); + if (inp[i] != i) { + util.requestErrorStop("ERROR: Expected " + i); + } + received = true; + } else { + console.log("Reactor " + this.getBankIndex() + " channel " + i + " is absent."); + util.requestErrorStop("ERROR: Expected " + i); + } + } + =} + reaction(shutdown) {= + if (!received) { + util.requestErrorStop("ERROR: No inputs received."); + } + =} +} +main reactor { + a = new[4] A(); + (a.out)+ -> a.inp; +} \ No newline at end of file diff --git a/test/TypeScript/src/multiport/Broadcast.lf b/test/TypeScript/src/multiport/Broadcast.lf new file mode 100644 index 0000000000..46e229e562 --- /dev/null +++ b/test/TypeScript/src/multiport/Broadcast.lf @@ -0,0 +1,26 @@ +target TypeScript; + +reactor Source { + output out:number; + + reaction (startup) -> out {= + out = 42; + =} +} + +reactor Sink { + input inp:number; + + reaction (inp) {= + console.log("Received " + inp); + if (inp != 42) { + util.requestErrorStop("Error: expected " + 42); + } + =} +} + +main reactor { + source = new Source(); + sink = new[4] Sink(); + (source.out)+ -> sink.inp; +} From a1d9616dd8b32f5a857585b5436f96f0b5d6f0b6 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Thu, 17 Feb 2022 19:16:15 -0800 Subject: [PATCH 27/64] Fix CountTest.lf --- test/TypeScript/src/CountTest.lf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/TypeScript/src/CountTest.lf b/test/TypeScript/src/CountTest.lf index 3705a04d06..9a69b080c0 100644 --- a/test/TypeScript/src/CountTest.lf +++ b/test/TypeScript/src/CountTest.lf @@ -17,5 +17,5 @@ reactor Test { main reactor CountTest { count = new Count(); test = new Test(); - count.c -> test.c; + count.out -> test.c; } From c1f762e6cbc2f94968b85ad61d31a6fe3cb95e84 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Thu, 17 Feb 2022 21:13:36 -0800 Subject: [PATCH 28/64] Add MultiportToMultiport2After.lf and support for generic type mapping (T -> numeric conversion for _lf_GenDelay actor) --- org.lflang/src/lib/ts/reactor-ts | 2 +- .../generator/ts/TSConnectionGenerator.kt | 23 ++++++++--- .../generator/ts/TSInstanceGenerator.kt | 28 ++++++++----- .../multiport/MultiportToMultiport2After.lf | 40 +++++++++++++++++++ 4 files changed, 76 insertions(+), 17 deletions(-) create mode 100644 test/TypeScript/src/multiport/MultiportToMultiport2After.lf diff --git a/org.lflang/src/lib/ts/reactor-ts b/org.lflang/src/lib/ts/reactor-ts index 6d2659c601..996d31dcf7 160000 --- a/org.lflang/src/lib/ts/reactor-ts +++ b/org.lflang/src/lib/ts/reactor-ts @@ -1 +1 @@ -Subproject commit 6d2659c601a4e1bba8c289d9c66919860b2399c8 +Subproject commit 996d31dcf7a417f2279eec1ad2d1bdd4f5babe39 diff --git a/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt index e97e9fe744..049e3f770d 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt @@ -7,7 +7,9 @@ import org.lflang.isInput import org.lflang.isMultiport import org.lflang.lf.Connection import org.lflang.lf.Port +import org.lflang.lf.Reactor import org.lflang.lf.VarRef +import org.lflang.toText import java.util.* /** @@ -20,16 +22,20 @@ class TSConnectionGenerator ( ) { // There is no generateClassProperties() for connections - private fun getPortTypeName(port: Port) = - "${if(port.isInput) "__In" else "__Out"}${if(port.isMultiport) "Multi" else ""}Port<${getPortType(port)}>" + private fun getPortTypeName(port: Port, genericTypeMap: HashMap): String { + var portType = getPortType(port) + portType = genericTypeMap[portType]?: portType + return "${if(port.isInput) "__In" else "__Out"}" + + "${if(port.isMultiport) "Multi" else ""}Port<$portType>" + } - private fun getPortName(port: VarRef): String { + private fun getPortName(port: VarRef, genericTypeMap: HashMap): String { var portName = "" if (port.container != null) { if (port.container.isBank) { portName += "...this.${port.container.name}.all().reduce(" + "(__acc, __val) => __acc.concat(__val.${port.variable.name}), " + - "new Array<${getPortTypeName(port.variable as Port)}>(0))" + "new Array<${getPortTypeName(port.variable as Port, genericTypeMap)}>(0))" return portName } portName += port.container.name + "." @@ -40,8 +46,15 @@ class TSConnectionGenerator ( private fun getPortNames(ports: List): List { var portNames = LinkedList() + var genericTypeMap: HashMap? = null for (varRef in ports) { - portNames.add(getPortName(varRef)) + if (genericTypeMap == null) { + genericTypeMap = HashMap() + } + for ((index, typeParam) in (varRef.container.reactorClass as Reactor).typeParms.withIndex()) { + genericTypeMap[typeParam.toText().split(" ")[0]] = varRef.container.typeParms[index].toText() + } + portNames.add(getPortName(varRef, genericTypeMap)) } return portNames } diff --git a/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt index 00f6a8f37a..797c498b98 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt @@ -6,6 +6,7 @@ import org.lflang.isBank import org.lflang.lf.Instantiation import org.lflang.lf.Parameter import org.lflang.lf.Reactor +import org.lflang.lf.TypeParm import org.lflang.reactor import org.lflang.toDefinition import org.lflang.toText @@ -43,21 +44,25 @@ class TSInstanceGenerator ( return tsReactorGenerator.getTargetInitializerHelper(param, getInitializerList(param, i)) } - private fun getChildReactorTypeParams(childReactor: Instantiation): String = - if (childReactor.typeParms.isEmpty()) {""} else { - childReactor.typeParms.joinToString(", ", "<", ">") { it.toText() }} + private fun getTypeParams(typeParms: List): String = + if (typeParms.isEmpty()) {""} else { + typeParms.joinToString(", ", "<", ">") { it.toText() }} + + private fun getReactorParameterList(parameters: List): String = + if (parameters.isEmpty()) { "[__Reactor]" } else { + parameters.joinToString(", ", "[__Reactor, ", "]") { it.getTargetType() }} + fun generateClassProperties(): String { val childReactorClassProperties = LinkedList() for (childReactor in childReactors) { if (childReactor.isBank) { - val childReactorParamTypes = - childReactor.reactor.parameters.joinToString(", ", "[", "]") { it.getTargetType() } childReactorClassProperties.add("${childReactor.name}: " + - "__Bank<${childReactor.reactorClass.name}${getChildReactorTypeParams(childReactor)}, " + - "$childReactorParamTypes>") + "__Bank<${childReactor.reactorClass.name}${getTypeParams(childReactor.typeParms)}, " + + "${getReactorParameterList(childReactor.reactor.parameters)}>") } else { - childReactorClassProperties.add("${childReactor.name}: ${childReactor.reactorClass.name}${getChildReactorTypeParams(childReactor)}") + childReactorClassProperties.add("${childReactor.name}: " + + "${childReactor.reactorClass.name}${getTypeParams(childReactor.typeParms)}") } } return childReactorClassProperties.joinToString("\n") @@ -75,9 +80,10 @@ class TSInstanceGenerator ( if (childReactor.isBank) { childReactorInstantiations.add( "this.${childReactor.name} = " + - "new __Bank" + - "(this, ${childReactor.widthSpec.toTSCode()}, " + - "${childReactor.reactorClass.name}${getChildReactorTypeParams(childReactor)}, " + + "new __Bank<${childReactor.reactorClass.name}${getTypeParams(childReactor.typeParms)}, " + + "${getReactorParameterList(childReactor.reactor.parameters)}>" + + "(${childReactor.widthSpec.toTSCode()}, " + + "${childReactor.reactorClass.name}, " + "$childReactorArguments)") } else { childReactorInstantiations.add( diff --git a/test/TypeScript/src/multiport/MultiportToMultiport2After.lf b/test/TypeScript/src/multiport/MultiportToMultiport2After.lf new file mode 100644 index 0000000000..208c1686eb --- /dev/null +++ b/test/TypeScript/src/multiport/MultiportToMultiport2After.lf @@ -0,0 +1,40 @@ +// Test multiport to multiport connections. +// See also MultiportToMultiport. +target TypeScript; + +reactor Source(width:number(2)) { + output[width] out:number; + reaction (startup) -> out {= + for (let i = 0; i < out.length; i++) { + out[i] = i; + } + =} +} + +reactor Destination(width:number(2)) { + input[width] inp:number; + reaction (inp) {= + for (let i = 0; i < inp.length; i++) { + if (inp[i] !== undefined) { + let value = inp[i]; + console.log("Received on channel " + i + ": " + value); + // NOTE: For testing purposes, this assumes the specific + // widths instantiated below. + if (value != i % 3) { + util.requestErrorStop("ERROR: expected " + i % 3); + } + } + } + let elapsedTime = util.getElapsedLogicalTime(); + if (!elapsedTime.isEqualTo(TimeValue.msec(1000))) { + util.requestErrorStop("ERROR: Expected to receive input after one second."); + } + =} +} + +main reactor { + a1 = new Source(width = 3); + a2 = new Source(width = 2); + b = new Destination(width = 5); + a1.out, a2.out -> b.inp after 1 sec; +} From 6fab59ae64673d9e103914342680f517c88d58f5 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Thu, 17 Feb 2022 21:25:50 -0800 Subject: [PATCH 29/64] Add BankToBank.lf test. --- .../generator/ts/TSConnectionGenerator.kt | 2 +- test/TypeScript/src/multiport/BankToBank.lf | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 test/TypeScript/src/multiport/BankToBank.lf diff --git a/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt index 049e3f770d..cb623109ba 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt @@ -25,7 +25,7 @@ class TSConnectionGenerator ( private fun getPortTypeName(port: Port, genericTypeMap: HashMap): String { var portType = getPortType(port) portType = genericTypeMap[portType]?: portType - return "${if(port.isInput) "__In" else "__Out"}" + + return (if(port.isInput) "__In" else "__Out") + "${if(port.isMultiport) "Multi" else ""}Port<$portType>" } diff --git a/test/TypeScript/src/multiport/BankToBank.lf b/test/TypeScript/src/multiport/BankToBank.lf new file mode 100644 index 0000000000..9d6535cc69 --- /dev/null +++ b/test/TypeScript/src/multiport/BankToBank.lf @@ -0,0 +1,37 @@ + // Check bank of reactors sending to bank of reactors. +target TypeScript { + timeout: 2 sec +}; +reactor Source { + timer t(0, 200 msec); + output out:number; + state s:number(0); + reaction(t) -> out {= + out = s; + s += this.getBankIndex(); + =} +} +reactor Destination { + state s:number(0); + input inp:number; + reaction(inp) {= + console.log("Destination " + this.getBankIndex() + " received: " + inp); + if (inp != s) { + util.requestErrorStop("ERROR: Expected " + s + "."); + } + s += this.getBankIndex(); + =} + reaction(shutdown) {= + if (s == 0 && this.getBankIndex() != 0) { + util.requestErrorStop("ERROR: Destination " + this.getBankIndex() + " received no input!"); + } + console.log("Success."); + =} +} + +main reactor BankToBank(width:number(4)) { + // FIXME: Should set the width to "width" rather than "4". + a = new[4] Source(); + b = new[4] Destination(); + a.out -> b.inp; +} From 91d8f12d0c501f4b25bfb8061d78b673fdba52a8 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Tue, 22 Feb 2022 14:06:30 -0800 Subject: [PATCH 30/64] Fix code generation for multiports and banks to make them work with updated reactor-ts. --- org.lflang/src/lib/ts/reactor-ts | 2 +- org.lflang/src/org/lflang/generator/ts/TSGenerator.kt | 8 ++++---- .../org/lflang/generator/ts/TSImportPreambleGenerator.kt | 7 +++++-- .../src/org/lflang/generator/ts/TSReactionGenerator.kt | 4 ++-- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/org.lflang/src/lib/ts/reactor-ts b/org.lflang/src/lib/ts/reactor-ts index 996d31dcf7..f70086b870 160000 --- a/org.lflang/src/lib/ts/reactor-ts +++ b/org.lflang/src/lib/ts/reactor-ts @@ -1 +1 @@ -Subproject commit 996d31dcf7a417f2279eec1ad2d1bdd4f5babe39 +Subproject commit f70086b870d0a2d178f234206379dc11f833873d diff --git a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt index 2bfe98575c..6eccde116b 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt @@ -96,10 +96,10 @@ class TSGenerator( * Files to be copied from the reactor-ts submodule into the generated * source directory. */ - val RUNTIME_FILES = arrayOf("bank.ts", "cli.ts", "command-line-args.d.ts", - "command-line-usage.d.ts", "component.ts", "federation.ts", "reaction.ts", - "reactor.ts", "microtime.d.ts", "nanotimer.d.ts", "time.ts", "ulog.d.ts", - "util.ts") + val RUNTIME_FILES = arrayOf("action.ts", "bank.ts", "cli.ts", "command-line-args.d.ts", + "command-line-usage.d.ts", "component.ts", "event.ts", "federation.ts", "reaction.ts", + "reactor.ts", "microtime.d.ts", "nanotimer.d.ts", "port.ts", "state.ts", "time.ts", + "trigger.ts", "ulog.d.ts", "util.ts") private val VG = ValueGenerator(::timeInTargetLanguage) { param -> "this.${param.name}.get()" } diff --git a/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt index f6ccf5586e..fa13f58683 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt @@ -51,10 +51,13 @@ class TSImportPreambleGenerator( const val DEFAULT_IMPORTS = """ |import commandLineArgs from 'command-line-args' |import commandLineUsage from 'command-line-usage' - |import {Args as __Args, Present, Parameter as __Parameter, State as __State, Variable as __Variable, Read, Triggers as __Triggers, ReadWrite, Write, Action as __Action, Startup as __Startup, Sched, Timer as __Timer, Reactor as __Reactor, Port as __Port, OutPort as __OutPort, InPort as __InPort, App as __App, InMultiPort as __InMultiPort, OutMultiPort as __OutMultiPort} from './core/reactor' + |import {Args as __Args, Present, Parameter as __Parameter, Variable as __Variable, Read, Triggers as __Triggers, ReadWrite, Write, Sched, Timer as __Timer, Reactor as __Reactor, OutPort as __OutPort, InPort as __InPort, App as __App} from './core/reactor' + |import {Action as __Action, Startup as __Startup} from './core/action' |import {Bank as __Bank} from './core/bank' - |import {Reaction as __Reaction} from './core/reaction' |import {FederatedApp as __FederatedApp} from './core/federation' + |import {Port as __Port, InMultiPort as __InMultiPort, OutMultiPort as __OutMultiPort} from './core/port' + |import {Reaction as __Reaction} from './core/reaction' + |import {State as __State} from './core/state' |import {TimeUnit, TimeValue, Tag as __Tag, Origin as __Origin} from './core/time' |import {Log} from './core/util' |import {ProcessedCommandLineArgs as __ProcessedCommandLineArgs, CommandLineOptionDefs as __CommandLineOptionDefs, CommandLineUsageDefs as __CommandLineUsageDefs, CommandLineOptionSpec as __CommandLineOptionSpec, unitBasedTimeValueCLAType as __unitBasedTimeValueCLAType, booleanCLAType as __booleanCLAType} from './core/cli' diff --git a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt index 957702e4b2..b68e046299 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt @@ -118,7 +118,7 @@ class TSReactionGenerator( for (trigger in reaction.triggers) { if (trigger is VarRef) { if (trigger.variable.isMultiport) { - reactionTriggers.add("this.${trigger.generateVarRef()}.ports") + reactionTriggers.add("this.${trigger.generateVarRef()}.channels()") } else { reactionTriggers.add("this.${trigger.generateVarRef()}") } @@ -242,7 +242,7 @@ class TSReactionGenerator( if (trigOrSource.variable.isMultiport) { val inputPort = trigOrSource.variable as Port reactPrologue.add( - "let ${inputPort.name} = ${generateArg(trigOrSource)}.getValueArray();") + "let ${inputPort.name} = ${generateArg(trigOrSource)}.values();") } else { reactPrologue.add("let ${trigOrSource.variable.name} = ${generateArg(trigOrSource)}.get();") } From 2d89ce4bfebf3a8edd2997b7cbd784d1ebc697ff Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Thu, 24 Feb 2022 02:03:01 -0800 Subject: [PATCH 31/64] Fix imports and code generation for multiport as a trigger. --- org.lflang/src/lib/ts/reactor-ts | 2 +- org.lflang/src/org/lflang/generator/ts/TSGenerator.kt | 6 +++--- .../org/lflang/generator/ts/TSImportPreambleGenerator.kt | 6 ++++-- .../src/org/lflang/generator/ts/TSReactionGenerator.kt | 6 +----- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/org.lflang/src/lib/ts/reactor-ts b/org.lflang/src/lib/ts/reactor-ts index f70086b870..5dd7175ac2 160000 --- a/org.lflang/src/lib/ts/reactor-ts +++ b/org.lflang/src/lib/ts/reactor-ts @@ -1 +1 @@ -Subproject commit f70086b870d0a2d178f234206379dc11f833873d +Subproject commit 5dd7175ac224fc58fdf482facd9f96281e5ce8a1 diff --git a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt index 6eccde116b..d91c096daa 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt @@ -97,9 +97,9 @@ class TSGenerator( * source directory. */ val RUNTIME_FILES = arrayOf("action.ts", "bank.ts", "cli.ts", "command-line-args.d.ts", - "command-line-usage.d.ts", "component.ts", "event.ts", "federation.ts", "reaction.ts", - "reactor.ts", "microtime.d.ts", "nanotimer.d.ts", "port.ts", "state.ts", "time.ts", - "trigger.ts", "ulog.d.ts", "util.ts") + "command-line-usage.d.ts", "component.ts", "event.ts", "federation.ts", "internal.ts", + "reaction.ts", "reactor.ts", "microtime.d.ts", "multiport.ts", "nanotimer.d.ts", "port.ts", + "state.ts", "time.ts", "trigger.ts", "types.ts", "ulog.d.ts", "util.ts") private val VG = ValueGenerator(::timeInTargetLanguage) { param -> "this.${param.name}.get()" } diff --git a/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt index fa13f58683..0fc6bbdefd 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt @@ -51,14 +51,16 @@ class TSImportPreambleGenerator( const val DEFAULT_IMPORTS = """ |import commandLineArgs from 'command-line-args' |import commandLineUsage from 'command-line-usage' - |import {Args as __Args, Present, Parameter as __Parameter, Variable as __Variable, Read, Triggers as __Triggers, ReadWrite, Write, Sched, Timer as __Timer, Reactor as __Reactor, OutPort as __OutPort, InPort as __InPort, App as __App} from './core/reactor' + |import {Parameter as __Parameter, Timer as __Timer, Reactor as __Reactor, App as __App} from './core/reactor' |import {Action as __Action, Startup as __Startup} from './core/action' |import {Bank as __Bank} from './core/bank' |import {FederatedApp as __FederatedApp} from './core/federation' - |import {Port as __Port, InMultiPort as __InMultiPort, OutMultiPort as __OutMultiPort} from './core/port' + |import {InPort as __InPort, OutPort as __OutPort, Port as __Port} from './core/port' + |import {InMultiPort as __InMultiPort, OutMultiPort as __OutMultiPort} from './core/multiport' |import {Reaction as __Reaction} from './core/reaction' |import {State as __State} from './core/state' |import {TimeUnit, TimeValue, Tag as __Tag, Origin as __Origin} from './core/time' + |import {Args as __Args, Variable as __Variable, Triggers as __Triggers, Present, Read, Write, ReadWrite, Sched} from './core/types' |import {Log} from './core/util' |import {ProcessedCommandLineArgs as __ProcessedCommandLineArgs, CommandLineOptionDefs as __CommandLineOptionDefs, CommandLineUsageDefs as __CommandLineUsageDefs, CommandLineOptionSpec as __CommandLineOptionSpec, unitBasedTimeValueCLAType as __unitBasedTimeValueCLAType, booleanCLAType as __booleanCLAType} from './core/cli' |""" diff --git a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt index b68e046299..e0e2d20c1f 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt @@ -117,11 +117,7 @@ class TSReactionGenerator( val reactionTriggers = StringJoiner(",\n") for (trigger in reaction.triggers) { if (trigger is VarRef) { - if (trigger.variable.isMultiport) { - reactionTriggers.add("this.${trigger.generateVarRef()}.channels()") - } else { - reactionTriggers.add("this.${trigger.generateVarRef()}") - } + reactionTriggers.add("this.${trigger.generateVarRef()}") } else if (trigger.isStartup) { reactionTriggers.add("this.startup") } else if (trigger.isShutdown) { From 291736f8b22d240dfa4531545f6852e7f17c43c6 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Sat, 26 Feb 2022 14:53:25 -0800 Subject: [PATCH 32/64] Fix code generation for multiports so that it uses allWritable() interface for OutMultiPort. --- org.lflang/src/lib/ts/reactor-ts | 2 +- org.lflang/src/org/lflang/generator/ts/TSExtensions.kt | 4 ++-- org.lflang/src/org/lflang/generator/ts/TSGenerator.kt | 2 +- .../org/lflang/generator/ts/TSImportPreambleGenerator.kt | 2 +- .../src/org/lflang/generator/ts/TSReactionGenerator.kt | 6 +++--- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/org.lflang/src/lib/ts/reactor-ts b/org.lflang/src/lib/ts/reactor-ts index 5dd7175ac2..226ee69a54 160000 --- a/org.lflang/src/lib/ts/reactor-ts +++ b/org.lflang/src/lib/ts/reactor-ts @@ -1 +1 @@ -Subproject commit 5dd7175ac224fc58fdf482facd9f96281e5ce8a1 +Subproject commit 226ee69a54f1b3423bdf7f26bbdd15633b8a0fbc diff --git a/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt b/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt index 968f958df3..33061cc12b 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt @@ -18,10 +18,10 @@ fun WidthSpec.toTSCode(): String = terms.joinToString(" + ") { it.parameter != null -> "${it.parameter.name}" it.port != null -> with(it.port) { if (container?.isBank == true) { - if ((variable as Port).isMultiport) "this.${container.name}.all().length * this.${container.name}.all()[0].${variable.name}.width())" + if ((variable as Port).isMultiport) "this.${container.name}.all().length * this.${container.name}.all()[0].${variable.name}.width)" else "this.${container.name}.all().length" } else { - if ((variable as Port).isMultiport) "this.${container.name}.${variable.name}.width()" + if ((variable as Port).isMultiport) "this.${container.name}.${variable.name}.width" else "1" } } diff --git a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt index d91c096daa..beb9bc0bb9 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt @@ -99,7 +99,7 @@ class TSGenerator( val RUNTIME_FILES = arrayOf("action.ts", "bank.ts", "cli.ts", "command-line-args.d.ts", "command-line-usage.d.ts", "component.ts", "event.ts", "federation.ts", "internal.ts", "reaction.ts", "reactor.ts", "microtime.d.ts", "multiport.ts", "nanotimer.d.ts", "port.ts", - "state.ts", "time.ts", "trigger.ts", "types.ts", "ulog.d.ts", "util.ts") + "state.ts", "strings.ts", "time.ts", "trigger.ts", "types.ts", "ulog.d.ts", "util.ts") private val VG = ValueGenerator(::timeInTargetLanguage) { param -> "this.${param.name}.get()" } diff --git a/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt index 0fc6bbdefd..a80a6d832b 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt @@ -60,7 +60,7 @@ class TSImportPreambleGenerator( |import {Reaction as __Reaction} from './core/reaction' |import {State as __State} from './core/state' |import {TimeUnit, TimeValue, Tag as __Tag, Origin as __Origin} from './core/time' - |import {Args as __Args, Variable as __Variable, Triggers as __Triggers, Present, Read, Write, ReadWrite, Sched} from './core/types' + |import {Args as __Args, Variable as __Variable, Triggers as __Triggers, Present, Read, Write, ReadWrite, MultiReadWrite, Sched} from './core/types' |import {Log} from './core/util' |import {ProcessedCommandLineArgs as __ProcessedCommandLineArgs, CommandLineOptionDefs as __CommandLineOptionDefs, CommandLineUsageDefs as __CommandLineUsageDefs, CommandLineOptionSpec as __CommandLineOptionSpec, unitBasedTimeValueCLAType as __unitBasedTimeValueCLAType, booleanCLAType as __booleanCLAType} from './core/cli' |""" diff --git a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt index e0e2d20c1f..790cfa0c5b 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt @@ -270,7 +270,7 @@ class TSReactionGenerator( } else if (effect.variable is Port){ val outputPort = effect.variable as Port if (outputPort.isMultiport) { - reactSignatureElement += ": __OutMultiPort<${getPortType(effect.variable as Port)}>" + reactSignatureElement += ": MultiReadWrite<${getPortType(effect.variable as Port)}>" } else { reactSignatureElement += ": ReadWrite<${getPortType(effect.variable as Port)}>" } @@ -280,7 +280,7 @@ class TSReactionGenerator( """ |${outputPort.name}.forEach((element, index) => { | if (element !== undefined) { - | __${outputPort.name}.writablePorts[index].set(element); + | __${outputPort.name}.set(index, element); | } |});""".trimMargin() }) @@ -303,7 +303,7 @@ class TSReactionGenerator( } else if (effect.variable is Port) { val port = effect.variable as Port if (port.isMultiport) { - reactFunctArgs.add("this.${effect.generateVarRef()}") + reactFunctArgs.add("this.allWritable($functArg)") } else { reactFunctArgs.add("this.writable($functArg)") } From 5c4cae1110f56c2ff9a5bd563530d09b7031c393 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Sat, 26 Feb 2022 15:20:22 -0800 Subject: [PATCH 33/64] Fix error in generic type mapping. --- .../src/org/lflang/generator/ts/TSConnectionGenerator.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt index cb623109ba..ebb3794b70 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt @@ -51,8 +51,10 @@ class TSConnectionGenerator ( if (genericTypeMap == null) { genericTypeMap = HashMap() } - for ((index, typeParam) in (varRef.container.reactorClass as Reactor).typeParms.withIndex()) { - genericTypeMap[typeParam.toText().split(" ")[0]] = varRef.container.typeParms[index].toText() + if (varRef.container != null) { + for ((index, typeParam) in (varRef.container.reactorClass as Reactor).typeParms.withIndex()) { + genericTypeMap[typeParam.toText().split(" ")[0]] = varRef.container.typeParms[index].toText() + } } portNames.add(getPortName(varRef, genericTypeMap)) } From bcde86d8f2ddc2788204b785a0ff26c49342d69c Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Sat, 26 Feb 2022 15:37:10 -0800 Subject: [PATCH 34/64] Add FullyConnected.lf test. --- .../src/multiport/FullyConnected.lf | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 test/TypeScript/src/multiport/FullyConnected.lf diff --git a/test/TypeScript/src/multiport/FullyConnected.lf b/test/TypeScript/src/multiport/FullyConnected.lf new file mode 100644 index 0000000000..6621d88cd5 --- /dev/null +++ b/test/TypeScript/src/multiport/FullyConnected.lf @@ -0,0 +1,42 @@ +target TypeScript; + +reactor Node( + numNodes: number(4) +) { + input[numNodes] inp: number; + output out: number; + + state received: boolean(false); + + reaction (startup) -> out{= + console.log("Hello from node " + this.getBankIndex() + "!"); + // broadcast my ID to everyone + out = this.getBankIndex(); + =} + + reaction (inp) {= + console.log("Node " + this.getBankIndex() + " received messages from "); + let count = 0; + for (let i = 0; i < inp.length; i++) { + let val = inp[i] + if (val !== undefined) { + received = true; + count++; + console.log(val + ", "); + } + } + console.log(""); + if (count != numNodes) { + util.requestErrorStop("Received fewer messages than expected!"); + } + =} + reaction (shutdown) {= + if (!received) { + util.reportError("Received no input!"); + } + =} +} +main reactor(numNodes: number(4)) { + nodes = new[numNodes] Node(numNodes=numNodes); + (nodes.out)+ -> nodes.inp; +} From b3ad432ce806b26af0da4905eea8bc09b9f1636c Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Sat, 26 Feb 2022 20:36:36 -0800 Subject: [PATCH 35/64] Fix lsp-test error (Mixed spaces and tabs. [BankToBank.ts]) --- test/TypeScript/src/multiport/BankToBank.lf | 38 ++++++++++----------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/test/TypeScript/src/multiport/BankToBank.lf b/test/TypeScript/src/multiport/BankToBank.lf index 9d6535cc69..52a6d2995a 100644 --- a/test/TypeScript/src/multiport/BankToBank.lf +++ b/test/TypeScript/src/multiport/BankToBank.lf @@ -3,25 +3,25 @@ target TypeScript { timeout: 2 sec }; reactor Source { - timer t(0, 200 msec); - output out:number; - state s:number(0); - reaction(t) -> out {= + timer t(0, 200 msec); + output out:number; + state s:number(0); + reaction(t) -> out {= out = s; - s += this.getBankIndex(); - =} + s += this.getBankIndex(); + =} } reactor Destination { - state s:number(0); - input inp:number; - reaction(inp) {= - console.log("Destination " + this.getBankIndex() + " received: " + inp); - if (inp != s) { - util.requestErrorStop("ERROR: Expected " + s + "."); - } - s += this.getBankIndex(); - =} - reaction(shutdown) {= + state s:number(0); + input inp:number; + reaction(inp) {= + console.log("Destination " + this.getBankIndex() + " received: " + inp); + if (inp != s) { + util.requestErrorStop("ERROR: Expected " + s + "."); + } + s += this.getBankIndex(); + =} + reaction(shutdown) {= if (s == 0 && this.getBankIndex() != 0) { util.requestErrorStop("ERROR: Destination " + this.getBankIndex() + " received no input!"); } @@ -31,7 +31,7 @@ reactor Destination { main reactor BankToBank(width:number(4)) { // FIXME: Should set the width to "width" rather than "4". - a = new[4] Source(); - b = new[4] Destination(); - a.out -> b.inp; + a = new[4] Source(); + b = new[4] Destination(); + a.out -> b.inp; } From 56ae311c689b1ae8db6c53fea9def36f7cdf6f55 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Sat, 26 Feb 2022 22:47:50 -0800 Subject: [PATCH 36/64] Add 3 more TypeScript multiport tests --- .../multiport/MultiportFromBankHierarchy.lf | 41 ++++++++++++++ .../src/multiport/MultiportFromHierarchy.lf | 53 +++++++++++++++++++ .../src/multiport/MultiportToBankHierarchy.lf | 41 ++++++++++++++ 3 files changed, 135 insertions(+) create mode 100644 test/TypeScript/src/multiport/MultiportFromBankHierarchy.lf create mode 100644 test/TypeScript/src/multiport/MultiportFromHierarchy.lf create mode 100644 test/TypeScript/src/multiport/MultiportToBankHierarchy.lf diff --git a/test/TypeScript/src/multiport/MultiportFromBankHierarchy.lf b/test/TypeScript/src/multiport/MultiportFromBankHierarchy.lf new file mode 100644 index 0000000000..05ad5de638 --- /dev/null +++ b/test/TypeScript/src/multiport/MultiportFromBankHierarchy.lf @@ -0,0 +1,41 @@ +// Check multiport output to bank of recipients. +// Here, the bank is smaller than the width of the sending port. +target TypeScript { + timeout: 2 sec +}; +reactor Source { + output out:number; + reaction(startup) -> out {= + out = this.getBankIndex(); + =} +} +reactor Container { + output[3] out:number; + s = new[3] Source(); + s.out -> out; +} +reactor Destination { + input[3] inp:number; + state received:boolean(false); + reaction(inp) {= + for (let i = 0; i < inp.length; i++) { + console.log("Destination channel " + i + " received " + inp[i] + "."); + if (i != inp[i]) { + util.requestErrorStop("ERROR: Expected " + i + "."); + } + } + received = true; + =} + reaction(shutdown) {= + if (!received) { + util.reportError("ERROR: Destination received no input!"); + } + console.log("Success."); + =} +} + +main reactor { + a = new Container(); + b = new Destination(); + a.out -> b.inp; +} diff --git a/test/TypeScript/src/multiport/MultiportFromHierarchy.lf b/test/TypeScript/src/multiport/MultiportFromHierarchy.lf new file mode 100644 index 0000000000..6babf66074 --- /dev/null +++ b/test/TypeScript/src/multiport/MultiportFromHierarchy.lf @@ -0,0 +1,53 @@ +// Check multiport output to multiport input, where the former is a hierarchical reactor. +target TypeScript { + timeout: 2 sec +}; +reactor Source(width:number(3)) { + timer t(0, 200 msec); + output[width] out:number; + state s:number(0); + reaction(t) -> out {= + for(let i = 0; i < out.length; i++) { + out[i] = s++; + } + =} +} +reactor Destination(width:number(3)) { + state s:number(6); + input[width] inp:number; + reaction(inp) {= + let sum = 0; + for (let i = 0; i < inp.length; i++) { + let val = inp[i] + if (val !== undefined) sum += val; + } + console.log("Sum of received: " + sum + "."); + if (sum != s) { + util.requestErrorStop("ERROR: Expected " + s + "."); + } + s += 16; + =} + reaction(shutdown) {= + if (s <= 6) { + util.reportError("ERROR: Destination received no input!"); + } + console.log("Success."); + =} +} +reactor Container(width:number(3)) { + output[width] out:number; + src = new InsideContainer(width = width); + src.out -> out; +} + +reactor InsideContainer(width:number(3)) { + output[width] out:number; + src = new Source(width = width); + src.out -> out; +} + +main reactor MultiportFromHierarchy(width:number(4)) { + a = new Container(width = width); + b = new Destination(width = width); + a.out -> b.inp; +} \ No newline at end of file diff --git a/test/TypeScript/src/multiport/MultiportToBankHierarchy.lf b/test/TypeScript/src/multiport/MultiportToBankHierarchy.lf new file mode 100644 index 0000000000..8104ea07ce --- /dev/null +++ b/test/TypeScript/src/multiport/MultiportToBankHierarchy.lf @@ -0,0 +1,41 @@ +// Check multiport output to bank of recipients. +// Here, the bank is smaller than the width of the sending port. +target TypeScript { + timeout: 2 sec +}; +reactor Source { + output[3] out:number; + reaction(startup) -> out {= + for(let i = 0; i < out.length; i++) { + out[i] = i; + } + =} +} +reactor Destination { + input inp:number; + state received:boolean(false); + reaction(inp) {= + console.log("Destination " + this.getBankIndex() + " received " + inp + "."); + if (this.getBankIndex() != inp) { + util.requestErrorStop("ERROR: Expected " + this.getBankIndex() + "."); + } + received = true; + =} + reaction(shutdown) {= + if (!received) { + util.reportError("ERROR: Destination " + this.getBankIndex() + " received no input!"); + } + console.log("Success."); + =} +} +reactor Container { + input[3] inp:number; + c = new[3] Destination(); + inp -> c.inp; +} + +main reactor MultiportToBankHierarchy { + a = new Source(); + b = new Container(); + a.out -> b.inp; +} \ No newline at end of file From 567085edb39d822b4115f0ecbd7d48f2ad18a7bf Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Sat, 26 Feb 2022 23:29:50 -0800 Subject: [PATCH 37/64] Add multiport support for referencing hierarchical reactors, for example, referencing multiports with a form of b.inp or b.out in the reaction. --- org.lflang/src/lib/ts/reactor-ts | 2 +- .../generator/ts/TSReactionGenerator.kt | 21 ++++++++++++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/org.lflang/src/lib/ts/reactor-ts b/org.lflang/src/lib/ts/reactor-ts index 226ee69a54..fbc49bc928 160000 --- a/org.lflang/src/lib/ts/reactor-ts +++ b/org.lflang/src/lib/ts/reactor-ts @@ -1 +1 @@ -Subproject commit 226ee69a54f1b3423bdf7f26bbdd15633b8a0fbc +Subproject commit fbc49bc9283de6b6767031a0fd99346bcaf2e692 diff --git a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt index 790cfa0c5b..56243411df 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt @@ -365,12 +365,27 @@ class TSReactionGenerator( for (entry in containerToArgs.entries) { val initializer = StringJoiner(", ") for (variable in entry.value) { - initializer.add("${variable.name}: __${entry.key.name}_${variable.name}.get()") + + initializer.add("${variable.name}: __${entry.key.name}_${variable.name}" + + if (variable.isMultiport) ".values()" else ".get()") if (variable is Input) { - reactEpilogue.add(with(PrependOperator) {""" + if (variable.isMultiport) { + reactEpilogue.add( + """ + |${entry.key.name}.${variable.name}.forEach((element, index) => { + | if (element !== undefined) { + | __${entry.key.name}_${variable.name}.set(index, element) + | } + |});""".trimMargin() + ) + } else { + reactEpilogue.add( + """ |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}}") From 62afeb012c6551b4f9a81867540131f46332bbab Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Sat, 26 Feb 2022 23:30:35 -0800 Subject: [PATCH 38/64] Add 2 multiport tests for contained reactors. --- .../src/multiport/MultiportFromReaction.lf | 40 +++++++++++++++++++ .../src/multiport/MultiportToReaction.lf | 37 +++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 test/TypeScript/src/multiport/MultiportFromReaction.lf create mode 100644 test/TypeScript/src/multiport/MultiportToReaction.lf diff --git a/test/TypeScript/src/multiport/MultiportFromReaction.lf b/test/TypeScript/src/multiport/MultiportFromReaction.lf new file mode 100644 index 0000000000..657e15eef3 --- /dev/null +++ b/test/TypeScript/src/multiport/MultiportFromReaction.lf @@ -0,0 +1,40 @@ +// Check reaction to multiport input of a contained reactor. +target TypeScript { + timeout: 2 sec +}; +reactor Destination(width:number(1)) { + state s:number(6); + input[width] inp:number; + reaction(inp) {= + let sum = 0; + for (let i = 0; i < inp.length; i++) { + let val = inp[i] + if (val !== undefined) sum += val; + } + console.log("Sum of received: " + sum + "."); + if (sum != s) { + util.requestErrorStop("ERROR: Expected " + s + "."); + } + s += 16; + =} + reaction(shutdown) {= + if (s <= 6) { + util.reportError("ERROR: Destination received no input!"); + } + console.log("Success."); + =} +} +main reactor MultiportFromReaction(width:number(4)) { + timer t(0, 200 msec); + state s:number(0); + reaction(t) -> b.inp {= + for (let i = 0; i < b.inp.length; i++) { + console.log("Before SET, b.inp[" + i + "] !== undefined has value " + b.inp[i] !== undefined); + b.inp[i] = s++; + console.log("AFTER set, b.inp[" + i + "] !== undefined has value " + b.inp[i] !== undefined); + console.log("AFTER set, b.inp[" + i + "] has value " + b.inp[i]); + } + =} + b = new Destination(width = width); +} + diff --git a/test/TypeScript/src/multiport/MultiportToReaction.lf b/test/TypeScript/src/multiport/MultiportToReaction.lf new file mode 100644 index 0000000000..2eebb116b1 --- /dev/null +++ b/test/TypeScript/src/multiport/MultiportToReaction.lf @@ -0,0 +1,37 @@ +// Check reaction to multiport output of a contained reactor. +target TypeScript { + timeout: 2 sec +}; +reactor Source(width:number(1)) { + timer t(0, 200 msec); + state s:number(0); + output[width] out:number; + reaction(t) -> out {= + console.log("Sending."); + for (let i = 0; i < out.length; i++) { + out[i] = s++; + } + =} +} +main reactor MultiportToReaction { + state s:number(6); + reaction(b.out) {= + let sum = 0; + for (let i = 0; i < b.out.length; i++) { + let val = b.out[i] + if (val !== undefined) sum += val; + } + console.log("Sum of received: " + sum + "."); + if (sum != s) { + util.requestErrorStop("ERROR: Expected " + s + "."); + } + s += 16; + =} + reaction(shutdown) {= + if (s <= 6) { + util.reportError("ERROR: Destination received no input!"); + } + console.log("Success."); + =} + b = new Source(width = 4); +} From f5de3b5fb138187a424312931f4bf24114e6d37a Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Mon, 28 Feb 2022 18:00:53 -0800 Subject: [PATCH 39/64] Add uuid to default package.json --- org.lflang/src/lib/ts/package.json | 4 +++- org.lflang/src/lib/ts/reactor-ts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/org.lflang/src/lib/ts/package.json b/org.lflang/src/lib/ts/package.json index 39d8c64e52..f8ea477625 100644 --- a/org.lflang/src/lib/ts/package.json +++ b/org.lflang/src/lib/ts/package.json @@ -12,7 +12,8 @@ "command-line-usage": "^6.1.0", "microtime": "^3.0.0", "ulog": "^2.0.0-beta.7", - "google-protobuf": "^3.7.4" + "google-protobuf": "^3.7.4", + "uuid": "^8.3.2" }, "devDependencies": { "@babel/cli": "^7.8.4", @@ -26,6 +27,7 @@ "@babel/preset-typescript": "^7.8.3", "@types/google-protobuf": "^3.7.4", "@types/node": "^13.9.2", + "@types/uuid": "^8.3.4", "@typescript-eslint/eslint-plugin": "^5.8.1", "@typescript-eslint/parser": "^5.8.1", "eslint": "^8.5.0", diff --git a/org.lflang/src/lib/ts/reactor-ts b/org.lflang/src/lib/ts/reactor-ts index fbc49bc928..681283f93b 160000 --- a/org.lflang/src/lib/ts/reactor-ts +++ b/org.lflang/src/lib/ts/reactor-ts @@ -1 +1 @@ -Subproject commit fbc49bc9283de6b6767031a0fd99346bcaf2e692 +Subproject commit 681283f93b83c2881277cf2d94df21ba0f37a592 From 7662f6fc11b29915d11d334beb422d2ddfe05357 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Mon, 28 Feb 2022 22:37:25 -0800 Subject: [PATCH 40/64] Adjust to API change for getting the width --- org.lflang/src/org/lflang/generator/ts/TSExtensions.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt b/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt index 33061cc12b..968f958df3 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt @@ -18,10 +18,10 @@ fun WidthSpec.toTSCode(): String = terms.joinToString(" + ") { it.parameter != null -> "${it.parameter.name}" it.port != null -> with(it.port) { if (container?.isBank == true) { - if ((variable as Port).isMultiport) "this.${container.name}.all().length * this.${container.name}.all()[0].${variable.name}.width)" + if ((variable as Port).isMultiport) "this.${container.name}.all().length * this.${container.name}.all()[0].${variable.name}.width())" else "this.${container.name}.all().length" } else { - if ((variable as Port).isMultiport) "this.${container.name}.${variable.name}.width" + if ((variable as Port).isMultiport) "this.${container.name}.${variable.name}.width()" else "1" } } From 5c79f0cc8ed8bc527721f2dbde16a9d50bac5c14 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Tue, 1 Mar 2022 00:14:40 -0800 Subject: [PATCH 41/64] Updated submodule --- org.lflang/src/lib/ts/reactor-ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/ts/reactor-ts b/org.lflang/src/lib/ts/reactor-ts index 681283f93b..6d0028caab 160000 --- a/org.lflang/src/lib/ts/reactor-ts +++ b/org.lflang/src/lib/ts/reactor-ts @@ -1 +1 @@ -Subproject commit 681283f93b83c2881277cf2d94df21ba0f37a592 +Subproject commit 6d0028caabfd45a53598fd1333cf46964d440b1c From 239ece7adc0009feddb2d934dbd8dd8c6ce272e9 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Tue, 1 Mar 2022 10:12:49 -0800 Subject: [PATCH 42/64] Add 4 more multiport tests for TypeScript target. --- .../src/multiport/BankToBankMultiport.lf | 41 ++++++++++++++++++ .../src/multiport/BankToBankMultiportAfter.lf | 41 ++++++++++++++++++ .../src/multiport/BroadcastAfter.lf | 39 +++++++++++++++++ .../src/multiport/BroadcastMultipleAfter.lf | 42 +++++++++++++++++++ 4 files changed, 163 insertions(+) create mode 100644 test/TypeScript/src/multiport/BankToBankMultiport.lf create mode 100644 test/TypeScript/src/multiport/BankToBankMultiportAfter.lf create mode 100644 test/TypeScript/src/multiport/BroadcastAfter.lf create mode 100644 test/TypeScript/src/multiport/BroadcastMultipleAfter.lf diff --git a/test/TypeScript/src/multiport/BankToBankMultiport.lf b/test/TypeScript/src/multiport/BankToBankMultiport.lf new file mode 100644 index 0000000000..a395f8711e --- /dev/null +++ b/test/TypeScript/src/multiport/BankToBankMultiport.lf @@ -0,0 +1,41 @@ + // Check bank of reactors sending to bank of reactors with multiports. +target TypeScript { + timeout: 2 sec +}; +reactor Source(width:number(1)) { + timer t(0, 200 msec); + output[width] out:number; + state s:number(0); + reaction(t) -> out {= + for(let i = 0; i < out.length; i++) { + out[i] = s++; + } + =} +} +reactor Destination(width:number(1)) { + state s:number(6); + input[width] inp:number; + reaction(inp) {= + let sum = 0; + for (let i = 0; i < inp.length; i++) { + let val = inp[i]; + if (val != undefined) sum += val; + } + console.log("Sum of received: " + sum + "."); + if (sum != s) { + util.requestErrorStop("ERROR: Expected " + s + "."); + } + s += 16; + =} + reaction(shutdown) {= + if (s <= 6) { + util.reportError("ERROR: Destination received no input!"); + } + console.log("Success."); + =} +} +main reactor BankToBankMultiport(bankWidth:number(4)) { + a = new[bankWidth] Source(width = 4); + b = new[bankWidth] Destination(width = 4); + a.out -> b.inp; +} \ No newline at end of file diff --git a/test/TypeScript/src/multiport/BankToBankMultiportAfter.lf b/test/TypeScript/src/multiport/BankToBankMultiportAfter.lf new file mode 100644 index 0000000000..2e9b1acc2a --- /dev/null +++ b/test/TypeScript/src/multiport/BankToBankMultiportAfter.lf @@ -0,0 +1,41 @@ + // Check bank of reactors sending to bank of reactors with multiports. +target TypeScript { + timeout: 2 sec +}; +reactor Source(width:number(1)) { + timer t(0, 200 msec); + output[width] out:number; + state s:number(0); + reaction(t) -> out {= + for(let i = 0; i < out.length; i++) { + out[i] = s++; + } + =} +} +reactor Destination(width:number(1)) { + state s:number(6); + input[width] inp:number; + reaction(inp) {= + let sum = 0; + for (let i = 0; i < inp.length; i++) { + let val = inp[i]; + if (val != undefined) sum += val; + } + console.log("Sum of received: " + sum + "."); + if (sum != s) { + util.requestErrorStop("ERROR: Expected " + s + "."); + } + s += 16; + =} + reaction(shutdown) {= + if (s <= 6) { + util.reportError("ERROR: Destination received no input!"); + } + console.log("Success."); + =} +} +main reactor (bankWidth:number(4)) { + a = new[bankWidth] Source(width = 4); + b = new[bankWidth] Destination(width = 4); + a.out -> b.inp after 200 msec; +} \ No newline at end of file diff --git a/test/TypeScript/src/multiport/BroadcastAfter.lf b/test/TypeScript/src/multiport/BroadcastAfter.lf new file mode 100644 index 0000000000..e6b205c25a --- /dev/null +++ b/test/TypeScript/src/multiport/BroadcastAfter.lf @@ -0,0 +1,39 @@ +target TypeScript { + timeout: 2 sec +}; + +reactor Source { + output out:number; + + reaction(startup) -> out {= + out = 42; + =} +} + +reactor Destination { + input inp:number; + state received:boolean(false); + reaction(inp) {= + console.log("Destination " + this.getBankIndex() + " received " + inp + "."); + if (inp != 42) { + util.requestErrorStop("ERROR: Expected 42."); + } + let elapsedTime = util.getElapsedLogicalTime(); + if (!elapsedTime.isEqualTo(TimeValue.sec(1))) { + util.requestErrorStop("ERROR: Expected to receive input after one second."); + } + received = true; + =} + reaction(shutdown) {= + if (!received) { + util.reportError("ERROR: Destination " + this.getBankIndex() + " received no input!"); + } + console.log("Success."); + =} +} + +main reactor { + a = new Source(); + b = new[4] Destination(); + (a.out)+ -> b.inp after 1 sec; +} \ No newline at end of file diff --git a/test/TypeScript/src/multiport/BroadcastMultipleAfter.lf b/test/TypeScript/src/multiport/BroadcastMultipleAfter.lf new file mode 100644 index 0000000000..f1e54ad0b3 --- /dev/null +++ b/test/TypeScript/src/multiport/BroadcastMultipleAfter.lf @@ -0,0 +1,42 @@ +target TypeScript { + timeout: 2 sec +}; + +reactor Source(value:number(42)) { + output out:number; + + reaction(startup) -> out {= + out = value; + =} +} + +reactor Destination { + input inp:number; + state received:boolean(false); + reaction(inp) {= + console.log("Destination " + this.getBankIndex() + " received " + inp + "."); + let expected = (this.getBankIndex() % 3) + 1; + if (inp != expected) { + util.requestErrorStop("ERROR: Expected " + expected + "."); + } + let elapsedTime = util.getElapsedLogicalTime(); + if (!elapsedTime.isEqualTo(TimeValue.sec(1))) { + util.requestErrorStop("ERROR: Expected to receive input after one second."); + } + received = true; + =} + reaction(shutdown) {= + if (!received) { + util.requestErrorStop("ERROR: Destination " + this.getBankIndex() + " received no input!"); + } + console.log("Success."); + =} +} + +main reactor { + a1 = new Source(value=1); + a2 = new Source(value=2); + a3 = new Source(value=3); + b = new[9] Destination(); + (a1.out, a2.out, a3.out)+ -> b.inp after 1 sec; +} From 201c3d9906fc3821ee5dbcaeb1fd8f25e33f2a79 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Tue, 1 Mar 2022 12:34:06 -0800 Subject: [PATCH 43/64] Fix error in WidthSpec.toTSCode(), add more multiport tests, remove use of temporary variables when unnecessary. --- .../org/lflang/generator/ts/TSExtensions.kt | 2 +- .../src/multiport/BankToBankMultiport.lf | 2 +- .../src/multiport/BankToBankMultiportAfter.lf | 2 +- .../src/multiport/MultiportFromBank.lf | 2 +- .../multiport/MultiportFromBankHierarchy.lf | 34 ++------ .../MultiportFromBankHierarchyAfter.lf | 13 +++ .../src/multiport/MultiportInParameterized.lf | 65 +++++++++++++++ .../src/multiport/MultiportMutableInput.lf | 45 +++++++++++ .../multiport/MultiportMutableInputArray.lf | 79 +++++++++++++++++++ .../src/multiport/MultiportToBankAfter.lf | 40 ++++++++++ .../src/multiport/MultiportToMultiport2.lf | 13 ++- 11 files changed, 256 insertions(+), 41 deletions(-) create mode 100644 test/TypeScript/src/multiport/MultiportFromBankHierarchyAfter.lf create mode 100644 test/TypeScript/src/multiport/MultiportInParameterized.lf create mode 100644 test/TypeScript/src/multiport/MultiportMutableInput.lf create mode 100644 test/TypeScript/src/multiport/MultiportMutableInputArray.lf create mode 100644 test/TypeScript/src/multiport/MultiportToBankAfter.lf diff --git a/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt b/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt index 968f958df3..4ba4790fa5 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt @@ -18,7 +18,7 @@ fun WidthSpec.toTSCode(): String = terms.joinToString(" + ") { it.parameter != null -> "${it.parameter.name}" it.port != null -> with(it.port) { if (container?.isBank == true) { - if ((variable as Port).isMultiport) "this.${container.name}.all().length * this.${container.name}.all()[0].${variable.name}.width())" + if ((variable as Port).isMultiport) "this.${container.name}.all().length * this.${container.name}.all()[0].${variable.name}.width()" else "this.${container.name}.all().length" } else { if ((variable as Port).isMultiport) "this.${container.name}.${variable.name}.width()" diff --git a/test/TypeScript/src/multiport/BankToBankMultiport.lf b/test/TypeScript/src/multiport/BankToBankMultiport.lf index a395f8711e..75e6c2d835 100644 --- a/test/TypeScript/src/multiport/BankToBankMultiport.lf +++ b/test/TypeScript/src/multiport/BankToBankMultiport.lf @@ -19,7 +19,7 @@ reactor Destination(width:number(1)) { let sum = 0; for (let i = 0; i < inp.length; i++) { let val = inp[i]; - if (val != undefined) sum += val; + if (val !== undefined) sum += val; } console.log("Sum of received: " + sum + "."); if (sum != s) { diff --git a/test/TypeScript/src/multiport/BankToBankMultiportAfter.lf b/test/TypeScript/src/multiport/BankToBankMultiportAfter.lf index 2e9b1acc2a..9f61227e06 100644 --- a/test/TypeScript/src/multiport/BankToBankMultiportAfter.lf +++ b/test/TypeScript/src/multiport/BankToBankMultiportAfter.lf @@ -19,7 +19,7 @@ reactor Destination(width:number(1)) { let sum = 0; for (let i = 0; i < inp.length; i++) { let val = inp[i]; - if (val != undefined) sum += val; + if (val !== undefined) sum += val; } console.log("Sum of received: " + sum + "."); if (sum != s) { diff --git a/test/TypeScript/src/multiport/MultiportFromBank.lf b/test/TypeScript/src/multiport/MultiportFromBank.lf index 5143df7849..30c928029c 100644 --- a/test/TypeScript/src/multiport/MultiportFromBank.lf +++ b/test/TypeScript/src/multiport/MultiportFromBank.lf @@ -9,7 +9,7 @@ reactor Source { out = this.getBankIndex(); =} } -reactor Destination(portWidth:number(2)) { +reactor Destination(portWidth:number(3)) { input[portWidth] inp:number; state received:boolean(false); reaction(inp) {= diff --git a/test/TypeScript/src/multiport/MultiportFromBankHierarchy.lf b/test/TypeScript/src/multiport/MultiportFromBankHierarchy.lf index 05ad5de638..a74709081e 100644 --- a/test/TypeScript/src/multiport/MultiportFromBankHierarchy.lf +++ b/test/TypeScript/src/multiport/MultiportFromBankHierarchy.lf @@ -2,37 +2,13 @@ // Here, the bank is smaller than the width of the sending port. target TypeScript { timeout: 2 sec -}; -reactor Source { - output out:number; - reaction(startup) -> out {= - out = this.getBankIndex(); - =} -} -reactor Container { - output[3] out:number; - s = new[3] Source(); +}; +import Source, Destination from "MultiportFromBank.lf" +reactor Container(portWidth:number(3)) { + output[portWidth] out:number; + s = new[portWidth] Source(); s.out -> out; } -reactor Destination { - input[3] inp:number; - state received:boolean(false); - reaction(inp) {= - for (let i = 0; i < inp.length; i++) { - console.log("Destination channel " + i + " received " + inp[i] + "."); - if (i != inp[i]) { - util.requestErrorStop("ERROR: Expected " + i + "."); - } - } - received = true; - =} - reaction(shutdown) {= - if (!received) { - util.reportError("ERROR: Destination received no input!"); - } - console.log("Success."); - =} -} main reactor { a = new Container(); diff --git a/test/TypeScript/src/multiport/MultiportFromBankHierarchyAfter.lf b/test/TypeScript/src/multiport/MultiportFromBankHierarchyAfter.lf new file mode 100644 index 0000000000..41e9861edd --- /dev/null +++ b/test/TypeScript/src/multiport/MultiportFromBankHierarchyAfter.lf @@ -0,0 +1,13 @@ + // Check multiport output to bank of recipients. + // Here, the bank is smaller than the width of the sending port. +target TypeScript { + timeout: 2 sec +}; +import Destination from "MultiportFromBank.lf" +import Container from "MultiportFromBankHierarchy.lf" + +main reactor MultiportFromBankHierarchyAfter { + a = new Container(portWidth = 4); + b = new Destination(portWidth = 4); + a.out -> b.inp after 1 sec; +} \ No newline at end of file diff --git a/test/TypeScript/src/multiport/MultiportInParameterized.lf b/test/TypeScript/src/multiport/MultiportInParameterized.lf new file mode 100644 index 0000000000..4ce050928c --- /dev/null +++ b/test/TypeScript/src/multiport/MultiportInParameterized.lf @@ -0,0 +1,65 @@ +// This is a version of the Threaded test that uses a multiport input at the destination. +// Its purpose is to test multiport inputs. +target TypeScript { + timeout: 2 sec +}; + +reactor Source { + timer t(0, 200 msec); + output out:number; + state s:number(0); + reaction(t) -> out {= + out = s; + s++; + =} +} +reactor Computation { + input inp:number; + output out:number; + reaction(inp) -> out {= + out = inp; + =} +} +reactor Destination(width:number(1)) { + state s:number(0); + input[width] inp:number; + reaction(inp) {= + let sum = 0; + for (let i = 0; i < inp.length; i++) { + let val = inp[i]; + if (val !== undefined) { + sum += val; + } + } + console.log("Sum of received: " + sum + "."); + if (sum != s) { + util.requestErrorStop("ERROR: Expected " + s + "."); + } + s += 4; + =} + reaction(shutdown) {= + if (s == 0) { + util.reportError("ERROR: Destination received no input!"); + } + console.log("Success."); + =} +} + +main reactor { + a = new Source(); + t1 = new Computation(); + t2 = new Computation(); + t3 = new Computation(); + t4 = new Computation(); + b = new Destination(width = 4); + a.out -> t1.inp; + a.out -> t2.inp; + a.out -> t3.inp; + a.out -> t4.inp; + t1.out, t2.out, t3.out, t4.out -> b.inp; + // I.e.: + // t1.out -> b.inp[0]; + // t2.out -> b.inp[1]; + // t3.out -> b.inp[2]; + // dt4.out -> b.inp[3]; +} \ No newline at end of file diff --git a/test/TypeScript/src/multiport/MultiportMutableInput.lf b/test/TypeScript/src/multiport/MultiportMutableInput.lf new file mode 100644 index 0000000000..387df2e24d --- /dev/null +++ b/test/TypeScript/src/multiport/MultiportMutableInput.lf @@ -0,0 +1,45 @@ +// Source produces a ints on a multiport, which it passes +// to Scale. Scale requests a writable copy. +// It modifies it and passes it to Print. It gets freed after +// Print is done with it. +target TypeScript; +reactor Source { + output[2] out:number; + reaction(startup) -> out {= + out[0] = 21; + out[1] = 42; + =} +} +// The scale parameter is just for testing. +reactor Print(scale:number(1)) { + input[2] inp:number; + reaction(inp) {= + let expected = 42; + for (let j = 0; j < 2; j++) { + console.log("Received on channel " + j + ": " + inp[j]); + if (inp[j] != expected) { + util.requestErrorStop("ERROR: Expected " + expected + "!"); + } + expected *=2; + } + =} +} + +reactor Scale(scale:number(2)) { + mutable input[2] inp:number; + output[2] out:number; + reaction(inp) -> out {= + for (let j = 0; j < 2; j++) { + // Modify the input, allowed because mutable. + (inp[j] as number) *= scale; + out[j] = inp[j] as number; + } + =} +} +main reactor { + s = new Source(); + c = new Scale(); + p = new Print(scale=2); + s.out -> c.inp; + c.out -> p.inp; +} diff --git a/test/TypeScript/src/multiport/MultiportMutableInputArray.lf b/test/TypeScript/src/multiport/MultiportMutableInputArray.lf new file mode 100644 index 0000000000..15a68648e9 --- /dev/null +++ b/test/TypeScript/src/multiport/MultiportMutableInputArray.lf @@ -0,0 +1,79 @@ +// Source produces a dynamically allocated arrays on a multiport, which it passes +// to Scale. Scale requests a writable copy, which, instead of +// copying, it just gets ownership of the original array. +// It modifies it and passes it to Print. It gets freed after +// Print is done with it. +target TypeScript; + +reactor Source { + output[2] out:{=Array=}; + reaction(startup) -> out {= + // Dynamically allocate an output array of length 3. + out[0] = new Array(3); + + // Above allocates the array, which then must be populated. + out[0][0] = 0; + out[0][1] = 1; + out[0][2] = 2; + + // Dynamically allocate an output array of length 3. + out[1] = new Array(3); + + // Above allocates the array, which then must be populated. + out[1][0] = 3; + out[1][1] = 4; + out[1][2] = 5; + =} +} +// The scale parameter is just for testing. +reactor Print(scale:number(1)) { + input[2] inp:{=Array=}; + reaction(inp) {= + let count = 0; // For testing. + let failed = false; // For testing. + for(let j = 0; j < 2; j++) { + let logString = "Received on channel " + j + ": ["; + if (inp[j] === undefined) { + continue; + } + for (let i = 0; i < (inp[j] as Array).length; i++) { + if (i > 0) logString += ", "; + logString += (inp[j] as Array)[i]; + // For testing, check whether values match expectation. + if ((inp[j] as Array)[i] != scale * count) { + failed = true; + } + count++; // For testing. + } + logString += "]"; + console.log(logString); + } + if (failed) { + util.requestErrorStop("ERROR: Value received by Print does not match expectation!"); + } + =} +} + +reactor Scale(scale:number(2)) { + mutable input[2] inp:{=Array=}; + output[2] out:{=Array=}; + reaction(inp) -> out {= + for (let j = 0; j < inp.length; j++) { + if (inp[j] === undefined) { + continue; + } + for (let i = 0; i < (inp[j] as Array).length; i++) { + (inp[j] as Array)[i] *= scale; + } + out[j] = (inp[j] as Array); + } + =} +} + +main reactor { + s = new Source(); + c = new Scale(); + p = new Print(scale=2); + s.out -> c.inp; + c.out -> p.inp; +} diff --git a/test/TypeScript/src/multiport/MultiportToBankAfter.lf b/test/TypeScript/src/multiport/MultiportToBankAfter.lf new file mode 100644 index 0000000000..3290a84fb0 --- /dev/null +++ b/test/TypeScript/src/multiport/MultiportToBankAfter.lf @@ -0,0 +1,40 @@ +// Check multiport output to bank of recipients where the width of the bank is inferred. +target TypeScript { + timeout: 2 sec +}; +reactor Source(width:number(2)) { + output[width] out:number; + reaction(startup) -> out {= + for (let i = 0; i < out.length; i++) { + out[i] = i; + } + =} +} +reactor Destination { + input inp:number; + state received:boolean(false); + reaction(inp) {= + console.log("Destination " + this.getBankIndex() + " received " + inp + "."); + if (this.getBankIndex() != inp) { + util.requestErrorStop("ERROR: Expected " + this.getBankIndex() + "."); + } + + let elapsedTime = util.getElapsedLogicalTime(); + if (!elapsedTime.isEqualTo(TimeValue.sec(1))) { + util.requestErrorStop("ERROR: Expected to receive input after one second."); + } + received = true; + =} + reaction(shutdown) {= + if (!received) { + util.reportError("ERROR: Destination " + this.getBankIndex() + " received no input!", ); + } + console.log("Success."); + =} +} + +main reactor(width:number(3)) { + a = new Source(width = width); + b = new[width] Destination(); + a.out -> b.inp after 1 sec; // Width of the bank of delays will be inferred. +} \ No newline at end of file diff --git a/test/TypeScript/src/multiport/MultiportToMultiport2.lf b/test/TypeScript/src/multiport/MultiportToMultiport2.lf index 73b26bb81f..4230245310 100644 --- a/test/TypeScript/src/multiport/MultiportToMultiport2.lf +++ b/test/TypeScript/src/multiport/MultiportToMultiport2.lf @@ -15,14 +15,11 @@ reactor Destination(width:number(2)) { input[width] inp:number; reaction (inp) {= for (let i = 0; i < inp.length; i++) { - if (inp[i] !== undefined) { - let value = inp[i]; - console.log("Received on channel " + i + ": " + value); - // NOTE: For testing purposes, this assumes the specific - // widths instantiated below. - if (value != i % 3) { - util.requestErrorStop("ERROR: expected " + i % 3); - } + console.log("Received on channel " + i + ": " + inp[i]); + // NOTE: For testing purposes, this assumes the specific + // widths instantiated below. + if (inp[i] != i % 3) { + util.requestErrorStop("ERROR: expected " + i % 3); } } =} From 6d9dd697a750e81f7c7f101221299397b9965c41 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Tue, 1 Mar 2022 21:51:39 -0800 Subject: [PATCH 44/64] Add ReactionsToNested.lf test and add missing newlines for some TS multiport tests. --- .../src/multiport/MultiportInParameterized.lf | 2 +- .../src/multiport/MultiportToBankAfter.lf | 2 +- .../src/multiport/MultiportToBankHierarchy.lf | 2 +- .../src/multiport/MultiportToMultiport2.lf | 2 +- .../src/multiport/ReactionsToNested.lf | 38 +++++++++++++++++++ 5 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 test/TypeScript/src/multiport/ReactionsToNested.lf diff --git a/test/TypeScript/src/multiport/MultiportInParameterized.lf b/test/TypeScript/src/multiport/MultiportInParameterized.lf index 4ce050928c..ffd11d2561 100644 --- a/test/TypeScript/src/multiport/MultiportInParameterized.lf +++ b/test/TypeScript/src/multiport/MultiportInParameterized.lf @@ -62,4 +62,4 @@ main reactor { // t2.out -> b.inp[1]; // t3.out -> b.inp[2]; // dt4.out -> b.inp[3]; -} \ No newline at end of file +} diff --git a/test/TypeScript/src/multiport/MultiportToBankAfter.lf b/test/TypeScript/src/multiport/MultiportToBankAfter.lf index 3290a84fb0..3bb2af6888 100644 --- a/test/TypeScript/src/multiport/MultiportToBankAfter.lf +++ b/test/TypeScript/src/multiport/MultiportToBankAfter.lf @@ -37,4 +37,4 @@ main reactor(width:number(3)) { a = new Source(width = width); b = new[width] Destination(); a.out -> b.inp after 1 sec; // Width of the bank of delays will be inferred. -} \ No newline at end of file +} diff --git a/test/TypeScript/src/multiport/MultiportToBankHierarchy.lf b/test/TypeScript/src/multiport/MultiportToBankHierarchy.lf index 8104ea07ce..dd8d18110d 100644 --- a/test/TypeScript/src/multiport/MultiportToBankHierarchy.lf +++ b/test/TypeScript/src/multiport/MultiportToBankHierarchy.lf @@ -38,4 +38,4 @@ main reactor MultiportToBankHierarchy { a = new Source(); b = new Container(); a.out -> b.inp; -} \ No newline at end of file +} diff --git a/test/TypeScript/src/multiport/MultiportToMultiport2.lf b/test/TypeScript/src/multiport/MultiportToMultiport2.lf index 4230245310..239d510a00 100644 --- a/test/TypeScript/src/multiport/MultiportToMultiport2.lf +++ b/test/TypeScript/src/multiport/MultiportToMultiport2.lf @@ -30,4 +30,4 @@ main reactor MultiportToMultiport2 { a2 = new Source(width = 2); b = new Destination(width = 5); a1.out, a2.out -> b.inp; -} \ No newline at end of file +} diff --git a/test/TypeScript/src/multiport/ReactionsToNested.lf b/test/TypeScript/src/multiport/ReactionsToNested.lf new file mode 100644 index 0000000000..0afb3eb86d --- /dev/null +++ b/test/TypeScript/src/multiport/ReactionsToNested.lf @@ -0,0 +1,38 @@ +// This test connects a simple counting source to tester +// that checks against its own count. +target TypeScript { + timeout: 1 sec +}; +reactor T(expected:number(0)) { + input z:number; + state received:boolean(false); + reaction(z) {= + console.log("T received " + z); + received = true; + if (z != expected) { + util.requestErrorStop("Expected " + expected); + } + =} + reaction(shutdown) {= + if (!received) { + util.reportError("No input received"); + } + =} +} + +reactor D { + input[2] y:number; + t1 = new T(expected = 42); + t2 = new T(expected = 43); + y -> t1.z, t2.z; +} + +main reactor { + d = new D(); + reaction(startup) -> d.y {= + d.y[0] = 42; + =} + reaction(startup) -> d.y {= + d.y[1] = 43; + =} +} From be0d4d2793c677d4f6dab5e1f23b1d20142c3ad0 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Tue, 1 Mar 2022 21:56:43 -0800 Subject: [PATCH 45/64] Bump reactor-ts version. --- org.lflang/src/lib/ts/reactor-ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/ts/reactor-ts b/org.lflang/src/lib/ts/reactor-ts index 6d0028caab..1fb6e3e7c2 160000 --- a/org.lflang/src/lib/ts/reactor-ts +++ b/org.lflang/src/lib/ts/reactor-ts @@ -1 +1 @@ -Subproject commit 6d0028caabfd45a53598fd1333cf46964d440b1c +Subproject commit 1fb6e3e7c2538a9911b8e0843885e1f18271a4f7 From 84c1988cb08d50f0dec7d0c978341d540141fa79 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Tue, 1 Mar 2022 22:05:35 -0800 Subject: [PATCH 46/64] Fix indentation. --- test/TypeScript/src/multiport/ReactionsToNested.lf | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/TypeScript/src/multiport/ReactionsToNested.lf b/test/TypeScript/src/multiport/ReactionsToNested.lf index 0afb3eb86d..cef6d0a268 100644 --- a/test/TypeScript/src/multiport/ReactionsToNested.lf +++ b/test/TypeScript/src/multiport/ReactionsToNested.lf @@ -6,14 +6,14 @@ target TypeScript { reactor T(expected:number(0)) { input z:number; state received:boolean(false); - reaction(z) {= - console.log("T received " + z); - received = true; - if (z != expected) { + reaction(z) {= + console.log("T received " + z); + received = true; + if (z != expected) { util.requestErrorStop("Expected " + expected); } - =} - reaction(shutdown) {= + =} + reaction(shutdown) {= if (!received) { util.reportError("No input received"); } From b5e137779856abb5ab4e894acfc3900fdb222575 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Fri, 4 Mar 2022 14:35:28 -0800 Subject: [PATCH 47/64] Match the constructor for Bank and add PipelineAfter.lf test. --- .../generator/ts/TSInstanceGenerator.kt | 2 +- .../TypeScript/src/multiport/PipelineAfter.lf | 41 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 test/TypeScript/src/multiport/PipelineAfter.lf diff --git a/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt index 797c498b98..065b0dc320 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt @@ -82,7 +82,7 @@ class TSInstanceGenerator ( "this.${childReactor.name} = " + "new __Bank<${childReactor.reactorClass.name}${getTypeParams(childReactor.typeParms)}, " + "${getReactorParameterList(childReactor.reactor.parameters)}>" + - "(${childReactor.widthSpec.toTSCode()}, " + + "(this, ${childReactor.widthSpec.toTSCode()}, " + "${childReactor.reactorClass.name}, " + "$childReactorArguments)") } else { diff --git a/test/TypeScript/src/multiport/PipelineAfter.lf b/test/TypeScript/src/multiport/PipelineAfter.lf new file mode 100644 index 0000000000..6bc1ed108b --- /dev/null +++ b/test/TypeScript/src/multiport/PipelineAfter.lf @@ -0,0 +1,41 @@ +target TypeScript; + +reactor Source { + output out:number; + + reaction (startup) -> out {= + out = 40; + =} +} + +reactor Compute { + input inp:number; + output out:number; + + reaction (inp) -> out {= + out = (inp as number) + 2; + =} +} + +reactor Sink { + input inp:number; + + reaction (inp) {= + console.log("Received " + inp); + if (inp != 42) { + util.requestErrorStop("ERROR: expected 42!"); + } + if (!util.getElapsedLogicalTime().isEqualTo(TimeValue.sec(1))) { + util.requestErrorStop("ERROR: Expected to receive input after one second."); + } + =} + +} + +main reactor { + source = new Source(); + compute = new Compute(); + sink = new Sink(); + + source.out, compute.out -> compute.inp, sink.inp after 500 msec; +} From e996c5e5f3ac02e85fd330c82cd759b1c76f372f Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Fri, 4 Mar 2022 15:15:07 -0800 Subject: [PATCH 48/64] Add NestedBanks.lf to TypeScript multiport tests. --- org.lflang/src/lib/ts/reactor-ts | 2 +- test/TypeScript/src/multiport/NestedBanks.lf | 70 ++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 test/TypeScript/src/multiport/NestedBanks.lf diff --git a/org.lflang/src/lib/ts/reactor-ts b/org.lflang/src/lib/ts/reactor-ts index 1fb6e3e7c2..0c75655b62 160000 --- a/org.lflang/src/lib/ts/reactor-ts +++ b/org.lflang/src/lib/ts/reactor-ts @@ -1 +1 @@ -Subproject commit 1fb6e3e7c2538a9911b8e0843885e1f18271a4f7 +Subproject commit 0c75655b62701c87afcfc1e0cffed9d492fb37df diff --git a/test/TypeScript/src/multiport/NestedBanks.lf b/test/TypeScript/src/multiport/NestedBanks.lf new file mode 100644 index 0000000000..f8940c9d8a --- /dev/null +++ b/test/TypeScript/src/multiport/NestedBanks.lf @@ -0,0 +1,70 @@ +/** + * Test of nested banks with multiports. + * @author Edward A. Lee + * @author Hokeun Kim + */ +target TypeScript; +main reactor { + a = new[2] A(); + c = new[3] C(); + d = new D(); + e = new E(); + + (a.x)+ -> c.z, d.u, e.t; +} +reactor A { + output[4] x:number; + b = new[2] B(aBankIndex = {=this.getBankIndex()=}); + b.y -> x; +} +reactor B(aBankIndex:number(0)) { + output[2] y:number; + reaction(startup) -> y {= + let base = aBankIndex * 4 + this.getBankIndex() * 2; + y[0] = base; + y[1] = base + 1; + =} +} +reactor C { + input[2] z:number; + f = new F(cBankIndex = {=this.getBankIndex()=}); + g = new G(cBankIndex = {=this.getBankIndex()=}); + z -> f.w, g.s; +} +reactor D { + input[2] u:number; + reaction(u) {= + for (let i = 0; i < u.length; i++) { + console.log("d.u[" + i + "] received " + u[i] + "."); + if (u[i] != 6 + i) { + util.requestErrorStop("Expected " + (6 + i) + " but received " + u[i] + "."); + } + } + =} +} +reactor E { + input[8] t:number; + reaction(t) {= + for (let i = 0; i < t.length; i++) { + console.log("e.t[" + i + "] received " + t[i] + "."); + } + =} +} +reactor F(cBankIndex:number(0)) { + input w:number; + reaction(w) {= + console.log("c[" + cBankIndex + "].f.w received " + w + "."); + if (w != cBankIndex * 2) { + util.requestErrorStop("Expected " + cBankIndex * 2 + " but received " + w + "."); + } + =} +} +reactor G(cBankIndex:number(0)) { + input s:number; + reaction(s) {= + console.log("c[" + cBankIndex + "].g.s received " + s + "."); + if (s != cBankIndex * 2 + 1) { + util.requestErrorStop("Expected " + (cBankIndex * 2 + 1) + " but received " + s + "."); + } + =} +} From 65fbd56b2ecee3bcbe00301e6727b56b8c67efdd Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Mon, 7 Mar 2022 12:06:24 -0800 Subject: [PATCH 49/64] Update reactor-ts submodule --- org.lflang/src/lib/c/reactor-c | 2 +- org.lflang/src/lib/ts/reactor-ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index baabc6af3b..e7306edb43 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit baabc6af3baae7a6ed6e95c9ca5828613156f879 +Subproject commit e7306edb430560aca043dc47345e24bf5f1f87d3 diff --git a/org.lflang/src/lib/ts/reactor-ts b/org.lflang/src/lib/ts/reactor-ts index 0c75655b62..21393554b8 160000 --- a/org.lflang/src/lib/ts/reactor-ts +++ b/org.lflang/src/lib/ts/reactor-ts @@ -1 +1 @@ -Subproject commit 0c75655b62701c87afcfc1e0cffed9d492fb37df +Subproject commit 21393554b8b3446b6b77206e6d28e97ef48ffca7 From 0e0aa71ed852fe56f6ceab9b2cdc3c11f2b982f9 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Wed, 9 Mar 2022 08:02:49 +0900 Subject: [PATCH 50/64] Add MultiportToMultiportParameter.lf to test. --- .../MultiportToMultiportParameter.lf | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 test/TypeScript/src/multiport/MultiportToMultiportParameter.lf diff --git a/test/TypeScript/src/multiport/MultiportToMultiportParameter.lf b/test/TypeScript/src/multiport/MultiportToMultiportParameter.lf new file mode 100644 index 0000000000..21796377bb --- /dev/null +++ b/test/TypeScript/src/multiport/MultiportToMultiportParameter.lf @@ -0,0 +1,43 @@ +// Check multiport output to multiport input. +target TypeScript { + timeout: 2 sec +}; +reactor Source(width:number(1)) { + timer t(0, 200 msec); + output[width] out:number; + state s:number(0); + reaction(t) -> out {= + for (let i = 0; i < out.length; i++) { + console.log("Before set, out[" + i + "] !== undefined has value " + out[i] !== undefined); + out[i] = s++; + console.log("AFTER set, out[" + i + "] !== undefined has value " + out[i] !== undefined); + console.log("AFTER set, out[" + i + "] has value " + out[i]); + } + =} +} +reactor Destination(width:number(1)) { + state s:number(6); + input[width] inp:number; // Width is one larger than that of the source. + reaction(inp) {= + let sum = 0; + for (let i = 0; i < inp.length; i++) { + if (inp[i] !== undefined) sum += (inp[i] as number); + } + console.log("Sum of received: " + sum + ".", ); + if (sum != s) { + util.requestErrorStop("ERROR: Expected " + s + "."); + } + s += 16; + =} + reaction(shutdown) {= + if (s <= 6) { + util.reportError("ERROR: Destination received no input!"); + } + console.log("Success."); + =} +} +main reactor (width:number(4)) { + a = new Source(width = width); + b = new Destination(width = width); + a.out -> b.inp; +} \ No newline at end of file From 95472cfc4d28446a16da3059c2e16a9e93a51450 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Wed, 9 Mar 2022 08:16:46 +0900 Subject: [PATCH 51/64] Remove logging that is not relevant typescript target. --- test/TypeScript/src/multiport/MultiportToMultiportParameter.lf | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/TypeScript/src/multiport/MultiportToMultiportParameter.lf b/test/TypeScript/src/multiport/MultiportToMultiportParameter.lf index 21796377bb..018210d31f 100644 --- a/test/TypeScript/src/multiport/MultiportToMultiportParameter.lf +++ b/test/TypeScript/src/multiport/MultiportToMultiportParameter.lf @@ -8,10 +8,7 @@ reactor Source(width:number(1)) { state s:number(0); reaction(t) -> out {= for (let i = 0; i < out.length; i++) { - console.log("Before set, out[" + i + "] !== undefined has value " + out[i] !== undefined); out[i] = s++; - console.log("AFTER set, out[" + i + "] !== undefined has value " + out[i] !== undefined); - console.log("AFTER set, out[" + i + "] has value " + out[i]); } =} } From e274bce808e1313e9a5d892ad0de3fd7d92aa7c5 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Wed, 9 Mar 2022 11:05:56 +0900 Subject: [PATCH 52/64] Simplify multi connection generation using .port() API. --- .../generator/ts/TSConnectionGenerator.kt | 27 +++++-------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt index ebb3794b70..166774537f 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt @@ -22,20 +22,11 @@ class TSConnectionGenerator ( ) { // There is no generateClassProperties() for connections - private fun getPortTypeName(port: Port, genericTypeMap: HashMap): String { - var portType = getPortType(port) - portType = genericTypeMap[portType]?: portType - return (if(port.isInput) "__In" else "__Out") + - "${if(port.isMultiport) "Multi" else ""}Port<$portType>" - } - - private fun getPortName(port: VarRef, genericTypeMap: HashMap): String { + private fun getPortName(port: VarRef): String { var portName = "" if (port.container != null) { if (port.container.isBank) { - portName += "...this.${port.container.name}.all().reduce(" + - "(__acc, __val) => __acc.concat(__val.${port.variable.name}), " + - "new Array<${getPortTypeName(port.variable as Port, genericTypeMap)}>(0))" + portName += "...this.${port.container.name}.port(it => it.${port.variable.name})" return portName } portName += port.container.name + "." @@ -44,19 +35,13 @@ class TSConnectionGenerator ( return "this.$portName" } + /** + * Return names of the ports on multiple connections. + */ private fun getPortNames(ports: List): List { var portNames = LinkedList() - var genericTypeMap: HashMap? = null for (varRef in ports) { - if (genericTypeMap == null) { - genericTypeMap = HashMap() - } - if (varRef.container != null) { - for ((index, typeParam) in (varRef.container.reactorClass as Reactor).typeParms.withIndex()) { - genericTypeMap[typeParam.toText().split(" ")[0]] = varRef.container.typeParms[index].toText() - } - } - portNames.add(getPortName(varRef, genericTypeMap)) + portNames.add(getPortName(varRef)) } return portNames } From 6afeebd1d51bc019af6322dc9a5c4e46bc7297b6 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Wed, 9 Mar 2022 11:45:09 +0900 Subject: [PATCH 53/64] Add newlines at the end of TS multiport tests. --- test/TypeScript/src/lib/Count.lf | 2 +- test/TypeScript/src/lib/TestCount.lf | 2 +- test/TypeScript/src/multiport/BankSelfBroadcast.lf | 2 +- test/TypeScript/src/multiport/BankToBankMultiport.lf | 2 +- test/TypeScript/src/multiport/BankToBankMultiportAfter.lf | 2 +- test/TypeScript/src/multiport/BroadcastAfter.lf | 2 +- .../TypeScript/src/multiport/MultiportFromBankHierarchyAfter.lf | 2 +- test/TypeScript/src/multiport/MultiportFromHierarchy.lf | 2 +- test/TypeScript/src/multiport/MultiportToMultiportParameter.lf | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test/TypeScript/src/lib/Count.lf b/test/TypeScript/src/lib/Count.lf index a2c390eeae..b7b698f4a3 100644 --- a/test/TypeScript/src/lib/Count.lf +++ b/test/TypeScript/src/lib/Count.lf @@ -7,4 +7,4 @@ reactor Count(offset:time(0), period:time(1 sec)) { reaction(t) -> out {= out = count++; =} -} \ No newline at end of file +} diff --git a/test/TypeScript/src/lib/TestCount.lf b/test/TypeScript/src/lib/TestCount.lf index 6d75554872..0acfad7365 100644 --- a/test/TypeScript/src/lib/TestCount.lf +++ b/test/TypeScript/src/lib/TestCount.lf @@ -26,4 +26,4 @@ reactor TestCount(start:number(1), stride:number(1), numInputs:number(1)) { console.log("ERROR: Expected to receive " + numInputs + " inputs, but got " + inputsReceived + "."); } =} -} \ No newline at end of file +} diff --git a/test/TypeScript/src/multiport/BankSelfBroadcast.lf b/test/TypeScript/src/multiport/BankSelfBroadcast.lf index 7f50088749..3f0c42f6fc 100644 --- a/test/TypeScript/src/multiport/BankSelfBroadcast.lf +++ b/test/TypeScript/src/multiport/BankSelfBroadcast.lf @@ -40,4 +40,4 @@ reactor A { main reactor { a = new[4] A(); (a.out)+ -> a.inp; -} \ No newline at end of file +} diff --git a/test/TypeScript/src/multiport/BankToBankMultiport.lf b/test/TypeScript/src/multiport/BankToBankMultiport.lf index 75e6c2d835..2ec1c132df 100644 --- a/test/TypeScript/src/multiport/BankToBankMultiport.lf +++ b/test/TypeScript/src/multiport/BankToBankMultiport.lf @@ -38,4 +38,4 @@ main reactor BankToBankMultiport(bankWidth:number(4)) { a = new[bankWidth] Source(width = 4); b = new[bankWidth] Destination(width = 4); a.out -> b.inp; -} \ No newline at end of file +} diff --git a/test/TypeScript/src/multiport/BankToBankMultiportAfter.lf b/test/TypeScript/src/multiport/BankToBankMultiportAfter.lf index 9f61227e06..5fac9a2dd1 100644 --- a/test/TypeScript/src/multiport/BankToBankMultiportAfter.lf +++ b/test/TypeScript/src/multiport/BankToBankMultiportAfter.lf @@ -38,4 +38,4 @@ main reactor (bankWidth:number(4)) { a = new[bankWidth] Source(width = 4); b = new[bankWidth] Destination(width = 4); a.out -> b.inp after 200 msec; -} \ No newline at end of file +} diff --git a/test/TypeScript/src/multiport/BroadcastAfter.lf b/test/TypeScript/src/multiport/BroadcastAfter.lf index e6b205c25a..5fd9740866 100644 --- a/test/TypeScript/src/multiport/BroadcastAfter.lf +++ b/test/TypeScript/src/multiport/BroadcastAfter.lf @@ -36,4 +36,4 @@ main reactor { a = new Source(); b = new[4] Destination(); (a.out)+ -> b.inp after 1 sec; -} \ No newline at end of file +} diff --git a/test/TypeScript/src/multiport/MultiportFromBankHierarchyAfter.lf b/test/TypeScript/src/multiport/MultiportFromBankHierarchyAfter.lf index 41e9861edd..c18de5366a 100644 --- a/test/TypeScript/src/multiport/MultiportFromBankHierarchyAfter.lf +++ b/test/TypeScript/src/multiport/MultiportFromBankHierarchyAfter.lf @@ -10,4 +10,4 @@ main reactor MultiportFromBankHierarchyAfter { a = new Container(portWidth = 4); b = new Destination(portWidth = 4); a.out -> b.inp after 1 sec; -} \ No newline at end of file +} diff --git a/test/TypeScript/src/multiport/MultiportFromHierarchy.lf b/test/TypeScript/src/multiport/MultiportFromHierarchy.lf index 6babf66074..abdfa1fee7 100644 --- a/test/TypeScript/src/multiport/MultiportFromHierarchy.lf +++ b/test/TypeScript/src/multiport/MultiportFromHierarchy.lf @@ -50,4 +50,4 @@ main reactor MultiportFromHierarchy(width:number(4)) { a = new Container(width = width); b = new Destination(width = width); a.out -> b.inp; -} \ No newline at end of file +} diff --git a/test/TypeScript/src/multiport/MultiportToMultiportParameter.lf b/test/TypeScript/src/multiport/MultiportToMultiportParameter.lf index 018210d31f..71fd5a3474 100644 --- a/test/TypeScript/src/multiport/MultiportToMultiportParameter.lf +++ b/test/TypeScript/src/multiport/MultiportToMultiportParameter.lf @@ -37,4 +37,4 @@ main reactor (width:number(4)) { a = new Source(width = width); b = new Destination(width = width); a.out -> b.inp; -} \ No newline at end of file +} From 2c652a87b0e59b444c1371824ae5018059f916c8 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Wed, 9 Mar 2022 12:50:23 +0900 Subject: [PATCH 54/64] Update the reactor-ts version to fix the error with type mismatch between port() and _connectMulti(). --- org.lflang/src/lib/ts/reactor-ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/ts/reactor-ts b/org.lflang/src/lib/ts/reactor-ts index 21393554b8..2d7ae77b09 160000 --- a/org.lflang/src/lib/ts/reactor-ts +++ b/org.lflang/src/lib/ts/reactor-ts @@ -1 +1 @@ -Subproject commit 21393554b8b3446b6b77206e6d28e97ef48ffca7 +Subproject commit 2d7ae77b0973098f0c83bdbd884d5cba95dca000 From 4e4686b0a77055ed23c19a311e8ee89bf5b7b313 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Wed, 9 Mar 2022 17:30:07 +0900 Subject: [PATCH 55/64] Add support for port of bank used as a trigger (e.g., b.out used as a trigger when b is a bank). Also add BankToReaction.lf to tests. --- org.lflang/src/lib/ts/reactor-ts | 2 +- .../generator/ts/TSReactionGenerator.kt | 80 +++++++++++++------ .../src/multiport/BankToReaction.lf | 20 +++++ 3 files changed, 75 insertions(+), 27 deletions(-) create mode 100644 test/TypeScript/src/multiport/BankToReaction.lf diff --git a/org.lflang/src/lib/ts/reactor-ts b/org.lflang/src/lib/ts/reactor-ts index 2d7ae77b09..b7649ad473 160000 --- a/org.lflang/src/lib/ts/reactor-ts +++ b/org.lflang/src/lib/ts/reactor-ts @@ -1 +1 @@ -Subproject commit 2d7ae77b0973098f0c83bdbd884d5cba95dca000 +Subproject commit b7649ad4733166f2ec860516492450991fdd8020 diff --git a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt index f990edf499..0a430491f5 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt @@ -4,6 +4,7 @@ import org.lflang.ErrorReporter import org.lflang.ASTUtils import org.lflang.federated.FederateInstance import org.lflang.generator.PrependOperator +import org.lflang.isBank import org.lflang.isMultiport import org.lflang.lf.* import org.lflang.lf.Timer @@ -32,7 +33,13 @@ class TSReactionGenerator( private fun StateVar.getTargetType(): String = tsGenerator.getTargetTypeW(this) private fun Type.getTargetType(): String = tsGenerator.getTargetTypeW(this) - private fun VarRef.generateVarRef(): String = ASTUtils.generateVarRef(this) + private fun VarRef.generateVarRef(): String { + return if (this.container != null && this.container.isBank && this.variable is Port) { + "this.${this.container.name}.port(it => it.${this.variable.name})" + } else { + "this.${ASTUtils.generateVarRef(this)}" + } + } /** * Return a TS type for the specified action. @@ -117,7 +124,7 @@ class TSReactionGenerator( val reactionTriggers = StringJoiner(",\n") for (trigger in reaction.triggers) { if (trigger is VarRef) { - reactionTriggers.add("this.${trigger.generateVarRef()}") + reactionTriggers.add(trigger.generateVarRef()) } else if (trigger.isStartup) { reactionTriggers.add("this.startup") } else if (trigger.isShutdown) { @@ -147,6 +154,35 @@ class TSReactionGenerator( } } + private fun generateReactionSignature(trigOrSource: VarRef): String { + var reactSignatureElementType = if (trigOrSource.variable.name == "networkMessage") { + // 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. + "Buffer" + } else if (trigOrSource.variable is Timer) { + "__Tag" + } else if (trigOrSource.variable is Action) { + getActionType(trigOrSource.variable as Action) + } else if (trigOrSource.variable is Port) { + getPortType(trigOrSource.variable as Port) + } else { + errorReporter.reportError("Invalid trigger: ${trigOrSource.variable.name}") + } + + val portClassType = if (trigOrSource.variable.isMultiport) { + "__InMultiPort<${reactSignatureElementType}>" + } else { + "Read<${reactSignatureElementType}>" + } + return if (trigOrSource.container != null && trigOrSource.container.isBank) { + "${generateArg(trigOrSource)}: Array<$portClassType>" + } else { + "${generateArg(trigOrSource)}: $portClassType" + } + } + // TODO(hokeun): Decompose this function further. private fun generateSingleReaction(reactor : Reactor, reaction: Reaction): String { // Determine signature of the react function @@ -212,28 +248,8 @@ class TSReactionGenerator( val trigOrSourcePair = Pair(trigOrSourceKey, trigOrSourceValue) if (!effectSet.contains(trigOrSourcePair)) { - var reactSignatureElementType = ""; - - if (trigOrSource.variable.name == "networkMessage") { - // 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. - reactSignatureElementType = "Buffer" - } else if (trigOrSource.variable is Timer) { - reactSignatureElementType = "__Tag" - } else if (trigOrSource.variable is Action) { - reactSignatureElementType = getActionType(trigOrSource.variable as Action) - } else if (trigOrSource.variable is Port) { - reactSignatureElementType = getPortType(trigOrSource.variable as Port) - } - - if (trigOrSource.variable.isMultiport) { - reactSignature.add("${generateArg(trigOrSource)}: __InMultiPort<${reactSignatureElementType}>") - } else { - reactSignature.add("${generateArg(trigOrSource)}: Read<${reactSignatureElementType}>") - } - reactFunctArgs.add("this.${trigOrSource.generateVarRef()}") + reactSignature.add(generateReactionSignature(trigOrSource)) + reactFunctArgs.add(trigOrSource.generateVarRef()) if (trigOrSource.container == null) { if (trigOrSource.variable.isMultiport) { val inputPort = trigOrSource.variable as Port @@ -297,7 +313,7 @@ class TSReactionGenerator( reactSignature.add(reactSignatureElement) - functArg = "this." + effect.generateVarRef() + functArg = effect.generateVarRef() if (effect.variable is Action){ reactFunctArgs.add("this.schedulable($functArg)") } else if (effect.variable is Port) { @@ -367,6 +383,8 @@ class TSReactionGenerator( for (variable in entry.value) { initializer.add("${variable.name}: __${entry.key.name}_${variable.name}" + + // The parentheses are needed below to separate two if-else statements. + (if (entry.key.isBank) "[i]" else "") + if (variable.isMultiport) ".values()" else ".get()") if (variable is Input) { if (variable.isMultiport) { @@ -388,7 +406,17 @@ class TSReactionGenerator( } } } - reactPrologue.add("let ${entry.key.name} = {${initializer}}") + if (entry.key.isBank) { + reactPrologue.add( + """ + |let ${entry.key.name} = [] + |for (let i = 0; i < ${entry.key.widthSpec.toTSCode()}; i++) { + | ${entry.key.name}.push({${initializer}}) + |}""".trimMargin() + ) + } else { + reactPrologue.add("let ${entry.key.name} = {${initializer}}") + } } // Generate reaction as a formatted string. diff --git a/test/TypeScript/src/multiport/BankToReaction.lf b/test/TypeScript/src/multiport/BankToReaction.lf new file mode 100644 index 0000000000..711ab5328b --- /dev/null +++ b/test/TypeScript/src/multiport/BankToReaction.lf @@ -0,0 +1,20 @@ +target TypeScript { + timeout: 5 sec +}; +import Count from "../lib/Count.lf"; + +main reactor { + state count:number(1); + + s = new[2] Count(); + + reaction(s.out) {= + for (let i = 0; i < s.length; i++) { + console.log("Received " + s[i].out + "."); + if (count != s[i].out) { + util.requestErrorStop("Expected " + count + "."); + } + } + count++; + =} +} From 997be6d9c7273f9fbe1523dbfc20608a9fc41de1 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Wed, 9 Mar 2022 18:42:19 +0900 Subject: [PATCH 56/64] Add test/TypeScript/src/multiport/BankMultiportToReaction.lf. --- .../src/multiport/BankMultiportToReaction.lf | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 test/TypeScript/src/multiport/BankMultiportToReaction.lf diff --git a/test/TypeScript/src/multiport/BankMultiportToReaction.lf b/test/TypeScript/src/multiport/BankMultiportToReaction.lf new file mode 100644 index 0000000000..f6e4b75e70 --- /dev/null +++ b/test/TypeScript/src/multiport/BankMultiportToReaction.lf @@ -0,0 +1,38 @@ +target TypeScript { + timeout: 5 sec +}; +import Count from "../lib/Count.lf"; + +reactor DoubleCount { + output[2] out:number; + c1 = new Count(); + c2 = new Count(); + c1.out, c2.out -> out; +} + +main reactor { + state count:number(1); + state received:boolean(false); + + s = new[2] DoubleCount(); + + reaction(s.out) {= + for (let i = 0; i < s.length; i++) { + for (let j = 0; j < s[0].out.length; j++) { + if (s[i].out[j] !== undefined) { + console.log("Received " + (s[i].out[j] as number) + "."); + if (count !== s[i].out[j]) { + util.requestErrorStop("Expected " + count + "."); + } + received = true; + } + } + } + count++; + =} + reaction(shutdown) {= + if (!received) { + util.reportError("No inputs present."); + } + =} +} From 12a82fabd94d51598c4449b9b8df79b206643422 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Wed, 9 Mar 2022 19:26:59 +0900 Subject: [PATCH 57/64] Refactor reaction signature generation code for port effects. --- .../generator/ts/TSReactionGenerator.kt | 99 ++++++++++--------- 1 file changed, 52 insertions(+), 47 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt index 0a430491f5..eb37131ccb 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt @@ -154,7 +154,7 @@ class TSReactionGenerator( } } - private fun generateReactionSignature(trigOrSource: VarRef): String { + private fun generateReactionSignatureForTrigger(trigOrSource: VarRef): String { var reactSignatureElementType = if (trigOrSource.variable.name == "networkMessage") { // Special handling for the networkMessage action created by // FedASTUtils.makeCommunication(), by assigning TypeScript @@ -183,6 +183,54 @@ class TSReactionGenerator( } } + private fun generateReactionSignatureElementForPortEffect(effect: VarRef): String { + val outputPort = effect.variable as Port + val portClassType = if (outputPort.isMultiport) { + "MultiReadWrite<${getPortType(effect.variable as Port)}>" + } else { + "ReadWrite<${getPortType(effect.variable as Port)}>" + } + + return if (effect.container != null && effect.container.isBank) { + "Array<${portClassType}>" + } else { + portClassType + } + } + + private fun generateReactionEpilogueForPortEffect(effect: VarRef): String { + val portEffect = effect.variable as Port + if (effect.container == null) { + if (portEffect.isMultiport) { + return """ + |${portEffect.name}.forEach((element, index) => { + | if (element !== undefined) { + | __${portEffect.name}.set(index, element); + | } + |});""".trimMargin() + } else { + return """ + |if (${portEffect.name} !== undefined) { + | __${portEffect.name}.set(${portEffect.name}); + |}""".trimMargin() + } + } else { + if (portEffect.isMultiport) { + return """ + |${effect.container.name}.${portEffect.name}.forEach((element, index) => { + | if (element !== undefined) { + | __${effect.container.name}_${portEffect.name}.set(index, element) + | } + |});""".trimMargin() + } else { + return """ + |if (${effect.container.name}.${portEffect.name} !== undefined) { + | __${effect.container.name}_${portEffect.name}.set(${effect.container.name}.${portEffect.name}) + |}""".trimMargin() + } + } + } + // TODO(hokeun): Decompose this function further. private fun generateSingleReaction(reactor : Reactor, reaction: Reaction): String { // Determine signature of the react function @@ -248,7 +296,7 @@ class TSReactionGenerator( val trigOrSourcePair = Pair(trigOrSourceKey, trigOrSourceValue) if (!effectSet.contains(trigOrSourcePair)) { - reactSignature.add(generateReactionSignature(trigOrSource)) + reactSignature.add(generateReactionSignatureForTrigger(trigOrSource)) reactFunctArgs.add(trigOrSource.generateVarRef()) if (trigOrSource.container == null) { if (trigOrSource.variable.isMultiport) { @@ -284,31 +332,8 @@ class TSReactionGenerator( reactSignatureElement += ": Sched<" + getActionType(effect.variable as Action) + ">" schedActionSet.add(effect.variable as Action) } else if (effect.variable is Port){ - val outputPort = effect.variable as Port - if (outputPort.isMultiport) { - reactSignatureElement += ": MultiReadWrite<${getPortType(effect.variable as Port)}>" - } else { - reactSignatureElement += ": ReadWrite<${getPortType(effect.variable as Port)}>" - } - if (effect.container == null) { - if (outputPort.isMultiport) { - reactEpilogue.add(with(PrependOperator) { - """ - |${outputPort.name}.forEach((element, index) => { - | if (element !== undefined) { - | __${outputPort.name}.set(index, element); - | } - |});""".trimMargin() - }) - } else { - reactEpilogue.add(with(PrependOperator) { - """ - |if (${outputPort.name} !== undefined) { - | __${outputPort.name}.set(${outputPort.name}); - |}""".trimMargin() - }) - } - } + reactSignatureElement += ": ${generateReactionSignatureElementForPortEffect(effect)}" + reactEpilogue.add(generateReactionEpilogueForPortEffect(effect)) } reactSignature.add(reactSignatureElement) @@ -381,30 +406,10 @@ class TSReactionGenerator( for (entry in containerToArgs.entries) { val initializer = StringJoiner(", ") for (variable in entry.value) { - initializer.add("${variable.name}: __${entry.key.name}_${variable.name}" + // The parentheses are needed below to separate two if-else statements. (if (entry.key.isBank) "[i]" else "") + if (variable.isMultiport) ".values()" else ".get()") - if (variable is Input) { - if (variable.isMultiport) { - reactEpilogue.add( - """ - |${entry.key.name}.${variable.name}.forEach((element, index) => { - | if (element !== undefined) { - | __${entry.key.name}_${variable.name}.set(index, element) - | } - |});""".trimMargin() - ) - } else { - reactEpilogue.add( - """ - |if (${entry.key.name}.${variable.name} !== undefined) { - | __${entry.key.name}_${variable.name}.set(${entry.key.name}.${variable.name}) - |}""".trimMargin() - ) - } - } } if (entry.key.isBank) { reactPrologue.add( From 9d91b7ad2f87176e80dd45280a0cec4afc36fed1 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Wed, 9 Mar 2022 21:20:52 +0900 Subject: [PATCH 58/64] Add support for effect ports from banks, and add BankReactionsInContainer.lf. --- .../generator/ts/TSReactionGenerator.kt | 50 ++++++++++--- .../src/multiport/BankReactionsInContainer.lf | 71 +++++++++++++++++++ 2 files changed, 110 insertions(+), 11 deletions(-) create mode 100644 test/TypeScript/src/multiport/BankReactionsInContainer.lf diff --git a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt index eb37131ccb..31c2caba30 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt @@ -203,9 +203,9 @@ class TSReactionGenerator( if (effect.container == null) { if (portEffect.isMultiport) { return """ - |${portEffect.name}.forEach((element, index) => { - | if (element !== undefined) { - | __${portEffect.name}.set(index, element); + |${portEffect.name}.forEach((__element, __index) => { + | if (__element !== undefined) { + | __${portEffect.name}.set(__index, __element); | } |});""".trimMargin() } else { @@ -215,18 +215,38 @@ class TSReactionGenerator( |}""".trimMargin() } } else { - if (portEffect.isMultiport) { - return """ - |${effect.container.name}.${portEffect.name}.forEach((element, index) => { - | if (element !== undefined) { - | __${effect.container.name}_${portEffect.name}.set(index, element) + if (effect.container.isBank) { + if (portEffect.isMultiport) { + return """ + |${effect.container.name}.forEach((__reactor, __reactorIndex) => { + | __reactor.${portEffect.name}.forEach((__element, __index) => { + | if (__element !== undefined) { + | __${effect.container.name}_${portEffect.name}[__reactorIndex].set(__index, __element) + | } + | }) + |});""".trimMargin() + } else { + return """ + |${effect.container.name}.forEach((__reactor, __reactorIndex) => { + | if (__reactor.${portEffect.name} !== undefined) { + | __${effect.container.name}_${portEffect.name}[__reactorIndex].set(__reactor.${portEffect.name}) | } |});""".trimMargin() + } } else { - return """ + if (portEffect.isMultiport) { + return """ + |${effect.container.name}.${portEffect.name}.forEach((__element, __index) => { + | if (__element !== undefined) { + | __${effect.container.name}_${portEffect.name}.set(__index, __element) + | } + |});""".trimMargin() + } else { + return """ |if (${effect.container.name}.${portEffect.name} !== undefined) { | __${effect.container.name}_${portEffect.name}.set(${effect.container.name}.${portEffect.name}) |}""".trimMargin() + } } } } @@ -344,9 +364,17 @@ class TSReactionGenerator( } else if (effect.variable is Port) { val port = effect.variable as Port if (port.isMultiport) { - reactFunctArgs.add("this.allWritable($functArg)") + if (effect.container != null && effect.container.isBank) { + reactFunctArgs.add("this.${effect.container.name}.allWritable($functArg)") + } else { + reactFunctArgs.add("this.allWritable($functArg)") + } } else { - reactFunctArgs.add("this.writable($functArg)") + if (effect.container != null && effect.container.isBank) { + reactFunctArgs.add("this.${effect.container.name}.writable($functArg)") + } else { + reactFunctArgs.add("this.writable($functArg)") + } } } diff --git a/test/TypeScript/src/multiport/BankReactionsInContainer.lf b/test/TypeScript/src/multiport/BankReactionsInContainer.lf new file mode 100644 index 0000000000..ffe0e3d588 --- /dev/null +++ b/test/TypeScript/src/multiport/BankReactionsInContainer.lf @@ -0,0 +1,71 @@ +/** + * This tests an output that is broadcast back to a multiport input of a bank. + */ +target TypeScript { + timeout: 1 sec +}; +reactor R { + output[2] out:number; + input[2] inp:number; + state received:boolean(false); + + reaction(startup) -> out {= + for (let i = 0; i < out.length; i++) { + let value = this.getBankIndex() * 2 + i; + out[i] = value; + console.log("Inner sending " + value + " to bank " + + this.getBankIndex() + " channel " + i + "."); + } + =} + + reaction(inp) {= + for (let i = 0; i < inp.length; i++) { + if (inp[i] !== undefined) { + console.log("Inner received " + inp[i] + " inp bank " + this.getBankIndex() + ", channel " + i); + received = true; + if (inp[i] != this.getBankIndex() * 2 + i) { + util.requestErrorStop("Expected " + this.getBankIndex() * 2 + i + "."); + } + } + } + =} + reaction(shutdown) {= + console.log("Inner shutdown invoked."); + if (!received) { + util.reportError("Received no input."); + } + =} +} +main reactor { + s = new[2] R(); + state received:boolean(false); + + reaction(startup) -> s.inp {= + let count = 0; + for (let i = 0; i < s.length; i++) { + for (let j = 0; j < s[i].inp.length; j++) { + console.log("Sending " + count + " to bank " + i + " channel " + j + "."); + s[i].inp[j] = count++; + } + } + =} + reaction(s.out) {= + for (let j = 0; j < s.length; j++) { + for (let i = 0; i < s[j].out.length; i++) { + if (s[j].out[i] !== undefined) { + console.log("Outer received " + s[j].out[i] + " on bank " + j + " channel " + i + "."); + received = true; + if (s[j].out[i] != j * 2 + i) { + util.requestErrorStop("Expected " + j * 2 + i + "."); + } + } + } + } + =} + reaction(shutdown) {= + console.log("Outer shutdown invoked."); + if (!received) { + util.reportError("Received no input."); + } + =} +} From 8a273699bf961194bf2fcb89ddde407f8942df04 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Wed, 9 Mar 2022 21:21:15 +0900 Subject: [PATCH 59/64] Update reactor-ts version. --- org.lflang/src/lib/ts/reactor-ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/ts/reactor-ts b/org.lflang/src/lib/ts/reactor-ts index b7649ad473..5317f12bad 160000 --- a/org.lflang/src/lib/ts/reactor-ts +++ b/org.lflang/src/lib/ts/reactor-ts @@ -1 +1 @@ -Subproject commit b7649ad4733166f2ec860516492450991fdd8020 +Subproject commit 5317f12bad463131f8c809dbc8d4d5d484ecfca9 From ba5745b1c26950266315d1a6d0d623fb5c21f241 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Wed, 9 Mar 2022 21:38:13 +0900 Subject: [PATCH 60/64] Add two more TypeScript multiport tests. --- .../src/multiport/MultiportToBankDouble.lf | 43 +++++++++++++++++++ .../src/multiport/ReactionToContainedBank.lf | 19 ++++++++ 2 files changed, 62 insertions(+) create mode 100644 test/TypeScript/src/multiport/MultiportToBankDouble.lf create mode 100644 test/TypeScript/src/multiport/ReactionToContainedBank.lf diff --git a/test/TypeScript/src/multiport/MultiportToBankDouble.lf b/test/TypeScript/src/multiport/MultiportToBankDouble.lf new file mode 100644 index 0000000000..b738904ba4 --- /dev/null +++ b/test/TypeScript/src/multiport/MultiportToBankDouble.lf @@ -0,0 +1,43 @@ + // Check multiport output to bank of recipients where the source + // has two reactions that write to the output. +target TypeScript { + timeout: 2 sec +}; +reactor Source { + output[3] out:number; // Connected to a bank of Destination reactors + reaction(startup) -> out {= + for (let i = 0; i < out.length; i++) { + out[i] = i; + } + =} + // Test also that multiple appearances of the same effect port + // do not result in multiple allocations of memory for the port. + reaction(startup) -> out {= // Contents of the reactions does not matter (either could be empty) + for (let i = 0; i < out.length; i++) { + out[i] = i * 2; + } + =} +} +reactor Destination { + input inp:number; + state received:boolean(false); + reaction(inp) {= + console.log("Destination " + this.getBankIndex() + " received " + inp + "."); + if (this.getBankIndex() * 2 != inp) { + util.requestErrorStop("ERROR: Expected " + this.getBankIndex() * 2 + "."); + } + received = true; + =} + reaction(shutdown) {= + if (!received) { + util.reportError("ERROR: Destination " + this.getBankIndex() + " received no input!"); + } + console.log("Success."); + =} +} + +main reactor { + a = new Source(); + b = new[3] Destination(); + a.out -> b.inp; +} \ No newline at end of file diff --git a/test/TypeScript/src/multiport/ReactionToContainedBank.lf b/test/TypeScript/src/multiport/ReactionToContainedBank.lf new file mode 100644 index 0000000000..9f8b20077a --- /dev/null +++ b/test/TypeScript/src/multiport/ReactionToContainedBank.lf @@ -0,0 +1,19 @@ + // Test reaction sending messages to a contained bank of reactors. + target TypeScript { + timeout: 1 sec +}; +import TestCount from "../lib/TestCount.lf"; + +main reactor ReactionToContainedBank(width:number(2)) { + timer t(0, 100 msec); + state count:number(1); + + test = new[width] TestCount(numInputs = 11); + + reaction(t) -> test.inp {= + for (let i = 0; i < width; i++) { + (test[i].inp as number) = count; + } + count++; + =} +} From 9bfd8c1bd03bf2229056ad029f02184dc9249058 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Thu, 10 Mar 2022 08:15:05 +0900 Subject: [PATCH 61/64] Add a new line at the end of test. --- test/TypeScript/src/multiport/MultiportToBankDouble.lf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/TypeScript/src/multiport/MultiportToBankDouble.lf b/test/TypeScript/src/multiport/MultiportToBankDouble.lf index b738904ba4..7b8964bcb9 100644 --- a/test/TypeScript/src/multiport/MultiportToBankDouble.lf +++ b/test/TypeScript/src/multiport/MultiportToBankDouble.lf @@ -40,4 +40,4 @@ main reactor { a = new Source(); b = new[3] Destination(); a.out -> b.inp; -} \ No newline at end of file +} From b271246bdbc1416787a613ed99366f297b8edd60 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Thu, 10 Mar 2022 17:42:57 -0800 Subject: [PATCH 62/64] Fix compile error --- org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt index bb64c8df1a..daa7c0711f 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt @@ -357,7 +357,7 @@ class TSReactionGenerator( reactSignature.add(reactSignatureElement) - functArg = effect.generateVarRef() + var functArg = effect.generateVarRef() if (effect.variable is Action){ reactFunctArgs.add("this.schedulable($functArg)") } else if (effect.variable is Port) { From 20016e42299e552bde197da4e808b605c51cf376 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Thu, 10 Mar 2022 17:51:24 -0800 Subject: [PATCH 63/64] Update submodule --- org.lflang/src/lib/ts/reactor-ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/ts/reactor-ts b/org.lflang/src/lib/ts/reactor-ts index e4e1aca013..47830e13bf 160000 --- a/org.lflang/src/lib/ts/reactor-ts +++ b/org.lflang/src/lib/ts/reactor-ts @@ -1 +1 @@ -Subproject commit e4e1aca01335bd5220c90522ceec46031b9f92b5 +Subproject commit 47830e13bf2d32ed5012a51b720a4df54c9afcbe From ce506ff5ed221e43ad9d27da74cb201076d7161d Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Thu, 10 Mar 2022 17:57:00 -0800 Subject: [PATCH 64/64] Remove threads target properties from templates. How come this was not caught in other branches? --- .../org/lflang/ui/wizard/templates/c/src/Interactive.lf | 1 - .../src/org/lflang/ui/wizard/templates/c/src/Parallel.lf | 4 ++-- .../src/org/lflang/ui/wizard/templates/c/src/Pipeline.lf | 8 ++++---- .../org/lflang/ui/wizard/templates/c/src/ReflexGame.lf | 1 - 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/org.lflang.ui/src/org/lflang/ui/wizard/templates/c/src/Interactive.lf b/org.lflang.ui/src/org/lflang/ui/wizard/templates/c/src/Interactive.lf index 6041462bd8..ef705d0bf5 100644 --- a/org.lflang.ui/src/org/lflang/ui/wizard/templates/c/src/Interactive.lf +++ b/org.lflang.ui/src/org/lflang/ui/wizard/templates/c/src/Interactive.lf @@ -3,7 +3,6 @@ * This has no audio output, but just tests the ncurses interface. */ target C { - threads: 2, cmake-include: [ "include/ncurses-cmake-extension.txt", // Adds support for ncurses "/lib/c/reactor-c/util/sensor_simulator.cmake" diff --git a/org.lflang.ui/src/org/lflang/ui/wizard/templates/c/src/Parallel.lf b/org.lflang.ui/src/org/lflang/ui/wizard/templates/c/src/Parallel.lf index a66ab5f00c..5e62ff66cd 100644 --- a/org.lflang.ui/src/org/lflang/ui/wizard/templates/c/src/Parallel.lf +++ b/org.lflang.ui/src/org/lflang/ui/wizard/templates/c/src/Parallel.lf @@ -3,13 +3,13 @@ * transport the input to the output. Four of them are * instantiated. Note that without parallel execution, there is * no way this program can keep up with real time since in every - * 200 msec cycle it has 800 msec of work to do. Given 4 threads, + * 200 msec cycle it has 800 msec of work to do. Given 4 workers, * however, this program can complete 800 msec of work in about * 225 msec. */ target C { timeout: 2 sec, - threads: 1, // Change to 4 to see speed up. + workers: 1, // Change to 4 to see speed up. }; reactor Source { timer t(0, 200 msec); diff --git a/org.lflang.ui/src/org/lflang/ui/wizard/templates/c/src/Pipeline.lf b/org.lflang.ui/src/org/lflang/ui/wizard/templates/c/src/Pipeline.lf index 76932c5636..87501f13e0 100644 --- a/org.lflang.ui/src/org/lflang/ui/wizard/templates/c/src/Pipeline.lf +++ b/org.lflang.ui/src/org/lflang/ui/wizard/templates/c/src/Pipeline.lf @@ -3,19 +3,19 @@ * a chain of reactors that can all execute in parallel * at each logical time step. * - * The threads argument specifies the number of worker - * threads, which enables the reactors in the chain to + * The workers argument specifies the number of worker + * workers, which enables the reactors in the chain to * execute on multiple cores simultaneously. * * This uses the TakeTime reactor to perform computation. - * If you reduce the number of worker threads to 1, the + * If you reduce the number of worker workers to 1, the * execution time will be approximately four times as long. * * @author Edward A. Lee * @author Marten Lohstroh */ target C { - threads: 4, + workers: 4, } /** diff --git a/org.lflang.ui/src/org/lflang/ui/wizard/templates/c/src/ReflexGame.lf b/org.lflang.ui/src/org/lflang/ui/wizard/templates/c/src/ReflexGame.lf index a342da1d58..16a65b0527 100644 --- a/org.lflang.ui/src/org/lflang/ui/wizard/templates/c/src/ReflexGame.lf +++ b/org.lflang.ui/src/org/lflang/ui/wizard/templates/c/src/ReflexGame.lf @@ -12,7 +12,6 @@ * @author Marten Lohstroh */ target C { - threads: 1, keepalive: true }; /**