chandeliers_syn/translate/
mod.rs

1//! Translator from the Lustre parsing AST to the Candle pre-analysis AST.
2//!
3//! Translating from Lustre to Candle is not a difficult task because the two
4//! languages have mostly identical structures, but there are nevertheless
5//! numerous traps and subtleties that although minor for a programmer are very
6//! relevant for a systematic translator.
7//!
8//! We summarize here the differences between the two languages that constitute
9//! the details to look out for in the implementations of this file.
10//!
11//!
12//!
13//! # Global vs Local name resolution
14//!
15//! Relevant `impl`: `VarExpr`.
16//!
17//! Lustre allows for local variables to shadow global constants, and any
18//! use of a variable -- local or constant -- is syntactically identical.
19//! Not so in Candle, where several technical restrictions require that the
20//! Candle AST differentiate between the two:
21//! - Rust does not allow global constant shadowing, so during codegen
22//!   Candle must pick fresh identifiers for local variables that cannot
23//!   collide with those of constants,
24//! - global constants are given a Candle type that is not the same as that
25//!   of local variables with the same Lustre type.
26//!   Indeed the Candle type for variables of `int` is `Nillable<i64>`,
27//!   and the Candle type for constants of `int` is a plain `i64`.
28//! These thus require differences in the handling of variables and constants
29//! at both definition site and call site, which means that the translator
30//! must maintain a mutable context of currently shadowed variables.
31//!
32//!
33//!
34//! # Precedence
35//!
36//! Relevant `impl`s: all `expr::*`.
37//!
38//! The hierarchy of expressions exists only for parsing purposes to express
39//! the precedence of operators, and in Candle there is no longer any notion
40//! of `AtomicExpr`, all exressions are at the same level.
41//! Candle has much fewer expression types (`Expr`) than the Lustre parsing
42//! AST (`MulExpr`, `AddExpr`, `PreExpr`, `AtomicExpr`, ...), and this is
43//! reflected in the translation that needs to create much more `Box`es
44//! but in return has more translation functions with the same output type.
45//!
46//!
47//!
48//! # Associativity
49//!
50//! Relevant `impl`s: `AddExpr`, `OrExpr`, `AndExpr`, `FbyExpr`, `MulExpr`.
51//!
52//! The parser cannot handle associativity, and instead associative expressions
53//! are returned as a flat vector. For example `1 + 2 + 3 + 4` is parsed as a
54//! vector of pairs `[Pair(1, +), Pair(2, +), Pair(3, +), End(4)]`, and we
55//! want to resolve it into
56//! ```skip
57//! Bin {
58//!     lhs: Bin {
59//!         lhs: Bin {
60//!             lhs: 1,
61//!             op: +,
62//!             rhs: 2,
63//!         },
64//!         op: +,
65//!         rhs: 3,
66//!     },
67//!     op: +,
68//!     rhs: 4,
69//! }
70//! ```
71//! Symetrically, `1 fby 2 fby 3 fby 4` is parsed as
72//! `[Pair(1, fby), Pair(2, fby), Pair(3, fby), End(4)]` and should be resolved as
73//! ```skip
74//! Fby {
75//!     depth: 0,
76//!     before: 1,
77//!     after: Fby {
78//!         depth: 1,
79//!         before: 2,
80//!         after: Fby {
81//!             depth: 2,
82//!             before: 3,
83//!             after: 4,
84//!         },
85//!     },
86//! }
87//! ```
88//! To this end we provide helpers in `assoc` to efficiently handle binary associative
89//! operators.
90//!
91//!
92//!
93//! # Noassoc comparisons
94//!
95//! Relevant `impl`: `CmpExpr`.
96//!
97//! A complementary problem to the above issue is the fact that comparisons are not
98//! associative, but the parser accepts them as such. `1 <= 2 < 3 <> 4` is parsed
99//! as `[Pair(1, <=), Pair(2, <), Pair(3, <>), End(4)]` but must be rejected on
100//! the account of `<=` not being associative with `<`.
101//!
102//!
103//!
104//! # Size-1 tuple flattening
105//!
106//! Relevant `impl`s: `TargetExprTuple`, `TargetExpr`, `CallExpr`.
107//!
108//! For Lustre we adapt and generalize the convention of Rust on what is a tuple
109//! or simply a parenthesized expression, and where trailing commas are allowed.
110//!
111//! For tuples, we employ exactly the Rust convention, where
112//! - `()`, `(())`, `((()))`, ... are all equal to the unit tuple,
113//! - `1`, `(1)`, `((1))`, ... are all equal to the integer `1`,
114//! - `(1, 2)`, `(1, 2,)`, `((1), (2))`, `((1, 2))`, ... are all tuples with two elements of `int`
115//!   (i.e. up to one trailing comma and an unlimited amount of surrounding parentheses are allowed),
116//! - however, specifically for size 1, a trailing comma changes the meaning:
117//!   - `(1) <> (1,)` (left is of type `int`, right is of type `(int)` a 1-element tuple)
118//!   - `(()) <> ((),)` (left is of type `unit`, right is a size-1 tuple containing `unit`)
119//!
120//! For function calls however we differ slightly from Rust, because the arguments of
121//! a function are not a `Tuple<Expr>` but a plain `Expr` that just might happen to
122//! contain a tuple. This has the following notable consequences:
123//! - A node defined with `node foo(_, _, _ : int)` may accept interchangeably
124//!   - `foo(1, 2, 3)`,
125//!   - `foo((1, 2, 3))`,
126//!   - or even `foo(bar())` if `node bar returns(_, _, _ : int)`.
127//! - A node without any arguments can be given one unit argument
128//!   - `node count() returns (n : int)` can accept `count(())`
129//! - trailing commas are allowed except when the size is exactly one:
130//!   - `foo(1, 2, 3,) = foo(1, 2, 3)`
131//!   - `foo(1) <> foo(1,)`
132//!   - `foo(()) <> foo((),)`
133//!
134//!
135//!
136//! # Fby-expansion and pre-pushing
137//!
138//! Relevant `impl`s: `FbyExpr`, `PreExpr`, `VarExpr`.
139//!
140//! Lustre and Candle don't quite have the same temporal operators.
141//! Both have a construct to fetch one value before a certain clock value
142//! and another after (`->` in Lustre, `later!` in Candle), but
143//! 1. only Lustre has `fby`, and
144//! 2. only Lustre can apply `pre` to arbitrary expressions.
145//! The first of these is easy: `e1 fby e2` expands to `e1 -> pre e2`,
146//! but this still leaves the issue that `e2` could be an expression on which
147//! Candle does not support `pre`. Indeed Candle's only other temporal operator
148//! is `pre^n v` (written `var!(self <~ n; v)`) that fetches the value `n` instants
149//! ago, but only for `v` a variable, not an arbitrary expression.
150//!
151//! The translator must thus push the `pre` to the leaves, according to the
152//! following non-exhaustive list of rules:
153//! - `pre n ~~> n` (`n` a literal or constant)
154//! - `pre (e1 + e2) ~~> (pre e1) + (pre e2)`
155//! - `pre (-e) ~~> -(pre e)`
156//! - `pre (e1 or e2) ~~> (pre e1) or (pre e2)`
157//! - `pre (e1 ->{n} e2) ~~> (pre e1) ->{n-1} (pre e2)`
158//! - etc...
159//!
160//! To this end the translator carries in its context the current depth at
161//! with the expression is evaluated. Usually "regular" (`+`, `-`, `or`, ...)
162//! operators propagate the same depth to their arguments, and temporal operators
163//! act upon the depth: `pre` increases it by one, `fby` evaluates its two arguments
164//! on different depths, etc.
165//!
166//! Alternatively, temporal operators are translated differently when
167//! the `universal_pre` option is set, and this generates `Register` and `Flip`
168//! instead of `Pre` and `Then`.
169
170use std::collections::HashSet;
171
172use chandeliers_err::{self as err, EAccum, Transparent};
173use chandeliers_san::ast as tgt;
174use chandeliers_san::sp::{Sp, Span, WithSpan};
175
176use crate::ast as src;
177
178mod options;
179
180/// Current compiler pass for options manager (see [`chandeliers_san::ast::options`] for details).
181type This = tgt::options::usage::Parsing;
182
183/// Trait to translate from the parsing AST to the analysis AST.
184pub trait Translate {
185    /// Corresponding item in the analysis AST.
186    type Output;
187    /// Context that the function needs (e.g. known variables, type of the context, ...)
188    type Ctx<'i>;
189    /// Convert from one AST node to another.
190    /// - `run_uid`: used in order to generate unique identifiers that will not
191    ///   collide with those of other invocations of the toplevel macro.
192    /// - `span`: provided by the wrapper, the `Span` of the entire node.
193    ///   You probably won't need it unless internal components need the same span
194    ///   as their parent.
195    /// - `ctx`: you may ask for any immutable state to be provided as context.
196    ///
197    /// # Errors
198    /// The contents cannot be translated.
199    fn translate(
200        self,
201        eaccum: &mut EAccum,
202        run_uid: Transparent<usize>,
203        span: Span,
204        ctx: Self::Ctx<'_>,
205    ) -> Option<Self::Output>;
206}
207
208/// Helper trait to translate from the parsing AST to the analysis AST.
209/// Only `Sp<T>` is expected to implement this trait.
210#[expect(
211    clippy::module_name_repetitions,
212    reason = "False positive: we want the name to match the `Translate` trait."
213)]
214pub trait SpanTranslate {
215    /// Corresponding item in the analysis AST.
216    type Output;
217    /// Context that the function needs (e.g. known variables, type of the context, ...)
218    type Ctx<'i>;
219    /// Convert from one AST node to another.
220    /// - `run_uid`: used in order to generate unique identifiers that will not
221    ///   collide with those of other invocations of the toplevel macro.
222    /// - `span`: provided by the wrapper, the `Span` of the entire node.
223    ///   You probably won't need it unless internal components need the same span
224    ///   as their parent.
225    /// - `ctx`: you may ask for any immutable state to be provided as context.
226    ///
227    /// # Errors
228    /// The contents cannot be translated.
229    fn translate(
230        self,
231        eaccum: &mut EAccum,
232        run_uid: Transparent<usize>,
233        ctx: Self::Ctx<'_>,
234    ) -> Option<Sp<Self::Output>>;
235    /// Same as `translate`, but do now wrap in a span.
236    /// This helps for very nested structures that don't want a `Sp<_>` at every level.
237    ///
238    /// # Errors
239    /// The contents cannot be translated.
240    fn flat_translate(
241        self,
242        eaccum: &mut EAccum,
243        run_uid: Transparent<usize>,
244        ctx: Self::Ctx<'_>,
245    ) -> Option<Self::Output>;
246}
247
248/// `Sp<_>` is transparently translatable, but it makes its span available to the inner translation
249/// function.
250impl<T> SpanTranslate for Sp<T>
251where
252    T: Translate,
253{
254    type Ctx<'i> = T::Ctx<'i>;
255    type Output = T::Output;
256
257    fn translate(
258        self,
259        eaccum: &mut EAccum,
260        run_uid: Transparent<usize>,
261        ctx: Self::Ctx<'_>,
262    ) -> Option<Sp<Self::Output>> {
263        let res = self.t.translate(eaccum, run_uid, self.span, ctx)?;
264        Some(Sp {
265            t: res,
266            span: self.span,
267        })
268    }
269
270    fn flat_translate(
271        self,
272        eaccum: &mut EAccum,
273        run_uid: Transparent<usize>,
274        ctx: Self::Ctx<'_>,
275    ) -> Option<Self::Output> {
276        self.t.translate(eaccum, run_uid, self.span, ctx)
277    }
278}
279
280/// Accumulator of detected dependent types.
281///
282/// We find these in the input and output clocks and we're going
283/// to insert sode code to make them not appear as unused variables.
284type DepTyAccum<'i> = &'i mut Vec<Sp<tgt::var::Local>>;
285
286/// `Box<_>` is transparently translatable, *and it consumes the `Box<_>`*.
287///
288/// The output is not wrapped by default because the parsing AST and the analysis
289/// AST require boxes in different places.
290impl<T> Translate for Box<T>
291where
292    T: Translate,
293{
294    type Ctx<'i> = T::Ctx<'i>;
295    type Output = T::Output;
296    fn translate(
297        self,
298        eaccum: &mut EAccum,
299        run_uid: Transparent<usize>,
300        span: Span,
301        ctx: Self::Ctx<'_>,
302    ) -> Option<Self::Output> {
303        (*self).translate(eaccum, run_uid, span, ctx)
304    }
305}
306
307impl Translate for src::Prog {
308    type Ctx<'i> = ();
309    type Output = tgt::decl::Prog;
310    fn translate(
311        self,
312        eaccum: &mut EAccum,
313        run_uid: Transparent<usize>,
314        _span: Span,
315        (): (),
316    ) -> Option<tgt::decl::Prog> {
317        let mut decls = Vec::new();
318        for decl in self.decls {
319            decls.push(decl.translate(eaccum, run_uid, options::Decl::default())?);
320        }
321        Some(tgt::decl::Prog { decls })
322    }
323}
324
325impl Translate for src::AttrDecl {
326    type Ctx<'i> = options::Decl;
327    type Output = tgt::decl::Decl;
328    fn translate(
329        self,
330        eaccum: &mut EAccum,
331        run_uid: Transparent<usize>,
332        _span: Span,
333        options: options::Decl,
334    ) -> Option<tgt::decl::Decl> {
335        match self {
336            Self::Tagged(attr, n) => {
337                let options = options.with(eaccum, attr.map(|_, x| *x))?;
338                n.flat_translate(eaccum, run_uid, options)
339            }
340            Self::Node(n) => n.flat_translate(eaccum, run_uid, options),
341        }
342    }
343}
344
345impl Translate for src::Decl {
346    type Ctx<'i> = options::Decl;
347    type Output = tgt::decl::Decl;
348    fn translate(
349        self,
350        eaccum: &mut EAccum,
351        run_uid: Transparent<usize>,
352        _span: Span,
353        options: options::Decl,
354    ) -> Option<tgt::decl::Decl> {
355        Some(match self {
356            Self::Const(c) => {
357                let options = options.for_const(eaccum)?;
358                tgt::decl::Decl::Const(c.translate(eaccum, run_uid, options)?)
359            }
360            Self::Node(n) => {
361                let options = options.for_node(eaccum)?;
362                tgt::decl::Decl::Node(n.translate(eaccum, run_uid, options)?)
363            }
364            Self::Extern(e) => e.flat_translate(eaccum, run_uid, options)?,
365        })
366    }
367}
368
369impl Translate for src::Extern {
370    type Ctx<'i> = options::Decl;
371    type Output = tgt::decl::Decl;
372    fn translate(
373        self,
374        eaccum: &mut EAccum,
375        run_uid: Transparent<usize>,
376        _span: Span,
377        options: options::Decl,
378    ) -> Option<tgt::decl::Decl> {
379        Some(match self {
380            Self::Const(c) => {
381                let options = options.for_ext_const(eaccum)?;
382                tgt::decl::Decl::ExtConst(c.translate(eaccum, run_uid, options)?)
383            }
384            Self::Node(n) => {
385                let options = options.for_ext_node(eaccum)?;
386                tgt::decl::Decl::ExtNode(n.translate(eaccum, run_uid, options)?)
387            }
388        })
389    }
390}
391
392impl Translate for src::ExtConst {
393    type Ctx<'i> = tgt::options::ExtConst;
394    type Output = tgt::decl::ExtConst;
395    fn translate(
396        self,
397        eaccum: &mut EAccum,
398        run_uid: Transparent<usize>,
399        _span: Span,
400        options: tgt::options::ExtConst,
401    ) -> Option<Self::Output> {
402        let name = self.name.map(|span, name| tgt::var::Global {
403            repr: name.to_string().with_span(span),
404            run_uid,
405        });
406        let ty = self.ty.translate(eaccum, run_uid, &mut Vec::new())?;
407        Some(tgt::decl::ExtConst {
408            name,
409            ty: ty.t.inner,
410            options,
411        })
412    }
413}
414
415impl Translate for src::ExtNode {
416    type Ctx<'i> = tgt::options::ExtNode;
417    type Output = tgt::decl::ExtNode;
418    fn translate(
419        self,
420        eaccum: &mut EAccum,
421        run_uid: Transparent<usize>,
422        _span: Span,
423        options: tgt::options::ExtNode,
424    ) -> Option<tgt::decl::ExtNode> {
425        let name = self.name.map(|span, name| tgt::decl::NodeName {
426            repr: name.to_string().with_span(span),
427            run_uid,
428        });
429        let inputs = self.inputs.translate(eaccum, run_uid, &mut Vec::new())?;
430        let outputs = self.outputs.translate(eaccum, run_uid, &mut Vec::new())?;
431        Some(tgt::decl::ExtNode {
432            name,
433            inputs,
434            outputs,
435            options,
436        })
437    }
438}
439
440/// Helper to construct an expression.
441///
442/// This exists to extract additional information as a byproduct of constructing
443/// the expression.
444#[derive(Default)]
445struct ExprCtx {
446    /// List of blocks to append to whenever there is a function call.
447    blocks: Vec<tgt::decl::NodeInstance>,
448    /// List of statements to append to whenever a new statement is complete.
449    stmts: Vec<Sp<tgt::stmt::Statement>>,
450    /// Local variable names that might shadow global variables.
451    shadow_glob: HashSet<String>,
452    /// Are we using the temporal operators with buffers or registers ?
453    use_registers: bool,
454    /// List of registers, if any.
455    /// A register is a struct that stores a value from one iteration to the next.
456    registers: Vec<tgt::decl::RegisterInstance>,
457    /// List of flips, if any.
458    /// A flip is a 0-then-1 boolean to perform an initialization exactly once.
459    flips: Vec<tgt::decl::FlipInstance>,
460}
461
462/// Accessor for `ExprCtx`.
463/// This is not `Copy` or even `Clone`, so if you need to duplicate it
464/// you should use the `fork!` macro that will reborrow a fresh copy.
465pub struct ExprCtxView<'i> {
466    /// List of blocks to append to whenever there is a function call.
467    blocks: &'i mut Vec<tgt::decl::NodeInstance>,
468    /// List of statements to append to whenever a new statement is complete.
469    stmts: &'i mut Vec<Sp<tgt::stmt::Statement>>,
470    /// Local variable names that might shadow global variables.
471    shadow_glob: &'i HashSet<String>,
472    /// Current depth of the translation (i.e. number of `pre` in front),
473    /// influences the production of `var::Past` with the current value.
474    depth: usize,
475    /// Are we using the temporal operators with buffers or registers ?
476    use_registers: bool,
477    /// List of registers, if any.
478    registers: &'i mut Vec<tgt::decl::RegisterInstance>,
479    /// List of flips, if any.
480    flips: &'i mut Vec<tgt::decl::FlipInstance>,
481}
482
483impl ExprCtx {
484    /// Create a new accessor.
485    fn view(&mut self) -> ExprCtxView<'_> {
486        ExprCtxView {
487            blocks: &mut self.blocks,
488            stmts: &mut self.stmts,
489            shadow_glob: &self.shadow_glob,
490            depth: 0,
491            use_registers: self.use_registers,
492            registers: &mut self.registers,
493            flips: &mut self.flips,
494        }
495    }
496}
497
498impl ExprCtxView<'_> {
499    /// Increase the depth of the analysis. This affects the temporal operators
500    /// `pre`, `fby`, and `->`.
501    fn bump(self, n: usize) -> Self {
502        Self {
503            depth: self.depth + n,
504            ..self
505        }
506    }
507
508    /// Increase the depth by 1.
509    fn incr(self) -> Self {
510        self.bump(1)
511    }
512}
513
514/// Reborrow a view on an `ExprCtx`.
515macro_rules! fork {
516    ($this:expr) => {
517        crate::translate::ExprCtxView {
518            blocks: &mut *$this.blocks,
519            stmts: &mut *$this.stmts,
520            shadow_glob: &*$this.shadow_glob,
521            registers: &mut *$this.registers,
522            flips: &mut *$this.flips,
523            depth: $this.depth,
524            use_registers: $this.use_registers,
525        }
526    };
527}
528
529impl Translate for src::Node {
530    type Ctx<'i> = tgt::options::Node;
531    type Output = tgt::decl::Node;
532    fn translate(
533        self,
534        eaccum: &mut EAccum,
535        run_uid: Transparent<usize>,
536        _span: Span,
537        options: tgt::options::Node,
538    ) -> Option<Self::Output> {
539        let name = self.name.map(|span, name| tgt::decl::NodeName {
540            repr: name.to_string().with_span(span),
541            run_uid,
542        });
543        let mut deptys = Vec::default();
544        let inputs = self.inputs.translate(eaccum, run_uid, &mut deptys)?;
545        let outputs = self.outputs.translate(eaccum, run_uid, &mut deptys)?;
546        let locals = self.locals.translate(eaccum, run_uid, &mut deptys)?;
547        let mut ectx = ExprCtx {
548            use_registers: *options.universal_pre.fetch::<This>(),
549            ..Default::default()
550        };
551        for shadows in &[&inputs, &outputs, &locals] {
552            for s in shadows.t.iter() {
553                ectx.shadow_glob.insert(s.t.name.t.repr.t.clone());
554            }
555        }
556
557        for def in self.defs {
558            def.translate(eaccum, run_uid, ectx.view())?;
559        }
560        let ExprCtx {
561            blocks,
562            stmts,
563            registers,
564            flips,
565            ..
566        } = ectx;
567        Some(tgt::decl::Node {
568            name,
569            options,
570            inputs,
571            outputs,
572            locals,
573            blocks,
574            deptys,
575            stmts,
576            registers,
577            flips,
578        })
579    }
580}
581
582impl Translate for src::Const {
583    type Ctx<'i> = tgt::options::Const;
584    type Output = tgt::decl::Const;
585    fn translate(
586        self,
587        eaccum: &mut EAccum,
588        run_uid: Transparent<usize>,
589        span: Span,
590        options: tgt::options::Const,
591    ) -> Option<tgt::decl::Const> {
592        let name = self.name.map(|span, name| tgt::var::Global {
593            repr: name.to_string().with_span(span),
594            run_uid,
595        });
596        let ty = self.ty.translate(eaccum, run_uid, ())?;
597        let mut ectx = ExprCtx::default();
598        let value = self.value.translate(eaccum, run_uid, ectx.view())?;
599        if !ectx.stmts.is_empty() || !ectx.blocks.is_empty() {
600            eaccum.error(err::NotConst {
601                site: span,
602                what: "Function calls are",
603            })?;
604        }
605        Some(tgt::decl::Const {
606            name,
607            options,
608            ty,
609            value,
610        })
611    }
612}
613
614impl Translate for src::ArgsTys {
615    type Ctx<'i> = DepTyAccum<'i>;
616    type Output = tgt::Tuple<Sp<tgt::decl::TyVar>>;
617    fn translate(
618        self,
619        eaccum: &mut EAccum,
620        run_uid: Transparent<usize>,
621        _span: Span,
622        ctx: Self::Ctx<'_>,
623    ) -> Option<tgt::Tuple<Sp<tgt::decl::TyVar>>> {
624        let mut vs = tgt::Tuple::default();
625        for item in self.items {
626            item.translate(eaccum, run_uid, (&mut vs, ctx))?;
627        }
628        Some(vs)
629    }
630}
631
632impl Translate for src::ArgsTy {
633    type Ctx<'i> = (&'i mut tgt::Tuple<Sp<tgt::decl::TyVar>>, DepTyAccum<'i>);
634    type Output = ();
635    fn translate(
636        self,
637        eaccum: &mut EAccum,
638        run_uid: Transparent<usize>,
639        _span: Span,
640        ctx: Self::Ctx<'_>,
641    ) -> Option<()> {
642        let ty = self.ty.translate(eaccum, run_uid, ctx.1)?;
643        self.args.translate(eaccum, run_uid, (ctx.0, ty))?;
644        Some(())
645    }
646}
647
648impl Translate for src::Decls {
649    type Ctx<'i> = (
650        &'i mut tgt::Tuple<Sp<tgt::decl::TyVar>>,
651        Sp<tgt::ty::Clocked<tgt::ty::Base>>,
652    );
653    type Output = ();
654    fn translate(
655        self,
656        _eaccum: &mut EAccum,
657        _run_uid: Transparent<usize>,
658        _span: Span,
659        (vars, ty): Self::Ctx<'_>,
660    ) -> Option<()> {
661        for id in self.ids {
662            vars.push(id.map(|span, id| {
663                tgt::decl::TyVar {
664                    name: tgt::var::Local {
665                        repr: id.to_string().with_span(span),
666                    }
667                    .with_span(span),
668                    ty: ty.clone().map(|span, ty| {
669                        tgt::ty::Stream {
670                            ty: ty.with_span(span),
671                            depth: tgt::past::Depth {
672                                // Putting in a dummy value 0 for `dt`, don't forget to update
673                                // it by depth propagation in the positivity check...
674                                dt: 0,
675                            }
676                            .with_span(span),
677                        }
678                    }),
679                }
680            }));
681        }
682        Some(())
683    }
684}
685
686impl Translate for src::ty::Type {
687    type Ctx<'i> = DepTyAccum<'i>;
688    type Output = tgt::ty::Clocked<tgt::ty::Base>;
689    fn translate(
690        self,
691        eaccum: &mut EAccum,
692        run_uid: Transparent<usize>,
693        _span: Span,
694        ctx: Self::Ctx<'_>,
695    ) -> Option<Self::Output> {
696        let inner = self.base.translate(eaccum, run_uid, ())?;
697        let clk = self.clock.translate(eaccum, run_uid, ctx)?;
698        Some(tgt::ty::Clocked { inner, clk })
699    }
700}
701
702impl Translate for src::ty::Base {
703    type Ctx<'i> = ();
704    type Output = tgt::ty::Base;
705    fn translate(
706        self,
707        _eaccum: &mut EAccum,
708        _run_uid: Transparent<usize>,
709        _span: Span,
710        (): (),
711    ) -> Option<Self::Output> {
712        Some(match self {
713            Self::Int(_) => tgt::ty::Base::Int,
714            Self::Float(_) => tgt::ty::Base::Float,
715            Self::Bool(_) => tgt::ty::Base::Bool,
716            Self::Other(t) => tgt::ty::Base::Other(t.as_ref().map(|_, c| format!("{c}"))),
717        })
718    }
719}
720
721impl Translate for src::ty::Clock {
722    type Ctx<'i> = DepTyAccum<'i>;
723    type Output = tgt::ty::Clock;
724    fn translate(
725        self,
726        eaccum: &mut EAccum,
727        run_uid: Transparent<usize>,
728        _span: Span,
729        ctx: Self::Ctx<'_>,
730    ) -> Option<Self::Output> {
731        match self {
732            Self::When(w) => w.flat_translate(eaccum, run_uid, ctx),
733            Self::Whenot(w) => w.flat_translate(eaccum, run_uid, ctx),
734            Self::None => Some(tgt::ty::Clock::Implicit),
735        }
736    }
737}
738
739impl Translate for src::ty::When {
740    type Ctx<'i> = DepTyAccum<'i>;
741    type Output = tgt::ty::Clock;
742    fn translate(
743        self,
744        _eaccum: &mut EAccum,
745        _run_uid: Transparent<usize>,
746        _span: Span,
747        ctx: Self::Ctx<'_>,
748    ) -> Option<Self::Output> {
749        ctx.push(
750            tgt::var::Local {
751                repr: self.clock.as_ref().map(|_, c| format!("{c}")),
752            }
753            .with_span(self.clock.span),
754        );
755        Some(tgt::ty::Clock::Explicit {
756            activation: true,
757            id: self.clock.map(|_, c| format!("{c}")),
758        })
759    }
760}
761
762impl Translate for src::ty::Whenot {
763    type Ctx<'i> = DepTyAccum<'i>;
764    type Output = tgt::ty::Clock;
765    fn translate(
766        self,
767        _eaccum: &mut EAccum,
768        _run_uid: Transparent<usize>,
769        _span: Span,
770        ctx: Self::Ctx<'_>,
771    ) -> Option<Self::Output> {
772        ctx.push(
773            tgt::var::Local {
774                repr: self.clock.as_ref().map(|_, c| format!("{c}")),
775            }
776            .with_span(self.clock.span),
777        );
778        Some(tgt::ty::Clock::Explicit {
779            activation: false,
780            id: self.clock.map(|_, c| format!("{c}")),
781        })
782    }
783}
784
785impl Translate for src::OptionalVarsDecl {
786    type Ctx<'i> = DepTyAccum<'i>;
787    type Output = tgt::Tuple<Sp<tgt::decl::TyVar>>;
788    fn translate(
789        self,
790        eaccum: &mut EAccum,
791        run_uid: Transparent<usize>,
792        _span: Span,
793        ctx: Self::Ctx<'_>,
794    ) -> Option<Self::Output> {
795        match self {
796            Self::Decls(d) => d.flat_translate(eaccum, run_uid, ctx),
797            Self::None => Some(tgt::Tuple::default()),
798        }
799    }
800}
801
802impl Translate for src::VarsDecl {
803    type Ctx<'i> = DepTyAccum<'i>;
804    type Output = tgt::Tuple<Sp<tgt::decl::TyVar>>;
805    fn translate(
806        self,
807        eaccum: &mut EAccum,
808        run_uid: Transparent<usize>,
809        _span: Span,
810        ctx: Self::Ctx<'_>,
811    ) -> Option<Self::Output> {
812        self.decls.flat_translate(eaccum, run_uid, ctx)
813    }
814}
815
816impl Translate for src::Statement {
817    type Ctx<'i> = ExprCtxView<'i>;
818    type Output = ();
819    fn translate(
820        self,
821        eaccum: &mut EAccum,
822        run_uid: Transparent<usize>,
823        _span: Span,
824        ctx: Self::Ctx<'_>,
825    ) -> Option<()> {
826        match self {
827            Self::Assert(ass) => ass.flat_translate(eaccum, run_uid, ctx),
828            Self::Def(def) => def.flat_translate(eaccum, run_uid, ctx),
829        }
830    }
831}
832
833impl Translate for src::Assertion {
834    type Ctx<'i> = ExprCtxView<'i>;
835    type Output = ();
836    fn translate(
837        self,
838        eaccum: &mut EAccum,
839        run_uid: Transparent<usize>,
840        span: Span,
841        ctx: Self::Ctx<'_>,
842    ) -> Option<()> {
843        let e = self.expr.translate(eaccum, run_uid, fork!(ctx))?;
844        ctx.stmts
845            .push(tgt::stmt::Statement::Assert(e).with_span(span));
846        Some(())
847    }
848}
849
850impl Translate for src::Def {
851    type Ctx<'i> = ExprCtxView<'i>;
852    type Output = ();
853    fn translate(
854        self,
855        eaccum: &mut EAccum,
856        run_uid: Transparent<usize>,
857        span: Span,
858        ctx: Self::Ctx<'_>,
859    ) -> Option<()> {
860        let target = self.target.translate(eaccum, run_uid, ())?;
861        let source = self.source.translate(eaccum, run_uid, fork!(ctx))?;
862        ctx.stmts
863            .push(tgt::stmt::Statement::Let { target, source }.with_span(span));
864        Some(())
865    }
866}
867
868impl Translate for src::TargetExpr {
869    type Ctx<'i> = ();
870    type Output = tgt::stmt::VarTuple;
871    fn translate(
872        self,
873        eaccum: &mut EAccum,
874        run_uid: Transparent<usize>,
875        span: Span,
876        (): (),
877    ) -> Option<Self::Output> {
878        match self {
879            Self::Var(i) => Some(tgt::stmt::VarTuple::Single(
880                tgt::var::Local {
881                    repr: i.map(|_, t| t.to_string()),
882                }
883                .with_span(span),
884            )),
885            Self::Tuple(ts) => ts.flat_translate(eaccum, run_uid, ()),
886        }
887    }
888}
889
890impl Translate for src::TargetExprTuple {
891    type Ctx<'i> = ();
892    type Output = tgt::stmt::VarTuple;
893    fn translate(
894        self,
895        eaccum: &mut EAccum,
896        run_uid: Transparent<usize>,
897        span: Span,
898        (): (),
899    ) -> Option<Self::Output> {
900        let mut vs = tgt::Tuple::default();
901        let trail = self.fields.trailing_punct();
902        for t in self.fields {
903            vs.push(t.translate(eaccum, run_uid, ())?);
904        }
905        if vs.len() == 1 && !trail {
906            Some(
907                vs.into_iter()
908                    .next()
909                    .unwrap_or_else(|| chandeliers_err::malformed!())
910                    .t,
911            )
912        } else {
913            Some(tgt::stmt::VarTuple::Multiple(vs.with_span(span)))
914        }
915    }
916}
917
918impl Translate for src::Expr {
919    type Ctx<'i> = ExprCtxView<'i>;
920    type Output = tgt::expr::Expr;
921    fn translate(
922        self,
923        eaccum: &mut EAccum,
924        run_uid: Transparent<usize>,
925        _span: Span,
926        ctx: Self::Ctx<'_>,
927    ) -> Option<tgt::expr::Expr> {
928        self.inner.flat_translate(eaccum, run_uid, ctx)
929    }
930}
931
932impl Translate for src::expr::If {
933    type Ctx<'i> = ExprCtxView<'i>;
934    type Output = tgt::expr::Expr;
935    fn translate(
936        self,
937        eaccum: &mut EAccum,
938        run_uid: Transparent<usize>,
939        _span: Span,
940        ctx: Self::Ctx<'_>,
941    ) -> Option<tgt::expr::Expr> {
942        let cond = self.cond.translate(eaccum, run_uid, fork!(ctx))?.boxed();
943        let yes = self.yes.translate(eaccum, run_uid, fork!(ctx))?.boxed();
944        let no = self.no.translate(eaccum, run_uid, ctx)?.boxed();
945        Some(tgt::expr::Expr::Ifx { cond, yes, no })
946    }
947}
948
949/// Helpers to resolve associativity.
950///
951/// The parsing AST has associativity that the analysis AST does not:
952/// - `+` and `-`, `*` and `/` and `%`, `or`, `and` are left associative,
953/// - `fby`, `->` are right associative,
954/// - `<` and `>` and `<=` and `>=` and `<>` and `=` are not associative,
955/// but all are parsed as a `Punctuated<_, _>`.
956///
957/// This module offers functions that resolve associativity (or the absence
958/// of associativity) to transform e.g.
959/// `Punctuated [ (a, +), (b, +), (c, -), (d, +), (e, -), (f) ]`
960/// into
961/// `Bin { Bin { Bin { Bin { Bin { a, +, b }, +, c }, -, d }, +, e }, -, f }`
962///
963/// This translation is done here as generically as possible, including
964/// differentiating between `Accum` and `Item` even though they will eventually
965/// both be instanciated with `Expr`, to guarantee the absence of errors.
966mod assoc {
967    use super::{Sp, WithSpan};
968    use chandeliers_err as err;
969    use std::fmt::Display;
970    use syn::punctuated::{Pair, Punctuated};
971
972    /// Descriptor for a translation.
973    pub struct Descr<'i, Label, Convert, Compose> {
974        /// Parent context.
975        pub ctx: super::ExprCtxView<'i>,
976        /// Text to print if there is an error. Typically the name of the type
977        /// being translated.
978        pub label: Label,
979        /// How to embed the type of elements in expressions.
980        pub convert: Convert,
981        /// How to combine two expressions into one.
982        pub compose: Compose,
983    }
984
985    impl<'i, Label, Convert, Compose> Descr<'i, Label, Convert, Compose>
986    where
987        Label: Display,
988    {
989        /// Build the tree of left associative operations from a flat representation.
990        ///
991        /// Notice the type of `Compose`: the accumulator is to the left.
992        pub fn left_associative<Elem, Punct, Accum, Item>(
993            &mut self,
994            elems: Punctuated<Sp<Elem>, Punct>,
995        ) -> Option<Accum>
996        where
997            Convert: FnMut(Sp<Elem>, usize, super::ExprCtxView<'_>) -> Option<Sp<Item>>,
998            Compose: FnMut(Sp<Accum>, Punct, usize, Sp<Item>, super::ExprCtxView<'_>) -> Accum,
999            Accum: WithSpan<Output = Sp<Accum>>,
1000            Item: Into<Accum>,
1001        {
1002            // We always assume that there is a trailing _element_, not a trailing punctuation.
1003            chandeliers_err::consistency!(
1004                !elems.trailing_punct(),
1005                "Bug in the parser: {} should not accept trailing punctuation",
1006                self.label
1007            );
1008            let mut pairs = elems.into_pairs().enumerate();
1009            let mut oper: Punct;
1010            let mut accum: Sp<Accum>;
1011            // If the first element is a `Punctuated` we can initialize
1012            // our accumulator. Otherwise this is a transparent expression
1013            // and we just defer directly to the translation for the inner type.
1014            let Some((depth, fst)) = pairs.next() else {
1015                chandeliers_err::abort!("Should not be able to get an empty `Pairs` from a `Punctuated::parse_nonempty`");
1016            };
1017            match fst {
1018                Pair::Punctuated(elem, punct) => {
1019                    accum = (self.convert)(elem, depth, fork!(self.ctx))?.map(|_, i| i.into());
1020                    oper = punct;
1021                }
1022                Pair::End(elem) => {
1023                    return Some((self.convert)(elem, depth, fork!(self.ctx))?.t.into());
1024                }
1025            }
1026            // Looping case.
1027            for (depth, pair) in pairs {
1028                match pair {
1029                    // One more element: add it to the accumulator and record
1030                    // the punctuation to be used in the next iteration.
1031                    Pair::Punctuated(elem, punct) => {
1032                        let expr = (self.convert)(elem, depth, fork!(self.ctx))?;
1033                        let Some(span) = expr.span.join(accum.span) else {
1034                            chandeliers_err::abort!(
1035                                "Malformed span between {:?} and {:?}",
1036                                expr.span,
1037                                accum.span
1038                            );
1039                        };
1040                        accum = (self.compose)(accum, oper, depth, expr, fork!(self.ctx))
1041                            .with_span(span);
1042                        oper = punct;
1043                    }
1044                    // End: combine now then return the accumulator.
1045                    Pair::End(elem) => {
1046                        let expr = (self.convert)(elem, depth, fork!(self.ctx))?;
1047                        return Some((self.compose)(accum, oper, depth, expr, fork!(self.ctx)));
1048                    }
1049                }
1050            }
1051            err::malformed!()
1052        }
1053
1054        /// Build the tree of right associative operations from a flat representation.
1055        ///
1056        /// Notice the type of `Compose`: the accumulator is to the right.
1057        pub fn right_associative<Elem, Punct, Accum, Item>(
1058            &mut self,
1059            elems: Punctuated<Sp<Elem>, Punct>,
1060        ) -> Option<Accum>
1061        where
1062            Accum: WithSpan<Output = Sp<Accum>>,
1063            Convert: FnMut(Sp<Elem>, usize, super::ExprCtxView<'_>) -> Option<Sp<Item>>,
1064            Compose: FnMut(Sp<Item>, Punct, usize, Sp<Accum>, super::ExprCtxView<'_>) -> Accum,
1065            Item: Into<Accum>,
1066        {
1067            // We always assume that there is a trailing _element_, not a trailing punctuation.
1068            chandeliers_err::consistency!(
1069                !elems.trailing_punct(),
1070                "Bug in the parser: {} should not accept trailing punctuation",
1071                self.label
1072            );
1073
1074            let mut pairs = elems.into_pairs().enumerate().rev();
1075            let mut accum: Sp<Accum>;
1076            // Because we've reversed the iterator, the first element is always
1077            // and `End`.
1078            let Some((depth, last)) = pairs.next() else {
1079                chandeliers_err::abort!("Should not be able to get an empty `Pairs` from a `Punctuated::parse_nonempty`");
1080            };
1081
1082            let Pair::End(elem) = last else {
1083                err::malformed!()
1084            };
1085            accum = (self.convert)(elem, depth, fork!(self.ctx))?.map(|_, i| i.into());
1086            // Looping case.
1087            // WARNING: contrary to the left associative case, `End` is not
1088            // the end! In fact `End` is unreachable because it has already
1089            // been seen in the handling of `last`.
1090            for (depth, pair) in pairs {
1091                let Pair::Punctuated(elem, punct) = pair else {
1092                    err::malformed!()
1093                };
1094                let expr = (self.convert)(elem, depth, fork!(self.ctx))?;
1095                let Some(span) = expr.span.join(accum.span) else {
1096                    chandeliers_err::abort!(
1097                        "Malformed span between {:?} and {:?}",
1098                        expr.span,
1099                        accum.span
1100                    );
1101                };
1102                accum = (self.compose)(expr, punct, depth, accum, fork!(self.ctx)).with_span(span);
1103            }
1104            Some(accum.t)
1105        }
1106    }
1107}
1108
1109impl Translate for src::expr::Or {
1110    type Ctx<'i> = ExprCtxView<'i>;
1111    type Output = tgt::expr::Expr;
1112    fn translate(
1113        self,
1114        eaccum: &mut EAccum,
1115        run_uid: Transparent<usize>,
1116        _span: Span,
1117        ctx: Self::Ctx<'_>,
1118    ) -> Option<tgt::expr::Expr> {
1119        assoc::Descr {
1120            ctx,
1121            label: "OrExpr",
1122            convert: |elem: Sp<src::expr::And>, _depth, ctx: ExprCtxView| {
1123                elem.translate(eaccum, run_uid, fork!(ctx))
1124            },
1125            compose: |lhs: Sp<tgt::expr::Expr>,
1126                      _op,
1127                      _depth,
1128                      rhs: Sp<tgt::expr::Expr>,
1129                      _ctx: ExprCtxView| {
1130                tgt::expr::Expr::Bin {
1131                    op: tgt::op::Bin::BitOr,
1132                    lhs: lhs.boxed(),
1133                    rhs: rhs.boxed(),
1134                }
1135            },
1136        }
1137        .left_associative(self.items)
1138    }
1139}
1140
1141impl Translate for src::expr::And {
1142    type Ctx<'i> = ExprCtxView<'i>;
1143    type Output = tgt::expr::Expr;
1144    fn translate(
1145        self,
1146        eaccum: &mut EAccum,
1147        run_uid: Transparent<usize>,
1148        _span: Span,
1149        ctx: Self::Ctx<'_>,
1150    ) -> Option<tgt::expr::Expr> {
1151        assoc::Descr {
1152            ctx,
1153            label: "AndExpr",
1154            convert: |elem: Sp<src::expr::Cmp>, _depth, ctx: ExprCtxView| {
1155                elem.translate(eaccum, run_uid, fork!(ctx))
1156            },
1157            compose: |lhs: Sp<tgt::expr::Expr>,
1158                      _op,
1159                      _depth,
1160                      rhs: Sp<tgt::expr::Expr>,
1161                      _ctx: ExprCtxView| {
1162                tgt::expr::Expr::Bin {
1163                    op: tgt::op::Bin::BitAnd,
1164                    lhs: lhs.boxed(),
1165                    rhs: rhs.boxed(),
1166                }
1167            },
1168        }
1169        .left_associative(self.items)
1170    }
1171}
1172
1173impl Translate for src::expr::Not {
1174    type Ctx<'i> = ExprCtxView<'i>;
1175    type Output = tgt::expr::Expr;
1176    fn translate(
1177        self,
1178        eaccum: &mut EAccum,
1179        run_uid: Transparent<usize>,
1180        _span: Span,
1181        ctx: Self::Ctx<'_>,
1182    ) -> Option<tgt::expr::Expr> {
1183        let inner = self.inner.translate(eaccum, run_uid, ctx)?.boxed();
1184        Some(tgt::expr::Expr::Un {
1185            op: tgt::op::Un::Not,
1186            inner,
1187        })
1188    }
1189}
1190
1191impl Translate for src::expr::Cmp {
1192    type Ctx<'i> = ExprCtxView<'i>;
1193    type Output = tgt::expr::Expr;
1194    fn translate(
1195        self,
1196        eaccum: &mut EAccum,
1197        run_uid: Transparent<usize>,
1198        span: Span,
1199        ctx: Self::Ctx<'_>,
1200    ) -> Option<tgt::expr::Expr> {
1201        use syn::punctuated::Pair;
1202        chandeliers_err::consistency!(
1203            !self.items.trailing_punct(),
1204            "Bug in the parser: Comparisons should not accept trailing punctuation",
1205        );
1206        let mut it = self.items.into_pairs();
1207        // We must have a first element
1208        let Some(first) = it.next() else {
1209            err::abort!("Bug in the parser: comparison should have at least one member")
1210        };
1211        let Some(second) = it.next() else {
1212            // If we don't have a second element then this is just dropping
1213            // to the level below.
1214            // The first one can't have punctuation
1215            let Pair::End(first) = first else {
1216                err::malformed!()
1217            };
1218            return first.flat_translate(eaccum, run_uid, ctx);
1219        };
1220        let Pair::Punctuated(lhs, op) = first else {
1221            err::malformed!()
1222        };
1223        let lhs = lhs.translate(eaccum, run_uid, fork!(ctx))?.boxed();
1224        let op = op.translate(eaccum, run_uid, span, ())?;
1225        // We must not have a third element.
1226        if let Some(third) = it.next() {
1227            // We're going to throw this path anyway, we might as well
1228            // make destructive changes to get a better error message.
1229            let Pair::Punctuated(second, oper2) = second else {
1230                err::malformed!()
1231            };
1232            let second = second.translate(eaccum, run_uid, fork!(ctx))?;
1233            let oper2 = oper2.translate(eaccum, run_uid, span, ())?;
1234            let third = match third {
1235                Pair::Punctuated(third, _) | Pair::End(third) => third,
1236            };
1237            let third = third.translate(eaccum, run_uid, fork!(ctx))?;
1238            return eaccum.error(err::CmpNotAssociative {
1239                site: span,
1240                first: lhs,
1241                oper1: op,
1242                oper2,
1243                second,
1244                third,
1245            });
1246        }
1247        let Pair::End(rhs) = second else {
1248            err::malformed!()
1249        };
1250        let rhs = rhs.translate(eaccum, run_uid, ctx)?.boxed();
1251        Some(tgt::expr::Expr::Cmp { op, lhs, rhs })
1252    }
1253}
1254
1255impl Translate for src::op::Cmp {
1256    type Ctx<'i> = ();
1257    type Output = tgt::op::Cmp;
1258    fn translate(
1259        self,
1260        _eaccum: &mut EAccum,
1261        _run_uid: Transparent<usize>,
1262        _span: Span,
1263        (): (),
1264    ) -> Option<Self::Output> {
1265        Some(match self {
1266            Self::Le(_) => tgt::op::Cmp::Le,
1267            Self::Lt(_) => tgt::op::Cmp::Lt,
1268            Self::Ge(_) => tgt::op::Cmp::Ge,
1269            Self::Gt(_) => tgt::op::Cmp::Gt,
1270            Self::Eq(_) => tgt::op::Cmp::Eq,
1271            Self::Ne(_) => tgt::op::Cmp::Ne,
1272        })
1273    }
1274}
1275
1276impl Translate for src::expr::Fby {
1277    type Ctx<'i> = ExprCtxView<'i>;
1278    type Output = tgt::expr::Expr;
1279    fn translate(
1280        self,
1281        eaccum: &mut EAccum,
1282        run_uid: Transparent<usize>,
1283        span: Span,
1284        ctx: Self::Ctx<'_>,
1285    ) -> Option<tgt::expr::Expr> {
1286        if ctx.use_registers {
1287            assoc::Descr {
1288                ctx,
1289                label: "FbyExpr",
1290                convert: |elem: Sp<src::expr::Then>, _depth, ctx: ExprCtxView| {
1291                    elem.translate(eaccum, run_uid, fork!(ctx) /* DO NOT BUMP */)
1292                },
1293                compose: |before: Sp<tgt::expr::Expr>,
1294                          _op,
1295                          _depth,
1296                          after: Sp<tgt::expr::Expr>,
1297                          ctx: ExprCtxView| {
1298                    let id: Sp<tgt::var::Register> = tgt::var::Register {
1299                        id: ctx.registers.len().with_span(span),
1300                    }
1301                    .with_span(span);
1302                    ctx.registers
1303                        .push(tgt::decl::RegisterInstance { id, typ: None });
1304                    ctx.stmts.push(
1305                        tgt::stmt::Statement::UpdateRegister {
1306                            id,
1307                            val: after.clone(),
1308                        }
1309                        .with_span(span),
1310                    );
1311                    ctx.stmts.push(
1312                        tgt::stmt::Statement::InitRegister {
1313                            id,
1314                            val: Some(before.clone()),
1315                            clk: None,
1316                        }
1317                        .with_span(span),
1318                    );
1319
1320                    tgt::expr::Expr::FetchRegister {
1321                        id,
1322                        dummy_init: Some(before.boxed()),
1323                        dummy_followed_by: after.boxed(),
1324                    }
1325                },
1326            }
1327            .right_associative(self.items)
1328        } else {
1329            assoc::Descr {
1330                ctx,
1331                label: "FbyExpr",
1332                convert: |elem: Sp<src::expr::Then>, depth, ctx: ExprCtxView| {
1333                    elem.translate(eaccum, run_uid, fork!(ctx).bump(depth) /* BUMP */)
1334                },
1335                compose: |before: Sp<tgt::expr::Expr>,
1336                          _op,
1337                          depth,
1338                          after: Sp<tgt::expr::Expr>,
1339                          ctx: ExprCtxView<'_>| {
1340                    tgt::expr::Expr::Later {
1341                        delay: tgt::past::Depth {
1342                            dt: ctx.depth + depth, /* INCREASE DEPTH */
1343                        }
1344                        .with_span(
1345                            before.span.join(after.span).unwrap_or_else(|| {
1346                                err::abort!("Malformed span between {before:?} and {after:?}")
1347                            }),
1348                        ),
1349                        before: before.boxed(),
1350                        after: after.boxed(),
1351                    }
1352                },
1353            }
1354            .right_associative(self.items)
1355        }
1356    }
1357}
1358
1359impl Translate for src::expr::Pre {
1360    type Ctx<'i> = ExprCtxView<'i>;
1361    type Output = tgt::expr::Expr;
1362    fn translate(
1363        self,
1364        eaccum: &mut EAccum,
1365        run_uid: Transparent<usize>,
1366        span: Span,
1367        ctx: Self::Ctx<'_>,
1368    ) -> Option<tgt::expr::Expr> {
1369        if ctx.use_registers {
1370            let id = tgt::var::Register {
1371                id: ctx.registers.len().with_span(span),
1372            }
1373            .with_span(span);
1374            ctx.registers
1375                .push(tgt::decl::RegisterInstance { id, typ: None });
1376            // We don't increment the depth here because nodes with
1377            // registers don't have variables in the past.
1378            let inner = self.inner.translate(eaccum, run_uid, fork!(ctx))?;
1379            ctx.stmts.push(
1380                tgt::stmt::Statement::UpdateRegister {
1381                    id,
1382                    val: inner.clone(),
1383                }
1384                .with_span(span),
1385            );
1386            ctx.stmts.push(
1387                tgt::stmt::Statement::InitRegister {
1388                    id,
1389                    val: None,
1390                    clk: None,
1391                }
1392                .with_span(span),
1393            );
1394            Some(tgt::expr::Expr::FetchRegister {
1395                id,
1396                dummy_init: None,
1397                dummy_followed_by: inner.boxed(),
1398            })
1399        } else {
1400            Some(tgt::expr::Expr::DummyPre(
1401                self.inner.translate(eaccum, run_uid, ctx.incr())?.boxed(),
1402            ))
1403        }
1404    }
1405}
1406
1407impl Translate for src::expr::Then {
1408    type Ctx<'i> = ExprCtxView<'i>;
1409    type Output = tgt::expr::Expr;
1410    fn translate(
1411        self,
1412        eaccum: &mut EAccum,
1413        run_uid: Transparent<usize>,
1414        span: Span,
1415        ctx: Self::Ctx<'_>,
1416    ) -> Option<tgt::expr::Expr> {
1417        if ctx.use_registers {
1418            assoc::Descr {
1419                ctx,
1420                label: "ThenExpr",
1421                convert: |elem: Sp<src::expr::Add>, _depth, ctx: ExprCtxView| {
1422                    elem.translate(eaccum, run_uid, fork!(ctx) /* DO NOT BUMP */)
1423                },
1424                compose: |before: Sp<tgt::expr::Expr>,
1425                          _op,
1426                          _depth,
1427                          after: Sp<tgt::expr::Expr>,
1428                          ctx: ExprCtxView| {
1429                    let id: Sp<tgt::var::Flip> = tgt::var::Flip {
1430                        id: ctx.flips.len().with_span(span),
1431                    }
1432                    .with_span(span);
1433                    ctx.flips.push(tgt::decl::FlipInstance { id });
1434                    tgt::expr::Expr::Flip {
1435                        id,
1436                        initial: before.boxed(),
1437                        continued: after.boxed(),
1438                    }
1439                },
1440            }
1441            .right_associative(self.items)
1442        } else {
1443            // This looks similar to `FbyExpr`, but notice how we aren't using `depth`
1444            // in the same way.
1445            assoc::Descr {
1446                ctx,
1447                label: "ThenExpr",
1448                convert: |elem: Sp<src::expr::Add>, _depth, ctx: ExprCtxView| {
1449                    elem.translate(eaccum, run_uid, fork!(ctx) /* DO NOT BUMP */)
1450                },
1451                compose: |before: Sp<tgt::expr::Expr>,
1452                          _op,
1453                          depth,
1454                          after: Sp<tgt::expr::Expr>,
1455                          ctx: ExprCtxView<'_>| {
1456                    tgt::expr::Expr::Later {
1457                        delay: tgt::past::Depth {
1458                            dt: ctx.depth + depth,
1459                        }
1460                        .with_span(
1461                            before.span.join(after.span).unwrap_or_else(|| {
1462                                chandeliers_err::abort!(
1463                                    "Malformed span between {before:?} and {after:?}"
1464                                )
1465                            }),
1466                        ),
1467                        before: before.boxed(),
1468                        after: after.boxed(),
1469                    }
1470                },
1471            }
1472            .right_associative(self.items)
1473        }
1474    }
1475}
1476
1477impl Translate for src::expr::Add {
1478    type Ctx<'i> = ExprCtxView<'i>;
1479    type Output = tgt::expr::Expr;
1480    fn translate(
1481        self,
1482        eaccum: &mut EAccum,
1483        run_uid: Transparent<usize>,
1484        _span: Span,
1485        ctx: Self::Ctx<'_>,
1486    ) -> Option<tgt::expr::Expr> {
1487        // Back to normal left assaciative stuff that doesn't care about the depth,
1488        // but this time we have to handle the fact that there are multiple possible operators.
1489        assoc::Descr {
1490            ctx,
1491            label: "AddExpr",
1492            convert: |elem: Sp<src::expr::Mul>, _depth, ctx: ExprCtxView| {
1493                elem.translate(eaccum, run_uid, fork!(ctx))
1494            },
1495            compose: |lhs: Sp<tgt::expr::Expr>,
1496                      op: src::op::Add,
1497                      _depth,
1498                      rhs: Sp<tgt::expr::Expr>,
1499                      _ctx: ExprCtxView| {
1500                tgt::expr::Expr::Bin {
1501                    op: op.translate(),
1502                    lhs: lhs.boxed(),
1503                    rhs: rhs.boxed(),
1504                }
1505            },
1506        }
1507        .left_associative(self.items)
1508    }
1509}
1510
1511impl src::op::Add {
1512    /// Convert an additive operator from one AST to the other.
1513    fn translate(self) -> tgt::op::Bin {
1514        match self {
1515            Self::Add(_) => tgt::op::Bin::Add,
1516            Self::Sub(_) => tgt::op::Bin::Sub,
1517        }
1518    }
1519}
1520
1521impl src::op::Mul {
1522    /// Convert a multiplicative operator from one AST to the other.
1523    fn translate(self) -> tgt::op::Bin {
1524        match self {
1525            Self::Mul(_) => tgt::op::Bin::Mul,
1526            Self::Div(_) => tgt::op::Bin::Div,
1527            Self::Rem(_) => tgt::op::Bin::Rem,
1528        }
1529    }
1530}
1531
1532impl Translate for src::expr::Mul {
1533    type Ctx<'i> = ExprCtxView<'i>;
1534    type Output = tgt::expr::Expr;
1535    fn translate(
1536        self,
1537        eaccum: &mut EAccum,
1538        run_uid: Transparent<usize>,
1539        _span: Span,
1540        ctx: Self::Ctx<'_>,
1541    ) -> Option<tgt::expr::Expr> {
1542        assoc::Descr {
1543            ctx,
1544            label: "MulExpr",
1545            convert: |elem: Sp<src::expr::Clock>, _depth, ctx: ExprCtxView| {
1546                elem.translate(eaccum, run_uid, fork!(ctx))
1547            },
1548            compose: |lhs: Sp<tgt::expr::Expr>,
1549                      op: src::op::Mul,
1550                      _depth,
1551                      rhs: Sp<tgt::expr::Expr>,
1552                      _ctx: ExprCtxView| {
1553                tgt::expr::Expr::Bin {
1554                    op: op.translate(),
1555                    lhs: lhs.boxed(),
1556                    rhs: rhs.boxed(),
1557                }
1558            },
1559        }
1560        .left_associative(self.items)
1561    }
1562}
1563
1564impl src::op::Clock {
1565    /// Convert a clock operator from one AST to the other.
1566    fn translate(self) -> tgt::op::Clock {
1567        match self {
1568            Self::When(_) => tgt::op::Clock::When,
1569            Self::Whenot(_) => tgt::op::Clock::Whenot,
1570        }
1571    }
1572}
1573
1574impl Translate for src::expr::Clock {
1575    type Ctx<'i> = ExprCtxView<'i>;
1576    type Output = tgt::expr::Expr;
1577    fn translate(
1578        self,
1579        eaccum: &mut EAccum,
1580        run_uid: Transparent<usize>,
1581        _span: Span,
1582        ctx: Self::Ctx<'_>,
1583    ) -> Option<tgt::expr::Expr> {
1584        assoc::Descr {
1585            ctx,
1586            label: "ClockExpr",
1587            convert: |elem: Sp<src::expr::Positive>, _depth, ctx: ExprCtxView| {
1588                elem.translate(eaccum, run_uid, fork!(ctx))
1589            },
1590            compose: |lhs: Sp<tgt::expr::Expr>,
1591                      op: src::op::Clock,
1592                      _depth,
1593                      rhs: Sp<tgt::expr::Expr>,
1594                      _ctx: ExprCtxView| {
1595                tgt::expr::Expr::Clock {
1596                    op: op.translate(),
1597                    inner: lhs.boxed(),
1598                    activate: rhs.boxed(),
1599                }
1600            },
1601        }
1602        .left_associative(self.items)
1603    }
1604}
1605
1606impl Translate for src::expr::Neg {
1607    type Ctx<'i> = ExprCtxView<'i>;
1608    type Output = tgt::expr::Expr;
1609    fn translate(
1610        self,
1611        eaccum: &mut EAccum,
1612        run_uid: Transparent<usize>,
1613        _span: Span,
1614        ctx: Self::Ctx<'_>,
1615    ) -> Option<tgt::expr::Expr> {
1616        let inner = self.inner.translate(eaccum, run_uid, ctx)?.boxed();
1617        Some(tgt::expr::Expr::Un {
1618            op: tgt::op::Un::Neg,
1619            inner,
1620        })
1621    }
1622}
1623
1624impl Translate for src::expr::Paren {
1625    type Ctx<'i> = ExprCtxView<'i>;
1626    type Output = tgt::expr::Expr;
1627    fn translate(
1628        self,
1629        eaccum: &mut EAccum,
1630        run_uid: Transparent<usize>,
1631        span: Span,
1632        ctx: Self::Ctx<'_>,
1633    ) -> Option<tgt::expr::Expr> {
1634        let mut es = tgt::Tuple::default();
1635        let trail = self.inner.trailing_punct();
1636        for e in self.inner {
1637            es.push(e.translate(eaccum, run_uid, fork!(ctx))?);
1638        }
1639        if es.len() == 1 && !trail {
1640            Some(tgt::expr::Expr::DummyParen(
1641                es.into_iter()
1642                    .next()
1643                    .unwrap_or_else(|| err::malformed!())
1644                    .boxed(),
1645            ))
1646        } else {
1647            Some(tgt::expr::Expr::Tuple(es.with_span(span)))
1648        }
1649    }
1650}
1651
1652impl Translate for src::expr::Call {
1653    type Ctx<'i> = ExprCtxView<'i>;
1654    type Output = tgt::expr::Expr;
1655    fn translate(
1656        self,
1657        eaccum: &mut EAccum,
1658        run_uid: Transparent<usize>,
1659        span: Span,
1660        ctx: Self::Ctx<'_>,
1661    ) -> Option<tgt::expr::Expr> {
1662        let args = self.args.translate(eaccum, run_uid, fork!(ctx))?;
1663        let repr = self.fun.map(|_, t| t.to_string());
1664        let id = tgt::var::Node {
1665            id: ctx.blocks.len().with_span(span),
1666            repr: repr.clone(),
1667        }
1668        .with_span(span);
1669        ctx.blocks.push(tgt::decl::NodeInstance {
1670            name: tgt::decl::NodeName { repr, run_uid }.with_span(span),
1671            generics: None,
1672        });
1673        Some(tgt::expr::Expr::Substep {
1674            delay: ctx.depth,
1675            id: id.clone(),
1676            args: args.boxed(),
1677        })
1678    }
1679}
1680
1681impl Translate for src::expr::Atomic {
1682    type Ctx<'i> = ExprCtxView<'i>;
1683    type Output = tgt::expr::Expr;
1684    fn translate(
1685        self,
1686        eaccum: &mut EAccum,
1687        run_uid: Transparent<usize>,
1688        _span: Span,
1689        ctx: Self::Ctx<'_>,
1690    ) -> Option<tgt::expr::Expr> {
1691        match self {
1692            Self::Lit(l) => l.flat_translate(eaccum, run_uid, ctx),
1693            Self::Var(v) => v.flat_translate(eaccum, run_uid, ctx),
1694            Self::Paren(p) => p.flat_translate(eaccum, run_uid, ctx),
1695        }
1696    }
1697}
1698
1699impl Translate for src::expr::Positive {
1700    type Ctx<'i> = ExprCtxView<'i>;
1701    type Output = tgt::expr::Expr;
1702    fn translate(
1703        self,
1704        eaccum: &mut EAccum,
1705        run_uid: Transparent<usize>,
1706        _span: Span,
1707        ctx: Self::Ctx<'_>,
1708    ) -> Option<tgt::expr::Expr> {
1709        match self {
1710            Self::If(i) => i.flat_translate(eaccum, run_uid, ctx),
1711            Self::Merge(m) => m.flat_translate(eaccum, run_uid, ctx),
1712            Self::Call(c) => c.flat_translate(eaccum, run_uid, ctx),
1713            Self::Pre(p) => p.flat_translate(eaccum, run_uid, ctx),
1714            Self::Neg(n) => n.flat_translate(eaccum, run_uid, ctx),
1715            Self::Not(n) => n.flat_translate(eaccum, run_uid, ctx),
1716            Self::Atomic(a) => a.flat_translate(eaccum, run_uid, ctx),
1717        }
1718    }
1719}
1720
1721impl Translate for src::expr::Merge {
1722    type Ctx<'i> = ExprCtxView<'i>;
1723    type Output = tgt::expr::Expr;
1724    fn translate(
1725        self,
1726        eaccum: &mut EAccum,
1727        run_uid: Transparent<usize>,
1728        _span: Span,
1729        ctx: Self::Ctx<'_>,
1730    ) -> Option<tgt::expr::Expr> {
1731        let switch = self.clk.translate(eaccum, run_uid, fork!(ctx))?.boxed();
1732        let on = self.on.translate(eaccum, run_uid, fork!(ctx))?.boxed();
1733        let off = self.off.translate(eaccum, run_uid, fork!(ctx))?.boxed();
1734        Some(tgt::expr::Expr::Merge { switch, on, off })
1735    }
1736}
1737
1738impl Translate for src::expr::Var {
1739    type Ctx<'i> = ExprCtxView<'i>;
1740    type Output = tgt::expr::Expr;
1741    fn translate(
1742        self,
1743        _eaccum: &mut EAccum,
1744        run_uid: Transparent<usize>,
1745        span: Span,
1746        ctx: Self::Ctx<'_>,
1747    ) -> Option<tgt::expr::Expr> {
1748        let repr = self.name.map(|_, t| t.to_string());
1749        if ctx.shadow_glob.contains(&repr.t) {
1750            Some(tgt::expr::Expr::Reference(
1751                tgt::var::Reference::Var(
1752                    tgt::var::Past {
1753                        var: tgt::var::Local { repr }.with_span(span),
1754                        depth: tgt::past::Depth { dt: ctx.depth }.with_span(span),
1755                    }
1756                    .with_span(span),
1757                )
1758                .with_span(span),
1759            ))
1760        } else {
1761            Some(tgt::expr::Expr::Reference(
1762                tgt::var::Reference::Global(tgt::var::Global { repr, run_uid }.with_span(span))
1763                    .with_span(span),
1764            ))
1765        }
1766    }
1767}
1768
1769impl Translate for src::expr::Lit {
1770    type Ctx<'i> = ExprCtxView<'i>;
1771    type Output = tgt::expr::Expr;
1772    fn translate(
1773        self,
1774        eaccum: &mut EAccum,
1775        _run_uid: Transparent<usize>,
1776        span: Span,
1777        _ctx: Self::Ctx<'_>,
1778    ) -> Option<tgt::expr::Expr> {
1779        use syn::Lit;
1780        let lit = match self.lit.t {
1781            Lit::Bool(b) => tgt::expr::Lit::Bool(b.value()),
1782            Lit::Int(i) => tgt::expr::Lit::Int(
1783                i.base10_parse()
1784                    .unwrap_or_else(|e| err::abort!("Unable to parse {i} as an int: {e}")),
1785            ),
1786            Lit::Float(f) => tgt::expr::Lit::Float(
1787                f.base10_parse()
1788                    .unwrap_or_else(|e| err::abort!("Unable to parse {f} as a float: {e}")),
1789            ),
1790            _ => eaccum.error(err::UnhandledLitType { site: span })?,
1791        };
1792        Some(tgt::expr::Expr::Lit(lit.with_span(span)))
1793    }
1794}