use of org.lflang.lf.Input 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;
}
use of org.lflang.lf.Input 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.Input 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.Input in project lingua-franca by lf-lang.
the class CReactionGenerator method generatePortVariablesInReaction.
/**
* Generate into the specified string builder the code to
* initialize local variables for ports in a reaction function
* from the "self" struct. The port may be an input of the
* reactor or an output of a contained reactor. The second
* argument provides, for each contained reactor, a place to
* write the declaration of the output of that reactor that
* is triggering reactions.
* @param builder The place into which to write the code.
* @param structs A map from reactor instantiations to a place to write
* struct fields.
* @param port The port.
* @param reactor The reactor or import statement.
*/
private static void generatePortVariablesInReaction(CodeBuilder builder, Map<Instantiation, CodeBuilder> structs, VarRef port, ReactorDecl decl, CTypes types) {
if (port.getVariable() instanceof Input) {
builder.pr(generateInputVariablesInReaction((Input) port.getVariable(), decl, types));
} else {
// port is an output of a contained reactor.
Output output = (Output) port.getVariable();
String portStructType = CGenerator.variableStructType(output, port.getContainer().getReactorClass()).toString();
CodeBuilder structBuilder = structs.get(port.getContainer());
if (structBuilder == null) {
structBuilder = new CodeBuilder();
structs.put(port.getContainer(), structBuilder);
}
String reactorName = port.getContainer().getName();
String reactorWidth = generateWidthVariable(reactorName);
String outputName = output.getName();
String outputWidth = generateWidthVariable(outputName);
// of its presence.
if (!ASTUtils.isMultiport(output)) {
// Output is not a multiport.
structBuilder.pr(portStructType + "* " + outputName + ";");
} else {
// Output is a multiport.
structBuilder.pr(String.join("\n", portStructType + "** " + outputName + ";", "int " + outputWidth + ";"));
}
// Next, initialize the struct with the current values.
if (port.getContainer().getWidthSpec() != null) {
// Output is in a bank.
builder.pr(String.join("\n", "for (int i = 0; i < " + reactorWidth + "; i++) {", " " + reactorName + "[i]." + outputName + " = self->_lf_" + reactorName + "[i]." + outputName + ";", "}"));
if (ASTUtils.isMultiport(output)) {
builder.pr(String.join("\n", "for (int i = 0; i < " + reactorWidth + "; i++) {", " " + reactorName + "[i]." + outputWidth + " = self->_lf_" + reactorName + "[i]." + outputWidth + ";", "}"));
}
} else {
// Output is not in a bank.
builder.pr(reactorName + "." + outputName + " = self->_lf_" + reactorName + "." + outputName + ";");
if (ASTUtils.isMultiport(output)) {
builder.pr(reactorName + "." + outputWidth + " = self->_lf_" + reactorName + "." + outputWidth + ";");
}
}
}
}
use of org.lflang.lf.Input in project lingua-franca by lf-lang.
the class FederateInstance method indexExcludedTopLevelReactions.
/**
* Build an index of reactions at the top-level (in the
* federatedReactor) that don't belong to this federate
* instance. This index is put in the excludeReactions
* class variable.
*
* @param federatedReactor The top-level federated reactor
*/
private void indexExcludedTopLevelReactions(Reactor federatedReactor) {
boolean inFederate = false;
if (excludeReactions != null) {
throw new IllegalStateException("The index for excluded reactions at the top level is already built.");
}
excludeReactions = new LinkedHashSet<Reaction>();
// Construct the set of excluded reactions for this federate.
// If a reaction is a network reaction that belongs to this federate, we
// don't need to perform this analysis.
Iterable<Reaction> reactions = IterableExtensions.filter(ASTUtils.allReactions(federatedReactor), it -> {
return !networkReactions.contains(it);
});
for (Reaction react : reactions) {
// Create a collection of all the VarRefs (i.e., triggers, sources, and effects) in the react
// signature that are ports that reference federates.
// We then later check that all these VarRefs reference this federate. If not, we will add this
// react to the list of reactions that have to be excluded (note that mixing VarRefs from
// different federates is not allowed).
List<VarRef> allVarRefsReferencingFederates = new ArrayList<VarRef>();
// Add all the triggers that are outputs
Stream<VarRef> triggersAsVarRef = react.getTriggers().stream().filter(it -> it instanceof VarRef).map(it -> (VarRef) it);
allVarRefsReferencingFederates.addAll(triggersAsVarRef.filter(it -> it.getVariable() instanceof Output).collect(Collectors.toList()));
// Add all the sources that are outputs
allVarRefsReferencingFederates.addAll(react.getSources().stream().filter(it -> it.getVariable() instanceof Output).collect(Collectors.toList()));
// Add all the effects that are inputs
allVarRefsReferencingFederates.addAll(react.getEffects().stream().filter(it -> it.getVariable() instanceof Input).collect(Collectors.toList()));
inFederate = containsAllVarRefs(allVarRefsReferencingFederates);
if (!inFederate) {
excludeReactions.add(react);
}
}
}
Aggregations