1
0

Begin value overhaul

This commit is contained in:
Jeff 2025-01-30 20:48:17 -05:00
parent 3fcbde59e5
commit a89b927a80
11 changed files with 390 additions and 685 deletions

4
Cargo.lock generated
View File

@ -349,6 +349,7 @@ dependencies = [
"rand",
"serde",
"serde_json",
"smallvec",
"smartstring",
"tracing",
]
@ -805,6 +806,9 @@ name = "smallvec"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
dependencies = [
"serde",
]
[[package]]
name = "smartstring"

View File

@ -22,6 +22,7 @@ smartstring = { version = "1.0.1", features = [
], default-features = false }
tracing = "0.1.41"
crossbeam-channel = "0.5.14"
smallvec = { version = "1.13.2", features = ["serde", "const_generics"] }
[dev-dependencies]
criterion = { version = "0.3.4", features = ["html_reports"] }

View File

@ -27,7 +27,7 @@ use std::sync::Arc;
use serde::{Deserialize, Serialize};
use crate::{DustString, Function, FunctionType, Instruction, Span, Value};
use crate::{DustString, Function, FunctionType, Instruction, Span};
/// Representation of a Dust program or function.
///
@ -39,8 +39,14 @@ pub struct Chunk {
pub(crate) instructions: Vec<Instruction>,
pub(crate) positions: Vec<Span>,
pub(crate) constants: Vec<Value>,
pub(crate) constant_characters: Vec<char>,
pub(crate) constant_floats: Vec<f64>,
pub(crate) constant_integers: Vec<i64>,
pub(crate) constant_strings: Vec<DustString>,
pub(crate) locals: Vec<Local>,
pub(crate) prototypes: Vec<Arc<Chunk>>,
pub(crate) register_count: usize,
@ -48,29 +54,6 @@ pub struct Chunk {
}
impl Chunk {
#[cfg(any(test, debug_assertions))]
pub fn with_data(
name: Option<DustString>,
r#type: FunctionType,
instructions: impl Into<Vec<Instruction>>,
positions: impl Into<Vec<Span>>,
constants: impl Into<Vec<Value>>,
locals: impl Into<Vec<Local>>,
prototypes: impl IntoIterator<Item = Chunk>,
) -> Self {
Self {
name,
r#type,
instructions: instructions.into(),
positions: positions.into(),
constants: constants.into(),
locals: locals.into(),
prototypes: prototypes.into_iter().map(Arc::new).collect(),
register_count: 0,
prototype_index: 0,
}
}
pub fn as_function(&self) -> Function {
Function {
name: self.name.clone(),
@ -125,7 +108,11 @@ impl PartialEq for Chunk {
self.name == other.name
&& self.r#type == other.r#type
&& self.instructions == other.instructions
&& self.constants == other.constants
&& self.positions == other.positions
&& self.constant_characters == other.constant_characters
&& self.constant_floats == other.constant_floats
&& self.constant_integers == other.constant_integers
&& self.constant_strings == other.constant_strings
&& self.locals == other.locals
&& self.prototypes == other.prototypes
}

View File

@ -42,15 +42,13 @@ pub mod vm;
pub use crate::chunk::{Chunk, Disassembler, Local, Scope};
pub use crate::compiler::{CompileError, Compiler, compile};
pub use crate::dust_error::{AnnotatedError, DustError};
pub use crate::instruction::{Operand, Instruction, Operation};
pub use crate::instruction::{Instruction, Operand, Operation};
pub use crate::lexer::{LexError, Lexer, lex};
pub use crate::native_function::{NativeFunction, NativeFunctionError};
pub use crate::token::{Token, TokenKind, TokenOwned};
pub use crate::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict};
pub use crate::value::{
AbstractList, ConcreteValue, DustString, Function, RangeValue, Value, ValueError,
};
pub use crate::vm::{Pointer, Vm, run};
pub use crate::value::{DustString, Function, RangeValue, Value, ValueError};
pub use crate::vm::{Vm, run};
use std::fmt::Display;

View File

@ -1,238 +1,40 @@
//! Runtime values used by the VM.
mod abstract_list;
mod concrete_value;
mod function;
mod range_value;
pub use abstract_list::AbstractList;
pub use concrete_value::{ConcreteValue, DustString};
pub use function::Function;
pub use range_value::RangeValue;
use serde::{Deserialize, Serialize};
use smartstring::{LazyCompact, SmartString};
use std::fmt::{self, Debug, Display, Formatter};
use crate::{Type, vm::ThreadData};
pub type DustString = SmartString<LazyCompact>;
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
pub enum Value {
Concrete(ConcreteValue),
#[serde(skip)]
AbstractList(AbstractList),
Boolean(bool),
Byte(u8),
Character(char),
Float(f64),
Integer(i64),
String(DustString),
List(Vec<Value>),
#[serde(skip)]
Function(Function),
}
impl Value {
pub fn boolean(boolean: bool) -> Self {
Value::Concrete(ConcreteValue::Boolean(boolean))
}
pub fn byte(byte: u8) -> Self {
Value::Concrete(ConcreteValue::Byte(byte))
}
pub fn character(character: char) -> Self {
Value::Concrete(ConcreteValue::Character(character))
}
pub fn float(float: f64) -> Self {
Value::Concrete(ConcreteValue::Float(float))
}
pub fn integer(integer: i64) -> Self {
Value::Concrete(ConcreteValue::Integer(integer))
}
pub fn string(string: impl Into<DustString>) -> Self {
Value::Concrete(ConcreteValue::String(string.into()))
}
pub fn as_boolean(&self) -> Option<bool> {
if let Value::Concrete(ConcreteValue::Boolean(boolean)) = self {
Some(*boolean)
} else {
None
}
}
pub fn as_byte(&self) -> Option<u8> {
if let Value::Concrete(ConcreteValue::Byte(byte)) = self {
Some(*byte)
} else {
None
}
}
pub fn as_character(&self) -> Option<char> {
if let Value::Concrete(ConcreteValue::Character(character)) = self {
Some(*character)
} else {
None
}
}
pub fn as_float(&self) -> Option<f64> {
if let Value::Concrete(ConcreteValue::Float(float)) = self {
Some(*float)
} else {
None
}
}
pub fn as_function(&self) -> Option<&Function> {
if let Value::Function(function) = self {
Some(function)
} else {
None
}
}
pub fn as_integer(&self) -> Option<i64> {
if let Value::Concrete(ConcreteValue::Integer(integer)) = self {
Some(*integer)
} else {
None
}
}
pub fn as_string(&self) -> Option<&DustString> {
if let Value::Concrete(ConcreteValue::String(value)) = self {
Some(value)
} else {
None
}
}
pub fn is_string(&self) -> bool {
matches!(self, Value::Concrete(ConcreteValue::String(_)))
}
pub fn is_function(&self) -> bool {
matches!(self, Value::Function(_))
}
pub fn r#type(&self) -> Type {
match self {
Value::Concrete(concrete_value) => concrete_value.r#type(),
Value::AbstractList(AbstractList { item_type, .. }) => {
Type::List(Box::new(item_type.clone()))
}
Value::Function(Function { r#type, .. }) => Type::Function(Box::new(r#type.clone())),
}
}
pub fn add(&self, other: &Value) -> Value {
let sum = match (self, other) {
(Value::Concrete(left), Value::Concrete(right)) => left.add(right),
_ => panic!("{}", ValueError::CannotAdd(self.clone(), other.clone())),
};
Value::Concrete(sum)
}
pub fn subtract(&self, other: &Value) -> Value {
let difference = match (self, other) {
(Value::Concrete(left), Value::Concrete(right)) => left.subtract(right),
_ => panic!(
"{}",
ValueError::CannotSubtract(self.clone(), other.clone())
),
};
Value::Concrete(difference)
}
pub fn multiply(&self, other: &Value) -> Result<Value, ValueError> {
match (self, other) {
(Value::Concrete(left), Value::Concrete(right)) => {
left.multiply(right).map(Value::Concrete)
}
_ => Err(ValueError::CannotMultiply(
self.to_owned(),
other.to_owned(),
)),
}
}
pub fn divide(&self, other: &Value) -> Result<Value, ValueError> {
match (self, other) {
(Value::Concrete(left), Value::Concrete(right)) => {
left.divide(right).map(Value::Concrete)
}
_ => Err(ValueError::CannotDivide(self.to_owned(), other.to_owned())),
}
}
pub fn modulo(&self, other: &Value) -> Result<Value, ValueError> {
match (self, other) {
(Value::Concrete(left), Value::Concrete(right)) => {
left.modulo(right).map(Value::Concrete)
}
_ => Err(ValueError::CannotModulo(self.to_owned(), other.to_owned())),
}
}
pub fn negate(&self) -> Value {
let concrete = match self {
Value::Concrete(concrete_value) => concrete_value.negate(),
_ => panic!("{}", ValueError::CannotNegate(self.clone())),
};
Value::Concrete(concrete)
}
pub fn not(&self) -> Result<Value, ValueError> {
match self {
Value::Concrete(concrete_value) => concrete_value.not().map(Value::Concrete),
_ => Err(ValueError::CannotNot(self.to_owned())),
}
}
pub fn equals(&self, other: &Value) -> bool {
match (self, other) {
(Value::Concrete(left), Value::Concrete(right)) => left.equals(right),
_ => panic!(
"{}",
ValueError::CannotCompare(self.to_owned(), other.to_owned())
),
}
}
pub fn less(&self, other: &Value) -> Result<Value, ValueError> {
match (self, other) {
(Value::Concrete(left), Value::Concrete(right)) => {
left.less_than(right).map(Value::Concrete)
}
_ => Err(ValueError::CannotCompare(self.to_owned(), other.to_owned())),
}
}
pub fn less_than_or_equals(&self, other: &Value) -> Result<Value, ValueError> {
match (self, other) {
(Value::Concrete(left), Value::Concrete(right)) => {
left.less_than_or_equals(right).map(Value::Concrete)
}
_ => Err(ValueError::CannotCompare(self.to_owned(), other.to_owned())),
}
}
pub fn display(&self, data: &ThreadData) -> DustString {
match self {
Value::AbstractList(list) => list.display(data),
Value::Concrete(concrete_value) => concrete_value.display(),
Value::Function(function) => DustString::from(function.to_string()),
}
}
}
impl Display for Value {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Value::Concrete(concrete_value) => write!(f, "{concrete_value}"),
Value::AbstractList(list) => write!(f, "{list}"),
Value::Boolean(boolean) => write!(f, "{boolean}"),
Value::Byte(byte) => write!(f, "{byte}"),
Value::Character(character) => write!(f, "{character}"),
Value::Float(float) => write!(f, "{float}"),
Value::Integer(integer) => write!(f, "{integer}"),
Value::String(string) => write!(f, "{string}"),
Value::List(list) => write!(f, "{list:?}"),
Value::Function(function) => write!(f, "{function}"),
}
}

View File

@ -7,27 +7,27 @@ use crate::{
LoadConstant, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not, Point,
Return, SetLocal, Subtract, Test, TestSet, TypeCode,
},
vm::FunctionCall,
vm::CallFrame,
};
use super::{Pointer, Register, thread::ThreadData};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct RunAction {
pub struct Action {
pub logic: RunnerLogic,
pub instruction: Instruction,
}
impl From<Instruction> for RunAction {
impl From<Instruction> for Action {
fn from(instruction: Instruction) -> Self {
let operation = instruction.operation();
let logic = RUNNER_LOGIC_TABLE[operation.0 as usize];
RunAction { logic, instruction }
Action { logic, instruction }
}
}
pub type RunnerLogic = fn(Instruction, &mut ThreadData) -> bool;
pub type RunnerLogic = fn(Instruction, &mut Thread) -> bool;
pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [
point,
@ -57,7 +57,7 @@ pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [
r#return,
];
pub(crate) fn get_next_action(data: &mut ThreadData) -> RunAction {
pub(crate) fn get_next_action(data: &mut ThreadData) -> Action {
let current_call = data.call_stack.last_mut_unchecked();
let instruction = current_call.chunk.instructions[current_call.ip];
let operation = instruction.operation();
@ -65,7 +65,7 @@ pub(crate) fn get_next_action(data: &mut ThreadData) -> RunAction {
current_call.ip += 1;
RunAction { logic, instruction }
Action { logic, instruction }
}
pub fn point(instruction: Instruction, data: &mut ThreadData) -> bool {
@ -782,7 +782,7 @@ pub fn call(instruction: Instruction, data: &mut ThreadData) -> bool {
current_call.chunk.prototypes[function.prototype_index as usize].clone()
};
let mut next_call = FunctionCall::new(prototype, return_register);
let mut next_call = CallFrame::new(prototype, return_register);
let mut argument_index = 0;
for register_index in first_argument_register..return_register {

View File

@ -0,0 +1,73 @@
use std::{
fmt::{self, Debug, Display, Formatter},
sync::Arc,
};
use smallvec::{SmallVec, smallvec};
use crate::{Chunk, DustString};
#[derive(Debug)]
pub struct CallFrame {
pub chunk: Arc<Chunk>,
pub ip: usize,
pub return_register: u16,
pub registers: RegisterTable,
}
impl CallFrame {
pub fn new(chunk: Arc<Chunk>, return_register: u16) -> Self {
let register_count = chunk.register_count;
Self {
chunk,
ip: 0,
return_register,
registers: RegisterTable::new(),
}
}
}
impl Display for CallFrame {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(
f,
"FunctionCall: {} | IP: {}",
self.chunk
.name
.as_ref()
.unwrap_or(&DustString::from("anonymous")),
self.ip,
)
}
}
#[derive(Debug)]
pub struct RegisterTable {
pub booleans: SmallVec<[Register<bool>; 64]>,
pub bytes: SmallVec<[Register<u8>; 64]>,
pub characters: SmallVec<[Register<char>; 64]>,
pub floats: SmallVec<[Register<f64>; 64]>,
pub integers: SmallVec<[Register<i64>; 64]>,
pub strings: SmallVec<[Register<DustString>; 64]>,
}
impl RegisterTable {
pub fn new() -> Self {
Self {
booleans: smallvec![Register::Empty; 64],
bytes: smallvec![Register::Empty; 64],
characters: smallvec![Register::Empty; 64],
floats: smallvec![Register::Empty; 64],
integers: smallvec![Register::Empty; 64],
strings: smallvec![Register::Empty; 64],
}
}
}
#[derive(Debug, Clone)]
pub enum Register<T> {
Empty,
Value(T),
Pointer(*const T),
}

View File

@ -1,44 +0,0 @@
use std::{
fmt::{self, Debug, Display, Formatter},
sync::Arc,
};
use crate::{Chunk, DustString};
use super::Register;
#[derive(Debug)]
pub struct FunctionCall {
pub chunk: Arc<Chunk>,
pub ip: usize,
pub return_register: u16,
pub registers: Vec<Register>,
}
impl FunctionCall {
pub fn new(chunk: Arc<Chunk>, return_register: u16) -> Self {
let register_count = chunk.register_count;
Self {
chunk,
ip: 0,
return_register,
registers: vec![Register::Empty; 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,7 +1,6 @@
//! Virtual machine and errors
mod function_call;
mod run_action;
mod stack;
mod action;
mod call_frame;
mod thread;
use std::{
@ -10,11 +9,10 @@ use std::{
thread::Builder,
};
pub use function_call::FunctionCall;
pub use run_action::RunAction;
pub(crate) use run_action::get_next_action;
pub use stack::Stack;
pub use thread::{Thread, ThreadData};
pub use action::Action;
pub(crate) use action::get_next_action;
pub use call_frame::{CallFrame, Register, RegisterTable};
pub use thread::Thread;
use crossbeam_channel::bounded;
use tracing::{Level, span};
@ -46,12 +44,13 @@ impl Vm {
.as_ref()
.map(|name| name.to_string())
.unwrap_or_else(|| "anonymous".to_string());
let mut main_thread = Thread::new(Arc::new(self.main_chunk));
let (tx, rx) = bounded(1);
let main_chunk = Arc::new(self.main_chunk);
Builder::new()
.name(thread_name)
.spawn(move || {
let mut main_thread = Thread::new(main_chunk);
let value_option = main_thread.run();
let _ = tx.send(value_option);
})
@ -62,39 +61,3 @@ impl Vm {
rx.recv().unwrap_or(None)
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum Register {
Empty,
Value(Value),
Pointer(Pointer),
}
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::Pointer(pointer) => write!(f, "{}", pointer),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum Pointer {
Register(u16),
Constant(u16),
Stack(usize, u16),
}
impl Display for Pointer {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
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,137 +0,0 @@
use std::{
fmt::{self, Debug, Display, Formatter},
ops::{Index, IndexMut, Range},
};
use super::FunctionCall;
#[derive(Clone, PartialEq)]
pub struct Stack<T> {
items: Vec<T>,
}
impl<T> Stack<T> {
pub fn new() -> Self {
Stack {
items: Vec::with_capacity(1),
}
}
pub fn with_capacity(capacity: usize) -> Self {
Stack {
items: Vec::with_capacity(capacity),
}
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
pub fn len(&self) -> usize {
self.items.len()
}
pub fn get_unchecked(&self, index: usize) -> &T {
if cfg!(debug_assertions) {
assert!(index < self.len(), "Stack underflow");
&self.items[index]
} else {
unsafe { self.items.get_unchecked(index) }
}
}
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);
}
pub fn pop(&mut self) -> Option<T> {
self.items.pop()
}
pub fn last(&self) -> Option<&T> {
self.items.last()
}
pub fn last_mut(&mut self) -> Option<&mut T> {
self.items.last_mut()
}
pub fn pop_unchecked(&mut self) -> T {
if cfg!(debug_assertions) {
assert!(!self.is_empty(), "Stack underflow");
self.items.pop().unwrap()
} else {
unsafe { self.items.pop().unwrap_unchecked() }
}
}
pub fn last_unchecked(&self) -> &T {
if cfg!(debug_assertions) {
assert!(!self.is_empty(), "Stack underflow");
self.items.last().unwrap()
} else {
unsafe { self.items.last().unwrap_unchecked() }
}
}
pub fn last_mut_unchecked(&mut self) -> &mut T {
if cfg!(debug_assertions) {
assert!(!self.is_empty(), "Stack underflow");
self.items.last_mut().unwrap()
} else {
unsafe { self.items.last_mut().unwrap_unchecked() }
}
}
}
impl<T> Default for Stack<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> Index<Range<usize>> for Stack<T> {
type Output = [T];
fn index(&self, index: Range<usize>) -> &Self::Output {
&self.items[index]
}
}
impl<T> IndexMut<Range<usize>> for Stack<T> {
fn index_mut(&mut self, index: Range<usize>) -> &mut Self::Output {
&mut self.items[index]
}
}
impl<T: Debug> Debug for Stack<T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{:?}", self.items)
}
}
impl Display for Stack<FunctionCall> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
writeln!(f, "----- DUST CALL STACK -----")?;
for (index, function_call) in self.items.iter().enumerate().rev() {
writeln!(f, "{index:02} | {function_call}")?;
}
write!(f, "---------------------------")
}
}

View File

@ -1,18 +1,36 @@
use std::{mem::replace, sync::Arc, thread::JoinHandle};
use std::{sync::Arc, thread::JoinHandle};
use tracing::{info, trace};
use crate::{Chunk, DustString, Operand, Span, Value, vm::FunctionCall};
use crate::{Chunk, DustString, Span, Value, vm::CallFrame};
use super::{Pointer, Register, RunAction, Stack};
use super::{Action, Register};
pub struct Thread {
chunk: Arc<Chunk>,
call_stack: Vec<CallFrame>,
next_action: Action,
return_value: Option<Value>,
spawned_threads: Vec<JoinHandle<()>>,
}
impl Thread {
pub fn new(chunk: Arc<Chunk>) -> Self {
Thread { chunk }
let mut call_stack = Vec::with_capacity(chunk.prototypes.len() + 1);
let mut main_call = CallFrame::new(chunk.clone(), 0);
main_call.ip = 1; // The first action is already known
call_stack.push(main_call);
let first_action = Action::from(*chunk.instructions.first().unwrap());
Thread {
chunk,
call_stack,
next_action: first_action,
return_value: None,
spawned_threads: Vec::new(),
}
}
pub fn run(&mut self) -> Option<Value> {
@ -24,247 +42,287 @@ impl Thread {
.unwrap_or_else(|| DustString::from("anonymous"))
);
let mut call_stack = Stack::with_capacity(self.chunk.prototypes.len() + 1);
let mut main_call = FunctionCall::new(self.chunk.clone(), 0);
main_call.ip = 1; // The first action is already known
call_stack.push(main_call);
let first_action = RunAction::from(*self.chunk.instructions.first().unwrap());
let mut thread_data = ThreadData {
call_stack,
next_action: first_action,
return_value_index: None,
spawned_threads: Vec::new(),
};
loop {
trace!("Instruction: {}", thread_data.next_action.instruction);
trace!("Instruction: {}", self.next_action.instruction);
let should_end = (thread_data.next_action.logic)(
thread_data.next_action.instruction,
&mut thread_data,
);
let should_end = (self.next_action.logic)(self.next_action.instruction, self);
if should_end {
let value_option = if let Some(register_index) = thread_data.return_value_index {
let value =
thread_data.empty_register_or_clone_constant_unchecked(register_index);
self.spawned_threads.into_iter().for_each(|join_handle| {
let _ = join_handle.join();
});
Some(value)
} else {
None
};
thread_data
.spawned_threads
.into_iter()
.for_each(|join_handle| {
let _ = join_handle.join();
});
return value_option;
return self.return_value.take();
}
}
}
}
#[derive(Debug)]
pub struct ThreadData {
pub call_stack: Stack<FunctionCall>,
pub next_action: RunAction,
pub return_value_index: Option<u16>,
pub spawned_threads: Vec<JoinHandle<()>>,
}
impl ThreadData {
pub fn current_position(&self) -> Span {
let current_call = self.call_stack.last_unchecked();
let current_frame = self.current_frame();
current_call.chunk.positions[current_call.ip]
current_frame.chunk.positions[current_frame.ip]
}
pub(crate) fn follow_pointer_unchecked(&self, pointer: Pointer) -> &Value {
trace!("Follow {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: u16) -> &Register {
trace!("Get R{register_index}");
let register_index = register_index as usize;
pub fn current_frame(&self) -> &CallFrame {
if cfg!(debug_assertions) {
&self.call_stack.last_unchecked().registers[register_index]
self.call_stack.last().unwrap()
} else {
unsafe {
self.call_stack
.last_unchecked()
.registers
.get_unchecked(register_index)
}
unsafe { self.call_stack.last().unwrap_unchecked() }
}
}
pub fn set_register(&mut self, to_register: u16, register: Register) {
let to_register = to_register as usize;
self.call_stack.last_mut_unchecked().registers[to_register] = register;
pub fn current_frame_mut(&mut self) -> &mut CallFrame {
if cfg!(debug_assertions) {
self.call_stack.last_mut().unwrap()
} else {
unsafe { self.call_stack.last_mut().unwrap_unchecked() }
}
}
pub fn open_register_unchecked(&self, register_index: u16) -> &Value {
let register_index = register_index as usize;
pub fn get_frame(&self, index: usize) -> &CallFrame {
if cfg!(debug_assertions) {
self.call_stack.get(index).unwrap()
} else {
unsafe { self.call_stack.get_unchecked(index) }
}
}
pub fn get_boolean_register(&self, index: usize) -> bool {
let register = if cfg!(debug_assertions) {
&self.call_stack.last_unchecked().registers[register_index]
self.current_frame()
.registers
.booleans
.get(index as usize)
.unwrap()
} else {
unsafe {
self.call_stack
.last_unchecked()
self.current_frame()
.registers
.get_unchecked(register_index)
.booleans
.get_unchecked(index as usize)
}
};
trace!("Open R{register_index} to {register}");
match register {
Register::Value(value) => value,
Register::Pointer(pointer) => self.follow_pointer_unchecked(*pointer),
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
Register::Value(boolean) => *boolean,
Register::Pointer(pointer) => unsafe { **pointer },
Register::Empty => panic!("Attempted to get a boolean from an empty register"),
}
}
pub fn open_register_allow_empty_unchecked(&self, register_index: u16) -> Option<&Value> {
trace!("Open R{register_index}");
let register = self.get_register_unchecked(register_index);
trace!("Open R{register_index} to {register}");
match register {
Register::Value(value) => Some(value),
Register::Pointer(pointer) => Some(self.follow_pointer_unchecked(*pointer)),
Register::Empty => None,
}
}
pub fn empty_register_or_clone_constant_unchecked(&mut self, register_index: u16) -> 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: u16) -> 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: Operand) -> &Value {
match argument {
Operand::Constant(constant_index) => self.get_constant_unchecked(constant_index),
Operand::Register(register_index) => self.open_register_unchecked(register_index),
}
}
pub fn get_constant_unchecked(&self, constant_index: u16) -> &Value {
let constant_index = constant_index as usize;
if cfg!(debug_assertions) {
&self.call_stack.last().unwrap().chunk.constants[constant_index]
pub fn set_boolean_register(&mut self, index: usize, new_register: Register<bool>) {
let old_register = if cfg!(debug_assertions) {
self.current_frame_mut()
.registers
.booleans
.get_mut(index as usize)
.unwrap()
} else {
unsafe {
self.call_stack
.last_unchecked()
.chunk
.constants
.get_unchecked(constant_index)
self.current_frame_mut()
.registers
.booleans
.get_unchecked_mut(index as usize)
}
};
*old_register = new_register;
}
pub fn get_byte_register(&self, index: usize) -> u8 {
let register = if cfg!(debug_assertions) {
self.current_frame()
.registers
.bytes
.get(index as usize)
.unwrap()
} else {
unsafe {
self.current_frame()
.registers
.bytes
.get_unchecked(index as usize)
}
};
match register {
Register::Value(byte) => *byte,
Register::Pointer(pointer) => unsafe { **pointer },
Register::Empty => panic!("Attempted to get a byte from an empty register"),
}
}
pub fn get_local_register(&self, local_index: u16) -> u16 {
let local_index = local_index as usize;
let chunk = &self.call_stack.last_unchecked().chunk;
pub fn set_byte_register(&mut self, index: usize, new_register: Register<u8>) {
let old_register = if cfg!(debug_assertions) {
self.current_frame_mut()
.registers
.bytes
.get_mut(index as usize)
.unwrap()
} else {
unsafe {
self.current_frame_mut()
.registers
.bytes
.get_unchecked_mut(index as usize)
}
};
assert!(
local_index < chunk.locals.len(),
"VM Error: Local index out of bounds"
);
*old_register = new_register;
}
chunk.locals[local_index].register_index
pub fn get_character_register(&self, index: usize) -> char {
let register = if cfg!(debug_assertions) {
self.current_frame()
.registers
.characters
.get(index as usize)
.unwrap()
} else {
unsafe {
self.current_frame()
.registers
.characters
.get_unchecked(index as usize)
}
};
match register {
Register::Value(character) => *character,
Register::Pointer(pointer) => unsafe { **pointer },
Register::Empty => panic!("Attempted to get a character from an empty register"),
}
}
pub fn set_character_register(&mut self, index: usize, new_register: Register<char>) {
let old_register = if cfg!(debug_assertions) {
self.current_frame_mut()
.registers
.characters
.get_mut(index as usize)
.unwrap()
} else {
unsafe {
self.current_frame_mut()
.registers
.characters
.get_unchecked_mut(index as usize)
}
};
*old_register = new_register;
}
pub fn get_float_register(&self, index: usize) -> f64 {
let register = if cfg!(debug_assertions) {
self.current_frame()
.registers
.floats
.get(index as usize)
.unwrap()
} else {
unsafe {
self.current_frame()
.registers
.floats
.get_unchecked(index as usize)
}
};
match register {
Register::Value(float) => *float,
Register::Pointer(pointer) => unsafe { **pointer },
Register::Empty => panic!("Attempted to get a float from an empty register"),
}
}
pub fn get_integer_register(&self, index: usize) -> i64 {
let register = if cfg!(debug_assertions) {
self.current_frame()
.registers
.integers
.get(index as usize)
.unwrap()
} else {
unsafe {
self.current_frame()
.registers
.integers
.get_unchecked(index as usize)
}
};
match register {
Register::Value(integer) => *integer,
Register::Pointer(pointer) => unsafe { **pointer },
Register::Empty => panic!("Attempted to get an integer from an empty register"),
}
}
pub fn set_integer_register(&mut self, index: usize, new_register: Register<i64>) {
let old_register = if cfg!(debug_assertions) {
self.current_frame_mut()
.registers
.integers
.get_mut(index as usize)
.unwrap()
} else {
unsafe {
self.current_frame_mut()
.registers
.integers
.get_unchecked_mut(index as usize)
}
};
*old_register = new_register;
}
pub fn get_string_register(&self, index: usize) -> &DustString {
let register = if cfg!(debug_assertions) {
self.current_frame()
.registers
.strings
.get(index as usize)
.unwrap()
} else {
unsafe {
self.current_frame()
.registers
.strings
.get_unchecked(index as usize)
}
};
match register {
Register::Value(string) => string,
Register::Pointer(pointer) => {
if cfg!(debug_assertions) {
unsafe { pointer.as_ref().unwrap() }
} else {
unsafe { pointer.as_ref().unwrap_unchecked() }
}
}
Register::Empty => panic!("Attempted to get a string from an empty register"),
}
}
pub fn set_string_register(&mut self, index: usize, new_register: Register<DustString>) {
let old_register = if cfg!(debug_assertions) {
self.current_frame_mut()
.registers
.strings
.get_mut(index as usize)
.unwrap()
} else {
unsafe {
self.current_frame_mut()
.registers
.strings
.get_unchecked_mut(index as usize)
}
};
*old_register = new_register;
}
}