egglog_ast/
generic_ast_helpers.rs

1use std::fmt::{Display, Formatter};
2use std::hash::Hash;
3
4use ordered_float::OrderedFloat;
5
6use super::util::ListDisplay;
7use crate::generic_ast::*;
8use crate::span::Span;
9
10// Macro to implement From conversions for Literal types
11macro_rules! impl_from {
12    ($ctor:ident($t:ty)) => {
13        impl From<Literal> for $t {
14            fn from(literal: Literal) -> Self {
15                match literal {
16                    Literal::$ctor(t) => t,
17                    #[allow(unreachable_patterns)]
18                    _ => panic!("Expected {}, got {literal}", stringify!($ctor)),
19                }
20            }
21        }
22
23        impl From<$t> for Literal {
24            fn from(t: $t) -> Self {
25                Literal::$ctor(t)
26            }
27        }
28    };
29}
30
31pub const INTERNAL_SYMBOL_PREFIX: &str = "@";
32
33impl<Head: Display, Leaf: Display> Display for GenericRule<Head, Leaf>
34where
35    Head: Clone + Display,
36    Leaf: Clone + PartialEq + Eq + Display + Hash,
37{
38    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
39        let indent = " ".repeat(7);
40        write!(f, "(rule (")?;
41        for (i, fact) in self.body.iter().enumerate() {
42            if i > 0 {
43                write!(f, "{indent}")?;
44            }
45
46            if i != self.body.len() - 1 {
47                writeln!(f, "{fact}")?;
48            } else {
49                write!(f, "{fact}")?;
50            }
51        }
52        write!(f, ")\n      (")?;
53        for (i, action) in self.head.0.iter().enumerate() {
54            if i > 0 {
55                write!(f, "{indent}")?;
56            }
57            if i != self.head.0.len() - 1 {
58                writeln!(f, "{action}")?;
59            } else {
60                write!(f, "{action}")?;
61            }
62        }
63        let ruleset = if !self.ruleset.is_empty() {
64            format!(":ruleset {}", &self.ruleset)
65        } else {
66            "".into()
67        };
68        let name = if !self.name.is_empty() {
69            format!(":name \"{}\"", &self.name)
70        } else {
71            "".into()
72        };
73        let eval_mode = match self.eval_mode {
74            RuleEvalMode::Seminaive => "",
75            RuleEvalMode::Naive => " :naive",
76            RuleEvalMode::UnsafeSeminaive => " :unsafe-seminaive",
77        };
78        let no_decomp = if self.no_decomp { " :no-decomp" } else { "" };
79        let include_subsumed = if self.include_subsumed {
80            " :internal-include-subsumed"
81        } else {
82            ""
83        };
84        write!(
85            f,
86            ")\n{indent} {ruleset} {name}{eval_mode}{no_decomp}{include_subsumed})"
87        )
88    }
89}
90
91// Use the macro for Int, Float, and String conversions
92impl_from!(Int(i64));
93impl_from!(Float(OrderedFloat<f64>));
94impl_from!(String(String));
95
96impl<Head: Display, Leaf: Display> Display for GenericFact<Head, Leaf> {
97    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
98        match self {
99            GenericFact::Eq(_, e1, e2) => write!(f, "(= {e1} {e2})"),
100            GenericFact::Fact(expr) => write!(f, "{expr}"),
101        }
102    }
103}
104
105// Implement Display for GenericAction
106impl<Head: Display, Leaf: Display> Display for GenericAction<Head, Leaf>
107where
108    Head: Clone + Display,
109    Leaf: Clone + PartialEq + Eq + Display + Hash,
110{
111    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
112        match self {
113            GenericAction::Let(_, lhs, rhs) => write!(f, "(let {lhs} {rhs})"),
114            GenericAction::Set(_, lhs, args, rhs) => {
115                if args.is_empty() {
116                    write!(f, "(set ({lhs}) {rhs})")
117                } else {
118                    write!(
119                        f,
120                        "(set ({} {}) {})",
121                        lhs,
122                        args.iter()
123                            .map(|a| format!("{a}"))
124                            .collect::<Vec<_>>()
125                            .join(" "),
126                        rhs
127                    )
128                }
129            }
130            GenericAction::Union(_, lhs, rhs) => write!(f, "(union {lhs} {rhs})"),
131            GenericAction::Change(_, change, lhs, args) => {
132                let change_str = match change {
133                    Change::Delete => "delete",
134                    Change::Subsume => "subsume",
135                };
136                if args.is_empty() {
137                    write!(f, "({change_str} ({lhs}))")
138                } else {
139                    write!(
140                        f,
141                        "({} ({} {}))",
142                        change_str,
143                        lhs,
144                        args.iter()
145                            .map(|a| format!("{a}"))
146                            .collect::<Vec<_>>()
147                            .join(" ")
148                    )
149                }
150            }
151            GenericAction::Panic(_, msg) => write!(f, "(panic \"{msg}\")"),
152            GenericAction::Expr(_, e) => write!(f, "{e}"),
153        }
154    }
155}
156
157impl<Head, Leaf> Display for GenericExpr<Head, Leaf>
158where
159    Head: Display,
160    Leaf: Display,
161{
162    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
163        match self {
164            GenericExpr::Lit(_ann, lit) => write!(f, "{lit}"),
165            GenericExpr::Var(_ann, var) => write!(f, "{var}"),
166            GenericExpr::Call(_ann, op, children) => match children.is_empty() {
167                true => write!(f, "({op})"),
168                false => write!(f, "({} {})", op, ListDisplay(children, " ")),
169            },
170        }
171    }
172}
173
174impl<Head, Leaf> Default for GenericActions<Head, Leaf>
175where
176    Head: Clone + Display,
177    Leaf: Clone + PartialEq + Eq + Display + Hash,
178{
179    fn default() -> Self {
180        Self(vec![])
181    }
182}
183
184impl<Head, Leaf> GenericRule<Head, Leaf>
185where
186    Head: Clone + Display,
187    Leaf: Clone + PartialEq + Eq + Display + Hash,
188{
189    /// Applies `f` to every expression that appears in the rule body or head.
190    pub fn visit_exprs(
191        self,
192        f: &mut impl FnMut(GenericExpr<Head, Leaf>) -> GenericExpr<Head, Leaf>,
193    ) -> Self {
194        Self {
195            span: self.span,
196            head: self.head.visit_exprs(f),
197            body: self
198                .body
199                .into_iter()
200                .map(|bexpr| bexpr.visit_exprs(f))
201                .collect(),
202            name: self.name.clone(),
203            ruleset: self.ruleset.clone(),
204            eval_mode: self.eval_mode,
205            no_decomp: self.no_decomp,
206            include_subsumed: self.include_subsumed,
207        }
208    }
209
210    /// Applies `f` to each action in the rule head, leaving the body unchanged.
211    pub fn visit_actions(
212        self,
213        f: &mut impl FnMut(GenericAction<Head, Leaf>) -> GenericAction<Head, Leaf>,
214    ) -> Self {
215        Self {
216            span: self.span,
217            head: self.head.visit_actions(f),
218            body: self.body,
219            name: self.name,
220            ruleset: self.ruleset,
221            eval_mode: self.eval_mode,
222            no_decomp: self.no_decomp,
223            include_subsumed: self.include_subsumed,
224        }
225    }
226
227    /// Applies the provided `head` and `leaf` mappings to every symbol that appears in the rule.
228    pub fn map_symbols<Head2, Leaf2>(
229        self,
230        head: &mut impl FnMut(Head) -> Head2,
231        leaf: &mut impl FnMut(Leaf) -> Leaf2,
232    ) -> GenericRule<Head2, Leaf2>
233    where
234        Head2: Clone + Display,
235        Leaf2: Clone + PartialEq + Eq + Display + Hash,
236    {
237        GenericRule {
238            span: self.span,
239            head: self.head.map_symbols(head, leaf),
240            body: self
241                .body
242                .into_iter()
243                .map(|fact| fact.map_symbols(head, leaf))
244                .collect(),
245            name: self.name,
246            ruleset: self.ruleset,
247            eval_mode: self.eval_mode,
248            no_decomp: self.no_decomp,
249            include_subsumed: self.include_subsumed,
250        }
251    }
252
253    /// Converts the rule into its unresolved representation by formatting heads and leaves.
254    pub fn make_unresolved(self) -> GenericRule<String, String> {
255        let mut map_head = |h: Head| h.to_string();
256        let mut map_leaf = |l: Leaf| l.to_string();
257        self.map_symbols(&mut map_head, &mut map_leaf)
258    }
259}
260
261impl<Head, Leaf> GenericActions<Head, Leaf>
262where
263    Head: Clone + Display,
264    Leaf: Clone + PartialEq + Eq + Display + Hash,
265{
266    pub fn len(&self) -> usize {
267        self.0.len()
268    }
269
270    pub fn is_empty(&self) -> bool {
271        self.0.is_empty()
272    }
273
274    pub fn iter(&self) -> impl Iterator<Item = &GenericAction<Head, Leaf>> {
275        self.0.iter()
276    }
277
278    pub fn visit_vars(&self, f: &mut impl FnMut(&Span, &Leaf)) {
279        for action in &self.0 {
280            action.visit_vars(f);
281        }
282    }
283
284    /// Transforms every expression appearing in the action list using `f`.
285    pub fn visit_exprs(
286        self,
287        f: &mut impl FnMut(GenericExpr<Head, Leaf>) -> GenericExpr<Head, Leaf>,
288    ) -> Self {
289        Self(self.0.into_iter().map(|a| a.visit_exprs(f)).collect())
290    }
291
292    /// Rewrites each action in the collection with the provided closure.
293    pub fn visit_actions(
294        self,
295        f: &mut impl FnMut(GenericAction<Head, Leaf>) -> GenericAction<Head, Leaf>,
296    ) -> Self {
297        Self(self.0.into_iter().map(f).collect())
298    }
299
300    pub fn new(actions: Vec<GenericAction<Head, Leaf>>) -> Self {
301        Self(actions)
302    }
303
304    pub fn singleton(action: GenericAction<Head, Leaf>) -> Self {
305        Self(vec![action])
306    }
307
308    /// Applies the provided `head` and `leaf` mappings to each action.
309    pub fn map_symbols<Head2, Leaf2>(
310        self,
311        head: &mut impl FnMut(Head) -> Head2,
312        leaf: &mut impl FnMut(Leaf) -> Leaf2,
313    ) -> GenericActions<Head2, Leaf2>
314    where
315        Head2: Clone + Display,
316        Leaf2: Clone + PartialEq + Eq + Display + Hash,
317    {
318        GenericActions(
319            self.0
320                .into_iter()
321                .map(|action| action.map_symbols(head, leaf))
322                .collect(),
323        )
324    }
325
326    /// Converts the actions into their unresolved representation by formatting heads and leaves.
327    pub fn make_unresolved(self) -> GenericActions<String, String> {
328        let mut map_head = |h: Head| h.to_string();
329        let mut map_leaf = |l: Leaf| l.to_string();
330        self.map_symbols(&mut map_head, &mut map_leaf)
331    }
332}
333
334impl<Head, Leaf> GenericAction<Head, Leaf>
335where
336    Head: Clone + Display,
337    Leaf: Clone + Eq + Display + Hash,
338{
339    pub fn visit_vars(&self, f: &mut impl FnMut(&Span, &Leaf)) {
340        if let GenericAction::Let(span, lhs, _) = self {
341            f(span, lhs);
342        }
343        let mut visit = |expr: GenericExpr<Head, Leaf>| match expr {
344            GenericExpr::Var(span, var) => {
345                f(&span, &var);
346                GenericExpr::Var(span, var)
347            }
348            other => other,
349        };
350        let _ = self.clone().visit_exprs(&mut visit);
351    }
352
353    // Applys `f` to all expressions in the action.
354    pub fn map_exprs(
355        &self,
356        f: &mut impl FnMut(&GenericExpr<Head, Leaf>) -> GenericExpr<Head, Leaf>,
357    ) -> Self {
358        match self {
359            GenericAction::Let(span, lhs, rhs) => {
360                GenericAction::Let(span.clone(), lhs.clone(), f(rhs))
361            }
362            GenericAction::Set(span, lhs, args, rhs) => {
363                let right = f(rhs);
364                GenericAction::Set(
365                    span.clone(),
366                    lhs.clone(),
367                    args.iter().map(f).collect(),
368                    right,
369                )
370            }
371            GenericAction::Change(span, change, lhs, args) => GenericAction::Change(
372                span.clone(),
373                *change,
374                lhs.clone(),
375                args.iter().map(f).collect(),
376            ),
377            GenericAction::Union(span, lhs, rhs) => {
378                GenericAction::Union(span.clone(), f(lhs), f(rhs))
379            }
380            GenericAction::Panic(span, msg) => GenericAction::Panic(span.clone(), msg.clone()),
381            GenericAction::Expr(span, e) => GenericAction::Expr(span.clone(), f(e)),
382        }
383    }
384
385    /// Applys `f` to all sub-expressions (including `self`)
386    /// bottom-up, collecting the results.
387    pub fn visit_exprs(
388        self,
389        f: &mut impl FnMut(GenericExpr<Head, Leaf>) -> GenericExpr<Head, Leaf>,
390    ) -> Self {
391        match self {
392            GenericAction::Let(span, lhs, rhs) => {
393                GenericAction::Let(span, lhs.clone(), rhs.visit_exprs(f))
394            }
395            // TODO should we refactor `Set` so that we can map over Expr::Call(lhs, args)?
396            // This seems more natural to oflatt
397            // Currently, visit_exprs does not apply f to the first argument of Set.
398            GenericAction::Set(span, lhs, args, rhs) => {
399                let args = args.into_iter().map(|e| e.visit_exprs(f)).collect();
400                GenericAction::Set(span, lhs.clone(), args, rhs.visit_exprs(f))
401            }
402            GenericAction::Change(span, change, lhs, args) => {
403                let args = args.into_iter().map(|e| e.visit_exprs(f)).collect();
404                GenericAction::Change(span, change, lhs.clone(), args)
405            }
406            GenericAction::Union(span, lhs, rhs) => {
407                GenericAction::Union(span, lhs.visit_exprs(f), rhs.visit_exprs(f))
408            }
409            GenericAction::Panic(span, msg) => GenericAction::Panic(span, msg.clone()),
410            GenericAction::Expr(span, e) => GenericAction::Expr(span, e.visit_exprs(f)),
411        }
412    }
413
414    pub fn subst(&self, subst: &mut impl FnMut(&Span, &Leaf) -> GenericExpr<Head, Leaf>) -> Self {
415        self.map_exprs(&mut |e| e.subst_leaf(subst))
416    }
417
418    pub fn map_def_use(self, fvar: &mut impl FnMut(Leaf, bool) -> Leaf) -> Self {
419        macro_rules! fvar_expr {
420            () => {
421                |span, s: _| GenericExpr::Var(span.clone(), fvar(s.clone(), false))
422            };
423        }
424        match self {
425            GenericAction::Let(span, lhs, rhs) => {
426                let lhs = fvar(lhs, true);
427                let rhs = rhs.subst_leaf(&mut fvar_expr!());
428                GenericAction::Let(span, lhs, rhs)
429            }
430            GenericAction::Set(span, lhs, args, rhs) => {
431                let args = args
432                    .into_iter()
433                    .map(|e| e.subst_leaf(&mut fvar_expr!()))
434                    .collect();
435                let rhs = rhs.subst_leaf(&mut fvar_expr!());
436                GenericAction::Set(span, lhs.clone(), args, rhs)
437            }
438            GenericAction::Change(span, change, lhs, args) => {
439                let args = args
440                    .into_iter()
441                    .map(|e| e.subst_leaf(&mut fvar_expr!()))
442                    .collect();
443                GenericAction::Change(span, change, lhs.clone(), args)
444            }
445            GenericAction::Union(span, lhs, rhs) => {
446                let lhs = lhs.subst_leaf(&mut fvar_expr!());
447                let rhs = rhs.subst_leaf(&mut fvar_expr!());
448                GenericAction::Union(span, lhs, rhs)
449            }
450            GenericAction::Panic(span, msg) => GenericAction::Panic(span, msg.clone()),
451            GenericAction::Expr(span, e) => {
452                GenericAction::Expr(span, e.subst_leaf(&mut fvar_expr!()))
453            }
454        }
455    }
456
457    /// Applies the provided `head` and `leaf` mappings to the action and all nested expressions.
458    pub fn map_symbols<Head2, Leaf2>(
459        self,
460        head: &mut impl FnMut(Head) -> Head2,
461        leaf: &mut impl FnMut(Leaf) -> Leaf2,
462    ) -> GenericAction<Head2, Leaf2>
463    where
464        Head2: Clone + Display,
465        Leaf2: Clone + Eq + Display + Hash,
466    {
467        match self {
468            GenericAction::Let(span, lhs, rhs) => {
469                GenericAction::Let(span, leaf(lhs), rhs.map_symbols(head, leaf))
470            }
471            GenericAction::Set(span, head_sym, args, rhs) => {
472                let mut mapped_args = Vec::with_capacity(args.len());
473                for arg in args {
474                    mapped_args.push(arg.map_symbols(head, leaf));
475                }
476                GenericAction::Set(
477                    span,
478                    head(head_sym),
479                    mapped_args,
480                    rhs.map_symbols(head, leaf),
481                )
482            }
483            GenericAction::Change(span, change, head_sym, args) => {
484                let mut mapped_args = Vec::with_capacity(args.len());
485                for arg in args {
486                    mapped_args.push(arg.map_symbols(head, leaf));
487                }
488                GenericAction::Change(span, change, head(head_sym), mapped_args)
489            }
490            GenericAction::Union(span, lhs, rhs) => GenericAction::Union(
491                span,
492                lhs.map_symbols(head, leaf),
493                rhs.map_symbols(head, leaf),
494            ),
495            GenericAction::Panic(span, msg) => GenericAction::Panic(span, msg),
496            GenericAction::Expr(span, expr) => {
497                GenericAction::Expr(span, expr.map_symbols(head, leaf))
498            }
499        }
500    }
501
502    /// Converts the action into its unresolved representation using String by
503    /// formatting heads and leaves.
504    pub fn make_unresolved(self) -> GenericAction<String, String> {
505        let mut map_head = |h: Head| h.to_string();
506        let mut map_leaf = |l: Leaf| l.to_string();
507        self.map_symbols(&mut map_head, &mut map_leaf)
508    }
509}
510
511impl<Head, Leaf> GenericFact<Head, Leaf>
512where
513    Head: Clone + Display,
514    Leaf: Clone + PartialEq + Eq + Display + Hash,
515{
516    pub fn visit_vars(&self, f: &mut impl FnMut(&Span, &Leaf)) {
517        let mut visit = |expr: GenericExpr<Head, Leaf>| match expr {
518            GenericExpr::Var(span, var) => {
519                f(&span, &var);
520                GenericExpr::Var(span, var)
521            }
522            other => other,
523        };
524        let _ = self.clone().visit_exprs(&mut visit);
525    }
526
527    pub fn visit_exprs(
528        self,
529        f: &mut impl FnMut(GenericExpr<Head, Leaf>) -> GenericExpr<Head, Leaf>,
530    ) -> GenericFact<Head, Leaf> {
531        match self {
532            GenericFact::Eq(span, e1, e2) => {
533                GenericFact::Eq(span, e1.visit_exprs(f), e2.visit_exprs(f))
534            }
535            GenericFact::Fact(expr) => GenericFact::Fact(expr.visit_exprs(f)),
536        }
537    }
538
539    pub fn map_exprs<Head2, Leaf2>(
540        &self,
541        f: &mut impl FnMut(&GenericExpr<Head, Leaf>) -> GenericExpr<Head2, Leaf2>,
542    ) -> GenericFact<Head2, Leaf2> {
543        match self {
544            GenericFact::Eq(span, e1, e2) => GenericFact::Eq(span.clone(), f(e1), f(e2)),
545            GenericFact::Fact(expr) => GenericFact::Fact(f(expr)),
546        }
547    }
548
549    pub fn subst<Leaf2, Head2>(
550        &self,
551        subst_leaf: &mut impl FnMut(&Span, &Leaf) -> GenericExpr<Head2, Leaf2>,
552        subst_head: &mut impl FnMut(&Head) -> Head2,
553    ) -> GenericFact<Head2, Leaf2> {
554        self.map_exprs(&mut |e| e.subst(subst_leaf, subst_head))
555    }
556}
557
558impl<Head, Leaf> GenericFact<Head, Leaf>
559where
560    Leaf: Clone + PartialEq + Eq + Display + Hash,
561    Head: Clone + Display,
562{
563    /// Applies the provided `head` and `leaf` mappings to the fact.
564    pub fn map_symbols<Head2, Leaf2>(
565        self,
566        head: &mut impl FnMut(Head) -> Head2,
567        leaf: &mut impl FnMut(Leaf) -> Leaf2,
568    ) -> GenericFact<Head2, Leaf2>
569    where
570        Head2: Clone + Display,
571        Leaf2: Clone + PartialEq + Eq + Display + Hash,
572    {
573        match self {
574            GenericFact::Eq(span, e1, e2) => {
575                GenericFact::Eq(span, e1.map_symbols(head, leaf), e2.map_symbols(head, leaf))
576            }
577            GenericFact::Fact(expr) => GenericFact::Fact(expr.map_symbols(head, leaf)),
578        }
579    }
580
581    /// Converts all heads and leaves to strings.
582    pub fn make_unresolved(self) -> GenericFact<String, String> {
583        let mut map_head = |h: Head| h.to_string();
584        let mut map_leaf = |l: Leaf| l.to_string();
585        self.map_symbols(&mut map_head, &mut map_leaf)
586    }
587}
588
589impl<Head: Clone + Display, Leaf: Hash + Clone + Display + Eq> GenericExpr<Head, Leaf> {
590    pub fn visit_vars(&self, f: &mut impl FnMut(&Span, &Leaf)) {
591        let mut visit = |expr: GenericExpr<Head, Leaf>| match expr {
592            GenericExpr::Var(span, var) => {
593                f(&span, &var);
594                GenericExpr::Var(span, var)
595            }
596            other => other,
597        };
598        let _ = self.clone().visit_exprs(&mut visit);
599    }
600
601    pub fn span(&self) -> Span {
602        match self {
603            GenericExpr::Lit(span, _) => span.clone(),
604            GenericExpr::Var(span, _) => span.clone(),
605            GenericExpr::Call(span, _, _) => span.clone(),
606        }
607    }
608
609    pub fn is_var(&self) -> bool {
610        matches!(self, GenericExpr::Var(_, _))
611    }
612
613    pub fn get_var(&self) -> Option<Leaf> {
614        match self {
615            GenericExpr::Var(_ann, v) => Some(v.clone()),
616            _ => None,
617        }
618    }
619
620    fn children(&self) -> &[Self] {
621        match self {
622            GenericExpr::Var(_, _) | GenericExpr::Lit(_, _) => &[],
623            GenericExpr::Call(_, _, children) => children,
624        }
625    }
626
627    pub fn ast_size(&self) -> usize {
628        let mut size = 0;
629        self.walk(&mut |_e| size += 1, &mut |_| {});
630        size
631    }
632
633    /// Traverse the expression tree, calling `pre` before visiting children
634    /// and `post` after visiting children. Visits all nodes in the tree.
635    pub fn walk(&self, pre: &mut impl FnMut(&Self), post: &mut impl FnMut(&Self)) {
636        pre(self);
637        self.children()
638            .iter()
639            .for_each(|child| child.walk(pre, post));
640        post(self);
641    }
642
643    /// Fold over the expression tree bottom-up, collecting results from children.
644    /// The function `f` is called on each node with the node itself and the results
645    /// from folding over its children. Results are computed from leaves to root.
646    pub fn fold<Out>(&self, f: &mut impl FnMut(&Self, Vec<Out>) -> Out) -> Out {
647        let ts = self.children().iter().map(|child| child.fold(f)).collect();
648        f(self, ts)
649    }
650
651    /// Search for the first node matching a predicate, returning early once found.
652    /// Traverses the tree in pre-order (top-down).
653    pub fn find<Out>(&self, f: &mut impl FnMut(&Self) -> Option<Out>) -> Option<Out> {
654        // Check current node first
655        if let Some(result) = f(self) {
656            return Some(result);
657        }
658
659        // Then check children
660        for child in self.children().iter() {
661            if let Some(result) = child.find(f) {
662                return Some(result);
663            }
664        }
665
666        None
667    }
668
669    /// Applys `f` to all sub-expressions (including `self`)
670    /// bottom-up, collecting the results.
671    pub fn visit_exprs(self, f: &mut impl FnMut(Self) -> Self) -> Self {
672        match self {
673            GenericExpr::Lit(..) => f(self),
674            GenericExpr::Var(..) => f(self),
675            GenericExpr::Call(span, op, children) => {
676                let children = children.into_iter().map(|c| c.visit_exprs(f)).collect();
677                f(GenericExpr::Call(span, op.clone(), children))
678            }
679        }
680    }
681
682    /// `subst` replaces occurrences of variables and head symbols in the expression.
683    pub fn subst<Head2, Leaf2>(
684        &self,
685        subst_leaf: &mut impl FnMut(&Span, &Leaf) -> GenericExpr<Head2, Leaf2>,
686        subst_head: &mut impl FnMut(&Head) -> Head2,
687    ) -> GenericExpr<Head2, Leaf2> {
688        match self {
689            GenericExpr::Lit(span, lit) => GenericExpr::Lit(span.clone(), lit.clone()),
690            GenericExpr::Var(span, v) => subst_leaf(span, v),
691            GenericExpr::Call(span, op, children) => {
692                let children = children
693                    .iter()
694                    .map(|c| c.subst(subst_leaf, subst_head))
695                    .collect();
696                GenericExpr::Call(span.clone(), subst_head(op), children)
697            }
698        }
699    }
700
701    pub fn subst_leaf<Leaf2>(
702        &self,
703        subst_leaf: &mut impl FnMut(&Span, &Leaf) -> GenericExpr<Head, Leaf2>,
704    ) -> GenericExpr<Head, Leaf2> {
705        self.subst(subst_leaf, &mut |x| x.clone())
706    }
707
708    /// Applies the provided `head` and `leaf` mappings to every symbol within the expression.
709    pub fn map_symbols<Head2, Leaf2>(
710        self,
711        head: &mut impl FnMut(Head) -> Head2,
712        leaf: &mut impl FnMut(Leaf) -> Leaf2,
713    ) -> GenericExpr<Head2, Leaf2> {
714        match self {
715            GenericExpr::Lit(span, lit) => GenericExpr::Lit(span, lit),
716            GenericExpr::Var(span, var) => GenericExpr::Var(span, leaf(var)),
717            GenericExpr::Call(span, op, children) => {
718                let mut mapped_children = Vec::with_capacity(children.len());
719                for child in children {
720                    mapped_children.push(child.map_symbols(head, leaf));
721                }
722                GenericExpr::Call(span, head(op), mapped_children)
723            }
724        }
725    }
726
727    /// Converts all heads and leaves to strings.
728    pub fn make_unresolved(self) -> GenericExpr<String, String> {
729        let mut map_head = |h: Head| h.to_string();
730        let mut map_leaf = |l: Leaf| l.to_string();
731        self.map_symbols(&mut map_head, &mut map_leaf)
732    }
733
734    pub fn vars(&self) -> impl Iterator<Item = Leaf> + '_ {
735        let iterator: Box<dyn Iterator<Item = Leaf>> = match self {
736            GenericExpr::Lit(_ann, _l) => Box::new(std::iter::empty()),
737            GenericExpr::Var(_ann, v) => Box::new(std::iter::once(v.clone())),
738            GenericExpr::Call(_ann, _head, exprs) => Box::new(exprs.iter().flat_map(|e| e.vars())),
739        };
740        iterator
741    }
742}
743
744impl Display for Literal {
745    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
746        match &self {
747            Literal::Int(i) => Display::fmt(i, f),
748            Literal::Float(n) => {
749                // need to display with decimal if there is none
750                let str = n.to_string();
751                if let Ok(_num) = str.parse::<i64>() {
752                    write!(f, "{str}.0")
753                } else {
754                    write!(f, "{str}")
755                }
756            }
757            Literal::Bool(b) => Display::fmt(b, f),
758            // Escape backslashes and quotes so the output round-trips through the
759            // lexer; otherwise a string holding either character produces text
760            // that fails to re-parse. Other characters (newlines, tabs, ...) are
761            // accepted verbatim by the lexer, so we leave them as-is.
762            Literal::String(s) => {
763                write!(f, "\"")?;
764                for c in s.chars() {
765                    match c {
766                        '\\' => write!(f, "\\\\")?,
767                        '"' => write!(f, "\\\"")?,
768                        c => write!(f, "{c}")?,
769                    }
770                }
771                write!(f, "\"")
772            }
773            Literal::Unit => write!(f, "()"),
774        }
775    }
776}
777
778#[cfg(test)]
779mod tests {
780    use super::*;
781
782    #[test]
783    fn display_nullary_call_without_trailing_space() {
784        let expr = GenericExpr::<String, String>::Call(Span::Panic, "foo".into(), vec![]);
785
786        assert_eq!(expr.to_string(), "(foo)");
787    }
788
789    #[test]
790    fn display_nullary_change_without_trailing_space() {
791        let delete = GenericAction::<String, String>::Change(
792            Span::Panic,
793            Change::Delete,
794            "foo".into(),
795            vec![],
796        );
797        let subsume = GenericAction::<String, String>::Change(
798            Span::Panic,
799            Change::Subsume,
800            "foo".into(),
801            vec![],
802        );
803
804        assert_eq!(delete.to_string(), "(delete (foo))");
805        assert_eq!(subsume.to_string(), "(subsume (foo))");
806    }
807
808    #[test]
809    fn display_string_literal_escapes_special_characters() {
810        assert_eq!(Literal::String("plain".into()).to_string(), "\"plain\"");
811        assert_eq!(Literal::String("a\"b".into()).to_string(), "\"a\\\"b\"");
812        assert_eq!(Literal::String("a\\b".into()).to_string(), "\"a\\\\b\"");
813        // Newlines and tabs are accepted verbatim by the lexer, so they are not escaped.
814        assert_eq!(Literal::String("a\nb".into()).to_string(), "\"a\nb\"");
815    }
816}