diff options
-rw-r--r-- | crates/rebel-lang/src/typing.rs | 52 | ||||
-rw-r--r-- | crates/rebel-lang/src/value.rs | 61 | ||||
-rw-r--r-- | crates/rebel-parse/src/ast/expr.rs | 9 | ||||
-rw-r--r-- | crates/rebel-parse/src/grammar/recipe.rs | 1 |
4 files changed, 96 insertions, 27 deletions
diff --git a/crates/rebel-lang/src/typing.rs b/crates/rebel-lang/src/typing.rs index bc6b0ce..5a0ca0a 100644 --- a/crates/rebel-lang/src/typing.rs +++ b/crates/rebel-lang/src/typing.rs @@ -1,4 +1,8 @@ -use std::{collections::HashMap, fmt::Display, rc::Rc}; +use std::{ + collections::{HashMap, HashSet}, + fmt::Display, + rc::Rc, +}; use enum_kinds::EnumKind; @@ -181,7 +185,7 @@ impl<'scope> Context<'scope> { }) } - pub fn type_expr(&self, expr: &expr::Expr<'_>) -> Result<Type> { + pub fn type_expr(&mut self, expr: &expr::Expr<'_>) -> Result<Type> { use expr::Expr::*; match expr { @@ -195,6 +199,7 @@ impl<'scope> Context<'scope> { } => self.type_method(expr, method, params), Index { base, index } => self.type_index(base, index), Field { base, field } => self.type_field(base, field), + Block(block) => self.type_block(block), Paren(subexpr) => self.type_expr(subexpr), Path(path) => self.type_path(path), Literal(lit) => self.type_literal(lit), @@ -202,7 +207,7 @@ impl<'scope> Context<'scope> { } fn type_binary_op( - &self, + &mut self, left: &expr::Expr<'_>, op: expr::OpBinary, right: &expr::Expr<'_>, @@ -247,7 +252,7 @@ impl<'scope> Context<'scope> { }) } - fn type_unary_op(&self, op: expr::OpUnary, expr: &expr::Expr<'_>) -> Result<Type> { + fn type_unary_op(&mut self, op: expr::OpUnary, expr: &expr::Expr<'_>) -> Result<Type> { use expr::OpUnary::*; use Type::*; @@ -260,7 +265,7 @@ impl<'scope> Context<'scope> { }) } - fn type_index(&self, base: &expr::Expr<'_>, index: &expr::Expr<'_>) -> Result<Type> { + fn type_index(&mut self, base: &expr::Expr<'_>, index: &expr::Expr<'_>) -> Result<Type> { use Type::*; let base_type = self.type_expr(base)?; @@ -275,7 +280,7 @@ impl<'scope> Context<'scope> { Ok(*elem_type) } - fn type_apply(&self, expr: &expr::Expr<'_>, params: &[expr::Expr]) -> Result<Type> { + fn type_apply(&mut self, expr: &expr::Expr<'_>, params: &[expr::Expr]) -> Result<Type> { use Type::*; let expr_type = self.type_expr(expr)?; @@ -301,7 +306,7 @@ impl<'scope> Context<'scope> { } fn type_method( - &self, + &mut self, expr: &expr::Expr<'_>, method: &ast::Ident<'_>, params: &[expr::Expr], @@ -314,7 +319,8 @@ impl<'scope> Context<'scope> { .methods .get(&type_family) .and_then(|methods| methods.get(method.name)) - .ok_or(Error::lookup("undefined method"))?; + .ok_or(Error::lookup("undefined method"))? + .clone(); let (self_param, func_params) = method .typ @@ -337,7 +343,7 @@ impl<'scope> Context<'scope> { Ok(method.typ.ret.clone()) } - fn type_field(&self, base: &expr::Expr<'_>, field: &ast::Ident<'_>) -> Result<Type> { + fn type_field(&mut self, base: &expr::Expr<'_>, field: &ast::Ident<'_>) -> Result<Type> { use Type::*; let base_type = self.type_expr(base)?; @@ -356,6 +362,23 @@ impl<'scope> Context<'scope> { }) } + fn type_scope(&mut self, stmts: &[ast::BlockStmt]) -> Result<(Type, HashSet<String>)> { + let (ret, upvalues) = self.scoped(|ctx| { + let mut ret = Type::Unit; + for stmt in stmts { + ret = ctx.type_block_stmt(stmt)?; + } + Ok(ret) + }); + Ok((ret?, upvalues)) + } + + fn type_block(&mut self, block: &ast::Block) -> Result<Type> { + let (ret, upvalues) = self.type_scope(&block.0)?; + self.0.initialize_all(upvalues); + Ok(ret) + } + fn type_path(&self, path: &ast::Path<'_>) -> Result<Type> { let var = self.0.lookup_value(path)?; if !self.0.is_initialized(path) { @@ -364,7 +387,7 @@ impl<'scope> Context<'scope> { Ok(var.inferred_type.clone()) } - fn check_string_piece(&self, piece: &expr::StrPiece, kind: expr::StrKind) -> Result<()> { + fn check_string_piece(&mut self, piece: &expr::StrPiece, kind: expr::StrKind) -> Result<()> { let typ = match piece { expr::StrPiece::Chars(_) => return Ok(()), expr::StrPiece::Escape(_) => return Ok(()), @@ -380,7 +403,7 @@ impl<'scope> Context<'scope> { } } - fn type_literal(&self, lit: &expr::Literal<'_>) -> Result<Type> { + fn type_literal(&mut self, lit: &expr::Literal<'_>) -> Result<Type> { use expr::Literal; use Type::*; @@ -488,6 +511,13 @@ impl<'scope> Context<'scope> { Ok(inferred_type) } + + fn scoped<F, R>(&mut self, f: F) -> (R, HashSet<String>) + where + F: FnOnce(&mut Context) -> R, + { + self.0.scoped(|scope| f(&mut Context(scope))) + } } impl Display for Type { diff --git a/crates/rebel-lang/src/value.rs b/crates/rebel-lang/src/value.rs index c3b1630..2b07803 100644 --- a/crates/rebel-lang/src/value.rs +++ b/crates/rebel-lang/src/value.rs @@ -1,5 +1,6 @@ use std::{ - collections::HashMap, + cell::RefCell, + collections::{HashMap, HashSet}, fmt::{Display, Write}, iter, }; @@ -91,7 +92,7 @@ impl<'scope> Context<'scope> { }) } - pub fn eval_expr(&self, expr: &expr::Expr<'_>) -> Result<Value> { + pub fn eval_expr(&mut self, expr: &expr::Expr<'_>) -> Result<Value> { use expr::Expr::*; match expr { @@ -105,6 +106,7 @@ impl<'scope> Context<'scope> { } => self.eval_method(expr, method, params), Index { base, index } => self.eval_index(base, index), Field { base, field } => self.eval_field(base, field), + Block(block) => self.eval_block(block), Paren(subexpr) => self.eval_expr(subexpr), Path(path) => self.eval_path(path), Literal(lit) => self.eval_literal(lit), @@ -112,7 +114,7 @@ impl<'scope> Context<'scope> { } fn eval_binary_op( - &self, + &mut self, left: &expr::Expr<'_>, op: expr::OpBinary, right: &expr::Expr<'_>, @@ -163,7 +165,7 @@ impl<'scope> Context<'scope> { }) } - fn eval_unary_op(&self, op: expr::OpUnary, expr: &expr::Expr<'_>) -> Result<Value> { + fn eval_unary_op(&mut self, op: expr::OpUnary, expr: &expr::Expr<'_>) -> Result<Value> { use expr::OpUnary::*; use Value::*; @@ -178,7 +180,7 @@ impl<'scope> Context<'scope> { }) } - fn eval_index(&self, base: &expr::Expr<'_>, index: &expr::Expr<'_>) -> Result<Value> { + fn eval_index(&mut self, base: &expr::Expr<'_>, index: &expr::Expr<'_>) -> Result<Value> { use Value::*; let base_value = self.eval_expr(base)?; @@ -199,7 +201,7 @@ impl<'scope> Context<'scope> { .ok_or(Error::eval("array index out of bounds")) } - fn eval_apply(&self, expr: &expr::Expr<'_>, params: &[expr::Expr]) -> Result<Value> { + fn eval_apply(&mut self, expr: &expr::Expr<'_>, params: &[expr::Expr]) -> Result<Value> { use Value::*; let value = self.eval_expr(expr)?; @@ -235,7 +237,7 @@ impl<'scope> Context<'scope> { } fn eval_method( - &self, + &mut self, expr: &expr::Expr<'_>, method: &ast::Ident<'_>, params: &[expr::Expr], @@ -249,7 +251,8 @@ impl<'scope> Context<'scope> { .methods .get(&type_family) .and_then(|methods| methods.get(method.name)) - .ok_or(Error::lookup("undefined method"))?; + .ok_or(Error::lookup("undefined method"))? + .clone(); let (self_param_type, func_param_types) = method .typ @@ -284,7 +287,7 @@ impl<'scope> Context<'scope> { } } - fn eval_field(&self, base: &expr::Expr<'_>, field: &ast::Ident<'_>) -> Result<Value> { + fn eval_field(&mut self, base: &expr::Expr<'_>, field: &ast::Ident<'_>) -> Result<Value> { use Value::*; let base_value = self.eval_expr(base)?; @@ -303,11 +306,28 @@ impl<'scope> Context<'scope> { }) } + fn eval_scope(&mut self, stmts: &[ast::BlockStmt]) -> Result<(Value, HashSet<String>)> { + let (ret, upvalues) = self.scoped(|ctx| { + let mut ret = Value::Unit; + for stmt in stmts { + ret = ctx.eval_block_stmt(stmt)?; + } + Ok(ret) + }); + Ok((ret?, upvalues)) + } + + fn eval_block(&mut self, block: &ast::Block) -> Result<Value> { + let (ret, upvalues) = self.eval_scope(&block.0)?; + self.0.initialize_all(upvalues); + Ok(ret) + } + fn eval_path(&self, path: &ast::Path<'_>) -> Result<Value> { Ok(self.0.lookup_value(path)?.clone()) } - fn eval_literal(&self, lit: &expr::Literal<'_>) -> Result<Value> { + fn eval_literal(&mut self, lit: &expr::Literal<'_>) -> Result<Value> { use expr::Literal; use Value::*; @@ -318,7 +338,7 @@ impl<'scope> Context<'scope> { Literal::Str { pieces, kind } => Str(StrDisplay { pieces, kind: *kind, - ctx: self, + ctx: RefCell::new(self), } .to_string()), Literal::Tuple(elems) => Tuple( @@ -392,6 +412,13 @@ impl<'scope> Context<'scope> { Ok(value) } + + fn scoped<F, R>(&mut self, f: F) -> (R, HashSet<String>) + where + F: FnOnce(&mut Context) -> R, + { + self.0.scoped(|scope| f(&mut Context(scope))) + } } impl Display for Value { @@ -504,20 +531,24 @@ impl<'a> Display for ScriptStringify<'a> { } #[derive(Debug)] -struct StrDisplay<'a> { +struct StrDisplay<'a, 'scope> { pieces: &'a [expr::StrPiece<'a>], kind: expr::StrKind, - ctx: &'a Context<'a>, + ctx: RefCell<&'a mut Context<'scope>>, } -impl<'a> Display for StrDisplay<'a> { +impl<'a, 'scope> Display for StrDisplay<'a, 'scope> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { for piece in self.pieces { match piece { expr::StrPiece::Chars(chars) => f.write_str(chars)?, expr::StrPiece::Escape(c) => f.write_char(*c)?, expr::StrPiece::Interp(expr) => { - let val = self.ctx.eval_expr(expr).or(Err(std::fmt::Error))?; + let val = self + .ctx + .borrow_mut() + .eval_expr(expr) + .or(Err(std::fmt::Error))?; match self.kind { expr::StrKind::Regular => Stringify(&val).fmt(f), expr::StrKind::Raw => unreachable!(), diff --git a/crates/rebel-parse/src/ast/expr.rs b/crates/rebel-parse/src/ast/expr.rs index 9ac4a38..2e8d750 100644 --- a/crates/rebel-parse/src/ast/expr.rs +++ b/crates/rebel-parse/src/ast/expr.rs @@ -1,6 +1,6 @@ use std::collections::HashSet; -use super::{DestrPat, Ident, Path, ValidationError}; +use super::{Block, DestrPat, Ident, Path, ValidationError}; use crate::token; pub use token::StrKind; @@ -33,6 +33,7 @@ pub enum Expr<'a> { base: Box<Expr<'a>>, field: Ident<'a>, }, + Block(Block<'a>), Paren(Box<Expr<'a>>), Path(Path<'a>), Literal(Literal<'a>), @@ -122,6 +123,12 @@ impl<'a> Expr<'a> { base.validate() } Expr::Field { base, field: _ } => base.validate(), + Expr::Block(block) => { + for stmt in &block.0 { + stmt.validate()?; + } + Ok(()) + } Expr::Paren(expr) => expr.validate(), Expr::Path(_) => Ok(()), Expr::Literal(lit) => lit.validate(), diff --git a/crates/rebel-parse/src/grammar/recipe.rs b/crates/rebel-parse/src/grammar/recipe.rs index 3ce06c1..11f1ce6 100644 --- a/crates/rebel-parse/src/grammar/recipe.rs +++ b/crates/rebel-parse/src/grammar/recipe.rs @@ -134,6 +134,7 @@ peg::parser! { base:@ p('.') field:field() { Expr::field(base, field) } -- p('(') e:expr() p(')') { Expr::paren(e) } + p('{') block:block() p('}') { Expr::Block(block) } e:atom() { e } } |