Implement if/else, loops and breaks

This commit is contained in:
Jeff 2024-03-08 14:29:53 -05:00
parent 5571418d44
commit b7288ceed8
5 changed files with 57 additions and 17 deletions

View File

@ -28,11 +28,15 @@ impl<'src> IfElse<'src> {
impl<'src> AbstractTree for IfElse<'src> {
fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> {
todo!()
Ok(Type::None)
}
fn validate(&self, _context: &Context) -> Result<(), ValidationError> {
todo!()
if let Type::Boolean = self.if_expression.expected_type(_context)? {
Ok(())
} else {
Err(ValidationError::ExpectedBoolean)
}
}
fn run(self, _context: &Context) -> Result<Action, RuntimeError> {

View File

@ -40,11 +40,12 @@ impl<'src> AbstractTree for Loop<'src> {
}
let statement = self.statements[index].clone();
let action = statement.run(_context)?;
if let Statement::Break(expression) = statement {
break expression.run(_context);
} else {
statement.run(_context)?;
match action {
Action::Return(_) => {}
Action::None => {}
r#break => return Ok(r#break),
}
}
}
@ -66,6 +67,6 @@ mod tests {
}
.run(&Context::new());
assert_eq!(result, Ok(Action::Return(Value::integer(42))))
assert_eq!(result, Ok(Action::Break(Value::integer(42))))
}
}

View File

@ -22,7 +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::IfElse(if_else) => if_else.expected_type(_context),
Statement::Loop(r#loop) => r#loop.expected_type(_context),
}
}
@ -30,10 +30,10 @@ impl<'src> AbstractTree for Statement<'src> {
fn validate(&self, _context: &Context) -> Result<(), ValidationError> {
match self {
Statement::Assignment(assignment) => assignment.validate(_context),
Statement::Block(_) => todo!(),
Statement::Block(block) => block.validate(_context),
Statement::Break(expression) => expression.validate(_context),
Statement::Expression(expression) => expression.validate(_context),
Statement::IfElse(_) => todo!(),
Statement::IfElse(if_else) => if_else.validate(_context),
Statement::Loop(r#loop) => r#loop.validate(_context),
}
}
@ -41,10 +41,14 @@ impl<'src> AbstractTree for Statement<'src> {
fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
match self {
Statement::Assignment(assignment) => assignment.run(_context),
Statement::Block(_) => todo!(),
Statement::Break(expression) => expression.run(_context),
Statement::Block(block) => block.run(_context),
Statement::Break(expression) => {
let value = expression.run(_context)?.as_return_value()?;
Ok(Action::Break(value))
}
Statement::Expression(expression) => expression.run(_context),
Statement::IfElse(_) => todo!(),
Statement::IfElse(if_else) => if_else.run(_context),
Statement::Loop(r#loop) => r#loop.run(_context),
}
}

View File

@ -259,10 +259,13 @@ pub fn parser<'src>() -> DustParser<'src> {
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))
.then(
just(Token::Keyword("else"))
.ignore_then(statement.clone())
.or_not(),
)
.map(|((if_expression, if_statement), else_statement)| {
Statement::IfElse(IfElse::new(if_expression, if_statement, else_statement))
})
.boxed();
@ -290,6 +293,18 @@ mod tests {
use super::*;
#[test]
fn r#if() {
assert_eq!(
parse(&lex("if true 'foo'").unwrap()).unwrap()[0].0,
Statement::IfElse(IfElse::new(
Expression::Value(ValueNode::Boolean(true)),
Statement::Expression(Expression::Value(ValueNode::String("foo"))),
None
))
)
}
#[test]
fn if_else() {
assert_eq!(

View File

@ -18,3 +18,19 @@ fn loops_and_breaks() {
Ok(Some(Value::string("foobar")))
)
}
#[test]
fn r#if() {
assert_eq!(
interpret("if true 'foobar'"),
Ok(Some(Value::string("foobar")))
)
}
#[test]
fn if_else() {
assert_eq!(
interpret("if false 'foo' else 'bar'"),
Ok(Some(Value::string("bar")))
)
}