gramma/ast/
print.rs

1use core::fmt::{self, Debug, Formatter};
2
3use either::Either;
4
5use crate::utils::DebugFn;
6
7use super::Rule;
8
9pub struct PrintContext<'src> {
10    src: &'src str,
11    debug: bool,
12}
13
14enum IterSpecialCase<I: Iterator> {
15    Zero,
16    One(I::Item),
17    Many(I),
18}
19
20fn iter_special_case<T>(
21    iter: impl IntoIterator<Item = T>,
22) -> IterSpecialCase<impl Iterator<Item = T>> {
23    let mut iter = iter.into_iter();
24    match iter.size_hint() {
25        (_, Some(0)) => IterSpecialCase::Zero,
26        (_, Some(1)) => match iter.next() {
27            Some(item) => IterSpecialCase::One(item),
28            None => IterSpecialCase::Zero,
29        },
30        (2, _) => IterSpecialCase::Many(Either::Left(iter)),
31        _ => {
32            let Some(first) = iter.next() else {
33                return IterSpecialCase::Zero;
34            };
35
36            let Some(second) = iter.next() else {
37                return IterSpecialCase::One(first);
38            };
39
40            IterSpecialCase::Many(Either::Right([first, second].into_iter().chain(iter)))
41        }
42    }
43}
44
45impl<'src> PrintContext<'src> {
46    pub fn src(&self) -> &'src str {
47        self.src
48    }
49
50    pub fn new(src: &'src str) -> Self {
51        Self { src, debug: false }
52    }
53
54    pub fn debuggable<'lt, R: Rule + ?Sized>(&'lt self, ast: &'lt R) -> impl Debug + 'lt {
55        DebugFn(move |f| ast.print_tree(self, f))
56    }
57
58    pub fn filter_ignored<'short, 'item, R: Rule + ?Sized>(
59        &'short self,
60        items: impl IntoIterator<Item = &'item R> + 'short,
61    ) -> impl Iterator<Item = &'item R> + 'short {
62        items
63            .into_iter()
64            .filter(|item| match item.print_visibility(self) {
65                PrintVisibility::Never => false,
66                PrintVisibility::DebugOnly => self.debug,
67                PrintVisibility::Always => true,
68            })
69    }
70
71    pub fn fold_printable<'item, T>(
72        &self,
73        items: impl IntoIterator<Item = &'item dyn Rule>,
74        init: T,
75        mut f: impl FnMut(T, &dyn Debug) -> T,
76    ) -> T {
77        items
78            .into_iter()
79            .fold(init, move |acc, ast| f(acc, &self.debuggable(ast)))
80    }
81
82    pub fn debug_tuple<'item>(
83        &self,
84        name: &str,
85        f: &mut Formatter,
86        items: impl IntoIterator<Item = &'item dyn Rule>,
87    ) -> fmt::Result {
88        match iter_special_case(self.filter_ignored(items)) {
89            IterSpecialCase::Zero => f.write_str("()"),
90            IterSpecialCase::One(item) => item.print_tree(self, f),
91            IterSpecialCase::Many(items) => self
92                .fold_printable(items, &mut f.debug_tuple(name), |d, item| d.field(item))
93                .finish(),
94        }
95    }
96
97    pub fn debug_list<'item>(
98        &self,
99        f: &mut Formatter,
100        items: impl IntoIterator<Item = &'item dyn Rule>,
101    ) -> fmt::Result {
102        match iter_special_case(self.filter_ignored(items)) {
103            IterSpecialCase::Zero => f.write_str("[]"),
104            IterSpecialCase::One(item) => {
105                f.write_str("[")?;
106                item.print_tree(self, f)?;
107                f.write_str("]")
108            }
109            IterSpecialCase::Many(items) => self
110                .fold_printable(items, &mut f.debug_list(), |d, item| d.entry(item))
111                .finish(),
112        }
113    }
114
115    pub fn debug_rule<'item>(
116        &self,
117        f: &mut Formatter,
118        items: impl IntoIterator<Item = &'item dyn Rule>,
119    ) -> fmt::Result {
120        match iter_special_case(self.filter_ignored(items)) {
121            IterSpecialCase::Zero => f.write_str("{}"),
122            IterSpecialCase::One(item) => item.print_tree(self, f),
123            IterSpecialCase::Many(items) => self
124                .fold_printable(items, &mut f.debug_set(), |d, item| d.entry(item))
125                .finish(),
126        }
127    }
128
129    pub fn is_debug(&self) -> bool {
130        self.debug
131    }
132
133    pub fn set_debug(&mut self, debug: bool) -> &mut Self {
134        self.debug = debug;
135        self
136    }
137}
138
139#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
140pub enum PrintVisibility {
141    Never,
142    DebugOnly,
143    #[default]
144    Always,
145}
146
147impl PrintVisibility {
148    pub fn should_print(self, cx: &PrintContext) -> bool {
149        match self {
150            PrintVisibility::Never => false,
151            PrintVisibility::DebugOnly => cx.is_debug(),
152            PrintVisibility::Always => true,
153        }
154    }
155}