Rewrite while loops

This commit is contained in:
Jeff 2024-03-17 16:59:52 -04:00
parent fed119f38b
commit c3402394a2
7 changed files with 235 additions and 82 deletions

View File

@ -1,6 +1,7 @@
use crate::{ use crate::{
context::Context, context::Context,
error::{RuntimeError, ValidationError}, error::{RuntimeError, ValidationError},
value::ValueInner,
}; };
use super::{AbstractTree, Action, Expression, Type, WithPosition}; use super::{AbstractTree, Action, Expression, Type, WithPosition};
@ -25,24 +26,43 @@ impl FunctionCall {
impl AbstractTree for FunctionCall { impl AbstractTree for FunctionCall {
fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> { fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> {
if let Type::Function { return_type, .. } = self.function.node.expected_type(_context)? { let function_node_type = self.function.node.expected_type(_context)?;
if let Type::Function { return_type, .. } = function_node_type {
Ok(*return_type) Ok(*return_type)
} else { } else {
Err(ValidationError::ExpectedFunction) Err(ValidationError::ExpectedFunction {
actual: function_node_type,
position: self.function.position,
})
} }
} }
fn validate(&self, _context: &Context) -> Result<(), ValidationError> { fn validate(&self, _context: &Context) -> Result<(), ValidationError> {
if let Type::Function { .. } = self.function.node.expected_type(_context)? { let function_node_type = self.function.node.expected_type(_context)?;
if let Type::Function { .. } = function_node_type {
Ok(()) Ok(())
} else { } else {
Err(ValidationError::ExpectedFunction) Err(ValidationError::ExpectedFunction {
actual: function_node_type,
position: self.function.position,
})
} }
} }
fn run(self, context: &Context) -> Result<Action, RuntimeError> { fn run(self, context: &Context) -> Result<Action, RuntimeError> {
let value = self.function.node.run(context)?.as_return_value()?; let value = self.function.node.run(context)?.as_return_value()?;
let function = value.as_function()?; let function = if let ValueInner::Function(function) = value.inner().as_ref() {
function
} else {
return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedFunction {
actual: value.r#type(),
position: self.function.position,
},
));
};
let mut arguments = Vec::with_capacity(self.arguments.len()); let mut arguments = Vec::with_capacity(self.arguments.len());
for expression in self.arguments { for expression in self.arguments {

View File

@ -1,6 +1,7 @@
use crate::{ use crate::{
context::Context, context::Context,
error::{RuntimeError, ValidationError}, error::{RuntimeError, ValidationError},
value::ValueInner,
}; };
use super::{AbstractTree, Action, Block, Expression, Type, WithPosition}; use super::{AbstractTree, Action, Block, Expression, Type, WithPosition};
@ -35,7 +36,9 @@ impl AbstractTree for IfElse {
self.if_expression.node.validate(context)?; self.if_expression.node.validate(context)?;
self.if_block.validate(context)?; self.if_block.validate(context)?;
if let Type::Boolean = self.if_expression.node.expected_type(context)? { let if_expression_type = self.if_expression.node.expected_type(context)?;
if let Type::Boolean = if_expression_type {
if let Some(else_block) = &self.else_block { if let Some(else_block) = &self.else_block {
else_block.validate(context)?; else_block.validate(context)?;
@ -53,25 +56,32 @@ impl AbstractTree for IfElse {
Ok(()) Ok(())
} else { } else {
Err(ValidationError::ExpectedBoolean) Err(ValidationError::ExpectedBoolean {
actual: if_expression_type,
position: self.if_expression.position,
})
} }
} }
fn run(self, _context: &Context) -> Result<Action, RuntimeError> { fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
let if_boolean = self let value = self.if_expression.node.run(_context)?.as_return_value()?;
.if_expression
.node
.run(_context)?
.as_return_value()?
.as_boolean()?;
if if_boolean { if let ValueInner::Boolean(if_boolean) = value.inner().as_ref() {
if *if_boolean {
self.if_block.run(_context) self.if_block.run(_context)
} else if let Some(else_statement) = self.else_block { } else if let Some(else_statement) = self.else_block {
else_statement.run(_context) else_statement.run(_context)
} else { } else {
Ok(Action::None) Ok(Action::None)
} }
} else {
Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedBoolean {
actual: value.r#type(),
position: self.if_expression.position,
},
))
}
} }
} }

View File

@ -1,6 +1,7 @@
use crate::{ use crate::{
context::Context, context::Context,
error::{RuntimeError, ValidationError}, error::{RuntimeError, ValidationError},
value::ValueInner,
Value, Value,
}; };
@ -52,14 +53,20 @@ impl AbstractTree for Logic {
if let (Type::Boolean, Type::Boolean) = (left, right) { if let (Type::Boolean, Type::Boolean) = (left, right) {
Ok(()) Ok(())
} else { } else {
Err(ValidationError::ExpectedBoolean) Err(ValidationError::ExpectedBoolean {
actual: todo!(),
position: todo!(),
})
} }
} }
Logic::Not(expression) => { Logic::Not(expression) => {
if let Type::Boolean = expression.node.expected_type(context)? { if let Type::Boolean = expression.node.expected_type(context)? {
Ok(()) Ok(())
} else { } else {
Err(ValidationError::ExpectedBoolean) Err(ValidationError::ExpectedBoolean {
actual: todo!(),
position: todo!(),
})
} }
} }
} }
@ -104,25 +111,72 @@ impl AbstractTree for Logic {
left <= right left <= right
} }
Logic::And(left, right) => { Logic::And(left, right) => {
let left = left.node.run(_context)?.as_return_value()?.as_boolean()?; let left_value = left.node.run(_context)?.as_return_value()?;
let right = right.node.run(_context)?.as_return_value()?.as_boolean()?; let right_value = right.node.run(_context)?.as_return_value()?;
left && right let left = if let ValueInner::Boolean(boolean) = left_value.inner().as_ref() {
boolean
} else {
return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedBoolean {
actual: left_value.r#type(),
position: left.position,
},
));
};
let right = if let ValueInner::Boolean(boolean) = right_value.inner().as_ref() {
boolean
} else {
return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedBoolean {
actual: right_value.r#type(),
position: right.position,
},
));
};
*left && *right
} }
Logic::Or(left, right) => { Logic::Or(left, right) => {
let left = left.node.run(_context)?.as_return_value()?.as_boolean()?; let left_value = left.node.run(_context)?.as_return_value()?;
let right = right.node.run(_context)?.as_return_value()?.as_boolean()?; let right_value = right.node.run(_context)?.as_return_value()?;
left || right let left = if let ValueInner::Boolean(boolean) = left_value.inner().as_ref() {
boolean
} else {
return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedBoolean {
actual: left_value.r#type(),
position: left.position,
},
));
};
let right = if let ValueInner::Boolean(boolean) = right_value.inner().as_ref() {
boolean
} else {
return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedBoolean {
actual: right_value.r#type(),
position: right.position,
},
));
};
*left || *right
} }
Logic::Not(statement) => { Logic::Not(statement) => {
let boolean = statement let value = statement.node.run(_context)?.as_return_value()?;
.node
.run(_context)?
.as_return_value()?
.as_boolean()?;
if let ValueInner::Boolean(boolean) = value.inner().as_ref() {
!boolean !boolean
} else {
return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedBoolean {
actual: value.r#type(),
position: statement.position,
},
));
}
} }
}; };

