Struct budvm::VirtualMachine

source ·
pub struct VirtualMachine<Env>where
    Env: Environment,{
    pub stack: Stack,
    /* private fields */
}
Expand description

A Bud virtual machine instance.

Each instance of this type has its own sandboxed environment. Its stack space, function declarations, and Environment are unique from all other instances of Bud with the exception that Symbols are tracked globally.

General Virtual Machine design

At the core of this type is a Stack, which is a list of Values. The virtual machine divides the stack into “frames” (regions of Values) as it executes and calls functions.

Each stack frame is divided into two sections: arguments that were passed to the function, and space for local variables. Stack frames are automatically managed by the virtual machine.

The only time Values need to be pushed to the stack directly is when calling a function. Each argument being passed to the function is pushed to the stack, and the virtual machine adopts the pushed values as part of the called function’s stack frame.

This example demonstrates a basic function that takes one argument and uses 1 local variable. It performs the equivalent of creating a string like Hello, {name}!.

use std::borrow::Cow;
use budvm::{VirtualMachine, Function, Instruction, Value, ValueOrSource, Destination, Symbol};

let greet = Function {
    name: Symbol::from("greet"),
    arg_count: 1,
    variable_count: 1,
    code: vec![
        Instruction::Add {
            left: ValueOrSource::Value(Value::dynamic(String::from("Hello, "))),
            right: ValueOrSource::Argument(0),
            destination: Destination::Variable(0),
        },
        Instruction::Add {
            left: ValueOrSource::Variable(0),
            right: ValueOrSource::Value(Value::dynamic(String::from("!"))),
            destination: Destination::Return,
        },
    ],
};

let mut vm = VirtualMachine::empty().with_function(greet);
let result: String = vm
    .run(
        &[
            Instruction::Push(ValueOrSource::Value(Value::dynamic(String::from("Ferris")))),
            Instruction::Call {
                vtable_index: Some(0),
                arg_count: 1,
                destination: Destination::Stack,
            },
        ],
        0,
    )
    .unwrap();
assert_eq!(result, "Hello, Ferris!");

When the virtual machine finishes executing Instruction::Call, Instruction::CallIntrinsic, or Instruction::CallInstance, the return value is placed in the correct location and the stack is cleaned up to remove all pushed arguments and local variables of the function being called.

The Rust interface will automatically pop a value from the stack upon returning. The FromStack trait allows a type to be converted seamlessly as in the above example.

The previous example also highlights one other interesting aspect of the virtual machine: all heap-allocated types are grouped into a single Dynamic type. This means that the virtual machine doesn’t actually know anything about the String type. The virtual machine knows how to perform operations with Dynamic values, and any Rust type that implements DynamicValue can be used in the virtual machine.

Each Instruction variant is documented with its expected behavior.

Custom Environments

The virtual machine has several opportunities to customize its behavior. For default behavior, Environment is implemented for ().

There are multiple reasons to implement a custom Environment:

  • The Environment::Intrinsic associated type allows extending the virtual machine with intrinsic functions. Bud uses this to initialize map and list literals at runtime via NewMap and NewList intrinsics.
  • The Environment::String type can be replaced to use another string type.
  • The Environment::step() function can be overriden to pause execution conditionally.

Fields§

§stack: Stack

The stack for this virtual machine. Take care when manually manipulating the stack.

Implementations§

source§

impl VirtualMachine<()>

source

pub fn empty() -> Self

Returns a default instance of Bud with no custom Environment

source§

impl<Env> VirtualMachine<Env>where Env: Environment,

source

pub fn new( environment: Env, initial_stack_capacity: usize, maximum_stack_capacity: usize ) -> Self

Returns a new instance with the provided environment.

source

pub fn default_for(environment: Env) -> Self

Returns a new instance with the provided environment.

source

pub fn environment(&self) -> &Env

Returns a reference to the environment for this instance.

source

pub fn environment_mut(&mut self) -> &mut Env

Returns a mutable refernce to the environment for this instance.

source

pub fn persistent_variables(&self) -> &[Symbol]

