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 Symbol
s are tracked globally.
General Virtual Machine design
At the core of this type is a Stack
, which is a list of Value
s. The
virtual machine divides the stack into “frames” (regions of Value
s) 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 Value
s 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 viaNewMap
andNewList
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<()>
impl VirtualMachine<()>
sourcepub fn empty() -> Self
pub fn empty() -> Self
Returns a default instance of Bud with no custom Environment
source§impl<Env> VirtualMachine<Env>where
Env: Environment,
impl<Env> VirtualMachine<Env>where Env: Environment,
sourcepub fn new(
environment: Env,
initial_stack_capacity: usize,
maximum_stack_capacity: usize
) -> Self
pub fn new( environment: Env, initial_stack_capacity: usize, maximum_stack_capacity: usize ) -> Self
Returns a new instance with the provided environment.
sourcepub fn default_for(environment: Env) -> Self
pub fn default_for(environment: Env) -> Self
Returns a new instance with the provided environment.
sourcepub fn environment(&self) -> &Env
pub fn environment(&self) -> &Env
Returns a reference to the environment for this instance.
sourcepub fn environment_mut(&mut self) -> &mut Env
pub fn environment_mut(&mut self) -> &mut Env
Returns a mutable refernce to the environment for this instance.
sourcepub fn persistent_variables(&self) -> &[Symbol]
pub fn persistent_variables(&self) -> &[Symbol]
Returns a list of persistent variables defined with
Scope::define_persistent_variable()
sourcepub fn with_function(self, function: Function<Env::Intrinsic>) -> Self
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.
sourcepub fn with_native_function(
self,
name: impl Into<Symbol>,
function: impl NativeFunction + 'static
) -> Self
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.
sourcepub fn define_native_function(
&mut self,
name: impl Into<Symbol>,
function: impl NativeFunction + 'static
) -> usize
pub fn define_native_function( &mut self, name: impl Into<Symbol>, function: impl NativeFunction + 'static ) -> usize
Defines a native function with the provided name.
sourcepub 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,
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.
sourcepub fn run<'a, Output: FromStack>(
&'a mut self,
instructions: impl Into<Instructions<'a, Env::Intrinsic>>,
variable_count: usize
) -> Result<Output, Fault<'a, Env, Output>>
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.
sourcepub 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>>
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,
impl<Env> Clone for VirtualMachine<Env>where Env: Environment + Clone, Env::Intrinsic: Clone,
source§fn clone(&self) -> VirtualMachine<Env>
fn clone(&self) -> VirtualMachine<Env>
1.0.0 · source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source
. Read moresource§impl<Env> Debug for VirtualMachine<Env>where
Env: Environment + Debug,
Env::Intrinsic: Debug,
impl<Env> Debug for VirtualMachine<Env>where Env: Environment + Debug, Env::Intrinsic: Debug,
source§impl<Env> Default for VirtualMachine<Env>where
Env: Environment + Default,
Env::Intrinsic: Default,
impl<Env> Default for VirtualMachine<Env>where Env: Environment + Default, Env::Intrinsic: Default,
source§fn default() -> VirtualMachine<Env>
fn default() -> VirtualMachine<Env>
source§impl<Env> PartialEq<VirtualMachine<Env>> for VirtualMachine<Env>where
Env: Environment + PartialEq,
Env::Intrinsic: PartialEq,
impl<Env> PartialEq<VirtualMachine<Env>> for VirtualMachine<Env>where Env: Environment + PartialEq, Env::Intrinsic: PartialEq,
source§fn eq(&self, other: &VirtualMachine<Env>) -> bool
fn eq(&self, other: &VirtualMachine<Env>) -> bool
self
and other
values to be equal, and is used
by ==
.source§impl<Env> Scope for VirtualMachine<Env>where
Env: Environment,
impl<Env> Scope for VirtualMachine<Env>where Env: Environment,
§type Environment = Env
type Environment = Env
source§fn resolve_function_vtable_index(&self, name: &Symbol) -> Option<usize>
fn resolve_function_vtable_index(&self, name: &Symbol) -> Option<usize>
source§fn map_each_symbol(&self, callback: &mut impl FnMut(Symbol, ScopeSymbolKind))
fn map_each_symbol(&self, callback: &mut impl FnMut(Symbol, ScopeSymbolKind))
callback
for each symbol defined in this scope.