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 block;
|
||||||
pub mod expression;
|
pub mod expression;
|
||||||
pub mod identifier;
|
pub mod identifier;
|
||||||
|
pub mod if_else;
|
||||||
pub mod index;
|
pub mod index;
|
||||||
pub mod logic;
|
pub mod logic;
|
||||||
pub mod r#loop;
|
pub mod r#loop;
|
||||||
@ -15,6 +16,7 @@ pub use self::{
|
|||||||
block::Block,
|
block::Block,
|
||||||
expression::Expression,
|
expression::Expression,
|
||||||
identifier::Identifier,
|
identifier::Identifier,
|
||||||
|
if_else::IfElse,
|
||||||
index::Index,
|
index::Index,
|
||||||
logic::Logic,
|
logic::Logic,
|
||||||
math::Math,
|
math::Math,
|
||||||
|
@ -3,7 +3,7 @@ use crate::{
|
|||||||
error::{RuntimeError, ValidationError},
|
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)]
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub enum Statement<'src> {
|
pub enum Statement<'src> {
|
||||||
@ -11,6 +11,7 @@ pub enum Statement<'src> {
|
|||||||
Block(Block<'src>),
|
Block(Block<'src>),
|
||||||
Break(Expression<'src>),
|
Break(Expression<'src>),
|
||||||
Expression(Expression<'src>),
|
Expression(Expression<'src>),
|
||||||
|
IfElse(IfElse<'src>),
|
||||||
Loop(Loop<'src>),
|
Loop(Loop<'src>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,6 +22,7 @@ impl<'src> AbstractTree for Statement<'src> {
|
|||||||
Statement::Block(block) => block.expected_type(_context),
|
Statement::Block(block) => block.expected_type(_context),
|
||||||
Statement::Break(expression) => expression.expected_type(_context),
|
Statement::Break(expression) => expression.expected_type(_context),
|
||||||
Statement::Expression(expression) => expression.expected_type(_context),
|
Statement::Expression(expression) => expression.expected_type(_context),
|
||||||
|
Statement::IfElse(_) => todo!(),
|
||||||
Statement::Loop(r#loop) => r#loop.expected_type(_context),
|
Statement::Loop(r#loop) => r#loop.expected_type(_context),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -31,6 +33,7 @@ impl<'src> AbstractTree for Statement<'src> {
|
|||||||
Statement::Block(_) => todo!(),
|
Statement::Block(_) => todo!(),
|
||||||
Statement::Break(expression) => expression.validate(_context),
|
Statement::Break(expression) => expression.validate(_context),
|
||||||
Statement::Expression(expression) => expression.validate(_context),
|
Statement::Expression(expression) => expression.validate(_context),
|
||||||
|
Statement::IfElse(_) => todo!(),
|
||||||
Statement::Loop(r#loop) => r#loop.validate(_context),
|
Statement::Loop(r#loop) => r#loop.validate(_context),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -41,6 +44,7 @@ impl<'src> AbstractTree for Statement<'src> {
|
|||||||
Statement::Block(_) => todo!(),
|
Statement::Block(_) => todo!(),
|
||||||
Statement::Break(expression) => expression.run(_context),
|
Statement::Break(expression) => expression.run(_context),
|
||||||
Statement::Expression(expression) => expression.run(_context),
|
Statement::Expression(expression) => expression.run(_context),
|
||||||
|
Statement::IfElse(_) => todo!(),
|
||||||
Statement::Loop(r#loop) => r#loop.run(_context),
|
Statement::Loop(r#loop) => r#loop.run(_context),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,8 +208,10 @@ pub fn lexer<'src>() -> impl Parser<
|
|||||||
let keyword = choice((
|
let keyword = choice((
|
||||||
just("bool").padded(),
|
just("bool").padded(),
|
||||||
just("break").padded(),
|
just("break").padded(),
|
||||||
|
just("else").padded(),
|
||||||
just("float").padded(),
|
just("float").padded(),
|
||||||
just("int").padded(),
|
just("int").padded(),
|
||||||
|
just("if").padded(),
|
||||||
just("list").padded(),
|
just("list").padded(),
|
||||||
just("map").padded(),
|
just("map").padded(),
|
||||||
just("range").padded(),
|
just("range").padded(),
|
||||||
|
@ -217,7 +217,7 @@ pub fn parser<'src>() -> DustParser<'src> {
|
|||||||
.boxed();
|
.boxed();
|
||||||
|
|
||||||
let r#break = just(Token::Keyword("break"))
|
let r#break = just(Token::Keyword("break"))
|
||||||
.ignore_then(expression)
|
.ignore_then(expression.clone())
|
||||||
.map(|expression| Statement::Break(expression));
|
.map(|expression| Statement::Break(expression));
|
||||||
|
|
||||||
let assignment = identifier
|
let assignment = identifier
|
||||||
@ -256,8 +256,25 @@ pub fn parser<'src>() -> DustParser<'src> {
|
|||||||
.map(|statements| Statement::Loop(Loop::new(statements)))
|
.map(|statements| Statement::Loop(Loop::new(statements)))
|
||||||
.boxed();
|
.boxed();
|
||||||
|
|
||||||
choice((assignment, expression_statement, r#break, block, r#loop))
|
let if_else = just(Token::Keyword("if"))
|
||||||
.then_ignore(just(Token::Control(Control::Semicolon)).or_not())
|
.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
|
statement
|
||||||
@ -273,6 +290,20 @@ mod tests {
|
|||||||
|
|
||||||
use super::*;
|
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]
|
#[test]
|
||||||
fn map() {
|
fn map() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
Loading…
Reference in New Issue
Block a user