Search in sources :

Example 11 with Reactor

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);
    Value defaultValue = factory.createValue();
    defaultValue.setTime(defaultTime);
    delayParameter.getInit().add(defaultValue);
    // Name the newly created action; set its delay and type.
    action.setName("act");
    action.setMinDelay(factory.createValue());
    action.getMinDelay().setParameter(delayParameter);
    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;
}
Also used : VarRef(org.lflang.lf.VarRef) Action(org.lflang.lf.Action) Input(org.lflang.lf.Input) Output(org.lflang.lf.Output) Value(org.lflang.lf.Value) Parameter(org.lflang.lf.Parameter) Time(org.lflang.lf.Time) ImportedReactor(org.lflang.lf.ImportedReactor) Reactor(org.lflang.lf.Reactor) Reaction(org.lflang.lf.Reaction) TypeParm(org.lflang.lf.TypeParm)

Example 12 with Reactor

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;
}
Also used : VarRef(org.lflang.lf.VarRef) Connection(org.lflang.lf.Connection) ArrayList(java.util.ArrayList) ImportedReactor(org.lflang.lf.ImportedReactor) Reactor(org.lflang.lf.Reactor)

Example 13 with Reactor

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<Reactor>();
    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;
}
Also used : LinkedHashSet(java.util.LinkedHashSet) ImportedReactor(org.lflang.lf.ImportedReactor) Reactor(org.lflang.lf.Reactor) ReactorDecl(org.lflang.lf.ReactorDecl)

Example 14 with Reactor

use of org.lflang.lf.Reactor 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;
}
Also used : Code(org.lflang.lf.Code) LfPackage(org.lflang.lf.LfPackage) Delay(org.lflang.lf.Delay) WidthSpec(org.lflang.lf.WidthSpec) EStructuralFeature(org.eclipse.emf.ecore.EStructuralFeature) Action(org.lflang.lf.Action) Input(org.lflang.lf.Input) ILeafNode(org.eclipse.xtext.nodemodel.ILeafNode) StateVar(org.lflang.lf.StateVar) Matcher(java.util.regex.Matcher) HashMultimap(com.google.common.collect.HashMultimap) Port(org.lflang.lf.Port) Map(java.util.Map) Instantiation(org.lflang.lf.Instantiation) INode(org.eclipse.xtext.nodemodel.INode) Connection(org.lflang.lf.Connection) GeneratorBase(org.lflang.generator.GeneratorBase) Element(org.lflang.lf.Element) TypeParm(org.lflang.lf.TypeParm) Collection(java.util.Collection) CompositeNode(org.eclipse.xtext.nodemodel.impl.CompositeNode) Set(java.util.Set) InvalidSourceException(org.lflang.generator.InvalidSourceException) EObject(org.eclipse.emf.ecore.EObject) ICompositeNode(org.eclipse.xtext.nodemodel.ICompositeNode) ArraySpec(org.lflang.lf.ArraySpec) Mode(org.lflang.lf.Mode) Parameter(org.lflang.lf.Parameter) List(java.util.List) Value(org.lflang.lf.Value) CodeMap(org.lflang.generator.CodeMap) WidthTerm(org.lflang.lf.WidthTerm) Assignment(org.lflang.lf.Assignment) Resource(org.eclipse.emf.ecore.resource.Resource) ActionOrigin(org.lflang.lf.ActionOrigin) Pattern(java.util.regex.Pattern) StringExtensions(org.eclipse.xtext.xbase.lib.StringExtensions) Output(org.lflang.lf.Output) Variable(org.lflang.lf.Variable) Iterables(com.google.common.collect.Iterables) LfFactory(org.lflang.lf.LfFactory) NodeModelUtils(org.eclipse.xtext.nodemodel.util.NodeModelUtils) ImportedReactor(org.lflang.lf.ImportedReactor) Iterators(com.google.common.collect.Iterators) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) LinkedHashMap(java.util.LinkedHashMap) Pair(org.eclipse.xtext.util.Pair) TargetDecl(org.lflang.lf.TargetDecl) StringUtil(org.lflang.util.StringUtil) Reaction(org.lflang.lf.Reaction) KeyValuePair(org.lflang.lf.KeyValuePair) Type(org.lflang.lf.Type) LinkedHashSet(java.util.LinkedHashSet) Tuples(org.eclipse.xtext.util.Tuples) XtextResource(org.eclipse.xtext.resource.XtextResource) HiddenLeafNode(org.eclipse.xtext.nodemodel.impl.HiddenLeafNode) Model(org.lflang.lf.Model) ReactorDecl(org.lflang.lf.ReactorDecl) EcoreUtil(org.eclipse.emf.ecore.util.EcoreUtil) Time(org.lflang.lf.Time) EList(org.eclipse.emf.common.util.EList) IteratorExtensions(org.eclipse.xtext.xbase.lib.IteratorExtensions) IterableExtensions(org.eclipse.xtext.xbase.lib.IterableExtensions) TerminalRule(org.eclipse.xtext.TerminalRule) Reactor(org.lflang.lf.Reactor) VarRef(org.lflang.lf.VarRef) Timer(org.lflang.lf.Timer) EObject(org.eclipse.emf.ecore.EObject) Port(org.lflang.lf.Port) Mode(org.lflang.lf.Mode) Connection(org.lflang.lf.Connection) ImportedReactor(org.lflang.lf.ImportedReactor) Reactor(org.lflang.lf.Reactor) Reaction(org.lflang.lf.Reaction) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet) Pair(org.eclipse.xtext.util.Pair) KeyValuePair(org.lflang.lf.KeyValuePair)

Example 15 with Reactor

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());
}
Also used : PortInstance(org.lflang.generator.PortInstance) ReactorInstance(org.lflang.generator.ReactorInstance) Reactor(org.lflang.lf.Reactor) SendRange(org.lflang.generator.SendRange) Test(org.junit.jupiter.api.Test)

Aggregations

Reactor (org.lflang.lf.Reactor)54 ImportedReactor (org.lflang.lf.ImportedReactor)19 VarRef (org.lflang.lf.VarRef)19 ArrayList (java.util.ArrayList)16 Action (org.lflang.lf.Action)15 Instantiation (org.lflang.lf.Instantiation)14 Reaction (org.lflang.lf.Reaction)13 EObject (org.eclipse.emf.ecore.EObject)12 LinkedHashSet (java.util.LinkedHashSet)11 ReactorInstance (org.lflang.generator.ReactorInstance)9 Connection (org.lflang.lf.Connection)9 Input (org.lflang.lf.Input)9 LfFactory (org.lflang.lf.LfFactory)9 Output (org.lflang.lf.Output)9 Variable (org.lflang.lf.Variable)9 HashSet (java.util.HashSet)8 List (java.util.List)8 PortInstance (org.lflang.generator.PortInstance)8 Model (org.lflang.lf.Model)7 Port (org.lflang.lf.Port)7