Add Vm tests and refine how mutability and assignment works

This commit is contained in:
Jeff 2024-08-24 14:17:38 -04:00
parent 561a290b16
commit 5f733ba1dd
2 changed files with 110 additions and 53 deletions

View File

@ -82,8 +82,8 @@ impl Value {
Value::Raw(ValueData::String(to_string.to_string())) Value::Raw(ValueData::String(to_string.to_string()))
} }
pub fn list(value: Vec<Value>) -> Self { pub fn list<T: Into<Vec<Value>>>(values: T) -> Self {
Value::Raw(ValueData::List(value)) Value::Raw(ValueData::List(values.into()))
} }
pub fn map<T: Into<HashMap<Identifier, Value>>>(into_map: T) -> Self { 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 Eq for Value {}
impl PartialEq 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 { impl Display for ValueData {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {

View File

@ -7,6 +7,7 @@
use std::{ use std::{
collections::HashMap, collections::HashMap,
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
rc::Weak,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
@ -242,11 +243,18 @@ impl Vm {
let position = value.position(); let position = value.position();
let value = self let value = self
.run_expression(value, collect_garbage)? .run_expression(value, collect_garbage)?
.expect_value(position)? .expect_value(position)?;
.into_reference();
let reference = match value {
Value::Raw(_) => value.into_reference(),
Value::Reference(_) => value,
Value::Mutable(_) => {
return Err(RuntimeError::CannotAssignToMutable { position });
}
};
self.context self.context
.set_variable_value(identifier.inner, value) .set_variable_value(identifier.inner, reference)
.map_err(|error| RuntimeError::ContextError { error, position })?; .map_err(|error| RuntimeError::ContextError { error, position })?;
Ok(()) Ok(())
@ -1080,6 +1088,9 @@ impl Vm {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum RuntimeError { pub enum RuntimeError {
CannotAssignToMutable {
position: Span,
},
ConstructError { ConstructError {
error: ConstructError, error: ConstructError,
position: Span, position: Span,
@ -1182,6 +1193,7 @@ pub enum RuntimeError {
impl RuntimeError { impl RuntimeError {
pub fn position(&self) -> Span { pub fn position(&self) -> Span {
match self { match self {
Self::CannotAssignToMutable { position } => *position,
Self::ConstructError { position, .. } => *position, Self::ConstructError { position, .. } => *position,
Self::ContextError { position, .. } => *position, Self::ContextError { position, .. } => *position,
Self::BuiltInFunctionError { position, .. } => *position, Self::BuiltInFunctionError { position, .. } => *position,
@ -1233,6 +1245,13 @@ impl From<ParseError> for RuntimeError {
impl Display for RuntimeError { impl Display for RuntimeError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
Self::CannotAssignToMutable { position } => {
write!(
f,
"Cannot use immutable assignment with a mutable value at {:?}",
position
)
}
Self::ConstructError { error, position } => { Self::ConstructError { error, position } => {
write!(f, "Constructor error at {:?}: {}", position, error) write!(f, "Constructor error at {:?}: {}", position, error)
} }
@ -1398,6 +1417,44 @@ mod tests {
use super::*; 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] #[test]
fn block_scope_captures_parent() { fn block_scope_captures_parent() {
let source = "let x = 42; { x }"; let source = "let x = 42; { x }";