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();
}
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();
}
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);
}
}
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;
}
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());
}
}
}
}
}
Aggregations