Skip to main content

yaml/
yaml.rs

1//! Pretty prints a conlang AST in yaml
2
3use cl_ast::Expr;
4use cl_lexer::Lexer;
5use cl_parser::Parser;
6use cl_structures::intern::interned::Symbol;
7use repline::{Response, error::Error as RlError};
8
9fn main() -> Result<(), RlError> {
10    repline::read_and("\x1b[33m", "y> ", " > ", |line| {
11        let mut parser = Parser::new(Lexer::new(Symbol::default(), line));
12        let code = parser.parse::<Expr>(0)?;
13
14        Yamler::new().yaml(&code);
15        println!();
16        Ok(Response::Accept)
17    })
18}
19
20pub use yamler::Yamler;
21pub mod yamler {
22    use crate::yamlify::Yamlify;
23    use std::{
24        io::Write,
25        ops::{Deref, DerefMut},
26    };
27    #[derive(Debug, Default)]
28    pub struct Yamler {
29        depth: usize,
30        needs_indent: bool,
31    }
32
33    impl Yamler {
34        pub fn new() -> Self {
35            Self::default()
36        }
37
38        pub fn indent(&mut self) -> Section<'_> {
39            Section::new(self)
40        }
41
42        /// Prints a [Yamlify] value
43        #[inline]
44        pub fn yaml<T: Yamlify>(&mut self, yaml: &T) -> &mut Self {
45            yaml.yaml(self);
46            self
47        }
48
49        fn newline(&mut self) -> &mut Self {
50            if !self.needs_indent {
51                println!();
52            }
53            self.needs_indent = true;
54            self
55        }
56
57        fn increase(&mut self) {
58            self.depth += 1;
59        }
60
61        fn decrease(&mut self) {
62            self.depth -= 1;
63        }
64
65        fn print_indentation(&mut self, writer: &mut impl Write) {
66            if !self.needs_indent {
67                return;
68            }
69            for _ in 0..self.depth {
70                let _ = write!(writer, "  ");
71            }
72            self.needs_indent = false
73        }
74
75        /// Prints a section header and increases indentation
76        pub fn key(&mut self, name: impl Yamlify) -> Section<'_> {
77            self.print_indentation(&mut std::io::stdout().lock());
78            name.yaml(self);
79            println!(":");
80            self.needs_indent = true;
81            self.newline().indent()
82        }
83
84        /// Prints a yaml key value pair: `- name: "value"`
85        pub fn pair<D: Yamlify, T: Yamlify>(&mut self, name: D, value: T) -> &mut Self {
86            self.print_indentation(&mut std::io::stdout().lock());
87            self.key(name).value(value);
88            self
89        }
90
91        /// Prints a yaml scalar value: `"name"``
92        pub fn value<D: Yamlify>(&mut self, value: D) -> &mut Self {
93            self.print_indentation(&mut std::io::stdout().lock());
94            value.yaml(self);
95            self.newline()
96        }
97
98        pub fn list<D: Yamlify>(&mut self, list: &[D]) -> &mut Self {
99            for value in list {
100                self.print_indentation(&mut std::io::stdout().lock());
101                print!("- ");
102                self.yaml(value).newline();
103            }
104            self
105        }
106    }
107
108    /// Tracks the start and end of an indented block (a "section")
109    pub struct Section<'y> {
110        yamler: &'y mut Yamler,
111    }
112
113    impl<'y> Section<'y> {
114        pub fn new(yamler: &'y mut Yamler) -> Self {
115            yamler.increase();
116            Self { yamler }
117        }
118    }
119
120    impl Deref for Section<'_> {
121        type Target = Yamler;
122        fn deref(&self) -> &Self::Target {
123            self.yamler
124        }
125    }
126    impl DerefMut for Section<'_> {
127        fn deref_mut(&mut self) -> &mut Self::Target {
128            self.yamler
129        }
130    }
131
132    impl Drop for Section<'_> {
133        fn drop(&mut self) {
134            let Self { yamler } = self;
135            yamler.decrease();
136        }
137    }
138}
139
140pub mod yamlify {
141    use super::yamler::Yamler;
142    use cl_ast::{
143        AstTypes,
144        types::{Literal, Path, Symbol},
145        *,
146    };
147    use cl_structures::span::Span;
148
149    pub trait Yamlify {
150        fn yaml(&self, y: &mut Yamler);
151    }
152
153    impl Yamlify for Expr {
154        fn yaml(&self, y: &mut Yamler) {
155            match self {
156                Self::Omitted => y.yaml(&"Omitted"),
157                Self::Id(path) => y.yaml(path),
158                Self::MetId(id) => y.yaml(id),
159                Self::Lit(lit) => y.yaml(lit),
160                Self::Use(item) => y.yaml(item),
161                Self::Bind(bind) => y.yaml(bind),
162                Self::Make(make) => y.yaml(make),
163                Self::Match(mtch) => y.yaml(mtch),
164                Self::Op(op, annos) => y.pair(op, annos),
165            };
166        }
167    }
168
169    impl Yamlify for Pat {
170        fn yaml(&self, y: &mut Yamler) {
171            match self {
172                Self::Ignore => y.yaml(&"_"),
173                Self::Never => y.yaml(&"!"),
174                Self::MetId(id) => y.pair("meta", id),
175                Self::Name(name) => y.pair("named", name),
176                Self::Value(body) => y.pair("constant", body),
177                Self::Op(op, pats) => y.pair(op, pats),
178            };
179        }
180    }
181
182    impl Yamlify for Bind {
183        fn yaml(&self, y: &mut Yamler) {
184            let Self(op, gens, pat, exprs) = self;
185            y.key(op)
186                .pair("generics", gens)
187                .pair("pattern", pat)
188                .pair("body", exprs);
189        }
190    }
191
192    impl Yamlify for Make {
193        fn yaml(&self, y: &mut Yamler) {
194            let Self(ty, arms) = self;
195            y.pair(ty, arms);
196        }
197    }
198
199    impl Yamlify for MakeArm {
200        fn yaml(&self, y: &mut Yamler) {
201            let Self(name, expr) = self;
202            y.pair(name, expr);
203        }
204    }
205
206    impl Yamlify for Match {
207        fn yaml(&self, y: &mut Yamler) {
208            let Self(scrutinee, arms) = self;
209            y.pair("scrutinee", scrutinee).pair("arms", arms);
210        }
211    }
212
213    impl Yamlify for MatchArm {
214        fn yaml(&self, y: &mut Yamler) {
215            let Self(pat, expr) = self;
216            y.pair("pat", pat).pair("expr", expr);
217        }
218    }
219
220    impl Yamlify for Use {
221        fn yaml(&self, y: &mut Yamler) {
222            match self {
223                Self::Glob => y.yaml(&"*"),
224                Self::Name(name) => y.yaml(name),
225                Self::Alias(from, to) => y.pair(from, to),
226                Self::Path(name, rest) => y.pair(name, rest),
227                Self::Tree(items) => y.yaml(items),
228            };
229        }
230    }
231
232    impl<T: Yamlify + AstNode, A: AstTypes> Yamlify for At<T, A>
233    where A::Annotation: Yamlify
234    {
235        fn yaml(&self, y: &mut Yamler) {
236            let Self(t, _) = self;
237            y.yaml(t);
238        }
239    }
240
241    impl<T: Yamlify> Yamlify for Option<T> {
242        fn yaml(&self, y: &mut Yamler) {
243            if let Some(v) = self {
244                y.yaml(v);
245            } else {
246                y.yaml(&"");
247            }
248        }
249    }
250    impl<T: Yamlify> Yamlify for Box<T> {
251        fn yaml(&self, y: &mut Yamler) {
252            y.yaml(&**self);
253        }
254    }
255    impl<T: Yamlify> Yamlify for Vec<T> {
256        fn yaml(&self, y: &mut Yamler) {
257            y.list(self);
258        }
259    }
260    impl Yamlify for () {
261        fn yaml(&self, _y: &mut Yamler) {}
262    }
263
264    impl<T: Yamlify> Yamlify for &T {
265        fn yaml(&self, y: &mut Yamler) {
266            (*self).yaml(y)
267        }
268    }
269
270    macro_rules! scalar {
271        ($($t:ty),*$(,)?) => {
272            $(impl Yamlify for $t {
273                fn yaml(&self, _y: &mut Yamler) {
274                    print!("{self}");
275                }
276            })*
277        };
278    }
279
280    macro_rules! debug_scalar {
281        ($($t:ty),*$(,)?) => {
282            $(impl Yamlify for $t {
283                fn yaml(&self, _y: &mut Yamler) {
284                    print!("{self:?}");
285                }
286            })*
287        };
288    }
289
290    scalar! {
291        bool, char, u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, str, &str, String,
292        Symbol, Path, Literal
293    }
294
295    debug_scalar!(Op, BindOp, PatOp, Span);
296}