Begin value overhaul
This commit is contained in:
parent
3fcbde59e5
commit
a89b927a80
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -349,6 +349,7 @@ dependencies = [
|
|||||||
"rand",
|
"rand",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"smallvec",
|
||||||
"smartstring",
|
"smartstring",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
@ -805,6 +806,9 @@ name = "smallvec"
|
|||||||
version = "1.13.2"
|
version = "1.13.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smartstring"
|
name = "smartstring"
|
||||||
|
@ -22,6 +22,7 @@ smartstring = { version = "1.0.1", features = [
|
|||||||
], default-features = false }
|
], default-features = false }
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
crossbeam-channel = "0.5.14"
|
crossbeam-channel = "0.5.14"
|
||||||
|
smallvec = { version = "1.13.2", features = ["serde", "const_generics"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = { version = "0.3.4", features = ["html_reports"] }
|
criterion = { version = "0.3.4", features = ["html_reports"] }
|
||||||
|
@ -27,7 +27,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
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.
|
/// Representation of a Dust program or function.
|
||||||
///
|
///
|
||||||
@ -39,8 +39,14 @@ pub struct Chunk {
|
|||||||
|
|
||||||
pub(crate) instructions: Vec<Instruction>,
|
pub(crate) instructions: Vec<Instruction>,
|
||||||
pub(crate) positions: Vec<Span>,
|
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) locals: Vec<Local>,
|
||||||
|
|
||||||
pub(crate) prototypes: Vec<Arc<Chunk>>,
|
pub(crate) prototypes: Vec<Arc<Chunk>>,
|
||||||
|
|
||||||
pub(crate) register_count: usize,
|
pub(crate) register_count: usize,
|
||||||
@ -48,29 +54,6 @@ pub struct Chunk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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 {
|
pub fn as_function(&self) -> Function {
|
||||||
Function {
|
Function {
|
||||||
name: self.name.clone(),
|
name: self.name.clone(),
|
||||||
@ -125,7 +108,11 @@ impl PartialEq for Chunk {
|
|||||||
self.name == other.name
|
self.name == other.name
|
||||||
&& self.r#type == other.r#type
|
&& self.r#type == other.r#type
|
||||||
&& self.instructions == other.instructions
|
&& 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.locals == other.locals
|
||||||
&& self.prototypes == other.prototypes
|
&& self.prototypes == other.prototypes
|
||||||
}
|
}
|
||||||
|
@ -42,15 +42,13 @@ pub mod vm;
|
|||||||
pub use crate::chunk::{Chunk, Disassembler, Local, Scope};
|
pub use crate::chunk::{Chunk, Disassembler, Local, Scope};
|
||||||
pub use crate::compiler::{CompileError, Compiler, compile};
|
pub use crate::compiler::{CompileError, Compiler, compile};
|
||||||
pub use crate::dust_error::{AnnotatedError, DustError};
|
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::lexer::{LexError, Lexer, lex};
|
||||||
pub use crate::native_function::{NativeFunction, NativeFunctionError};
|
pub use crate::native_function::{NativeFunction, NativeFunctionError};
|
||||||
pub use crate::token::{Token, TokenKind, TokenOwned};
|
pub use crate::token::{Token, TokenKind, TokenOwned};
|
||||||
pub use crate::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict};
|
pub use crate::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict};
|
||||||
pub use crate::value::{
|
pub use crate::value::{DustString, Function, RangeValue, Value, ValueError};
|
||||||
AbstractList, ConcreteValue, DustString, Function, RangeValue, Value, ValueError,
|
pub use crate::vm::{Vm, run};
|
||||||
};
|
|
||||||
pub use crate::vm::{Pointer, Vm, run};
|
|
||||||
|
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
@ -1,238 +1,40 @@
|
|||||||
//! Runtime values used by the VM.
|
//! Runtime values used by the VM.
|
||||||
mod abstract_list;
|
|
||||||
mod concrete_value;
|
|
||||||
mod function;
|
mod function;
|
||||||
mod range_value;
|
mod range_value;
|
||||||
|
|
||||||
pub use abstract_list::AbstractList;
|
|
||||||
pub use concrete_value::{ConcreteValue, DustString};
|
|
||||||
pub use function::Function;
|
pub use function::Function;
|
||||||
pub use range_value::RangeValue;
|
pub use range_value::RangeValue;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use smartstring::{LazyCompact, SmartString};
|
||||||
|
|
||||||
use std::fmt::{self, Debug, Display, Formatter};
|
use std::fmt::{self, Debug, Display, Formatter};
|
||||||
|
|
||||||
use crate::{Type, vm::ThreadData};
|
pub type DustString = SmartString<LazyCompact>;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
Concrete(ConcreteValue),
|
Boolean(bool),
|
||||||
|
Byte(u8),
|
||||||
#[serde(skip)]
|
Character(char),
|
||||||
AbstractList(AbstractList),
|
Float(f64),
|
||||||
|
Integer(i64),
|
||||||
|
String(DustString),
|
||||||
|
List(Vec<Value>),
|
||||||
|
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
Function(Function),
|
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 {
|
impl Display for Value {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Value::Concrete(concrete_value) => write!(f, "{concrete_value}"),
|
Value::Boolean(boolean) => write!(f, "{boolean}"),
|
||||||
Value::AbstractList(list) => write!(f, "{list}"),
|
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}"),
|
Value::Function(function) => write!(f, "{function}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,27 +7,27 @@ use crate::{
|
|||||||
LoadConstant, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not, Point,
|
LoadConstant, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not, Point,
|
||||||
Return, SetLocal, Subtract, Test, TestSet, TypeCode,
|
Return, SetLocal, Subtract, Test, TestSet, TypeCode,
|
||||||
},
|
},
|
||||||
vm::FunctionCall,
|
vm::CallFrame,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{Pointer, Register, thread::ThreadData};
|
use super::{Pointer, Register, thread::ThreadData};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct RunAction {
|
pub struct Action {
|
||||||
pub logic: RunnerLogic,
|
pub logic: RunnerLogic,
|
||||||
pub instruction: Instruction,
|
pub instruction: Instruction,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Instruction> for RunAction {
|
impl From<Instruction> for Action {
|
||||||
fn from(instruction: Instruction) -> Self {
|
fn from(instruction: Instruction) -> Self {
|
||||||
let operation = instruction.operation();
|
let operation = instruction.operation();
|
||||||
let logic = RUNNER_LOGIC_TABLE[operation.0 as usize];
|
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] = [
|
pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [
|
||||||
point,
|
point,
|
||||||
@ -57,7 +57,7 @@ pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [
|
|||||||
r#return,
|
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 current_call = data.call_stack.last_mut_unchecked();
|
||||||
let instruction = current_call.chunk.instructions[current_call.ip];
|
let instruction = current_call.chunk.instructions[current_call.ip];
|
||||||
let operation = instruction.operation();
|
let operation = instruction.operation();
|
||||||
@ -65,7 +65,7 @@ pub(crate) fn get_next_action(data: &mut ThreadData) -> RunAction {
|
|||||||
|
|
||||||
current_call.ip += 1;
|
current_call.ip += 1;
|
||||||
|
|
||||||
RunAction { logic, instruction }
|
Action { logic, instruction }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn point(instruction: Instruction, data: &mut ThreadData) -> bool {
|
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()
|
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;
|
let mut argument_index = 0;
|
||||||
|
|
||||||
for register_index in first_argument_register..return_register {
|
for register_index in first_argument_register..return_register {
|
73
dust-lang/src/vm/call_frame.rs
Normal file
73
dust-lang/src/vm/call_frame.rs
Normal 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),
|
||||||
|
}
|
@ -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()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,6 @@
|
|||||||
//! Virtual machine and errors
|
//! Virtual machine and errors
|
||||||
mod function_call;
|
mod action;
|
||||||
mod run_action;
|
mod call_frame;
|
||||||
mod stack;
|
|
||||||
mod thread;
|
mod thread;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
@ -10,11 +9,10 @@ use std::{
|
|||||||
thread::Builder,
|
thread::Builder,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use function_call::FunctionCall;
|
pub use action::Action;
|
||||||
pub use run_action::RunAction;
|
pub(crate) use action::get_next_action;
|
||||||
pub(crate) use run_action::get_next_action;
|
pub use call_frame::{CallFrame, Register, RegisterTable};
|
||||||
pub use stack::Stack;
|
pub use thread::Thread;
|
||||||
pub use thread::{Thread, ThreadData};
|
|
||||||
|
|
||||||
use crossbeam_channel::bounded;
|
use crossbeam_channel::bounded;
|
||||||
use tracing::{Level, span};
|
use tracing::{Level, span};
|
||||||
@ -46,12 +44,13 @@ impl Vm {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|name| name.to_string())
|
.map(|name| name.to_string())
|
||||||
.unwrap_or_else(|| "anonymous".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 (tx, rx) = bounded(1);
|
||||||
|
let main_chunk = Arc::new(self.main_chunk);
|
||||||
|
|
||||||
Builder::new()
|
Builder::new()
|
||||||
.name(thread_name)
|
.name(thread_name)
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
|
let mut main_thread = Thread::new(main_chunk);
|
||||||
let value_option = main_thread.run();
|
let value_option = main_thread.run();
|
||||||
let _ = tx.send(value_option);
|
let _ = tx.send(value_option);
|
||||||
})
|
})
|
||||||
@ -62,39 +61,3 @@ impl Vm {
|
|||||||
rx.recv().unwrap_or(None)
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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, "---------------------------")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +1,36 @@
|
|||||||
use std::{mem::replace, sync::Arc, thread::JoinHandle};
|
use std::{sync::Arc, thread::JoinHandle};
|
||||||
|
|
||||||
use tracing::{info, trace};
|
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 {
|
pub struct Thread {
|
||||||
chunk: Arc<Chunk>,
|
chunk: Arc<Chunk>,
|
||||||
|
call_stack: Vec<CallFrame>,
|
||||||
|
next_action: Action,
|
||||||
|
return_value: Option<Value>,
|
||||||
|
spawned_threads: Vec<JoinHandle<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Thread {
|
impl Thread {
|
||||||
pub fn new(chunk: Arc<Chunk>) -> Self {
|
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> {
|
pub fn run(&mut self) -> Option<Value> {
|
||||||
@ -24,247 +42,287 @@ impl Thread {
|
|||||||
.unwrap_or_else(|| DustString::from("anonymous"))
|
.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 {
|
loop {
|
||||||
trace!("Instruction: {}", thread_data.next_action.instruction);
|
trace!("Instruction: {}", self.next_action.instruction);
|
||||||
|
|
||||||
let should_end = (thread_data.next_action.logic)(
|
let should_end = (self.next_action.logic)(self.next_action.instruction, self);
|
||||||
thread_data.next_action.instruction,
|
|
||||||
&mut thread_data,
|
|
||||||
);
|
|
||||||
|
|
||||||
if should_end {
|
if should_end {
|
||||||
let value_option = if let Some(register_index) = thread_data.return_value_index {
|
self.spawned_threads.into_iter().for_each(|join_handle| {
|
||||||
let value =
|
|
||||||
thread_data.empty_register_or_clone_constant_unchecked(register_index);
|
|
||||||
|
|
||||||
Some(value)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
thread_data
|
|
||||||
.spawned_threads
|
|
||||||
.into_iter()
|
|
||||||
.for_each(|join_handle| {
|
|
||||||
let _ = join_handle.join();
|
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 {
|
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 {
|
pub fn current_frame(&self) -> &CallFrame {
|
||||||
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;
|
|
||||||
|
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
&self.call_stack.last_unchecked().registers[register_index]
|
self.call_stack.last().unwrap()
|
||||||
} else {
|
} else {
|
||||||
unsafe {
|
unsafe { self.call_stack.last().unwrap_unchecked() }
|
||||||
self.call_stack
|
|
||||||
.last_unchecked()
|
|
||||||
.registers
|
|
||||||
.get_unchecked(register_index)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_register(&mut self, to_register: u16, register: Register) {
|
pub fn current_frame_mut(&mut self) -> &mut CallFrame {
|
||||||
let to_register = to_register as usize;
|
if cfg!(debug_assertions) {
|
||||||
|
self.call_stack.last_mut().unwrap()
|
||||||
self.call_stack.last_mut_unchecked().registers[to_register] = register;
|
} else {
|
||||||
|
unsafe { self.call_stack.last_mut().unwrap_unchecked() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_register_unchecked(&self, register_index: u16) -> &Value {
|
pub fn get_frame(&self, index: usize) -> &CallFrame {
|
||||||
let register_index = register_index as usize;
|
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) {
|
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 {
|
} else {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.call_stack
|
self.current_frame()
|
||||||
.last_unchecked()
|
|
||||||
.registers
|
.registers
|
||||||
.get_unchecked(register_index)
|
.booleans
|
||||||
|
.get_unchecked(index as usize)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
trace!("Open R{register_index} to {register}");
|
|
||||||
|
|
||||||
match register {
|
match register {
|
||||||
Register::Value(value) => value,
|
Register::Value(boolean) => *boolean,
|
||||||
Register::Pointer(pointer) => self.follow_pointer_unchecked(*pointer),
|
Register::Pointer(pointer) => unsafe { **pointer },
|
||||||
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
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> {
|
pub fn set_boolean_register(&mut self, index: usize, new_register: Register<bool>) {
|
||||||
trace!("Open R{register_index}");
|
let old_register = if cfg!(debug_assertions) {
|
||||||
|
self.current_frame_mut()
|
||||||
let register = self.get_register_unchecked(register_index);
|
.registers
|
||||||
|
.booleans
|
||||||
trace!("Open R{register_index} to {register}");
|
.get_mut(index as usize)
|
||||||
|
.unwrap()
|
||||||
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]
|
|
||||||
} else {
|
} else {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.call_stack
|
self.current_frame_mut()
|
||||||
.last_unchecked()
|
.registers
|
||||||
.chunk
|
.booleans
|
||||||
.constants
|
.get_unchecked_mut(index as usize)
|
||||||
.get_unchecked(constant_index)
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
*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 {
|
pub fn set_byte_register(&mut self, index: usize, new_register: Register<u8>) {
|
||||||
let local_index = local_index as usize;
|
let old_register = if cfg!(debug_assertions) {
|
||||||
let chunk = &self.call_stack.last_unchecked().chunk;
|
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!(
|
*old_register = new_register;
|
||||||
local_index < chunk.locals.len(),
|
}
|
||||||
"VM Error: Local index out of bounds"
|
|
||||||
);
|
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user