Add run Action type

This commit is contained in:
Jeff 2024-03-08 12:24:11 -05:00
parent dac7656572
commit d99ebc0a44
20 changed files with 276 additions and 101 deletions

View File

@ -1,10 +1,9 @@
use crate::{ use crate::{
error::{RuntimeError, ValidationError}, error::{RuntimeError, ValidationError},
value::Value,
Context, Context,
}; };
use super::{AbstractTree, Identifier, Statement, Type}; use super::{AbstractTree, Action, Identifier, Statement, Type};
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct Assignment<'src> { pub struct Assignment<'src> {
@ -42,12 +41,16 @@ impl<'src> AbstractTree for Assignment<'src> {
Ok(()) Ok(())
} }
fn run(self, context: &Context) -> Result<Value, RuntimeError> { fn run(self, context: &Context) -> Result<Action, RuntimeError> {
let value = self.statement.run(context)?; let action = self.statement.run(context)?;
let value = match action {
Action::Return(value) => value,
r#break => return Ok(r#break),
};
context.set_value(self.identifier, value)?; context.set_value(self.identifier, value)?;
Ok(Value::none()) Ok(Action::None)
} }
} }
@ -56,6 +59,7 @@ mod tests {
use crate::{ use crate::{
abstract_tree::{Expression, ValueNode}, abstract_tree::{Expression, ValueNode},
error::TypeCheckError, error::TypeCheckError,
Value,
}; };
use super::*; use super::*;

View File

@ -4,7 +4,7 @@ use crate::{
Value, Value,
}; };
use super::{AbstractTree, Statement, Type}; use super::{AbstractTree, Action, Statement, Type};
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct Block<'src> { pub struct Block<'src> {
@ -32,14 +32,18 @@ impl<'src> AbstractTree for Block<'src> {
Ok(()) Ok(())
} }
fn run(self, _context: &Context) -> Result<Value, RuntimeError> { fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
let mut previous = Value::none(); let mut previous = Value::none();
for statement in self.statements { for statement in self.statements {
previous = statement.run(_context)?; let action = statement.run(_context)?;
previous = match action {
Action::Return(value) => value,
r#break => return Ok(r#break),
};
} }
Ok(previous) Ok(Action::Return(previous))
} }
} }
@ -57,7 +61,10 @@ mod tests {
Statement::Expression(Expression::Value(ValueNode::Integer(42))), Statement::Expression(Expression::Value(ValueNode::Integer(42))),
]); ]);
assert_eq!(block.run(&Context::new()), Ok(Value::integer(42))) assert_eq!(
block.run(&Context::new()),
Ok(Action::Return(Value::integer(42)))
)
} }
#[test] #[test]

View File

@ -1,10 +1,9 @@
use crate::{ use crate::{
context::Context, context::Context,
error::{RuntimeError, ValidationError}, error::{RuntimeError, ValidationError},
Value,
}; };
use super::{AbstractTree, Identifier, Index, Logic, Math, Type, ValueNode}; use super::{AbstractTree, Action, Identifier, Index, Logic, Math, Type, ValueNode};
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum Expression<'src> { pub enum Expression<'src> {
@ -36,7 +35,7 @@ impl<'src> AbstractTree for Expression<'src> {
} }
} }
fn run(self, _context: &Context) -> Result<Value, RuntimeError> { fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
match self { match self {
Expression::Identifier(identifier) => identifier.run(_context), Expression::Identifier(identifier) => identifier.run(_context),
Expression::Index(index) => index.run(_context), Expression::Index(index) => index.run(_context),

View File

@ -9,7 +9,7 @@ use crate::{
Value, Value,
}; };
use super::{AbstractTree, Type}; use super::{AbstractTree, Action, Type};
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct Identifier(Arc<String>); pub struct Identifier(Arc<String>);
@ -41,13 +41,13 @@ impl AbstractTree for Identifier {
} }
} }
fn run(self, context: &Context) -> Result<Value, RuntimeError> { fn run(self, context: &Context) -> Result<Action, RuntimeError> {
let value = context let value = context
.get_value(&self)? .get_value(&self)?
.unwrap_or_else(Value::none) .unwrap_or_else(Value::none)
.clone(); .clone();
Ok(value) Ok(Action::Return(value))
} }
} }

