From 8d1107cb577f57229504cc2d9946776250ef2c9a Mon Sep 17 00:00:00 2001 From: John Date: Mon, 16 Oct 2023 22:51:07 -0500 Subject: [PATCH] Conlang: Add a pretty printer for the AST --- libconlang/src/lib.rs | 258 ++++++++++++++++++++++++++++++++++++++++++ readme.md | 2 +- 2 files changed, 259 insertions(+), 1 deletion(-) diff --git a/libconlang/src/lib.rs b/libconlang/src/lib.rs index 294a6f0..7fd54ac 100644 --- a/libconlang/src/lib.rs +++ b/libconlang/src/lib.rs @@ -1931,6 +1931,264 @@ pub mod parser { } } +pub mod pretty_printer { + use super::ast::preamble::*; + use std::{ + fmt::Display, + io::{stdout, Result as IOResult, StdoutLock, Write}, + }; + pub trait PrettyPrintable { + fn print(&self); + fn write(&self, into: impl Write) -> IOResult<()>; + } + impl PrettyPrintable for Start { + fn print(&self) { + let _ = self.walk(&mut Printer::default()); + } + fn write(&self, into: impl Write) -> IOResult<()> { + self.walk(&mut Printer::from(into)) + } + } + + #[derive(Debug)] + pub struct Printer { + level: u32, + writer: W, + } + impl<'t> Default for Printer> { + fn default() -> Self { + Self { level: 0, writer: stdout().lock() } + } + } + impl From for Printer { + fn from(writer: W) -> Self { + Self { level: 0, writer } + } + } + impl Printer { + fn pad(&mut self) -> IOResult<&mut Self> { + for _ in 0..self.level * 4 { + write!(self.writer, " ")?; + } + Ok(self) + } + fn newline(&mut self) -> IOResult<&mut Self> { + writeln!(self.writer)?; + self.pad() + } + fn put(&mut self, d: impl Display) -> IOResult<&mut Self> { + write!(self.writer, "{d} ")?; + Ok(self) + } + /// Increase the indentation level by 1 + fn indent(&mut self) -> &mut Self { + self.level += 1; + self + } + fn dedent(&mut self) -> &mut Self { + self.level -= 1; + self + } + } + macro visit_math($self:expr, $expr:expr) {{ + $expr.0.walk($self)?; + for (op, target) in &$expr.1 { + op.walk($self)?; + target.walk($self)?; + } + Ok(()) + }} + impl Visitor> for Printer { + fn visit_ignore(&mut self, expr: &math::Ignore) -> IOResult<()> { + expr.0.walk(self)?; + for (op, target) in &expr.1 { + op.walk(self)?; + target.walk(self.newline()?)?; + } + Ok(()) + } + fn visit_assign(&mut self, expr: &math::Assign) -> IOResult<()> { + visit_math!(self, expr) + } + fn visit_compare(&mut self, expr: &math::Compare) -> IOResult<()> { + visit_math!(self, expr) + } + fn visit_logic(&mut self, expr: &math::Logic) -> IOResult<()> { + visit_math!(self, expr) + } + fn visit_bitwise(&mut self, expr: &math::Bitwise) -> IOResult<()> { + visit_math!(self, expr) + } + fn visit_shift(&mut self, expr: &math::Shift) -> IOResult<()> { + visit_math!(self, expr) + } + fn visit_term(&mut self, expr: &math::Term) -> IOResult<()> { + visit_math!(self, expr) + } + fn visit_factor(&mut self, expr: &math::Factor) -> IOResult<()> { + visit_math!(self, expr) + } + fn visit_unary(&mut self, expr: &math::Unary) -> IOResult<()> { + for op in &expr.0 { + op.walk(self)?; + } + expr.1.walk(self) + } + fn visit_ignore_op(&mut self, op: &operator::Ignore) -> IOResult<()> { + self.put(match op { + operator::Ignore::Ignore => "\x08;", + }) + .map(drop) + } + fn visit_compare_op(&mut self, op: &operator::Compare) -> IOResult<()> { + self.put(match op { + operator::Compare::Less => "<", + operator::Compare::LessEq => "<=", + operator::Compare::Equal => "==", + operator::Compare::NotEq => "!=", + operator::Compare::GreaterEq => ">=", + operator::Compare::Greater => ">", + }) + .map(drop) + } + fn visit_assign_op(&mut self, op: &operator::Assign) -> IOResult<()> { + self.put(match op { + operator::Assign::Assign => "=", + operator::Assign::AddAssign => "+=", + operator::Assign::SubAssign => "-=", + operator::Assign::MulAssign => "*=", + operator::Assign::DivAssign => "/=", + operator::Assign::BitAndAssign => "&=", + operator::Assign::BitOrAssign => "|=", + operator::Assign::BitXorAssign => "^=", + operator::Assign::ShlAssign => "<<=", + operator::Assign::ShrAssign => ">>=", + }) + .map(drop) + } + fn visit_logic_op(&mut self, op: &operator::Logic) -> IOResult<()> { + self.put(match op { + operator::Logic::LogAnd => "&&", + operator::Logic::LogOr => "||", + operator::Logic::LogXor => "^^", + }) + .map(drop) + } + fn visit_bitwise_op(&mut self, op: &operator::Bitwise) -> IOResult<()> { + self.put(match op { + operator::Bitwise::BitAnd => "&", + operator::Bitwise::BitOr => "|", + operator::Bitwise::BitXor => "^", + }) + .map(drop) + } + fn visit_shift_op(&mut self, op: &operator::Shift) -> IOResult<()> { + self.put(match op { + operator::Shift::Lsh => "<<", + operator::Shift::Rsh => ">>", + }) + .map(drop) + } + fn visit_term_op(&mut self, op: &operator::Term) -> IOResult<()> { + self.put(match op { + operator::Term::Add => "+", + operator::Term::Sub => "-", + }) + .map(drop) + } + fn visit_factor_op(&mut self, op: &operator::Factor) -> IOResult<()> { + self.put(match op { + operator::Factor::Mul => "*", + operator::Factor::Div => "/", + operator::Factor::Rem => "%", + }) + .map(drop) + } + fn visit_unary_op(&mut self, op: &operator::Unary) -> IOResult<()> { + self.put(match op { + operator::Unary::Deref => "*", + operator::Unary::Ref => "&", + operator::Unary::Neg => "-", + operator::Unary::Not => "!", + operator::Unary::At => "@", + operator::Unary::Hash => "#", + operator::Unary::Tilde => "~", + }) + .map(drop) + } + + fn visit_if(&mut self, expr: &control::If) -> IOResult<()> { + expr.cond.walk(self.put("if")?)?; + expr.body.walk(self)?; + if let Some(e) = &expr.else_ { + e.walk(self)? + } + Ok(()) + } + fn visit_while(&mut self, expr: &control::While) -> IOResult<()> { + expr.cond.walk(self.put("while")?)?; + expr.body.walk(self)?; + if let Some(e) = &expr.else_ { + e.walk(self)? + } + Ok(()) + } + fn visit_for(&mut self, expr: &control::For) -> IOResult<()> { + expr.var.walk(self.put("for")?)?; + expr.iter.walk(self.put("in")?)?; + expr.body.walk(self)?; + self.visit_block(&expr.body)?; + if let Some(e) = &expr.else_ { + e.walk(self)? + } + Ok(()) + } + fn visit_else(&mut self, expr: &control::Else) -> IOResult<()> { + expr.block.walk(self.put("else")?) + } + fn visit_continue(&mut self, _expr: &control::Continue) -> IOResult<()> { + self.put("continue").map(drop) + } + fn visit_break(&mut self, expr: &control::Break) -> IOResult<()> { + expr.expr.walk(self.put("break")?) + } + fn visit_return(&mut self, expr: &control::Return) -> IOResult<()> { + expr.expr.walk(self.put("return")?) + } + + fn visit_identifier(&mut self, ident: &Identifier) -> IOResult<()> { + self.put(&ident.0).map(drop) + } + fn visit_string_literal(&mut self, string: &str) -> IOResult<()> { + self.put("\"")?.put(string)?.put("\"").map(drop) + } + fn visit_char_literal(&mut self, char: &char) -> IOResult<()> { + self.put(char).map(drop) + } + fn visit_bool_literal(&mut self, bool: &bool) -> IOResult<()> { + self.put(bool).map(drop) + } + fn visit_float_literal(&mut self, float: &literal::Float) -> IOResult<()> { + self.put(float.sign)? + .put(float.exponent)? + .put(float.mantissa) + .map(drop) + } + fn visit_int_literal(&mut self, int: &u128) -> IOResult<()> { + self.put(int).map(drop) + } + + fn visit_block(&mut self, expr: &expression::Block) -> IOResult<()> { + self.put('{')?.indent().newline()?.visit_expr(&expr.expr)?; + self.dedent().newline()?.put('}').map(drop) + } + + fn visit_group(&mut self, expr: &expression::Group) -> IOResult<()> { + self.put('(')?; + self.visit_expr(&expr.expr)?; + self.put(')').map(drop) + } + } } pub mod interpreter { diff --git a/readme.md b/readme.md index ab69a6c..2ccd41e 100644 --- a/readme.md +++ b/readme.md @@ -12,7 +12,7 @@ Friday each month. - [x] Write parser for AST - [ ] Create tests for parser (and AST) - [ ] Parse `dummy.cl` into a valid AST -- [ ] Pretty printer, for debugging +- [x] Pretty printer, for debugging - [ ] Create minimal statement grammar - [ ] Variable definition statements - [ ] Function definition statements