Search in sources :

Example 16 with Reactor

use of org.lflang.lf.Reactor in project lingua-franca by lf-lang.

the class PythonReactionGenerator method generateCPythonInitializers.

/**
 * Generate necessary Python-specific initialization code for <code>reaction<code> that belongs to reactor
 * <code>decl<code>.
 *
 * @param reaction The reaction to generate Python-specific initialization for.
 * @param decl The reactor to which <code>reaction<code> belongs to.
 * @param pyObjectDescriptor For each port object created, a Python-specific descriptor will be added to this that
 *  then can be used as an argument to <code>Py_BuildValue<code>
 *  (@see <a href="https://docs.python.org/3/c-api/arg.html#c.Py_BuildValue">docs.python.org/3/c-api</a>).
 * @param pyObjects A "," delimited list of expressions that would be (or result in a creation of) a PyObject.
 */
private static String generateCPythonInitializers(Reaction reaction, ReactorDecl decl, List<String> pyObjects, ErrorReporter errorReporter) {
    Set<Action> actionsAsTriggers = new LinkedHashSet<>();
    Reactor reactor = ASTUtils.toDefinition(decl);
    CodeBuilder code = new CodeBuilder();
    // TODO: handle triggers
    for (TriggerRef trigger : ASTUtils.convertToEmptyListIfNull(reaction.getTriggers())) {
        if (trigger instanceof VarRef) {
            VarRef triggerAsVarRef = (VarRef) trigger;
            code.pr(generateVariableToSendPythonReaction(triggerAsVarRef, actionsAsTriggers, decl, pyObjects));
        }
    }
    if (reaction.getTriggers() == null || reaction.getTriggers().size() == 0) {
        // NOTE: this does not include contained outputs.
        for (Input input : reactor.getInputs()) {
            PythonPortGenerator.generateInputVariablesToSendToPythonReaction(pyObjects, input, decl);
        }
    }
    // Next add non-triggering inputs.
    for (VarRef src : ASTUtils.convertToEmptyListIfNull(reaction.getSources())) {
        code.pr(generateVariableToSendPythonReaction(src, actionsAsTriggers, decl, pyObjects));
    }
    // Next, handle effects
    if (reaction.getEffects() != null) {
        for (VarRef effect : reaction.getEffects()) {
            if (effect.getVariable() instanceof Action) {
                // If it has already appeared as trigger, do not redefine it.
                if (!actionsAsTriggers.contains(effect.getVariable())) {
                    PythonPortGenerator.generateActionVariableToSendToPythonReaction(pyObjects, (Action) effect.getVariable(), decl);
                }
            } else {
                if (effect.getVariable() instanceof Output) {
                    PythonPortGenerator.generateOutputVariablesToSendToPythonReaction(pyObjects, (Output) effect.getVariable());
                } else if (effect.getVariable() instanceof Input) {
                    // It is the input of a contained reactor.
                    code.pr(PythonPortGenerator.generateVariablesForSendingToContainedReactors(pyObjects, effect.getContainer(), (Input) effect.getVariable()));
                } else {
                    errorReporter.reportError(reaction, "In generateReaction(): " + effect.getVariable().getName() + " is neither an input nor an output.");
                }
            }
        }
    }
    return code.toString();
}
Also used : LinkedHashSet(java.util.LinkedHashSet) VarRef(org.lflang.lf.VarRef) Action(org.lflang.lf.Action) Input(org.lflang.lf.Input) Output(org.lflang.lf.Output) Reactor(org.lflang.lf.Reactor) TriggerRef(org.lflang.lf.TriggerRef) CodeBuilder(org.lflang.generator.CodeBuilder)

Example 17 with Reactor

use of org.lflang.lf.Reactor in project lingua-franca by lf-lang.

the class PythonReactionGenerator method generateCPythonReactionLinkers.

/**
 * Generate Python code to link cpython functions to python functions for each reaction.
 * @param instance The reactor instance.
 * @param reactions The reactions of this instance.
 * @param mainDef The definition of the main reactor
 * @param topLevelName The name of the module
 */
