().first()
@@ -371,7 +368,7 @@ val Action.isPhysical get() = this.origin == ActionOrigin.PHYSICAL
/**
* Return true if the receiving is a multiport.
*/
-val Port.isMultiport get() = JavaAstUtils.isMultiport(this)
+val Port.isMultiport get() = ASTUtils.isMultiport(this)
/** Get the reactor that is instantiated in the receiving instantiation. */
val Instantiation.reactor get() = this.reactorClass.toDefinition()
@@ -390,9 +387,6 @@ val Reaction.containingReactor get() = this.eContainer() as Reactor
/** Returns true if this is an input port (not an output port). */
val Port.isInput get() = this is Input
-val Assignment.isInitWithBraces get() = braces.isNotEmpty()
-val StateVar.isInitWithBraces get() = braces.isNotEmpty()
-val Parameter.isInitWithBraces get() = braces.isNotEmpty()
/**
* Produce the text of the given node in the source LF file.
diff --git a/org.lflang/src/org/lflang/InferredType.java b/org.lflang/src/org/lflang/InferredType.java
index 4a55519345..479380dced 100644
--- a/org.lflang/src/org/lflang/InferredType.java
+++ b/org.lflang/src/org/lflang/InferredType.java
@@ -177,4 +177,18 @@ public static InferredType timeList(Integer size) {
public static InferredType timeList() {
return timeList(null);
}
+
+ /**
+ * Returns the component type, if this is a first-class
+ * list type. Eg returns {@code time} for the type {@code time[]}.
+ * If this is not a first-class list type, returns null.
+ *
+ * Todo this does not return int for int[], we need to
+ * make the parser production left-recursive. OTOH only
+ * time and time[] are first class in our framework so
+ * this doesn't contradict the contract of this method.
+ */
+ public InferredType getComponentType() {
+ return isTime && isList ? time() : null;
+ }
}
diff --git a/org.lflang/src/org/lflang/JavaAstUtils.java b/org.lflang/src/org/lflang/JavaAstUtils.java
deleted file mode 100644
index 865915d743..0000000000
--- a/org.lflang/src/org/lflang/JavaAstUtils.java
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Copyright (c) 2021, TU Dresden.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
- * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
- * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.lflang;
-
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.lflang.lf.Action;
-import org.lflang.lf.Parameter;
-import org.lflang.lf.Port;
-import org.lflang.lf.StateVar;
-import org.lflang.lf.Time;
-import org.lflang.lf.Type;
-import org.lflang.lf.Value;
-import org.lflang.lf.VarRef;
-
-/**
- * Helper class to manipulate the LF AST. This is partly
- * converted from {@link ASTUtils}.
- */
-public final class JavaAstUtils {
- /* Match an abbreviated form of a float literal. */
- private static final Pattern ABBREVIATED_FLOAT = Pattern.compile("[+\\-]?\\.\\d+[\\deE+\\-]*");
-
- private JavaAstUtils() {
- // utility class
- }
-
- /**
- * Return the type of a declaration with the given
- * (nullable) explicit type, and the given (nullable)
- * initializer. If the explicit type is null, then the
- * type is inferred from the initializer. Only two types
- * can be inferred: "time" and "timeList". Return the
- * "undefined" type if neither can be inferred.
- *
- * @param type Explicit type declared on the declaration
- * @param initList A list of values used to initialize a parameter or
- * state variable.
- * @return The inferred type, or "undefined" if none could be inferred.
- */
- public static InferredType getInferredType(Type type, List initList) {
- if (type != null) {
- return InferredType.fromAST(type);
- } else if (initList == null) {
- return InferredType.undefined();
- }
-
- if (initList.size() == 1) {
- // If there is a single element in the list, and it is a proper
- // time value with units, we infer the type "time".
- Value init = initList.get(0);
- if (init.getParameter() != null) {
- return getInferredType(init.getParameter());
- } else if (ASTUtils.isValidTime(init) && !ASTUtils.isZero(init)) {
- return InferredType.time();
- }
- } else if (initList.size() > 1) {
- // If there are multiple elements in the list, and there is at
- // least one proper time value with units, and all other elements
- // are valid times (including zero without units), we infer the
- // type "time list".
- var allValidTime = true;
- var foundNonZero = false;
-
- for (var init : initList) {
- if (!ASTUtils.isValidTime(init)) {
- allValidTime = false;
- }
- if (!ASTUtils.isZero(init)) {
- foundNonZero = true;
- }
- }
-
- if (allValidTime && foundNonZero) {
- // Conservatively, no bounds are inferred; the returned type
- // is a variable-size list.
- return InferredType.timeList();
- }
- }
- return InferredType.undefined();
- }
-
- /**
- * Given a parameter, return an inferred type. Only two types can be
- * inferred: "time" and "timeList". Return the "undefined" type if
- * neither can be inferred.
- *
- * @param p A parameter to infer the type of.
- * @return The inferred type, or "undefined" if none could be inferred.
- */
- public static InferredType getInferredType(Parameter p) {
- return getInferredType(p.getType(), p.getInit());
- }
-
- /**
- * Given a state variable, return an inferred type. Only two types can be
- * inferred: "time" and "timeList". Return the "undefined" type if
- * neither can be inferred.
- *
- * @param s A state variable to infer the type of.
- * @return The inferred type, or "undefined" if none could be inferred.
- */
- public static InferredType getInferredType(StateVar s) {
- return getInferredType(s.getType(), s.getInit());
- }
-
- /**
- * Construct an inferred type from an "action" AST node based
- * on its declared type. If no type is declared, return the "undefined"
- * type.
- *
- * @param a An action to construct an inferred type object for.
- * @return The inferred type, or "undefined" if none was declared.
- */
- public static InferredType getInferredType(Action a) {
- return getInferredType(a.getType(), null);
- }
-
- /**
- * Construct an inferred type from a "port" AST node based on its declared
- * type. If no type is declared, return the "undefined" type.
- *
- * @param p A port to construct an inferred type object for.
- * @return The inferred type, or "undefined" if none was declared.
- */
- public static InferredType getInferredType(Port p) {
- return getInferredType(p.getType(), null);
- }
-
- /**
- * Returns the time value represented by the given AST node.
- */
- public static TimeValue toTimeValue(Time e) {
- if (!isValidTime(e)) {
- // invalid unit, will have been reported by validator
- throw new IllegalArgumentException();
- }
- return new TimeValue(e.getInterval(), TimeUnit.fromName(e.getUnit()));
- }
-
- /**
- * If the given string can be recognized as a floating-point number that has a leading decimal point,
- * prepend the string with a zero and return it. Otherwise, return the original string.
- *
- * @param literal A string might be recognizable as a floating point number with a leading decimal point.
- * @return an equivalent representation of literal
- *
- */
- public static String addZeroToLeadingDot(String literal) {
- Matcher m = ABBREVIATED_FLOAT.matcher(literal);
- if (m.matches()) {
- return literal.replace(".", "0.");
- }
- return literal;
- }
-
- /**
- * Return true if the specified port is a multiport.
- * @param port The port.
- * @return True if the port is a multiport.
- */
- public static boolean isMultiport(Port port) {
- return port.getWidthSpec() != null;
- }
-
- ////////////////////////////////
- //// Utility functions for translating AST nodes into text
- // This is a continuation of a large section of ASTUtils.xtend
- // with the same name.
-
- /**
- * Generate code for referencing a port, action, or timer.
- * @param reference The reference to the variable.
- */
- public static String generateVarRef(VarRef reference) {
- var prefix = "";
- if (reference.getContainer() != null) {
- prefix = reference.getContainer().getName() + ".";
- }
- return prefix + reference.getVariable().getName();
- }
-
- /**
- * Assuming that the given value denotes a valid time literal,
- * return a time value.
- */
- public static TimeValue getLiteralTimeValue(Value v) {;
- if (v.getTime() != null) {
- return toTimeValue(v.getTime());
- } else if (v.getLiteral() != null && v.getLiteral().equals("0")) {
- return TimeValue.ZERO;
- } else {
- return null;
- }
- }
-
- /**
- * If the parameter is of time type, return its default value.
- * Otherwise, return null.
- */
- public static TimeValue getDefaultAsTimeValue(Parameter p) {
- if (isOfTimeType(p)) {
- var init = p.getInit().get(0);
- if (init != null) {
- return getLiteralTimeValue(init);
- }
- }
- return null;
- }
-
- /**
- * Return whether the given state variable is inferred
- * to a time type.
- */
- public static boolean isOfTimeType(StateVar state) {
- InferredType t = getInferredType(state);
- return t.isTime && !t.isList;
- }
-
- /**
- * Return whether the given parameter is inferred
- * to a time type.
- */
- public static boolean isOfTimeType(Parameter param) {
- InferredType t = getInferredType(param);
- return t.isTime && !t.isList;
- }
-
- /**
- * Returns true if the argument denotes a valid time, false otherwise.
- *
- * @param t AST node to inspect (non-null).
- */
- public static boolean isValidTime(Time t) {
- return t != null && TimeUnit.isValidUnit(t.getUnit())
- && (t.getUnit() != null || t.getInterval() == 0);
- }
-}
diff --git a/org.lflang/src/org/lflang/LinguaFranca.xtext b/org.lflang/src/org/lflang/LinguaFranca.xtext
index 9b69e83675..94e77dd409 100644
--- a/org.lflang/src/org/lflang/LinguaFranca.xtext
+++ b/org.lflang/src/org/lflang/LinguaFranca.xtext
@@ -139,12 +139,13 @@ TargetDecl:
* must be given, or a literal or code that denotes zero.
*/
StateVar:
- 'state' name=ID (
- (':' (type=Type))?
- ((parens+='(' (init+=Value (',' init+=Value)*)? parens+=')')
- | (braces+='{' (init+=Value (',' init+=Value)*)? braces+='}')
- )?
- ) ';'?
+ 'state' name=ID (':' type=Type)? init=Initializer? ';'?
+;
+
+Initializer:
+ parens?='(' (exprs+=Expr (',' exprs+=Expr)* (trailingComma?=',')?)? ')'
+ | braces?='{' (exprs+=Expr (',' exprs+=Expr)* (trailingComma?=',')?)? '}'
+ | assign?='=' exprs+=Expr
;
Method:
@@ -170,11 +171,7 @@ Output:
// E.g. (0) or (NOW) or (NOW, ONCE) or (100, 1000)
// The latter means fire with period 1000, offset 100.
Timer:
- 'timer' name=ID ('(' offset=Value (',' period=Value)? ')')? ';'?;
-
-Boolean:
- TRUE | FALSE
-;
+ 'timer' name=ID ('(' offset=Expr (',' period=Expr)? ')')? ';'?;
// Action that has either a physical or logical origin.
//
@@ -188,7 +185,7 @@ Boolean:
// the tags of two subsequently scheduled events.
Action:
(origin=ActionOrigin)? 'action' name=ID
- ('(' minDelay=Value (',' minSpacing=Value (',' policy=STRING)? )? ')')?
+ ('(' minDelay=Expr (',' minSpacing=Expr (',' policy=STRING)? )? ')')?
(':' type=Type)? ';'?;
Reaction:
@@ -204,10 +201,10 @@ TriggerRef:
VarRef | startup?='startup' | shutdown?='shutdown';
Deadline:
- 'deadline' '(' delay=Value ')' code=Code;
+ 'deadline' '(' delay=Expr ')' code=Code;
STP:
- 'STP' '(' value=Value ')' code=Code;
+ 'STP' '(' value=Expr ')' code=Code;
Mutation:
('mutation')
@@ -235,12 +232,8 @@ Connection:
';'?
;
-// After clause with a delay that can either be specified as a parameter or as
-// a literal value. If no units are given (which implies that the time interval
-// must be zero), the clause must be ended with a semicolon. If units are given,
-// the semicolon is optional.
Delay:
- 'after' (parameter=[Parameter] | time=TypedTime)
+ 'after' value=Expr
;
// Chooses the serializer to use for the connection
@@ -285,47 +278,63 @@ VarRef:
;
Assignment:
- (lhs=[Parameter] (
- (equals='=' rhs+=Value)
- | ((equals='=')? (
- parens+='(' (rhs+=Value (',' rhs+=Value)*)? parens+=')'
- | braces+='{' (rhs+=Value (',' rhs+=Value)*)? braces+='}'))
- ));
+ lhs=[Parameter] rhs=Initializer;
/**
* Parameter declaration with optional type and mandatory initialization.
*/
Parameter:
- name=ID (':' (type=Type))?
- // FIXME: rename Value to Expr
- ((parens+='(' (init+=Value (',' init+=Value)*)? parens+=')')
- | (braces+='{' (init+=Value (',' init+=Value)*)? braces+='}')
- )?
-;
+ name=ID (':' type=Type)? init=Initializer?;
-Sum :
- terms+=Difference ('+' terms+=Difference)*;
-Difference:
- terms+=Product ('-' terms +=Product)*;
+// fake rule used to generate an interface that Sum, Product, Atom implement
+Value: Sum | Product | Atom;
-Product:
- terms+=Quotient ('*' terms+=Quotient)*;
+Expr returns Value: Sum;
-Quotient:
- terms+=Expr ('/' terms += Expr)*;
+Sum returns Value:
+ Product ({AddExpr.left=current} op=('+'|'-') right=Product)*
+;
-Expr :
- Value | '(' Sum ')';
+Product returns Value:
+ Atom ({MulExpr.left=current} op=('*'|'/') right=Atom)*
+;
-// Time is either a reference to a parameter or an integer value,
-// a number followed by a unit specification (unless the value is zero).
-// If it is a constant, the validator should check that if the value
-// is non-zero, it is accompanied by a unit.
-Value:
- (parameter=[Parameter] | time=Time | literal=Literal | code=Code);
+Atom returns Value:
+ ParamRef
+ | Time
+ | {Literal} literal=Literal
+ | BracketExpr
+ | BraceExpr
+ | TupleExpr
+ | {CodeExpr} code=Code
+;
+
+ParamRef: parameter=[Parameter];
+
+// This translates to a tuple in Python & Rust (at least).
+// If it's just a single expression without trailing comma,
+// it's equivalent to the wrapped expression (in all targets)
+// and no node is pushed.
+// (expr) = expr
+// (expr,) = tuple of 1 component
+// (expr,...,expr) = tuple of n component
+// () = tuple of zero components (in Rust and Caml, this is called "unit")
+TupleExpr returns Value:
+ '(' (
+ {TupleExpr} ')' // empty tuple
+ | Expr ( ')' // regular parenthesized expr
+ | {TupleExpr.items+=current} trailingComma?=',' ')'
+ | {TupleExpr.items+=current} (',' items+=Expr)+ trailingComma?=','? ')'
+ )
+ )
+;
+// Time is either a reference to a parameter or an integer value,
+// a number followed by a unit specification (unless the value is zero).
+// If it is a constant, the validator should check that if the value
+// is non-zero, it is accompanied by a unit.
Time:
(interval=INT unit=TimeUnit)
;
@@ -341,18 +350,20 @@ Port:
// A type is in the target language, hence either an ID or target code.
Type:
- time?='time' (arraySpec=ArraySpec)?
+ time?='time' (stars+='*')* (arraySpec=ArraySpec)?
| id=DottedName ('<' typeParms+=Type (',' typeParms+=Type)* '>')? (stars+='*')* (arraySpec=ArraySpec)?
| code=Code
;
ArraySpec:
- ofVariableLength?='[]' | '[' length=INT ']';
+ '[' ( ofVariableLength?=']' | length=INT ']' );
WidthSpec:
- ofVariableLength?='[]' | '[' (terms+=WidthTerm) ('+' terms+=WidthTerm)* ']';
-
-WidthTerm:
+ '[' ( ofVariableLength?=']' | (terms+=WidthTerm) ('+' terms+=WidthTerm)* ']' );
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ // todo replace this with just Expr.
+
+WidthTerm: // todo remove this.
width=INT
| parameter=[Parameter]
| 'widthof(' port=VarRef ')'
@@ -391,8 +402,25 @@ SignedInt:
INT | NEGINT
;
+// corresponds to a python list
+BracketExpr:
+ {BracketExpr} '[' ( items+=Expr (',' items+=Expr)* ','? )? ']'
+;
+
+// corresponds to an array initializer for C/C++. For Python,
+// we may improve this production to parse dictionaries. A "dictionary"
+// production would also help with Rust struct initializers.
+BraceExpr:
+ {BraceExpr} '{' ( items+=Expr (',' items+=Expr)* ','? )? '}'
+;
+
Literal:
- STRING | CHAR_LIT | SignedFloat | SignedInt | Boolean;
+ STRING | CHAR_LIT | SignedFloat | SignedInt | Boolean
+;
+
+Boolean:
+ TRUE | FALSE
+;
/////////// Elementary components
@@ -520,7 +548,7 @@ Token:
// Braces
'(' | ')' | '{' | '}' |
// Brackets
- '[' | ']' | '<' | '>' | '[]' |
+ '[' | ']' | '<' | '>' |
// Punctuation
':' | ';' | ',' | '.' | '::' |
// Slashes
diff --git a/org.lflang/src/org/lflang/ModelInfo.java b/org.lflang/src/org/lflang/ModelInfo.java
index d1748040a9..568ddacfe7 100644
--- a/org.lflang/src/org/lflang/ModelInfo.java
+++ b/org.lflang/src/org/lflang/ModelInfo.java
@@ -41,9 +41,11 @@
import org.lflang.lf.Deadline;
import org.lflang.lf.Instantiation;
import org.lflang.lf.Model;
+import org.lflang.lf.ParamRef;
import org.lflang.lf.Parameter;
import org.lflang.lf.Reactor;
import org.lflang.lf.STP;
+import org.lflang.lf.Value;
/**
@@ -79,7 +81,7 @@ public class ModelInfo {
* interval.
*/
public Set overflowingDeadlines;
-
+
/**
* The set of STP offsets that use a too-large constant to specify their time
* interval.
@@ -157,19 +159,21 @@ private void collectOverflowingNodes() {
// Visit all deadlines in the model; detect possible overflow.
for (var deadline : filter(toIterable(model.eAllContents()), Deadline.class)) {
// If the time value overflows, mark this deadline as overflowing.
- if (isTooLarge(JavaAstUtils.getLiteralTimeValue(deadline.getDelay()))) {
+ if (isTooLarge(ASTUtils.getLiteralTimeValue(deadline.getDelay()))) {
this.overflowingDeadlines.add(deadline);
}
- // If any of the upstream parameters overflow, report this deadline.
- if (detectOverflow(new HashSet<>(), deadline.getDelay().getParameter())) {
- this.overflowingDeadlines.add(deadline);
+ if (deadline.getDelay() instanceof ParamRef) {
+ // If any of the upstream parameters overflow, report this deadline.
+ if (detectOverflow(new HashSet<>(), ((ParamRef) deadline.getDelay()).getParameter())) {
+ this.overflowingDeadlines.add(deadline);
+ }
}
}
// Visit all STP offsets in the model; detect possible overflow.
for (var stp : filter(toIterable(model.eAllContents()), STP.class)) {
// If the time value overflows, mark this deadline as overflowing.
- if (isTooLarge(JavaAstUtils.getLiteralTimeValue(stp.getValue()))) {
+ if (isTooLarge(ASTUtils.getLiteralTimeValue(stp.getValue()))) {
this.overflowingSTP.add(stp);
}
}
@@ -196,7 +200,7 @@ private boolean detectOverflow(Set visited, Parameter current) {
var overflow = false;
// Determine whether the parameter's default value overflows or not.
- if (isTooLarge(JavaAstUtils.getDefaultAsTimeValue(current))) {
+ if (isTooLarge(ASTUtils.getDefaultAsTimeValue(current))) {
this.overflowingParameters.add(current);
overflow = true;
}
@@ -211,14 +215,14 @@ private boolean detectOverflow(Set visited, Parameter current) {
// 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) {
+ Value init = assignment.getRhs().getExprs().get(0);
+ if (init instanceof ParamRef) {
// Check for overflow in the referenced parameter.
- overflow = detectOverflow(visited, parameter) || overflow;
+ overflow = detectOverflow(visited, ((ParamRef) init).getParameter()) || overflow;
} else {
// The right-hand side of the assignment is a
// constant; check whether it is too large.
- if (isTooLarge(JavaAstUtils.getLiteralTimeValue(assignment.getRhs().get(0)))) {
+ if (isTooLarge(ASTUtils.getLiteralTimeValue(init))) {
this.overflowingAssignments.add(assignment);
overflow = true;
}
diff --git a/org.lflang/src/org/lflang/Target.java b/org.lflang/src/org/lflang/Target.java
index 13d0e2b11e..d13dc1006e 100644
--- a/org.lflang/src/org/lflang/Target.java
+++ b/org.lflang/src/org/lflang/Target.java
@@ -408,6 +408,43 @@ public enum Target {
this(displayName, requiresTypes, "N/A", "N/A", keywords);
}
+ /**
+ * Returns true if the target supports bracketed list literals in LF syntax.
+ * If not, they produce validator errors.
+ */
+ public boolean supportsLfBracketListExpressions() {
+ return this == Python;
+ }
+
+ /**
+ * Returns true if the target supports braced list literals in LF syntax.
+ * If not, they produce validator errors.
+ */
+ public boolean supportsLfBraceListExpressions() {
+ return this == C || this == CCPP || this == CPP;
+ }
+
+ /**
+ * Returns true if the target supports tuple literals in LF syntax.
+ * If not, they produce validator errors.
+ *
+ * Tuple literals use the production {@link org.lflang.lf.TupleExpr},
+ * they're only interpreted as a tuple if they match:
+ *
+ * The empty tuple: {@code ()}
+ * A one element tuple: eg {@code (expr,)} note that the trailing comma is required
+ * A 2+ element tuple: eg {@code ( expr, expr )} note that the trailing comma is NOT required but allowed.
+ *
+ *
+ * In other words, writing {@code (expr)} is never
+ * interpreted as a tuple, but always as equivalent to
+ * the contained {@code expr}. Hence, that syntax is allowed
+ * in all targets and this method cannot switch it off.
+ */
+ public boolean supportsLfTupleLiterals() {
+ return this == Python;
+ }
+
/**
* Return the target whose {@linkplain #getDisplayName() display name}
diff --git a/org.lflang/src/org/lflang/TimeValue.java b/org.lflang/src/org/lflang/TimeValue.java
index 0a2c205667..3775d09e64 100644
--- a/org.lflang/src/org/lflang/TimeValue.java
+++ b/org.lflang/src/org/lflang/TimeValue.java
@@ -42,7 +42,6 @@ public final class TimeValue implements Comparable {
*/
public static final TimeValue ZERO = new TimeValue(0, null);
-
/**
* Primitive numerical representation of this time value,
* to be interpreted in terms the associated time unit.
diff --git a/org.lflang/src/org/lflang/federated/CGeneratorExtension.java b/org.lflang/src/org/lflang/federated/CGeneratorExtension.java
index 64214f1231..264a764f73 100644
--- a/org.lflang/src/org/lflang/federated/CGeneratorExtension.java
+++ b/org.lflang/src/org/lflang/federated/CGeneratorExtension.java
@@ -27,7 +27,7 @@
package org.lflang.federated;
import org.lflang.ASTUtils;
-import org.lflang.JavaAstUtils;
+import org.lflang.ASTUtils;
import org.lflang.TimeValue;
import org.lflang.generator.ReactorInstance;
import org.lflang.generator.c.CGenerator;
@@ -196,7 +196,7 @@ public static String createPortStatusFieldForInput(Input input,
CGenerator generator) {
StringBuilder builder = new StringBuilder();
// Check if the port is a multiport
- if (JavaAstUtils.isMultiport(input)) {
+ if (ASTUtils.isMultiport(input)) {
// If it is a multiport, then create an auxiliary list of port
// triggers for each channel of
// the multiport to keep track of the status of each channel
@@ -210,41 +210,5 @@ public static String createPortStatusFieldForInput(Input input,
}
return builder.toString();
}
-
- /**
- * Given a connection 'delay' predicate, return a string that represents the
- * interval_t value of the additional delay that needs to be applied to the
- * outgoing message.
- *
- * The returned additional delay in absence of after on network connection
- * (i.e., if delay is passed as a null) is NEVER. This has a special
- * meaning in C library functions that send network messages that carry
- * timestamps (@see send_timed_message and send_port_absent_to_federate
- * in lib/core/federate.c). In this case, the sender will send its current
- * tag as the timestamp of the outgoing message without adding a microstep delay.
- * If the user has assigned an after delay to the network connection (that
- * can be zero) either as a time value (e.g., 200 msec) or as a literal
- * (e.g., a parameter), that delay in nsec will be returned.
- *
- * @param delay
- * @param generator
- * @return
- */
- public static String getNetworkDelayLiteral(Delay delay, CGenerator generator) {
- String additionalDelayString = "NEVER";
- if (delay != null) {
- Parameter p = delay.getParameter();
- TimeValue tv;
- if (delay.getParameter() != null) {
- // The parameter has to be parameter of the main reactor.
- // And that value has to be a Time.
- tv = JavaAstUtils.getDefaultAsTimeValue(p);
- } else {
- tv = JavaAstUtils.toTimeValue(delay.getTime());
- }
- additionalDelayString = Long.toString(tv.toNanoSeconds());
- }
- return additionalDelayString;
- }
}
diff --git a/org.lflang/src/org/lflang/federated/FedASTUtils.java b/org.lflang/src/org/lflang/federated/FedASTUtils.java
index 03c408eef8..28a605a9cc 100644
--- a/org.lflang/src/org/lflang/federated/FedASTUtils.java
+++ b/org.lflang/src/org/lflang/federated/FedASTUtils.java
@@ -1,7 +1,7 @@
/*************
* Copyright (c) 2021, The University of California at Berkeley.
* Copyright (c) 2021, The University of Texas at Dallas.
- *
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
@@ -27,7 +27,6 @@
package org.lflang.federated;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
@@ -36,20 +35,22 @@
import java.util.stream.Collectors;
import org.eclipse.emf.ecore.util.EcoreUtil;
+
import org.lflang.ASTUtils;
import org.lflang.InferredType;
-import org.lflang.JavaAstUtils;
+import org.lflang.ASTUtils;
import org.lflang.TargetProperty.CoordinationType;
import org.lflang.TimeValue;
import org.lflang.federated.serialization.SupportedSerializers;
import org.lflang.generator.GeneratorBase;
import org.lflang.generator.PortInstance;
+import org.lflang.generator.ReactorInstance;
import org.lflang.lf.Action;
import org.lflang.lf.ActionOrigin;
import org.lflang.lf.Connection;
import org.lflang.lf.Delay;
-import org.lflang.lf.Instantiation;
import org.lflang.lf.LfFactory;
+import org.lflang.lf.ParamRef;
import org.lflang.lf.Parameter;
import org.lflang.lf.Reaction;
import org.lflang.lf.Reactor;
@@ -78,7 +79,7 @@ public class FedASTUtils {
public static List safe(List list) {
return list == null ? Collections.emptyList() : list;
}
-
+
/**
* Create a "network action" in the reactor that contains the given
* connection and return it.
@@ -89,7 +90,7 @@ public static List safe(List list) {
* connection is assumed to be between two reactors that reside in
* distinct federates. Hence, the container of the connection is
* assumed to be top-level.
- *
+ *
* @param connection A connection between to federates.
* @param serializer The serializer used on the connection.
* @param type The type of the source port (indicating the type of
@@ -117,7 +118,7 @@ private static Action createNetworkAction(
action_type.setId(networkBufferType);
action.setType(action_type);
}
-
+
// The connection is 'physical' if it uses the ~> notation.
if (connection.isPhysical()) {
action.setOrigin(ActionOrigin.PHYSICAL);
@@ -126,13 +127,13 @@ private static Action createNetworkAction(
// provided using after is enforced by setting
// the minDelay.
if (connection.getDelay() != null) {
- action.setMinDelay(factory.createValue());
- action.getMinDelay().setTime(connection.getDelay().getTime());
+ // todo can't we clone a value?
+ action.setMinDelay(connection.getDelay().getValue());
}
} else {
action.setOrigin(ActionOrigin.LOGICAL);
}
-
+
return action;
}
@@ -143,9 +144,9 @@ private static Action createNetworkAction(
* 'networkAction' will contain the actual message that is sent by the sender
* in 'action->value'. This value is forwarded to 'destination' in the network
* receiver reaction.
- *
+ *
* @note: Used in federated execution
- *
+ *
* @param networkAction The network action (also, @see createNetworkAction)
* @param source The source port instance.
* @param destination The destination port instance.
@@ -162,7 +163,7 @@ private static void addNetworkReceiverReaction(
Action networkAction,
PortInstance source,
PortInstance destination,
- Connection connection,
+ Connection connection,
FederateInstance sourceFederate,
FederateInstance destinationFederate,
int rightBankIndex,
@@ -180,11 +181,11 @@ private static void addNetworkReceiverReaction(
// These reactions do not require any dependency relationship
// to other reactions in the container.
generator.makeUnordered(networkReceiverReaction);
-
+
// If the sender or receiver is in a bank of reactors, then we want
// these reactions to appear only in the federate whose bank ID matches.
generator.setReactionBankIndex(networkReceiverReaction, rightBankIndex);
-
+
// The connection is 'physical' if it uses the ~> notation.
if (connection.isPhysical()) {
destinationFederate.inboundP2PConnections.add(sourceFederate);
@@ -196,22 +197,22 @@ private static void addNetworkReceiverReaction(
destinationFederate.inboundP2PConnections.add(sourceFederate);
}
}
-
+
// Record this action in the right federate.
// The ID of the receiving port (rightPort) is the position
// of the action in this list.
int receivingPortID = destinationFederate.networkMessageActions.size();
-
+
// Establish references to the involved ports.
sourceRef.setContainer(source.getParent().getDefinition());
sourceRef.setVariable(source.getDefinition());
destRef.setContainer(destination.getParent().getDefinition());
destRef.setVariable(destination.getDefinition());
-
- if (!connection.isPhysical()) {
+
+ if (!connection.isPhysical()) {
// If the connection is not physical,
// add the original output port of the source federate
- // as a trigger to keep the overall dependency structure.
+ // as a trigger to keep the overall dependency structure.
// This is useful when assigning levels.
VarRef senderOutputPort = factory.createVarRef();
senderOutputPort.setContainer(source.getParent().getDefinition());
@@ -220,16 +221,16 @@ private static void addNetworkReceiverReaction(
// Add this trigger to the list of disconnected network reaction triggers
destinationFederate.remoteNetworkReactionTriggers.add(senderOutputPort);
}
-
+
// Add the input port at the receiver federate reactor as an effect
networkReceiverReaction.getEffects().add(destRef);
-
+
VarRef triggerRef = factory.createVarRef();
// Establish references to the action.
triggerRef.setVariable(networkAction);
// Add the action as a trigger to the receiver reaction
networkReceiverReaction.getTriggers().add(triggerRef);
-
+
// Generate code for the network receiver reaction
networkReceiverReaction.setCode(factory.createCode());
networkReceiverReaction.getCode().setBody(generator.generateNetworkReceiverBody(
@@ -241,19 +242,19 @@ private static void addNetworkReceiverReaction(
destinationFederate,
rightBankIndex,
rightChannelIndex,
- JavaAstUtils.getInferredType(networkAction),
+ ASTUtils.getInferredType(networkAction),
connection.isPhysical(),
serializer
));
-
+
// Add the receiver reaction to the parent
parent.getReactions().add(networkReceiverReaction);
-
+
// Add the network receiver reaction to the federate instance's list
// of network reactions
destinationFederate.networkReactions.add(networkReceiverReaction);
}
-
+
/**
* Add a network control reaction for a given input port 'destination' to
* destination's parent reactor. This reaction will block for
@@ -263,7 +264,7 @@ private static void addNetworkReceiverReaction(
* @note Used in federated execution
*
* @param source The output port of the source federate reactor.
- * Added as a trigger to the network control reaction to preserve the
+ * Added as a trigger to the network control reaction to preserve the
* overall dependency structure of the program across federates.
* @param destination The input port of the destination federate reactor.
* @param recevingPortID The ID of the receiving port
@@ -314,17 +315,17 @@ private static void addNetworkInputControlReaction(
// Add the appropriate triggers to the list of triggers of the reaction
reaction.getTriggers().add(newTriggerForControlReaction);
-
+
// Add the original output port of the source federate
- // as a trigger to keep the overall dependency structure.
+ // as a trigger to keep the overall dependency structure.
// This is useful when assigning levels.
reaction.getTriggers().add(sourceRef);
// Add this trigger to the list of disconnected network reaction triggers
instance.remoteNetworkReactionTriggers.add(sourceRef);
-
+
// Add the destination port as an effect of the reaction
reaction.getEffects().add(destRef);
-
+
// Generate code for the network input control reaction
reaction.setCode(factory.createCode());
@@ -347,7 +348,7 @@ private static void addNetworkInputControlReaction(
// Add the trigger for this reaction to the list of triggers, used to actually
// trigger the reaction at the beginning of each logical time.
instance.networkInputControlReactionsTriggers.add(newTriggerForControlReactionInput);
-
+
// Add the network input control reaction to the federate instance's list
// of network reactions
instance.networkReactions.add(reaction);
@@ -405,10 +406,9 @@ private static TimeValue findMaxSTP(Variable port,
// If STP offset is determined, add it
// If not, assume it is zero
if (r.getStp() != null) {
- if (r.getStp().getValue().getParameter() != null) {
- List instantList = new ArrayList<>();
- instantList.add(instance.instantiation);
- STPList.addAll(ASTUtils.initialValue(r.getStp().getValue().getParameter(), instantList));
+ if (r.getStp().getValue() instanceof ParamRef) {
+ Parameter parameter = ((ParamRef) r.getStp().getValue()).getParameter();
+ STPList.addAll(ASTUtils.initialValue(parameter, List.of(instance.instantiation)));
} else {
STPList.add(r.getStp().getValue());
}
@@ -442,10 +442,9 @@ private static TimeValue findMaxSTP(Variable port,
// If STP offset is determined, add it
// If not, assume it is zero
if (r.getStp() != null) {
- if (r.getStp().getValue() instanceof Parameter) {
- List instantList = new ArrayList<>();
- instantList.add(childPort.getContainer());
- STPList.addAll(ASTUtils.initialValue(r.getStp().getValue().getParameter(), instantList));
+ if (r.getStp().getValue() instanceof ParamRef) {
+ Parameter parameter = ((ParamRef) r.getStp().getValue()).getParameter();
+ STPList.addAll(ASTUtils.initialValue(parameter, List.of(childPort.getContainer())));
} else {
STPList.add(r.getStp().getValue());
}
@@ -455,18 +454,17 @@ private static TimeValue findMaxSTP(Variable port,
}
return STPList.stream()
- .map(JavaAstUtils::getLiteralTimeValue)
+ .map(ASTUtils::getLiteralTimeValue)
.filter(Objects::nonNull)
.reduce(TimeValue.ZERO, TimeValue::max);
}
-
/**
* Add a network sender reaction for a given input port 'source' to
* source's parent reactor. This reaction will react to the 'source'
* and then send a message on the network destined for the destinationFederate.
- *
+ *
* @note Used in federated execution
- *
+ *
* @param source The source port instance.
* @param destination The destination port instance.
* @param connection The network connection.
@@ -481,14 +479,15 @@ private static TimeValue findMaxSTP(Variable port,
private static void addNetworkSenderReaction(
PortInstance source,
PortInstance destination,
- Connection connection,
+ Connection connection,
FederateInstance sourceFederate,
int leftBankIndex,
int leftChannelIndex,
FederateInstance destinationFederate,
GeneratorBase generator,
CoordinationType coordination,
- SupportedSerializers serializer
+ SupportedSerializers serializer,
+ ReactorInstance mainInstance
) {
LfFactory factory = LfFactory.eINSTANCE;
// Assume all the types are the same, so just use the first on the right.
@@ -501,11 +500,11 @@ private static void addNetworkSenderReaction(
// These reactions do not require any dependency relationship
// to other reactions in the container.
generator.makeUnordered(networkSenderReaction);
-
+
// If the sender or receiver is in a bank of reactors, then we want
// these reactions to appear only in the federate whose bank ID matches.
generator.setReactionBankIndex(networkSenderReaction, leftBankIndex);
-
+
// The connection is 'physical' if it uses the ~> notation.
if (connection.isPhysical()) {
sourceFederate.outboundP2PConnections.add(destinationFederate);
@@ -517,7 +516,7 @@ private static void addNetworkSenderReaction(
sourceFederate.outboundP2PConnections.add(destinationFederate);
}
}
-
+
// Record this action in the right federate.
// The ID of the receiving port (rightPort) is the position
// of the action in this list.
@@ -529,8 +528,13 @@ private static void addNetworkSenderReaction(
sourceRef.setVariable(source.getDefinition());
destRef.setContainer(destination.getParent().getDefinition());
destRef.setVariable(destination.getDefinition());
-
+
// Configure the sending reaction.
+
+ Delay delay = connection.getDelay();
+ TimeValue delayValue = mainInstance.getTimeValue(delay == null ? null : delay.getValue());
+
+
networkSenderReaction.getTriggers().add(sourceRef);
networkSenderReaction.setCode(factory.createCode());
networkSenderReaction.getCode().setBody(generator.generateNetworkSenderBody(
@@ -543,20 +547,20 @@ private static void addNetworkSenderReaction(
destinationFederate,
InferredType.fromAST(type),
connection.isPhysical(),
- connection.getDelay(),
+ delayValue,
serializer
));
-
+
// Add the sending reaction to the parent.
parent.getReactions().add(networkSenderReaction);
-
+
// Add the network sender reaction to the federate instance's list
// of network reactions
sourceFederate.networkReactions.add(networkSenderReaction);
}
/**
- * Add a network control reaction for a given output port 'source' to
+ * Add a network control reaction for a given output port 'source' to
* source's parent reactor. This reaction will send a port absent
* message if the status of the output port is absent.
*
@@ -573,14 +577,14 @@ private static void addNetworkSenderReaction(
* @param delay The delay value imposed on the connection using after
*/
private static void addNetworkOutputControlReaction(
- PortInstance source,
- FederateInstance instance,
- int receivingPortID,
- int bankIndex,
- int channelIndex,
- int receivingFedID,
- GeneratorBase generator,
- Delay delay
+ PortInstance source,
+ FederateInstance instance,
+ int receivingPortID,
+ int bankIndex,
+ int channelIndex,
+ int receivingFedID,
+ GeneratorBase generator,
+ TimeValue delay
) {
LfFactory factory = LfFactory.eINSTANCE;
Reaction reaction = factory.createReaction();
@@ -608,7 +612,7 @@ private static void addNetworkOutputControlReaction(
// Find the trigger definition in the reactor definition, which could have been
// generated for another federate instance if there are multiple instances
// of the same reactor that are each distinct federates.
- Optional optTriggerInput
+ Optional optTriggerInput
= top.getActions().stream().filter(
I -> I.getName().equals(triggerName)).findFirst();
@@ -622,7 +626,7 @@ private static void addNetworkOutputControlReaction(
top.getActions().add(newTriggerForControlReactionVariable);
// Now that the variable is created, store it in the federate instance
- instance.networkOutputControlReactionsTrigger
+ instance.networkOutputControlReactionsTrigger
= newTriggerForControlReactionVariable;
} else {
// If the "outputControlReactionTrigger" trigger is already
@@ -651,7 +655,7 @@ private static void addNetworkOutputControlReaction(
// Insert the newly generated reaction after the generated sender and
// receiver top-level reactions.
top.getReactions().add(reaction);
-
+
// Add the network output control reaction to the federate instance's list
// of network reactions
instance.networkReactions.add(reaction);
@@ -672,18 +676,19 @@ private static void addNetworkOutputControlReaction(
* @param coordination One of CoordinationType.DECENTRALIZED or CoordinationType.CENTRALIZED.
*/
public static void makeCommunication(
- PortInstance source,
- PortInstance destination,
- Connection connection,
- FederateInstance sourceFederate,
- int leftBankIndex,
- int leftChannelIndex,
- FederateInstance destinationFederate,
- int rightBankIndex,
- int rightChannelIndex,
- GeneratorBase generator,
- CoordinationType coordination
- ) {
+ PortInstance source,
+ PortInstance destination,
+ Connection connection,
+ FederateInstance sourceFederate,
+ int leftBankIndex,
+ int leftChannelIndex,
+ FederateInstance destinationFederate,
+ int rightBankIndex,
+ int rightChannelIndex,
+ GeneratorBase generator,
+ CoordinationType coordination,
+ ReactorInstance mainInstance
+ ) {
// Get the serializer
var serializer = SupportedSerializers.NATIVE;
if (connection.getSerializer() != null) {
@@ -693,27 +698,31 @@ public static void makeCommunication(
}
// Add it to the list of enabled serializers
generator.enabledSerializers.add(serializer);
-
+
// Add the sender reaction.
addNetworkSenderReaction(
source,
destination,
- connection,
+ connection,
sourceFederate,
- leftBankIndex,
- leftChannelIndex,
+ leftBankIndex,
+ leftChannelIndex,
destinationFederate,
- generator,
- coordination,
- serializer
+ generator,
+ coordination,
+ serializer,
+ mainInstance
);
if (!connection.isPhysical()) {
-
+
// The ID of the receiving port (rightPort) is the position
// of the networkAction (see below) in this list.
int receivingPortID = destinationFederate.networkMessageActions.size();
-
+
+ Delay delay = connection.getDelay();
+ TimeValue delayValue = mainInstance.getTimeValue(delay == null ? null : delay.getValue());
+
// Add the network output control reaction to the parent
FedASTUtils.addNetworkOutputControlReaction(
source,
@@ -723,7 +732,7 @@ public static void makeCommunication(
leftChannelIndex,
destinationFederate.id,
generator,
- connection.getDelay()
+ delayValue
);
// Add the network input control reaction to the parent
@@ -753,16 +762,17 @@ public static void makeCommunication(
// Add the network receiver reaction in the destinationFederate
addNetworkReceiverReaction(
networkAction,
- source,
- destination,
- connection,
+ source,
+ destination,
+ connection,
sourceFederate,
destinationFederate,
- rightBankIndex,
- rightChannelIndex,
- generator,
+ rightBankIndex,
+ rightChannelIndex,
+ generator,
coordination,
serializer
);
}
+
}
diff --git a/org.lflang/src/org/lflang/federated/PythonGeneratorExtension.java b/org.lflang/src/org/lflang/federated/PythonGeneratorExtension.java
index 2d5b5117ab..9ae4150bab 100644
--- a/org.lflang/src/org/lflang/federated/PythonGeneratorExtension.java
+++ b/org.lflang/src/org/lflang/federated/PythonGeneratorExtension.java
@@ -27,8 +27,9 @@
package org.lflang.federated;
import org.lflang.InferredType;
-import org.lflang.JavaAstUtils;
+import org.lflang.ASTUtils;
import org.lflang.TargetProperty.CoordinationType;
+import org.lflang.TimeValue;
import org.lflang.federated.serialization.FedNativePythonSerialization;
import org.lflang.federated.serialization.FedSerialization;
import org.lflang.federated.serialization.SupportedSerializers;
@@ -41,7 +42,7 @@
/**
* An extension class to the PythonGenerator that enables certain federated
* functionalities.
- *
+ *
* @author Soroush Bateni {soroush@utdallas.edu}
*
*/
@@ -65,21 +66,21 @@ public class PythonGeneratorExtension {
public static String generateNetworkSenderBody(
VarRef sendingPort,
VarRef receivingPort,
- int receivingPortID,
+ int receivingPortID,
FederateInstance sendingFed,
int sendingBankIndex,
int sendingChannelIndex,
FederateInstance receivingFed,
InferredType type,
boolean isPhysical,
- Delay delay,
+ TimeValue delay,
SupportedSerializers serializer,
PythonGenerator generator
- ) {
+ ) {
String sendRef = CUtil.portRefInReaction(sendingPort, sendingBankIndex, sendingChannelIndex);
- String receiveRef = JavaAstUtils.generateVarRef(receivingPort); // Used for comments only, so no need for bank/multiport index.
+ String receiveRef = ASTUtils.generateVarRef(receivingPort); // Used for comments only, so no need for bank/multiport index.
StringBuilder result = new StringBuilder();
- result.append("// Sending from " + sendRef +
+ result.append("// Sending from " + sendRef +
" in federate " + sendingFed.name + " to " + receiveRef + " in federate " + receivingFed.name + "\n");
// If the connection is physical and the receiving federate is remote, send it directly on a socket.
// If the connection is logical and the coordination mode is centralized, send via RTI.
@@ -87,14 +88,7 @@ public static String generateNetworkSenderBody(
String messageType;
// Name of the next immediate destination of this message
String next_destination_name = "\"federate " + receivingFed.id + "\"";
-
- // Get the delay literal
- String additionalDelayString =
- CGeneratorExtension.getNetworkDelayLiteral(
- delay,
- generator
- );
-
+
if (isPhysical) {
messageType = "MSG_TYPE_P2P_MESSAGE";
} else if (generator.getTargetConfig().coordination == CoordinationType.DECENTRALIZED) {
@@ -105,23 +99,23 @@ public static String generateNetworkSenderBody(
messageType = "MSG_TYPE_TAGGED_MESSAGE";
next_destination_name = "\"federate " + receivingFed.id + " via the RTI\"";
}
-
-
+
+
String sendingFunction = "send_timed_message";
- String commonArgs = additionalDelayString + ", "
- + messageType + ", "
- + receivingPortID + ", "
- + receivingFed.id + ", "
- + next_destination_name + ", "
+ String commonArgs = getNetworkDelayLiteral(delay) + ", "
+ + messageType + ", "
+ + receivingPortID + ", "
+ + receivingFed.id + ", "
+ + next_destination_name + ", "
+ "message_length";
if (isPhysical) {
// Messages going on a physical connection do not
// carry a timestamp or require the delay;
- sendingFunction = "send_message";
+ sendingFunction = "send_message";
commonArgs = messageType + ", " + receivingPortID + ", " + receivingFed.id + ", "
+ next_destination_name + ", message_length";
}
-
+
var lengthExpression = "";
var pointerExpression = "";
switch (serializer) {
@@ -143,11 +137,11 @@ public static String generateNetworkSenderBody(
case ROS2: {
throw new UnsupportedOperationException("ROS2 serialization is not supported yet.");
}
-
+
}
return result.toString();
}
-
+
/**
* Generate code for the body of a reaction that handles the
* action that is triggered by receiving a message from a remote
@@ -169,7 +163,7 @@ public static String generateNetworkReceiverBody(
Action action,
VarRef sendingPort,
VarRef receivingPort,
- int receivingPortID,
+ int receivingPortID,
FederateInstance sendingFed,
FederateInstance receivingFed,
int receivingBankIndex,
@@ -182,11 +176,11 @@ public static String generateNetworkReceiverBody(
String receiveRef = CUtil.portRefInReaction(receivingPort, receivingBankIndex, receivingChannelIndex);
StringBuilder result = new StringBuilder();
-
+
// Transfer the physical time of arrival from the action to the port
result.append(receiveRef+"->physical_time_of_arrival = self->_lf__"+action.getName()+".physical_time_of_arrival;\n");
-
-
+
+
String value = "";
switch (serializer) {
case NATIVE: {
@@ -202,9 +196,22 @@ public static String generateNetworkReceiverBody(
case ROS2: {
throw new UnsupportedOperationException("ROS2 serialization is not supported yet.");
}
-
+
}
-
+
return result.toString();
}
+
+
+
+
+ /**
+ * Given a time value, returns the C code for the corresponding
+ * interval_t value.
+ *
+ * @param delay A time value
+ */
+ public static String getNetworkDelayLiteral(TimeValue delay) {
+ return delay != null ? Long.toString(delay.toNanoSeconds()) : "NEVER";
+ }
}
diff --git a/org.lflang/src/org/lflang/generator/DeadlineInstance.java b/org.lflang/src/org/lflang/generator/DeadlineInstance.java
index 6b18ef7f1a..ca70584d05 100644
--- a/org.lflang/src/org/lflang/generator/DeadlineInstance.java
+++ b/org.lflang/src/org/lflang/generator/DeadlineInstance.java
@@ -27,10 +27,8 @@
package org.lflang.generator;
-import org.lflang.JavaAstUtils;
import org.lflang.TimeValue;
import org.lflang.lf.Deadline;
-import org.lflang.lf.Parameter;
/**
* Instance of a deadline. Upon creation the actual delay is converted into
diff --git a/org.lflang/src/org/lflang/generator/GeneratorBase.xtend b/org.lflang/src/org/lflang/generator/GeneratorBase.xtend
index 849e11169b..7242f6b75a 100644
--- a/org.lflang/src/org/lflang/generator/GeneratorBase.xtend
+++ b/org.lflang/src/org/lflang/generator/GeneratorBase.xtend
@@ -324,7 +324,7 @@ abstract class GeneratorBase extends AbstractLFValidator {
// done here.
copyUserFiles(this.targetConfig, this.fileConfig);
- // Collect reactors and create an instantiation graph.
+ // Collect reactors and create an instantiation graph.
// These are needed to figure out which resources we need
// to validate, which happens in setResources().
setReactorsAndInstantiationGraph()
@@ -339,12 +339,12 @@ abstract class GeneratorBase extends AbstractLFValidator {
JavaGeneratorUtils.accommodatePhysicalActionsIfPresent(allResources, target, targetConfig, errorReporter);
// FIXME: Should the GeneratorBase pull in `files` from imported
// resources?
-
- // Reroute connections that have delays associated with them via
+
+ // Reroute connections that have delays associated with them via
// generated delay reactors.
transformDelays()
- // Invoke these functions a second time because transformations
+ // Invoke these functions a second time because transformations
// may have introduced new reactors!
setReactorsAndInstantiationGraph()
@@ -432,7 +432,7 @@ abstract class GeneratorBase extends AbstractLFValidator {
* Return the TargetTypes instance associated with this.
*/
abstract def TargetTypes getTargetTypes();
-
+
/**
* Generate code for the body of a reaction that takes an input and
* schedules an action with the value of that input.
@@ -523,28 +523,6 @@ abstract class GeneratorBase extends AbstractLFValidator {
if (reactionBankIndices.get(reaction) === null) return -1
return reactionBankIndices.get(reaction)
}
-
- /**
- * Given a representation of time that may possibly include units, return
- * a string that the target language can recognize as a value. In this base
- * class, if units are given, e.g. "msec", then we convert the units to upper
- * case and return an expression of the form "MSEC(value)". Particular target
- * generators will need to either define functions or macros for each possible
- * time unit or override this method to return something acceptable to the
- * target language.
- * @param time A TimeValue that represents a time.
- * @return A string, such as "MSEC(100)" for 100 milliseconds.
- */
- def String timeInTargetLanguage(TimeValue time) {
- if (time !== null) {
- if (time.unit !== null) {
- return time.unit.cMacroName + '(' + time.magnitude + ')'
- } else {
- return time.magnitude.toString()
- }
- }
- return "0" // FIXME: do this or throw exception?
- }
// note that this is moved out by #544
final def String cMacroName(TimeUnit unit) {
@@ -660,7 +638,7 @@ abstract class GeneratorBase extends AbstractLFValidator {
FederateInstance receivingFed,
InferredType type,
boolean isPhysical,
- Delay delay,
+ TimeValue delay,
SupportedSerializers serializer
) {
throw new UnsupportedOperationException("This target does not support network connections between federates.")
@@ -698,7 +676,7 @@ abstract class GeneratorBase extends AbstractLFValidator {
int receivingFederateID,
int sendingBankIndex,
int sendingChannelIndex,
- Delay delay
+ TimeValue delay
) {
throw new UnsupportedOperationException("This target does not support network connections between federates.")
}
@@ -920,7 +898,7 @@ abstract class GeneratorBase extends AbstractLFValidator {
}
}
- /**
+ /**
* Set the RTI hostname, port and username if given as compiler arguments
*/
private def setFederationRTIProperties(LFGeneratorContext context) {
@@ -949,7 +927,7 @@ abstract class GeneratorBase extends AbstractLFValidator {
}
}
- /**
+ /**
* Analyze the AST to determine whether code is being mapped to
* single or to multiple target machines. If it is being mapped
* to multiple machines, then set the {@link #isFederated} field to true,
@@ -1105,12 +1083,12 @@ abstract class GeneratorBase extends AbstractLFValidator {
) {
for (srcRange : output.dependentPorts) {
for (RuntimeRange dstRange : srcRange.destinations) {
-
+
var srcID = srcRange.startMR();
val dstID = dstRange.startMR();
var dstCount = 0;
var srcCount = 0;
-
+
while (dstCount++ < dstRange.width) {
val srcChannel = srcID.digits.get(0);
val srcBank = srcID.get(1);
@@ -1123,16 +1101,16 @@ abstract class GeneratorBase extends AbstractLFValidator {
val dstFederate = federatesByInstantiation.get(
dstRange.instance.parent.definition
).get(dstBank);
-
+
val connection = srcRange.connection;
-
+
if (connection === null) {
// This should not happen.
- errorReporter.reportError(output.definition,
+ errorReporter.reportError(output.definition,
"Unexpected error. Cannot find output connection for port")
} else {
if (srcFederate !== dstFederate
- && !connection.physical
+ && !connection.physical
&& targetConfig.coordination !== CoordinationType.DECENTRALIZED) {
// Map the delays on connections between federates.
// First see if the cache has been created.
@@ -1162,7 +1140,7 @@ abstract class GeneratorBase extends AbstractLFValidator {
sendsToDelays.add(null)
}
}
-
+
FedASTUtils.makeCommunication(
srcRange.instance,
dstRange.instance,
@@ -1174,9 +1152,10 @@ abstract class GeneratorBase extends AbstractLFValidator {
dstBank,
dstChannel,
this,
- targetConfig.coordination
- );
- }
+ targetConfig.coordination,
+ mainInstance
+ );
+ }
dstID.increment();
srcID.increment();
srcCount++;
@@ -1229,23 +1208,14 @@ abstract class GeneratorBase extends AbstractLFValidator {
* @return A time string in the target language
*/
protected def getTargetTime(Time t) {
- val value = new TimeValue(t.interval, TimeUnit.fromName(t.unit))
- return value.timeInTargetLanguage
+ return targetTypes.getTargetTimeExpr(t)
}
/**
- * Get textual representation of a value in the target language.
- *
- * If the value evaluates to 0, it is interpreted as a normal value.
- *
- * @param v A time AST node
- * @return A time string in the target language
+ * Returns the textual representation of a value in the target language.
*/
protected def getTargetValue(Value v) {
- if (v.time !== null) {
- return v.time.targetTime
- }
- return v.toText
+ return targetTypes.getTargetExpr(v, null)
}
/**
@@ -1257,20 +1227,11 @@ abstract class GeneratorBase extends AbstractLFValidator {
* @return A time string in the target language
*/
protected def getTargetTime(Value v) {
- if (v.time !== null) {
- return v.time.targetTime
+ if (v instanceof Time) {
+ return targetTypes.getTargetTimeExpr(v)
} else if (v.isZero) {
- val value = TimeValue.ZERO
- return value.timeInTargetLanguage
+ return targetTypes.getTargetTimeExpr(TimeValue.ZERO)
}
return v.toText
}
-
- protected def getTargetTime(Delay d) {
- if (d.parameter !== null) {
- return d.toText
- } else {
- return d.time.targetTime
- }
- }
}
diff --git a/org.lflang/src/org/lflang/generator/GeneratorUtils.kt b/org.lflang/src/org/lflang/generator/GeneratorUtils.kt
index 1a1cfb9f68..05a4c5f2c5 100644
--- a/org.lflang/src/org/lflang/generator/GeneratorUtils.kt
+++ b/org.lflang/src/org/lflang/generator/GeneratorUtils.kt
@@ -2,6 +2,8 @@ package org.lflang.generator
import org.eclipse.emf.ecore.EObject
import org.eclipse.xtext.nodemodel.util.NodeModelUtils
+import org.lflang.*
+import org.lflang.lf.*
import org.lflang.ErrorReporter
import org.lflang.toPath
import org.lflang.toUnixString
@@ -9,6 +11,40 @@ import org.lflang.toTextTokenBased
import org.lflang.lf.Instantiation
+fun TargetTypes.getTargetInitializer(sv: StateVar): TargetCode =
+ this.getTargetInitializer(sv.init, sv.type)
+
+fun TargetTypes.getTargetInitializer(sv: Parameter): TargetCode =
+ this.getTargetInitializer(sv.init, sv.type)
+
+/**
+ * Returns the target code for the [getActualValue] of the
+ * param for this instantiation.
+ */
+fun TargetTypes.getTargetInitializer(param: Parameter, inst: Instantiation): TargetCode {
+ val init = inst.getActualValue(param)
+ return getTargetInitializer(init, param.inferredType)
+}
+
+/**
+ * Returns the actual value of a parameter for the given instantiation.
+ * The value is defaulted to the default value for the parameter if
+ * there is no explicit assignment. If there is no default value, the
+ * source code is invalid (param is required)
+ */
+fun Instantiation.getActualValue(param: Parameter): Initializer =
+ parameters.firstOrNull { it.lhs == param }?.rhs
+ ?: param.init
+ ?: throw InvalidLfSourceException(this, "No value for parameter ${param.name}")
+
+
+fun TargetTypes.getTargetTimeExpr(v: Value): TargetCode =
+ this.getTargetExpr(v, InferredType.time())
+
+/** If this is null, returns the literal 0. */
+fun Value?.orZero(): Value =
+ this ?: LfFactory.eINSTANCE.createLiteral().apply { literal = "0" }
+
/** A transparent type alias to document when a string contains target code. */
typealias TargetCode = String
diff --git a/org.lflang/src/org/lflang/generator/ParameterInstance.java b/org.lflang/src/org/lflang/generator/ParameterInstance.java
index ee2d044e6e..4f57e1363b 100644
--- a/org.lflang/src/org/lflang/generator/ParameterInstance.java
+++ b/org.lflang/src/org/lflang/generator/ParameterInstance.java
@@ -31,8 +31,9 @@
import java.util.Optional;
import org.lflang.InferredType;
-import org.lflang.JavaAstUtils;
+import org.lflang.ASTUtils;
import org.lflang.lf.Assignment;
+import org.lflang.lf.Initializer;
import org.lflang.lf.Parameter;
import org.lflang.lf.Value;
@@ -59,13 +60,13 @@ public ParameterInstance(Parameter definition, ReactorInstance parent) {
throw new InvalidSourceException("Cannot create a ParameterInstance with no parent.");
}
- this.type = JavaAstUtils.getInferredType(definition);
+ this.type = ASTUtils.getInferredType(definition);
}
/////////////////////////////////////////////
//// Public Fields
- public InferredType type;
+ public final InferredType type;
/////////////////////////////////////////////
//// Public Methods
@@ -79,6 +80,16 @@ public ParameterInstance(Parameter definition, ReactorInstance parent) {
public List getInitialValue() {
return parent.initialParameterValue(this.definition);
}
+
+ /**
+ * Return a view of the initial value as an initializer.
+ * TODO there might be problems with it, it is used for
+ * compatibility with the C generator. Most other generators
+ * do not use the instance tree anyway (C++, Rust, TS)
+ */
+ public Initializer getInit() {
+ return ASTUtils.listAsInitializer(getInitialValue());
+ }
/**
* Return the name of this parameter.
diff --git a/org.lflang/src/org/lflang/generator/ReactorInstance.java b/org.lflang/src/org/lflang/generator/ReactorInstance.java
index 09cc8c2c96..6ab76844b1 100644
--- a/org.lflang/src/org/lflang/generator/ReactorInstance.java
+++ b/org.lflang/src/org/lflang/generator/ReactorInstance.java
@@ -35,15 +35,15 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
import org.lflang.ASTUtils;
import org.lflang.ErrorReporter;
-import org.lflang.JavaAstUtils;
+import org.lflang.ASTUtils;
import org.lflang.TimeValue;
import org.lflang.generator.TriggerInstance.BuiltinTriggerVariable;
import org.lflang.lf.Action;
import org.lflang.lf.Connection;
-import org.lflang.lf.Delay;
import org.lflang.lf.Input;
import org.lflang.lf.Instantiation;
import org.lflang.lf.Output;
+import org.lflang.lf.ParamRef;
import org.lflang.lf.Parameter;
import org.lflang.lf.Port;
import org.lflang.lf.Reaction;
@@ -628,29 +628,18 @@ public String toString() {
* precise time value assigned to this reactor instance.
*/
public TimeValue getTimeValue(Value v) {
- Parameter p = v.getParameter();
- if (p != null) {
- return JavaAstUtils.getLiteralTimeValue(lookupParameterInstance(p).getInitialValue().get(0));
+ if (v instanceof ParamRef) {
+ ParameterInstance instance = lookupParameterInstance(((ParamRef) v).getParameter());
+ List initialValue = instance.getInitialValue();
+ if (!initialValue.isEmpty()) {
+ return ASTUtils.getLiteralTimeValue(initialValue.get(0));
+ }
+ return TimeValue.ZERO; //fixme
} else {
- return JavaAstUtils.getLiteralTimeValue(v);
+ return ASTUtils.getLiteralTimeValue(v);
}
}
- /**
- * Assuming that the given delay denotes a valid time, return a time value.
- *
- * If the delay is given as a parameter reference, this will look up the
- * precise time value assigned to this reactor instance.
- */
- public TimeValue getTimeValue(Delay d) {
- Parameter p = d.getParameter();
- if (p != null) {
- return JavaAstUtils.getLiteralTimeValue(lookupParameterInstance(p).getInitialValue().get(0));
- } else {
- return JavaAstUtils.toTimeValue(d.getTime());
- }
- }
-
//////////////////////////////////////////////////////
//// Protected fields.
diff --git a/org.lflang/src/org/lflang/generator/TargetTypes.java b/org.lflang/src/org/lflang/generator/TargetTypes.java
index d52c6341c5..3212e0b78a 100644
--- a/org.lflang/src/org/lflang/generator/TargetTypes.java
+++ b/org.lflang/src/org/lflang/generator/TargetTypes.java
@@ -1,22 +1,59 @@
+/*
+ * Copyright (c) 2021, The Authors of this file and their respective Institutions.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
package org.lflang.generator;
-import java.util.List;
-import java.util.Objects;
+import static org.lflang.ASTUtils.addZeroToLeadingDot;
+
import java.util.stream.Collectors;
import org.lflang.ASTUtils;
import org.lflang.InferredType;
-import org.lflang.JavaAstUtils;
-import org.lflang.TimeUnit;
+import org.lflang.ASTUtils;
+import org.lflang.Target;
import org.lflang.TimeValue;
import org.lflang.lf.Action;
+import org.lflang.lf.AddExpr;
+import org.lflang.lf.BraceExpr;
+import org.lflang.lf.BracketExpr;
+import org.lflang.lf.Code;
+import org.lflang.lf.CodeExpr;
+import org.lflang.lf.Initializer;
+import org.lflang.lf.Literal;
+import org.lflang.lf.MulExpr;
+import org.lflang.lf.ParamRef;
import org.lflang.lf.Parameter;
import org.lflang.lf.Port;
import org.lflang.lf.StateVar;
import org.lflang.lf.Time;
+import org.lflang.lf.TupleExpr;
import org.lflang.lf.Type;
import org.lflang.lf.Value;
+import static org.lflang.ASTUtils.addZeroToLeadingDot;
+
/**
* Information about the types of a target language. Contains
* utilities to convert LF expressions and types to the target
@@ -32,8 +69,8 @@ public interface TargetTypes {
/**
- * Return true if the target supports generics (i.e., parametric
- * polymorphism), false otherwise.
+ * Return true if the target supports generic types
+ * (i.e., parametric polymorphism), false otherwise.
*/
boolean supportsGenerics();
@@ -81,33 +118,68 @@ default String escapeIdentifier(String ident) {
* Returns an expression in the target language that corresponds
* to the given time value ({@link #getTargetTimeType()}).
*/
- default String getTargetTimeExpr(TimeValue timeValue) {
- // todo make non-default when we reuse this for all generators,
- // all targets should support this.
- Objects.requireNonNull(timeValue);
- throw new UnsupportedGeneratorFeatureException("Time expressions");
+ String getTargetTimeExpr(TimeValue timeValue);
+
+ /**
+ * Returns an expression in the target language that is the translation
+ * of the given parameter reference. All targets should support this.
+ * The default returns the simple name of the parameter.
+ */
+ default String getTargetParamRef(ParamRef expr, InferredType type) {
+ return escapeIdentifier(expr.getParameter().getName());
}
/**
- * Returns an expression in the target language that corresponds
- * to a variable-size list expression.
- *
- * @throws UnsupportedGeneratorFeatureException If the target does not support this
+ * Returns an expression in the target language that is
+ * the translation of the given literal. All targets
+ * should support this.
+ * The default returns the literal converted to a string.
*/
- default String getVariableSizeListInitExpression(List contents, boolean withBraces) {
- throw new UnsupportedGeneratorFeatureException("Variable size lists");
+ default String getTargetLiteral(Literal expr, InferredType type) {
+ if (ASTUtils.isZero(expr) && type != null && type.isTime) {
+ return getTargetTimeExpr(TimeValue.ZERO);
+ }
+ return addZeroToLeadingDot(expr.getLiteral()); // unescaped
}
/**
- * Returns an expression in the target language that corresponds
- * to a fixed-size list expression.
- *
- * @throws UnsupportedGeneratorFeatureException If the target does not support this
+ * Returns an expression in the target language that is the translation
+ * of the given tuple expression. To support tuple expressions, a target
+ * must also register this capability in {@link Target#supportsLfTupleLiterals()}.
*/
- default String getFixedSizeListInitExpression(List contents, int listSize, boolean withBraces) {
- throw new UnsupportedGeneratorFeatureException("Fixed size lists");
+ default String getTargetTupleExpr(TupleExpr expr, InferredType type) {
+ if (expr.getItems().isEmpty()) {
+ return "()";
+ }
+ InferredType compType = type == null ? null : type.getComponentType();
+ return expr.getItems().stream()
+ .map(it -> getTargetExpr(it, compType))
+ .collect(Collectors.joining(", ", "(", ",)"));
}
+ /**
+ * Returns an expression in the target language that is the translation
+ * of the given list expression. To support list expressions, a target
+ * must also register this capability in {@link Target#supportsLfBracketListExpressions()}.
+ */
+ default String getTargetBracketExpr(BracketExpr expr, InferredType type) {
+ InferredType compType = type == null ? null : type.getComponentType();
+ return expr.getItems().stream()
+ .map(it -> getTargetExpr(it, compType))
+ .collect(Collectors.joining(", ", "[", "]"));
+ }
+
+ /**
+ * Returns an expression in the target language that is the translation
+ * of the given list expression. To support list expressions, a target
+ * must also register this capability in {@link Target#supportsLfBraceListExpressions()}.
+ */
+ default String getTargetBraceExpr(BraceExpr expr, InferredType type) {
+ InferredType compType = type == null ? null : type.getComponentType();
+ return expr.getItems().stream()
+ .map(it -> getTargetExpr(it, compType))
+ .collect(Collectors.joining(", ", "{", "}"));
+ }
/**
* Returns the expression that is used to replace a
@@ -128,13 +200,13 @@ default String getMissingExpr(InferredType type) {
* initializer list. If both are absent, then the undefined
* type is returned.
*/
- default String getTargetType(Type type, List init) {
- return getTargetType(JavaAstUtils.getInferredType(type, init));
+ default String getTargetType(Type type, Initializer init) {
+ return getTargetType(ASTUtils.getInferredType(type, init));
}
/**
* Returns the target type of the type node. This just provides
- * a default parameter for {@link #getTargetType(Type, List)}.
+ * a default parameter for {@link #getTargetType(Type, Initializer)}.
* If the parameter is null, then the undefined type is returned.
*/
default String getTargetType(Type type) {
@@ -169,7 +241,7 @@ default String getTargetType(InferredType type) {
* parameter.
*/
default String getTargetType(Parameter p) {
- return getTargetType(JavaAstUtils.getInferredType(p));
+ return getTargetType(ASTUtils.getInferredType(p));
}
/**
@@ -177,7 +249,7 @@ default String getTargetType(Parameter p) {
* state variable.
*/
default String getTargetType(StateVar s) {
- return getTargetType(JavaAstUtils.getInferredType(s));
+ return getTargetType(ASTUtils.getInferredType(s));
}
/**
@@ -185,7 +257,7 @@ default String getTargetType(StateVar s) {
* action.
*/
default String getTargetType(Action a) {
- return getTargetType(JavaAstUtils.getInferredType(a));
+ return getTargetType(ASTUtils.getInferredType(a));
}
/**
@@ -193,7 +265,7 @@ default String getTargetType(Action a) {
* port.
*/
default String getTargetType(Port p) {
- return getTargetType(JavaAstUtils.getInferredType(p));
+ return getTargetType(ASTUtils.getInferredType(p));
}
/**
@@ -201,24 +273,40 @@ default String getTargetType(Port p) {
* expression in target code. The given type, if non-null,
* may inform the code generation.
*
- * @param init Initializer list (non-null)
- * @param type Declared type of the expression (nullable)
- * @param initWithBraces Whether the initializer uses the braced form.
- */
- default String getTargetInitializer(List init, Type type, boolean initWithBraces) {
- Objects.requireNonNull(init);
- var inferredType = JavaAstUtils.getInferredType(type, init);
- if (init.size() == 1) {
- return getTargetExpr(init.get(0), inferredType);
- }
- var targetValues = init.stream().map(it -> getTargetExpr(it, inferredType)).collect(Collectors.toList());
- if (inferredType.isFixedSizeList) {
- return getFixedSizeListInitExpression(targetValues, inferredType.listSize, initWithBraces);
- } else if (inferredType.isVariableSizeList) {
- return getVariableSizeListInitExpression(targetValues, initWithBraces);
- } else {
+ * By default, only initializers with exactly one expression
+ * are handled. If you want to provide semantics for other kinds
+ * of initializers, for instance, braced initializer lists, override
+ * this method.
+ *
+ * @param init Initializer list (nullable)
+ * @param type Declared type of the expression (nullable)
+ */
+ default String getTargetInitializer(Initializer init, Type type) {
+ InferredType inferredType = ASTUtils.getInferredType(type, init);
+ return getTargetInitializer(init, inferredType);
+ }
+
+ default String getTargetInitializer(Initializer init, InferredType inferredType) {
+ if (init == null) {
return getMissingExpr(inferredType);
}
+ Value single = ASTUtils.asSingleValue(init);
+ return single != null ? getTargetExpr(single, inferredType)
+ : getTargetInitializerWithNotExactlyOneValue(init, inferredType);
+ }
+
+ /**
+ * Returns the representation of the given initializer if
+ * it has not exactly one value. This is an internal implementation
+ * detail of {@link #getTargetInitializer(Initializer, Type)},
+ * used to support the syntax {@code a(1,2,3)}.
+ *
+ * @param init Initializer, non-null
+ * @param type Inferred type, non-null
+ */
+ default String getTargetInitializerWithNotExactlyOneValue(Initializer init, InferredType type) {
+ // override this method to give semantics to this
+ throw new UnsupportedGeneratorFeatureException(init, "Initializers with != 1 value");
}
@@ -227,16 +315,36 @@ default String getTargetInitializer(List init, Type type, boolean initWit
* The given type, if non-null, may inform the code generation.
*/
default String getTargetExpr(Value value, InferredType type) {
- if (ASTUtils.isZero(value) && type != null && type.isTime) {
- return getTargetTimeExpr(TimeValue.ZERO);
- } else if (value.getParameter() != null) {
- return escapeIdentifier(value.getParameter().getName());
- } else if (value.getTime() != null) {
- return getTargetTimeExpr(value.getTime());
- } else if (value.getLiteral() != null) {
- return JavaAstUtils.addZeroToLeadingDot(value.getLiteral()); // here we don't escape
- } else if (value.getCode() != null) {
- return ASTUtils.toText(value.getCode());
+ if (value instanceof ParamRef) {
+ return getTargetParamRef((ParamRef) value, type);
+ } else if (value instanceof Time) {
+ return getTargetTimeExpr((Time) value);
+ } else if (value instanceof Literal) {
+ return getTargetLiteral((Literal) value, type);
+ } else if (value instanceof CodeExpr) {
+ Code code = ((CodeExpr) value).getCode();
+ if (ASTUtils.isZero(code) && type != null && type.isTime) {
+ // todo remove this branch, this is not useful
+ return getTargetTimeExpr(TimeValue.ZERO);
+ } else {
+ return ASTUtils.toText(code);
+ }
+ } else if (value instanceof TupleExpr) {
+ return getTargetTupleExpr((TupleExpr) value, type);
+ } else if (value instanceof BracketExpr) {
+ return getTargetBracketExpr((BracketExpr) value, type);
+ } else if (value instanceof BraceExpr) {
+ return getTargetBraceExpr((BraceExpr) value, type);
+ } else if (value instanceof MulExpr) {
+ MulExpr e = (MulExpr) value;
+ return getTargetExpr(e.getLeft(), null)
+ + " " + e.getOp()
+ + " " + getTargetExpr(e.getRight(), null);
+ } else if (value instanceof AddExpr) {
+ AddExpr e = (AddExpr) value;
+ return getTargetExpr(e.getLeft(), null)
+ + " " + e.getOp()
+ + " " + getTargetExpr(e.getRight(), null);
} else {
throw new IllegalStateException("Invalid value " + value);
}
@@ -247,6 +355,8 @@ default String getTargetExpr(Value value, InferredType type) {
* target code.
*/
default String getTargetTimeExpr(Time t) {
- return getTargetTimeExpr(new TimeValue(t.getInterval(), TimeUnit.fromName(t.getUnit())));
+ return getTargetTimeExpr(ASTUtils.toTimeValue(t));
}
+
+
}
diff --git a/org.lflang/src/org/lflang/generator/TimerInstance.java b/org.lflang/src/org/lflang/generator/TimerInstance.java
index f1250ea05f..cdf586f523 100644
--- a/org.lflang/src/org/lflang/generator/TimerInstance.java
+++ b/org.lflang/src/org/lflang/generator/TimerInstance.java
@@ -45,7 +45,7 @@ public class TimerInstance extends TriggerInstance {
private TimeValue offset = DEFAULT_OFFSET;
- private TimeValue period = DEFAULT_PERIOD;
+ private TimeValue period = DEFAULT_OFFSET;
/**
* Create a new timer instance.
@@ -54,25 +54,28 @@ public class TimerInstance extends TriggerInstance {
* @param parent The parent reactor.
*/
public TimerInstance(Timer definition, ReactorInstance parent) {
- super(definition, parent);
+ super(definition, parent);
if (parent == null) {
throw new InvalidSourceException("Cannot create an TimerInstance with no parent.");
}
- if (definition != null) {
- if (definition.getOffset() != null) {
- try {
- this.offset = parent.getTimeValue(definition.getOffset());
- } catch (IllegalArgumentException ex) {
- parent.reporter.reportError(definition.getOffset(), "Invalid time.");
- }
+ if (definition != null && definition.getOffset() != null) {
+ try {
+ this.offset = parent.getTimeValue(definition.getOffset());
+ } catch (IllegalArgumentException ex) {
+ parent.reporter.reportError(definition.getOffset(), "Invalid time.");
}
- if (definition.getPeriod() != null) {
- try {
- this.period = parent.getTimeValue(definition.getPeriod());
- } catch (IllegalArgumentException ex) {
- parent.reporter.reportError(definition.getOffset(), "Invalid time.");
- }
+
+ } else {
+ this.offset = DEFAULT_OFFSET;
+ }
+ if (definition != null && definition.getPeriod() != null) {
+ try {
+ this.period = parent.getTimeValue(definition.getPeriod());
+ } catch (IllegalArgumentException ex) {
+ parent.reporter.reportError(definition.getPeriod(), "Invalid time.");
}
+ } else {
+ this.period = DEFAULT_PERIOD;
}
}
diff --git a/org.lflang/src/org/lflang/generator/ValueGenerator.java b/org.lflang/src/org/lflang/generator/ValueGenerator.java
deleted file mode 100644
index 190777f764..0000000000
--- a/org.lflang/src/org/lflang/generator/ValueGenerator.java
+++ /dev/null
@@ -1,175 +0,0 @@
-package org.lflang.generator;
-
-import java.util.List;
-import java.util.ArrayList;
-import java.util.stream.Collectors;
-
-import org.lflang.ASTUtils;
-import org.lflang.JavaAstUtils;
-import org.lflang.TimeValue;
-import org.lflang.lf.Assignment;
-import org.lflang.lf.Delay;
-import org.lflang.lf.Instantiation;
-import org.lflang.lf.Parameter;
-import org.lflang.lf.StateVar;
-import org.lflang.lf.Time;
-import org.lflang.TimeUnit;
-import org.lflang.lf.Value;
-
-/**
- * Encapsulates logic for representing {@code Value}s in a
- * target language.
- */
-public final class ValueGenerator {
-
- /**
- * A {@code TimeInTargetLanguage} is a
- * target-language-specific time representation
- * strategy.
- */
- public interface TimeInTargetLanguage {
- String apply(TimeValue t);
- }
-
- /**
- * A {@code GetTargetReference} instance is a
- * target-language-specific function. It provides the
- * target language code that refers to the given
- * parameter {@code param}.
- */
- public interface GetTargetReference {
- String apply(Parameter param);
- }
-
- private final TimeInTargetLanguage timeInTargetLanguage;
- private final GetTargetReference getTargetReference;
-
- /**
- * Instantiates a target-language-specific
- * ValueGenerator parameterized by {@code f}.
- * @param f a time representation strategy
- */
- public ValueGenerator(TimeInTargetLanguage f, GetTargetReference g) {
- this.timeInTargetLanguage = f;
- this.getTargetReference = g;
- }
-
- /**
- * Create a list of state initializers in target code.
- *
- * @param state The state variable to create initializers for
- * @return A list of initializers in target code
- */
- public List getInitializerList(StateVar state) {
- List list = new ArrayList<>();
- // FIXME: Previously, we returned null if it was not initialized, which would have caused an
- // NPE in TSStateGenerator. Is this the desired behavior?
- if (!ASTUtils.isInitialized(state)) return list;
- for (Value v : state.getInit()) {
- if (v.getParameter() != null) {
- list.add(getTargetReference.apply(v.getParameter()));
- } else {
- list.add(getTargetValue(v, JavaAstUtils.isOfTimeType(state)));
- }
- }
- return list;
- }
-
- /**
- * Create a list of default parameter initializers in target code.
- *
- * @param param The parameter to create initializers for
- * @return A list of initializers in target code
- */
- public List getInitializerList(Parameter param) {
- List list = new ArrayList<>();
- if (param == null) return list;
- for (Value v : param.getInit())
- list.add(getTargetValue(v, JavaAstUtils.isOfTimeType(param)));
- return list;
- }
-
- /**
- * Create a list of parameter initializers in target code in the context
- * of an reactor instantiation.
- *
- * This respects the parameter assignments given in the reactor
- * instantiation and falls back to the reactors default initializers
- * if no value is assigned to it.
- *
- * @param param The parameter to create initializers for
- * @return A list of initializers in target code
- */
- public List getInitializerList(Parameter param, Instantiation i) {
- List assignments = i.getParameters().stream()
- .filter(it -> it.getLhs() == param)
- .collect(Collectors.toList());
- if (assignments.isEmpty()) // Case 0: The parameter was not overwritten in the instantiation
- return getInitializerList(param);
- // Case 1: The parameter was overwritten in the instantiation
- List list = new ArrayList<>();
- if (assignments.get(0) == null) return list;
- for (Value init : assignments.get(0).getRhs())
- list.add(getTargetValue(init, JavaAstUtils.isOfTimeType(param)));
- return list;
- }
-
- /**
- * Return the time specified by {@code t}, expressed as
- * code that is valid for some target languages.
- */
- public String getTargetTime(TimeValue t) {
- return timeInTargetLanguage.apply(t);
- }
-
- /**
- * Return the time specified by {@code t}, expressed as
- * code that is valid for some target languages.
- */
- public String getTargetTime(Time t) {
- return timeInTargetLanguage.apply(new TimeValue(t.getInterval(), TimeUnit.fromName(t.getUnit())));
- }
-
- /**
- * Return the time specified by {@code d}, expressed as
- * code that is valid for some target languages.
- */
- public String getTargetTime(Delay d) {
- return d.getParameter() != null ? ASTUtils.toText(d) : timeInTargetLanguage.apply(
- JavaAstUtils.toTimeValue(d.getTime()) // The time is given as a parameter reference.
- );
- }
-
- /**
- * Return the time specified by {@code v}, expressed as
- * code that is valid for some target languages.
- */
- public String getTargetTime(Value v) {
- return getTargetValue(v, true);
- }
-
- /**
- * Get textual representation of a value in the target language.
- *
- * If the value evaluates to 0, it is interpreted as a normal value.
- *
- * @param v A time AST node
- * @return A time string in the target language
- */
- public String getTargetValue(Value v) {
- return ASTUtils.toText(v);
- }
-
- /**
- * Get textual representation of a value in the target language.
- *
- * @param v A time AST node
- * @param isTime Whether {@code v} is expected to be a time
- * @return A time string in the target language
- */
- public String getTargetValue(Value v, boolean isTime) {
- if (v.getTime() != null) return getTargetTime(v.getTime());
- if (isTime && ASTUtils.isZero(v)) return timeInTargetLanguage.apply(TimeValue.ZERO);
- return ASTUtils.toText(v);
- }
-}
diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend
index 61ee3e3b51..3b07f48b9c 100644
--- a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend
+++ b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend
@@ -34,7 +34,6 @@ import java.util.LinkedHashSet
import java.util.LinkedList
import java.util.Set
import java.util.concurrent.Executors
-import java.util.concurrent.TimeUnit
import java.util.regex.Pattern
import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.xtext.util.CancelIndicator
@@ -42,7 +41,7 @@ import org.lflang.ASTUtils
import org.lflang.ErrorReporter
import org.lflang.FileConfig
import org.lflang.InferredType
-import org.lflang.JavaAstUtils
+import org.lflang.ASTUtils
import org.lflang.Target
import org.lflang.TargetConfig
import org.lflang.TargetConfig.Mode
@@ -73,13 +72,14 @@ import org.lflang.generator.SubContext
import org.lflang.generator.TriggerInstance
import org.lflang.lf.Action
import org.lflang.lf.ActionOrigin
-import org.lflang.lf.Assignment
import org.lflang.lf.Delay
import org.lflang.lf.Input
+import org.lflang.lf.Initializer
import org.lflang.lf.Instantiation
import org.lflang.lf.Model
import org.lflang.lf.Output
import org.lflang.lf.Port
+import org.lflang.lf.ParamRef
import org.lflang.lf.Reaction
import org.lflang.lf.Reactor
import org.lflang.lf.ReactorDecl
@@ -90,7 +90,6 @@ import org.lflang.lf.Variable
import org.lflang.util.XtendUtil
import static extension org.lflang.ASTUtils.*
-import static extension org.lflang.JavaAstUtils.*
/**
* Generator for C target. This class generates C code defining each reactor
@@ -311,16 +310,20 @@ import static extension org.lflang.JavaAstUtils.*
* @author{Soroush Bateni
*/
class CGenerator extends GeneratorBase {
-
- protected new(FileConfig fileConfig, ErrorReporter errorReporter, boolean CCppMode, CTypes types) {
+
+ new(FileConfig fileConfig, ErrorReporter errorReporter, boolean CCppMode, CTypes ctypes) {
super(fileConfig, errorReporter)
this.CCppMode = CCppMode;
- this.types = types
+ this.types = ctypes;
}
new(FileConfig fileConfig, ErrorReporter errorReporter, boolean CCppMode) {
- this(fileConfig, errorReporter, CCppMode, new CTypes(errorReporter))
+ this(fileConfig, errorReporter, CCppMode, new CTypes() {
+ override String getTargetUndefinedType() {
+ return String.format("/* %s */", errorReporter.reportError("undefined type"));
+ }
+ })
}
new(FileConfig fileConfig, ErrorReporter errorReporter) {
@@ -439,7 +442,7 @@ class CGenerator extends GeneratorBase {
if (!dir.exists()) dir.mkdirs()
dir = fileConfig.binPath.toFile
if (!dir.exists()) dir.mkdirs()
-
+
targetConfig.compileAdditionalSources.add("ctarget.c");
targetConfig.compileAdditionalSources.add("core" + File.separator + "mixed_radix.c");
@@ -497,7 +500,7 @@ class CGenerator extends GeneratorBase {
// Copy the code generated so far.
var commonCode = new CodeBuilder(code);
-
+
// Keep a separate file config for each federate
val oldFileConfig = fileConfig;
val numOfCompileThreads = Math.min(6,
@@ -594,7 +597,7 @@ class CGenerator extends GeneratorBase {
// Generate main instance, if there is one.
// Note that any main reactors in imported files are ignored.
- // Skip generation if there are cycles.
+ // Skip generation if there are cycles.
if (this.main !== null) {
initializeTriggerObjects.pr('''
int _lf_startup_reactions_count = 0;
@@ -817,7 +820,7 @@ class CGenerator extends GeneratorBase {
// Create an anonymous Runnable class and add it to the compileThreadPool
// so that compilation can happen in parallel.
val cleanCode = code.removeLines("#line");
-
+
val execName = topLevelName
val threadFileConfig = fileConfig;
val generator = this; // FIXME: currently only passed to report errors with line numbers in the Eclipse IDE
@@ -858,13 +861,13 @@ class CGenerator extends GeneratorBase {
}
writeFederatesDockerComposeFile(dockerComposeDir, dockerComposeServices, dockerComposeNetworkName);
}
-
+
// Initiate an orderly shutdown in which previously submitted tasks are
// executed, but no new tasks will be accepted.
compileThreadPool.shutdown();
// Wait for all compile threads to finish (NOTE: Can block forever)
- compileThreadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
+ compileThreadPool.awaitTermination(Long.MAX_VALUE, java.util.concurrent.TimeUnit.NANOSECONDS);
// Restore the base filename.
topLevelName = baseFilename
@@ -892,7 +895,7 @@ class CGenerator extends GeneratorBase {
} else {
context.finish(GeneratorResult.GENERATED_NO_EXECUTABLE.apply(null));
}
-
+
// In case we are in Eclipse, make sure the generated code is visible.
JavaGeneratorUtils.refreshProject(resource, context.mode)
}
@@ -987,12 +990,12 @@ class CGenerator extends GeneratorBase {
// FIXME: For python target, almost surely in the wrong place.
code.pr(CGeneratorExtension.initializeTriggerForControlReactions(this.main, federate, this).toString());
- var reactionsInFederate = main.reactions.filter[
+ var reactionsInFederate = main.reactions.filter[
r | return currentFederate.contains(r.definition);
];
deferredInitialize(main, reactionsInFederate)
-
+
deferredInitializeNonNested(main, reactionsInFederate)
// Next, for every input port, populate its "self" struct
@@ -1358,13 +1361,13 @@ class CGenerator extends GeneratorBase {
protected def initializeClockSynchronization() {
// Check if clock synchronization should be enabled for this federate in the first place
if (targetConfig.clockSync != ClockSyncMode.OFF
- && (!federationRTIProperties.get('host').toString.equals(currentFederate.host)
+ && (!federationRTIProperties.get('host').toString.equals(currentFederate.host)
|| targetConfig.clockSyncOptions.localFederatesOn)
) {
// Insert the #defines at the beginning
code.insert(0, '''
#define _LF_CLOCK_SYNC_INITIAL
- #define _LF_CLOCK_SYNC_PERIOD_NS «targetConfig.clockSyncOptions.period.timeInTargetLanguage»
+ #define _LF_CLOCK_SYNC_PERIOD_NS «targetConfig.clockSyncOptions.period.targetTimeExpr»
#define _LF_CLOCK_SYNC_EXCHANGES_PER_INTERVAL «targetConfig.clockSyncOptions.trials»
#define _LF_CLOCK_SYNC_ATTENUATION «targetConfig.clockSyncOptions.attenuation»
''')
@@ -1413,10 +1416,10 @@ class CGenerator extends GeneratorBase {
val reactorInstance = main.getChildReactorInstance(federate.instantiation)
for (param : reactorInstance.parameters) {
if (param.name.equalsIgnoreCase("STP_offset") && param.type.isTime) {
- val stp = param.getInitialValue().get(0).getLiteralTimeValue
+ val stp = param.init.asSingleValue?.getLiteralTimeValue
if (stp !== null) {
code.pr('''
- set_stp_offset(«stp.timeInTargetLanguage»);
+ set_stp_offset(«stp.targetTimeExpr»);
''')
}
}
@@ -1580,18 +1583,14 @@ class CGenerator extends GeneratorBase {
rtiCode.pr('''
candidate_tmp = FOREVER;
''')
- for (delay : delays) {
+ for (Delay delay : delays) {
if (delay === null) {
// Use NEVER to encode no delay at all.
rtiCode.pr('''
candidate_tmp = NEVER;
''')
} else {
- var delayTime = delay.getTargetTime
- if (delay.parameter !== null) {
- // The delay is given as a parameter reference. Find its value.
- delayTime = delay.parameter.defaultAsTimeValue.timeInTargetLanguage
- }
+ var delayTime = delay.value.targetTime
rtiCode.pr('''
if («delayTime» < candidate_tmp) {
candidate_tmp = «delayTime»;
@@ -1599,7 +1598,7 @@ class CGenerator extends GeneratorBase {
''')
}
}
- rtiCode.pr('''
+ rtiCode.pr('''
encode_int64((int64_t)candidate_tmp, &(buffer_to_send[message_head]));
message_head += sizeof(int64_t);
''')
@@ -1888,7 +1887,7 @@ class CGenerator extends GeneratorBase {
// pointers that will be allocated separately for each instance
// because the sizes may be different. Otherwise, it is a simple
// pointer.
- if (JavaAstUtils.isMultiport(input)) {
+ if (ASTUtils.isMultiport(input)) {
body.pr(input, '''
// Multiport input array will be malloc'd later.
«variableStructType(input, decl)»** _lf_«input.name»;
@@ -1917,14 +1916,14 @@ class CGenerator extends GeneratorBase {
for (output : reactor.allOutputs) {
// If the port is a multiport, create an array to be allocated
// at instantiation.
- if (JavaAstUtils.isMultiport(output)) {
+ if (ASTUtils.isMultiport(output)) {
body.pr(output, '''
// Array of output ports.
«variableStructType(output, decl)»* _lf_«output.name»;
int _lf_«output.name»_width;
// An array of pointers to the individual ports. Useful
// for the SET macros to work out-of-the-box for
- // multiports in the body of reactions because their
+ // multiports in the body of reactions because their
// value can be accessed via a -> operator (e.g.,foo[i]->value).
// So we have to handle multiports specially here a construct that
// array of pointers.
@@ -2012,7 +2011,7 @@ class CGenerator extends GeneratorBase {
if (port instanceof Input) {
// If the variable is a multiport, then the place to store the data has
// to be malloc'd at initialization.
- if (!JavaAstUtils.isMultiport(port)) {
+ if (!ASTUtils.isMultiport(port)) {
// Not a multiport.
body.pr(port, '''
«variableStructType(port, containedReactor.reactorClass)» «port.name»;
@@ -2029,7 +2028,7 @@ class CGenerator extends GeneratorBase {
// Must be an output port.
// Outputs of contained reactors are pointers to the source of data on the
// self struct of the container.
- if (!JavaAstUtils.isMultiport(port)) {
+ if (!ASTUtils.isMultiport(port)) {
// Not a multiport.
body.pr(port, '''
«variableStructType(port, containedReactor.reactorClass)»* «port.name»;
@@ -2055,7 +2054,7 @@ class CGenerator extends GeneratorBase {
constructorCode.indent()
}
val portOnSelf = '''self->_lf_«containedReactor.name»«reactorIndex».«port.name»'''
-
+
if (isFederatedAndDecentralized) {
constructorCode.pr(port, '''
«portOnSelf»_trigger.intended_tag = (tag_t) { .time = NEVER, .microstep = 0u};
@@ -2146,7 +2145,7 @@ class CGenerator extends GeneratorBase {
* @param builder The StringBuilder that the generated code is appended to
* @return
*/
- def generateStateVariablesForReactor(CodeBuilder builder, Reactor reactor) {
+ def generateStateVariablesForReactor(CodeBuilder builder, Reactor reactor) {
for (stateVar : reactor.allStateVars) {
builder.prSourceLineNumber(stateVar)
builder.pr(types.getTargetType(stateVar) + ' ' + stateVar.name + ';');
@@ -2163,9 +2162,9 @@ class CGenerator extends GeneratorBase {
* @param constructorCode The place to put the constructor code.
*/
protected def void generateReactionAndTriggerStructs(
- CodeBuilder body,
+ CodeBuilder body,
ReactorDecl decl,
- CodeBuilder constructorCode
+ CodeBuilder constructorCode
) {
var reactionCount = 0;
val reactor = decl.toDefinition
@@ -2363,7 +2362,7 @@ class CGenerator extends GeneratorBase {
* @param constructorCode The place to write the constructor code.
*/
private def void createTriggerT(
- CodeBuilder body,
+ CodeBuilder body,
Variable variable,
LinkedHashMap> triggerMap,
CodeBuilder constructorCode
@@ -2514,42 +2513,40 @@ class CGenerator extends GeneratorBase {
// For each reaction instance, allocate the arrays that will be used to
// trigger downstream reactions.
for (reaction : instance.reactions) {
- if (currentFederate.contains(reaction.getDefinition())) {
- val reactor = reaction.parent;
-
- val temp = new CodeBuilder();
- var foundOne = false;
-
- val reactionRef = CUtil.reactionRef(reaction)
-
- // Next handle triggers of the reaction that come from a multiport output
- // of a contained reactor. Also, handle startup and shutdown triggers.
- for (trigger : reaction.triggers) {
- if (trigger.isStartup) {
- temp.pr('''
- _lf_startup_reactions[_lf_startup_reactions_count++] = &«reactionRef»;
- ''')
- startupReactionCount += currentFederate.numRuntimeInstances(reactor);
- foundOne = true;
- } else if (trigger.isShutdown) {
+ if (currentFederate.contains(reaction.getDefinition())) {val reactor = reaction.parent;
+
+ val temp = new CodeBuilder();
+ var foundOne = false;
+
+ val reactionRef = CUtil.reactionRef(reaction)
+
+ // Next handle triggers of the reaction that come from a multiport output
+ // of a contained reactor. Also, handle startup and shutdown triggers.
+ for (trigger : reaction.triggers) {
+ if (trigger.isStartup) {
+ temp.pr('''
+ _lf_startup_reactions[_lf_startup_reactions_count++] = &«reactionRef»;
+ ''')
+ startupReactionCount += currentFederate.numRuntimeInstances(reactor);
+ foundOne = true;
+ } else if (trigger.isShutdown) {
+ temp.pr('''
+ _lf_shutdown_reactions[_lf_shutdown_reactions_count++] = &«reactionRef»;
+ ''')
+ foundOne = true;
+ shutdownReactionCount += currentFederate.numRuntimeInstances(reactor);
+
+ if (targetConfig.tracing !== null) {
+ val description = getShortenedName(reactor)
+ val reactorRef = CUtil.reactorRef(reactor)
temp.pr('''
- _lf_shutdown_reactions[_lf_shutdown_reactions_count++] = &«reactionRef»;
+ _lf_register_trace_event(«reactorRef», &(«reactorRef»->_lf__shutdown),
+ trace_trigger, "«description».shutdown");
''')
- foundOne = true;
- shutdownReactionCount += currentFederate.numRuntimeInstances(reactor);
-
- if (targetConfig.tracing !== null) {
- val description = getShortenedName(reactor)
- val reactorRef = CUtil.reactorRef(reactor)
- temp.pr('''
- _lf_register_trace_event(«reactorRef», &(«reactorRef»->_lf__shutdown),
- trace_trigger, "«description».shutdown");
- ''')
- }
}
}
- if (foundOne) initializeTriggerObjects.pr(temp.toString);
}
+ if (foundOne) initializeTriggerObjects.pr(temp.toString);}
}
}
@@ -2574,8 +2571,8 @@ class CGenerator extends GeneratorBase {
#pragma GCC diagnostic ignored "-Wunused-variable"
if (self->_lf__reaction_«reactionIndex».is_STP_violated == true) {
''')
- intendedTagInheritenceCode.indent();
- intendedTagInheritenceCode.pr('''
+ intendedTagInheritenceCode.indent();
+ intendedTagInheritenceCode.pr('''
// The operations inside this if clause (if any exists) are expensive
// and must only be done if the reaction has unhandled STP violation.
// Otherwise, all intended_tag values are (NEVER, 0) by default.
@@ -2594,7 +2591,7 @@ class CGenerator extends GeneratorBase {
if (inputTrigger.variable instanceof Output) {
// Output from a contained reactor
val outputPort = inputTrigger.variable as Output
- if (JavaAstUtils.isMultiport(outputPort)) {
+ if (ASTUtils.isMultiport(outputPort)) {
intendedTagInheritenceCode.pr('''
for (int i=0; i < «inputTrigger.container.name».«inputTrigger.variable.name»_width; i++) {
if (compare_tags(«inputTrigger.container.name».«inputTrigger.variable.name»[i]->intended_tag,
@@ -2614,7 +2611,7 @@ class CGenerator extends GeneratorBase {
} else if (inputTrigger.variable instanceof Port) {
// Input port
val inputPort = inputTrigger.variable as Port
- if (JavaAstUtils.isMultiport(inputPort)) {
+ if (ASTUtils.isMultiport(inputPort)) {
intendedTagInheritenceCode.pr('''
for (int i=0; i < «inputTrigger.variable.name»_width; i++) {
if (compare_tags(«inputTrigger.variable.name»[i]->intended_tag, inherited_min_intended_tag) < 0) {
@@ -2665,7 +2662,7 @@ class CGenerator extends GeneratorBase {
intendedTagInheritenceCode.indent();
for (effect : reaction.effects ?: emptyList) {
if (effect.variable instanceof Input) {
- if (JavaAstUtils.isMultiport(effect.variable as Port)) {
+ if (ASTUtils.isMultiport(effect.variable as Port)) {
intendedTagInheritenceCode.pr('''
for(int i=0; i < «effect.container.name».«effect.variable.name»_width; i++) {
«effect.container.name».«effect.variable.name»[i]->intended_tag = inherited_min_intended_tag;
@@ -2701,7 +2698,7 @@ class CGenerator extends GeneratorBase {
// Write the the intended tag inheritance initialization
// to the main code.
- code.pr(intendedTagInheritenceCode.toString)
+ code.pr(intendedTagInheritenceCode.toString)
}
return intendedTagInheritenceCode
}
@@ -2962,11 +2959,11 @@ class CGenerator extends GeneratorBase {
// input of a contained reactor that is present.
for (child : instance.children) {
if (currentFederate.contains(child) && child.inputs.size > 0) {
-
+
// Avoid generating code if not needed.
var foundOne = false;
val temp = new CodeBuilder();
-
+
startScopedBlock(temp, child, true);
for (input : child.inputs) {
@@ -3028,7 +3025,7 @@ class CGenerator extends GeneratorBase {
temp.pr('''
// Add port «port.getFullName» to array of is_present fields.
''')
-
+
if (port.parent != instance) {
// The port belongs to contained reactor, so we also have
// iterate over the instance bank members.
@@ -3041,7 +3038,7 @@ class CGenerator extends GeneratorBase {
}
val portRef = CUtil.portRefNested(port);
val con = (port.isMultiport)? "->" : ".";
-
+
temp.pr('''
_lf_is_present_fields[«startTimeStepIsPresentCount» + count] = &«portRef»«con»is_present;
''')
@@ -3073,19 +3070,19 @@ class CGenerator extends GeneratorBase {
// This reaction is receiving data from the port.
if (isTokenType((port.definition as Output).inferredType)) {
foundOne = true;
-
+
temp.pr('''
// Add port «port.getFullName» to array _lf_tokens_with_ref_count.
''')
-
+
// Potentially have to iterate over bank members of the instance
// (parent of the reaction), bank members of the contained reactor (if a bank),
// and channels of the multiport (if multiport).
startScopedBlock(temp, instance, true);
startScopedBankChannelIteration(temp, port, "count");
-
+
val portRef = CUtil.portRef(port, true, true, null, null, null);
-
+
temp.pr('''
_lf_tokens_with_ref_count[_lf_tokens_with_ref_count_count].token = &«portRef»->token;
_lf_tokens_with_ref_count[_lf_tokens_with_ref_count_count].status = (port_status_t*)&«portRef»->is_present;
@@ -3103,12 +3100,12 @@ class CGenerator extends GeneratorBase {
if (foundOne) startTimeStep.pr(temp.toString());
temp = new CodeBuilder();
foundOne = false;
-
+
for (action : instance.actions) {
if (currentFederate === null || currentFederate.contains(action.definition)) {
foundOne = true;
startScopedBlock(temp, instance, true);
-
+
temp.pr('''
// Add action «action.getFullName» to array of is_present fields.
_lf_is_present_fields[«startTimeStepIsPresentCount»]
@@ -3129,29 +3126,29 @@ class CGenerator extends GeneratorBase {
if (foundOne) startTimeStep.pr(temp.toString());
temp = new CodeBuilder();
foundOne = false;
-
+
// Next, set up the table to mark each output of each contained reactor absent.
for (child : instance.children) {
if (currentFederate.contains(child) && child.outputs.size > 0) {
-
+
startScopedBlock(temp);
temp.pr("int count = 0;");
startScopedBlock(temp, child, true);
-
+
var channelCount = 0;
for (output : child.outputs) {
if (!output.dependsOnReactions.isEmpty){
foundOne = true;
-
+
temp.pr('''
// Add port «output.getFullName» to array of is_present fields.
''')
startChannelIteration(temp, output);
-
+
temp.pr('''
_lf_is_present_fields[«startTimeStepIsPresentCount» + count] = &«CUtil.portRef(output)».is_present;
''')
-
+
if (isFederatedAndDecentralized) {
// Intended_tag is only applicable to ports in federated execution with decentralized coordination.
temp.pr('''
@@ -3159,7 +3156,7 @@ class CGenerator extends GeneratorBase {
_lf_intended_tag_fields[«startTimeStepIsPresentCount» + count] = &«CUtil.portRef(output)».intended_tag;
''')
}
-
+
temp.pr("count++;");
channelCount += output.width;
endChannelIteration(temp, output);
@@ -3175,7 +3172,7 @@ class CGenerator extends GeneratorBase {
/**
* For each action of the specified reactor instance, generate initialization code
- * for the offset and period fields.
+ * for the offset and period fields.
* @param instance The reactor.
*/
private def generateActionInitializations(ReactorInstance instance) {
@@ -3187,9 +3184,9 @@ class CGenerator extends GeneratorBase {
var minSpacing = action.minSpacing
initializeTriggerObjects.pr('''
// Initializing action «action.fullName»
- «triggerStructName».offset = «minDelay.timeInTargetLanguage»;
+ «triggerStructName».offset = «minDelay.targetTimeExpr»;
«IF minSpacing !== null»
- «triggerStructName».period = «minSpacing.timeInTargetLanguage»;
+ «triggerStructName».period = «minSpacing.targetTimeExpr»;
«ELSE»
«triggerStructName».period = «CGenerator.UNDEFINED_MIN_SPACING»;
«ENDIF»
@@ -3214,8 +3211,8 @@ class CGenerator extends GeneratorBase {
if (currentFederate.contains(timer.getDefinition())) {
if (!timer.isStartup) {
val triggerStructName = CUtil.reactorRef(timer.parent) + "->_lf__" + timer.name;
- val offset = timer.offset.timeInTargetLanguage
- val period = timer.period.timeInTargetLanguage
+ val offset = timer.offset.targetTimeExpr
+ val period = timer.period.targetTimeExpr
initializeTriggerObjects.pr('''
// Initializing timer «timer.fullName».
«triggerStructName».offset = «offset»;
@@ -3284,8 +3281,8 @@ class CGenerator extends GeneratorBase {
'''
}
}
-
- /**
+
+ /**
* Construct a unique type for the struct of the specified
* typed variable (port or action) of the specified reactor class.
* This is required to be the same as the type name returned by
@@ -3298,7 +3295,7 @@ class CGenerator extends GeneratorBase {
'''«reactor.name.toLowerCase»_«variable.name»_t'''
}
- /**
+ /**
* Construct a unique type for the struct of the specified
* instance (port or action).
* This is required to be the same as the type name returned by
@@ -3309,8 +3306,8 @@ class CGenerator extends GeneratorBase {
static def variableStructType(TriggerInstance> portOrAction) {
'''«portOrAction.parent.reactorDeclaration.name.toLowerCase»_«portOrAction.name»_t'''
}
-
- /**
+
+ /**
* Return the function name for specified reaction of the
* specified reactor.
* @param reactor The reactor
@@ -3331,14 +3328,14 @@ class CGenerator extends GeneratorBase {
def getHeapPortMember(String portName, String member) '''
«portName»->«member»
'''
-
+
/**
* Return the operator used to retrieve struct members
*/
def getStackStructOperator() '''
.
'''
-
+
/**
* Return the full name of the specified instance without
* the leading name of the top-level reactor, unless this
@@ -3401,13 +3398,13 @@ class CGenerator extends GeneratorBase {
var fullName = instance.fullName
initializeTriggerObjects.pr(
'// ***** Start initializing ' + fullName + ' of class ' + reactorClass.name)
-
+
// Generate the instance self struct containing parameters, state variables,
// and outputs (the "self" struct).
initializeTriggerObjects.pr('''
«CUtil.reactorRefName(instance)»[«CUtil.runtimeIndex(instance)»] = new_«reactorClass.name»();
''')
-
+
// Generate code to initialize the "self" struct in the
// _lf_initialize_trigger_objects function.
@@ -3427,7 +3424,7 @@ class CGenerator extends GeneratorBase {
// Generate trigger objects for the instance.
generateTimerInitializations(instance);
generateActionInitializations(instance);
-
+
generateInitializeActionToken(instance);
generateSetDeadline(instance);
@@ -3439,9 +3436,9 @@ class CGenerator extends GeneratorBase {
// Need to do this for each of the builders into which the code writes.
startScopedBlock(startTimeStep, child, true);
startScopedBlock(initializeTriggerObjects, child, true);
-
+
generateReactorInstance(child);
-
+
endScopedBlock(initializeTriggerObjects);
endScopedBlock(startTimeStep);
}
@@ -3474,7 +3471,7 @@ class CGenerator extends GeneratorBase {
parameter coordination-options with a value like {advance-message-interval: 10 msec}"''')
}
initializeTriggerObjects.pr('''
- _fed.min_delay_from_physical_action_to_federate_output = «minDelay.timeInTargetLanguage»;
+ _fed.min_delay_from_physical_action_to_federate_output = «minDelay.targetTimeExpr»;
''')
}
}
@@ -3488,7 +3485,7 @@ class CGenerator extends GeneratorBase {
initializeTriggerObjects.pr("//***** End initializing " + fullName)
}
-
+
/**
* Initialize actions by creating a lf_token_t in the self struct.
* This has the information required to allocate memory for the action payload.
@@ -3498,7 +3495,7 @@ class CGenerator extends GeneratorBase {
private def void generateInitializeActionToken(ReactorInstance reactor) {
for (action : reactor.actions) {
// Skip this step if the action is not in use.
- if (action.parent.triggers.contains(action)
+ if (action.parent.triggers.contains(action)
&& currentFederate.contains(action.definition)
) {
var type = action.definition.inferredType
@@ -3563,7 +3560,7 @@ class CGenerator extends GeneratorBase {
val selfRef = CUtil.reactorRef(instance)
for (stateVar : reactorClass.toDefinition.stateVars) {
- val initializer = getInitializer(stateVar, instance)
+ val initializer = getInitializer(stateVar.init, stateVar.inferredType, instance)
if (stateVar.initialized) {
if (stateVar.isOfTimeType) {
initializeTriggerObjects.pr(selfRef + "->" + stateVar.name + " = " + initializer + ";")
@@ -3573,7 +3570,7 @@ class CGenerator extends GeneratorBase {
// static initializers for arrays and structs have to be handled
// this way, and there is no way to tell whether the type of the array
// is a struct.
- if (stateVar.isParameterized && stateVar.init.size > 0) {
+ if (stateVar.isParameterized) {
initializeTriggerObjects.pr(
selfRef + "->" + stateVar.name + " = " + initializer + ";")
} else {
@@ -3603,7 +3600,7 @@ class CGenerator extends GeneratorBase {
var deadline = reaction.declaredDeadline.maxDelay
val selfRef = '''«CUtil.reactorRef(reaction.parent)»->_lf__reaction_«reaction.index»'''
initializeTriggerObjects.pr('''
- «selfRef».deadline = «deadline.timeInTargetLanguage»;
+ «selfRef».deadline = «deadline.targetTimeExpr»;
''')
}
}
@@ -3623,7 +3620,7 @@ class CGenerator extends GeneratorBase {
// or struct (the initialization expression is surrounded by { ... }), then we
// have to declare a static variable to ensure that the memory is put in data space
// and not on the stack.
- // FIXME: Is there a better way to determine this than the string comparison?
+ // FIXME: Is there a better way to determine this than the string comparison?
val initializer = getInitializer(parameter);
if (initializer.startsWith("{")) {
val temporaryVariableName = parameter.uniqueID
@@ -3670,10 +3667,10 @@ class CGenerator extends GeneratorBase {
// width of -2 indicates that it is not a multiport.
«CUtil.portRefName(output)»_width = -2;
''')
- }
+ }
}
}
-
+
/**
* Allocate memory for inputs.
* @param reactor The reactor.
@@ -3703,7 +3700,7 @@ class CGenerator extends GeneratorBase {
}
}
}
-
+
/**
* If the argument is a multiport, return a string that is a valid
* C expression consisting of an (optional) integer added to any number of
@@ -3750,8 +3747,21 @@ class CGenerator extends GeneratorBase {
}
return result.toString
}
-
- /**
+
+ protected def String getTargetTimeExpr(TimeValue value) {
+ return targetTypes.getTargetTimeExpr(value)
+ }
+
+ protected def getInitializer(Initializer init, InferredType t, ReactorInstance parent) {
+ val customExprMaker = new CTypes() {
+ override String getTargetParamRef(ParamRef expr, InferredType t) {
+ return CUtil.reactorRef(parent) + "->" + expr.parameter.name
+ }
+ }
+ return customExprMaker.getTargetInitializer(init, t)
+ }
+
+ /**
* Set the reaction priorities based on dependency analysis.
* @param reactor The reactor on which to do this.
* @param builder Where to write the code.
@@ -3761,7 +3771,7 @@ class CGenerator extends GeneratorBase {
// Force calculation of levels if it has not been done.
reactor.assignLevels();
-
+
// If any reaction has multiple levels, then we need to create
// an array with the levels here, before entering the iteration over banks.
val prolog = new CodeBuilder();
@@ -3784,7 +3794,7 @@ class CGenerator extends GeneratorBase {
val temp = new CodeBuilder();
temp.pr("// Set reaction priorities for " + reactor.toString());
-
+
startScopedBlock(temp, reactor, true);
for (r : reactor.reactions) {
@@ -3806,7 +3816,7 @@ class CGenerator extends GeneratorBase {
temp.pr('''
«CUtil.reactionRef(r)».chain_id = «r.chainID.toString»;
- // index is the OR of level «level» and
+ // index is the OR of level «level» and
// deadline «r.deadline.toNanoSeconds» shifted left 16 bits.
«CUtil.reactionRef(r)».index = «reactionIndex»;
''')
@@ -3815,7 +3825,7 @@ class CGenerator extends GeneratorBase {
temp.pr('''
«CUtil.reactionRef(r)».chain_id = «r.chainID.toString»;
- // index is the OR of levels[«CUtil.runtimeIndex(r.parent)»] and
+ // index is the OR of levels[«CUtil.runtimeIndex(r.parent)»] and
// deadline «r.deadline.toNanoSeconds» shifted left 16 bits.
«CUtil.reactionRef(r)».index = («reactionDeadline» << 16) | «r.uniqueID»_levels[«CUtil.runtimeIndex(r.parent)»];
''')
@@ -3828,11 +3838,11 @@ class CGenerator extends GeneratorBase {
}
}
endScopedBlock(temp);
-
+
if (foundOne) {
builder.pr(prolog.toString());
builder.pr(temp.toString());
- builder.pr(epilog.toString());
+ builder.pr(epilog.toString());
}
return foundOne;
}
@@ -3851,7 +3861,7 @@ class CGenerator extends GeneratorBase {
* @param port The port to read from
*/
override generateDelayBody(Action action, VarRef port) {
- val ref = JavaAstUtils.generateVarRef(port);
+ val ref = ASTUtils.generateVarRef(port);
// Note that the action.type set by the base class is actually
// the port type.
if (action.inferredType.isTokenType) {
@@ -3878,7 +3888,7 @@ class CGenerator extends GeneratorBase {
* @param port The port to write to.
*/
override generateForwardBody(Action action, VarRef port) {
- val outputName = JavaAstUtils.generateVarRef(port)
+ val outputName = ASTUtils.generateVarRef(port)
if (action.inferredType.isTokenType) {
// Forward the entire token and prevent freeing.
// Increment the ref_count because it will be decremented
@@ -4038,11 +4048,11 @@ class CGenerator extends GeneratorBase {
FederateInstance receivingFed,
InferredType type,
boolean isPhysical,
- Delay delay,
+ TimeValue delay,
SupportedSerializers serializer
) {
var sendRef = CUtil.portRefInReaction(sendingPort, sendingBankIndex, sendingChannelIndex);
- val receiveRef = JavaAstUtils.generateVarRef(receivingPort); // Used for comments only, so no need for bank/multiport index.
+ val receiveRef = ASTUtils.generateVarRef(receivingPort); // Used for comments only, so no need for bank/multiport index.
val result = new StringBuilder()
// We currently have no way to mark a reaction "unordered"
@@ -4060,12 +4070,8 @@ class CGenerator extends GeneratorBase {
var String next_destination_name = '''"federate «receivingFed.id»"'''
// Get the delay literal
- var String additionalDelayString =
- CGeneratorExtension.getNetworkDelayLiteral(
- delay,
- this
- );
-
+ var String additionalDelayString = delay?.targetTimeExpr ?: "NEVER";
+
if (isPhysical) {
messageType = "MSG_TYPE_P2P_MESSAGE"
} else if (targetConfig.coordination === CoordinationType.DECENTRALIZED) {
@@ -4183,7 +4189,7 @@ class CGenerator extends GeneratorBase {
// Find the maximum STP for decentralized coordination
if(isFederatedAndDecentralized) {
result.append('''
- max_STP = «maxSTP.timeInTargetLanguage»;
+ max_STP = «maxSTP.targetTimeExpr»;
''')
}
@@ -4212,7 +4218,7 @@ class CGenerator extends GeneratorBase {
int receivingFederateID,
int sendingBankIndex,
int sendingChannelIndex,
- Delay delay
+ TimeValue delay
) {
// Store the code
val result = new StringBuilder();
@@ -4224,11 +4230,7 @@ class CGenerator extends GeneratorBase {
var sendRef = CUtil.portRefInReaction(port, sendingBankIndex, sendingChannelIndex);
// Get the delay literal
- var String additionalDelayString =
- CGeneratorExtension.getNetworkDelayLiteral(
- delay,
- this
- );
+ var String additionalDelayString = delay?.targetTimeExpr ?: "NEVER"
result.append('''
// If the output port has not been SET for the current logical time,
@@ -4296,13 +4298,13 @@ class CGenerator extends GeneratorBase {
}
}
- /**
+ /**
* Generate code that needs appear at the top of the generated
* C file, such as #define and #include statements.
*/
def generatePreamble() {
code.pr(this.defineLogLevel)
-
+
if (isFederated) {
// FIXME: Instead of checking
// #ifdef FEDERATED, we could
@@ -4342,13 +4344,13 @@ class CGenerator extends GeneratorBase {
code.pr('#define TARGET_FILES_DIRECTORY "' + fileConfig.srcGenPath + '"');
if (targetConfig.coordinationOptions.advance_message_interval !== null) {
- code.pr('#define ADVANCE_MESSAGE_INTERVAL ' + targetConfig.coordinationOptions.advance_message_interval.timeInTargetLanguage)
+ code.pr('#define ADVANCE_MESSAGE_INTERVAL ' + targetConfig.coordinationOptions.advance_message_interval.targetTimeExpr)
}
includeTargetLanguageSourceFiles()
code.pr("#include \"core/mixed_radix.h\"");
-
+
// Do this after the above includes so that the preamble can
// call built-in functions.
code.prComment("Code generated by the Lingua Franca compiler from:")
@@ -4376,7 +4378,7 @@ class CGenerator extends GeneratorBase {
}
''')
}
-
+
/**
* Parse the target parameters and set flags to the runCommand
* accordingly.
@@ -4425,7 +4427,7 @@ class CGenerator extends GeneratorBase {
code.pr('#include "ctarget.h"')
if (targetConfig.tracing !== null) {
- code.pr('#include "core/trace.c"')
+ code.pr('#include "core/trace.c"')
}
}
@@ -4501,7 +4503,7 @@ class CGenerator extends GeneratorBase {
builder.indent();
}
}
-
+
/**
* If the specified port is a multiport, then start a specified iteration
* over the channels of the multiport using as the channel index the
@@ -4538,14 +4540,14 @@ class CGenerator extends GeneratorBase {
* starts a scoped block by printing an opening curly brace.
* This also adds a declaration of a pointer to the self
* struct of the reactor or bank member.
- *
+ *
* This block is intended to be nested, where each block is
* put within a similar block for the reactor's parent.
* This ensures that all (possibly nested) bank index variables
* are defined within the block.
- *
+ *
* This must be followed by an {@link endScopedBlock(StringBuilder)}.
- *
+ *
* @param builder The place to write the code.
* @param reactor The reactor instance.
* @param restrict For federated execution only, if this is true, then
@@ -4582,7 +4584,7 @@ class CGenerator extends GeneratorBase {
builder.unindent();
builder.pr("}");
}
-
+
/**
* Start a scoped block to iterate over bank members and
* channels for the specified port with a a variable with
@@ -4632,10 +4634,10 @@ class CGenerator extends GeneratorBase {
endScopedBlock(builder);
}
}
-
+
/**
* Start a scoped block that iterates over the specified range of port channels.
- *
+ *
* This must be followed by a call to
* {@link #endScopedRangeBlock(StringBuilder, RuntimeRange)}.
*
@@ -4645,7 +4647,7 @@ class CGenerator extends GeneratorBase {
* {@link CUtil.reactorRef(ReactorInstance, String)}
* must provide the second argument, a runtime index variable name,
* that must match the runtimeIndex parameter given here.
- *
+ *
* @param builder Where to write the code.
* @param range The range of port channels.
* @param runtimeIndex A variable name to use to index the runtime instance of
@@ -4663,15 +4665,15 @@ class CGenerator extends GeneratorBase {
* are not in the current federate.
*/
private def void startScopedRangeBlock(
- CodeBuilder builder,
- RuntimeRange range,
+ CodeBuilder builder,
+ RuntimeRange range,
String runtimeIndex,
String bankIndex,
String channelIndex,
boolean nested,
boolean restrict
) {
-
+
builder.pr('''
// Iterate over range «range.toString()».
''')
@@ -4757,7 +4759,7 @@ class CGenerator extends GeneratorBase {
}
endScopedBlock(builder);
}
-
+
/** Standardized name for channel index variable for a source. */
static val sc = "src_channel";
/** Standardized name for bank index variable for a source. */
@@ -4776,47 +4778,47 @@ class CGenerator extends GeneratorBase {
* The destination range can be wider than the source range, in which case the
* source range is reused until the destination range is filled.
* The following integer variables will be defined within the scoped block:
- *
+ *
* * src_channel: The channel index for the source.
* * src_bank: The bank index of the source port's parent.
* * src_runtime: The runtime index of the source port's parent or
* the parent's parent (if the source is an input).
- *
+ *
* * dst_channel: The channel index for the destination.
* * dst_bank: The bank index of the destination port's parent.
* * dst_runtime: The runtime index of the destination port's parent or
* the parent's parent (if destination is an output).
- *
+ *
* For convenience, the above variable names are defined in the private
* class variables sc, sb, sr, and dc, db, dr.
- *
+ *
* This block should NOT be nested, where each block is
* put within a similar block for the reactor's parent.
* Within the created block, every use of
* {@link CUtil.reactorRef(ReactorInstance, String, String)}
* and related functions must provide the above variable names.
- *
+ *
* This must be followed by a call to
* {@link #endScopedRangeBlock(StringBuilder, SendRange, RuntimeRange)}.
- *
+ *
* @param builder Where to write the code.
* @param srcRange The send range.
* @param dstRange The destination range.
*/
private def void startScopedRangeBlock(
- CodeBuilder builder,
- SendRange srcRange,
- RuntimeRange dstRange
+ CodeBuilder builder,
+ SendRange srcRange,
+ RuntimeRange dstRange
) {
val srcRangeMR = srcRange.startMR();
val srcSizeMR = srcRangeMR.radixes.size();
val srcNestedLevel = (srcRange.instance.isInput) ? 2 : 1;
val dstNested = dstRange.instance.isOutput;
-
+
builder.pr('''
// Iterate over ranges «srcRange.toString» and «dstRange.toString».
''')
-
+
if (isFederated && srcRange.width == 1) {
// Skip this whole block if the src is not in the federate.
builder.pr('''
@@ -4826,7 +4828,7 @@ class CGenerator extends GeneratorBase {
} else {
startScopedBlock(builder);
}
-
+
if (srcRange.width > 1) {
builder.pr('''
int src_start[] = { «srcRangeMR.getDigits().join(", ")» };
@@ -4850,7 +4852,7 @@ class CGenerator extends GeneratorBase {
int «sb» = «biValue»; // Bank index.
''')
}
-
+
startScopedRangeBlock(builder, dstRange, dr, db, dc, dstNested, true);
if (srcRange.width > 1) {
@@ -4860,7 +4862,7 @@ class CGenerator extends GeneratorBase {
int «sb» = «IF srcSizeMR <= 1»0«ELSE»src_range_mr.digits[1]«ENDIF»; // Bank index.
''')
}
-
+
// The above startScopedRangeBlock() call will skip any iteration where the destination
// is a bank member is not in the federation. Here, we skip any iteration where the
// source is a bank member not in the federation.
@@ -4876,15 +4878,15 @@ class CGenerator extends GeneratorBase {
/**
* End a scoped block that iterates over the specified pair of ranges.
- *
+ *
* @param builder Where to write the code.
* @param srcRange The send range.
* @param dstRange The destination range.
*/
private def void endScopedRangeBlock(
- CodeBuilder builder,
- SendRange srcRange,
- RuntimeRange dstRange
+ CodeBuilder builder,
+ SendRange srcRange,
+ RuntimeRange dstRange
) {
// Do not use endScopedRangeBlock because we need things nested.
if (isFederated) {
@@ -4913,7 +4915,7 @@ class CGenerator extends GeneratorBase {
// Terminate unconditional scope block in startScopedRangeBlock calls.
endScopedBlock(builder);
endScopedBlock(builder);
- }
+ }
/**
* Generate assignments of pointers in the "self" struct of a destination
@@ -4930,20 +4932,20 @@ class CGenerator extends GeneratorBase {
for (dstRange : srcRange.destinations) {
val dst = dstRange.instance;
val destStructType = variableStructType(dst)
-
+
// NOTE: For federated execution, dst.parent should always be contained
// by the currentFederate because an AST transformation removes connections
// between ports of distinct federates. So the following check is not
// really necessary.
if (currentFederate.contains(dst.parent)) {
-
+
val mod = (dst.isMultiport || (src.isInput && src.isMultiport))? "" : "&";
-
+
code.pr('''
// Connect «srcRange.toString» to port «dstRange.toString»
''')
startScopedRangeBlock(code, srcRange, dstRange);
-
+
if (src.isInput) {
// Source port is written to by reaction in port's parent's parent
// and ultimate destination is further downstream.
@@ -5029,12 +5031,12 @@ class CGenerator extends GeneratorBase {
// depending on whether the input is mutable, whether it is a multiport,
// and whether it is a token type.
// Easy case first.
- if (!input.isMutable && !inputType.isTokenType && !JavaAstUtils.isMultiport(input)) {
+ if (!input.isMutable && !inputType.isTokenType && !ASTUtils.isMultiport(input)) {
// Non-mutable, non-multiport, primitive type.
builder.pr('''
«structType»* «input.name» = self->_lf_«input.name»;
''')
- } else if (input.isMutable && !inputType.isTokenType && !JavaAstUtils.isMultiport(input)) {
+ } else if (input.isMutable && !inputType.isTokenType && !ASTUtils.isMultiport(input)) {
// Mutable, non-multiport, primitive type.
builder.pr('''
// Mutable input, so copy the input into a temporary variable.
@@ -5042,7 +5044,7 @@ class CGenerator extends GeneratorBase {
«structType» _lf_tmp_«input.name» = *(self->_lf_«input.name»);
«structType»* «input.name» = &_lf_tmp_«input.name»;
''')
- } else if (!input.isMutable && inputType.isTokenType && !JavaAstUtils.isMultiport(input)) {
+ } else if (!input.isMutable && inputType.isTokenType && !ASTUtils.isMultiport(input)) {
// Non-mutable, non-multiport, token type.
builder.pr('''
«structType»* «input.name» = self->_lf_«input.name»;
@@ -5053,7 +5055,7 @@ class CGenerator extends GeneratorBase {
«input.name»->length = 0;
}
''')
- } else if (input.isMutable && inputType.isTokenType && !JavaAstUtils.isMultiport(input)) {
+ } else if (input.isMutable && inputType.isTokenType && !ASTUtils.isMultiport(input)) {
// Mutable, non-multiport, token type.
builder.pr('''
// Mutable input, so copy the input struct into a temporary variable.
@@ -5076,7 +5078,7 @@ class CGenerator extends GeneratorBase {
«input.name»->length = 0;
}
''')
- } else if (!input.isMutable && JavaAstUtils.isMultiport(input)) {
+ } else if (!input.isMutable && ASTUtils.isMultiport(input)) {
// Non-mutable, multiport, primitive or token type.
builder.pr('''
«structType»** «input.name» = self->_lf_«input.name»;
@@ -5167,7 +5169,7 @@ class CGenerator extends GeneratorBase {
val reactorName = port.container.name
// First define the struct containing the output value and indicator
// of its presence.
- if (!JavaAstUtils.isMultiport(output)) {
+ if (!ASTUtils.isMultiport(output)) {
// Output is not a multiport.
structBuilder.pr('''
«portStructType»* «output.name»;
@@ -5188,7 +5190,7 @@ class CGenerator extends GeneratorBase {
«reactorName»[i].«output.name» = self->_lf_«reactorName»[i].«output.name»;
}
''')
- if (JavaAstUtils.isMultiport(output)) {
+ if (ASTUtils.isMultiport(output)) {
builder.pr('''
for (int i = 0; i < «port.container.name»_width; i++) {
«reactorName»[i].«output.name»_width = self->_lf_«reactorName»[i].«output.name»_width;
@@ -5200,7 +5202,7 @@ class CGenerator extends GeneratorBase {
builder.pr('''
«reactorName».«output.name» = self->_lf_«reactorName».«output.name»;
''')
- if (JavaAstUtils.isMultiport(output)) {
+ if (ASTUtils.isMultiport(output)) {
builder.pr('''
«reactorName».«output.name»_width = self->_lf_«reactorName».«output.name»_width;
''')
@@ -5232,7 +5234,7 @@ class CGenerator extends GeneratorBase {
variableStructType(output, decl)
:
variableStructType(output, effect.container.reactorClass)
- if (!JavaAstUtils.isMultiport(output)) {
+ if (!ASTUtils.isMultiport(output)) {
// Output port is not a multiport.
builder.pr('''
«outputStructType»* «output.name» = &self->_lf_«output.name»;
@@ -5276,7 +5278,7 @@ class CGenerator extends GeneratorBase {
structs.put(definition, structBuilder)
}
val inputStructType = variableStructType(input, definition.reactorClass)
- if (!JavaAstUtils.isMultiport(input)) {
+ if (!ASTUtils.isMultiport(input)) {
// Contained reactor's input is not a multiport.
structBuilder.pr('''
«inputStructType»* «input.name»;
@@ -5322,7 +5324,7 @@ class CGenerator extends GeneratorBase {
return !type.isUndefined && sharedPointerVariable.matcher(types.getTargetType(type)).find()
}
- /**
+ /**
* Given a type for an input or output, return true if it should be
* carried by a lf_token_t struct rather than the type itself.
* It should be carried by such a struct if the type ends with *
@@ -5332,7 +5334,7 @@ class CGenerator extends GeneratorBase {
protected def isTokenType(InferredType type) {
if (type.isUndefined) return false
// This is a hacky way to do this. It is now considered to be a bug (#657)
- val targetType = types.getVariableDeclaration(type, "", false)
+ val targetType = types.getVariableDeclaration(type, "", false)
return type.isVariableSizeList || targetType.trim.endsWith("*")
}
@@ -5379,25 +5381,9 @@ class CGenerator extends GeneratorBase {
* references are replaced with accesses to the self struct of the parent.
*/
protected def String getInitializer(StateVar state, ReactorInstance parent) {
- var list = new LinkedList();
-
- for (i : state?.init) {
- if (i.parameter !== null) {
- list.add(CUtil.reactorRef(parent) + "->" + i.parameter.name)
- } else if (state.isOfTimeType) {
- list.add(i.targetTime)
- } else {
- list.add(i.targetTime)
- }
- }
-
- if (list.size == 1) {
- return list.get(0)
- } else {
- return list.join('{', ', ', '}', [it])
- }
+ return getInitializer(state.init, state.inferredType, parent)
}
-
+
/**
* Return a C expression that can be used to initialize the specified
* parameter instance. If the parameter initializer refers to other
@@ -5409,55 +5395,16 @@ class CGenerator extends GeneratorBase {
if (p.name.equals("bank_index")) {
return CUtil.bankIndex(p.parent);
}
-
- // Handle overrides in the intantiation.
- // In case there is more than one assignment to this parameter, we need to
- // find the last one.
- var lastAssignment = null as Assignment;
- for (assignment: p.parent.definition.parameters) {
- if (assignment.lhs == p.definition) {
- lastAssignment = assignment;
- }
- }
- var list = new LinkedList();
- if (lastAssignment !== null) {
- // The parameter has an assignment.
- // Right hand side can be a list. Collect the entries.
- for (value: lastAssignment.rhs) {
- if (value.parameter !== null) {
- // The parameter is being assigned a parameter value.
- // Assume that parameter belongs to the parent's parent.
- // This should have been checked by the validator.
- list.add(CUtil.reactorRef(p.parent.parent) + "->" + value.parameter.name);
- } else {
- list.add(value.targetTime)
- }
- }
- } else {
- // there was no assignment in the instantiation. So just use the
- // parameter's initial value.
- for (i : p.parent.initialParameterValue(p.definition)) {
- if (p.definition.isOfTimeType) {
- list.add(i.targetTime)
- } else {
- list.add(i.targetTime)
- }
- }
- }
- if (list.size == 1) {
- return list.get(0)
- } else {
- return list.join('{', ', ', '}', [it])
- }
+ return getInitializer(p.init, p.type, p.parent)
}
-
+
override generateDelayGeneric() {
throw new UnsupportedOperationException("TODO: auto-generated method stub")
}
////////////////////////////////////////////////////////////
//// Private methods
-
+
/**
* If a main or federted reactor has been declared, create a ReactorInstance
* for this top level. This will also assign levels to reactions, then,
@@ -5469,7 +5416,7 @@ class CGenerator extends GeneratorBase {
if (this.main === null) {
// Recursively build instances. This is done once because
// it is the same for all federates.
- this.main = new ReactorInstance(mainDef.reactorClass.toDefinition, errorReporter,
+ this.main = new ReactorInstance(mainDef.reactorClass.toDefinition, errorReporter,
this.unorderedReactions)
if (this.main.assignLevels().nodeCount > 0) {
errorReporter.reportError("Main reactor has causality cycles. Skipping code generation.");
@@ -5478,13 +5425,13 @@ class CGenerator extends GeneratorBase {
// Force reconstruction of dependence information.
if (isFederated) {
// Avoid compile errors by removing disconnected network ports.
- // This must be done after assigning levels.
+ // This must be done after assigning levels.
removeRemoteFederateConnectionPorts(main);
// There will be AST transformations that invalidate some info
// cached in ReactorInstance.
- this.main.clearCaches(false);
+ this.main.clearCaches(false);
}
- }
+ }
}
}
@@ -5502,13 +5449,13 @@ class CGenerator extends GeneratorBase {
if (!currentFederate.contains(reactor)) {
return;
}
-
+
code.pr('''// **** Start deferred initialize for «reactor.getFullName()»''')
-
+
// First batch of initializations is within a for loop iterating
// over bank members for the reactor's parent.
startScopedBlock(code, reactor, true);
-
+
// If the child has a multiport that is an effect of some reaction in its container,
// then we have to generate code to allocate memory for arrays pointing to
// its data. If the child is a bank, then memory is allocated for the entire
@@ -5527,12 +5474,12 @@ class CGenerator extends GeneratorBase {
deferredInitialize(child, child.reactions);
}
}
-
+
endScopedBlock(code)
-
+
code.pr('''// **** End of deferred initialize for «reactor.getFullName()»''')
}
-
+
/**
* Perform initialization functions that must be performed after
* all reactor runtime instances have been created.
@@ -5547,26 +5494,26 @@ class CGenerator extends GeneratorBase {
ReactorInstance reactor, Iterable reactions
) {
code.pr('''// **** Start non-nested deferred initialize for «reactor.getFullName()»''')
-
+
// Initialize the num_destinations fields of port structs on the self struct.
// This needs to be outside the above scoped block because it performs
// its own iteration over ranges.
deferredInputNumDestinations(reactions);
-
+
// Second batch of initializes cannot be within a for loop
// iterating over bank members because they iterate over send
// ranges which may span bank members.
deferredOutputNumDestinations(reactor); // NOTE: Does nothing for top level.
deferredFillTriggerTable(reactions);
-
+
deferredOptimizeForSingleDominatingReaction(reactor);
-
+
for (child: reactor.children) {
if (currentFederate.contains(child)) {
deferredInitializeNonNested(child, child.reactions);
}
}
-
+
code.pr('''// **** End of non-nested deferred initialize for «reactor.getFullName()»''')
}
@@ -5579,26 +5526,26 @@ class CGenerator extends GeneratorBase {
*/
private def void deferredConnectInputsToOutputs(ReactorInstance instance) {
code.pr('''// Connect inputs and outputs for reactor «instance.getFullName».''')
-
+
// Iterate over all ports of this reactor that depend on reactions.
for (input : instance.inputs) {
if (!input.dependsOnReactions.isEmpty()) {
// Input is written to by reactions in the parent of the port's parent.
- connectPortToEventualDestinations(input);
+ connectPortToEventualDestinations(input);
}
}
for (output : instance.outputs) {
if (!output.dependsOnReactions.isEmpty()) {
// Output is written to by reactions in the port's parent.
- connectPortToEventualDestinations(output);
+ connectPortToEventualDestinations(output);
}
}
for (child: instance.children) {
deferredConnectInputsToOutputs(child);
}
}
-
- /**
+
+ /**
* For each output of the specified reactor that has a token type
* (type* or type[]), create a default token and put it on the self struct.
* @param parent The reactor.
@@ -5622,7 +5569,7 @@ class CGenerator extends GeneratorBase {
}
}
}
-
+
/**
* For each output port of the specified reactor,
* set the num_destinations field of port structs on its self struct
@@ -5634,7 +5581,7 @@ class CGenerator extends GeneratorBase {
private def void deferredOutputNumDestinations(ReactorInstance reactor) {
// For top-level, ignore this.
if (reactor == main) return;
-
+
// Reference counts are decremented by each destination reactor
// at the conclusion of a time step. Hence, the initial reference
// count should equal the number of destination _reactors_, not the
@@ -5644,18 +5591,18 @@ class CGenerator extends GeneratorBase {
for (output : reactor.outputs) {
for (sendingRange : output.eventualDestinations) {
code.pr("// For reference counting, set num_destinations for port " + output.fullName + ".");
-
+
startScopedRangeBlock(code, sendingRange, sr, sb, sc, sendingRange.instance.isInput, true);
-
+
code.pr('''
«CUtil.portRef(output, sr, sb, sc)».num_destinations = «sendingRange.getNumberOfDestinationReactors()»;
''')
-
+
endScopedRangeBlock(code, sendingRange);
}
}
}
-
+
/**
* For each input port of a contained reactor that receives data
* from one or more of the specified reactions, set the num_destinations
@@ -5681,16 +5628,16 @@ class CGenerator extends GeneratorBase {
if (port.isInput && !portsHandled.contains(port)) {
// Port is an input of a contained reactor that gets data from a reaction of this reactor.
portsHandled.add(port);
-
+
code.pr('''
// For reference counting, set num_destinations for port «port.parent.name».«port.name».
''')
-
+
// The input port may itself have multiple destinations.
for (sendingRange : port.eventualDestinations) {
-
+
startScopedRangeBlock(code, sendingRange, sr, sb, sc, sendingRange.instance.isInput, true);
-
+
// Syntax is slightly different for a multiport output vs. single port.
val connector = (port.isMultiport())? "->" : ".";
code.pr('''
@@ -5718,9 +5665,9 @@ class CGenerator extends GeneratorBase {
val portsHandled = new HashSet();
val reactorSelfStruct = CUtil.reactorRef(reactor);
-
+
// Find parent reactions that mention multiport inputs of this reactor.
- for (reaction : reactor.reactions) {
+ for (reaction : reactor.reactions) {
for (effect : reaction.effects.filter(PortInstance)) {
if (effect.parent.depth > reactor.depth // port of a contained reactor.
&& effect.isMultiport
@@ -5729,11 +5676,11 @@ class CGenerator extends GeneratorBase {
) {
code.pr("// A reaction writes to a multiport of a child. Allocate memory.")
portsHandled.add(effect);
-
+
val portStructType = variableStructType(effect)
-
+
startScopedBlock(code, effect.parent, true);
-
+
val effectRef = CUtil.portRefNestedName(effect);
code.pr('''
@@ -5749,19 +5696,19 @@ class CGenerator extends GeneratorBase {
&«reactorSelfStruct»->base.allocations);
}
''')
-
+
endScopedBlock(code);
}
}
}
}
-
+
/**
* For each reaction of the specified reactor,
* Set the last_enabling_reaction field of the reaction struct to point
* to the single dominating upstream reaction, if there is one, or to be
* NULL if not.
- *
+ *
* @param reactor The reactor.
*/
private def deferredOptimizeForSingleDominatingReaction (ReactorInstance r) {
@@ -5769,7 +5716,7 @@ class CGenerator extends GeneratorBase {
if (currentFederate.contains(reaction.definition)
&& currentFederate.contains(reaction.parent)
) {
-
+
// For federated systems, the above test may not be enough if there is a bank
// of federates. Calculate the divisor needed to compute the federate bank
// index from the instance index of the reaction.
@@ -5781,7 +5728,7 @@ class CGenerator extends GeneratorBase {
parent = parent.parent;
}
}
-
+
// The following code attempts to gather into a loop assignments of successive
// bank members relations between reactions to avoid large chunks of inline code
// when a large bank sends to a large bank or when a large bank receives from
@@ -5840,7 +5787,7 @@ class CGenerator extends GeneratorBase {
}
}
}
-
+
/**
* Print statement that sets the last_enabling_reaction field of a reaction.
*/
@@ -5858,16 +5805,16 @@ class CGenerator extends GeneratorBase {
return;
}
// To really know whether the dominating reaction is in the federate,
- // we need to calculate a divisor for its runtime index.
+ // we need to calculate a divisor for its runtime index.
var parent = runtime.dominating.getReaction().parent;
while (parent.depth > 1) {
domDivisor *= parent.width;
parent = parent.parent;
}
}
-
+
var dominatingRef = "NULL";
-
+
if (end > start + 1) {
startScopedBlock(code);
val reactionRef = CUtil.reactionRef(runtime.reaction, "i");
@@ -5899,8 +5846,8 @@ class CGenerator extends GeneratorBase {
) {
dominatingRef = "&(" + CUtil.reactionRef(runtime.dominating.reaction, "" + domStart) + ")";
}
- if (!isFederated
- || (start/divisor == currentFederate.bankIndex)
+ if (!isFederated
+ || (start/divisor == currentFederate.bankIndex)
&& (runtime.dominating === null || domStart/domDivisor == currentFederate.bankIndex)
) {
code.pr('''
@@ -5910,7 +5857,7 @@ class CGenerator extends GeneratorBase {
}
}
}
-
+
/**
* Generate code to allocate the memory needed by reactions for triggering
* downstream reactions.
@@ -5932,11 +5879,11 @@ class CGenerator extends GeneratorBase {
if (trigger.isMultiport() && trigger.parent !== null && trigger.isOutput) {
// Trigger is an output of a contained reactor or bank.
code.pr('''
- // Allocate memory to store pointers to the multiport output «trigger.name»
+ // Allocate memory to store pointers to the multiport output «trigger.name»
// of a contained reactor «trigger.parent.getFullName»
''')
startScopedBlock(code, trigger.parent, true);
-
+
val width = trigger.width;
val portStructType = variableStructType(trigger)
@@ -5947,26 +5894,26 @@ class CGenerator extends GeneratorBase {
«width», sizeof(«portStructType»*),
&«reactorSelfStruct»->base.allocations);
''')
-
+
endScopedBlock(code);
}
}
}
}
-
+
/**
* For the specified reaction, for ports that it writes to,
* set up the arrays that store the results (if necessary) and
* that are used to trigger downstream reactions if an effect is actually
* produced. The port may be an output of the reaction's parent
* or an input to a reactor contained by the parent.
- *
+ *
* @param The reaction instance.
*/
private def void deferredReactionOutputs(ReactionInstance reaction) {
// val selfRef = CUtil.reactorRef(reaction.parent);
val name = reaction.parent.getFullName;
- // Insert a string name to facilitate debugging.
+ // Insert a string name to facilitate debugging.
if (targetConfig.logLevel >= LogLevel.LOG) {
code.pr('''
«CUtil.reactionRef(reaction)».name = "«name» reaction «reaction.index»";
@@ -5990,7 +5937,7 @@ class CGenerator extends GeneratorBase {
// Create the entry in the output_produced array for this port.
// If the port is a multiport, then we need to create an entry for each
// individual channel.
-
+
// If the port is an input of a contained reactor, then, if that
// contained reactor is a bank, we will have to iterate over bank
// members.
@@ -6005,12 +5952,12 @@ class CGenerator extends GeneratorBase {
startScopedBlock(init);
portRef = CUtil.portRefName(effect);
}
-
+
if (effect.isMultiport()) {
// Form is slightly different for inputs vs. outputs.
var connector = ".";
if (effect.isInput) connector = "->";
-
+
// Point the output_produced field to where the is_present field of the port is.
init.pr('''
for (int i = 0; i < «effect.width»; i++) {
@@ -6051,27 +5998,26 @@ class CGenerator extends GeneratorBase {
&«reactorSelfStruct»->base.allocations);
''')
}
-
+
code.pr('''
«init.toString»
// ** End initialization for reaction «reaction.index» of «name»
''')
}
-
+
/**
* For the specified reaction, for ports that it writes to,
* fill the trigger table for triggering downstream reactions.
- *
+ *
* @param reactions The reactions.
*/
private def void deferredFillTriggerTable(Iterable reactions) {
for (reaction: reactions) {
val name = reaction.parent.getFullName;
-
val reactorSelfStruct = CUtil.reactorRef(reaction.parent, sr);
var foundPort = false;
-
+
for (port : reaction.effects.filter(PortInstance)) {
if (!foundPort) {
// Need a separate index for the triggers array for each bank member.
@@ -6089,7 +6035,7 @@ class CGenerator extends GeneratorBase {
for (SendRange srcRange : port.eventualDestinations()) {
val srcNested = (port.isInput)? true : false;
startScopedRangeBlock(code, srcRange, sr, sb, sc, srcNested, true);
-
+
var triggerArray = '''«CUtil.reactionRef(reaction, sr)».triggers[triggers_index[«sr»]++]'''
// Skip ports whose parent is not in the federation.
// This can happen with reactions in the top-level that have
@@ -6127,9 +6073,9 @@ class CGenerator extends GeneratorBase {
var multicastCount = 0;
for (dstRange : srcRange.destinations) {
val dst = dstRange.instance;
-
+
startScopedRangeBlock(code, srcRange, dstRange);
-
+
// If the source is nested, need to take into account the parent's bank index
// when indexing into the triggers array.
var triggerArray = "";
@@ -6140,7 +6086,7 @@ class CGenerator extends GeneratorBase {
} else {
triggerArray = '''«CUtil.reactionRef(reaction, sr)».triggers[triggers_index[«sr»] + «sc»]'''
}
-
+
if (dst.isOutput) {
// Include this destination port only if it has at least one
// reaction in the federation.
@@ -6181,7 +6127,7 @@ class CGenerator extends GeneratorBase {
if (foundPort) endScopedBlock(code);
}
}
-
+
/**
* Generate an array of self structs for the reactor
* and one for each of its children.
@@ -6205,7 +6151,7 @@ class CGenerator extends GeneratorBase {
//////////////////////////////////////////////////////////////
// Inner class
-
+
/**
* Data structure that for each instantiation of a contained
* reactor. This provides a set of input and output ports that trigger
@@ -6351,7 +6297,7 @@ class CGenerator extends GeneratorBase {
////////////////////////////////////////////
//// Protected fields
-
+
/** The main place to put generated code. */
protected var code = new CodeBuilder();
@@ -6361,7 +6307,7 @@ class CGenerator extends GeneratorBase {
/** Place to collect code to initialize the trigger objects for all reactor instances. */
protected var initializeTriggerObjects = new CodeBuilder()
- /**
+ /**
* Count of the number of is_present fields of the self struct that
* need to be reinitialized in _lf_start_time_step().
*/
@@ -6369,13 +6315,13 @@ class CGenerator extends GeneratorBase {
////////////////////////////////////////////
//// Private fields
-
+
/** The command to run the generated code if specified in the target directive. */
var runCommand = new ArrayList();
/** Place to collect code to execute at the start of a time step. */
var startTimeStep = new CodeBuilder()
-
+
/** Count of the number of token pointers that need to have their
* reference count decremented in _lf_start_time_step().
*/
@@ -6387,9 +6333,11 @@ class CGenerator extends GeneratorBase {
// For each reactor, we collect a set of input and parameter names.
var triggerCount = 0
-
+
// Indicate whether the generator is in Cpp mode or not
var boolean CCppMode = false;
- var CTypes types;
+ val CTypes types;
+
+
}
diff --git a/org.lflang/src/org/lflang/generator/c/CTypes.java b/org.lflang/src/org/lflang/generator/c/CTypes.java
index 256222bcb9..1bce0342bc 100644
--- a/org.lflang/src/org/lflang/generator/c/CTypes.java
+++ b/org.lflang/src/org/lflang/generator/c/CTypes.java
@@ -1,32 +1,56 @@
+/*
+ * Copyright (c) 2021, TU Dresden.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
package org.lflang.generator.c;
+import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.util.stream.Collectors;
-import org.lflang.ErrorReporter;
import org.lflang.InferredType;
+import org.lflang.TimeUnit;
+import org.lflang.TimeValue;
import org.lflang.generator.TargetTypes;
+import org.lflang.generator.UnsupportedGeneratorFeatureException;
+import org.lflang.lf.Initializer;
+import org.lflang.lf.ParamRef;
+/**
+ * {@link TargetTypes} impl for {@link CGenerator}.
+ *
+ * @author Clément Fournier
+ */
public class CTypes implements TargetTypes {
// Regular expression pattern for array types.
// For example, for "foo[10]", the first match will be "foo" and the second "[10]".
// For "foo[]", the first match will be "foo" and the second "".
- static final Pattern arrayPattern = Pattern.compile("^\\s*(?:/\\*.*?\\*/)?\\s*(\\w+)\\s*\\[([0-9]*)]\\s*$");
+ private static final Pattern arrayPattern = Pattern.compile("^\\s*(?:/\\*.*?\\*/)?\\s*(\\w+)\\s*\\[([0-9]*)]\\s*$");
- // FIXME: Instead of using the ErrorReporter, perhaps we should be raising assertion errors or
- // UnsupportedOperationExceptions or some other non-recoverable errors.
- private final ErrorReporter errorReporter;
- /**
- * Initializes a {@code CTargetTypes} with the given
- * error reporter.
- * @param errorReporter The error reporter for any
- * errors raised in the code
- * generation process.
- */
- public CTypes(ErrorReporter errorReporter) {
- this.errorReporter = errorReporter;
+ protected CTypes() {
}
@Override
@@ -46,17 +70,43 @@ public String getTargetTagType() {
@Override
public String getTargetFixedSizeListType(String baseType, int size) {
- return String.format("%s[%d]", baseType, size);
+ return baseType + "[" + size + "]";
}
@Override
public String getTargetVariableSizeListType(String baseType) {
- return String.format("%s[]", baseType);
+ return baseType + "[]";
}
@Override
public String getTargetUndefinedType() {
- return String.format("/* %s */", errorReporter.reportError("undefined type"));
+ // todo C used to insert a marker in the code
+ // return String.format("/* %s */", errorReporter.reportError("undefined type"));
+ return "/* error! */";
+ }
+
+ @Override
+ public String getTargetTimeExpr(TimeValue timeValue) {
+ if (timeValue.equals(TimeValue.ZERO)) {
+ return "0";
+ }
+ return cMacroName(timeValue.getUnit()) + '(' + timeValue.getMagnitude() + ')';
+ }
+
+ private static String cMacroName(TimeUnit unit) {
+ return unit.getCanonicalName().toUpperCase(Locale.ROOT);
+ }
+
+ @Override
+ public String getMissingExpr(InferredType type) {
+ return "0";
+ }
+
+ @Override
+ public String getTargetInitializerWithNotExactlyOneValue(Initializer init, InferredType type) {
+ return init.getExprs().stream()
+ .map(it -> getTargetExpr(it, type.getComponentType()))
+ .collect(Collectors.joining(", ", "{", "}"));
}
/**
@@ -65,7 +115,7 @@ public String getTargetUndefinedType() {
* as a type, and {@code int*} must be used instead, except when initializing
* a variable using a static initializer, as in {@code int[] foo = {1, 2, 3};}.
* When initializing a variable using a static initializer, use
- * {@link #getVariableDeclaration(InferredType, String)} instead.
+ * {@link #getVariableDeclaration(InferredType, String, boolean)} instead.
* @param type The type.
*/
@Override
@@ -97,9 +147,9 @@ public String getTargetType(InferredType type) {
* @param initializer True to return a form usable in a static initializer.
*/
public String getVariableDeclaration(
- InferredType type,
- String variableName,
- boolean initializer
+ InferredType type,
+ String variableName,
+ boolean initializer
) {
String t = TargetTypes.super.getTargetType(type);
Matcher matcher = arrayPattern.matcher(t);
@@ -111,10 +161,10 @@ public String getVariableDeclaration(
// form [], and in a struct definition, it has to use *.
if (matcher.group(2).equals("") && !initializer) {
declaration = String.format("%s* %s",
- matcher.group(1), variableName);
+ matcher.group(1), variableName);
} else {
declaration = String.format("%s %s[%s]",
- matcher.group(1), variableName, matcher.group(2));
+ matcher.group(1), variableName, matcher.group(2));
}
}
return declaration;
diff --git a/org.lflang/src/org/lflang/generator/cpp/CppActionGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppActionGenerator.kt
index 80cebc30a1..d1ac02dab5 100644
--- a/org.lflang/src/org/lflang/generator/cpp/CppActionGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/cpp/CppActionGenerator.kt
@@ -59,7 +59,7 @@ class CppActionGenerator(private val reactor: Reactor, private val errorReporter
"minSpacing and spacing violation policies are not yet supported for logical actions in reactor-ccp!"
)
} else {
- val time = action.minDelay?.toTime() ?: "reactor::Duration::zero()"
+ val time = action.minDelay.toCppTime()
""", ${action.name}{"${action.name}", this, $time}"""
}
}
diff --git a/org.lflang/src/org/lflang/generator/cpp/CppAssembleMethodGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppAssembleMethodGenerator.kt
index b9ac6cc00e..3ce9ce995d 100644
--- a/org.lflang/src/org/lflang/generator/cpp/CppAssembleMethodGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/cpp/CppAssembleMethodGenerator.kt
@@ -120,7 +120,7 @@ class CppAssembleMethodGenerator(private val reactor: Reactor) {
}
private fun setDeadline(reaction: Reaction): String =
- "${reaction.name}.set_deadline(${reaction.deadline.delay.toTime(true)}, [this]() { ${reaction.name}_deadline_handler(); });"
+ "${reaction.name}.set_deadline(${reaction.deadline.delay.toCppTime(true)}, [this]() { ${reaction.name}_deadline_handler(); });"
private fun assembleReaction(reaction: Reaction) = with(PrependOperator) {
"""
@@ -201,4 +201,4 @@ class CppAssembleMethodGenerator(private val reactor: Reactor) {
|}
""".trimMargin()
}
-}
\ No newline at end of file
+}
diff --git a/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt b/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt
index 0888d5c982..270f0ca6a7 100644
--- a/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt
+++ b/org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt
@@ -2,6 +2,7 @@ package org.lflang.generator.cpp
import org.eclipse.emf.ecore.resource.Resource
import org.lflang.*
+import org.lflang.generator.getTargetTimeExpr
import org.lflang.lf.*
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
@@ -48,9 +49,6 @@ val Reaction.name
// corresponding generator classes. See for instance the CppParameterGenerator
-/** Convert a LF time value to a representation in C++ code */
-fun TimeValue.toCppCode() = CppTypes.getTargetTimeExpr(this)
-
/** Convert a value to a time representation in C++ code*
*
* If the value evaluates to 0, it is interpreted as a time.
@@ -58,16 +56,10 @@ fun TimeValue.toCppCode() = CppTypes.getTargetTimeExpr(this)
* @param outerContext A flag indicating whether to generate code for the scope of the outer reactor class.
* This should be set to false if called from code generators for the inner class.
*/
-fun Value.toTime(outerContext: Boolean = false): String =
- if (outerContext && this.parameter != null) "__lf_inner.${parameter.name}"
- else CppTypes.getTargetExpr(this, InferredType.time())
-
-/**
- * Get textual representation of a value in C++ code
- *
- * If the value evaluates to 0, it is interpreted as a normal value.
- */
-fun Value.toCppCode(): String = CppTypes.getTargetExpr(this, null)
+fun Value?.toCppTime(outerContext: Boolean = false): String =
+ if (this == null) "reactor::Duration::zero()"
+ else if (outerContext) CppOuterTypes.getTargetTimeExpr(this)
+ else CppTypes.getTargetTimeExpr(this)
/** Get the textual representation of a width in C++ code */
fun WidthSpec.toCppCode(): String = terms.joinToString(" + ") {
@@ -78,7 +70,7 @@ fun WidthSpec.toCppCode(): String = terms.joinToString(" + ") {
if ((variable as Port).isMultiport) "(${container.name}.size() * ${container.name}[0]->${variable.name}.size())"
else "${container.name}.size()"
} else {
- if ((variable as Port).isMultiport) "${name}.size()"
+ if ((variable as Port).isMultiport) "$name.size()"
else "1"
}
}
@@ -128,3 +120,5 @@ fun fileComment(r: Resource) = """
val InferredType.cppType: String
get() = CppTypes.getTargetType(this)
+
+
diff --git a/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt
index 4d12c7e38c..472ad8e1dd 100644
--- a/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt
@@ -41,8 +41,7 @@ import org.lflang.generator.LFGeneratorContext
import org.lflang.generator.TargetTypes
import org.lflang.generator.canGenerate
import org.lflang.isGeneric
-import org.lflang.lf.Action
-import org.lflang.lf.VarRef
+import org.lflang.lf.*
import org.lflang.scoping.LFGlobalScopeProvider
import org.lflang.toDefinition
import org.lflang.toUnixString
@@ -322,36 +321,3 @@ class CppGenerator(
override fun getTargetTypes(): TargetTypes = CppTypes
}
-
-object CppTypes : TargetTypes {
-
- override fun supportsGenerics() = true
-
- override fun getTargetTimeType() = "reactor::Duration"
- override fun getTargetTagType() = "reactor::Tag"
-
- override fun getTargetFixedSizeListType(baseType: String, size: Int) = "std::array<$baseType, $size>"
- override fun getTargetVariableSizeListType(baseType: String) = "std::vector<$baseType>"
-
- override fun getTargetUndefinedType() = "void"
-
- override fun getTargetTimeExpr(timeValue: TimeValue): String =
- with (timeValue) {
- if (magnitude == 0L) "reactor::Duration::zero()"
- else magnitude.toString() + unit.cppUnit
- }
-
-}
-/** Get a C++ representation of a LF unit. */
-val TimeUnit?.cppUnit
- get() = when (this) {
- TimeUnit.NANO -> "ns"
- TimeUnit.MICRO -> "us"
- TimeUnit.MILLI -> "ms"
- TimeUnit.SECOND -> "s"
- TimeUnit.MINUTE -> "min"
- TimeUnit.HOUR -> "h"
- TimeUnit.DAY -> "d"
- TimeUnit.WEEK -> "d*7"
- else -> ""
- }
diff --git a/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt
index a8968559ec..db5c531942 100644
--- a/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt
@@ -25,7 +25,7 @@
package org.lflang.generator.cpp
import org.lflang.*
-import org.lflang.generator.cpp.CppParameterGenerator.Companion.targetType
+import org.lflang.generator.getActualValue
import org.lflang.lf.Instantiation
import org.lflang.lf.Parameter
import org.lflang.lf.Reactor
@@ -51,37 +51,15 @@ class CppInstanceGenerator(
"std::unique_ptr<$cppType> $name;"
}
- private fun Instantiation.getParameterValue(param: Parameter, isBankInstantiation: Boolean = false): String {
- val assignment = this.parameters.firstOrNull { it.lhs === param }
-
- return if (isBankInstantiation && param.name == "bank_index") {
+ private fun Instantiation.getParameterValue(param: Parameter, isBankInstantiation: Boolean = false): String =
+ if (isBankInstantiation && param.name == "bank_index") {
// If we are in a bank instantiation (instanceId != null), then assign the instanceId
// to the parameter named "bank_index"
"""__lf_idx"""
- } else if (assignment == null) {
- // If no assignment was found, then the parameter is not overwritten and we assign the
- // default value
- with(CppParameterGenerator) { param.defaultValue }
} else {
- // Otherwise, we use the assigned value.
- if (assignment.equals == "=") {
- if (!assignment.braces.isNullOrEmpty()) {
- "{${assignment.rhs.joinToString(", ") { it.toCppCode() }}}"
- } else if (!assignment.parens.isNullOrEmpty()) {
- "(${assignment.rhs.joinToString(", ") { it.toCppCode() }})"
- } else {
- assert(assignment.rhs.size == 1)
- assignment.rhs[0].toCppCode()
- }
- } else {
- if (!assignment.braces.isNullOrEmpty()) {
- "${param.targetType}{${assignment.rhs.joinToString(", ") { it.toCppCode() }}}"
- } else {
- "${param.targetType}(${assignment.rhs.joinToString(", ") { it.toCppCode() }})"
- }
- }
+ val value = this.getActualValue(param)
+ CppTypes.getCppStandaloneInitializer(value, param.inferredType)
}
- }
private fun generateInitializer(inst: Instantiation): String {
assert(!inst.isBank)
diff --git a/org.lflang/src/org/lflang/generator/cpp/CppMainGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppMainGenerator.kt
index 4774c1eefb..8b03bf2567 100644
--- a/org.lflang/src/org/lflang/generator/cpp/CppMainGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/cpp/CppMainGenerator.kt
@@ -1,12 +1,10 @@
package org.lflang.generator.cpp
-import org.lflang.TargetConfig
+import org.lflang.*
import org.lflang.generator.PrependOperator
-import org.lflang.generator.PrependOperator.rangeTo
import org.lflang.inferredType
import org.lflang.lf.Parameter
import org.lflang.lf.Reactor
-import org.lflang.toUnixString
/** C++ code generator responsible for generating the main file including the main() function */
class CppMainGenerator(
@@ -80,7 +78,7 @@ class CppMainGenerator(
| unsigned threads = ${if (targetConfig.threads != 0) targetConfig.threads else "std::thread::hardware_concurrency()"};
| bool fast{${targetConfig.fastMode}};
| bool keepalive{${targetConfig.keepalive}};
- | reactor::Duration timeout = ${targetConfig.timeout?.toCppCode() ?: "reactor::Duration::zero()"};
+ | reactor::Duration timeout = ${CppTypes.getTargetTimeExpr(targetConfig.timeout.orZero())};
|
| // the timeout variable needs to be tested beyond fitting the Duration-type
| options
diff --git a/org.lflang/src/org/lflang/generator/cpp/CppMethodGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppMethodGenerator.kt
index 17aa30032c..9686b254da 100644
--- a/org.lflang/src/org/lflang/generator/cpp/CppMethodGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/cpp/CppMethodGenerator.kt
@@ -34,7 +34,7 @@ import org.lflang.toText
/** A C++ code generator for state variables */
class CppMethodGenerator(private val reactor: Reactor) {
- private val Method.targetType: String get() = if (`return` != null) InferredType.fromAST(`return`).cppType else "void"
+ private val Method.targetType: String get() = InferredType.fromAST(`return`).cppType
private val MethodArgument.targetType: String get() = InferredType.fromAST(type).cppType
private val Method.cppArgs get() = this.arguments.map { "${it.targetType} ${it.name}" }
diff --git a/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt
index c330a83fcc..3fd3f9df2f 100644
--- a/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt
@@ -25,7 +25,6 @@
package org.lflang.generator.cpp
import org.lflang.inferredType
-import org.lflang.isOfTimeType
import org.lflang.lf.Parameter
import org.lflang.lf.Reactor
@@ -34,24 +33,12 @@ class CppParameterGenerator(private val reactor: Reactor) {
companion object {
- /**
- * Create a list of initializers for the given parameter
- *
- * TODO This is redundant to ValueGenerator.getInitializerList
- */
- private fun Parameter.getInitializerList() = init.map {
- if (isOfTimeType) it.toTime()
- else it.toCppCode()
- }
-
/** Type of the parameter in C++ code */
val Parameter.targetType get(): String = this.inferredType.cppType
/** Get the default value of the receiver parameter in C++ code */
val Parameter.defaultValue: String
- get() =
- if (braces?.size == 2) "$targetType{${getInitializerList().joinToString(", ")}}"
- else "$targetType(${getInitializerList().joinToString(", ")})"
+ get() = CppTypes.getCppStandaloneInitializer(init, inferredType)
/** Get a C++ type that is a const reference to the parameter type */
val Parameter.constRefType: String
diff --git a/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt
index d7e62102dd..3a68e2aacb 100644
--- a/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt
@@ -25,39 +25,20 @@
package org.lflang.generator.cpp
import org.lflang.inferredType
-import org.lflang.isInitialized
-import org.lflang.isOfTimeType
import org.lflang.lf.Reactor
import org.lflang.lf.StateVar
/** A C++ code generator for state variables */
class CppStateGenerator(private val reactor: Reactor) {
- /**
- * Create a list of state initializers in target code.
- *
- * TODO This is redundant to ValueGenerator.getInitializerList
- */
- private fun getInitializerList(state: StateVar) = state.init.map {
- when {
- it.parameter != null -> it.parameter.name
- state.isOfTimeType -> it.toTime()
- else -> it.toCppCode()
- }
- }
-
private fun generateInitializer(state: StateVar): String =
- if (state.parens.isNullOrEmpty())
- "${state.name}{${getInitializerList(state).joinToString(separator = ", ")}}"
- else
- "${state.name}(${getInitializerList(state).joinToString(separator = ", ")})"
-
+ state.name + CppTypes.getCppInitializerList(state.init, state.inferredType)
/** Get all state declarations */
fun generateDeclarations() =
reactor.stateVars.joinToString("\n", "// state variable\n", "\n") { "${it.inferredType.cppType} ${it.name};" }
/** Get all timer initializers */
- fun generateInitializers(): String = reactor.stateVars.filter { it.isInitialized }
+ fun generateInitializers(): String = reactor.stateVars.filter { it.init != null }
.joinToString(separator = "\n", prefix = "// state variables\n") { ", ${generateInitializer(it)}" }
}
diff --git a/org.lflang/src/org/lflang/generator/cpp/CppTimerGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppTimerGenerator.kt
index 578d87e76d..61648e4a5e 100644
--- a/org.lflang/src/org/lflang/generator/cpp/CppTimerGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/cpp/CppTimerGenerator.kt
@@ -31,8 +31,8 @@ import org.lflang.lf.Timer
class CppTimerGenerator(private val reactor: Reactor) {
private fun generateInitializer(timer: Timer): String {
- val offset = timer.offset?.toTime() ?: "reactor::Duration::zero()"
- val period = timer.period?.toTime() ?: "reactor::Duration::zero()"
+ val offset = timer.offset.toCppTime()
+ val period = timer.period.toCppTime()
return """${timer.name}{"${timer.name}", this, $period, $offset}"""
}
@@ -43,4 +43,4 @@ class CppTimerGenerator(private val reactor: Reactor) {
/** Get all timer initializers */
fun generateInitializers() =
reactor.timers.joinToString(separator = "\n", prefix = "// timers\n") { ", ${generateInitializer(it)}" }
-}
\ No newline at end of file
+}
diff --git a/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt b/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt
new file mode 100644
index 0000000000..129b076edf
--- /dev/null
+++ b/org.lflang/src/org/lflang/generator/cpp/CppTypes.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2021, TU Dresden.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.lflang.generator.cpp
+
+import org.lflang.InferredType
+import org.lflang.TimeUnit
+import org.lflang.TimeValue
+import org.lflang.generator.TargetTypes
+import org.lflang.lf.BraceExpr
+import org.lflang.lf.Initializer
+import org.lflang.lf.ParamRef
+
+/**
+ * Implementation of [TargetTypes] for C++.
+ *
+ * @author Clément Fournier
+ */
+object CppTypes : TargetTypes {
+
+ override fun supportsGenerics() = true
+
+ override fun getTargetTimeType() = "reactor::Duration"
+ override fun getTargetTagType() = "reactor::Tag"
+
+ override fun getTargetFixedSizeListType(baseType: String, size: Int) = "std::array<$baseType, $size>"
+ override fun getTargetVariableSizeListType(baseType: String) = "std::vector<$baseType>"
+
+ override fun getTargetInitializer(init: Initializer?, inferredType: InferredType): String {
+ return getCppStandaloneInitializer(init, inferredType)
+ }
+
+ override fun getTargetUndefinedType() = "void"
+
+ override fun getTargetTimeExpr(value: TimeValue): String =
+ with (value) {
+ if (magnitude == 0L) "reactor::Duration::zero()"
+ else magnitude.toString() + unit.cppUnit
+ }
+
+}
+
+/**
+ * Returns the initializer list used in direct initialization in ctor definition.
+ */
+fun CppTypes.getCppInitializerList(init: Initializer?, inferredType: InferredType?): String {
+ if (init == null) {
+ return getMissingExpr(inferredType)
+ }
+ val singleExpr = init.exprs.singleOrNull()
+ return if (init.isAssign && singleExpr is BraceExpr)
+ singleExpr.items.joinToString(", ", "{", "}") {
+ getTargetExpr(it, inferredType?.componentType)
+ }
+ else buildString {
+ if (init.isAssign) {
+ val expr = init.exprs.single()
+ append("(").append(getTargetExpr(expr, inferredType)).append(")")
+ } else {
+ val (prefix, postfix) = if (init.isBraces) Pair("{", "}") else Pair("(", ")")
+ init.exprs.joinTo(this, ", ", prefix, postfix) {
+ getTargetExpr(it, inferredType?.componentType)
+ }
+ }
+ }
+}
+
+fun CppTypes.getCppStandaloneInitializer(init: Initializer?, inferredType: InferredType?): String {
+ if (init == null) {
+ return getMissingExpr(inferredType)
+ }
+
+ return buildString {
+ if (init.exprs.size == 1) { // also the case for = assignment
+ append(getTargetExpr(init.exprs.single(), inferredType))
+ } else {
+ append(getTargetType(inferredType)) // treat as ctor call
+ val (prefix, postfix) = if (init.isBraces) Pair("{", "}") else Pair("(", ")")
+ init.exprs.joinTo(this, ", ", prefix, postfix) {
+ getTargetExpr(it, inferredType?.componentType)
+ }
+ }
+ }
+}
+
+
+/**
+ * This object generates types in the context of the outer class,
+ * where parameter references need special handling.
+ */
+object CppOuterTypes : TargetTypes by CppTypes {
+
+ override fun getTargetParamRef(expr: ParamRef, type: InferredType?): String {
+ return "__lf_inner.${expr.parameter.name}"
+ }
+
+}
+
+/** Get a C++ representation of a LF unit. */
+val TimeUnit.cppUnit
+ get() = when (this) {
+ TimeUnit.SECOND -> "s"
+ TimeUnit.MINUTE -> "min"
+ TimeUnit.HOUR -> "h"
+ TimeUnit.DAY -> "d"
+ TimeUnit.WEEK -> "d*7"
+ else -> ""
+ }
diff --git a/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend b/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend
index 2c911104a7..f748f1d0c3 100644
--- a/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend
+++ b/org.lflang/src/org/lflang/generator/python/PythonGenerator.xtend
@@ -32,17 +32,16 @@ import java.util.ArrayList
import java.util.HashMap
import java.util.HashSet
import java.util.LinkedHashSet
-import java.util.LinkedList
-import java.util.List
import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.xtext.util.CancelIndicator
import org.lflang.ErrorReporter
import org.lflang.FileConfig
import org.lflang.InferredType
-import org.lflang.JavaAstUtils
+import org.lflang.ASTUtils
import org.lflang.Target
import org.lflang.TargetConfig.Mode
import org.lflang.TargetProperty.CoordinationType
+import org.lflang.TimeValue
import org.lflang.federated.FedFileConfig
import org.lflang.federated.FederateInstance
import org.lflang.federated.PythonGeneratorExtension
@@ -55,31 +54,26 @@ import org.lflang.generator.GeneratorResult
import org.lflang.generator.IntegratedBuilder
import org.lflang.generator.JavaGeneratorUtils
import org.lflang.generator.LFGeneratorContext
-import org.lflang.generator.ParameterInstance
import org.lflang.generator.ReactionInstance
import org.lflang.generator.ReactorInstance
import org.lflang.generator.SubContext
import org.lflang.generator.c.CGenerator
import org.lflang.generator.c.CUtil
import org.lflang.lf.Action
-import org.lflang.lf.Assignment
-import org.lflang.lf.Delay
+import org.lflang.lf.Initializer
import org.lflang.lf.Input
import org.lflang.lf.Instantiation
import org.lflang.lf.Model
import org.lflang.lf.Output
-import org.lflang.lf.Parameter
import org.lflang.lf.Port
import org.lflang.lf.Reaction
import org.lflang.lf.Reactor
import org.lflang.lf.ReactorDecl
-import org.lflang.lf.StateVar
import org.lflang.lf.TriggerRef
-import org.lflang.lf.Value
import org.lflang.lf.VarRef
import static extension org.lflang.ASTUtils.*
-import static extension org.lflang.JavaAstUtils.*
+import static extension org.lflang.ASTUtils.*
/**
* Generator for Python target. This class generates Python code defining each reactor
@@ -90,7 +84,7 @@ import static extension org.lflang.JavaAstUtils.*
*
* A backend is also generated using the CGenerator that interacts with the C code library (see CGenerator.xtend).
* The backend is responsible for passing arguments to the Python reactor functions.
- *
+ *
* @author{Soroush Bateni }
*/
class PythonGenerator extends CGenerator {
@@ -101,19 +95,12 @@ class PythonGenerator extends CGenerator {
// Used to add module requirements to setup.py (delimited with ,)
var pythonRequiredModules = new StringBuilder()
- var PythonTypes types;
-
new(FileConfig fileConfig, ErrorReporter errorReporter) {
- this(fileConfig, errorReporter, new PythonTypes(errorReporter))
- }
-
- private new(FileConfig fileConfig, ErrorReporter errorReporter, PythonTypes types) {
- super(fileConfig, errorReporter, false, types)
+ super(fileConfig, errorReporter, false, PythonTypes.INSTANCE)
// set defaults
targetConfig.compiler = "gcc"
targetConfig.compilerFlags = newArrayList // -Wall -Wconversion"
targetConfig.linkerFlags = ""
- this.types = types
}
/**
@@ -126,7 +113,7 @@ class PythonGenerator extends CGenerator {
* int num_destinations;
* FEDERATED_CAPSULE_EXTENSION
* } generic_port_instance_struct;
- *
+ *
* @see reactor-c-py/lib/pythontarget.h
*/
val generic_port_type = "generic_port_instance_struct"
@@ -144,7 +131,7 @@ class PythonGenerator extends CGenerator {
* int length;
* FEDERATED_CAPSULE_EXTENSION
* } generic_port_instance_with_token_struct;
- *
+ *
* @see reactor-c-py/lib/pythontarget.h
*/
val generic_port_type_with_token = "generic_port_instance_with_token_struct"
@@ -170,6 +157,14 @@ class PythonGenerator extends CGenerator {
return Target.Python
}
+ override PythonTypes getTargetTypes() {
+ return super.getTargetTypes() as PythonTypes
+ }
+
+ protected def String getTargetInitializer(Initializer init) {
+ return targetTypes.getTargetInitializer(init, InferredType.undefined())
+ }
+
val protoNames = new HashSet()
// //////////////////////////////////////////
@@ -188,10 +183,10 @@ class PythonGenerator extends CGenerator {
*/
def printSetupInfo() {
println('''
-
+
#####################################
To compile and install the generated code, do:
-
+
cd «fileConfig.srcGenPath»«File.separator»
python3 -m pip install --force-reinstall .
''');
@@ -202,12 +197,12 @@ class PythonGenerator extends CGenerator {
*/
def printRunInfo() {
println('''
-
+
#####################################
To run the generated program, use:
-
+
python3 «fileConfig.srcGenPath»«File.separator»«topLevelName».py
-
+
#####################################
''');
}
@@ -217,154 +212,18 @@ class PythonGenerator extends CGenerator {
*/
def printFedRunInfo() {
println('''
-
+
#####################################
To run the generated program, run:
-
+
bash «fileConfig.binPath»/«fileConfig.name»
-
+
#####################################
''');
}
- override getTargetTypes() {
- return types;
- }
-
- // //////////////////////////////////////////
- // // Protected methods
- /**
- * Override to convert some C types to their
- * Python equivalent.
- * Examples:
- * true/false -> True/False
- * @param v A value
- * @return A value string in the target language
- */
- private def getPythonTargetValue(Value v) {
- var String returnValue = "";
- switch (v.toText) {
- case "false": returnValue = "False"
- case "true": returnValue = "True"
- default: returnValue = v.targetValue
- }
-
- // Parameters in Python are always prepended with a 'self.'
- // predicate. Therefore, we need to append the returned value
- // if it is a parameter.
- if (v.parameter !== null) {
- returnValue = "self." + returnValue;
- }
-
- return returnValue;
- }
-
- /**
- * Create a list of state initializers in target code.
- *
- * @param state The state variable to create initializers for
- * @return A list of initializers in target code
- */
- protected def List getPythonInitializerList(StateVar state) {
- if (!state.isInitialized) {
- return null
- }
-
- var list = new ArrayList();
-
- for (i : state?.init) {
- if (i.parameter !== null) {
- list.add(i.parameter.name)
- } else if (state.isOfTimeType) {
- list.add(i.targetTime)
- } else {
- list.add(i.pythonTargetValue)
- }
- }
- return list
- }
-
- /**
- * Create a Python tuple for parameter initialization in target code.
- *
- * @param p The parameter instance to create initializers for
- * @return Initialization code
- */
- protected def String getPythonInitializer(StateVar state) throws Exception {
- if (state.init.size > 1) {
- // state variables are initialized as mutable lists
- return state.init.join('[', ', ', ']', [it.pythonTargetValue])
- } else if (state.isInitialized) {
- return state.init.get(0).getPythonTargetValue
- } else {
- return "None"
- }
-
- }
-
- /**
- * Return a Python expression that can be used to initialize the specified
- * parameter instance. If the parameter initializer refers to other
- * parameters, then those parameter references are replaced with
- * accesses to the Python reactor instance class of the parents of
- * those parameters.
- *
- * @param p The parameter instance to create initializer for
- * @return Initialization code
- */
- protected def String getPythonInitializer(ParameterInstance p) {
- // Handle overrides in the intantiation.
- // In case there is more than one assignment to this parameter, we need to
- // find the last one.
- var lastAssignment = null as Assignment;
- for (assignment : p.parent.definition.parameters) {
- if (assignment.lhs == p.definition) {
- lastAssignment = assignment;
- }
- }
-
- var list = new LinkedList();
- if (lastAssignment !== null) {
- // The parameter has an assignment.
- // Right hand side can be a list. Collect the entries.
- for (value : lastAssignment.rhs) {
- if (value.parameter !== null) {
- // The parameter is being assigned a parameter value.
- // Assume that parameter belongs to the parent's parent.
- // This should have been checked by the validator.
- list.add(PyUtil.reactorRef(p.parent.parent) + "." + value.parameter.name);
- } else {
- list.add(value.targetTime)
- }
- }
- } else {
- for (i : p.parent.initialParameterValue(p.definition)) {
- list.add(i.getPythonTargetValue)
- }
- }
-
- if (list.size == 1) {
- return list.get(0)
- } else {
- return list.join('(', ', ', ')', [it])
- }
-
- }
-
- /**
- * Create a Python list for parameter initialization in target code.
- *
- * @param p The parameter to create initializers for
- * @return Initialization code
- */
- protected def String getPythonInitializer(Parameter p) {
- if (p.init.size > 1) {
- // parameters are initialized as immutable tuples
- return p.init.join('(', ', ', ')', [it.pythonTargetValue])
- } else {
- return p.init.get(0).pythonTargetValue
- }
- }
+ ////////////////////////////////////////////
+ //// Protected methods
/**
* Generate parameters and their respective initialization code for a reaction function
@@ -388,7 +247,7 @@ class PythonGenerator extends CGenerator {
generatedParams.add('''mutable_«trigger.variable.name»''')
// Create a deep copy
- if (JavaAstUtils.isMultiport(trigger.variable as Input)) {
+ if (ASTUtils.isMultiport(trigger.variable as Input)) {
inits.
pr('''«trigger.variable.name» = [Make() for i in range(len(mutable_«trigger.variable.name»))]''')
inits.pr('''for i in range(len(mutable_«trigger.variable.name»)):''')
@@ -448,7 +307,7 @@ class PythonGenerator extends CGenerator {
} else {
generatedParams.add(effect.variable.name)
if (effect.variable instanceof Port) {
- if (JavaAstUtils.isMultiport(effect.variable as Port)) {
+ if (ASTUtils.isMultiport(effect.variable as Port)) {
// Handle multiports
}
}
@@ -461,7 +320,7 @@ class PythonGenerator extends CGenerator {
}
}
-
+
/**
* Generate into the specified string builder (inits) the code to
* initialize local variable for port so that it can be used in the body of
@@ -478,25 +337,13 @@ class PythonGenerator extends CGenerator {
«port.container.name»[i] = Make()
«port.container.name»[i].«port.variable.name» = «port.container.name»_«port.variable.name»[i]
''')
-
+
} else {
inits.pr('''«port.container.name» = Make''')
inits.pr('''«port.container.name».«port.variable.name» = «port.container.name»_«port.variable.name»''')
}
-
- return inits;
- }
- /**
- * Handle initialization for state variable
- * @param state a state variable
- */
- def String getTargetInitializer(StateVar state) {
- if (!state.isInitialized) {
- return '''None'''
- }
-
- '''«FOR init : state.pythonInitializerList SEPARATOR ", "»«init»«ENDFOR»'''
+ return inits;
}
/**
@@ -541,7 +388,7 @@ class PythonGenerator extends CGenerator {
if (federate.contains(instance) && !instantiatedClasses.contains(className)) {
pythonClasses.pr('''
-
+
# Python class for reactor «className»
class _«className»:
''');
@@ -549,21 +396,21 @@ class PythonGenerator extends CGenerator {
// Generate preamble code
pythonClasses.indent()
pythonClasses.pr('''
-
+
«generatePythonPreamblesForReactor(decl.toDefinition)»
''')
val reactor = decl.toDefinition
// Handle runtime initializations
- pythonClasses.pr('''
+ pythonClasses.pr('''
def __init__(self, **kwargs):
''')
pythonClasses.pr(generateParametersAndStateVariables(decl))
-
+
var reactionToGenerate = reactor.allReactions
-
+
if (reactor.isFederated) {
// Filter out reactions that are automatically generated in C in the top level federated reactor
reactionToGenerate.removeIf([
@@ -573,7 +420,7 @@ class PythonGenerator extends CGenerator {
])
}
-
+
var reactionIndex = 0
for (reaction : reactionToGenerate) {
val reactionParameters = new StringBuilder() // Will contain parameters for the function (e.g., Foo(x,y,z,...)
@@ -595,7 +442,7 @@ class PythonGenerator extends CGenerator {
reactionIndex = reactionIndex + 1;
}
-
+
pythonClasses.unindent()
instantiatedClasses.add(className)
}
@@ -621,14 +468,14 @@ class PythonGenerator extends CGenerator {
''')
for (param : decl.toDefinition.allParameters) {
- if (!types.getTargetType(param).equals("PyObject*")) {
+ if (!targetTypes.getTargetType(param).equals("PyObject*")) {
// If type is given, use it
temporary_code.
- pr('''self._«param.name»:«types.getPythonType(param.inferredType)» = «param.pythonInitializer»
+ pr('''self._«param.name»:«targetTypes.getPythonType(param.inferredType)» = «param.init.targetInitializer»
''')
} else {
// If type is not given, just pass along the initialization
- temporary_code.pr('''self._«param.name» = «param.pythonInitializer»
+ temporary_code.pr('''self._«param.name» = «param.init.targetInitializer»
''')
}
@@ -647,7 +494,7 @@ class PythonGenerator extends CGenerator {
for (stateVar : reactor.allStateVars) {
if (stateVar.isInitialized) {
// If initialized, pass along the initialization directly if it is present
- temporary_code.pr('''self.«stateVar.name» = «stateVar.pythonInitializer»
+ temporary_code.pr('''self.«stateVar.name» = «stateVar.init.targetInitializer»
''')
} else {
// If neither the type nor the initialization is given, use None
@@ -656,7 +503,7 @@ class PythonGenerator extends CGenerator {
}
}
-
+
temporary_code.pr('''
''')
@@ -671,7 +518,7 @@ class PythonGenerator extends CGenerator {
temporary_code.pr('''def «param.name»(self):
''')
temporary_code.pr(''' return self._«param.name» # pylint: disable=no-member
-
+
''')
}
}
@@ -682,7 +529,7 @@ class PythonGenerator extends CGenerator {
temporary_code.pr('''def bank_index(self):
''')
temporary_code.pr(''' return self._bank_index # pylint: disable=no-member
-
+
''')
return temporary_code;
@@ -697,7 +544,7 @@ class PythonGenerator extends CGenerator {
*/
def generateDeadlineFunctionForReaction(Reaction reaction, int reactionIndex, String reactionParameters) '''
«val deadlineFunctionName = 'deadline_function_' + reactionIndex»
-
+
def «deadlineFunctionName»(self «reactionParameters»):
«reaction.deadline.code.toText»
return 0
@@ -743,7 +590,7 @@ class PythonGenerator extends CGenerator {
// Non-bank reactor instances will be a list of size 1. var reactorClass = instance.definition.reactorClass
var fullName = instance.fullName
pythonClassesInstantiation.pr( '''
-
+
# Start initializing «fullName» of class «className»
for «PyUtil.bankIndexName(instance)» in range(«instance.width»):
''')
@@ -754,7 +601,7 @@ class PythonGenerator extends CGenerator {
_bank_index = «PyUtil.bankIndex(instance)»,
«FOR param : instance.parameters»
«IF !param.name.equals("bank_index")»
- _«param.name»=«param.pythonInitializer»,
+ _«param.name»=«param.initializer»,
«ENDIF»«ENDFOR»
)
''')
@@ -767,13 +614,13 @@ class PythonGenerator extends CGenerator {
}
/**
- * Generate code to instantiate a Python list that will hold the Python
- * class instance of reactor instance. Will recursively do
+ * Generate code to instantiate a Python list that will hold the Python
+ * class instance of reactor instance. Will recursively do
* the same for the children of instance as well.
- *
+ *
* @param instance The reactor instance for which the Python list will be created.
- * @param pythonClassesInstantiation StringBuilder to hold the generated code.
- * @param federate Will check if instance (or any of its children) belong to
+ * @param pythonClassesInstantiation StringBuilder to hold the generated code.
+ * @param federate Will check if instance (or any of its children) belong to
* federate before generating code for them.
*/
def void generateListsToHoldClassInstances(
@@ -836,11 +683,11 @@ class PythonGenerator extends CGenerator {
from LinguaFrancaBase.classes import Make
import sys
import copy
-
+
«pythonPreamble.toString»
-
+
«generatePythonReactorClasses(federate)»
-
+
«PythonMainGenerator.generateCode()»
'''
@@ -854,12 +701,12 @@ class PythonGenerator extends CGenerator {
*/
def generatePythonSetupFile() '''
from setuptools import setup, Extension
-
+
linguafranca«topLevelName»module = Extension("LinguaFranca«topLevelName»",
sources = ["«topLevelName».c", «FOR src : targetConfig.compileAdditionalSources SEPARATOR ", "» "«src»"«ENDFOR»],
- define_macros=[('MODULE_NAME', 'LinguaFranca«topLevelName»')«IF (targetConfig.threads !== 0 || (targetConfig.tracing !== null))»,
+ define_macros=[('MODULE_NAME', 'LinguaFranca«topLevelName»')«IF (targetConfig.threads !== 0 || (targetConfig.tracing !== null))»,
('NUMBER_OF_WORKERS', '«targetConfig.threads»')«ENDIF»])
-
+
setup(name="LinguaFranca«topLevelName»", version="1.0",
ext_modules = [linguafranca«topLevelName»module],
install_requires=['LinguaFrancaBase' «pythonRequiredModules»],)
@@ -1150,7 +997,7 @@ class PythonGenerator extends CGenerator {
FederateInstance receivingFed,
InferredType type,
boolean isPhysical,
- Delay delay,
+ TimeValue delay,
SupportedSerializers serializer
) {
var result = new StringBuilder();
@@ -1262,7 +1109,7 @@ class PythonGenerator extends CGenerator {
* uniformly across all target languages.
*/
override includeTargetLanguageHeaders() {
- code.pr('''#define _LF_GARBAGE_COLLECTED''')
+ code.pr('''#define _LF_GARBAGE_COLLECTED''')
if (targetConfig.tracing !== null) {
var filename = "";
if (targetConfig.tracing.traceFileName !== null) {
@@ -1273,7 +1120,7 @@ class PythonGenerator extends CGenerator {
code.pr('#include "pythontarget.c"')
if (targetConfig.tracing !== null) {
- code.pr('#include "core/trace.c"')
+ code.pr('#include "core/trace.c"')
}
}
@@ -1383,7 +1230,7 @@ class PythonGenerator extends CGenerator {
"bash")
}
}
-
+
/**
* Copy Python specific target code to the src-gen directory
*/
@@ -1420,7 +1267,7 @@ class PythonGenerator extends CGenerator {
* @param port The port to read from
*/
override generateDelayBody(Action action, VarRef port) {
- val ref = JavaAstUtils.generateVarRef(port);
+ val ref = ASTUtils.generateVarRef(port);
// Note that the action.type set by the base class is actually
// the port type.
if (action.inferredType.isTokenType) {
@@ -1444,7 +1291,7 @@ class PythonGenerator extends CGenerator {
#endif
t->value = self->_lf_«ref»->value;
t->length = 1; // Length is 1
-
+
// Pass the token along
schedule_token(«action.name», 0, t);
'''
@@ -1460,7 +1307,7 @@ class PythonGenerator extends CGenerator {
* @param port The port to write to.
*/
override generateForwardBody(Action action, VarRef port) {
- val outputName = JavaAstUtils.generateVarRef(port)
+ val outputName = ASTUtils.generateVarRef(port)
if (action.inferredType.isTokenType) {
super.generateForwardBody(action, port)
} else {
@@ -1469,16 +1316,16 @@ class PythonGenerator extends CGenerator {
'''
}
}
-
-
+
+
/**
- * Generate necessary Python-specific initialization code for reaction that belongs to reactor
+ * Generate necessary Python-specific initialization code for reaction that belongs to reactor
* decl.
- *
+ *
* @param reaction The reaction to generate Python-specific initialization for.
* @param decl The reactor to which reaction 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 Py_BuildValue
+ * then can be used as an argument to Py_BuildValue
* (@see docs.python.org/3/c-api ).
* @param pyObjects A "," delimited list of expressions that would be (or result in a creation of) a PyObject.
*/
@@ -1561,7 +1408,7 @@ class PythonGenerator extends CGenerator {
* actions (triggering or produced), and outputs.
* @param reaction The reaction.
* @param reactor The reactor.
- * @param reactionIndex The position of the reaction within the reactor.
+ * @param reactionIndex The position of the reaction within the reactor.
*/
override generateReaction(Reaction reaction, ReactorDecl decl, int reactionIndex) {
@@ -1601,13 +1448,13 @@ class PythonGenerator extends CGenerator {
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
''')
-
+
// Generate Python-related initializations
generatePythonInitializationForReaction(reaction, decl, pyObjectDescriptor, pyObjects)
-
+
// Call the Python reaction
code.pr('''
-
+
DEBUG_PRINT("Calling reaction function «decl.name».«pythonFunctionName»");
PyObject *rValue = PyObject_CallObject(
self->_lf_py_reaction_function_«reactionIndex»,
@@ -1746,7 +1593,7 @@ class PythonGenerator extends CGenerator {
* @param builder The place that the generated code is written to.
* @return
*/
- override generateStateVariablesForReactor(CodeBuilder builder, Reactor reactor) {
+ override generateStateVariablesForReactor(CodeBuilder builder, Reactor reactor) {
// Do nothing
}
@@ -1786,17 +1633,17 @@ class PythonGenerator extends CGenerator {
val pythonFunctionName = pythonReactionFunctionName(reaction.index)
// Create a PyObject for each reaction
initializeTriggerObjects.pr('''
- «nameOfSelfStruct»->_lf_py_reaction_function_«reaction.index» =
- get_python_function("__main__",
+ «nameOfSelfStruct»->_lf_py_reaction_function_«reaction.index» =
+ get_python_function("__main__",
«nameOfSelfStruct»->_lf_name,
«CUtil.runtimeIndex(instance)»,
"«pythonFunctionName»");
''')
-
+
if (reaction.definition.deadline !== null) {
initializeTriggerObjects.pr('''
- «nameOfSelfStruct»->_lf_py_deadline_function_«reaction.index» =
- get_python_function("«topLevelName»",
+ «nameOfSelfStruct»->_lf_py_deadline_function_«reaction.index» =
+ get_python_function("«topLevelName»",
«nameOfSelfStruct»->_lf_name,
«CUtil.runtimeIndex(instance)»,
"deadline_function_«reaction.index»");
@@ -1814,7 +1661,7 @@ class PythonGenerator extends CGenerator {
*/
override generateSelfStructExtension(
CodeBuilder selfStructBody,
- ReactorDecl decl,
+ ReactorDecl decl,
CodeBuilder constructorCode
) {
val reactor = decl.toDefinition
@@ -1838,7 +1685,7 @@ class PythonGenerator extends CGenerator {
/**
* Generate code to convert C actions to Python action capsules
* @see pythontarget.h.
- * @param pyObjectDescriptor A string representing a list of Python format types (e.g., "O") that
+ * @param pyObjectDescriptor A string representing a list of Python format types (e.g., "O") that
* can be passed to Py_BuildValue. The object type for the converted action will
* be appended to this string (e.g., "OO").
* @param pyObjects A string containing a list of comma-separated expressions that will create the
@@ -1854,12 +1701,12 @@ class PythonGenerator extends CGenerator {
pyObjects.append(''', convert_C_action_to_py(«action.name»)''')
}
- /**
+ /**
* Generate code to convert C ports to Python ports capsules (@see pythontarget.h).
- *
+ *
* The port may be an input of the reactor or an output of a contained reactor.
- *
- * @param pyObjectDescriptor A string representing a list of Python format types (e.g., "O") that
+ *
+ * @param pyObjectDescriptor A string representing a list of Python format types (e.g., "O") that
* can be passed to Py_BuildValue. The object type for the converted port will
* be appended to this string (e.g., "OO").
* @param pyObjects A string containing a list of comma-separated expressions that will create the
@@ -1882,7 +1729,7 @@ class PythonGenerator extends CGenerator {
// port is an output of a contained reactor.
if (port.container.widthSpec !== null) {
var String widthSpec = "-2"
- if (JavaAstUtils.isMultiport(port.variable as Port)) {
+ if (ASTUtils.isMultiport(port.variable as Port)) {
widthSpec = '''self->_lf_«reactorName»[i].«output.name»_width'''
}
// Output is in a bank.
@@ -1891,25 +1738,25 @@ class PythonGenerator extends CGenerator {
pyObjects.append(''', «reactorName»_py_list''')
} else {
var String widthSpec = "-2"
- if (JavaAstUtils.isMultiport(port.variable as Port)) {
+ if (ASTUtils.isMultiport(port.variable as Port)) {
widthSpec = '''«port.container.name».«port.variable.name»_width'''
}
pyObjects.append(''', convert_C_port_to_py(«reactorName».«port.variable.name», «widthSpec»)''')
}
}
}
-
+
/**
* Generate code that creates a Python list (i.e., []) for contained banks to be passed to Python reactions.
- * The Python reaction will then subsequently be able to address each individual bank member of the contained
- * bank using an index or an iterator. Each list member will contain the given port
+ * The Python reaction will then subsequently be able to address each individual bank member of the contained
+ * bank using an index or an iterator. Each list member will contain the given port
* (which could be a multiport with a width determined by widthSpec).
- *
+ *
* This is to accommodate reactions like reaction() -> s.out where s is a bank. In this example,
* the generated Python function will have the signature reaction_function_0(self, s_out), where
* s_out is a list of out ports. This will later be turned into the proper s.out format using the
* Python code generated in {@link #generatePythonPortVariableInReaction}.
- *
+ *
* @param reactorName The name of the bank of reactors (which is the name of the reactor class).
* @param port The port that should be put in the Python list.
* @param widthSpec A string that should be -2 for non-multiports and the width expression for multiports.
@@ -1917,7 +1764,7 @@ class PythonGenerator extends CGenerator {
protected def void generatePythonListForContainedBank(String reactorName, Port port, String widthSpec) {
code.pr('''
PyObject* «reactorName»_py_list = PyList_New(«reactorName»_width);
-
+
if(«reactorName»_py_list == NULL) {
error_print("Could not create the list needed for «reactorName».");
if (PyErr_Occurred()) {
@@ -1929,13 +1776,13 @@ class PythonGenerator extends CGenerator {
Py_FinalizeEx();
exit(1);
}
-
+
for (int i = 0; i < «reactorName»_width; i++) {
if (PyList_SetItem(
«reactorName»_py_list,
i,
convert_C_port_to_py(
- self->_lf_«reactorName»[i].«port.name»,
+ self->_lf_«reactorName»[i].«port.name»,
«widthSpec»
)
) != 0) {
@@ -1950,7 +1797,7 @@ class PythonGenerator extends CGenerator {
exit(1);
}
}
-
+
''')
}
@@ -1977,11 +1824,11 @@ class PythonGenerator extends CGenerator {
// FIXME: The C Generator also has this awkwardness. It makes the code generators
// unnecessarily difficult to maintain, and it may have performance consequences as well.
// Maybe we should change the SET macros.
- if (!JavaAstUtils.isMultiport(output)) {
+ if (!ASTUtils.isMultiport(output)) {
pyObjectDescriptor.append("O")
pyObjects.append(''', convert_C_port_to_py(«output.name», -2)''')
} else {
- // Set the _width variable.
+ // Set the _width variable.
pyObjectDescriptor.append("O")
pyObjects.append(''', convert_C_port_to_py(«output.name»,«output.name»_width) ''')
}
@@ -2002,10 +1849,10 @@ class PythonGenerator extends CGenerator {
ReactorDecl decl
) {
pyObjectDescriptor.append("O")
-
+
if (definition.widthSpec !== null) {
var String widthSpec = "-2"
- if (JavaAstUtils.isMultiport(input)) {
+ if (ASTUtils.isMultiport(input)) {
widthSpec = '''self->_lf_«definition.name»[i].«input.name»_width'''
}
// Contained reactor is a bank.
@@ -2015,7 +1862,7 @@ class PythonGenerator extends CGenerator {
}
else {
var String widthSpec = "-2"
- if (JavaAstUtils.isMultiport(input)) {
+ if (ASTUtils.isMultiport(input)) {
widthSpec = '''«definition.name».«input.name»_width'''
}
pyObjects.
@@ -2046,16 +1893,16 @@ class PythonGenerator extends CGenerator {
// depending on whether the input is mutable, whether it is a multiport,
// and whether it is a token type.
// Easy case first.
- if (!input.isMutable && !JavaAstUtils.isMultiport(input)) {
+ if (!input.isMutable && !ASTUtils.isMultiport(input)) {
// Non-mutable, non-multiport, primitive type.
pyObjectDescriptor.append("O")
pyObjects.append(''', convert_C_port_to_py(«input.name», «input.name»_width)''')
- } else if (input.isMutable && !JavaAstUtils.isMultiport(input)) {
+ } else if (input.isMutable && !ASTUtils.isMultiport(input)) {
// Mutable, non-multiport, primitive type.
// TODO: handle mutable
pyObjectDescriptor.append("O")
pyObjects.append(''', convert_C_port_to_py(«input.name», «input.name»_width)''')
- } else if (!input.isMutable && JavaAstUtils.isMultiport(input)) {
+ } else if (!input.isMutable && ASTUtils.isMultiport(input)) {
// Non-mutable, multiport, primitive.
// TODO: support multiports
pyObjectDescriptor.append("O")
diff --git a/org.lflang/src/org/lflang/generator/python/PythonTypes.java b/org.lflang/src/org/lflang/generator/python/PythonTypes.java
index c066494047..422044593f 100644
--- a/org.lflang/src/org/lflang/generator/python/PythonTypes.java
+++ b/org.lflang/src/org/lflang/generator/python/PythonTypes.java
@@ -5,22 +5,18 @@
import org.lflang.ErrorReporter;
import org.lflang.InferredType;
import org.lflang.generator.c.CTypes;
+import org.lflang.lf.Initializer;
+import org.lflang.lf.Literal;
+import org.lflang.lf.ParamRef;
+import org.lflang.lf.Type;
public class PythonTypes extends CTypes {
// Regular expression pattern for pointer types. The star at the end has to be visible.
static final Pattern pointerPatternVariable = Pattern.compile("^\\s*+(\\w+)\\s*\\*\\s*$");
+ public static final PythonTypes INSTANCE = new PythonTypes();
- /**
- * Initializes a {@code CTargetTypes} with the given
- * error reporter.
- *
- * @param errorReporter The error reporter for any
- * errors raised in the code
- * generation process.
- */
- public PythonTypes(ErrorReporter errorReporter) {
- super(errorReporter);
+ protected PythonTypes() {
}
@Override
@@ -28,6 +24,22 @@ public String getTargetUndefinedType() {
return "PyObject*";
}
+ @Override
+ public String getTargetLiteral(Literal expr, InferredType type) {
+ switch (expr.getLiteral()) {
+ case "False":
+ case "True":
+ return expr.getLiteral();
+ default:
+ return super.getTargetLiteral(expr, type);
+ }
+ }
+
+ @Override
+ public String getTargetParamRef(ParamRef expr, InferredType type) {
+ return "self." + expr.getParameter().getName();
+ }
+
/**
* This generator inherits types from the CGenerator.
* This function reverts them back to Python types
diff --git a/org.lflang/src/org/lflang/generator/rust/RustModel.kt b/org.lflang/src/org/lflang/generator/rust/RustModel.kt
index 39ad1304d8..8ae583e006 100644
--- a/org.lflang/src/org/lflang/generator/rust/RustModel.kt
+++ b/org.lflang/src/org/lflang/generator/rust/RustModel.kt
@@ -315,7 +315,7 @@ sealed class ReactorComponent {
lfName = v.name,
isLogical = v.isLogical,
dataType = RustTypes.getTargetType(v.type),
- minDelay = v.minDelay?.time?.let(RustTypes::getTargetTimeExpr)
+ minDelay = (v.minDelay as? Time)?.let(RustTypes::getTargetTimeExpr)
)
is Timer -> TimerData(
lfName = v.name,
@@ -326,16 +326,16 @@ sealed class ReactorComponent {
}
private fun Value?.toTimerTimeValue(): TargetCode =
- when {
- this == null -> "Duration::from_millis(0)"
- parameter != null -> "${parameter.name}.clone()"
- literal != null ->
+ when (this) {
+ null -> "Duration::from_millis(0)"
+ is ParamRef -> "${parameter.name}.clone()"
+ is Literal ->
literal.toIntOrNull()
?.let { TimeValue(it.toLong(), DEFAULT_TIME_UNIT_IN_TIMER).toRustTimeExpr() }
?: throw InvalidLfSourceException("Not an integer literal", this)
- time != null -> time.toRustTimeExpr()
- code != null -> code.toText().inBlock()
- else -> RustTypes.getTargetExpr(this, InferredType.time())
+ is Time -> toRustTimeExpr()
+ is CodeExpr -> code.toText().inBlock()
+ else -> RustTypes.getTargetExpr(this, InferredType.time())
}
}
}
@@ -396,7 +396,7 @@ fun WidthSpec.toRustExpr(): String = terms.joinToString(" + ") {
}
fun TimeValue.toRustTimeExpr(): TargetCode = RustTypes.getTargetTimeExpr(this)
-private fun Time.toRustTimeExpr(): TargetCode = this.toTimeValue().toRustTimeExpr()
+private fun Time.toRustTimeExpr(): TargetCode = ASTUtils.toTimeValue(this).toRustTimeExpr()
/** Regex to match a target code block, captures the insides as $1. */
private val TARGET_BLOCK_R = Regex("\\{=(.*)=}", RegexOption.DOT_MATCHES_ALL)
@@ -565,7 +565,7 @@ object RustModelBuilder {
StateVarInfo(
lfName = it.name,
type = RustTypes.getTargetType(it.type, it.init),
- init = RustTypes.getTargetInitializer(it.init, it.type, it.braces.isNotEmpty())
+ init = RustTypes.getTargetInitializer(it.init, it.type),
)
},
nestedInstances = reactor.instantiations.map { it.toModel() },
@@ -574,11 +574,11 @@ object RustModelBuilder {
CtorParamInfo(
lfName = it.name,
type = RustTypes.getTargetType(it.type, it.init),
- defaultValue = RustTypes.getTargetInitializer(it.init, it.type, it.braces.isNotEmpty()),
+ defaultValue = it.init?.let { init -> RustTypes.getTargetInitializer(init, it.type) },
documentation = null, // todo
isTime = it.inferredType.isTime,
isList = it.inferredType.isList,
- defaultValueAsTimeValue = JavaAstUtils.getDefaultAsTimeValue(it),
+ defaultValueAsTimeValue = ASTUtils.getDefaultAsTimeValue(it),
)
}
)
@@ -587,15 +587,16 @@ object RustModelBuilder {
private fun Instantiation.toModel(): NestedReactorInstance {
val byName = parameters.associateBy { it.lhs.name }
- val args = reactor.parameters.associate { ithParam ->
+ val args = reactor.parameters.associate { ithParam: Parameter ->
// use provided argument
- val value = byName[ithParam.name]?.let { RustTypes.getTargetInitializer(it.rhs, ithParam.type, it.isInitWithBraces) }
- ?: if (ithParam.name == "bank_index" && this.isBank) "bank_index" else null // special value
- ?: ithParam?.let { RustTypes.getTargetInitializer(it.init, it.type, it.isInitWithBraces) }
- ?: throw InvalidLfSourceException(
- "Cannot find value of parameter ${ithParam.name}",
- this
- )
+ val value: String =
+ byName[ithParam.name]?.let { RustTypes.getTargetInitializer(it.rhs, ithParam.type) }
+ ?: if (ithParam.name == "bank_index" && this.isBank) "bank_index" else null // special value
+ ?: ithParam.init?.let { RustTypes.getTargetInitializer(it, ithParam.type) }
+ ?: throw InvalidLfSourceException(
+ "Cannot find value of parameter ${ithParam.name}",
+ this
+ )
ithParam.name to value
}
diff --git a/org.lflang/src/org/lflang/generator/rust/RustTypes.kt b/org.lflang/src/org/lflang/generator/rust/RustTypes.kt
index 2401c966e4..01318d38bc 100644
--- a/org.lflang/src/org/lflang/generator/rust/RustTypes.kt
+++ b/org.lflang/src/org/lflang/generator/rust/RustTypes.kt
@@ -29,6 +29,8 @@ import org.lflang.TimeValue
import org.lflang.generator.TargetCode
import org.lflang.generator.TargetTypes
import org.lflang.inBlock
+import org.lflang.lf.CodeExpr
+import org.lflang.lf.Initializer
import org.lflang.lf.Value
import org.lflang.toText
@@ -52,31 +54,30 @@ object RustTypes : TargetTypes {
if (ident in RustKeywords) "r#$ident"
else ident
- override fun getTargetExpr(value: Value, type: InferredType?): String = when {
- // wrap in a block to enable writing several statements
- value.code != null -> value.code.toText().inBlock()
- else -> super.getTargetExpr(value, type)
- }
+ override fun getTargetExpr(value: Value, type: InferredType?): String =
+ when (value) {
+ // wrap in a block to enable writing several statements
+ is CodeExpr -> value.code.toText().inBlock()
+ else -> super.getTargetExpr(value, type)
+ }
override fun getTargetTimeExpr(timeValue: TimeValue): TargetCode = with(timeValue) {
val unit = unit?.canonicalName.orEmpty()
"delay!($magnitude $unit)"
}
- override fun getFixedSizeListInitExpression(
- contents: List,
- listSize: Int,
- withBraces: Boolean
- ): String =
- contents.joinToString(", ", "[", "]")
-
- override fun getVariableSizeListInitExpression(contents: List, withBraces: Boolean): String =
- contents.joinToString(", ", "vec![", "]")
+ override fun getTargetInitializerWithNotExactlyOneValue(init: Initializer, type: InferredType): String =
+ if (type.isFixedSizeList)
+ init.exprs.joinToString(", ", "[", "]") { getTargetExpr(it, type.componentType) }
+ else
+ init.exprs.joinToString(", ", "vec![", "].into()") { getTargetExpr(it, type.componentType) }
override fun getMissingExpr(type: InferredType): String =
- "<${getTargetType(type)} as Default>::default()"
+ "Default::default()"
+
}
+
val RustKeywords = setOf(
// https://doc.rust-lang.org/reference/keywords.html
"as", "break", "const", "continue", "crate", "else",
diff --git a/org.lflang/src/org/lflang/generator/ts/TSActionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSActionGenerator.kt
index d3bb1a2f0a..91581a0298 100644
--- a/org.lflang/src/org/lflang/generator/ts/TSActionGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/ts/TSActionGenerator.kt
@@ -1,6 +1,8 @@
package org.lflang.generator.ts
+import org.lflang.InferredType
import org.lflang.lf.Action
+import org.lflang.lf.ParamRef
import org.lflang.lf.Type
import org.lflang.lf.Value
import java.util.*
@@ -8,13 +10,9 @@ import java.util.*
/**
* Generator for actions in TypeScript target.
*/
-class TSActionGenerator (
- // TODO(hokeun): Remove dependency on TSGenerator.
- private val tsGenerator: TSGenerator,
+class TSActionGenerator(
private val actions: List
) {
- private fun Value.getTargetValue(): String = tsGenerator.getTargetValueW(this)
- private fun Type.getTargetType(): String = tsGenerator.getTargetTypeW(this)
/**
* Return a TS type for the specified action.
@@ -23,19 +21,16 @@ class TSActionGenerator (
* @param action The action
* @return The TS type.
*/
- private fun getActionType(action: Action): String {
+ private fun getActionType(action: Action): String =
// Special handling for the networkMessage action created by
// FedASTUtils.makeCommunication(), by assigning TypeScript
// Buffer type for the action. Action is used as
// FederatePortAction in federation.ts.
if (action.name == "networkMessage") {
- return "Buffer"
- } else if (action.type != null) {
- return action.type.getTargetType()
+ "Buffer"
} else {
- return "Present"
+ TSTypes.getTargetType(action.type)
}
- }
fun generateClassProperties(): String {
val stateClassProperties = LinkedList()
@@ -63,11 +58,7 @@ class TSActionGenerator (
if (action.minDelay != null) {
// Actions in the TypeScript target are constructed
// with an optional minDelay argument which defaults to 0.
- if (action.minDelay.parameter != null) {
- actionArgs+= ", " + action.minDelay.parameter.name
- } else {
- actionArgs+= ", " + action.minDelay.getTargetValue()
- }
+ actionArgs += ", " + TSTypes.getTargetExpr(action.minDelay, InferredType.time())
}
actionInstantiations.add(
"this.${action.name} = new __Action<${getActionType(action)}>($actionArgs);")
@@ -75,4 +66,4 @@ class TSActionGenerator (
}
return actionInstantiations.joinToString("\n")
}
-}
\ No newline at end of file
+}
diff --git a/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt
index 300951c66f..bbe5a5b98f 100644
--- a/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/ts/TSConstructorGenerator.kt
@@ -3,6 +3,7 @@ package org.lflang.generator.ts
import org.lflang.ErrorReporter
import org.lflang.federated.FederateInstance
import org.lflang.generator.PrependOperator
+import org.lflang.inferredType
import org.lflang.lf.Action
import org.lflang.lf.Parameter
import org.lflang.lf.Reactor
@@ -16,37 +17,21 @@ import java.util.*
* and code to register reactions. This generator also generates federate port action
* registrations.
*/
-class TSConstructorGenerator (
+class TSConstructorGenerator(
private val tsGenerator: TSGenerator,
private val errorReporter: ErrorReporter,
- private val reactor : Reactor,
+ private val reactor: Reactor,
private val federate: FederateInstance
) {
- private fun getInitializerList(param: Parameter): List =
- tsGenerator.getInitializerListW(param)
- private fun Parameter.getTargetType(): String = tsGenerator.getTargetTypeW(this)
-
- // Initializer functions
- private fun getTargetInitializerHelper(param: Parameter,
- list: List): String {
- return if (list.size == 0) {
- errorReporter.reportError(param, "Parameters must have a default value!")
- } else if (list.size == 1) {
- list[0]
- } else {
- list.joinToString(", ", "[", "]")
- }
- }
- private fun getTargetInitializer(param: Parameter): String {
- return getTargetInitializerHelper(param, getInitializerList(param))
- }
private fun initializeParameter(p: Parameter): String {
- return """${p.name}: ${p.getTargetType()} = ${getTargetInitializer(p)}"""
+ val type = TSTypes.getTargetType(p.inferredType)
+ val init = TSTypes.getTargetInitializer(p.init, p.type)
+ return "${p.name}: $type = $init"
}
private fun generateConstructorArguments(reactor: Reactor): String {
- val arguments = LinkedList()
+ val arguments = mutableListOf()
if (reactor.isMain || reactor.isFederated) {
arguments.add("timeout: TimeValue | undefined = undefined")
arguments.add("keepAlive: boolean = false")
@@ -69,7 +54,7 @@ class TSConstructorGenerator (
return arguments.joinToString(", \n")
}
- private fun federationRTIProperties(): LinkedHashMap {
+ private fun federationRTIProperties(): Map {
return tsGenerator.federationRTIPropertiesW()
}
@@ -94,36 +79,27 @@ class TSConstructorGenerator (
// If the app is federated, register its
// networkMessageActions with the RTIClient
- private fun generateFederatePortActionRegistrations(networkMessageActions: List): String {
- var fedPortID = 0;
- val connectionInstantiations = LinkedList()
- for (nAction in networkMessageActions) {
- val registration = """
- this.registerFederatePortAction(${fedPortID}, this.${nAction.name});
- """
- connectionInstantiations.add(registration)
- fedPortID++
+ private fun generateFederatePortActionRegistrations(networkMessageActions: List): String =
+ networkMessageActions.withIndex().joinToString("\n") { (fedPortID, nAction) ->
+ "this.registerFederatePortAction($fedPortID, this.${nAction.name});"
}
- return connectionInstantiations.joinToString("\n")
- }
// Generate code for registering Fed IDs that are connected to
// this federate via ports in the TypeScript's FederatedApp.
// These Fed IDs are used to let the RTI know about the connections
// between federates during the initialization with the RTI.
- fun generateFederateConfigurations(): String {
- val federateConfigurations = LinkedList()
- if (reactor.isFederated) {
- for ((key, _) in federate.dependsOn) {
- // FIXME: Get delay properly considering the unit instead of hardcoded BigInt(0).
- federateConfigurations.add("this.addUpstreamFederate(${key.id}, BigInt(0));")
- }
- for ((key, _) in federate.sendsTo) {
- federateConfigurations.add("this.addDownstreamFederate(${key.id});")
+ fun generateFederateConfigurations(): String =
+ buildString {
+ if (reactor.isFederated) {
+ for ((key, _) in federate.dependsOn) {
+ // FIXME: Get delay properly considering the unit instead of hardcoded BigInt(0).
+ this.appendLine("this.addUpstreamFederate(${key.id}, BigInt(0));")
+ }
+ for ((key, _) in federate.sendsTo) {
+ this.appendLine("this.addDownstreamFederate(${key.id});")
+ }
}
}
- return federateConfigurations.joinToString("\n")
- }
fun generateConstructor(
instances: TSInstanceGenerator,
@@ -143,9 +119,9 @@ class TSConstructorGenerator (
|) {
${" | "..generateSuperConstructorCall(reactor, federate)}
${" | "..generateFederateConfigurations()}
+ ${" | "..parameters.generateInstantiations()} // generate this first so that later expressions can use them
${" | "..instances.generateInstantiations()}
${" | "..timers.generateInstantiations()}
- ${" | "..parameters.generateInstantiations()}
${" | "..states.generateInstantiations()}
${" | "..actions.generateInstantiations()}
${" | "..ports.generateInstantiations()}
@@ -156,4 +132,4 @@ class TSConstructorGenerator (
""".trimMargin()
}
}
-}
\ No newline at end of file
+}
diff --git a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt
index 3fe2fd783d..3a1d536122 100644
--- a/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/ts/TSGenerator.kt
@@ -27,28 +27,12 @@ package org.lflang.generator.ts
import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.xtext.util.CancelIndicator
-import org.lflang.ErrorReporter
-import org.lflang.inferredType
-import org.lflang.InferredType
-import org.lflang.TimeValue
-import org.lflang.JavaAstUtils
+import org.lflang.*
import org.lflang.Target
import org.lflang.TargetConfig.Mode
-import org.lflang.federated.launcher.FedTSLauncher
import org.lflang.federated.FederateInstance
-import org.lflang.lf.Action
-import org.lflang.lf.Delay
-import org.lflang.lf.Instantiation
-import org.lflang.lf.Parameter
-import org.lflang.lf.StateVar
-import org.lflang.lf.Type
-import org.lflang.lf.Value
-import org.lflang.lf.VarRef
-import org.lflang.scoping.LFGlobalScopeProvider
-import java.nio.file.Files
-import java.util.LinkedList
+import org.lflang.federated.launcher.FedTSLauncher
import org.lflang.federated.serialization.SupportedSerializers
-import org.lflang.generator.canGenerate
import org.lflang.generator.CodeMap
import org.lflang.generator.GeneratorBase
import org.lflang.generator.GeneratorResult
@@ -56,12 +40,16 @@ import org.lflang.generator.IntegratedBuilder
import org.lflang.generator.JavaGeneratorUtils
import org.lflang.generator.LFGeneratorContext
import org.lflang.generator.PrependOperator
-import org.lflang.generator.TargetTypes
-import org.lflang.generator.ValueGenerator
import org.lflang.generator.SubContext
+import org.lflang.generator.TargetTypes
+import org.lflang.generator.canGenerate
+import org.lflang.lf.Action
+import org.lflang.lf.VarRef
+import org.lflang.scoping.LFGlobalScopeProvider
+import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.StandardCopyOption
-import kotlin.collections.HashMap
+import java.util.*
private const val NO_NPM_MESSAGE = "The TypeScript target requires npm >= 6.14.4. " +
"For installation instructions, see: https://www.npmjs.com/get-npm. \n" +
@@ -82,6 +70,8 @@ class TSGenerator(
private val scopeProvider: LFGlobalScopeProvider
) : GeneratorBase(tsFileConfig, errorReporter) {
+ override fun getTargetTypes(): TargetTypes = TSTypes
+
companion object {
/** Path to the Cpp lib directory (relative to class path) */
const val LIB_PATH = "/lib/ts"
@@ -101,7 +91,6 @@ class TSGenerator(
"reactor.ts", "microtime.d.ts", "nanotimer.d.ts", "time.ts", "ulog.d.ts",
"util.ts")
- private val VG = ValueGenerator(::timeInTargetLanguage) { param -> "this.${param.name}.get()" }
private fun timeInTargetLanguage(value: TimeValue): String {
return if (value.unit != null) {
@@ -128,16 +117,6 @@ class TSGenerator(
// Wrappers to expose GeneratorBase methods.
fun federationRTIPropertiesW() = federationRTIProperties
- fun getTargetValueW(v: Value): String = VG.getTargetValue(v, false)
- fun getTargetTypeW(p: Parameter): String = TSTypes.getTargetType(p.inferredType)
- fun getTargetTypeW(state: StateVar): String = TSTypes.getTargetType(state)
- fun getTargetTypeW(t: Type): String = TSTypes.getTargetType(t)
-
- fun getInitializerListW(state: StateVar): List = VG.getInitializerList(state)
- fun getInitializerListW(param: Parameter): List = VG.getInitializerList(param)
- fun getInitializerListW(param: Parameter, i: Instantiation): List =
- VG.getInitializerList(param, i)
-
/** Generate TypeScript code from the Lingua Franca model contained by the
* specified resource. This is the main entry point for code
* generation.
@@ -258,7 +237,7 @@ class TSGenerator(
targetConfig.protoFiles)
tsCode.append(preambleGenerator.generatePreamble())
- val parameterGenerator = TSParameterPreambleGenerator(this, fileConfig, targetConfig, reactors)
+ val parameterGenerator = TSParameterPreambleGenerator(fileConfig, targetConfig, reactors)
val (mainParameters, parameterCode) = parameterGenerator.generateParameters()
tsCode.append(parameterCode)
@@ -483,22 +462,6 @@ class TSGenerator(
return true
}
- override fun getTargetTypes(): TargetTypes = TSTypes
-
- /**
- * Return a TS type for the specified action.
- * If the type has not been specified, return
- * "Present" which is the base type for Actions.
- * @param action The action
- * @return The TS type.
- */
- private fun getActionType(action: Action): String {
- return if (action.type != null) {
- TSTypes.getTargetType(action.type)
- } else {
- "Present"
- }
- }
/**
* Generate code for the body of a reaction that handles the
@@ -565,7 +528,7 @@ class TSGenerator(
receivingFed: FederateInstance,
type: InferredType,
isPhysical: Boolean,
- delay: Delay?,
+ delay: TimeValue?,
serializer: SupportedSerializers
): String {
return with(PrependOperator) {"""
@@ -596,7 +559,7 @@ class TSGenerator(
receivingFederateID: Int,
sendingBankIndex: Int,
sendingChannelIndex: Int,
- delay: Delay?
+ delay: TimeValue?
): String? {
return with(PrependOperator) {"""
|// TODO(hokeun): Figure out what to do for generateNetworkOutputControlReactionBody
@@ -635,11 +598,11 @@ class TSGenerator(
// Virtual methods.
override fun generateDelayBody(action: Action, port: VarRef): String {
- return "actions.${action.name}.schedule(0, ${JavaAstUtils.generateVarRef(port)} as ${getActionType(action)});"
+ return "actions.${action.name}.schedule(0, ${ASTUtils.generateVarRef(port)} as ${targetTypes.getTargetType(action.type)});"
}
override fun generateForwardBody(action: Action, port: VarRef): String {
- return "${JavaAstUtils.generateVarRef(port)} = ${action.name} as ${getActionType(action)};"
+ return "${ASTUtils.generateVarRef(port)} = ${action.name} as ${targetTypes.getTargetType(action.type)};"
}
override fun generateDelayGeneric(): String {
diff --git a/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt
index dc400c4293..c38eda7b7d 100644
--- a/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/ts/TSInstanceGenerator.kt
@@ -1,8 +1,8 @@
package org.lflang.generator.ts
import org.lflang.federated.FederateInstance
+import org.lflang.generator.getTargetInitializer
import org.lflang.lf.Instantiation
-import org.lflang.lf.Parameter
import org.lflang.lf.Reactor
import org.lflang.toDefinition
import org.lflang.toText
@@ -11,10 +11,7 @@ import java.util.*
/**
* Generator for child reactor instantiations in TypeScript target.
*/
-class TSInstanceGenerator (
- // TODO(hokeun): Remove dependency on TSGenerator.
- private val tsGenerator: TSGenerator,
- private val tsReactorGenerator: TSReactorGenerator,
+class TSInstanceGenerator(
reactor: Reactor,
federate: FederateInstance
) {
@@ -32,13 +29,6 @@ class TSInstanceGenerator (
}
}
- private fun getInitializerList(param: Parameter, i: Instantiation): List =
- tsGenerator.getInitializerListW(param, i)
-
- private fun getTargetInitializer(param: Parameter, i: Instantiation): String {
- return tsReactorGenerator.getTargetInitializerHelper(param, getInitializerList(param, i))
- }
-
fun generateClassProperties(): String {
val childReactorClassProperties = LinkedList()
for (childReactor in childReactors) {
@@ -52,7 +42,7 @@ class TSInstanceGenerator (
fun generateInstantiations(): String {
val childReactorInstantiations = LinkedList()
for (childReactor in childReactors) {
- val childReactorArguments = StringJoiner(", ");
+ val childReactorArguments = StringJoiner(", ")
childReactorArguments.add("this")
// Iterate through parameters in the order they appear in the
@@ -60,7 +50,7 @@ class TSInstanceGenerator (
// the reactor instance, and write the corresponding parameter
// value as an argument for the TypeScript constructor
for (parameter in childReactor.reactorClass.toDefinition().parameters) {
- childReactorArguments.add(getTargetInitializer(parameter, childReactor))
+ childReactorArguments.add(TSTypes.getTargetInitializer(parameter, childReactor))
}
childReactorInstantiations.add(
@@ -68,4 +58,4 @@ class TSInstanceGenerator (
}
return childReactorInstantiations.joinToString("\n")
}
-}
\ No newline at end of file
+}
diff --git a/org.lflang/src/org/lflang/generator/ts/TSParameterGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSParameterGenerator.kt
index 1cd987b815..f5d1a13684 100644
--- a/org.lflang/src/org/lflang/generator/ts/TSParameterGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/ts/TSParameterGenerator.kt
@@ -1,23 +1,20 @@
package org.lflang.generator.ts
-import org.lflang.generator.PrependOperator
+import org.lflang.inferredType
import org.lflang.lf.Parameter
import java.util.*
/**
* Generate parameters for TypeScript target.
*/
-class TSParameterGenerator (
- // TODO(hokeun): Remove dependency on TSGenerator.
- private val tsGenerator: TSGenerator,
+class TSParameterGenerator(
private val parameters: List
- ) {
- private fun Parameter.getTargetType(): String = tsGenerator.getTargetTypeW(this)
+) {
fun generateClassProperties(): String {
val paramClassProperties = LinkedList()
for (param in parameters) {
- paramClassProperties.add("${param.name}: __Parameter<${param.getTargetType()}>;")
+ paramClassProperties.add("${param.name}: __Parameter<${TSTypes.getTargetType(param.inferredType)}>;")
}
return paramClassProperties.joinToString("\n")
}
@@ -29,4 +26,4 @@ class TSParameterGenerator (
}
return paramInstantiations.joinToString("\n")
}
-}
\ No newline at end of file
+}
diff --git a/org.lflang/src/org/lflang/generator/ts/TSParameterPreambleGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSParameterPreambleGenerator.kt
index c012b62644..ad1040744f 100644
--- a/org.lflang/src/org/lflang/generator/ts/TSParameterPreambleGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/ts/TSParameterPreambleGenerator.kt
@@ -29,6 +29,7 @@ import org.lflang.FileConfig
import org.lflang.TargetConfig
import org.lflang.TimeValue
import org.lflang.generator.PrependOperator
+import org.lflang.inferredType
import org.lflang.lf.Parameter
import org.lflang.lf.Reactor
import org.lflang.TimeUnit
@@ -45,12 +46,11 @@ import java.util.StringJoiner
*/
class TSParameterPreambleGenerator(
- private val tsGenerator: TSGenerator,
private val fileConfig: FileConfig,
private val targetConfig: TargetConfig,
private val reactors: MutableList
) {
- private fun getTargetType(p: Parameter): String = tsGenerator.getTargetTypeW(p)
+ private fun getTargetType(p: Parameter): String = TSTypes.getTargetType(p.inferredType)
private fun getTimeoutTimeValue(): String {
return if (targetConfig.timeout != null) {
@@ -85,7 +85,8 @@ class TSParameterPreambleGenerator(
private fun assignCustomCLArgs(mainParameters: HashSet): String {
val code = StringJoiner("\n")
for (parameter in mainParameters) {
- code.add("""
+ code.add(
+ """
|let __CL${parameter.name}: ${getTargetType(parameter)} | undefined = undefined;
|if (__processedCLArgs.${parameter.name} !== undefined) {
| if (__processedCLArgs.${parameter.name} !== null) {
@@ -95,7 +96,8 @@ class TSParameterPreambleGenerator(
| throw new Error("Custom '${parameter.name}' command line argument is malformed.");
| }
|}
- """)
+ """
+ )
}
return code.toString()
}
@@ -111,11 +113,13 @@ class TSParameterPreambleGenerator(
// to cause the generation of variables with a "__" prefix
// because they could collide with other variables.
// So prefix variables created here with __CL
- code.add("""
+ code.add(
+ """
|if (__processedCLArgs.${parameter.name} !== undefined && __processedCLArgs.${parameter.name} !== null
| && !__noStart) {
| Log.global.info("'${parameter.name}' property overridden by command line argument.");
- |}""")
+ |}"""
+ )
}
return code.toString()
}
@@ -157,20 +161,24 @@ class TSParameterPreambleGenerator(
if (customArgType != null) {
clTypeExtension.add(parameter.name + ": " + paramType)
if (customTypeLabel != null) {
- customArgs.add(with(PrependOperator) {"""
+ customArgs.add(with(PrependOperator) {
+ """
|{
| name: '${parameter.name}',
| type: ${customArgType},
| typeLabel: "{underline ${customTypeLabel}}",
| description: 'Custom argument. Refer to ${fileConfig.srcFile} for documentation.'
- |}""".trimMargin()})
+ |}""".trimMargin()
+ })
} else {
- customArgs.add(with(PrependOperator) {"""
+ customArgs.add(with(PrependOperator) {
+ """
|{
| name: '${parameter.name}',
| type: ${customArgType},
| description: 'Custom argument. Refer to ${fileConfig.srcFile} for documentation.'
- |}""".trimMargin()})
+ |}""".trimMargin()
+ })
}
}
}
@@ -178,7 +186,8 @@ class TSParameterPreambleGenerator(
val customArgsList = "[\n$customArgs]"
val clTypeExtensionDef = "{$clTypeExtension}"
- val codeText = with(PrependOperator) {"""
+ val codeText = with(PrependOperator) {
+ """
|// ************* App Parameters
|let __timeout: TimeValue | undefined = ${getTimeoutTimeValue()};
|let __keepAlive: boolean = ${targetConfig.keepalive};
diff --git a/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt
index 35bcfa42df..d4de160c11 100644
--- a/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/ts/TSPortGenerator.kt
@@ -1,56 +1,37 @@
package org.lflang.generator.ts
+import org.lflang.generator.ts.TSTypes.getTargetType
import org.lflang.lf.Input
import org.lflang.lf.Output
-import org.lflang.lf.Port
-import org.lflang.lf.Type
import java.util.*
/**
* Generate input and output ports for TypeScript target.
*/
-class TSPortGenerator (
- // TODO(hokeun): Remove dependency on TSGenerator.
- private val tsGenerator: TSGenerator,
+class TSPortGenerator(
private val inputs: List ,
private val outputs: List
) {
- private fun Type.getTargetType(): String = tsGenerator.getTargetTypeW(this)
-
- /**
- * Return a TS type for the specified port.
- * If the type has not been specified, return
- * "Present" which is the base type for ports.
- * @param port The port
- * @return The TS type.
- */
- private fun getPortType(port: Port): String {
- if (port.type != null) {
- return port.type.getTargetType()
- } else {
- return "Present"
- }
- }
fun generateClassProperties(): String {
- val portClassProperties = LinkedList()
+ val portClassProperties = mutableListOf()
for (input in inputs) {
- portClassProperties.add("${input.name}: __InPort<${getPortType(input)}>;")
+ portClassProperties.add("${input.name}: __InPort<${getTargetType(input.type)}>;")
}
for (output in outputs) {
- portClassProperties.add("${output.name}: __OutPort<${getPortType(output)}>;")
+ portClassProperties.add("${output.name}: __OutPort<${getTargetType(output.type)}>;")
}
return portClassProperties.joinToString("\n")
}
fun generateInstantiations(): String {
- val porInstantiations = LinkedList()
+ val porInstantiations = mutableListOf()
for (input in inputs) {
- porInstantiations.add("this.${input.name} = new __InPort<${getPortType(input)}>(this);")
+ porInstantiations.add("this.${input.name} = new __InPort<${getTargetType(input.type)}>(this);")
}
for (output in outputs) {
- porInstantiations.add("this.${output.name} = new __OutPort<${getPortType(output)}>(this);")
+ porInstantiations.add("this.${output.name} = new __OutPort<${getTargetType(output.type)}>(this);")
}
return porInstantiations.joinToString("\n")
}
-}
\ No newline at end of file
+}
diff --git a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt
index e82f5693e7..87eff7738f 100644
--- a/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt
@@ -1,9 +1,12 @@
package org.lflang.generator.ts
import org.lflang.ErrorReporter
-import org.lflang.JavaAstUtils
+import org.lflang.ASTUtils
import org.lflang.federated.FederateInstance
import org.lflang.generator.PrependOperator
+import org.lflang.generator.getTargetTimeExpr
+import org.lflang.generator.orZero
+import org.lflang.inferredType
import org.lflang.lf.*
import org.lflang.lf.Timer
import org.lflang.toText
@@ -23,45 +26,12 @@ class TSReactionGenerator(
// TODO(hokeun): Remove dependency on TSGenerator.
private val tsGenerator: TSGenerator,
private val errorReporter: ErrorReporter,
- private val reactor : Reactor,
+ private val reactor: Reactor,
private val federate: FederateInstance
) {
- private fun Value.getTargetValue(): String = tsGenerator.getTargetValueW(this)
- private fun Parameter.getTargetType(): String = tsGenerator.getTargetTypeW(this)
- private fun StateVar.getTargetType(): String = tsGenerator.getTargetTypeW(this)
- private fun Type.getTargetType(): String = tsGenerator.getTargetTypeW(this)
-
- private fun VarRef.generateVarRef(): String = JavaAstUtils.generateVarRef(this)
-
- /**
- * Return a TS type for the specified action.
- * If the type has not been specified, return
- * "Present" which is the base type for Actions.
- * @param action The action
- * @return The TS type.
- */
- private fun getActionType(action: Action): String {
- if (action.type != null) {
- return action.type.getTargetType()
- } else {
- return "Present"
- }
- }
- /**
- * Return a TS type for the specified port.
- * If the type has not been specified, return
- * "Present" which is the base type for ports.
- * @param port The port
- * @return The TS type.
- */
- private fun getPortType(port: Port): String {
- if (port.type != null) {
- return port.type.getTargetType()
- } else {
- return "Present"
- }
- }
+ private fun VarRef.generateVarRef(): String = ASTUtils.generateVarRef(this)
+
private fun generateArg(v: VarRef): String {
return if (v.container != null) {
@@ -77,12 +47,7 @@ class TSReactionGenerator(
reactEpilogue: String,
reactSignature: StringJoiner
): String {
- var deadlineArgs = ""
- if (reaction.deadline.delay.parameter != null) {
- deadlineArgs += "this.${reaction.deadline.delay.parameter.name}.get()";
- } else {
- deadlineArgs += reaction.deadline.delay.getTargetValue()
- }
+ val deadlineArgs = TSTypes.getTargetTimeExpr(reaction.deadline.delay.orZero())
return with(PrependOperator) {
"""
@@ -140,14 +105,21 @@ class TSReactionGenerator(
${" | "..reactEpilogue}
| // =============== END react epilogue
| }
- ${" | "..if (reaction.deadline != null) generateDeadlineHandler(reaction, reactPrologue, reactEpilogue, reactSignature) else "}"}
+ ${
+ " | "..if (reaction.deadline != null) generateDeadlineHandler(
+ reaction,
+ reactPrologue,
+ reactEpilogue,
+ reactSignature
+ ) else "}"
+ }
|);
""".trimMargin()
}
}
// TODO(hokeun): Decompose this function further.
- private fun generateSingleReaction(reactor : Reactor, reaction: Reaction): String {
+ private fun generateSingleReaction(reactor: Reactor, reaction: Reaction): String {
// Determine signature of the react function
val reactSignature = StringJoiner(", ")
reactSignature.add("this")
@@ -222,12 +194,12 @@ class TSReactionGenerator(
} else if (trigOrSource.variable is Timer) {
reactSignatureElementType = "__Tag"
} else if (trigOrSource.variable is Action) {
- reactSignatureElementType = getActionType(trigOrSource.variable as Action)
+ reactSignatureElementType = TSTypes.getTargetType((trigOrSource.variable as Action).inferredType)
} else if (trigOrSource.variable is Port) {
- reactSignatureElementType = getPortType(trigOrSource.variable as Port)
+ reactSignatureElementType = TSTypes.getTargetType((trigOrSource.variable as Port).inferredType)
}
- reactSignature.add("${generateArg(trigOrSource)}: Read<${reactSignatureElementType}>")
+ reactSignature.add("${generateArg(trigOrSource)}: Read<$reactSignatureElementType>")
reactFunctArgs.add("this.${trigOrSource.generateVarRef()}")
if (trigOrSource.container == null) {
reactPrologue.add("let ${trigOrSource.variable.name} = ${generateArg(trigOrSource)}.get();")
@@ -253,23 +225,25 @@ class TSReactionGenerator(
var reactSignatureElement = generateArg(effect)
if (effect.variable is Timer) {
errorReporter.reportError("A timer cannot be an effect of a reaction")
- } else if (effect.variable is Action){
- reactSignatureElement += ": Sched<" + getActionType(effect.variable as Action) + ">"
+ } else if (effect.variable is Action) {
+ reactSignatureElement += ": Sched<" + TSTypes.getTargetType((effect.variable as Action).inferredType) + ">"
schedActionSet.add(effect.variable as Action)
- } else if (effect.variable is Port){
- reactSignatureElement += ": ReadWrite<" + getPortType(effect.variable as Port) + ">"
+ } else if (effect.variable is Port) {
+ reactSignatureElement += ": ReadWrite<" + TSTypes.getTargetType((effect.variable as Port).inferredType) + ">"
if (effect.container == null) {
- reactEpilogue.add(with(PrependOperator) {"""
+ reactEpilogue.add(with(PrependOperator) {
+ """
|if (${effect.variable.name} !== undefined) {
| __${effect.variable.name}.set(${effect.variable.name});
- |}""".trimMargin()})
+ |}""".trimMargin()
+ })
}
}
reactSignature.add(reactSignatureElement)
functArg = "this." + effect.generateVarRef()
- if (effect.variable is Action){
+ if (effect.variable is Action) {
reactFunctArgs.add("this.schedulable($functArg)")
} else if (effect.variable is Port) {
reactFunctArgs.add("this.writable($functArg)")
@@ -303,7 +277,7 @@ class TSReactionGenerator(
for (param in reactor.parameters) {
// Underscores are added to parameter names to prevent conflict with prologue
- reactSignature.add("__${param.name}: __Parameter<${param.getTargetType()}>")
+ reactSignature.add("__${param.name}: __Parameter<${TSTypes.getTargetType(param.inferredType)}>")
reactFunctArgs.add("this.${param.name}")
reactPrologue.add("let ${param.name} = __${param.name}.get();")
@@ -312,14 +286,16 @@ class TSReactionGenerator(
// Add state to the react function
for (state in reactor.stateVars) {
// Underscores are added to state names to prevent conflict with prologue
- reactSignature.add("__${state.name}: __State<${state.getTargetType()}>")
+ reactSignature.add("__${state.name}: __State<${TSTypes.getTargetType(state)}>")
reactFunctArgs.add("this.${state.name}")
reactPrologue.add("let ${state.name} = __${state.name}.get();")
- reactEpilogue.add(with(PrependOperator) {"""
+ reactEpilogue.add(with(PrependOperator) {
+ """
|if (${state.name} !== undefined) {
| __${state.name}.set(${state.name});
- |}""".trimMargin()})
+ |}""".trimMargin()
+ })
}
// Initialize objects to enable hierarchical references.
@@ -328,10 +304,12 @@ class TSReactionGenerator(
for (variable in entry.value) {
initializer.add("${variable.name}: __${entry.key.name}_${variable.name}.get()")
if (variable is Input) {
- reactEpilogue.add(with(PrependOperator) {"""
+ reactEpilogue.add(with(PrependOperator) {
+ """
|if (${entry.key.name}.${variable.name} !== undefined) {
| __${entry.key.name}_${variable.name}.set(${entry.key.name}.${variable.name})
- |}""".trimMargin()})
+ |}""".trimMargin()
+ })
}
}
reactPrologue.add("let ${entry.key.name} = {${initializer}}")
@@ -360,7 +338,8 @@ class TSReactionGenerator(
// Do not add reactions created by generateNetworkOutputControlReactionBody
// or generateNetworkInputControlReactionBody.
if (reaction.code.toText().contains("generateNetworkOutputControlReactionBody")
- || reaction.code.toText().contains("generateNetworkInputControlReactionBody")) {
+ || reaction.code.toText().contains("generateNetworkInputControlReactionBody")
+ ) {
continue;
}
if (federate.contains(reaction)) {
@@ -379,4 +358,4 @@ class TSReactionGenerator(
}
return reactionCodes.joinToString("\n")
}
-}
\ No newline at end of file
+}
diff --git a/org.lflang/src/org/lflang/generator/ts/TSReactorGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSReactorGenerator.kt
index d4a8b2b9cb..be760646a7 100644
--- a/org.lflang/src/org/lflang/generator/ts/TSReactorGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/ts/TSReactorGenerator.kt
@@ -113,12 +113,12 @@ class TSReactorGenerator(
"export class $reactorName extends __Reactor {"
}
- val instanceGenerator = TSInstanceGenerator(tsGenerator, this, reactor, federate)
- val timerGenerator = TSTimerGenerator(tsGenerator, reactor.timers)
- val parameterGenerator = TSParameterGenerator(tsGenerator, reactor.parameters)
- val stateGenerator = TSStateGenerator(tsGenerator, reactor.stateVars)
- val actionGenerator = TSActionGenerator(tsGenerator, reactor.actions)
- val portGenerator = TSPortGenerator(tsGenerator, reactor.inputs, reactor.outputs)
+ val instanceGenerator = TSInstanceGenerator(reactor, federate)
+ val timerGenerator = TSTimerGenerator(reactor.timers)
+ val parameterGenerator = TSParameterGenerator(reactor.parameters)
+ val stateGenerator = TSStateGenerator(reactor.stateVars)
+ val actionGenerator = TSActionGenerator(reactor.actions)
+ val portGenerator = TSPortGenerator(reactor.inputs, reactor.outputs)
val constructorGenerator = TSConstructorGenerator(tsGenerator, errorReporter, reactor, federate)
return with(PrependOperator) {
@@ -151,4 +151,4 @@ class TSReactorGenerator(
"""
}.trimMargin()
}
-}
\ No newline at end of file
+}
diff --git a/org.lflang/src/org/lflang/generator/ts/TSStateGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSStateGenerator.kt
index 58ed7d4eee..9010baa4ae 100644
--- a/org.lflang/src/org/lflang/generator/ts/TSStateGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/ts/TSStateGenerator.kt
@@ -1,42 +1,24 @@
package org.lflang.generator.ts
-import org.lflang.ASTUtils
+import org.lflang.generator.getTargetInitializer
import org.lflang.lf.StateVar
import java.util.*
/**
* Generator for state variables in TypeScript target.
*/
-class TSStateGenerator (
- private val tsGenerator: TSGenerator,
+class TSStateGenerator(
private val stateVars: List
) {
- private fun StateVar.getTargetType(): String = tsGenerator.getTargetTypeW(this)
- fun generateClassProperties(): String {
- val stateClassProperties = LinkedList()
- for (stateVar in stateVars) {
- stateClassProperties.add("${stateVar.name}: __State<${stateVar.getTargetType()}>;");
+ fun generateClassProperties(): String =
+ stateVars.joinToString("\n") {
+ "${it.name}: __State<${TSTypes.getTargetType(it)}>;"
}
- return stateClassProperties.joinToString("\n")
- }
- private fun getInitializerList(state: StateVar): List =
- tsGenerator.getInitializerListW(state)
-
- private fun getTargetInitializer(state: StateVar): String {
- return getInitializerList(state).joinToString(",")
- }
- fun generateInstantiations(): String {
- val stateInstantiations = LinkedList()
- // Next handle states.
- for (stateVar in stateVars) {
- if (ASTUtils.isInitialized(stateVar)) {
- stateInstantiations.add("this.${stateVar.name} = new __State(${getTargetInitializer(stateVar)});");
- } else {
- stateInstantiations.add("this.${stateVar.name} = new __State(undefined);");
- }
+ fun generateInstantiations(): String =
+ stateVars.joinToString("\n") {
+ val init = if (it.init != null) TSTypes.getTargetInitializer(it) else "undefined"
+ "this.${it.name} = new __State($init);"
}
- return stateInstantiations.joinToString("\n")
- }
-}
\ No newline at end of file
+}
diff --git a/org.lflang/src/org/lflang/generator/ts/TSTimerGenerator.kt b/org.lflang/src/org/lflang/generator/ts/TSTimerGenerator.kt
index 4039716297..1d7118128d 100644
--- a/org.lflang/src/org/lflang/generator/ts/TSTimerGenerator.kt
+++ b/org.lflang/src/org/lflang/generator/ts/TSTimerGenerator.kt
@@ -1,22 +1,19 @@
package org.lflang.generator.ts;
-import org.lflang.generator.PrependOperator
+import org.lflang.generator.getTargetTimeExpr
+import org.lflang.generator.orZero
import org.lflang.lf.Timer
-import org.lflang.lf.Value
import java.util.*
/**
* Generator timers for TypeScript target.
*/
class TSTimerGenerator (
- // TODO(hokeun): Remove dependency on TSGenerator.
- private val tsGenerator: TSGenerator,
private val timers: List
) {
- private fun Value.getTargetValue(): String = tsGenerator.getTargetValueW(this)
fun generateClassProperties(): String {
- val timerClassProperties = LinkedList()
+ val timerClassProperties = mutableListOf()
for (timer in timers) {
timerClassProperties.add("${timer.name}: __Timer;")
}
@@ -24,10 +21,10 @@ class TSTimerGenerator (
}
fun generateInstantiations(): String {
- val timerInstantiations = LinkedList()
+ val timerInstantiations = mutableListOf()
for (timer in timers) {
- val timerPeriod: String = timer.period?.getTargetValue() ?: "0"
- val timerOffset: String = timer.offset?.getTargetValue() ?: "0"
+ val timerPeriod: String = TSTypes.getTargetTimeExpr(timer.period.orZero())
+ val timerOffset: String = TSTypes.getTargetTimeExpr(timer.offset.orZero())
timerInstantiations.add("this.${timer.name} = new __Timer(this, $timerOffset, $timerPeriod);")
}
diff --git a/org.lflang/src/org/lflang/generator/ts/TSTypes.kt b/org.lflang/src/org/lflang/generator/ts/TSTypes.kt
index d996f5638e..8fa5b61fbd 100644
--- a/org.lflang/src/org/lflang/generator/ts/TSTypes.kt
+++ b/org.lflang/src/org/lflang/generator/ts/TSTypes.kt
@@ -1,14 +1,30 @@
package org.lflang.generator.ts
-import org.lflang.ASTUtils
+import org.lflang.InferredType
+import org.lflang.TimeValue
import org.lflang.generator.TargetTypes
+import org.lflang.inferredType
+import org.lflang.lf.Initializer
+import org.lflang.lf.ParamRef
import org.lflang.lf.StateVar
+/**
+ * [TargetTypes] implementation for [TSGenerator].
+ *
+ * @author Peter Donovan
+ * @author Clément Fournier
+ * @author Hokeun Kim
+ */
+
object TSTypes : TargetTypes {
+ /**
+ * Returns a type for the state variable. Note that this is TS-specific
+ * and not equivalent to `s.type.targetType`.
+ */
override fun getTargetType(s: StateVar): String {
- val type = super.getTargetType(s)
- return if (!ASTUtils.isInitialized(s)) {
+ val type = getTargetType(s.inferredType)
+ return if (s.init == null) {
"$type | undefined"
} else {
type
@@ -38,4 +54,21 @@ object TSTypes : TargetTypes {
override fun getTargetVariableSizeListType(baseType: String): String {
return "Array<$baseType>"
}
-}
\ No newline at end of file
+
+
+ override fun getTargetTimeExpr(value: TimeValue): String =
+ with(value) {
+ if (unit != null) "TimeValue.${unit.canonicalName}($magnitude)"
+ // The value must be zero.
+ else "TimeValue.zero()"
+ }
+
+ override fun getTargetParamRef(expr: ParamRef, type: InferredType?): String =
+ "this.${expr.parameter.name}.get()"
+
+ override fun getTargetInitializerWithNotExactlyOneValue(init: Initializer, type: InferredType): String =
+ init.exprs.joinToString(", ", "[", "]") {
+ getTargetExpr(it, type.componentType)
+ }
+
+}
diff --git a/org.lflang/src/org/lflang/validation/LFValidator.java b/org.lflang/src/org/lflang/validation/LFValidator.java
index 8a95bf8f72..54190ba0be 100644
--- a/org.lflang/src/org/lflang/validation/LFValidator.java
+++ b/org.lflang/src/org/lflang/validation/LFValidator.java
@@ -34,7 +34,12 @@
import static org.lflang.ASTUtils.isZero;
import static org.lflang.ASTUtils.toDefinition;
import static org.lflang.ASTUtils.toText;
-import static org.lflang.JavaAstUtils.isOfTimeType;
+import static org.lflang.ASTUtils.inferPortWidth;
+import static org.lflang.ASTUtils.isGeneric;
+import static org.lflang.ASTUtils.isValidTime;
+import static org.lflang.ASTUtils.toDefinition;
+import static org.lflang.ASTUtils.toText;
+import static org.lflang.ASTUtils.isOfTimeType;
import java.io.IOException;
import java.util.ArrayList;
@@ -45,6 +50,8 @@
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
+import java.util.Optional;
+import java.util.Set;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
@@ -57,7 +64,8 @@
import org.eclipse.xtext.validation.ValidationMessageAcceptor;
import org.lflang.ASTUtils;
import org.lflang.FileConfig;
-import org.lflang.JavaAstUtils;
+import org.lflang.InferredType;
+import org.lflang.ASTUtils;
import org.lflang.ModelInfo;
import org.lflang.Target;
import org.lflang.TargetProperty;
@@ -66,7 +74,11 @@
import org.lflang.generator.NamedInstance;
import org.lflang.lf.Action;
import org.lflang.lf.ActionOrigin;
+import org.lflang.lf.AddExpr;
import org.lflang.lf.Assignment;
+import org.lflang.lf.BraceExpr;
+import org.lflang.lf.BracketExpr;
+import org.lflang.lf.CodeExpr;
import org.lflang.lf.Connection;
import org.lflang.lf.Deadline;
import org.lflang.lf.Host;
@@ -74,14 +86,18 @@
import org.lflang.lf.IPV6Host;
import org.lflang.lf.Import;
import org.lflang.lf.ImportedReactor;
+import org.lflang.lf.Initializer;
import org.lflang.lf.Input;
import org.lflang.lf.Instantiation;
import org.lflang.lf.KeyValuePair;
import org.lflang.lf.KeyValuePairs;
import org.lflang.lf.LfPackage.Literals;
+import org.lflang.lf.Literal;
import org.lflang.lf.Model;
+import org.lflang.lf.MulExpr;
import org.lflang.lf.NamedHost;
import org.lflang.lf.Output;
+import org.lflang.lf.ParamRef;
import org.lflang.lf.Parameter;
import org.lflang.lf.Port;
import org.lflang.lf.Preamble;
@@ -92,8 +108,10 @@
import org.lflang.lf.Serializer;
import org.lflang.lf.StateVar;
import org.lflang.lf.TargetDecl;
+import org.lflang.lf.Time;
import org.lflang.lf.Timer;
import org.lflang.lf.TriggerRef;
+import org.lflang.lf.TupleExpr;
import org.lflang.lf.Type;
import org.lflang.lf.TypedVariable;
import org.lflang.lf.Value;
@@ -121,11 +139,11 @@ public class LFValidator extends BaseLFValidator {
//////////////////////////////////////////////////////////////
//// Public check methods.
-
+
// These methods are automatically invoked on AST nodes matching
// the types of their arguments.
// CheckType.FAST ensures that these checks run whenever a file is modified.
- // Alternatives are CheckType.NORMAL (when saving) and
+ // Alternatives are CheckType.NORMAL (when saving) and
// CheckType.EXPENSIVE (only when right-click, validate).
// FIXME: What is the default when nothing is specified?
@@ -156,26 +174,24 @@ public void checkAction(Action action) {
public void checkAssignment(Assignment assignment) {
// If the left-hand side is a time parameter, make sure the assignment has units
if (isOfTimeType(assignment.getLhs())) {
- if (assignment.getRhs().size() > 1) {
- error("Incompatible type.", Literals.ASSIGNMENT__RHS);
- } else if (assignment.getRhs().size() > 0) {
- Value v = assignment.getRhs().get(0);
- if (!isValidTime(v)) {
- if (v.getParameter() == null) {
- // This is a value. Check that units are present.
- error(
- "Missing time unit.", Literals.ASSIGNMENT__RHS);
+ if (ASTUtils.isList(assignment.getRhs())) {
+ error("Incompatible type.", Literals.ASSIGNMENT__RHS);
+ } else {
+ var v = ASTUtils.asSingleValue(assignment.getRhs());
+ if (v != null && !isValidTime(v)) {
+ if (v instanceof ParamRef) {
+ error("Cannot assign parameter: "
+ + ((ParamRef) v).getParameter().getName()
+ + " to " + assignment.getLhs().getName()
+ + ". The latter is a time parameter, but the former is not.",
+ Literals.ASSIGNMENT__RHS);
} else {
- // This is a reference to another parameter. Report problem.
- error(
- "Cannot assign parameter: " +
- v.getParameter().getName() + " to " +
- assignment.getLhs().getName() +
- ". The latter is a time parameter, but the former is not.",
- Literals.ASSIGNMENT__RHS);
+ // This is a value. Check that units are present.
+ error("Missing time unit.", Literals.ASSIGNMENT__RHS);
}
}
}
+
// If this assignment overrides a parameter that is used in a deadline,
// report possible overflow.
if (isCBasedTarget() &&
@@ -187,19 +203,52 @@ public void checkAssignment(Assignment assignment) {
}
}
- EList braces = assignment.getBraces();
- if(!(braces == null || braces.isEmpty()) && this.target != Target.CPP) {
- error("Brace initializers are only supported for the C++ target", Literals.ASSIGNMENT__BRACES);
- }
-
// FIXME: lhs is list => rhs is list
// lhs is fixed with size n => rhs is fixed with size n
// FIXME": similar checks for decl/init
// Specifically for C: list can only be literal or time lists
}
+ @Check(CheckType.FAST)
+ public void checkBracketExpr(BracketExpr list) {
+ if (!target.supportsLfBracketListExpressions()) {
+ error("Target " + target
+ + " does not support this expression form.",
+ Literals.BRACKET_EXPR__ITEMS);
+ }
+ }
+
+ @Check(CheckType.FAST)
+ public void checkBraceExpr(BraceExpr list) {
+ if (!target.supportsLfBraceListExpressions()) {
+ error("Target " + target
+ + " does not support this expression form.",
+ Literals.BRACE_EXPR__ITEMS);
+ }
+ }
+
+ @Check(CheckType.FAST)
+ public void checkTupleLiteral(TupleExpr expr) {
+ if (expr.getItems().size() == 1 && !expr.isTrailingComma()) {
+ throw new AssertionError(
+ "this parenthesized expr should not have been parsed as a tuple");
+ }
+ if (!target.supportsLfTupleLiterals()) {
+ if (expr.getItems().size() == 1)
+ error("Target " + target
+ + " does not support tuple literals. You might want to remove the trailing comma.",
+ Literals.TUPLE_EXPR__ITEMS);
+ else
+ error("Target " + target + " does not support tuple literals.",
+ Literals.TUPLE_EXPR__ITEMS);
+ }
+ }
+
@Check(CheckType.FAST)
public void checkConnection(Connection connection) {
+ if (connection.getDelay() != null) {
+ checkValueIsTime(connection.getDelay().getValue(), Literals.CONNECTION__DELAY);
+ }
// Report if connection is part of a cycle.
Set> cycles = this.info.topologyCycles();
@@ -351,6 +400,9 @@ public void checkConnection(Connection connection) {
@Check(CheckType.FAST)
public void checkDeadline(Deadline deadline) {
+
+ checkValueIsTime(deadline.getDelay(), Literals.DEADLINE__DELAY);
+
if (isCBasedTarget() &&
this.info.overflowingDeadlines.contains(deadline)) {
error(
@@ -370,6 +422,8 @@ public void checkHost(Host host) {
Literals.HOST__USER
);
}
+ // TODO Soroush can you help with this? Where did the STP go?
+ // checkValueIsTime(host.get, Literals.STP__VALUE);
if (host instanceof IPV4Host && !addr.matches(IPV4_REGEX)) {
warning(
"Invalid IP address.",
@@ -387,7 +441,7 @@ public void checkHost(Host host) {
);
}
}
-
+
@Check
public void checkImport(Import imp) {
if (toDefinition(imp.getReactorClasses().get(0)).eResource().getErrors().size() > 0) {
@@ -403,7 +457,7 @@ public void checkImport(Import imp) {
}
warning("Unused import.", Literals.IMPORT__IMPORT_URI);
}
-
+
@Check
public void checkImportedReactor(ImportedReactor reactor) {
if (isUnused(reactor)) {
@@ -417,7 +471,7 @@ public void checkImportedReactor(ImportedReactor reactor) {
cycleSet.addAll(cycle);
}
if (dependsOnCycle(toDefinition(reactor), cycleSet, new HashSet<>())) {
- error("Imported reactor '" + toDefinition(reactor).getName() +
+ error("Imported reactor '" + toDefinition(reactor).getName() +
"' has cyclic instantiation in it.", Literals.IMPORTED_REACTOR__REACTOR_CLASS);
}
}
@@ -579,48 +633,26 @@ public void checkOutput(Output output) {
public void checkParameter(Parameter param) {
checkName(param.getName(), Literals.PARAMETER__NAME);
- for (Value it : param.getInit()) {
- if (it.getParameter() != null) {
- // Initialization using parameters is forbidden.
- error("Parameter cannot be initialized using parameter.",
- Literals.PARAMETER__INIT);
- }
- }
-
- if (param.getInit() == null || param.getInit().size() == 0) {
+ if (param.getInit() == null || param.getInit().getExprs().size() == 0) {
// All parameters must be initialized.
error("Uninitialized parameter.", Literals.PARAMETER__INIT);
- } else if (isOfTimeType(param)) {
- // We do additional checks on types because we can make stronger
- // assumptions about them.
-
- // If the parameter is not a list, cannot be initialized
- // using a one.
- if (param.getInit().size() > 1 && param.getType().getArraySpec() == null) {
- error("Time parameter cannot be initialized using a list.",
- Literals.PARAMETER__INIT);
- } else {
- // The parameter is a singleton time.
- Value init = param.getInit().get(0);
- if (init.getTime() == null) {
- if (init != null && !isZero(init)) {
- if (isInteger(init)) {
- error("Missing time unit.", Literals.PARAMETER__INIT);
- } else {
- error("Invalid time literal.",
- Literals.PARAMETER__INIT);
- }
- }
- } // If time is not null, we know that a unit is also specified.
- }
- } else if (this.target.requiresTypes) {
- // Report missing target type. param.inferredType.undefine
- if ((JavaAstUtils.getInferredType(param).isUndefined())) {
+ return;
+ } else if (param.getInit().getExprs().stream()
+ .anyMatch(it -> it instanceof ParamRef)) {
+ error("Parameter cannot be initialized using parameter.",
+ Literals.PARAMETER__INIT);
+ } else {
+ typeCheck(param.getInit(), ASTUtils.getInferredType(param), Literals.PARAMETER__INIT);
+ }
+
+ if (this.target.requiresTypes) {
+ // Report missing target type.
+ if (ASTUtils.getInferredType(param).isUndefined()) {
error("Type declaration missing.", Literals.PARAMETER__TYPE);
}
}
- if(this.target == Target.CPP) {
+ if (this.target == Target.CPP) {
EObject container = param.eContainer();
Reactor reactor = (Reactor) container;
if(reactor.isMain()){
@@ -640,13 +672,9 @@ public void checkParameter(Parameter param) {
TimeValue.MAX_LONG_DEADLINE + " nanoseconds.",
Literals.PARAMETER__INIT);
}
-
- EList braces = param.getBraces();
- if(!(braces == null || braces.isEmpty()) && this.target != Target.CPP) {
- error("Brace initializers are only supported for the C++ target", Literals.PARAMETER__BRACES);
- }
}
+
@Check(CheckType.FAST)
public void checkPreamble(Preamble preamble) {
if (this.target == Target.CPP) {
@@ -940,7 +968,7 @@ public void checkReactor(Reactor reactor) throws IOException {
names.add(it.getName());
}
error(
- String.format("Cannot extend %s due to the following conflicts: %s.",
+ String.format("Cannot extend %s due to the following conflicts: %s.",
superClass.getName(), String.join(",", names)),
Literals.REACTOR__SUPER_CLASSES
);
@@ -972,52 +1000,25 @@ public void checkSerializer(Serializer serializer) {
public void checkState(StateVar stateVar) {
checkName(stateVar.getName(), Literals.STATE_VAR__NAME);
- if (isOfTimeType(stateVar)) {
- // If the state is declared to be a time,
- // make sure that it is initialized correctly.
- if (stateVar.getInit() != null) {
- for (Value init : stateVar.getInit()) {
- if (stateVar.getType() != null && stateVar.getType().isTime() &&
- !isValidTime(init)) {
- if (isParameterized(stateVar)) {
- error(
- "Referenced parameter does not denote a time.",
- Literals.STATE_VAR__INIT);
- } else {
- if (init != null && !isZero(init)) {
- if (isInteger(init)) {
- error(
- "Missing time unit.", Literals.STATE_VAR__INIT);
- } else {
- error("Invalid time literal.",
- Literals.STATE_VAR__INIT);
- }
- }
- }
- }
- }
- }
- } else if (this.target.requiresTypes && (JavaAstUtils.getInferredType(stateVar)).isUndefined()) {
+ if (stateVar.getInit() != null && stateVar.getInit().getExprs().size() != 0) {
+ typeCheck(stateVar.getInit(), ASTUtils.getInferredType(stateVar), Literals.STATE_VAR__INIT);
+ }
+
+ if (this.target.requiresTypes
+ && ASTUtils.getInferredType(stateVar).isUndefined()) {
// Report if a type is missing
error("State must have a type.", Literals.STATE_VAR__TYPE);
}
- if (isCBasedTarget() && stateVar.getInit().size() > 1) {
+ if (isCBasedTarget() && ASTUtils.isList(stateVar.getInit())) {
// In C, if initialization is done with a list, elements cannot
// refer to parameters.
- for (Value it : stateVar.getInit()) {
- if (it.getParameter() != null) {
- error("List items cannot refer to a parameter.",
+ if (stateVar.getInit().getExprs().stream()
+ .anyMatch(it -> it instanceof ParamRef)) {
+ error("List items cannot refer to a parameter.",
Literals.STATE_VAR__INIT);
- break;
- }
}
}
-
- EList braces = stateVar.getBraces();
- if(!(braces == null || braces.isEmpty()) && this.target != Target.CPP) {
- error("Brace initializers are only supported for the C++ target", Literals.STATE_VAR__BRACES);
- }
}
@Check(CheckType.FAST)
@@ -1111,9 +1112,110 @@ public void checkTargetProperties(KeyValuePairs targetProperties) {
}
}
+ @Check(CheckType.FAST)
+ public void checkAddExpr(AddExpr e) {
+ checkValidArithmeticExpr(e, Literals.ADD_EXPR__LEFT);
+ }
+
+ @Check(CheckType.FAST)
+ public void checkMulExpr(MulExpr e) {
+ checkValidArithmeticExpr(e, Literals.MUL_EXPR__LEFT);
+ }
+
+ /**
+ * Check the form of an arithmetic expression.
+ * todo would be nice to allow `time + time` and `int * time`.
+ * But we'd need to build up our typing infrastructure.
+ */
+ public void checkValidArithmeticExpr(Value e, EStructuralFeature feature) {
+ if (e instanceof AddExpr) {
+ checkValidArithmeticExpr(((AddExpr)e).getLeft(), feature);
+ checkValidArithmeticExpr(((AddExpr)e).getRight(), feature);
+ } else if (e instanceof MulExpr) {
+ checkValidArithmeticExpr(((AddExpr)e).getLeft(), feature);
+ checkValidArithmeticExpr(((AddExpr)e).getRight(), feature);
+ } else if (e instanceof Literal || e instanceof CodeExpr || e instanceof ParamRef) {
+ // Assume it's ok, language will check validity later.
+ // Eg in Python, the following is fine:
+ // 2 * "a"
+ // or in C/Java/C++:
+ // c - 'a'
+ } else {
+ error("Unexpected operand in arithmetic expression.", feature);
+ }
+ }
+
+ /**
+ * Check that the initializer is compatible with the type of value.
+ * Note that if the type is inferred it will necessarily be compatible
+ * so this method is not harmful.
+ */
+ public void typeCheck(Initializer init, InferredType type, EStructuralFeature feature) {
+ if (init == null) return;
+
+ if (type.isTime) {
+ if (type.isList) {
+ // list of times
+ var exprs = init.getExprs();
+ if (init.isAssign()) {
+ var list = ASTUtils.asSingleValue(init);
+ if (list instanceof BracketExpr) exprs = ((BracketExpr)list).getItems();
+ if (list instanceof BraceExpr) exprs = ((BraceExpr)list).getItems();
+ else if (list instanceof CodeExpr) return; // cannot check it
+ else {
+ error("Expected a list of time values.", feature);
+ return;
+ }
+ }
+ for (var component : exprs) {
+ checkValueIsTime(component, feature);
+ }
+ } else {
+ checkValueIsTime(init, feature);
+ }
+ }
+ }
+
+ public void checkValueIsTime(Initializer init, EStructuralFeature feature) {
+ if (init == null) return;
+
+ if (init.getExprs().size() != 1) {
+ error("Expected exactly one time value.", feature);
+ } else {
+ checkValueIsTime(ASTUtils.asSingleValue(init), feature);
+ }
+ }
+
+ public void checkValueIsTime(Value value, EStructuralFeature feature) {
+ if (value == null) return;
+
+ if (value instanceof ParamRef) {
+ if (!(ASTUtils.isOfTimeType(((ParamRef)value).getParameter())) && target.requiresTypes) {
+ error("Referenced parameter does not have time type.", feature);
+ }
+ return;
+ } else if (value instanceof Literal) {
+ if (ASTUtils.isZero(((Literal)value).getLiteral())) return;
+
+ if (ASTUtils.isInteger(((Literal)value).getLiteral())) {
+ error("Missing time unit.", feature);
+ }
+ } else if (value instanceof CodeExpr) {
+ if (ASTUtils.isZero(((CodeExpr) value).getCode())) {
+ return;
+ }
+ error("Invalid time literal.", feature);
+ } else if (!(value instanceof Time)) {
+ error("Invalid time literal.", feature);
+ }
+ }
+
+
@Check(CheckType.FAST)
public void checkTimer(Timer timer) {
checkName(timer.getName(), Literals.VARIABLE__NAME);
+ checkValueIsTime(timer.getOffset(), Literals.TIMER__OFFSET);
+ checkValueIsTime(timer.getPeriod(), Literals.TIMER__PERIOD);
}
@Check(CheckType.FAST)
@@ -1141,33 +1243,6 @@ else if (this.target == Target.Python) {
}
}
}
-
- @Check(CheckType.FAST)
- public void checkValueAsTime(Value value) {
- EObject container = value.eContainer();
-
- if (container instanceof Timer || container instanceof Action ||
- container instanceof Connection || container instanceof Deadline) {
- // If parameter is referenced, check that it is of the correct type.
- if (value.getParameter() != null) {
- if (!isOfTimeType(value.getParameter()) && target.requiresTypes) {
- error("Parameter is not of time type",
- Literals.VALUE__PARAMETER);
- }
- } else if (value.getTime() == null) {
- if (value.getLiteral() != null && !isZero(value.getLiteral())) {
- if (isInteger(value.getLiteral())) {
- error("Missing time unit.", Literals.VALUE__LITERAL);
- } else {
- error("Invalid time literal.",
- Literals.VALUE__LITERAL);
- }
- } else if (value.getCode() != null) {
- error("Invalid time literal.", Literals.VALUE__CODE);
- }
- }
- }
- }
@Check(CheckType.FAST)
public void checkVarRef(VarRef varRef) {
@@ -1221,13 +1296,13 @@ public void checkWidthSpec(WidthSpec widthSpec) {
//////////////////////////////////////////////////////////////
//// Public methods.
- /**
- * Return the error reporter for this validator.
+ /**
+ * Return the error reporter for this validator.
*/
public ValidatorErrorReporter getErrorReporter() {
return this.errorReporter;
}
-
+
/**
* Implementation required by xtext to report validation errors.
*/
@@ -1235,7 +1310,7 @@ public ValidatorErrorReporter getErrorReporter() {
public ValidationMessageAcceptor getMessageAcceptor() {
return messageAcceptor == null ? this : messageAcceptor;
}
-
+
/**
* Return a list of error messages for the target declaration.
*/
@@ -1245,7 +1320,7 @@ public List getTargetPropertyErrors() {
//////////////////////////////////////////////////////////////
//// Private methods.
-
+
/**
* For each input, report a conflict if:
* 1) the input exists and the type doesn't match; or
@@ -1384,7 +1459,7 @@ private boolean isCBasedTarget() {
private boolean isUnused(ImportedReactor reactor) {
TreeIterator instantiations = reactor.eResource().getAllContents();
TreeIterator subclasses = reactor.eResource().getAllContents();
-
+
boolean instantiationsCheck = true;
while (instantiations.hasNext() && instantiationsCheck) {
EObject obj = instantiations.next();
@@ -1465,14 +1540,14 @@ private boolean sameType(Type type1, Type type2) {
//////////////////////////////////////////////////////////////
//// Private static constants.
- private static String ACTIONS_MESSAGE
+ private static String ACTIONS_MESSAGE
= "\"actions\" is a reserved word for the TypeScript target for objects "
+ "(inputs, outputs, actions, timers, parameters, state, reactor definitions, "
+ "and reactor instantiation): ";
- private static String HOST_OR_FQN_REGEX
+ private static String HOST_OR_FQN_REGEX
= "^([a-z0-9]+(-[a-z0-9]+)*)|(([a-z0-9]+(-[a-z0-9]+)*\\.)+[a-z]{2,})$";
-
+
/**
* Regular expression to check the validity of IPV4 addresses (due to David M. Syzdek).
*/
@@ -1501,11 +1576,11 @@ private boolean sameType(Type type1, Type type2) {
private static String RESERVED_MESSAGE = "Reserved words in the target language are not allowed for objects "
+ "(inputs, outputs, actions, timers, parameters, state, reactor definitions, and reactor instantiation): ";
-
+
private static List SPACING_VIOLATION_POLICIES = List.of("defer", "drop", "replace");
-
+
private static String UNDERSCORE_MESSAGE = "Names of objects (inputs, outputs, actions, timers, parameters, "
+ "state, reactor definitions, and reactor instantiation) may not start with \"__\": ";
-
+
private static String USERNAME_REGEX = "^[a-z_]([a-z0-9_-]{0,31}|[a-z0-9_-]{0,30}\\$)$";
}
diff --git a/test/C/src/ArrayAsParameter.lf b/test/C/src/ArrayAsParameter.lf
index 48c84be597..3e2f448db5 100644
--- a/test/C/src/ArrayAsParameter.lf
+++ b/test/C/src/ArrayAsParameter.lf
@@ -35,7 +35,7 @@ reactor Print {
=}
}
main reactor ArrayAsParameter {
- s = new Source(sequence = (1, 2, 3, 4), n_sequence=4);
+ s = new Source(sequence(1, 2, 3, 4), n_sequence=4);
p = new Print();
s.out -> p.in;
-}
\ No newline at end of file
+}
diff --git a/test/C/src/Initializers.lf b/test/C/src/Initializers.lf
new file mode 100644
index 0000000000..8ba604b5bc
--- /dev/null
+++ b/test/C/src/Initializers.lf
@@ -0,0 +1,37 @@
+target C;
+
+reactor Foo(
+ p1: int[](4, 2),
+ p2: int[](4, 2),
+
+ p4: string("a")
+) {
+ state s0: int = 2 - 2 - 2; state s1: int(2 - 2 - 2);
+ state s2: string = "a"; state s4: string("a");
+ state s3: char(2 + 'a'); state s5: char = 2 + 'a';
+
+ preamble{=
+#include "assert.h"
+ =}
+
+ reaction(startup) {=
+ assert(p1[0] == 4); assert(p1[1] == 2);
+ assert(p2[0] == 3); assert(p2[1] == 5);
+
+ assert(s0 == -2);
+ assert(s1 == -2);
+
+ assert(strcmp(s2, "a"));
+ assert(strcmp(s4, "a"));
+ assert(strcmp(p4, "a"));
+
+ assert(s3 == 'c');
+ assert(s5 == 'c');
+
+ printf("Success\n");
+ =}
+}
+
+main reactor {
+ foo = new Foo(p2(3,5));
+}
diff --git a/test/C/src/MovingAverage.lf b/test/C/src/MovingAverage.lf
index 2adbf63925..d5cbc1f924 100644
--- a/test/C/src/MovingAverage.lf
+++ b/test/C/src/MovingAverage.lf
@@ -45,7 +45,7 @@ reactor MovingAverageImpl {
main reactor MovingAverage {
s = new MASource();
m = new MovingAverageImpl();
- p = new TestDouble(expected=(0.0, 0.25, 0.75, 1.5, 2.5, 3.5));
+ p = new TestDouble(expected(0.0, 0.25, 0.75, 1.5, 2.5, 3.5));
s.out -> m.in;
m.out -> p.in;
-}
\ No newline at end of file
+}
diff --git a/test/C/src/NativeListsAndTimes.lf b/test/C/src/NativeListsAndTimes.lf
index d75b511779..e835c27d9c 100644
--- a/test/C/src/NativeListsAndTimes.lf
+++ b/test/C/src/NativeListsAndTimes.lf
@@ -6,9 +6,12 @@ main reactor(x:int(0),
y:time(0), // Units are missing but not required
z(1 msec), // Type is missing but not required
p:int[](1, 2, 3, 4), // List of integers
+ p2:int[] = { 1, 2, 3, 4 }, // List of integers
q:interval_t[](1 msec, 2 msec, 3 msec), // list of time values
+ q2:interval_t[] = { 1 msec, 2 msec, 3 msec }, // list of time values
r:time({=0=}), // Zero-valued target code also is a valid time
- g:time[](1 msec, 2 msec) // List of time values
+ g:time[](1 msec, 2 msec), // List of time values
+ g2:time[] = {1 msec, 2 msec} // List of time values
) {
state s:time(y); // Reference to explicitly typed time parameter
state t:time(z); // Reference to implicitly typed time parameter
@@ -19,7 +22,10 @@ main reactor(x:int(0),
timer toe(z); // Implicit type time
state baz(p); // Implicit type int[]
state period(z); // Implicit type time
+
+ state intArrayState: int[] = {1, 2}; // Array of int values
+
reaction(tick) {=
// Target code
=}
-}
\ No newline at end of file
+}
diff --git a/test/Cpp/src/Initializers.lf b/test/Cpp/src/Initializers.lf
new file mode 100644
index 0000000000..31c47bd523
--- /dev/null
+++ b/test/Cpp/src/Initializers.lf
@@ -0,0 +1,45 @@
+target Cpp;
+
+reactor Foo(
+ p1: std::vector(4, 2), // list containing [2,2,2,2]
+ p2: std::vector{4, 2}, // list containing [4,2]
+
+ p3: std::vector(4, 2), // list containing [2,2,2,2]
+ p4: std::vector{4, 2} // list containing [4,2]
+
+ // p5: std::vector = (4, 2) // disallowed
+) {
+ state s0: int = 2 - 2 - 2; state s1: int(2 - 2 - 2);
+ state s2: std::string = "a"; state s3: char(2 + 'a');
+
+ state s4: Point{1, 2};
+ state s4p: Point = {1, 2};
+
+ private preamble{=
+#include
+ =}
+
+ public preamble {=
+struct Point { int x; int y; };
+ =}
+
+ reaction(startup) {=
+ assert(p1 == std::vector(4, 2));
+ assert(p2 == (std::vector { 4, 2 }));
+ assert(p3 == std::vector(3, 5));
+ assert(p4 == (std::vector { 3, 5 }));
+
+ assert(s0 == -2);
+ assert(s1 == -2);
+ assert(s2 == "a");
+ assert(s3 == 'c');
+ assert(s4 == (Point {1, 2}));
+ assert(s4p == (Point {1, 2}));
+
+ std::cerr << "Success\n";
+ =}
+}
+
+main reactor {
+ foo = new Foo(p3(3,5), p4{3,5});
+}
diff --git a/test/Cpp/src/NativeListsAndTimes.lf b/test/Cpp/src/NativeListsAndTimes.lf
index 205b8d6c34..69bafc8242 100644
--- a/test/Cpp/src/NativeListsAndTimes.lf
+++ b/test/Cpp/src/NativeListsAndTimes.lf
@@ -6,9 +6,11 @@ reactor Foo(x:int(0),
y:time(0), // Units are missing but not required
z(1 msec), // Type is missing but not required
p:int[]{1, 2, 3, 4}, // List of integers
+ p2:int[] = {1, 2, 3, 4}, // List of integers
q:{=std::vector=}{1 msec, 2 msec, 3 msec}, // list of time values
r:time({=0=}), // Zero-valued target code also is a valid time
- g:time[]{1 msec, 2 msec} // List of time values
+ g:time[]{1 msec, 2 msec}, // List of time values
+ g2:time[] = {1 msec, 2 msec} // List of time values
) {
state s:time(y); // Reference to explicitly typed time parameter
state t:time(z); // Reference to implicitly typed time parameter
@@ -19,6 +21,11 @@ reactor Foo(x:int(0),
timer toe(z); // Implicit type time
state baz(p); // Implicit type int[]
state period(z); // Implicit type time
+ state period2 = z; // Implicit type time
+
+ state timeListState: time[] = {1 msec, 2 msec}; // Vector of time values
+ state intArrayState: int[] = {1, 2}; // Vector of int values
+
state times: std::vector>{q, g}; // a list of lists
reaction(tick) {=
// Target code
diff --git a/test/Cpp/src/target/BraceAndParenInitialization.lf b/test/Cpp/src/target/BraceAndParenInitialization.lf
index 46df6e86ee..4d0c457344 100644
--- a/test/Cpp/src/target/BraceAndParenInitialization.lf
+++ b/test/Cpp/src/target/BraceAndParenInitialization.lf
@@ -4,19 +4,35 @@ reactor Foo(
param_list_1: std::vector(4, 2), // list containing [2,2,2,2]
param_list_2: std::vector{4, 2}, // list containing [4,2]
param_list_3: std::vector(4, 2), // list containing [2,2,2,2]
- param_list_4: std::vector{4, 2} // list containing [4,2]
+ param_list_4: std::vector{4, 2}, // list containing [4,2]
+
+ param_list_5: std::vector = {= std::vector(4, 2) =} // list containing [2,2,2,2]
) {
state state_list_1: std::vector(6,42); // list containing [42,42,42,42,42,42]
state state_list_2: std::vector{6,42}; // list containing [6,42]
+ state state_list_eq: std::vector = {= std::vector(4, 2) =}; // list containing [2,2,2,2]
+
+ state state_int: int = 22;
+ state state_int_mul: int = 22 * 5;
+ state state_str: std::string = "-----";
+ state state_str_paren: std::string("-----");
+
reaction(startup) {=
- std::cerr << "Hello!\n";
+ std::cerr << "Hello!\n";
if (param_list_1.size() != 4 || param_list_1[0] != 2 ||
param_list_2.size() != 2 || param_list_2[0] != 4 ||
param_list_3.size() != 3 || param_list_3[0] != 5 ||
param_list_4.size() != 2 || param_list_4[0] != 3 ||
+ param_list_5.size() != 4 || param_list_5[0] != 2 ||
state_list_1.size() != 6 || state_list_1[0] != 42 ||
- state_list_2.size() != 2 || state_list_2[0] != 6) {
+ state_list_2.size() != 2 || state_list_2[0] != 6 ||
+ state_list_eq.size() != 4 || state_list_eq[0] != 2 ||
+ state_int != 22 ||
+ state_int_mul != 22 * 5 ||
+ state_str != "-----" ||
+ state_str_paren != "-----"
+ ) {
std::cerr << "Error!\n";
exit(1);
}
diff --git a/test/Cpp/src/target/GenericParameterDefaultValue.lf b/test/Cpp/src/target/GenericParameterDefaultValue.lf
new file mode 100644
index 0000000000..860564e064
--- /dev/null
+++ b/test/Cpp/src/target/GenericParameterDefaultValue.lf
@@ -0,0 +1,15 @@
+target Cpp;
+
+reactor Foo (bar:T(0)) {
+
+ reaction (startup) {=
+ if (bar != 0) {
+ std::cerr << "ERROR: Expected bar=0 but got bar=" << bar << '\n';
+ exit(1);
+ }
+ =}
+}
+
+main reactor {
+ foo = new Foo() // bar takes a default value
+}
diff --git a/test/Python/src/ArrayAsParameter.lf b/test/Python/src/ArrayAsParameter.lf
index 8c08da4ea1..d23116ede3 100644
--- a/test/Python/src/ArrayAsParameter.lf
+++ b/test/Python/src/ArrayAsParameter.lf
@@ -1,7 +1,7 @@
// Source has an array as a parameter, the elements of which it passes
// to Print.
target Python;
-reactor Source(sequence(0, 1, 2)) {
+reactor Source(sequence([0, 1, 2])) {
output out;
state count(0);
logical action next;
@@ -31,7 +31,7 @@ reactor Print {
=}
}
main reactor ArrayAsParameter {
- s = new Source(sequence = (1, 2, 3, 4));
+ s = new Source(sequence = [1, 2, 3, 4]);
p = new Print();
s.out -> p._in;
-}
\ No newline at end of file
+}
diff --git a/test/Python/src/ArrayAsType.lf b/test/Python/src/ArrayAsType.lf
index ea2cc5c2c2..e4a7cb6412 100644
--- a/test/Python/src/ArrayAsType.lf
+++ b/test/Python/src/ArrayAsType.lf
@@ -1,5 +1,5 @@
-// Source produces a statically allocated array, which it passes
-// to Print. The destination references the array directly.
+// Source produces a tuple, which it passes to Print.
+// The destination references the array directly.
target Python;
reactor Source {
output out;
diff --git a/test/Python/src/CompositionGain.lf b/test/Python/src/CompositionGain.lf
index 8d223b1c12..5c7615de30 100644
--- a/test/Python/src/CompositionGain.lf
+++ b/test/Python/src/CompositionGain.lf
@@ -4,7 +4,7 @@ reactor Gain {
input gainin;
output y;
reaction(gainin) -> y {=
- print("Gain received " + str(gainin.value))
+ undefined_name15291838("Gain received " + str(gainin.value))
y.set(gainin.value * 2)
=}
}
@@ -26,4 +26,4 @@ main reactor {
sys.stderr.write("ERROR: Received value should have been ", str(42 * 2))
exit(2)
=}
-}
\ No newline at end of file
+}
diff --git a/test/Python/src/Initializers.lf b/test/Python/src/Initializers.lf
new file mode 100644
index 0000000000..947a97c86d
--- /dev/null
+++ b/test/Python/src/Initializers.lf
@@ -0,0 +1,72 @@
+target Python;
+
+reactor Sub(seq([1,2])) {}
+
+reactor Foo(
+ p1(4, 2), // tuple
+ p2 = (4, 2), // tuple
+
+ p3([4, 2]), // list
+ p4 = [4, 2], // list
+
+ p5("a"),
+ p6 = "a",
+
+ p12 = 2
+) {
+ state s0 = 2 - 2 - 2; state s1(2 - 2 - 2);
+ # todo # state s0 = 2 - 2 - p12; state s1(2 - p12 - 2);
+ state s2 = "a"; state s4("a");
+ # state s3(2 + 'a'); state s5 = 2 + 'a';
+
+ reaction(startup) {=
+ assert self.p1 == (4, 2);
+ assert self.p2 == (3, 5);
+ assert self.p3 == [4, 2];
+ assert self.p4 == [4, 2];
+
+ assert self.s0 == -2;
+ assert self.s1 == -2;
+
+ assert self.s2 == "a";
+ assert self.s4 == "a";
+ assert self.p5 == "a";
+ assert self.p6 == "a";
+
+ assert self.t0 == ()
+ assert self.t02 == ()
+ assert self.t1 == (1,)
+ assert self.t12 == (1,)
+ assert self.t2 == (1,2)
+ assert self.t22 == (1,2)
+ assert self.justin == 1
+ assert self.justin2 == 1
+
+ if __debug__: # otherwise asserts are noop
+ print("Success\n")
+ =}
+
+
+ state t0 = (); // empty tuple
+ state t02(); // empty tuple
+
+ state t1 = (1,); // tuple of 1
+ state t12(1,); // tuple of 1
+
+ state t2 = (1,2); // tuple of 2
+ state t22(1,2); // tuple of 2
+
+ state justin = (1); // just 1
+ state justin2(1); // just 1
+
+ sub = new Sub(seq = ());
+ sub2 = new Sub(seq = (1,));
+ sub3 = new Sub(seq = (1, 2));
+ sub4 = new Sub(seq((1, 2,)));
+
+
+}
+
+main reactor {
+ foo = new Foo(p2(3,5));
+}
diff --git a/test/Python/src/MovingAverage.lf b/test/Python/src/MovingAverage.lf
index 38d8e3ef44..96220d544f 100644
--- a/test/Python/src/MovingAverage.lf
+++ b/test/Python/src/MovingAverage.lf
@@ -18,7 +18,7 @@ reactor MASource {
=}
}
reactor MovingAverageImpl {
- state delay_line(0.0, 0.0, 0.0);
+ state delay_line([0.0, 0.0, 0.0]);
state index(0);
input m_in;
output out;
@@ -46,4 +46,4 @@ main reactor MovingAverage {
p = new TestDouble(expected=(0.0, 0.25, 0.75, 1.5, 2.5, 3.5));
s.out -> m.m_in;
m.out -> p.t_in;
-}
\ No newline at end of file
+}
diff --git a/test/Python/src/NativeListsAndTimes.lf b/test/Python/src/NativeListsAndTimes.lf
index 8f88e07ac7..19c9b3d353 100644
--- a/test/Python/src/NativeListsAndTimes.lf
+++ b/test/Python/src/NativeListsAndTimes.lf
@@ -5,10 +5,11 @@
main reactor(x(0),
y(0), // Units are missing but not required
z(1 msec), // Type is missing but not required
- p(1, 2, 3, 4), // List of integers
- q(1 msec, 2 msec, 3 msec), // list of time values
- r({=0=}), // Zero-valued target code also is a valid time
- g(1 msec, 2 msec) // List of time values
+ p(1, 2, 3, 4), // tuple of integers
+ pList([1, 2, 3, 4]), // List of integers
+ q(1 msec, 2 msec, 3 msec), // tuple of time values
+ qList([1 msec, 2 msec, 3 msec]), // list of time values
+ r({=0=}) // Zero-valued target code also is a valid time
) {
state s(y); // Reference to explicitly typed time parameter
state t(z); // Reference to implicitly typed time parameter
@@ -18,10 +19,24 @@ main reactor(x(0),
timer tock(1 sec); // Implicit type time
timer toe(z); // Implicit type time
state baz(p); // Implicit type int[]
+ state bazList(pList);
state period(z); // Implicit type time
- state bar(1 msec, 2 msec, 3 msec); // list of time values
- state notype(1, 2, 3, 4);
- reaction(tick) {=
- # Target code
+ state bar(1 msec, 2 msec, 3 msec); // tuple of time values
+ state barList([1 msec, 2 msec, 3 msec]); // list of time values
+ state notype(1, 2, 3, 4); // tuple
+ state notypeList([1, 2, 3, 4]); // list
+
+ reaction(startup) {=
+ assert self.notype == (1,2,3,4)
+ assert self.p == (1,2,3,4)
+ assert self.pList == [1,2,3,4]
+ assert self.pList != (1,2,3,4)
+ assert self.notypeList != (1,2,3,4)
+ assert self.notypeList == [1,2,3,4]
+ assert self.baz == (1,2,3,4)
+ assert self.bazList == [1,2,3,4]
+
+ assert self.bar == (MSEC(1),MSEC(2),MSEC(3))
+ assert self.barList == [MSEC(1),MSEC(2),MSEC(3)]
=}
-}
\ No newline at end of file
+}
diff --git a/test/Python/src/target/ListLiterals.lf b/test/Python/src/target/ListLiterals.lf
new file mode 100644
index 0000000000..28bd69968b
--- /dev/null
+++ b/test/Python/src/target/ListLiterals.lf
@@ -0,0 +1,28 @@
+// This example illustrates state variables and parameters on the wiki.
+// For this test, success is just compiling and running.
+target Python;
+
+
+reactor Sub(seq([1,2])) {
+
+}
+
+main reactor {
+ state l12([1,2]);
+ state l12_trailing([1,2,]);
+ state empty([]);
+ state empty2 = [];
+ // doesn't parse // state empty2([,]);
+
+ sub = new Sub(seq = [1, 2]);
+ sub2 = new Sub(seq = (1, 2));
+ sub3 = new Sub(seq = ([1, 2]));
+
+ reaction(startup) {=
+ assert self.empty == []
+ assert self.empty2 == []
+ assert self.l12 == [1, 2]
+ assert self.l12 == self.l12_trailing
+ print("Success")
+ =}
+}