use std::collections::HashMap; use itertools::Itertools; use super::{BinaryOperator, Ident, Literal, UnaryOperator}; #[derive(Debug, PartialEq, Eq, Clone)] pub struct Binding<'a, T> { pub ident: Ident<'a>, pub type_: T, pub body: Expr<'a, T>, } impl<'a, T> Binding<'a, T> { fn to_owned(&self) -> Binding<'static, T> where T: Clone, { Binding { ident: self.ident.to_owned(), type_: self.type_.clone(), body: self.body.to_owned(), } } } #[derive(Debug, PartialEq, Eq, Clone)] pub enum Expr<'a, T> { Ident(Ident<'a>, T), Literal(Literal<'a>, T), UnaryOp { op: UnaryOperator, rhs: Box<Expr<'a, T>>, type_: T, }, BinaryOp { lhs: Box<Expr<'a, T>>, op: BinaryOperator, rhs: Box<Expr<'a, T>>, type_: T, }, Let { bindings: Vec<Binding<'a, T>>, body: Box<Expr<'a, T>>, type_: T, }, If { condition: Box<Expr<'a, T>>, then: Box<Expr<'a, T>>, else_: Box<Expr<'a, T>>, type_: T, }, Fun { type_args: Vec<Ident<'a>>, args: Vec<(Ident<'a>, T)>, body: Box<Expr<'a, T>>, type_: T, }, Call { fun: Box<Expr<'a, T>>, type_args: HashMap<Ident<'a>, T>, args: Vec<Expr<'a, T>>, type_: T, }, } impl<'a, T> Expr<'a, T> { pub fn type_(&self) -> &T { match self { Expr::Ident(_, t) => t, Expr::Literal(_, t) => t, Expr::UnaryOp { type_, .. } => type_, Expr::BinaryOp { type_, .. } => type_, Expr::Let { type_, .. } => type_, Expr::If { type_, .. } => type_, Expr::Fun { type_, .. } => type_, Expr::Call { type_, .. } => type_, } } pub fn traverse_type<F, U, E>(self, f: F) -> Result<Expr<'a, U>, E> where F: Fn(T) -> Result<U, E> + Clone, { match self { Expr::Ident(id, t) => Ok(Expr::Ident(id, f(t)?)), Expr::Literal(lit, t) => Ok(Expr::Literal(lit, f(t)?)), Expr::UnaryOp { op, rhs, type_ } => Ok(Expr::UnaryOp { op, rhs: Box::new(rhs.traverse_type(f.clone())?), type_: f(type_)?, }), Expr::BinaryOp { lhs, op, rhs, type_, } => Ok(Expr::BinaryOp { lhs: Box::new(lhs.traverse_type(f.clone())?), op, rhs: Box::new(rhs.traverse_type(f.clone())?), type_: f(type_)?, }), Expr::Let { bindings, body, type_, } => Ok(Expr::Let { bindings: bindings .into_iter() .map(|Binding { ident, type_, body }| { Ok(Binding { ident, type_: f(type_)?, body: body.traverse_type(f.clone())?, }) }) .collect::<Result<Vec<_>, E>>()?, body: Box::new(body.traverse_type(f.clone())?), type_: f(type_)?, }), Expr::If { condition, then, else_, type_, } => Ok(Expr::If { condition: Box::new(condition.traverse_type(f.clone())?), then: Box::new(then.traverse_type(f.clone())?), else_: Box::new(else_.traverse_type(f.clone())?), type_: f(type_)?, }), Expr::Fun { args, type_args, body, type_, } => Ok(Expr::Fun { args: args .into_iter() .map(|(id, t)| Ok((id, f.clone()(t)?))) .collect::<Result<Vec<_>, E>>()?, type_args, body: Box::new(body.traverse_type(f.clone())?), type_: f(type_)?, }), Expr::Call { fun, type_args, args, type_, } => Ok(Expr::Call { fun: Box::new(fun.traverse_type(f.clone())?), type_args: type_args .into_iter() .map(|(id, ty)| Ok((id, f.clone()(ty)?))) .collect::<Result<HashMap<_, _>, E>>()?, args: args .into_iter() .map(|e| e.traverse_type(f.clone())) .collect::<Result<Vec<_>, E>>()?, type_: f(type_)?, }), } } pub fn to_owned(&self) -> Expr<'static, T> where T: Clone, { match self { Expr::Ident(id, t) => Expr::Ident(id.to_owned(), t.clone()), Expr::Literal(lit, t) => Expr::Literal(lit.to_owned(), t.clone()), Expr::UnaryOp { op, rhs, type_ } => Expr::UnaryOp { op: *op, rhs: Box::new((**rhs).to_owned()), type_: type_.clone(), }, Expr::BinaryOp { lhs, op, rhs, type_, } => Expr::BinaryOp { lhs: Box::new((**lhs).to_owned()), op: *op, rhs: Box::new((**rhs).to_owned()), type_: type_.clone(), }, Expr::Let { bindings, body, type_, } => Expr::Let { bindings: bindings.iter().map(|b| b.to_owned()).collect(), body: Box::new((**body).to_owned()), type_: type_.clone(), }, Expr::If { condition, then, else_, type_, } => Expr::If { condition: Box::new((**condition).to_owned()), then: Box::new((**then).to_owned()), else_: Box::new((**else_).to_owned()), type_: type_.clone(), }, Expr::Fun { args, type_args, body, type_, } => Expr::Fun { args: args .iter() .map(|(id, t)| (id.to_owned(), t.clone())) .collect(), type_args: type_args.iter().map(|arg| arg.to_owned()).collect(), body: Box::new((**body).to_owned()), type_: type_.clone(), }, Expr::Call { fun, type_args, args, type_, } => Expr::Call { fun: Box::new((**fun).to_owned()), type_args: type_args .iter() .map(|(id, t)| (id.to_owned(), t.clone())) .collect(), args: args.iter().map(|e| e.to_owned()).collect(), type_: type_.clone(), }, } } } #[derive(Debug, Clone)] pub enum Decl<'a, T> { Fun { name: Ident<'a>, type_args: Vec<Ident<'a>>, args: Vec<(Ident<'a>, T)>, body: Box<Expr<'a, T>>, type_: T, }, Extern { name: Ident<'a>, arg_types: Vec<T>, ret_type: T, }, } impl<'a, T> Decl<'a, T> { pub fn name(&self) -> &Ident<'a> { match self { Decl::Fun { name, .. } => name, Decl::Extern { name, .. } => name, } } pub fn set_name(&mut self, new_name: Ident<'a>) { match self { Decl::Fun { name, .. } => *name = new_name, Decl::Extern { name, .. } => *name = new_name, } } pub fn type_(&self) -> Option<&T> { match self { Decl::Fun { type_, .. } => Some(type_), Decl::Extern { .. } => None, } } pub fn traverse_type<F, U, E>(self, f: F) -> Result<Decl<'a, U>, E> where F: Fn(T) -> Result<U, E> + Clone, { match self { Decl::Fun { name, type_args, args, body, type_, } => Ok(Decl::Fun { name, type_args, args: args .into_iter() .map(|(id, t)| Ok((id, f(t)?))) .try_collect()?, body: Box::new(body.traverse_type(f.clone())?), type_: f(type_)?, }), Decl::Extern { name, arg_types, ret_type, } => Ok(Decl::Extern { name, arg_types: arg_types.into_iter().map(f.clone()).try_collect()?, ret_type: f(ret_type)?, }), } } }