From 0c793218267993edad56edbf4b1dcb6dbef1b9fd Mon Sep 17 00:00:00 2001 From: azur Date: Wed, 12 Apr 2023 10:35:35 +0700 Subject: [PATCH] Prepare for proper type inference --- src/main.rs | 40 +---- src/parse/parser.rs | 33 ++-- src/typing/check.rs | 425 -------------------------------------------- src/typing/mod.rs | 1 - src/typing/typed.rs | 55 +----- 5 files changed, 32 insertions(+), 522 deletions(-) delete mode 100644 src/typing/check.rs diff --git a/src/main.rs b/src/main.rs index 989ab10..ce55261 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ use ariadne::{sources, Color, Label, Report, ReportKind}; use chumsky::{Parser, prelude::Input}; -use self::{parse::parser::{lexer, exprs_parser}, typing::check::check}; +use self::{parse::parser::{lexer, exprs_parser}}; pub mod parse; pub mod typing; @@ -8,11 +8,10 @@ pub mod typing; fn main() { let src = " { - let foo : num = - let a : num = true, - b : num = 3 - in - a + b; + let foo = + let a = true in + let b = false in + a + b; foo * 2 } ".to_string(); @@ -27,34 +26,7 @@ fn main() { .into_output_errors(); if let Some(ast) = ast.filter(|_| errs.len() + parse_errs.len() == 0) { - match check(ast.0) { - Ok(tast) => println!("{:?}", tast), - Err(ty_err) => { - let mut r = Report::build(ReportKind::Error, filename.clone(), ty_err.loc.start) - .with_message(ty_err.msg) - .with_label(Label::new((filename.clone(), ty_err.loc.into_range())) - .with_message(match ty_err.note { - Some(note) => note, - None => "While type checking this expression".to_string(), - }) - .with_color(Color::Red) - ); - - if let Some((hint, loc)) = ty_err.hint { - r = r.with_label(Label::new((filename.clone(), loc.into_range())) - .with_message(hint) - .with_color(Color::Yellow), - ); - } - - r.finish() - .print(sources([( - filename.clone(), - src.clone(), - )])) - .unwrap(); - } - } + println!("{:?}", ast); } parse_errs diff --git a/src/parse/parser.rs b/src/parse/parser.rs index 06cf30d..9dca651 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -178,8 +178,6 @@ pub enum BinaryOp { } pub type Spanned = (T, Span); -type Binding<'src> = - (&'src str, Option, Spanned>>); // Clone is needed for type checking since the type checking // algorithm is recursive and sometimes consume the AST. @@ -200,10 +198,16 @@ pub enum Expr<'src> { f: Spanned>, }, Let { - bindings: Vec>, + name: &'src str, + ty: Option, + value: Spanned>, body: Spanned>, }, - Assign(Vec>), + Define { + name: &'src str, + ty: Option, + value: Spanned>, + }, Block { exprs: Vec>>, void: bool, // True if last expression is discarded (ends with semicolon). @@ -268,8 +272,8 @@ pub fn expr_parser<'tokens, 'src: 'tokens>() -> impl Parser< .then(expr.clone()) .map(|(args, body)| Expr::Lambda(args, boxspan(body))); - // (ident (: type)?)* - let binds = symbol + // ident (: type)? + let bind = symbol .then( just(Token::Colon) .ignore_then(type_parser()) @@ -277,21 +281,18 @@ pub fn expr_parser<'tokens, 'src: 'tokens>() -> impl Parser< ) .then_ignore(just(Token::Assign)) .then(expr.clone()) - .map(|((name, ty), expr)| (name, ty, boxspan(expr))) - .separated_by(just(Token::Comma)) - .allow_trailing() - .collect::>(); + .map(|((name, ty), expr)| (name, ty, boxspan(expr))); - let let_or_assign = just(Token::Let) - .ignore_then(binds) + let let_or_define = just(Token::Let) + .ignore_then(bind) .then( just(Token::In) .ignore_then(expr.clone()) .or_not() ) - .map(|(bindings, body)| match body { - Some(body) => Expr::Let { bindings, body: boxspan(body) }, - None => Expr::Assign(bindings), + .map(|((name, ty, expr), body)| match body { + Some(body) => Expr::Let { name, ty, value: expr, body: boxspan(body) }, + None => Expr::Define { name, ty, value: expr }, }); let if_ = just(Token::If) @@ -333,7 +334,7 @@ pub fn expr_parser<'tokens, 'src: 'tokens>() -> impl Parser< .or(ident) .or(paren_expr) .or(lambda) - .or(let_or_assign) + .or(let_or_define) .or(if_) .or(block) .map_with_span(|e, s| (e, s)) diff --git a/src/typing/check.rs b/src/typing/check.rs deleted file mode 100644 index cd08ce5..0000000 --- a/src/typing/check.rs +++ /dev/null @@ -1,425 +0,0 @@ -use crate::parse::parser::{ - Span, Spanned, - UnaryOp, BinaryOp, Lit, Expr, -}; -use super::{ty::Type, typed::TExpr}; - -#[derive(Clone, Debug)] -struct TypeContext<'src> { - bindings: Vec<(&'src str, Type)>, - funcs: Vec<(&'src str, Vec, Type)>, -} - -impl<'src> TypeContext<'src> { - fn new() -> Self { - Self { - bindings: Vec::new(), - funcs: Vec::new(), - } - } - - /// Bind a type to a name. - fn bind(&mut self, name: &'src str, ty: Type) { - self.bindings.push((name, ty)); - } - - /// Bind a function (parameters and return type) to a name. - fn bind_func(&mut self, name: &'src str, args: Vec, ret_ty: Type) { - self.funcs.push((name, args, ret_ty)); - } - - fn lookup(&self, name: &str) -> Option { - self.bindings.iter() - .rev() - .find(|(n, _)| *n == name) - .map(|(_, t)| t.clone()) - } - - fn lookup_func(&self, name: &str) -> Option<(Vec, Type)> { - self.funcs.iter() - .rev() - .find(|(n, _, _)| *n == name) - .map(|(_, args, ret_ty)| (args.clone(), ret_ty.clone())) - } -} - -#[derive(Debug)] -pub struct TypeError { - pub msg: String, - pub note: Option, - pub hint: Option<(String, Span)>, - pub loc: Span, -} - -impl TypeError { - fn new(msg: String, loc: Span) -> Self { - Self { - msg, - note: None, - hint: None, - loc, - } - } - - fn with_note(mut self, note: String) -> Self { - self.note = Some(note); - self - } - - fn with_hint(mut self, hint: String, loc: Span) -> Self { - self.hint = Some((hint, loc)); - self - } -} - -fn type_expr<'src>( - env: &mut TypeContext<'src>, expr: Spanned> -) -> Result>, TypeError> { - macro_rules! oks { // Spanned Ok macro. - ($e:expr $(,)?) => { - Ok(($e, expr.1)) - }; - } - macro_rules! unbox { // Unbox a Spanned> into a Spanned. - ($e:expr) => { - (*$e.0, $e.1) - }; - } - macro_rules! sbox { // Box the first value of a Spanned. - ($e:expr) => { - (Box::new($e.0), $e.1) - }; - } - - match expr.0 { - Expr::Lit(lit) => match lit { - Lit::Unit => oks!(TExpr::Lit(Lit::Unit)), - Lit::Bool(x) => oks!(TExpr::Lit(Lit::Bool(x))), - Lit::Num(x) => oks!(TExpr::Lit(Lit::Num(x))), - Lit::Str(x) => oks!(TExpr::Lit(Lit::Str(x))), - } - - Expr::Ident(name) => { - let ty = env.lookup(name) - .ok_or(TypeError::new(format!("Unknown identifier `{}`", name), expr.1))?; - oks!(TExpr::Ident(name, ty)) - } - - Expr::Unary(op, e) => { - let te = type_expr(env, unbox!(e))?; - let ret_ty = match op { - UnaryOp::Neg => Type::Num, - UnaryOp::Not => Type::Bool, - }; - - if te.0.ty() != &ret_ty { - return Err(TypeError::new(format!("Expected `{}` but found `{}`", ret_ty, te.0.ty()), te.1) - .with_note(format!("This have type `{}`", te.0.ty())) - .with_hint(format!("This operator requires a `{}`", ret_ty), (te.1.start-1..te.1.start).into())); - } - - oks!(TExpr::Unary { - op, - expr: sbox!(te), - ret_ty, - }) - } - - Expr::Binary(op, lhs, rhs) => { - let tlhs = type_expr(env, unbox!(lhs))?; - let trhs = type_expr(env, unbox!(rhs))?; - let op_ty = match op { - BinaryOp::Add - | BinaryOp::Sub - | BinaryOp::Mul - | BinaryOp::Div - | BinaryOp::Rem => Some(Type::Num), - BinaryOp::And - | BinaryOp::Or => Some(Type::Bool), - BinaryOp::Eq - | BinaryOp::Ne - | BinaryOp::Lt - | BinaryOp::Le - | BinaryOp::Gt - | BinaryOp::Ge => None, - }; - - let ret_ty; - if let Some(op_ty) = op_ty { - if tlhs.0.ty() != &op_ty { - return Err(TypeError::new(format!("Expected `{}` but found `{}`", op_ty, tlhs.0.ty()), tlhs.1) - .with_note(format!("This have type `{}`", tlhs.0.ty())) - .with_hint(format!("This operator requires a `{}`", op_ty), (tlhs.1.start-1..tlhs.1.start).into())); - } - if trhs.0.ty() != &op_ty { - return Err(TypeError::new(format!("Expected `{}` but found `{}`", op_ty, trhs.0.ty()), trhs.1) - .with_note(format!("This have type `{}`", trhs.0.ty())) - .with_hint(format!("This operator requires a `{}`", op_ty), (trhs.1.start-1..trhs.1.start).into())); - } - ret_ty = op_ty; - } else { - if tlhs.0.ty() != trhs.0.ty() { - return Err(TypeError::new(format!("Expected `{}` but found `{}`", tlhs.0.ty(), trhs.0.ty()), trhs.1) - .with_hint( - format!("Both have to be the same type. Got `{}` and `{}`", tlhs.0.ty(), trhs.0.ty()), - (tlhs.1.start..trhs.1.end).into(), - )); - } - ret_ty = Type::Bool; - } - - oks!(TExpr::Binary { - op, - lhs: sbox!(tlhs), - rhs: sbox!(trhs), - ret_ty, - }) - } - - Expr::Lambda(args, body) => { - // Create a new type environment. - let mut new_env = env.clone(); - - // Bind the arguments to the new environment. - let mut arg_tys = Vec::new(); - for (arg, maybe_ty) in args { - let ty = match maybe_ty { - Some(ty) => ty, - None => todo!(), // TODO: infer the type of the argument after type checking the body. - }; - arg_tys.push((arg, ty.clone())); - new_env.bind(arg, ty); - } - - // Type check the body. - let tbody = type_expr(&mut new_env, unbox!(body))?; - - // Return the typed lambda expression. - oks!(TExpr::Lambda { - params: arg_tys, - body: sbox!(tbody.clone()), - ret_ty: tbody.0.ty().clone(), - }) - } - - Expr::Call(func, cargs) => { - // Get span of the arguments. - let args_span = cargs.iter() - .map(|arg| arg.1.into_range()) - .fold(None, |acc: Option>, range| match acc { - Some(acc) => Some(acc.start..range.end), - None => Some(range), - }) - .unwrap_or(func.1.end..func.1.end+2); - - // Type check the arguments. - let mut targs = Vec::new(); - for arg in cargs { - let targ = type_expr(env, arg)?; - targs.push(targ); - } - - // Type check the function (callee). - let tfunc = type_expr(env, unbox!(func))?; - - // Get the function type of the callee. (if any). - if let Some((param_tys, ret_ty)) = tfunc.0.clone().as_fn() { - - // Check if the number of arguments match the number of parameters. - if param_tys.len() != targs.len() { - return Err(TypeError::new( - format!( - "Expected {} arguments, got {}", - param_tys.len(), - targs.len(), - ), - args_span.into(), - ).with_note(format!( - "Expected {} arguments", - param_tys.len(), - )).with_hint( - format!( - "This expect arguments of type `{}`", - param_tys.iter().map(|ty| ty.to_string()).collect::>().join(", ") - ), - func.1, - )); - } - - // Check if the types of the arguments match the types of the parameters. - for (arg, param) in targs.iter().zip(param_tys.iter()) { - if arg.0.ty() != param { - return Err(TypeError::new( - format!( - "Expected argument of type `{}`, got `{}`", - param, - arg.0.ty(), - ), - arg.1, - ).with_note(format!( - "Expected argument of type `{}`", - param, - ))); - } - } - - // Return the typed call expression. - oks!(TExpr::Call { - func: sbox!(tfunc), - args: targs, - ret_ty, - }) - } else { - Err(TypeError::new( - format!("Expected function, got `{}`", tfunc.0.ty()), - tfunc.1, - )) - } - } - - Expr::If { cond, t, f } => { - let tcond = type_expr(env, unbox!(cond))?; - let tt = type_expr(env, unbox!(t))?; - let tf = type_expr(env, unbox!(f))?; - - // Check if the condition is of type `bool`. - if tcond.0.ty() != &Type::Bool { - return Err(TypeError::new( - format!("Expected condition of type `bool`, got `{}`", tcond.0.ty()), - tcond.1, - )); - } - - // Check if the true and false branches have the same type. - if tt.0.ty() != tf.0.ty() { - return Err(TypeError::new( - format!( - "Expected the branches to have the same type, got `{}` and `{}`", - tt.0.ty(), - tf.0.ty(), - ), - tf.1, - ).with_note(format!( - "Expected this branch to be type of `{}`", - tt.0.ty(), - ))); - } - - oks!(TExpr::If { - cond: sbox!(tcond), - br_ty: tt.0.ty().clone(), - t: sbox!(tt), - f: sbox!(tf), - }) - } - - Expr::Let { bindings, body } => { - // Create a new type environment. - let mut new_env = env.clone(); - - // Type check the bindings. - let mut tbindings = Vec::new(); - for (name, maybe_ty, expr) in bindings { - let ty = match maybe_ty { - Some(ty) => ty, - None => todo!("Type inferrence"), // TODO: infer. - }; - let texpr = type_expr(&mut new_env, unbox!(expr))?; - - // Check if the type of the binding matches the type of the expression. - if texpr.0.ty() != &ty { - return Err(TypeError::new( - format!( - "Expected the value to be of type `{}`, got `{}`", - ty, - texpr.0.ty(), - ), - texpr.1, - ).with_note(format!( - "Expected this value to be of type `{}`", - ty, - ))); - } - - tbindings.push((name, ty.clone(), sbox!(texpr))); - new_env.bind(name, ty); - } - - // Type check the body. - let tbody = type_expr(&mut new_env, unbox!(body))?; - - // Return the typed let expression. - oks!(TExpr::Let { - bindings: tbindings, - body: sbox!(tbody), - }) - } - - Expr::Assign(bindings) => { - // Type check the bindings. - let mut tbindings = Vec::new(); - for (name, maybe_ty, expr) in bindings { - let ty = match maybe_ty { - Some(ty) => ty, - None => todo!("Type inferrence"), // TODO: infer. - }; - let texpr = type_expr(env, unbox!(expr))?; - - // Check if the type of the binding matches the type of the expression. - if texpr.0.ty() != &ty { - return Err(TypeError::new( - format!( - "Expected the binding to be of type `{}`, got `{}`", - ty, - texpr.0.ty(), - ), - texpr.1, - ).with_note(format!( - "Expected this binding to be of type `{}`", - ty, - ))); - } - - tbindings.push((name, ty.clone(), sbox!(texpr))); - env.bind(name, ty); - } - - // Return the typed assign expression. - oks!(TExpr::Assign(tbindings)) - } - - Expr::Block { exprs, void } => { - let texprs = exprs - .into_iter() - .map(|e| type_expr(env, unbox!(e))) - .collect::, _>>()?; - - let ret_ty = if void { - Type::Unit - } else if let Some(texpr) = texprs.last() { - texpr.0.ty().clone() - } else { - Type::Unit - }; - - oks!(TExpr::Block { - exprs: texprs, - void, - ret_ty, - }) - } - - #[allow(unreachable_patterns)] - _ => todo!(), - } -} - -pub fn check(es: Vec>>) -> Result>>, TypeError> { - let mut env = TypeContext::new(); - let mut tes = Vec::new(); - for e in es { - let te = type_expr(&mut env, e)?; - tes.push(te); - } - Ok(tes) -} \ No newline at end of file diff --git a/src/typing/mod.rs b/src/typing/mod.rs index cf180ae..53b10eb 100644 --- a/src/typing/mod.rs +++ b/src/typing/mod.rs @@ -1,3 +1,2 @@ pub mod ty; -pub mod check; pub mod typed; \ No newline at end of file diff --git a/src/typing/typed.rs b/src/typing/typed.rs index e87d758..d2b2452 100644 --- a/src/typing/typed.rs +++ b/src/typing/typed.rs @@ -6,14 +6,11 @@ use crate::parse::parser::{ Spanned, }; -type TypedBinding<'src> = - (&'src str, Type, Spanned>>); - // Typed version of the expression. #[derive(Clone, Debug)] pub enum TExpr<'src> { Lit(Lit<'src>), - Ident(&'src str, Type), + Ident(&'src str), Unary { op: UnaryOp, @@ -35,7 +32,6 @@ pub enum TExpr<'src> { Call { func: Spanned>, args: Vec>, - ret_ty: Type, }, If { cond: Spanned>, @@ -44,52 +40,19 @@ pub enum TExpr<'src> { br_ty: Type, }, Let { - bindings: Vec>, + name: &'src str, + ty: Type, + value: Spanned>, body: Spanned>, }, - Assign(Vec>), + Define { + name: &'src str, + ty: Type, + value: Spanned>, + }, Block { exprs: Vec>, void: bool, ret_ty: Type, }, } - -impl<'src> TExpr<'src> { - pub fn ty(&self) -> &Type { - match self { - TExpr::Lit(lit) => match lit { - Lit::Unit => &Type::Unit, - Lit::Bool(_) => &Type::Bool, - Lit::Num(_) => &Type::Num, - Lit::Str(_) => &Type::Str, - }, - TExpr::Ident(_, ty) => ty, - TExpr::Unary { ret_ty, .. } => ret_ty, - TExpr::Binary { ret_ty, .. } => ret_ty, - TExpr::Lambda { ret_ty, .. } => ret_ty, - TExpr::Call { ret_ty, .. } => ret_ty, - TExpr::If { br_ty, .. } => br_ty, - // Get the type from the body. - TExpr::Let { body, .. } => body.0.ty(), - // Assignment is always unit. - TExpr::Assign { .. } => &Type::Unit, - // Get the type from the last expression in the block - // if the expression is not ended with a semicolon. - TExpr::Block { ret_ty, .. } => ret_ty, - } - } - - pub fn as_fn(self) -> Option<(Vec, Type)> { - match self { - TExpr::Ident(_, Type::Func(params, ret_ty)) => Some((params, *ret_ty)), - TExpr::Lambda { params, ret_ty, .. } => { - let p = params.into_iter() - .map(|(_, ty)| ty) - .collect(); - Some((p, ret_ty)) - } - _ => None, - } - } -} \ No newline at end of file