Consolidate Record type into the ThreadData type
This commit is contained in:
parent
9d370aea2a
commit
d9a0f6866b
@ -3,18 +3,20 @@ 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_str(&string);
|
||||
message.push('\n');
|
||||
}
|
||||
}
|
||||
|
||||
panic!("{}", message)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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()),
|
||||
}
|
||||
|
39
dust-lang/src/vm/function_call.rs
Normal file
39
dust-lang/src/vm/function_call.rs
Normal 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()
|
||||
)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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 = ¤t_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 = ¤t_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
|
||||
let current_call = data.call_stack.last_unchecked();
|
||||
let first_argument_register = return_register - argument_count;
|
||||
let prototype = if is_recursive {
|
||||
current_call.chunk
|
||||
} else {
|
||||
let function = data
|
||||
.open_register_unchecked(function_register)
|
||||
.as_function()
|
||||
.unwrap();
|
||||
let first_argument_register = return_register - argument_count;
|
||||
let prototype = if is_recursive {
|
||||
current_call.record.chunk
|
||||
} else {
|
||||
¤t_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),
|
||||
|
||||
¤t_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
|
||||
}
|
||||
|
@ -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, "---------------------------")
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user