1
0

Consolidate Record type into the ThreadData type

This commit is contained in:
Jeff 2025-01-09 05:31:45 -05:00
parent 9d370aea2a
commit d9a0f6866b
11 changed files with 423 additions and 410 deletions

View File

@ -3,17 +3,19 @@ use std::{ops::Range, panic};
use crate::vm::ThreadData;
pub fn panic(data: &mut ThreadData, _: Option<u8>, argument_range: Range<u8>) -> bool {
let record = &mut data.call_stack.last_mut_unchecked().record;
let position = record.current_position();
let position = data.current_position();
let mut message = format!("Dust panic at {position}!");
for register_index in argument_range {
let value = record.open_register_unchecked(register_index);
let value_option = data.open_register_allow_empty_unchecked(register_index);
let value = match value_option {
Some(value) => value,
None => continue,
};
let string = value.display(data);
if let Some(string) = value.as_string() {
message.push_str(string);
message.push('\n');
}
message.push_str(&string);
message.push('\n');
}
panic!("{}", message)

View File

@ -11,7 +11,6 @@ pub fn read_line(
destination: Option<u8>,
_argument_range: Range<u8>,
) -> bool {
let record = &mut data.call_stack.last_mut_unchecked().record;
let destination = destination.unwrap();
let mut buffer = String::new();
@ -22,27 +21,26 @@ pub fn read_line(
let register = Register::Value(Value::Concrete(ConcreteValue::string(buffer)));
record.set_register(destination, register);
data.set_register(destination, register);
}
data.next_action = get_next_action(record);
data.next_action = get_next_action(data);
false
}
pub fn write(data: &mut ThreadData, _destination: Option<u8>, argument_range: Range<u8>) -> bool {
let record = &mut data.call_stack.last_mut_unchecked().record;
let mut stdout = stdout();
for register_index in argument_range {
if let Some(value) = record.open_register_allow_empty_unchecked(register_index) {
let string = value.display(record);
if let Some(value) = data.open_register_allow_empty_unchecked(register_index) {
let string = value.display(data);
let _ = stdout.write(string.as_bytes());
}
}
let _ = stdout.flush();
data.next_action = get_next_action(record);
data.next_action = get_next_action(data);
false
}
@ -52,19 +50,18 @@ pub fn write_line(
_destination: Option<u8>,
argument_range: Range<u8>,
) -> bool {
let record = &mut data.call_stack.last_mut_unchecked().record;
let mut stdout = stdout().lock();
for register_index in argument_range {
if let Some(value) = record.open_register_allow_empty_unchecked(register_index) {
let string = value.display(record);
if let Some(value) = data.open_register_allow_empty_unchecked(register_index) {
let string = value.display(data);
let _ = stdout.write(string.as_bytes());
let _ = stdout.write(b"\n");
}
}
let _ = stdout.flush();
data.next_action = get_next_action(record);
data.next_action = get_next_action(data);
false
}

View File

@ -10,15 +10,14 @@ pub fn to_string(
destination: Option<u8>,
argument_range: Range<u8>,
) -> bool {
let record = &mut data.call_stack.last_mut_unchecked().record;
let argument_value = record.open_register_unchecked(argument_range.start);
let argument_string = argument_value.display(record);
let argument_value = data.open_register_unchecked(argument_range.start);
let argument_string = argument_value.display(data);
let destination = destination.unwrap();
let register = Register::Value(Value::Concrete(ConcreteValue::string(argument_string)));
record.set_register(destination, register);
data.set_register(destination, register);
data.next_action = get_next_action(record);
data.next_action = get_next_action(data);
false
}

View File

@ -1,6 +1,6 @@
use std::fmt::{self, Display, Formatter};
use crate::{vm::Record, Pointer, Type};
use crate::{vm::ThreadData, Pointer, Type};
use super::DustString;
@ -11,7 +11,7 @@ pub struct AbstractList {
}
impl AbstractList {
pub fn display(&self, record: &Record) -> DustString {
pub fn display(&self, data: &ThreadData) -> DustString {
let mut display = DustString::new();
display.push('[');
@ -21,7 +21,7 @@ impl AbstractList {
display.push_str(", ");
}
let item_display = record.follow_pointer(*item).display(record);
let item_display = data.follow_pointer_unchecked(*item).display(data);
display.push_str(&item_display);
}

View File

@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize};
use std::fmt::{self, Debug, Display, Formatter};
use crate::{vm::Record, Type};
use crate::{vm::ThreadData, Type};
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
pub enum Value {
@ -179,9 +179,9 @@ impl Value {
}
}
pub fn display(&self, record: &Record) -> DustString {
pub fn display(&self, data: &ThreadData) -> DustString {
match self {
Value::AbstractList(list) => list.display(record),
Value::AbstractList(list) => list.display(data),
Value::Concrete(concrete_value) => concrete_value.display(),
Value::Function(function) => DustString::from(function.to_string()),
}

View File

@ -0,0 +1,39 @@
use std::fmt::{self, Debug, Display, Formatter};
use crate::{Chunk, DustString};
use super::Register;
#[derive(Debug)]
pub struct FunctionCall<'a> {
pub chunk: &'a Chunk,
pub ip: usize,
pub return_register: u8,
pub registers: Vec<Register>,
}
impl<'a> FunctionCall<'a> {
pub fn new(chunk: &'a Chunk, return_register: u8) -> Self {
Self {
chunk,
ip: 0,
return_register,
registers: vec![Register::Empty; chunk.register_count],
}
}
}
impl Display for FunctionCall<'_> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(
f,
"FunctionCall: {} | IP: {} | Registers: {}",
self.chunk
.name
.as_ref()
.unwrap_or(&DustString::from("anonymous")),
self.ip,
self.registers.len()
)
}
}

View File

@ -1,5 +1,5 @@
//! Virtual machine and errors
mod record;
mod function_call;
mod run_action;
mod stack;
mod thread;
@ -10,10 +10,10 @@ use std::{
thread::spawn,
};
pub use record::Record;
pub use function_call::FunctionCall;
pub(crate) use run_action::get_next_action;
pub use run_action::RunAction;
pub use stack::{FunctionCall, Stack};
pub use stack::Stack;
pub use thread::{Thread, ThreadData};
use tracing::{span, Level};
@ -85,15 +85,19 @@ impl Display for Register {
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum Pointer {
Stack(u8),
Register(u8),
Constant(u8),
Stack(usize, u8),
}
impl Display for Pointer {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Stack(index) => write!(f, "PR{}", index),
Self::Register(index) => write!(f, "PR{}", index),
Self::Constant(index) => write!(f, "PC{}", index),
Self::Stack(call_index, register_index) => {
write!(f, "PS{}R{}", call_index, register_index)
}
}
}
}

View File

@ -1,181 +0,0 @@
use std::mem::replace;
use tracing::trace;
use crate::{Argument, Chunk, DustString, Function, Span, Value};
use super::{Pointer, Register};
#[derive(Debug)]
pub struct Record<'a> {
pub ip: usize,
pub chunk: &'a Chunk,
registers: Vec<Register>,
}
impl<'a> Record<'a> {
pub fn new(chunk: &'a Chunk) -> Self {
Self {
ip: 0,
registers: vec![Register::Empty; chunk.register_count],
chunk,
}
}
pub fn name(&self) -> Option<&DustString> {
self.chunk.name.as_ref()
}
pub fn stack_size(&self) -> usize {
self.registers.len()
}
pub fn current_position(&self) -> Span {
self.chunk.positions[self.ip]
}
pub fn as_function(&self) -> Function {
self.chunk.as_function()
}
pub(crate) fn follow_pointer(&self, pointer: Pointer) -> &Value {
trace!("Follow pointer {pointer}");
match pointer {
Pointer::Stack(register_index) => self.open_register_unchecked(register_index),
Pointer::Constant(constant_index) => self.get_constant_unchecked(constant_index),
}
}
pub fn get_register_unchecked(&self, register_index: u8) -> &Register {
trace!("Get register R{register_index}");
let register_index = register_index as usize;
if cfg!(debug_assertions) {
&self.registers[register_index]
} else {
unsafe { self.registers.get_unchecked(register_index) }
}
}
pub fn set_register(&mut self, to_register: u8, register: Register) {
let to_register = to_register as usize;
self.registers[to_register] = register;
}
pub fn open_register_unchecked(&self, register_index: u8) -> &Value {
let register_index = register_index as usize;
let register = if cfg!(debug_assertions) {
&self.registers[register_index]
} else {
unsafe { self.registers.get_unchecked(register_index) }
};
match register {
Register::Value(value) => {
trace!("Register R{register_index} opened to value {value}");
value
}
Register::Pointer(pointer) => {
trace!("Open register R{register_index} opened to pointer {pointer}");
self.follow_pointer(*pointer)
}
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
}
}
pub fn open_register_allow_empty_unchecked(&self, register_index: u8) -> Option<&Value> {
trace!("Open register R{register_index}");
let register = self.get_register_unchecked(register_index);
match register {
Register::Value(value) => {
trace!("Register R{register_index} openned to value {value}");
Some(value)
}
Register::Pointer(pointer) => {
trace!("Open register R{register_index} openned to pointer {pointer}");
Some(self.follow_pointer(*pointer))
}
Register::Empty => None,
}
}
pub fn empty_register_or_clone_constant_unchecked(&mut self, register_index: u8) -> Value {
let register_index = register_index as usize;
assert!(
register_index < self.registers.len(),
"VM Error: Register index out of bounds"
);
let old_register = replace(&mut self.registers[register_index], Register::Empty);
match old_register {
Register::Value(value) => value,
Register::Pointer(pointer) => match pointer {
Pointer::Stack(register_index) => {
self.empty_register_or_clone_constant_unchecked(register_index)
}
Pointer::Constant(constant_index) => {
self.get_constant_unchecked(constant_index).clone()
}
},
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
}
}
pub fn clone_register_value_or_constant_unchecked(&self, register_index: u8) -> Value {
let register = self.get_register_unchecked(register_index);
match register {
Register::Value(value) => value.clone(),
Register::Pointer(pointer) => match pointer {
Pointer::Stack(register_index) => {
self.open_register_unchecked(*register_index).clone()
}
Pointer::Constant(constant_index) => {
self.get_constant_unchecked(*constant_index).clone()
}
},
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
}
}
/// DRY helper to get a value from an Argument
pub fn get_argument_unchecked(&self, argument: Argument) -> &Value {
match argument {
Argument::Constant(constant_index) => self.get_constant_unchecked(constant_index),
Argument::Register(register_index) => self.open_register_unchecked(register_index),
}
}
pub fn get_constant_unchecked(&self, constant_index: u8) -> &Value {
let constant_index = constant_index as usize;
if cfg!(debug_assertions) {
&self.chunk.constants[constant_index]
} else {
unsafe { self.chunk.constants.get_unchecked(constant_index) }
}
}
pub fn get_local_register(&self, local_index: u8) -> u8 {
let local_index = local_index as usize;
assert!(
local_index < self.chunk.locals.len(),
"VM Error: Local index out of bounds"
);
self.chunk.locals[local_index].register_index
}
}

