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}