View File

@ -4,7 +4,7 @@ use crate::{
Value, Value,
}; };
use super::{AbstractTree, Expression, Type, ValueNode}; use super::{AbstractTree, Action, Expression, Type, ValueNode};
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct Index<'src> { pub struct Index<'src> {
@ -58,15 +58,16 @@ impl<'src> AbstractTree for Index<'src> {
} }
} }
fn run(self, _context: &Context) -> Result<Value, RuntimeError> { fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
let left_value = self.left.run(_context)?; let left_value = self.left.run(_context)?.as_return_value()?;
let right_value = self.right.run(_context)?; let right_value = self.right.run(_context)?.as_return_value()?;
if let (Some(list), Some(index)) = (left_value.as_list(), right_value.as_integer()) { if let (Some(list), Some(index)) = (left_value.as_list(), right_value.as_integer()) {
Ok(list Ok(Action::Return(
.get(index as usize) list.get(index as usize)
.cloned() .cloned()
.unwrap_or_else(Value::none)) .unwrap_or_else(Value::none),
))
} else { } else {
Err(RuntimeError::ValidationFailure( Err(RuntimeError::ValidationFailure(
ValidationError::CannotIndexWith(left_value.r#type(), right_value.r#type()), ValidationError::CannotIndexWith(left_value.r#type(), right_value.r#type()),

View File

@ -4,7 +4,7 @@ use crate::{
Value, Value,
}; };
use super::{AbstractTree, Expression, Type}; use super::{AbstractTree, Action, Expression, Type};
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum Logic<'src> { pub enum Logic<'src> {
@ -59,24 +59,64 @@ impl<'src> AbstractTree for Logic<'src> {
} }
} }
fn run(self, _context: &Context) -> Result<Value, RuntimeError> { fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
let boolean = match self { let boolean = match self {
Logic::Equal(left, right) => left.run(_context)? == right.run(_context)?, Logic::Equal(left, right) => {
Logic::NotEqual(left, right) => left.run(_context)? != right.run(_context)?, let left = left.run(_context)?.as_return_value()?;
Logic::Greater(left, right) => left.run(_context)? > right.run(_context)?, let right = right.run(_context)?.as_return_value()?;
Logic::Less(left, right) => left.run(_context)? < right.run(_context)?,
Logic::GreaterOrEqual(left, right) => left.run(_context)? >= right.run(_context)?, left == right
Logic::LessOrEqual(left, right) => left.run(_context)? <= right.run(_context)?, }
Logic::NotEqual(left, right) => {
let left = left.run(_context)?.as_return_value()?;
let right = right.run(_context)?.as_return_value()?;
left != right
}
Logic::Greater(left, right) => {
let left = left.run(_context)?.as_return_value()?;
let right = right.run(_context)?.as_return_value()?;
left > right
}
Logic::Less(left, right) => {
let left = left.run(_context)?.as_return_value()?;
let right = right.run(_context)?.as_return_value()?;
left < right
}
Logic::GreaterOrEqual(left, right) => {
let left = left.run(_context)?.as_return_value()?;
let right = right.run(_context)?.as_return_value()?;
left >= right
}
Logic::LessOrEqual(left, right) => {
let left = left.run(_context)?.as_return_value()?;
let right = right.run(_context)?.as_return_value()?;
left <= right
}
Logic::And(left, right) => { Logic::And(left, right) => {
left.run(_context)?.as_boolean()? && right.run(_context)?.as_boolean()? let left = left.run(_context)?.as_return_value()?.as_boolean()?;
let right = right.run(_context)?.as_return_value()?.as_boolean()?;
left && right
} }
Logic::Or(left, right) => { Logic::Or(left, right) => {
left.run(_context)?.as_boolean()? || right.run(_context)?.as_boolean()? let left = left.run(_context)?.as_return_value()?.as_boolean()?;
let right = right.run(_context)?.as_return_value()?.as_boolean()?;
left || right
}
Logic::Not(statement) => {
let boolean = statement.run(_context)?.as_return_value()?.as_boolean()?;
!boolean
} }
Logic::Not(statement) => !statement.run(_context)?.as_boolean()?,
}; };
Ok(Value::boolean(boolean)) Ok(Action::Return(Value::boolean(boolean)))
} }
} }
@ -94,6 +134,8 @@ mod tests {
) )
.run(&Context::new()) .run(&Context::new())
.unwrap() .unwrap()
.as_return_value()
.unwrap()
.as_boolean() .as_boolean()
.unwrap()) .unwrap())
} }
@ -106,6 +148,8 @@ mod tests {
) )
.run(&Context::new()) .run(&Context::new())
.unwrap() .unwrap()
.as_return_value()
.unwrap()
.as_boolean() .as_boolean()
.unwrap()) .unwrap())
} }
@ -118,6 +162,8 @@ mod tests {
) )
.run(&Context::new()) .run(&Context::new())
.unwrap() .unwrap()
.as_return_value()
.unwrap()
.as_boolean() .as_boolean()
.unwrap()) .unwrap())
} }
@ -130,6 +176,8 @@ mod tests {
) )
.run(&Context::new()) .run(&Context::new())
.unwrap() .unwrap()
.as_return_value()
.unwrap()
.as_boolean() .as_boolean()
.unwrap()) .unwrap())
} }
@ -142,6 +190,8 @@ mod tests {
) )
.run(&Context::new()) .run(&Context::new())
.unwrap() .unwrap()
.as_return_value()
.unwrap()
.as_boolean() .as_boolean()
.unwrap()); .unwrap());
@ -151,6 +201,8 @@ mod tests {
) )
.run(&Context::new()) .run(&Context::new())
.unwrap() .unwrap()
.as_return_value()
.unwrap()
.as_boolean() .as_boolean()
.unwrap()) .unwrap())
} }
@ -163,6 +215,8 @@ mod tests {
) )
.run(&Context::new()) .run(&Context::new())
.unwrap() .unwrap()
.as_return_value()
.unwrap()
.as_boolean() .as_boolean()
.unwrap()); .unwrap());
@ -172,6 +226,8 @@ mod tests {
) )
.run(&Context::new()) .run(&Context::new())
.unwrap() .unwrap()
.as_return_value()
.unwrap()
.as_boolean() .as_boolean()
.unwrap()) .unwrap())
} }
@ -184,6 +240,8 @@ mod tests {
) )
.run(&Context::new()) .run(&Context::new())
.unwrap() .unwrap()
.as_return_value()
.unwrap()
.as_boolean() .as_boolean()
.unwrap()) .unwrap())
} }
@ -196,6 +254,8 @@ mod tests {
) )
.run(&Context::new()) .run(&Context::new())
.unwrap() .unwrap()
.as_return_value()
.unwrap()
.as_boolean() .as_boolean()
.unwrap()) .unwrap())
} }
@ -205,6 +265,8 @@ mod tests {
assert!(Logic::Not(Expression::Value(ValueNode::Boolean(false))) assert!(Logic::Not(Expression::Value(ValueNode::Boolean(false)))
.run(&Context::new()) .run(&Context::new())
.unwrap() .unwrap()
.as_return_value()
.unwrap()
.as_boolean() .as_boolean()
.unwrap()) .unwrap())
} }

