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::{
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::*;

View File

@ -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]

View File

@ -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),

View File

@ -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))
}
}

View File

@ -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)
.cloned()
.unwrap_or_else(Value::none))
Ok(Action::Return(
list.get(index as usize)
.cloned()
.unwrap_or_else(Value::none),
))
} else {
Err(RuntimeError::ValidationFailure(
ValidationError::CannotIndexWith(left_value.r#type(), right_value.r#type()),

View File

@ -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())
}

View File

@ -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)?;
}
Ok(())
}
fn run(self, _: &Context) -> Result<Value, RuntimeError> {
todo!()
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))))
}
}

View File

@ -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))
}
}

View File

@ -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)
}
}
}

View File

@ -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),
}
}
}

View File

@ -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)
}
}

View File

@ -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))
}
}

View File

@ -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),

View File

@ -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(),

View File

@ -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)]),
}
}

View File

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

View File

@ -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)
))]))
);
}

View File

@ -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
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]
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)))
);
}