View File

@ -1,19 +1,27 @@
use crate::{ use crate::{
context::Context, context::Context,
error::{RuntimeError, ValidationError}, error::{RuntimeError, ValidationError},
value::ValueInner,
Value,
}; };
use super::{AbstractTree, Action, Block, Expression, Type, WithPosition}; use super::{AbstractTree, Action, Expression, Statement, Type, WithPosition};
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct While { pub struct While {
expression: WithPosition<Expression>, expression: WithPosition<Expression>,
block: Block, statements: Vec<WithPosition<Statement>>,
} }
impl While { impl While {
pub fn new(expression: WithPosition<Expression>, block: Block) -> Self { pub fn new(
Self { expression, block } expression: WithPosition<Expression>,
statements: Vec<WithPosition<Statement>>,
) -> Self {
Self {
expression,
statements,
}
} }
} }
@ -24,23 +32,79 @@ impl AbstractTree for While {
fn validate(&self, _context: &Context) -> Result<(), ValidationError> { fn validate(&self, _context: &Context) -> Result<(), ValidationError> {
self.expression.node.validate(_context)?; self.expression.node.validate(_context)?;
self.block.validate(_context)
for statement in &self.statements {
statement.node.validate(_context)?;
} }
fn run(self, context: &Context) -> Result<Action, RuntimeError> { Ok(())
while self }
.expression
.node fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
.clone() let get_boolean = || -> Result<Value, RuntimeError> {
.run(context)? let value = self.expression.node.run(_context)?.as_return_value()?;
.as_return_value()?
.as_boolean()? Ok(value)
{ };
if let Action::Break = self.block.clone().run(context)? {
break; if let ValueInner::Boolean(boolean) = get_boolean()?.inner().as_ref() {
while *boolean {
for statement in &self.statements {
let action = statement.node.clone().run(_context)?;
match action {
Action::Return(_) => {}
Action::None => {}
Action::Break => return Ok(Action::Break),
}
}
} }
} }
Ok(Action::None) Ok(Action::None)
} }
} }
#[cfg(test)]
mod tests {
use crate::abstract_tree::{
Assignment, AssignmentOperator, Block, Identifier, Logic, ValueNode,
};
use super::*;
#[test]
fn simple_while_loop() {
let result = Statement::Block(Block::new(vec![
Statement::Assignment(Assignment::new(
Identifier::new("i"),
None,
AssignmentOperator::Assign,
Statement::Expression(Expression::Value(ValueNode::Integer(3)))
.with_position((0, 0)),
))
.with_position((0, 0)),
Statement::While(While {
expression: Expression::Logic(Box::new(Logic::Less(
Expression::Identifier(Identifier::new("i")).with_position((0, 0)),
Expression::Value(ValueNode::Integer(3)).with_position((0, 0)),
)))
.with_position((0, 0)),
statements: vec![Statement::Assignment(Assignment::new(
Identifier::new("i"),
None,
AssignmentOperator::AddAssign,
Statement::Expression(Expression::Value(ValueNode::Integer(1)))
.with_position((0, 0)),
))
.with_position((0, 0))],
})
.with_position((0, 0)),
Statement::Expression(Expression::Identifier(Identifier::new("i")))
.with_position((0, 0)),
]))
.run(&Context::new());
assert_eq!(result, Ok(Action::Return(Value::integer(3))))
}
}