View File

@ -1,10 +1,9 @@
use crate::{ use crate::{
context::Context, context::Context,
error::{RuntimeError, ValidationError}, error::{RuntimeError, ValidationError},
Value,
}; };
use super::{AbstractTree, Statement, Type}; use super::{AbstractTree, Action, Statement, Type};
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct Loop<'src> { pub struct Loop<'src> {
@ -19,14 +18,54 @@ impl<'src> Loop<'src> {
impl<'src> AbstractTree for Loop<'src> { impl<'src> AbstractTree for Loop<'src> {
fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> { fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> {
todo!() Ok(Type::None)
} }
fn validate(&self, _context: &Context) -> Result<(), ValidationError> { fn validate(&self, _context: &Context) -> Result<(), ValidationError> {
todo!() for statement in &self.statements {
statement.validate(_context)?;
}
Ok(())
} }
fn run(self, _: &Context) -> Result<Value, RuntimeError> { fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
todo!() let mut index = 0;
loop {
if index == self.statements.len() - 1 {
index = 0;
} else {
index += 1;
}
let statement = self.statements[index].clone();
if let Statement::Break(expression) = statement {
break expression.run(_context);
} else {
statement.run(_context)?;
}
}
}
}
#[cfg(test)]
mod tests {
use crate::{
abstract_tree::{Expression, ValueNode},
Value,
};
use super::*;
#[test]
fn basic_loop() {
let result = Loop {
statements: vec![Statement::Break(Expression::Value(ValueNode::Integer(42)))],
}
.run(&Context::new());
assert_eq!(result, Ok(Action::Return(Value::integer(42))))
} }
} }

View File

@ -5,7 +5,7 @@ use crate::{
Value, Value,
}; };
use super::{AbstractTree, Expression, Type}; use super::{AbstractTree, Action, Expression, Type};
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum Math<'src> { pub enum Math<'src> {
@ -48,78 +48,80 @@ impl<'src> AbstractTree for Math<'src> {
} }
} }
fn run(self, _context: &Context) -> Result<Value, RuntimeError> { fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
match self { let value = match self {
Math::Add(left, right) => { Math::Add(left, right) => {
let left_value = left.run(_context)?; let left_value = left.run(_context)?.as_return_value()?;
let right_value = right.run(_context)?; let right_value = right.run(_context)?.as_return_value()?;
if let (ValueInner::Integer(left), ValueInner::Integer(right)) = if let (ValueInner::Integer(left), ValueInner::Integer(right)) =
(left_value.inner().as_ref(), right_value.inner().as_ref()) (left_value.inner().as_ref(), right_value.inner().as_ref())
{ {
Ok(Value::integer(left + right)) Value::integer(left + right)
} else { } else {
Err(RuntimeError::ValidationFailure( return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedIntegerOrFloat, ValidationError::ExpectedIntegerOrFloat,
)) ));
} }
} }
Math::Subtract(left, right) => { Math::Subtract(left, right) => {
let left_value = left.run(_context)?; let left_value = left.run(_context)?.as_return_value()?;
let right_value = right.run(_context)?; let right_value = right.run(_context)?.as_return_value()?;
if let (ValueInner::Integer(left), ValueInner::Integer(right)) = if let (ValueInner::Integer(left), ValueInner::Integer(right)) =
(left_value.inner().as_ref(), right_value.inner().as_ref()) (left_value.inner().as_ref(), right_value.inner().as_ref())
{ {
Ok(Value::integer(left - right)) Value::integer(left - right)
} else { } else {
Err(RuntimeError::ValidationFailure( return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedIntegerOrFloat, ValidationError::ExpectedIntegerOrFloat,
)) ));
} }
} }
Math::Multiply(left, right) => { Math::Multiply(left, right) => {
let left_value = left.run(_context)?; let left_value = left.run(_context)?.as_return_value()?;
let right_value = right.run(_context)?; let right_value = right.run(_context)?.as_return_value()?;
if let (ValueInner::Integer(left), ValueInner::Integer(right)) = if let (ValueInner::Integer(left), ValueInner::Integer(right)) =
(left_value.inner().as_ref(), right_value.inner().as_ref()) (left_value.inner().as_ref(), right_value.inner().as_ref())
{ {
Ok(Value::integer(left * right)) Value::integer(left * right)
} else { } else {
Err(RuntimeError::ValidationFailure( return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedIntegerOrFloat, ValidationError::ExpectedIntegerOrFloat,
)) ));
} }
} }
Math::Divide(left, right) => { Math::Divide(left, right) => {
let left_value = left.run(_context)?; let left_value = left.run(_context)?.as_return_value()?;
let right_value = right.run(_context)?; let right_value = right.run(_context)?.as_return_value()?;
if let (ValueInner::Integer(left), ValueInner::Integer(right)) = if let (ValueInner::Integer(left), ValueInner::Integer(right)) =
(left_value.inner().as_ref(), right_value.inner().as_ref()) (left_value.inner().as_ref(), right_value.inner().as_ref())
{ {
Ok(Value::integer(left / right)) Value::integer(left / right)
} else { } else {
Err(RuntimeError::ValidationFailure( return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedIntegerOrFloat, ValidationError::ExpectedIntegerOrFloat,
)) ));
} }
} }
Math::Modulo(left, right) => { Math::Modulo(left, right) => {
let left_value = left.run(_context)?; let left_value = left.run(_context)?.as_return_value()?;
let right_value = right.run(_context)?; let right_value = right.run(_context)?.as_return_value()?;
if let (ValueInner::Integer(left), ValueInner::Integer(right)) = if let (ValueInner::Integer(left), ValueInner::Integer(right)) =
(left_value.inner().as_ref(), right_value.inner().as_ref()) (left_value.inner().as_ref(), right_value.inner().as_ref())
{ {
Ok(Value::integer(left % right)) Value::integer(left % right)
} else { } else {
Err(RuntimeError::ValidationFailure( return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedIntegerOrFloat, ValidationError::ExpectedIntegerOrFloat,
)) ));
} }
} }
} };
Ok(Action::Return(value))
} }
} }

