Implement VM and Analyzer by using the new AST

This commit is contained in:
Jeff 2024-08-16 09:22:36 -04:00
parent fedefdb29f
commit 7d721beb31
5 changed files with 348 additions and 75 deletions

View File

@ -10,8 +10,8 @@ use std::{
}; };
use crate::{ use crate::{
ast::{AbstractSyntaxTree, Node, Statement}, ast::{AbstractSyntaxTree, LetStatement, Node, OperatorExpression, Statement},
parse, Context, DustError, Identifier, Span, Type, parse, Context, DustError, Expression, Identifier, Span, Type,
}; };
/// Analyzes the abstract syntax tree for errors. /// Analyzes the abstract syntax tree for errors.
@ -65,14 +65,125 @@ impl<'a> Analyzer<'a> {
} }
pub fn analyze(&mut self) -> Result<(), AnalyzerError> { pub fn analyze(&mut self) -> Result<(), AnalyzerError> {
for node in &self.abstract_tree.statements { for statement in &self.abstract_tree.statements {
self.analyze_statement(node)?; self.analyze_statement(statement)?;
} }
Ok(()) Ok(())
} }
fn analyze_statement(&mut self, _: &Statement) -> Result<(), AnalyzerError> { fn analyze_statement(&mut self, statement: &Statement) -> Result<(), AnalyzerError> {
match statement {
Statement::Expression(expression) => self.analyze_expression(expression)?,
Statement::ExpressionNullified(expression_node) => {
self.analyze_expression(&expression_node.inner)?;
}
Statement::Let(let_statement) => match &let_statement.inner {
LetStatement::Let { identifier, value } => {
let type_option = value.return_type(self.context);
if let Some(r#type) = type_option {
self.context.set_type(
identifier.inner.clone(),
r#type,
identifier.position,
);
} else {
return Err(AnalyzerError::UndefinedVariable {
identifier: identifier.clone(),
});
}
self.analyze_expression(value)?;
}
LetStatement::LetMut { identifier, value } => {
let type_option = value.return_type(self.context);
if let Some(r#type) = type_option {
self.context.set_type(
identifier.inner.clone(),
r#type,
identifier.position,
);
} else {
return Err(AnalyzerError::UndefinedVariable {
identifier: identifier.clone(),
});
}
self.analyze_expression(value)?;
}
LetStatement::LetType {
identifier,
r#type,
value,
} => todo!(),
LetStatement::LetMutType {
identifier,
r#type,
value,
} => todo!(),
},
Statement::StructDefinition(_) => {}
}
Ok(())
}
fn analyze_expression(&mut self, expression: &Expression) -> Result<(), AnalyzerError> {
match expression {
Expression::Block(_) => {}
Expression::Call(_) => {}
Expression::FieldAccess(_) => {}
Expression::Grouped(_) => {}
Expression::Identifier(identifier) => {
let found = self
.context
.update_last_position(&identifier.inner, identifier.position);
if !found {
return Err(AnalyzerError::UndefinedVariable {
identifier: identifier.clone(),
});
}
}
Expression::If(_) => {}
Expression::List(_) => {}
Expression::ListIndex(_) => {}
Expression::Literal(_) => {}
Expression::Loop(_) => {}
Expression::Operator(operator_expression) => match operator_expression.inner.as_ref() {
OperatorExpression::Assignment { assignee, value } => {
self.analyze_expression(assignee)?;
self.analyze_expression(value)?;
}
OperatorExpression::Comparison { left, right, .. } => {
self.analyze_expression(left)?;
self.analyze_expression(right)?;
}
OperatorExpression::CompoundAssignment {
assignee, modifier, ..
} => {
self.analyze_expression(assignee)?;
self.analyze_expression(modifier)?;
}
OperatorExpression::ErrorPropagation(_) => todo!(),
OperatorExpression::Negation(_) => todo!(),
OperatorExpression::Not(_) => todo!(),
OperatorExpression::Math { left, right, .. } => {
self.analyze_expression(left)?;
self.analyze_expression(right)?;
}
OperatorExpression::Logic { left, right, .. } => {
self.analyze_expression(left)?;
self.analyze_expression(right)?;
}
},
Expression::Range(_) => {}
Expression::Struct(_) => {}
Expression::TupleAccess(_) => {}
}
Ok(()) Ok(())
} }
} }
@ -124,13 +235,13 @@ pub enum AnalyzerError {
identifier: Node<Identifier>, identifier: Node<Identifier>,
}, },
UnexpectedIdentifier { UnexpectedIdentifier {
identifier: Statement, identifier: Node<Identifier>,
}, },
UnexectedString { UnexectedString {
actual: Statement, actual: Statement,
}, },
UndefinedVariable { UndefinedVariable {
identifier: Statement, identifier: Node<Identifier>,
}, },
} }
@ -151,8 +262,8 @@ impl AnalyzerError {
} => actual_statement.position(), } => actual_statement.position(),
AnalyzerError::UndefinedField { identifier, .. } => identifier.position(), AnalyzerError::UndefinedField { identifier, .. } => identifier.position(),
AnalyzerError::UndefinedType { identifier } => identifier.position, AnalyzerError::UndefinedType { identifier } => identifier.position,
AnalyzerError::UndefinedVariable { identifier } => identifier.position(), AnalyzerError::UndefinedVariable { identifier } => identifier.position,
AnalyzerError::UnexpectedIdentifier { identifier } => identifier.position(), AnalyzerError::UnexpectedIdentifier { identifier } => identifier.position,
AnalyzerError::UnexectedString { actual } => actual.position(), AnalyzerError::UnexectedString { actual } => actual.position(),
} }
} }

