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()))
|
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 {
|
||||||
|
@ -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 }";
|
||||||
|
Loading…
Reference in New Issue
Block a user