View File

@ -64,11 +64,17 @@ impl Error {
} }
}, },
Error::Validation { error, position } => match error { Error::Validation { error, position } => match error {
ValidationError::ExpectedBoolean => { ValidationError::ExpectedBoolean { actual, position } => {
builder.add_label(Label::new(0..0).with_message("Expected boolean.")); builder.add_label(
Label::new(position.0..position.1)
.with_message(format!("Expected boolean but got {actual}.")),
);
} }
ValidationError::ExpectedIntegerOrFloat => { ValidationError::ExpectedIntegerOrFloat => {
builder.add_label(Label::new(0..0).with_message("Expected integer or float.")); builder.add_label(
Label::new(position.0..position.1)
.with_message("Expected integer or float."),
);
} }
ValidationError::RwLockPoison(_) => todo!(), ValidationError::RwLockPoison(_) => todo!(),
ValidationError::TypeCheck { ValidationError::TypeCheck {
@ -95,7 +101,7 @@ impl Error {
ValidationError::CannotIndex(_) => todo!(), ValidationError::CannotIndex(_) => todo!(),
ValidationError::CannotIndexWith(_, _) => todo!(), ValidationError::CannotIndexWith(_, _) => todo!(),
ValidationError::InterpreterExpectedReturn => todo!(), ValidationError::InterpreterExpectedReturn => todo!(),
ValidationError::ExpectedFunction => todo!(), ValidationError::ExpectedFunction { actual, position } => todo!(),
ValidationError::ExpectedValue => todo!(), ValidationError::ExpectedValue => todo!(),
}, },
} }
@ -144,8 +150,14 @@ impl From<ValidationError> for RuntimeError {
pub enum ValidationError { pub enum ValidationError {
CannotIndex(Type), CannotIndex(Type),
CannotIndexWith(Type, Type), CannotIndexWith(Type, Type),
ExpectedBoolean, ExpectedBoolean {
ExpectedFunction, actual: Type,
position: SourcePosition,
},
ExpectedFunction {
actual: Type,
position: SourcePosition,
},
ExpectedIntegerOrFloat, ExpectedIntegerOrFloat,
ExpectedValue, ExpectedValue,
InterpreterExpectedReturn, InterpreterExpectedReturn,

View File

@ -386,9 +386,18 @@ pub fn parser<'src>() -> DustParser<'src> {
let r#while = just(Token::Keyword("while")) let r#while = just(Token::Keyword("while"))
.ignore_then(positioned_expression.clone()) .ignore_then(positioned_expression.clone())
.then(block.clone()) .then(
.map_with(|(expression, block), state| { positioned_statement
Statement::While(While::new(expression, block)).with_position(state.span()) .clone()
.repeated()
.collect()
.delimited_by(
just(Token::Control(Control::CurlyOpen)),
just(Token::Control(Control::CurlyClose)),
),
)
.map_with(|(expression, statements), state| {
Statement::While(While::new(expression, statements)).with_position(state.span())
}); });
let if_else = just(Token::Keyword("if")) let if_else = just(Token::Keyword("if"))
@ -431,14 +440,14 @@ mod tests {
parse(&lex("while true { output('hi') }").unwrap()).unwrap()[0].node, parse(&lex("while true { output('hi') }").unwrap()).unwrap()[0].node,
Statement::While(While::new( Statement::While(While::new(
Expression::Value(ValueNode::Boolean(true)).with_position((6, 11)), Expression::Value(ValueNode::Boolean(true)).with_position((6, 11)),
Block::new(vec![Statement::Expression(Expression::FunctionCall( vec![
FunctionCall::new( Statement::Expression(Expression::FunctionCall(FunctionCall::new(
Expression::Identifier(Identifier::new("output")).with_position((13, 19)), Expression::Identifier(Identifier::new("output")).with_position((13, 19)),
vec![Expression::Value(ValueNode::String("hi".to_string())) vec![Expression::Value(ValueNode::String("hi".to_string()))
.with_position((20, 24))] .with_position((20, 24))]
) )))
)) .with_position((13, 26))
.with_position((13, 26))]) ]
)) ))
) )
} }

View File

@ -93,28 +93,12 @@ impl Value {
} }
} }
pub fn as_boolean(&self) -> Result<bool, ValidationError> { pub fn as_boolean(&self) -> Option<bool> {
if let ValueInner::Boolean(boolean) = self.0.as_ref() { if let ValueInner::Boolean(boolean) = self.0.as_ref() {
return Ok(*boolean); Some(*boolean)
} else {
None
} }
Err(ValidationError::ExpectedBoolean)
}
pub fn as_number(&self) -> Result<bool, ValidationError> {
if let ValueInner::Boolean(boolean) = self.0.as_ref() {
return Ok(*boolean);
}
Err(ValidationError::ExpectedBoolean)
}
pub fn as_function(&self) -> Result<&Function, ValidationError> {
if let ValueInner::Function(function) = self.0.as_ref() {
return Ok(function);
}
Err(ValidationError::ExpectedFunction)
} }
pub fn as_list(&self) -> Option<&Vec<Value>> { pub fn as_list(&self) -> Option<&Vec<Value>> {