use of org.lflang.lf.Parameter in project lingua-franca by lf-lang.
the class FedASTUtils method findMaxSTP.
/**
* Find the maximum STP offset for the given 'port'.
*
* An STP offset predicate can be nested in contained reactors in
* the federate.
* @param port The port to generate the STP list for.
* @param generator The GeneratorBase instance used to perform some target-specific actions
* @param reactor The top-level reactor (not the federate reactor)
* @return The maximum STP as a TimeValue
*/
private static TimeValue findMaxSTP(Variable port, FederateInstance instance, GeneratorBase generator, Reactor reactor) {
// Find a list of STP offsets (if any exists)
List<Value> STPList = new LinkedList<>();
// First, check if there are any connections to contained reactors that
// need to be handled
List<Connection> connectionsWithPort = ASTUtils.allConnections(reactor).stream().filter(c -> c.getLeftPorts().stream().anyMatch((VarRef v) -> v.getVariable().equals(port))).collect(Collectors.toList());
// Find the list of reactions that have the port as trigger or source
// (could be a variable name)
List<Reaction> reactionsWithPort = ASTUtils.allReactions(reactor).stream().filter(r -> {
// Check the triggers of reaction r first
return r.getTriggers().stream().anyMatch(t -> {
if (t instanceof VarRef) {
// Check if the variables match
return ((VarRef) t).getVariable() == port;
} else {
// Not a network port (startup or shutdown)
return false;
}
}) || // Then check the sources of reaction r
r.getSources().stream().anyMatch(s -> s.getVariable() == port);
}).collect(Collectors.toList());
// Find a list of STP offsets (if any exists)
if (generator.isFederatedAndDecentralized()) {
for (Reaction r : safe(reactionsWithPort)) {
if (!instance.contains(r)) {
continue;
}
// If not, assume it is zero
if (r.getStp() != null) {
if (r.getStp().getValue().getParameter() != null) {
List<Instantiation> instantList = new ArrayList<>();
instantList.add(instance.instantiation);
STPList.addAll(ASTUtils.initialValue(r.getStp().getValue().getParameter(), instantList));
} else {
STPList.add(r.getStp().getValue());
}
}
}
// Check the children for STPs as well
for (Connection c : safe(connectionsWithPort)) {
VarRef childPort = c.getRightPorts().get(0);
Reactor childReactor = (Reactor) childPort.getVariable().eContainer();
// Find the list of reactions that have the port as trigger or
// source (could be a variable name)
List<Reaction> childReactionsWithPort = ASTUtils.allReactions(childReactor).stream().filter(r -> r.getTriggers().stream().anyMatch(t -> {
if (t instanceof VarRef) {
// Check if the variables match
return ((VarRef) t).getVariable() == childPort.getVariable();
} else {
// Not a network port (startup or shutdown)
return false;
}
}) || r.getSources().stream().anyMatch(s -> s.getVariable() == childPort.getVariable())).collect(Collectors.toList());
for (Reaction r : safe(childReactionsWithPort)) {
if (!instance.contains(r)) {
continue;
}
// If not, assume it is zero
if (r.getStp() != null) {
if (r.getStp().getValue() instanceof Parameter) {
List<Instantiation> instantList = new ArrayList<>();
instantList.add(childPort.getContainer());
STPList.addAll(ASTUtils.initialValue(r.getStp().getValue().getParameter(), instantList));
} else {
STPList.add(r.getStp().getValue());
}
}
}
}
}
return STPList.stream().map(ASTUtils::getLiteralTimeValue).filter(Objects::nonNull).reduce(TimeValue.ZERO, TimeValue::max);
}
use of org.lflang.lf.Parameter in project lingua-franca by lf-lang.
the class ModelInfo method detectOverflow.
/**
* Given a parameter that is used in a deadline specification, recursively
* track down its definition and check whether it is overflowing. Also
* detect and report overrides that are overflowing.
*/
private boolean detectOverflow(Set<Instantiation> visited, Parameter current) {
var overflow = false;
// Determine whether the parameter's default value overflows or not.
if (isTooLarge(ASTUtils.getDefaultAsTimeValue(current))) {
this.overflowingParameters.add(current);
overflow = true;
}
// Iterate over the instantiations of the reactor in which the
// current parameter was found.
Set<Instantiation> instantiations = this.instantiationGraph.getInstantiations((Reactor) current.eContainer());
for (var instantiation : instantiations) {
// Only visit each instantiation once per deadline to avoid cycles.
if (!visited.contains(instantiation)) {
visited.add(instantiation);
// Find assignments that override the current parameter.
for (var assignment : instantiation.getParameters()) {
if (assignment.getLhs().equals(current)) {
Parameter parameter = assignment.getRhs().get(0).getParameter();
if (parameter != null) {
// Check for overflow in the referenced parameter.
overflow = detectOverflow(visited, parameter) || overflow;
} else {
// constant; check whether it is too large.
if (isTooLarge(ASTUtils.getLiteralTimeValue(assignment.getRhs().get(0)))) {
this.overflowingAssignments.add(assignment);
overflow = true;
}
}
}
}
}
}
return overflow;
}
use of org.lflang.lf.Parameter 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.Parameter in project lingua-franca by lf-lang.
the class PythonParameterGenerator method generatePythonInstantiations.
/**
* Generate Python code that instantiates and initializes parameters for a reactor 'decl'.
*
* @param decl The reactor declaration
* @return The generated code as a StringBuilder
*/
public static String generatePythonInstantiations(ReactorDecl decl, PythonTypes types) {
List<String> lines = new ArrayList<>();
lines.add("# Define parameters and their default values");
for (Parameter param : getAllParameters(decl)) {
if (!types.getTargetType(param).equals("PyObject*")) {
// If type is given, use it
String type = types.getPythonType(ASTUtils.getInferredType(param));
lines.add("self._" + param.getName() + ":" + type + " = " + generatePythonInitializer(param));
} else {
// If type is not given, just pass along the initialization
lines.add("self._" + param.getName() + " = " + generatePythonInitializer(param));
}
}
// Handle parameters that are set in instantiation
lines.addAll(List.of("# Handle parameters that are set in instantiation", "self.__dict__.update(kwargs)", ""));
return String.join("\n", lines);
}
use of org.lflang.lf.Parameter in project lingua-franca by lf-lang.
the class LinguaFrancaASTUtilsTest method initialValue.
/**
* Test reading initial values of parameters.
* This checks that the example given in the documentation of the
* ASTUtils.initialValue() function behaves as stated in the docs.
*/
@Test
public void initialValue() throws Exception {
// Java 17:
// Model model = parser.parse("""
// target C;
// reactor A(x:int(1)) {}
// reactor B(y:int(2)) {
// a1 = new A(x = y);
// a2 = new A(x = -1);
// }
// reactor C(z:int(3)) {
// b1 = new B(y = z);
// b2 = new B(y = -2);
// }
// """
// Java 11:
Model model = parser.parse(String.join(System.getProperty("line.separator"), "target C;", "reactor A(x:int(1)) {}", "reactor B(y:int(2)) {", " a1 = new A(x = y);", " a2 = new A(x = -1);", "}", "reactor C(z:int(3)) {", " b1 = new B(y = z);", " b2 = new B(y = -2);", "}"));
Assertions.assertNotNull(model);
Assertions.assertTrue(model.eResource().getErrors().isEmpty(), "Encountered unexpected error while parsing: " + model.eResource().getErrors());
var map = getInsts(model);
/* Check for this:
* initialValue(x, null) returns 1
* initialValue(x, [a1]) returns 2
* initialValue(x, [a2]) returns -1
* initialValue(x, [a1, b1]) returns 3
* initialValue(x, [a2, b1]) returns -1
* initialValue(x, [a1, b2]) returns -2
* initialValue(x, [a2, b2]) returns -1
*
* initialValue(y, null) returns 2
* initialValue(y, [a1]) throws an IllegalArgumentException
* initialValue(y, [b1]) returns 3
* initialValue(y, [b2]) returns -2
*/
model.eAllContents().forEachRemaining((obj) -> {
if (obj instanceof Parameter) {
Parameter parameter = (Parameter) obj;
if (parameter.getName() == "x") {
var values = ASTUtils.initialValue(parameter, null);
Assertions.assertEquals(values.get(0).getLiteral(), "1");
values = ASTUtils.initialValue(parameter, List.of(map.get("a1")));
Assertions.assertEquals(values.get(0).getLiteral(), "2");
values = ASTUtils.initialValue(parameter, List.of(map.get("a2")));
Assertions.assertEquals(values.get(0).getLiteral(), "-1");
values = ASTUtils.initialValue(parameter, List.of(map.get("a1"), map.get("b1")));
Assertions.assertEquals(values.get(0).getLiteral(), "3");
values = ASTUtils.initialValue(parameter, List.of(map.get("a2"), map.get("b1")));
Assertions.assertEquals(values.get(0).getLiteral(), "-1");
values = ASTUtils.initialValue(parameter, List.of(map.get("a1"), map.get("b2")));
Assertions.assertEquals(values.get(0).getLiteral(), "-2");
values = ASTUtils.initialValue(parameter, List.of(map.get("a2"), map.get("b2")));
Assertions.assertEquals(values.get(0).getLiteral(), "-1");
} else if (parameter.getName() == "y") {
var values = ASTUtils.initialValue(parameter, null);
Assertions.assertEquals(values.get(0).getLiteral(), "2");
try {
values = ASTUtils.initialValue(parameter, List.of(map.get("a1")));
} catch (IllegalArgumentException ex) {
Assertions.assertTrue(ex.getMessage().startsWith("Parameter y is not"));
}
values = ASTUtils.initialValue(parameter, List.of(map.get("b1")));
Assertions.assertEquals(values.get(0).getLiteral(), "3");
values = ASTUtils.initialValue(parameter, List.of(map.get("b2")));
Assertions.assertEquals(values.get(0).getLiteral(), "-2");
}
}
});
}
Aggregations