summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Schiffer <mschiffer@universe-factory.net>2024-05-04 13:45:43 +0200
committerMatthias Schiffer <mschiffer@universe-factory.net>2024-05-04 20:14:47 +0200
commit318a842241fccef0184c69e31a7d08306799c6c6 (patch)
tree74ac021a78a06f4f253793c98c87149d705ec675
parent50443fbd17651861a738d940d8ebd2883f6f2651 (diff)
downloadrebel-318a842241fccef0184c69e31a7d08306799c6c6.tar
rebel-318a842241fccef0184c69e31a7d08306799c6c6.zip
rebel-parse, rebel-lang: implement if-else expressions
-rw-r--r--crates/rebel-lang/src/typing.rs28
-rw-r--r--crates/rebel-lang/src/value.rs21
-rw-r--r--crates/rebel-parse/src/ast/expr.rs17
-rw-r--r--crates/rebel-parse/src/grammar/recipe.rs15
4 files changed, 78 insertions, 3 deletions
diff --git a/crates/rebel-lang/src/typing.rs b/crates/rebel-lang/src/typing.rs
index 5a0ca0a..6c5ba81 100644
--- a/crates/rebel-lang/src/typing.rs
+++ b/crates/rebel-lang/src/typing.rs
@@ -200,6 +200,10 @@ impl<'scope> Context<'scope> {
Index { base, index } => self.type_index(base, index),
Field { base, field } => self.type_field(base, field),
Block(block) => self.type_block(block),
+ IfElse {
+ if_blocks,
+ else_block,
+ } => self.type_ifelse(if_blocks, else_block),
Paren(subexpr) => self.type_expr(subexpr),
Path(path) => self.type_path(path),
Literal(lit) => self.type_literal(lit),
@@ -379,6 +383,30 @@ impl<'scope> Context<'scope> {
Ok(ret)
}
+ fn type_ifelse(
+ &mut self,
+ if_blocks: &[(expr::Expr, ast::Block)],
+ else_block: &Option<Box<ast::Block>>,
+ ) -> Result<Type> {
+ let (mut ret, mut common_upvalues) = if let Some(block) = else_block {
+ self.type_scope(&block.0)?
+ } else {
+ (Type::Unit, HashSet::new())
+ };
+
+ for (cond, block) in if_blocks {
+ Type::Bool.unify(self.type_expr(cond)?, Coerce::Assign)?;
+
+ let (typ, upvalues) = self.type_scope(&block.0)?;
+ ret = ret.unify(typ, Coerce::Common)?;
+ common_upvalues = &common_upvalues & &upvalues;
+ }
+
+ self.0.initialize_all(common_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) {
diff --git a/crates/rebel-lang/src/value.rs b/crates/rebel-lang/src/value.rs
index 2b07803..97a6f8b 100644
--- a/crates/rebel-lang/src/value.rs
+++ b/crates/rebel-lang/src/value.rs
@@ -107,6 +107,10 @@ impl<'scope> Context<'scope> {
Index { base, index } => self.eval_index(base, index),
Field { base, field } => self.eval_field(base, field),
Block(block) => self.eval_block(block),
+ IfElse {
+ if_blocks,
+ else_block,
+ } => self.eval_ifelse(if_blocks, else_block),
Paren(subexpr) => self.eval_expr(subexpr),
Path(path) => self.eval_path(path),
Literal(lit) => self.eval_literal(lit),
@@ -323,6 +327,23 @@ impl<'scope> Context<'scope> {
Ok(ret)
}
+ fn eval_ifelse(
+ &mut self,
+ if_blocks: &[(expr::Expr, ast::Block)],
+ else_block: &Option<Box<ast::Block>>,
+ ) -> Result<Value> {
+ for (cond, block) in if_blocks {
+ if self.eval_expr(cond)? == Value::Bool(true) {
+ return self.eval_block(block);
+ }
+ }
+ if let Some(block) = else_block {
+ self.eval_block(block)
+ } else {
+ Ok(Value::Unit)
+ }
+ }
+
fn eval_path(&self, path: &ast::Path<'_>) -> Result<Value> {
Ok(self.0.lookup_value(path)?.clone())
}
diff --git a/crates/rebel-parse/src/ast/expr.rs b/crates/rebel-parse/src/ast/expr.rs
index 2e8d750..3994296 100644
--- a/crates/rebel-parse/src/ast/expr.rs
+++ b/crates/rebel-parse/src/ast/expr.rs
@@ -34,6 +34,10 @@ pub enum Expr<'a> {
field: Ident<'a>,
},
Block(Block<'a>),
+ IfElse {
+ if_blocks: Vec<(Expr<'a>, Block<'a>)>,
+ else_block: Option<Box<Block<'a>>>,
+ },
Paren(Box<Expr<'a>>),
Path(Path<'a>),
Literal(Literal<'a>),
@@ -129,6 +133,19 @@ impl<'a> Expr<'a> {
}
Ok(())
}
+ Expr::IfElse {
+ if_blocks,
+ else_block,
+ } => {
+ for (cond, block) in if_blocks {
+ cond.validate()?;
+ block.validate()?;
+ }
+ if let Some(block) = else_block {
+ block.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 11f1ce6..1ada80c 100644
--- a/crates/rebel-parse/src/grammar/recipe.rs
+++ b/crates/rebel-parse/src/grammar/recipe.rs
@@ -133,15 +133,24 @@ 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 }
}
rule atom() -> Expr<'a>
- = lit:literal() { Expr::Literal(lit) }
+ = p('(') e:expr() p(')') { Expr::paren(e) }
+ / [Token::Keyword(Keyword::If)]
+ if_blocks:(cond_block() ++ ([Token::Keyword(Keyword::Else)] [Token::Keyword(Keyword::If)]))
+ else_block:([Token::Keyword(Keyword::Else)] p('{') block:block() p('}') { Box::new(block) })?
+ {
+ Expr::IfElse { if_blocks, else_block }
+ }
+ / p('{') block:block() p('}') { Expr::Block(block) }
+ / lit:literal() { Expr::Literal(lit) }
/ path:path() { Expr::Path(path) }
+ rule cond_block() -> (Expr<'a>, ast::Block<'a>)
+ = cond:expr() p('{') block:block() p('}') { (cond, block) }
+
rule call_params() -> Vec<expr::Expr<'a>>
= args:delimited(<expr()>, <p(',')>) { args }