Skip to content

Commit

Permalink
Merge pull request #2421 from lf-lang/forever-time-literal
Browse files Browse the repository at this point in the history
Native `forever` and `never` time literal
  • Loading branch information
lhstrh authored Oct 9, 2024
2 parents ae1740e + de7723c commit 7cbc49b
Show file tree
Hide file tree
Showing 13 changed files with 99 additions and 44 deletions.
12 changes: 10 additions & 2 deletions core/src/main/java/org/lflang/LinguaFranca.xtext
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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 |
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/java/org/lflang/TimeValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ public final class TimeValue implements Comparable<TimeValue> {
/** 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);

Expand Down
50 changes: 50 additions & 0 deletions core/src/main/java/org/lflang/ast/ASTUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand All @@ -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.
*
Expand Down Expand Up @@ -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;
}
Expand Down
4 changes: 4 additions & 0 deletions core/src/main/java/org/lflang/generator/TargetTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
8 changes: 8 additions & 0 deletions core/src/main/java/org/lflang/validation/LFValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
8 changes: 1 addition & 7 deletions test/C/src/LastTimeDefer.lf
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
8 changes: 1 addition & 7 deletions test/C/src/LastTimeDrop.lf
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
8 changes: 1 addition & 7 deletions test/C/src/LastTimeReplace.lf
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
6 changes: 3 additions & 3 deletions test/C/src/modal_models/BanksCount3ModesComplex.lf
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -46,7 +46,7 @@ reactor MetaCounter {
mode3_counters = new[2] CounterCycle()

(next)+ -> mode3_counters.next
mode3_counters.count -> never
mode3_counters.count -> neverp
}
}

Expand All @@ -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 {=
Expand Down
8 changes: 4 additions & 4 deletions test/C/src/modal_models/ModalNestedReactions.lf
Original file line number Diff line number Diff line change
Expand Up @@ -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) {=
Expand All @@ -29,8 +29,8 @@ reactor CounterCycle {
}

mode Three {
reaction(next) -> never {=
lf_set(never, true);
reaction(next) -> neverp {=
lf_set(neverp, true);
=}
}
}
Expand Down Expand Up @@ -69,7 +69,7 @@ main reactor {
}
=}

reaction(counter.never) {=
reaction(counter.neverp) {=
printf("ERROR: Detected output from unreachable mode.\n");
exit(4);
=}
Expand Down
14 changes: 7 additions & 7 deletions test/Python/src/federated/Dataflow.lf
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ preamble {=
import time
=}

reactor Client(STP_offset = {= FOREVER =}) {
reactor Client(STA=forever) {
input server_message
output client_message

Expand All @@ -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
Expand All @@ -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()
Expand Down
6 changes: 3 additions & 3 deletions test/Python/src/modal_models/BanksCount3ModesComplex.lf
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -46,7 +46,7 @@ reactor MetaCounter {
mode3_counters = new[2] CounterCycle()

(next)+ -> mode3_counters.next
mode3_counters.count -> never
mode3_counters.count -> neverp
}
}

Expand All @@ -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 {=
Expand Down
8 changes: 4 additions & 4 deletions test/Python/src/modal_models/ModalNestedReactions.lf
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ reactor CounterCycle {

output count
output only_in_two
output never
output neverp

initial mode One {
reaction(next) -> count, reset(Two) {=
Expand All @@ -29,8 +29,8 @@ reactor CounterCycle {
}

mode Three {
reaction(next) -> never {=
never.set(True)
reaction(next) -> neverp {=
neverp.set(True)
=}
}
}
Expand Down Expand Up @@ -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)
=}
Expand Down

0 comments on commit 7cbc49b

Please sign in to comment.