Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adjust grammar to distinguish paranthesis and brace based initialization in C++ #399

Merged
merged 17 commits into from
Jul 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ crashlytics-build.properties
fabric.properties

### Maven ###
target/
/org.lflang*/target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
Expand Down
61 changes: 9 additions & 52 deletions org.lflang/src/org/lflang/ASTUtils.xtend
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ import org.lflang.lf.Value
import org.lflang.lf.VarRef
import org.lflang.lf.WidthSpec

import static extension org.eclipse.emf.ecore.util.EcoreUtil.*

/**
* A helper class for modifying and analyzing the AST.
* @author{Marten Lohstroh <[email protected]>}
Expand Down Expand Up @@ -415,56 +417,7 @@ class ASTUtils {
}
return name + suffix
}

/**
* Given a "type" AST node, return a deep copy of that node.
* @param original The original to create a deep copy of.
* @return A deep copy of the given AST node.
*/
private static def getCopy(TypeParm original) {
val clone = factory.createTypeParm
if (!original.literal.isNullOrEmpty) {
clone.literal = original.literal
} else if (original.code !== null) {
clone.code = factory.createCode
clone.code.body = original.code.body
}
return clone
}

/**
* Given a "type" AST node, return a deep copy of that node.
* @param original The original to create a deep copy of.
* @return A deep copy of the given AST node.
*/
static def getCopy(Type original) {
if (original !== null) {
val clone = factory.createType

clone.id = original.id
// Set the type based on the argument type.
if (original.code !== null) {
clone.code = factory.createCode
clone.code.body = original.code.body
}
if (original.stars !== null) {
original.stars?.forEach[clone.stars.add(it)]
}

if (original.arraySpec !== null) {
clone.arraySpec = factory.createArraySpec
clone.arraySpec.ofVariableLength = original.arraySpec.
ofVariableLength
clone.arraySpec.length = original.arraySpec.length
}
clone.time = original.time

original.typeParms?.forEach[parm | clone.typeParms.add(parm.copy)]

return clone
}
}


////////////////////////////////
//// Utility functions for supporting inheritance

