Skip to main content

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            rec,
259            pos: _,
260        } = self;
261        let mut parts = vec![SExpr::atom("decl")];
262        if *pure {
263            parts.push(SExpr::atom("pure"));
264        }
265        if *multi {
266            parts.push(SExpr::atom("multi"));
267        }
268        if *partial {
269            parts.push(SExpr::atom("partial"));
270        }
271        if *rec {
272            parts.push(SExpr::atom("rec"));
273        }
274        parts.push(term.to_sexpr());
275        parts.push(SExpr::list(arg_tys));
276        parts.push(ret_ty.to_sexpr());
277        SExpr::List(parts)
278    }
279}
280
281impl ToSExpr for Spec {
282    fn to_sexpr(&self) -> SExpr {
283        let Spec {
284            term,
285            args,
286            provides,
287            requires,
288        } = self;
289        let mut sig = vec![term.to_sexpr()];
290        sig.extend(args.iter().map(ToSExpr::to_sexpr));
291
292        let mut parts = vec![SExpr::atom("spec")];
293        parts.push(SExpr::List(sig));
294        if !provides.is_empty() {
295            parts.push(SExpr::tagged("provide", &self.provides));
296        }
297        if !requires.is_empty() {
298            parts.push(SExpr::tagged("require", &self.requires));
299        }
300        SExpr::List(parts)
301    }
302}
303
304impl ToSExpr for Model {
305    fn to_sexpr(&self) -> SExpr {
306        let Model { name, val } = self;
307        SExpr::List(vec![SExpr::atom("model"), name.to_sexpr(), val.to_sexpr()])
308    }
309}
310
311impl ToSExpr for Form {
312    fn to_sexpr(&self) -> SExpr {
313        let Form {
314            name,
315            signatures,
316            pos: _,
317        } = self;
318        let mut parts = vec![SExpr::atom("form"), name.to_sexpr()];
319        parts.extend(signatures.iter().map(ToSExpr::to_sexpr));
320        SExpr::List(parts)
321    }
322}
323
324impl ToSExpr for Instantiation {
325    fn to_sexpr(&self) -> SExpr {
326        let Instantiation {
327            term,
328            form,
329            signatures,
330            pos: _,
331        } = self;
332        let mut parts = vec![SExpr::atom("instantiate"), term.to_sexpr()];
333        if let Some(form) = form {
334            parts.push(form.to_sexpr());
335        } else {
336            parts.extend(signatures.iter().map(ToSExpr::to_sexpr));
337        }
338        SExpr::List(parts)
339    }
340}
341
342impl ToSExpr for Extern {
343    fn to_sexpr(&self) -> SExpr {
344        match self {
345            Extern::Extractor {
346                term,
347                func,
348                infallible,
349                pos: _,
350            } => {
351                let mut parts = vec![SExpr::atom("extern"), SExpr::atom("extractor")];
352                if *infallible {
353                    parts.push(SExpr::atom("infallible"));
354                }
355                parts.push(term.to_sexpr());
356                parts.push(func.to_sexpr());
357                SExpr::List(parts)
358            }
359            Extern::Constructor { term, func, pos: _ } => SExpr::List(vec![
360                SExpr::atom("extern"),
361                SExpr::atom("constructor"),
362                term.to_sexpr(),
363                func.to_sexpr(),
364            ]),
365            Extern::Const { name, ty, pos: _ } => SExpr::List(vec![
366                SExpr::atom("extern"),
367                SExpr::atom("const"),
368                SExpr::atom(format!("${}", name.0)),
369                ty.to_sexpr(),
370            ]),
371        }
372    }
373}
374
375impl ToSExpr for Converter {
376    fn to_sexpr(&self) -> SExpr {
377        let Converter {
378            inner_ty,
379            outer_ty,
380            term,
381            pos: _,
382        } = self;
383        SExpr::List(vec![
384            SExpr::atom("convert"),
385            inner_ty.to_sexpr(),
386            outer_ty.to_sexpr(),
387            term.to_sexpr(),
388        ])
389    }
390}
391
392impl ToSExpr for TypeValue {
393    fn to_sexpr(&self) -> SExpr {
394        match self {
395            TypeValue::Primitive(name, _) => {
396                SExpr::List(vec![SExpr::atom("primitive"), name.to_sexpr()])
397            }
398            TypeValue::Enum(variants, _) => {
399                let mut parts = vec![SExpr::atom("enum")];
400                parts.extend(variants.iter().map(ToSExpr::to_sexpr));
401                SExpr::List(parts)
402            }
403        }
404    }
405}
406
407impl ToSExpr for Variant {
408    fn to_sexpr(&self) -> SExpr {
409        let Variant {
410            name,
411            fields,
412            pos: _,
413        } = self;
414        let mut parts = vec![name.to_sexpr()];
415        parts.extend(fields.iter().map(ToSExpr::to_sexpr));
416        SExpr::List(parts)
417    }
418}
419
420impl ToSExpr for Field {
421    fn to_sexpr(&self) -> SExpr {
422        let Field { name, ty, pos: _ } = self;
423        SExpr::List(vec![name.to_sexpr(), ty.to_sexpr()])
424    }
425}
426
427impl ToSExpr for ModelValue {
428    fn to_sexpr(&self) -> SExpr {
429        match self {
430            ModelValue::TypeValue(mt) => SExpr::List(vec![SExpr::atom("type"), mt.to_sexpr()]),
431            ModelValue::EnumValues(enumerators) => {
432                let mut parts = vec![SExpr::atom("enum")];
433                for (variant, value) in enumerators {
434                    parts.push(SExpr::List(vec![variant.to_sexpr(), value.to_sexpr()]));
435                }
436                SExpr::List(parts)
437            }
438        }
439    }
440}
441
442impl ToSExpr for ModelType {
443    fn to_sexpr(&self) -> SExpr {
444        match self {
445            ModelType::Unit => SExpr::atom("Unit"),
446            ModelType::Int => SExpr::atom("Int"),
447            ModelType::Bool => SExpr::atom("Bool"),
448            ModelType::BitVec(Some(size)) => {
449                SExpr::List(vec![SExpr::atom("bv"), SExpr::atom(size)])
450            }
451            ModelType::BitVec(None) => SExpr::List(vec![SExpr::atom("bv")]),
452        }
453    }
454}
455
456impl ToSExpr for Signature {
457    fn to_sexpr(&self) -> SExpr {
458        let Signature {
459            args,
460            ret,
461            canonical,
462            pos: _,
463        } = self;
464        SExpr::List(vec![
465            SExpr::tagged("args", args),
466            SExpr::tagged("ret", std::slice::from_ref(ret)),
467            SExpr::tagged("canon", std::slice::from_ref(canonical)),
468        ])
469    }
470}
471
472impl ToSExpr for SpecExpr {
473    fn to_sexpr(&self) -> SExpr {
474        match self {
475            SpecExpr::ConstInt { val, pos: _ } => SExpr::atom(val),
476            SpecExpr::ConstBitVec { val, width, pos: _ } => SExpr::atom(if *width % 4 == 0 {
477                format!("#x{val:0width$x}", width = *width as usize / 4)
478            } else {
479                format!("#b{val:0width$b}", width = *width as usize)
480            }),
481            SpecExpr::ConstBool { val, pos: _ } => SExpr::atom(if *val { "true" } else { "false" }),
482            SpecExpr::ConstUnit { pos: _ } => SExpr::List(Vec::new()),
483            SpecExpr::Var { var, pos: _ } => var.to_sexpr(),
484            SpecExpr::Op { op, args, pos: _ } => {
485                let mut parts = vec![op.to_sexpr()];
486                parts.extend(args.iter().map(ToSExpr::to_sexpr));
487                SExpr::List(parts)
488            }
489            SpecExpr::Pair { l, r } => SExpr::List(vec![l.to_sexpr(), r.to_sexpr()]),
490            SpecExpr::Enum { name } => SExpr::List(vec![name.to_sexpr()]),
491        }
492    }
493}
494
495impl ToSExpr for SpecOp {
496    fn to_sexpr(&self) -> SExpr {
497        SExpr::atom(match self {
498            SpecOp::Eq => "=",
499            SpecOp::And => "and",
500            SpecOp::Not => "not",
501            SpecOp::Imp => "=>",
502            SpecOp::Or => "or",
503            SpecOp::Lte => "<=",
504            SpecOp::Lt => "<",
505            SpecOp::Gte => ">=",
506            SpecOp::Gt => ">",
507            SpecOp::BVNot => "bvnot",
508            SpecOp::BVAnd => "bvand",
509            SpecOp::BVOr => "bvor",
510            SpecOp::BVXor => "bvxor",
511            SpecOp::BVNeg => "bvneg",
512            SpecOp::BVAdd => "bvadd",
513            SpecOp::BVSub => "bvsub",
514            SpecOp::BVMul => "bvmul",
515            SpecOp::BVUdiv => "bvudiv",
516            SpecOp::BVUrem => "bvurem",
517            SpecOp::BVSdiv => "bvsdiv",
518            SpecOp::BVSrem => "bvsrem",
519            SpecOp::BVShl => "bvshl",
520            SpecOp::BVLshr => "bvlshr",
521            SpecOp::BVAshr => "bvashr",
522            SpecOp::BVSaddo => "bvsaddo",
523            SpecOp::BVUle => "bvule",
524            SpecOp::BVUlt => "bvult",
525            SpecOp::BVUgt => "bvugt",
526            SpecOp::BVUge => "bvuge",
527            SpecOp::BVSlt => "bvslt",
528            SpecOp::BVSle => "bvsle",
529            SpecOp::BVSgt => "bvsgt",
530            SpecOp::BVSge => "bvsge",
531            SpecOp::Rotr => "rotr",
532            SpecOp::Rotl => "rotl",
533            SpecOp::Extract => "extract",
534            SpecOp::ZeroExt => "zero_ext",
535            SpecOp::SignExt => "sign_ext",
536            SpecOp::Concat => "concat",
537            SpecOp::ConvTo => "conv_to",
538            SpecOp::Int2BV => "int2bv",
539            SpecOp::WidthOf => "widthof",
540            SpecOp::If => "if",
541            SpecOp::Switch => "switch",
542            SpecOp::Popcnt => "popcnt",
543            SpecOp::Rev => "rev",
544            SpecOp::Cls => "cls",
545            SpecOp::Clz => "clz",
546            SpecOp::Subs => "subs",
547            SpecOp::BV2Int => "bv2int",
548            SpecOp::LoadEffect => "load_effect",
549            SpecOp::StoreEffect => "store_effect",
550        })
551    }
552}
553
554impl ToSExpr for Pattern {
555    fn to_sexpr(&self) -> SExpr {
556        match self {
557            Pattern::Var {
558                var: Ident(var, _),
559                pos: _,
560            } => SExpr::atom(var.clone()),
561            Pattern::BindPattern {
562                var: Ident(var, _),
563                subpat,
564                pos: _,
565            } => SExpr::Binding(var.clone(), Box::new(subpat.to_sexpr())),
566            Pattern::ConstInt { val, pos: _ } => SExpr::atom(val),
567            Pattern::ConstBool { val, pos: _ } => SExpr::atom(if *val { "true" } else { "false" }),
568            Pattern::ConstPrim { val, pos: _ } => SExpr::atom(format!("${}", val.0)),
569            Pattern::Wildcard { pos: _ } => SExpr::atom("_"),
570            Pattern::Term { sym, args, pos: _ } => {
571                let mut parts = vec![sym.to_sexpr()];
572                parts.extend(args.iter().map(ToSExpr::to_sexpr));
573                SExpr::List(parts)
574            }
575            Pattern::And { subpats, pos: _ } => {
576                let mut parts = vec![SExpr::atom("and")];
577                parts.extend(subpats.iter().map(ToSExpr::to_sexpr));
578                SExpr::List(parts)
579            }
580            Pattern::MacroArg { .. } => unimplemented!("macro arguments are for internal use only"),
581        }
582    }
583}
584
585impl ToSExpr for IfLet {
586    fn to_sexpr(&self) -> SExpr {
587        let IfLet {
588            pattern,
589            expr,
590            pos: _,
591        } = self;
592        SExpr::List(vec![
593            SExpr::atom("if-let"),
594            pattern.to_sexpr(),
595            expr.to_sexpr(),
596        ])
597    }
598}
599
600impl ToSExpr for Expr {
601    fn to_sexpr(&self) -> SExpr {
602        match self {
603            Expr::Term { sym, args, pos: _ } => {
604                let mut parts = vec![sym.to_sexpr()];
605                parts.extend(args.iter().map(ToSExpr::to_sexpr));
606                SExpr::List(parts)
607            }
608            Expr::Var { name, pos: _ } => name.to_sexpr(),
609            Expr::ConstInt { val, pos: _ } => SExpr::atom(val),
610            Expr::ConstBool { val, pos: _ } => SExpr::atom(if *val { "true" } else { "false" }),
611            Expr::ConstPrim { val, pos: _ } => SExpr::atom(format!("${}", val.0)),
612            Expr::Let { defs, body, pos: _ } => {
613                let mut parts = vec![SExpr::atom("let")];
614                parts.push(SExpr::list(&defs));
615                parts.push(body.to_sexpr());
616                SExpr::List(parts)
617            }
618        }
619    }
620}
621
622impl ToSExpr for LetDef {
623    fn to_sexpr(&self) -> SExpr {
624        let LetDef {
625            var,
626            ty,
627            val,
628            pos: _,
629        } = self;
630        SExpr::List(vec![var.to_sexpr(), ty.to_sexpr(), val.to_sexpr()])
631    }
632}
633
634impl ToSExpr for Ident {
635    fn to_sexpr(&self) -> SExpr {
636        let Ident(name, _) = self;
637        SExpr::atom(name.clone())
638    }
639}