From 29ff7f69c654b5f0d3ea5a9d34ea747fab1e2d3d Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Mon, 29 Apr 2024 23:07:07 +0200 Subject: rebel-lang: merge ast_stmt_type() into record_type() and execute() This deduplicates some code and improves type checking. --- crates/rebel-lang/src/scope.rs | 175 ++++++++++++++++++++++------------------ crates/rebel-lang/src/typing.rs | 35 +------- 2 files changed, 99 insertions(+), 111 deletions(-) diff --git a/crates/rebel-lang/src/scope.rs b/crates/rebel-lang/src/scope.rs index 0a906de..64fe505 100644 --- a/crates/rebel-lang/src/scope.rs +++ b/crates/rebel-lang/src/scope.rs @@ -4,7 +4,7 @@ use rebel_parse::ast::{self, pat}; use crate::{ func::Func, - typing::{self, Type, TypeError, TypeFamily}, + typing::{self, Coerce, Type, TypeError, TypeFamily}, value::{self, EvalError, Value}, }; @@ -45,122 +45,139 @@ impl Context { pub fn record_type(&mut self, stmt: &ast::BlockStmt) -> typing::Result { match stmt { ast::BlockStmt::Let { dest, expr } => { - let typ = if let Some(expr) = expr { - Type::ast_expr_type(self, expr)? - } else { - Type::Free - }; - - // TODO: Handle explicit type - let ast::TypedPat { pat, typ: _ } = dest.as_ref(); + let ast::TypedPat { pat, typ } = dest.as_ref(); let pat::Pat::Path(dest_path) = pat; let [dest_ident] = dest_path.components[..] else { return Err(TypeError); }; + let explicit_type = if let Some(typ) = typ { + Type::ast_type(self, typ)? + } else { + Type::Free + }; + let inferred_type = if let Some(expr) = expr { + let expr_type = Type::ast_expr_type(self, expr)?; + explicit_type.clone().unify(expr_type, Coerce::Assign)? + } else { + explicit_type.clone() + }; + // TODO: Lexical scoping - // TODO: Typechecking - self.vars.0.insert( - dest_ident.name.to_owned(), - ModuleEntry::Def(Var { - explicit_type: Type::Free, - inferred_type: typ.clone(), - value: None, - }), - ); - - Ok(typ) + // TODO: Free fixed array length + + if dest_ident.name != "_" { + self.vars.insert( + dest_ident.name, + Var { + explicit_type, + inferred_type, + value: None, + }, + ); + } } ast::BlockStmt::Assign { dest, expr } => { - let typ = Type::ast_expr_type(self, expr)?; - let pat::Pat::Path(dest_path) = dest.as_ref(); let [dest_ident] = dest_path.components[..] else { return Err(TypeError); }; - if !self.vars.0.contains_key(dest_ident.name) { - return Err(TypeError); - } + let expr_type = Type::ast_expr_type(self, expr)?; // TODO: Lexical scoping - // TODO: Typechecking - self.vars.0.insert( - dest_ident.name.to_owned(), - ModuleEntry::Def(Var { - explicit_type: Type::Free, - inferred_type: typ.clone(), - value: None, - }), - ); - - Ok(typ) + let Some(ModuleEntry::Def(var)) = self.vars.0.get_mut(dest_ident.name) else { + return Err(TypeError); + }; + let inferred_type = var.inferred_type.clone().unify(expr_type, Coerce::Common)?; + + var.explicit_type + .clone() + .unify(inferred_type.clone(), Coerce::Assign)?; + + var.inferred_type = inferred_type.clone(); + + return Ok(inferred_type); } - ast::BlockStmt::Expr { expr } => Type::ast_expr_type(self, expr), - ast::BlockStmt::Empty => Ok(Type::Unit), + ast::BlockStmt::Expr { expr } => { + return Type::ast_expr_type(self, expr); + } + ast::BlockStmt::Empty => {} } + Ok(Type::Unit) } pub fn execute(&mut self, stmt: &ast::BlockStmt) -> value::Result { match stmt { ast::BlockStmt::Let { dest, expr } => { - let (typ, value) = if let Some(expr) = expr { - let value = Value::eval(self, expr)?; - (value.typ().or(Err(EvalError))?, Some(value)) - } else { - (Type::Free, None) - }; - - // TODO: Handle explicit type - let ast::TypedPat { pat, typ: _ } = dest.as_ref(); + let ast::TypedPat { pat, typ } = dest.as_ref(); let pat::Pat::Path(dest_path) = pat; let [dest_ident] = dest_path.components[..] else { - return Err(EvalError); + unreachable!("Type error during eval"); }; - if dest_ident.name == "_" { - return Ok(Value::Unit); - } + let explicit_type = if let Some(typ) = typ { + Type::ast_type(self, typ).expect("Type error during eval") + } else { + Type::Free + }; + let (inferred_type, value) = if let Some(expr) = expr { + let value = Value::eval(self, expr)?; + let expr_type = value.typ().expect("Type error during eval"); + ( + explicit_type + .clone() + .unify(expr_type, Coerce::Assign) + .expect("Type error during eval"), + Some(value), + ) + } else { + (explicit_type.clone(), None) + }; // TODO: Lexical scoping - self.vars.0.insert( - dest_ident.name.to_owned(), - ModuleEntry::Def(Var { - explicit_type: Type::Free, - inferred_type: typ, - value, - }), - ); - - Ok(Value::Unit) + + if dest_ident.name != "_" { + self.vars.insert( + dest_ident.name, + Var { + explicit_type, + inferred_type, + value, + }, + ); + } } ast::BlockStmt::Assign { dest, expr } => { - let value = Value::eval(self, expr)?; - let typ = value.typ().or(Err(EvalError))?; - - // TODO: Handle explicit type let pat::Pat::Path(dest_path) = dest.as_ref(); let [dest_ident] = dest_path.components[..] else { - return Err(EvalError); + unreachable!("Type error during eval"); }; - // TODO: Lexical scoping - self.vars.0.insert( - dest_ident.name.to_owned(), - ModuleEntry::Def(Var { - explicit_type: Type::Free, - inferred_type: typ, - value: Some(value.clone()), - }), - ); - - Ok(value) + let value = Value::eval(self, expr)?; + let expr_type = value.typ().or(Err(EvalError))?; + + let Some(ModuleEntry::Def(var)) = self.vars.0.get_mut(dest_ident.name) else { + unreachable!("Type error during eval"); + }; + + var.inferred_type = var + .inferred_type + .clone() + .unify(expr_type, Coerce::Common) + .expect("Type error during eval"); + // TODO: Debug check: Test against explicit type + var.value = Some(value.clone()); + return Ok(value); + } + ast::BlockStmt::Expr { expr } => { + return Value::eval(self, expr); } - ast::BlockStmt::Expr { expr } => Value::eval(self, expr), - ast::BlockStmt::Empty => Ok(Value::Unit), + ast::BlockStmt::Empty => {} } + Ok(Value::Unit) } } diff --git a/crates/rebel-lang/src/typing.rs b/crates/rebel-lang/src/typing.rs index 140b8cb..aad2fb3 100644 --- a/crates/rebel-lang/src/typing.rs +++ b/crates/rebel-lang/src/typing.rs @@ -2,7 +2,7 @@ use std::{collections::HashMap, fmt::Display}; use enum_kinds::EnumKind; -use rebel_parse::ast::{self, expr, pat, typ}; +use rebel_parse::ast::{self, expr, typ}; use crate::{func::FuncType, scope::Context}; @@ -79,10 +79,10 @@ impl Type { }) } - pub fn ast_type(ctx: &Context, expr: &typ::Type<'_>) -> Result { + pub fn ast_type(ctx: &Context, typ: &typ::Type<'_>) -> Result { use typ::Type::*; - Ok(match expr { + Ok(match typ { Paren(subexpr) => Self::ast_type(ctx, subexpr)?, Path(path) => Self::path_type(ctx, path)?, Literal(lit) => Self::literal_type(ctx, lit)?, @@ -123,35 +123,6 @@ impl Type { }) } - pub fn ast_stmt_type(ctx: &Context, stmt: &ast::BlockStmt<'_>) -> Result { - match stmt { - ast::BlockStmt::Let { dest: _, expr } => { - // TODO: Assignability, dest type - let mut dest_type = Type::Free; - if let Some(expr) = expr { - let expr_type = Self::ast_expr_type(ctx, expr)?; - dest_type = dest_type.unify(expr_type, Coerce::Assign)?; - } - Ok(dest_type) - } - ast::BlockStmt::Assign { dest, expr } => { - // TODO: Assignability - let pat::Pat::Path(dest_path) = dest.as_ref(); - - let dest_type = ctx - .vars - .lookup(&dest_path.components) - .map(|var| var.inferred_type.clone()) - .ok_or(TypeError)?; - - let expr_type = Self::ast_expr_type(ctx, expr)?; - dest_type.unify(expr_type, Coerce::Assign) - } - ast::BlockStmt::Expr { expr } => Self::ast_expr_type(ctx, expr), - ast::BlockStmt::Empty => Ok(Type::Unit), - } - } - pub fn ast_expr_type(ctx: &Context, expr: &expr::Expr<'_>) -> Result { use expr::Expr::*; -- cgit v1.2.3