diff --git a/src/syntax.rs b/src/syntax.rs index 740e3ec..9d978bf 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -2,6 +2,7 @@ use codespan_reporting::{diagnostic::Diagnostic, files::SimpleFiles}; use lalrpop_util::lalrpop_mod; use logos::Logos; +pub mod ast; mod location; mod simplify; mod tokens; @@ -10,7 +11,7 @@ lalrpop_mod!( parser, "/syntax/parser.rs" ); -pub mod ast; +mod pretty; mod validate; pub use crate::syntax::ast::*; diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index 7bc45d6..d5acea3 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -1,59 +1,18 @@ use crate::syntax::Location; -use pretty::{DocAllocator, DocBuilder, Pretty}; -static BINARY_OPERATORS: &[&str] = &["+", "-", "*", "/"]; +pub static BINARY_OPERATORS: &[&str] = &["+", "-", "*", "/"]; #[derive(Debug, PartialEq)] pub struct Program { pub statements: Vec, } -impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Program -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, -{ - fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> { - let mut result = allocator.nil(); - - for stmt in self.statements.iter() { - result = result - .append(stmt.pretty(allocator)) - .append(allocator.text(";")) - .append(allocator.hardline()); - } - - result - } -} - #[derive(Debug, PartialEq)] pub enum Statement { Binding(Location, String, Expression), Print(Location, String), } -impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Statement -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, -{ - fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> { - match self { - Statement::Binding(_, var, expr) => allocator - .text(var.to_string()) - .append(allocator.space()) - .append(allocator.text("=")) - .append(allocator.space()) - .append(expr.pretty(allocator)), - Statement::Print(_, var) => allocator - .text("print") - .append(allocator.space()) - .append(allocator.text(var.to_string())), - } - } -} - #[derive(Debug, PartialEq)] pub enum Expression { Value(Location, Value), @@ -61,79 +20,7 @@ pub enum Expression { Primitive(Location, String, Vec), } -impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Expression -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, -{ - fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> { - match self { - Expression::Value(_, val) => val.pretty(allocator), - Expression::Reference(_, var) => allocator.text(var.to_string()), - Expression::Primitive(_, op, exprs) if BINARY_OPERATORS.contains(&op.as_ref()) => { - assert_eq!( - exprs.len(), - 2, - "Found binary operator with {} components?", - exprs.len() - ); - - let left = exprs[0].pretty(allocator); - let right = exprs[1].pretty(allocator); - - left.append(allocator.space()) - .append(allocator.text(op.to_string())) - .append(allocator.space()) - .append(right) - .parens() - } - Expression::Primitive(_, op, exprs) => { - let call = allocator.text(op.to_string()); - let args = exprs.iter().map(|x| x.pretty(allocator)); - let comma_sepped_args = allocator.intersperse(args, CommaSep {}); - call.append(comma_sepped_args.parens()) - } - } - } -} - #[derive(Debug, PartialEq, Eq)] pub enum Value { Number(Option, i64), -} - -impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Value -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, -{ - fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> { - match self { - Value::Number(opt_base, value) => { - let value_str = match opt_base { - None => format!("{}", value), - Some(2) => format!("0b{:b}", value), - Some(8) => format!("0o{:o}", value), - Some(10) => format!("0d{}", value), - Some(16) => format!("0x{:x}", value), - Some(_) => format!("!!{:x}!!", value), - }; - - allocator.text(value_str) - } - } - } -} - -#[derive(Clone, Copy)] -struct CommaSep {} - -impl<'a, D, A> Pretty<'a, D, A> for CommaSep -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, -{ - fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> { - allocator.text(",").append(allocator.space()) - } -} +} \ No newline at end of file diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs new file mode 100644 index 0000000..f9d18ff --- /dev/null +++ b/src/syntax/pretty.rs @@ -0,0 +1,114 @@ +use pretty::{Pretty, DocAllocator, DocBuilder}; +use crate::syntax::ast::{Program, Statement, Expression, Value, BINARY_OPERATORS}; + +impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Program +where + A: 'a, + D: ?Sized + DocAllocator<'a, A>, +{ + fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> { + let mut result = allocator.nil(); + + for stmt in self.statements.iter() { + result = result + .append(stmt.pretty(allocator)) + .append(allocator.text(";")) + .append(allocator.hardline()); + } + + result + } +} + +impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Statement +where + A: 'a, + D: ?Sized + DocAllocator<'a, A>, +{ + fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> { + match self { + Statement::Binding(_, var, expr) => allocator + .text(var.to_string()) + .append(allocator.space()) + .append(allocator.text("=")) + .append(allocator.space()) + .append(expr.pretty(allocator)), + Statement::Print(_, var) => allocator + .text("print") + .append(allocator.space()) + .append(allocator.text(var.to_string())), + } + } +} + +impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Expression +where + A: 'a, + D: ?Sized + DocAllocator<'a, A>, +{ + fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> { + match self { + Expression::Value(_, val) => val.pretty(allocator), + Expression::Reference(_, var) => allocator.text(var.to_string()), + Expression::Primitive(_, op, exprs) if BINARY_OPERATORS.contains(&op.as_ref()) => { + assert_eq!( + exprs.len(), + 2, + "Found binary operator with {} components?", + exprs.len() + ); + + let left = exprs[0].pretty(allocator); + let right = exprs[1].pretty(allocator); + + left.append(allocator.space()) + .append(allocator.text(op.to_string())) + .append(allocator.space()) + .append(right) + .parens() + } + Expression::Primitive(_, op, exprs) => { + let call = allocator.text(op.to_string()); + let args = exprs.iter().map(|x| x.pretty(allocator)); + let comma_sepped_args = allocator.intersperse(args, CommaSep {}); + call.append(comma_sepped_args.parens()) + } + } + } +} + +impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Value +where + A: 'a, + D: ?Sized + DocAllocator<'a, A>, +{ + fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> { + match self { + Value::Number(opt_base, value) => { + let value_str = match opt_base { + None => format!("{}", value), + Some(2) => format!("0b{:b}", value), + Some(8) => format!("0o{:o}", value), + Some(10) => format!("0d{}", value), + Some(16) => format!("0x{:x}", value), + Some(_) => format!("!!{:x}!!", value), + }; + + allocator.text(value_str) + } + } + } +} + +#[derive(Clone, Copy)] +struct CommaSep {} + +impl<'a, D, A> Pretty<'a, D, A> for CommaSep +where + A: 'a, + D: ?Sized + DocAllocator<'a, A>, +{ + fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> { + allocator.text(",").append(allocator.space()) + } +} \ No newline at end of file