Rewrite while loops
This commit is contained in:
parent
fed119f38b
commit
c3402394a2
@ -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 {
|
||||
|
@ -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,
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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))))
|
||||
}
|
||||
}
|
||||
|
24
src/error.rs
24
src/error.rs
@ -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,
|
||||
|
@ -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))
|
||||
]
|
||||
))
|
||||
)
|
||||
}
|
||||
|
24
src/value.rs
24
src/value.rs
@ -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>> {
|
||||
|
Loading…
Reference in New Issue
Block a user