summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Schiffer <mschiffer@universe-factory.net>2024-04-29 23:07:07 +0200
committerMatthias Schiffer <mschiffer@universe-factory.net>2024-04-29 23:14:57 +0200
commit29ff7f69c654b5f0d3ea5a9d34ea747fab1e2d3d (patch)
tree01bfe16251477cb8622fcaa890f6a3c7f023a707
parent2d68c8428cc4b7418bf8e556c8c3f430a4ec1211 (diff)
downloadrebel-29ff7f69c654b5f0d3ea5a9d34ea747fab1e2d3d.tar
rebel-29ff7f69c654b5f0d3ea5a9d34ea747fab1e2d3d.zip
rebel-lang: merge ast_stmt_type() into record_type() and execute()
This deduplicates some code and improves type checking.
-rw-r--r--crates/rebel-lang/src/scope.rs175
-rw-r--r--crates/rebel-lang/src/typing.rs35
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<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)
}
}
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<Type> {
+ pub fn ast_type(ctx: &Context, typ: &typ::Type<'_>) -> Result<Type> {
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<Type> {
- 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<Type> {
use expr::Expr::*;