public static String generateCPythonReactionLinkers(ReactorInstance instance, Instantiation mainDef, String topLevelName) {
    String nameOfSelfStruct = CUtil.reactorRef(instance);
    Reactor reactor = ASTUtils.toDefinition(instance.getDefinition().getReactorClass());
    CodeBuilder code = new CodeBuilder();
    // Delay reactors and top-level reactions used in the top-level reactor(s) in federated execution are generated in C
    if (reactor.getName().contains(GeneratorBase.GEN_DELAY_CLASS_NAME) || instance.getDefinition().getReactorClass() == (mainDef != null ? mainDef.getReactorClass() : null) && reactor.isFederated()) {
        return "";
    }
    // Initialize the name field to the unique name of the instance
    code.pr(nameOfSelfStruct + "->_lf_name = \"" + instance.uniqueID() + "_lf\";");
    for (ReactionInstance reaction : instance.reactions) {
        // Create a PyObject for each reaction
        code.pr(generateCPythonReactionLinker(instance, reaction, topLevelName, nameOfSelfStruct));
    }
    return code.toString();
}
Also used : ReactionInstance(org.lflang.generator.ReactionInstance) Reactor(org.lflang.lf.Reactor) CodeBuilder(org.lflang.generator.CodeBuilder)

Example 18 with Reactor

use of org.lflang.lf.Reactor in project lingua-franca by lf-lang.

the class PythonReactionGenerator method generatePythonReactionParametersAndInitializations.

/**
 * Generate parameters and their respective initialization code for a reaction function
 * The initialization code is put at the beginning of the reaction before user code
 * @param parameters The parameters used for function definition
 * @param inits The initialization code for those paramters
 * @param decl Reactor declaration
 * @param reaction The reaction to be used to generate parameters for
 */
public static void generatePythonReactionParametersAndInitializations(List<String> parameters, CodeBuilder inits, ReactorDecl decl, Reaction reaction) {
    Reactor reactor = ASTUtils.toDefinition(decl);
    LinkedHashSet<String> generatedParams = new LinkedHashSet<>();
    // Handle triggers
    for (TriggerRef trigger : ASTUtils.convertToEmptyListIfNull(reaction.getTriggers())) {
        if (!(trigger instanceof VarRef)) {
            continue;
        }
        VarRef triggerAsVarRef = (VarRef) trigger;
        if (triggerAsVarRef.getVariable() instanceof Port) {
            if (triggerAsVarRef.getVariable() instanceof Input) {
                if (((Input) triggerAsVarRef.getVariable()).isMutable()) {
                    generatedParams.add("mutable_" + triggerAsVarRef.getVariable().getName() + "");
                    // Create a deep copy
                    if (ASTUtils.isMultiport((Input) triggerAsVarRef.getVariable())) {
                        inits.pr(triggerAsVarRef.getVariable().getName() + " = [Make() for i in range(len(mutable_" + triggerAsVarRef.getVariable().getName() + "))]");
                        inits.pr("for i in range(len(mutable_" + triggerAsVarRef.getVariable().getName() + ")):");
                        inits.pr("    " + triggerAsVarRef.getVariable().getName() + "[i].value = copy.deepcopy(mutable_" + triggerAsVarRef.getVariable().getName() + "[i].value)");
                    } else {
                        inits.pr(triggerAsVarRef.getVariable().getName() + " = Make()");
                        inits.pr(triggerAsVarRef.getVariable().getName() + ".value = copy.deepcopy(mutable_" + triggerAsVarRef.getVariable().getName() + ".value)");
                    }
                } else {
                    generatedParams.add(triggerAsVarRef.getVariable().getName());
                }
            } else {
                // Handle contained reactors' ports
                generatedParams.add(triggerAsVarRef.getContainer().getName() + "_" + triggerAsVarRef.getVariable().getName());
                inits.pr(PythonPortGenerator.generatePythonPortVariableInReaction(triggerAsVarRef));
            }
        } else if (triggerAsVarRef.getVariable() instanceof Action) {
            generatedParams.add(triggerAsVarRef.getVariable().getName());
        }
    }
    // Handle non-triggering inputs
    if (reaction.getTriggers() == null || reaction.getTriggers().size() == 0) {
        for (Input input : ASTUtils.convertToEmptyListIfNull(reactor.getInputs())) {
            generatedParams.add(input.getName());
            if (input.isMutable()) {
                // Create a deep copy
                inits.pr(input.getName() + " = copy.deepcopy(" + input.getName() + ")");
            }
        }
    }
    for (VarRef src : ASTUtils.convertToEmptyListIfNull(reaction.getSources())) {
        if (src.getVariable() instanceof Output) {
            // Output of a contained reactor
            generatedParams.add(src.getContainer().getName() + "_" + src.getVariable().getName());
            inits.pr(PythonPortGenerator.generatePythonPortVariableInReaction(src));
        } else {
            generatedParams.add(src.getVariable().getName());
            if (src.getVariable() instanceof Input) {
                if (((Input) src.getVariable()).isMutable()) {
                    // Create a deep copy
                    inits.pr(src.getVariable().getName() + " = copy.deepcopy(" + src.getVariable().getName() + ")");
                }
            }
        }
    }
    // Handle effects
    for (VarRef effect : ASTUtils.convertToEmptyListIfNull(reaction.getEffects())) {
        if (effect.getVariable() instanceof Input) {
            generatedParams.add(effect.getContainer().getName() + "_" + effect.getVariable().getName());
            inits.pr(PythonPortGenerator.generatePythonPortVariableInReaction(effect));
        } else {
            generatedParams.add(effect.getVariable().getName());
            if (effect.getVariable() instanceof Port) {
                if (ASTUtils.isMultiport((Port) effect.getVariable())) {
                // Handle multiports
                }
            }
        }
    }
    for (String s : generatedParams) {
        parameters.add(s);
    }
}
Also used : LinkedHashSet(java.util.LinkedHashSet) VarRef(org.lflang.lf.VarRef) Input(org.lflang.lf.Input) Action(org.lflang.lf.Action) Port(org.lflang.lf.Port) Output(org.lflang.lf.Output) Reactor(org.lflang.lf.Reactor) TriggerRef(org.lflang.lf.TriggerRef)