View File

@ -25,5 +25,22 @@ use crate::{
pub trait AbstractTree { pub trait AbstractTree {
fn expected_type(&self, context: &Context) -> Result<Type, ValidationError>; fn expected_type(&self, context: &Context) -> Result<Type, ValidationError>;
fn validate(&self, context: &Context) -> Result<(), ValidationError>; fn validate(&self, context: &Context) -> Result<(), ValidationError>;
fn run(self, context: &Context) -> Result<Value, RuntimeError>; fn run(self, context: &Context) -> Result<Action, RuntimeError>;
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord)]
pub enum Action {
Break(Value),
Return(Value),
None,
}
impl Action {
pub fn as_return_value(self) -> Result<Value, ValidationError> {
if let Action::Return(value) = self {
Ok(value)
} else {
Err(ValidationError::InterpreterExpectedReturn)
}
}
} }

View File

@ -3,12 +3,13 @@ use crate::{
error::{RuntimeError, ValidationError}, error::{RuntimeError, ValidationError},
}; };
use super::{AbstractTree, Assignment, Block, Expression, Loop, Type, Value}; use super::{AbstractTree, Action, Assignment, Block, Expression, Loop, Type};
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum Statement<'src> { pub enum Statement<'src> {
Assignment(Assignment<'src>), Assignment(Assignment<'src>),
Block(Block<'src>), Block(Block<'src>),
Break(Expression<'src>),
Expression(Expression<'src>), Expression(Expression<'src>),
Loop(Loop<'src>), Loop(Loop<'src>),
} }
@ -18,6 +19,7 @@ impl<'src> AbstractTree for Statement<'src> {
match self { match self {
Statement::Assignment(assignment) => assignment.expected_type(_context), Statement::Assignment(assignment) => assignment.expected_type(_context),
Statement::Block(block) => block.expected_type(_context), Statement::Block(block) => block.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::Loop(r#loop) => r#loop.expected_type(_context), Statement::Loop(r#loop) => r#loop.expected_type(_context),
} }
@ -27,17 +29,19 @@ impl<'src> AbstractTree for Statement<'src> {
match self { match self {
Statement::Assignment(assignment) => assignment.validate(_context), Statement::Assignment(assignment) => assignment.validate(_context),
Statement::Block(_) => todo!(), Statement::Block(_) => todo!(),
Statement::Break(expression) => expression.validate(_context),
Statement::Expression(expression) => expression.validate(_context), Statement::Expression(expression) => expression.validate(_context),
Statement::Loop(_) => todo!(), Statement::Loop(r#loop) => r#loop.validate(_context),
} }
} }
fn run(self, _context: &Context) -> Result<Value, RuntimeError> { fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
match self { match self {
Statement::Assignment(assignment) => assignment.run(_context), Statement::Assignment(assignment) => assignment.run(_context),
Statement::Block(_) => todo!(), Statement::Block(_) => todo!(),
Statement::Break(expression) => expression.run(_context),
Statement::Expression(expression) => expression.run(_context), Statement::Expression(expression) => expression.run(_context),
Statement::Loop(_) => todo!(), Statement::Loop(r#loop) => r#loop.run(_context),
} }
} }
} }