View File

@ -170,6 +170,10 @@ impl<'src> Parser<'src> {
let value = self.parse_expression(0)?; let value = self.parse_expression(0)?;
if let Token::Semicolon = self.current_token {
self.next_token()?;
}
let r#let = if is_mutable { let r#let = if is_mutable {
LetStatement::LetMut { identifier, value } LetStatement::LetMut { identifier, value }
} else { } else {

View File

@ -366,7 +366,7 @@ impl Display for TokenOwned {
TokenOwned::Async => Token::Async.fmt(f), TokenOwned::Async => Token::Async.fmt(f),
TokenOwned::Bang => Token::Bang.fmt(f), TokenOwned::Bang => Token::Bang.fmt(f),
TokenOwned::BangEqual => Token::BangEqual.fmt(f), TokenOwned::BangEqual => Token::BangEqual.fmt(f),
TokenOwned::Bool => write!(f, "bool"), TokenOwned::Bool => Token::Bool.fmt(f),
TokenOwned::Boolean(boolean) => write!(f, "{boolean}"), TokenOwned::Boolean(boolean) => write!(f, "{boolean}"),
TokenOwned::Colon => Token::Colon.fmt(f), TokenOwned::Colon => Token::Colon.fmt(f),
TokenOwned::Comma => Token::Comma.fmt(f), TokenOwned::Comma => Token::Comma.fmt(f),
@ -379,12 +379,12 @@ impl Display for TokenOwned {
TokenOwned::Eof => Token::Eof.fmt(f), TokenOwned::Eof => Token::Eof.fmt(f),
TokenOwned::Equal => Token::Equal.fmt(f), TokenOwned::Equal => Token::Equal.fmt(f),
TokenOwned::Float(float) => write!(f, "{float}"), TokenOwned::Float(float) => write!(f, "{float}"),
TokenOwned::FloatKeyword => write!(f, "float"), TokenOwned::FloatKeyword => Token::FloatKeyword.fmt(f),
TokenOwned::Greater => Token::Greater.fmt(f), TokenOwned::Greater => Token::Greater.fmt(f),
TokenOwned::GreaterOrEqual => Token::GreaterEqual.fmt(f), TokenOwned::GreaterOrEqual => Token::GreaterEqual.fmt(f),
TokenOwned::Identifier(text) => write!(f, "{text}"), TokenOwned::Identifier(text) => write!(f, "{text}"),
TokenOwned::If => Token::If.fmt(f), TokenOwned::If => Token::If.fmt(f),
TokenOwned::Int => write!(f, "int"), TokenOwned::Int => Token::Int.fmt(f),
TokenOwned::Integer(integer) => write!(f, "{integer}"), TokenOwned::Integer(integer) => write!(f, "{integer}"),
TokenOwned::IsEven => Token::IsEven.fmt(f), TokenOwned::IsEven => Token::IsEven.fmt(f),
TokenOwned::IsOdd => Token::IsOdd.fmt(f), TokenOwned::IsOdd => Token::IsOdd.fmt(f),
@ -408,7 +408,7 @@ impl Display for TokenOwned {
TokenOwned::Semicolon => Token::Semicolon.fmt(f), TokenOwned::Semicolon => Token::Semicolon.fmt(f),
TokenOwned::Star => Token::Star.fmt(f), TokenOwned::Star => Token::Star.fmt(f),
TokenOwned::Slash => Token::Slash.fmt(f), TokenOwned::Slash => Token::Slash.fmt(f),
TokenOwned::Str => write!(f, "str"), TokenOwned::Str => Token::Str.fmt(f),
TokenOwned::String(string) => write!(f, "{string}"), TokenOwned::String(string) => write!(f, "{string}"),
TokenOwned::Struct => Token::Struct.fmt(f), TokenOwned::Struct => Token::Struct.fmt(f),
TokenOwned::ToString => Token::ToString.fmt(f), TokenOwned::ToString => Token::ToString.fmt(f),

View File

@ -105,12 +105,17 @@ impl Value {
matches!(self, Value::Mutable(_)) matches!(self, Value::Mutable(_))
} }
pub fn to_mut(self) -> Self { pub fn to_mut(self) -> Result<Value, ValueError> {
match self { if let Value::Immutable(arc) = self {
Value::Immutable(inner) => { let value_data = if let Some(value_data) = Arc::into_inner(arc) {
Value::Mutable(Arc::new(RwLock::new(inner.as_ref().clone()))) value_data
} } else {
_ => self, return Err(ValueError::CannotMakeMutable);
};
Ok(Value::Mutable(Arc::new(RwLock::new(value_data))))
} else {
Ok(self)
} }
} }
@ -317,7 +322,7 @@ impl Value {
Err(ValueError::CannotAdd(self.clone(), other.clone())) Err(ValueError::CannotAdd(self.clone(), other.clone()))
} }
pub fn add_mut(&self, other: &Value) -> Result<(), ValueError> { pub fn add_assign(&self, other: &Value) -> Result<(), ValueError> {
match (self, other) { match (self, other) {
(Value::Mutable(left), Value::Mutable(right)) => { (Value::Mutable(left), Value::Mutable(right)) => {
match (&mut *left.write().unwrap(), &*right.read().unwrap()) { match (&mut *left.write().unwrap(), &*right.read().unwrap()) {
@ -412,7 +417,7 @@ impl Value {
Err(ValueError::CannotSubtract(self.clone(), other.clone())) Err(ValueError::CannotSubtract(self.clone(), other.clone()))
} }
pub fn subtract_mut(&self, other: &Value) -> Result<(), ValueError> { pub fn subtract_assign(&self, other: &Value) -> Result<(), ValueError> {
match (self, other) { match (self, other) {
(Value::Mutable(left), Value::Mutable(right)) => { (Value::Mutable(left), Value::Mutable(right)) => {
match (&mut *left.write().unwrap(), &*right.read().unwrap()) { match (&mut *left.write().unwrap(), &*right.read().unwrap()) {
@ -489,6 +494,42 @@ impl Value {
Err(ValueError::CannotMultiply(self.clone(), other.clone())) Err(ValueError::CannotMultiply(self.clone(), other.clone()))
} }
pub fn multiply_assign(&self, other: &Value) -> Result<(), ValueError> {
match (self, other) {
(Value::Mutable(left), Value::Mutable(right)) => {
match (&mut *left.write().unwrap(), &*right.read().unwrap()) {
(ValueData::Float(left), ValueData::Float(right)) => {
*left *= right;
return Ok(());
}
(ValueData::Integer(left), ValueData::Integer(right)) => {
*left = left.saturating_mul(*right);
return Ok(());
}
_ => {}
}
}
(Value::Mutable(left), Value::Immutable(right)) => {
match (&mut *left.write().unwrap(), right.as_ref()) {
(ValueData::Float(left), ValueData::Float(right)) => {
*left *= right;
return Ok(());
}
(ValueData::Integer(left), ValueData::Integer(right)) => {
*left = left.saturating_mul(*right);
return Ok(());
}
_ => {}
}
}
(Value::Immutable(_), _) => {
return Err(ValueError::CannotMutate(self.clone()));
}
}
Err(ValueError::CannotMultiply(self.clone(), other.clone()))
}
pub fn divide(&self, other: &Value) -> Result<Value, ValueError> { pub fn divide(&self, other: &Value) -> Result<Value, ValueError> {
match (self, other) { match (self, other) {
(Value::Immutable(left), Value::Immutable(right)) => { (Value::Immutable(left), Value::Immutable(right)) => {
@ -564,6 +605,54 @@ impl Value {
Err(ValueError::CannotDivide(self.clone(), other.clone())) Err(ValueError::CannotDivide(self.clone(), other.clone()))
} }
pub fn divide_assign(&self, other: &Value) -> Result<(), ValueError> {
match (self, other) {
(Value::Mutable(left), Value::Mutable(right)) => {
match (&mut *left.write().unwrap(), &*right.read().unwrap()) {
(ValueData::Float(left), ValueData::Float(right)) => {
if *right == 0.0 {
return Err(ValueError::DivisionByZero);
}
*left /= right;
return Ok(());
}
(ValueData::Integer(left), ValueData::Integer(right)) => {
if *right == 0 {
return Err(ValueError::DivisionByZero);
}
*left = left.saturating_div(*right);
return Ok(());
}
_ => {}
}
}
(Value::Mutable(left), Value::Immutable(right)) => {
match (&mut *left.write().unwrap(), right.as_ref()) {
(ValueData::Float(left), ValueData::Float(right)) => {
if *right == 0.0 {
return Err(ValueError::DivisionByZero);
}
*left /= right;
return Ok(());
}
(ValueData::Integer(left), ValueData::Integer(right)) => {
if *right == 0 {
return Err(ValueError::DivisionByZero);
}
*left = left.saturating_div(*right);
return Ok(());
}
_ => {}
}
}
(Value::Immutable(_), _) => {
return Err(ValueError::CannotMutate(self.clone()));
}
}
Err(ValueError::CannotDivide(self.clone(), other.clone()))
}
pub fn modulo(&self, other: &Value) -> Result<Value, ValueError> { pub fn modulo(&self, other: &Value) -> Result<Value, ValueError> {
match (self, other) { match (self, other) {
(Value::Immutable(left), Value::Immutable(right)) => { (Value::Immutable(left), Value::Immutable(right)) => {
@ -627,6 +716,48 @@ impl Value {
Err(ValueError::CannotModulo(self.clone(), other.clone())) Err(ValueError::CannotModulo(self.clone(), other.clone()))
} }
pub fn modulo_assign(&self, other: &Value) -> Result<(), ValueError> {
match (self, other) {
(Value::Mutable(left), Value::Mutable(right)) => {
match (&mut *left.write().unwrap(), &*right.read().unwrap()) {
(ValueData::Float(left), ValueData::Float(right)) => {
*left %= right;
return Ok(());
}
(ValueData::Integer(left), ValueData::Integer(right)) => {
if *right == 0 {
return Err(ValueError::DivisionByZero);
}
*left %= right;
return Ok(());
}
_ => {}
}
}
(Value::Mutable(left), Value::Immutable(right)) => {
match (&mut *left.write().unwrap(), right.as_ref()) {
(ValueData::Float(left), ValueData::Float(right)) => {
*left %= right;
return Ok(());
}
(ValueData::Integer(left), ValueData::Integer(right)) => {
if *right == 0 {
return Err(ValueError::DivisionByZero);
}
*left %= right;
return Ok(());
}
_ => {}
}
}
(Value::Immutable(_), _) => {
return Err(ValueError::CannotMutate(self.clone()));
}
}
Err(ValueError::CannotModulo(self.clone(), other.clone()))
}
pub fn equal(&self, other: &Value) -> Value { pub fn equal(&self, other: &Value) -> Value {
let is_equal = match (self, other) { let is_equal = match (self, other) {
(Value::Immutable(left), Value::Immutable(right)) => left == right, (Value::Immutable(left), Value::Immutable(right)) => left == right,
@ -1614,6 +1745,7 @@ pub enum ValueError {
CannotGreaterThanOrEqual(Value, Value), CannotGreaterThanOrEqual(Value, Value),
CannotLessThan(Value, Value), CannotLessThan(Value, Value),
CannotLessThanOrEqual(Value, Value), CannotLessThanOrEqual(Value, Value),
CannotMakeMutable,
CannotModulo(Value, Value), CannotModulo(Value, Value),
CannotMultiply(Value, Value), CannotMultiply(Value, Value),
CannotMutate(Value), CannotMutate(Value),
@ -1644,6 +1776,10 @@ impl Display for ValueError {
ValueError::CannotMultiply(left, right) => { ValueError::CannotMultiply(left, right) => {
write!(f, "Cannot multiply {} and {}", left, right) write!(f, "Cannot multiply {} and {}", left, right)
} }
ValueError::CannotMakeMutable => write!(
f,
"Failed to make mutable value because the value has an immutable reference to it"
),
ValueError::CannotMutate(value) => write!(f, "Cannot mutate {}", value), ValueError::CannotMutate(value) => write!(f, "Cannot mutate {}", value),
ValueError::CannotSubtract(left, right) => { ValueError::CannotSubtract(left, right) => {
write!(f, "Cannot subtract {} and {}", left, right) write!(f, "Cannot subtract {} and {}", left, right)

View File

@ -14,12 +14,12 @@ use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterato
use crate::{ use crate::{
ast::{ ast::{
AbstractSyntaxTree, BlockExpression, CallExpression, ComparisonOperator, ElseExpression, AbstractSyntaxTree, BlockExpression, CallExpression, ComparisonOperator, ElseExpression,
FieldAccessExpression, IfExpression, ListExpression, ListIndexExpression, FieldAccessExpression, IfExpression, LetStatement, ListExpression, ListIndexExpression,
LiteralExpression, LogicOperator, LoopExpression, MathOperator, Node, OperatorExpression, LiteralExpression, LogicOperator, LoopExpression, MathOperator, Node, OperatorExpression,
Statement, Statement,
}, },
parse, Analyzer, BuiltInFunctionError, Context, DustError, Expression, Identifier, ParseError, parse, Analyzer, BuiltInFunctionError, Context, DustError, Expression, Identifier, ParseError,
Span, Type, Value, ValueError, Span, Value, ValueError,
}; };
/// Run the source code and return the result. /// Run the source code and return the result.
@ -90,21 +90,12 @@ impl Vm {
} }
pub fn run(&mut self) -> Result<Option<Value>, VmError> { pub fn run(&mut self) -> Result<Option<Value>, VmError> {
let mut previous_position = (0, 0);
let mut previous_value = None; let mut previous_value = None;
while let Some(statement) = self.abstract_tree.statements.pop_front() { while let Some(statement) = self.abstract_tree.statements.pop_front() {
let new_position = statement.position();
previous_value = self.run_statement(statement)?; previous_value = self.run_statement(statement)?;
self.context.collect_garbage(previous_position.1);
previous_position = new_position;
} }
self.context.collect_garbage(previous_position.1);
Ok(previous_value) Ok(previous_value)
} }
@ -119,16 +110,50 @@ impl Vm {
Ok(None) Ok(None)
} }
Statement::Let(_) => todo!(), Statement::Let(let_statement) => {
self.run_let_statement(let_statement.inner)?;
Ok(None)
}
Statement::StructDefinition(_) => todo!(), Statement::StructDefinition(_) => todo!(),
}; };
self.context.collect_garbage(position.1);
result.map_err(|error| VmError::Trace { result.map_err(|error| VmError::Trace {
error: Box::new(error), error: Box::new(error),
position, position,
}) })
} }
fn run_let_statement(&self, let_statement: LetStatement) -> Result<(), VmError> {
match let_statement {
LetStatement::Let { identifier, value } => {
let value_position = value.position();
let value = self.run_expression(value)?.expect_value(value_position)?;
self.context.set_value(identifier.inner, value);
Ok(())
}
LetStatement::LetMut { identifier, value } => {
let value_position = value.position();
let value = self.run_expression(value)?.expect_value(value_position)?;
let mutable_value = value.to_mut().map_err(|error| VmError::ValueError {
error,
left_position: identifier.position,
right_position: value_position,
})?;
self.context.set_value(identifier.inner, mutable_value);
Ok(())
}
LetStatement::LetType { .. } => todo!(),
LetStatement::LetMutType { .. } => todo!(),
}
}
fn run_expression(&self, expression: Expression) -> Result<Evaluation, VmError> { fn run_expression(&self, expression: Expression) -> Result<Evaluation, VmError> {
let position = expression.position(); let position = expression.position();
let evaluation_result = match expression { let evaluation_result = match expression {
@ -177,7 +202,13 @@ impl Vm {
let value_position = value.position(); let value_position = value.position();
let value = self.run_expression(value)?.expect_value(value_position)?; let value = self.run_expression(value)?.expect_value(value_position)?;
assignee.mutate(value); assignee
.mutate(value)
.map_err(|error| VmError::ValueError {
error,
left_position: assignee_position,
right_position: value_position,
})?;
Ok(Evaluation::Return(None)) Ok(Evaluation::Return(None))
} }
@ -230,7 +261,31 @@ impl Vm {
assignee, assignee,
operator, operator,
modifier, modifier,
} => todo!(), } => {
let assignee_position = assignee.position();
let assignee = self
.run_expression(assignee)?
.expect_value(assignee_position)?;
let modifier_position = modifier.position();
let modifier = self
.run_expression(modifier)?
.expect_value(modifier_position)?;
match operator.inner {
MathOperator::Add => assignee.add_assign(&modifier),
MathOperator::Subtract => assignee.subtract_assign(&modifier),
MathOperator::Multiply => assignee.multiply_assign(&modifier),
MathOperator::Divide => assignee.divide_assign(&modifier),
MathOperator::Modulo => assignee.modulo_assign(&modifier),
}
.map_err(|error| VmError::ValueError {
error,
left_position: assignee_position,
right_position: modifier_position,
})?;
Ok(Evaluation::Return(None))
}
OperatorExpression::ErrorPropagation(_) => todo!(), OperatorExpression::ErrorPropagation(_) => todo!(),
OperatorExpression::Negation(_) => todo!(), OperatorExpression::Negation(_) => todo!(),
OperatorExpression::Not(_) => todo!(), OperatorExpression::Not(_) => todo!(),
@ -742,30 +797,16 @@ mod tests {
use super::*; use super::*;
#[test]
fn mutate_variable() {
let input = "
mut x = ''
x += 'foo'
x += 'bar'
x
";
assert_eq!(run(input), Ok(Some(Value::string_mut("foobar"))));
}
#[test] #[test]
fn async_block() { fn async_block() {
let input = "mut x = 1; async { x += 1; x -= 1; } x"; let input = "let mut x = 1; async { x += 1; x -= 1; } x";
assert!(run(input).unwrap().unwrap().as_integer().is_some()); assert!(run(input).unwrap().unwrap().as_integer().is_some());
} }
#[test] #[test]
fn define_and_instantiate_fields_struct() { fn define_and_instantiate_fields_struct() {
let input = "struct Foo { bar: int, baz: float } Foo { bar = 42, baz = 4.0 }"; let input = "struct Foo { bar: int, baz: float } Foo { bar: 42, baz: 4.0 }";
assert_eq!( assert_eq!(
run(input), run(input),
@ -844,20 +885,6 @@ mod tests {
assert_eq!(run(input), Ok(Some(Value::integer(42)))); assert_eq!(run(input), Ok(Some(Value::integer(42))));
} }
#[test]
fn map_property() {
let input = "{ x = 42 }.x";
assert_eq!(run(input), Ok(Some(Value::integer(42))));
}
#[test]
fn map_property_nested() {
let input = "{ x = { y = 42 } }.x.y";
assert_eq!(run(input), Ok(Some(Value::integer(42))));
}
#[test] #[test]
fn list_index_range() { fn list_index_range() {
let input = "[1, 2, 3, 4, 5][1..3]"; let input = "[1, 2, 3, 4, 5][1..3]";
@ -950,21 +977,23 @@ mod tests {
#[test] #[test]
fn while_loop() { fn while_loop() {
let input = "mut x = 0; while x < 5 { x += 1; } x"; let input = "let mut x = 0; while x < 5 { x += 1; } x";
assert_eq!(run(input), Ok(Some(Value::integer(5)))); assert_eq!(run(input), Ok(Some(Value::integer(5))));
} }
#[test] #[test]
fn subtract_assign() { fn subtract_assign() {
let input = "mut x = 1; x -= 1; x"; let input = "let mut x = 1; x -= 1; x";
assert_eq!(run(input), Ok(Some(Value::integer(0)))); assert_eq!(run(input), Ok(Some(Value::integer(0))));
} }
#[test] #[test]
fn add_assign() { fn add_assign() {
let input = "mut x = 1; x += 1; x"; env_logger::builder().is_test(true).try_init().ok();
let input = "let mut x = 1; x += 1; x";
assert_eq!(run(input), Ok(Some(Value::integer(2)))); assert_eq!(run(input), Ok(Some(Value::integer(2))));
} }
@ -983,13 +1012,6 @@ mod tests {
assert_eq!(run(input), Ok(Some(Value::boolean(true)))); assert_eq!(run(input), Ok(Some(Value::boolean(true))));
} }
#[test]
fn map_equal() {
let input = "{ y = 'foo' } == { y = 'foo' }";
assert_eq!(run(input), Ok(Some(Value::boolean(true))));
}
#[test] #[test]
fn integer_equal() { fn integer_equal() {
let input = "42 == 42"; let input = "42 == 42";