From c68a5b2fd7cffe17d2a896513a5dd266880e226c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 13 Sep 2023 15:44:10 +0200 Subject: [PATCH 1/8] Add bracket list expression --- .../main/java/org/lflang/LinguaFranca.xtext | 8 ++++++++ core/src/main/java/org/lflang/Target.java | 5 +++++ .../src/main/java/org/lflang/ast/IsEqual.java | 8 ++++++++ core/src/main/java/org/lflang/ast/ToLf.java | 10 ++++++++++ core/src/main/java/org/lflang/ast/ToText.java | 6 ++++++ .../lflang/generator/LfExpressionVisitor.java | 19 +++++++++++++++++++ .../org/lflang/generator/TargetTypes.java | 11 +++++++++++ .../org/lflang/validation/LFValidator.java | 10 ++++++++++ 8 files changed, 77 insertions(+) diff --git a/core/src/main/java/org/lflang/LinguaFranca.xtext b/core/src/main/java/org/lflang/LinguaFranca.xtext index b00456ce07..593b06317e 100644 --- a/core/src/main/java/org/lflang/LinguaFranca.xtext +++ b/core/src/main/java/org/lflang/LinguaFranca.xtext @@ -320,6 +320,7 @@ Expression: | ParameterReference | {CodeExpr} code=Code | BracedListExpression + | BracketListExpression ; // A list of expressions within braces. @@ -328,6 +329,13 @@ BracedListExpression: '{' {BracedListExpression} (items+=Expression (',' items+=Expression)*)? ','? '}' ; +// A list of expressions within square brackets. +// In Python and TS, this is a list literal. In Rust this could be an array but Rust +// array expressions are relatively rare so probably not worth supporting. +BracketListExpression: + '[' {BracketListExpression} (items+=Expression (',' items+=Expression)*)? ','? ']' +; + ParameterReference: parameter=[Parameter] ; diff --git a/core/src/main/java/org/lflang/Target.java b/core/src/main/java/org/lflang/Target.java index 096788484e..8dc820f826 100644 --- a/core/src/main/java/org/lflang/Target.java +++ b/core/src/main/java/org/lflang/Target.java @@ -524,6 +524,11 @@ public boolean allowsBracedListExpressions() { return this == C || this == CCPP || this == CPP; } + /** Allow expressions of the form {@code [a, b, c]}. */ + public boolean allowsBracketListExpressions() { + return this == Python || this == TS || this == Rust; + } + /** Return a string that demarcates the beginning of a single-line comment. */ public String getSingleLineCommentPrefix() { return this.equals(Target.Python) ? "#" : "//"; diff --git a/core/src/main/java/org/lflang/ast/IsEqual.java b/core/src/main/java/org/lflang/ast/IsEqual.java index af06923d35..9ac2a0a389 100644 --- a/core/src/main/java/org/lflang/ast/IsEqual.java +++ b/core/src/main/java/org/lflang/ast/IsEqual.java @@ -16,6 +16,7 @@ import org.lflang.lf.AttrParm; import org.lflang.lf.Attribute; import org.lflang.lf.BracedListExpression; +import org.lflang.lf.BracketListExpression; import org.lflang.lf.BuiltinTriggerRef; import org.lflang.lf.Code; import org.lflang.lf.CodeExpr; @@ -465,6 +466,13 @@ public Boolean caseBracedListExpression(BracedListExpression object) { .conclusion; } + @Override + public Boolean caseBracketListExpression(BracketListExpression object) { + return new ComparisonMachine<>(object, BracketListExpression.class) + .listsEquivalent(BracketListExpression::getItems) + .conclusion; + } + @Override public Boolean caseParameterReference(ParameterReference object) { return new ComparisonMachine<>(object, ParameterReference.class) diff --git a/core/src/main/java/org/lflang/ast/ToLf.java b/core/src/main/java/org/lflang/ast/ToLf.java index b91ea5499c..149b9211a3 100644 --- a/core/src/main/java/org/lflang/ast/ToLf.java +++ b/core/src/main/java/org/lflang/ast/ToLf.java @@ -30,6 +30,7 @@ import org.lflang.lf.AttrParm; import org.lflang.lf.Attribute; import org.lflang.lf.BracedListExpression; +import org.lflang.lf.BracketListExpression; import org.lflang.lf.BuiltinTriggerRef; import org.lflang.lf.Code; import org.lflang.lf.CodeExpr; @@ -873,6 +874,15 @@ public MalleableString caseBracedListExpression(BracedListExpression object) { return bracedListExpression(object.getItems()); } + + @Override + public MalleableString caseBracketListExpression(BracketListExpression object) { + if (object.getItems().isEmpty()) { + return MalleableString.anyOf("[]"); + } + return list(", ", "[", "]", false, false, true, object.getItems()); + } + /** * Represent a braced list expression. Do not invoke on expressions that may have comments * attached. diff --git a/core/src/main/java/org/lflang/ast/ToText.java b/core/src/main/java/org/lflang/ast/ToText.java index 6845fde4e7..515eb29fe5 100644 --- a/core/src/main/java/org/lflang/ast/ToText.java +++ b/core/src/main/java/org/lflang/ast/ToText.java @@ -6,6 +6,7 @@ import org.eclipse.xtext.nodemodel.util.NodeModelUtils; import org.lflang.lf.ArraySpec; import org.lflang.lf.BracedListExpression; +import org.lflang.lf.BracketListExpression; import org.lflang.lf.Code; import org.lflang.lf.CodeExpr; import org.lflang.lf.Host; @@ -80,6 +81,11 @@ public String caseBracedListExpression(BracedListExpression object) { return new ToLf().caseBracedListExpression(object).toString(); } + @Override + public String caseBracketListExpression(BracketListExpression object) { + return new ToLf().caseBracketListExpression(object).toString(); + } + @Override public String caseHost(Host host) { return new ToLf().caseHost(host).toString(); diff --git a/core/src/main/java/org/lflang/generator/LfExpressionVisitor.java b/core/src/main/java/org/lflang/generator/LfExpressionVisitor.java index 15f0e8ddad..e5b669b6f2 100644 --- a/core/src/main/java/org/lflang/generator/LfExpressionVisitor.java +++ b/core/src/main/java/org/lflang/generator/LfExpressionVisitor.java @@ -25,6 +25,7 @@ package org.lflang.generator; import org.lflang.lf.BracedListExpression; +import org.lflang.lf.BracketListExpression; import org.lflang.lf.Code; import org.lflang.lf.CodeExpr; import org.lflang.lf.Expression; @@ -43,6 +44,7 @@ public interface LfExpressionVisitor { R visitLiteral(Literal expr, P param); R visitBracedListExpr(BracedListExpression expr, P param); + R visitBracketListExpr(BracketListExpression expr, P param); R visitTimeLiteral(Time expr, P param); @@ -66,6 +68,8 @@ static R dispatch( return visitor.visitLiteral((Literal) e, arg); } else if (e instanceof BracedListExpression) { return visitor.visitBracedListExpr((BracedListExpression) e, arg); + } else if (e instanceof BracketListExpression) { + return visitor.visitBracketListExpr((BracketListExpression) e, arg); } else if (e instanceof Time) { return visitor.visitTimeLiteral((Time) e, arg); } else if (e instanceof CodeExpr) { @@ -106,6 +110,11 @@ public R visitCodeExpr(CodeExpr expr, P param) { public R visitParameterRef(ParameterReference expr, P param) { return visitExpression(expr, param); } + + @Override + public R visitBracketListExpr(BracketListExpression expr, P param) { + return visitExpression(expr, param); + } } /** @@ -147,6 +156,16 @@ public Expression visitParameterRef(ParameterReference expr, P param) { return clone; } + @Override + public Expression visitBracketListExpr(BracketListExpression expr, P param) { + BracketListExpression clone = LfFactory.eINSTANCE.createBracketListExpression(); + for (Expression item : expr.getItems()) { + clone.getItems().add(dispatch(item, param, this)); + } + return clone; + + } + @Override public Expression visitCodeExpr(CodeExpr expr, P param) { CodeExpr codeExpr = LfFactory.eINSTANCE.createCodeExpr(); diff --git a/core/src/main/java/org/lflang/generator/TargetTypes.java b/core/src/main/java/org/lflang/generator/TargetTypes.java index 968a641ed3..65552edc59 100644 --- a/core/src/main/java/org/lflang/generator/TargetTypes.java +++ b/core/src/main/java/org/lflang/generator/TargetTypes.java @@ -8,6 +8,7 @@ import org.lflang.ast.ASTUtils; import org.lflang.lf.Action; import org.lflang.lf.BracedListExpression; +import org.lflang.lf.BracketListExpression; import org.lflang.lf.CodeExpr; import org.lflang.lf.Expression; import org.lflang.lf.Initializer; @@ -60,6 +61,14 @@ default String getTargetBracedListExpr(BracedListExpression expr, InferredType t .collect(Collectors.joining(",", "{", "}")); } + /** Translate the bracket list expression into target language syntax. */ + default String getTargetBracketListExpr(BracketListExpression expr, InferredType typeOrNull) { + InferredType t = typeOrNull == null ? InferredType.undefined() : typeOrNull; + return expr.getItems().stream() + .map(e -> getTargetExpr(e, t)) + .collect(Collectors.joining(", ", "[", "]")); + } + /** Return an "unknown" type which is used as a default when a type cannot be inferred. */ String getTargetUndefinedType(); @@ -224,6 +233,8 @@ default String getTargetExpr(Expression expr, InferredType type) { return ASTUtils.toText(((CodeExpr) expr).getCode()); } else if (expr instanceof BracedListExpression) { return getTargetBracedListExpr((BracedListExpression) expr, type); + } else if (expr instanceof BracketListExpression) { + return getTargetBracketListExpr((BracketListExpression) expr, type); } else { throw new IllegalStateException("Invalid value " + expr); } diff --git a/core/src/main/java/org/lflang/validation/LFValidator.java b/core/src/main/java/org/lflang/validation/LFValidator.java index 7dcf00d107..755d28a42b 100644 --- a/core/src/main/java/org/lflang/validation/LFValidator.java +++ b/core/src/main/java/org/lflang/validation/LFValidator.java @@ -72,6 +72,7 @@ import org.lflang.lf.Assignment; import org.lflang.lf.Attribute; import org.lflang.lf.BracedListExpression; +import org.lflang.lf.BracketListExpression; import org.lflang.lf.BuiltinTrigger; import org.lflang.lf.BuiltinTriggerRef; import org.lflang.lf.Connection; @@ -193,6 +194,15 @@ public void checkBracedExpression(BracedListExpression expr) { } } + @Check(CheckType.FAST) + public void checkBracketExpression(BracketListExpression expr) { + if (!target.allowsBracketListExpressions()) { + var message = + "Bracketed expression lists are not a valid expression for the " + target + " target."; + error(message, Literals.BRACKET_LIST_EXPRESSION.eContainmentFeature()); + } + } + @Check(CheckType.FAST) public void checkAssignment(Assignment assignment) { From 2bb8212be4411485d2c0891636ff71acdf898a2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 13 Sep 2023 15:46:41 +0200 Subject: [PATCH 2/8] Update tests --- .../src/modal_models/util/TraceTesting.lf | 4 +-- test/Rust/src/ArrayAsParameter.lf | 36 +++++++++++++++++++ test/TypeScript/src/ArrayAsParameter.lf | 4 +-- test/TypeScript/src/MovingAverage.lf | 2 +- 4 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 test/Rust/src/ArrayAsParameter.lf diff --git a/test/Python/src/modal_models/util/TraceTesting.lf b/test/Python/src/modal_models/util/TraceTesting.lf index 0d5053b844..4bad50cd26 100644 --- a/test/Python/src/modal_models/util/TraceTesting.lf +++ b/test/Python/src/modal_models/util/TraceTesting.lf @@ -1,12 +1,12 @@ /** Utility reactor to record and test execution traces. */ target Python -reactor TraceTesting(events_size=0, trace = {= [] =}, training=False) { +reactor TraceTesting(events_size=0, trace = [], training=False) { input[events_size] events state last_reaction_time = 0 state trace_idx = 0 - state recorded_events = {= [] =} + state recorded_events = [] state recorded_events_next = 0 reaction(startup) {= diff --git a/test/Rust/src/ArrayAsParameter.lf b/test/Rust/src/ArrayAsParameter.lf new file mode 100644 index 0000000000..2692512273 --- /dev/null +++ b/test/Rust/src/ArrayAsParameter.lf @@ -0,0 +1,36 @@ +// Source has an array as a parameter, the elements of which it passes to Print. +target Rust + +reactor Source(sequence: {= [i32; 3] =} = [0, 1, 2]) { + output out: i32 + state count: usize = 0 + state seq = sequence + logical action next + + reaction(startup, next) -> out, next {= + ctx.set(out, self.seq[self.count]); + self.count += 1; + if self.count < self.seq.len() { + ctx.schedule(next, Asap); + } + =} +} + +reactor Print { + input x: i32 + state count: usize = 0 + + reaction(x) {= + let expected = [2, 3, 4]; + let x = ctx.get(x).unwrap(); + println!("Received: {}.", x); + assert_eq!(x, expected[self.count]); + self.count += 1; + =} +} + +main reactor ArrayAsParameter { + s = new Source(sequence = [2, 3, 4]) + p = new Print() + s.out -> p.x +} diff --git a/test/TypeScript/src/ArrayAsParameter.lf b/test/TypeScript/src/ArrayAsParameter.lf index ef9ba9f414..10ef7a5c32 100644 --- a/test/TypeScript/src/ArrayAsParameter.lf +++ b/test/TypeScript/src/ArrayAsParameter.lf @@ -1,7 +1,7 @@ // Source has an array as a parameter, the elements of which it passes to Print. target TypeScript -reactor Source(sequence: {= Array =} = {= [0, 1, 2] =}) { +reactor Source(sequence: Array = [0, 1, 2]) { output out: number state count: number = 0 logical action next @@ -29,7 +29,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.x } diff --git a/test/TypeScript/src/MovingAverage.lf b/test/TypeScript/src/MovingAverage.lf index f99e436ab7..dfe550f612 100644 --- a/test/TypeScript/src/MovingAverage.lf +++ b/test/TypeScript/src/MovingAverage.lf @@ -17,7 +17,7 @@ reactor Source { } reactor MovingAverageImpl { - state delay_line: {= Array =} = {= [0.0, 0.0, 0.0] =} + state delay_line: Array = [0.0, 0.0, 0.0] state index: number = 0 input x: number output out: number From 82dd4d64fd50038f0a414a333e3e8632dd4de8c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 13 Sep 2023 16:32:57 +0200 Subject: [PATCH 3/8] Format --- test/Python/src/modal_models/util/TraceTesting.lf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Python/src/modal_models/util/TraceTesting.lf b/test/Python/src/modal_models/util/TraceTesting.lf index 4bad50cd26..027b1ccb19 100644 --- a/test/Python/src/modal_models/util/TraceTesting.lf +++ b/test/Python/src/modal_models/util/TraceTesting.lf @@ -1,7 +1,7 @@ /** Utility reactor to record and test execution traces. */ target Python -reactor TraceTesting(events_size=0, trace = [], training=False) { +reactor TraceTesting(events_size=0, trace=[], training=False) { input[events_size] events state last_reaction_time = 0 From e9f3bade4f8e5c9e72b6e8ddd7f329ece28ab081 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Thu, 14 Sep 2023 17:03:29 -0700 Subject: [PATCH 4/8] Build epoch against fork --- .github/workflows/build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6d7edf2145..25c12ebf0e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,7 +32,8 @@ jobs: files: core/build/reports/jacoco/testCodeCoverageReport/testCodeCoverageReport.xml,cli/base/build/reports/jacoco/testCodeCoverageReport/testCodeCoverageReport.xml,cli/lfc/build/reports/jacoco/testCodeCoverageReport/testCodeCoverageReport.xml,cli/lfd/build/reports/jacoco/testCodeCoverageReport/testCodeCoverageReport.xml,cli/lff/build/reports/jacoco/testCodeCoverageReport/testCodeCoverageReport.xml epoch: - uses: lf-lang/epoch/.github/workflows/build.yml@main + uses: lf-lang/epoch/.github/workflows/build.yml@build-against-fork with: lingua-franca-ref: ${{ github.head_ref || github.ref_name }} + lingua-franca-repo: ${{ github.repository }} upload-artifacts: false From 91b219b4fa42a2e51c3f384157cf80328467d363 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Thu, 14 Sep 2023 18:24:29 -0700 Subject: [PATCH 5/8] Pass in URL instead of repo name --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 25c12ebf0e..32e34c9ffb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -35,5 +35,5 @@ jobs: uses: lf-lang/epoch/.github/workflows/build.yml@build-against-fork with: lingua-franca-ref: ${{ github.head_ref || github.ref_name }} - lingua-franca-repo: ${{ github.repository }} + lingua-franca-repo: ${{ github.event.pull_request.head.repo.full_name }} upload-artifacts: false From 63ddf066a7821fbd2272d02aca7ad0661f85b601 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Thu, 14 Sep 2023 19:24:44 -0700 Subject: [PATCH 6/8] Apply formatter --- core/src/main/java/org/lflang/ast/ToLf.java | 1 - .../src/main/java/org/lflang/generator/LfExpressionVisitor.java | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/core/src/main/java/org/lflang/ast/ToLf.java b/core/src/main/java/org/lflang/ast/ToLf.java index 149b9211a3..dd8096a5e3 100644 --- a/core/src/main/java/org/lflang/ast/ToLf.java +++ b/core/src/main/java/org/lflang/ast/ToLf.java @@ -874,7 +874,6 @@ public MalleableString caseBracedListExpression(BracedListExpression object) { return bracedListExpression(object.getItems()); } - @Override public MalleableString caseBracketListExpression(BracketListExpression object) { if (object.getItems().isEmpty()) { diff --git a/core/src/main/java/org/lflang/generator/LfExpressionVisitor.java b/core/src/main/java/org/lflang/generator/LfExpressionVisitor.java index e5b669b6f2..c50f805209 100644 --- a/core/src/main/java/org/lflang/generator/LfExpressionVisitor.java +++ b/core/src/main/java/org/lflang/generator/LfExpressionVisitor.java @@ -44,6 +44,7 @@ public interface LfExpressionVisitor { R visitLiteral(Literal expr, P param); R visitBracedListExpr(BracedListExpression expr, P param); + R visitBracketListExpr(BracketListExpression expr, P param); R visitTimeLiteral(Time expr, P param); @@ -163,7 +164,6 @@ public Expression visitBracketListExpr(BracketListExpression expr, P param) { clone.getItems().add(dispatch(item, param, this)); } return clone; - } @Override From da30b7925e7358e622755a2e0516191f42b12894 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Thu, 14 Sep 2023 22:04:35 -0700 Subject: [PATCH 7/8] Update .github/workflows/build.yml --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 32e34c9ffb..da1990ca3f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,7 +32,7 @@ jobs: files: core/build/reports/jacoco/testCodeCoverageReport/testCodeCoverageReport.xml,cli/base/build/reports/jacoco/testCodeCoverageReport/testCodeCoverageReport.xml,cli/lfc/build/reports/jacoco/testCodeCoverageReport/testCodeCoverageReport.xml,cli/lfd/build/reports/jacoco/testCodeCoverageReport/testCodeCoverageReport.xml,cli/lff/build/reports/jacoco/testCodeCoverageReport/testCodeCoverageReport.xml epoch: - uses: lf-lang/epoch/.github/workflows/build.yml@build-against-fork + uses: lf-lang/epoch/.github/workflows/build.yml@main with: lingua-franca-ref: ${{ github.head_ref || github.ref_name }} lingua-franca-repo: ${{ github.event.pull_request.head.repo.full_name }} From 9edab375febce6a03a2d11b90c32d889990e42c1 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Fri, 22 Sep 2023 16:14:11 -0700 Subject: [PATCH 8/8] Fix ArrayAsParameter.lf --- test/Python/src/ArrayAsParameter.lf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Python/src/ArrayAsParameter.lf b/test/Python/src/ArrayAsParameter.lf index b794008a7c..58b4cb50e3 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 @@ -36,7 +36,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 }