View File

@ -4,10 +4,9 @@ use crate::{
abstract_tree::Identifier, abstract_tree::Identifier,
context::Context, context::Context,
error::{RuntimeError, TypeCheckError, ValidationError}, error::{RuntimeError, TypeCheckError, ValidationError},
Value,
}; };
use super::AbstractTree; use super::{AbstractTree, Action};
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum Type { pub enum Type {
@ -100,8 +99,8 @@ impl AbstractTree for Type {
Ok(()) Ok(())
} }
fn run(self, _: &Context) -> Result<Value, RuntimeError> { fn run(self, _: &Context) -> Result<Action, RuntimeError> {
Ok(Value::none()) Ok(Action::None)
} }
} }

View File

@ -6,7 +6,7 @@ use crate::{
Value, Value,
}; };
use super::{AbstractTree, Expression, Identifier, Type}; use super::{AbstractTree, Action, Expression, Identifier, Type};
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum ValueNode<'src> { pub enum ValueNode<'src> {
@ -48,7 +48,7 @@ impl<'src> AbstractTree for ValueNode<'src> {
Ok(()) Ok(())
} }
fn run(self, _context: &Context) -> Result<Value, RuntimeError> { fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
let value = match self { let value = match self {
ValueNode::Boolean(boolean) => Value::boolean(boolean), ValueNode::Boolean(boolean) => Value::boolean(boolean),
ValueNode::Float(float) => Value::float(float), ValueNode::Float(float) => Value::float(float),
@ -57,7 +57,7 @@ impl<'src> AbstractTree for ValueNode<'src> {
let mut value_list = Vec::with_capacity(expression_list.len()); let mut value_list = Vec::with_capacity(expression_list.len());
for expression in expression_list { for expression in expression_list {
let value = expression.run(_context)?; let value = expression.run(_context)?.as_return_value()?;
value_list.push(value); value_list.push(value);
} }
@ -68,7 +68,7 @@ impl<'src> AbstractTree for ValueNode<'src> {
let mut property_map = BTreeMap::new(); let mut property_map = BTreeMap::new();
for (identifier, _type, expression) in property_list { for (identifier, _type, expression) in property_list {
let value = expression.run(_context)?; let value = expression.run(_context)?.as_return_value()?;
property_map.insert(identifier, value); property_map.insert(identifier, value);
} }
@ -86,7 +86,7 @@ impl<'src> AbstractTree for ValueNode<'src> {
} }
}; };
Ok(value) Ok(Action::Return(value))
} }
} }

