Add run Action type
This commit is contained in:
parent
dac7656572
commit
d99ebc0a44
@ -1,10 +1,9 @@
|
||||
use crate::{
|
||||
error::{RuntimeError, ValidationError},
|
||||
value::Value,
|
||||
Context,
|
||||
};
|
||||
|
||||
use super::{AbstractTree, Identifier, Statement, Type};
|
||||
use super::{AbstractTree, Action, Identifier, Statement, Type};
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub struct Assignment<'src> {
|
||||
@ -42,12 +41,16 @@ impl<'src> AbstractTree for Assignment<'src> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(self, context: &Context) -> Result<Value, RuntimeError> {
|
||||
let value = self.statement.run(context)?;
|
||||
fn run(self, context: &Context) -> Result<Action, RuntimeError> {
|
||||
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)?;
|
||||
|
||||
Ok(Value::none())
|
||||
Ok(Action::None)
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,6 +59,7 @@ mod tests {
|
||||
use crate::{
|
||||
abstract_tree::{Expression, ValueNode},
|
||||
error::TypeCheckError,
|
||||
Value,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
@ -4,7 +4,7 @@ use crate::{
|
||||
Value,
|
||||
};
|
||||
|
||||
use super::{AbstractTree, Statement, Type};
|
||||
use super::{AbstractTree, Action, Statement, Type};
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub struct Block<'src> {
|
||||
@ -32,14 +32,18 @@ impl<'src> AbstractTree for Block<'src> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(self, _context: &Context) -> Result<Value, RuntimeError> {
|
||||
fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
|
||||
let mut previous = Value::none();
|
||||
|
||||
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))),
|
||||
]);
|
||||
|
||||
assert_eq!(block.run(&Context::new()), Ok(Value::integer(42)))
|
||||
assert_eq!(
|
||||
block.run(&Context::new()),
|
||||
Ok(Action::Return(Value::integer(42)))
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1,10 +1,9 @@
|
||||
use crate::{
|
||||
context::Context,
|
||||
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)]
|
||||
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 {
|
||||
Expression::Identifier(identifier) => identifier.run(_context),
|
||||
Expression::Index(index) => index.run(_context),
|
||||
|
@ -9,7 +9,7 @@ use crate::{
|
||||
Value,
|
||||
};
|
||||
|
||||
use super::{AbstractTree, Type};
|
||||
use super::{AbstractTree, Action, Type};
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
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
|
||||
.get_value(&self)?
|
||||
.unwrap_or_else(Value::none)
|
||||
.clone();
|
||||
|
||||
Ok(value)
|
||||
Ok(Action::Return(value))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ use crate::{
|
||||
Value,
|
||||
};
|
||||
|
||||
use super::{AbstractTree, Expression, Type, ValueNode};
|
||||
use super::{AbstractTree, Action, Expression, Type, ValueNode};
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub struct Index<'src> {
|
||||
@ -58,15 +58,16 @@ impl<'src> AbstractTree for Index<'src> {
|
||||
}
|
||||
}
|
||||
|
||||
fn run(self, _context: &Context) -> Result<Value, RuntimeError> {
|
||||
let left_value = self.left.run(_context)?;
|
||||
let right_value = self.right.run(_context)?;
|
||||
fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
|
||||
let left_value = self.left.run(_context)?.as_return_value()?;
|
||||
let right_value = self.right.run(_context)?.as_return_value()?;
|
||||
|
||||
if let (Some(list), Some(index)) = (left_value.as_list(), right_value.as_integer()) {
|
||||
Ok(list
|
||||
.get(index as usize)
|
||||
Ok(Action::Return(
|
||||
list.get(index as usize)
|
||||
.cloned()
|
||||
.unwrap_or_else(Value::none))
|
||||
.unwrap_or_else(Value::none),
|
||||
))
|
||||
} else {
|
||||
Err(RuntimeError::ValidationFailure(
|
||||
ValidationError::CannotIndexWith(left_value.r#type(), right_value.r#type()),
|
||||
|
@ -4,7 +4,7 @@ use crate::{
|
||||
Value,
|
||||
};
|
||||
|
||||
use super::{AbstractTree, Expression, Type};
|
||||
use super::{AbstractTree, Action, Expression, Type};
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
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 {
|
||||
Logic::Equal(left, right) => left.run(_context)? == right.run(_context)?,
|
||||
Logic::NotEqual(left, right) => left.run(_context)? != right.run(_context)?,
|
||||
Logic::Greater(left, right) => left.run(_context)? > right.run(_context)?,
|
||||
Logic::Less(left, right) => left.run(_context)? < right.run(_context)?,
|
||||
Logic::GreaterOrEqual(left, right) => left.run(_context)? >= right.run(_context)?,
|
||||
Logic::LessOrEqual(left, right) => left.run(_context)? <= right.run(_context)?,
|
||||
Logic::Equal(left, right) => {
|
||||
let left = left.run(_context)?.as_return_value()?;
|
||||
let right = right.run(_context)?.as_return_value()?;
|
||||
|
||||
left == right
|
||||
}
|
||||
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) => {
|
||||
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) => {
|
||||
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())
|
||||
.unwrap()
|
||||
.as_return_value()
|
||||
.unwrap()
|
||||
.as_boolean()
|
||||
.unwrap())
|
||||
}
|
||||
@ -106,6 +148,8 @@ mod tests {
|
||||
)
|
||||
.run(&Context::new())
|
||||
.unwrap()
|
||||
.as_return_value()
|
||||
.unwrap()
|
||||
.as_boolean()
|
||||
.unwrap())
|
||||
}
|
||||
@ -118,6 +162,8 @@ mod tests {
|
||||
)
|
||||
.run(&Context::new())
|
||||
.unwrap()
|
||||
.as_return_value()
|
||||
.unwrap()
|
||||
.as_boolean()
|
||||
.unwrap())
|
||||
}
|
||||
@ -130,6 +176,8 @@ mod tests {
|
||||
)
|
||||
.run(&Context::new())
|
||||
.unwrap()
|
||||
.as_return_value()
|
||||
.unwrap()
|
||||
.as_boolean()
|
||||
.unwrap())
|
||||
}
|
||||
@ -142,6 +190,8 @@ mod tests {
|
||||
)
|
||||
.run(&Context::new())
|
||||
.unwrap()
|
||||
.as_return_value()
|
||||
.unwrap()
|
||||
.as_boolean()
|
||||
.unwrap());
|
||||
|
||||
@ -151,6 +201,8 @@ mod tests {
|
||||
)
|
||||
.run(&Context::new())
|
||||
.unwrap()
|
||||
.as_return_value()
|
||||
.unwrap()
|
||||
.as_boolean()
|
||||
.unwrap())
|
||||
}
|
||||
@ -163,6 +215,8 @@ mod tests {
|
||||
)
|
||||
.run(&Context::new())
|
||||
.unwrap()
|
||||
.as_return_value()
|
||||
.unwrap()
|
||||
.as_boolean()
|
||||
.unwrap());
|
||||
|
||||
@ -172,6 +226,8 @@ mod tests {
|
||||
)
|
||||
.run(&Context::new())
|
||||
.unwrap()
|
||||
.as_return_value()
|
||||
.unwrap()
|
||||
.as_boolean()
|
||||
.unwrap())
|
||||
}
|
||||
@ -184,6 +240,8 @@ mod tests {
|
||||
)
|
||||
.run(&Context::new())
|
||||
.unwrap()
|
||||
.as_return_value()
|
||||
.unwrap()
|
||||
.as_boolean()
|
||||
.unwrap())
|
||||
}
|
||||
@ -196,6 +254,8 @@ mod tests {
|
||||
)
|
||||
.run(&Context::new())
|
||||
.unwrap()
|
||||
.as_return_value()
|
||||
.unwrap()
|
||||
.as_boolean()
|
||||
.unwrap())
|
||||
}
|
||||
@ -205,6 +265,8 @@ mod tests {
|
||||
assert!(Logic::Not(Expression::Value(ValueNode::Boolean(false)))
|
||||
.run(&Context::new())
|
||||
.unwrap()
|
||||
.as_return_value()
|
||||
.unwrap()
|
||||
.as_boolean()
|
||||
.unwrap())
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
use crate::{
|
||||
context::Context,
|
||||
error::{RuntimeError, ValidationError},
|
||||
Value,
|
||||
};
|
||||
|
||||
use super::{AbstractTree, Statement, Type};
|
||||
use super::{AbstractTree, Action, Statement, Type};
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub struct Loop<'src> {
|
||||
@ -19,14 +18,54 @@ impl<'src> Loop<'src> {
|
||||
|
||||
impl<'src> AbstractTree for Loop<'src> {
|
||||
fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> {
|
||||
todo!()
|
||||
Ok(Type::None)
|
||||
}
|
||||
|
||||
fn validate(&self, _context: &Context) -> Result<(), ValidationError> {
|
||||
todo!()
|
||||
for statement in &self.statements {
|
||||
statement.validate(_context)?;
|
||||
}
|
||||
|
||||
fn run(self, _: &Context) -> Result<Value, RuntimeError> {
|
||||
todo!()
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
|
||||
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))))
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ use crate::{
|
||||
Value,
|
||||
};
|
||||
|
||||
use super::{AbstractTree, Expression, Type};
|
||||
use super::{AbstractTree, Action, Expression, Type};
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub enum Math<'src> {
|
||||
@ -48,78 +48,80 @@ impl<'src> AbstractTree for Math<'src> {
|
||||
}
|
||||
}
|
||||
|
||||
fn run(self, _context: &Context) -> Result<Value, RuntimeError> {
|
||||
match self {
|
||||
fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
|
||||
let value = match self {
|
||||
Math::Add(left, right) => {
|
||||
let left_value = left.run(_context)?;
|
||||
let right_value = right.run(_context)?;
|
||||
let left_value = left.run(_context)?.as_return_value()?;
|
||||
let right_value = right.run(_context)?.as_return_value()?;
|
||||
|
||||
if let (ValueInner::Integer(left), ValueInner::Integer(right)) =
|
||||
(left_value.inner().as_ref(), right_value.inner().as_ref())
|
||||
{
|
||||
Ok(Value::integer(left + right))
|
||||
Value::integer(left + right)
|
||||
} else {
|
||||
Err(RuntimeError::ValidationFailure(
|
||||
return Err(RuntimeError::ValidationFailure(
|
||||
ValidationError::ExpectedIntegerOrFloat,
|
||||
))
|
||||
));
|
||||
}
|
||||
}
|
||||
Math::Subtract(left, right) => {
|
||||
let left_value = left.run(_context)?;
|
||||
let right_value = right.run(_context)?;
|
||||
let left_value = left.run(_context)?.as_return_value()?;
|
||||
let right_value = right.run(_context)?.as_return_value()?;
|
||||
|
||||
if let (ValueInner::Integer(left), ValueInner::Integer(right)) =
|
||||
(left_value.inner().as_ref(), right_value.inner().as_ref())
|
||||
{
|
||||
Ok(Value::integer(left - right))
|
||||
Value::integer(left - right)
|
||||
} else {
|
||||
Err(RuntimeError::ValidationFailure(
|
||||
return Err(RuntimeError::ValidationFailure(
|
||||
ValidationError::ExpectedIntegerOrFloat,
|
||||
))
|
||||
));
|
||||
}
|
||||
}
|
||||
Math::Multiply(left, right) => {
|
||||
let left_value = left.run(_context)?;
|
||||
let right_value = right.run(_context)?;
|
||||
let left_value = left.run(_context)?.as_return_value()?;
|
||||
let right_value = right.run(_context)?.as_return_value()?;
|
||||
|
||||
if let (ValueInner::Integer(left), ValueInner::Integer(right)) =
|
||||
(left_value.inner().as_ref(), right_value.inner().as_ref())
|
||||
{
|
||||
Ok(Value::integer(left * right))
|
||||
Value::integer(left * right)
|
||||
} else {
|
||||
Err(RuntimeError::ValidationFailure(
|
||||
return Err(RuntimeError::ValidationFailure(
|
||||
ValidationError::ExpectedIntegerOrFloat,
|
||||
))
|
||||
));
|
||||
}
|
||||
}
|
||||
Math::Divide(left, right) => {
|
||||
let left_value = left.run(_context)?;
|
||||
let right_value = right.run(_context)?;
|
||||
let left_value = left.run(_context)?.as_return_value()?;
|
||||
let right_value = right.run(_context)?.as_return_value()?;
|
||||
|
||||
if let (ValueInner::Integer(left), ValueInner::Integer(right)) =
|
||||
(left_value.inner().as_ref(), right_value.inner().as_ref())
|
||||
{
|
||||
Ok(Value::integer(left / right))
|
||||
Value::integer(left / right)
|
||||
} else {
|
||||
Err(RuntimeError::ValidationFailure(
|
||||
return Err(RuntimeError::ValidationFailure(
|
||||
ValidationError::ExpectedIntegerOrFloat,
|
||||
))
|
||||
));
|
||||
}
|
||||
}
|
||||
Math::Modulo(left, right) => {
|
||||
let left_value = left.run(_context)?;
|
||||
let right_value = right.run(_context)?;
|
||||
let left_value = left.run(_context)?.as_return_value()?;
|
||||
let right_value = right.run(_context)?.as_return_value()?;
|
||||
|
||||
if let (ValueInner::Integer(left), ValueInner::Integer(right)) =
|
||||
(left_value.inner().as_ref(), right_value.inner().as_ref())
|
||||
{
|
||||
Ok(Value::integer(left % right))
|
||||
Value::integer(left % right)
|
||||
} else {
|
||||
Err(RuntimeError::ValidationFailure(
|
||||
return Err(RuntimeError::ValidationFailure(
|
||||
ValidationError::ExpectedIntegerOrFloat,
|
||||
))
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Action::Return(value))
|
||||
}
|
||||
}
|
||||
|
@ -25,5 +25,22 @@ use crate::{
|
||||
pub trait AbstractTree {
|
||||
fn expected_type(&self, context: &Context) -> Result<Type, 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,12 +3,13 @@ use crate::{
|
||||
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)]
|
||||
pub enum Statement<'src> {
|
||||
Assignment(Assignment<'src>),
|
||||
Block(Block<'src>),
|
||||
Break(Expression<'src>),
|
||||
Expression(Expression<'src>),
|
||||
Loop(Loop<'src>),
|
||||
}
|
||||
@ -18,6 +19,7 @@ impl<'src> AbstractTree for Statement<'src> {
|
||||
match self {
|
||||
Statement::Assignment(assignment) => assignment.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::Loop(r#loop) => r#loop.expected_type(_context),
|
||||
}
|
||||
@ -27,17 +29,19 @@ impl<'src> AbstractTree for Statement<'src> {
|
||||
match self {
|
||||
Statement::Assignment(assignment) => assignment.validate(_context),
|
||||
Statement::Block(_) => todo!(),
|
||||
Statement::Break(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 {
|
||||
Statement::Assignment(assignment) => assignment.run(_context),
|
||||
Statement::Block(_) => todo!(),
|
||||
Statement::Break(expression) => expression.run(_context),
|
||||
Statement::Expression(expression) => expression.run(_context),
|
||||
Statement::Loop(_) => todo!(),
|
||||
Statement::Loop(r#loop) => r#loop.run(_context),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,10 +4,9 @@ use crate::{
|
||||
abstract_tree::Identifier,
|
||||
context::Context,
|
||||
error::{RuntimeError, TypeCheckError, ValidationError},
|
||||
Value,
|
||||
};
|
||||
|
||||
use super::AbstractTree;
|
||||
use super::{AbstractTree, Action};
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub enum Type {
|
||||
@ -100,8 +99,8 @@ impl AbstractTree for Type {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(self, _: &Context) -> Result<Value, RuntimeError> {
|
||||
Ok(Value::none())
|
||||
fn run(self, _: &Context) -> Result<Action, RuntimeError> {
|
||||
Ok(Action::None)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ use crate::{
|
||||
Value,
|
||||
};
|
||||
|
||||
use super::{AbstractTree, Expression, Identifier, Type};
|
||||
use super::{AbstractTree, Action, Expression, Identifier, Type};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ValueNode<'src> {
|
||||
@ -48,7 +48,7 @@ impl<'src> AbstractTree for ValueNode<'src> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(self, _context: &Context) -> Result<Value, RuntimeError> {
|
||||
fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
|
||||
let value = match self {
|
||||
ValueNode::Boolean(boolean) => Value::boolean(boolean),
|
||||
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());
|
||||
|
||||
for expression in expression_list {
|
||||
let value = expression.run(_context)?;
|
||||
let value = expression.run(_context)?.as_return_value()?;
|
||||
|
||||
value_list.push(value);
|
||||
}
|
||||
@ -68,7 +68,7 @@ impl<'src> AbstractTree for ValueNode<'src> {
|
||||
let mut property_map = BTreeMap::new();
|
||||
|
||||
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);
|
||||
}
|
||||
@ -86,7 +86,7 @@ impl<'src> AbstractTree for ValueNode<'src> {
|
||||
}
|
||||
};
|
||||
|
||||
Ok(value)
|
||||
Ok(Action::Return(value))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,6 +87,7 @@ impl Error {
|
||||
}
|
||||
ValidationError::CannotIndex(_) => todo!(),
|
||||
ValidationError::CannotIndexWith(_, _) => todo!(),
|
||||
ValidationError::InterpreterExpectedReturn => todo!(),
|
||||
}
|
||||
|
||||
report.finish()
|
||||
@ -143,6 +144,7 @@ pub enum ValidationError {
|
||||
CannotIndexWith(Type, Type),
|
||||
ExpectedBoolean,
|
||||
ExpectedIntegerOrFloat,
|
||||
InterpreterExpectedReturn,
|
||||
RwLockPoison(RwLockPoisonError),
|
||||
TypeCheck(TypeCheckError),
|
||||
VariableNotFound(Identifier),
|
||||
|
@ -207,6 +207,7 @@ pub fn lexer<'src>() -> impl Parser<
|
||||
|
||||
let keyword = choice((
|
||||
just("bool").padded(),
|
||||
just("break").padded(),
|
||||
just("float").padded(),
|
||||
just("int").padded(),
|
||||
just("list").padded(),
|
||||
|
14
src/lib.rs
14
src/lib.rs
@ -5,14 +5,14 @@ pub mod lexer;
|
||||
pub mod parser;
|
||||
pub mod value;
|
||||
|
||||
use abstract_tree::AbstractTree;
|
||||
use abstract_tree::{AbstractTree, Action};
|
||||
use context::Context;
|
||||
use error::Error;
|
||||
use lexer::lex;
|
||||
pub use parser::{parse, parser, DustParser};
|
||||
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 mut interpreter = Interpreter::new(context);
|
||||
|
||||
@ -28,7 +28,7 @@ impl Interpreter {
|
||||
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 statements = parse(&tokens)?;
|
||||
let errors = statements
|
||||
@ -48,11 +48,15 @@ impl Interpreter {
|
||||
return Err(errors);
|
||||
}
|
||||
|
||||
let mut value = Value::none();
|
||||
let mut value = None;
|
||||
|
||||
for (statement, _span) in statements {
|
||||
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)]),
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ fn main() {
|
||||
|
||||
match eval_result {
|
||||
Ok(value) => {
|
||||
if !value.is_none() {
|
||||
if let Some(value) = value {
|
||||
println!("{value}")
|
||||
}
|
||||
}
|
||||
|
@ -212,9 +212,14 @@ pub fn parser<'src>() -> DustParser<'src> {
|
||||
|
||||
let statement = recursive(|statement| {
|
||||
let expression_statement = expression
|
||||
.clone()
|
||||
.map(|expression| Statement::Expression(expression))
|
||||
.boxed();
|
||||
|
||||
let r#break = just(Token::Keyword("break"))
|
||||
.ignore_then(expression)
|
||||
.map(|expression| Statement::Break(expression));
|
||||
|
||||
let assignment = identifier
|
||||
.then(type_specification.clone().or_not())
|
||||
.then_ignore(just(Token::Operator(Operator::Assign)))
|
||||
@ -238,6 +243,7 @@ pub fn parser<'src>() -> DustParser<'src> {
|
||||
let r#loop = statement
|
||||
.clone()
|
||||
.repeated()
|
||||
.at_least(1)
|
||||
.collect()
|
||||
.delimited_by(
|
||||
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)))
|
||||
.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())
|
||||
});
|
||||
|
||||
@ -319,8 +325,10 @@ mod tests {
|
||||
#[test]
|
||||
fn r#loop() {
|
||||
assert_eq!(
|
||||
parse(&lex("loop {}").unwrap()).unwrap()[0].0,
|
||||
Statement::Loop(Loop::new(vec![]))
|
||||
parse(&lex("loop { 42 }").unwrap()).unwrap()[0].0,
|
||||
Statement::Loop(Loop::new(vec![Statement::Expression(Expression::Value(
|
||||
ValueNode::Integer(42)
|
||||
))]))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2,23 +2,26 @@ use dust_lang::*;
|
||||
|
||||
#[test]
|
||||
fn logic() {
|
||||
assert_eq!(interpret("1 == 1"), Ok(Value::boolean(true)));
|
||||
assert_eq!(interpret("1 == 1"), Ok(Some(Value::boolean(true))));
|
||||
assert_eq!(
|
||||
interpret("('42' == '42') && (42 != 0)"),
|
||||
Ok(Value::boolean(true))
|
||||
Ok(Some(Value::boolean(true)))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn math() {
|
||||
assert_eq!(interpret("1 + 1"), Ok(Value::integer(2)));
|
||||
assert_eq!(interpret("1 + 1"), Ok(Some(Value::integer(2))));
|
||||
assert_eq!(
|
||||
interpret("2 * (21 + 19 + 1 * 2) / 2"),
|
||||
Ok(Value::integer(42))
|
||||
Ok(Some(Value::integer(42)))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
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
20
tests/statements.rs
Normal 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")))
|
||||
)
|
||||
}
|
@ -6,14 +6,17 @@ use dust_lang::{
|
||||
|
||||
#[test]
|
||||
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]
|
||||
fn set_variable_with_type() {
|
||||
assert_eq!(
|
||||
interpret("foobar: bool = true; foobar"),
|
||||
Ok(Value::boolean(true))
|
||||
Ok(Some(Value::boolean(true)))
|
||||
);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user