Add run Action type
This commit is contained in:
parent
dac7656572
commit
d99ebc0a44
@ -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::*;
|
||||||
|
@ -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]
|
||||||
|
@ -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),
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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()),
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
|
@ -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))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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),
|
||||||
|
@ -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(),
|
||||||
|
14
src/lib.rs
14
src/lib.rs
@ -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)]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
))]))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
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]
|
#[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)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user