Expand Down Expand Up @@ -888,7 +841,11 @@ class ASTUtils {
for (s : type.stars ?: emptyList) {
stars += s
}
return type.id + stars
if (!type.typeParms.isNullOrEmpty) {
return '''«type.id»<«FOR p : type.typeParms SEPARATOR ", "»«p.toText»«ENDFOR»>'''
} else {
return type.id + stars
}
}
}
}
Expand Down Expand Up @@ -1499,7 +1456,7 @@ class ASTUtils {
* @return True if the variable was initialized, false otherwise.
*/
def static boolean isInitialized(StateVar v) {
if (v !== null && v.parens.size == 2) {
if (v !== null && (v.parens.size == 2 || v.braces.size == 2)) {
return true
}
return false
Expand Down
2 changes: 1 addition & 1 deletion org.lflang/src/org/lflang/AstExtensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ val Port.inferredType: InferredType get() = ASTUtils.getInferredType(this)
* @receiver The state variable to be checked.
* @return True if the variable was initialized, false otherwise.
*/
val StateVar.isInitialized: Boolean get() = (this.parens.size == 2)
val StateVar.isInitialized: Boolean get() = (this.parens.size == 2 || this.braces.size == 2)

/**
* Given the width specification of port or instantiation
Expand Down
21 changes: 15 additions & 6 deletions org.lflang/src/org/lflang/LinguaFranca.xtext
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ TargetDecl:
StateVar:
'state' name=ID (
(':' (type=Type))?
(parens+='(' (init+=Value
(',' init+=Value)*)? parens+=')'
((parens+='(' (init+=Value (',' init+=Value)*)? parens+=')')
| (braces+='{' (init+=Value (',' init+=Value)*)? braces+='}')
)?
) ';'?
;
Expand Down Expand Up @@ -267,15 +267,21 @@ VarRef:
| container=[Instantiation] '.' variable=[Variable];

Assignment:
(lhs=[Parameter] (('=' rhs+=Value) | ( '='? '(' (rhs+=Value(',' rhs+=Value)*)? ')')));
(lhs=[Parameter] (
(equals='=' rhs+=Value)
| ((equals='=')? (
parens+='(' (rhs+=Value (',' rhs+=Value)*)? parens+=')'
| braces+='{' (rhs+=Value (',' rhs+=Value)*)? braces+='}'))
));

/**
* Parameter declaration with optional type and mandatory initialization.
*/
Parameter:
name=ID (':' (type=Type))?
(parens+='(' (init+=Value // FIXME: rename Value to Expr
(',' init+=Value)*)? parens+=')'
// FIXME: rename Value to Expr
((parens+='(' (init+=Value (',' init+=Value)*)? parens+=')')
| (braces+='{' (init+=Value (',' init+=Value)*)? braces+='}')
)?
;

Expand Down Expand Up @@ -311,7 +317,10 @@ Port:

// A type is in the target language, hence either an ID or target code.
Type:
(time?='time' (arraySpec=ArraySpec)?) | ((id=(DottedName) (stars+='*')* ('<' typeParms+=TypeParm (',' typeParms+=TypeParm)* '>')? (arraySpec=ArraySpec)?) | code=Code);
time?='time' (arraySpec=ArraySpec)?
| id=DottedName ('<' typeParms+=Type (',' typeParms+=Type)* '>')? (stars+='*')* (arraySpec=ArraySpec)?
| code=Code
;

ArraySpec:
ofVariableLength?='[]' | '[' length=INT ']';
Expand Down
7 changes: 4 additions & 3 deletions org.lflang/src/org/lflang/federated/FedASTUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.util.Optional;
import java.util.stream.Collectors;

import org.eclipse.emf.ecore.util.EcoreUtil;
import org.lflang.ASTUtils;
import org.lflang.TargetProperty.CoordinationType;
import org.lflang.TimeValue;
Expand Down Expand Up @@ -101,8 +102,8 @@ private static void addNetworkInputControlReaction(
) {
LfFactory factory = LfFactory.eINSTANCE;
Reaction reaction = factory.createReaction();
VarRef newPortRef = factory.createVarRef();
Type portType = ASTUtils.getCopy(destination.getDefinition().getType());
VarRef newPortRef = factory.createVarRef();
Type portType = EcoreUtil.copy(destination.getDefinition().getType());

// 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.
Expand Down Expand Up @@ -406,7 +407,7 @@ public static void makeCommunication(
) {
LfFactory factory = LfFactory.eINSTANCE;
// Assume all the types are the same, so just use the first on the right.
Type type = ASTUtils.getCopy(source.getDefinition().getType());
Type type = EcoreUtil.copy(source.getDefinition().getType());
Action action = factory.createAction();
VarRef triggerRef = factory.createVarRef();
VarRef sourceRef = factory.createVarRef();
Expand Down
19 changes: 17 additions & 2 deletions org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
package org.lflang.generator.cpp

import org.lflang.*
import org.lflang.generator.cpp.CppParameterGenerator.Companion.targetType
import org.lflang.lf.Instantiation
import org.lflang.lf.Parameter
import org.lflang.lf.Reactor
Expand Down Expand Up @@ -64,8 +65,22 @@ class CppInstanceGenerator(
with(CppParameterGenerator) { param.defaultValue }
} else {
// Otherwise, we use the assigned value.
val initializers = assignment.rhs.map { if (param.isOfTimeType) it.toTime() else it.toCode() }
with(CppParameterGenerator) { param.generateInstance(initializers) }
if (assignment.equals == "=") {
if (!assignment.braces.isNullOrEmpty()) {
"{${assignment.rhs.joinToString(", ") { it.toCode() }}}"
} else if (!assignment.parens.isNullOrEmpty()) {
"(${assignment.rhs.joinToString(", ") { it.toCode() }})"
} else {
assert(assignment.rhs.size == 1)
assignment.rhs[0].toCode()
}
} else {
if (!assignment.braces.isNullOrEmpty()) {
"${param.targetType}{${assignment.rhs.joinToString(", ") { it.toCode() }}}"
} else {
"${param.targetType}(${assignment.rhs.joinToString(", ") { it.toCode() }})"
}
}
}
}

Expand Down
13 changes: 4 additions & 9 deletions org.lflang/src/org/lflang/generator/cpp/CppParameterGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,11 @@ class CppParameterGenerator(private val reactor: Reactor) {
/** Type of the parameter in C++ code */
val Parameter.targetType get():String = this.inferredType.targetType

/** Get a parameter instantiated from the given initializer list */
fun Parameter.generateInstance(initializers: List<String>) =
when {
initializers.size > 1 -> "$targetType{${initializers.joinToString(", ")}}"
initializers.size == 1 -> initializers[0]
else -> "$targetType{}"
}

/** Get the default value of the receiver parameter in C++ code */
val Parameter.defaultValue: String get() = generateInstance(getInitializerList())
val Parameter.defaultValue: String
get() =
if (braces?.size == 2) "$targetType{${getInitializerList().joinToString(", ")}}"
else "$targetType(${getInitializerList().joinToString(", ")})"

/** Get a C++ type that is a const reference to the parameter type */
val Parameter.constRefType: String
Expand Down
9 changes: 6 additions & 3 deletions org.lflang/src/org/lflang/generator/cpp/CppStateGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,12 @@ class CppStateGenerator(private val reactor: Reactor) {
}
}

private fun generateInitializer(state: StateVar): String {
return "${state.name}{${getInitializerList(state).joinToString(separator = ", ")}}"
}
private fun generateInitializer(state: StateVar): String =
if (state.parens.isNullOrEmpty())
"${state.name}{${getInitializerList(state).joinToString(separator = ", ")}}"
else
"${state.name}(${getInitializerList(state).joinToString(separator = ", ")})"


/** Get all state declarations */
fun generateDeclarations() =
Expand Down
15 changes: 14 additions & 1 deletion org.lflang/src/org/lflang/validation/LFValidatorImpl.xtend
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,11 @@ class LFValidatorImpl extends AbstractLFValidator {
Literals.ASSIGNMENT__RHS)
}
}

if(!assignment.braces.isNullOrEmpty() && 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
Expand Down Expand Up @@ -757,6 +762,11 @@ class LFValidatorImpl extends AbstractLFValidator {
TimeValue.MAX_LONG_DEADLINE + " nanoseconds.",
Literals.PARAMETER__INIT)
}

if(!param.braces.isNullOrEmpty && this.target != Target.CPP) {
error("Brace initializers are only supported for the C++ target", Literals.PARAMETER__BRACES)
}

}

@Check(FAST)
Expand Down Expand Up @@ -1130,7 +1140,10 @@ class LFValidatorImpl extends AbstractLFValidator {
Literals.STATE_VAR__INIT)
}
}


