diff options
author | Matthias Schiffer <mschiffer@universe-factory.net> | 2024-04-29 20:49:55 +0200 |
---|---|---|
committer | Matthias Schiffer <mschiffer@universe-factory.net> | 2024-04-29 21:15:42 +0200 |
commit | bcec2e72909bc63bdc2cb88808789a04f3a13d72 (patch) | |
tree | dd6a0437ecc216398c324b3ce5ccea1559227831 | |
parent | 62abc9e8059a70b2f8106238d724428af9bda76b (diff) | |
download | rebel-bcec2e72909bc63bdc2cb88808789a04f3a13d72.tar rebel-bcec2e72909bc63bdc2cb88808789a04f3a13d72.zip |
rebel-parse, rebel-lang: distinguish let statements and simple assignments
-rw-r--r-- | crates/rebel-lang/src/scope.rs | 76 | ||||
-rw-r--r-- | crates/rebel-lang/src/typing.rs | 22 | ||||
-rw-r--r-- | crates/rebel-parse/src/ast/mod.rs | 24 | ||||
-rw-r--r-- | crates/rebel-parse/src/grammar/recipe.rs | 10 |
4 files changed, 115 insertions, 17 deletions
diff --git a/crates/rebel-lang/src/scope.rs b/crates/rebel-lang/src/scope.rs index c57fd3c..0a906de 100644 --- a/crates/rebel-lang/src/scope.rs +++ b/crates/rebel-lang/src/scope.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, ops::Deref}; +use std::collections::HashMap; use rebel_parse::ast::{self, pat}; @@ -44,19 +44,48 @@ impl Default for Context { impl Context { pub fn record_type(&mut self, stmt: &ast::BlockStmt) -> typing::Result<Type> { match stmt { - ast::BlockStmt::Assign { dest, expr } => { - let typ = Type::ast_expr_type(self, expr)?; + 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.deref(); + let ast::TypedPat { pat, typ: _ } = dest.as_ref(); - // TODO: Handle other assignable expressions let pat::Pat::Path(dest_path) = pat; let [dest_ident] = dest_path.components[..] else { return Err(TypeError); }; // 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) + } + 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); + } + + // TODO: Lexical scoping + // TODO: Typechecking self.vars.0.insert( dest_ident.name.to_owned(), ModuleEntry::Def(Var { @@ -75,15 +104,44 @@ impl Context { 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 pat::Pat::Path(dest_path) = pat; + let [dest_ident] = dest_path.components[..] else { + return Err(EvalError); + }; + + if dest_ident.name == "_" { + return Ok(Value::Unit); + } + + // 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) + } ast::BlockStmt::Assign { dest, expr } => { let value = Value::eval(self, expr)?; let typ = value.typ().or(Err(EvalError))?; // TODO: Handle explicit type - let ast::TypedPat { pat, typ: _ } = dest.deref(); - - // TODO: Handle other assignable expressions - let pat::Pat::Path(dest_path) = pat; + let pat::Pat::Path(dest_path) = dest.as_ref(); let [dest_ident] = dest_path.components[..] else { return Err(EvalError); }; diff --git a/crates/rebel-lang/src/typing.rs b/crates/rebel-lang/src/typing.rs index 6bb0fb5..70fc759 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, typ}; +use rebel_parse::ast::{self, expr, pat, typ}; use crate::{func::FuncType, scope::Context}; @@ -125,9 +125,25 @@ impl Type { pub fn ast_stmt_type(ctx: &Context, stmt: &ast::BlockStmt<'_>) -> Result<Type> { match stmt { - ast::BlockStmt::Assign { dest: _, expr } => { + ast::BlockStmt::Let { dest: _, expr } => { // TODO: Assignability, dest type - let dest_type = Type::Free; + 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) } diff --git a/crates/rebel-parse/src/ast/mod.rs b/crates/rebel-parse/src/ast/mod.rs index ebf7241..4fa8e63 100644 --- a/crates/rebel-parse/src/ast/mod.rs +++ b/crates/rebel-parse/src/ast/mod.rs @@ -53,8 +53,12 @@ impl<'a> Block<'a> { #[derive(Debug, Clone, PartialEq, Eq)] pub enum BlockStmt<'a> { - Assign { + Let { dest: Box<TypedPat<'a>>, + expr: Option<Box<Expr<'a>>>, + }, + Assign { + dest: Box<Pat<'a>>, expr: Box<Expr<'a>>, }, Expr { @@ -64,15 +68,22 @@ pub enum BlockStmt<'a> { } impl<'a> BlockStmt<'a> { + pub(crate) fn let_assign(dest: TypedPat<'a>, expr: Option<Expr<'a>>) -> Self { + BlockStmt::Let { + dest: Box::new(dest), + expr: expr.map(Box::new), + } + } + pub(crate) fn assign( - dest: TypedPat<'a>, + dest: Pat<'a>, op: Option<OpBinary>, swapped: bool, expr: Expr<'a>, ) -> Self { let expr = match op { Some(op) => { - let dest_expr = match &dest.pat { + let dest_expr = match &dest { Pat::Path(path) => Expr::Path(path.clone()), }; if swapped { @@ -91,6 +102,13 @@ impl<'a> BlockStmt<'a> { pub fn validate(&self) -> Result<(), ValidationError> { match self { + BlockStmt::Let { dest: _, expr } => { + // TODO: Destination validation + if let Some(expr) = expr { + expr.validate()?; + } + Ok(()) + } BlockStmt::Assign { dest: _, expr } => { // TODO: Destination validation expr.validate() diff --git a/crates/rebel-parse/src/grammar/recipe.rs b/crates/rebel-parse/src/grammar/recipe.rs index 6b96c85..c9fe1e2 100644 --- a/crates/rebel-parse/src/grammar/recipe.rs +++ b/crates/rebel-parse/src/grammar/recipe.rs @@ -34,10 +34,16 @@ peg::parser! { = block:block_stmt() ++ p(';') { ast::Block(block) } pub rule block_stmt() -> ast::BlockStmt<'a> - = dest:typed_pat() op:assign_op() expr:expr() { + = [Token::Keyword(Keyword::Let)] dest:typed_pat() p('=') expr:expr() { + ast::BlockStmt::let_assign(dest, Some(expr)) + } + / [Token::Keyword(Keyword::Let)] dest:typed_pat() { + ast::BlockStmt::let_assign(dest, None) + } + / dest:pat() op:assign_op() expr:expr() { ast::BlockStmt::assign(dest, op, false, expr) } - / dest:typed_pat() p2('=', '+') expr:expr() { + / dest:pat() p2('=', '+') expr:expr() { ast::BlockStmt::assign(dest, Some(Add), true, expr) } / expr:expr() { |