Skip to main content

pliron/
printable.rs

1//! IR objects that are to be printed must implement [Printable].
2
3use std::{
4    any::Any,
5    cell::{Ref, RefCell, RefMut},
6    fmt::{self, Display},
7    rc::Rc,
8};
9
10use rustc_hash::FxHashMap;
11
12use crate::{common_traits::RcShare, context::Context, identifier::Identifier};
13
14struct StateInner {
15    // Number of spaces per indentation
16    indent_width: u16,
17    // Current indentation
18    cur_indent: u16,
19    // Aribtrary state data that different printers may want to use.
20    aux_data: FxHashMap<Identifier, Box<dyn Any>>,
21}
22
23impl Default for StateInner {
24    fn default() -> Self {
25        Self {
26            indent_width: 2,
27            cur_indent: 0,
28            aux_data: FxHashMap::default(),
29        }
30    }
31}
32
33/// A light weight reference counted wrapper around a state for [Printable].
34#[derive(Default)]
35pub struct State(Rc<RefCell<StateInner>>);
36
37impl RcShare for State {
38    fn share(&self) -> Self {
39        State(Rc::clone(&self.0))
40    }
41}
42
43impl State {
44    /// Number of spaces per indentation
45    pub fn indent_width(&self) -> u16 {
46        self.0.as_ref().borrow().indent_width
47    }
48
49    /// Set the current indentation width
50    pub fn set_indent_width(&self, indent_width: u16) {
51        self.0.as_ref().borrow_mut().indent_width = indent_width;
52    }
53
54    /// What's the indentation we're at right now?
55    pub fn current_indent(&self) -> u16 {
56        self.0.as_ref().borrow().cur_indent
57    }
58
59    /// Increase the current indentation by [Self::indent_width]
60    pub fn push_indent(&self) {
61        let mut inner = self.0.as_ref().borrow_mut();
62        inner.cur_indent += inner.indent_width;
63    }
64
65    /// Decrease the current indentation by [Self::indent_width].
66    pub fn pop_indent(&self) {
67        let mut inner = self.0.as_ref().borrow_mut();
68        inner.cur_indent -= inner.indent_width;
69    }
70
71    /// Get a reference to the aux data table. The returned [Ref] is borrowed
72    /// from the entire [State] object, so release it at the earliest.
73    pub fn aux_data_ref(&self) -> Ref<'_, FxHashMap<Identifier, Box<dyn Any>>> {
74        Ref::map(self.0.borrow(), |inner| &inner.aux_data)
75    }
76
77    /// Get a mutable reference to the aux data table. The returned [RefMut] is borrowed
78    /// from the entire [State] object, so release it at the earliest.
79    pub fn aux_data_mut(&self) -> RefMut<'_, FxHashMap<Identifier, Box<dyn Any>>> {
80        RefMut::map(self.0.borrow_mut(), |inner| &mut inner.aux_data)
81    }
82}
83
84/// All statements in the block are indented during [fmt](Printable::fmt).
85/// Simply wraps the block with [State::push_indent] and [State::pop_indent].
86///
87/// See [Printable] for example usage.
88#[macro_export]
89macro_rules! indented_block {
90    ($state:ident, { $($tt:tt)* }) => {
91        $state.push_indent();
92        $($tt)*
93        $state.pop_indent();
94    }
95}
96
97/// An object that implements [Display].
98struct Displayable<'t, 'c, T: Printable + ?Sized> {
99    t: &'t T,
100    ctx: &'c Context,
101    state: State,
102}
103
104impl<T: Printable + ?Sized> Display for Displayable<'_, '_, T> {
105    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106        self.t.fmt(self.ctx, &self.state, f)
107    }
108}
109
110/// Easy printing of IR objects.
111///
112/// [disp](Self::disp) calls [print](Self::print) with a default [State],
113/// but otherwise, are both equivalent.
114///
115/// Example:
116/// ```
117/// use pliron::{context::Context, printable::{State, Printable, ListSeparator}};
118/// use std::fmt;
119/// struct S {
120///     i: i64,
121/// }
122/// impl Printable for S {
123///     fn fmt(&self, _ctx: &Context, _state: &State, f: &mut fmt::Formatter<'_>)
124///     -> fmt::Result
125///     {
126///         write!(f, "{}", self.i)
127///     }
128/// }
129///
130/// let ctx = Context::new();
131/// assert!(S { i: 108 }.disp(&ctx).to_string() == "108");
132/// let state = State::default();
133/// assert!(S { i: 0 }.print(&ctx, &state).to_string() == "0");
134/// let svec = vec![ S { i: 8 }, S { i: 16 } ];
135/// use pliron::{indented_block, printable::indented_nl};
136/// indented_block!(state, {
137///     assert_eq!(format!("{}{}", indented_nl(&state), S { i: 108 }.print(&ctx, &state)), "\n  108");
138/// });
139/// ```
140pub trait Printable {
141    fn fmt(&self, ctx: &Context, state: &State, f: &mut fmt::Formatter<'_>) -> fmt::Result;
142
143    /// Get a [Display]'able object from the given [Context] and default [State].
144    fn disp<'t, 'c>(&'t self, ctx: &'c Context) -> Box<dyn Display + 'c>
145    where
146        't: 'c,
147    {
148        self.print(ctx, &State::default())
149    }
150
151    /// Get a [Display]'able object from the given [Context] and [State].
152    fn print<'t, 'c>(&'t self, ctx: &'c Context, state: &State) -> Box<dyn Display + 'c>
153    where
154        't: 'c,
155    {
156        Box::new(Displayable {
157            t: self,
158            ctx,
159            state: state.share(),
160        })
161    }
162}
163/// Implement [Printable] for a type that already implements [Display].
164/// Example:
165/// ```
166///     struct MyType;
167///     impl std::fmt::Display for MyType {
168///         fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
169///             write!(f, "{}", self)
170///         }
171///     }
172///     pliron::impl_printable_for_display!(MyType);
173/// ```
174#[macro_export]
175macro_rules! impl_printable_for_display {
176    ($ty_name:ty) => {
177        impl $crate::printable::Printable for $ty_name {
178            fn fmt(
179                &self,
180                _ctx: &pliron::context::Context,
181                _state: &pliron::printable::State,
182                f: &mut std::fmt::Formatter<'_>,
183            ) -> std::fmt::Result {
184                write!(f, "{}", self)
185            }
186        }
187    };
188}
189
190impl_printable_for_display!(&str);
191impl_printable_for_display!(String);
192impl_printable_for_display!(usize);
193impl_printable_for_display!(u64);
194impl_printable_for_display!(u32);
195impl_printable_for_display!(i64);
196impl_printable_for_display!(i32);
197impl_printable_for_display!(bool);
198impl_printable_for_display!(char);
199
200impl<T: Printable + ?Sized> Printable for &T {
201    fn fmt(&self, ctx: &Context, state: &State, f: &mut fmt::Formatter<'_>) -> fmt::Result {
202        (*self).fmt(ctx, state, f)
203    }
204}
205
206#[derive(Clone, Copy)]
207/// When printing lists, how must they be separated
208pub enum ListSeparator {
209    /// No separator
210    None,
211    /// Newline
212    Newline,
213    /// Character followed by a newline.
214    CharNewline(char),
215    /// Single character
216    Char(char),
217    /// Single character followed by a space
218    CharSpace(char),
219}
220
221impl Printable for ListSeparator {
222    fn fmt(&self, _ctx: &Context, state: &State, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223        match self {
224            ListSeparator::None => Ok(()),
225            ListSeparator::Newline => fmt_indented_newline(state, f),
226            ListSeparator::CharNewline(c) => {
227                write!(f, "{c}")?;
228                fmt_indented_newline(state, f)
229            }
230            ListSeparator::Char(c) => write!(f, "{c}"),
231            ListSeparator::CharSpace(c) => write!(f, "{c} "),
232        }
233    }
234}
235
236/// Iterate over [Item](Iterator::Item)s in an [Iterator] and print them.
237pub fn fmt_iter<I>(
238    mut iter: I,
239    ctx: &Context,
240    state: &State,
241    sep: ListSeparator,
242    f: &mut fmt::Formatter<'_>,
243) -> fmt::Result
244where
245    I: Iterator,
246    I::Item: Printable,
247{
248    if let Some(first) = iter.next() {
249        first.fmt(ctx, state, f)?;
250    }
251    for item in iter {
252        sep.fmt(ctx, state, f)?;
253        item.fmt(ctx, state, f)?;
254    }
255    Ok(())
256}
257
258/// Print a new line followed by indentation as per current state.
259pub fn fmt_indented_newline(state: &State, f: &mut fmt::Formatter<'_>) -> fmt::Result {
260    let align = state.current_indent().into();
261    write!(f, "\n{:>align$}", "")?;
262    Ok(())
263}
264
265struct IndentedNewliner {
266    state: State,
267}
268
269impl Display for IndentedNewliner {
270    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
271        fmt_indented_newline(&self.state, f)
272    }
273}
274
275/// Print a new line followed by indentation as per current state.
276pub fn indented_nl(state: &State) -> impl Display {
277    IndentedNewliner {
278        state: state.share(),
279    }
280}