if(!stateVar.braces.isNullOrEmpty && this.target != Target.CPP) {
error("Brace initializers are only supported for the C++ target", Literals.STATE_VAR__BRACES)
}
}

@Check(FAST)
Expand Down
2 changes: 1 addition & 1 deletion test/Cpp/src/ActionDelay.lf
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ target Cpp;
reactor GeneratedDelay {
input y_in:int;
output y_out:int;
state y_state:int(0);
state y_state: int{0};
logical action act(100 msec):void;
reaction(y_in) -> act {=
y_state = *y_in.get();
Expand Down
12 changes: 6 additions & 6 deletions test/Cpp/src/ArrayAsParameter.lf
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Source has an variable sized list as a parameter, the elements of which it passes to Print.
target Cpp;

reactor Source(sequence:int[](0, 1, 2)) {
output out:int;
state count:int(0);
reactor Source(sequence:int[]{0, 1, 2}) {
output out: size_t;
state count: size_t{0};
logical action next:void;
reaction(startup, next) -> out, next {=
out.set(sequence[count]);
Expand All @@ -15,8 +15,8 @@ reactor Source(sequence:int[](0, 1, 2)) {
}

reactor Print {
input in:int;
state count:int(1);
input in: size_t;
state count: size_t{1};
reaction(in) {=
std::cout << "Received: " << *in.get() << '\n';
if (*in.get() != count) {
Expand All @@ -33,7 +33,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;
}
10 changes: 5 additions & 5 deletions test/Cpp/src/MovingAverage.lf
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ target Cpp {
fast: true
};
reactor Source {
output out:double;
state count:int(0);
output out: double;
state count: int{0};
timer clock(0, 200 msec);
reaction(clock) -> out {=
out.set(count);
count++;
=}
}
reactor MovingAverageImpl {
state delay_line:double[3](0.0, 0.0, 0.0);
state index:int(0);
state delay_line: double[3] {0.0, 0.0, 0.0};
state index: int{0};
input in:double;
output out:double;

Expand All @@ -42,7 +42,7 @@ reactor MovingAverageImpl {

reactor Print {
input in:double;
state count:int(0);
state count: int{0};
reaction(in) {=
std::cout << "Received: " << *in.get() << '\n';
constexpr double expected[6] = {0.0, 0.25, 0.75, 1.5, 2.5, 3.5};
Expand Down
9 changes: 5 additions & 4 deletions test/Cpp/src/NativeListsAndTimes.lf
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ target Cpp;
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
q:{=std::vector<reactor::Duration>=}(1 msec, 2 msec, 3 msec), // list of time values
p:int[]{1, 2, 3, 4}, // List of integers
q:{=std::vector<reactor::Duration>=}{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
) {
state s:time(y); // Reference to explicitly typed time parameter
state t:time(z); // Reference to implicitly typed time parameter
Expand All @@ -19,11 +19,12 @@ reactor Foo(x:int(0),
timer toe(z); // Implicit type time
state baz(p); // Implicit type int[]
state period(z); // Implicit type time
state times: std::vector<std::vector<{=reactor::Duration=}>>{q, g}; // a list of lists
reaction(tick) {=
// Target code
=}
}

main reactor NativeListsAndTimes {
foo = new Foo();
}
}
Loading