use of org.lflang.lf.Mode in project lingua-franca by lf-lang.
the class ASTUtils method findConflictingConnectionsInModalReactors.
/**
* Find connections in the given resource that would be conflicting writes if they were not located in mutually
* exclusive modes.
*
* @param resource The AST.
* @return a list of connections being able to be transformed
*/
public static Collection<Connection> findConflictingConnectionsInModalReactors(Resource resource) {
var transform = new HashSet<Connection>();
var reactors = Iterables.filter(IteratorExtensions.toIterable(resource.getAllContents()), Reactor.class);
for (Reactor reactor : reactors) {
if (!reactor.getModes().isEmpty()) {
// Only for modal reactors
var allWriters = HashMultimap.<Pair<Instantiation, Variable>, EObject>create();
// Collect destinations
for (var rea : allReactions(reactor)) {
for (var eff : rea.getEffects()) {
if (eff.getVariable() instanceof Port) {
allWriters.put(Tuples.pair(eff.getContainer(), eff.getVariable()), rea);
}
}
}
for (var con : ASTUtils.<Connection>collectElements(reactor, featurePackage.getReactor_Connections(), false, true)) {
for (var port : con.getRightPorts()) {
allWriters.put(Tuples.pair(port.getContainer(), port.getVariable()), con);
}
}
// Handle conflicting writers
for (var key : allWriters.keySet()) {
var writers = allWriters.get(key);
if (writers.size() > 1) {
// has multiple sources
var writerModes = HashMultimap.<Mode, EObject>create();
// find modes
for (var writer : writers) {
if (writer.eContainer() instanceof Mode) {
writerModes.put((Mode) writer.eContainer(), writer);
} else {
writerModes.put(null, writer);
}
}
// Conflicting connection can only be handled if..
if (// no writer is on root level (outside of modes) and...
!writerModes.containsKey(null) && writerModes.keySet().stream().map(m -> writerModes.get(m)).allMatch(// all writers in a mode are either...
writersInMode -> // the only writer or...
writersInMode.size() == 1 || // all are reactions and hence ordered
writersInMode.stream().allMatch(w -> w instanceof Reaction))) {
// Add connections to transform list
writers.stream().filter(w -> w instanceof Connection).forEach(c -> transform.add((Connection) c));
}
}
}
}
}
return transform;
}
use of org.lflang.lf.Mode in project lingua-franca by lf-lang.
the class ASTUtils method insertGeneratedDelays.
/**
* Find connections in the given resource that have a delay associated with them,
* and reroute them via a generated delay reactor.
* @param resource The AST.
* @param generator A code generator.
*/
public static void insertGeneratedDelays(Resource resource, GeneratorBase generator) {
// The resulting changes to the AST are performed _after_ iterating
// in order to avoid concurrent modification problems.
List<Connection> oldConnections = new ArrayList<>();
Map<EObject, List<Connection>> newConnections = new LinkedHashMap<>();
Map<EObject, List<Instantiation>> delayInstances = new LinkedHashMap<>();
Iterable<Reactor> containers = Iterables.filter(IteratorExtensions.toIterable(resource.getAllContents()), Reactor.class);
// Iterate over the connections in the tree.
for (Reactor container : containers) {
for (Connection connection : allConnections(container)) {
if (connection.getDelay() != null) {
EObject parent = connection.eContainer();
// Assume all the types are the same, so just use the first on the right.
Type type = ((Port) connection.getRightPorts().get(0).getVariable()).getType();
Reactor delayClass = getDelayClass(type, generator);
String generic = generator.getTargetTypes().supportsGenerics() ? generator.getTargetTypes().getTargetType(InferredType.fromAST(type)) : "";
Instantiation delayInstance = getDelayInstance(delayClass, connection, generic, !generator.generateAfterDelaysWithVariableWidth());
// Stage the new connections for insertion into the tree.
List<Connection> connections = convertToEmptyListIfNull(newConnections.get(parent));
connections.addAll(rerouteViaDelay(connection, delayInstance));
newConnections.put(parent, connections);
// Stage the original connection for deletion from the tree.
oldConnections.add(connection);
// Stage the newly created delay reactor instance for insertion
List<Instantiation> instances = convertToEmptyListIfNull(delayInstances.get(parent));
instances.add(delayInstance);
delayInstances.put(parent, instances);
}
}
}
// Remove old connections; insert new ones.
oldConnections.forEach(connection -> {
var container = connection.eContainer();
if (container instanceof Reactor) {
((Reactor) container).getConnections().remove(connection);
} else if (container instanceof Mode) {
((Mode) container).getConnections().remove(connection);
}
});
newConnections.forEach((container, connections) -> {
if (container instanceof Reactor) {
((Reactor) container).getConnections().addAll(connections);
} else if (container instanceof Mode) {
((Mode) container).getConnections().addAll(connections);
}
});
// Finally, insert the instances and, before doing so, assign them a unique name.
delayInstances.forEach((container, instantiations) -> instantiations.forEach(instantiation -> {
if (container instanceof Reactor) {
instantiation.setName(getUniqueIdentifier((Reactor) container, "delay"));
((Reactor) container).getInstantiations().add(instantiation);
} else if (container instanceof Mode) {
instantiation.setName(getUniqueIdentifier((Reactor) container.eContainer(), "delay"));
((Mode) container).getInstantiations().add(instantiation);
}
}));
}
use of org.lflang.lf.Mode in project lingua-franca by lf-lang.
the class ASTUtils method collectElements.
/**
* Collect elements of type T contained in given reactor definition, including
* modes and the class hierarchy defined depending on configuration.
* @param definition The reactor definition.
* @param feature The structual model elements to collect.
* @param includeSuperClasses Whether to include elements in super classes.
* @param includeModes Whether to include elements in modes.
* @param <T> The type of elements to collect (e.g., Port, Timer, etc.)
* @return
*/
@SuppressWarnings("unchecked")
public static <T> List<T> collectElements(Reactor definition, EStructuralFeature feature, boolean includeSuperClasses, boolean includeModes) {
List<T> result = new ArrayList<T>();
if (includeSuperClasses) {
// Add elements of elements defined in superclasses.
LinkedHashSet<Reactor> s = superClasses(definition);
if (s != null) {
for (Reactor superClass : s) {
result.addAll((EList<T>) superClass.eGet(feature));
}
}
}
// Add elements of the current reactor.
result.addAll((EList<T>) definition.eGet(feature));
if (includeModes && reactorModeFeatureMap.containsKey(feature)) {
var modeFeature = reactorModeFeatureMap.get(feature);
// Add elements of elements defined in modes.
for (Mode mode : includeSuperClasses ? allModes(definition) : definition.getModes()) {
result.addAll((EList<T>) mode.eGet(modeFeature));
}
}
return result;
}
use of org.lflang.lf.Mode in project lingua-franca by lf-lang.
the class CReactionGenerator method generateInitializationForReaction.
/**
* Generate necessary initialization code inside the body of the reaction that belongs to reactor decl.
* @param body The body of the reaction. Used to check for the DISABLE_REACTION_INITIALIZATION_MARKER.
* @param reaction The initialization code will be generated for this specific reaction
* @param decl The reactor that has the reaction
* @param reactionIndex The index of the reaction relative to other reactions in the reactor, starting from 0
*/
public static String generateInitializationForReaction(String body, Reaction reaction, ReactorDecl decl, int reactionIndex, CTypes types, ErrorReporter errorReporter, Instantiation mainDef, boolean isFederatedAndDecentralized, boolean requiresTypes) {
Reactor reactor = ASTUtils.toDefinition(decl);
// Construct the reactionInitialization code to go into
// the body of the function before the verbatim code.
CodeBuilder reactionInitialization = new CodeBuilder();
CodeBuilder code = new CodeBuilder();
// Define the "self" struct.
String structType = CUtil.selfType(decl);
// or anything else. No need to declare it.
if (structType != null) {
code.pr(String.join("\n", "#pragma GCC diagnostic push", "#pragma GCC diagnostic ignored \"-Wunused-variable\"", structType + "* self = (" + structType + "*)instance_args;"));
}
// to not generate it.
if (body.startsWith(CGenerator.DISABLE_REACTION_INITIALIZATION_MARKER)) {
code.pr("#pragma GCC diagnostic pop");
return code.toString();
}
// A reaction may send to or receive from multiple ports of
// a contained reactor. The variables for these ports need to
// all be declared as fields of the same struct. Hence, we first
// collect the fields to be defined in the structs and then
// generate the structs.
Map<Instantiation, CodeBuilder> fieldsForStructsForContainedReactors = new LinkedHashMap<>();
// Actions may appear twice, first as a trigger, then with the outputs.
// But we need to declare it only once. Collect in this data structure
// the actions that are declared as triggered so that if they appear
// again with the outputs, they are not defined a second time.
// That second redefinition would trigger a compile error.
Set<Action> actionsAsTriggers = new LinkedHashSet<>();
// defined so that they can be used in the verbatim code.
for (TriggerRef trigger : ASTUtils.convertToEmptyListIfNull(reaction.getTriggers())) {
if (trigger instanceof VarRef) {
VarRef triggerAsVarRef = (VarRef) trigger;
if (triggerAsVarRef.getVariable() instanceof Port) {
generatePortVariablesInReaction(reactionInitialization, fieldsForStructsForContainedReactors, triggerAsVarRef, decl, types);
} else if (triggerAsVarRef.getVariable() instanceof Action) {
reactionInitialization.pr(generateActionVariablesInReaction((Action) triggerAsVarRef.getVariable(), decl, types));
actionsAsTriggers.add((Action) triggerAsVarRef.getVariable());
}
}
}
if (reaction.getTriggers() == null || reaction.getTriggers().size() == 0) {
// NOTE: this does not include contained outputs.
for (Input input : reactor.getInputs()) {
reactionInitialization.pr(generateInputVariablesInReaction(input, decl, types));
}
}
// Define argument for non-triggering inputs.
for (VarRef src : ASTUtils.convertToEmptyListIfNull(reaction.getSources())) {
if (src.getVariable() instanceof Port) {
generatePortVariablesInReaction(reactionInitialization, fieldsForStructsForContainedReactors, src, decl, types);
} else if (src.getVariable() instanceof Action) {
// It's a bit odd to read but not be triggered by an action, but
// OK, I guess we allow it.
reactionInitialization.pr(generateActionVariablesInReaction((Action) src.getVariable(), decl, types));
actionsAsTriggers.add((Action) src.getVariable());
}
}
// value that may have been written to that output in an earlier reaction.
if (reaction.getEffects() != null) {
for (VarRef effect : reaction.getEffects()) {
Variable variable = effect.getVariable();
if (variable instanceof Action) {
// If it has already appeared as trigger, do not redefine it.
if (!actionsAsTriggers.contains(effect.getVariable())) {
reactionInitialization.pr(CGenerator.variableStructType(variable, decl) + "* " + variable.getName() + " = &self->_lf_" + variable.getName() + ";");
}
} else if (effect.getVariable() instanceof Mode) {
// Mode change effect
int idx = ASTUtils.allModes(reactor).indexOf((Mode) effect.getVariable());
String name = effect.getVariable().getName();
if (idx >= 0) {
reactionInitialization.pr("reactor_mode_t* " + name + " = &self->_lf__modes[" + idx + "];\n" + "char _lf_" + name + "_change_type = " + (ModeTransitionType.getModeTransitionType(effect) == ModeTransitionType.HISTORY ? 2 : 1) + ";");
} else {
errorReporter.reportError(reaction, "In generateReaction(): " + name + " not a valid mode of this reactor.");
}
} else {
if (variable instanceof Output) {
reactionInitialization.pr(generateOutputVariablesInReaction(effect, decl, errorReporter, requiresTypes));
} else if (variable instanceof Input) {
// It is the input of a contained reactor.
generateVariablesForSendingToContainedReactors(reactionInitialization, fieldsForStructsForContainedReactors, effect.getContainer(), (Input) variable);
} else {
errorReporter.reportError(reaction, "In generateReaction(): effect is neither an input nor an output.");
}
}
}
}
// generate the structs used for communication to and from contained reactors.
for (Instantiation containedReactor : fieldsForStructsForContainedReactors.keySet()) {
String array = "";
if (containedReactor.getWidthSpec() != null) {
String containedReactorWidthVar = generateWidthVariable(containedReactor.getName());
code.pr("int " + containedReactorWidthVar + " = self->_lf_" + containedReactorWidthVar + ";");
// Windows does not support variables in arrays declared on the stack,
// so we use the maximum size over all bank members.
array = "[" + maxContainedReactorBankWidth(containedReactor, null, 0, mainDef) + "]";
}
code.pr(String.join("\n", "struct " + containedReactor.getName() + " {", " " + fieldsForStructsForContainedReactors.get(containedReactor) + "", "} " + containedReactor.getName() + array + ";"));
}
// Next generate all the collected setup code.
code.pr(reactionInitialization.toString());
code.pr("#pragma GCC diagnostic pop");
if (reaction.getStp() == null) {
// Pass down the intended_tag to all input and output effects
// downstream if the current reaction does not have a STP
// handler.
code.pr(generateIntendedTagInheritence(body, reaction, decl, reactionIndex, types, isFederatedAndDecentralized));
}
return code.toString();
}
use of org.lflang.lf.Mode in project lingua-franca by lf-lang.
the class LFScopeProviderImpl method getScopeForVarRef.
protected IScope getScopeForVarRef(VarRef variable, EReference reference) {
if (reference == LfPackage.Literals.VAR_REF__VARIABLE) {
// Resolve hierarchical reference
Reactor reactor;
Mode mode = null;
if (variable.eContainer().eContainer() instanceof Reactor) {
reactor = (Reactor) variable.eContainer().eContainer();
} else if (variable.eContainer().eContainer() instanceof Mode) {
mode = (Mode) variable.eContainer().eContainer();
reactor = (Reactor) variable.eContainer().eContainer().eContainer();
} else {
return Scopes.scopeFor(emptyList());
}
RefType type = getRefType(variable);
if (variable.getContainer() != null) {
// Resolve hierarchical port reference
var instanceName = nameProvider.getFullyQualifiedName(variable.getContainer());
var instances = new ArrayList<Instantiation>(reactor.getInstantiations());
if (mode != null) {
instances.addAll(mode.getInstantiations());
}
if (instanceName != null) {
for (var instance : instances) {
var defn = toDefinition(instance.getReactorClass());
if (defn != null && instance.getName().equals(instanceName.toString())) {
switch(type) {
case TRIGGER:
case SOURCE:
case CLEFT:
return Scopes.scopeFor(allOutputs(defn));
case EFFECT:
case DEADLINE:
case CRIGHT:
return Scopes.scopeFor(allInputs(defn));
}
}
}
}
return Scopes.scopeFor(emptyList());
} else {
// Resolve local reference
switch(type) {
case TRIGGER:
{
var candidates = new ArrayList<EObject>();
if (mode != null) {
candidates.addAll(mode.getActions());
candidates.addAll(mode.getTimers());
}
candidates.addAll(allInputs(reactor));
candidates.addAll(allActions(reactor));
candidates.addAll(allTimers(reactor));
return Scopes.scopeFor(candidates);
}
case SOURCE:
return super.getScope(variable, reference);
case EFFECT:
{
var candidates = new ArrayList<EObject>();
if (mode != null) {
candidates.addAll(mode.getActions());
candidates.addAll(reactor.getModes());
}
candidates.addAll(allOutputs(reactor));
candidates.addAll(allActions(reactor));
return Scopes.scopeFor(candidates);
}
case DEADLINE:
case CLEFT:
return Scopes.scopeFor(allInputs(reactor));
case CRIGHT:
return Scopes.scopeFor(allOutputs(reactor));
default:
return Scopes.scopeFor(emptyList());
}
}
} else {
// Resolve instance
return super.getScope(variable, reference);
}
}
Aggregations