From 32a5c0ff0fc58aa6721c1e0ad41950bde2d66744 Mon Sep 17 00:00:00 2001 From: Griffin Smith Date: Sat, 13 Mar 2021 21:57:27 -0500 Subject: Add the start of a hindley-milner typechecker The beginning of a parse-don't-validate-based hindley-milner typechecker, which returns on success an IR where every AST node trivially knows its own type, and using those types to determine LLVM types in codegen. --- src/ast/hir.rs | 246 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/ast/mod.rs | 3 + 2 files changed, 249 insertions(+) create mode 100644 src/ast/hir.rs (limited to 'src/ast') diff --git a/src/ast/hir.rs b/src/ast/hir.rs new file mode 100644 index 000000000000..151ddd529872 --- /dev/null +++ b/src/ast/hir.rs @@ -0,0 +1,246 @@ +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, T), + + UnaryOp { + op: UnaryOperator, + rhs: Box>, + type_: T, + }, + + BinaryOp { + lhs: Box>, + op: BinaryOperator, + rhs: Box>, + type_: T, + }, + + Let { + bindings: Vec>, + body: Box>, + type_: T, + }, + + If { + condition: Box>, + then: Box>, + else_: Box>, + type_: T, + }, + + Fun { + args: Vec<(Ident<'a>, T)>, + body: Box>, + type_: T, + }, + + Call { + fun: Box>, + args: Vec>, + 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(self, f: F) -> Result, E> + where + F: Fn(T) -> Result + 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::, 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, body, type_ } => Ok(Expr::Fun { + args: args + .into_iter() + .map(|(id, t)| Ok((id, f.clone()(t)?))) + .collect::, E>>()?, + body: Box::new(body.traverse_type(f.clone())?), + type_: f(type_)?, + }), + Expr::Call { fun, args, type_ } => Ok(Expr::Call { + fun: Box::new(fun.traverse_type(f.clone())?), + args: args + .into_iter() + .map(|e| e.traverse_type(f.clone())) + .collect::, 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.clone(), 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.into_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, body, type_ } => Expr::Fun { + args: args + .into_iter() + .map(|(id, t)| (id.to_owned(), t.clone())) + .collect(), + body: Box::new((**body).to_owned()), + type_: type_.clone(), + }, + Expr::Call { fun, args, type_ } => Expr::Call { + fun: Box::new((**fun).to_owned()), + args: args.into_iter().map(|e| e.to_owned()).collect(), + type_: type_.clone(), + }, + } + } +} + +pub enum Decl<'a, T> { + Fun { + name: Ident<'a>, + args: Vec<(Ident<'a>, T)>, + body: Box>, + type_: T, + }, +} + +impl<'a, T> Decl<'a, T> { + pub fn traverse_type(self, f: F) -> Result, E> + where + F: Fn(T) -> Result + Clone, + { + match self { + Decl::Fun { + name, + args, + body, + type_, + } => Ok(Decl::Fun { + name, + args: args + .into_iter() + .map(|(id, t)| Ok((id, f(t)?))) + .try_collect()?, + body: Box::new(body.traverse_type(f.clone())?), + type_: f(type_)?, + }), + } + } +} diff --git a/src/ast/mod.rs b/src/ast/mod.rs index dc22ac3cdb56..cef366d16e04 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1,3 +1,5 @@ +pub(crate) mod hir; + use std::borrow::Cow; use std::convert::TryFrom; use std::fmt::{self, Display, Formatter}; @@ -107,6 +109,7 @@ pub enum UnaryOperator { #[derive(Debug, PartialEq, Eq, Clone)] pub enum Literal { Int(u64), + Bool(bool), } #[derive(Debug, PartialEq, Eq, Clone)] -- cgit 1.4.1