View File

@ -7,10 +7,10 @@ use crate::{
Return, SetLocal, Subtract, Test, TestSet,
},
vm::FunctionCall,
AbstractList, Argument, ConcreteValue, DustString, Instruction, Type, Value,
AbstractList, Argument, ConcreteValue, Instruction, Type, Value,
};
use super::{thread::ThreadData, Pointer, Record, Register};
use super::{thread::ThreadData, Pointer, Register};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct RunAction {
@ -57,48 +57,46 @@ pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [
r#return,
];
pub(crate) fn get_next_action(record: &mut Record) -> RunAction {
let instruction = record.chunk.instructions[record.ip];
pub(crate) fn get_next_action(data: &mut ThreadData) -> RunAction {
let current_call = data.call_stack.last_mut_unchecked();
let instruction = current_call.chunk.instructions[current_call.ip];
let operation = instruction.operation();
let logic = RUNNER_LOGIC_TABLE[operation.0 as usize];
record.ip += 1;
current_call.ip += 1;
RunAction { logic, instruction }
}
pub fn point(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = &mut data.call_stack.last_mut_unchecked().record;
let Point { from, to } = instruction.into();
let from_register = record.get_register_unchecked(from);
let from_register = data.get_register_unchecked(from);
let from_register_is_empty = matches!(from_register, Register::Empty);
if !from_register_is_empty {
let register = Register::Pointer(Pointer::Stack(to));
let register = Register::Pointer(Pointer::Register(to));
record.set_register(from, register);
data.set_register(from, register);
}
data.next_action = get_next_action(record);
data.next_action = get_next_action(data);
false
}
pub fn close(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = &mut data.call_stack.last_mut_unchecked().record;
let Close { from, to } = instruction.into();
for register_index in from..to {
record.set_register(register_index, Register::Empty);
data.set_register(register_index, Register::Empty);
}
data.next_action = get_next_action(record);
data.next_action = get_next_action(data);
false
}
pub fn load_boolean(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = &mut data.call_stack.last_mut_unchecked().record;
let LoadBoolean {
destination,
value,
@ -107,19 +105,20 @@ pub fn load_boolean(instruction: Instruction, data: &mut ThreadData) -> bool {
let boolean = Value::Concrete(ConcreteValue::Boolean(value));
let register = Register::Value(boolean);
record.set_register(destination, register);
data.set_register(destination, register);
if jump_next {
record.ip += 1;
let current_call = data.call_stack.last_mut_unchecked();
current_call.ip += 1;
}
data.next_action = get_next_action(record);
data.next_action = get_next_action(data);
false
}
pub fn load_constant(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = &mut data.call_stack.last_mut_unchecked().record;
let LoadConstant {
destination,
constant_index,
@ -129,19 +128,20 @@ pub fn load_constant(instruction: Instruction, data: &mut ThreadData) -> bool {
trace!("Load constant {constant_index} into R{destination}");
record.set_register(destination, register);
data.set_register(destination, register);
if jump_next {
record.ip += 1;
let current_call = data.call_stack.last_mut_unchecked();
current_call.ip += 1;
}
data.next_action = get_next_action(record);
data.next_action = get_next_action(data);
false
}
pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = &mut data.call_stack.last_mut_unchecked().record;
let LoadList {
destination,
start_register,
@ -150,7 +150,7 @@ pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> bool {
let mut item_type = Type::Any;
for register_index in start_register..destination {
match record.get_register_unchecked(register_index) {
match data.get_register_unchecked(register_index) {
Register::Empty => continue,
Register::Value(value) => {
if item_type == Type::Any {
@ -159,12 +159,12 @@ pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> bool {
}
Register::Pointer(pointer) => {
if item_type == Type::Any {
item_type = record.follow_pointer(*pointer).r#type();
item_type = data.follow_pointer_unchecked(*pointer).r#type();
}
}
}
let pointer = Pointer::Stack(register_index);
let pointer = Pointer::Register(register_index);
item_pointers.push(pointer);
}
@ -175,122 +175,119 @@ pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> bool {
});
let register = Register::Value(list_value);
record.set_register(destination, register);
data.set_register(destination, register);
data.next_action = get_next_action(record);
data.next_action = get_next_action(data);
false
}
pub fn load_function(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = &mut data.call_stack.last_mut_unchecked().record;
let LoadFunction {
destination,
prototype_index,
} = instruction.into();
let prototype_index = prototype_index as usize;
let function = record.chunk.prototypes[prototype_index].as_function();
let current_call = data.call_stack.last_mut_unchecked();
let prototype = &current_call.chunk.prototypes[prototype_index];
let function = prototype.as_function();
let register = Register::Value(Value::Function(function));
record.set_register(destination, register);
data.set_register(destination, register);
data.next_action = get_next_action(record);
data.next_action = get_next_action(data);
false
}
pub fn load_self(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = &mut data.call_stack.last_mut_unchecked().record;
let LoadSelf { destination } = instruction.into();
let function = record.as_function();
let current_call = data.call_stack.last_mut_unchecked();
let prototype = &current_call.chunk;
let function = prototype.as_function();
let register = Register::Value(Value::Function(function));
record.set_register(destination, register);
data.set_register(destination, register);
data.next_action = get_next_action(record);
data.next_action = get_next_action(data);
false
}
pub fn get_local(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = &mut data.call_stack.last_mut_unchecked().record;
let GetLocal {
destination,
local_index,
} = instruction.into();
let local_register_index = record.get_local_register(local_index);
let register = Register::Pointer(Pointer::Stack(local_register_index));
let local_register_index = data.get_local_register(local_index);
let register = Register::Pointer(Pointer::Register(local_register_index));
record.set_register(destination, register);
data.set_register(destination, register);
data.next_action = get_next_action(record);
data.next_action = get_next_action(data);
false
}
pub fn set_local(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = &mut data.call_stack.last_mut_unchecked().record;
let SetLocal {
register_index,
local_index,
} = instruction.into();
let local_register_index = record.get_local_register(local_index);
let register = Register::Pointer(Pointer::Stack(register_index));
let local_register_index = data.get_local_register(local_index);
let register = Register::Pointer(Pointer::Register(register_index));
record.set_register(local_register_index, register);
data.set_register(local_register_index, register);
data.next_action = get_next_action(record);
data.next_action = get_next_action(data);
false
}
pub fn add(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = &mut data.call_stack.last_mut_unchecked().record;
let Add {
destination,
left,
right,
} = instruction.into();
let left = record.get_argument_unchecked(left);
let right = record.get_argument_unchecked(right);
let left = data.get_argument_unchecked(left);
let right = data.get_argument_unchecked(right);
let sum = left.add(right);
let register = Register::Value(sum);
record.set_register(destination, register);
data.set_register(destination, register);
data.next_action = get_next_action(record);
data.next_action = get_next_action(data);
false
}
pub fn subtract(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = &mut data.call_stack.last_mut_unchecked().record;
let Subtract {
destination,
left,
right,
} = instruction.into();
let left = record.get_argument_unchecked(left);
let right = record.get_argument_unchecked(right);
let left = data.get_argument_unchecked(left);
let right = data.get_argument_unchecked(right);
let difference = left.subtract(right);
let register = Register::Value(difference);
record.set_register(destination, register);
data.set_register(destination, register);
data.next_action = get_next_action(record);
data.next_action = get_next_action(data);
false
}
pub fn multiply(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = &mut data.call_stack.last_mut_unchecked().record;
let Multiply {
destination,
left,
right,
} = instruction.into();
let left = record.get_argument_unchecked(left);
let right = record.get_argument_unchecked(right);
let left = data.get_argument_unchecked(left);
let right = data.get_argument_unchecked(right);
let product = match (left, right) {
(Value::Concrete(left), Value::Concrete(right)) => match (left, right) {
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
@ -302,22 +299,21 @@ pub fn multiply(instruction: Instruction, data: &mut ThreadData) -> bool {
};
let register = Register::Value(product);
record.set_register(destination, register);
data.set_register(destination, register);
data.next_action = get_next_action(record);
data.next_action = get_next_action(data);
false
}
pub fn divide(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = &mut data.call_stack.last_mut_unchecked().record;
let Divide {
destination,
left,
right,
} = instruction.into();
let left = record.get_argument_unchecked(left);
let right = record.get_argument_unchecked(right);
let left = data.get_argument_unchecked(left);
let right = data.get_argument_unchecked(right);
let quotient = match (left, right) {
(Value::Concrete(left), Value::Concrete(right)) => match (left, right) {
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
@ -329,22 +325,21 @@ pub fn divide(instruction: Instruction, data: &mut ThreadData) -> bool {
};
let register = Register::Value(quotient);
record.set_register(destination, register);
data.set_register(destination, register);
data.next_action = get_next_action(record);
data.next_action = get_next_action(data);
false
}
pub fn modulo(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = &mut data.call_stack.last_mut_unchecked().record;
let Modulo {
destination,
left,
right,
} = instruction.into();
let left = record.get_argument_unchecked(left);
let right = record.get_argument_unchecked(right);
let left = data.get_argument_unchecked(left);
let right = data.get_argument_unchecked(right);
let remainder = match (left, right) {
(Value::Concrete(left), Value::Concrete(right)) => match (left, right) {
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
@ -356,20 +351,19 @@ pub fn modulo(instruction: Instruction, data: &mut ThreadData) -> bool {
};
let register = Register::Value(remainder);
record.set_register(destination, register);
data.set_register(destination, register);
data.next_action = get_next_action(record);
data.next_action = get_next_action(data);
false
}
pub fn test(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = &mut data.call_stack.last_mut_unchecked().record;
let Test {
operand_register,
test_value,
} = instruction.into();
let value = record.open_register_unchecked(operand_register);
let value = data.open_register_unchecked(operand_register);
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
*boolean
} else {
@ -377,22 +371,23 @@ pub fn test(instruction: Instruction, data: &mut ThreadData) -> bool {
};
if boolean == test_value {
record.ip += 1;
let current_call = data.call_stack.last_mut_unchecked();
current_call.ip += 1;
}
data.next_action = get_next_action(record);
data.next_action = get_next_action(data);
false
}
pub fn test_set(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = &mut data.call_stack.last_mut_unchecked().record;
let TestSet {
destination,
argument,
test_value,
} = instruction.into();
let value = record.get_argument_unchecked(argument);
let value = data.get_argument_unchecked(argument);
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
*boolean
} else {
@ -403,179 +398,161 @@ pub fn test_set(instruction: Instruction, data: &mut ThreadData) -> bool {
} else {
let pointer = match argument {
Argument::Constant(constant_index) => Pointer::Constant(constant_index),
Argument::Register(register_index) => Pointer::Stack(register_index),
Argument::Register(register_index) => Pointer::Register(register_index),
};
let register = Register::Pointer(pointer);
record.set_register(destination, register);
data.set_register(destination, register);
}
data.next_action = get_next_action(record);
data.next_action = get_next_action(data);
false
}
pub fn equal(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = &mut data.call_stack.last_mut_unchecked().record;
let Equal { value, left, right } = instruction.into();
let left = record.get_argument_unchecked(left);
let right = record.get_argument_unchecked(right);
let left = data.get_argument_unchecked(left);
let right = data.get_argument_unchecked(right);
let is_equal = left.equals(right);
if is_equal == value {
record.ip += 1;
let current_call = data.call_stack.last_mut_unchecked();
current_call.ip += 1;
}
data.next_action = get_next_action(record);
data.next_action = get_next_action(data);
false
}
pub fn less(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = &mut data.call_stack.last_mut_unchecked().record;
let Less { value, left, right } = instruction.into();
let left = record.get_argument_unchecked(left);
let right = record.get_argument_unchecked(right);
let left = data.get_argument_unchecked(left);
let right = data.get_argument_unchecked(right);
let is_less = left < right;
if is_less == value {
record.ip += 1;
let current_call = data.call_stack.last_mut_unchecked();
current_call.ip += 1;
}
data.next_action = get_next_action(record);
data.next_action = get_next_action(data);
false
}
pub fn less_equal(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = &mut data.call_stack.last_mut_unchecked().record;
let LessEqual { value, left, right } = instruction.into();
let left = record.get_argument_unchecked(left);
let right = record.get_argument_unchecked(right);
let left = data.get_argument_unchecked(left);
let right = data.get_argument_unchecked(right);
let is_less_or_equal = left <= right;
if is_less_or_equal == value {
record.ip += 1;
let current_call = data.call_stack.last_mut_unchecked();
current_call.ip += 1;
}
data.next_action = get_next_action(record);
data.next_action = get_next_action(data);
false
}
pub fn negate(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = &mut data.call_stack.last_mut_unchecked().record;
let Negate {
destination,
argument,
} = instruction.into();
let argument = record.get_argument_unchecked(argument);
let argument = data.get_argument_unchecked(argument);
let negated = argument.negate();
let register = Register::Value(negated);
record.set_register(destination, register);
data.set_register(destination, register);
data.next_action = get_next_action(record);
data.next_action = get_next_action(data);
false
}
pub fn not(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = &mut data.call_stack.last_mut_unchecked().record;
let Not {
destination,
argument,
} = instruction.into();
let argument = record.get_argument_unchecked(argument);
let argument = data.get_argument_unchecked(argument);
let not = match argument {
Value::Concrete(ConcreteValue::Boolean(boolean)) => ConcreteValue::Boolean(!boolean),
_ => panic!("VM Error: Expected boolean value for NOT operation"),
};
let register = Register::Value(Value::Concrete(not));
record.set_register(destination, register);
data.set_register(destination, register);
data.next_action = get_next_action(record);
data.next_action = get_next_action(data);
false
}
pub fn jump(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = &mut data.call_stack.last_mut_unchecked().record;
let Jump {
offset,
is_positive,
} = instruction.into();
let offset = offset as usize;
let current_call = data.call_stack.last_mut_unchecked();
if is_positive {
record.ip += offset;
current_call.ip += offset;
} else {
record.ip -= offset + 1;
current_call.ip -= offset + 1;
}
data.next_action = get_next_action(record);
data.next_action = get_next_action(data);
false
}
pub fn call(instruction: Instruction, data: &mut ThreadData) -> bool {
let current_call = data.call_stack.pop_unchecked();
let Call {
destination: return_register,
function_register,
argument_count,
is_recursive,
} = instruction.into();
let function = current_call
.record
.open_register_unchecked(function_register)
.as_function()
.unwrap();
let current_call = data.call_stack.last_unchecked();
let first_argument_register = return_register - argument_count;
let prototype = if is_recursive {
current_call.record.chunk
current_call.chunk
} else {
&current_call.record.chunk.prototypes[function.prototype_index as usize]
};
let mut next_call = FunctionCall {
name: prototype.name.clone(),
return_register,
ip: current_call.ip,
record: Record::new(prototype),
let function = data
.open_register_unchecked(function_register)
.as_function()
.unwrap();
&current_call.chunk.prototypes[function.prototype_index as usize]
};
let mut next_call = FunctionCall::new(prototype, return_register);
let mut argument_index = 0;
for register_index in first_argument_register..return_register {
let value_option = current_call
.record
.open_register_allow_empty_unchecked(register_index);
let value_option = data.open_register_allow_empty_unchecked(register_index);
let argument = if let Some(value) = value_option {
value.clone()
} else {
continue;
};
trace!(
"Passing argument \"{argument}\" to {}",
function
.name
.clone()
.unwrap_or_else(|| DustString::from("anonymous"))
);
next_call
.record
.set_register(argument_index, Register::Value(argument));
next_call.registers[argument_index] = Register::Value(argument);
argument_index += 1;
}
data.next_action = get_next_action(&mut next_call.record);
data.call_stack.push(current_call);
data.call_stack.push(next_call);
data.next_action = get_next_action(data);
false
}
@ -598,30 +575,24 @@ pub fn r#return(instruction: Instruction, data: &mut ThreadData) -> bool {
should_return_value,
return_register,
} = instruction.into();
let mut current_call = if data.call_stack.len() == 1 {
let (destination, return_value) = if data.call_stack.len() == 1 {
if should_return_value {
data.return_value_index = Some(return_register);
};
return true;
} else {
data.call_stack.pop_unchecked()
let return_value = data.empty_register_or_clone_constant_unchecked(return_register);
let destination = data.call_stack.pop_unchecked().return_register;
(destination, return_value)
};
let outer_call = data.call_stack.last_mut_unchecked();
if should_return_value {
let return_value = current_call
.record
.empty_register_or_clone_constant_unchecked(return_register);
let destination = current_call.return_register;
outer_call
.record
.set_register(destination, Register::Value(return_value));
data.set_register(destination, Register::Value(return_value));
}
data.next_action = get_next_action(&mut outer_call.record);
data.next_action = get_next_action(data);
false
}

View File

@ -3,9 +3,7 @@ use std::{
ops::{Index, IndexMut, Range},
};
use crate::DustString;
use super::Record;
use super::FunctionCall;
#[derive(Clone, PartialEq)]
pub struct Stack<T> {
@ -43,6 +41,16 @@ impl<T> Stack<T> {
}
}
pub fn get_unchecked_mut(&mut self, index: usize) -> &mut T {
if cfg!(debug_assertions) {
assert!(index < self.len(), "Stack underflow");
&mut self.items[index]
} else {
unsafe { self.items.get_unchecked_mut(index) }
}
}
pub fn push(&mut self, item: T) {
self.items.push(item);
}
@ -118,36 +126,12 @@ impl<T: Debug> Debug for Stack<T> {
impl Display for Stack<FunctionCall<'_>> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
writeln!(f, "-- DUST CALL STACK --")?;
writeln!(f, "----- DUST CALL STACK -----")?;
for function_call in self.items.iter().rev() {
writeln!(f, "{function_call}")?;
for (index, function_call) in self.items.iter().enumerate().rev() {
writeln!(f, "{index:02} | {function_call}")?;
}
writeln!(f, "--")
}
}
#[derive(Debug)]
pub struct FunctionCall<'a> {
pub name: Option<DustString>,
pub return_register: u8,
pub ip: usize,
pub record: Record<'a>,
}
impl Display for FunctionCall<'_> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let FunctionCall {
name,
return_register,
..
} = self;
let name = name
.as_ref()
.map(|name| name.as_str())
.unwrap_or("anonymous");
write!(f, "{name} (Return register: {return_register})")
write!(f, "---------------------------")
}
}

View File

@ -1,8 +1,10 @@
use std::mem::replace;
use tracing::{info, trace};
use crate::{vm::FunctionCall, Chunk, DustString, Value};
use crate::{vm::FunctionCall, Argument, Chunk, DustString, Span, Value};
use super::{record::Record, RunAction, Stack};
use super::{Pointer, Register, RunAction, Stack};
pub struct Thread {
chunk: Chunk,
@ -23,12 +25,7 @@ impl Thread {
);
let mut call_stack = Stack::with_capacity(self.chunk.prototypes.len() + 1);
let main_call = FunctionCall {
name: self.chunk.name.clone(),
return_register: 0, // Never used, the main function's return is the thread's return
ip: 0,
record: Record::new(&self.chunk),
};
let main_call = FunctionCall::new(&self.chunk, 0);
call_stack.push(main_call);
@ -49,11 +46,8 @@ impl Thread {
if should_end {
let return_value = if let Some(register_index) = thread_data.return_value_index {
let value = thread_data
.call_stack
.last_mut_unchecked()
.record
.empty_register_or_clone_constant_unchecked(register_index);
let value =
thread_data.empty_register_or_clone_constant_unchecked(register_index);
Some(value)
} else {
@ -72,3 +66,207 @@ pub struct ThreadData<'a> {
pub next_action: RunAction,
pub return_value_index: Option<u8>,
}
impl ThreadData<'_> {
pub fn current_position(&self) -> Span {
let current_call = self.call_stack.last_unchecked();
current_call.chunk.positions[current_call.ip]
}
pub(crate) fn follow_pointer_unchecked(&self, pointer: Pointer) -> &Value {
trace!("Follow pointer {pointer}");
match pointer {
Pointer::Register(register_index) => self.open_register_unchecked(register_index),
Pointer::Constant(constant_index) => self.get_constant_unchecked(constant_index),
Pointer::Stack(stack_index, register_index) => unsafe {
let register = self
.call_stack
.get_unchecked(stack_index)
.registers
.get_unchecked(register_index as usize);
match register {
Register::Value(value) => value,
Register::Pointer(pointer) => self.follow_pointer_unchecked(*pointer),
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
}
},
}
}
pub fn get_register_unchecked(&self, register_index: u8) -> &Register {
trace!("Get register R{register_index}");
let register_index = register_index as usize;
if cfg!(debug_assertions) {
&self.call_stack.last_unchecked().registers[register_index]
} else {
unsafe {
self.call_stack
.last_unchecked()
.registers
.get_unchecked(register_index)
}
}
}
pub fn set_register(&mut self, to_register: u8, register: Register) {
let to_register = to_register as usize;
self.call_stack.last_mut_unchecked().registers[to_register] = register;
}
pub fn open_register_unchecked(&self, register_index: u8) -> &Value {
let register_index = register_index as usize;
let register = if cfg!(debug_assertions) {
&self.call_stack.last_unchecked().registers[register_index]
} else {
unsafe {
self.call_stack
.last_unchecked()
.registers
.get_unchecked(register_index)
}
};
match register {
Register::Value(value) => {
trace!("Register R{register_index} opened to value {value}");
value
}
Register::Pointer(pointer) => {
trace!("Open register R{register_index} opened to pointer {pointer}");
self.follow_pointer_unchecked(*pointer)
}
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
}
}
pub fn open_register_allow_empty_unchecked(&self, register_index: u8) -> Option<&Value> {
trace!("Open register R{register_index}");
let register = self.get_register_unchecked(register_index);
match register {
Register::Value(value) => {
trace!("Register R{register_index} openned to value {value}");
Some(value)
}
Register::Pointer(pointer) => {
trace!("Open register R{register_index} openned to pointer {pointer}");
Some(self.follow_pointer_unchecked(*pointer))
}
Register::Empty => None,
}
}
pub fn empty_register_or_clone_constant_unchecked(&mut self, register_index: u8) -> Value {
let register_index = register_index as usize;
let old_register = replace(
&mut self.call_stack.last_mut_unchecked().registers[register_index],
Register::Empty,
);
match old_register {
Register::Value(value) => value,
Register::Pointer(pointer) => match pointer {
Pointer::Register(register_index) => {
self.empty_register_or_clone_constant_unchecked(register_index)
}
Pointer::Constant(constant_index) => {
self.get_constant_unchecked(constant_index).clone()
}
Pointer::Stack(stack_index, register_index) => {
let call = self.call_stack.get_unchecked_mut(stack_index);
let old_register = replace(
&mut call.registers[register_index as usize],
Register::Empty,
);
match old_register {
Register::Value(value) => value,
Register::Pointer(pointer) => {
self.follow_pointer_unchecked(pointer).clone()
}
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
}
}
},
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
}
}
pub fn clone_register_value_or_constant_unchecked(&self, register_index: u8) -> Value {
let register = self.get_register_unchecked(register_index);
match register {
Register::Value(value) => value.clone(),
Register::Pointer(pointer) => match pointer {
Pointer::Register(register_index) => {
self.open_register_unchecked(*register_index).clone()
}
Pointer::Constant(constant_index) => {
self.get_constant_unchecked(*constant_index).clone()
}
Pointer::Stack(stack_index, register_index) => {
let call = self.call_stack.get_unchecked(*stack_index);
let register = &call.registers[*register_index as usize];
match register {
Register::Value(value) => value.clone(),
Register::Pointer(pointer) => {
self.follow_pointer_unchecked(*pointer).clone()
}
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
}
}
},
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
}
}
/// DRY helper to get a value from an Argument
pub fn get_argument_unchecked(&self, argument: Argument) -> &Value {
match argument {
Argument::Constant(constant_index) => self.get_constant_unchecked(constant_index),
Argument::Register(register_index) => self.open_register_unchecked(register_index),
}
}
pub fn get_constant_unchecked(&self, constant_index: u8) -> &Value {
let constant_index = constant_index as usize;
if cfg!(debug_assertions) {
&self.call_stack.last().unwrap().chunk.constants[constant_index]
} else {
unsafe {
self.call_stack
.last_unchecked()
.chunk
.constants
.get_unchecked(constant_index)
}
}
}
pub fn get_local_register(&self, local_index: u8) -> u8 {
let local_index = local_index as usize;
let chunk = self.call_stack.last_unchecked().chunk;
assert!(
local_index < chunk.locals.len(),
"VM Error: Local index out of bounds"
);
chunk.locals[local_index].register_index
}
}