From 5571418d448bef4346bd464c272fe1aed9e8a090 Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 8 Mar 2024 14:01:05 -0500 Subject: [PATCH] Begin implementing if/else --- src/abstract_tree/if_else.rs | 92 ++++++++++++++++++++++++++++++++++ src/abstract_tree/mod.rs | 2 + src/abstract_tree/statement.rs | 6 ++- src/lexer.rs | 2 + src/parser.rs | 37 ++++++++++++-- 5 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 src/abstract_tree/if_else.rs diff --git a/src/abstract_tree/if_else.rs b/src/abstract_tree/if_else.rs new file mode 100644 index 0000000..5a4ac10 --- /dev/null +++ b/src/abstract_tree/if_else.rs @@ -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>, + else_statement: Option>>, +} + +impl<'src> IfElse<'src> { + pub fn new( + if_expression: Expression<'src>, + if_statement: Statement<'src>, + else_statement: Option>, + ) -> 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 { + todo!() + } + + fn validate(&self, _context: &Context) -> Result<(), ValidationError> { + todo!() + } + + fn run(self, _context: &Context) -> Result { + 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"))) + ) + } +} diff --git a/src/abstract_tree/mod.rs b/src/abstract_tree/mod.rs index b84b9db..f5215a4 100644 --- a/src/abstract_tree/mod.rs +++ b/src/abstract_tree/mod.rs @@ -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, diff --git a/src/abstract_tree/statement.rs b/src/abstract_tree/statement.rs index e226713..5791771 100644 --- a/src/abstract_tree/statement.rs +++ b/src/abstract_tree/statement.rs @@ -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), } } diff --git a/src/lexer.rs b/src/lexer.rs index 930e470..d7a8fca 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -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(), diff --git a/src/parser.rs b/src/parser.rs index 3cc4909..a56a930 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -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,8 +256,25 @@ pub fn parser<'src>() -> DustParser<'src> { .map(|statements| Statement::Loop(Loop::new(statements))) .boxed(); - choice((assignment, expression_statement, r#break, block, r#loop)) - .then_ignore(just(Token::Control(Control::Semicolon)).or_not()) + 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()) }); statement @@ -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!(