diff --git a/core/src/main/java/org/lflang/LinguaFranca.xtext b/core/src/main/java/org/lflang/LinguaFranca.xtext index ba45b3bd0c..103224e23c 100644 --- a/core/src/main/java/org/lflang/LinguaFranca.xtext +++ b/core/src/main/java/org/lflang/LinguaFranca.xtext @@ -392,8 +392,16 @@ SignedInt: INT | NEGINT ; +Forever: + 'forever' +; + +Never: + 'never' +; + Literal: - STRING | CHAR_LIT | SignedFloat | SignedInt | Boolean + STRING | CHAR_LIT | SignedFloat | SignedInt | Boolean | Forever | Never ; Boolean: @@ -521,7 +529,7 @@ Token: 'startup' | 'shutdown' | 'after' | 'deadline' | 'mutation' | 'preamble' | 'new' | 'federated' | 'at' | 'as' | 'from' | 'widthof' | 'const' | 'method' | 'interleaved' | 'mode' | 'initial' | 'reset' | 'history' | 'watchdog' | - 'extends' | + 'extends' | 'forever' | 'never' | // Other terminals NEGINT | TRUE | FALSE | diff --git a/core/src/main/java/org/lflang/TimeValue.java b/core/src/main/java/org/lflang/TimeValue.java index f393544c5a..832e9c754b 100644 --- a/core/src/main/java/org/lflang/TimeValue.java +++ b/core/src/main/java/org/lflang/TimeValue.java @@ -36,6 +36,9 @@ public final class TimeValue implements Comparable { /** The maximum value of this type. This is approximately equal to 292 years. */ public static final TimeValue MAX_VALUE = new TimeValue(Long.MAX_VALUE, TimeUnit.NANO); + /** The minimum value of this type. */ + public static final TimeValue MIN_VALUE = new TimeValue(Long.MIN_VALUE, TimeUnit.NANO); + /** A time value equal to zero. */ public static final TimeValue ZERO = new TimeValue(0, null); diff --git a/core/src/main/java/org/lflang/ast/ASTUtils.java b/core/src/main/java/org/lflang/ast/ASTUtils.java index 4f4f843fd3..7e55e3cabc 100644 --- a/core/src/main/java/org/lflang/ast/ASTUtils.java +++ b/core/src/main/java/org/lflang/ast/ASTUtils.java @@ -944,6 +944,26 @@ public static boolean isZero(String literal) { return false; } + /** + * Report whether the given literal is forever or not. + * + * @param literal AST node to inspect. + * @return True if the given literal denotes the constant {@code forever}, false otherwise. + */ + public static boolean isForever(String literal) { + return literal != null && literal.equals("forever"); + } + + /** + * Report whether the given literal is never or not. + * + * @param literal AST node to inspect. + * @return True if the given literal denotes the constant {@code never}, false otherwise. + */ + public static boolean isNever(String literal) { + return literal != null && literal.equals("never"); + } + /** * Report whether the given expression is zero or not. * @@ -957,6 +977,32 @@ public static boolean isZero(Expression expr) { return false; } + /** + * Report whether the given expression is forever or not. + * + * @param expr AST node to inspect. + * @return True if the given value denotes the constant {@code forever}, false otherwise. + */ + public static boolean isForever(Expression expr) { + if (expr instanceof Literal) { + return isForever(((Literal) expr).getLiteral()); + } + return false; + } + + /** + * Report whether the given expression is never or not. + * + * @param expr AST node to inspect. + * @return True if the given value denotes the constant {@code never}, false otherwise. + */ + public static boolean isNever(Expression expr) { + if (expr instanceof Literal) { + return isNever(((Literal) expr).getLiteral()); + } + return false; + } + /** * Report whether the given string literal is an integer number or not. * @@ -1137,6 +1183,10 @@ public static TimeValue getLiteralTimeValue(Expression expr) { return toTimeValue((Time) expr); } else if (expr instanceof Literal && isZero(((Literal) expr).getLiteral())) { return TimeValue.ZERO; + } else if (expr instanceof Literal && isForever(((Literal) expr).getLiteral())) { + return TimeValue.MAX_VALUE; + } else if (expr instanceof Literal && isNever(((Literal) expr).getLiteral())) { + return TimeValue.MIN_VALUE; } else { return null; } diff --git a/core/src/main/java/org/lflang/generator/TargetTypes.java b/core/src/main/java/org/lflang/generator/TargetTypes.java index 86bad63818..42d3e62064 100644 --- a/core/src/main/java/org/lflang/generator/TargetTypes.java +++ b/core/src/main/java/org/lflang/generator/TargetTypes.java @@ -176,6 +176,10 @@ default String getTargetInitializer(Initializer init, Type type) { default String getTargetExpr(Expression expr, InferredType type) { if (ASTUtils.isZero(expr) && type != null && type.isTime) { return getTargetTimeExpr(TimeValue.ZERO); + } else if (ASTUtils.isForever(expr) && type != null) { + return getTargetTimeExpr(TimeValue.MAX_VALUE); + } else if (ASTUtils.isNever(expr) && type != null) { + return getTargetTimeExpr(TimeValue.MIN_VALUE); } else if (expr instanceof ParameterReference) { return getTargetParamRef((ParameterReference) expr, type); } else if (expr instanceof Time) { diff --git a/core/src/main/java/org/lflang/validation/LFValidator.java b/core/src/main/java/org/lflang/validation/LFValidator.java index 4c5f87fc6a..eb721cf032 100644 --- a/core/src/main/java/org/lflang/validation/LFValidator.java +++ b/core/src/main/java/org/lflang/validation/LFValidator.java @@ -1594,6 +1594,14 @@ private void checkExpressionIsTime(Expression value, EStructuralFeature feature) return; } + if (ASTUtils.isForever(((Literal) value).getLiteral())) { + return; + } + + if (ASTUtils.isNever(((Literal) value).getLiteral())) { + return; + } + if (ASTUtils.isInteger(((Literal) value).getLiteral())) { error("Missing time unit.", feature); return; diff --git a/test/C/src/LastTimeDefer.lf b/test/C/src/LastTimeDefer.lf index 2509a5e896..65001bdc9c 100644 --- a/test/C/src/LastTimeDefer.lf +++ b/test/C/src/LastTimeDefer.lf @@ -9,13 +9,7 @@ main reactor { logical action a(1 ms, 300 ms): int state c: int = 0 state c2: int = 0 // For expected values. - state last: time = 0 - - reaction(startup) {= - // Unfortunately, a time state variable cannot be initialized with NEVER. - // So we do that here. - self->last = NEVER; - =} + state last: time = never reaction(t) -> a {= tag_t now = lf_tag(); diff --git a/test/C/src/LastTimeDrop.lf b/test/C/src/LastTimeDrop.lf index 2862ac75f0..5cfc7c2ad2 100644 --- a/test/C/src/LastTimeDrop.lf +++ b/test/C/src/LastTimeDrop.lf @@ -8,13 +8,7 @@ main reactor { timer t(0, 100 ms) logical action a(1 ms, 300 ms, "drop"): int state c: int = 0 - state last: time = 0 - - reaction(startup) {= - // Unfortunately, a time state variable cannot be initialized with NEVER. - // So we do that here. - self->last = NEVER; - =} + state last: time = never reaction(t) -> a {= tag_t now = lf_tag(); diff --git a/test/C/src/LastTimeReplace.lf b/test/C/src/LastTimeReplace.lf index 0d44bb58b5..51b29f972a 100644 --- a/test/C/src/LastTimeReplace.lf +++ b/test/C/src/LastTimeReplace.lf @@ -8,13 +8,7 @@ main reactor { timer t(0, 100 ms) logical action a(1 ms, 300 ms, "replace"): int state c: int = 0 - state last: time = 0 - - reaction(startup) {= - // Unfortunately, a time state variable cannot be initialized with NEVER. - // So we do that here. - self->last = NEVER; - =} + state last: time = never reaction(t) -> a {= tag_t now = lf_tag(); diff --git a/test/C/src/modal_models/BanksCount3ModesComplex.lf b/test/C/src/modal_models/BanksCount3ModesComplex.lf index 48774e0b80..1385573be0 100644 --- a/test/C/src/modal_models/BanksCount3ModesComplex.lf +++ b/test/C/src/modal_models/BanksCount3ModesComplex.lf @@ -12,7 +12,7 @@ reactor MetaCounter { output[2] always: int output[2] mode1: int output[2] mode2: int - output[2] never: int + output[2] neverp: int outer_counters = new[2] CounterCycle() (next)+ -> outer_counters.next @@ -46,7 +46,7 @@ reactor MetaCounter { mode3_counters = new[2] CounterCycle() (next)+ -> mode3_counters.next - mode3_counters.count -> never + mode3_counters.count -> neverp } } @@ -72,7 +72,7 @@ main reactor { 250000000,1,1,1,1,1,1,1,1,0,3,0,3,0,3,0,3,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0 }, training = false) - counters.always, counters.mode1, counters.mode2, counters.never -> test.events + counters.always, counters.mode1, counters.mode2, counters.neverp -> test.events // Trigger reaction(stepper) -> counters.next {= diff --git a/test/C/src/modal_models/ModalNestedReactions.lf b/test/C/src/modal_models/ModalNestedReactions.lf index 88bb61c8bb..90b04674ce 100644 --- a/test/C/src/modal_models/ModalNestedReactions.lf +++ b/test/C/src/modal_models/ModalNestedReactions.lf @@ -9,7 +9,7 @@ reactor CounterCycle { output count: int output only_in_two: bool - output never: int + output neverp: int initial mode One { reaction(next) -> count, reset(Two) {= @@ -29,8 +29,8 @@ reactor CounterCycle { } mode Three { - reaction(next) -> never {= - lf_set(never, true); + reaction(next) -> neverp {= + lf_set(neverp, true); =} } } @@ -69,7 +69,7 @@ main reactor { } =} - reaction(counter.never) {= + reaction(counter.neverp) {= printf("ERROR: Detected output from unreachable mode.\n"); exit(4); =} diff --git a/test/Python/src/federated/Dataflow.lf b/test/Python/src/federated/Dataflow.lf index 4d33235af2..fb6625abde 100644 --- a/test/Python/src/federated/Dataflow.lf +++ b/test/Python/src/federated/Dataflow.lf @@ -6,7 +6,7 @@ preamble {= import time =} -reactor Client(STP_offset = {= FOREVER =}) { +reactor Client(STA=forever) { input server_message output client_message @@ -24,13 +24,13 @@ reactor Client(STP_offset = {= FOREVER =}) { request_stop() # Need to unconditionally produce output or downstream could lock up waiting for it. client_message.set(val) - =} STP(10 s) {= - print("Client STP Violated!") + =} STP(forever) {= + print("Client STAA Violated!") exit(1) =} } -reactor Server(STP_offset = {= FOREVER =}) { +reactor Server(STA=forever) { output server_message input client_message1 input client_message2 @@ -51,13 +51,13 @@ reactor Server(STP_offset = {= FOREVER =}) { request_stop() # Need to unconditionally produce output or downstream could lock up waiting for it. server_message.set(val) - =} STP(10 s) {= - print("Server STP Violated!") + =} STP(forever) {= + print("Server STAA Violated!") exit(1) =} } -federated reactor(STP_offset = {= FOREVER =}) { +federated reactor(STA=forever) { client1 = new Client() client2 = new Client() server = new Server() diff --git a/test/Python/src/modal_models/BanksCount3ModesComplex.lf b/test/Python/src/modal_models/BanksCount3ModesComplex.lf index 77b8461861..fde46b539c 100644 --- a/test/Python/src/modal_models/BanksCount3ModesComplex.lf +++ b/test/Python/src/modal_models/BanksCount3ModesComplex.lf @@ -12,7 +12,7 @@ reactor MetaCounter { output[2] always output[2] mode1 output[2] mode2 - output[2] never + output[2] neverp outer_counters = new[2] CounterCycle() (next)+ -> outer_counters.next @@ -46,7 +46,7 @@ reactor MetaCounter { mode3_counters = new[2] CounterCycle() (next)+ -> mode3_counters.next - mode3_counters.count -> never + mode3_counters.count -> neverp } } @@ -69,7 +69,7 @@ main reactor { 250000000,1,1,1,1,1,1,1,1,0,3,0,3,0,3,0,3,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0 ], training = False) - counters.always, counters.mode1, counters.mode2, counters.never -> test.events + counters.always, counters.mode1, counters.mode2, counters.neverp -> test.events # Trigger reaction(stepper) -> counters.next {= diff --git a/test/Python/src/modal_models/ModalNestedReactions.lf b/test/Python/src/modal_models/ModalNestedReactions.lf index a3b89c997a..5ce8f1b529 100644 --- a/test/Python/src/modal_models/ModalNestedReactions.lf +++ b/test/Python/src/modal_models/ModalNestedReactions.lf @@ -9,7 +9,7 @@ reactor CounterCycle { output count output only_in_two - output never + output neverp initial mode One { reaction(next) -> count, reset(Two) {= @@ -29,8 +29,8 @@ reactor CounterCycle { } mode Three { - reaction(next) -> never {= - never.set(True) + reaction(next) -> neverp {= + neverp.set(True) =} } } @@ -68,7 +68,7 @@ main reactor { exit(3) =} - reaction(counter.never) {= + reaction(counter.neverp) {= sys.stderr.write("ERROR: Detected output from unreachable mode.\n") exit(4) =}