Example 19 with Reactor

use of org.lflang.lf.Reactor in project lingua-franca by lf-lang.

the class CReactionGenerator method maxContainedReactorBankWidth.

/**
 * Return the maximum bank width for the given instantiation within all
 * instantiations of its parent reactor.
 * On the first call to this method, the breadcrumbs should be null and the max
 * argument should be zero. On recursive calls, breadcrumbs is a list of nested
 * instantiations, the max is the maximum width found so far.  The search for
 * instances of the parent reactor will begin with the last instantiation
 * in the specified list.
 *
 * This rather complicated method is used when a reaction sends or receives data
 * to or from a bank of contained reactors. There will be an array of structs on
 * the self struct of the parent, and the size of the array is conservatively set
 * to the maximum of all the identified bank widths.  This is a bit wasteful of
 * memory, but it avoids having to malloc the array for each instance, and in
 * typical usage, there will be few instances or instances that are all the same
 * width.
 *
 * @param containedReactor The contained reactor instantiation.
 * @param breadcrumbs null on first call (non-recursive).
 * @param max 0 on first call.
 */
public static int maxContainedReactorBankWidth(Instantiation containedReactor, LinkedList<Instantiation> breadcrumbs, int max, Instantiation mainDef) {
    // If the instantiation is not a bank, return 1.
    if (containedReactor.getWidthSpec() == null) {
        return 1;
    }
    // If there is no main, then we just use the default width.
    if (mainDef == null) {
        return ASTUtils.width(containedReactor.getWidthSpec(), null);
    }
    LinkedList<Instantiation> nestedBreadcrumbs = breadcrumbs;
    if (nestedBreadcrumbs == null) {
        nestedBreadcrumbs = new LinkedList<>();
        nestedBreadcrumbs.add(mainDef);
    }
    int result = max;
    Reactor parent = (Reactor) containedReactor.eContainer();
    if (parent == ASTUtils.toDefinition(mainDef.getReactorClass())) {
        // The parent is main, so there can't be any other instantiations of it.
        return ASTUtils.width(containedReactor.getWidthSpec(), null);
    }
    // Search for instances of the parent within the tail of the breadcrumbs list.
    Reactor container = ASTUtils.toDefinition(nestedBreadcrumbs.get(0).getReactorClass());
    for (Instantiation instantiation : container.getInstantiations()) {
        // Put this new instantiation at the head of the list.
        nestedBreadcrumbs.add(0, instantiation);
        if (ASTUtils.toDefinition(instantiation.getReactorClass()) == parent) {
            // Found a matching instantiation of the parent.
            // Evaluate the original width specification in this context.
            int candidate = ASTUtils.width(containedReactor.getWidthSpec(), nestedBreadcrumbs);
            if (candidate > result) {
                result = candidate;
            }
        } else {
            // Found some other instantiation, not the parent.
            // Search within it for instantiations of the parent.
            // Note that we assume here that the parent cannot contain
            // instances of itself.
            int candidate = maxContainedReactorBankWidth(containedReactor, nestedBreadcrumbs, result, mainDef);
            if (candidate > result) {
                result = candidate;
            }
        }
        nestedBreadcrumbs.remove();
    }
    return result;
}
Also used : Instantiation(org.lflang.lf.Instantiation) Reactor(org.lflang.lf.Reactor)

