Skip to content

Commit

Permalink
Merge pull request #942 from lf-lang/ts-multiport
Browse files Browse the repository at this point in the history
Adds bank & multiport support in TypeScript code generator with > 30 multiport tests implemented.
  • Loading branch information
lhstrh authored Mar 11, 2022
2 parents 99631e4 + ce506ff commit f6e4534
Show file tree
Hide file tree
Showing 59 changed files with 2,005 additions and 113 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
* @author Marten Lohstroh
*/
target C {
threads: 1,
keepalive: true
};
/**
Expand Down
4 changes: 3 additions & 1 deletion org.lflang/src/lib/ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion org.lflang/src/lib/ts/reactor-ts
Submodule reactor-ts updated 58 files
+6 −13 .github/workflows/api-docs.yml
+47 −0 .github/workflows/ci.yml
+2 −2 .gitignore
+3 −2 README.md
+3 −6 __tests__/ActionTrigger.test.ts
+1 −1 __tests__/Adder.test.ts
+3 −5 __tests__/Clock.test.ts
+4 −5 __tests__/HierarchicalSingleEvent.test.ts
+1 −5 __tests__/Logger.test.ts
+1 −2 __tests__/OutputEvent.test.ts
+2 −4 __tests__/OutputGet.test.ts
+1 −3 __tests__/SingleEvent.test.ts
+70 −3 __tests__/alarm.ts
+56 −0 __tests__/bank.ts
+99 −0 __tests__/connection.test.ts
+0 −91 __tests__/defunct/Deadline.ts
+25 −20 __tests__/dependencies.ts
+45 −80 __tests__/hierarchy.ts
+92 −0 __tests__/multiport.test.ts
+4 −6 __tests__/mutations.test.ts
+4 −5 __tests__/reactors.errors.test.ts
+1 −4 __tests__/reactors.test.ts
+27 −20 __tests__/simple.ts
+58 −0 __tests__/time.test.ts
+0 −65 __tests__/types/canConnect.ts
+0 −2 __tests__/types/index.d.ts
+0 −18 __tests__/types/tsconfig.json
+0 −7 __tests__/types/tslint.json
+1 −0 lingua-franca-ref.txt
+935 −2,239 package-lock.json
+6 −3 package.json
+462 −0 src/benchmark/FacilityLocation.ts
+2 −4 src/benchmark/PingPong.ts
+7 −6 src/benchmark/Sieve.ts
+142 −0 src/core/action.ts
+99 −0 src/core/bank.ts
+1 −2 src/core/cli.ts
+75 −53 src/core/component.ts
+61 −0 src/core/event.ts
+74 −51 src/core/federation.ts
+16 −0 src/core/internal.ts
+257 −0 src/core/multiport.ts
+195 −0 src/core/port.ts
+8 −4 src/core/reaction.ts
+282 −696 src/core/reactor.ts
+33 −0 src/core/state.ts
+29 −0 src/core/strings.ts
+26 −0 src/core/time.ts
+127 −0 src/core/trigger.ts
+109 −0 src/core/types.ts
+3 −0 src/core/util.ts
+0 −99 src/example/generated/DistributedLogical/Distributed_dsp.ts
+0 −114 src/example/generated/DistributedLogical/Distributed_msg.ts
+0 −97 src/example/generated/DistributedPhysical/Distributed_dsp.ts
+0 −112 src/example/generated/DistributedPhysical/Distributed_msg.ts
+0 −27 src/example/generated/README.md
+2 −1 src/share/Logger.ts
+1 −1 src/share/SingleEvent.ts
5 changes: 5 additions & 0 deletions org.lflang/src/org/lflang/AstExtensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
2 changes: 2 additions & 0 deletions org.lflang/src/org/lflang/Target.java
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,7 @@ public boolean supportsMultiports() {
case CPP:
case Python:
case Rust:
case TS:
return true;
}
return false;
Expand All @@ -491,6 +492,7 @@ public boolean supportsParameterizedWidths() {
case CPP:
case Python:
case Rust:
case TS:
return true;
}
return false;
Expand Down
57 changes: 45 additions & 12 deletions org.lflang/src/org/lflang/generator/ts/TSConnectionGenerator.kt
Original file line number Diff line number Diff line change
@@ -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.*

/**
Expand All @@ -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<VarRef>): List<String> {
var portNames = LinkedList<String>()
for (varRef in ports) {
portNames.add(getPortName(varRef))
}
return portNames
}

fun generateInstantiations(): String {
val connectionInstantiations = LinkedList<String>()
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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ class TSConstructorGenerator (
private fun getInitializerList(param: Parameter): List<String> =
tsGenerator.getInitializerListW(param)

private fun Parameter.getTargetType(): String = tsGenerator.getTargetTypeW(this)

// Initializer functions
private fun getTargetInitializerHelper(param: Parameter,
list: List<String>): String {
Expand Down
50 changes: 50 additions & 0 deletions org.lflang/src/org/lflang/generator/ts/TSExtensions.kt
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>}
*/
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)
10 changes: 6 additions & 4 deletions org.lflang/src/org/lflang/generator/ts/TSGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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()" }

Expand Down Expand Up @@ -653,4 +653,6 @@ class TSGenerator(
override fun getTarget(): Target {
return Target.TS
}

override fun generateAfterDelaysWithVariableWidth() = false
}
Original file line number Diff line number Diff line change
Expand Up @@ -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'
|"""
Expand Down
45 changes: 35 additions & 10 deletions org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt
Original file line number Diff line number Diff line change
@@ -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.*
Expand All @@ -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
Expand All @@ -39,12 +44,26 @@ class TSInstanceGenerator (
return tsReactorGenerator.getTargetInitializerHelper(param, getInitializerList(param, i))
}

private fun getTypeParams(typeParms: List<TypeParm>): String =
if (typeParms.isEmpty()) {""} else {
typeParms.joinToString(", ", "<", ">") { it.toText() }}

private fun getReactorParameterList(parameters: List<Parameter>): String =
if (parameters.isEmpty()) { "[__Reactor]" } else {
parameters.joinToString(", ", "[__Reactor, ", "]") { it.getTargetType() }}


fun generateClassProperties(): String {
val childReactorClassProperties = LinkedList<String>()
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")
}
Expand All @@ -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")
}
Expand Down
Loading

0 comments on commit f6e4534

Please sign in to comment.