use {
crate::{
core::CoreLib,
external::{self, Args, ExternalFunction},
frame::Frame,
num2, num4, type_as_string,
value::{
self, add_values, multiply_values, value_is_callable, value_size, RegisterSlice,
RuntimeFunction,
},
value_iterator::{IntRange, Iterable, ValueIterator, ValueIteratorOutput},
vm_error, Loader, RuntimeError, RuntimeResult, Value, ValueList, ValueMap, ValueNumber,
ValueString, ValueVec,
},
koto_bytecode::{Chunk, Instruction, InstructionReader, TypeId},
koto_parser::ConstantIndex,
parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard},
std::{
collections::HashMap,
fmt,
path::PathBuf,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
},
};
#[derive(Clone, Debug)]
pub enum ControlFlow {
Continue,
Return(Value),
Yield(Value),
}
pub type InstructionResult = Result<(), RuntimeError>;
struct SharedContext {
pub prelude: ValueMap,
core_lib: CoreLib,
}
impl Default for SharedContext {
fn default() -> Self {
let core_lib = CoreLib::default();
let mut prelude = ValueMap::default();
prelude.add_map("io", core_lib.io.clone());
prelude.add_map("iterator", core_lib.iterator.clone());
prelude.add_map("koto", core_lib.koto.clone());
prelude.add_map("list", core_lib.list.clone());
prelude.add_map("map", core_lib.map.clone());
prelude.add_map("os", core_lib.os.clone());
prelude.add_map("number", core_lib.number.clone());
prelude.add_map("range", core_lib.range.clone());
prelude.add_map("string", core_lib.string.clone());
prelude.add_map("test", core_lib.test.clone());
prelude.add_map("thread", core_lib.thread.clone());
prelude.add_map("tuple", core_lib.tuple.clone());
Self { prelude, core_lib }
}
}
#[derive(Default)]
pub struct ModuleContext {
global: ValueMap,
loader: Loader,
modules: HashMap<PathBuf, Option<ValueMap>>,
spawned_stop_flags: Vec<Arc<AtomicBool>>,
}
impl ModuleContext {
fn spawn_new_context(&self) -> Self {
Self {
loader: self.loader.clone(),
modules: self.modules.clone(),
global: Default::default(),
spawned_stop_flags: Default::default(),
}
}
fn reset(&mut self) {
self.loader = Default::default();
self.stop_spawned_vms();
}
fn stop_spawned_vms(&mut self) {
for stop_flag in self.spawned_stop_flags.iter() {
stop_flag.store(true, Ordering::Relaxed);
}
self.spawned_stop_flags.clear();
}
}
impl Drop for ModuleContext {
fn drop(&mut self) {
self.stop_spawned_vms();
}
}
pub struct Vm {
context: Arc<RwLock<ModuleContext>>,
context_shared: Arc<SharedContext>,
reader: InstructionReader,
value_stack: Vec<Value>,
call_stack: Vec<Frame>,
stop_flag: Option<Arc<AtomicBool>>,
}
impl Default for Vm {
fn default() -> Self {
Self {
context: Arc::new(RwLock::new(ModuleContext::default())),
context_shared: Arc::new(SharedContext::default()),
reader: InstructionReader::default(),
value_stack: Vec::with_capacity(32),
call_stack: vec![],
stop_flag: None,
}
}
}
impl Vm {
pub fn spawn_new_vm(&mut self) -> Self {
Self {
context: Arc::new(RwLock::new(self.context().spawn_new_context())),
context_shared: self.context_shared.clone(),
reader: InstructionReader::default(),
value_stack: Vec::with_capacity(32),
call_stack: vec![],
stop_flag: None,
}
}
pub fn spawn_shared_vm(&mut self) -> Self {
Self {
context: self.context.clone(),
context_shared: self.context_shared.clone(),
reader: self.reader.clone(),
value_stack: Vec::with_capacity(8),
call_stack: vec![],
stop_flag: None,
}
}
pub fn spawn_shared_concurrent_vm(&mut self) -> Self {
let stop_flag = Arc::new(AtomicBool::new(false));
self.context_mut()
.spawned_stop_flags
.push(stop_flag.clone());
Self {
context: self.context.clone(),
context_shared: self.context_shared.clone(),
reader: self.reader.clone(),
value_stack: Vec::with_capacity(8),
call_stack: vec![],
stop_flag: Some(stop_flag),
}
}
pub fn prelude(&self) -> ValueMap {
self.context_shared.prelude.clone()
}
fn context(&self) -> RwLockReadGuard<ModuleContext> {
self.context.read()
}
fn context_mut(&mut self) -> RwLockWriteGuard<ModuleContext> {
self.context.write()
}
pub fn get_global_value(&self, id: &str) -> Option<Value> {
self.context().global.data().get_with_string(id).cloned()
}
pub fn get_global_function(&self, id: &str) -> Option<Value> {
match self.get_global_value(id) {
Some(function) if value_is_callable(&function) => Some(function),
_ => None,
}
}
pub fn reset(&mut self) {
self.context_mut().reset();
self.value_stack = Default::default();
self.call_stack = Default::default();
}
pub fn run(&mut self, chunk: Arc<Chunk>) -> RuntimeResult {
self.push_frame(chunk, 0, 0, None);
self.execute_instructions()
}
pub fn continue_running(&mut self) -> RuntimeResult {
if self.call_stack.is_empty() {
Ok(Value::Empty)
} else {
self.execute_instructions()
}
}
pub fn run_function(&mut self, function: Value, args: &[Value]) -> RuntimeResult {
self.call_and_run_function(None, function, args)
}
pub fn run_instance_function(
&mut self,
instance: Value,
function: Value,
args: &[Value],
) -> RuntimeResult {
self.call_and_run_function(Some(instance), function, args)
}
fn call_and_run_function(
&mut self,
instance: Option<Value>,
function: Value,
args: &[Value],
) -> RuntimeResult {
if !self.call_stack.is_empty() {
return vm_error!(
self.chunk(),
self.ip(),
"run_function: the call stack must be empty,
are you calling run_function on an active VM?"
);
}
if !value_is_callable(&function) {
return vm_error!(
self.chunk(),
self.ip(),
"run_function: the provided value isn't a function"
);
}
let result_register = 0;
let frame_base = 1;
let instance_register = if instance.is_some() {
Some(frame_base)
} else {
None
};
self.value_stack.clear();
self.value_stack.push(Value::Empty);
self.value_stack.push(instance.unwrap_or_default());
self.value_stack.extend_from_slice(args);
self.call_function(
result_register,
function,
frame_base,
args.len() as u8,
instance_register,
0,
)?;
if self.call_stack.is_empty() {
match self.value_stack.first() {
Some(value) => Ok(value.clone()),
None => vm_error!(
self.chunk(),
self.ip(),
"run_function: missing return register"
),
}
} else {
self.frame_mut().catch_barrier = true;
let result = self.execute_instructions();
if result.is_err() {
self.pop_frame(Value::Empty)?;
}
result
}
}
pub fn run_tests(&mut self, tests: ValueMap) -> RuntimeResult {
use Value::*;
let self_arg = Map(tests.clone());
let pre_test = tests.data().get_with_string("pre_test").cloned();
let post_test = tests.data().get_with_string("post_test").cloned();
let pass_self_to_pre_test = match &pre_test {
Some(Function(f)) => f.arg_count == 1,
_ => false,
};
let pass_self_to_post_test = match &post_test {
Some(Function(f)) => f.arg_count == 1,
_ => false,
};
for (key, value) in tests.cloned_iter() {
match (key, value) {
(Str(id), test) if id.starts_with("test_") && value_is_callable(&test) => {
let make_test_error = |error: RuntimeError, message: &str| {
Err(error.with_prefix(&format!("{} '{}'", message, &id[5..])))
};
if let Some(pre_test) = &pre_test {
if value_is_callable(pre_test) {
let pre_test_result = if pass_self_to_pre_test {
self.run_instance_function(self_arg.clone(), pre_test.clone(), &[])
} else {
self.run_function(pre_test.clone(), &[])
};
if let Err(error) = pre_test_result {
return make_test_error(error, "Error while preparing to run test");
}
}
}
let pass_self_to_test = match &test {
Function(f) => f.arg_count == 1,
_ => false,
};
let test_result = if pass_self_to_test {
self.run_instance_function(self_arg.clone(), test, &[])
} else {
self.run_function(test, &[])
};
if let Err(error) = test_result {
return make_test_error(error, "Error while running test");
}
if let Some(post_test) = &post_test {
if value_is_callable(post_test) {
let post_test_result = if pass_self_to_post_test {
self.run_instance_function(self_arg.clone(), post_test.clone(), &[])
} else {
self.run_function(post_test.clone(), &[])
};
if let Err(error) = post_test_result {
return make_test_error(error, "Error after running test");
}
}
}
}
_ => {}
}
}
Ok(Empty)
}
fn execute_instructions(&mut self) -> RuntimeResult {
let mut result = Value::Empty;
let mut instruction_ip = self.ip();
while let Some(instruction) = self.reader.next() {
if let Some(stop_flag) = &self.stop_flag {
if stop_flag.load(Ordering::Relaxed) {
break;
}
}
match self.execute_instruction(instruction, instruction_ip) {
Ok(ControlFlow::Continue) => {}
Ok(ControlFlow::Return(value)) => {
result = value;
break;
}
Ok(ControlFlow::Yield(value)) => {
result = value;
break;
}
Err(error) => {
let mut recover_register_and_ip = None;
while let Some(frame) = self.call_stack.last() {
if let Some((error_register, catch_ip)) = frame.catch_stack.last() {
recover_register_and_ip = Some((*error_register, *catch_ip));
break;
} else {
if frame.catch_barrier {
return Err(error);
}
self.pop_frame(Value::Empty)?;
}
}
if let Some((register, ip)) = recover_register_and_ip {
self.set_register(register, Value::Str(error.to_string().into()));
self.set_ip(ip);
} else {
return Err(error);
}
}
}
instruction_ip = self.ip();
}
Ok(result)
}
fn execute_instruction(
&mut self,
instruction: Instruction,
instruction_ip: usize,
) -> Result<ControlFlow, RuntimeError> {
use Value::*;
let mut control_flow = ControlFlow::Continue;
match instruction {
Instruction::Error { message } => {
vm_error!(self.chunk(), instruction_ip, "{}", message)
}
Instruction::Copy { target, source } => {
self.run_copy(target, source);
Ok(())
}
Instruction::SetEmpty { register } => {
self.set_register(register, Empty);
Ok(())
}
Instruction::SetBool { register, value } => {
self.set_register(register, Bool(value));
Ok(())
}
Instruction::SetNumber { register, value } => {
self.set_register(register, Number(value.into()));
Ok(())
}
Instruction::LoadFloat { register, constant } => {
let n = self.reader.chunk.constants.get_f64(constant);
self.set_register(register, Number(n.into()));
Ok(())
}
Instruction::LoadInt { register, constant } => {
let n = self.reader.chunk.constants.get_i64(constant);
self.set_register(register, Number(n.into()));
Ok(())
}
Instruction::LoadString { register, constant } => {
let string = self.value_string_from_constant(constant);
self.set_register(register, Str(string));
Ok(())
}
Instruction::LoadGlobal { register, constant } => {
self.run_load_global(register, constant, instruction_ip)
}
Instruction::SetGlobal { global, source } => {
self.run_set_global(global, source);
Ok(())
}
Instruction::Import { register, constant } => {
self.run_import(register, constant, instruction_ip)
}
Instruction::MakeTuple {
register,
start,
count,
} => {
self.run_make_tuple(register, start, count);
Ok(())
}
Instruction::MakeTempTuple {
register,
start,
count,
} => {
self.set_register(register, TemporaryTuple(RegisterSlice { start, count }));
Ok(())
}
Instruction::MakeList {
register,
size_hint,
} => {
self.set_register(register, List(ValueList::with_capacity(size_hint)));
Ok(())
}
Instruction::MakeMap {
register,
size_hint,
} => {
self.set_register(register, Map(ValueMap::with_capacity(size_hint)));
Ok(())
}
Instruction::MakeNum2 {
register,
count,
element_register,
} => self.run_make_num2(register, count, element_register, instruction_ip),
Instruction::MakeNum4 {
register,
count,
element_register,
} => self.run_make_num4(register, count, element_register, instruction_ip),
Instruction::Range {
register,
start,
end,
} => self.run_make_range(register, Some(start), Some(end), false, instruction_ip),
Instruction::RangeInclusive {
register,
start,
end,
} => self.run_make_range(register, Some(start), Some(end), true, instruction_ip),
Instruction::RangeTo { register, end } => {
self.run_make_range(register, None, Some(end), false, instruction_ip)
}
Instruction::RangeToInclusive { register, end } => {
self.run_make_range(register, None, Some(end), true, instruction_ip)
}
Instruction::RangeFrom { register, start } => {
self.run_make_range(register, Some(start), None, false, instruction_ip)
}
Instruction::RangeFull { register } => {
self.run_make_range(register, None, None, false, instruction_ip)
}
Instruction::MakeIterator { register, iterable } => {
self.run_make_iterator(register, iterable, instruction_ip)
}
Instruction::Function { .. } => {
self.run_make_function(instruction);
Ok(())
}
Instruction::Capture {
function,
target,
source,
} => self.run_capture_value(function, target, source, instruction_ip),
Instruction::LoadCapture { register, capture } => {
self.run_load_capture(register, capture, instruction_ip)
}
Instruction::SetCapture { capture, source } => {
self.run_set_capture(capture, source, instruction_ip)
}
Instruction::Negate { register, source } => {
self.run_negate(register, source, instruction_ip)
}
Instruction::Add { register, lhs, rhs } => {
self.run_add(register, lhs, rhs, &instruction, instruction_ip)
}
Instruction::Subtract { register, lhs, rhs } => {
self.run_subtract(register, lhs, rhs, &instruction, instruction_ip)
}
Instruction::Multiply { register, lhs, rhs } => {
self.run_multiply(register, lhs, rhs, &instruction, instruction_ip)
}
Instruction::Divide { register, lhs, rhs } => {
self.run_divide(register, lhs, rhs, &instruction, instruction_ip)
}
Instruction::Modulo { register, lhs, rhs } => {
self.run_modulo(register, lhs, rhs, &instruction, instruction_ip)
}
Instruction::Less { register, lhs, rhs } => {
self.run_less(register, lhs, rhs, &instruction, instruction_ip)
}
Instruction::LessOrEqual { register, lhs, rhs } => {
self.run_less_or_equal(register, lhs, rhs, &instruction, instruction_ip)
}
Instruction::Greater { register, lhs, rhs } => {
self.run_greater(register, lhs, rhs, &instruction, instruction_ip)
}
Instruction::GreaterOrEqual { register, lhs, rhs } => {
self.run_greater_or_equal(register, lhs, rhs, &instruction, instruction_ip)
}
Instruction::Equal { register, lhs, rhs } => {
self.run_equal(register, lhs, rhs);
Ok(())
}
Instruction::NotEqual { register, lhs, rhs } => {
self.run_not_equal(register, lhs, rhs);
Ok(())
}
Instruction::Jump { offset } => {
self.jump_ip(offset);
Ok(())
}
Instruction::JumpIf {
register,
offset,
jump_condition,
} => self.run_jump_if(register, offset, jump_condition, instruction_ip),
Instruction::JumpBack { offset } => {
self.jump_ip_back(offset);
Ok(())
}
Instruction::JumpBackIf {
register,
offset,
jump_condition,
} => self.run_jump_back_if(register, offset, jump_condition, instruction_ip),
Instruction::Call {
result,
function,
frame_base,
arg_count,
} => self.call_function(
result,
self.clone_register(function),
frame_base,
arg_count,
None,
instruction_ip,
),
Instruction::CallChild {
result,
function,
frame_base,
arg_count,
parent,
} => self.call_function(
result,
self.clone_register(function),
frame_base,
arg_count,
Some(parent),
instruction_ip,
),
Instruction::Return { register } => {
if let Some(return_value) = self.pop_frame(self.clone_register(register))? {
control_flow = ControlFlow::Return(return_value);
}
Ok(())
}
Instruction::Yield { register } => {
control_flow = ControlFlow::Yield(self.clone_register(register));
Ok(())
}
Instruction::Size { register, value } => {
self.run_size(register, value);
Ok(())
}
Instruction::IsTuple { register, value } => {
let result = matches!(self.get_register(value), Tuple(_));
self.set_register(register, Bool(result));
Ok(())
}
Instruction::IsList { register, value } => {
let result = matches!(self.get_register(value), List(_));
self.set_register(register, Bool(result));
Ok(())
}
Instruction::IterNext {
register,
iterator,
jump_offset,
} => {
self.run_iterator_next(Some(register), iterator, jump_offset, false, instruction_ip)
}
Instruction::IterNextTemp {
register,
iterator,
jump_offset,
} => {
self.run_iterator_next(Some(register), iterator, jump_offset, true, instruction_ip)
}
Instruction::IterNextQuiet {
iterator,
jump_offset,
} => self.run_iterator_next(None, iterator, jump_offset, false, instruction_ip),
Instruction::ValueIndex {
register,
value,
index,
} => self.run_value_index(register, value, index, instruction_ip),
Instruction::SliceFrom {
register,
value,
index,
} => self.run_slice(register, value, index, false, instruction_ip),
Instruction::SliceTo {
register,
value,
index,
} => self.run_slice(register, value, index, true, instruction_ip),
Instruction::ListPushValue { list, value } => {
self.run_list_push(list, value, instruction_ip)
}
Instruction::ListPushValues {
list,
values_start,
count,
} => {
for value_register in values_start..(values_start + count) {
self.run_list_push(list, value_register, instruction_ip)?;
}
Ok(())
}
Instruction::ListUpdate { list, index, value } => {
self.run_list_update(list, index, value, instruction_ip)
}
Instruction::Index {
register,
value,
index,
} => self.run_index(register, value, index, instruction_ip),
Instruction::MapInsert {
register,
value,
key,
} => self.run_map_insert(register, value, key, instruction_ip),
Instruction::Access { register, map, key } => {
self.run_access(register, map, key, instruction_ip)
}
Instruction::TryStart {
arg_register,
catch_offset,
} => {
let catch_ip = self.ip() + catch_offset;
self.frame_mut().catch_stack.push((arg_register, catch_ip));
Ok(())
}
Instruction::TryEnd => {
self.frame_mut().catch_stack.pop();
Ok(())
}
Instruction::Debug { register, constant } => {
self.run_debug(register, constant, instruction_ip);
Ok(())
}
Instruction::CheckType { register, type_id } => {
self.run_check_type(register, type_id, instruction_ip)
}
Instruction::CheckSize { register, size } => {
self.run_check_size(register, size, instruction_ip)
}
}?;
Ok(control_flow)
}
fn run_copy(&mut self, target: u8, source: u8) {
let value = match self.clone_register(source) {
Value::TemporaryTuple(RegisterSlice { start, count }) => {
Value::Tuple(self.register_slice(start, count).into())
}
other => other,
};
self.set_register(target, value);
}
fn run_load_global(
&mut self,
register: u8,
constant_index: ConstantIndex,
instruction_ip: usize,
) -> InstructionResult {
let global_name = self.get_constant_str(constant_index);
let global = self
.context()
.global
.data()
.get_with_string(global_name)
.cloned();
match global {
Some(value) => {
self.set_register(register, value);
Ok(())
}
None => vm_error!(self.chunk(), instruction_ip, "'{}' not found", global_name),
}
}
fn run_set_global(&mut self, constant_index: ConstantIndex, source_register: u8) {
let global_name = Value::Str(self.value_string_from_constant(constant_index));
let value = self.clone_register(source_register);
self.context_mut()
.global
.data_mut()
.insert(global_name, value);
}
fn run_make_tuple(&mut self, register: u8, start: u8, count: u8) {
let mut copied = Vec::with_capacity(count as usize);
for register in start..start + count {
copied.push(self.clone_register(register));
}
self.set_register(register, Value::Tuple(copied.into()));
}
fn run_make_range(
&mut self,
register: u8,
start_register: Option<u8>,
end_register: Option<u8>,
inclusive: bool,
instruction_ip: usize,
) -> InstructionResult {
use Value::{IndexRange, Number, Range};
let start = start_register.map(|register| self.get_register(register));
let end = end_register.map(|register| self.get_register(register));
let range = match (start, end) {
(Some(Number(start)), Some(Number(end))) => {
let istart = isize::from(start);
let iend = isize::from(end);
let (start, end) = if inclusive {
if istart <= iend {
(istart, iend + 1)
} else {
(istart, iend - 1)
}
} else {
(istart, iend)
};
Range(IntRange { start, end })
}
(None, Some(Number(end))) => {
if *end < 0.0 {
return vm_error!(
self.chunk(),
instruction_ip,
"RangeTo: negative numbers not allowed, found '{}'",
end
);
}
let end = if inclusive {
usize::from(end) + 1
} else {
usize::from(end)
};
IndexRange(value::IndexRange {
start: 0,
end: Some(end),
})
}
(Some(Number(start)), None) => {
if *start < 0.0 {
return vm_error!(
self.chunk(),
instruction_ip,
"RangeFrom: negative numbers not allowed, found '{}'",
start
);
}
IndexRange(value::IndexRange {
start: usize::from(start),
end: None,
})
}
(None, None) => {
IndexRange(value::IndexRange {
start: 0,
end: None,
})
}
(Some(unexpected), _) => {
return self.unexpected_type_error(
"Expected Number for range start",
unexpected,
instruction_ip,
);
}
(_, Some(unexpected)) => {
return self.unexpected_type_error(
"Expected Number for range end",
unexpected,
instruction_ip,
);
}
};
self.set_register(register, range);
Ok(())
}
fn run_make_iterator(
&mut self,
register: u8,
iterable_register: u8,
instruction_ip: usize,
) -> InstructionResult {
use Value::*;
let iterable = self.clone_register(iterable_register);
if matches!(iterable, Iterator(_)) {
self.set_register(register, iterable);
} else {
let iterator = match iterable {
Range(int_range) => ValueIterator::with_range(int_range),
List(list) => ValueIterator::with_list(list),
Map(map) => ValueIterator::with_map(map),
Tuple(tuple) => ValueIterator::with_tuple(tuple),
Str(s) => ValueIterator::with_string(s),
unexpected => {
return self.unexpected_type_error(
"Expected iterable value while making iterator",
&unexpected,
instruction_ip,
);
}
};
self.set_register(register, iterator.into());
}
Ok(())
}
fn run_iterator_next(
&mut self,
result_register: Option<u8>,
iterator: u8,
jump_offset: usize,
output_is_temporary: bool,
instruction_ip: usize,
) -> InstructionResult {
use Value::{Iterator, TemporaryTuple, Tuple};
let result = match self.get_register_mut(iterator) {
Iterator(iterator) => iterator.next(),
unexpected => {
return vm_error!(
self.chunk(),
instruction_ip,
"Expected Iterator, found '{}'",
type_as_string(unexpected)
);
}
};
match (result, result_register) {
(Some(Ok(_)), None) => {}
(Some(Ok(ValueIteratorOutput::Value(value))), Some(register)) => {
self.set_register(register, value)
}
(Some(Ok(ValueIteratorOutput::ValuePair(first, second))), Some(register)) => {
if output_is_temporary {
self.set_register(
register,
TemporaryTuple(RegisterSlice {
start: register + 1,
count: 2,
}),
);
self.set_register(register + 1, first);
self.set_register(register + 2, second);
} else {
self.set_register(register, Tuple(vec![first, second].into()));
}
}
(Some(Err(error)), _) => {
return vm_error!(self.chunk(), instruction_ip, error.to_string())
}
(None, _) => self.jump_ip(jump_offset),
};
Ok(())
}
fn run_value_index(
&mut self,
register: u8,
value: u8,
index: i8,
instruction_ip: usize,
) -> InstructionResult {
use Value::*;
let result = match self.get_register(value) {
List(list) => {
let index = signed_index_to_unsigned(index, list.data().len());
list.data().get(index).cloned().unwrap_or(Empty)
}
Tuple(tuple) => {
let index = signed_index_to_unsigned(index, tuple.data().len());
tuple.data().get(index).cloned().unwrap_or(Empty)
}
TemporaryTuple(RegisterSlice { start, count }) => {
let count = *count;
if (index.abs() as u8) < count {
let index = signed_index_to_unsigned(index, count as usize);
self.clone_register(start + index as u8)
} else {
Empty
}
}
Num2(n) => {
let index = signed_index_to_unsigned(index, 2);
if index < 2 {
Number(n[index].into())
} else {
Empty
}
}
Num4(n) => {
let index = signed_index_to_unsigned(index, 4);
if index < 4 {
Number(n[index].into())
} else {
Empty
}
}
unexpected => {
return self.unexpected_type_error(
"ValueIndex: Expected indexable value",
unexpected,
instruction_ip,
);
}
};
self.set_register(register, result);
Ok(())
}
fn run_slice(
&mut self,
register: u8,
value: u8,
index: i8,
is_slice_to: bool,
instruction_ip: usize,
) -> InstructionResult {
use Value::*;
let result = match self.get_register(value) {
List(list) => {
let index = signed_index_to_unsigned(index, list.data().len());
if is_slice_to {
list.data()
.get(..index)
.map_or(Empty, |entries| List(ValueList::from_slice(entries)))
} else {
list.data()
.get(index..)
.map_or(Empty, |entries| List(ValueList::from_slice(entries)))
}
}
Tuple(tuple) => {
let index = signed_index_to_unsigned(index, tuple.data().len());
if is_slice_to {
tuple
.data()
.get(..index)
.map_or(Empty, |entries| Tuple(entries.into()))
} else {
tuple
.data()
.get(index..)
.map_or(Empty, |entries| Tuple(entries.into()))
}
}
unexpected => {
return self.unexpected_type_error(
"SliceFrom: expected List or Tuple",
unexpected,
instruction_ip,
);
}
};
self.set_register(register, result);
Ok(())
}
fn run_make_function(&mut self, function_instruction: Instruction) {
use Value::*;
match function_instruction {
Instruction::Function {
register,
arg_count,
capture_count,
instance_function,
variadic,
generator,
size,
} => {
let captures = if capture_count > 0 {
let mut captures = ValueVec::new();
captures.resize(capture_count as usize, Empty);
Some(ValueList::with_data(captures))
} else {
None
};
let function = RuntimeFunction {
chunk: self.chunk(),
ip: self.ip(),
arg_count,
instance_function,
variadic,
captures,
};
let value = if generator {
Generator(function)
} else {
Function(function)
};
self.jump_ip(size);
self.set_register(register, value);
}
_ => unreachable!(),
}
}
fn run_capture_value(
&mut self,
function: u8,
capture_index: u8,
value: u8,
instruction_ip: usize,
) -> InstructionResult {
let capture_list = match self.get_register(function) {
Value::Function(f) => &f.captures,
Value::Generator(g) => &g.captures,
unexpected => {
return self.unexpected_type_error(
"Capture: expected Function",
unexpected,
instruction_ip,
);
}
};
match capture_list {
Some(capture_list) => {
capture_list.data_mut()[capture_index as usize] = self.clone_register(value)
}
None => {
return vm_error!(
self.chunk(),
instruction_ip,
"Capture: missing capture list for function"
)
}
}
Ok(())
}
fn run_load_capture(
&mut self,
register: u8,
capture_index: u8,
instruction_ip: usize,
) -> InstructionResult {
match self.frame().get_capture(capture_index) {
Some(value) => {
self.set_register(register, value);
}
None => {
if self.call_stack.len() == 1 {
return vm_error!(
self.chunk(),
instruction_ip,
"LoadCapture: attempting to capture outside of function"
);
} else {
return vm_error!(
self.chunk(),
instruction_ip,
"LoadCapture: invalid capture index"
);
}
}
}
Ok(())
}
fn run_set_capture(
&mut self,
capture_index: u8,
value_register: u8,
instruction_ip: usize,
) -> InstructionResult {
let value = self.clone_register(value_register);
if !self.frame_mut().set_capture(capture_index, value) {
return vm_error!(
self.chunk(),
instruction_ip,
"SetCapture: invalid capture index: {} ",
capture_index
);
}
Ok(())
}
fn run_negate(&mut self, register: u8, value: u8, instruction_ip: usize) -> InstructionResult {
use Value::*;
let result = match &self.get_register(value) {
Bool(b) => Bool(!b),
Number(n) => Number(-n),
Num2(v) => Num2(-v),
Num4(v) => Num4(-v),
unexpected => {
return self.unexpected_type_error(
"Negate: expected negatable value",
unexpected,
instruction_ip,
);
}
};
self.set_register(register, result);
Ok(())
}
fn run_add(
&mut self,
register: u8,
lhs: u8,
rhs: u8,
instruction: &Instruction,
instruction_ip: usize,
) -> InstructionResult {
let lhs_value = self.get_register(lhs);
let rhs_value = self.get_register(rhs);
match add_values(lhs_value, rhs_value) {
Some(result) => {
self.set_register(register, result);
Ok(())
}
None => self.binary_op_error(lhs_value, rhs_value, instruction, instruction_ip),
}
}
fn run_subtract(
&mut self,
register: u8,
lhs: u8,
rhs: u8,
instruction: &Instruction,
instruction_ip: usize,
) -> InstructionResult {
use Value::*;
let lhs_value = self.get_register(lhs);
let rhs_value = self.get_register(rhs);
let result = match (lhs_value, rhs_value) {
(Number(a), Number(b)) => Number(a - b),
(Number(a), Num2(b)) => Num2(a - b),
(Num2(a), Num2(b)) => Num2(a - b),
(Num2(a), Number(b)) => Num2(a - b),
(Number(a), Num4(b)) => Num4(a - b),
(Num4(a), Num4(b)) => Num4(a - b),
(Num4(a), Number(b)) => Num4(a - b),
_ => {
return self.binary_op_error(lhs_value, rhs_value, instruction, instruction_ip);
}
};
self.set_register(register, result);
Ok(())
}
fn run_multiply(
&mut self,
register: u8,
lhs: u8,
rhs: u8,
instruction: &Instruction,
instruction_ip: usize,
) -> InstructionResult {
let lhs_value = self.get_register(lhs);
let rhs_value = self.get_register(rhs);
match multiply_values(lhs_value, rhs_value) {
Some(result) => {
self.set_register(register, result);
Ok(())
}
None => self.binary_op_error(lhs_value, rhs_value, instruction, instruction_ip),
}
}
fn run_divide(
&mut self,
register: u8,
lhs: u8,
rhs: u8,
instruction: &Instruction,
instruction_ip: usize,
) -> InstructionResult {
use Value::*;
let lhs_value = self.get_register(lhs);
let rhs_value = self.get_register(rhs);
let result = match (lhs_value, rhs_value) {
(Number(a), Number(b)) => Number(a / b),
(Number(a), Num2(b)) => Num2(a / b),
(Num2(a), Num2(b)) => Num2(a / b),
(Num2(a), Number(b)) => Num2(a / b),
(Number(a), Num4(b)) => Num4(a / b),
(Num4(a), Num4(b)) => Num4(a / b),
(Num4(a), Number(b)) => Num4(a / b),
_ => {
return self.binary_op_error(lhs_value, rhs_value, instruction, instruction_ip);
}
};
self.set_register(register, result);
Ok(())
}
fn run_modulo(
&mut self,
register: u8,
lhs: u8,
rhs: u8,
instruction: &Instruction,
instruction_ip: usize,
) -> InstructionResult {
use Value::*;
let lhs_value = self.get_register(lhs);
let rhs_value = self.get_register(rhs);
let result = match (lhs_value, rhs_value) {
(Number(a), Number(b)) => Number(a % b),
(Number(a), Num2(b)) => Num2(a % b),
(Num2(a), Num2(b)) => Num2(a % b),
(Num2(a), Number(b)) => Num2(a % b),
(Number(a), Num4(b)) => Num4(a % b),
(Num4(a), Num4(b)) => Num4(a % b),
(Num4(a), Number(b)) => Num4(a % b),
_ => {
return self.binary_op_error(lhs_value, rhs_value, instruction, instruction_ip);
}
};
self.set_register(register, result);
Ok(())
}
fn run_less(
&mut self,
register: u8,
lhs: u8,
rhs: u8,
instruction: &Instruction,
instruction_ip: usize,
) -> InstructionResult {
use Value::*;
let lhs_value = self.get_register(lhs);
let rhs_value = self.get_register(rhs);
let result = match (lhs_value, rhs_value) {
(Number(a), Number(b)) => Bool(a < b),
(Str(a), Str(b)) => Bool(a.as_str() < b.as_str()),
_ => {
return self.binary_op_error(lhs_value, rhs_value, instruction, instruction_ip);
}
};
self.set_register(register, result);
Ok(())
}
fn run_less_or_equal(
&mut self,
register: u8,
lhs: u8,
rhs: u8,
instruction: &Instruction,
instruction_ip: usize,
) -> InstructionResult {
use Value::*;
let lhs_value = self.get_register(lhs);
let rhs_value = self.get_register(rhs);
let result = match (lhs_value, rhs_value) {
(Number(a), Number(b)) => Bool(a <= b),
(Str(a), Str(b)) => Bool(a.as_str() <= b.as_str()),
_ => {
return self.binary_op_error(lhs_value, rhs_value, instruction, instruction_ip);
}
};
self.set_register(register, result);
Ok(())
}
fn run_greater(
&mut self,
register: u8,
lhs: u8,
rhs: u8,
instruction: &Instruction,
instruction_ip: usize,
) -> InstructionResult {
use Value::*;
let lhs_value = self.get_register(lhs);
let rhs_value = self.get_register(rhs);
let result = match (lhs_value, rhs_value) {
(Number(a), Number(b)) => Bool(a > b),
(Str(a), Str(b)) => Bool(a.as_str() > b.as_str()),
_ => {
return self.binary_op_error(lhs_value, rhs_value, instruction, instruction_ip);
}
};
self.set_register(register, result);
Ok(())
}
fn run_greater_or_equal(
&mut self,
register: u8,
lhs: u8,
rhs: u8,
instruction: &Instruction,
instruction_ip: usize,
) -> InstructionResult {
use Value::*;
let lhs_value = self.get_register(lhs);
let rhs_value = self.get_register(rhs);
let result = match (lhs_value, rhs_value) {
(Number(a), Number(b)) => Bool(a >= b),
(Str(a), Str(b)) => Bool(a.as_str() >= b.as_str()),
_ => {
return self.binary_op_error(lhs_value, rhs_value, instruction, instruction_ip);
}
};
self.set_register(register, result);
Ok(())
}
fn run_equal(&mut self, register: u8, lhs: u8, rhs: u8) {
let lhs_value = self.get_register(lhs);
let rhs_value = self.get_register(rhs);
let result = (lhs_value == rhs_value).into();
self.set_register(register, result);
}
fn run_not_equal(&mut self, register: u8, lhs: u8, rhs: u8) {
let lhs_value = self.get_register(lhs);
let rhs_value = self.get_register(rhs);
let result = (lhs_value != rhs_value).into();
self.set_register(register, result);
}
fn run_jump_if(
&mut self,
register: u8,
offset: usize,
jump_condition: bool,
instruction_ip: usize,
) -> InstructionResult {
match self.get_register(register) {
Value::Bool(b) => {
if *b == jump_condition {
self.jump_ip(offset);
}
}
unexpected => {
return self.unexpected_type_error(
"JumpIf: expected Bool",
unexpected,
instruction_ip,
);
}
}
Ok(())
}
fn run_jump_back_if(
&mut self,
register: u8,
offset: usize,
jump_condition: bool,
instruction_ip: usize,
) -> InstructionResult {
match self.get_register(register) {
Value::Bool(b) => {
if *b == jump_condition {
self.jump_ip_back(offset);
}
}
unexpected => {
return self.unexpected_type_error(
"JumpIf: expected Bool",
unexpected,
instruction_ip,
);
}
}
Ok(())
}
fn run_size(&mut self, register: u8, value: u8) {
let result = value_size(self.get_register(value));
self.set_register(register, Value::Number(result.into()));
}
fn run_import(
&mut self,
result_register: u8,
import_constant: ConstantIndex,
instruction_ip: usize,
) -> InstructionResult {
let import_name = self.value_string_from_constant(import_constant);
let maybe_global = self
.context()
.global
.data()
.get_with_string(&import_name)
.cloned();
if let Some(value) = maybe_global {
self.set_register(result_register, value);
} else {
let maybe_in_prelude = self
.context_shared
.prelude
.data()
.get_with_string(&import_name)
.cloned();
if let Some(value) = maybe_in_prelude {
self.set_register(result_register, value);
} else {
let source_path = self.reader.chunk.source_path.clone();
let compile_result = self
.context_mut()
.loader
.compile_module(&import_name, source_path);
let (module_chunk, module_path) = match compile_result {
Ok(chunk) => chunk,
Err(e) => {
return vm_error!(
self.chunk(),
instruction_ip,
"Failed to import '{}': {}",
import_name,
e
)
}
};
let maybe_module = self.context().modules.get(&module_path).cloned();
match maybe_module {
Some(Some(module)) => self.set_register(result_register, Value::Map(module)),
Some(None) => {
return vm_error!(
self.chunk(),
instruction_ip,
"Recursive import of module '{}'",
import_name
)
}
None => {
self.context_mut().modules.insert(module_path.clone(), None);
let mut vm = self.spawn_new_vm();
match vm.run(module_chunk) {
Ok(_) => {
if let Some(main) = vm.get_global_function("main") {
if let Err(error) = vm.run_function(main, &[]) {
self.context_mut().modules.remove(&module_path);
return Err(error);
}
}
}
Err(error) => {
self.context_mut().modules.remove(&module_path);
return Err(error);
}
}
let module_global = vm.context().global.clone();
self.context_mut()
.modules
.insert(module_path, Some(module_global.clone()));
self.set_register(result_register, Value::Map(module_global));
}
}
}
}
Ok(())
}
fn run_make_num2(
&mut self,
result_register: u8,
element_count: u8,
element_register: u8,
instruction_ip: usize,
) -> InstructionResult {
use Value::*;
let result = if element_count == 1 {
match self.get_register(element_register) {
Number(n) => num2::Num2(n.into(), n.into()),
Num2(n) => *n,
List(list) => {
let mut result = num2::Num2::default();
for (i, value) in list.data().iter().take(2).enumerate() {
match value {
Number(n) => result[i] = n.into(),
unexpected => {
return self.unexpected_type_error(
"num2: Expected Number",
unexpected,
instruction_ip,
);
}
}
}
result
}
unexpected => {
return self.unexpected_type_error(
"num2: Expected Number, Num2, or List",
unexpected,
instruction_ip,
);
}
}
} else {
let mut result = num2::Num2::default();
for i in 0..element_count {
match self.get_register(element_register + i) {
Number(n) => result[i as usize] = n.into(),
unexpected => {
return self.unexpected_type_error(
"num2: Expected Number, Num2, or List",
unexpected,
instruction_ip,
);
}
}
}
result
};
self.set_register(result_register, Num2(result));
Ok(())
}
fn run_make_num4(
&mut self,
result_register: u8,
element_count: u8,
element_register: u8,
instruction_ip: usize,
) -> InstructionResult {
use Value::*;
let result = if element_count == 1 {
match self.get_register(element_register) {
Number(n) => {
let n = n.into();
num4::Num4(n, n, n, n)
}
Num2(n) => num4::Num4(n[0] as f32, n[1] as f32, 0.0, 0.0),
Num4(n) => *n,
List(list) => {
let mut result = num4::Num4::default();
for (i, value) in list.data().iter().take(4).enumerate() {
match value {
Number(n) => result[i] = n.into(),
unexpected => {
return self.unexpected_type_error(
"num4: Expected Number",
unexpected,
instruction_ip,
);
}
}
}
result
}
unexpected => {
return self.unexpected_type_error(
"num4: Expected Number, Num4, or List",
unexpected,
instruction_ip,
);
}
}
} else {
let mut result = num4::Num4::default();
for i in 0..element_count {
match self.get_register(element_register + i) {
Number(n) => result[i as usize] = n.into(),
unexpected => {
return self.unexpected_type_error(
"num4: Expected Number, Num4, or List",
unexpected,
instruction_ip,
);
}
}
}
result
};
self.set_register(result_register, Num4(result));
Ok(())
}
fn run_list_push(
&mut self,
list_register: u8,
value_register: u8,
instruction_ip: usize,
) -> InstructionResult {
use Value::*;
let value = self.clone_register(value_register);
match self.get_register_mut(list_register) {
List(list) => match value {
Range(range) => {
list.data_mut()
.extend(ValueIterator::new(Iterable::Range(range)).map(
|iterator_output| match iterator_output {
Ok(ValueIteratorOutput::Value(value)) => value,
_ => unreachable!(),
},
));
}
_ => list.data_mut().push(value),
},
unexpected => {
return vm_error!(
self.chunk(),
instruction_ip,
"Expected List, found '{}'",
unexpected,
);
}
};
Ok(())
}
fn run_list_update(
&mut self,
list_register: u8,
index_register: u8,
value_register: u8,
instruction_ip: usize,
) -> InstructionResult {
use Value::*;
let index_value = self.clone_register(index_register);
let value = self.clone_register(value_register);
match self.get_register_mut(list_register) {
List(list) => {
let list_len = list.len();
match index_value {
Number(index) => {
let u_index = usize::from(index);
if index >= 0.0 && u_index < list_len {
list.data_mut()[u_index] = value;
} else {
return vm_error!(
self.chunk(),
instruction_ip,
"Index '{}' not in List",
index
);
}
}
Range(IntRange { start, end }) => {
let ustart = start as usize;
let uend = end as usize;
if start < 0 || end < 0 {
return vm_error!(
self.chunk(),
instruction_ip,
"Indexing with negative indices isn't supported, \
start: {}, end: {}",
start,
end
);
} else if start > end {
return vm_error!(
self.chunk(),
instruction_ip,
"Indexing with a descending range isn't supported, \
start: {}, end: {}",
start,
end
);
} else if ustart > list_len || uend > list_len {
return vm_error!(
self.chunk(),
instruction_ip,
"Index out of bounds, \
List has a length of {} - start: {}, end: {}",
list_len,
start,
end
);
} else {
let mut list_data = list.data_mut();
for i in ustart..uend {
list_data[i] = value.clone();
}
}
}
IndexRange(value::IndexRange { start, end }) => {
let end = end.unwrap_or(list_len);
if start > end {
return vm_error!(
self.chunk(),
instruction_ip,
"Indexing with a descending range isn't supported, \
start: {}, end: {}",
start,
end
);
} else if start > list_len || end > list_len {
return vm_error!(
self.chunk(),
instruction_ip,
"Index out of bounds, \
List has a length of {} - start: {}, end: {}",
list_len,
start,
end
);
} else {
let mut list_data = list.data_mut();
for i in start..end {
list_data[i] = value.clone();
}
}
}
unexpected => {
return self.unexpected_type_error(
"Expected List",
&unexpected,
instruction_ip,
);
}
}
}
unexpected => {
return vm_error!(
self.chunk(),
instruction_ip,
"Expected List, found '{}'",
unexpected
);
}
};
Ok(())
}
fn validate_index(
&self,
n: ValueNumber,
size: usize,
instruction_ip: usize,
) -> Result<usize, RuntimeError> {
let index = usize::from(n);
if n < 0.0 {
vm_error!(
self.chunk(),
instruction_ip,
"Negative indices aren't allowed ('{}')",
n
)
} else if index >= size {
vm_error!(
self.chunk(),
instruction_ip,
"Index out of bounds - index: {}, size: {}",
n,
size
)
} else {
Ok(index)
}
}
fn validate_int_range(
&self,
start: isize,
end: isize,
size: usize,
instruction_ip: usize,
) -> InstructionResult {
let ustart = start as usize;
let uend = end as usize;
if start < 0 || end < 0 {
vm_error!(
self.chunk(),
instruction_ip,
"Indexing with negative indices isn't supported, start: {}, end: {}",
start,
end
)
} else if start > end {
vm_error!(
self.chunk(),
instruction_ip,
"Indexing with a descending range isn't supported, start: {}, end: {}",
start,
end
)
} else if ustart > size || uend > size {
vm_error!(
self.chunk(),
instruction_ip,
"Index out of bounds, size of {} - start: {}, end: {}",
size,
start,
end
)
} else {
Ok(())
}
}
fn validate_index_range(
&self,
start: usize,
end: usize,
size: usize,
instruction_ip: usize,
) -> InstructionResult {
if start > end {
vm_error!(
self.chunk(),
instruction_ip,
"Indexing with a descending range isn't supported, start: {}, end: {}",
start,
end
)
} else if start > size || end > size {
vm_error!(
self.chunk(),
instruction_ip,
"Index out of bounds, size of {} - start: {}, end: {}",
size,
start,
end
)
} else {
Ok(())
}
}
fn run_index(
&mut self,
result_register: u8,
value_register: u8,
index_register: u8,
instruction_ip: usize,
) -> InstructionResult {
use Value::*;
let value = self.clone_register(value_register);
let index = self.clone_register(index_register);
match (value, index) {
(List(l), Number(n)) => {
let index = self.validate_index(n, l.len(), instruction_ip)?;
self.set_register(result_register, l.data()[index].clone());
}
(List(l), Range(IntRange { start, end })) => {
self.validate_int_range(start, end, l.len(), instruction_ip)?;
self.set_register(
result_register,
List(ValueList::from_slice(
&l.data()[(start as usize)..(end as usize)],
)),
)
}
(List(l), IndexRange(value::IndexRange { start, end })) => {
let end = end.unwrap_or_else(|| l.len());
self.validate_index_range(start, end, l.len(), instruction_ip)?;
self.set_register(
result_register,
List(ValueList::from_slice(&l.data()[start..end])),
)
}
(Tuple(t), Number(n)) => {
let index = self.validate_index(n, t.data().len(), instruction_ip)?;
self.set_register(result_register, t.data()[index].clone());
}
(Tuple(t), Range(IntRange { start, end })) => {
self.validate_int_range(start, end, t.data().len(), instruction_ip)?;
self.set_register(
result_register,
Tuple(t.data()[(start as usize)..(end as usize)].into()),
)
}
(Tuple(t), IndexRange(value::IndexRange { start, end })) => {
let end = end.unwrap_or(t.data().len());
self.validate_index_range(start, end, t.data().len(), instruction_ip)?;
self.set_register(result_register, Tuple(t.data()[start..end].into()))
}
(Num2(n), Number(i)) => {
let i = usize::from(i);
match i {
0 | 1 => self.set_register(result_register, Number(n[i].into())),
other => {
return vm_error!(
self.chunk(),
instruction_ip,
"Index out of bounds for Num2, {}",
other
)
}
}
}
(Num4(n), Number(i)) => {
let i = usize::from(i);
match i {
0 | 1 | 2 | 3 => self.set_register(result_register, Number(n[i].into())),
other => {
return vm_error!(
self.chunk(),
instruction_ip,
"Index out of bounds for Num4, {}",
other
)
}
}
}
(unexpected_value, unexpected_index) => {
return vm_error!(
self.chunk(),
instruction_ip,
"Unable to index '{}' with '{}'",
type_as_string(&unexpected_value),
type_as_string(&unexpected_index),
)
}
};
Ok(())
}
fn run_map_insert(
&mut self,
map_register: u8,
value: u8,
key: ConstantIndex,
instruction_ip: usize,
) -> InstructionResult {
let key_string = self.value_string_from_constant(key);
let value = self.clone_register(value);
match self.get_register_mut(map_register) {
Value::Map(map) => {
map.data_mut().insert(Value::Str(key_string), value);
Ok(())
}
unexpected => vm_error!(
self.chunk(),
instruction_ip,
"MapInsert: Expected Map, found '{}'",
type_as_string(&unexpected)
),
}
}
fn run_access(
&mut self,
result_register: u8,
map_register: u8,
key: ConstantIndex,
instruction_ip: usize,
) -> InstructionResult {
use Value::*;
let map_value = self.clone_register(map_register);
let key_string = self.get_constant_str(key);
macro_rules! core_op {
($module:ident, $iterator_fallback:expr) => {{
let op = self.get_core_op(
key_string,
&self.context_shared.core_lib.$module,
stringify!($module),
$iterator_fallback,
instruction_ip,
)?;
self.set_register(result_register, op);
}};
};
match map_value {
Map(map) => match map.data().get_with_string(&key_string) {
Some(value) => {
self.set_register(result_register, value.clone());
}
None => core_op!(map, true),
},
List(_) => core_op!(list, true),
Num2(_) => core_op!(num2, false),
Num4(_) => core_op!(num4, false),
Number(_) => core_op!(number, false),
Range(_) => core_op!(range, true),
Str(_) => core_op!(string, true),
Tuple(_) => core_op!(tuple, true),
Iterator(_) => core_op!(iterator, false),
unexpected => {
return self.unexpected_type_error(
"MapAccess: Expected Map",
&unexpected,
instruction_ip,
)
}
}
Ok(())
}
fn get_core_op(
&self,
key: &str,
module: &ValueMap,
module_name: &str,
iterator_fallback: bool,
instruction_ip: usize,
) -> RuntimeResult {
use Value::*;
let maybe_op = match module.data().get_with_string(key).cloned() {
None if iterator_fallback => self
.context_shared
.core_lib
.iterator
.data()
.get_with_string(&key)
.cloned(),
maybe_op => maybe_op,
};
let result = match maybe_op {
Some(op) => match op {
ExternalFunction(f) => {
let f_as_instance_function = external::ExternalFunction {
is_instance_function: true,
..f
};
ExternalFunction(f_as_instance_function)
}
Function(f) => {
let f_as_instance_function = RuntimeFunction {
instance_function: true,
..f
};
Function(f_as_instance_function)
}
Generator(f) => {
let f_as_instance_function = RuntimeFunction {
instance_function: true,
..f
};
Generator(f_as_instance_function)
}
other => other,
},
None => {
return vm_error!(
self.chunk(),
instruction_ip,
"'{}' not found in module '{}'",
key,
module_name
)
}
};
Ok(result)
}
fn call_external_function(
&mut self,
result_register: u8,
external_function: ExternalFunction,
frame_base: u8,
call_arg_count: u8,
instance_register: Option<u8>,
instruction_ip: usize,
) -> InstructionResult {
let function = external_function.function.as_ref();
let mut call_arg_count = call_arg_count;
let adjusted_frame_base = if external_function.is_instance_function {
if let Some(instance_register) = instance_register {
if instance_register != frame_base {
let instance = self.clone_register(instance_register);
self.set_register(frame_base, instance);
}
call_arg_count += 1;
frame_base
} else {
return vm_error!(
self.chunk(),
instruction_ip,
"Expected self for external instance function"
);
}
} else {
frame_base + 1
};
let result = (&*function)(
self,
&Args {
register: adjusted_frame_base,
count: call_arg_count,
},
);
match result {
Ok(value) => {
self.set_register(result_register, value);
self.truncate_registers(frame_base);
}
Err(error) => return vm_error!(self.chunk(), instruction_ip, error.to_string()),
}
Ok(())
}
fn call_generator(
&mut self,
result_register: u8,
function: RuntimeFunction,
frame_base: u8,
call_arg_count: u8,
instance_register: Option<u8>,
instruction_ip: usize,
) -> InstructionResult {
let RuntimeFunction {
chunk,
ip: function_ip,
arg_count: function_arg_count,
instance_function,
variadic,
captures,
} = function;
let mut generator_vm = self.spawn_shared_vm();
generator_vm.push_frame(
chunk,
function_ip,
0,
captures,
);
let expected_arg_count = match (instance_function, variadic) {
(true, true) => function_arg_count - 2,
(true, false) | (false, true) => function_arg_count - 1,
(false, false) => function_arg_count,
};
let arg_offset = if instance_function {
if let Some(instance_register) = instance_register {
let instance = self.clone_register(instance_register);
generator_vm.set_register(0, instance);
1
} else {
return vm_error!(
self.chunk(),
instruction_ip,
"Missing instance for call to instance function"
);
}
} else {
0
};
if variadic {
if call_arg_count >= expected_arg_count {
let varargs_start = frame_base + 1 + expected_arg_count;
let varargs_count = call_arg_count - expected_arg_count;
let varargs =
Value::Tuple(self.register_slice(varargs_start, varargs_count).into());
generator_vm.set_register(expected_arg_count + arg_offset, varargs);
} else {
return vm_error!(
self.chunk(),
instruction_ip,
"Insufficient arguments for function call, expected {}, found {}",
expected_arg_count,
call_arg_count,
);
}
} else if call_arg_count != expected_arg_count {
return vm_error!(
self.chunk(),
instruction_ip,
"Incorrect argument count, expected {}, found {}",
expected_arg_count,
call_arg_count,
);
}
for (arg_index, arg) in self
.register_slice(frame_base + 1, expected_arg_count)
.iter()
.cloned()
.enumerate()
{
generator_vm.set_register(arg_index as u8 + arg_offset, arg);
}
self.truncate_registers(frame_base);
self.set_register(result_register, ValueIterator::with_vm(generator_vm).into());
Ok(())
}
fn call_function(
&mut self,
result_register: u8,
function: Value,
frame_base: u8,
call_arg_count: u8,
instance_register: Option<u8>,
instruction_ip: usize,
) -> InstructionResult {
use Value::*;
match function {
ExternalFunction(external_function) => self.call_external_function(
result_register,
external_function,
frame_base,
call_arg_count,
instance_register,
instruction_ip,
),
Generator(runtime_function) => self.call_generator(
result_register,
runtime_function,
frame_base,
call_arg_count,
instance_register,
instruction_ip,
),
Function(RuntimeFunction {
chunk,
ip: function_ip,
arg_count: function_arg_count,
instance_function,
variadic,
captures,
}) => {
let expected_count = match (instance_function, variadic) {
(true, true) => function_arg_count - 2,
(true, false) | (false, true) => function_arg_count - 1,
(false, false) => function_arg_count,
};
let adjusted_frame_base = if instance_function {
if let Some(instance_register) = instance_register {
if instance_register != frame_base {
let instance = self.clone_register(instance_register);
self.set_register(frame_base, instance);
}
frame_base
} else {
return vm_error!(
self.chunk(),
instruction_ip,
"Missing instance for call to instance function"
);
}
} else {
frame_base + 1
};
if variadic {
if call_arg_count >= expected_count {
let arg_base = frame_base + 1;
let varargs_start = arg_base + expected_count;
let varargs_count = call_arg_count - expected_count;
let varargs =
Value::Tuple(self.register_slice(varargs_start, varargs_count).into());
self.set_register(varargs_start, varargs);
self.truncate_registers(varargs_start + 1);
} else {
return vm_error!(
self.chunk(),
instruction_ip,
"Insufficient arguments for function call, expected {}, found {}",
expected_count,
call_arg_count,
);
}
} else if call_arg_count != expected_count {
return vm_error!(
self.chunk(),
instruction_ip,
"Incorrect argument count, expected {}, found {}",
expected_count,
call_arg_count,
);
}
if !self.call_stack.is_empty() {
self.frame_mut().return_register_and_ip = Some((result_register, self.ip()));
}
self.push_frame(chunk, function_ip, adjusted_frame_base, captures);
Ok(())
}
unexpected => {
self.unexpected_type_error("Expected Function", &unexpected, instruction_ip)
}
}
}
fn run_debug(&self, register: u8, constant: ConstantIndex, instruction_ip: usize) {
let prefix = match (
self.reader.chunk.debug_info.get_source_span(instruction_ip),
self.reader.chunk.source_path.as_ref(),
) {
(Some(span), Some(path)) => format!("[{}: {}] ", path.display(), span.start.line),
(Some(span), None) => format!("[{}] ", span.start.line),
(None, Some(path)) => format!("[{}: #ERR] ", path.display()),
(None, None) => "[#ERR] ".to_string(),
};
let value = self.get_register(register);
println!("{}{}: {}", prefix, self.get_constant_str(constant), value);
}
fn run_check_type(
&self,
register: u8,
type_id: TypeId,
instruction_ip: usize,
) -> Result<(), RuntimeError> {
let value = self.get_register(register);
match type_id {
TypeId::List => {
if !matches!(value, Value::List(_)) {
return self.unexpected_type_error("Expected List", &value, instruction_ip);
}
}
TypeId::Tuple => {
if !matches!(value, Value::Tuple(_)) {
return self.unexpected_type_error("Expected Tuple", &value, instruction_ip);
}
}
}
Ok(())
}
fn run_check_size(
&self,
register: u8,
expected_size: usize,
instruction_ip: usize,
) -> Result<(), RuntimeError> {
let value_size = value_size(self.get_register(register));
if value_size == expected_size {
Ok(())
} else {
vm_error!(
self.chunk(),
instruction_ip,
"Value has a size of '{}', expected '{}'",
value_size,
expected_size
)
}
}
pub fn chunk(&self) -> Arc<Chunk> {
self.reader.chunk.clone()
}
fn set_chunk_and_ip(&mut self, chunk: Arc<Chunk>, ip: usize) {
self.reader = InstructionReader { chunk, ip };
}
fn ip(&self) -> usize {
self.reader.ip
}
fn set_ip(&mut self, ip: usize) {
self.reader.ip = ip;
}
fn jump_ip(&mut self, offset: usize) {
self.reader.ip += offset;
}
fn jump_ip_back(&mut self, offset: usize) {
self.reader.ip -= offset;
}
fn frame(&self) -> &Frame {
self.call_stack.last().expect("Empty call stack")
}
fn frame_mut(&mut self) -> &mut Frame {
self.call_stack.last_mut().expect("Empty call stack")
}
fn push_frame(
&mut self,
chunk: Arc<Chunk>,
ip: usize,
frame_base: u8,
captures: Option<ValueList>,
) {
let previous_frame_base = if let Some(frame) = self.call_stack.last() {
frame.register_base
} else {
0
};
let new_frame_base = previous_frame_base + frame_base as usize;
self.call_stack
.push(Frame::new(chunk.clone(), new_frame_base, captures));
self.set_chunk_and_ip(chunk, ip);
}
fn pop_frame(&mut self, return_value: Value) -> Result<Option<Value>, RuntimeError> {
self.truncate_registers(0);
if self.call_stack.pop().is_none() {
return vm_error!(self.chunk(), 0, "pop_frame: Empty call stack");
};
if !self.call_stack.is_empty() && self.frame().return_register_and_ip.is_some() {
let (return_register, return_ip) = self.frame().return_register_and_ip.unwrap();
self.set_register(return_register, return_value);
self.set_chunk_and_ip(self.frame().chunk.clone(), return_ip);
Ok(None)
} else {
Ok(Some(return_value))
}
}
fn register_base(&self) -> usize {
match self.call_stack.last() {
Some(frame) => frame.register_base,
None => 0,
}
}
fn register_index(&self, register: u8) -> usize {
self.register_base() + register as usize
}
fn set_register(&mut self, register: u8, value: Value) {
let index = self.register_index(register);
if index >= self.value_stack.len() {
self.value_stack.resize(index + 1, Value::Empty);
}
self.value_stack[index] = value;
}
fn clone_register(&self, register: u8) -> Value {
self.get_register(register).clone()
}
fn get_register(&self, register: u8) -> &Value {
let index = self.register_index(register);
match self.value_stack.get(index) {
Some(value) => value,
None => {
panic!(
"Out of bounds access, index: {}, register: {}, ip: {}",
index,
register,
self.ip()
);
}
}
}
fn get_register_mut(&mut self, register: u8) -> &mut Value {
let index = self.register_index(register);
&mut self.value_stack[index]
}
pub fn register_slice(&self, register: u8, count: u8) -> &[Value] {
if count > 0 {
let start = self.register_index(register);
&self.value_stack[start..start + count as usize]
} else {
&[]
}
}
fn truncate_registers(&mut self, len: u8) {
self.value_stack
.truncate(self.register_base() + len as usize);
}
pub fn get_args(&self, args: &Args) -> &[Value] {
self.register_slice(args.register, args.count)
}
fn get_constant_str(&self, constant_index: ConstantIndex) -> &str {
self.reader.chunk.constants.get_str(constant_index)
}
fn value_string_from_constant(&self, constant_index: ConstantIndex) -> ValueString {
let bounds = self.reader.chunk.constants.get_str_bounds(constant_index);
ValueString::new_with_bounds(self.reader.chunk.string_constants_arc.clone(), bounds)
.unwrap()
}
fn unexpected_type_error<T>(
&self,
message: &str,
value: &Value,
instruction_ip: usize,
) -> Result<T, RuntimeError> {
vm_error!(
self.chunk(),
instruction_ip,
format!("{}, found '{}'", message, type_as_string(&value))
)
}
fn binary_op_error(
&self,
lhs: &Value,
rhs: &Value,
op: &Instruction,
ip: usize,
) -> InstructionResult {
vm_error!(
self.chunk(),
ip,
"Unable to perform operation {} with '{}' and '{}'",
op,
type_as_string(lhs),
type_as_string(rhs),
)
}
}
impl fmt::Debug for Vm {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Vm")
}
}
fn signed_index_to_unsigned(index: i8, size: usize) -> usize {
if index < 0 {
size - (index.abs() as usize).min(size)
} else {
index as usize
}
}