From 9f9a21b4c3f8707efbe6fe1385d0d24a34d77650 Mon Sep 17 00:00:00 2001 From: John Date: Wed, 27 Mar 2024 01:24:28 -0500 Subject: [PATCH] cl-repl: Add example that prints the AST in a more friendly way than `Debug` but in a more verbose way than `Display` --- cl-repl/examples/yaml.rs | 653 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 653 insertions(+) create mode 100644 cl-repl/examples/yaml.rs diff --git a/cl-repl/examples/yaml.rs b/cl-repl/examples/yaml.rs new file mode 100644 index 0000000..7625393 --- /dev/null +++ b/cl-repl/examples/yaml.rs @@ -0,0 +1,653 @@ +//! Pretty prints a conlang AST in yaml + +use cl_lexer::Lexer; +use cl_parser::Parser; +use cl_repl::repline::{error::Error as RlError, Repline}; +use std::error::Error; + +fn main() -> Result<(), Box> { + let mut rl = Repline::new("\x1b[33m", "cl>", "? >"); + loop { + let line = match rl.read() { + Err(RlError::CtrlC(_)) => break, + Err(RlError::CtrlD(line)) => { + rl.deny(); + line + } + Ok(line) => line, + Err(e) => Err(e)?, + }; + + let mut parser = Parser::new(Lexer::new(&line)); + let code = match parser.stmt() { + Ok(code) => { + rl.accept(); + code + } + Err(e) => { + print!("\x1b[40G\x1bJ\x1b[91m{e}\x1b[0m"); + continue; + } + }; + print!("\x1b[G\x1b[J\x1b[A"); + Yamler::new().yaml(&code); + println!(); + } + Ok(()) +} + +pub use yamler::Yamler; +pub mod yamler { + use crate::yamlify::Yamlify; + use std::{ + fmt::Display, + io::Write, + ops::{Deref, DerefMut}, + }; + #[derive(Debug, Default)] + pub struct Yamler { + depth: usize, + } + + impl Yamler { + pub fn new() -> Self { + Self::default() + } + + pub fn indent(&mut self) -> Section { + Section::new(self) + } + + /// Prints a [Yamlify] value + #[inline] + pub fn yaml(&mut self, yaml: &T) -> &mut Self { + yaml.yaml(self); + self + } + + fn increase(&mut self) { + self.depth += 1; + } + + fn decrease(&mut self) { + self.depth -= 1; + } + + fn print_indentation(&self, writer: &mut impl Write) { + for _ in 0..self.depth { + let _ = write!(writer, " "); + } + } + + /// Prints a section header and increases indentation + pub fn key(&mut self, name: impl Display) -> Section { + println!(); + self.print_indentation(&mut std::io::stdout().lock()); + print!("- {name}:"); + self.indent() + } + + /// Prints a yaml key value pair: `- name: "value"` + pub fn pair(&mut self, name: D, value: T) -> &mut Self { + self.key(name).yaml(&value); + self + } + + /// Prints a yaml scalar value: `"name"`` + pub fn value(&mut self, value: D) -> &mut Self { + print!(" {value}"); + self + } + + pub fn list(&mut self, list: &[D]) -> &mut Self { + for (idx, value) in list.iter().enumerate() { + self.pair(idx, value); + } + self + } + } + + /// Tracks the start and end of an indented block (a "section") + pub struct Section<'y> { + yamler: &'y mut Yamler, + } + + impl<'y> Section<'y> { + pub fn new(yamler: &'y mut Yamler) -> Self { + yamler.increase(); + Self { yamler } + } + } + + impl<'y> Deref for Section<'y> { + type Target = Yamler; + fn deref(&self) -> &Self::Target { + self.yamler + } + } + impl<'y> DerefMut for Section<'y> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.yamler + } + } + + impl<'y> Drop for Section<'y> { + fn drop(&mut self) { + let Self { yamler } = self; + yamler.decrease(); + } + } +} + +pub mod yamlify { + use super::yamler::Yamler; + use cl_ast::*; + + pub trait Yamlify { + fn yaml(&self, y: &mut Yamler); + } + + impl Yamlify for File { + fn yaml(&self, y: &mut Yamler) { + let File { items } = self; + y.key("File").yaml(items); + } + } + impl Yamlify for Visibility { + fn yaml(&self, y: &mut Yamler) { + if let Visibility::Public = self { + y.pair("vis", "pub"); + } + } + } + impl Yamlify for Mutability { + fn yaml(&self, y: &mut Yamler) { + if let Mutability::Mut = self { + y.pair("mut", true); + } + } + } + + impl Yamlify for Attrs { + fn yaml(&self, y: &mut Yamler) { + let Self { meta } = self; + y.key("Attrs").yaml(meta); + } + } + impl Yamlify for Meta { + fn yaml(&self, y: &mut Yamler) { + let Self { name, kind } = self; + y.key("Meta").pair("name", name).yaml(kind); + } + } + impl Yamlify for MetaKind { + fn yaml(&self, y: &mut Yamler) { + match self { + MetaKind::Plain => y, + MetaKind::Equals(value) => y.pair("equals", value), + MetaKind::Func(args) => y.pair("args", args), + }; + } + } + + impl Yamlify for Item { + fn yaml(&self, y: &mut Yamler) { + let Self { extents: _, attrs, vis, kind } = self; + y.key("Item").yaml(attrs).yaml(vis).yaml(kind); + } + } + impl Yamlify for ItemKind { + fn yaml(&self, y: &mut Yamler) { + match self { + ItemKind::Alias(f) => y.yaml(f), + ItemKind::Const(f) => y.yaml(f), + ItemKind::Static(f) => y.yaml(f), + ItemKind::Module(f) => y.yaml(f), + ItemKind::Function(f) => y.yaml(f), + ItemKind::Struct(f) => y.yaml(f), + ItemKind::Enum(f) => y.yaml(f), + ItemKind::Impl(f) => y.yaml(f), + }; + } + } + impl Yamlify for Alias { + fn yaml(&self, y: &mut Yamler) { + let Self { to, from } = self; + y.key("Alias").pair("to", to).pair("from", from); + } + } + impl Yamlify for Const { + fn yaml(&self, y: &mut Yamler) { + let Self { name, ty, init } = self; + y.key("Const") + .pair("name", name) + .pair("ty", ty) + .pair("init", init); + } + } + impl Yamlify for Static { + fn yaml(&self, y: &mut Yamler) { + let Self { mutable, name, ty, init } = self; + y.key(name).yaml(mutable).pair("ty", ty).pair("init", init); + } + } + impl Yamlify for Module { + fn yaml(&self, y: &mut Yamler) { + let Self { name, kind } = self; + y.key("Module").pair("name", name).yaml(kind); + } + } + impl Yamlify for ModuleKind { + fn yaml(&self, y: &mut Yamler) { + match self { + ModuleKind::Inline(f) => y.yaml(f), + ModuleKind::Outline => y, + }; + } + } + impl Yamlify for Function { + fn yaml(&self, y: &mut Yamler) { + let Self { name, args, body, rety } = self; + y.key("Function") + .pair("name", name) + .pair("args", args) + .pair("body", body) + .pair("rety", rety); + } + } + impl Yamlify for Struct { + fn yaml(&self, y: &mut Yamler) { + let Self { name, kind } = self; + y.key("Struct").pair("name", name).yaml(kind); + } + } + impl Yamlify for StructKind { + fn yaml(&self, y: &mut Yamler) { + match self { + StructKind::Empty => y, + StructKind::Tuple(k) => y.yaml(k), + StructKind::Struct(k) => y.yaml(k), + }; + } + } + impl Yamlify for StructMember { + fn yaml(&self, y: &mut Yamler) { + let Self { vis, name, ty } = self; + y.key("StructMember").yaml(vis).pair("name", name).yaml(ty); + } + } + impl Yamlify for Enum { + fn yaml(&self, y: &mut Yamler) { + let Self { name, kind } = self; + y.key("Enum").pair("name", name).yaml(kind); + } + } + impl Yamlify for EnumKind { + fn yaml(&self, y: &mut Yamler) { + match self { + EnumKind::NoVariants => y, + EnumKind::Variants(v) => y.yaml(v), + }; + } + } + impl Yamlify for Variant { + fn yaml(&self, y: &mut Yamler) { + let Self { name, kind } = self; + y.key("Variant").pair("name", name).yaml(kind); + } + } + impl Yamlify for VariantKind { + fn yaml(&self, y: &mut Yamler) { + match self { + VariantKind::Plain => y, + VariantKind::CLike(v) => y.yaml(v), + VariantKind::Tuple(v) => y.yaml(v), + VariantKind::Struct(v) => y.yaml(v), + }; + } + } + impl Yamlify for Impl { + fn yaml(&self, y: &mut Yamler) { + let Self { target, body } = self; + y.key("Impl").pair("target", target).pair("body", body); + } + } + impl Yamlify for Block { + fn yaml(&self, y: &mut Yamler) { + let Self { stmts } = self; + y.key("Block").yaml(stmts); + } + } + impl Yamlify for Stmt { + fn yaml(&self, y: &mut Yamler) { + let Self { extents: _, kind, semi } = self; + y.key("Stmt").yaml(kind).yaml(semi); + } + } + impl Yamlify for Semi { + fn yaml(&self, y: &mut Yamler) { + if let Semi::Terminated = self { + y.pair("terminated", true); + } + } + } + impl Yamlify for StmtKind { + fn yaml(&self, y: &mut Yamler) { + match self { + StmtKind::Empty => y, + StmtKind::Local(s) => y.yaml(s), + StmtKind::Item(s) => y.yaml(s), + StmtKind::Expr(s) => y.yaml(s), + }; + } + } + impl Yamlify for Let { + fn yaml(&self, y: &mut Yamler) { + let Self { mutable, name, ty, init } = self; + y.key("Let") + .pair("name", name) + .yaml(mutable) + .pair("ty", ty) + .pair("init", init); + } + } + impl Yamlify for Expr { + fn yaml(&self, y: &mut Yamler) { + let Self { extents: _, kind } = self; + y.yaml(kind); + } + } + impl Yamlify for ExprKind { + fn yaml(&self, y: &mut Yamler) { + match self { + ExprKind::Assign(k) => k.yaml(y), + ExprKind::Binary(k) => k.yaml(y), + ExprKind::Unary(k) => k.yaml(y), + ExprKind::Member(k) => k.yaml(y), + ExprKind::Call(k) => k.yaml(y), + ExprKind::Index(k) => k.yaml(y), + ExprKind::Path(k) => k.yaml(y), + ExprKind::Literal(k) => k.yaml(y), + ExprKind::Array(k) => k.yaml(y), + ExprKind::ArrayRep(k) => k.yaml(y), + ExprKind::AddrOf(k) => k.yaml(y), + ExprKind::Block(k) => k.yaml(y), + ExprKind::Empty => {} + ExprKind::Group(k) => k.yaml(y), + ExprKind::Tuple(k) => k.yaml(y), + ExprKind::While(k) => k.yaml(y), + ExprKind::If(k) => k.yaml(y), + ExprKind::For(k) => k.yaml(y), + ExprKind::Break(k) => k.yaml(y), + ExprKind::Return(k) => k.yaml(y), + ExprKind::Continue(k) => k.yaml(y), + } + } + } + impl Yamlify for Assign { + fn yaml(&self, y: &mut Yamler) { + let Self { head, op: _, tail } = self; + y.key("Assign").pair("head", head).pair("tail", tail); + } + } + impl Yamlify for Binary { + fn yaml(&self, y: &mut Yamler) { + let Self { head, tail } = self; + let mut y = y.key("Binary"); + y.pair("head", head); + for (op, expr) in tail { + y.key("tail").pair("op", op).pair("expr", expr); + } + } + } + impl Yamlify for BinaryKind { + fn yaml(&self, y: &mut Yamler) { + y.value(self); + } + } + impl Yamlify for Unary { + fn yaml(&self, y: &mut Yamler) { + let Self { ops, tail } = self; + let mut y = y.key("Unary"); + for op in ops { + y.pair("op", op); + } + y.pair("tail", tail); + } + } + impl Yamlify for UnaryKind { + fn yaml(&self, y: &mut Yamler) { + y.value(self); + } + } + impl Yamlify for Member { + fn yaml(&self, y: &mut Yamler) { + let Self { head, tail } = self; + y.key("Member").pair("head", head).pair("tail", tail); + } + } + impl Yamlify for Call { + fn yaml(&self, y: &mut Yamler) { + let Self { callee, args } = self; + y.key("Call").pair("callee", callee).pair("args", args); + } + } + impl Yamlify for Tuple { + fn yaml(&self, y: &mut Yamler) { + let Self { exprs } = self; + y.key("Tuple").list(exprs); + } + } + impl Yamlify for Index { + fn yaml(&self, y: &mut Yamler) { + let Self { head, indices } = self; + y.key("Index").pair("head", head).list(indices); + } + } + impl Yamlify for Indices { + fn yaml(&self, y: &mut Yamler) { + let Self { exprs } = self; + y.key("Indices").list(exprs); + } + } + impl Yamlify for Array { + fn yaml(&self, y: &mut Yamler) { + let Self { values } = self; + y.key("Array").list(values); + } + } + impl Yamlify for ArrayRep { + fn yaml(&self, y: &mut Yamler) { + let Self { value, repeat } = self; + y.key("ArrayRep") + .pair("value", value) + .pair("repeat", repeat); + } + } + impl Yamlify for AddrOf { + fn yaml(&self, y: &mut Yamler) { + let Self { count: _, mutable, expr } = self; + y.key("Addr").yaml(mutable).pair("expr", expr); + } + } + impl Yamlify for Group { + fn yaml(&self, y: &mut Yamler) { + let Self { expr } = self; + y.key("Group").yaml(expr); + } + } + impl Yamlify for While { + fn yaml(&self, y: &mut Yamler) { + let Self { cond, pass, fail } = self; + y.key("While") + .pair("cond", cond) + .pair("pass", pass) + .pair("fail", fail); + } + } + impl Yamlify for Else { + fn yaml(&self, y: &mut Yamler) { + let Self { body } = self; + y.key("Else").yaml(body); + } + } + impl Yamlify for If { + fn yaml(&self, y: &mut Yamler) { + let Self { cond, pass, fail } = self; + y.key("If").pair("cond", cond).pair("pass", pass).yaml(fail); + } + } + impl Yamlify for For { + fn yaml(&self, y: &mut Yamler) { + let Self { bind, cond, pass, fail } = self; + y.key("For") + .pair("bind", bind) + .pair("cond", cond) + .pair("pass", pass) + .yaml(fail); + } + } + impl Yamlify for Break { + fn yaml(&self, y: &mut Yamler) { + let Self { body } = self; + y.key("Break").yaml(body); + } + } + impl Yamlify for Return { + fn yaml(&self, y: &mut Yamler) { + let Self { body } = self; + y.key("Return").yaml(body); + } + } + impl Yamlify for Continue { + fn yaml(&self, y: &mut Yamler) { + y.key("Continue"); + } + } + impl Yamlify for Literal { + fn yaml(&self, y: &mut Yamler) { + y.value(format_args!("\"{self}\"")); + } + } + impl Yamlify for Identifier { + fn yaml(&self, y: &mut Yamler) { + let Self(name) = self; + y.value(name); + } + } + impl Yamlify for Param { + fn yaml(&self, y: &mut Yamler) { + let Self { mutability, name, ty } = self; + y.key("Param") + .yaml(mutability) + .pair("name", name) + .pair("ty", ty); + } + } + impl Yamlify for Ty { + fn yaml(&self, y: &mut Yamler) { + let Self { extents: _, kind } = self; + y.key("Ty").yaml(kind); + } + } + impl Yamlify for TyKind { + fn yaml(&self, y: &mut Yamler) { + match self { + TyKind::Never => y.value("Never"), + TyKind::Empty => y.value("Empty"), + TyKind::SelfTy => y.value("Self"), + TyKind::Path(t) => y.yaml(t), + TyKind::Tuple(t) => y.yaml(t), + TyKind::Ref(t) => y.yaml(t), + TyKind::Fn(t) => y.yaml(t), + }; + } + } + impl Yamlify for Path { + fn yaml(&self, y: &mut Yamler) { + let Self { absolute, parts } = self; + let mut y = y.key("Path"); + if *absolute { + y.pair("absolute", absolute); + } + for part in parts { + y.pair("part", part); + } + } + } + impl Yamlify for PathPart { + fn yaml(&self, y: &mut Yamler) { + match self { + PathPart::SuperKw => y.value("super"), + PathPart::SelfKw => y.value("self"), + PathPart::Ident(i) => y.yaml(i), + }; + } + } + impl Yamlify for TyTuple { + fn yaml(&self, y: &mut Yamler) { + let Self { types } = self; + let mut y = y.key("TyTuple"); + for ty in types { + y.yaml(ty); + } + } + } + impl Yamlify for TyRef { + fn yaml(&self, y: &mut Yamler) { + let Self { count, to } = self; + y.key("TyRef").pair("count", count).pair("to", to); + } + } + impl Yamlify for TyFn { + fn yaml(&self, y: &mut Yamler) { + let Self { args, rety } = self; + y.key("TyFn").pair("args", args).pair("rety", rety); + } + } + + impl Yamlify for Option { + fn yaml(&self, y: &mut Yamler) { + if let Some(v) = self { + y.yaml(v); + } else { + y.value(""); + } + } + } + impl Yamlify for Box { + fn yaml(&self, y: &mut Yamler) { + y.yaml(&**self); + } + } + impl Yamlify for Vec { + fn yaml(&self, y: &mut Yamler) { + for thing in self { + y.yaml(thing); + } + } + } + impl Yamlify for () { + fn yaml(&self, _y: &mut Yamler) {} + } + + impl Yamlify for &T { + fn yaml(&self, y: &mut Yamler) { + (*self).yaml(y) + } + } + + macro_rules! scalar { + ($($t:ty),*$(,)?) => { + $(impl Yamlify for $t { + fn yaml(&self, y: &mut Yamler) { + y.value(self); + } + })* + }; + } + + scalar! { + bool, char, u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, &str, String + } +}