diff options
Diffstat (limited to 'crates/rebel-lang/src/scope.rs')
-rw-r--r-- | crates/rebel-lang/src/scope.rs | 175 |
1 files changed, 96 insertions, 79 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<Type> { 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<Value> { 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) } } |