summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--crates/rebel-lang/src/typing.rs52
-rw-r--r--crates/rebel-lang/src/value.rs61
-rw-r--r--crates/rebel-parse/src/ast/expr.rs9
-rw-r--r--crates/rebel-parse/src/grammar/recipe.rs1
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 }
}