View File

@ -87,6 +87,7 @@ impl Error {
} }
ValidationError::CannotIndex(_) => todo!(), ValidationError::CannotIndex(_) => todo!(),
ValidationError::CannotIndexWith(_, _) => todo!(), ValidationError::CannotIndexWith(_, _) => todo!(),
ValidationError::InterpreterExpectedReturn => todo!(),
} }
report.finish() report.finish()
@ -143,6 +144,7 @@ pub enum ValidationError {
CannotIndexWith(Type, Type), CannotIndexWith(Type, Type),
ExpectedBoolean, ExpectedBoolean,
ExpectedIntegerOrFloat, ExpectedIntegerOrFloat,
InterpreterExpectedReturn,
RwLockPoison(RwLockPoisonError), RwLockPoison(RwLockPoisonError),
TypeCheck(TypeCheckError), TypeCheck(TypeCheckError),
VariableNotFound(Identifier), VariableNotFound(Identifier),

View File

@ -207,6 +207,7 @@ pub fn lexer<'src>() -> impl Parser<
let keyword = choice(( let keyword = choice((
just("bool").padded(), just("bool").padded(),
just("break").padded(),
just("float").padded(), just("float").padded(),
just("int").padded(), just("int").padded(),
just("list").padded(), just("list").padded(),

View File

@ -5,14 +5,14 @@ pub mod lexer;
pub mod parser; pub mod parser;
pub mod value; pub mod value;
use abstract_tree::AbstractTree; use abstract_tree::{AbstractTree, Action};
use context::Context; use context::Context;
use error::Error; use error::Error;
use lexer::lex; use lexer::lex;
pub use parser::{parse, parser, DustParser}; pub use parser::{parse, parser, DustParser};
pub use value::Value; pub use value::Value;
pub fn interpret(source: &str) -> Result<Value, Vec<Error>> { pub fn interpret(source: &str) -> Result<Option<Value>, Vec<Error>> {
let context = Context::new(); let context = Context::new();
let mut interpreter = Interpreter::new(context); let mut interpreter = Interpreter::new(context);
@ -28,7 +28,7 @@ impl Interpreter {
Interpreter { context } Interpreter { context }
} }
pub fn run(&mut self, source: &str) -> Result<Value, Vec<Error>> { pub fn run(&mut self, source: &str) -> Result<Option<Value>, Vec<Error>> {
let tokens = lex(source)?; let tokens = lex(source)?;
let statements = parse(&tokens)?; let statements = parse(&tokens)?;
let errors = statements let errors = statements
@ -48,11 +48,15 @@ impl Interpreter {
return Err(errors); return Err(errors);
} }
let mut value = Value::none(); let mut value = None;
for (statement, _span) in statements { for (statement, _span) in statements {
value = match statement.run(&self.context) { value = match statement.run(&self.context) {
Ok(value) => value, Ok(action) => match action {
Action::Break(value) => Some(value),
Action::Return(value) => Some(value),
Action::None => continue,
},
Err(runtime_error) => return Err(vec![Error::Runtime(runtime_error)]), Err(runtime_error) => return Err(vec![Error::Runtime(runtime_error)]),
} }
} }

View File

@ -48,7 +48,7 @@ fn main() {
match eval_result { match eval_result {
Ok(value) => { Ok(value) => {
if !value.is_none() { if let Some(value) = value {
println!("{value}") println!("{value}")
} }
} }

View File

@ -212,9 +212,14 @@ pub fn parser<'src>() -> DustParser<'src> {
let statement = recursive(|statement| { let statement = recursive(|statement| {
let expression_statement = expression let expression_statement = expression
.clone()
.map(|expression| Statement::Expression(expression)) .map(|expression| Statement::Expression(expression))
.boxed(); .boxed();
let r#break = just(Token::Keyword("break"))
.ignore_then(expression)
.map(|expression| Statement::Break(expression));
let assignment = identifier let assignment = identifier
.then(type_specification.clone().or_not()) .then(type_specification.clone().or_not())
.then_ignore(just(Token::Operator(Operator::Assign))) .then_ignore(just(Token::Operator(Operator::Assign)))
@ -238,6 +243,7 @@ pub fn parser<'src>() -> DustParser<'src> {
let r#loop = statement let r#loop = statement
.clone() .clone()
.repeated() .repeated()
.at_least(1)
.collect() .collect()
.delimited_by( .delimited_by(
just(Token::Keyword("loop")).then(just(Token::Control(Control::CurlyOpen))), just(Token::Keyword("loop")).then(just(Token::Control(Control::CurlyOpen))),
@ -246,7 +252,7 @@ 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, block, r#loop)) choice((assignment, expression_statement, r#break, block, r#loop))
.then_ignore(just(Token::Control(Control::Semicolon)).or_not()) .then_ignore(just(Token::Control(Control::Semicolon)).or_not())
}); });
@ -319,8 +325,10 @@ mod tests {
#[test] #[test]
fn r#loop() { fn r#loop() {
assert_eq!( assert_eq!(
parse(&lex("loop {}").unwrap()).unwrap()[0].0, parse(&lex("loop { 42 }").unwrap()).unwrap()[0].0,
Statement::Loop(Loop::new(vec![])) Statement::Loop(Loop::new(vec![Statement::Expression(Expression::Value(
ValueNode::Integer(42)
))]))
); );
} }

View File

@ -2,23 +2,26 @@ use dust_lang::*;
#[test] #[test]
fn logic() { fn logic() {
assert_eq!(interpret("1 == 1"), Ok(Value::boolean(true))); assert_eq!(interpret("1 == 1"), Ok(Some(Value::boolean(true))));
assert_eq!( assert_eq!(
interpret("('42' == '42') && (42 != 0)"), interpret("('42' == '42') && (42 != 0)"),
Ok(Value::boolean(true)) Ok(Some(Value::boolean(true)))
); );
} }
#[test] #[test]
fn math() { fn math() {
assert_eq!(interpret("1 + 1"), Ok(Value::integer(2))); assert_eq!(interpret("1 + 1"), Ok(Some(Value::integer(2))));
assert_eq!( assert_eq!(
interpret("2 * (21 + 19 + 1 * 2) / 2"), interpret("2 * (21 + 19 + 1 * 2) / 2"),
Ok(Value::integer(42)) Ok(Some(Value::integer(42)))
); );
} }
#[test] #[test]
fn list_index() { fn list_index() {
assert_eq!(interpret("foo = [1, 2, 3]; foo.2"), Ok(Value::integer(3))); assert_eq!(
interpret("foo = [1, 2, 3]; foo.2"),
Ok(Some(Value::integer(3)))
);
} }

20
tests/statements.rs Normal file
View File

@ -0,0 +1,20 @@
use dust_lang::*;
#[test]
fn loops_and_breaks() {
assert_eq!(
interpret(
"
i = 0;
loop {
if i == 3 {
break 'foobar'
} else {
i += 1
}
}
"
),
Ok(Some(Value::string("foobar")))
)
}

View File

@ -6,14 +6,17 @@ use dust_lang::{
#[test] #[test]
fn set_and_get_variable() { fn set_and_get_variable() {
assert_eq!(interpret("foobar = true; foobar"), Ok(Value::boolean(true))); assert_eq!(
interpret("foobar = true; foobar"),
Ok(Some(Value::boolean(true)))
);
} }
#[test] #[test]
fn set_variable_with_type() { fn set_variable_with_type() {
assert_eq!( assert_eq!(
interpret("foobar: bool = true; foobar"), interpret("foobar: bool = true; foobar"),
Ok(Value::boolean(true)) Ok(Some(Value::boolean(true)))
); );
} }