Begin implementing if/else

This commit is contained in:
Jeff 2024-03-08 14:01:05 -05:00
parent ec9f17070c
commit 5571418d44
5 changed files with 135 additions and 4 deletions

View File

@ -0,0 +1,92 @@
use crate::{
context::Context,
error::{RuntimeError, ValidationError},
};
use super::{AbstractTree, Action, Expression, Statement, Type};
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct IfElse<'src> {
if_expression: Expression<'src>,
if_statement: Box<Statement<'src>>,
else_statement: Option<Box<Statement<'src>>>,
}
impl<'src> IfElse<'src> {
pub fn new(
if_expression: Expression<'src>,
if_statement: Statement<'src>,
else_statement: Option<Statement<'src>>,
) -> Self {
Self {
if_expression,
if_statement: Box::new(if_statement),
else_statement: else_statement.map(|statement| Box::new(statement)),
}
}
}
impl<'src> AbstractTree for IfElse<'src> {
fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> {
todo!()
}
fn validate(&self, _context: &Context) -> Result<(), ValidationError> {
todo!()
}
fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
let if_boolean = self
.if_expression
.run(_context)?
.as_return_value()?
.as_boolean()?;
if if_boolean {
self.if_statement.run(_context)
} else if let Some(else_statement) = self.else_statement {
else_statement.run(_context)
} else {
Ok(Action::None)
}
}
}
#[cfg(test)]
mod tests {
use crate::{
abstract_tree::{Action, ValueNode},
context::Context,
Value,
};
use super::*;
#[test]
fn simple_if() {
assert_eq!(
IfElse::new(
Expression::Value(ValueNode::Boolean(true)),
Statement::Expression(Expression::Value(ValueNode::String("foo"))),
None
)
.run(&Context::new()),
Ok(Action::Return(Value::string("foo")))
)
}
#[test]
fn simple_if_else() {
assert_eq!(
IfElse::new(
Expression::Value(ValueNode::Boolean(false)),
Statement::Expression(Expression::Value(ValueNode::String("foo"))),
Some(Statement::Expression(Expression::Value(ValueNode::String(
"bar"
))))
)
.run(&Context::new()),
Ok(Action::Return(Value::string("bar")))
)
}
}

View File

@ -2,6 +2,7 @@ pub mod assignment;
pub mod block;
pub mod expression;
pub mod identifier;
pub mod if_else;
pub mod index;
pub mod logic;
pub mod r#loop;
@ -15,6 +16,7 @@ pub use self::{
block::Block,
expression::Expression,
identifier::Identifier,
if_else::IfElse,
index::Index,
logic::Logic,
math::Math,

View File

@ -3,7 +3,7 @@ use crate::{
error::{RuntimeError, ValidationError},
};
use super::{AbstractTree, Action, Assignment, Block, Expression, Loop, Type};
use super::{AbstractTree, Action, Assignment, Block, Expression, IfElse, Loop, Type};
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum Statement<'src> {
@ -11,6 +11,7 @@ pub enum Statement<'src> {
Block(Block<'src>),
Break(Expression<'src>),
Expression(Expression<'src>),
IfElse(IfElse<'src>),
Loop(Loop<'src>),
}
@ -21,6 +22,7 @@ impl<'src> AbstractTree for Statement<'src> {
Statement::Block(block) => block.expected_type(_context),
Statement::Break(expression) => expression.expected_type(_context),
Statement::Expression(expression) => expression.expected_type(_context),
Statement::IfElse(_) => todo!(),
Statement::Loop(r#loop) => r#loop.expected_type(_context),
}
}
@ -31,6 +33,7 @@ impl<'src> AbstractTree for Statement<'src> {
Statement::Block(_) => todo!(),
Statement::Break(expression) => expression.validate(_context),
Statement::Expression(expression) => expression.validate(_context),
Statement::IfElse(_) => todo!(),
Statement::Loop(r#loop) => r#loop.validate(_context),
}
}
@ -41,6 +44,7 @@ impl<'src> AbstractTree for Statement<'src> {
Statement::Block(_) => todo!(),
Statement::Break(expression) => expression.run(_context),
Statement::Expression(expression) => expression.run(_context),
Statement::IfElse(_) => todo!(),
Statement::Loop(r#loop) => r#loop.run(_context),
}
}

View File

@ -208,8 +208,10 @@ pub fn lexer<'src>() -> impl Parser<
let keyword = choice((
just("bool").padded(),
just("break").padded(),
just("else").padded(),
just("float").padded(),
just("int").padded(),
just("if").padded(),
just("list").padded(),
just("map").padded(),
just("range").padded(),

View File

@ -217,7 +217,7 @@ pub fn parser<'src>() -> DustParser<'src> {
.boxed();
let r#break = just(Token::Keyword("break"))
.ignore_then(expression)
.ignore_then(expression.clone())
.map(|expression| Statement::Break(expression));
let assignment = identifier
@ -256,7 +256,24 @@ pub fn parser<'src>() -> DustParser<'src> {
.map(|statements| Statement::Loop(Loop::new(statements)))
.boxed();
choice((assignment, expression_statement, r#break, block, r#loop))
let if_else = just(Token::Keyword("if"))
.ignore_then(expression.clone())
.then(statement.clone())
.then_ignore(just(Token::Keyword("else")))
.then(statement.clone().or_not())
.map(|((if_expression, if_block), else_block)| {
Statement::IfElse(IfElse::new(if_expression, if_block, else_block))
})
.boxed();
choice((
assignment,
expression_statement,
r#break,
block,
r#loop,
if_else,
))
.then_ignore(just(Token::Control(Control::Semicolon)).or_not())
});
@ -273,6 +290,20 @@ mod tests {
use super::*;
#[test]
fn if_else() {
assert_eq!(
parse(&lex("if true 'foo' else 'bar'").unwrap()).unwrap()[0].0,
Statement::IfElse(IfElse::new(
Expression::Value(ValueNode::Boolean(true)),
Statement::Expression(Expression::Value(ValueNode::String("foo"))),
Some(Statement::Expression(Expression::Value(ValueNode::String(
"bar"
))))
))
)
}
#[test]
fn map() {
assert_eq!(