Add parents to the VM; Improve the disassembler
This commit is contained in:
parent
f54d2d0d22
commit
04a1c81a2a
@ -95,18 +95,21 @@ impl<'src> Compiler<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn next_register(&mut self) -> u8 {
|
fn next_register(&mut self) -> u8 {
|
||||||
self.chunk
|
let current = self
|
||||||
|
.chunk
|
||||||
.instructions()
|
.instructions()
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.find_map(|(instruction, _)| {
|
.find_map(|(instruction, _)| {
|
||||||
if instruction.yields_value() {
|
if instruction.yields_value() {
|
||||||
Some(instruction.a() + 1)
|
Some(instruction.a())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.unwrap_or(self.minimum_register)
|
.unwrap_or(self.minimum_register);
|
||||||
|
|
||||||
|
current.overflowing_add(1).0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn advance(&mut self) -> Result<(), CompileError> {
|
fn advance(&mut self) -> Result<(), CompileError> {
|
||||||
|
@ -51,7 +51,7 @@ const INSTRUCTION_HEADER: [&str; 4] = [
|
|||||||
"Instructions",
|
"Instructions",
|
||||||
"------------",
|
"------------",
|
||||||
" i BYTECODE OPERATION INFO TYPE POSITION ",
|
" i BYTECODE OPERATION INFO TYPE POSITION ",
|
||||||
"--- -------- ------------- -------------------- ---------------- ------------",
|
"--- -------- ------------- -------------------- ---------------- ----------",
|
||||||
];
|
];
|
||||||
|
|
||||||
const CONSTANT_HEADER: [&str; 4] = [
|
const CONSTANT_HEADER: [&str; 4] = [
|
||||||
@ -277,7 +277,7 @@ impl<'a> Disassembler<'a> {
|
|||||||
let position = position.to_string();
|
let position = position.to_string();
|
||||||
|
|
||||||
let instruction_display = format!(
|
let instruction_display = format!(
|
||||||
"{index:^3} {bytecode:>8} {operation:13} {info:20} {type_display:^16} {position:12}"
|
"{index:^3} {bytecode:>8} {operation:13} {info:^20} {type_display:^16} {position:10}"
|
||||||
);
|
);
|
||||||
|
|
||||||
self.push_details(&instruction_display);
|
self.push_details(&instruction_display);
|
||||||
|
@ -28,7 +28,7 @@ use std::{
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{Chunk, FunctionType, RangeableType, Span, Type, Vm, VmError};
|
use crate::{Chunk, FunctionType, RangeableType, Type};
|
||||||
|
|
||||||
/// Dust value representation
|
/// Dust value representation
|
||||||
///
|
///
|
||||||
@ -255,25 +255,6 @@ impl Value {
|
|||||||
|
|
||||||
Ok(not)
|
Ok(not)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_concrete(self, vm: &mut Vm, position: Span) -> Result<Value, VmError> {
|
|
||||||
match self {
|
|
||||||
Value::Concrete(_) => Ok(self),
|
|
||||||
Value::Abstract(AbstractValue::List { start, end, .. }) => {
|
|
||||||
let mut items = Vec::new();
|
|
||||||
|
|
||||||
for register_index in start..end {
|
|
||||||
let get_value = vm.empty_register(register_index, position);
|
|
||||||
|
|
||||||
if let Ok(value) = get_value {
|
|
||||||
items.push(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Value::Concrete(ConcreteValue::List(items)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<bool> for Value {
|
impl From<bool> for Value {
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
//! Virtual machine and errors
|
//! Virtual machine and errors
|
||||||
use std::{cmp::Ordering, mem::replace};
|
use std::{
|
||||||
|
cmp::Ordering,
|
||||||
|
fmt::{self, Display, Formatter},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
compile, value::ConcreteValue, AnnotatedError, Chunk, ChunkError, DustError, FunctionType,
|
compile, value::ConcreteValue, AnnotatedError, Chunk, ChunkError, DustError, FunctionType,
|
||||||
@ -9,42 +12,53 @@ use crate::{
|
|||||||
|
|
||||||
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
||||||
let chunk = compile(source)?;
|
let chunk = compile(source)?;
|
||||||
let vm = Vm::new(chunk);
|
let mut vm = Vm::new(chunk, None);
|
||||||
|
|
||||||
vm.run()
|
vm.run()
|
||||||
|
.map(|option| option.cloned())
|
||||||
.map_err(|error| DustError::Runtime { error, source })
|
.map_err(|error| DustError::Runtime { error, source })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn run_and_display_output(source: &str) {
|
||||||
|
match run(source) {
|
||||||
|
Ok(Some(value)) => println!("{}", value),
|
||||||
|
Ok(None) => {}
|
||||||
|
Err(error) => eprintln!("{}", error.report()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Dust virtual machine.
|
/// Dust virtual machine.
|
||||||
///
|
///
|
||||||
/// See the [module-level documentation](index.html) for more information.
|
/// See the [module-level documentation](index.html) for more information.
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub struct Vm {
|
pub struct Vm<'parent> {
|
||||||
ip: usize,
|
ip: usize,
|
||||||
chunk: Chunk,
|
chunk: Chunk,
|
||||||
stack: Vec<Register>,
|
stack: Vec<Register>,
|
||||||
last_assigned_register: Option<u8>,
|
last_assigned_register: Option<u8>,
|
||||||
|
parent: Option<&'parent Vm<'parent>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Vm {
|
impl<'parent> Vm<'parent> {
|
||||||
const STACK_LIMIT: usize = u16::MAX as usize;
|
const STACK_LIMIT: usize = u16::MAX as usize;
|
||||||
|
|
||||||
pub fn new(chunk: Chunk) -> Self {
|
pub fn new(chunk: Chunk, parent: Option<&'parent Vm<'parent>>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ip: 0,
|
ip: 0,
|
||||||
chunk,
|
chunk,
|
||||||
stack: Vec::new(),
|
stack: Vec::new(),
|
||||||
last_assigned_register: None,
|
last_assigned_register: None,
|
||||||
|
parent,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(mut self) -> Result<Option<Value>, VmError> {
|
pub fn run(&mut self) -> Result<Option<&Value>, VmError> {
|
||||||
// DRY helper to get constant or register values for binary operations
|
// DRY helper to get constant or register values for binary operations
|
||||||
fn get_arguments(
|
fn get_arguments<'a>(
|
||||||
vm: &mut Vm,
|
vm: &'a mut Vm,
|
||||||
instruction: Instruction,
|
instruction: Instruction,
|
||||||
position: Span,
|
position: Span,
|
||||||
) -> Result<(&Value, &Value), VmError> {
|
) -> Result<(&'a Value, &'a Value), VmError> {
|
||||||
let left = if instruction.b_is_constant() {
|
let left = if instruction.b_is_constant() {
|
||||||
vm.get_constant(instruction.b(), position)?
|
vm.get_constant(instruction.b(), position)?
|
||||||
} else {
|
} else {
|
||||||
@ -78,7 +92,11 @@ impl Vm {
|
|||||||
.is_some_and(|register| !matches!(register, Register::Empty));
|
.is_some_and(|register| !matches!(register, Register::Empty));
|
||||||
|
|
||||||
if from_register_has_value {
|
if from_register_has_value {
|
||||||
self.set_pointer(to_register, from_register, position)?;
|
self.set_register(
|
||||||
|
to_register,
|
||||||
|
Register::StackPointer(from_register),
|
||||||
|
position,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::Close => {
|
Operation::Close => {
|
||||||
@ -99,7 +117,7 @@ impl Vm {
|
|||||||
let jump = instruction.c_as_boolean();
|
let jump = instruction.c_as_boolean();
|
||||||
let value = Value::boolean(boolean);
|
let value = Value::boolean(boolean);
|
||||||
|
|
||||||
self.set_register(to_register, value, position)?;
|
self.set_register(to_register, Register::Value(value), position)?;
|
||||||
|
|
||||||
if jump {
|
if jump {
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
@ -110,7 +128,11 @@ impl Vm {
|
|||||||
let from_constant = instruction.b();
|
let from_constant = instruction.b();
|
||||||
let jump = instruction.c_as_boolean();
|
let jump = instruction.c_as_boolean();
|
||||||
|
|
||||||
self.set_constant(to_register, from_constant, position)?;
|
self.set_register(
|
||||||
|
to_register,
|
||||||
|
Register::ConstantPointer(from_constant),
|
||||||
|
position,
|
||||||
|
)?;
|
||||||
|
|
||||||
if jump {
|
if jump {
|
||||||
self.ip += 1
|
self.ip += 1
|
||||||
@ -130,7 +152,7 @@ impl Vm {
|
|||||||
.unwrap_or(Type::Any);
|
.unwrap_or(Type::Any);
|
||||||
let value = Value::abstract_list(start_register, to_register, item_type);
|
let value = Value::abstract_list(start_register, to_register, item_type);
|
||||||
|
|
||||||
self.set_register(to_register, value, position)?;
|
self.set_register(to_register, Register::Value(value), position)?;
|
||||||
}
|
}
|
||||||
Operation::LoadSelf => {
|
Operation::LoadSelf => {
|
||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
@ -143,7 +165,7 @@ impl Vm {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
self.set_register(to_register, value, position)?;
|
self.set_register(to_register, Register::Value(value), position)?;
|
||||||
}
|
}
|
||||||
Operation::DefineLocal => {
|
Operation::DefineLocal => {
|
||||||
let from_register = instruction.a();
|
let from_register = instruction.a();
|
||||||
@ -156,7 +178,11 @@ impl Vm {
|
|||||||
let local_index = instruction.b();
|
let local_index = instruction.b();
|
||||||
let local = self.get_local(local_index, position)?;
|
let local = self.get_local(local_index, position)?;
|
||||||
|
|
||||||
self.set_pointer(to_register, local.register_index, position)?;
|
self.set_register(
|
||||||
|
to_register,
|
||||||
|
Register::StackPointer(local.register_index),
|
||||||
|
position,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
Operation::SetLocal => {
|
Operation::SetLocal => {
|
||||||
let register = instruction.a();
|
let register = instruction.a();
|
||||||
@ -165,44 +191,49 @@ impl Vm {
|
|||||||
self.define_local(local_index, register, position)?;
|
self.define_local(local_index, register, position)?;
|
||||||
}
|
}
|
||||||
Operation::Add => {
|
Operation::Add => {
|
||||||
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
let to_register = instruction.a();
|
||||||
|
let (left, right) = get_arguments(self, instruction, position)?;
|
||||||
let sum = left
|
let sum = left
|
||||||
.add(right)
|
.add(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.set_register(instruction.a(), sum, position)?;
|
self.set_register(to_register, Register::Value(sum), position)?;
|
||||||
}
|
}
|
||||||
Operation::Subtract => {
|
Operation::Subtract => {
|
||||||
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
let to_register = instruction.a();
|
||||||
|
let (left, right) = get_arguments(self, instruction, position)?;
|
||||||
let difference = left
|
let difference = left
|
||||||
.subtract(right)
|
.subtract(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.set_register(instruction.a(), difference, position)?;
|
self.set_register(to_register, Register::Value(difference), position)?;
|
||||||
}
|
}
|
||||||
Operation::Multiply => {
|
Operation::Multiply => {
|
||||||
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
let to_register = instruction.a();
|
||||||
|
let (left, right) = get_arguments(self, instruction, position)?;
|
||||||
let product = left
|
let product = left
|
||||||
.multiply(right)
|
.multiply(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.set_register(instruction.a(), product, position)?;
|
self.set_register(to_register, Register::Value(product), position)?;
|
||||||
}
|
}
|
||||||
Operation::Divide => {
|
Operation::Divide => {
|
||||||
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
let to_register = instruction.a();
|
||||||
|
let (left, right) = get_arguments(self, instruction, position)?;
|
||||||
let quotient = left
|
let quotient = left
|
||||||
.divide(right)
|
.divide(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.set_register(instruction.a(), quotient, position)?;
|
self.set_register(to_register, Register::Value(quotient), position)?;
|
||||||
}
|
}
|
||||||
Operation::Modulo => {
|
Operation::Modulo => {
|
||||||
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
let to_register = instruction.a();
|
||||||
|
let (left, right) = get_arguments(self, instruction, position)?;
|
||||||
let remainder = left
|
let remainder = left
|
||||||
.modulo(right)
|
.modulo(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.set_register(instruction.a(), remainder, position)?;
|
self.set_register(to_register, Register::Value(remainder), position)?;
|
||||||
}
|
}
|
||||||
Operation::Test => {
|
Operation::Test => {
|
||||||
let register = instruction.a();
|
let register = instruction.a();
|
||||||
@ -228,7 +259,7 @@ impl Vm {
|
|||||||
Operation::Jump
|
Operation::Jump
|
||||||
);
|
);
|
||||||
|
|
||||||
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
let (left, right) = get_arguments(self, instruction, position)?;
|
||||||
let equal_result = left
|
let equal_result = left
|
||||||
.equal(right)
|
.equal(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
@ -264,7 +295,7 @@ impl Vm {
|
|||||||
Operation::Jump
|
Operation::Jump
|
||||||
);
|
);
|
||||||
|
|
||||||
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
let (left, right) = get_arguments(self, instruction, position)?;
|
||||||
let less_result = left
|
let less_result = left
|
||||||
.less_than(right)
|
.less_than(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
@ -300,7 +331,7 @@ impl Vm {
|
|||||||
Operation::Jump
|
Operation::Jump
|
||||||
);
|
);
|
||||||
|
|
||||||
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
let (left, right) = get_arguments(self, instruction, position)?;
|
||||||
let less_or_equal_result = left
|
let less_or_equal_result = left
|
||||||
.less_than_or_equal(right)
|
.less_than_or_equal(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
@ -341,7 +372,7 @@ impl Vm {
|
|||||||
.negate()
|
.negate()
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.set_register(instruction.a(), negated, position)?;
|
self.set_register(instruction.a(), Register::Value(negated), position)?;
|
||||||
}
|
}
|
||||||
Operation::Not => {
|
Operation::Not => {
|
||||||
let value = if instruction.b_is_constant() {
|
let value = if instruction.b_is_constant() {
|
||||||
@ -353,7 +384,7 @@ impl Vm {
|
|||||||
.not()
|
.not()
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.set_register(instruction.a(), not, position)?;
|
self.set_register(instruction.a(), Register::Value(not), position)?;
|
||||||
}
|
}
|
||||||
Operation::Jump => {
|
Operation::Jump => {
|
||||||
let jump_distance = instruction.b();
|
let jump_distance = instruction.b();
|
||||||
@ -379,36 +410,35 @@ impl Vm {
|
|||||||
position,
|
position,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
let mut function_vm = Vm::new(function.take_chunk());
|
let mut function_vm = Vm::new(function.take_chunk(), Some(self));
|
||||||
let first_argument_index = function_register + 1;
|
let first_argument_index = function_register + 1;
|
||||||
|
|
||||||
for argument_index in
|
for argument_index in
|
||||||
first_argument_index..first_argument_index + argument_count
|
first_argument_index..first_argument_index + argument_count
|
||||||
{
|
{
|
||||||
let argument = match self.get_register(argument_index, position) {
|
|
||||||
Ok(value) => value.clone(),
|
|
||||||
Err(VmError::EmptyRegister { .. }) => continue,
|
|
||||||
Err(error) => return Err(error),
|
|
||||||
};
|
|
||||||
let top_of_stack = function_vm.stack.len() as u8;
|
let top_of_stack = function_vm.stack.len() as u8;
|
||||||
|
|
||||||
function_vm.set_register(top_of_stack, argument, position)?;
|
function_vm.set_register(
|
||||||
|
top_of_stack,
|
||||||
|
Register::ParentStackPointer(argument_index),
|
||||||
|
position,
|
||||||
|
)?
|
||||||
}
|
}
|
||||||
|
|
||||||
let return_value = function_vm.run()?;
|
let return_value = function_vm.run()?.cloned();
|
||||||
|
|
||||||
if let Some(value) = return_value {
|
if let Some(value) = return_value {
|
||||||
self.set_register(to_register, value, position)?;
|
self.set_register(to_register, Register::Value(value), position)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::CallNative => {
|
Operation::CallNative => {
|
||||||
let native_function = NativeFunction::from(instruction.b());
|
let native_function = NativeFunction::from(instruction.b());
|
||||||
let return_value = native_function.call(instruction, &self, position)?;
|
let return_value = native_function.call(instruction, self, position)?;
|
||||||
|
|
||||||
if let Some(value) = return_value {
|
if let Some(value) = return_value {
|
||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
|
|
||||||
self.set_register(to_register, value, position)?;
|
self.set_register(to_register, Register::Value(value), position)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::Return => {
|
Operation::Return => {
|
||||||
@ -418,15 +448,13 @@ impl Vm {
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(register) = self.last_assigned_register {
|
let return_value = if let Some(register_index) = self.last_assigned_register {
|
||||||
let value = self
|
self.get_register(register_index, position)?
|
||||||
.empty_register(register, position)?
|
|
||||||
.to_concrete(&mut self, position)?;
|
|
||||||
|
|
||||||
return Ok(Some(value));
|
|
||||||
} else {
|
} else {
|
||||||
return Err(VmError::StackUnderflow { position });
|
return Err(VmError::StackUnderflow { position });
|
||||||
}
|
};
|
||||||
|
|
||||||
|
return Ok(Some(return_value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -437,7 +465,7 @@ impl Vm {
|
|||||||
fn set_register(
|
fn set_register(
|
||||||
&mut self,
|
&mut self,
|
||||||
to_register: u8,
|
to_register: u8,
|
||||||
value: Value,
|
register: Register,
|
||||||
position: Span,
|
position: Span,
|
||||||
) -> Result<(), VmError> {
|
) -> Result<(), VmError> {
|
||||||
self.last_assigned_register = Some(to_register);
|
self.last_assigned_register = Some(to_register);
|
||||||
@ -451,16 +479,16 @@ impl Vm {
|
|||||||
|
|
||||||
match to_register.cmp(&length) {
|
match to_register.cmp(&length) {
|
||||||
Ordering::Less => {
|
Ordering::Less => {
|
||||||
log::trace!("Change R{to_register} to {value}");
|
log::trace!("Change R{to_register} to {register}");
|
||||||
|
|
||||||
self.stack[to_register] = Register::Value(value);
|
self.stack[to_register] = register;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Ordering::Equal => {
|
Ordering::Equal => {
|
||||||
log::trace!("Set R{to_register} to {value}");
|
log::trace!("Set R{to_register} to {register}");
|
||||||
|
|
||||||
self.stack.push(Register::Value(value));
|
self.stack.push(register);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -473,157 +501,58 @@ impl Vm {
|
|||||||
self.stack.push(Register::Empty);
|
self.stack.push(Register::Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
log::trace!("Set R{to_register} to {value}");
|
log::trace!("Set R{to_register} to {register}");
|
||||||
|
|
||||||
self.stack.push(Register::Value(value));
|
self.stack.push(register);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_pointer(
|
|
||||||
&mut self,
|
|
||||||
to_register: u8,
|
|
||||||
from_register: u8,
|
|
||||||
position: Span,
|
|
||||||
) -> Result<(), VmError> {
|
|
||||||
self.last_assigned_register = Some(to_register);
|
|
||||||
|
|
||||||
let length = self.stack.len();
|
|
||||||
let to_register = to_register as usize;
|
|
||||||
|
|
||||||
if length == Self::STACK_LIMIT {
|
|
||||||
return Err(VmError::StackOverflow { position });
|
|
||||||
}
|
|
||||||
|
|
||||||
match to_register.cmp(&length) {
|
|
||||||
Ordering::Less => {
|
|
||||||
log::trace!("Change R{to_register} to R{from_register}");
|
|
||||||
|
|
||||||
self.stack[to_register] = Register::Pointer(from_register);
|
|
||||||
}
|
|
||||||
Ordering::Equal => {
|
|
||||||
log::trace!("Set R{to_register} to R{from_register}");
|
|
||||||
|
|
||||||
self.stack.push(Register::Pointer(from_register));
|
|
||||||
}
|
|
||||||
Ordering::Greater => {
|
|
||||||
let difference = to_register - length;
|
|
||||||
|
|
||||||
for index in 0..difference {
|
|
||||||
log::trace!("Set R{index} to empty");
|
|
||||||
|
|
||||||
self.stack.push(Register::Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
log::trace!("Set R{to_register} to R{from_register}");
|
|
||||||
|
|
||||||
self.stack.push(Register::Pointer(from_register));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_constant(
|
|
||||||
&mut self,
|
|
||||||
to_register: u8,
|
|
||||||
constant_index: u8,
|
|
||||||
position: Span,
|
|
||||||
) -> Result<(), VmError> {
|
|
||||||
self.last_assigned_register = Some(to_register);
|
|
||||||
|
|
||||||
let length = self.stack.len();
|
|
||||||
let to_register = to_register as usize;
|
|
||||||
|
|
||||||
if length == Self::STACK_LIMIT {
|
|
||||||
return Err(VmError::StackOverflow { position });
|
|
||||||
}
|
|
||||||
|
|
||||||
match to_register.cmp(&length) {
|
|
||||||
Ordering::Less => {
|
|
||||||
log::trace!("Change R{to_register} to C{constant_index}");
|
|
||||||
|
|
||||||
self.stack[to_register] = Register::Constant(constant_index);
|
|
||||||
}
|
|
||||||
Ordering::Equal => {
|
|
||||||
log::trace!("Set R{to_register} to C{constant_index}");
|
|
||||||
|
|
||||||
self.stack.push(Register::Constant(constant_index));
|
|
||||||
}
|
|
||||||
Ordering::Greater => {
|
|
||||||
let difference = to_register - length;
|
|
||||||
|
|
||||||
for index in 0..difference {
|
|
||||||
log::trace!("Set R{index} to empty");
|
|
||||||
|
|
||||||
self.stack.push(Register::Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
log::trace!("Set R{to_register} to C{constant_index}");
|
|
||||||
|
|
||||||
self.stack.push(Register::Constant(constant_index));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_constant(&self, index: u8, position: Span) -> Result<&Value, VmError> {
|
fn get_constant(&self, index: u8, position: Span) -> Result<&Value, VmError> {
|
||||||
self.chunk
|
self.chunk
|
||||||
.get_constant(index)
|
.get_constant(index)
|
||||||
.map_err(|error| VmError::Chunk { error, position })
|
.map_err(|error| VmError::Chunk { error, position })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_register(&self, index: u8, position: Span) -> Result<&Value, VmError> {
|
pub fn get_register(&self, register_index: u8, position: Span) -> Result<&Value, VmError> {
|
||||||
let index = index as usize;
|
let register_index = register_index as usize;
|
||||||
let register = self
|
let register =
|
||||||
.stack
|
self.stack
|
||||||
.get(index)
|
.get(register_index)
|
||||||
.ok_or_else(|| VmError::RegisterIndexOutOfBounds { index, position })?;
|
.ok_or_else(|| VmError::RegisterIndexOutOfBounds {
|
||||||
|
index: register_index,
|
||||||
|
position,
|
||||||
|
})?;
|
||||||
|
|
||||||
match register {
|
match register {
|
||||||
Register::Value(value) => Ok(value),
|
Register::Value(value) => Ok(value),
|
||||||
Register::Pointer(register_index) => self.get_register(*register_index, position),
|
Register::StackPointer(register_index) => self.get_register(*register_index, position),
|
||||||
Register::Constant(constant_index) => self.get_constant(*constant_index, position),
|
Register::ConstantPointer(constant_index) => {
|
||||||
Register::Empty => Err(VmError::EmptyRegister { index, position }),
|
self.get_constant(*constant_index, position)
|
||||||
}
|
}
|
||||||
|
Register::ParentStackPointer(register_index) => {
|
||||||
|
let parent = self
|
||||||
|
.parent
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(VmError::ExpectedParent { position })?;
|
||||||
|
|
||||||
|
parent.get_register(*register_index, position)
|
||||||
}
|
}
|
||||||
|
Register::ParentConstantPointer(constant_index) => {
|
||||||
|
let parent = self
|
||||||
|
.parent
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(VmError::ExpectedParent { position })?;
|
||||||
|
|
||||||
pub fn empty_register(&mut self, index: u8, position: Span) -> Result<Value, VmError> {
|
parent.get_constant(*constant_index, position)
|
||||||
let index = index as usize;
|
|
||||||
|
|
||||||
if index >= self.stack.len() {
|
|
||||||
return Err(VmError::RegisterIndexOutOfBounds { index, position });
|
|
||||||
}
|
}
|
||||||
|
Register::Empty => Err(VmError::EmptyRegister {
|
||||||
let register = replace(&mut self.stack[index], Register::Empty);
|
index: register_index,
|
||||||
let value = match register {
|
|
||||||
Register::Value(value) => value,
|
|
||||||
Register::Pointer(register_index) => self.empty_register(register_index, position)?,
|
|
||||||
Register::Constant(constant_index) => {
|
|
||||||
let constant_index = constant_index as usize;
|
|
||||||
|
|
||||||
if constant_index >= self.chunk.constants().len() {
|
|
||||||
return Err(VmError::Chunk {
|
|
||||||
error: ChunkError::ConstantIndexOutOfBounds {
|
|
||||||
index: constant_index,
|
|
||||||
},
|
|
||||||
position,
|
position,
|
||||||
});
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
let constant = &mut self.chunk.constants_mut()[constant_index];
|
|
||||||
|
|
||||||
replace(constant, Value::integer(0))
|
|
||||||
}
|
|
||||||
Register::Empty => return Err(VmError::EmptyRegister { index, position }),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.chunk.is_poisoned = true;
|
|
||||||
|
|
||||||
Ok(value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read(&mut self, position: Span) -> Result<&(Instruction, Span), VmError> {
|
fn read(&mut self, position: Span) -> Result<&(Instruction, Span), VmError> {
|
||||||
@ -681,8 +610,23 @@ impl Vm {
|
|||||||
enum Register {
|
enum Register {
|
||||||
Empty,
|
Empty,
|
||||||
Value(Value),
|
Value(Value),
|
||||||
Pointer(u8),
|
StackPointer(u8),
|
||||||
Constant(u8),
|
ConstantPointer(u8),
|
||||||
|
ParentStackPointer(u8),
|
||||||
|
ParentConstantPointer(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Register {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Empty => write!(f, "empty"),
|
||||||
|
Self::Value(value) => write!(f, "{}", value),
|
||||||
|
Self::StackPointer(index) => write!(f, "R{}", index),
|
||||||
|
Self::ConstantPointer(index) => write!(f, "C{}", index),
|
||||||
|
Self::ParentStackPointer(index) => write!(f, "PR{}", index),
|
||||||
|
Self::ParentConstantPointer(index) => write!(f, "PC{}", index),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
@ -698,6 +642,7 @@ pub enum VmError {
|
|||||||
// Execution errors
|
// Execution errors
|
||||||
ExpectedBoolean { found: Value, position: Span },
|
ExpectedBoolean { found: Value, position: Span },
|
||||||
ExpectedFunction { found: Value, position: Span },
|
ExpectedFunction { found: Value, position: Span },
|
||||||
|
ExpectedParent { position: Span },
|
||||||
|
|
||||||
// Wrappers for foreign errors
|
// Wrappers for foreign errors
|
||||||
Chunk { error: ChunkError, position: Span },
|
Chunk { error: ChunkError, position: Span },
|
||||||
@ -716,6 +661,7 @@ impl AnnotatedError for VmError {
|
|||||||
Self::EmptyRegister { .. } => "Empty register",
|
Self::EmptyRegister { .. } => "Empty register",
|
||||||
Self::ExpectedBoolean { .. } => "Expected boolean",
|
Self::ExpectedBoolean { .. } => "Expected boolean",
|
||||||
Self::ExpectedFunction { .. } => "Expected function",
|
Self::ExpectedFunction { .. } => "Expected function",
|
||||||
|
Self::ExpectedParent { .. } => "Expected parent",
|
||||||
Self::NativeFunction(error) => error.description(),
|
Self::NativeFunction(error) => error.description(),
|
||||||
Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds",
|
Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds",
|
||||||
Self::StackOverflow { .. } => "Stack overflow",
|
Self::StackOverflow { .. } => "Stack overflow",
|
||||||
@ -744,6 +690,7 @@ impl AnnotatedError for VmError {
|
|||||||
Self::EmptyRegister { position, .. } => *position,
|
Self::EmptyRegister { position, .. } => *position,
|
||||||
Self::ExpectedBoolean { position, .. } => *position,
|
Self::ExpectedBoolean { position, .. } => *position,
|
||||||
Self::ExpectedFunction { position, .. } => *position,
|
Self::ExpectedFunction { position, .. } => *position,
|
||||||
|
Self::ExpectedParent { position } => *position,
|
||||||
Self::NativeFunction(error) => error.position(),
|
Self::NativeFunction(error) => error.position(),
|
||||||
Self::RegisterIndexOutOfBounds { position, .. } => *position,
|
Self::RegisterIndexOutOfBounds { position, .. } => *position,
|
||||||
Self::StackOverflow { position } => *position,
|
Self::StackOverflow { position } => *position,
|
||||||
|
@ -17,7 +17,7 @@ fn empty_list() {
|
|||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(Value::list(vec![]))));
|
assert_eq!(run(source), Ok(Some(Value::abstract_list(0, 0, Type::Any))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -35,18 +35,14 @@ fn list() {
|
|||||||
(Instruction::load_list(3, 0), Span(0, 9)),
|
(Instruction::load_list(3, 0), Span(0, 9)),
|
||||||
(Instruction::r#return(true), Span(9, 9)),
|
(Instruction::r#return(true), Span(9, 9)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(1), Value::integer(2), Value::integer(3),],
|
vec![Value::integer(1), Value::integer(2), Value::integer(3)],
|
||||||
vec![]
|
vec![]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run(source),
|
run(source),
|
||||||
Ok(Some(Value::list([
|
Ok(Some(Value::abstract_list(0, 3, Type::Integer)))
|
||||||
Value::integer(1),
|
|
||||||
Value::integer(2),
|
|
||||||
Value::integer(3)
|
|
||||||
])))
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,10 +86,7 @@ fn list_with_complex_expression() {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run(source),
|
run(source),
|
||||||
Ok(Some(Value::list([
|
Ok(Some(Value::abstract_list(0, 4, Type::Integer)))
|
||||||
Value::integer(1),
|
|
||||||
Value::integer(2 + 3 - 4 * 5)
|
|
||||||
])))
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,10 +122,6 @@ fn list_with_simple_expression() {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run(source),
|
run(source),
|
||||||
Ok(Some(Value::list([
|
Ok(Some(Value::abstract_list(0, 3, Type::Integer)))
|
||||||
Value::integer(1),
|
|
||||||
Value::integer(2 + 3),
|
|
||||||
Value::integer(4)
|
|
||||||
])))
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -8,4 +8,4 @@ function fib(n) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(fib(10));
|
console.log(fib(20));
|
||||||
|
@ -5,4 +5,4 @@ fn fib (n: int) -> int {
|
|||||||
fib(n - 1) + fib(n - 2)
|
fib(n - 1) + fib(n - 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
fib(10)
|
fib(20)
|
||||||
|
Loading…
Reference in New Issue
Block a user