cranelift_isle/
printer.rs

1//! Printer for ISLE language.
2
3use std::{io::Write, vec};
4
5use crate::ast::*;
6
7/// Print ISLE definitions.
8pub fn print<W: Write>(defs: &[Def], width: usize, out: &mut W) -> std::io::Result<()> {
9    for (i, def) in defs.iter().enumerate() {
10        if i > 0 {
11            writeln!(out)?;
12        }
13        print_node(def, width, out)?;
14        writeln!(out)?;
15    }
16    Ok(())
17}
18
19/// Print a single ISLE node.
20pub fn print_node<N: ToSExpr, W: Write>(
21    node: &N,
22    width: usize,
23    out: &mut W,
24) -> std::io::Result<()> {
25    let mut printer = Printer::new(out, width);
26    let sexpr = node.to_sexpr();
27    printer.print(&sexpr)
28}
29
30/// S-expression representation of ISLE source code prior to printing.
31#[derive(Debug, Clone, PartialEq, Eq)]
32pub enum SExpr {
33    /// Atom is a plain string to be printed.
34    Atom(String),
35    /// A binding for an ISLE structure, e.g. `x @ (...)`.
36    Binding(String, Box<SExpr>),
37    /// A parenthesized list of S-expressions, e.g. `(x y z)`.
38    List(Vec<SExpr>),
39}
40
41/// Trait for converting ISLE definitions to S-expressions.
42pub trait ToSExpr {
43    /// Convert the given value to an S-expression.
44    fn to_sexpr(&self) -> SExpr;
45}
46
47impl SExpr {
48    fn atom<S: ToString>(atom: S) -> Self {
49        SExpr::Atom(atom.to_string())
50    }
51
52    fn list(items: &[impl ToSExpr]) -> Self {
53        SExpr::List(items.into_iter().map(|i| i.to_sexpr()).collect())
54    }
55
56    fn tagged(tag: &str, items: &[impl ToSExpr]) -> Self {
57        let mut parts = vec![SExpr::atom(tag)];
58        parts.extend(items.iter().map(ToSExpr::to_sexpr));
59        SExpr::List(parts)
60    }
61}
62
63struct Printer<'a, W: Write> {
64    out: &'a mut W,
65    col: usize,
66    indent: usize,
67    width: usize,
68}
69
70#[derive(Clone, Copy, PartialEq, Eq)]
71enum Wrapping {
72    Wrap,
73    SingleLine,
74}
75
76impl<'a, W: Write> Printer<'a, W> {
77    fn new(out: &'a mut W, width: usize) -> Self {
78        Self {
79            out,
80            col: 0,
81            indent: 0,
82            width,
83        }
84    }
85
86    fn print(&mut self, sexpr: &SExpr) -> std::io::Result<()> {
87        self.print_wrapped(sexpr, Wrapping::Wrap)
88    }
89
90    fn print_wrapped(&mut self, sexpr: &SExpr, wrapping: Wrapping) -> std::io::Result<()> {
91        match sexpr {
92            SExpr::Atom(atom) => self.put(atom),
93            SExpr::Binding(name, sexpr) => {
94                self.put(name)?;
95                self.put(" @ ")?;
96                self.print_wrapped(sexpr, wrapping)
97            }
98            SExpr::List(items) => {
99                if wrapping == Wrapping::SingleLine || self.fits(sexpr) {
100                    self.put("(")?;
101                    for (i, item) in items.iter().enumerate() {
102                        if i > 0 {
103                            self.put(" ")?;
104                        }
105                        self.print_wrapped(item, Wrapping::SingleLine)?;
106                    }
107                    self.put(")")
108                } else {
109                    let (first, rest) = items.split_first().expect("non-empty list");
110                    self.put("(")?;
111                    self.print_wrapped(first, wrapping)?;
112                    self.indent += 1;
113                    for item in rest {
114                        self.nl()?;
115                        self.print_wrapped(item, wrapping)?;
116                    }
117                    self.indent -= 1;
118                    self.nl()?;
119                    self.put(")")?;
120                    Ok(())
121                }
122            }
123        }
124    }
125
126    // Would the expressions fit in the current line?
127    fn fits(&self, sexpr: &SExpr) -> bool {
128        let Some(mut remaining) = self.width.checked_sub(self.col) else {
129            return false;
130        };
131        let mut stack = vec![sexpr];
132        while let Some(sexpr) = stack.pop() {
133            let consume = match sexpr {
134                SExpr::Atom(atom) => atom.len(),
135                SExpr::Binding(name, inner) => {
136                    stack.push(inner);
137                    name.len() + 3 // " @ "
138                }
139                SExpr::List(items) => {
140                    stack.extend(items.iter().rev());
141                    2 + items.len() - 1 // "(" + ")" + spaces
142                }
143            };
144            if consume > remaining {
145                return false;
146            }
147            remaining -= consume;
148        }
149        true
150    }
151
152    fn put(&mut self, s: &str) -> std::io::Result<()> {
153        write!(self.out, "{s}")?;
154        self.col += s.len();
155        Ok(())
156    }
157
158    fn nl(&mut self) -> std::io::Result<()> {
159        writeln!(self.out)?;
160        self.col = 0;
161        for _ in 0..self.indent {
162            write!(self.out, "    ")?;
163        }
164        Ok(())
165    }
166}
167
168impl ToSExpr for Def {
169    fn to_sexpr(&self) -> SExpr {
170        match self {
171            Def::Pragma(_) => unimplemented!("pragmas not supported"),
172            Def::Type(ty) => ty.to_sexpr(),
173            Def::Rule(rule) => rule.to_sexpr(),
174            Def::Extractor(extractor) => extractor.to_sexpr(),
175            Def::Decl(decl) => decl.to_sexpr(),
176            Def::Spec(spec) => spec.to_sexpr(),
177            Def::Model(model) => model.to_sexpr(),
178            Def::Form(form) => form.to_sexpr(),
179            Def::Instantiation(instantiation) => instantiation.to_sexpr(),
180            Def::Extern(ext) => ext.to_sexpr(),
181            Def::Converter(converter) => converter.to_sexpr(),
182        }
183    }
184}
185
186impl ToSExpr for Type {
187    fn to_sexpr(&self) -> SExpr {
188        let Type {
189            name,
190            ty,
191            is_extern,
192            is_nodebug,
193            pos: _,
194        } = self;
195        let mut parts = vec![SExpr::atom("type"), name.to_sexpr()];
196        if *is_extern {
197            parts.push(SExpr::atom("extern"));
198        }
199        if *is_nodebug {
200            parts.push(SExpr::Atom("nodebug".to_string()));
201        }
202        parts.push(ty.to_sexpr());
203        SExpr::List(parts)
204    }
205}
206
207impl ToSExpr for Rule {
208    fn to_sexpr(&self) -> SExpr {
209        let Rule {
210            name,
211            prio,
212            pattern,
213            iflets,
214            expr,
215            pos: _,
216        } = self;
217        let mut parts = vec![SExpr::atom("rule")];
218        if let Some(name) = name {
219            parts.push(name.to_sexpr());
220        }
221        if let Some(prio) = prio {
222            parts.push(SExpr::atom(prio.to_string()));
223        }
224        parts.push(pattern.to_sexpr());
225        parts.extend(iflets.iter().map(ToSExpr::to_sexpr));
226        parts.push(expr.to_sexpr());
227        SExpr::List(parts)
228    }
229}
230
231impl ToSExpr for Extractor {
232    fn to_sexpr(&self) -> SExpr {
233        let Extractor {
234            term,
235            args,
236            template,
237            pos: _,
238        } = self;
239        let mut sig = vec![term.to_sexpr()];
240        sig.extend(args.iter().map(ToSExpr::to_sexpr));
241
242        let mut parts = vec![SExpr::atom("extractor")];
243        parts.push(SExpr::List(sig));
244        parts.push(template.to_sexpr());
245        SExpr::List(parts)
246    }
247}
248
249impl ToSExpr for Decl {
250    fn to_sexpr(&self) -> SExpr {
251        let Decl {
252            term,
253            arg_tys,
254            ret_ty,
255            pure,
256            multi,
257            partial,
258            pos: _,
259        } = self;
260        let mut parts = vec![SExpr::atom("decl")];
261        if *pure {
262            parts.push(SExpr::atom("pure"));
263        }
264        if *multi {
265            parts.push(SExpr::atom("multi"));
266        }
267        if *partial {
268            parts.push(SExpr::atom("partial"));
269        }
270        parts.push(term.to_sexpr());
271        parts.push(SExpr::list(arg_tys));
272        parts.push(ret_ty.to_sexpr());
273        SExpr::List(parts)
274    }
275}
276
277impl ToSExpr for Spec {
278    fn to_sexpr(&self) -> SExpr {
279        let Spec {
280            term,
281            args,
282            provides,
283            requires,
284        } = self;
285        let mut sig = vec![term.to_sexpr()];
286        sig.extend(args.iter().map(ToSExpr::to_sexpr));
287
288        let mut parts = vec![SExpr::atom("spec")];
289        parts.push(SExpr::List(sig));
290        if !provides.is_empty() {
291            parts.push(SExpr::tagged("provide", &self.provides));
292        }
293        if !requires.is_empty() {
294            parts.push(SExpr::tagged("require", &self.requires));
295        }
296        SExpr::List(parts)
297    }
298}
299
300impl ToSExpr for Model {
301    fn to_sexpr(&self) -> SExpr {
302        let Model { name, val } = self;
303        SExpr::List(vec![SExpr::atom("model"), name.to_sexpr(), val.to_sexpr()])
304    }
305}
306
307impl ToSExpr for Form {
308    fn to_sexpr(&self) -> SExpr {
309        let Form {
310            name,
311            signatures,
312            pos: _,
313        } = self;
314        let mut parts = vec![SExpr::atom("form"), name.to_sexpr()];
315        parts.extend(signatures.iter().map(ToSExpr::to_sexpr));
316        SExpr::List(parts)
317    }
318}
319
320impl ToSExpr for Instantiation {
321    fn to_sexpr(&self) -> SExpr {
322        let Instantiation {
323            term,
324            form,
325            signatures,
326            pos: _,
327        } = self;
328        let mut parts = vec![SExpr::atom("instantiate"), term.to_sexpr()];
329        if let Some(form) = form {
330            parts.push(form.to_sexpr());
331        } else {
332            parts.extend(signatures.iter().map(ToSExpr::to_sexpr));
333        }
334        SExpr::List(parts)
335    }
336}
337
338impl ToSExpr for Extern {
339    fn to_sexpr(&self) -> SExpr {
340        match self {
341            Extern::Extractor {
342                term,
343                func,
344                infallible,
345                pos: _,
346            } => {
347                let mut parts = vec![SExpr::atom("extern"), SExpr::atom("extractor")];
348                if *infallible {
349                    parts.push(SExpr::atom("infallible"));
350                }
351                parts.push(term.to_sexpr());
352                parts.push(func.to_sexpr());
353                SExpr::List(parts)
354            }
355            Extern::Constructor { term, func, pos: _ } => SExpr::List(vec![
356                SExpr::atom("extern"),
357                SExpr::atom("constructor"),
358                term.to_sexpr(),
359                func.to_sexpr(),
360            ]),
361            Extern::Const { name, ty, pos: _ } => SExpr::List(vec![
362                SExpr::atom("extern"),
363                SExpr::atom("const"),
364                SExpr::atom(format!("${}", name.0)),
365                ty.to_sexpr(),
366            ]),
367        }
368    }
369}
370
371impl ToSExpr for Converter {
372    fn to_sexpr(&self) -> SExpr {
373        let Converter {
374            inner_ty,
375            outer_ty,
376            term,
377            pos: _,
378        } = self;
379        SExpr::List(vec![
380            SExpr::atom("convert"),
381            inner_ty.to_sexpr(),
382            outer_ty.to_sexpr(),
383            term.to_sexpr(),
384        ])
385    }
386}
387
388impl ToSExpr for TypeValue {
389    fn to_sexpr(&self) -> SExpr {
390        match self {
391            TypeValue::Primitive(name, _) => {
392                SExpr::List(vec![SExpr::atom("primitive"), name.to_sexpr()])
393            }
394            TypeValue::Enum(variants, _) => {
395                let mut parts = vec![SExpr::atom("enum")];
396                parts.extend(variants.iter().map(ToSExpr::to_sexpr));
397                SExpr::List(parts)
398            }
399        }
400    }
401}
402
403impl ToSExpr for Variant {
404    fn to_sexpr(&self) -> SExpr {
405        let Variant {
406            name,
407            fields,
408            pos: _,
409        } = self;
410        let mut parts = vec![name.to_sexpr()];
411        parts.extend(fields.iter().map(ToSExpr::to_sexpr));
412        SExpr::List(parts)
413    }
414}
415
416impl ToSExpr for Field {
417    fn to_sexpr(&self) -> SExpr {
418        let Field { name, ty, pos: _ } = self;
419        SExpr::List(vec![name.to_sexpr(), ty.to_sexpr()])
420    }
421}
422
423impl ToSExpr for ModelValue {
424    fn to_sexpr(&self) -> SExpr {
425        match self {
426            ModelValue::TypeValue(mt) => SExpr::List(vec![SExpr::atom("type"), mt.to_sexpr()]),
427            ModelValue::EnumValues(enumerators) => {
428                let mut parts = vec![SExpr::atom("enum")];
429                for (variant, value) in enumerators {
430                    parts.push(SExpr::List(vec![variant.to_sexpr(), value.to_sexpr()]));
431                }
432                SExpr::List(parts)
433            }
434        }
435    }
436}
437
438impl ToSExpr for ModelType {
439    fn to_sexpr(&self) -> SExpr {
440        match self {
441            ModelType::Unit => SExpr::atom("Unit"),
442            ModelType::Int => SExpr::atom("Int"),
443            ModelType::Bool => SExpr::atom("Bool"),
444            ModelType::BitVec(Some(size)) => {
445                SExpr::List(vec![SExpr::atom("bv"), SExpr::atom(size)])
446            }
447            ModelType::BitVec(None) => SExpr::List(vec![SExpr::atom("bv")]),
448        }
449    }
450}
451
452impl ToSExpr for Signature {
453    fn to_sexpr(&self) -> SExpr {
454        let Signature {
455            args,
456            ret,
457            canonical,
458            pos: _,
459        } = self;
460        SExpr::List(vec![
461            SExpr::tagged("args", args),
462            SExpr::tagged("ret", std::slice::from_ref(ret)),
463            SExpr::tagged("canon", std::slice::from_ref(canonical)),
464        ])
465    }
466}
467
468impl ToSExpr for SpecExpr {
469    fn to_sexpr(&self) -> SExpr {
470        match self {
471            SpecExpr::ConstInt { val, pos: _ } => SExpr::atom(val),
472            SpecExpr::ConstBitVec { val, width, pos: _ } => SExpr::atom(if *width % 4 == 0 {
473                format!("#x{val:0width$x}", width = *width as usize / 4)
474            } else {
475                format!("#b{val:0width$b}", width = *width as usize)
476            }),
477            SpecExpr::ConstBool { val, pos: _ } => SExpr::atom(if *val { "true" } else { "false" }),
478            SpecExpr::ConstUnit { pos: _ } => SExpr::List(Vec::new()),
479            SpecExpr::Var { var, pos: _ } => var.to_sexpr(),
480            SpecExpr::Op { op, args, pos: _ } => {
481                let mut parts = vec![op.to_sexpr()];
482                parts.extend(args.iter().map(ToSExpr::to_sexpr));
483                SExpr::List(parts)
484            }
485            SpecExpr::Pair { l, r } => SExpr::List(vec![l.to_sexpr(), r.to_sexpr()]),
486            SpecExpr::Enum { name } => SExpr::List(vec![name.to_sexpr()]),
487        }
488    }
489}
490
491impl ToSExpr for SpecOp {
492    fn to_sexpr(&self) -> SExpr {
493        SExpr::atom(match self {
494            SpecOp::Eq => "=",
495            SpecOp::And => "and",
496            SpecOp::Not => "not",
497            SpecOp::Imp => "=>",
498            SpecOp::Or => "or",
499            SpecOp::Lte => "<=",
500            SpecOp::Lt => "<",
501            SpecOp::Gte => ">=",
502            SpecOp::Gt => ">",
503            SpecOp::BVNot => "bvnot",
504            SpecOp::BVAnd => "bvand",
505            SpecOp::BVOr => "bvor",
506            SpecOp::BVXor => "bvxor",
507            SpecOp::BVNeg => "bvneg",
508            SpecOp::BVAdd => "bvadd",
509            SpecOp::BVSub => "bvsub",
510            SpecOp::BVMul => "bvmul",
511            SpecOp::BVUdiv => "bvudiv",
512            SpecOp::BVUrem => "bvurem",
513            SpecOp::BVSdiv => "bvsdiv",
514            SpecOp::BVSrem => "bvsrem",
515            SpecOp::BVShl => "bvshl",
516            SpecOp::BVLshr => "bvlshr",
517            SpecOp::BVAshr => "bvashr",
518            SpecOp::BVSaddo => "bvsaddo",
519            SpecOp::BVUle => "bvule",
520            SpecOp::BVUlt => "bvult",
521            SpecOp::BVUgt => "bvugt",
522            SpecOp::BVUge => "bvuge",
523            SpecOp::BVSlt => "bvslt",
524            SpecOp::BVSle => "bvsle",
525            SpecOp::BVSgt => "bvsgt",
526            SpecOp::BVSge => "bvsge",
527            SpecOp::Rotr => "rotr",
528            SpecOp::Rotl => "rotl",
529            SpecOp::Extract => "extract",
530            SpecOp::ZeroExt => "zero_ext",
531            SpecOp::SignExt => "sign_ext",
532            SpecOp::Concat => "concat",
533            SpecOp::ConvTo => "conv_to",
534            SpecOp::Int2BV => "int2bv",
535            SpecOp::WidthOf => "widthof",
536            SpecOp::If => "if",
537            SpecOp::Switch => "switch",
538            SpecOp::Popcnt => "popcnt",
539            SpecOp::Rev => "rev",
540            SpecOp::Cls => "cls",
541            SpecOp::Clz => "clz",
542            SpecOp::Subs => "subs",
543            SpecOp::BV2Int => "bv2int",
544            SpecOp::LoadEffect => "load_effect",
545            SpecOp::StoreEffect => "store_effect",
546        })
547    }
548}
549
550impl ToSExpr for Pattern {
551    fn to_sexpr(&self) -> SExpr {
552        match self {
553            Pattern::Var {
554                var: Ident(var, _),
555                pos: _,
556            } => SExpr::atom(var.clone()),
557            Pattern::BindPattern {
558                var: Ident(var, _),
559                subpat,
560                pos: _,
561            } => SExpr::Binding(var.clone(), Box::new(subpat.to_sexpr())),
562            Pattern::ConstInt { val, pos: _ } => SExpr::atom(val),
563            Pattern::ConstBool { val, pos: _ } => SExpr::atom(if *val { "true" } else { "false" }),
564            Pattern::ConstPrim { val, pos: _ } => SExpr::atom(format!("${}", val.0)),
565            Pattern::Wildcard { pos: _ } => SExpr::atom("_"),
566            Pattern::Term { sym, args, pos: _ } => {
567                let mut parts = vec![sym.to_sexpr()];
568                parts.extend(args.iter().map(ToSExpr::to_sexpr));
569                SExpr::List(parts)
570            }
571            Pattern::And { subpats, pos: _ } => {
572                let mut parts = vec![SExpr::atom("and")];
573                parts.extend(subpats.iter().map(ToSExpr::to_sexpr));
574                SExpr::List(parts)
575            }
576            Pattern::MacroArg { .. } => unimplemented!("macro arguments are for internal use only"),
577        }
578    }
579}
580
581impl ToSExpr for IfLet {
582    fn to_sexpr(&self) -> SExpr {
583        let IfLet {
584            pattern,
585            expr,
586            pos: _,
587        } = self;
588        SExpr::List(vec![
589            SExpr::atom("if-let"),
590            pattern.to_sexpr(),
591            expr.to_sexpr(),
592        ])
593    }
594}
595
596impl ToSExpr for Expr {
597    fn to_sexpr(&self) -> SExpr {
598        match self {
599            Expr::Term { sym, args, pos: _ } => {
600                let mut parts = vec![sym.to_sexpr()];
601                parts.extend(args.iter().map(ToSExpr::to_sexpr));
602                SExpr::List(parts)
603            }
604            Expr::Var { name, pos: _ } => name.to_sexpr(),
605            Expr::ConstInt { val, pos: _ } => SExpr::atom(val),
606            Expr::ConstBool { val, pos: _ } => SExpr::atom(if *val { "true" } else { "false" }),
607            Expr::ConstPrim { val, pos: _ } => SExpr::atom(format!("${}", val.0)),
608            Expr::Let { defs, body, pos: _ } => {
609                let mut parts = vec![SExpr::atom("let")];
610                parts.push(SExpr::list(&defs));
611                parts.push(body.to_sexpr());
612                SExpr::List(parts)
613            }
614        }
615    }
616}
617
618impl ToSExpr for LetDef {
619    fn to_sexpr(&self) -> SExpr {
620        let LetDef {
621            var,
622            ty,
623            val,
624            pos: _,
625        } = self;
626        SExpr::List(vec![var.to_sexpr(), ty.to_sexpr(), val.to_sexpr()])
627    }
628}
629
630impl ToSExpr for Ident {
631    fn to_sexpr(&self) -> SExpr {
632        let Ident(name, _) = self;
633        SExpr::atom(name.clone())
634    }
635}