Add Vm tests and refine how mutability and assignment works
This commit is contained in:
parent
561a290b16
commit
5f733ba1dd
@ -82,8 +82,8 @@ impl Value {
|
||||
Value::Raw(ValueData::String(to_string.to_string()))
|
||||
}
|
||||
|
||||
pub fn list(value: Vec<Value>) -> Self {
|
||||
Value::Raw(ValueData::List(value))
|
||||
pub fn list<T: Into<Vec<Value>>>(values: T) -> Self {
|
||||
Value::Raw(ValueData::List(values.into()))
|
||||
}
|
||||
|
||||
pub fn map<T: Into<HashMap<Identifier, Value>>>(into_map: T) -> Self {
|
||||
@ -737,6 +737,54 @@ impl Display for Value {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for Value {
|
||||
fn from(value: bool) -> Self {
|
||||
Value::boolean(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for Value {
|
||||
fn from(value: u8) -> Self {
|
||||
Value::byte(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<char> for Value {
|
||||
fn from(value: char) -> Self {
|
||||
Value::character(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for Value {
|
||||
fn from(value: f64) -> Self {
|
||||
Value::float(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for Value {
|
||||
fn from(value: i32) -> Self {
|
||||
Value::integer(value as i64)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i64> for Value {
|
||||
fn from(value: i64) -> Self {
|
||||
Value::integer(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Value {
|
||||
fn from(value: String) -> Self {
|
||||
Value::string(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Value {
|
||||
fn from(str: &str) -> Self {
|
||||
Value::string(str)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Value {}
|
||||
|
||||
impl PartialEq for Value {
|
||||
@ -1118,54 +1166,6 @@ impl ValueData {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for ValueData {
|
||||
fn from(value: bool) -> Self {
|
||||
ValueData::Boolean(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for ValueData {
|
||||
fn from(value: u8) -> Self {
|
||||
ValueData::Byte(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<char> for ValueData {
|
||||
fn from(value: char) -> Self {
|
||||
ValueData::Character(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for ValueData {
|
||||
fn from(value: f64) -> Self {
|
||||
ValueData::Float(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for ValueData {
|
||||
fn from(value: i32) -> Self {
|
||||
ValueData::Integer(value as i64)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i64> for ValueData {
|
||||
fn from(value: i64) -> Self {
|
||||
ValueData::Integer(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for ValueData {
|
||||
fn from(value: String) -> Self {
|
||||
ValueData::String(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for ValueData {
|
||||
fn from(value: &str) -> Self {
|
||||
ValueData::String(value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ValueData {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
|
@ -7,6 +7,7 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fmt::{self, Display, Formatter},
|
||||
rc::Weak,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
@ -242,11 +243,18 @@ impl Vm {
|
||||
let position = value.position();
|
||||
let value = self
|
||||
.run_expression(value, collect_garbage)?
|
||||
.expect_value(position)?
|
||||
.into_reference();
|
||||
.expect_value(position)?;
|
||||
|
||||
let reference = match value {
|
||||
Value::Raw(_) => value.into_reference(),
|
||||
Value::Reference(_) => value,
|
||||
Value::Mutable(_) => {
|
||||
return Err(RuntimeError::CannotAssignToMutable { position });
|
||||
}
|
||||
};
|
||||
|
||||
self.context
|
||||
.set_variable_value(identifier.inner, value)
|
||||
.set_variable_value(identifier.inner, reference)
|
||||
.map_err(|error| RuntimeError::ContextError { error, position })?;
|
||||
|
||||
Ok(())
|
||||
@ -1080,6 +1088,9 @@ impl Vm {
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum RuntimeError {
|
||||
CannotAssignToMutable {
|
||||
position: Span,
|
||||
},
|
||||
ConstructError {
|
||||
error: ConstructError,
|
||||
position: Span,
|
||||
@ -1182,6 +1193,7 @@ pub enum RuntimeError {
|
||||
impl RuntimeError {
|
||||
pub fn position(&self) -> Span {
|
||||
match self {
|
||||
Self::CannotAssignToMutable { position } => *position,
|
||||
Self::ConstructError { position, .. } => *position,
|
||||
Self::ContextError { position, .. } => *position,
|
||||
Self::BuiltInFunctionError { position, .. } => *position,
|
||||
@ -1233,6 +1245,13 @@ impl From<ParseError> for RuntimeError {
|
||||
impl Display for RuntimeError {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::CannotAssignToMutable { position } => {
|
||||
write!(
|
||||
f,
|
||||
"Cannot use immutable assignment with a mutable value at {:?}",
|
||||
position
|
||||
)
|
||||
}
|
||||
Self::ConstructError { error, position } => {
|
||||
write!(f, "Constructor error at {:?}: {}", position, error)
|
||||
}
|
||||
@ -1398,6 +1417,44 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn mutate_copied_variable() {
|
||||
let source = "let mut x = 42; let y = [x; 3]; x += 1; y";
|
||||
|
||||
assert_eq!(
|
||||
run(source),
|
||||
Ok(Some(Value::list([43.into(), 43.into(), 43.into()])))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auto_fill_list() {
|
||||
let source = "[42; 3]";
|
||||
|
||||
assert_eq!(
|
||||
run(source),
|
||||
Ok(Some(Value::list(vec![
|
||||
Value::integer(42),
|
||||
Value::integer(42),
|
||||
Value::integer(42)
|
||||
])))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mutate_assigned_variable() {
|
||||
let source = "let mut x = 42; let mut y = x; x += 1; y";
|
||||
|
||||
assert_eq!(run(source), Ok(Some(Value::integer(43))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assign_to_variable() {
|
||||
let source = "let x = 42; let y = x; y";
|
||||
|
||||
assert_eq!(run(source), Ok(Some(Value::integer(42))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_scope_captures_parent() {
|
||||
let source = "let x = 42; { x }";
|
||||
|
Loading…
Reference in New Issue
Block a user