miden_formatting/prettier/
mod.rs

1//! This module provides a generic pretty printer implementation of the "Prettier" variety, i.e.
2//! based on the design described by Philip Wadler in
3//! [_A prettier printer_](https://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf).
4//!
5//! This can be used to implement a pretty printer for arbitrarily complex and nested data
6//! structures and syntaxes, and handles the tricky details of indentation, when to break lines,
7//! etc.
8//!
9//! See the [PrettyPrint] trait for more on how to use this module.
10mod document;
11mod print;
12#[cfg(test)]
13mod tests;
14
15use alloc::string::String;
16use core::fmt;
17
18pub use self::document::{concat, const_text, display, flatten, indent, nl, split, text, Document};
19
20/// The [PrettyPrint] trait is used as a building block for pretty printing data or syntax trees,
21/// as commonly seen in tools like Prettier.
22///
23/// It relies on providing the desired layout for a given type in the form of a [Document]. The
24/// pretty printer algorithm then renders this document to an output stream, using details of
25/// the output and the document to drive the choices it makes as it displays the document.
26///
27/// To get started, simply implement [PrettyPrint::render] for the type you wish to have pretty
28/// printed. You can then call [PrettyPrint::to_pretty_string] to obtain a [String] containing
29/// the pretty printed output, or if you have a [core::fmt::Formatter] handy, you can pretty
30/// print directly to that formatter using [PrettyPrint::pretty_print].
31///
32/// # Example
33///
34/// The following is the AST for a simple expression language with a couple ops, that we wish to
35/// implement a pretty printer for. Let's take a look at how that is done, and how we can make
36/// use of the various document constructors to acheive various effects:
37///
38/// ```rust
39/// use miden_formatting::prettier::{self, PrettyPrint, Document};
40///
41/// pub enum Expr {
42///     Term(Term),
43///     Binary(BinaryOp),
44/// }
45///
46/// pub struct BinaryOp {
47///     pub op: Op,
48///     pub lhs: Box<Expr>,
49///     pub rhs: Box<Expr>,
50/// }
51///
52/// #[derive(Copy, Clone)]
53/// pub enum Op {
54///     Add,
55///     Sub,
56///     Mul,
57/// }
58///
59/// pub enum Term {
60///     Var(String),
61///     Num(isize),
62/// }
63///
64/// impl PrettyPrint for Expr {
65///     fn render(&self) -> Document {
66///         match self {
67///             Self::Term(term) => term.render(),
68///             Self::Binary(expr) => expr.render(),
69///         }
70///     }
71/// }
72///
73/// /// We can trivially implement Display for our AST with our PrettyPrint impl
74/// impl core::fmt::Display for Expr {
75///     fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
76///         self.pretty_print(f)
77///     }
78/// }
79///
80/// impl PrettyPrint for BinaryOp {
81///     fn render(&self) -> Document {
82///         // Bring all of the document constructors into scope
83///         use prettier::*;
84///
85///         let maybe_wrap = move |expr: &Expr| -> Document {
86///             match expr {
87///                 Expr::Term(term) => term.render(),
88///                 Expr::Binary(expr) => '(' + expr.render() + ')',
89///             }
90///         };
91///
92///         // When the printer runs out of space to hold more of the document on a single line,
93///         // it will prefer to introduce breaks at `nl`, but may also choose to break at any
94///         // point where two documents are joined together. We can guide this behavior by providing
95///         // a choice between two documents: the first being the single-line layout, and the second
96///         // being the multi-line layout. The printer will choose the single-line layout unless it
97///         // has insufficient space for it, in which case it will choose the multi-line layout instead.
98///         let single_line = self.lhs.render()
99///                 + ' '
100///                 + display(self.op)
101///                 + ' '
102///                 + maybe_wrap(&self.rhs);
103///         // Here, we're choosing to break after the operator, indent 4 spaces, then continue to
104///         // print the remainder of the expression, e.g:
105///         //
106///         //     $a + ($b * ($c -
107///         //         256))
108///         let multi_line =
109///                 indent(4, flatten(self.lhs.render() + ' ' + display(self.op))
110///                 + nl()
111///                 + maybe_wrap(&self.rhs)
112///                 );
113///         single_line | multi_line
114///     }
115/// }
116///
117/// impl PrettyPrint for Term {
118///     fn render(&self) -> Document {
119///         use prettier::*;
120///         // NOTE: We could have just used a Display impl for Term, but in more complex syntaxes
121///         // terms might have aggregate data structures and things of that nature, where more
122///         // complex pretty printing is desired. For now, this just demonstrates how you can
123///         // implement PrettyPrint for types you don't control with custom formatting.
124///         match self {
125///             Self::Var(v) => text(format!("${v}")),
126///             Self::Num(n) => display(*n),
127///         }
128///     }
129/// }
130///
131/// /// Rather than implement both PrettyPrint and Display for things which reduce to keywords,
132/// /// integers, etc., you can simply delegate to the Display implementation when building the
133/// /// higher-level PrettyPrint impls using the `display` helper.
134/// impl core::fmt::Display for Op {
135///     fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
136///         match self {
137///             Self::Add => f.write_str("+"),
138///             Self::Sub => f.write_str("-"),
139///             Self::Mul => f.write_str("*"),
140///         }
141///     }
142/// }
143/// ```
144///
145/// See the documentation for the various [Document] constructors for more information on their
146/// usage and semantics. I would recommend starting with [indent], as that is a common one that
147/// you will want to use.
148pub trait PrettyPrint {
149    /// The core of the [PrettyPrint] functionality.
150    ///
151    /// When called, the implementation must render a [Document] which represents the layout
152    /// of the thing being pretty-printed. The rendered [Document] is then displayed via the
153    /// pretty printer, using details about the output stream, and the structure of the document,
154    /// to make decisions about when and where to introduce line breaks, etc.
155    ///
156    /// Implementations do not need to worry about or manage things like the width of the output,
157    /// indentation level, etc. Instead the focus is purely on the layout, leaving the heavy
158    /// lifting to the pretty printer.
159    ///
160    /// This method is the only one required to be implemented.
161    fn render(&self) -> Document;
162
163    /// Produce a [String] containing the results of pretty-printing this object.
164    ///
165    /// The string is formatted with an assumed width of 80 columns. If you wish to customize this,
166    /// you should instead prefer to use [PrettyPrint::pretty_print], or if you have implemented
167    /// [core::fmt::Display] for this type by delegating to [PrettyPrint::pretty_print], you can
168    /// use the Rust formatting syntax to do this, e.g. `format!("{:width$}", self, width = 100)`
169    fn to_pretty_string(&self) -> String {
170        format!("{:width$}", Prettier(self), width = 80)
171    }
172
173    /// Pretty-print this object to the given [core::fmt::Formatter].
174    ///
175    /// You may implement [core::fmt::Display] for your type in terms of this function like so:
176    ///
177    /// ```rust,ignore
178    /// impl fmt::Display for Foo {
179    ///     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
180    ///         self.pretty_print(f)
181    ///     }
182    /// }
183    /// ```
184    fn pretty_print(&self, f: &mut fmt::Formatter) -> fmt::Result {
185        let doc = self.render();
186        let width = f.width().unwrap_or(80);
187        print::pretty_print(&doc, width, f)
188    }
189}
190
191impl fmt::Display for dyn PrettyPrint {
192    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
193        PrettyPrint::pretty_print(self, f)
194    }
195}
196
197/// Generate an implementation of [PrettyPrint] for a given type by delegating to [core::fmt::Display].
198///
199/// # Example
200///
201/// ```rust,ignore
202/// pretty_via_to_display!(Foo);
203/// ```
204#[macro_export]
205macro_rules! pretty_via_display {
206    ($name:ty) => {
207        impl $crate::prettier::PrettyPrint for $name {
208            fn render(&self) -> $crate::prettier::Document {
209                $crate::prettier::display(*self)
210            }
211        }
212    };
213}
214
215/// Generate an implementation of [PrettyPrint] for a given type by delegating to [alloc::string::ToString].
216///
217/// # Example
218///
219/// ```rust,ignore
220/// pretty_via_to_string!(Foo);
221/// ```
222#[macro_export]
223macro_rules! pretty_via_to_string {
224    ($name:ty) => {
225        impl $crate::prettier::PrettyPrint for $name {
226            fn render(&self) -> $crate::prettier::Document {
227                $crate::prettier::text(&**self)
228            }
229        }
230    };
231}
232
233pretty_via_display!(bool);
234pretty_via_display!(u8);
235pretty_via_display!(i8);
236pretty_via_display!(u16);
237pretty_via_display!(i16);
238pretty_via_display!(u32);
239pretty_via_display!(i32);
240pretty_via_display!(u64);
241pretty_via_display!(i64);
242pretty_via_display!(u128);
243pretty_via_display!(i128);
244pretty_via_display!(usize);
245pretty_via_display!(isize);
246pretty_via_display!(core::num::NonZeroU8);
247pretty_via_display!(core::num::NonZeroI8);
248pretty_via_display!(core::num::NonZeroU16);
249pretty_via_display!(core::num::NonZeroI16);
250pretty_via_display!(core::num::NonZeroU32);
251pretty_via_display!(core::num::NonZeroI32);
252pretty_via_display!(core::num::NonZeroU64);
253pretty_via_display!(core::num::NonZeroI64);
254pretty_via_display!(core::num::NonZeroU128);
255pretty_via_display!(core::num::NonZeroI128);
256pretty_via_display!(core::num::NonZeroUsize);
257pretty_via_display!(core::num::NonZeroIsize);
258
259impl<'a, T: ?Sized + PrettyPrint> PrettyPrint for &'a T {
260    #[inline]
261    fn render(&self) -> Document {
262        (**self).render()
263    }
264    #[inline]
265    fn to_pretty_string(&self) -> String {
266        (**self).to_pretty_string()
267    }
268    #[inline]
269    fn pretty_print(&self, f: &mut fmt::Formatter) -> fmt::Result {
270        (**self).pretty_print(f)
271    }
272}
273
274impl PrettyPrint for str {
275    fn render(&self) -> Document {
276        self.lines()
277            .map(text)
278            .reduce(|acc, doc| match acc {
279                Document::Empty => doc + nl(),
280                other => other + doc + nl(),
281            })
282            .unwrap_or(Document::Empty)
283    }
284}
285
286impl PrettyPrint for String {
287    fn render(&self) -> Document {
288        PrettyPrint::render(self.as_str())
289    }
290    fn pretty_print(&self, f: &mut fmt::Formatter) -> fmt::Result {
291        PrettyPrint::pretty_print(self.as_str(), f)
292    }
293}
294
295impl<'a> PrettyPrint for alloc::borrow::Cow<'a, str> {
296    fn render(&self) -> Document {
297        PrettyPrint::render(self.as_ref())
298    }
299    fn pretty_print(&self, f: &mut fmt::Formatter) -> fmt::Result {
300        PrettyPrint::pretty_print(self.as_ref(), f)
301    }
302}
303
304impl<T: PrettyPrint> PrettyPrint for alloc::boxed::Box<T> {
305    fn render(&self) -> Document {
306        PrettyPrint::render(self.as_ref())
307    }
308    fn pretty_print(&self, f: &mut fmt::Formatter) -> fmt::Result {
309        PrettyPrint::pretty_print(self.as_ref(), f)
310    }
311}
312
313impl<T: PrettyPrint> PrettyPrint for alloc::rc::Rc<T> {
314    fn render(&self) -> Document {
315        PrettyPrint::render(self.as_ref())
316    }
317    fn pretty_print(&self, f: &mut fmt::Formatter) -> fmt::Result {
318        PrettyPrint::pretty_print(self.as_ref(), f)
319    }
320}
321
322impl<T: PrettyPrint> PrettyPrint for alloc::sync::Arc<T> {
323    fn render(&self) -> Document {
324        PrettyPrint::render(self.as_ref())
325    }
326    fn pretty_print(&self, f: &mut fmt::Formatter) -> fmt::Result {
327        PrettyPrint::pretty_print(self.as_ref(), f)
328    }
329}
330
331impl<T: PrettyPrint> PrettyPrint for alloc::vec::Vec<T> {
332    fn render(&self) -> Document {
333        let single = self.iter().fold(Document::Empty, |acc, e| match acc {
334            Document::Empty => e.render(),
335            acc => acc + ',' + ' ' + e.render(),
336        });
337        let multi = self.iter().fold(Document::Empty, |acc, e| match acc {
338            Document::Empty => e.render(),
339            acc => acc + ',' + nl() + e.render(),
340        });
341        let single_line = '[' + single + ']';
342        let multi_line = '[' + indent(4, nl() + multi) + nl() + ']';
343        single_line | multi_line
344    }
345}
346
347impl<T: PrettyPrint> PrettyPrint for alloc::collections::BTreeSet<T> {
348    fn render(&self) -> Document {
349        let single = self.iter().fold(Document::Empty, |acc, e| match acc {
350            Document::Empty => e.render(),
351            acc => acc + ',' + ' ' + e.render(),
352        });
353        let multi = self.iter().fold(Document::Empty, |acc, e| match acc {
354            Document::Empty => e.render(),
355            acc => acc + ',' + nl() + e.render(),
356        });
357        let single_line = '{' + single + '}';
358        let multi_line = '{' + indent(4, nl() + multi) + nl() + '}';
359        single_line | multi_line
360    }
361}
362
363impl<K: PrettyPrint, V: PrettyPrint> PrettyPrint for alloc::collections::BTreeMap<K, V> {
364    fn render(&self) -> Document {
365        let single = self.iter().fold(Document::Empty, |acc, (k, v)| match acc {
366            Document::Empty => k.render() + " => " + v.render(),
367            acc => acc + ',' + ' ' + k.render() + " => " + v.render(),
368        });
369        let multi = self.iter().fold(Document::Empty, |acc, (k, v)| match acc {
370            Document::Empty => k.render() + " => " + v.render(),
371            acc => acc + ',' + nl() + k.render() + " => " + v.render(),
372        });
373        let single_line = '{' + single + '}';
374        let multi_line = '{' + indent(4, nl() + multi) + nl() + '}';
375        single_line | multi_line
376    }
377}
378
379struct Prettier<'a, P: ?Sized + PrettyPrint>(&'a P);
380
381impl<'a, P: ?Sized + PrettyPrint> fmt::Display for Prettier<'a, P> {
382    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
383        self.0.pretty_print(f)
384    }
385}