From 8bff39a7db1668c6bd0d925b415a175672868772 Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 16 Aug 2024 11:21:20 -0400 Subject: [PATCH] Prepare for value overhaul --- dust-lang/src/analyzer.rs | 8 +- dust-lang/src/ast/expression.rs | 29 +++- dust-lang/src/parser.rs | 4 +- dust-lang/src/value.rs | 47 ++++-- dust-lang/src/vm.rs | 268 +++++++++++++++++++++++--------- 5 files changed, 261 insertions(+), 95 deletions(-) diff --git a/dust-lang/src/analyzer.rs b/dust-lang/src/analyzer.rs index 99eb0d6..7ce0534 100644 --- a/dust-lang/src/analyzer.rs +++ b/dust-lang/src/analyzer.rs @@ -168,8 +168,12 @@ impl<'a> Analyzer<'a> { self.analyze_expression(modifier)?; } OperatorExpression::ErrorPropagation(_) => todo!(), - OperatorExpression::Negation(_) => todo!(), - OperatorExpression::Not(_) => todo!(), + OperatorExpression::Negation(expression) => { + self.analyze_expression(expression)?; + } + OperatorExpression::Not(expression) => { + self.analyze_expression(expression)?; + } OperatorExpression::Math { left, right, .. } => { self.analyze_expression(left)?; self.analyze_expression(right)?; diff --git a/dust-lang/src/ast/expression.rs b/dust-lang/src/ast/expression.rs index 023d069..eb53e56 100644 --- a/dust-lang/src/ast/expression.rs +++ b/dust-lang/src/ast/expression.rs @@ -22,7 +22,7 @@ pub enum Expression { Literal(Node>), Loop(Node>), Operator(Node>), - Range(Node>), + Range(Node>), Struct(Node>), TupleAccess(Node>), } @@ -32,8 +32,18 @@ impl Expression { Self::Operator(Node::new(Box::new(operator_expression), position)) } - pub fn range(start: Expression, end: Expression, position: Span) -> Self { - Self::Range(Node::new(Box::new(Range { start, end }), position)) + pub fn exclusive_range(start: Expression, end: Expression, position: Span) -> Self { + Self::Range(Node::new( + Box::new(RangeExpression::Exclusive { start, end }), + position, + )) + } + + pub fn inclusive_range(start: Expression, end: Expression, position: Span) -> Self { + Self::Range(Node::new( + Box::new(RangeExpression::Inclusive { start, end }), + position, + )) } pub fn call(invoker: Expression, arguments: Vec, position: Span) -> Self { @@ -391,14 +401,17 @@ impl Display for TupleAccess { } #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] -pub struct Range { - pub start: Expression, - pub end: Expression, +pub enum RangeExpression { + Exclusive { start: Expression, end: Expression }, + Inclusive { start: Expression, end: Expression }, } -impl Display for Range { +impl Display for RangeExpression { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{}..{}", self.start, self.end) + match self { + RangeExpression::Exclusive { start, end } => write!(f, "{}..{}", start, end), + RangeExpression::Inclusive { start, end } => write!(f, "{}..={}", start, end), + } } } diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index 21b2b45..8f546e7 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -633,7 +633,7 @@ impl<'src> Parser<'src> { let end = self.parse_expression(operator_precedence)?; let position = (left_start, end.position().1); - return Ok(Expression::range(left, end, position)); + return Ok(Expression::exclusive_range(left, end, position)); } if let Token::Minus | Token::Plus | Token::Star | Token::Slash | Token::Percent = @@ -1335,7 +1335,7 @@ mod tests { assert_eq!( parse(source), Ok(AbstractSyntaxTree::with_statements([ - Statement::Expression(Expression::range( + Statement::Expression(Expression::exclusive_range( Expression::literal(LiteralExpression::Integer(0), (0, 1)), Expression::literal(LiteralExpression::Integer(42), (3, 5)), (0, 5) diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index 0dd48fe..d5b4fa5 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -4,7 +4,7 @@ use std::{ collections::BTreeMap, error::Error, fmt::{self, Display, Formatter}, - ops::Range, + ops::{Range, RangeInclusive}, sync::{Arc, RwLock, RwLockWriteGuard}, }; @@ -80,7 +80,7 @@ impl Value { } pub fn range(range: Range) -> Self { - Value::Immutable(Arc::new(ValueData::Range(range))) + Value::Immutable(Arc::new(ValueData::RangeExclusive(range))) } pub fn string(to_string: T) -> Self { @@ -1137,7 +1137,8 @@ pub enum ValueData { Integer(i64), List(Vec), Map(BTreeMap), - Range(Range), + RangeExclusive(Range), + RangeInclusive(RangeInclusive), String(String), Struct(Struct), } @@ -1172,7 +1173,8 @@ impl ValueData { Type::Map(type_map) } - ValueData::Range(_) => Type::Range, + ValueData::RangeExclusive(_) => Type::Range, + ValueData::RangeInclusive(_) => Type::Range, ValueData::String(_) => Type::String, ValueData::Struct(r#struct) => match r#struct { Struct::Unit { name } => Type::Struct(StructType::Unit { name: name.clone() }), @@ -1196,12 +1198,12 @@ impl ValueData { } } - fn get_index(&self, index: usize) -> Option { + fn get_index(&self, index: Value) -> Option { if let ValueData::List(list) = self { return list.get(index).cloned(); } - if let ValueData::Range(range) = self { + if let ValueData::RangeExclusive(range) = self { if range.contains(&(index as i64)) { return Some(Value::integer(index as i64)); } @@ -1271,7 +1273,8 @@ impl Display for ValueData { write!(f, " }}") } - ValueData::Range(range) => write!(f, "{}..{}", range.start, range.end), + ValueData::RangeExclusive(range) => write!(f, "{}..{}", range.start, range.end), + ValueData::RangeInclusive(range) => write!(f, "{}..={}", range.start(), range.end()), ValueData::String(string) => write!(f, "{string}"), ValueData::Struct(r#struct) => write!(f, "{struct}"), } @@ -1303,7 +1306,7 @@ impl Ord for ValueData { (List(_), _) => Ordering::Greater, (Map(left), Map(right)) => left.cmp(right), (Map(_), _) => Ordering::Greater, - (Range(left), Range(right)) => { + (RangeExclusive(left), RangeExclusive(right)) => { let start_cmp = left.start.cmp(&right.start); if start_cmp.is_eq() { @@ -1312,7 +1315,17 @@ impl Ord for ValueData { start_cmp } } - (Range(_), _) => Ordering::Greater, + (RangeExclusive(_), _) => Ordering::Greater, + (RangeInclusive(left), RangeInclusive(right)) => { + let start_cmp = left.start().cmp(right.start()); + + if start_cmp.is_eq() { + left.end().cmp(right.end()) + } else { + start_cmp + } + } + (RangeInclusive(_), _) => Ordering::Greater, (String(left), String(right)) => left.cmp(right), (String(_), _) => Ordering::Greater, (Struct(left), Struct(right)) => left.cmp(right), @@ -1349,7 +1362,7 @@ impl Serialize for ValueData { map_ser.end() } - ValueData::Range(range) => { + ValueData::RangeExclusive(range) => { let mut tuple_ser = serializer.serialize_tuple(2)?; tuple_ser.serialize_element(&range.start)?; @@ -1357,6 +1370,14 @@ impl Serialize for ValueData { tuple_ser.end() } + ValueData::RangeInclusive(range) => { + let mut tuple_ser = serializer.serialize_tuple(2)?; + + tuple_ser.serialize_element(&range.start())?; + tuple_ser.serialize_element(&range.end())?; + + tuple_ser.end() + } ValueData::String(string) => serializer.serialize_str(string), ValueData::Struct(r#struct) => r#struct.serialize(serializer), } @@ -1626,7 +1647,7 @@ impl Function { if let (Some(value_parameters), Some(value_arguments)) = (&self.value_parameters, value_arguments) { - for ((identifier, _), value) in value_parameters.into_iter().zip(value_arguments) { + for ((identifier, _), value) in value_parameters.iter().zip(value_arguments) { new_context.set_value(identifier.clone(), value); } } @@ -1743,6 +1764,7 @@ pub enum ValueError { CannotDivide(Value, Value), CannotGreaterThan(Value, Value), CannotGreaterThanOrEqual(Value, Value), + CannotIndex { value: Value, index: Value }, CannotLessThan(Value, Value), CannotLessThanOrEqual(Value, Value), CannotMakeMutable, @@ -1770,6 +1792,9 @@ impl Display for ValueError { ValueError::CannotDivide(left, right) => { write!(f, "Cannot divide {} by {}", left, right) } + ValueError::CannotIndex { value, index } => { + write!(f, "Cannot index {} with {}", value, index) + } ValueError::CannotModulo(left, right) => { write!(f, "Cannot modulo {} by {}", left, right) } diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index ed25474..42ee329 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -6,6 +6,7 @@ //! - `Vm` struct that can be used to run an abstract syntax tree use std::{ fmt::{self, Display, Formatter}, + ops::Range, sync::{Arc, Mutex}, }; @@ -16,7 +17,7 @@ use crate::{ AbstractSyntaxTree, BlockExpression, CallExpression, ComparisonOperator, ElseExpression, FieldAccessExpression, IfExpression, LetStatement, ListExpression, ListIndexExpression, LiteralExpression, LogicOperator, LoopExpression, MathOperator, Node, OperatorExpression, - Statement, + RangeExpression, Statement, }, parse, Analyzer, BuiltInFunctionError, Context, DustError, Expression, Identifier, ParseError, Span, Value, ValueError, @@ -93,32 +94,38 @@ impl Vm { let mut previous_value = None; while let Some(statement) = self.abstract_tree.statements.pop_front() { - previous_value = self.run_statement(statement)?; + previous_value = self.run_statement(statement, true)?; } Ok(previous_value) } - fn run_statement(&self, statement: Statement) -> Result, VmError> { + fn run_statement( + &self, + statement: Statement, + collect_garbage: bool, + ) -> Result, VmError> { let position = statement.position(); let result = match statement { Statement::Expression(expression) => self - .run_expression(expression) + .run_expression(expression, collect_garbage) .map(|evaluation| evaluation.value()), Statement::ExpressionNullified(expression) => { - self.run_expression(expression.inner)?; + self.run_expression(expression.inner, collect_garbage)?; Ok(None) } Statement::Let(let_statement) => { - self.run_let_statement(let_statement.inner)?; + self.run_let_statement(let_statement.inner, collect_garbage)?; Ok(None) } Statement::StructDefinition(_) => todo!(), }; - self.context.collect_garbage(position.1); + if collect_garbage { + self.context.collect_garbage(position.1); + } result.map_err(|error| VmError::Trace { error: Box::new(error), @@ -126,11 +133,17 @@ impl Vm { }) } - fn run_let_statement(&self, let_statement: LetStatement) -> Result<(), VmError> { + fn run_let_statement( + &self, + let_statement: LetStatement, + collect_garbage: bool, + ) -> Result<(), VmError> { match let_statement { LetStatement::Let { identifier, value } => { let value_position = value.position(); - let value = self.run_expression(value)?.expect_value(value_position)?; + let value = self + .run_expression(value, collect_garbage)? + .expect_value(value_position)?; self.context.set_value(identifier.inner, value); @@ -138,7 +151,9 @@ impl Vm { } LetStatement::LetMut { identifier, value } => { let value_position = value.position(); - let value = self.run_expression(value)?.expect_value(value_position)?; + let value = self + .run_expression(value, collect_garbage)? + .expect_value(value_position)?; let mutable_value = value.to_mut().map_err(|error| VmError::ValueError { error, left_position: identifier.position, @@ -154,13 +169,21 @@ impl Vm { } } - fn run_expression(&self, expression: Expression) -> Result { + fn run_expression( + &self, + expression: Expression, + collect_garbage: bool, + ) -> Result { let position = expression.position(); let evaluation_result = match expression { - Expression::Block(Node { inner, .. }) => self.run_block(*inner), - Expression::Call(call) => self.run_call(*call.inner), - Expression::FieldAccess(field_access) => self.run_field_access(*field_access.inner), - Expression::Grouped(expression) => self.run_expression(*expression.inner), + Expression::Block(Node { inner, .. }) => self.run_block(*inner, collect_garbage), + Expression::Call(call) => self.run_call(*call.inner, collect_garbage), + Expression::FieldAccess(field_access) => { + self.run_field_access(*field_access.inner, collect_garbage) + } + Expression::Grouped(expression) => { + self.run_expression(*expression.inner, collect_garbage) + } Expression::Identifier(identifier) => { let get_value = self.context.get_value(&identifier.inner); @@ -173,15 +196,33 @@ impl Vm { }) } } - Expression::If(if_expression) => self.run_if(*if_expression.inner), - Expression::List(list_expression) => self.run_list(*list_expression.inner), - Expression::ListIndex(list_index) => self.run_list_index(*list_index.inner), + Expression::If(if_expression) => self.run_if(*if_expression.inner, collect_garbage), + Expression::List(list_expression) => { + self.run_list(*list_expression.inner, collect_garbage) + } + Expression::ListIndex(list_index) => { + self.run_list_index(*list_index.inner, collect_garbage) + } Expression::Literal(literal) => self.run_literal(*literal.inner), Expression::Loop(loop_expression) => self.run_loop(*loop_expression.inner), Expression::Operator(operator_expression) => { - self.run_operator(*operator_expression.inner) + self.run_operator(*operator_expression.inner, collect_garbage) } - Expression::Range(_) => todo!(), + Expression::Range(range_expression) => match range_expression.inner.as_ref() { + RangeExpression::Exclusive { start, end } => { + let start_position = start.position(); + let start = self + .run_expression(*start.inner, collect_garbage)? + .expect_value(start_position)?; + let end_position = end.position(); + let end = self + .run_expression(*end.inner, collect_garbage)? + .expect_value(end_position)?; + + Ok(Evaluation::Return(Some(Value::range(start..end)))) + } + RangeExpression::Inclusive { start, end } => todo!(), + }, Expression::Struct(_) => todo!(), Expression::TupleAccess(_) => todo!(), }; @@ -192,15 +233,21 @@ impl Vm { }) } - fn run_operator(&self, operator: OperatorExpression) -> Result { + fn run_operator( + &self, + operator: OperatorExpression, + collect_garbage: bool, + ) -> Result { match operator { OperatorExpression::Assignment { assignee, value } => { let assignee_position = assignee.position(); let assignee = self - .run_expression(assignee)? + .run_expression(assignee, collect_garbage)? .expect_value(assignee_position)?; let value_position = value.position(); - let value = self.run_expression(value)?.expect_value(value_position)?; + let value = self + .run_expression(value, collect_garbage)? + .expect_value(value_position)?; assignee .mutate(value) @@ -218,9 +265,13 @@ impl Vm { right, } => { let left_position = left.position(); - let left_value = self.run_expression(left)?.expect_value(left_position)?; + let left_value = self + .run_expression(left, collect_garbage)? + .expect_value(left_position)?; let right_position = right.position(); - let right_value = self.run_expression(right)?.expect_value(right_position)?; + let right_value = self + .run_expression(right, collect_garbage)? + .expect_value(right_position)?; let outcome = match operator.inner { ComparisonOperator::Equal => left_value.equal(&right_value), @@ -264,11 +315,11 @@ impl Vm { } => { let assignee_position = assignee.position(); let assignee = self - .run_expression(assignee)? + .run_expression(assignee, collect_garbage)? .expect_value(assignee_position)?; let modifier_position = modifier.position(); let modifier = self - .run_expression(modifier)? + .run_expression(modifier, collect_garbage)? .expect_value(modifier_position)?; match operator.inner { @@ -287,17 +338,43 @@ impl Vm { Ok(Evaluation::Return(None)) } OperatorExpression::ErrorPropagation(_) => todo!(), - OperatorExpression::Negation(_) => todo!(), - OperatorExpression::Not(_) => todo!(), + OperatorExpression::Negation(expression) => { + let position = expression.position(); + let value = self + .run_expression(expression, collect_garbage)? + .expect_value(position)?; + let integer = value + .as_integer() + .ok_or(VmError::ExpectedBoolean { position })?; + let negated = Value::integer(-integer); + + Ok(Evaluation::Return(Some(negated))) + } + OperatorExpression::Not(expression) => { + let position = expression.position(); + let value = self + .run_expression(expression, collect_garbage)? + .expect_value(position)?; + let boolean = value + .as_boolean() + .ok_or(VmError::ExpectedBoolean { position })?; + let not = Value::boolean(!boolean); + + Ok(Evaluation::Return(Some(not))) + } OperatorExpression::Math { left, operator, right, } => { let left_position = left.position(); - let left_value = self.run_expression(left)?.expect_value(left_position)?; + let left_value = self + .run_expression(left, collect_garbage)? + .expect_value(left_position)?; let right_position = right.position(); - let right_value = self.run_expression(right)?.expect_value(right_position)?; + let right_value = self + .run_expression(right, collect_garbage)? + .expect_value(right_position)?; let outcome = match operator.inner { MathOperator::Add => left_value.add(&right_value), MathOperator::Subtract => left_value.subtract(&right_value), @@ -319,9 +396,13 @@ impl Vm { right, } => { let left_position = left.position(); - let left_value = self.run_expression(left)?.expect_value(left_position)?; + let left_value = self + .run_expression(left, collect_garbage)? + .expect_value(left_position)?; let right_position = right.position(); - let right_value = self.run_expression(right)?.expect_value(right_position)?; + let right_value = self + .run_expression(right, collect_garbage)? + .expect_value(right_position)?; let outcome = match operator.inner { LogicOperator::And => left_value.and(&right_value), LogicOperator::Or => left_value.or(&right_value), @@ -340,9 +421,22 @@ impl Vm { fn run_loop(&self, loop_expression: LoopExpression) -> Result { match loop_expression { LoopExpression::Infinite { block } => loop { - self.run_expression(Expression::block(block.inner.clone(), block.position))?; + self.run_block(block.inner.clone(), false)?; }, - LoopExpression::While { condition, block } => todo!(), + LoopExpression::While { condition, block } => { + while self + .run_expression(condition.clone(), false)? + .expect_value(condition.position())? + .as_boolean() + .ok_or_else(|| VmError::ExpectedBoolean { + position: condition.position(), + })? + { + self.run_block(block.inner.clone(), false)?; + } + + Ok(Evaluation::Return(None)) + } LoopExpression::For { identifier, iterator, @@ -363,14 +457,22 @@ impl Vm { Ok(Evaluation::Return(Some(value))) } - fn run_list_index(&self, list_index: ListIndexExpression) -> Result { + fn run_list_index( + &self, + list_index: ListIndexExpression, + collect_garbage: bool, + ) -> Result { let ListIndexExpression { list, index } = list_index; let list_position = list.position(); - let list_value = self.run_expression(list)?.expect_value(list_position)?; + let list_value = self + .run_expression(list, collect_garbage)? + .expect_value(list_position)?; let index_position = index.position(); - let index_value = self.run_expression(index)?.expect_value(index_position)?; + let index_value = self + .run_expression(index, collect_garbage)? + .expect_value(index_position)?; let index = if let Some(index) = index_value.as_integer() { index as usize @@ -385,17 +487,22 @@ impl Vm { Ok(Evaluation::Return(value_option)) } - fn run_call(&self, call_expression: CallExpression) -> Result { + fn run_call( + &self, + call_expression: CallExpression, + collect_garbage: bool, + ) -> Result { let CallExpression { invoker, arguments } = call_expression; let invoker_position = invoker.position(); - let invoker_value = if let Some(value) = self.run_expression(invoker)?.value() { - value - } else { - return Err(VmError::ExpectedValue { - position: invoker_position, - }); - }; + let invoker_value = + if let Some(value) = self.run_expression(invoker, collect_garbage)?.value() { + value + } else { + return Err(VmError::ExpectedValue { + position: invoker_position, + }); + }; let function = if let Some(function) = invoker_value.as_function() { function @@ -411,7 +518,7 @@ impl Vm { for argument in arguments { let position = argument.position(); - if let Some(value) = self.run_expression(argument)?.value() { + if let Some(value) = self.run_expression(argument, collect_garbage)?.value() { value_arguments.push(value); } else { return Err(VmError::ExpectedValue { position }); @@ -425,22 +532,31 @@ impl Vm { .map(Evaluation::Return) } - fn run_field_access(&self, field_access: FieldAccessExpression) -> Result { + fn run_field_access( + &self, + field_access: FieldAccessExpression, + collect_garbage: bool, + ) -> Result { let FieldAccessExpression { container, field } = field_access; let container_position = container.position(); - let container_value = if let Some(value) = self.run_expression(container)?.value() { - value - } else { - return Err(VmError::ExpectedValue { - position: container_position, - }); - }; + let container_value = + if let Some(value) = self.run_expression(container, collect_garbage)?.value() { + value + } else { + return Err(VmError::ExpectedValue { + position: container_position, + }); + }; Ok(Evaluation::Return(container_value.get_field(&field.inner))) } - fn run_list(&self, list_expression: ListExpression) -> Result { + fn run_list( + &self, + list_expression: ListExpression, + collect_garbage: bool, + ) -> Result { match list_expression { ListExpression::AutoFill { repeat_operand, @@ -448,14 +564,14 @@ impl Vm { } => { let position = length_operand.position(); let length = self - .run_expression(length_operand)? + .run_expression(length_operand, collect_garbage)? .expect_value(position)? .as_integer() .ok_or(VmError::ExpectedInteger { position })?; let position = repeat_operand.position(); let value = self - .run_expression(repeat_operand)? + .run_expression(repeat_operand, collect_garbage)? .expect_value(position)?; Ok(Evaluation::Return(Some(Value::list(vec![ @@ -468,7 +584,9 @@ impl Vm { for expression in expressions { let position = expression.position(); - let value = self.run_expression(expression)?.expect_value(position)?; + let value = self + .run_expression(expression, collect_garbage)? + .expect_value(position)?; values.push(value); } @@ -478,7 +596,11 @@ impl Vm { } } - fn run_block(&self, block: BlockExpression) -> Result { + fn run_block( + &self, + block: BlockExpression, + collect_garbage: bool, + ) -> Result { match block { BlockExpression::Async(statements) => { let final_result = Arc::new(Mutex::new(None)); @@ -488,7 +610,7 @@ impl Vm { .into_par_iter() .enumerate() .find_map_any(|(i, statement)| { - let evaluation_result = self.run_statement(statement); + let evaluation_result = self.run_statement(statement, false); match evaluation_result { Ok(evaluation) => { @@ -516,7 +638,7 @@ impl Vm { for statement in statements { let position = statement.position(); - previous_value = self.run_statement(statement)?; + previous_value = self.run_statement(statement, collect_garbage)?; self.context.collect_garbage(position.1); } @@ -526,7 +648,11 @@ impl Vm { } } - fn run_if(&self, if_expression: IfExpression) -> Result { + fn run_if( + &self, + if_expression: IfExpression, + collect_garbage: bool, + ) -> Result { match if_expression { IfExpression::If { condition, @@ -534,13 +660,13 @@ impl Vm { } => { let position = condition.position(); let boolean = self - .run_expression(condition)? + .run_expression(condition, collect_garbage)? .expect_value(position)? .as_boolean() .ok_or(VmError::ExpectedBoolean { position })?; if boolean { - self.run_expression(Expression::block(if_block.inner, if_block.position))?; + self.run_block(if_block.inner, collect_garbage)?; } Ok(Evaluation::Return(None)) @@ -552,22 +678,20 @@ impl Vm { } => { let position = condition.position(); let boolean = self - .run_expression(condition)? + .run_expression(condition, collect_garbage)? .expect_value(position)? .as_boolean() .ok_or(VmError::ExpectedBoolean { position })?; if boolean { - self.run_expression(Expression::block(if_block.inner, if_block.position))?; + self.run_block(if_block.inner, collect_garbage)?; } match r#else { ElseExpression::If(if_expression) => { - self.run_expression(Expression::If(if_expression)) - } - ElseExpression::Block(block) => { - self.run_expression(Expression::block(block.inner, block.position)) + self.run_expression(Expression::If(if_expression), collect_garbage) } + ElseExpression::Block(block) => self.run_block(block.inner, collect_garbage), } } } @@ -907,7 +1031,7 @@ mod tests { #[test] fn negate_expression() { - let input = "x = -42; -x"; + let input = "let x = -42; -x"; assert_eq!(run(input), Ok(Some(Value::integer(42)))); }