use of org.lflang.lf.Reactor in project lingua-franca by lf-lang.
the class PortInstanceTests method createRange.
@Test
public void createRange() throws Exception {
Reactor main = factory.createReactor();
ReactorInstance maini = new ReactorInstance(main, reporter);
ReactorInstance a = newReactor("A", maini);
ReactorInstance b = newReactor("B", maini);
ReactorInstance c = newReactor("C", maini);
PortInstance p = newOutputPort("p", a);
PortInstance q = newInputPort("q", b);
PortInstance r = newInputPort("r", c);
Assertions.assertEquals(".A.p", p.getFullName());
connect(p, q);
connect(p, r);
List<SendRange> sr = p.eventualDestinations();
// Destinations should be empty because there are no reactions.
Assertions.assertEquals("[]", sr.toString());
// Clear caches to make a mutation.
maini.clearCaches();
newReaction(q);
// Re-retrieve destinations.
sr = p.eventualDestinations();
Assertions.assertEquals("[.A.p(0,1)->[.B.q(0,1)]]", sr.toString());
maini.clearCaches();
newReaction(r);
// Re-retrieve destinations.
sr = p.eventualDestinations();
Assertions.assertEquals("[.A.p(0,1)->[.B.q(0,1), .C.r(0,1)]]", sr.toString());
// Now test multiports.
p.setWidth(3);
r.setWidth(2);
// Have to redo the connections.
clearConnections(maini);
maini.clearCaches();
connect(p, 0, 1, q, 0, 1);
connect(p, 1, 2, r, 0, 2);
// Re-retrieve destinations.
sr = p.eventualDestinations();
Assertions.assertEquals("[.A.p(0,1)->[.B.q(0,1)], .A.p(1,2)->[.C.r(0,2)]]", sr.toString());
// More complicated multiport connection.
clearConnections(maini);
maini.clearCaches();
ReactorInstance d = newReactor("D", maini);
PortInstance v = newOutputPort("v", d);
v.setWidth(2);
q.setWidth(3);
connect(v, 0, 2, q, 0, 2);
connect(p, 0, 1, q, 2, 1);
connect(p, 1, 2, r, 0, 2);
sr = p.eventualDestinations();
Assertions.assertEquals("[.A.p(0,1)->[.B.q(2,1)], .A.p(1,2)->[.C.r(0,2)]]", sr.toString());
// Additional multicast connection.
maini.clearCaches();
ReactorInstance e = newReactor("E", maini);
PortInstance s = newPort("s", e);
s.setWidth(3);
newReaction(s);
connect(p, s);
sr = p.eventualDestinations();
Assertions.assertEquals("[.A.p(0,1)->[.B.q(2,1), .E.s(0,1)], .A.p(1,2)->[.C.r(0,2), .E.s(1,2)]]", sr.toString());
// Add hierarchical reactors that further split the ranges.
maini.clearCaches();
ReactorInstance f = newReactor("F", e);
PortInstance t = newPort("t", f);
newReaction(t);
ReactorInstance g = newReactor("G", e);
PortInstance u = newPort("u", g);
u.setWidth(2);
newReaction(u);
connect(s, 0, 1, t, 0, 1);
connect(s, 1, 2, u, 0, 2);
sr = p.eventualDestinations();
// FIXME: Multicast destinations should be able to be reported in arbitrary order.
Assertions.assertEquals("[.A.p(0,1)->[.E.F.t(0,1), .E.s(0,1), .B.q(2,1)], .A.p(1,2)->[.E.G.u(0,2), .E.s(1,2), .C.r(0,2)]]", sr.toString());
}
use of org.lflang.lf.Reactor in project lingua-franca by lf-lang.
the class RangeTests method createRange.
@Test
public void createRange() throws Exception {
Reactor main = factory.createReactor();
ReactorInstance maini = new ReactorInstance(main, reporter);
Reactor a = factory.createReactor();
a.setName("A");
ReactorInstance ai = new ReactorInstance(a, maini, reporter);
ai.setWidth(2);
Reactor b = factory.createReactor();
b.setName("B");
ReactorInstance bi = new ReactorInstance(b, ai, reporter);
bi.setWidth(2);
Port p = factory.createPort();
p.setName("P");
PortInstance pi = new PortInstance(p, bi, reporter);
pi.setWidth(2);
Assertions.assertEquals(".A.B.P", pi.getFullName());
RuntimeRange<PortInstance> range = new RuntimeRange.Port(pi, 3, 4, null);
Assertions.assertEquals(8, range.maxWidth);
Assertions.assertEquals(".A.B.P(3,4)", range.toString());
// The results expected below are derived from the class comment for RuntimeRange,
// which includes this example.
List<Integer> instances = range.instances();
Assertions.assertEquals(List.of(3, 4, 5, 6), instances);
Set<Integer> parents = range.parentInstances(1);
Assertions.assertEquals(Set.of(1, 2, 3), parents);
parents = range.parentInstances(2);
Assertions.assertEquals(Set.of(0, 1), parents);
// Test startMR().getDigits.
Assertions.assertEquals(List.of(1, 1, 0), range.startMR().getDigits());
// Create a SendRange sending from and to this range.
SendRange sendRange = new SendRange(pi, 3, 4, null, null);
sendRange.destinations.add(range);
// Test getNumberOfDestinationReactors.
Assertions.assertEquals(3, sendRange.getNumberOfDestinationReactors());
// Make first interleaved version.
range = range.toggleInterleaved(bi);
instances = range.instances();
Assertions.assertEquals(List.of(3, 4, 6, 5), instances);
// Test startMR().getDigits.
Assertions.assertEquals(List.of(1, 1, 0), range.startMR().getDigits());
// Make second interleaved version.
range = range.toggleInterleaved(ai);
instances = range.instances();
Assertions.assertEquals(List.of(6, 1, 5, 3), instances);
// Test startMR().getDigits.
Assertions.assertEquals(List.of(0, 1, 1), range.startMR().getDigits());
// Test instances of the parent.
Assertions.assertEquals(Set.of(3, 0, 2, 1), range.parentInstances(1));
// Add this range to the sendRange destinations and verify
// that the number of destination reactors becomes 4.
sendRange.addDestination(range);
Assertions.assertEquals(4, sendRange.getNumberOfDestinationReactors());
// Make third interleaved version.
range = range.toggleInterleaved(bi);
instances = range.instances();
Assertions.assertEquals(List.of(5, 2, 6, 3), instances);
// Test startMR().getDigits.
Assertions.assertEquals(List.of(1, 0, 1), range.startMR().getDigits());
}
use of org.lflang.lf.Reactor in project lingua-franca by lf-lang.
the class ASTUtils method rerouteViaDelay.
/**
* Take a connection and reroute it via an instance of a generated delay
* reactor. This method returns a list to new connections to substitute
* the original one.
* @param connection The connection to reroute.
* @param delayInstance The delay instance to route the connection through.
*/
private static List<Connection> rerouteViaDelay(Connection connection, Instantiation delayInstance) {
List<Connection> connections = new ArrayList<>();
Connection upstream = factory.createConnection();
Connection downstream = factory.createConnection();
VarRef input = factory.createVarRef();
VarRef output = factory.createVarRef();
Reactor delayClass = toDefinition(delayInstance.getReactorClass());
// Establish references to the involved ports.
input.setContainer(delayInstance);
input.setVariable(delayClass.getInputs().get(0));
output.setContainer(delayInstance);
output.setVariable(delayClass.getOutputs().get(0));
upstream.getLeftPorts().addAll(connection.getLeftPorts());
upstream.getRightPorts().add(input);
downstream.getLeftPorts().add(output);
downstream.getRightPorts().addAll(connection.getRightPorts());
downstream.setIterated(connection.isIterated());
connections.add(upstream);
connections.add(downstream);
return connections;
}
use of org.lflang.lf.Reactor in project lingua-franca by lf-lang.
the class ASTUtils method superClasses.
/**
* Return all the superclasses of the specified reactor
* in deepest-first order. For example, if A extends B and C, and
* B and C both extend D, this will return the list [D, B, C, A].
* Duplicates are removed. If the specified reactor does not extend
* any other reactor, then return an empty list.
* If a cycle is found, where X extends Y and Y extends X, or if
* a superclass is declared that is not found, then return null.
* @param reactor The specified reactor.
* @param extensions A set of reactors extending the specified reactor
* (used to detect circular extensions).
*/
private static LinkedHashSet<Reactor> superClasses(Reactor reactor, Set<Reactor> extensions) {
LinkedHashSet<Reactor> result = new LinkedHashSet<>();
for (ReactorDecl superDecl : convertToEmptyListIfNull(reactor.getSuperClasses())) {
Reactor r = toDefinition(superDecl);
if (r == reactor || r == null)
return null;
// If r is in the extensions, then we have a circular inheritance structure.
if (extensions.contains(r))
return null;
extensions.add(r);
LinkedHashSet<Reactor> baseExtends = superClasses(r, extensions);
extensions.remove(r);
if (baseExtends == null)
return null;
result.addAll(baseExtends);
result.add(r);
}
return result;
}
use of org.lflang.lf.Reactor in project lingua-franca by lf-lang.
the class ASTUtils method getDelayClass.
/**
* Return a synthesized AST node that represents the definition of a delay
* reactor. Depending on whether the target supports generics, either this
* method will synthesize a generic definition and keep returning it upon
* subsequent calls, or otherwise, it will synthesize a new definition for
* each new type it hasn't yet created a compatible delay reactor for.
* @param type The type the delay class must be compatible with.
* @param generator A code generator.
*/
private static Reactor getDelayClass(Type type, GeneratorBase generator) {
String className;
if (generator.getTargetTypes().supportsGenerics()) {
className = GeneratorBase.GEN_DELAY_CLASS_NAME;
} else {
String id = Integer.toHexString(InferredType.fromAST(type).toText().hashCode());
className = String.format("%s_%s", GeneratorBase.GEN_DELAY_CLASS_NAME, id);
}
// Only add class definition if it is not already there.
Reactor classDef = generator.findDelayClass(className);
if (classDef != null) {
return classDef;
}
Reactor delayClass = factory.createReactor();
Parameter delayParameter = factory.createParameter();
Action action = factory.createAction();
VarRef triggerRef = factory.createVarRef();
VarRef effectRef = factory.createVarRef();
Input input = factory.createInput();
Output output = factory.createOutput();
VarRef inRef = factory.createVarRef();
VarRef outRef = factory.createVarRef();
Reaction r1 = factory.createReaction();
Reaction r2 = factory.createReaction();
delayParameter.setName("delay");
delayParameter.setType(factory.createType());
delayParameter.getType().setId("time");
delayParameter.getType().setTime(true);
Time defaultTime = factory.createTime();
defaultTime.setUnit(null);
defaultTime.setInterval(0);
delayParameter.getInit().add(defaultTime);
// Name the newly created action; set its delay and type.
action.setName("act");
var paramRef = factory.createParameterReference();
paramRef.setParameter(delayParameter);
action.setMinDelay(paramRef);
action.setOrigin(ActionOrigin.LOGICAL);
if (generator.getTargetTypes().supportsGenerics()) {
action.setType(factory.createType());
action.getType().setId("T");
} else {
action.setType(EcoreUtil.copy(type));
}
input.setName("inp");
input.setType(EcoreUtil.copy(action.getType()));
output.setName("out");
output.setType(EcoreUtil.copy(action.getType()));
// Establish references to the involved ports.
inRef.setVariable(input);
outRef.setVariable(output);
// Establish references to the action.
triggerRef.setVariable(action);
effectRef.setVariable(action);
// Add the action to the reactor.
delayClass.setName(className);
delayClass.getActions().add(action);
// Configure the second reaction, which reads the input.
r1.getTriggers().add(inRef);
r1.getEffects().add(effectRef);
r1.setCode(factory.createCode());
r1.getCode().setBody(generator.generateDelayBody(action, inRef));
// Configure the first reaction, which produces the output.
r2.getTriggers().add(triggerRef);
r2.getEffects().add(outRef);
r2.setCode(factory.createCode());
r2.getCode().setBody(generator.generateForwardBody(action, outRef));
// Add the reactions to the newly created reactor class.
// These need to go in the opposite order in case
// a new input arrives at the same time the delayed
// output is delivered!
delayClass.getReactions().add(r2);
delayClass.getReactions().add(r1);
// Add a type parameter if the target supports it.
if (generator.getTargetTypes().supportsGenerics()) {
TypeParm parm = factory.createTypeParm();
parm.setLiteral(generator.generateDelayGeneric());
delayClass.getTypeParms().add(parm);
}
delayClass.getInputs().add(input);
delayClass.getOutputs().add(output);
delayClass.getParameters().add(delayParameter);
generator.addDelayClass(delayClass);
return delayClass;
}
Aggregations