use of org.lflang.generator.NamedInstance in project lingua-franca by lf-lang.
the class CycleVisualization method detectAndHighlightCycles.
/**
* Performs cycle detection based on the diagram's graph structure and applies given highlighting to the included elements
*/
public boolean detectAndHighlightCycles(ReactorInstance rootReactorInstance, Map<ReactorInstance, KNode> allReactorNodes, Consumer<KGraphElement> highlighter) {
if (rootReactorInstance.hasCycles() && highlighter != null) {
// Highlight cycles
// A cycle consists of reactions and ports.
HashMultimap<ReactorInstance, NamedInstance<?>> cycleElementsByReactor = HashMultimap.create();
Set<NamedInstance<?>> cycles = rootReactorInstance.getCycles();
for (NamedInstance<?> element : cycles) {
// First find the involved reactor instances
if (element instanceof ReactorInstance) {
cycleElementsByReactor.put((ReactorInstance) element, element);
} else {
cycleElementsByReactor.put(element.getParent(), element);
}
}
for (ReactorInstance reactor : cycleElementsByReactor.keySet()) {
KNode node = allReactorNodes.get(reactor);
if (node != null) {
node.setProperty(DEPENDENCY_CYCLE, true);
highlighter.accept(node);
Set<NamedInstance<?>> reactorContentInCycle = cycleElementsByReactor.get(reactor);
// Reactor edges
for (KEdge edge : node.getOutgoingEdges()) {
if (connectsCycleElements(edge, cycles)) {
edge.setProperty(DEPENDENCY_CYCLE, true);
highlighter.accept(edge);
}
}
// Reactor ports
for (KPort port : node.getPorts()) {
if (reactorContentInCycle.contains(NamedInstanceUtil.getLinkedInstance(port))) {
port.setProperty(DEPENDENCY_CYCLE, true);
highlighter.accept(port);
}
}
// Child Nodes
for (KNode childNode : node.getChildren()) {
if (reactorContentInCycle.contains(NamedInstanceUtil.getLinkedInstance(childNode)) && !_utilityExtensions.sourceIsReactor(childNode)) {
childNode.setProperty(DEPENDENCY_CYCLE, true);
highlighter.accept(childNode);
for (KEdge edge : childNode.getOutgoingEdges()) {
if (connectsCycleElements(edge, cycles)) {
edge.setProperty(DEPENDENCY_CYCLE, true);
highlighter.accept(edge);
}
}
}
}
}
}
return true;
}
return false;
}
use of org.lflang.generator.NamedInstance in project lingua-franca by lf-lang.
the class LFValidator method checkConnection.
@Check(CheckType.FAST)
public void checkConnection(Connection connection) {
// Report if connection is part of a cycle.
Set<NamedInstance<?>> cycles = this.info.topologyCycles();
for (VarRef lp : connection.getLeftPorts()) {
for (VarRef rp : connection.getRightPorts()) {
boolean leftInCycle = false;
for (NamedInstance<?> it : cycles) {
if ((lp.getContainer() == null && it.getDefinition().equals(lp.getVariable())) || (it.getDefinition().equals(lp.getVariable()) && it.getParent().equals(lp.getContainer()))) {
leftInCycle = true;
break;
}
}
for (NamedInstance<?> it : cycles) {
if ((rp.getContainer() == null && it.getDefinition().equals(rp.getVariable())) || (it.getDefinition().equals(rp.getVariable()) && it.getParent().equals(rp.getContainer()))) {
if (leftInCycle) {
Reactor reactor = ASTUtils.getEnclosingReactor(connection);
String reactorName = reactor.getName();
error(String.format("Connection in reactor %s creates", reactorName) + String.format("a cyclic dependency between %s and %s.", toText(lp), toText(rp)), Literals.CONNECTION__DELAY);
}
}
}
}
}
// we leave type compatibility that language's compiler or interpreter.
if (isCBasedTarget()) {
Type type = (Type) null;
for (VarRef port : connection.getLeftPorts()) {
// error. Avoid a class cast exception.
if (port.getVariable() instanceof Port) {
if (type == null) {
type = ((Port) port.getVariable()).getType();
} else {
// method for AST types, so we have to manually check the types.
if (!sameType(type, ((Port) port.getVariable()).getType())) {
error("Types do not match.", Literals.CONNECTION__LEFT_PORTS);
}
}
}
}
for (VarRef port : connection.getRightPorts()) {
// error. Avoid a class cast exception.
if (port.getVariable() instanceof Port) {
if (type == null) {
type = ((Port) port.getVariable()).getType();
} else {
if (!sameType(type, type = ((Port) port.getVariable()).getType())) {
error("Types do not match.", Literals.CONNECTION__RIGHT_PORTS);
}
}
}
}
}
// Check whether the total width of the left side of the connection
// matches the total width of the right side. This cannot be determined
// here if the width is not given as a constant. In that case, it is up
// to the code generator to check it.
int leftWidth = 0;
for (VarRef port : connection.getLeftPorts()) {
// null args imply incomplete check.
int width = inferPortWidth(port, null, null);
if (width < 0 || leftWidth < 0) {
// Cannot determine the width of the left ports.
leftWidth = -1;
} else {
leftWidth += width;
}
}
int rightWidth = 0;
for (VarRef port : connection.getRightPorts()) {
// null args imply incomplete check.
int width = inferPortWidth(port, null, null);
if (width < 0 || rightWidth < 0) {
// Cannot determine the width of the left ports.
rightWidth = -1;
} else {
rightWidth += width;
}
}
if (leftWidth != -1 && rightWidth != -1 && leftWidth != rightWidth) {
if (connection.isIterated()) {
if (leftWidth == 0 || rightWidth % leftWidth != 0) {
// FIXME: The second argument should be Literals.CONNECTION, but
// stupidly, xtext will not accept that. There seems to be no way to
// report an error for the whole connection statement.
warning(String.format("Left width %s does not divide right width %s", leftWidth, rightWidth), Literals.CONNECTION__LEFT_PORTS);
}
} else {
// FIXME: The second argument should be Literals.CONNECTION, but
// stupidly, xtext will not accept that. There seems to be no way to
// report an error for the whole connection statement.
warning(String.format("Left width %s does not match right width %s", leftWidth, rightWidth), Literals.CONNECTION__LEFT_PORTS);
}
}
Reactor reactor = ASTUtils.getEnclosingReactor(connection);
// Make sure the right port is not already an effect of a reaction.
for (Reaction reaction : ASTUtils.allReactions(reactor)) {
for (VarRef effect : reaction.getEffects()) {
for (VarRef rightPort : connection.getRightPorts()) {
if (// Refers to the same variable
rightPort.getVariable().equals(effect.getVariable()) && // Refers to the same instance
rightPort.getContainer() == effect.getContainer() && (// Either is not part of a mode
reaction.eContainer() instanceof Reactor || connection.eContainer() instanceof Reactor || // Or they are in the same mode
connection.eContainer() == reaction.eContainer())) {
error("Cannot connect: Port named '" + effect.getVariable().getName() + "' is already effect of a reaction.", Literals.CONNECTION__RIGHT_PORTS);
}
}
}
}
// upstream connection.
for (Connection c : reactor.getConnections()) {
if (c != connection) {
for (VarRef thisRightPort : connection.getRightPorts()) {
for (VarRef thatRightPort : c.getRightPorts()) {
if (// Refers to the same variable
thisRightPort.getVariable().equals(thatRightPort.getVariable()) && // Refers to the same instance
thisRightPort.getContainer() == thatRightPort.getContainer() && (// Or either of the connections in not part of a mode
connection.eContainer() instanceof Reactor || c.eContainer() instanceof Reactor || // Or they are in the same mode
connection.eContainer() == c.eContainer())) {
error("Cannot connect: Port named '" + thisRightPort.getVariable().getName() + "' may only appear once on the right side of a connection.", Literals.CONNECTION__RIGHT_PORTS);
}
}
}
}
}
}
use of org.lflang.generator.NamedInstance in project lingua-franca by lf-lang.
the class LFValidator method checkReaction.
@Check(CheckType.FAST)
public void checkReaction(Reaction reaction) {
if (reaction.getTriggers() == null || reaction.getTriggers().size() == 0) {
warning("Reaction has no trigger.", Literals.REACTION__TRIGGERS);
}
HashSet<Variable> triggers = new HashSet<>();
// Make sure input triggers have no container and output sources do.
for (TriggerRef trigger : reaction.getTriggers()) {
if (trigger instanceof VarRef) {
VarRef triggerVarRef = (VarRef) trigger;
triggers.add(triggerVarRef.getVariable());
if (triggerVarRef instanceof Input) {
if (triggerVarRef.getContainer() != null) {
error(String.format("Cannot have an input of a contained reactor as a trigger: %s.%s", triggerVarRef.getContainer().getName(), triggerVarRef.getVariable().getName()), Literals.REACTION__TRIGGERS);
}
} else if (triggerVarRef.getVariable() instanceof Output) {
if (triggerVarRef.getContainer() == null) {
error(String.format("Cannot have an output of this reactor as a trigger: %s", triggerVarRef.getVariable().getName()), Literals.REACTION__TRIGGERS);
}
}
}
}
// Also check that a source is not already listed as a trigger.
for (VarRef source : reaction.getSources()) {
if (triggers.contains(source.getVariable())) {
error(String.format("Source is already listed as a trigger: %s", source.getVariable().getName()), Literals.REACTION__SOURCES);
}
if (source.getVariable() instanceof Input) {
if (source.getContainer() != null) {
error(String.format("Cannot have an input of a contained reactor as a source: %s.%s", source.getContainer().getName(), source.getVariable().getName()), Literals.REACTION__SOURCES);
}
} else if (source.getVariable() instanceof Output) {
if (source.getContainer() == null) {
error(String.format("Cannot have an output of this reactor as a source: %s", source.getVariable().getName()), Literals.REACTION__SOURCES);
}
}
}
// Make sure output effects have no container and input effects do.
for (VarRef effect : reaction.getEffects()) {
if (effect.getVariable() instanceof Input) {
if (effect.getContainer() == null) {
error(String.format("Cannot have an input of this reactor as an effect: %s", effect.getVariable().getName()), Literals.REACTION__EFFECTS);
}
} else if (effect.getVariable() instanceof Output) {
if (effect.getContainer() != null) {
error(String.format("Cannot have an output of a contained reactor as an effect: %s.%s", effect.getContainer().getName(), effect.getVariable().getName()), Literals.REACTION__EFFECTS);
}
}
}
// // Report error if this reaction is part of a cycle.
Set<NamedInstance<?>> cycles = this.info.topologyCycles();
Reactor reactor = ASTUtils.getEnclosingReactor(reaction);
boolean reactionInCycle = false;
for (NamedInstance<?> it : cycles) {
if (it.getDefinition().equals(reaction)) {
reactionInCycle = true;
}
}
if (reactionInCycle) {
// Report involved triggers.
List<CharSequence> trigs = new ArrayList<>();
for (TriggerRef t : reaction.getTriggers()) {
if (!(t instanceof VarRef)) {
continue;
}
VarRef tVarRef = (VarRef) t;
boolean triggerExistsInCycle = false;
for (NamedInstance<?> c : cycles) {
if (c.getDefinition().equals(tVarRef.getVariable())) {
triggerExistsInCycle = true;
break;
}
}
if (triggerExistsInCycle) {
trigs.add(toText(tVarRef));
}
}
if (trigs.size() > 0) {
error(String.format("Reaction triggers involved in cyclic dependency in reactor %s: %s.", reactor.getName(), String.join(", ", trigs)), Literals.REACTION__TRIGGERS);
}
// Report involved sources.
List<CharSequence> sources = new ArrayList<>();
for (VarRef t : reaction.getSources()) {
boolean sourceExistInCycle = false;
for (NamedInstance<?> c : cycles) {
if (c.getDefinition().equals(t.getVariable())) {
sourceExistInCycle = true;
break;
}
}
if (sourceExistInCycle) {
sources.add(toText(t));
}
}
if (sources.size() > 0) {
error(String.format("Reaction sources involved in cyclic dependency in reactor %s: %s.", reactor.getName(), String.join(", ", sources)), Literals.REACTION__SOURCES);
}
// Report involved effects.
List<CharSequence> effects = new ArrayList<>();
for (VarRef t : reaction.getEffects()) {
boolean effectExistInCycle = false;
for (NamedInstance<?> c : cycles) {
if (c.getDefinition().equals(t.getVariable())) {
effectExistInCycle = true;
break;
}
}
if (effectExistInCycle) {
effects.add(toText(t));
}
}
if (effects.size() > 0) {
error(String.format("Reaction effects involved in cyclic dependency in reactor %s: %s.", reactor.getName(), String.join(", ", effects)), Literals.REACTION__EFFECTS);
}
if (trigs.size() + sources.size() == 0) {
error(String.format("Cyclic dependency due to preceding reaction. Consider reordering reactions within reactor %s to avoid causality loop.", reactor.getName()), reaction.eContainer(), Literals.REACTOR__REACTIONS, reactor.getReactions().indexOf(reaction));
} else if (effects.size() == 0) {
error(String.format("Cyclic dependency due to succeeding reaction. Consider reordering reactions within reactor %s to avoid causality loop.", reactor.getName()), reaction.eContainer(), Literals.REACTOR__REACTIONS, reactor.getReactions().indexOf(reaction));
}
// Not reporting reactions that are part of cycle _only_ due to reaction ordering.
// Moving them won't help solve the problem.
}
// FIXME: improve error message.
}
Aggregations