Example 20 with Reactor

use of org.lflang.lf.Reactor in project lingua-franca by lf-lang.

the class GeneratorUtils method validate.

/**
 * Validate the files containing reactors in the given
 * {@code instantiationGraph}. If a file is imported by
 * another file in the instantiation graph, propagate the
 * resulting errors to the importing file.
 * @param context The context providing the cancel
 *                indicator used by the validator.
 * @param fileConfig The file system configuration.
 * @param instantiationGraph A DAG containing all
 *                           reactors of interest.
 * @param errorReporter An error acceptor.
 */
public static void validate(IGeneratorContext context, FileConfig fileConfig, InstantiationGraph instantiationGraph, ErrorReporter errorReporter) {
    // NOTE: This method was previously misnamed validateImports.
    // It validates all files, including the main file that does the importing.
    // Also, it is now the only invocation of validation during code generation,
    // and yet it used to only report errors in the files doing the importing.
    IResourceValidator validator = ((XtextResource) fileConfig.resource).getResourceServiceProvider().getResourceValidator();
    HashSet<Resource> bad = new HashSet<>();
    HashSet<Resource> visited = new HashSet<>();
    // levels.
    for (Reactor reactor : instantiationGraph.nodesInTopologicalOrder()) {
        Resource resource = reactor.eResource();
        if (visited.contains(resource))
            continue;
        visited.add(resource);
        List<Issue> issues = validator.validate(resource, CheckMode.ALL, context.getCancelIndicator());
        if (bad.contains(resource) || issues.size() > 0) {
            // Report the error on this resource.
            Path path = null;
            try {
                path = FileUtil.toPath(resource);
            } catch (IOException e) {
                // Not sure if this is what we want.
                path = Paths.get("Unknown file");
            }
            for (Issue issue : issues) {
                errorReporter.reportError(path, issue.getLineNumber(), issue.getMessage());
            }
            // Report errors on resources that import this one.
            for (Reactor downstreamReactor : instantiationGraph.getDownstreamAdjacentNodes(reactor)) {
                for (Import importStatement : ((Model) downstreamReactor.eContainer()).getImports()) {
                    // FIXME: This will report the error on ALL import statements in
                    // file doing the importing, not just the one importing this resource.
                    // I have no idea how to determine which import statement is the right one.
                    errorReporter.reportError(importStatement, String.format("Unresolved compilation issues in '%s': " + issues.toString(), importStatement.getImportURI()));
                    bad.add(downstreamReactor.eResource());
                }
            }
        }
    }
}
Also used : Path(java.nio.file.Path) Issue(org.eclipse.xtext.validation.Issue) Import(org.lflang.lf.Import) IResourceValidator(org.eclipse.xtext.validation.IResourceValidator) XtextResource(org.eclipse.xtext.resource.XtextResource) IResource(org.eclipse.core.resources.IResource) Resource(org.eclipse.emf.ecore.resource.Resource) Model(org.lflang.lf.Model) IOException(java.io.IOException) Reactor(org.lflang.lf.Reactor) HashSet(java.util.HashSet)

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