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::{
context::Context,
error::{RuntimeError, ValidationError},
value::ValueInner,
};
use super::{AbstractTree, Action, Expression, Type, WithPosition};
@ -25,24 +26,43 @@ impl FunctionCall {
impl AbstractTree for FunctionCall {
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)
} else {
Err(ValidationError::ExpectedFunction)
Err(ValidationError::ExpectedFunction {
actual: function_node_type,
position: self.function.position,
})
}
}
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(())
} else {
Err(ValidationError::ExpectedFunction)
Err(ValidationError::ExpectedFunction {
actual: function_node_type,
position: self.function.position,
})
}
}
fn run(self, context: &Context) -> Result<Action, RuntimeError> {
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());
for expression in self.arguments {

View File

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

View File

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

View File

@ -1,19 +1,27 @@
use crate::{
context::Context,
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)]
pub struct While {
expression: WithPosition<Expression>,
block: Block,
statements: Vec<WithPosition<Statement>>,
}
impl While {
pub fn new(expression: WithPosition<Expression>, block: Block) -> Self {
Self { expression, block }
pub fn new(
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> {
self.expression.node.validate(_context)?;
self.block.validate(_context)
for statement in &self.statements {
statement.node.validate(_context)?;
}
Ok(())
}
fn run(self, context: &Context) -> Result<Action, RuntimeError> {
while self
.expression
.node
.clone()
.run(context)?
.as_return_value()?
.as_boolean()?
{
if let Action::Break = self.block.clone().run(context)? {
break;
fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
let get_boolean = || -> Result<Value, RuntimeError> {
let value = self.expression.node.run(_context)?.as_return_value()?;
Ok(value)
};
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)
}
}
#[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 {
ValidationError::ExpectedBoolean => {
builder.add_label(Label::new(0..0).with_message("Expected boolean."));
ValidationError::ExpectedBoolean { actual, position } => {
builder.add_label(
Label::new(position.0..position.1)
.with_message(format!("Expected boolean but got {actual}.")),
);
}
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::TypeCheck {
@ -95,7 +101,7 @@ impl Error {
ValidationError::CannotIndex(_) => todo!(),
ValidationError::CannotIndexWith(_, _) => todo!(),
ValidationError::InterpreterExpectedReturn => todo!(),
ValidationError::ExpectedFunction => todo!(),
ValidationError::ExpectedFunction { actual, position } => todo!(),
ValidationError::ExpectedValue => todo!(),
},
}
@ -144,8 +150,14 @@ impl From<ValidationError> for RuntimeError {
pub enum ValidationError {
CannotIndex(Type),
CannotIndexWith(Type, Type),
ExpectedBoolean,
ExpectedFunction,
ExpectedBoolean {
actual: Type,
position: SourcePosition,
},
ExpectedFunction {
actual: Type,
position: SourcePosition,
},
ExpectedIntegerOrFloat,
ExpectedValue,
InterpreterExpectedReturn,

View File

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