use internment::ArcIntern; use pretty::{DocAllocator, Pretty}; use proptest::{ prelude::Arbitrary, strategy::{BoxedStrategy, Strategy}, }; use crate::syntax::Location; type Variable = ArcIntern; #[derive(Debug)] 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) -> pretty::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 Arbitrary for Program { type Parameters = (); type Strategy = BoxedStrategy; fn arbitrary_with(args: Self::Parameters) -> Self::Strategy { crate::syntax::Program::arbitrary_with(args) .prop_map(|x| Program::from(x.simplify())) .boxed() } } #[derive(Debug)] pub enum Statement { Binding(Location, Variable, Expression), Print(Location, Variable), } 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) -> pretty::DocBuilder<'a, D, A> { match self { Statement::Binding(_, var, expr) => allocator .text(var.as_ref().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.as_ref().to_string())), } } } #[derive(Debug)] pub enum Expression { Value(Location, Value), Reference(Location, Variable), Primitive(Location, Primitive, 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) -> pretty::DocBuilder<'a, D, A> { match self { Expression::Value(_, val) => val.pretty(allocator), Expression::Reference(_, var) => allocator.text(var.as_ref().to_string()), Expression::Primitive(_, op, exprs) if exprs.len() == 1 => { op.pretty(allocator).append(exprs[0].pretty(allocator)) } Expression::Primitive(_, op, exprs) if exprs.len() == 2 => { let left = exprs[0].pretty(allocator); let right = exprs[1].pretty(allocator); left.append(allocator.space()) .append(op.pretty(allocator)) .append(allocator.space()) .append(right) .parens() } Expression::Primitive(_, op, exprs) => { allocator.text(format!("!!{:?} with {} arguments!!", op, exprs.len())) } } } } #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Primitive { Plus, Minus, Times, Divide, } impl<'a> TryFrom<&'a str> for Primitive { type Error = String; fn try_from(value: &str) -> Result { match value { "+" => Ok(Primitive::Plus), "-" => Ok(Primitive::Minus), "*" => Ok(Primitive::Times), "/" => Ok(Primitive::Divide), _ => Err(format!("Illegal primitive {}", value)), } } } impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Primitive where A: 'a, D: ?Sized + DocAllocator<'a, A>, { fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { match self { Primitive::Plus => allocator.text("+"), Primitive::Minus => allocator.text("-"), Primitive::Times => allocator.text("*"), Primitive::Divide => allocator.text("/"), } } } #[derive(Debug)] pub enum ValueOrRef { Value(Location, Value), Ref(Location, ArcIntern), } impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b ValueOrRef where A: 'a, D: ?Sized + DocAllocator<'a, A>, { fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { match self { ValueOrRef::Value(_, v) => v.pretty(allocator), ValueOrRef::Ref(_, v) => allocator.text(v.as_ref().to_string()), } } } #[derive(Debug)] 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) -> pretty::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) } } } }