chandeliers_sem/candle.rs
1//! Syntax and semantics of the Candle language.
2//!
3//! Candle is a shallow embedding of
4//! [Lustre](https://en.wikipedia.org/wiki/Lustre_(programming_language))
5//! in Rust.
6//!
7//! How to use the macros below:
8//! A `struct` described using the constructs of Candle simulates a
9//! Lustre `node` by
10//! - deriving the `Default` trait,
11//! - (optional) having a `__clock` field of type `usize`,
12//! - (optional) having a `__nodes` field of type a tuple of all the subnodes
13//! that the node uses,
14//! - (optional) having a `__trace` boolean field that enables
15//! printing debug information,
16//! - having fields of a type provided by the `ty!(_)` macro that converts
17//! standard types into types with a memory of the past,
18//! - implementing the trait `stepping::Step`, of signature
19//! `(&mut self, ty!(...)) -> ty!(...)` that takes the node inputs,
20//! applies one step of computation, and returns the updated values of
21//! the node outputs.
22//!
23//! We provide here a thoroughly commented implementation of a simple integer
24//! counter in Candle and Lustre side-by-side to showcase the general structure.
25//! See more examples in `tests/impls/*.rs`
26//!
27//! ### Simple integer counter
28//!
29//! An integer counter has no inputs, and a single integer output that starts
30//! at `0` and increments by one at each time step.
31//!
32//! A Lustre implementation would look like this:
33//! ```ml
34//! node counter() returns (n : int);
35//! let
36//! n = 0 fby n + 1;
37//! tel;
38//! ```
39//! In Candle we would define the same node like this:
40//!
41//! ```
42//! use chandeliers_sem::macros::*;
43//! use chandeliers_sem::traits::*;
44//!
45//! #[allow(non_camel_case_types)]
46//! #[derive(Default)]
47//! pub struct counter {
48//! __clock: usize,
49//! __trace: bool,
50//! n: ty!(int+),
51//! __nodes: (),
52//! }
53//!
54//! impl Step for counter {
55//! type Input = ();
56//! type Output = i64;
57//! fn step(&mut self, __inputs: ty!()) -> ty!(int) {
58//! implicit_clock!(__inputs);
59//! node_trace!(self, "() => counter(n={})", self.n);
60//! let n = later!(self <~ 0; lit!(0), var!(self <~ 1; n) + lit!(1));
61//! update!(self, n);
62//! tick!(self);
63//! node_trace!(self, "counter(n={}) => (n={})", self.n, self.n);
64//! n
65//! }
66//! }
67//!
68//! fn usage() {
69//! let mut c = counter::default();
70//! for i in 0..10 {
71//! assert_eq!(c.step(().embed()).trusted(), i);
72//! }
73//! }
74//! ```
75//! You may notice that there are a lot of low level and error-prone details
76//! here: the numbers in each `self <~ n` are carefully chosen, and we must
77//! not forget to invoque `tick!(self)` to increment the clock
78//! or `update!(self, n)` to make the update persistent.
79//!
80//! That is
81//!
82//! In the above code, the irreducible template that will be used for all
83//! definitions is:
84//! ```ignore
85//! #[derive(Default)]
86//! pub struct ... {
87//! // The names "__clock", "__trace", and "__nodes" are mandatory,
88//! // as they are hard-coded in some macros.
89//! __clock: usize,
90//! __trace: bool,
91//! // Here you should put the subnodes that you use, as a tuple.
92//! // If the node is primitive and has no subnodes, this field is optional.
93//! __nodes: (...),
94//! ... // add any extra variables that you want to keep in-between
95//! // executions of `step`.
96//! }
97//!
98//! impl Step for ... {
99//! type Input = ...;
100//! type Output = ...;
101//! pub fn step(&mut self, inputs: ...) -> ... {
102//! node_trace!(self, ...); // Print any relevant information
103//! ...
104//! // You must always increment the clock once per call to `step`.
105//! tick!(self);
106//! // And you must *never perform any computation* after incrementing the clock.
107//! node_trace!(self, ...); // Print any relevant information
108//! ... // (return the computed value(s) here)
109//! }
110//! }
111//! ```
112
113/// Do not use explicitly, call `ty` instead.
114/// If you don't know `S` and `O` and want to understand the purpose of
115/// this macro, consult module `time_travel`.
116///
117/// This macro converts a scalar type into a stream of fixed size.
118/// ```ignore
119/// past_ty!(T,) ~ O<T>
120/// past_ty!(T, +) ~ S<O<T>, T>
121/// past_ty!(T, +++) ~ S<S<S<O<T>, T>, T>, T>
122/// ```
123#[doc(hidden)]
124#[macro_export]
125macro_rules! past_ty {
126 ( $t:ty, ) => { $crate::time_travel::O<$t> };
127 ( $t:ty, + $( $rest:tt )* ) => { $crate::time_travel::S<$crate::past_ty!($t, $($rest)*), $t> };
128}
129
130/// Do not use explicitly, call `ty` instead.
131/// If you don't know `Nillable` and want to understand the purpose of
132/// this macro, consult module `nillable`.
133///
134/// This macro converts a scalar type into a nillable type.
135/// ```ignore
136/// present_ty!(T) ~ Nillable<T>
137/// ```
138#[doc(hidden)]
139#[macro_export]
140macro_rules! present_ty {
141 ( $t:ty ) => { $crate::nillable::Nillable<$t> };
142}
143
144/// It is recommended to call `ty` instead.
145///
146/// Convert type names of Candle/Lustre to their internal Rust representation.
147/// ```ignore
148/// ty_mapping!(float) ~ f64
149/// ty_mapping!(int) ~ i64
150/// ty_mapping!(bool) ~ bool
151/// ty_mapping!(T) ~ T
152/// ```
153#[doc(hidden)]
154#[macro_export]
155macro_rules! ty_mapping {
156 ( float ) => {
157 f64
158 };
159 ( int ) => {
160 i64
161 };
162 ( bool ) => {
163 bool
164 };
165 ( $other:ident ) => {
166 $other
167 };
168}
169
170/// Convert a Candle type into its internal Rust representation.
171///
172/// It builds upon the types provided by modules `nillable` and `time_travel`:
173/// - `nillable` provides the `Nillable` monad that represents values extended
174/// with a corrupted/uninitialized value.
175/// - `time_travel` provides the `O<T>` and `S<N, T>` types that provide an
176/// abstraction for looking into the past values of a variable:
177/// `S(5, S(3, S(2, O(0))))` represents a value of which we know the 4
178/// most recent values: 0 then 2 then 3 then 5 (most acient to most recent).
179///
180/// ```ignore
181/// ty!(float) ~ Nillable<f64>
182/// ty!(int+) ~ O<i64>
183/// ty!(bool++++) ~ S<S<S<O<bool>, bool>, bool>, bool>
184/// ```
185#[macro_export]
186macro_rules! ty {
187 () => { $crate::nillable::Nillable<()> };
188 ( $t:ident ) => { $crate::present_ty!($crate::ty_mapping!($t)) };
189 ( $t:ident + $($rest:tt)* ) => { $crate::past_ty!($crate::ty_mapping!($t), $($rest)*) };
190}
191
192/// Conditional debug printing. (pure)
193///
194/// Usage: `node_trace!(self, "debugging at step {}", self.__clock);` (statement)
195/// Assumption: `self` has a field `__trace: bool`.
196///
197/// If the trace is enabled by means of `self.__trace`, pass all the
198/// remaining arguments to `println!` for debugging.
199#[macro_export]
200macro_rules! node_trace {
201 ($this:ident, $($fmt:expr),+) => {
202 if $this.__trace {
203 println!($($fmt),+);
204 }
205 }
206}
207
208/// Remember a variable for the next iteration.
209///
210/// Usage: `update!(self, $foo)` (statement)
211/// Assumption: `self` has a field `$foo` AND `$foo` exists as a local variable.
212///
213/// Updates the internal field to reflect the new value of the variable in the
214/// environment. This invoques `S<_, _>::update_mut` which shifts by one all
215/// the past values of `$foo` (forgetting the most ancient one) and pushes
216/// the new value as the most recent.
217#[macro_export]
218macro_rules! update {
219 ($this:ident, $var:ident) => {{
220 use $crate::traits::Update;
221 $this.$var.update_mut($var)
222 }};
223}
224
225/// Fetch a variable from the environment. (pure)
226///
227/// Usage: `var!(self <~ $dt; $var)` (expression)
228/// Assumption: if `$dt = 0` then `$var` exists as a local variable.
229/// (`0` must appear textually, not as a variable)
230/// Assumption: if `$dt > 0` then `$var` exists as a field of `self`.
231///
232/// Reads the value that `$var` had `$dt` steps ago.
233/// This will either fetch `$var` from the current environment or
234/// read it as a field of self.
235#[macro_export]
236macro_rules! var {
237 ($this:ident <~ 0 ; $field:tt) => {
238 $field
239 };
240 ($this:ident <~ $dt:expr ; $field:tt) => {{
241 use $crate::traits::Ago;
242 *$this.$field.ago($dt)
243 }};
244}
245
246/// Wrap a value as a `Nillable`. (pure)
247///
248/// Usage: `lit!(true)`, `lit!(0.5)`, `lit!(42)` (expression)
249///
250/// Wraps a value of a compatible type (`bool`, `f64`, `i64`)
251/// as a `Nillable`.
252#[macro_export]
253macro_rules! lit {
254 ($lit:expr) => {
255 $crate::nillable::Defined($lit)
256 };
257}
258
259/// The uninitialized value. (pure)
260///
261/// Usage: `nil!()` (expression)
262///
263/// The value `Nil`.
264#[macro_export]
265macro_rules! nil {
266 ($($t:tt)*) => {
267 $crate::nillable::Nil
268 };
269}
270
271/// Increment the internal clock. [side-effects: only call once at the end]
272///
273/// Usage: `tick!(self)` (statement)
274/// Assumption: `self` has a field `__clock: usize`.
275///
276/// Increments the clock by 1.
277#[macro_export]
278macro_rules! tick {
279 ($this:ident) => {
280 $this.__clock += 1
281 };
282}
283
284/// Convert from `int` to `float`. (pure)
285///
286/// Usage: `float!($v)` (expression)
287///
288/// Converts a `Nillable<u64>` to a `Nillable<f64>`.
289#[macro_export]
290macro_rules! float {
291 ($val:expr) => {
292 $val.map(|i| i as f64)
293 };
294}
295
296/// The `->` Lustre operator. (pure)
297///
298/// Usage: `later!(self <~ $dt; $lhs, $rhs)` (expression)
299/// Assumption: `self` has a field `__clock: usize`
300///
301/// This performs a comparison against the clock:
302/// for instants before `$dt` (inclusive) it will return the left value,
303/// and for instants after `$dt` it will return the right value.
304#[macro_export]
305macro_rules! later {
306 ($this:ident <~ $dt:expr ; $lhs:expr, $rhs:expr) => {{
307 let rhs = $rhs;
308 if $this.__clock > $dt {
309 rhs
310 } else {
311 $lhs
312 }
313 }};
314}
315
316/// Invocation of subnodes. [side-effects: only call once for each node id]
317///
318/// Usage: `substep!(self; $id => { ... })` (expression)
319/// Assumption: `self` has a tuple field `__nodes` of which the `$id`'th component
320/// has a method `step` of the correct arity, input and output types.
321///
322/// This advances the `$id`'th subnode by one step by providing it with the arguments to
323/// its `step` method (provide the expression that serves as arguments between
324/// the `{ ... }`) and gives the return value of said method.
325/// This computation will not be performed if the clock is not at least `$dt`,
326/// allowing delayed execution of subnodes.
327#[macro_export]
328macro_rules! substep {
329 ($this:ident <~ 0 ; $id:tt => { $args:expr } ) => {{
330 $this.__nodes.$id.step($args.embed())
331 }};
332 ($this:ident <~ $dt:expr ; $id:tt => { $args:expr } ) => {{
333 use $crate::traits::*;
334 if $this.__clock >= $dt {
335 $this.__nodes.$id.step($args.embed())
336 } else {
337 AllNil::auto_size()
338 }
339 }};
340}
341
342/// Conditional on `Nillable`s. (pure)
343///
344/// Usage: `ifx!(($b) then { $yes } else { $no })` (expression)
345///
346/// Will return `$yes` if `$b` holds (`true` and not `Nil`),
347/// and `$no` if `$b` does not hold (`false` and not `Nil`).
348/// A `Nil` test condition contaminates the entire expression.
349#[macro_export]
350macro_rules! ifx {
351 ( ( $b:expr ) then { $yes:expr } else { $no:expr } ) => {{
352 use $crate::nillable::*;
353 let yes = $yes;
354 let no = $no;
355 match $b {
356 Defined(true) => yes,
357 Defined(false) => no,
358 Nil => AllNil::auto_size(),
359 }
360 }};
361}
362
363/// Application of a binary operator. (pure)
364///
365/// Usage: `binop!(+; $lhs, $rhs)`, ... (expression)
366/// Reminder: `Nillable` has wrapper implementations for `+`, `-`, `/`, `*`, `%`, `|`, `&`, `^`
367///
368/// If the two arguments properly implement the given binary operator,
369/// this will apply it.
370#[macro_export]
371macro_rules! binop {
372 ($op:tt ; $lhs:expr, $rhs:expr) => { $lhs $op $rhs };
373}
374
375/// Application of a unary operator. (pure)
376///
377/// Usage: `binop!(-; $val)`, ... (expression)
378/// Reminder: `Nillable` has wrapper implementations for `-`, `!`
379///
380/// If the argument properly implements the given binary operator,
381/// this will apply it.
382#[macro_export]
383macro_rules! unop {
384 ($op:tt ; $e:expr) => { $op $e };
385}
386
387/// Do not use directly, call `cmp` instead.
388#[doc(hidden)]
389#[macro_export]
390macro_rules! nillable_cmp_ord {
391 ($ord:ident, $res:expr, $lhs:expr, $rhs:expr) => {{
392 match $lhs.cmp($rhs) {
393 Some(::std::cmp::Ordering::$ord) => $crate::lit!($res),
394 Some(_) => $crate::lit!(!$res),
395 None => $crate::nillable::AllNil::auto_size(),
396 }
397 }};
398}
399
400/// Do not use directly, call `cmp` instead.
401#[doc(hidden)]
402#[macro_export]
403macro_rules! nillable_cmp_eq {
404 ($equal:expr, $lhs:expr, $rhs:expr) => {{
405 match $lhs.eq($rhs) {
406 Some(true) => $crate::lit!($equal),
407 Some(false) => $crate::lit!(!$equal),
408 None => $crate::nillable::AllNil::auto_size(),
409 }
410 }};
411}
412
413/// Comparison operators on `Nillable`. (pure)
414///
415/// Usage: `cmp!(<=; $lhs, $rhs)`, ... (expression)
416/// Reminder: `Nillable` has wrapper implementations for `<=`, `>=`, `<`, `>`, `!=`, `==`
417/// Warning: the pairs `<=` and `>`, `>` and `<=`, `==` and `!=`,
418/// are only opposites of each other as long as the arguments
419/// are not `Nil`.
420///
421/// Evaluates the comparison of its arguments outputting a `Nillable<bool>`:
422/// a `Nil` argument will contaminate the result.
423#[macro_export]
424macro_rules! cmp {
425 (== ; $lhs:expr, $rhs:expr) => {{
426 $crate::nillable_cmp_eq!(true, $lhs, $rhs)
427 }};
428 (!= ; $lhs:expr, $rhs:expr) => {{
429 $crate::nillable_cmp_eq!(false, $lhs, $rhs)
430 }};
431 (< ; $lhs:expr, $rhs:expr) => {{
432 $crate::nillable_cmp_ord!(Less, true, $lhs, $rhs)
433 }};
434 (> ; $lhs:expr, $rhs:expr) => {{
435 $crate::nillable_cmp_ord!(Greater, true, $lhs, $rhs)
436 }};
437 (<= ; $lhs:expr, $rhs:expr) => {{
438 $crate::nillable_cmp_ord!(Greater, false, $lhs, $rhs)
439 }};
440 (>= ; $lhs:expr, $rhs:expr) => {{
441 $crate::nillable_cmp_ord!(Less, false, $lhs, $rhs)
442 }};
443}
444
445/// Assert that a boolean holds. (statement)
446///
447/// Usage: `truth!(v, "Assertion v failed")`.
448/// This fails on both `false` and `nil`.
449#[macro_export]
450macro_rules! truth {
451 ($b:expr, $msg:expr) => {{
452 use $crate::nillable::*;
453 let b: Nillable<bool> = $b;
454 match b {
455 Defined(true) => {}
456 Defined(false) => panic!("Assertion failed: {}", $msg),
457 _ => panic!("Value is nil: {}", $msg),
458 }
459 }};
460}
461
462/// Select only when the guard is true. (pure)
463///
464/// `when!(b; e)` is `e` when `b` is `true` and `nil` otherwise.
465#[macro_export]
466macro_rules! when {
467 ( $b:expr ; $e:expr ) => {{
468 use $crate::nillable::*;
469 let b: Nillable<bool> = $b;
470 let e = $e;
471 match b {
472 Defined(true) => e,
473 _ => AllNil::auto_size(),
474 }
475 }};
476}
477
478/// Select only when the guard is true. (pure)
479///
480/// `whenot!(b; e)` is `e` when `b` is `false` and `nil` otherwise.
481#[macro_export]
482macro_rules! whenot {
483 ( $b:expr ; $e:expr ) => {{
484 use $crate::nillable::*;
485 let b: Nillable<bool> = $b;
486 let e = $e;
487 match b {
488 Defined(false) => e,
489 _ => AllNil::auto_size(),
490 }
491 }};
492}
493
494/// Merge two streams (equivalent to `ifx`) (pure)
495///
496/// `merge!(b; on, off)` is `on` when `b` is `true` and `off` when
497/// `b` is `false`.
498#[macro_export]
499macro_rules! merge {
500 ( $b:expr ; $on:expr, $off:expr ) => {{
501 use $crate::nillable::*;
502 let b: Nillable<bool> = $b;
503 let on = $on;
504 let off = $off;
505 match $b {
506 Defined(true) => on,
507 Defined(false) => off,
508 _ => AllNil::auto_size(),
509 }
510 }};
511}
512
513/// Read the inputs of the node and immediately return if
514/// the implicit clock is inactive, i.e. if the first element of the arguments
515/// is `nil`.
516///
517/// `implicit_clock!(inputs);`
518///
519/// This should definitely be called before `tick!`, and preferably
520/// before any computation.
521#[macro_export]
522macro_rules! implicit_clock {
523 ( $inputs:expr ) => {{
524 use $crate::nillable::*;
525 if $inputs.first_is_nil() {
526 return AllNil::auto_size();
527 }
528 }};
529}