diff --git a/org.lflang/src/org/lflang/generator/GeneratorBase.xtend b/org.lflang/src/org/lflang/generator/GeneratorBase.java similarity index 64% rename from org.lflang/src/org/lflang/generator/GeneratorBase.xtend rename to org.lflang/src/org/lflang/generator/GeneratorBase.java index 30357fabbf..34f6e41e48 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorBase.xtend +++ b/org.lflang/src/org/lflang/generator/GeneratorBase.java @@ -22,53 +22,62 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************/ -package org.lflang.generator +package org.lflang.generator; -import java.io.File +import java.io.File; import java.io.IOException; -import java.nio.file.Paths -import java.util.ArrayList -import java.util.Collection -import java.util.HashSet -import java.util.LinkedHashMap -import java.util.LinkedHashSet -import java.util.List -import java.util.Map -import java.util.Set -import java.util.regex.Pattern -import java.util.stream.Collectors -import org.eclipse.core.resources.IMarker -import org.eclipse.emf.ecore.resource.Resource -import org.eclipse.xtext.util.CancelIndicator -import org.lflang.ASTUtils -import org.lflang.ErrorReporter -import org.lflang.FileConfig -import org.lflang.InferredType -import org.lflang.MainConflictChecker -import org.lflang.Target -import org.lflang.TargetConfig -import org.lflang.TargetProperty.CoordinationType -import org.lflang.TimeUnit -import org.lflang.TimeValue -import org.lflang.federated.FedASTUtils -import org.lflang.federated.FederateInstance -import org.lflang.federated.serialization.SupportedSerializers -import org.lflang.graph.InstantiationGraph -import org.lflang.lf.Action -import org.lflang.lf.Connection -import org.lflang.lf.Delay -import org.lflang.lf.Instantiation -import org.lflang.lf.LfFactory -import org.lflang.lf.Model -import org.lflang.lf.Parameter -import org.lflang.lf.Reaction -import org.lflang.lf.Reactor -import org.lflang.lf.Time -import org.lflang.lf.Value -import org.lflang.lf.VarRef -import org.lflang.validation.AbstractLFValidator - -import static extension org.lflang.ASTUtils.* +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import com.google.common.base.Objects; +import com.google.common.collect.Iterables; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.xtext.util.CancelIndicator; +import org.eclipse.xtext.xbase.lib.CollectionLiterals; +import org.eclipse.xtext.xbase.lib.IterableExtensions; +import org.eclipse.xtext.xbase.lib.IteratorExtensions; +import org.eclipse.xtext.xbase.lib.Pair; +import org.lflang.ASTUtils; +import org.lflang.ErrorReporter; +import org.lflang.FileConfig; +import org.lflang.InferredType; +import org.lflang.MainConflictChecker; +import org.lflang.Target; +import org.lflang.TargetConfig; +import org.lflang.TargetProperty.CoordinationType; +import org.lflang.TimeUnit; +import org.lflang.TimeValue; +import org.lflang.federated.FedASTUtils; +import org.lflang.federated.FederateInstance; +import org.lflang.federated.serialization.SupportedSerializers; +import org.lflang.graph.InstantiationGraph; +import org.lflang.lf.Action; +import org.lflang.lf.Connection; +import org.lflang.lf.Delay; +import org.lflang.lf.Instantiation; +import org.lflang.lf.LfFactory; +import org.lflang.lf.Model; +import org.lflang.lf.Parameter; +import org.lflang.lf.Reaction; +import org.lflang.lf.Reactor; +import org.lflang.lf.Time; +import org.lflang.lf.Value; +import org.lflang.lf.VarRef; +import org.lflang.validation.AbstractLFValidator; /** * Generator base class for specifying core functionality @@ -80,7 +89,7 @@ * @author{Matt Weber } * @author{Soroush Bateni } */ -abstract class GeneratorBase extends AbstractLFValidator { +public abstract class GeneratorBase extends AbstractLFValidator { //////////////////////////////////////////// //// Public fields. @@ -88,15 +97,15 @@ abstract class GeneratorBase extends AbstractLFValidator { /** * Constant that specifies how to name generated delay reactors. */ - public static val GEN_DELAY_CLASS_NAME = "_lf_GenDelay" + public static String GEN_DELAY_CLASS_NAME = "_lf_GenDelay"; /** * The main (top-level) reactor instance. */ - public ReactorInstance main + public ReactorInstance main; /** A error reporter for reporting any errors or warnings during the code generation */ - public ErrorReporter errorReporter + public ErrorReporter errorReporter; //////////////////////////////////////////// //// Protected fields. @@ -104,31 +113,32 @@ abstract class GeneratorBase extends AbstractLFValidator { /** * The current target configuration. */ - protected var TargetConfig targetConfig = new TargetConfig() - def TargetConfig getTargetConfig() { return this.targetConfig;} + protected TargetConfig targetConfig = new TargetConfig(); + + public TargetConfig getTargetConfig() { return this.targetConfig;} /** * The current file configuration. */ - protected var FileConfig fileConfig + protected FileConfig fileConfig; /** * A factory for compiler commands. */ - protected var GeneratorCommandFactory commandFactory + protected GeneratorCommandFactory commandFactory; /** * Collection of generated delay classes. */ - val delayClasses = new LinkedHashSet() + private LinkedHashSet delayClasses = new LinkedHashSet<>(); /** * Definition of the main (top-level) reactor. * This is an automatically generated AST node for the top-level * reactor. */ - protected Instantiation mainDef - def getMainDef() { return mainDef; } + protected Instantiation mainDef; + public Instantiation getMainDef() { return mainDef; } /** * A list of Reactor definitions in the main resource, including non-main @@ -136,12 +146,12 @@ abstract class GeneratorBase extends AbstractLFValidator { * such a way that each reactor is preceded by any reactor that it instantiates * using a command like `foo = new Foo();` */ - protected var List reactors = new ArrayList + protected List reactors = new ArrayList<>(); /** * The set of resources referenced reactor classes reside in. */ - protected var Set resources = newLinkedHashSet + protected Set resources = new LinkedHashSet<>(); /** * Graph that tracks dependencies between instantiations. @@ -152,7 +162,7 @@ abstract class GeneratorBase extends AbstractLFValidator { * Hence, after this method returns, `this.reactors` will be a list of Reactors such that any * reactor is preceded in the list by reactors that it instantiates. */ - protected var InstantiationGraph instantiationGraph + protected InstantiationGraph instantiationGraph; /** * The set of unordered reactions. An unordered reaction is one that does @@ -165,29 +175,29 @@ abstract class GeneratorBase extends AbstractLFValidator { * state of the containing reactor. To make a reaction unordered, when * the Reaction instance is created, add that instance to this set. */ - protected var Set unorderedReactions = null + protected Set unorderedReactions = null; /** * Map from reactions to bank indices */ - protected var Map reactionBankIndices = null + protected Map reactionBankIndices = null; /** * Keep a unique list of enabled serializers */ - public var HashSet enabledSerializers = new HashSet(); + public HashSet enabledSerializers = new HashSet<>(); /** * Indicates whether or not the current Lingua Franca program * contains a federation. */ - public var boolean isFederated = false + public boolean isFederated = false; /** * Indicates whether or not the current Lingua Franca program * contains model reactors. */ - public var boolean hasModalReactors = false + public boolean hasModalReactors = false; // ////////////////////////////////////////// // // Target properties, if they are included. @@ -195,36 +205,36 @@ abstract class GeneratorBase extends AbstractLFValidator { * A list of federate instances or a list with a single empty string * if there are no federates specified. FIXME: Why put a single empty string there? It should be just empty... */ - public var List federates = new ArrayList + public List federates = new ArrayList<>(); /** * A map from federate IDs to federate instances. */ - protected var Map federateByID = new LinkedHashMap() + protected Map federateByID = new LinkedHashMap<>(); /** * A map from instantiations to the federate instances for that instantiation. * If the instantiation has a width, there may be more than one federate instance. */ - protected var Map> federatesByInstantiation + protected Map> federatesByInstantiation; /** * The federation RTI properties, which defaults to 'localhost: 15045'. */ - protected val federationRTIProperties = newLinkedHashMap( - 'host' -> 'localhost', - 'port' -> 0 // Indicator to use the default port, typically 15045. - ) + protected LinkedHashMap federationRTIProperties = CollectionLiterals.newLinkedHashMap( + Pair.of("host", "localhost"), + Pair.of("port", 0) // Indicator to use the default port, typically 15045. + ); /** * Contents of $LF_CLASSPATH, if it was set. */ - protected String classpathLF + protected String classpathLF; /** * The name of the top-level reactor. */ - protected var String topLevelName; // FIXME: remove and use fileConfig.name instead + protected String topLevelName; // FIXME: remove and use fileConfig.name instead // ////////////////////////////////////////// // // Private fields. @@ -232,11 +242,11 @@ abstract class GeneratorBase extends AbstractLFValidator { /** * Create a new GeneratorBase object. */ - new(FileConfig fileConfig, ErrorReporter errorReporter) { - this.fileConfig = fileConfig - this.topLevelName = fileConfig.name - this.errorReporter = errorReporter - this.commandFactory = new GeneratorCommandFactory(errorReporter, fileConfig) + public GeneratorBase(FileConfig fileConfig, ErrorReporter errorReporter) { + this.fileConfig = fileConfig; + this.topLevelName = fileConfig.name; + this.errorReporter = errorReporter; + this.commandFactory = new GeneratorCommandFactory(errorReporter, fileConfig); } // ////////////////////////////////////////// @@ -246,33 +256,35 @@ abstract class GeneratorBase extends AbstractLFValidator { * Store the given reactor in the collection of generated delay classes * and insert it in the AST under the top-level reactors node. */ - def void addDelayClass(Reactor generatedDelay) { + public void addDelayClass(Reactor generatedDelay) { // Record this class, so it can be reused. - this.delayClasses.add(generatedDelay) + delayClasses.add(generatedDelay); // And hook it into the AST. - (fileConfig.resource.allContents.findFirst[it|it instanceof Model] as Model).reactors.add(generatedDelay) + EObject node = IteratorExtensions.findFirst(fileConfig.resource.getAllContents(), Model.class::isInstance); + ((Model) node).getReactors().add(generatedDelay); } /** * Return the generated delay reactor that corresponds to the given class * name if it had been created already, `null` otherwise. */ - def Reactor findDelayClass(String className) { - return this.delayClasses.findFirst[it|it.name.equals(className)] + public Reactor findDelayClass(String className) { + return IterableExtensions.findFirst(delayClasses, it -> it.getName().equals(className)); } /** * If there is a main or federated reactor, then create a synthetic Instantiation * for that top-level reactor and set the field mainDef to refer to it. */ - private def createMainInstantiation() { + private void createMainInstantiation() { // Find the main reactor and create an AST node for its instantiation. - for (reactor : fileConfig.resource.allContents.toIterable.filter(Reactor)) { - if (reactor.isMain || reactor.isFederated) { + Iterable nodes = IteratorExtensions.toIterable(fileConfig.resource.getAllContents()); + for (Reactor reactor : Iterables.filter(nodes, Reactor.class)) { + if (reactor.isMain() || reactor.isFederated()) { // Creating an definition for the main reactor because there isn't one. - this.mainDef = LfFactory.eINSTANCE.createInstantiation() - this.mainDef.setName(reactor.name) - this.mainDef.setReactorClass(reactor) + this.mainDef = LfFactory.eINSTANCE.createInstantiation(); + this.mainDef.setName(reactor.getName()); + this.mainDef.setReactorClass(reactor); } } } @@ -289,42 +301,42 @@ private def createMainInstantiation() { * @param context Context relating to invocation of the code generator. * In stand alone mode, this object is also used to relay CLI arguments. */ - def void doGenerate(Resource resource, LFGeneratorContext context) { + public void doGenerate(Resource resource, LFGeneratorContext context) { GeneratorUtils.setTargetConfig( context, GeneratorUtils.findTarget(fileConfig.resource), targetConfig, errorReporter - ) + ); - cleanIfNeeded(context) + cleanIfNeeded(context); - printInfo(context.mode) + printInfo(context.getMode()); // Clear any IDE markers that may have been created by a previous build. // Markers mark problems in the Eclipse IDE when running in integrated mode. if (errorReporter instanceof EclipseErrorReporter) { - errorReporter.clearMarkers() + ((EclipseErrorReporter) errorReporter).clearMarkers(); } - ASTUtils.setMainName(fileConfig.resource, fileConfig.name) + ASTUtils.setMainName(fileConfig.resource, fileConfig.name); - createMainInstantiation() + createMainInstantiation(); // Check if there are any conflicting main reactors elsewhere in the package. - if (context.mode == LFGeneratorContext.Mode.STANDALONE && mainDef !== null) { + if (Objects.equal(context.getMode(), LFGeneratorContext.Mode.STANDALONE) && mainDef != null) { for (String conflict : new MainConflictChecker(fileConfig).conflicts) { - errorReporter.reportError(this.mainDef.reactorClass, "Conflicting main reactor in " + conflict); + errorReporter.reportError(this.mainDef.getReactorClass(), "Conflicting main reactor in " + conflict); } } // Configure the command factory commandFactory.setVerbose(); - if (context.mode == LFGeneratorContext.Mode.STANDALONE && context.getArgs().containsKey("quiet")) { + if (Objects.equal(context.getMode(), LFGeneratorContext.Mode.STANDALONE) && context.getArgs().containsKey("quiet")) { commandFactory.setQuiet(); } // This must be done before desugaring delays below. - analyzeFederates(context) - + analyzeFederates(context); + // Process target files. Copy each of them into the src-gen dir. // FIXME: Should we do this here? This doesn't make sense for federates the way it is // done here. @@ -333,43 +345,43 @@ def void doGenerate(Resource resource, LFGeneratorContext context) { // Collect reactors and create an instantiation graph. // These are needed to figure out which resources we need // to validate, which happens in setResources(). - setReactorsAndInstantiationGraph(context.mode) + setReactorsAndInstantiationGraph(context.getMode()); - GeneratorUtils.validate(context, fileConfig, instantiationGraph, errorReporter) - val allResources = GeneratorUtils.getResources(reactors) + GeneratorUtils.validate(context, fileConfig, instantiationGraph, errorReporter); + List allResources = GeneratorUtils.getResources(reactors); resources.addAll(allResources.stream() // FIXME: This filter reproduces the behavior of the method it replaces. But why must it be so complicated? Why are we worried about weird corner cases like this? - .filter [it | it != fileConfig.resource || (mainDef !== null && it === mainDef.reactorClass.eResource)] - .map [it | GeneratorUtils.getLFResource(it, fileConfig.getSrcGenBasePath(), context, errorReporter)] + .filter(it -> !Objects.equal(it, fileConfig.resource) || mainDef != null && it == mainDef.getReactorClass().eResource()) + .map(it -> GeneratorUtils.getLFResource(it, fileConfig.getSrcGenBasePath(), context, errorReporter)) .collect(Collectors.toList()) - ) - GeneratorUtils.accommodatePhysicalActionsIfPresent(allResources, target, targetConfig, errorReporter); + ); + GeneratorUtils.accommodatePhysicalActionsIfPresent(allResources, getTarget(), targetConfig, errorReporter); // FIXME: Should the GeneratorBase pull in `files` from imported // resources? // Reroute connections that have delays associated with them via // generated delay reactors. - transformDelays() + transformDelays(); // Transform connections that reside in mutually exclusive modes and are otherwise conflicting // This should be done before creating the instantiation graph - transformConflictingConnectionsInModalReactors() + transformConflictingConnectionsInModalReactors(); // Invoke these functions a second time because transformations // may have introduced new reactors! - setReactorsAndInstantiationGraph(context.mode) + setReactorsAndInstantiationGraph(context.getMode()); // Check for existence and support of modes - hasModalReactors = reactors.exists[!modes.empty] - checkModalReactorSupport(false) + hasModalReactors = IterableExtensions.exists(reactors, it -> !it.getModes().isEmpty()); + checkModalReactorSupport(false); - enableSupportForSerializationIfApplicable(context.cancelIndicator); + enableSupportForSerializationIfApplicable(context.getCancelIndicator()); } /** * Check if a clean was requested from the standalone compiler and perform * the clean step. */ - protected def cleanIfNeeded(LFGeneratorContext context) { + protected void cleanIfNeeded(LFGeneratorContext context) { if (context.getArgs().containsKey("clean")) { try { fileConfig.doClean(); @@ -387,22 +399,23 @@ protected def cleanIfNeeded(LFGeneratorContext context) { * Hence, after this method returns, `this.reactors` will be a list of Reactors such that any * reactor is preceded in the list by reactors that it instantiates. */ - protected def setReactorsAndInstantiationGraph(LFGeneratorContext.Mode mode) { + protected void setReactorsAndInstantiationGraph(LFGeneratorContext.Mode mode) { // Build the instantiation graph . - this.instantiationGraph = new InstantiationGraph(fileConfig.resource, false) + instantiationGraph = new InstantiationGraph(fileConfig.resource, false); // Topologically sort the reactors such that all of a reactor's instantiation dependencies occur earlier in // the sorted list of reactors. This helps the code generator output code in the correct order. // For example if `reactor Foo {bar = new Bar()}` then the definition of `Bar` has to be generated before // the definition of `Foo`. - this.reactors = this.instantiationGraph.nodesInTopologicalOrder + reactors = instantiationGraph.nodesInTopologicalOrder(); // If there is no main reactor or if all reactors in the file need to be validated, then make sure the reactors // list includes even reactors that are not instantiated anywhere. - if (mainDef === null || mode == LFGeneratorContext.Mode.LSP_MEDIUM) { - for (r : fileConfig.resource.allContents.toIterable.filter(Reactor)) { - if (!this.reactors.contains(r)) { - this.reactors.add(r); + if (mainDef == null || Objects.equal(mode, LFGeneratorContext.Mode.LSP_MEDIUM)) { + Iterable nodes = IteratorExtensions.toIterable(fileConfig.resource.getAllContents()); + for (Reactor r : IterableExtensions.filter(nodes, Reactor.class)) { + if (!reactors.contains(r)) { + reactors.add(r); } } } @@ -411,9 +424,9 @@ protected def setReactorsAndInstantiationGraph(LFGeneratorContext.Mode mode) { /** * For each involved resource, replace connections with delays with generated delay reactors. */ - private def transformDelays() { - for (r : this.resources) { - r.eResource.insertGeneratedDelays(this) + private void transformDelays() { + for (LFResource r : resources) { + ASTUtils.insertGeneratedDelays(r.eResource, this); } } @@ -425,21 +438,21 @@ private def transformDelays() { * @param targetConfig The targetConfig to read the `files` from. * @param fileConfig The fileConfig used to make the copy and resolve paths. */ - protected def void copyUserFiles(TargetConfig targetConfig, FileConfig fileConfig) {} + protected void copyUserFiles(TargetConfig targetConfig, FileConfig fileConfig) {} /** * Return true if errors occurred in the last call to doGenerate(). * This will return true if any of the reportError methods was called. * @return True if errors occurred. */ - def errorsOccurred() { + public boolean errorsOccurred() { return errorReporter.getErrorsOccurred(); } /* * Return the TargetTypes instance associated with this. */ - abstract def TargetTypes getTargetTypes(); + public abstract TargetTypes getTargetTypes(); /** * Generate code for the body of a reaction that takes an input and @@ -447,7 +460,7 @@ def errorsOccurred() { * @param the action to schedule * @param the port to read from */ - abstract def String generateDelayBody(Action action, VarRef port); + public abstract String generateDelayBody(Action action, VarRef port); /** * Generate code for the body of a reaction that is triggered by the @@ -455,13 +468,13 @@ def errorsOccurred() { * @param the action that triggers the reaction * @param the port to write to */ - abstract def String generateForwardBody(Action action, VarRef port); + public abstract String generateForwardBody(Action action, VarRef port); /** * Generate code for the generic type to be used in the class definition * of a generated delay reactor. */ - abstract def String generateDelayGeneric(); + public abstract String generateDelayGeneric(); /** * Return true if the reaction is unordered. An unordered reaction is one @@ -475,12 +488,8 @@ def errorsOccurred() { * the Reaction instance is created, add that instance to this set. * @return True if the reaction has been marked unordered. */ - def isUnordered(Reaction reaction) { - if (unorderedReactions !== null) { - unorderedReactions.contains(reaction) - } else { - false - } + public boolean isUnordered(Reaction reaction) { + return unorderedReactions != null ? unorderedReactions.contains(reaction) : false; } /** @@ -495,11 +504,11 @@ def isUnordered(Reaction reaction) { * instance is created, add that instance to this set. * @param reaction The reaction to make unordered. */ - def makeUnordered(Reaction reaction) { - if (unorderedReactions === null) { - unorderedReactions = new LinkedHashSet() + public void makeUnordered(Reaction reaction) { + if (unorderedReactions == null) { + unorderedReactions = new LinkedHashSet(); } - unorderedReactions.add(reaction) + unorderedReactions.add(reaction); } /** @@ -511,13 +520,14 @@ def makeUnordered(Reaction reaction) { * @param The reaction. * @param bankIndex The bank index, or -1 if there is no bank. */ - def setReactionBankIndex(Reaction reaction, int bankIndex) { - if (bankIndex >= 0) { - if (reactionBankIndices === null) { - reactionBankIndices = new LinkedHashMap() - } - reactionBankIndices.put(reaction, bankIndex) + public void setReactionBankIndex(Reaction reaction, int bankIndex) { + if (bankIndex < 0) { + return; } + if (reactionBankIndices == null) { + reactionBankIndices = new LinkedHashMap(); + } + reactionBankIndices.put(reaction, bankIndex); } /** @@ -526,10 +536,10 @@ def setReactionBankIndex(Reaction reaction, int bankIndex) { * @param The reaction. * @return The reaction bank index, if one has been set, and -1 otherwise. */ - def int getReactionBankIndex(Reaction reaction) { - if (reactionBankIndices === null) return -1 - if (reactionBankIndices.get(reaction) === null) return -1 - return reactionBankIndices.get(reaction) + public int getReactionBankIndex(Reaction reaction) { + if (reactionBankIndices == null) return -1; + if (reactionBankIndices.get(reaction) == null) return -1; + return reactionBankIndices.get(reaction); } /** @@ -543,20 +553,20 @@ def int getReactionBankIndex(Reaction reaction) { * @param time A TimeValue that represents a time. * @return A string, such as "MSEC(100)" for 100 milliseconds. */ - static def String timeInTargetLanguage(TimeValue time) { - if (time !== null) { - if (time.unit !== null) { - return time.unit.cMacroName + '(' + time.magnitude + ')' + public static String timeInTargetLanguage(TimeValue time) { + if (time != null) { + if (time.unit != null) { + return cMacroName(time.unit) + "(" + time.getMagnitude() + ")"; } else { - return time.magnitude.toString() + return Long.valueOf(time.getMagnitude()).toString(); } } - return "0" // FIXME: do this or throw exception? + return "0"; // FIXME: do this or throw exception? } // note that this is moved out by #544 - static def String cMacroName(TimeUnit unit) { - return unit.canonicalName.toUpperCase + public static final String cMacroName(TimeUnit unit) { + return unit.getCanonicalName().toUpperCase(); } // ////////////////////////////////////////// @@ -567,9 +577,10 @@ static def String cMacroName(TimeUnit unit) { * This will set the hasModalReactors variable. * @param isSupported indicates if modes are supported by this code generation. */ - protected def void checkModalReactorSupport(boolean isSupported) { + protected void checkModalReactorSupport(boolean isSupported) { if (hasModalReactors && !isSupported) { - errorReporter.reportError("The currently selected code generation or target configuration does not support modal reactors!") + errorReporter.reportError("The currently selected code generation or " + + "target configuration does not support modal reactors!"); } } @@ -577,9 +588,9 @@ protected def void checkModalReactorSupport(boolean isSupported) { * Finds and transforms connections into forwarding reactions iff the connections have the same destination as other * connections or reaction in mutually exclusive modes. */ - private def void transformConflictingConnectionsInModalReactors() { - for (r : this.resources) { - val transform = ASTUtils.findConflictingConnectionsInModalReactors(r.eResource); + private void transformConflictingConnectionsInModalReactors() { + for (LFResource r : resources) { + var transform = ASTUtils.findConflictingConnectionsInModalReactors(r.eResource); if (!transform.isEmpty()) { transformConflictingConnectionsInModalReactors(transform); } @@ -591,8 +602,10 @@ private def void transformConflictingConnectionsInModalReactors() { * * This methods needs to be overridden in target specific code generators that support modal reactors. */ - protected def void transformConflictingConnectionsInModalReactors(Collection transform) { - errorReporter.reportError("The currently selected code generation is missing an implementation for conflicting transforming connections in modal reactors.") + protected void transformConflictingConnectionsInModalReactors(Collection transform) { + errorReporter.reportError("The currently selected code generation " + + "is missing an implementation for conflicting " + + "transforming connections in modal reactors."); } /** @@ -611,7 +624,7 @@ protected def void transformConflictingConnectionsInModalReactors(Collection= 0) ? "docker-compose" : "docker compose" + public String getDockerComposeCommand() { + String OS = System.getProperty("os.name").toLowerCase(); + return (OS.indexOf("nux") >= 0) ? "docker-compose" : "docker compose"; } /** @@ -752,28 +765,30 @@ def getDockerComposeCommand() { * @param The name of the docker file. * @param The name of the federate. */ - def getDockerBuildCommand(String dockerFile, File dockerComposeDir, String federateName) { + public String getDockerBuildCommand(String dockerFile, File dockerComposeDir, String federateName) { return String.join("\n", - '''Dockerfile for «topLevelName» written to «dockerFile»''', - '''#####################################''', - '''To build the docker image, go to «dockerComposeDir» and run:''', + "Dockerfile for "+topLevelName+" written to "+dockerFile, + "#####################################", + "To build the docker image, go to "+dockerComposeDir+" and run:", "", - ''' «getDockerComposeCommand()» build «federateName»''', + " "+getDockerComposeCommand()+" build "+federateName, "", - '''#####################################''' + "#####################################" ); } /** * Parsed error message from a compiler is returned here. */ - static class ErrorFileAndLine { - public var filepath = null as String - public var line = "1" - public var character = "0" - public var message = "" - public var isError = true // false for a warning. - override String toString() { + public static class ErrorFileAndLine { + public String filepath = null; + public String line = "1"; + public String character = "0"; + public String message = ""; + public boolean isError = true; // false for a warning. + + @Override + public String toString() { return (isError ? "Error" : "Non-error") + " at " + line + ":" + character + " of file " + filepath + ": " + message; } } @@ -792,8 +807,8 @@ override String toString() { * "0" if there is none), and the message (or an empty string if there * is none). */ - protected def parseCommandOutput(String line) { - return null as ErrorFileAndLine + protected ErrorFileAndLine parseCommandOutput(String line) { + return null; } /** @@ -805,90 +820,90 @@ protected def parseCommandOutput(String line) { * * @param stderr The output on standard error of executing a command. */ - def reportCommandErrors(String stderr) { + public void reportCommandErrors(String stderr) { // NOTE: If the VS Code branch passes code review, then this function, // parseCommandOutput, and ErrorFileAndLine will be deleted soon after. // First, split the message into lines. - val lines = stderr.split("\\r?\\n") - var message = new StringBuilder() - var lineNumber = null as Integer - var path = fileConfig.srcFile + String[] lines = stderr.split("\\r?\\n"); + StringBuilder message = new StringBuilder(); + Integer lineNumber = null; + Path path = fileConfig.srcFile; // In case errors occur within an imported file, record the original path. - val originalPath = path; + Path originalPath = path; - var severity = IMarker.SEVERITY_ERROR - for (line : lines) { - val parsed = parseCommandOutput(line) - if (parsed !== null) { + int severity = IMarker.SEVERITY_ERROR; + for (String line : lines) { + ErrorFileAndLine parsed = parseCommandOutput(line); + if (parsed != null) { // Found a new line number designator. // If there is a previously accumulated message, report it. - if (message.length > 0) { + if (message.length() > 0) { if (severity == IMarker.SEVERITY_ERROR) - errorReporter.reportError(path, lineNumber, message.toString()) + errorReporter.reportError(path, lineNumber, message.toString()); else - errorReporter.reportWarning(path, lineNumber, message.toString()) + errorReporter.reportWarning(path, lineNumber, message.toString()); - if (originalPath.toFile != path.toFile) { + if (!Objects.equal(originalPath.toFile(), path.toFile())) { // Report an error also in the top-level resource. // FIXME: It should be possible to descend through the import // statements to find which one matches and mark all the // import statements down the chain. But what a pain! if (severity == IMarker.SEVERITY_ERROR) { - errorReporter.reportError(originalPath, 1, "Error in imported file: " + path) + errorReporter.reportError(originalPath, 1, "Error in imported file: " + path); } else { - errorReporter.reportWarning(originalPath, 1, "Warning in imported file: " + path) + errorReporter.reportWarning(originalPath, 1, "Warning in imported file: " + path); } } } if (parsed.isError) { - severity = IMarker.SEVERITY_ERROR + severity = IMarker.SEVERITY_ERROR; } else { - severity = IMarker.SEVERITY_WARNING + severity = IMarker.SEVERITY_WARNING; } // Start accumulating a new message. - message = new StringBuilder() + message = new StringBuilder(); // Append the message on the line number designator line. - message.append(parsed.message) + message.append(parsed.message); // Set the new line number. try { - lineNumber = Integer.decode(parsed.line) + lineNumber = Integer.decode(parsed.line); } catch (Exception ex) { // Set the line number unknown. - lineNumber = null + lineNumber = null; } // FIXME: Ignoring the position within the line. // Determine the path within which the error occurred. - path = Paths.get(parsed.filepath) + path = Paths.get(parsed.filepath); } else { // No line designator. - if (message.length > 0) { - message.append("\n") + if (message.length() > 0) { + message.append("\n"); } else { - if (!line.toLowerCase.contains('error:')) { - severity = IMarker.SEVERITY_WARNING + if (!line.toLowerCase().contains("error:")) { + severity = IMarker.SEVERITY_WARNING; } } message.append(line); } } - if (message.length > 0) { + if (message.length() > 0) { if (severity == IMarker.SEVERITY_ERROR) { - errorReporter.reportError(path, lineNumber, message.toString()) + errorReporter.reportError(path, lineNumber, message.toString()); } else { - errorReporter.reportWarning(path, lineNumber, message.toString()) + errorReporter.reportWarning(path, lineNumber, message.toString()); } - if (originalPath.toFile != path.toFile) { + if (originalPath.toFile() != path.toFile()) { // Report an error also in the top-level resource. // FIXME: It should be possible to descend through the import // statements to find which one matches and mark all the // import statements down the chain. But what a pain! if (severity == IMarker.SEVERITY_ERROR) { - errorReporter.reportError(originalPath, 1, "Error in imported file: " + path) + errorReporter.reportError(originalPath, 1, "Error in imported file: " + path); } else { - errorReporter.reportWarning(originalPath, 1, "Warning in imported file: " + path) + errorReporter.reportWarning(originalPath, 1, "Warning in imported file: " + path); } } } @@ -900,8 +915,8 @@ def reportCommandErrors(String stderr) { * @param param The parameter to generate code for * @return Parameter reference in target code */ - protected def String getTargetReference(Parameter param) { - return param.name + protected String getTargetReference(Parameter param) { + return param.getName(); } // ////////////////////////////////////////////////// @@ -919,24 +934,27 @@ protected def String getTargetReference(Parameter param) { * @param instance The reactor instance to remove these ports from if any. * Can be null. */ - protected def void removeRemoteFederateConnectionPorts(ReactorInstance instance) { - if (isFederated) { - for (federate: federates) { - // Remove disconnected network triggers from the AST - federate.removeRemoteFederateConnectionPorts(); - if (instance !== null) { - // If passed a reactor instance, also purge the disconnected network triggers - // from the reactor instance graph - for (reaction: federate.networkReactions) { - val networkReaction = instance.lookupReactionInstance(reaction) - if (networkReaction !== null) { - for (port: federate.remoteNetworkReactionTriggers) { - val disconnectedPortInstance = instance.lookupPortInstance(port); - if (disconnectedPortInstance !== null) { - networkReaction.removePortInstance(disconnectedPortInstance); - } - } - } + protected void removeRemoteFederateConnectionPorts(ReactorInstance instance) { + if (!isFederated) { + return; + } + for (FederateInstance federate : federates) { + // Remove disconnected network triggers from the AST + federate.removeRemoteFederateConnectionPorts(); + if (instance == null) { + continue; + } + // If passed a reactor instance, also purge the disconnected network triggers + // from the reactor instance graph + for (Reaction reaction : federate.networkReactions) { + ReactionInstance networkReaction = instance.lookupReactionInstance(reaction); + if (networkReaction == null) { + continue; + } + for (VarRef port : federate.remoteNetworkReactionTriggers) { + PortInstance disconnectedPortInstance = instance.lookupPortInstance(port); + if (disconnectedPortInstance != null) { + networkReaction.removePortInstance(disconnectedPortInstance); } } } @@ -946,29 +964,29 @@ protected def void removeRemoteFederateConnectionPorts(ReactorInstance instance) /** * Set the RTI hostname, port and username if given as compiler arguments */ - private def setFederationRTIProperties(LFGeneratorContext context) { - val rtiAddr = context.args.getProperty("rti").toString() - val pattern = Pattern.compile("([a-zA-Z0-9]+@)?([a-zA-Z0-9]+\\.?[a-z]{2,}|[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+):?([0-9]+)?") - val matcher = pattern.matcher(rtiAddr) + private void setFederationRTIProperties(LFGeneratorContext context) { + String rtiAddr = context.getArgs().getProperty("rti").toString(); + Pattern pattern = Pattern.compile("([a-zA-Z0-9]+@)?([a-zA-Z0-9]+\\.?[a-z]{2,}|[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+):?([0-9]+)?"); + Matcher matcher = pattern.matcher(rtiAddr); if (!matcher.find()) { return; } // the user match group contains a trailing "@" which needs to be removed. - val userWithAt = matcher.group(1) - val user = userWithAt === null ? null : userWithAt.substring(0, userWithAt.length() - 1) - val host = matcher.group(2) - val port = matcher.group(3) + String userWithAt = matcher.group(1); + String user = userWithAt == null ? null : userWithAt.substring(0, userWithAt.length() - 1); + String host = matcher.group(2); + String port = matcher.group(3); - if (host !== null) { - federationRTIProperties.put("host", host) + if (host != null) { + federationRTIProperties.put("host", host); } - if (port !== null) { - federationRTIProperties.put("port", port) + if (port != null) { + federationRTIProperties.put("port", port); } - if (user !== null) { - federationRTIProperties.put("user", user) + if (user != null) { + federationRTIProperties.put("user", user); } } @@ -990,85 +1008,85 @@ private def setFederationRTIProperties(LFGeneratorContext context) { * information between federates. See the C target * for a reference implementation. */ - private def analyzeFederates(LFGeneratorContext context) { + private void analyzeFederates(LFGeneratorContext context) { // Next, if there actually are federates, analyze the topology // interconnecting them and replace the connections between them // with an action and two reactions. - val mainReactor = this.mainDef?.reactorClass.toDefinition + Reactor mainReactor = mainDef != null ? ASTUtils.toDefinition(mainDef.getReactorClass()) : null; - if (this.mainDef === null || !mainReactor.isFederated) { + if (mainDef == null || !mainReactor.isFederated()) { // The program is not federated. // Ensure federates is never empty. - var federateInstance = new FederateInstance(null, 0, 0, this, errorReporter) - federates.add(federateInstance) - federateByID.put(0, federateInstance) + FederateInstance federateInstance = new FederateInstance(null, 0, 0, this, errorReporter); + federates.add(federateInstance); + federateByID.put(0, federateInstance); } else { // The Lingua Franca program is federated - isFederated = true + isFederated = true; // If the "--rti" flag is given to the compiler, use the argument from the flag. - if (context.args.containsKey("rti")) { - setFederationRTIProperties(context) - } else if (mainReactor.host !== null) { + if (context.getArgs().containsKey("rti")) { + setFederationRTIProperties(context); + } else if (mainReactor.getHost() != null) { // Get the host information, if specified. // If not specified, this defaults to 'localhost' - if (mainReactor.host.addr !== null) { - federationRTIProperties.put("host", mainReactor.host.addr) + if (mainReactor.getHost().getAddr() != null) { + federationRTIProperties.put("host", mainReactor.getHost().getAddr()); } // Get the port information, if specified. // If not specified, this defaults to 14045 - if (mainReactor.host.port !== 0) { - federationRTIProperties.put("port", mainReactor.host.port) + if (mainReactor.getHost().getPort() != 0) { + federationRTIProperties.put("port", mainReactor.getHost().getPort()); } // Get the user information, if specified. - if (mainReactor.host.user !== null) { - federationRTIProperties.put("user", mainReactor.host.user) + if (mainReactor.getHost().getUser() != null) { + federationRTIProperties.put("user", mainReactor.getHost().getUser()); } } // Since federates are always within the main (federated) reactor, // create a list containing just that one containing instantiation. // This will be used to look up parameter values. - val mainReactorContext = new ArrayList(); + List mainReactorContext = new ArrayList<>(); mainReactorContext.add(mainDef); // Create a FederateInstance for each top-level reactor. - for (instantiation : mainReactor.allInstantiations) { - var bankWidth = ASTUtils.width(instantiation.widthSpec, mainReactorContext); + for (Instantiation instantiation : ASTUtils.allInstantiations(mainReactor)) { + int bankWidth = ASTUtils.width(instantiation.getWidthSpec(), mainReactorContext); if (bankWidth < 0) { errorReporter.reportError(instantiation, "Cannot determine bank width! Assuming width of 1."); // Continue with a bank width of 1. bankWidth = 1; } // Create one federate instance for each instance in a bank of reactors. - val federateInstances = new ArrayList(bankWidth); - for (var i = 0; i < bankWidth; i++) { + List federateInstances = new ArrayList<>(bankWidth); + for (int i = 0; i < bankWidth; i++) { // Assign an integer ID to the federate. - var federateID = federates.size - var federateInstance = new FederateInstance(instantiation, federateID, i, this, errorReporter) + int federateID = federates.size(); + FederateInstance federateInstance = new FederateInstance(instantiation, federateID, i, this, errorReporter); federateInstance.bankIndex = i; - federates.add(federateInstance) - federateInstances.add(federateInstance) - federateByID.put(federateID, federateInstance) + federates.add(federateInstance); + federateInstances.add(federateInstance); + federateByID.put(federateID, federateInstance); - if (instantiation.host !== null) { - federateInstance.host = instantiation.host.addr + if (instantiation.getHost() != null) { + federateInstance.host = instantiation.getHost().getAddr(); // The following could be 0. - federateInstance.port = instantiation.host.port + federateInstance.port = instantiation.getHost().getPort(); // The following could be null. - federateInstance.user = instantiation.host.user + federateInstance.user = instantiation.getHost().getUser(); /* FIXME: The at keyword should support a directory component. - * federateInstance.dir = instantiation.host.dir + * federateInstance.dir = instantiation.getHost().dir */ - if (federateInstance.host !== null && - federateInstance.host != 'localhost' && - federateInstance.host != '0.0.0.0' + if (federateInstance.host != null && + federateInstance.host != "localhost" && + federateInstance.host != "0.0.0.0" ) { federateInstance.isRemote = true; } } } - if (federatesByInstantiation === null) { + if (federatesByInstantiation == null) { federatesByInstantiation = new LinkedHashMap>(); } federatesByInstantiation.put(instantiation, federateInstances); @@ -1077,7 +1095,7 @@ private def analyzeFederates(LFGeneratorContext context) { // In a federated execution, we need keepalive to be true, // otherwise a federate could exit simply because it hasn't received // any messages. - targetConfig.keepalive = true + targetConfig.keepalive = true; // Analyze the connection topology of federates. // First, find all the connections between federates. @@ -1085,10 +1103,10 @@ private def analyzeFederates(LFGeneratorContext context) { // AST with an action (which inherits the delay) and two reactions. // The action will be physical for physical connections and logical // for logical connections. - replaceFederateConnectionsWithActions() + replaceFederateConnectionsWithActions(); // Remove the connections at the top level - mainReactor.connections.clear() + mainReactor.getConnections().clear(); } } @@ -1096,8 +1114,8 @@ private def analyzeFederates(LFGeneratorContext context) { * Replace connections between federates in the AST with actions that * handle sending and receiving data. */ - private def replaceFederateConnectionsWithActions() { - val mainReactor = this.mainDef?.reactorClass.toDefinition + private void replaceFederateConnectionsWithActions() { + Reactor mainReactor = mainDef != null ? ASTUtils.toDefinition(mainDef.getReactorClass()) : null; // Each connection in the AST may represent more than one connection between // federate instances because of banks and multiports. We need to generate communication @@ -1105,11 +1123,11 @@ private def replaceFederateConnectionsWithActions() { // to duplicate the rather complicated logic in that class. We specify a depth of 1, // so it only creates the reactors immediately within the top level, not reactors // that those contain. - val mainInstance = new ReactorInstance(mainReactor, errorReporter, 1) + ReactorInstance mainInstance = new ReactorInstance(mainReactor, errorReporter, 1); - for (child : mainInstance.children) { - for (output : child.outputs) { - replaceConnectionFromFederate(output, child, mainInstance) + for (ReactorInstance child : mainInstance.children) { + for (PortInstance output : child.outputs) { + replaceConnectionFromFederate(output, child, mainInstance); } } } @@ -1121,68 +1139,68 @@ private def replaceFederateConnectionsWithActions() { * @param federateReactor The reactor instance for that federate. * @param mainInstance The main reactor instance. */ - private def void replaceConnectionFromFederate( + private void replaceConnectionFromFederate( PortInstance output, ReactorInstance federateReactor, ReactorInstance mainInstance ) { - for (srcRange : output.dependentPorts) { + for (SendRange srcRange : output.dependentPorts) { for (RuntimeRange dstRange : srcRange.destinations) { - var srcID = srcRange.startMR(); - val dstID = dstRange.startMR(); - var dstCount = 0; - var srcCount = 0; + MixedRadixInt srcID = srcRange.startMR(); + MixedRadixInt dstID = dstRange.startMR(); + int dstCount = 0; + int srcCount = 0; while (dstCount++ < dstRange.width) { - val srcChannel = srcID.digits.get(0); - val srcBank = srcID.get(1); - val dstChannel = dstID.digits.get(0); - val dstBank = dstID.get(1); + int srcChannel = srcID.getDigits().get(0); + int srcBank = srcID.get(1); + int dstChannel = dstID.getDigits().get(0); + int dstBank = dstID.get(1); - val srcFederate = federatesByInstantiation.get( + FederateInstance srcFederate = federatesByInstantiation.get( srcRange.instance.parent.definition ).get(srcBank); - val dstFederate = federatesByInstantiation.get( + FederateInstance dstFederate = federatesByInstantiation.get( dstRange.instance.parent.definition ).get(dstBank); - val connection = srcRange.connection; + Connection connection = srcRange.connection; - if (connection === null) { + if (connection == null) { // This should not happen. errorReporter.reportError(output.definition, - "Unexpected error. Cannot find output connection for port") + "Unexpected error. Cannot find output connection for port"); } else { - if (srcFederate !== dstFederate - && !connection.physical - && targetConfig.coordination !== CoordinationType.DECENTRALIZED) { + if (srcFederate != dstFederate + && !connection.isPhysical() + && targetConfig.coordination != CoordinationType.DECENTRALIZED) { // Map the delays on connections between federates. // First see if the cache has been created. - var dependsOnDelays = dstFederate.dependsOn.get(srcFederate) - if (dependsOnDelays === null) { + Set dependsOnDelays = dstFederate.dependsOn.get(srcFederate); + if (dependsOnDelays == null) { // If not, create it. - dependsOnDelays = new LinkedHashSet() - dstFederate.dependsOn.put(srcFederate, dependsOnDelays) + dependsOnDelays = new LinkedHashSet(); + dstFederate.dependsOn.put(srcFederate, dependsOnDelays); } // Put the delay on the cache. - if (connection.delay !== null) { - dependsOnDelays.add(connection.delay) + if (connection.getDelay() != null) { + dependsOnDelays.add(connection.getDelay()); } else { // To indicate that at least one connection has no delay, add a null entry. - dependsOnDelays.add(null) + dependsOnDelays.add(null); } // Map the connections between federates. - var sendsToDelays = srcFederate.sendsTo.get(dstFederate) - if (sendsToDelays === null) { - sendsToDelays = new LinkedHashSet() - srcFederate.sendsTo.put(dstFederate, sendsToDelays) + Set sendsToDelays = srcFederate.sendsTo.get(dstFederate); + if (sendsToDelays == null) { + sendsToDelays = new LinkedHashSet(); + srcFederate.sendsTo.put(dstFederate, sendsToDelays); } - if (connection.delay !== null) { - sendsToDelays.add(connection.delay) + if (connection.getDelay() != null) { + sendsToDelays.add(connection.getDelay()); } else { // To indicate that at least one connection has no delay, add a null entry. - sendsToDelays.add(null) + sendsToDelays.add(null); } } @@ -1215,10 +1233,10 @@ private def void replaceConnectionFromFederate( * Print to stdout information about what source file is being generated, * what mode the generator is in, and where the generated sources are to be put. */ - def printInfo(LFGeneratorContext.Mode mode) { - println("Generating code for: " + fileConfig.resource.getURI.toString) - println('******** mode: ' + mode) - println('******** generated sources: ' + fileConfig.getSrcGenPath) + public void printInfo(LFGeneratorContext.Mode mode) { + System.out.println("Generating code for: " + fileConfig.resource.getURI().toString()); + System.out.println("******** mode: " + mode); + System.out.println("******** generated sources: " + fileConfig.getSrcGenPath()); } /** @@ -1232,17 +1250,17 @@ def printInfo(LFGeneratorContext.Mode mode) { * side of the connection. This gives the code generator the information needed to infer the correct width at * runtime. */ - def boolean generateAfterDelaysWithVariableWidth() { return true } + public boolean generateAfterDelaysWithVariableWidth() { return true; } /** * Get the buffer type used for network messages */ - def String getNetworkBufferType() '''''' + public String getNetworkBufferType() { return ""; } /** * Return the Targets enum for the current target */ - abstract def Target getTarget() + public abstract Target getTarget(); /** * Get textual representation of a time in the target language. @@ -1250,9 +1268,9 @@ abstract def Target getTarget() * @param t A time AST node * @return A time string in the target language */ - static def getTargetTime(Time t) { - val value = new TimeValue(t.interval, TimeUnit.fromName(t.unit)) - return value.timeInTargetLanguage + public static String getTargetTime(Time t) { + TimeValue value = new TimeValue(t.getInterval(), TimeUnit.fromName(t.getUnit())); + return timeInTargetLanguage(value); } /** @@ -1263,11 +1281,11 @@ static def getTargetTime(Time t) { * @param v A time AST node * @return A time string in the target language */ - static def getTargetValue(Value v) { - if (v.time !== null) { - return v.time.targetTime + public static String getTargetValue(Value v) { + if (v.getTime() != null) { + return getTargetTime(v.getTime()); } - return v.toText + return ASTUtils.toText(v); } /** @@ -1278,21 +1296,21 @@ static def getTargetValue(Value v) { * @param v A time AST node * @return A time string in the target language */ - static def getTargetTime(Value v) { - if (v.time !== null) { - return v.time.targetTime - } else if (v.isZero) { - val value = TimeValue.ZERO - return value.timeInTargetLanguage + public static String getTargetTime(Value v) { + if (v.getTime() != null) { + return getTargetTime(v.getTime()); + } else if (ASTUtils.isZero(v)) { + TimeValue value = TimeValue.ZERO; + return timeInTargetLanguage(value); } - return v.toText + return ASTUtils.toText(v); } - static def getTargetTime(Delay d) { - if (d.parameter !== null) { - return d.toText + public static String getTargetTime(Delay d) { + if (d.getParameter() != null) { + return ASTUtils.toText(d); } else { - return d.time.targetTime + return getTargetTime(d.getTime()); } } }