Returns a list of persistent variables defined with Scope::define_persistent_variable()

source

pub fn with_function(self, function: Function<Env::Intrinsic>) -> Self

Registers a function with the provided name and returns self. This is a builder-style function.

source

pub fn with_native_function( self, name: impl Into<Symbol>, function: impl NativeFunction + 'static ) -> Self

Registers a function with the provided name and returns self. This is a builder-style function.

source

pub fn define_native_function( &mut self, name: impl Into<Symbol>, function: impl NativeFunction + 'static ) -> usize

Defines a native function with the provided name.

source

pub fn call<Output: FromStack, Args, ArgsIter>( &mut self, function: &Symbol, arguments: Args ) -> Result<Output, Fault<'_, Env, Output>>where Args: IntoIterator<Item = Value, IntoIter = ArgsIter>, ArgsIter: Iterator<Item = Value> + ExactSizeIterator + DoubleEndedIterator,

Runs a set of instructions.

source

pub fn run<'a, Output: FromStack>( &'a mut self, instructions: impl Into<Instructions<'a, Env::Intrinsic>>, variable_count: usize ) -> Result<Output, Fault<'a, Env, Output>>

Runs a set of instructions, allocating space for variable_count variables to be used by instructions. When this function returns, the stack space for the variables will be removed.

source

pub fn run_interactive<'a, Output: FromStack>( &'a mut self, instructions: impl Into<Instructions<'a, Env::Intrinsic>>, variable_count: usize ) -> Result<Output, Fault<'a, Env, Output>>

Runs a set of instructions without modifying the stack before executing.

The top variable_count slots on the stack are considered variables while executing instructions.

When the execution finishes, the stack will not be truncated in any way.

This function can be used to build an interactive environment, like a REPL.

Trait Implementations§

source§

impl<Env> Clone for VirtualMachine<Env>where Env: Environment + Clone, Env::Intrinsic: Clone,

source§

fn clone(&self) -> VirtualMachine<Env>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<Env> Debug for VirtualMachine<Env>where Env: Environment + Debug, Env::Intrinsic: Debug,

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<Env> Default for VirtualMachine<Env>where Env: Environment + Default, Env::Intrinsic: Default,

source§

fn default() -> VirtualMachine<Env>

Returns the “default value” for a type. Read more
source§

impl<Env> PartialEq<VirtualMachine<Env>> for VirtualMachine<Env>where Env: Environment + PartialEq, Env::Intrinsic: PartialEq,

source§

fn eq(&self, other: &VirtualMachine<Env>) -> bool

This method tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
source§

impl<Env> Scope for VirtualMachine<Env>where Env: Environment,

§

type Environment = Env

The environment for this scope. Read more
source§

fn resolve_function_vtable_index(&self, name: &Symbol) -> Option<usize>

Returns the vtable index of a function with the provided name.
source§

fn map_each_symbol(&self, callback: &mut impl FnMut(Symbol, ScopeSymbolKind))

Invokes callback for each symbol defined in this scope.
source§

fn define_function( &mut self, function: Function<Env::Intrinsic> ) -> Option<usize>

Defines a function with the provided name.
source§

fn define_persistent_variable(&mut self, name: Symbol, variable: Variable)

Defines a persistent variable. Read more
source§

impl<Env> StructuralPartialEq for VirtualMachine<Env>where Env: Environment,

Auto Trait Implementations§

§

impl<Env> !RefUnwindSafe for VirtualMachine<Env>

§

impl<Env> !Send for VirtualMachine<Env>

§

impl<Env> !Sync for VirtualMachine<Env>

§

impl<Env> Unpin for VirtualMachine<Env>where Env: Unpin, <Env as Environment>::Intrinsic: Unpin,

§

impl<Env> !UnwindSafe for VirtualMachine<Env>

Blanket Implementations§

source§

impl<T> Any for Twhere T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere T: ?Sized,

const: unstable · source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere T: ?Sized,

const: unstable · source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

const: unstable · source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for Twhere U: From<T>,

const: unstable · source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> ToOwned for Twhere T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
const: unstable · source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
const: unstable · source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.