Begin implementing if/else
This commit is contained in:
parent
ec9f17070c
commit
5571418d44
92
src/abstract_tree/if_else.rs
Normal file
92
src/abstract_tree/if_else.rs
Normal 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")))
|
||||
)
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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!(
|
||||
|
Loading…
Reference in New Issue
Block a user