Implement VM and Analyzer by using the new AST
This commit is contained in:
parent
fedefdb29f
commit
7d721beb31
@ -10,8 +10,8 @@ use std::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
ast::{AbstractSyntaxTree, Node, Statement},
|
||||
parse, Context, DustError, Identifier, Span, Type,
|
||||
ast::{AbstractSyntaxTree, LetStatement, Node, OperatorExpression, Statement},
|
||||
parse, Context, DustError, Expression, Identifier, Span, Type,
|
||||
};
|
||||
|
||||
/// Analyzes the abstract syntax tree for errors.
|
||||
@ -65,14 +65,125 @@ impl<'a> Analyzer<'a> {
|
||||
}
|
||||
|
||||
pub fn analyze(&mut self) -> Result<(), AnalyzerError> {
|
||||
for node in &self.abstract_tree.statements {
|
||||
self.analyze_statement(node)?;
|
||||
for statement in &self.abstract_tree.statements {
|
||||
self.analyze_statement(statement)?;
|
||||
}
|
||||
|
||||
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(())
|
||||
}
|
||||
}
|
||||
@ -124,13 +235,13 @@ pub enum AnalyzerError {
|
||||
identifier: Node<Identifier>,
|
||||
},
|
||||
UnexpectedIdentifier {
|
||||
identifier: Statement,
|
||||
identifier: Node<Identifier>,
|
||||
},
|
||||
UnexectedString {
|
||||
actual: Statement,
|
||||
},
|
||||
UndefinedVariable {
|
||||
identifier: Statement,
|
||||
identifier: Node<Identifier>,
|
||||
},
|
||||
}
|
||||
|
||||
@ -151,8 +262,8 @@ impl AnalyzerError {
|
||||
} => actual_statement.position(),
|
||||
AnalyzerError::UndefinedField { identifier, .. } => identifier.position(),
|
||||
AnalyzerError::UndefinedType { identifier } => identifier.position,
|
||||
AnalyzerError::UndefinedVariable { identifier } => identifier.position(),
|
||||
AnalyzerError::UnexpectedIdentifier { identifier } => identifier.position(),
|
||||
AnalyzerError::UndefinedVariable { identifier } => identifier.position,
|
||||
AnalyzerError::UnexpectedIdentifier { identifier } => identifier.position,
|
||||
AnalyzerError::UnexectedString { actual } => actual.position(),
|
||||
}
|
||||
}
|
||||
|
@ -170,6 +170,10 @@ impl<'src> Parser<'src> {
|
||||
|
||||
let value = self.parse_expression(0)?;
|
||||
|
||||
if let Token::Semicolon = self.current_token {
|
||||
self.next_token()?;
|
||||
}
|
||||
|
||||
let r#let = if is_mutable {
|
||||
LetStatement::LetMut { identifier, value }
|
||||
} else {
|
||||
|
@ -366,7 +366,7 @@ impl Display for TokenOwned {
|
||||
TokenOwned::Async => Token::Async.fmt(f),
|
||||
TokenOwned::Bang => Token::Bang.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::Colon => Token::Colon.fmt(f),
|
||||
TokenOwned::Comma => Token::Comma.fmt(f),
|
||||
@ -379,12 +379,12 @@ impl Display for TokenOwned {
|
||||
TokenOwned::Eof => Token::Eof.fmt(f),
|
||||
TokenOwned::Equal => Token::Equal.fmt(f),
|
||||
TokenOwned::Float(float) => write!(f, "{float}"),
|
||||
TokenOwned::FloatKeyword => write!(f, "float"),
|
||||
TokenOwned::FloatKeyword => Token::FloatKeyword.fmt(f),
|
||||
TokenOwned::Greater => Token::Greater.fmt(f),
|
||||
TokenOwned::GreaterOrEqual => Token::GreaterEqual.fmt(f),
|
||||
TokenOwned::Identifier(text) => write!(f, "{text}"),
|
||||
TokenOwned::If => Token::If.fmt(f),
|
||||
TokenOwned::Int => write!(f, "int"),
|
||||
TokenOwned::Int => Token::Int.fmt(f),
|
||||
TokenOwned::Integer(integer) => write!(f, "{integer}"),
|
||||
TokenOwned::IsEven => Token::IsEven.fmt(f),
|
||||
TokenOwned::IsOdd => Token::IsOdd.fmt(f),
|
||||
@ -408,7 +408,7 @@ impl Display for TokenOwned {
|
||||
TokenOwned::Semicolon => Token::Semicolon.fmt(f),
|
||||
TokenOwned::Star => Token::Star.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::Struct => Token::Struct.fmt(f),
|
||||
TokenOwned::ToString => Token::ToString.fmt(f),
|
||||
|
@ -105,12 +105,17 @@ impl Value {
|
||||
matches!(self, Value::Mutable(_))
|
||||
}
|
||||
|
||||
pub fn to_mut(self) -> Self {
|
||||
match self {
|
||||
Value::Immutable(inner) => {
|
||||
Value::Mutable(Arc::new(RwLock::new(inner.as_ref().clone())))
|
||||
}
|
||||
_ => self,
|
||||
pub fn to_mut(self) -> Result<Value, ValueError> {
|
||||
if let Value::Immutable(arc) = self {
|
||||
let value_data = if let Some(value_data) = Arc::into_inner(arc) {
|
||||
value_data
|
||||
} else {
|
||||
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()))
|
||||
}
|
||||
|
||||
pub fn add_mut(&self, other: &Value) -> Result<(), ValueError> {
|
||||
pub fn add_assign(&self, other: &Value) -> Result<(), ValueError> {
|
||||
match (self, other) {
|
||||
(Value::Mutable(left), Value::Mutable(right)) => {
|
||||
match (&mut *left.write().unwrap(), &*right.read().unwrap()) {
|
||||
@ -412,7 +417,7 @@ impl Value {
|
||||
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) {
|
||||
(Value::Mutable(left), Value::Mutable(right)) => {
|
||||
match (&mut *left.write().unwrap(), &*right.read().unwrap()) {
|
||||
@ -489,6 +494,42 @@ impl Value {
|
||||
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> {
|
||||
match (self, other) {
|
||||
(Value::Immutable(left), Value::Immutable(right)) => {
|
||||
@ -564,6 +605,54 @@ impl Value {
|
||||
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> {
|
||||
match (self, other) {
|
||||
(Value::Immutable(left), Value::Immutable(right)) => {
|
||||
@ -627,6 +716,48 @@ impl Value {
|
||||
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 {
|
||||
let is_equal = match (self, other) {
|
||||
(Value::Immutable(left), Value::Immutable(right)) => left == right,
|
||||
@ -1614,6 +1745,7 @@ pub enum ValueError {
|
||||
CannotGreaterThanOrEqual(Value, Value),
|
||||
CannotLessThan(Value, Value),
|
||||
CannotLessThanOrEqual(Value, Value),
|
||||
CannotMakeMutable,
|
||||
CannotModulo(Value, Value),
|
||||
CannotMultiply(Value, Value),
|
||||
CannotMutate(Value),
|
||||
@ -1644,6 +1776,10 @@ impl Display for ValueError {
|
||||
ValueError::CannotMultiply(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::CannotSubtract(left, right) => {
|
||||
write!(f, "Cannot subtract {} and {}", left, right)
|
||||
|
@ -14,12 +14,12 @@ use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterato
|
||||
use crate::{
|
||||
ast::{
|
||||
AbstractSyntaxTree, BlockExpression, CallExpression, ComparisonOperator, ElseExpression,
|
||||
FieldAccessExpression, IfExpression, ListExpression, ListIndexExpression,
|
||||
FieldAccessExpression, IfExpression, LetStatement, ListExpression, ListIndexExpression,
|
||||
LiteralExpression, LogicOperator, LoopExpression, MathOperator, Node, OperatorExpression,
|
||||
Statement,
|
||||
},
|
||||
parse, Analyzer, BuiltInFunctionError, Context, DustError, Expression, Identifier, ParseError,
|
||||
Span, Type, Value, ValueError,
|
||||
Span, Value, ValueError,
|
||||
};
|
||||
|
||||
/// Run the source code and return the result.
|
||||
@ -90,21 +90,12 @@ impl Vm {
|
||||
}
|
||||
|
||||
pub fn run(&mut self) -> Result<Option<Value>, VmError> {
|
||||
let mut previous_position = (0, 0);
|
||||
let mut previous_value = None;
|
||||
|
||||
while let Some(statement) = self.abstract_tree.statements.pop_front() {
|
||||
let new_position = statement.position();
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@ -119,16 +110,50 @@ impl Vm {
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
Statement::Let(_) => todo!(),
|
||||
Statement::Let(let_statement) => {
|
||||
self.run_let_statement(let_statement.inner)?;
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
Statement::StructDefinition(_) => todo!(),
|
||||
};
|
||||
|
||||
self.context.collect_garbage(position.1);
|
||||
|
||||
result.map_err(|error| VmError::Trace {
|
||||
error: Box::new(error),
|
||||
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> {
|
||||
let position = expression.position();
|
||||
let evaluation_result = match expression {
|
||||
@ -177,7 +202,13 @@ impl Vm {
|
||||
let value_position = 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))
|
||||
}
|
||||
@ -230,7 +261,31 @@ impl Vm {
|
||||
assignee,
|
||||
operator,
|
||||
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::Negation(_) => todo!(),
|
||||
OperatorExpression::Not(_) => todo!(),
|
||||
@ -742,30 +797,16 @@ mod tests {
|
||||
|
||||
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]
|
||||
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());
|
||||
}
|
||||
|
||||
#[test]
|
||||
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!(
|
||||
run(input),
|
||||
@ -844,20 +885,6 @@ mod tests {
|
||||
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]
|
||||
fn list_index_range() {
|
||||
let input = "[1, 2, 3, 4, 5][1..3]";
|
||||
@ -950,21 +977,23 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
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))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
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))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
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))));
|
||||
}
|
||||
@ -983,13 +1012,6 @@ mod tests {
|
||||
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]
|
||||
fn integer_equal() {
|
||||
let input = "42 == 42";
|
||||
|
Loading…
Reference in New Issue
Block a user