//! Virtual machine for running the abstract syntax tree.
//!
//! This module provides three running option:
//! - `run` convenience function that takes a source code string and runs it
//! - `run_with_context` convenience function that takes a source code string and a context
//! - `Vm` struct that can be used to run an abstract syntax tree
use std::{
collections::BTreeMap,
fmt::{self, Display, Formatter},
};
use crate::{
abstract_tree::{AbstractSyntaxTree, Node, Statement},
parse, Analyzer, BuiltInFunctionError, Context, DustError, Identifier, ParseError, Span,
Struct, StructType, Type, Value, ValueError,
};
/// Run the source code and return the result.
///
/// # Example
/// ```
/// # use dust_lang::vm::run;
/// # use dust_lang::value::Value;
/// let result = run("40 + 2");
///
/// assert_eq!(result, Ok(Some(Value::integer(42))));
/// ```
pub fn run(source: &str) -> Result, DustError> {
let context = Context::new();
run_with_context(source, context)
}
/// Run the source code with a context and return the result.
///
/// # Example
/// ```
/// # use dust_lang::{Context, Identifier, Value, run_with_context};
/// let context = Context::new();
///
/// context.set_value(Identifier::new("foo"), Value::integer(40));
/// context.update_last_position(&Identifier::new("foo"), (100, 100));
///
/// let result = run_with_context("foo + 2", context);
///
/// assert_eq!(result, Ok(Some(Value::integer(42))));
/// ```
pub fn run_with_context(source: &str, context: Context) -> Result , DustError> {
let abstract_syntax_tree = parse(source)?;
let mut analyzer = Analyzer::new(&abstract_syntax_tree, &context);
analyzer
.analyze()
.map_err(|analyzer_error| DustError::AnalyzerError {
analyzer_error,
source,
})?;
let mut vm = Vm::new(abstract_syntax_tree, context);
vm.run()
.map_err(|vm_error| DustError::VmError { vm_error, source })
}
/// Dust virtual machine.
///
/// **Warning**: Do not run an AbstractSyntaxTree that has not been analyzed *with the same
/// context*. Use the `run` or `run_with_context` functions to make sure the program is analyzed
/// before running it.
///
/// See the `run_with_context` function for an example of how to use the Analyzer and the VM.
pub struct Vm {
abstract_tree: AbstractSyntaxTree,
context: Context,
}
impl Vm {
pub fn new(abstract_tree: AbstractSyntaxTree, context: Context) -> Self {
Self {
abstract_tree,
context,
}
}
pub fn run(&mut self) -> Result , 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)
}
fn run_statement(&self, node: Statement) -> Result , VmError> {
todo!()
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum VmError {
ParseError(ParseError),
ValueError {
error: ValueError,
position: Span,
},
// Anaylsis Failures
// These should be prevented by running the analyzer before the VM
BuiltInFunctionError {
error: BuiltInFunctionError,
position: Span,
},
CannotMutate {
value: Value,
position: Span,
},
ExpectedBoolean {
position: Span,
},
ExpectedIdentifier {
position: Span,
},
ExpectedIntegerOrRange {
position: Span,
},
ExpectedIdentifierOrString {
position: Span,
},
ExpectedInteger {
position: Span,
},
ExpectedNumber {
position: Span,
},
ExpectedMap {
position: Span,
},
ExpectedFunction {
actual: Value,
position: Span,
},
ExpectedList {
position: Span,
},
ExpectedValue {
position: Span,
},
UndefinedVariable {
identifier: Node,
},
UndefinedProperty {
value: Value,
value_position: Span,
property: Identifier,
property_position: Span,
},
}
impl VmError {
pub fn position(&self) -> Span {
match self {
Self::ParseError(parse_error) => parse_error.position(),
Self::ValueError { position, .. } => *position,
Self::CannotMutate { position, .. } => *position,
Self::BuiltInFunctionError { position, .. } => *position,
Self::ExpectedBoolean { position } => *position,
Self::ExpectedIdentifier { position } => *position,
Self::ExpectedIdentifierOrString { position } => *position,
Self::ExpectedIntegerOrRange { position } => *position,
Self::ExpectedInteger { position } => *position,
Self::ExpectedFunction { position, .. } => *position,
Self::ExpectedList { position } => *position,
Self::ExpectedMap { position } => *position,
Self::ExpectedNumber { position } => *position,
Self::ExpectedValue { position } => *position,
Self::UndefinedVariable { identifier } => identifier.position,
Self::UndefinedProperty {
property_position, ..
} => *property_position,
}
}
}
impl From for VmError {
fn from(error: ParseError) -> Self {
Self::ParseError(error)
}
}
impl Display for VmError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::ParseError(parse_error) => write!(f, "{}", parse_error),
Self::ValueError { error, .. } => write!(f, "{}", error),
Self::CannotMutate { value, .. } => {
write!(f, "Cannot mutate immutable value {}", value)
}
Self::BuiltInFunctionError { error, .. } => {
write!(f, "{}", error)
}
Self::ExpectedBoolean { position } => {
write!(f, "Expected a boolean at position: {:?}", position)
}
Self::ExpectedFunction { actual, position } => {
write!(
f,
"Expected a function, but got {} at position: {:?}",
actual, position
)
}
Self::ExpectedIdentifier { position } => {
write!(f, "Expected an identifier at position: {:?}", position)
}
Self::ExpectedIdentifierOrString { position } => {
write!(
f,
"Expected an identifier or string at position: {:?}",
position
)
}
Self::ExpectedIntegerOrRange { position } => {
write!(
f,
"Expected an identifier, integer, or range at position: {:?}",
position
)
}
Self::ExpectedInteger { position } => {
write!(f, "Expected an integer at position: {:?}", position)
}
Self::ExpectedList { position } => {
write!(f, "Expected a list at position: {:?}", position)
}
Self::ExpectedMap { position } => {
write!(f, "Expected a map at position: {:?}", position)
}
Self::ExpectedNumber { position } => {
write!(
f,
"Expected an integer or float at position: {:?}",
position
)
}
Self::ExpectedValue { position } => {
write!(f, "Expected a value at position: {:?}", position)
}
Self::UndefinedVariable { identifier } => {
write!(f, "Undefined identifier: {}", identifier)
}
Self::UndefinedProperty {
value, property, ..
} => {
write!(f, "Value {} does not have the property {}", value, property)
}
}
}
}
#[cfg(test)]
mod tests {
use crate::Struct;
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";
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 }";
assert_eq!(
run(input),
Ok(Some(Value::r#struct(Struct::Fields {
name: Identifier::new("Foo"),
fields: vec![
(Identifier::new("bar"), Value::integer(42)),
(Identifier::new("baz"), Value::float(4.0))
]
})))
);
}
#[test]
fn assign_tuple_struct_variable() {
let input = "
struct Foo(int)
x = Foo(42)
x
";
assert_eq!(
run(input),
Ok(Some(Value::r#struct(Struct::Tuple {
name: Identifier::new("Foo"),
fields: vec![Value::integer(42)]
})))
)
}
#[test]
fn define_and_instantiate_tuple_struct() {
let input = "struct Foo(int) Foo(42)";
assert_eq!(
run(input),
Ok(Some(Value::r#struct(Struct::Tuple {
name: Identifier::new("Foo"),
fields: vec![Value::integer(42)]
})))
);
}
#[test]
fn assign_unit_struct_variable() {
let input = "
struct Foo
x = Foo
x
";
assert_eq!(
run(input),
Ok(Some(Value::r#struct(Struct::Unit {
name: Identifier::new("Foo")
})))
)
}
#[test]
fn define_and_instantiate_unit_struct() {
let input = "struct Foo Foo";
assert_eq!(
run(input),
Ok(Some(Value::r#struct(Struct::Unit {
name: Identifier::new("Foo")
})))
);
}
#[test]
fn list_index_nested() {
let input = "[[1, 2], [42, 4], [5, 6]][1][0]";
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 map_property_access_expression() {
let input = "{ foobar = 42 }.('foo' + 'bar')";
assert_eq!(run(input), Ok(Some(Value::integer(42))));
}
#[test]
fn list_index_range() {
let input = "[1, 2, 3, 4, 5][1..3]";
assert_eq!(
run(input),
Ok(Some(Value::list(vec![
Value::integer(2),
Value::integer(3)
])))
);
}
#[test]
fn range() {
let input = "1..5";
assert_eq!(run(input), Ok(Some(Value::range(1..5))));
}
#[test]
fn negate_expression() {
let input = "x = -42; -x";
assert_eq!(run(input), Ok(Some(Value::integer(42))));
}
#[test]
fn not_expression() {
let input = "!(1 == 2 || 3 == 4 || 5 == 6)";
assert_eq!(run(input), Ok(Some(Value::boolean(true))));
}
#[test]
fn list_index() {
let input = "[1, 42, 3][1]";
assert_eq!(run(input), Ok(Some(Value::integer(42))));
}
#[test]
fn map_property_access() {
let input = "{ a = 42 }.a";
assert_eq!(run(input), Ok(Some(Value::integer(42))));
}
#[test]
fn built_in_function_dot_notation() {
let input = "42.to_string()";
assert_eq!(run(input), Ok(Some(Value::string("42"))));
}
#[test]
fn to_string() {
let input = "to_string(42)";
assert_eq!(run(input), Ok(Some(Value::string("42".to_string()))));
}
#[test]
fn r#if() {
let input = "if true { 1 }";
assert_eq!(run(input), Ok(None));
}
#[test]
fn if_else() {
let input = "if false { 1 } else { 2 }";
assert_eq!(run(input), Ok(Some(Value::integer(2))));
}
#[test]
fn if_else_if() {
let input = "if false { 1 } else if true { 2 }";
assert_eq!(run(input), Ok(None));
}
#[test]
fn if_else_if_else() {
let input = "if false { 1 } else if false { 2 } else { 3 }";
assert_eq!(run(input), Ok(Some(Value::integer(3))));
}
#[test]
fn while_loop() {
let input = "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";
assert_eq!(run(input), Ok(Some(Value::integer(0))));
}
#[test]
fn add_assign() {
let input = "mut x = 1; x += 1; x";
assert_eq!(run(input), Ok(Some(Value::integer(2))));
}
#[test]
fn or() {
let input = "true || false";
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";
assert_eq!(run(input), Ok(Some(Value::boolean(true))));
}
#[test]
fn modulo() {
let input = "42 % 2";
assert_eq!(run(input), Ok(Some(Value::integer(0))));
}
#[test]
fn divide() {
let input = "42 / 2";
assert_eq!(run(input), Ok(Some(Value::integer(21))));
}
#[test]
fn less_than() {
let input = "2 < 3";
assert_eq!(run(input), Ok(Some(Value::boolean(true))));
}
#[test]
fn less_than_or_equal() {
let input = "42 <= 42";
assert_eq!(run(input), Ok(Some(Value::boolean(true))));
}
#[test]
fn greater_than() {
let input = "2 > 3";
assert_eq!(run(input), Ok(Some(Value::boolean(false))));
}
#[test]
fn greater_than_or_equal() {
let input = "42 >= 42";
assert_eq!(run(input), Ok(Some(Value::boolean(true))));
}
#[test]
fn integer_saturating_add() {
let input = "9223372036854775807 + 1";
assert_eq!(run(input), Ok(Some(Value::integer(i64::MAX))));
}
#[test]
fn integer_saturating_sub() {
let input = "-9223372036854775808 - 1";
assert_eq!(run(input), Ok(Some(Value::integer(i64::MIN))));
}
#[test]
fn multiply() {
let input = "2 * 3";
assert_eq!(run(input), Ok(Some(Value::integer(6))));
}
#[test]
fn boolean() {
let input = "true";
assert_eq!(run(input), Ok(Some(Value::boolean(true))));
}
#[test]
fn is_even() {
let input = "is_even(42)";
assert_eq!(run(input), Ok(Some(Value::boolean(true))));
}
#[test]
fn is_odd() {
let input = "is_odd(42)";
assert_eq!(run(input), Ok(Some(Value::boolean(false))));
}
#[test]
fn length() {
let input = "length([1, 2, 3])";
assert_eq!(run(input), Ok(Some(Value::integer(3))));
}
#[test]
fn add() {
let input = "1 + 2";
assert_eq!(run(input), Ok(Some(Value::integer(3))));
}
#[test]
fn add_multiple() {
let input = "1 + 2 + 3";
assert_eq!(run(input), Ok(Some(Value::integer(6))));
}
}