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 }; /** 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 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 diff --git a/org.lflang/src/org/lflang/AstExtensions.kt b/org.lflang/src/org/lflang/AstExtensions.kt index efeb0994c8..8549818c81 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() = ASTUtils.isMultiport(this) +/** + * Return true if the receiving Variable 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/Target.java b/org.lflang/src/org/lflang/Target.java index 13d0e2b11e..84a2711311 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; @@ -491,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 4a5e04300a..166774537f 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt @@ -1,7 +1,15 @@ 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 +import org.lflang.lf.Reactor +import org.lflang.lf.VarRef +import org.lflang.toText import java.util.* /** @@ -14,30 +22,55 @@ class TSConnectionGenerator ( ) { // There is no generateClassProperties() for connections + private fun getPortName(port: VarRef): String { + var portName = "" + if (port.container != null) { + if (port.container.isBank) { + portName += "...this.${port.container.name}.port(it => it.${port.variable.name})" + return portName + } + portName += port.container.name + "." + } + portName += port.variable.name + return "this.$portName" + } + + /** + * Return names of the ports on multiple connections. + */ + private fun getPortNames(ports: List): List { + var portNames = LinkedList() + for (varRef in ports) { + portNames.add(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._connectMulti($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.leftPorts.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/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 new file mode 100644 index 0000000000..4ba4790fa5 --- /dev/null +++ b/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt @@ -0,0 +1,50 @@ +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 +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.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() + } +} + +private fun Type.getTargetType(): String = TSTypes.getTargetType(this) + +/** + * Return a TS type for the specified port. + * If the type has not been specified, return + * "Present" which is the base type for ports. + * @param port The port + * @return The TS type. + */ +fun getPortType(port: Port): String { + if (port.type != null) { + return port.type.getTargetType() + } else { + return "Present" + } +} + +fun Parameter.getTargetType(): String = TSTypes.getTargetType(this) diff --git a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt index d8371691c7..3b85cde150 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt @@ -95,10 +95,10 @@ 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", - "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", "internal.ts", + "reaction.ts", "reactor.ts", "microtime.d.ts", "multiport.ts", "nanotimer.d.ts", "port.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()" } @@ -653,4 +653,6 @@ class TSGenerator( override fun getTarget(): Target { return Target.TS } + + override fun generateAfterDelaysWithVariableWidth() = false } diff --git a/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt index 337835f8ab..a80a6d832b 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt @@ -51,10 +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, 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 {Reaction as __Reaction} from './core/reaction' + |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 {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, 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/TSInstanceGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt index dc400c4293..065b0dc320 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt @@ -1,9 +1,13 @@ 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.lf.TypeParm +import org.lflang.reactor import org.lflang.toDefinition import org.lflang.toText import java.util.* @@ -14,6 +18,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 @@ -39,12 +44,26 @@ class TSInstanceGenerator ( return tsReactorGenerator.getTargetInitializerHelper(param, getInitializerList(param, i)) } + 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) { - val childReactorParams = if (childReactor.typeParms.isEmpty()) {""} else { - childReactor.typeParms.joinToString(", ", "<", ">") { it.toText() }} - childReactorClassProperties.add("${childReactor.name}: ${childReactor.reactorClass.name}$childReactorParams") + if (childReactor.isBank) { + childReactorClassProperties.add("${childReactor.name}: " + + "__Bank<${childReactor.reactorClass.name}${getTypeParams(childReactor.typeParms)}, " + + "${getReactorParameterList(childReactor.reactor.parameters)}>") + } else { + childReactorClassProperties.add("${childReactor.name}: " + + "${childReactor.reactorClass.name}${getTypeParams(childReactor.typeParms)}") + } } return childReactorClassProperties.joinToString("\n") } @@ -55,16 +74,22 @@ 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 for (parameter in childReactor.reactorClass.toDefinition().parameters) { 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<${childReactor.reactorClass.name}${getTypeParams(childReactor.typeParms)}, " + + "${getReactorParameterList(childReactor.reactor.parameters)}>" + + "(this, ${childReactor.widthSpec.toTSCode()}, " + + "${childReactor.reactorClass.name}, " + + "$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 35bcfa42df..10e2a4a644 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt @@ -1,5 +1,6 @@ package org.lflang.generator.ts +import org.lflang.isMultiport import org.lflang.lf.Input import org.lflang.lf.Output import org.lflang.lf.Port @@ -11,34 +12,25 @@ 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() for (input in inputs) { - portClassProperties.add("${input.name}: __InPort<${getPortType(input)}>;") + if (input.isMultiport) { + portClassProperties.add("${input.name}: __InMultiPort<${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}: __OutMultiPort<${getPortType(output)}>;") + } else { + portClassProperties.add("${output.name}: __OutPort<${getPortType(output)}>;") + } } return portClassProperties.joinToString("\n") } @@ -46,10 +38,20 @@ 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} = new __InMultiPort<${getPortType(input)}>(this, ${input.widthSpec.toTSCode()});") + } 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} = new __OutMultiPort<${getPortType(output)}>(this, ${output.widthSpec.toTSCode()});") + } else { + porInstantiations.add("this.${output.name} = 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 0820e37590..daa7c0711f 100644 --- a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt +++ b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt @@ -4,6 +4,8 @@ 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 import org.lflang.toText @@ -31,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. @@ -116,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) { @@ -146,6 +154,103 @@ class TSReactionGenerator( } } + 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 + // 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" + } + } + + 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 (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 { + 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 @@ -211,39 +316,29 @@ 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) - } - - reactSignature.add("${generateArg(trigOrSource)}: Read<${reactSignatureElementType}>") - reactFunctArgs.add("this.${trigOrSource.generateVarRef()}") + reactSignature.add(generateReactionSignatureForTrigger(trigOrSource)) + reactFunctArgs.add(trigOrSource.generateVarRef()) if (trigOrSource.container == null) { - reactPrologue.add("let ${trigOrSource.variable.name} = ${generateArg(trigOrSource)}.get();") + if (trigOrSource.variable.isMultiport) { + val inputPort = trigOrSource.variable as Port + reactPrologue.add( + "let ${inputPort.name} = ${generateArg(trigOrSource)}.values();") + } 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 @@ -256,26 +351,39 @@ 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) + ">" - if (effect.container == null) { - reactEpilogue.add(with(PrependOperator) {""" - |if (${effect.variable.name} !== undefined) { - | __${effect.variable.name}.set(${effect.variable.name}); - |}""".trimMargin()}) - } + reactSignatureElement += ": ${generateReactionSignatureElementForPortEffect(effect)}" + reactEpilogue.add(generateReactionEpilogueForPortEffect(effect)) } reactSignature.add(reactSignatureElement) - var functArg = "this." + effect.generateVarRef() + var functArg = effect.generateVarRef() 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) { + if (effect.container != null && effect.container.isBank) { + reactFunctArgs.add("this.${effect.container.name}.allWritable($functArg)") + } else { + reactFunctArgs.add("this.allWritable($functArg)") + } + } else { + if (effect.container != null && effect.container.isBank) { + reactFunctArgs.add("this.${effect.container.name}.writable($functArg)") + } else { + reactFunctArgs.add("this.writable($functArg)") + } + } } if (effect.container == null) { - reactPrologue.add("let ${effect.variable.name} = __${effect.variable.name}.get();") + if (effect.variable.isMultiport) { + val port = effect.variable as Port + reactPrologue.add("let ${port.name} = new Array<${getPortType(port)}>(__${port.name}.width());") + } else { + reactPrologue.add("let ${effect.variable.name} = __${effect.variable.name}.get();") + } } else { // Hierarchical references are handled later because there // could be references to other members of the same reactor. @@ -325,15 +433,22 @@ 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()") - if (variable is Input) { - reactEpilogue.add(with(PrependOperator) {""" - |if (${entry.key.name}.${variable.name} !== undefined) { - | __${entry.key.name}_${variable.name}.set(${entry.key.name}.${variable.name}) - |}""".trimMargin()}) - } + 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 (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}}") } - reactPrologue.add("let ${entry.key.name} = {${initializer}}") } // Generate reaction as a formatted string. 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/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; } diff --git a/test/TypeScript/src/lib/Count.lf b/test/TypeScript/src/lib/Count.lf index a53775efad..b7b698f4a3 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..0acfad7365 --- /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 + "."); + } + =} +} 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/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."); + } + =} +} 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."); + } + =} +} diff --git a/test/TypeScript/src/multiport/BankSelfBroadcast.lf b/test/TypeScript/src/multiport/BankSelfBroadcast.lf new file mode 100644 index 0000000000..3f0c42f6fc --- /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; +} diff --git a/test/TypeScript/src/multiport/BankToBank.lf b/test/TypeScript/src/multiport/BankToBank.lf new file mode 100644 index 0000000000..52a6d2995a --- /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; +} diff --git a/test/TypeScript/src/multiport/BankToBankMultiport.lf b/test/TypeScript/src/multiport/BankToBankMultiport.lf new file mode 100644 index 0000000000..2ec1c132df --- /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; +} diff --git a/test/TypeScript/src/multiport/BankToBankMultiportAfter.lf b/test/TypeScript/src/multiport/BankToBankMultiportAfter.lf new file mode 100644 index 0000000000..5fac9a2dd1 --- /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; +} diff --git a/test/TypeScript/src/multiport/BankToMultiport.lf b/test/TypeScript/src/multiport/BankToMultiport.lf new file mode 100644 index 0000000000..ef8b4abf83 --- /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 { + output out:number; + + reaction (startup) -> out {= + out = this.getBankIndex(); + =} +} + +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; +} 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++; + =} +} 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; +} diff --git a/test/TypeScript/src/multiport/BroadcastAfter.lf b/test/TypeScript/src/multiport/BroadcastAfter.lf new file mode 100644 index 0000000000..5fd9740866 --- /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; +} 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; +} 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; +} diff --git a/test/TypeScript/src/multiport/MultiportFromBank.lf b/test/TypeScript/src/multiport/MultiportFromBank.lf new file mode 100644 index 0000000000..30c928029c --- /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(3)) { + 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; +} diff --git a/test/TypeScript/src/multiport/MultiportFromBankHierarchy.lf b/test/TypeScript/src/multiport/MultiportFromBankHierarchy.lf new file mode 100644 index 0000000000..a74709081e --- /dev/null +++ b/test/TypeScript/src/multiport/MultiportFromBankHierarchy.lf @@ -0,0 +1,17 @@ +// 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 Source, Destination from "MultiportFromBank.lf" +reactor Container(portWidth:number(3)) { + output[portWidth] out:number; + s = new[portWidth] Source(); + s.out -> out; +} + +main reactor { + a = new Container(); + b = new Destination(); + a.out -> b.inp; +} diff --git a/test/TypeScript/src/multiport/MultiportFromBankHierarchyAfter.lf b/test/TypeScript/src/multiport/MultiportFromBankHierarchyAfter.lf new file mode 100644 index 0000000000..c18de5366a --- /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; +} diff --git a/test/TypeScript/src/multiport/MultiportFromHierarchy.lf b/test/TypeScript/src/multiport/MultiportFromHierarchy.lf new file mode 100644 index 0000000000..abdfa1fee7 --- /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; +} 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/MultiportIn.lf b/test/TypeScript/src/multiport/MultiportIn.lf new file mode 100644 index 0000000000..b774fb5666 --- /dev/null +++ b/test/TypeScript/src/multiport/MultiportIn.lf @@ -0,0 +1,61 @@ +// 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++) { + const val = inp[i]; + if (val === undefined) { + util.requestErrorStop("ERROR: input at [" + i + "] is missing."); + } else { + sum += val; + } + } + 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; +} diff --git a/test/TypeScript/src/multiport/MultiportInParameterized.lf b/test/TypeScript/src/multiport/MultiportInParameterized.lf new file mode 100644 index 0000000000..ffd11d2561 --- /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]; +} 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/MultiportOut.lf b/test/TypeScript/src/multiport/MultiportOut.lf new file mode 100644 index 0000000000..67c698adb8 --- /dev/null +++ b/test/TypeScript/src/multiport/MultiportOut.lf @@ -0,0 +1,56 @@ +// 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++) { + const 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.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; +} diff --git a/test/TypeScript/src/multiport/MultiportToBank.lf b/test/TypeScript/src/multiport/MultiportToBank.lf new file mode 100644 index 0000000000..5cf903c36b --- /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 { + input inp:number; + + reaction (inp) {= + console.log("Received " + inp); + if (inp != this.getBankIndex()) { + util.requestErrorStop("Error: expected " + this.getBankIndex()); + } + =} +} + +main reactor MultiportToBank { + source = new Source(); + sink = new[4] Sink(); + source.out -> sink.inp; +} diff --git a/test/TypeScript/src/multiport/MultiportToBankAfter.lf b/test/TypeScript/src/multiport/MultiportToBankAfter.lf new file mode 100644 index 0000000000..3bb2af6888 --- /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. +} diff --git a/test/TypeScript/src/multiport/MultiportToBankDouble.lf b/test/TypeScript/src/multiport/MultiportToBankDouble.lf new file mode 100644 index 0000000000..7b8964bcb9 --- /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; +} diff --git a/test/TypeScript/src/multiport/MultiportToBankHierarchy.lf b/test/TypeScript/src/multiport/MultiportToBankHierarchy.lf new file mode 100644 index 0000000000..dd8d18110d --- /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; +} diff --git a/test/TypeScript/src/multiport/MultiportToHierarchy.lf b/test/TypeScript/src/multiport/MultiportToHierarchy.lf new file mode 100644 index 0000000000..132ba10120 --- /dev/null +++ b/test/TypeScript/src/multiport/MultiportToHierarchy.lf @@ -0,0 +1,48 @@ + // 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++) { + 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) {= + 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; +} diff --git a/test/TypeScript/src/multiport/MultiportToMultiport.lf b/test/TypeScript/src/multiport/MultiportToMultiport.lf new file mode 100644 index 0000000000..ac01b63b92 --- /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; +} diff --git a/test/TypeScript/src/multiport/MultiportToMultiport2.lf b/test/TypeScript/src/multiport/MultiportToMultiport2.lf new file mode 100644 index 0000000000..239d510a00 --- /dev/null +++ b/test/TypeScript/src/multiport/MultiportToMultiport2.lf @@ -0,0 +1,33 @@ +// 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++) { + 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); + } + } + =} +} + +main reactor MultiportToMultiport2 { + a1 = new Source(width = 3); + a2 = new Source(width = 2); + b = new Destination(width = 5); + a1.out, a2.out -> b.inp; +} 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; +} diff --git a/test/TypeScript/src/multiport/MultiportToMultiportArray.lf b/test/TypeScript/src/multiport/MultiportToMultiportArray.lf new file mode 100644 index 0000000000..bb455d3715 --- /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++) { + const a = inp[i] + if (a !== undefined) { + 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; +} diff --git a/test/TypeScript/src/multiport/MultiportToMultiportParameter.lf b/test/TypeScript/src/multiport/MultiportToMultiportParameter.lf new file mode 100644 index 0000000000..71fd5a3474 --- /dev/null +++ b/test/TypeScript/src/multiport/MultiportToMultiportParameter.lf @@ -0,0 +1,40 @@ +// 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++) { + out[i] = s++; + } + =} +} +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; +} diff --git a/test/TypeScript/src/multiport/MultiportToPort.lf b/test/TypeScript/src/multiport/MultiportToPort.lf new file mode 100644 index 0000000000..49d0de0af7 --- /dev/null +++ b/test/TypeScript/src/multiport/MultiportToPort.lf @@ -0,0 +1,38 @@ +// 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(let i = 0; i < out.length; i++) { + console.log("Source sending " + i); + out[i] = 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("Success."); + =} +} + +main reactor MultiportToPort { + a = new Source(); + b1 = new Destination(); + b2 = new Destination(expected = 1); + a.out -> b1.inp, b2.inp; +} 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); +} 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 + "."); + } + =} +} 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; +} 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++; + =} +} diff --git a/test/TypeScript/src/multiport/ReactionsToNested.lf b/test/TypeScript/src/multiport/ReactionsToNested.lf new file mode 100644 index 0000000000..cef6d0a268 --- /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; + =} +}