files, Injector injector) {
}
}
for (Path path : files) {
- path = path.toAbsolutePath();
- Path pkgRoot = FileConfig.findPackageRoot(path, reporter::printWarning);
- String resolved;
- if (root != null) {
- resolved = root.resolve("src-gen").toString();
- } else {
- resolved = pkgRoot.resolve("src-gen").toString();
- }
- this.fileAccess.setOutputPath(resolved);
+ path = toAbsolutePath(path);
+ String outputPath = getActualOutputPath(root, path).toString();
+ this.fileAccess.setOutputPath(outputPath);
final Resource resource = getResource(path);
if (resource == null) {
reporter.printFatalErrorAndExit(
path + " is not an LF file. Use the .lf file extension to denote LF files.");
}
- else if (cmd != null && cmd.hasOption(CLIOption.FEDERATED.option.getOpt())) {
+ else if (cmd.hasOption(CLIOption.FEDERATED.option.getOpt())) {
if (!ASTUtils.makeFederated(resource)) {
reporter.printError("Unable to change main reactor to federated reactor.");
}
@@ -238,16 +200,29 @@ else if (cmd != null && cmd.hasOption(CLIOption.FEDERATED.option.getOpt())) {
LFGeneratorContext context = new MainContext(
LFGeneratorContext.Mode.STANDALONE, CancelIndicator.NullImpl, (m, p) -> {}, properties, false,
- fileConfig -> injector.getInstance(ErrorReporter.class)
+ fileConfig -> errorReporter
);
- this.generator.generate(resource, this.fileAccess, context);
+ try {
+ this.generator.generate(resource, this.fileAccess, context);
+ } catch (Exception e) {
+ reporter.printFatalErrorAndExit("Error running generator", e);
+ }
exitIfCollectedErrors();
// print all other issues (not errors)
issueCollector.getAllIssues().forEach(reporter::printIssue);
- System.out.println("Code generation finished.");
+ this.io.getOut().println("Code generation finished.");
+ }
+ }
+
+ private Path getActualOutputPath(Path root, Path path) {
+ if (root != null) {
+ return root.resolve("src-gen");
+ } else {
+ Path pkgRoot = FileConfig.findPackageRoot(path, reporter::printWarning);
+ return pkgRoot.resolve("src-gen");
}
}
diff --git a/org.lflang/src/org/lflang/cli/Lff.java b/org.lflang/src/org/lflang/cli/Lff.java
index 025389959e..8f0b43c94a 100644
--- a/org.lflang/src/org/lflang/cli/Lff.java
+++ b/org.lflang/src/org/lflang/cli/Lff.java
@@ -1,25 +1,23 @@
/** Stand-alone version of the Lingua Franca formatter (lff). */
package org.lflang.cli;
-import com.google.inject.Injector;
import java.io.IOException;
import java.nio.file.FileAlreadyExistsException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
import java.util.Arrays;
import java.util.List;
-import java.util.stream.Collectors;
-import org.apache.commons.cli.CommandLineParser;
-import org.apache.commons.cli.DefaultParser;
-import org.apache.commons.cli.HelpFormatter;
+import java.util.stream.Stream;
+
+import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
-import org.apache.commons.cli.ParseException;
import org.eclipse.emf.ecore.resource.Resource;
-import org.lflang.LFRuntimeModule;
-import org.lflang.LFStandaloneSetup;
-import org.lflang.LocalStrings;
+
import org.lflang.ast.FormattingUtils;
import org.lflang.util.FileUtil;
@@ -32,17 +30,21 @@
*/
public class Lff extends CliBase {
+ public Lff() {
+ super("lff");
+ }
+
/**
* Supported CLI options.
*
- * Stores an Apache Commons CLI Option for each entry, sets it to be required if so specified,
+ *
Stores an Apache Commons CLI Option for each entry, sets it to be required if so
+ * specified,
* and stores whether to pass the option to the code generator.
*
* @author Marten Lohstroh
* @author {Billy Bao }
*/
enum CLIOption {
- HELP("h", "help", false, false, "Display this information."),
DRY_RUN(
"d",
"dry-run",
@@ -69,7 +71,7 @@ enum CLIOption {
"If specified, outputs all formatted files into this directory instead of"
+ " overwriting the original files. Subdirectory structure will be preserved."),
VERBOSE("v", "verbose", false, false, "Print more details on files affected."),
- VERSION(null, "version", false, false, "Print version information.");
+ ;
/** The corresponding Apache CLI Option object. */
public final Option option;
@@ -102,74 +104,51 @@ public static Options getOptions() {
}
}
+
/**
- * Main function of the stand-alone compiler.
+ * Entry point of the formatter.
+ * Caution: this will invoke System.exit.
*
* @param args CLI arguments
*/
public static void main(final String[] args) {
- final ReportingBackend reporter = new ReportingBackend(new Io(), "lff: ");
-
- // Injector used to obtain Main instance.
- final Injector injector =
- new LFStandaloneSetup(new LFRuntimeModule(), new LFStandaloneModule(reporter))
- .createInjectorAndDoEMFRegistration();
- // Main instance.
- final Lff main = injector.getInstance(Lff.class);
- // Apache Commons Options object configured according to available CLI arguments.
- Options options = CLIOption.getOptions();
- // CLI arguments parser.
- CommandLineParser parser = new DefaultParser();
- // Helper object for printing "help" menu.
- HelpFormatter formatter = new HelpFormatter();
-
- try {
- main.cmd = parser.parse(options, args, true);
-
- // If requested, print help and abort
- if (main.cmd.hasOption(CLIOption.HELP.option.getOpt())) {
- formatter.printHelp("lff", options);
- System.exit(0);
- }
+ main(Io.SYSTEM, args);
+ }
- // If requested, print version and abort
- if (main.cmd.hasOption(CLIOption.VERSION.option.getLongOpt())) {
- System.out.println("lff " + LocalStrings.VERSION);
- System.exit(0);
- }
+ /**
+ * Programmatic entry point, with a custom IO.
+ *
+ * @param io IO streams.
+ * @param args Command-line arguments.
+ */
+ public static void main(Io io, final String... args) {
+ cliMain("lff", Lff.class, io, args);
+ }
- List files = main.cmd.getArgList();
- if (files.size() < 1) {
- reporter.printFatalErrorAndExit("No input files.");
- }
- try {
- List paths = files.stream().map(Paths::get).collect(Collectors.toList());
- main.runFormatter(paths);
- } catch (RuntimeException e) {
- reporter.printFatalErrorAndExit("An unexpected error occurred:", e);
- }
- } catch (ParseException e) {
- reporter.printFatalError("Unable to parse commandline arguments. Reason: " + e.getMessage());
- formatter.printHelp("lff", options);
- System.exit(1);
- }
+ @Override
+ protected Options getOptions() {
+ return CLIOption.getOptions();
}
/**
- * Check all given input paths and the output path, then invokes the formatter on all files given.
+ * Check all given input paths and the output path, then invokes the formatter on all files
+ * given.
*/
- private void runFormatter(List files) {
+ @Override
+ protected void runTool(CommandLine cmd, List files) {
String pathOption = CLIOption.OUTPUT_PATH.option.getOpt();
- Path outputRoot = null;
+ final Path outputRoot;
if (cmd.hasOption(pathOption)) {
- outputRoot = Paths.get(cmd.getOptionValue(pathOption)).toAbsolutePath().normalize();
+ outputRoot = io.getWd().resolve(cmd.getOptionValue(pathOption)).toAbsolutePath().normalize();
if (!Files.exists(outputRoot)) {
reporter.printFatalErrorAndExit("Output location '" + outputRoot + "' does not exist.");
}
if (!Files.isDirectory(outputRoot)) {
reporter.printFatalErrorAndExit("Output location '" + outputRoot + "' is not a directory.");
}
+ } else {
+ outputRoot = null;
}
for (Path path : files) {
@@ -185,72 +164,60 @@ private void runFormatter(List files) {
final boolean dryRun = cmd.hasOption(CLIOption.DRY_RUN.option.getOpt());
- for (Path path : files) {
- if (verbose()) {
- reporter.printInfo("Formatting " + path + ":");
+ boolean verbose = cmd.hasOption(CLIOption.VERBOSE.option.getOpt());
+ for (Path relPath : files) {
+ if (verbose) {
+ reporter.printInfo("Formatting " + io.getWd().relativize(relPath) + ":");
}
- path = path.toAbsolutePath();
+ Path path = toAbsolutePath(relPath);
if (Files.isDirectory(path) && !cmd.hasOption(CLIOption.NO_RECURSE.option.getLongOpt())) {
- formatRecursive(Paths.get("."), path, outputRoot, lineLength, dryRun);
- } else {
- if (outputRoot == null) {
- formatSingleFile(path, path, lineLength, dryRun);
- } else {
- formatSingleFile(path, outputRoot.resolve(path.getFileName()), lineLength, dryRun);
+ // this is a directory, walk its contents.
+ try {
+ Files.walkFileTree(path, new SimpleFileVisitor<>() {
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
+ formatSingleFile(file, path, outputRoot, lineLength, dryRun, verbose);
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ } catch (IOException e) {
+ reporter.printError("IO error: " + e);
}
+
+ } else {
+ // Simple file
+ formatSingleFile(path, path.getParent(), outputRoot, lineLength, dryRun, verbose);
}
}
- if (!dryRun || verbose()) reporter.printInfo("Done formatting.");
- }
- /**
- * Invoke the formatter on all files in a directory recursively.
- *
- * @param curPath Current relative path from inputRoot.
- * @param inputRoot Root directory of input files.
- * @param outputRoot Root output directory.
- * @param lineLength The preferred maximum number of columns per line.
- */
- private void formatRecursive(
- Path curPath, Path inputRoot, Path outputRoot, int lineLength, boolean dryRun) {
- Path curDir = inputRoot.resolve(curPath);
- try (var dirStream = Files.newDirectoryStream(curDir)) {
- for (Path path : dirStream) {
- Path newPath = curPath.resolve(path.getFileName());
- if (Files.isDirectory(path)) {
- formatRecursive(newPath, inputRoot, outputRoot, lineLength, dryRun);
- } else {
- if (outputRoot == null) {
- formatSingleFile(path, path, lineLength, dryRun);
- } else {
- formatSingleFile(path, outputRoot.resolve(newPath), lineLength, dryRun);
- }
- }
- }
- } catch (IOException e) {
- reporter.printError("Error reading directory " + curDir + ": " + e.getMessage());
+ exitIfCollectedErrors();
+ if (!dryRun || verbose) {
+ reporter.printInfo("Done formatting.");
}
}
/** Load and validate a single file, then format it and output to the given outputPath. */
- private void formatSingleFile(Path file, Path outputPath, int lineLength, boolean dryRun) {
+ private void formatSingleFile(Path file, Path inputRoot, Path outputRoot, int lineLength, boolean dryRun, boolean verbose) {
file = file.normalize();
- outputPath = outputPath.normalize();
+ Path outputPath = outputRoot == null
+ ? file // format in place
+ : outputRoot.resolve(inputRoot.relativize(file)).normalize();
final Resource resource = getResource(file);
if (resource == null) {
- if (verbose()) {
+ if (verbose) {
reporter.printInfo("Skipped " + file + ": not an LF file");
}
return; // not an LF file, nothing to do here
}
validateResource(resource);
+ // todo don't abort whole run if one file has errors
exitIfCollectedErrors();
final String formattedFileContents =
FormattingUtils.render(resource.getContents().get(0), lineLength);
if (dryRun) {
- System.out.print(formattedFileContents);
+ io.getOut().print(formattedFileContents);
} else {
try {
FileUtil.writeToFile(formattedFileContents, outputPath, true);
@@ -263,21 +230,16 @@ private void formatSingleFile(Path file, Path outputPath, int lineLength, boolea
+ ": file already exists. Make sure that no file or directory"
+ " within provided input paths have the same relative paths.");
}
- reporter.printFatalErrorAndExit("Error writing to " + outputPath + ": " + e.getMessage());
}
}
exitIfCollectedErrors();
issueCollector.getAllIssues().forEach(reporter::printIssue);
- if (verbose()) {
- String msg = "Formatted " + file;
- if (file != outputPath) msg += " -> " + outputPath;
+ if (verbose) {
+ String msg = "Formatted " + io.getWd().relativize(file);
+ if (file != outputPath) msg += " -> " + io.getWd().relativize(outputPath);
reporter.printInfo(msg);
}
}
- /** Return whether the "verbose" flag has been set. */
- private boolean verbose() {
- return cmd.hasOption(CLIOption.VERBOSE.option.getOpt());
- }
}
diff --git a/org.lflang/src/org/lflang/cli/ReportingUtil.kt b/org.lflang/src/org/lflang/cli/ReportingUtil.kt
index 2db16da06d..998b1174f9 100644
--- a/org.lflang/src/org/lflang/cli/ReportingUtil.kt
+++ b/org.lflang/src/org/lflang/cli/ReportingUtil.kt
@@ -29,12 +29,14 @@ import com.google.inject.Singleton
import org.eclipse.xtext.diagnostics.Severity
import java.io.IOException
import java.io.PrintStream
+import java.lang.Error
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.util.*
import java.util.Collections.nCopies
+import java.util.function.Consumer
import kotlin.collections.ArrayList
import kotlin.math.max
import kotlin.math.min
@@ -70,8 +72,50 @@ import kotlin.system.exitProcess
class Io @JvmOverloads constructor(
val err: PrintStream = System.err,
val out: PrintStream = System.out,
- val wd: Path = Paths.get("").toAbsolutePath()
-)
+ val wd: Path = Paths.get("").toAbsolutePath(),
+ /**
+ * A callback to quit the current process. Mapped to [System.exit]
+ * by default.
+ */
+ private val systemExit: (Int) -> Nothing = { exitProcess(it) }
+) {
+
+ /**
+ * Call the callback corresponding to System.exit. This function
+ * never returns.
+ */
+ fun callSystemExit(exitCode: Int): Nothing {
+ systemExit.invoke(exitCode)
+ }
+
+ /**
+ * Execute the [funWithIo] with a new [Io] instance, where
+ * [systemExit] is replaced with a catchable callback. If
+ * the provided [funWithIo] calls [systemExit], the VM will
+ * not exit, instead this method will return normally with
+ * the exit code. If the function exits normally, the exit
+ * code 0 is returned.
+ *
+ * This can be used to mock an environment for tests.
+ */
+ fun fakeSystemExit(funWithIo: Consumer): Int {
+ val newIo = Io(this.err, this.out, this.wd, systemExit = { throw ProcessExitError(it) })
+ return try {
+ funWithIo.accept(newIo)
+ 0
+ } catch (e: ProcessExitError) {
+ e.exitCode
+ }
+ }
+
+ companion object {
+ /** Use system streams. */
+ @JvmField
+ val SYSTEM = Io()
+
+ private class ProcessExitError(val exitCode: Int) : Error()
+ }
+}
/**
* Represents an issue at a particular point in the program.
@@ -178,7 +222,7 @@ class ReportingBackend constructor(
@JvmOverloads
fun printFatalErrorAndExit(message: String, cause: Throwable? = null): Nothing {
printFatalError(message, cause)
- exitProcess(1)
+ io.callSystemExit(1)
}
/** Print a fatal error message to [Io.err] and exit with code 1. */
diff --git a/org.lflang/src/org/lflang/generator/ExpressionGenerator.java b/org.lflang/src/org/lflang/generator/ExpressionGenerator.java
deleted file mode 100644
index df89b1b2cf..0000000000
--- a/org.lflang/src/org/lflang/generator/ExpressionGenerator.java
+++ /dev/null
@@ -1,164 +0,0 @@
-package org.lflang.generator;
-
-import java.util.List;
-import java.util.ArrayList;
-import java.util.stream.Collectors;
-
-import org.lflang.ASTUtils;
-import org.lflang.TimeValue;
-import org.lflang.lf.Assignment;
-import org.lflang.lf.Expression;
-import org.lflang.lf.Instantiation;
-import org.lflang.lf.Parameter;
-import org.lflang.lf.ParameterReference;
-import org.lflang.lf.StateVar;
-import org.lflang.lf.Time;
-import org.lflang.TimeUnit;
-
-/**
- * Encapsulates logic for representing {@code Value}s in a
- * target language.
- */
-public final class ExpressionGenerator {
-
- /**
- * A {@code TimeInTargetLanguage} is a
- * target-language-specific time representation
- * strategy.
- */
- public interface TimeInTargetLanguage {
- String apply(TimeValue t);
- }
-
- /**
- * A {@code GetTargetReference} instance is a
- * target-language-specific function. It provides the
- * target language code that refers to the given
- * parameter {@code param}.
- */
- public interface GetTargetReference {
- String apply(Parameter param);
- }
-
- private final TimeInTargetLanguage timeInTargetLanguage;
- private final GetTargetReference getTargetReference;
-
- /**
- * Instantiates a target-language-specific
- * ExpressionGenerator parameterized by {@code f}.
- * @param f a time representation strategy
- */
- public ExpressionGenerator(TimeInTargetLanguage f, GetTargetReference g) {
- this.timeInTargetLanguage = f;
- this.getTargetReference = g;
- }
-
- /**
- * Create a list of state initializers in target code.
- *
- * @param state The state variable to create initializers for
- * @return A list of initializers in target code
- */
- public List getInitializerList(StateVar state) {
- List list = new ArrayList<>();
- // FIXME: Previously, we returned null if it was not initialized, which would have caused an
- // NPE in TSStateGenerator. Is this the desired behavior?
- if (!ASTUtils.isInitialized(state)) return list;
- for (Expression expr : state.getInit()) {
- if (expr instanceof ParameterReference) {
- list.add(getTargetReference.apply(((ParameterReference)expr).getParameter()));
- } else {
- list.add(getTargetValue(expr, ASTUtils.isOfTimeType(state)));
- }
- }
- return list;
- }
-
- /**
- * Create a list of default parameter initializers in target code.
- *
- * @param param The parameter to create initializers for
- * @return A list of initializers in target code
- */
- public List getInitializerList(Parameter param) {
- List list = new ArrayList<>();
- if (param == null) return list;
- for (Expression expr : param.getInit())
- list.add(getTargetValue(expr, ASTUtils.isOfTimeType(param)));
- return list;
- }
-
- /**
- * Create a list of parameter initializers in target code in the context
- * of an reactor instantiation.
- *
- * This respects the parameter assignments given in the reactor
- * instantiation and falls back to the reactors default initializers
- * if no value is assigned to it.
- *
- * @param param The parameter to create initializers for
- * @return A list of initializers in target code
- */
- public List getInitializerList(Parameter param, Instantiation i) {
- List assignments = i.getParameters().stream()
- .filter(it -> it.getLhs() == param)
- .collect(Collectors.toList());
- if (assignments.isEmpty()) // Case 0: The parameter was not overwritten in the instantiation
- return getInitializerList(param);
- // Case 1: The parameter was overwritten in the instantiation
- List list = new ArrayList<>();
- if (assignments.get(0) == null) return list;
- for (Expression expr : assignments.get(0).getRhs())
- list.add(getTargetValue(expr, ASTUtils.isOfTimeType(param)));
- return list;
- }
-
- /**
- * Return the time specified by {@code t}, expressed as
- * code that is valid for some target languages.
- */
- public String getTargetTime(TimeValue t) {
- return timeInTargetLanguage.apply(t);
- }
-
- /**
- * Return the time specified by {@code t}, expressed as
- * code that is valid for some target languages.
- */
- public String getTargetTime(Time t) {
- return timeInTargetLanguage.apply(new TimeValue(t.getInterval(), TimeUnit.fromName(t.getUnit())));
- }
-
- /**
- * Return the time specified by {@code v}, expressed as
- * code that is valid for some target languages.
- */
- public String getTargetTime(Expression expr) {
- return getTargetValue(expr, true);
- }
-
- /**
- * Get textual representation of an expression in the target language.
- *
- * If the value evaluates to 0, it is interpreted as a literal.
- *
- * @param expr A time AST node
- * @return A time string in the target language
- */
- public String getTargetValue(Expression expr) {
- return ASTUtils.toText(expr);
- }
-
- /**
- * Get textual representation of an expression in the target language.
- *
- * @param expr A time AST node
- * @param isTime Whether {@code v} is expected to be a time
- * @return A time string in the target language
- */
- public String getTargetValue(Expression expr, boolean isTime) {
- if (expr instanceof Time) return getTargetTime((Time)expr);
- if (isTime && ASTUtils.isZero(expr)) return timeInTargetLanguage.apply(TimeValue.ZERO);
- return ASTUtils.toText(expr);
- }
-}
diff --git a/org.lflang/src/org/lflang/generator/GeneratorUtils.java b/org.lflang/src/org/lflang/generator/GeneratorUtils.java
index 959405e4af..be4e122c5e 100644
--- a/org.lflang/src/org/lflang/generator/GeneratorUtils.java
+++ b/org.lflang/src/org/lflang/generator/GeneratorUtils.java
@@ -19,12 +19,15 @@
import org.eclipse.xtext.validation.CheckMode;
import org.eclipse.xtext.validation.IResourceValidator;
import org.eclipse.xtext.validation.Issue;
+
+import org.lflang.ASTUtils;
import org.lflang.ErrorReporter;
import org.lflang.FileConfig;
import org.lflang.Target;
import org.lflang.TargetConfig;
import org.lflang.TargetProperty.BuildType;
import org.lflang.TargetProperty.LogLevel;
+import org.lflang.TargetProperty.UnionType;
import org.lflang.generator.LFGeneratorContext.Mode;
import org.lflang.TargetProperty;
import org.lflang.TargetProperty.SchedulerOption;
@@ -83,6 +86,9 @@ public static void setTargetConfig(
if (context.getArgs().containsKey("no-compile")) {
targetConfig.noCompile = true;
}
+ if (context.getArgs().containsKey("build-type")) {
+ targetConfig.cmakeBuildType = (BuildType) UnionType.BUILD_TYPE_UNION.forName(context.getArgs().getProperty("build-type"));
+ }
if (context.getArgs().containsKey("logging")) {
targetConfig.logLevel = LogLevel.valueOf(context.getArgs().getProperty("logging").toUpperCase());
}
diff --git a/org.lflang/src/org/lflang/generator/GeneratorUtils.kt b/org.lflang/src/org/lflang/generator/GeneratorUtils.kt
index e124080f82..6d11501b80 100644
--- a/org.lflang/src/org/lflang/generator/GeneratorUtils.kt
+++ b/org.lflang/src/org/lflang/generator/GeneratorUtils.kt
@@ -2,11 +2,16 @@ package org.lflang.generator
import org.eclipse.emf.ecore.EObject
import org.eclipse.xtext.nodemodel.util.NodeModelUtils
-import org.lflang.ErrorReporter
+import org.lflang.InferredType
+import org.lflang.isInitWithBraces
+import org.lflang.lf.Expression
+import org.lflang.lf.Instantiation
+import org.lflang.lf.LfFactory
+import org.lflang.lf.Parameter
+import org.lflang.lf.StateVar
import org.lflang.toPath
-import org.lflang.toUnixString
import org.lflang.toTextTokenBased
-import org.lflang.lf.Instantiation
+import org.lflang.toUnixString
/** A transparent type alias to document when a string contains target code. */
typealias TargetCode = String
@@ -42,3 +47,48 @@ fun EObject.locationInfo(): LocationInfo {
lfText = toTextTokenBased() ?: ""
)
}
+
+
+/**
+ * Returns the target code for the initial value of [sv].
+ */
+fun TargetTypes.getTargetInitializer(sv: StateVar): TargetCode =
+ this.getTargetInitializer(sv.init, sv.type, sv.braces.isNotEmpty())
+
+/**
+ * Returns the target code for the default value of the [param].
+ */
+fun TargetTypes.getTargetInitializer(param: Parameter): TargetCode =
+ this.getTargetInitializer(param.init, param.type, param.isInitWithBraces)
+
+/**
+ * Returns the target code for the [getActualValue] of the
+ * param for this instantiation.
+ */
+fun TargetTypes.getTargetInitializer(param: Parameter, inst: Instantiation): TargetCode {
+ val init = inst.getActualValue(param)
+ return getTargetInitializer(init, param.type, param.isInitWithBraces)
+}
+
+/**
+ * Return the actual value of a parameter for the given instantiation.
+ * The value is defaulted to the default value for the parameter if
+ * there is no explicit assignment. If there is no default value, the
+ * source code is invalid (param is required)
+ */
+fun Instantiation.getActualValue(param: Parameter): List =
+ parameters.firstOrNull { it.lhs == param }?.rhs
+ ?: param.init
+ ?: throw InvalidLfSourceException(this, "No value for parameter ${param.name}")
+
+
+/**
+ * Return the target code for the given expression, given
+ * that it's a time expression.
+ */
+fun TargetTypes.getTargetTimeExpr(v: Expression): TargetCode =
+ this.getTargetExpr(v, InferredType.time())
+
+/** If this is null, return the literal 0. */
+fun Expression?.orZero(): Expression =
+ this ?: LfFactory.eINSTANCE.createLiteral().apply { literal = "0" }
diff --git a/org.lflang/src/org/lflang/generator/LFGeneratorContext.java b/org.lflang/src/org/lflang/generator/LFGeneratorContext.java
index 3fd521dc17..f1f2a49ac8 100644
--- a/org.lflang/src/org/lflang/generator/LFGeneratorContext.java
+++ b/org.lflang/src/org/lflang/generator/LFGeneratorContext.java
@@ -25,6 +25,7 @@ public interface LFGeneratorContext extends IGeneratorContext {
* Enumeration of keys used to parameterize the build process.
*/
public enum BuildParm {
+ BUILD_TYPE("The build type to use"),
CLEAN("Clean before building."),
EXTERNAL_RUNTIME_PATH("Specify an external runtime library to be used by the compiled binary."),
FEDERATED("Treat main reactor as federated."),
diff --git a/org.lflang/src/org/lflang/generator/TargetTypes.java b/org.lflang/src/org/lflang/generator/TargetTypes.java
index 8e72c2a3f0..38d918d72a 100644
--- a/org.lflang/src/org/lflang/generator/TargetTypes.java
+++ b/org.lflang/src/org/lflang/generator/TargetTypes.java
@@ -6,7 +6,6 @@
import org.lflang.ASTUtils;
import org.lflang.InferredType;
-import org.lflang.TimeUnit;
import org.lflang.TimeValue;
import org.lflang.lf.Action;
import org.lflang.lf.Code;
@@ -64,6 +63,10 @@ public interface TargetTypes {
String getTargetVariableSizeListType(String baseType);
+ default String getTargetParamRef(ParameterReference expr, InferredType typeOrNull) {
+ return escapeIdentifier(expr.getParameter().getName());
+ }
+
/**
* Return an "undefined" type which is used as a default
* when a type cannot be inferred.
@@ -232,7 +235,7 @@ default String getTargetExpr(Expression expr, InferredType type) {
if (ASTUtils.isZero(expr) && type != null && type.isTime) {
return getTargetTimeExpr(TimeValue.ZERO);
} else if (expr instanceof ParameterReference) {
- return escapeIdentifier(((ParameterReference) expr).getParameter().getName());
+ return getTargetParamRef((ParameterReference) expr, type);
} else if (expr instanceof Time) {
return getTargetTimeExpr((Time) expr);
} else if (expr instanceof Literal) {
@@ -249,6 +252,6 @@ default String getTargetExpr(Expression expr, InferredType type) {
* target code.
*/
default String getTargetTimeExpr(Time t) {
- return getTargetTimeExpr(new TimeValue(t.getInterval(), TimeUnit.fromName(t.getUnit())));
+ return getTargetTimeExpr(ASTUtils.toTimeValue(t));
}
}
diff --git a/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java b/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java
index b87f53f9f2..ef3c5352eb 100644
--- a/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java
+++ b/org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java
@@ -54,6 +54,8 @@ public class CCmakeGenerator {
)
""";
+ public static final String MIN_CMAKE_VERSION = "3.19";
+
private final FileConfig fileConfig;
private final List additionalSources;
private final SetUpMainTarget setUpMainTarget;
@@ -115,10 +117,25 @@ CodeBuilder generateCMakeCode(
additionalSources.addAll(this.additionalSources);
cMakeCode.newLine();
- cMakeCode.pr("cmake_minimum_required(VERSION 3.13)");
+ cMakeCode.pr("cmake_minimum_required(VERSION " + MIN_CMAKE_VERSION + ")");
cMakeCode.pr("project("+executableName+" LANGUAGES C)");
-
cMakeCode.newLine();
+
+ // The Test build type is the Debug type plus coverage generation
+ cMakeCode.pr("if(CMAKE_BUILD_TYPE STREQUAL \"Test\")");
+ cMakeCode.pr(" set(CMAKE_BUILD_TYPE \"Debug\")");
+ cMakeCode.pr(" if(CMAKE_C_COMPILER_ID STREQUAL \"GNU\")");
+ cMakeCode.pr(" find_program(LCOV_BIN lcov)");
+ cMakeCode.pr(" if(LCOV_BIN MATCHES \"lcov$\")");
+ cMakeCode.pr(" set(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} --coverage -fprofile-arcs -ftest-coverage\")");
+ cMakeCode.pr(" else()");
+ cMakeCode.pr(" message(\"Not producing code coverage information since lcov was not found\")");
+ cMakeCode.pr(" endif()");
+ cMakeCode.pr(" else()");
+ cMakeCode.pr(" message(\"Not producing code coverage information since the selected compiler is no gcc\")");
+ cMakeCode.pr(" endif()");
+ cMakeCode.pr("endif()");
+
cMakeCode.pr("# Require C11");
cMakeCode.pr("set(CMAKE_C_STANDARD 11)");
cMakeCode.pr("set(CMAKE_C_STANDARD_REQUIRED ON)");
@@ -181,10 +198,20 @@ CodeBuilder generateCMakeCode(
// If the LF program itself is threaded or if tracing is enabled, we need to define
// NUMBER_OF_WORKERS so that platform-specific C files will contain the appropriate functions
- cMakeCode.pr("# Set the number of workers to enable threading");
+ cMakeCode.pr("# Set the number of workers to enable threading/tracing");
cMakeCode.pr("target_compile_definitions(${LF_MAIN_TARGET} PUBLIC NUMBER_OF_WORKERS="+targetConfig.workers+")");
cMakeCode.newLine();
}
+
+ // Add additional flags so runtime can distinguish between multi-threaded and single-threaded mode
+ if (targetConfig.threading) {
+ cMakeCode.pr("# Set flag to indicate a multi-threaded runtime");
+ cMakeCode.pr("target_compile_definitions( ${LF_MAIN_TARGET} PUBLIC LF_MULTI_THREADED)");
+ } else {
+ cMakeCode.pr("# Set flag to indicate a single-threaded runtime");
+ cMakeCode.pr("target_compile_definitions( ${LF_MAIN_TARGET} PUBLIC LF_SINGLE_THREADED)");
+ }
+ cMakeCode.newLine();
cMakeCode.pr("# Target definitions\n");
targetConfig.compileDefinitions.forEach((key, value) -> cMakeCode.pr(
diff --git a/org.lflang/src/org/lflang/generator/c/CCompiler.java b/org.lflang/src/org/lflang/generator/c/CCompiler.java
index 1c0145cd3d..02333e3704 100644
--- a/org.lflang/src/org/lflang/generator/c/CCompiler.java
+++ b/org.lflang/src/org/lflang/generator/c/CCompiler.java
@@ -36,6 +36,7 @@
import org.lflang.ErrorReporter;
import org.lflang.FileConfig;
import org.lflang.TargetConfig;
+import org.lflang.TargetProperty;
import org.lflang.TargetProperty.Platform;
import org.lflang.generator.GeneratorBase;
import org.lflang.generator.GeneratorCommandFactory;
@@ -174,7 +175,7 @@ public boolean runCCompiler(
if (makeReturnCode == 0 && build.getErrors().toString().length() == 0) {
- System.out.println("SUCCESS: Compiling generated code for "+ fileConfig.name +" finished with no errors.");
+ System.out.println("SUCCESS: Compiling generated code for " + fileConfig.name + " finished with no errors.");
}
}
@@ -195,8 +196,9 @@ public LFCommand compileCmakeCommand() {
buildPath);
if (command == null) {
errorReporter.reportError(
- "The C/CCpp target requires CMAKE >= 3.5 to compile the generated code. " +
- "Auto-compiling can be disabled using the \"no-compile: true\" target property.");
+ "The C/CCpp target requires CMAKE >= " + CCmakeGenerator.MIN_CMAKE_VERSION
+ + " to compile the generated code. " +
+ "Auto-compiling can be disabled using the \"no-compile: true\" target property.");
}
return command;
}
@@ -210,6 +212,7 @@ private static List cmakeOptions(TargetConfig targetConfig, FileConfig f
List arguments = new ArrayList<>();
cmakeCompileDefinitions(targetConfig).forEachOrdered(arguments::add);
arguments.addAll(List.of(
+ "-DCMAKE_BUILD_TYPE=" + ((targetConfig.cmakeBuildType!=null) ? targetConfig.cmakeBuildType.toString() : "Release"),
"-DCMAKE_INSTALL_PREFIX=" + FileUtil.toUnixString(fileConfig.getOutPath()),
"-DCMAKE_INSTALL_BINDIR=" + FileUtil.toUnixString(
fileConfig.getOutPath().relativize(
@@ -231,6 +234,20 @@ private static List cmakeOptions(TargetConfig targetConfig, FileConfig f
return arguments;
}
+ /**
+ * Return the cmake config name correspnding to a given build type.
+ */
+ private String buildTypeToCmakeConfig(TargetProperty.BuildType type) {
+ if (type == null) {
+ return "Release";
+ }
+ switch (type) {
+ case TEST:
+ return "Debug";
+ default:
+ return type.toString();
+ }
+ }
/**
* Return a command to build the specified C file using CMake.
@@ -246,8 +263,7 @@ public LFCommand buildCmakeCommand() {
LFCommand command = commandFactory.createCommand(
"cmake", List.of(
"--build", ".", "--target", "install", "--parallel", cores, "--config",
- targetConfig.cmakeBuildType != null ?
- targetConfig.cmakeBuildType.toString() : "Release"
+ buildTypeToCmakeConfig(targetConfig.cmakeBuildType)
),
buildPath);
if (command == null) {
diff --git a/org.lflang/src/org/lflang/generator/cpp/CppAssembleMethodGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppAssembleMethodGenerator.kt
index 32ba3c27d5..4ef12ee201 100644
--- a/org.lflang/src/org/lflang/generator/cpp/CppAssembleMethodGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/cpp/CppAssembleMethodGenerator.kt
@@ -28,6 +28,7 @@ import org.lflang.generator.PrependOperator
import org.lflang.isBank
import org.lflang.isMultiport
import org.lflang.hasMultipleConnections
+import org.lflang.joinWithLn
import org.lflang.lf.*
/**
@@ -175,9 +176,9 @@ class CppAssembleMethodGenerator(private val reactor: Reactor) {
"""
|// connection $idx
|std::vector<$portType> __lf_left_ports_$idx;
- ${" |"..c.leftPorts.joinToString("\n") { addAllPortsToVector(it, "__lf_left_ports_$idx") }}
+ ${" |"..c.leftPorts.joinWithLn { addAllPortsToVector(it, "__lf_left_ports_$idx") }}
|std::vector<$portType> __lf_right_ports_$idx;
- ${" |"..c.rightPorts.joinToString("\n") { addAllPortsToVector(it, "__lf_right_ports_$idx") }}
+ ${" |"..c.rightPorts.joinWithLn { addAllPortsToVector(it, "__lf_right_ports_$idx") }}
|lfutil::bind_multiple_ports(__lf_left_ports_$idx, __lf_right_ports_$idx, ${c.isIterated});
""".trimMargin()
}
diff --git a/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt
index a8968559ec..a7f6b6c920 100644
--- a/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt
@@ -132,7 +132,7 @@ class CppInstanceGenerator(
}
fun generateConstructorInitializers() =
- reactor.instantiations.filter { it.isBank }.joinToString("\n") { generateConstructorInitializer(it) }
+ reactor.instantiations.filter { it.isBank }.joinWithLn { generateConstructorInitializer(it) }
/** Generate constructor initializers for all reactor instantiations */
fun generateInitializers(): String =
diff --git a/org.lflang/src/org/lflang/generator/cpp/CppPortGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppPortGenerator.kt
index b2cb8aba3a..99f55655cd 100644
--- a/org.lflang/src/org/lflang/generator/cpp/CppPortGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/cpp/CppPortGenerator.kt
@@ -26,6 +26,7 @@ package org.lflang.generator.cpp
import org.lflang.inferredType
import org.lflang.isMultiport
+import org.lflang.joinWithLn
import org.lflang.lf.Input
import org.lflang.lf.Output
import org.lflang.lf.Port
@@ -89,8 +90,8 @@ class CppPortGenerator(private val reactor: Reactor) {
}
fun generateConstructorInitializers() =
- reactor.inputs.filter { it.isMultiport }.joinToString("\n") { generateConstructorInitializer(it) } +
- reactor.outputs.filter { it.isMultiport }.joinToString("\n") { generateConstructorInitializer(it) }
+ reactor.inputs.filter { it.isMultiport }.joinWithLn { generateConstructorInitializer(it) } +
+ reactor.outputs.filter { it.isMultiport }.joinWithLn { generateConstructorInitializer(it) }
fun generateDeclarations() =
reactor.inputs.joinToString("\n", "// input ports\n", postfix = "\n") { generateDeclaration(it) } +
diff --git a/org.lflang/src/org/lflang/generator/cpp/CppReactionGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppReactionGenerator.kt
index 8e2513dc71..ef58a5d2be 100644
--- a/org.lflang/src/org/lflang/generator/cpp/CppReactionGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/cpp/CppReactionGenerator.kt
@@ -26,6 +26,7 @@ package org.lflang.generator.cpp
import org.lflang.generator.PrependOperator
import org.lflang.isBank
+import org.lflang.joinWithLn
import org.lflang.label
import org.lflang.lf.*
import org.lflang.priority
@@ -193,15 +194,15 @@ class CppReactionGenerator(
}
private fun generateViews(r: Reaction) =
- r.allReferencedContainers.joinToString("\n") { generateViewForContainer(r, it) }
+ r.allReferencedContainers.joinWithLn { generateViewForContainer(r, it) }
private fun generateViewInitializers(r: Reaction) =
r.allReferencedContainers.filterNot { it.isBank }
- .joinToString("\n") { ", ${r.getViewInstanceName(it)}(${it.name}.get()) " }
+ .joinWithLn { ", ${r.getViewInstanceName(it)}(${it.name}.get()) " }
private fun generateViewConstructorInitializers(r: Reaction) =
r.allReferencedContainers.filter { it.isBank }
- .joinToString("\n") {
+ .joinWithLn {
val viewInstance = r.getViewInstanceName(it)
"""
$viewInstance.reserve(${it.name}.size());
diff --git a/org.lflang/src/org/lflang/generator/cpp/CppRos2PackageGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppRos2PackageGenerator.kt
index ff5fa22982..f15adfcde2 100644
--- a/org.lflang/src/org/lflang/generator/cpp/CppRos2PackageGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/cpp/CppRos2PackageGenerator.kt
@@ -1,6 +1,7 @@
package org.lflang.generator.cpp
import org.lflang.generator.PrependOperator
+import org.lflang.joinWithLn
import org.lflang.toUnixString
import java.nio.file.Path
@@ -30,7 +31,7 @@ class CppRos2PackageGenerator(generator: CppGenerator, private val nodeName: Str
| ament_cmake
| ament_cmake_auto
|
- ${" |"..dependencies.joinToString("\n") { "$it" } }
+ ${" |"..dependencies.joinWithLn { "$it" } }
|
| ament_lint_auto
| ament_lint_common
@@ -71,7 +72,7 @@ class CppRos2PackageGenerator(generator: CppGenerator, private val nodeName: Str
|
|ament_auto_add_library($S{LF_MAIN_TARGET} SHARED
| src/$nodeName.cc
- ${" | "..sources.joinToString("\n") { "src/$it" }}
+ ${" | "..sources.joinWithLn { "src/$it" }}
|)
|ament_target_dependencies($S{LF_MAIN_TARGET} ${dependencies.joinToString(" ")})
|target_include_directories($S{LF_MAIN_TARGET} PUBLIC
@@ -94,7 +95,7 @@ class CppRos2PackageGenerator(generator: CppGenerator, private val nodeName: Str
|
|ament_auto_package()
|
- ${" |"..(includeFiles?.joinToString("\n") { "include(\"$it\")" } ?: "")}
+ ${" |"..(includeFiles?.joinWithLn { "include(\"$it\")" } ?: "")}
""".trimMargin()
}
}
@@ -109,4 +110,4 @@ class CppRos2PackageGenerator(generator: CppGenerator, private val nodeName: Str
|ros2 run ${fileConfig.name} ${fileConfig.name}_exe
""".trimMargin()
}
-}
\ No newline at end of file
+}
diff --git a/org.lflang/src/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt
index af8aa4cd03..01a749acaa 100644
--- a/org.lflang/src/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt
@@ -26,6 +26,7 @@ package org.lflang.generator.cpp
import org.lflang.TargetConfig
import org.lflang.generator.PrependOperator
+import org.lflang.joinWithLn
import org.lflang.toUnixString
import java.nio.file.Path
@@ -46,6 +47,22 @@ class CppStandaloneCmakeGenerator(private val targetConfig: TargetConfig, privat
|cmake_minimum_required(VERSION 3.5)
|project($projectName VERSION 0.0.0 LANGUAGES CXX)
|
+ |# The Test build type is the Debug type plus coverage generation
+ |if(CMAKE_BUILD_TYPE STREQUAL "Test")
+ | set(CMAKE_BUILD_TYPE "Debug")
+ |
+ | if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+ | find_program(LCOV_BIN lcov)
+ | if(LCOV_BIN MATCHES "lcov$S")
+ | set(CMAKE_CXX_FLAGS "$S{CMAKE_CXX_FLAGS} --coverage -fprofile-arcs -ftest-coverage")
+ | else()
+ | message("Not producing code coverage information since lcov was not found")
+ | endif()
+ | else()
+ | message("Not producing code coverage information since the selected compiler is no gcc")
+ | endif()
+ |endif()
+ |
|# require C++ 17
|set(CMAKE_CXX_STANDARD 17 CACHE STRING "The C++ standard is cached for visibility in external tools." FORCE)
|set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -128,7 +145,7 @@ class CppStandaloneCmakeGenerator(private val targetConfig: TargetConfig, privat
|set(LF_MAIN_TARGET ${fileConfig.name})
|
|add_executable($S{LF_MAIN_TARGET}
- ${" | "..sources.joinToString("\n") { it.toUnixString() }}
+ ${" | "..sources.joinWithLn { it.toUnixString() }}
|)
|target_include_directories($S{LF_MAIN_TARGET} PUBLIC
| "$S{LF_SRC_PKG_PATH}/src"
@@ -156,7 +173,7 @@ class CppStandaloneCmakeGenerator(private val targetConfig: TargetConfig, privat
|set(${includesVarName(fileConfig.name)} $S{TARGET_INCLUDE_DIRECTORIES} CACHE STRING "Directories included in the main target." FORCE)
|set($compilerIdName $S{CMAKE_CXX_COMPILER_ID} CACHE STRING "The name of the C++ compiler." FORCE)
|
- ${" |"..(includeFiles?.joinToString("\n") { "include(\"$it\")" } ?: "")}
+ ${" |"..(includeFiles?.joinWithLn { "include(\"$it\")" } ?: "")}
""".trimMargin()
}
}
diff --git a/org.lflang/src/org/lflang/generator/cpp/CppStandaloneGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppStandaloneGenerator.kt
index f81db86228..c6899f5388 100644
--- a/org.lflang/src/org/lflang/generator/cpp/CppStandaloneGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/cpp/CppStandaloneGenerator.kt
@@ -1,5 +1,6 @@
package org.lflang.generator.cpp
+import org.lflang.TargetProperty
import org.lflang.generator.CodeMap
import org.lflang.generator.LFGeneratorContext
import org.lflang.toUnixString
@@ -122,6 +123,12 @@ class CppStandaloneGenerator(generator: CppGenerator) :
return 0
}
+ private fun buildTypeToCmakeConfig(type: TargetProperty.BuildType?) = when (type) {
+ null -> "Release"
+ TargetProperty.BuildType.TEST -> "Debug"
+ else -> type.toString()
+ }
+
private fun createMakeCommand(buildPath: Path, version: String, target: String): LFCommand {
val makeArgs: List
if (version.compareVersion("3.12.0") < 0) {
@@ -138,7 +145,7 @@ class CppStandaloneGenerator(generator: CppGenerator) :
"--parallel",
cores.toString(),
"--config",
- targetConfig.cmakeBuildType?.toString() ?: "Release"
+ buildTypeToCmakeConfig(targetConfig.cmakeBuildType)
)
}
diff --git a/org.lflang/src/org/lflang/generator/python/PythonGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonGenerator.java
index 1b0cd44d98..e61144a26b 100644
--- a/org.lflang/src/org/lflang/generator/python/PythonGenerator.java
+++ b/org.lflang/src/org/lflang/generator/python/PythonGenerator.java
@@ -799,7 +799,7 @@ private static String setUpMainTarget(boolean hasMain, String executableName, St
add_subdirectory(core)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR})
set(LF_MAIN_TARGET )
- find_package(Python COMPONENTS Interpreter Development)
+ find_package(Python 3.7.0...<3.11.0 COMPONENTS Interpreter Development)
Python_add_library(
${LF_MAIN_TARGET}
MODULE
diff --git a/org.lflang/src/org/lflang/generator/rust/RustCargoTomlEmitter.kt b/org.lflang/src/org/lflang/generator/rust/RustCargoTomlEmitter.kt
index c4ef1b8a16..a4b9ba0548 100644
--- a/org.lflang/src/org/lflang/generator/rust/RustCargoTomlEmitter.kt
+++ b/org.lflang/src/org/lflang/generator/rust/RustCargoTomlEmitter.kt
@@ -28,6 +28,7 @@ import org.lflang.TargetProperty.BuildType.*
import org.lflang.escapeStringLiteral
import org.lflang.generator.PrependOperator.rangeTo
import org.lflang.joinWithCommas
+import org.lflang.joinWithLn
import org.lflang.withDQuotes
import java.nio.file.Paths
@@ -52,7 +53,7 @@ object RustCargoTomlEmitter : RustEmitterBase() {
|env_logger = "0.9"
|log = { version = "0.4", features = ["release_max_level_info"] }
|clap = { version = "3.1.8", features = ["derive", "env"], optional = true }
-${" |"..crate.dependencies.asIterable().joinToString("\n") { (name, spec) -> name + " = " + spec.toToml() }}
+${" |"..crate.dependencies.asIterable().joinWithLn { (name, spec) -> name + " = " + spec.toToml() }}
|
|[[bin]]
|name = "${gen.executableName}"
diff --git a/org.lflang/src/org/lflang/generator/rust/RustEmitter.kt b/org.lflang/src/org/lflang/generator/rust/RustEmitter.kt
index a801367116..7a36bce23e 100644
--- a/org.lflang/src/org/lflang/generator/rust/RustEmitter.kt
+++ b/org.lflang/src/org/lflang/generator/rust/RustEmitter.kt
@@ -27,6 +27,7 @@ package org.lflang.generator.rust
import org.lflang.generator.CodeMap
import org.lflang.generator.PrependOperator
import org.lflang.generator.rust.RustEmitter.generateRustProject
+import org.lflang.joinWithLn
import org.lflang.util.FileUtil
import java.nio.file.Files
import java.nio.file.Path
@@ -95,7 +96,7 @@ object RustEmitter : RustEmitterBase() {
"""
|${generatedByComment("//")}
|
-${" |"..gen.reactors.joinToString("\n") { it.modDecl() }}
+${" |"..gen.reactors.joinWithLn { it.modDecl() }}
|
""".trimMargin()
}
diff --git a/org.lflang/src/org/lflang/generator/rust/RustMainFileEmitter.kt b/org.lflang/src/org/lflang/generator/rust/RustMainFileEmitter.kt
index 70d106f878..fb54556a2c 100644
--- a/org.lflang/src/org/lflang/generator/rust/RustMainFileEmitter.kt
+++ b/org.lflang/src/org/lflang/generator/rust/RustMainFileEmitter.kt
@@ -30,6 +30,7 @@ import org.lflang.generator.PrependOperator
import org.lflang.generator.PrependOperator.rangeTo
import org.lflang.generator.UnsupportedGeneratorFeatureException
import org.lflang.joinWithCommasLn
+import org.lflang.joinWithLn
import org.lflang.withoutQuotes
@@ -54,10 +55,10 @@ object RustMainFileEmitter : RustEmitterBase() {
|extern crate log;
|
|// user dependencies
-${" |"..gen.crate.dependencies.keys.joinToString("\n") { "extern crate ${it.replace('-', '_')};" }}
+${" |"..gen.crate.dependencies.keys.joinWithLn { "extern crate ${it.replace('-', '_')};" }}
|
|// user-defined modules
-${" |"..gen.crate.modulesToIncludeInMain.joinToString("\n") { "mod ${it.fileName.toString().removeSuffix(".rs")};" }}
+${" |"..gen.crate.modulesToIncludeInMain.joinWithLn { "mod ${it.fileName.toString().removeSuffix(".rs")};" }}
|
|use $rsRuntime::*;
|use log::LevelFilter;
diff --git a/org.lflang/src/org/lflang/generator/rust/RustModel.kt b/org.lflang/src/org/lflang/generator/rust/RustModel.kt
index 9de454ddcd..d4611451dc 100644
--- a/org.lflang/src/org/lflang/generator/rust/RustModel.kt
+++ b/org.lflang/src/org/lflang/generator/rust/RustModel.kt
@@ -547,7 +547,7 @@ object RustModelBuilder {
body = n.code.toText(),
isStartup = n.triggers.any { it is BuiltinTriggerRef && it.type == BuiltinTrigger.STARTUP },
isShutdown = n.triggers.any { it is BuiltinTriggerRef && it.type == BuiltinTrigger.SHUTDOWN },
- debugLabel = AttributeUtils.label(n),
+ debugLabel = AttributeUtils.getLabel(n),
loc = n.locationInfo().let {
// remove code block
it.copy(lfText = it.lfText.replace(TARGET_BLOCK_R, "{= ... =}"))
@@ -694,6 +694,7 @@ private val TypeParm.identifier: String
val BuildType.cargoProfileName: String
get() = when (this) {
BuildType.DEBUG -> "debug"
+ BuildType.TEST -> "test"
BuildType.RELEASE -> "release"
BuildType.REL_WITH_DEB_INFO -> "release-with-debug-info"
BuildType.MIN_SIZE_REL -> "release-with-min-size"
diff --git a/org.lflang/src/org/lflang/generator/rust/RustReactorEmitter.kt b/org.lflang/src/org/lflang/generator/rust/RustReactorEmitter.kt
index ab21972bd1..50b5a3c2b2 100644
--- a/org.lflang/src/org/lflang/generator/rust/RustReactorEmitter.kt
+++ b/org.lflang/src/org/lflang/generator/rust/RustReactorEmitter.kt
@@ -104,7 +104,7 @@ ${" | "..otherComponents.joinWithCommasLn { it.toStructField() }}
|
| let __impl = {
| // declare them all here so that they are visible to the initializers of state vars declared later
-${" | "..reactor.stateVars.joinToString("\n") { "let ${it.lfName} = ${it.init};" }}
+${" | "..reactor.stateVars.joinWithLn { "let ${it.lfName} = ${it.init};" }}
|
| $structName {
| __phantom: std::marker::PhantomData,
@@ -285,7 +285,7 @@ ${" | "..declareChildConnections()}
this += n.uses.map { trigger -> "__assembler.declare_uses(${n.invokerId}, __self.${trigger.rustFieldName}.get_id())?;" }
this += n.effects.filterIsInstance().map { port ->
if (port.isMultiport) {
- "__assembler.effects_bank(${n.invokerId}, &__self.${port.rustFieldName})?;"
+ "__assembler.effects_multiport(${n.invokerId}, &__self.${port.rustFieldName})?;"
} else {
"__assembler.effects_port(${n.invokerId}, &__self.${port.rustFieldName})?;"
}
@@ -337,7 +337,7 @@ ${" | "..declareChildConnections()}
if (isLogical) "$rsRuntime::LogicalAction<${dataType ?: "()"}>"
else "$rsRuntime::PhysicalActionRef<${dataType ?: "()"}>"
is PortLike -> with(this) {
- if (isMultiport) "$rsRuntime::PortBank<$dataType>"
+ if (isMultiport) "$rsRuntime::Multiport<$dataType>"
else "$rsRuntime::Port<$dataType>"
}
is TimerData -> "$rsRuntime::Timer"
@@ -354,7 +354,7 @@ ${" | "..declareChildConnections()}
is TimerData -> "__assembler.new_timer(\"$lfName\", $offset, $period)"
is PortData -> {
if (widthSpec != null) {
- "__assembler.new_port_bank::<$dataType>(\"$lfName\", $portKind, $widthSpec)?"
+ "__assembler.new_multiport::<$dataType>(\"$lfName\", $portKind, $widthSpec)?"
} else {
"__assembler.new_port::<$dataType>(\"$lfName\", $portKind)"
}
@@ -382,15 +382,9 @@ ${" | "..declareChildConnections()}
/** The type of the parameter injected into a reaction for the given dependency. */
private fun ReactorComponent.toBorrowedType(kind: DepKind): TargetCode =
- when (this) {
- is PortLike -> when {
- kind == DepKind.Effects && isMultiport -> "$rsRuntime::WritablePortBank<$dataType>" // note: owned
- kind == DepKind.Effects -> "$rsRuntime::WritablePort<$dataType>" // note: owned
- isMultiport -> "$rsRuntime::ReadablePortBank<$dataType>" // note: owned
- else -> "&$rsRuntime::ReadablePort<$dataType>" // note: a reference
- }
- is TimerData -> "&${toType()}"
- is ActionData -> if (kind == DepKind.Effects) "&mut ${toType()}" else "&${toType()}"
+ when (kind) {
+ DepKind.Effects -> "&mut ${toType()}"
+ else -> "&${toType()}"
}
/**
@@ -398,15 +392,9 @@ ${" | "..declareChildConnections()}
* into a reaction. This conceptually just borrows the field.
*/
private fun ReactorComponent.toBorrow(kind: DepKind): TargetCode =
- when (this) {
- is PortLike -> when {
- kind == DepKind.Effects && isMultiport -> "$rsRuntime::WritablePortBank::new(&mut self.$rustFieldName)" // note: owned
- kind == DepKind.Effects -> "$rsRuntime::WritablePort::new(&mut self.$rustFieldName)" // note: owned
- isMultiport -> "$rsRuntime::ReadablePortBank::new(&self.$rustFieldName)" // note: owned
- else -> "&$rsRuntime::ReadablePort::new(&self.$rustFieldName)" // note: a reference
- }
- is ActionData -> if (kind == DepKind.Effects) "&mut self.$rustFieldName" else "&self.$rustFieldName"
- is TimerData -> "&self.$rustFieldName"
+ when (kind) {
+ DepKind.Effects -> "&mut self.$rustFieldName"
+ else -> "&self.$rustFieldName"
}
private fun ReactorComponent.isNotInjectedInReaction(depKind: DepKind, n: ReactionInfo): Boolean =
@@ -418,12 +406,9 @@ ${" | "..declareChildConnections()}
// we skip the Trigger one and generate the Effects one.
depKind != DepKind.Effects && this in n.effects
- private fun ReactorComponent.isInjectedAsMut(depKind: DepKind): Boolean =
- depKind == DepKind.Effects && (this is PortData || this is ActionData)
-
/**
* Whether this component may be unused in a reaction.
- * Eg. actions on which we have just a trigger dependency
+ * E.g. actions on which we have just a trigger dependency
* are fine to ignore.
*/
private fun ReactorComponent.mayBeUnusedInReaction(depKind: DepKind): Boolean =
@@ -448,12 +433,7 @@ ${" | "..declareChildConnections()}
for (comp in comps) {
if (comp.isNotInjectedInReaction(kind, this@reactionParams)) continue
- // we want the user to be able to make
- // use of the mut if they want, but they
- // don't have to
- val mut = if (comp.isInjectedAsMut(kind)) "#[allow(unused_mut)] mut " else ""
-
- val param = "$mut${comp.rustRefName}: ${comp.toBorrowedType(kind)}"
+ val param = "${comp.rustRefName}: ${comp.toBorrowedType(kind)}"
if (comp.mayBeUnusedInReaction(kind)) {
yield("#[allow(unused)] $param")
diff --git a/org.lflang/src/org/lflang/generator/ts/TSActionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSActionGenerator.kt
index 73fec6a7f9..e162c16c26 100644
--- a/org.lflang/src/org/lflang/generator/ts/TSActionGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/ts/TSActionGenerator.kt
@@ -1,23 +1,18 @@
package org.lflang.generator.ts
import org.lflang.federated.FederateInstance
+import org.lflang.generator.getTargetTimeExpr
import org.lflang.lf.Action
-import org.lflang.lf.Expression
import org.lflang.lf.ParameterReference
-import org.lflang.lf.Type
import java.util.*
/**
* Generator for actions in TypeScript target.
*/
-class TSActionGenerator (
- // TODO(hokeun): Remove dependency on TSGenerator.
- private val tsGenerator: TSGenerator,
+class TSActionGenerator(
private val actions: List,
private val federate: FederateInstance
) {
- private fun Expression.getTargetValue(): String = tsGenerator.getTargetValueW(this)
- private fun Type.getTargetType(): String = tsGenerator.getTargetTypeW(this)
fun generateClassProperties(): String {
val stateClassProperties = LinkedList()
@@ -27,7 +22,7 @@ class TSActionGenerator (
// duplicate action if we included the one generated
// by LF.
if (action.name != "shutdown") {
- stateClassProperties.add("${action.name}: __Action<${getActionType(action)}>;")
+ stateClassProperties.add("${action.name}: __Action<${action.tsActionType}>;")
}
}
return stateClassProperties.joinToString("\n")
@@ -45,21 +40,21 @@ class TSActionGenerator (
if (action.minDelay != null) {
// Actions in the TypeScript target are constructed
// with an optional minDelay argument which defaults to 0.
- if (action.minDelay is ParameterReference) {
- actionArgs+= ", " + (action.minDelay as ParameterReference).parameter.name
+ actionArgs += if (action.minDelay is ParameterReference) {
+ ", " + (action.minDelay as ParameterReference).parameter.name
} else {
- actionArgs+= ", " + action.minDelay.getTargetValue()
+ ", " + action.minDelay.toTsTime()
}
}
if (action in networkMessageActions){
actionInstantiations.add(
- "this.${action.name} = new __FederatePortAction<${getActionType(action)}>($actionArgs);")
+ "this.${action.name} = new __FederatePortAction<${action.tsActionType}>($actionArgs);")
} else {
actionInstantiations.add(
- "this.${action.name} = new __Action<${getActionType(action)}>($actionArgs);")
+ "this.${action.name} = new __Action<${action.tsActionType}>($actionArgs);")
}
}
}
return actionInstantiations.joinToString("\n")
}
-}
\ No newline at end of file
+}
diff --git a/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt
index 070e6434d6..35ab8df514 100644
--- a/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt
@@ -4,6 +4,8 @@ import org.lflang.ErrorReporter
import org.lflang.TargetConfig
import org.lflang.federated.FederateInstance
import org.lflang.generator.PrependOperator
+import org.lflang.generator.getTargetInitializer
+import org.lflang.joinWithLn
import org.lflang.lf.Action
import org.lflang.lf.Parameter
import org.lflang.lf.Reactor
@@ -24,26 +26,9 @@ class TSConstructorGenerator (
private val federate: FederateInstance,
private val targetConfig: TargetConfig
) {
- private fun getInitializerList(param: Parameter): List =
- tsGenerator.getInitializerListW(param)
- // Initializer functions
- private fun getTargetInitializerHelper(param: Parameter,
- list: List): String {
- return if (list.size == 0) {
- errorReporter.reportError(param, "Parameters must have a default value!")
- } else if (list.size == 1) {
- list[0]
- } else {
- list.joinToString(", ", "[", "]")
- }
- }
- private fun getTargetInitializer(param: Parameter): String {
- return getTargetInitializerHelper(param, getInitializerList(param))
- }
- private fun initializeParameter(p: Parameter): String {
- return """${p.name}: ${p.getTargetType()} = ${getTargetInitializer(p)}"""
- }
+ private fun initializeParameter(p: Parameter): String =
+ "${p.name}: ${TSTypes.getTargetType(p)} = ${TSTypes.getTargetInitializer(p)}"
private fun generateConstructorArguments(reactor: Reactor): String {
val arguments = LinkedList()
@@ -83,7 +68,7 @@ class TSConstructorGenerator (
port = 15045
}
return """
- super(federationID, ${federate.id}, ${port},
+ super(federationID, ${federate.id}, $port,
"${federationRTIProperties()["host"]}",
timeout, keepAlive, fast, success, fail);
"""
@@ -94,29 +79,17 @@ class TSConstructorGenerator (
// If the app is federated, register its
// networkMessageActions with the RTIClient
- private fun generateFederatePortActionRegistrations(networkMessageActions: List): String {
- var fedPortID = 0;
- val connectionInstantiations = LinkedList()
- for (nAction in networkMessageActions) {
- val registration = """
- this.registerFederatePortAction(${fedPortID}, this.${nAction.name});
- """
- connectionInstantiations.add(registration)
- fedPortID++
+ private fun generateFederatePortActionRegistrations(networkMessageActions: List): String =
+ networkMessageActions.withIndex().joinWithLn { (fedPortID, nAction) ->
+ "this.registerFederatePortAction($fedPortID, this.${nAction.name});"
}
- return connectionInstantiations.joinToString("\n")
- }
// Generate code for setting target configurations.
- private fun generateTargetConfigurations(): String {
- val targetConfigurations = LinkedList()
- if ((reactor.isMain || reactor.isFederated) &&
- targetConfig.coordinationOptions.advance_message_interval != null) {
- targetConfigurations.add(
- "this.setAdvanceMessageInterval(${timeInTargetLanguage(targetConfig.coordinationOptions.advance_message_interval)})")
- }
- return targetConfigurations.joinToString("\n")
- }
+ private fun generateTargetConfigurations(): String =
+ if ((reactor.isMain || reactor.isFederated)
+ && targetConfig.coordinationOptions.advance_message_interval != null
+ ) "this.setAdvanceMessageInterval(${targetConfig.coordinationOptions.advance_message_interval.toTsTime()})"
+ else ""
// Generate code for registering Fed IDs that are connected to
// this federate via ports in the TypeScript's FederatedApp.
@@ -145,7 +118,7 @@ class TSConstructorGenerator (
ports: TSPortGenerator
): String {
val connections = TSConnectionGenerator(reactor.connections, errorReporter)
- val reactions = TSReactionGenerator(tsGenerator, errorReporter, reactor, federate)
+ val reactions = TSReactionGenerator(errorReporter, reactor, federate)
return with(PrependOperator) {
"""
@@ -168,4 +141,4 @@ class TSConstructorGenerator (
""".trimMargin()
}
}
-}
\ No newline at end of file
+}
diff --git a/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt b/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt
index 2e557db545..a01a01f4b3 100644
--- a/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt
+++ b/org.lflang/src/org/lflang/generator/ts/TSExtensions.kt
@@ -1,12 +1,12 @@
package org.lflang.generator.ts
import org.lflang.TimeValue
+import org.lflang.generator.getTargetTimeExpr
import org.lflang.isBank
import org.lflang.isMultiport
import org.lflang.lf.Action
-import org.lflang.lf.Parameter
+import org.lflang.lf.Expression
import org.lflang.lf.Port
-import org.lflang.lf.Type
import org.lflang.lf.WidthSpec
import org.lflang.toText
@@ -32,45 +32,23 @@ fun WidthSpec.toTSCode(): String = terms.joinToString(" + ") {
}
}
-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)
+val Port.tsPortType: String
+ get() = type?.let { TSTypes.getTargetType(it) } ?: "Present"
/**
* Return a TS type for the specified action.
* If the type has not been specified, return
* "Present" which is the base type for Actions.
- * @param action The action
* @return The TS type.
*/
-fun getActionType(action: Action): String {
- if (action.type != null) {
- return action.type.getTargetType()
- } else {
- return "Present"
- }
-}
+val Action.tsActionType: String
+ get() = type?.let { TSTypes.getTargetType(it) } ?: "Present"
-fun timeInTargetLanguage(value: TimeValue): String {
- return if (value.unit != null) {
- "TimeValue.${value.unit.canonicalName}(${value.time})"
- } else {
- // The value must be zero.
- "TimeValue.zero()"
- }
-}
+fun Expression.toTsTime(): String = TSTypes.getTargetTimeExpr(this)
+fun TimeValue.toTsTime(): String = TSTypes.getTargetTimeExpr(this)
diff --git a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt
index 82cd08c616..04f00cc788 100644
--- a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt
@@ -36,7 +36,6 @@ import org.lflang.federated.FederateInstance
import org.lflang.federated.launcher.FedTSLauncher
import org.lflang.federated.serialization.SupportedSerializers
import org.lflang.generator.CodeMap
-import org.lflang.generator.ExpressionGenerator
import org.lflang.generator.GeneratorBase
import org.lflang.generator.GeneratorResult
import org.lflang.generator.GeneratorUtils
@@ -47,13 +46,8 @@ import org.lflang.generator.PrependOperator
import org.lflang.generator.ReactorInstance
import org.lflang.generator.SubContext
import org.lflang.generator.TargetTypes
-import org.lflang.inferredType
import org.lflang.lf.Action
import org.lflang.lf.Expression
-import org.lflang.lf.Instantiation
-import org.lflang.lf.Parameter
-import org.lflang.lf.StateVar
-import org.lflang.lf.Type
import org.lflang.lf.VarRef
import org.lflang.scoping.LFGlobalScopeProvider
import org.lflang.util.FileUtil
@@ -91,9 +85,6 @@ class TSGenerator(
*/
val CONFIG_FILES = arrayOf("package.json", "tsconfig.json", "babel.config.js", ".eslintrc.json")
- private val VG =
- ExpressionGenerator(::timeInTargetLanguage) { param -> "this.${param.name}.get()" }
-
fun timeInTargetLanguage(value: TimeValue): String {
return if (value.unit != null) {
"TimeValue.${value.unit.canonicalName}(${value.magnitude})"
@@ -120,16 +111,6 @@ class TSGenerator(
// Wrappers to expose GeneratorBase methods.
fun federationRTIPropertiesW() = federationRTIProperties
- fun getTargetValueW(expr: Expression): String = VG.getTargetValue(expr, false)
- fun getTargetTypeW(p: Parameter): String = TSTypes.getTargetType(p.inferredType)
- fun getTargetTypeW(state: StateVar): String = TSTypes.getTargetType(state)
- fun getTargetTypeW(t: Type): String = TSTypes.getTargetType(t)
-
- fun getInitializerListW(state: StateVar): List = VG.getInitializerList(state)
- fun getInitializerListW(param: Parameter): List = VG.getInitializerList(param)
- fun getInitializerListW(param: Parameter, i: Instantiation): List =
- VG.getInitializerList(param, i)
-
/** Generate TypeScript code from the Lingua Franca model contained by the
* specified resource. This is the main entry point for code
* generation.
@@ -312,7 +293,7 @@ class TSGenerator(
targetConfig.protoFiles)
tsCode.append(preambleGenerator.generatePreamble())
- val parameterGenerator = TSParameterPreambleGenerator(this, fileConfig, targetConfig, reactors)
+ val parameterGenerator = TSParameterPreambleGenerator(fileConfig, targetConfig, reactors)
val (mainParameters, parameterCode) = parameterGenerator.generateParameters()
tsCode.append(parameterCode)
diff --git a/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt
index e623d2e05d..3b34214a78 100644
--- a/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/ts/TSImportPreambleGenerator.kt
@@ -26,8 +26,8 @@
package org.lflang.generator.ts
import org.lflang.generator.PrependOperator
+import org.lflang.joinWithLn
import java.nio.file.Path
-import java.util.*
/**
* Preamble generator for imports in TypeScript target.
@@ -48,54 +48,48 @@ class TSImportPreambleGenerator(
* Default imports for importing all the core classes and helper classes
* for CLI argument handling.
*/
- const val DEFAULT_IMPORTS = """
- |import commandLineArgs from 'command-line-args'
- |import commandLineUsage from 'command-line-usage'
- |import {Parameter as __Parameter, Timer as __Timer, Reactor as __Reactor, App as __App} from '@lf-lang/reactor-ts'
- |import {Action as __Action, Startup as __Startup, FederatePortAction as __FederatePortAction} from '@lf-lang/reactor-ts'
- |import {Bank as __Bank} from '@lf-lang/reactor-ts'
- |import {FederatedApp as __FederatedApp} from '@lf-lang/reactor-ts'
- |import {InPort as __InPort, OutPort as __OutPort, Port as __Port, WritablePort as __WritablePort, WritableMultiPort as __WritableMultiPort} from '@lf-lang/reactor-ts'
- |import {InMultiPort as __InMultiPort, OutMultiPort as __OutMultiPort} from '@lf-lang/reactor-ts'
- |import {Reaction as __Reaction} from '@lf-lang/reactor-ts'
- |import {State as __State} from '@lf-lang/reactor-ts'
- |import {TimeUnit, TimeValue, Tag as __Tag, Origin as __Origin} from '@lf-lang/reactor-ts'
- |import {Args as __Args, Variable as __Variable, Triggers as __Triggers, Present, Read, Write, ReadWrite, MultiReadWrite, Sched} from '@lf-lang/reactor-ts'
- |import {Log} from '@lf-lang/reactor-ts'
- |import {ProcessedCommandLineArgs as __ProcessedCommandLineArgs, CommandLineOptionDefs as __CommandLineOptionDefs, CommandLineUsageDefs as __CommandLineUsageDefs, CommandLineOptionSpec as __CommandLineOptionSpec, unitBasedTimeValueCLAType as __unitBasedTimeValueCLAType, booleanCLAType as __booleanCLAType} from '@lf-lang/reactor-ts'
- |"""
+ const val DEFAULT_IMPORTS = """
+ import commandLineArgs from 'command-line-args'
+ import commandLineUsage from 'command-line-usage'
+ import {Parameter as __Parameter, Timer as __Timer, Reactor as __Reactor, App as __App} from '@lf-lang/reactor-ts'
+ import {Action as __Action, Startup as __Startup, FederatePortAction as __FederatePortAction} from '@lf-lang/reactor-ts'
+ import {Bank as __Bank} from '@lf-lang/reactor-ts'
+ import {FederatedApp as __FederatedApp} from '@lf-lang/reactor-ts'
+ import {InPort as __InPort, OutPort as __OutPort, Port as __Port, WritablePort as __WritablePort, WritableMultiPort as __WritableMultiPort} from '@lf-lang/reactor-ts'
+ import {InMultiPort as __InMultiPort, OutMultiPort as __OutMultiPort} from '@lf-lang/reactor-ts'
+ import {Reaction as __Reaction} from '@lf-lang/reactor-ts'
+ import {State as __State} from '@lf-lang/reactor-ts'
+ import {TimeUnit, TimeValue, Tag as __Tag, Origin as __Origin} from '@lf-lang/reactor-ts'
+ import {Args as __Args, Variable as __Variable, Triggers as __Triggers, Present, Read, Write, ReadWrite, MultiReadWrite, Sched} from '@lf-lang/reactor-ts'
+ import {Log} from '@lf-lang/reactor-ts'
+ import {ProcessedCommandLineArgs as __ProcessedCommandLineArgs, CommandLineOptionDefs as __CommandLineOptionDefs, CommandLineUsageDefs as __CommandLineUsageDefs, CommandLineOptionSpec as __CommandLineOptionSpec, unitBasedTimeValueCLAType as __unitBasedTimeValueCLAType, booleanCLAType as __booleanCLAType} from '@lf-lang/reactor-ts'
+ """
}
private fun generateDefaultImports(): String {
return with(PrependOperator) {
"""
- |// Code generated by the Lingua Franca compiler from:
- |// file:/${filePath.toString()}
- $DEFAULT_IMPORTS
- |
+ |// Code generated by the Lingua Franca compiler from:
+ |// file:/$filePath
+${" |"..DEFAULT_IMPORTS}
+ |
""".trimMargin()
}
}
private fun generateProtoPreamble(): String {
- val protoFileImports = StringJoiner("\n")
- for (file in protoFiles) {
- var name = file
+ val protoFileImports = protoFiles.joinWithLn { file ->
// Remove any extension the file name may have.
- val dot = name.lastIndexOf('.')
- if (dot > 0) {
- name = name.substring(0, dot)
- }
- protoFileImports.add("""
- |import * as ${name} from "./${name}_pb"
- """.trimMargin())
+ val name = file.substringBeforeLast('.')
+ "import * as $name from \"./${name}_pb\""
}
- return with(PrependOperator) {"""
+ return with(PrependOperator) {
+ """
|// Imports for protocol buffers
- |${protoFileImports}
+${" |"..protoFileImports}
|
|
- """.trimMargin()
+ """.trimMargin()
}
}
diff --git a/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt
index 065b0dc320..2c349ebd19 100644
--- a/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt
@@ -2,7 +2,9 @@ package org.lflang.generator.ts
import org.lflang.ErrorReporter
import org.lflang.federated.FederateInstance
+import org.lflang.generator.getTargetInitializer
import org.lflang.isBank
+import org.lflang.joinWithLn
import org.lflang.lf.Instantiation
import org.lflang.lf.Parameter
import org.lflang.lf.Reactor
@@ -15,11 +17,8 @@ import java.util.*
/**
* Generator for child reactor instantiations in TypeScript target.
*/
-class TSInstanceGenerator (
- // TODO(hokeun): Remove dependency on TSGenerator.
- private val tsGenerator: TSGenerator,
+class TSInstanceGenerator(
private val errorReporter: ErrorReporter,
- private val tsReactorGenerator: TSReactorGenerator,
reactor: Reactor,
federate: FederateInstance
) {
@@ -37,36 +36,25 @@ class TSInstanceGenerator (
}
}
- private fun getInitializerList(param: Parameter, i: Instantiation): List =
- tsGenerator.getInitializerListW(param, i)
-
- private fun getTargetInitializer(param: Parameter, i: Instantiation): String {
- return tsReactorGenerator.getTargetInitializerHelper(param, getInitializerList(param, i))
- }
-
private fun getTypeParams(typeParms: List): String =
- if (typeParms.isEmpty()) {""} else {
- typeParms.joinToString(", ", "<", ">") { it.toText() }}
+ if (typeParms.isEmpty()) ""
+ else typeParms.joinToString(", ", "<", ">") { it.toText() }
private fun getReactorParameterList(parameters: List): String =
- if (parameters.isEmpty()) { "[__Reactor]" } else {
- parameters.joinToString(", ", "[__Reactor, ", "]") { it.getTargetType() }}
+ parameters.joinToString(", ", "[__Reactor, ", "]") { TSTypes.getTargetType(it) }
- fun generateClassProperties(): String {
- val childReactorClassProperties = LinkedList()
- for (childReactor in childReactors) {
+ fun generateClassProperties(): String =
+ childReactors.joinWithLn { childReactor ->
if (childReactor.isBank) {
- childReactorClassProperties.add("${childReactor.name}: " +
+ "${childReactor.name}: " +
"__Bank<${childReactor.reactorClass.name}${getTypeParams(childReactor.typeParms)}, " +
- "${getReactorParameterList(childReactor.reactor.parameters)}>")
+ "${getReactorParameterList(childReactor.reactor.parameters)}>"
} else {
- childReactorClassProperties.add("${childReactor.name}: " +
- "${childReactor.reactorClass.name}${getTypeParams(childReactor.typeParms)}")
+ "${childReactor.name}: " +
+ "${childReactor.reactorClass.name}${getTypeParams(childReactor.typeParms)}"
}
}
- return childReactorClassProperties.joinToString("\n")
- }
fun generateInstantiations(): String {
val childReactorInstantiations = LinkedList()
@@ -75,7 +63,7 @@ class TSInstanceGenerator (
childReactorArguments.add("this")
for (parameter in childReactor.reactorClass.toDefinition().parameters) {
- childReactorArguments.add(getTargetInitializer(parameter, childReactor))
+ childReactorArguments.add(TSTypes.getTargetInitializer(parameter, childReactor))
}
if (childReactor.isBank) {
childReactorInstantiations.add(
@@ -93,4 +81,4 @@ class TSInstanceGenerator (
}
return childReactorInstantiations.joinToString("\n")
}
-}
\ No newline at end of file
+}
diff --git a/org.lflang/src/org/lflang/generator/ts/TSParameterGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSParameterGenerator.kt
index 1cd987b815..5ded5814b0 100644
--- a/org.lflang/src/org/lflang/generator/ts/TSParameterGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/ts/TSParameterGenerator.kt
@@ -1,32 +1,22 @@
package org.lflang.generator.ts
-import org.lflang.generator.PrependOperator
+import org.lflang.joinWithLn
import org.lflang.lf.Parameter
-import java.util.*
/**
* Generate parameters for TypeScript target.
*/
-class TSParameterGenerator (
- // TODO(hokeun): Remove dependency on TSGenerator.
- private val tsGenerator: TSGenerator,
+class TSParameterGenerator(
private val parameters: List
) {
- private fun Parameter.getTargetType(): String = tsGenerator.getTargetTypeW(this)
- fun generateClassProperties(): String {
- val paramClassProperties = LinkedList()
- for (param in parameters) {
- paramClassProperties.add("${param.name}: __Parameter<${param.getTargetType()}>;")
+ fun generateClassProperties(): String =
+ parameters.joinWithLn {
+ "${it.name}: __Parameter<${TSTypes.getTargetType(it)}>;"
}
- return paramClassProperties.joinToString("\n")
- }
- fun generateInstantiations(): String {
- val paramInstantiations = LinkedList()
- for (param in parameters) {
- paramInstantiations.add("this.${param.name} = new __Parameter(${param.name});")
+ fun generateInstantiations(): String =
+ parameters.joinWithLn {
+ "this.${it.name} = new __Parameter(${it.name});"
}
- return paramInstantiations.joinToString("\n")
- }
-}
\ No newline at end of file
+}
diff --git a/org.lflang/src/org/lflang/generator/ts/TSParameterPreambleGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSParameterPreambleGenerator.kt
index 02380fa9fd..7ef44319ef 100644
--- a/org.lflang/src/org/lflang/generator/ts/TSParameterPreambleGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/ts/TSParameterPreambleGenerator.kt
@@ -27,8 +27,8 @@ package org.lflang.generator.ts
import org.lflang.FileConfig
import org.lflang.TargetConfig
-import org.lflang.TimeValue
-import org.lflang.generator.PrependOperator
+import org.lflang.joinWithCommasLn
+import org.lflang.joinWithLn
import org.lflang.lf.Parameter
import org.lflang.lf.Reactor
import java.util.StringJoiner
@@ -44,20 +44,13 @@ import java.util.StringJoiner
*/
class TSParameterPreambleGenerator(
- private val tsGenerator: TSGenerator,
private val fileConfig: FileConfig,
private val targetConfig: TargetConfig,
private val reactors: MutableList
) {
- private fun getTargetType(p: Parameter): String = tsGenerator.getTargetTypeW(p)
- private fun getTimeoutTimeValue(): String {
- return if (targetConfig.timeout != null) {
- timeInTargetLanguage(targetConfig.timeout)
- } else {
- "undefined"
- }
- }
+ private fun getTimeoutTimeValue(): String =
+ targetConfig.timeout?.toTsTime() ?: "undefined"
private fun getParameters(): List {
var mainReactor: Reactor? = null
@@ -72,11 +65,11 @@ class TSParameterPreambleGenerator(
/**
* Assign results of parsing custom command line arguments
*/
- private fun assignCustomCLArgs(mainParameters: HashSet): String {
- val code = StringJoiner("\n")
- for (parameter in mainParameters) {
- code.add("""
- |let __CL${parameter.name}: ${getTargetType(parameter)} | undefined = undefined;
+ private fun assignCustomCLArgs(mainParameters: HashSet): String =
+ mainParameters.joinWithLn { parameter ->
+
+ """
+ |let __CL${parameter.name}: ${TSTypes.getTargetType(parameter)} | undefined = undefined;
|if (__processedCLArgs.${parameter.name} !== undefined) {
| if (__processedCLArgs.${parameter.name} !== null) {
| __CL${parameter.name} = __processedCLArgs.${parameter.name};
@@ -85,30 +78,25 @@ class TSParameterPreambleGenerator(
| throw new Error("Custom '${parameter.name}' command line argument is malformed.");
| }
|}
- """)
+ """
}
- return code.toString()
- }
/**
* Generate code for extracting custom command line arguments
* from the object returned from commandLineArgs
*/
- private fun logCustomCLArgs(mainParameters: Set): String {
- val code = StringJoiner("\n")
- for (parameter in mainParameters) {
+ private fun logCustomCLArgs(mainParameters: Set): String =
+ mainParameters.joinWithLn { parameter ->
// We can't allow the programmer's parameter names
// to cause the generation of variables with a "__" prefix
// because they could collide with other variables.
// So prefix variables created here with __CL
- code.add("""
+ """
|if (__processedCLArgs.${parameter.name} !== undefined && __processedCLArgs.${parameter.name} !== null
| && !__noStart) {
| Log.global.info("'${parameter.name}' property overridden by command line argument.");
- |}""")
+ |}"""
}
- return code.toString()
- }
fun generateParameters(): Pair, String> {
/**
@@ -126,7 +114,7 @@ class TSParameterPreambleGenerator(
var customArgType: String? = null
var customTypeLabel: String? = null
- val paramType = getTargetType(parameter)
+ val paramType = TSTypes.getTargetType(parameter)
if (paramType == "string") {
mainParameters.add(parameter)
customArgType = "String";
@@ -147,20 +135,24 @@ class TSParameterPreambleGenerator(
if (customArgType != null) {
clTypeExtension.add(parameter.name + ": " + paramType)
if (customTypeLabel != null) {
- customArgs.add(with(PrependOperator) {"""
+ customArgs.add(
+ """
|{
| name: '${parameter.name}',
- | type: ${customArgType},
- | typeLabel: "{underline ${customTypeLabel}}",
+ | type: $customArgType,
+ | typeLabel: "{underline $customTypeLabel}",
| description: 'Custom argument. Refer to ${fileConfig.srcFile} for documentation.'
- |}""".trimMargin()})
+ |}""".trimMargin()
+ )
} else {
- customArgs.add(with(PrependOperator) {"""
+ customArgs.add(
+ """
|{
| name: '${parameter.name}',
- | type: ${customArgType},
+ | type: $customArgType,
| description: 'Custom argument. Refer to ${fileConfig.srcFile} for documentation.'
- |}""".trimMargin()})
+ |}""".trimMargin()
+ )
}
}
}
@@ -168,127 +160,126 @@ class TSParameterPreambleGenerator(
val customArgsList = "[\n$customArgs]"
val clTypeExtensionDef = "{$clTypeExtension}"
- val codeText = with(PrependOperator) {"""
- |// ************* App Parameters
- |let __timeout: TimeValue | undefined = ${getTimeoutTimeValue()};
- |let __keepAlive: boolean = ${targetConfig.keepalive};
- |let __fast: boolean = ${targetConfig.fastMode};
- |let __federationID: string = 'Unidentified Federation'
- |
- |let __noStart = false; // If set to true, don't start the app.
- |
- |// ************* Custom Command Line Arguments
- |let __additionalCommandLineArgs : __CommandLineOptionSpec = ${customArgsList};
- |let __customCommandLineArgs = __CommandLineOptionDefs.concat(__additionalCommandLineArgs);
- |let __customCommandLineUsageDefs = __CommandLineUsageDefs;
- |type __customCLTypeExtension = ${clTypeExtensionDef};
- |__customCommandLineUsageDefs[1].optionList = __customCommandLineArgs;
- |const __clUsage = commandLineUsage(__customCommandLineUsageDefs);
- |
- |// Set App parameters using values from the constructor or command line args.
- |// Command line args have precedence over values from the constructor
- |let __processedCLArgs: __ProcessedCommandLineArgs & __customCLTypeExtension;
- |try {
- | __processedCLArgs = commandLineArgs(__customCommandLineArgs) as __ProcessedCommandLineArgs & __customCLTypeExtension;
- |} catch (e){
- | Log.global.error(__clUsage);
- | throw new Error("Command line argument parsing failed with: " + e);
- |}
- |
- |// Fast Parameter
- |if (__processedCLArgs.fast !== undefined) {
- | if (__processedCLArgs.fast !== null) {
- | __fast = __processedCLArgs.fast;
- | } else {
- | Log.global.error(__clUsage);
- | throw new Error("'fast' command line argument is malformed.");
- | }
- |}
- |
- |// federationID Parameter
- |if (__processedCLArgs.id !== undefined) {
- | if (__processedCLArgs.id !== null) {
- | __federationID = __processedCLArgs.id;
- | } else {
- | Log.global.error(__clUsage);
- | throw new Error("'id (federationID)' command line argument is malformed.");
- | }
- |}
- |
- |// KeepAlive parameter
- |if (__processedCLArgs.keepalive !== undefined) {
- | if (__processedCLArgs.keepalive !== null) {
- | __keepAlive = __processedCLArgs.keepalive;
- | } else {
- | Log.global.error(__clUsage);
- | throw new Error("'keepalive' command line argument is malformed.");
- | }
- |}
- |
- |// Timeout parameter
- |if (__processedCLArgs.timeout !== undefined) {
- | if (__processedCLArgs.timeout !== null) {
- | __timeout = __processedCLArgs.timeout;
- | } else {
- | Log.global.error(__clUsage);
- | throw new Error("'timeout' command line argument is malformed.");
- | }
- |}
- |
- |// Logging parameter (not a constructor parameter, but a command line option)
- |if (__processedCLArgs.logging !== undefined) {
- | if (__processedCLArgs.logging !== null) {
- | Log.global.level = __processedCLArgs.logging;
- | } else {
- | Log.global.error(__clUsage);
- | throw new Error("'logging' command line argument is malformed.");
- | }
- |} else {
- | Log.global.level = Log.levels.${targetConfig.logLevel.name}; // Default from target property.
- |}
- |
- |// Help parameter (not a constructor parameter, but a command line option)
- |// NOTE: this arg has to be checked after logging, because the help mode should
- |// suppress debug statements from it changes logging
- |if (__processedCLArgs.help === true) {
- | Log.global.error(__clUsage);
- | __noStart = true;
- | // Don't execute the app if the help flag is given.
- |}
- |
- |// Now the logging property has been set to its final value,
- |// log information about how command line arguments were set,
- |// but only if not in help mode.
- |
- |// Runtime command line arguments
- |if (__processedCLArgs.fast !== undefined && __processedCLArgs.fast !== null
- | && !__noStart) {
- | Log.global.info("'fast' property overridden by command line argument.");
- |}
- |if (__processedCLArgs.id !== undefined && __processedCLArgs.id !== null
- | && !__noStart) {
- | Log.global.info("'id (federationID)' property overridden by command line argument.");
- |}
- |if (__processedCLArgs.keepalive !== undefined && __processedCLArgs.keepalive !== null
- | && !__noStart) {
- | Log.global.info("'keepalive' property overridden by command line argument.");
- |}
- |if (__processedCLArgs.timeout !== undefined && __processedCLArgs.timeout !== null
- | && !__noStart) {
- | Log.global.info("'timeout' property overridden by command line argument.");
- |}
- |if (__processedCLArgs.logging !== undefined && __processedCLArgs.logging !== null
- | && !__noStart) {
- | Log.global.info("'logging' property overridden by command line argument.");
- |}
- |
- |// Custom command line arguments
- |${logCustomCLArgs(mainParameters)}
- |// Assign custom command line arguments
- |${assignCustomCLArgs(mainParameters)}
- |
- """.trimMargin()
- }
+ val codeText = """
+ |// ************* App Parameters
+ |let __timeout: TimeValue | undefined = ${getTimeoutTimeValue()};
+ |let __keepAlive: boolean = ${targetConfig.keepalive};
+ |let __fast: boolean = ${targetConfig.fastMode};
+ |let __federationID: string = 'Unidentified Federation'
+ |
+ |let __noStart = false; // If set to true, don't start the app.
+ |
+ |// ************* Custom Command Line Arguments
+ |let __additionalCommandLineArgs : __CommandLineOptionSpec = $customArgsList;
+ |let __customCommandLineArgs = __CommandLineOptionDefs.concat(__additionalCommandLineArgs);
+ |let __customCommandLineUsageDefs = __CommandLineUsageDefs;
+ |type __customCLTypeExtension = $clTypeExtensionDef;
+ |__customCommandLineUsageDefs[1].optionList = __customCommandLineArgs;
+ |const __clUsage = commandLineUsage(__customCommandLineUsageDefs);
+ |
+ |// Set App parameters using values from the constructor or command line args.
+ |// Command line args have precedence over values from the constructor
+ |let __processedCLArgs: __ProcessedCommandLineArgs & __customCLTypeExtension;
+ |try {
+ | __processedCLArgs = commandLineArgs(__customCommandLineArgs) as __ProcessedCommandLineArgs & __customCLTypeExtension;
+ |} catch (e){
+ | Log.global.error(__clUsage);
+ | throw new Error("Command line argument parsing failed with: " + e);
+ |}
+ |
+ |// Fast Parameter
+ |if (__processedCLArgs.fast !== undefined) {
+ | if (__processedCLArgs.fast !== null) {
+ | __fast = __processedCLArgs.fast;
+ | } else {
+ | Log.global.error(__clUsage);
+ | throw new Error("'fast' command line argument is malformed.");
+ | }
+ |}
+ |
+ |// federationID Parameter
+ |if (__processedCLArgs.id !== undefined) {
+ | if (__processedCLArgs.id !== null) {
+ | __federationID = __processedCLArgs.id;
+ | } else {
+ | Log.global.error(__clUsage);
+ | throw new Error("'id (federationID)' command line argument is malformed.");
+ | }
+ |}
+ |
+ |// KeepAlive parameter
+ |if (__processedCLArgs.keepalive !== undefined) {
+ | if (__processedCLArgs.keepalive !== null) {
+ | __keepAlive = __processedCLArgs.keepalive;
+ | } else {
+ | Log.global.error(__clUsage);
+ | throw new Error("'keepalive' command line argument is malformed.");
+ | }
+ |}
+ |
+ |// Timeout parameter
+ |if (__processedCLArgs.timeout !== undefined) {
+ | if (__processedCLArgs.timeout !== null) {
+ | __timeout = __processedCLArgs.timeout;
+ | } else {
+ | Log.global.error(__clUsage);
+ | throw new Error("'timeout' command line argument is malformed.");
+ | }
+ |}
+ |
+ |// Logging parameter (not a constructor parameter, but a command line option)
+ |if (__processedCLArgs.logging !== undefined) {
+ | if (__processedCLArgs.logging !== null) {
+ | Log.global.level = __processedCLArgs.logging;
+ | } else {
+ | Log.global.error(__clUsage);
+ | throw new Error("'logging' command line argument is malformed.");
+ | }
+ |} else {
+ | Log.global.level = Log.levels.${targetConfig.logLevel.name}; // Default from target property.
+ |}
+ |
+ |// Help parameter (not a constructor parameter, but a command line option)
+ |// NOTE: this arg has to be checked after logging, because the help mode should
+ |// suppress debug statements from it changes logging
+ |if (__processedCLArgs.help === true) {
+ | Log.global.error(__clUsage);
+ | __noStart = true;
+ | // Don't execute the app if the help flag is given.
+ |}
+ |
+ |// Now the logging property has been set to its final value,
+ |// log information about how command line arguments were set,
+ |// but only if not in help mode.
+ |
+ |// Runtime command line arguments
+ |if (__processedCLArgs.fast !== undefined && __processedCLArgs.fast !== null
+ | && !__noStart) {
+ | Log.global.info("'fast' property overridden by command line argument.");
+ |}
+ |if (__processedCLArgs.id !== undefined && __processedCLArgs.id !== null
+ | && !__noStart) {
+ | Log.global.info("'id (federationID)' property overridden by command line argument.");
+ |}
+ |if (__processedCLArgs.keepalive !== undefined && __processedCLArgs.keepalive !== null
+ | && !__noStart) {
+ | Log.global.info("'keepalive' property overridden by command line argument.");
+ |}
+ |if (__processedCLArgs.timeout !== undefined && __processedCLArgs.timeout !== null
+ | && !__noStart) {
+ | Log.global.info("'timeout' property overridden by command line argument.");
+ |}
+ |if (__processedCLArgs.logging !== undefined && __processedCLArgs.logging !== null
+ | && !__noStart) {
+ | Log.global.info("'logging' property overridden by command line argument.");
+ |}
+ |
+ |// Custom command line arguments
+ |${logCustomCLArgs(mainParameters)}
+ |// Assign custom command line arguments
+ |${assignCustomCLArgs(mainParameters)}
+ |
+ """.trimMargin()
return Pair(mainParameters, codeText)
}
diff --git a/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt
index 10e2a4a644..338ef9600d 100644
--- a/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt
@@ -3,15 +3,12 @@ package org.lflang.generator.ts
import org.lflang.isMultiport
import org.lflang.lf.Input
import org.lflang.lf.Output
-import org.lflang.lf.Port
-import org.lflang.lf.Type
import java.util.*
/**
* Generate input and output ports for TypeScript target.
*/
class TSPortGenerator (
- // TODO(hokeun): Remove dependency on TSGenerator.
private val inputs: List,
private val outputs: List