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}