1
0

Begin refactor of how registers and values are handled in the VM

This commit is contained in:
Jeff 2025-01-19 23:37:43 -05:00
parent 7cfd60d281
commit e0579c6536
29 changed files with 1816 additions and 2071 deletions

674
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,674 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Debug unit tests in library 'dust_lang'",
"cargo": {
"args": [
"test",
"--no-run",
"--lib",
"--package=dust-lang"
],
"filter": {
"name": "dust_lang",
"kind": "lib"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug integration test 'assignment_errors'",
"cargo": {
"args": [
"test",
"--no-run",
"--test=assignment_errors",
"--package=dust-lang"
],
"filter": {
"name": "assignment_errors",
"kind": "test"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug integration test 'basic'",
"cargo": {
"args": [
"test",
"--no-run",
"--test=basic",
"--package=dust-lang"
],
"filter": {
"name": "basic",
"kind": "test"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug integration test 'comparison'",
"cargo": {
"args": [
"test",
"--no-run",
"--test=comparison",
"--package=dust-lang"
],
"filter": {
"name": "comparison",
"kind": "test"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug integration test 'functions'",
"cargo": {
"args": [
"test",
"--no-run",
"--test=functions",
"--package=dust-lang"
],
"filter": {
"name": "functions",
"kind": "test"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug integration test 'lists'",
"cargo": {
"args": [
"test",
"--no-run",
"--test=lists",
"--package=dust-lang"
],
"filter": {
"name": "lists",
"kind": "test"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug integration test 'logic_and'",
"cargo": {
"args": [
"test",
"--no-run",
"--test=logic_and",
"--package=dust-lang"
],
"filter": {
"name": "logic_and",
"kind": "test"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug integration test 'logic_and_and'",
"cargo": {
"args": [
"test",
"--no-run",
"--test=logic_and_and",
"--package=dust-lang"
],
"filter": {
"name": "logic_and_and",
"kind": "test"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug integration test 'logic_or'",
"cargo": {
"args": [
"test",
"--no-run",
"--test=logic_or",
"--package=dust-lang"
],
"filter": {
"name": "logic_or",
"kind": "test"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug integration test 'logic_variables'",
"cargo": {
"args": [
"test",
"--no-run",
"--test=logic_variables",
"--package=dust-lang"
],
"filter": {
"name": "logic_variables",
"kind": "test"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug integration test 'loops'",
"cargo": {
"args": [
"test",
"--no-run",
"--test=loops",
"--package=dust-lang"
],
"filter": {
"name": "loops",
"kind": "test"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug integration test 'math_add'",
"cargo": {
"args": [
"test",
"--no-run",
"--test=math_add",
"--package=dust-lang"
],
"filter": {
"name": "math_add",
"kind": "test"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug integration test 'math_add_assign'",
"cargo": {
"args": [
"test",
"--no-run",
"--test=math_add_assign",
"--package=dust-lang"
],
"filter": {
"name": "math_add_assign",
"kind": "test"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug integration test 'math_add_errors'",
"cargo": {
"args": [
"test",
"--no-run",
"--test=math_add_errors",
"--package=dust-lang"
],
"filter": {
"name": "math_add_errors",
"kind": "test"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug integration test 'math_divide'",
"cargo": {
"args": [
"test",
"--no-run",
"--test=math_divide",
"--package=dust-lang"
],
"filter": {
"name": "math_divide",
"kind": "test"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug integration test 'math_divide_assign'",
"cargo": {
"args": [
"test",
"--no-run",
"--test=math_divide_assign",
"--package=dust-lang"
],
"filter": {
"name": "math_divide_assign",
"kind": "test"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug integration test 'math_divide_erros'",
"cargo": {
"args": [
"test",
"--no-run",
"--test=math_divide_erros",
"--package=dust-lang"
],
"filter": {
"name": "math_divide_erros",
"kind": "test"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug integration test 'math_modulo'",
"cargo": {
"args": [
"test",
"--no-run",
"--test=math_modulo",
"--package=dust-lang"
],
"filter": {
"name": "math_modulo",
"kind": "test"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug integration test 'math_modulo_assign'",
"cargo": {
"args": [
"test",
"--no-run",
"--test=math_modulo_assign",
"--package=dust-lang"
],
"filter": {
"name": "math_modulo_assign",
"kind": "test"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug integration test 'math_modulo_errors'",
"cargo": {
"args": [
"test",
"--no-run",
"--test=math_modulo_errors",
"--package=dust-lang"
],
"filter": {
"name": "math_modulo_errors",
"kind": "test"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug integration test 'math_multiply'",
"cargo": {
"args": [
"test",
"--no-run",
"--test=math_multiply",
"--package=dust-lang"
],
"filter": {
"name": "math_multiply",
"kind": "test"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug integration test 'math_multiply_assign'",
"cargo": {
"args": [
"test",
"--no-run",
"--test=math_multiply_assign",
"--package=dust-lang"
],
"filter": {
"name": "math_multiply_assign",
"kind": "test"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug integration test 'math_multiply_errors'",
"cargo": {
"args": [
"test",
"--no-run",
"--test=math_multiply_errors",
"--package=dust-lang"
],
"filter": {
"name": "math_multiply_errors",
"kind": "test"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug integration test 'math_subtract'",
"cargo": {
"args": [
"test",
"--no-run",
"--test=math_subtract",
"--package=dust-lang"
],
"filter": {
"name": "math_subtract",
"kind": "test"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug integration test 'math_subtract_assign'",
"cargo": {
"args": [
"test",
"--no-run",
"--test=math_subtract_assign",
"--package=dust-lang"
],
"filter": {
"name": "math_subtract_assign",
"kind": "test"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug integration test 'math_subtract_errors'",
"cargo": {
"args": [
"test",
"--no-run",
"--test=math_subtract_errors",
"--package=dust-lang"
],
"filter": {
"name": "math_subtract_errors",
"kind": "test"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug integration test 'native_functions'",
"cargo": {
"args": [
"test",
"--no-run",
"--test=native_functions",
"--package=dust-lang"
],
"filter": {
"name": "native_functions",
"kind": "test"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug integration test 'scopes'",
"cargo": {
"args": [
"test",
"--no-run",
"--test=scopes",
"--package=dust-lang"
],
"filter": {
"name": "scopes",
"kind": "test"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug integration test 'unary_operations'",
"cargo": {
"args": [
"test",
"--no-run",
"--test=unary_operations",
"--package=dust-lang"
],
"filter": {
"name": "unary_operations",
"kind": "test"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug integration test 'variables'",
"cargo": {
"args": [
"test",
"--no-run",
"--test=variables",
"--package=dust-lang"
],
"filter": {
"name": "variables",
"kind": "test"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug benchmark 'addictive_addition'",
"cargo": {
"args": [
"test",
"--no-run",
"--bench=addictive_addition",
"--package=dust-lang"
],
"filter": {
"name": "addictive_addition",
"kind": "bench"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug benchmark 'fibonacci'",
"cargo": {
"args": [
"test",
"--no-run",
"--bench=fibonacci",
"--package=dust-lang"
],
"filter": {
"name": "fibonacci",
"kind": "bench"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug benchmark 'threads'",
"cargo": {
"args": [
"test",
"--no-run",
"--bench=threads",
"--package=dust-lang"
],
"filter": {
"name": "threads",
"kind": "bench"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug executable 'dust'",
"cargo": {
"args": [
"build",
"--bin=dust",
"--package=dust-cli"
],
"filter": {
"name": "dust",
"kind": "bin"
}
},
"args": [
"bench/addictive_addition/addictive_addition.ds"
],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug unit tests in executable 'dust'",
"cargo": {
"args": [
"test",
"--no-run",
"--bin=dust",
"--package=dust-cli"
],
"filter": {
"name": "dust",
"kind": "bin"
}
},
"args": [],
"cwd": "${workspaceFolder}"
}
]
}

4
Cargo.lock generated
View File

@ -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"

View File

@ -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"] }
[dev-dependencies] [dev-dependencies]
criterion = { version = "0.3.4", features = ["html_reports"] } criterion = { version = "0.3.4", features = ["html_reports"] }

View File

@ -0,0 +1,137 @@
use serde::{Deserialize, Serialize};
use crate::DustString;
#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct ConstantTable {
pub bytes: Vec<u8>,
pub characters: Vec<char>,
pub floats: Vec<f64>,
pub integers: Vec<i64>,
pub strings: Vec<DustString>,
}
impl ConstantTable {
pub fn new() -> Self {
Self {
bytes: Vec::with_capacity(0),
characters: Vec::with_capacity(0),
floats: Vec::with_capacity(0),
integers: Vec::with_capacity(0),
strings: Vec::with_capacity(0),
}
}
#[cfg(debug_assertions)]
pub fn with_data(
bytes: impl Into<Vec<u8>>,
characters: impl Into<Vec<char>>,
floats: impl Into<Vec<f64>>,
integers: impl Into<Vec<i64>>,
strings: impl Into<Vec<DustString>>,
) -> Self {
Self {
bytes: bytes.into(),
characters: characters.into(),
floats: floats.into(),
integers: integers.into(),
strings: strings.into(),
}
}
pub fn len(&self) -> usize {
self.bytes.len()
+ self.characters.len()
+ self.floats.len()
+ self.integers.len()
+ self.strings.len()
}
pub fn is_empty(&self) -> bool {
self.bytes.is_empty()
&& self.characters.is_empty()
&& self.floats.is_empty()
&& self.integers.is_empty()
&& self.strings.is_empty()
}
pub fn insert_byte(&mut self, byte: u8) -> u16 {
if let Some(index) = self.bytes.iter().position(|&probe| probe == byte) {
index as u16
} else {
let index = self.bytes.len() as u16;
self.bytes.push(byte);
index
}
}
pub fn insert_character(&mut self, character: char) -> u16 {
if let Some(index) = self.characters.iter().position(|&probe| probe == character) {
index as u16
} else {
let index = self.characters.len() as u16;
self.characters.push(character);
index
}
}
pub fn insert_float(&mut self, float: f64) -> u16 {
if let Some(index) = self.floats.iter().position(|&probe| probe == float) {
index as u16
} else {
let index = self.floats.len() as u16;
self.floats.push(float);
index
}
}
pub fn insert_integer(&mut self, integer: i64) -> u16 {
if let Some(index) = self.integers.iter().position(|&probe| probe == integer) {
index as u16
} else {
let index = self.integers.len() as u16;
self.integers.push(integer);
index
}
}
pub fn insert_string(&mut self, string: DustString) -> u16 {
if let Some(index) = self.strings.iter().position(|probe| probe == &string) {
index as u16
} else {
let index = self.strings.len() as u16;
self.strings.push(string);
index
}
}
pub fn get_byte(&self, index: u16) -> Option<u8> {
self.bytes.get(index as usize).copied()
}
pub fn get_character(&self, index: u16) -> Option<char> {
self.characters.get(index as usize).copied()
}
pub fn get_float(&self, index: u16) -> Option<f64> {
self.floats.get(index as usize).copied()
}
pub fn get_integer(&self, index: u16) -> Option<i64> {
self.integers.get(index as usize).copied()
}
pub fn get_string(&self, index: u16) -> Option<&DustString> {
self.strings.get(index as usize)
}
}

View File

@ -43,7 +43,7 @@ use std::io::{self, Write};
use colored::{ColoredString, Colorize}; use colored::{ColoredString, Colorize};
use crate::{Chunk, Local}; use crate::{Chunk, Local, Type};
const INSTRUCTION_COLUMNS: [(&str, usize); 4] = const INSTRUCTION_COLUMNS: [(&str, usize); 4] =
[("i", 5), ("POSITION", 12), ("OPERATION", 17), ("INFO", 41)]; [("i", 5), ("POSITION", 12), ("OPERATION", 17), ("INFO", 41)];
@ -66,11 +66,11 @@ const LOCAL_BORDERS: [&str; 3] = [
"╰─────┴────────────────┴──────────┴───────┴───────╯", "╰─────┴────────────────┴──────────┴───────┴───────╯",
]; ];
const CONSTANT_COLUMNS: [(&str, usize); 3] = [("i", 5), ("TYPE", 26), ("VALUE", 26)]; const CONSTANT_COLUMNS: [(&str, usize); 3] = [("i", 8), ("TYPE", 26), ("VALUE", 26)];
const CONSTANT_BORDERS: [&str; 3] = [ const CONSTANT_BORDERS: [&str; 3] = [
"╭─────┬──────────────────────────┬──────────────────────────╮", "╭────────┬──────────────────────────┬──────────────────────────╮",
"├─────┼──────────────────────────┼──────────────────────────┤", "├────────┼──────────────────────────┼──────────────────────────┤",
"╰─────┴──────────────────────────┴──────────────────────────╯", "╰────────┴──────────────────────────┴──────────────────────────╯",
]; ];
const INDENTATION: &str = ""; const INDENTATION: &str = "";
@ -322,6 +322,7 @@ impl<'a, W: Write> Disassembler<'a, W> {
let identifier_display = self let identifier_display = self
.chunk .chunk
.constants .constants
.strings
.get(*identifier_index as usize) .get(*identifier_index as usize)
.map(|value| value.to_string()) .map(|value| value.to_string())
.unwrap_or_else(|| "unknown".to_string()); .unwrap_or_else(|| "unknown".to_string());
@ -352,8 +353,74 @@ impl<'a, W: Write> Disassembler<'a, W> {
self.write_center_border(&column_name_line)?; self.write_center_border(&column_name_line)?;
self.write_center_border(CONSTANT_BORDERS[1])?; self.write_center_border(CONSTANT_BORDERS[1])?;
for (index, value) in self.chunk.constants.iter().enumerate() { for (index, value) in self.chunk.constants.bytes.iter().enumerate() {
let type_display = value.r#type().to_string(); let type_display = Type::Byte.to_string();
let value_display = {
let mut value_string = value.to_string();
if value_string.len() > 26 {
value_string = format!("{value_string:.23}...");
}
value_string
};
let constant_display = format!("{index:^5}{type_display:^26}{value_display:^26}");
self.write_center_border(&constant_display)?;
}
for (index, value) in self.chunk.constants.floats.iter().enumerate() {
let type_display = Type::Float.to_string();
let value_display = {
let mut value_string = value.to_string();
if value_string.len() > 26 {
value_string = format!("{value_string:.23}...");
}
value_string
};
let constant_display = format!("{index:^5}{type_display:^26}{value_display:^26}");
self.write_center_border(&constant_display)?;
}
for (index, value) in self.chunk.constants.characters.iter().enumerate() {
let type_display = Type::Character.to_string();
let value_display = {
let mut value_string = value.to_string();
if value_string.len() > 26 {
value_string = format!("{value_string:.23}...");
}
value_string
};
let constant_display = format!("{index:^5}{type_display:^26}{value_display:^26}");
self.write_center_border(&constant_display)?;
}
for (index, value) in self.chunk.constants.integers.iter().enumerate() {
let index_display = format!("INT_{index}");
let type_display = Type::Integer.to_string();
let value_display = {
let mut value_string = value.to_string();
if value_string.len() > 26 {
value_string = format!("{value_string:.23}...");
}
value_string
};
let constant_display =
format!("{index_display:^8}{type_display:^26}{value_display:^26}");
self.write_center_border(&constant_display)?;
}
for (index, value) in self.chunk.constants.strings.iter().enumerate() {
let type_display = Type::String.to_string();
let value_display = { let value_display = {
let mut value_string = value.to_string(); let mut value_string = value.to_string();

View File

@ -13,10 +13,12 @@
//! [`Chunk::with_data`] can be used to create a chunk for comparison to the compiler output. Do not //! [`Chunk::with_data`] can be used to create a chunk for comparison to the compiler output. Do not
//! try to run these chunks in a virtual machine. Due to their missing stack size and record index, //! try to run these chunks in a virtual machine. Due to their missing stack size and record index,
//! they will cause a panic or undefined behavior. //! they will cause a panic or undefined behavior.
mod constant_table;
mod disassembler; mod disassembler;
mod local; mod local;
mod scope; mod scope;
pub use constant_table::ConstantTable;
pub use disassembler::Disassembler; pub use disassembler::Disassembler;
pub use local::Local; pub use local::Local;
pub use scope::Scope; pub use scope::Scope;
@ -27,7 +29,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,7 +41,7 @@ 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) constants: ConstantTable,
pub(crate) locals: Vec<Local>, pub(crate) locals: Vec<Local>,
pub(crate) prototypes: Vec<Arc<Chunk>>, pub(crate) prototypes: Vec<Arc<Chunk>>,
@ -54,7 +56,7 @@ impl Chunk {
r#type: FunctionType, r#type: FunctionType,
instructions: impl Into<Vec<Instruction>>, instructions: impl Into<Vec<Instruction>>,
positions: impl Into<Vec<Span>>, positions: impl Into<Vec<Span>>,
constants: impl Into<Vec<Value>>, constants: ConstantTable,
locals: impl Into<Vec<Local>>, locals: impl Into<Vec<Local>>,
prototypes: impl IntoIterator<Item = Chunk>, prototypes: impl IntoIterator<Item = Chunk>,
) -> Self { ) -> Self {
@ -63,7 +65,7 @@ impl Chunk {
r#type, r#type,
instructions: instructions.into(), instructions: instructions.into(),
positions: positions.into(), positions: positions.into(),
constants: constants.into(), constants,
locals: locals.into(), locals: locals.into(),
prototypes: prototypes.into_iter().map(Arc::new).collect(), prototypes: prototypes.into_iter().map(Arc::new).collect(),
register_count: 0, register_count: 0,

View File

@ -33,8 +33,9 @@ use std::{mem::replace, sync::Arc};
use optimize::control_flow_register_consolidation; use optimize::control_flow_register_consolidation;
use crate::{ use crate::{
Chunk, ConcreteValue, DustError, DustString, FunctionType, Instruction, Lexer, Local, Chunk, DustError, DustString, FunctionType, Instruction, Lexer, Local, NativeFunction, Operand,
NativeFunction, Operand, Operation, Scope, Span, Token, TokenKind, Type, Value, Operation, Scope, Span, Token, TokenKind, Type,
chunk::ConstantTable,
instruction::{CallNative, Close, GetLocal, Jump, LoadList, Return, SetLocal, TypeCode}, instruction::{CallNative, Close, GetLocal, Jump, LoadList, Return, SetLocal, TypeCode},
}; };
@ -88,7 +89,7 @@ pub struct Compiler<'src> {
/// Constants that have been compiled. These are assigned to the chunk when [`Compiler::finish`] /// Constants that have been compiled. These are assigned to the chunk when [`Compiler::finish`]
/// is called. /// is called.
constants: Vec<Value>, constants: ConstantTable,
/// Block-local variables and their types. The locals are assigned to the chunk when /// Block-local variables and their types. The locals are assigned to the chunk when
/// [`Compiler::finish`] is called. The types are discarded after compilation. /// [`Compiler::finish`] is called. The types are discarded after compilation.
@ -144,7 +145,7 @@ impl<'src> Compiler<'src> {
return_type: Type::None, return_type: Type::None,
}, },
instructions: Vec::new(), instructions: Vec::new(),
constants: Vec::new(), constants: ConstantTable::new(),
locals: Vec::new(), locals: Vec::new(),
prototypes: Vec::new(), prototypes: Vec::new(),
stack_size: 0, stack_size: 0,
@ -215,7 +216,7 @@ impl<'src> Compiler<'src> {
r#type: self.r#type, r#type: self.r#type,
instructions, instructions,
positions, positions,
constants: self.constants.to_vec(), constants: self.constants,
locals, locals,
prototypes: self.prototypes, prototypes: self.prototypes,
register_count: self.stack_size, register_count: self.stack_size,
@ -275,13 +276,10 @@ impl<'src> Compiler<'src> {
.enumerate() .enumerate()
.rev() .rev()
.find_map(|(index, (local, _))| { .find_map(|(index, (local, _))| {
let constant = self.constants.get(local.identifier_index as usize)?; let identifier = self
let identifier = .constants
if let Value::Concrete(ConcreteValue::String(identifier)) = constant { .strings
identifier .get(local.identifier_index as usize)?;
} else {
return None;
};
if identifier == identifier_text { if identifier == identifier_text {
Some(index as u16) Some(index as u16)
@ -305,8 +303,8 @@ impl<'src> Compiler<'src> {
) -> (u16, u16) { ) -> (u16, u16) {
info!("Declaring local {identifier}"); info!("Declaring local {identifier}");
let identifier = Value::Concrete(ConcreteValue::string(identifier)); let identifier = DustString::from(identifier);
let identifier_index = self.push_or_get_constant(identifier); let identifier_index = self.push_or_get_constant_string(identifier);
let local_index = self.locals.len() as u16; let local_index = self.locals.len() as u16;
self.locals.push(( self.locals.push((
@ -322,22 +320,24 @@ impl<'src> Compiler<'src> {
.get(local_index as usize) .get(local_index as usize)
.and_then(|(local, _)| { .and_then(|(local, _)| {
self.constants self.constants
.strings
.get(local.identifier_index as usize) .get(local.identifier_index as usize)
.map(|value| value.to_string()) .map(|value| value.to_string())
}) })
} }
fn push_or_get_constant(&mut self, value: Value) -> u16 { fn push_or_get_constant_string(&mut self, string: DustString) -> u16 {
if let Some(index) = self if let Some(index) = self
.constants .constants
.strings
.iter() .iter()
.position(|constant| constant == &value) .position(|constant| constant == &string)
{ {
index as u16 index as u16
} else { } else {
let index = self.constants.len() as u16; let index = self.constants.len() as u16;
self.constants.push(value); self.constants.strings.push(string);
index index
} }
@ -466,17 +466,65 @@ impl<'src> Compiler<'src> {
self.instructions.push((instruction, r#type, position)); self.instructions.push((instruction, r#type, position));
} }
fn emit_constant( fn emit_byte_constant(&mut self, byte: u8, position: Span) -> Result<(), CompileError> {
let constant_index = self.constants.insert_byte(byte);
let destination = self.next_register();
let load_constant =
Instruction::load_constant(destination, TypeCode::BYTE, constant_index, false);
self.emit_instruction(load_constant, Type::Byte, position);
Ok(())
}
fn emit_character_constant(
&mut self, &mut self,
constant: ConcreteValue, character: char,
position: Span, position: Span,
) -> Result<(), CompileError> { ) -> Result<(), CompileError> {
let r#type = constant.r#type(); let constant_index = self.constants.insert_character(character);
let constant_index = self.push_or_get_constant(Value::Concrete(constant));
let destination = self.next_register(); let destination = self.next_register();
let load_constant = Instruction::load_constant(destination, constant_index, false); let load_constant =
Instruction::load_constant(destination, TypeCode::CHARACTER, constant_index, false);
self.emit_instruction(load_constant, r#type, position); self.emit_instruction(load_constant, Type::Character, position);
Ok(())
}
fn emit_float_constant(&mut self, float: f64, position: Span) -> Result<(), CompileError> {
let constant_index = self.constants.insert_float(float);
let destination = self.next_register();
let load_constant =
Instruction::load_constant(destination, TypeCode::FLOAT, constant_index, false);
self.emit_instruction(load_constant, Type::Float, position);
Ok(())
}
fn emit_integer_constant(&mut self, integer: i64, position: Span) -> Result<(), CompileError> {
let constant_index = self.constants.insert_integer(integer);
let destination = self.next_register();
let load_constant =
Instruction::load_constant(destination, TypeCode::INTEGER, constant_index, false);
self.emit_instruction(load_constant, Type::Integer, position);
Ok(())
}
fn emit_string_constant(
&mut self,
string: DustString,
position: Span,
) -> Result<(), CompileError> {
let constant_index = self.constants.insert_string(string);
let destination = self.next_register();
let load_constant =
Instruction::load_constant(destination, TypeCode::STRING, constant_index, false);
self.emit_instruction(load_constant, Type::String, position);
Ok(()) Ok(())
} }
@ -511,9 +559,8 @@ impl<'src> Compiler<'src> {
let byte = u8::from_str_radix(&text[2..], 16) let byte = u8::from_str_radix(&text[2..], 16)
.map_err(|error| CompileError::ParseIntError { error, position })?; .map_err(|error| CompileError::ParseIntError { error, position })?;
let value = ConcreteValue::Byte(byte);
self.emit_constant(value, position)?; self.emit_byte_constant(byte, position)?;
Ok(()) Ok(())
} else { } else {
@ -530,10 +577,7 @@ impl<'src> Compiler<'src> {
if let Token::Character(character) = self.current_token { if let Token::Character(character) = self.current_token {
self.advance()?; self.advance()?;
self.emit_character_constant(character, position)?;
let value = ConcreteValue::Character(character);
self.emit_constant(value, position)?;
Ok(()) Ok(())
} else { } else {
@ -557,9 +601,8 @@ impl<'src> Compiler<'src> {
error, error,
position: self.previous_position, position: self.previous_position,
})?; })?;
let value = ConcreteValue::Float(float);
self.emit_constant(value, position)?; self.emit_float_constant(float, position)?;
Ok(()) Ok(())
} else { } else {
@ -589,9 +632,7 @@ impl<'src> Compiler<'src> {
integer_value = integer_value * 10 + digit; integer_value = integer_value * 10 + digit;
} }
let value = ConcreteValue::Integer(integer_value); self.emit_integer_constant(integer_value, position)?;
self.emit_constant(value, position)?;
Ok(()) Ok(())
} else { } else {
@ -609,9 +650,9 @@ impl<'src> Compiler<'src> {
if let Token::String(text) = self.current_token { if let Token::String(text) = self.current_token {
self.advance()?; self.advance()?;
let value = ConcreteValue::string(text); let string = DustString::from(text);
self.emit_constant(value, position)?; self.emit_string_constant(string, position)?;
Ok(()) Ok(())
} else { } else {
@ -1478,24 +1519,34 @@ impl<'src> Compiler<'src> {
self.advance()?; self.advance()?;
let should_return_value = let (should_return_value, return_type) =
if matches!(self.current_token, Token::Semicolon | Token::RightBrace) { if matches!(self.current_token, Token::Semicolon | Token::RightBrace) {
self.update_return_type(Type::None)?; self.update_return_type(Type::None)?;
false (false, TypeCode(0))
} else { } else {
self.parse_expression()?; self.parse_expression()?;
let expression_type = self.get_last_instruction_type(); let expression_type = self.get_last_instruction_type();
let type_code = match expression_type {
Type::Boolean => TypeCode::BOOLEAN,
Type::Byte => TypeCode::BYTE,
Type::Character => TypeCode::CHARACTER,
Type::Float => TypeCode::FLOAT,
Type::Integer => TypeCode::INTEGER,
Type::String => TypeCode::STRING,
_ => TypeCode(0),
};
self.update_return_type(expression_type)?; self.update_return_type(expression_type)?;
true (true, type_code)
}; };
let end = self.current_position.1; let end = self.current_position.1;
let return_register = self.next_register() - 1; let return_register = self.next_register() - 1;
let r#return = Instruction::from(Return { let r#return = Instruction::from(Return {
should_return_value, should_return_value,
return_type,
return_register, return_register,
}); });
@ -1529,7 +1580,7 @@ impl<'src> Compiler<'src> {
{ {
// Do nothing if the last instruction is a return or a return followed by a jump // Do nothing if the last instruction is a return or a return followed by a jump
} else if self.allow(Token::Semicolon)? { } else if self.allow(Token::Semicolon)? {
let r#return = Instruction::r#return(false, 0); let r#return = Instruction::r#return(false, TypeCode(0), 0);
self.emit_instruction(r#return, Type::None, self.current_position); self.emit_instruction(r#return, Type::None, self.current_position);
} else { } else {
@ -1549,7 +1600,16 @@ impl<'src> Compiler<'src> {
})?; })?;
let should_return_value = previous_expression_type != Type::None; let should_return_value = previous_expression_type != Type::None;
let r#return = Instruction::r#return(should_return_value, previous_register); let type_code = match previous_expression_type {
Type::Boolean => TypeCode::BOOLEAN,
Type::Byte => TypeCode::BYTE,
Type::Character => TypeCode::CHARACTER,
Type::Float => TypeCode::FLOAT,
Type::Integer => TypeCode::INTEGER,
Type::String => TypeCode::STRING,
_ => TypeCode(0),
};
let r#return = Instruction::r#return(should_return_value, type_code, previous_register);
self.update_return_type(previous_expression_type.clone())?; self.update_return_type(previous_expression_type.clone())?;
self.emit_instruction(r#return, Type::None, self.current_position); self.emit_instruction(r#return, Type::None, self.current_position);

View File

@ -2,10 +2,11 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::InstructionBuilder; use super::{InstructionBuilder, TypeCode};
pub struct LoadConstant { pub struct LoadConstant {
pub destination: u16, pub destination: u16,
pub type_code: TypeCode,
pub constant_index: u16, pub constant_index: u16,
pub jump_next: bool, pub jump_next: bool,
} }
@ -13,11 +14,13 @@ pub struct LoadConstant {
impl From<Instruction> for LoadConstant { impl From<Instruction> for LoadConstant {
fn from(instruction: Instruction) -> Self { fn from(instruction: Instruction) -> Self {
let destination = instruction.a_field(); let destination = instruction.a_field();
let type_code = instruction.b_type();
let constant_index = instruction.b_field(); let constant_index = instruction.b_field();
let jump_next = instruction.c_field() != 0; let jump_next = instruction.c_field() != 0;
LoadConstant { LoadConstant {
destination, destination,
type_code,
constant_index, constant_index,
jump_next, jump_next,
} }
@ -29,6 +32,7 @@ impl From<LoadConstant> for Instruction {
InstructionBuilder { InstructionBuilder {
operation: Operation::LOAD_CONSTANT, operation: Operation::LOAD_CONSTANT,
a_field: load_constant.destination, a_field: load_constant.destination,
b_type: load_constant.type_code,
b_field: load_constant.constant_index, b_field: load_constant.constant_index,
c_field: load_constant.jump_next as u16, c_field: load_constant.jump_next as u16,
..Default::default() ..Default::default()
@ -41,11 +45,20 @@ impl Display for LoadConstant {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let LoadConstant { let LoadConstant {
destination, destination,
type_code,
constant_index, constant_index,
jump_next, jump_next,
} = self; } = self;
write!(f, "R{destination} = C{constant_index}")?; match *type_code {
TypeCode::BOOLEAN => write!(f, "R_BOOL_{destination} = C_BOOL_{constant_index}")?,
TypeCode::BYTE => write!(f, "R_BYTE_{destination} = C_BYTE_{constant_index}")?,
TypeCode::CHARACTER => write!(f, "R_CHAR_{destination} = C_CHAR_{constant_index}")?,
TypeCode::FLOAT => write!(f, "R_FLOAT_{destination} = C_FLOAT_{constant_index}")?,
TypeCode::INTEGER => write!(f, "R_INT_{destination} = C_INT_{constant_index}")?,
TypeCode::STRING => write!(f, "R_STR_{destination} = C_STR_{constant_index}")?,
unknown => unknown.panic_from_unknown_code(),
}
if *jump_next { if *jump_next {
write!(f, " JUMP +1")?; write!(f, " JUMP +1")?;

View File

@ -154,6 +154,7 @@ pub use type_code::TypeCode;
use crate::NativeFunction; use crate::NativeFunction;
#[derive(Clone, Copy, PartialEq, PartialOrd)]
pub struct InstructionBuilder { pub struct InstructionBuilder {
pub operation: Operation, pub operation: Operation,
pub a_field: u16, pub a_field: u16,
@ -198,6 +199,18 @@ impl Default for InstructionBuilder {
} }
} }
impl Debug for InstructionBuilder {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{self}")
}
}
impl Display for InstructionBuilder {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.build())
}
}
/// An instruction for the Dust virtual machine. /// An instruction for the Dust virtual machine.
/// ///
/// See the [module-level documentation](index.html) for more information. /// See the [module-level documentation](index.html) for more information.
@ -275,9 +288,15 @@ impl Instruction {
}) })
} }
pub fn load_constant(destination: u16, constant_index: u16, jump_next: bool) -> Instruction { pub fn load_constant(
destination: u16,
type_code: TypeCode,
constant_index: u16,
jump_next: bool,
) -> Instruction {
Instruction::from(LoadConstant { Instruction::from(LoadConstant {
destination, destination,
type_code,
constant_index, constant_index,
jump_next, jump_next,
}) })
@ -511,9 +530,14 @@ impl Instruction {
}) })
} }
pub fn r#return(should_return_value: bool, return_register: u16) -> Instruction { pub fn r#return(
should_return_value: bool,
return_type: TypeCode,
return_register: u16,
) -> Instruction {
Instruction::from(Return { Instruction::from(Return {
should_return_value, should_return_value,
return_type,
return_register, return_register,
}) })
} }

View File

@ -2,20 +2,23 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::InstructionBuilder; use super::{InstructionBuilder, TypeCode};
pub struct Return { pub struct Return {
pub should_return_value: bool, pub should_return_value: bool,
pub return_type: TypeCode,
pub return_register: u16, pub return_register: u16,
} }
impl From<Instruction> for Return { impl From<Instruction> for Return {
fn from(instruction: Instruction) -> Self { fn from(instruction: Instruction) -> Self {
let should_return_value = instruction.b_field() != 0; let should_return_value = instruction.b_field() != 0;
let return_type = instruction.b_type();
let return_register = instruction.c_field(); let return_register = instruction.c_field();
Return { Return {
should_return_value, should_return_value,
return_type,
return_register, return_register,
} }
} }
@ -25,11 +28,13 @@ impl From<Return> for Instruction {
fn from(r#return: Return) -> Self { fn from(r#return: Return) -> Self {
let operation = Operation::RETURN; let operation = Operation::RETURN;
let b_field = r#return.should_return_value as u16; let b_field = r#return.should_return_value as u16;
let b_type = r#return.return_type;
let c_field = r#return.return_register; let c_field = r#return.return_register;
InstructionBuilder { InstructionBuilder {
operation, operation,
b_field, b_field,
b_type,
c_field, c_field,
..Default::default() ..Default::default()
} }
@ -41,11 +46,18 @@ impl Display for Return {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let Return { let Return {
should_return_value, should_return_value,
return_type,
return_register, return_register,
} = self; } = self;
if *should_return_value { if *should_return_value {
write!(f, "RETURN R{return_register}") match *return_type {
TypeCode::INTEGER => write!(f, "RETURN R_INT_{}", return_register),
TypeCode::FLOAT => write!(f, "RETURN R_FLOAT_{}", return_register),
TypeCode::BOOLEAN => write!(f, "RETURN R_BOOL_{}", return_register),
TypeCode::STRING => write!(f, "RETURN R_STRING_{}", return_register),
unknown => unknown.panic_from_unknown_code(),
}
} else { } else {
write!(f, "RETURN") write!(f, "RETURN")
} }

View File

@ -1,6 +1,6 @@
use std::fmt::Display; use std::fmt::Display;
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct TypeCode(pub u8); pub struct TypeCode(pub u8);
impl TypeCode { impl TypeCode {

View File

@ -27,6 +27,7 @@
//! //!
//! println!("{}", report); //! println!("{}", report);
//! ``` //! ```
#![feature(get_many_mut)]
pub mod chunk; pub mod chunk;
pub mod compiler; pub mod compiler;
@ -42,15 +43,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::{AbstractList, DustString, Function, RangeValue, Value};
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;

View File

@ -3,20 +3,5 @@ use std::{ops::Range, panic};
use crate::vm::ThreadData; use crate::vm::ThreadData;
pub fn panic(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool { pub fn panic(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
let position = data.current_position(); todo!()
let mut message = format!("Dust panic at {position}!");
for register_index in argument_range {
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);
message.push_str(&string);
message.push('\n');
}
panic!("{}", message)
} }

View File

@ -1,12 +1,14 @@
use std::io::{Write, stdin, stdout}; use std::io::{Write, stdin, stdout};
use std::ops::Range; use std::ops::Range;
use crate::DustString;
use crate::{ use crate::{
ConcreteValue, Value, Value,
vm::{Register, ThreadData, get_next_action}, vm::{Register, ThreadData},
}; };
pub fn read_line(data: &mut ThreadData, destination: u16, _argument_range: Range<u16>) -> bool { pub fn read_line(data: &mut ThreadData, destination: u16, _argument_range: Range<u16>) -> bool {
let current_frame = data.current_frame_mut();
let mut buffer = String::new(); let mut buffer = String::new();
if stdin().read_line(&mut buffer).is_ok() { if stdin().read_line(&mut buffer).is_ok() {
@ -14,45 +16,18 @@ pub fn read_line(data: &mut ThreadData, destination: u16, _argument_range: Range
buffer.truncate(length.saturating_sub(1)); buffer.truncate(length.saturating_sub(1));
let register = Register::Value(Value::Concrete(ConcreteValue::string(buffer))); let register = current_frame.registers.get_string_mut(destination);
data.set_register(destination, register); *register = Register::Value(DustString::from(buffer));
} }
data.next_action = get_next_action(data);
false false
} }
pub fn write(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool { pub fn write(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
let mut stdout = stdout(); todo!()
for register_index in argument_range {
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(data);
false
} }
pub fn write_line(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool { pub fn write_line(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
let mut stdout = stdout().lock(); todo!()
for register_index in argument_range {
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(data);
false
} }

View File

@ -2,39 +2,38 @@ use std::ops::Range;
use rand::Rng; use rand::Rng;
use crate::{ use crate::vm::ThreadData;
Value,
vm::{Register, ThreadData, get_next_action},
};
pub fn random_int(data: &mut ThreadData, destination: u16, argument_range: Range<u16>) -> bool { pub fn random_int(data: &mut ThreadData, destination: u16, argument_range: Range<u16>) -> bool {
let mut argument_range_iter = argument_range.into_iter(); todo!()
let (min, max) = {
let mut min = None;
loop { // let mut argument_range_iter = argument_range.into_iter();
let register_index = argument_range_iter // let (min, max) = {
.next() // let mut min = None;
.unwrap_or_else(|| panic!("No argument was passed to \"random_int\""));
let value_option = data.open_register_allow_empty_unchecked(register_index);
if let Some(argument) = value_option { // loop {
if let Some(integer) = argument.as_integer() { // let register_index = argument_range_iter
if min.is_none() { // .next()
min = Some(integer); // .unwrap_or_else(|| panic!("No argument was passed to \"random_int\""));
} else { // let value_option = data.open_register_allow_empty_unchecked(register_index);
break (min, integer);
} // if let Some(argument) = value_option {
} // if let Some(integer) = argument.as_integer() {
} // if min.is_none() {
} // min = Some(integer);
}; // } else {
// break (min, integer);
let random_integer = rand::thread_rng().gen_range(min.unwrap()..max); // }
// }
data.set_register(destination, Register::Value(Value::integer(random_integer))); // }
// }
data.next_action = get_next_action(data); // };
false // let random_integer = rand::thread_rng().gen_range(min.unwrap()..max);
// data.set_register(destination, Register::Value(Value::integer(random_integer)));
// data.next_action = get_next_action(data);
// false
} }

View File

@ -1,18 +1,20 @@
use std::ops::Range; use std::ops::Range;
use crate::{ use crate::{
ConcreteValue, Value, Value,
vm::{Register, ThreadData, get_next_action}, vm::{Register, ThreadData},
}; };
pub fn to_string(data: &mut ThreadData, destination: u16, argument_range: Range<u16>) -> bool { pub fn to_string(data: &mut ThreadData, destination: u16, argument_range: Range<u16>) -> bool {
let argument_value = data.open_register_unchecked(argument_range.start); todo!()
let argument_string = argument_value.display(data);
let register = Register::Value(Value::Concrete(ConcreteValue::string(argument_string)));
data.set_register(destination, register); // let argument_value = data.open_register_unchecked(argument_range.start);
// let argument_string = argument_value.display(data);
// let register = Register::Value(Value::Concrete(ConcreteValue::string(argument_string)));
data.next_action = get_next_action(data); // data.set_register(destination, register);
false // data.next_action = get_next_action(data);
// false
} }

View File

@ -7,58 +7,62 @@ use tracing::{Level, info, span};
use crate::{ use crate::{
DustString, DustString,
vm::{Thread, ThreadData, get_next_action}, vm::{Thread, ThreadData},
}; };
fn start_thread(data: &mut ThreadData, argument_range: Range<u16>) -> JoinHandle<()> { fn start_thread(data: &mut ThreadData, argument_range: Range<u16>) -> JoinHandle<()> {
let mut argument_range_iter = argument_range.into_iter(); todo!()
let function_argument = {
loop {
let register_index = argument_range_iter
.next()
.unwrap_or_else(|| panic!("No argument was passed to \"spawn\""));
let value_option = data.open_register_allow_empty_unchecked(register_index);
if let Some(argument) = value_option { // let mut argument_range_iter = argument_range.into_iter();
break argument; // let function_argument = {
} // loop {
} // let register_index = argument_range_iter
}; // .next()
let function = function_argument.as_function().unwrap(); // .unwrap_or_else(|| panic!("No argument was passed to \"spawn\""));
let prototype_index = function.prototype_index as usize; // let value_option = data.open_register_allow_empty_unchecked(register_index);
let current_call = data.call_stack.last_unchecked();
let prototype = current_call.chunk.prototypes[prototype_index].clone();
info!( // if let Some(argument) = value_option {
"Spawning thread for \"{}\"", // break argument;
function // }
.name // }
.as_ref() // };
.cloned() // let function = function_argument.as_function().unwrap();
.unwrap_or_else(|| DustString::from("anonymous")) // let prototype_index = function.prototype_index as usize;
); // let current_call = data.stack.last_unchecked();
// let prototype = current_call.chunk.prototypes[prototype_index].clone();
let thread_name = prototype // info!(
.name // "Spawning thread for \"{}\"",
.as_ref() // function
.map(|name| name.to_string()) // .name
.unwrap_or_else(|| "anonymous".to_string()); // .as_ref()
let mut thread = Thread::new(prototype); // .cloned()
// .unwrap_or_else(|| DustString::from("anonymous"))
// );
Builder::new() // let thread_name = prototype
.name(thread_name) // .name
.spawn(move || { // .as_ref()
let span = span!(Level::INFO, "Spawned thread"); // .map(|name| name.to_string())
let _enter = span.enter(); // .unwrap_or_else(|| "anonymous".to_string());
// let mut thread = Thread::new(prototype);
thread.run(); // Builder::new()
}) // .name(thread_name)
.expect("Critical VM Error: Failed to spawn thread") // .spawn(move || {
// let span = span!(Level::INFO, "Spawned thread");
// let _enter = span.enter();
// thread.run();
// })
// .expect("Critical VM Error: Failed to spawn thread")
} }
pub fn spawn(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool { pub fn spawn(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
let _ = start_thread(data, argument_range); todo!()
data.next_action = get_next_action(data);
false // let _ = start_thread(data, argument_range);
// data.next_action = get_next_action(data);
// false
} }

View File

@ -1,6 +1,9 @@
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use crate::{vm::ThreadData, Pointer, Type}; use crate::{
Type,
vm::{Pointer, ThreadData},
};
use super::DustString; use super::DustString;
@ -12,23 +15,7 @@ pub struct AbstractList {
impl AbstractList { impl AbstractList {
pub fn display(&self, data: &ThreadData) -> DustString { pub fn display(&self, data: &ThreadData) -> DustString {
let mut display = DustString::new(); todo!()
display.push('[');
for (i, item) in self.item_pointers.iter().enumerate() {
if i > 0 {
display.push_str(", ");
}
let item_display = data.follow_pointer_unchecked(*item).display(data);
display.push_str(&item_display);
}
display.push(']');
display
} }
} }

View File

@ -1,341 +0,0 @@
use std::fmt::{self, Display, Formatter};
use serde::{Deserialize, Serialize};
use smartstring::{LazyCompact, SmartString};
use tracing::trace;
use crate::{Type, Value, ValueError};
use super::RangeValue;
pub type DustString = SmartString<LazyCompact>;
#[derive(Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
pub enum ConcreteValue {
Boolean(bool),
Byte(u8),
Character(char),
Float(f64),
Integer(i64),
List(Vec<ConcreteValue>),
Range(RangeValue),
String(DustString),
}
impl ConcreteValue {
pub fn to_value(self) -> Value {
Value::Concrete(self)
}
pub fn list<T: Into<Vec<ConcreteValue>>>(into_list: T) -> Self {
ConcreteValue::List(into_list.into())
}
pub fn string<T: Into<SmartString<LazyCompact>>>(to_string: T) -> Self {
ConcreteValue::String(to_string.into())
}
pub fn as_string(&self) -> Option<&DustString> {
if let ConcreteValue::String(string) = self {
Some(string)
} else {
None
}
}
pub fn display(&self) -> DustString {
DustString::from(self.to_string())
}
pub fn r#type(&self) -> Type {
match self {
ConcreteValue::Boolean(_) => Type::Boolean,
ConcreteValue::Byte(_) => Type::Byte,
ConcreteValue::Character(_) => Type::Character,
ConcreteValue::Float(_) => Type::Float,
ConcreteValue::Integer(_) => Type::Integer,
ConcreteValue::List(list) => {
let item_type = list.first().map_or(Type::Any, |item| item.r#type());
Type::List(Box::new(item_type))
}
ConcreteValue::Range(range) => range.r#type(),
ConcreteValue::String(_) => Type::String,
}
}
pub fn add(&self, other: &Self) -> ConcreteValue {
use ConcreteValue::*;
match (self, other) {
(Byte(left), Byte(right)) => {
let sum = left.saturating_add(*right);
Byte(sum)
}
(Character(left), Character(right)) => {
let mut concatenated = DustString::new();
concatenated.push(*left);
concatenated.push(*right);
String(concatenated)
}
(Character(left), String(right)) => {
let mut concatenated = DustString::new();
concatenated.push(*left);
concatenated.push_str(right);
String(concatenated)
}
(Float(left), Float(right)) => {
let sum = left + right;
Float(sum)
}
(Integer(left), Integer(right)) => {
let sum = left.saturating_add(*right);
Integer(sum)
}
(String(left), Character(right)) => {
let concatenated = format!("{}{}", left, right);
String(DustString::from(concatenated))
}
(String(left), String(right)) => {
let concatenated = format!("{}{}", left, right);
String(DustString::from(concatenated))
}
_ => panic!(
"{}",
ValueError::CannotAdd(
Value::Concrete(self.clone()),
Value::Concrete(other.clone())
)
),
}
}
pub fn subtract(&self, other: &Self) -> ConcreteValue {
use ConcreteValue::*;
match (self, other) {
(Byte(left), Byte(right)) => ConcreteValue::Byte(left.saturating_sub(*right)),
(Float(left), Float(right)) => ConcreteValue::Float(left - right),
(Integer(left), Integer(right)) => ConcreteValue::Integer(left.saturating_sub(*right)),
_ => panic!(
"{}",
ValueError::CannotSubtract(
Value::Concrete(self.clone()),
Value::Concrete(other.clone())
)
),
}
}
pub fn multiply(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
use ConcreteValue::*;
let product = match (self, other) {
(Byte(left), Byte(right)) => ConcreteValue::Byte(left.saturating_mul(*right)),
(Float(left), Float(right)) => ConcreteValue::Float(left * right),
(Integer(left), Integer(right)) => ConcreteValue::Integer(left.saturating_mul(*right)),
_ => {
return Err(ValueError::CannotMultiply(
self.clone().to_value(),
other.clone().to_value(),
))
}
};
Ok(product)
}
pub fn divide(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
use ConcreteValue::*;
let quotient = match (self, other) {
(Byte(left), Byte(right)) => ConcreteValue::Byte(left.saturating_div(*right)),
(Float(left), Float(right)) => ConcreteValue::Float(left / right),
(Integer(left), Integer(right)) => ConcreteValue::Integer(left.saturating_div(*right)),
_ => {
return Err(ValueError::CannotMultiply(
self.clone().to_value(),
other.clone().to_value(),
))
}
};
Ok(quotient)
}
pub fn modulo(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
use ConcreteValue::*;
let product = match (self, other) {
(Byte(left), Byte(right)) => ConcreteValue::Byte(left.wrapping_rem(*right)),
(Float(left), Float(right)) => ConcreteValue::Float(left % right),
(Integer(left), Integer(right)) => {
ConcreteValue::Integer(left.wrapping_rem_euclid(*right))
}
_ => {
return Err(ValueError::CannotMultiply(
self.clone().to_value(),
other.clone().to_value(),
))
}
};
Ok(product)
}
pub fn negate(&self) -> ConcreteValue {
use ConcreteValue::*;
match self {
Boolean(value) => ConcreteValue::Boolean(!value),
Byte(value) => ConcreteValue::Byte(value.wrapping_neg()),
Float(value) => ConcreteValue::Float(-value),
Integer(value) => ConcreteValue::Integer(value.wrapping_neg()),
_ => panic!("{}", ValueError::CannotNegate(self.clone().to_value())),
}
}
pub fn not(&self) -> Result<ConcreteValue, ValueError> {
use ConcreteValue::*;
let not = match self {
Boolean(value) => ConcreteValue::Boolean(!value),
_ => return Err(ValueError::CannotNot(self.clone().to_value())),
};
Ok(not)
}
pub fn equals(&self, other: &ConcreteValue) -> bool {
use ConcreteValue::*;
match (self, other) {
(Boolean(left), Boolean(right)) => left == right,
(Byte(left), Byte(right)) => left == right,
(Character(left), Character(right)) => left == right,
(Float(left), Float(right)) => left == right,
(Integer(left), Integer(right)) => left == right,
(List(left), List(right)) => left == right,
(Range(left), Range(right)) => left == right,
(String(left), String(right)) => left == right,
_ => {
panic!(
"{}",
ValueError::CannotCompare(
Value::Concrete(self.clone()),
Value::Concrete(other.clone())
)
)
}
}
}
pub fn less_than(&self, other: &ConcreteValue) -> Result<ConcreteValue, ValueError> {
use ConcreteValue::*;
let less_than = match (self, other) {
(Boolean(left), Boolean(right)) => ConcreteValue::Boolean(left < right),
(Byte(left), Byte(right)) => ConcreteValue::Boolean(left < right),
(Character(left), Character(right)) => ConcreteValue::Boolean(left < right),
(Float(left), Float(right)) => ConcreteValue::Boolean(left < right),
(Integer(left), Integer(right)) => ConcreteValue::Boolean(left < right),
(List(left), List(right)) => ConcreteValue::Boolean(left < right),
(Range(left), Range(right)) => ConcreteValue::Boolean(left < right),
(String(left), String(right)) => ConcreteValue::Boolean(left < right),
_ => {
return Err(ValueError::CannotCompare(
self.clone().to_value(),
other.clone().to_value(),
))
}
};
Ok(less_than)
}
pub fn less_than_or_equals(&self, other: &ConcreteValue) -> Result<ConcreteValue, ValueError> {
use ConcreteValue::*;
let less_than_or_equal = match (self, other) {
(Boolean(left), Boolean(right)) => ConcreteValue::Boolean(left <= right),
(Byte(left), Byte(right)) => ConcreteValue::Boolean(left <= right),
(Character(left), Character(right)) => ConcreteValue::Boolean(left <= right),
(Float(left), Float(right)) => ConcreteValue::Boolean(left <= right),
(Integer(left), Integer(right)) => ConcreteValue::Boolean(left <= right),
(List(left), List(right)) => ConcreteValue::Boolean(left <= right),
(Range(left), Range(right)) => ConcreteValue::Boolean(left <= right),
(String(left), String(right)) => ConcreteValue::Boolean(left <= right),
_ => {
return Err(ValueError::CannotCompare(
self.clone().to_value(),
other.clone().to_value(),
))
}
};
Ok(less_than_or_equal)
}
}
impl Clone for ConcreteValue {
fn clone(&self) -> Self {
trace!("Cloning concrete value {}", self);
match self {
ConcreteValue::Boolean(boolean) => ConcreteValue::Boolean(*boolean),
ConcreteValue::Byte(byte) => ConcreteValue::Byte(*byte),
ConcreteValue::Character(character) => ConcreteValue::Character(*character),
ConcreteValue::Float(float) => ConcreteValue::Float(*float),
ConcreteValue::Integer(integer) => ConcreteValue::Integer(*integer),
ConcreteValue::List(list) => ConcreteValue::List(list.clone()),
ConcreteValue::Range(range) => ConcreteValue::Range(*range),
ConcreteValue::String(string) => ConcreteValue::String(string.clone()),
}
}
}
impl Display for ConcreteValue {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
ConcreteValue::Boolean(boolean) => write!(f, "{boolean}"),
ConcreteValue::Byte(byte) => write!(f, "0x{byte:02x}"),
ConcreteValue::Character(character) => write!(f, "{character}"),
ConcreteValue::Float(float) => {
write!(f, "{float}")?;
if float.fract() == 0.0 {
write!(f, ".0")?;
}
Ok(())
}
ConcreteValue::Integer(integer) => write!(f, "{integer}"),
ConcreteValue::List(list) => {
write!(f, "[")?;
for (index, item) in list.iter().enumerate() {
if index > 0 {
write!(f, ", ")?;
}
write!(f, "{item}")?;
}
write!(f, "]")
}
ConcreteValue::Range(range_value) => {
write!(f, "{range_value}")
}
ConcreteValue::String(string) => write!(f, "{string}"),
}
}
}

View File

@ -1,57 +1,40 @@
//! Runtime values used by the VM. //! Runtime values used by the VM.
mod abstract_list; mod abstract_list;
mod concrete_value;
mod function; mod function;
mod range_value; mod range_value;
pub use abstract_list::AbstractList; 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}; 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),
Character(char),
Float(f64),
Integer(i64),
String(DustString),
List(Vec<Value>),
#[serde(skip)] #[serde(skip)]
AbstractList(AbstractList), AbstractList(AbstractList),
#[serde(skip)] #[serde(skip)]
Function(Function), Function(Function),
} }
impl Value { 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> { pub fn as_boolean(&self) -> Option<bool> {
if let Value::Concrete(ConcreteValue::Boolean(boolean)) = self { if let Value::Boolean(boolean) = self {
Some(*boolean) Some(*boolean)
} else { } else {
None None
@ -59,7 +42,7 @@ impl Value {
} }
pub fn as_byte(&self) -> Option<u8> { pub fn as_byte(&self) -> Option<u8> {
if let Value::Concrete(ConcreteValue::Byte(byte)) = self { if let Value::Byte(byte) = self {
Some(*byte) Some(*byte)
} else { } else {
None None
@ -67,7 +50,7 @@ impl Value {
} }
pub fn as_character(&self) -> Option<char> { pub fn as_character(&self) -> Option<char> {
if let Value::Concrete(ConcreteValue::Character(character)) = self { if let Value::Character(character) = self {
Some(*character) Some(*character)
} else { } else {
None None
@ -75,7 +58,7 @@ impl Value {
} }
pub fn as_float(&self) -> Option<f64> { pub fn as_float(&self) -> Option<f64> {
if let Value::Concrete(ConcreteValue::Float(float)) = self { if let Value::Float(float) = self {
Some(*float) Some(*float)
} else { } else {
None None
@ -91,7 +74,7 @@ impl Value {
} }
pub fn as_integer(&self) -> Option<i64> { pub fn as_integer(&self) -> Option<i64> {
if let Value::Concrete(ConcreteValue::Integer(integer)) = self { if let Value::Integer(integer) = self {
Some(*integer) Some(*integer)
} else { } else {
None None
@ -99,15 +82,15 @@ impl Value {
} }
pub fn as_string(&self) -> Option<&DustString> { pub fn as_string(&self) -> Option<&DustString> {
if let Value::Concrete(ConcreteValue::String(value)) = self { if let Value::String(string) = self {
Some(value) Some(string)
} else { } else {
None None
} }
} }
pub fn is_string(&self) -> bool { pub fn is_string(&self) -> bool {
matches!(self, Value::Concrete(ConcreteValue::String(_))) matches!(self, Value::String(_))
} }
pub fn is_function(&self) -> bool { pub fn is_function(&self) -> bool {
@ -116,7 +99,13 @@ impl Value {
pub fn r#type(&self) -> Type { pub fn r#type(&self) -> Type {
match self { match self {
Value::Concrete(concrete_value) => concrete_value.r#type(), Value::Boolean(_) => Type::Boolean,
Value::Byte(_) => Type::Byte,
Value::Character(_) => Type::Character,
Value::Float(_) => Type::Float,
Value::Integer(_) => Type::Integer,
Value::String(_) => Type::String,
Value::List(_) => Type::List(Box::new(Type::Any)),
Value::AbstractList(AbstractList { item_type, .. }) => { Value::AbstractList(AbstractList { item_type, .. }) => {
Type::List(Box::new(item_type.clone())) Type::List(Box::new(item_type.clone()))
} }
@ -124,105 +113,32 @@ impl Value {
} }
} }
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 { pub fn display(&self, data: &ThreadData) -> DustString {
match self { match self {
Value::Boolean(boolean) => DustString::from(boolean.to_string()),
Value::Byte(byte) => DustString::from(byte.to_string()),
Value::Character(character) => DustString::from(character.to_string()),
Value::Float(float) => DustString::from(float.to_string()),
Value::Integer(integer) => DustString::from(integer.to_string()),
Value::List(list) => {
let mut display = DustString::new();
display.push_str("[");
for (index, value) in list.iter().enumerate() {
if index > 0 {
display.push_str(", ");
}
display.push_str(&value.display(data));
}
display.push_str("]");
display
}
Value::String(string) => string.clone(),
Value::AbstractList(list) => list.display(data), Value::AbstractList(list) => list.display(data),
Value::Concrete(concrete_value) => concrete_value.display(),
Value::Function(function) => DustString::from(function.to_string()), Value::Function(function) => DustString::from(function.to_string()),
} }
} }
@ -231,60 +147,27 @@ impl Value {
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::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, "[")?;
for (index, value) in list.iter().enumerate() {
if index > 0 {
write!(f, ", ")?;
}
write!(f, "{}", value)?;
}
write!(f, "]")
}
Value::AbstractList(list) => write!(f, "{list}"), Value::AbstractList(list) => write!(f, "{list}"),
Value::Function(function) => write!(f, "{function}"), Value::Function(function) => write!(f, "{function}"),
} }
} }
} }
#[derive(Clone, Debug, PartialEq)]
pub enum ValueError {
CannotAdd(Value, Value),
CannotAnd(Value, Value),
CannotCompare(Value, Value),
CannotDivide(Value, Value),
CannotModulo(Value, Value),
CannotMultiply(Value, Value),
CannotNegate(Value),
CannotNot(Value),
CannotSubtract(Value, Value),
CannotOr(Value, Value),
}
impl Display for ValueError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
ValueError::CannotAdd(left, right) => {
write!(f, "Cannot add {left} and {right}")
}
ValueError::CannotAnd(left, right) => {
write!(f, "Cannot use logical AND operation on {left} and {right}")
}
ValueError::CannotCompare(left, right) => {
write!(f, "Cannot compare {left} and {right}")
}
ValueError::CannotDivide(left, right) => {
write!(f, "Cannot divide {left} by {right}")
}
ValueError::CannotModulo(left, right) => {
write!(f, "Cannot use modulo operation on {left} and {right}")
}
ValueError::CannotMultiply(left, right) => {
write!(f, "Cannot multiply {left} by {right}")
}
ValueError::CannotNegate(value) => {
write!(f, "Cannot negate {value}")
}
ValueError::CannotNot(value) => {
write!(f, "Cannot use logical NOT operation on {value}")
}
ValueError::CannotSubtract(left, right) => {
write!(f, "Cannot subtract {right} from {left}")
}
ValueError::CannotOr(left, right) => {
write!(f, "Cannot use logical OR operation on {left} and {right}")
}
}
}
}

254
dust-lang/src/vm/action.rs Normal file
View File

@ -0,0 +1,254 @@
use tracing::trace;
use crate::{
AbstractList, Instruction, Operation, Type, Value,
instruction::{InstructionBuilder, LoadBoolean, TypeCode},
};
use super::{Pointer, Register, thread::ThreadData};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Action {
pub logic: ActionLogic,
pub fields: InstructionBuilder,
}
impl From<&Instruction> for Action {
fn from(instruction: &Instruction) -> Self {
let logic = match instruction.operation() {
Operation::POINT => point,
Operation::CLOSE => close,
Operation::LOAD_BOOLEAN => load_boolean,
Operation::LOAD_CONSTANT => load_constant,
Operation::LOAD_LIST => load_list,
Operation::LOAD_FUNCTION => load_function,
Operation::LOAD_SELF => load_self,
Operation::GET_LOCAL => get_local,
Operation::SET_LOCAL => set_local,
Operation::ADD => add,
Operation::JUMP => jump,
Operation::RETURN => r#return,
unknown => unknown.panic_from_unknown_code(),
};
let fields = InstructionBuilder {
operation: instruction.operation(),
b_is_constant: instruction.b_is_constant(),
c_is_constant: instruction.c_is_constant(),
d_field: instruction.d_field(),
b_type: instruction.b_type(),
c_type: instruction.c_type(),
a_field: instruction.a_field(),
b_field: instruction.b_field(),
c_field: instruction.c_field(),
};
Action { logic, fields }
}
}
pub type ActionLogic = fn(InstructionBuilder, &mut ThreadData);
pub fn point(fields: InstructionBuilder, data: &mut ThreadData) {
todo!()
}
pub fn close(fields: InstructionBuilder, data: &mut ThreadData) {
let from = fields.b_field;
let to = fields.c_field;
let r#type = fields.b_type;
let current_frame = data.current_frame_mut();
match r#type {
TypeCode::INTEGER => {
let registers = current_frame.registers.get_many_integer_mut(from, to);
for register in registers {
*register = Register::Empty;
}
}
_ => todo!(),
}
}
pub fn load_boolean(fields: InstructionBuilder, data: &mut ThreadData) {
let destination = fields.a_field;
let boolean = fields.b_field != 0;
let new_register = Register::Value(boolean);
let current_frame = data.current_frame_mut();
let old_register = current_frame.registers.get_boolean_mut(destination);
*old_register = new_register;
if fields.c_field != 0 {
current_frame.instruction_pointer += 1;
}
}
pub fn load_constant(fields: InstructionBuilder, data: &mut ThreadData) {
let destination = fields.a_field;
let constant_index = fields.b_field;
let r#type = fields.b_type;
let current_frame = data.current_frame_mut();
match r#type {
TypeCode::INTEGER => {
let value = current_frame
.prototype
.constants
.get_integer(constant_index)
.unwrap();
let new_register = Register::Value(value);
let old_register = current_frame.registers.get_integer_mut(destination);
*old_register = new_register;
}
unknown => todo!(),
};
if fields.c_field != 0 {
current_frame.instruction_pointer += 1;
}
}
pub fn load_list(fields: InstructionBuilder, data: &mut ThreadData) {
todo!()
}
pub fn load_function(fields: InstructionBuilder, data: &mut ThreadData) {
todo!()
}
pub fn load_self(fields: InstructionBuilder, data: &mut ThreadData) {
todo!()
}
pub fn get_local(fields: InstructionBuilder, data: &mut ThreadData) {
let destination = fields.a_field;
let local_index = fields.b_field;
let r#type = fields.b_type;
let current_frame = data.current_frame_mut();
match r#type {
TypeCode::INTEGER => {
let new_register = Register::Pointer::<i64>(Pointer::ConstantInteger(local_index));
let old_register = current_frame.registers.get_integer_mut(destination);
*old_register = new_register;
}
unknown => todo!(),
}
}
pub fn set_local(fields: InstructionBuilder, data: &mut ThreadData) {
let register_index = fields.b_field;
let local_index = fields.c_field;
let r#type = fields.b_type;
let current_frame = data.current_frame_mut();
match r#type {
TypeCode::INTEGER => {
let new_register = Register::Pointer::<i64>(Pointer::ConstantInteger(local_index));
let old_register = current_frame.registers.get_integer_mut(register_index);
*old_register = new_register;
}
unknown => todo!(),
}
}
pub fn add(fields: InstructionBuilder, data: &mut ThreadData) {
let destination = fields.a_field;
let left_index = fields.b_field;
let left_is_constant = fields.b_is_constant;
let right_index = fields.c_field;
let right_is_constant = fields.c_is_constant;
let left_type = fields.b_type;
let right_type = fields.c_type;
let current_frame = data.current_frame_mut();
match (left_type, right_type) {
(TypeCode::INTEGER, TypeCode::INTEGER) => {
let left = if left_is_constant {
current_frame
.prototype
.constants
.get_integer(left_index)
.unwrap()
} else {
*current_frame
.registers
.get_integer(left_index)
.expect_value()
};
let right = if right_is_constant {
current_frame
.prototype
.constants
.get_integer(right_index)
.unwrap()
} else {
*current_frame
.registers
.get_integer(right_index)
.expect_value()
};
let new_register = Register::Value(left + right);
let old_register = current_frame.registers.get_integer_mut(destination);
*old_register = new_register;
}
unknown => todo!(),
}
}
pub fn jump(fields: InstructionBuilder, data: &mut ThreadData) {
let offset = fields.b_field as usize;
let is_positive = fields.c_field != 0;
let current_frame = data.current_frame_mut();
if is_positive {
current_frame.instruction_pointer += offset;
} else {
current_frame.instruction_pointer -= offset + 1;
}
}
pub fn r#return(fields: InstructionBuilder, data: &mut ThreadData) {
trace!("Returning. Stack size = {}", data.stack.len());
let should_return_value = fields.b_field != 0;
let r#type = fields.b_type;
let return_register = fields.c_field;
let current_frame = data.stack.pop().unwrap();
match r#type {
TypeCode::INTEGER => {
if data.stack.is_empty() {
if should_return_value {
let return_value = current_frame
.registers
.get_integer(return_register)
.expect_value();
data.return_value = Some(Some(Value::Integer(*return_value)));
} else {
data.return_value = Some(None);
}
return;
}
if should_return_value {
let return_value = current_frame
.registers
.get_integer(return_register)
.expect_value();
let outer_frame = data.current_frame_mut();
let register = outer_frame.registers.get_integer_mut(return_register);
*register = Register::Value(*return_value);
}
}
_ => todo!(),
}
}

View File

@ -0,0 +1,41 @@
use std::{
fmt::{self, Debug, Display, Formatter},
sync::Arc,
};
use crate::{Chunk, DustString};
use super::register_table::RegisterTable;
#[derive(Debug)]
pub struct CallFrame {
pub prototype: Arc<Chunk>,
pub registers: RegisterTable,
pub return_register: u16,
pub instruction_pointer: usize,
}
impl CallFrame {
pub fn new(prototype: Arc<Chunk>, return_register: u16) -> Self {
Self {
prototype,
return_register,
instruction_pointer: 0,
registers: RegisterTable::new(),
}
}
}
impl Display for CallFrame {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(
f,
"{} IP = {}",
self.prototype
.name
.as_ref()
.unwrap_or(&DustString::from("anonymous")),
self.instruction_pointer,
)
}
}

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,19 +1,16 @@
//! Virtual machine and errors //! Virtual machine and errors
mod function_call; mod action;
mod run_action; mod call_frame;
mod stack; mod pointer;
mod register_table;
mod thread; mod thread;
use std::{ use std::{sync::Arc, thread::Builder};
fmt::{self, Debug, Display, Formatter},
sync::Arc,
thread::Builder,
};
pub use function_call::FunctionCall; pub use action::Action;
pub use run_action::RunAction; pub use call_frame::CallFrame;
pub(crate) use run_action::get_next_action; pub use pointer::Pointer;
pub use stack::Stack; pub use register_table::{Register, RegisterTable};
pub use thread::{Thread, ThreadData}; pub use thread::{Thread, ThreadData};
use crossbeam_channel::bounded; use crossbeam_channel::bounded;
@ -62,39 +59,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)
}
}
}
}

View File

@ -0,0 +1,69 @@
use std::fmt::{self, Debug, Display, Formatter};
#[derive(Copy, Clone, PartialEq, PartialOrd)]
pub enum Pointer {
ConstantBoolean(u16),
ConstantByte(u16),
ConstantCharacter(u16),
ConstantFloat(u16),
ConstantInteger(u16),
ConstantString(u16),
RegisterBoolean(u16),
RegisterByte(u16),
RegisterCharacter(u16),
RegisterFloat(u16),
RegisterInteger(u16),
RegisterString(u16),
ForeignConstantBoolean(u16, u16),
ForeignConstantByte(u16, u16),
ForeignConstantCharacter(u16, u16),
ForeignConstantFloat(u16, u16),
ForeignConstantInteger(u16, u16),
ForeignConstantString(u16, u16),
ForeignRegisterBoolean(u16, u16),
ForeignRegisterByte(u16, u16),
ForeignRegisterCharacter(u16, u16),
ForeignRegisterFloat(u16, u16),
ForeignRegisterInteger(u16, u16),
ForeignRegisterString(u16, u16),
}
impl Debug for Pointer {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{self}")
}
}
impl Display for Pointer {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Pointer::ConstantBoolean(index) => write!(f, "pc_bool({index})"),
Pointer::ConstantByte(index) => write!(f, "pc_byte({index})"),
Pointer::ConstantCharacter(index) => write!(f, "pc_char({index})"),
Pointer::ConstantFloat(index) => write!(f, "pc_float({index})"),
Pointer::ConstantInteger(index) => write!(f, "pc_int({index})"),
Pointer::ConstantString(index) => write!(f, "pc_str({index})"),
Pointer::RegisterBoolean(index) => write!(f, "pr_bool({index})"),
Pointer::RegisterByte(index) => write!(f, "pr_byte({index})"),
Pointer::RegisterCharacter(index) => write!(f, "pr_char({index})"),
Pointer::RegisterFloat(index) => write!(f, "przfloat({index})"),
Pointer::RegisterInteger(index) => write!(f, "pr_int({index})"),
Pointer::RegisterString(index) => write!(f, "pr_str({index})"),
Pointer::ForeignConstantBoolean(index, _) => write!(f, "pfc_bool({index})"),
Pointer::ForeignConstantByte(index, _) => write!(f, "pfc_byte({index})"),
Pointer::ForeignConstantCharacter(index, _) => write!(f, "pfc_char({index})"),
Pointer::ForeignConstantFloat(index, _) => write!(f, "pfc_float({index})"),
Pointer::ForeignConstantInteger(index, _) => write!(f, "pfc_int({index})"),
Pointer::ForeignConstantString(index, _) => write!(f, "pfc_str({index})"),
Pointer::ForeignRegisterBoolean(index, _) => write!(f, "pfr_bool({index})"),
Pointer::ForeignRegisterByte(index, _) => write!(f, "pfr_byte({index})"),
Pointer::ForeignRegisterCharacter(index, _) => write!(f, "pfr_char({index})"),
Pointer::ForeignRegisterFloat(index, _) => write!(f, "pfr_float({index})"),
Pointer::ForeignRegisterInteger(index, _) => write!(f, "pfr_int({index})"),
Pointer::ForeignRegisterString(index, _) => write!(f, "pfr_str({index})"),
}
}
}

View File

@ -0,0 +1,185 @@
use smallvec::{SmallVec, smallvec};
use tracing::trace;
use crate::DustString;
use super::Pointer;
#[derive(Debug, Clone)]
pub enum Register<T: Clone> {
Empty,
Value(T),
Pointer(Pointer),
}
impl<T: Clone> Register<T> {
pub fn expect_value(&self) -> &T {
if let Self::Value(value) = self {
value
} else {
panic!("Expected a value")
}
}
}
#[derive(Debug)]
pub struct RegisterTable {
booleans: SmallVec<[Register<bool>; 64]>,
bytes: SmallVec<[Register<u8>; 64]>,
characters: SmallVec<[Register<char>; 64]>,
floats: SmallVec<[Register<f64>; 64]>,
integers: SmallVec<[Register<i64>; 64]>,
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],
}
}
pub fn get_boolean(&self, index: u16) -> &Register<bool> {
let index = index as usize;
if cfg!(debug_assertions) {
self.booleans.get(index).unwrap()
} else {
unsafe { self.booleans.get(index).unwrap_unchecked() }
}
}
pub fn get_boolean_mut(&mut self, index: u16) -> &mut Register<bool> {
let index = index as usize;
if cfg!(debug_assertions) {
self.booleans.get_mut(index).unwrap()
} else {
unsafe { self.booleans.get_mut(index).unwrap_unchecked() }
}
}
pub fn get_byte(&self, index: u16) -> &Register<u8> {
let index = index as usize;
if cfg!(debug_assertions) {
self.bytes.get(index).unwrap()
} else {
unsafe { self.bytes.get(index).unwrap_unchecked() }
}
}
pub fn get_byte_mut(&mut self, index: u16) -> &mut Register<u8> {
let index = index as usize;
if cfg!(debug_assertions) {
self.bytes.get_mut(index).unwrap()
} else {
unsafe { self.bytes.get_mut(index).unwrap_unchecked() }
}
}
pub fn get_character(&self, index: u16) -> &Register<char> {
let index = index as usize;
if cfg!(debug_assertions) {
self.characters.get(index).unwrap()
} else {
unsafe { self.characters.get(index).unwrap_unchecked() }
}
}
pub fn get_character_mut(&mut self, index: u16) -> &mut Register<char> {
let index = index as usize;
if cfg!(debug_assertions) {
self.characters.get_mut(index).unwrap()
} else {
unsafe { self.characters.get_mut(index).unwrap_unchecked() }
}
}
pub fn get_float(&self, index: u16) -> &Register<f64> {
let index = index as usize;
if cfg!(debug_assertions) {
self.floats.get(index).unwrap()
} else {
unsafe { self.floats.get(index).unwrap_unchecked() }
}
}
pub fn get_float_mut(&mut self, index: u16) -> &mut Register<f64> {
let index = index as usize;
if cfg!(debug_assertions) {
self.floats.get_mut(index).unwrap()
} else {
unsafe { self.floats.get_mut(index).unwrap_unchecked() }
}
}
pub fn get_integer(&self, index: u16) -> &Register<i64> {
let index = index as usize;
if cfg!(debug_assertions) {
self.integers.get(index).unwrap()
} else {
unsafe { self.integers.get(index).unwrap_unchecked() }
}
}
pub fn get_integer_mut(&mut self, index: u16) -> &mut Register<i64> {
let index = index as usize;
if cfg!(debug_assertions) {
self.integers.get_mut(index).unwrap()
} else {
unsafe { self.integers.get_mut(index).unwrap_unchecked() }
}
}
pub fn set_integer(&mut self, index: u16, value: i64) {
trace!("Set R_INT_{index} to value {value}");
let index = index as usize;
self.integers[index] = Register::Value(value);
}
pub fn get_many_integer_mut(&mut self, from: u16, to: u16) -> &mut [Register<i64>] {
let from = from as usize;
let to = to as usize;
if cfg!(debug_assertions) {
self.integers.get_many_mut([from..to]).unwrap()[0]
} else {
unsafe { self.integers.get_many_mut([from..to]).unwrap_unchecked()[0] }
}
}
pub fn get_string(&self, index: u16) -> &Register<DustString> {
let index = index as usize;
if cfg!(debug_assertions) {
self.strings.get(index).unwrap()
} else {
unsafe { self.strings.get(index).unwrap_unchecked() }
}
}
pub fn get_string_mut(&mut self, index: u16) -> &mut Register<DustString> {
let index = index as usize;
if cfg!(debug_assertions) {
self.strings.get_mut(index).unwrap()
} else {
unsafe { self.strings.get_mut(index).unwrap_unchecked() }
}
}
}

View File

@ -1,893 +0,0 @@
use tracing::trace;
use crate::{
AbstractList, ConcreteValue, Instruction, Operand, Type, Value,
instruction::{
Add, Call, CallNative, Close, Divide, Equal, GetLocal, Jump, Less, LessEqual, LoadBoolean,
LoadConstant, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not, Point,
Return, SetLocal, Subtract, Test, TestSet, TypeCode,
},
vm::FunctionCall,
};
use super::{Pointer, Register, thread::ThreadData};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct RunAction {
pub logic: RunnerLogic,
pub instruction: Instruction,
}
impl From<Instruction> for RunAction {
fn from(instruction: Instruction) -> Self {
let operation = instruction.operation();
let logic = RUNNER_LOGIC_TABLE[operation.0 as usize];
RunAction { logic, instruction }
}
}
pub type RunnerLogic = fn(Instruction, &mut ThreadData) -> bool;
pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [
point,
close,
load_boolean,
load_constant,
load_function,
load_list,
load_self,
get_local,
set_local,
add,
subtract,
multiply,
divide,
modulo,
equal,
less,
less_equal,
negate,
not,
test,
test_set,
call,
call_native,
jump,
r#return,
];
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];
current_call.ip += 1;
RunAction { logic, instruction }
}
pub fn point(instruction: Instruction, data: &mut ThreadData) -> bool {
let Point { from, to } = instruction.into();
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::Register(to));
data.set_register(from, register);
}
data.next_action = get_next_action(data);
false
}
pub fn close(instruction: Instruction, data: &mut ThreadData) -> bool {
let Close { from, to } = instruction.into();
for register_index in from..to {
data.set_register(register_index, Register::Empty);
}
data.next_action = get_next_action(data);
false
}
pub fn load_boolean(instruction: Instruction, data: &mut ThreadData) -> bool {
let LoadBoolean {
destination,
value,
jump_next,
} = instruction.into();
let boolean = Value::Concrete(ConcreteValue::Boolean(value));
let register = Register::Value(boolean);
data.set_register(destination, register);
if jump_next {
let current_call = data.call_stack.last_mut_unchecked();
current_call.ip += 1;
}
data.next_action = get_next_action(data);
false
}
pub fn load_constant(instruction: Instruction, data: &mut ThreadData) -> bool {
let LoadConstant {
destination,
constant_index,
jump_next,
} = instruction.into();
let register = Register::Pointer(Pointer::Constant(constant_index));
trace!("Load constant {constant_index} into R{destination}");
data.set_register(destination, register);
if jump_next {
let current_call = data.call_stack.last_mut_unchecked();
current_call.ip += 1;
}
data.next_action = get_next_action(data);
false
}
pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> bool {
let LoadList {
destination,
start_register,
jump_next,
} = instruction.into();
let mut item_pointers = Vec::with_capacity((destination - start_register) as usize);
let mut item_type = Type::Any;
for register_index in start_register..destination {
match data.get_register_unchecked(register_index) {
Register::Empty => continue,
Register::Value(value) => {
if item_type == Type::Any {
item_type = value.r#type();
}
}
Register::Pointer(pointer) => {
if item_type == Type::Any {
item_type = data.follow_pointer_unchecked(*pointer).r#type();
}
}
}
let pointer = Pointer::Register(register_index);
item_pointers.push(pointer);
}
let list_value = Value::AbstractList(AbstractList {
item_type,
item_pointers,
});
let register = Register::Value(list_value);
data.set_register(destination, register);
data.next_action = get_next_action(data);
false
}
pub fn load_function(instruction: Instruction, data: &mut ThreadData) -> bool {
let LoadFunction {
destination,
prototype_index,
jump_next,
} = instruction.into();
let prototype_index = prototype_index as usize;
let current_call = data.call_stack.last_mut_unchecked();
let prototype = &current_call.chunk.prototypes[prototype_index];
let function = prototype.as_function();
let register = Register::Value(Value::Function(function));
data.set_register(destination, register);
data.next_action = get_next_action(data);
false
}
pub fn load_self(instruction: Instruction, data: &mut ThreadData) -> bool {
let LoadSelf {
destination,
jump_next,
} = instruction.into();
let current_call = data.call_stack.last_mut_unchecked();
let prototype = &current_call.chunk;
let function = prototype.as_function();
let register = Register::Value(Value::Function(function));
data.set_register(destination, register);
data.next_action = get_next_action(data);
false
}
pub fn get_local(instruction: Instruction, data: &mut ThreadData) -> bool {
let GetLocal {
destination,
local_index,
} = instruction.into();
let local_register_index = data.get_local_register(local_index);
let register = Register::Pointer(Pointer::Register(local_register_index));
data.set_register(destination, register);
data.next_action = get_next_action(data);
false
}
pub fn set_local(instruction: Instruction, data: &mut ThreadData) -> bool {
let SetLocal {
register_index,
local_index,
} = instruction.into();
let local_register_index = data.get_local_register(local_index);
let register = Register::Pointer(Pointer::Register(register_index));
data.set_register(local_register_index, register);
data.next_action = get_next_action(data);
false
}
pub fn add(instruction: Instruction, data: &mut ThreadData) -> bool {
let Add {
destination,
left,
left_type,
right,
right_type,
} = instruction.into();
let sum = match (left_type, right_type) {
(TypeCode::INTEGER, TypeCode::INTEGER) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_integer()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_integer()
.unwrap_unchecked()
};
ConcreteValue::Integer(left + right)
}
(TypeCode::FLOAT, TypeCode::FLOAT) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_float()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_float()
.unwrap_unchecked()
};
ConcreteValue::Float(left + right)
}
_ => panic!("VM Error: Cannot add values"),
};
let register = Register::Value(Value::Concrete(sum));
data.set_register(destination, register);
data.next_action = get_next_action(data);
false
}
pub fn subtract(instruction: Instruction, data: &mut ThreadData) -> bool {
let Subtract {
destination,
left,
left_type,
right,
right_type,
} = instruction.into();
let difference = match (left_type, right_type) {
(TypeCode::INTEGER, TypeCode::INTEGER) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_integer()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_integer()
.unwrap_unchecked()
};
ConcreteValue::Integer(left - right)
}
(TypeCode::FLOAT, TypeCode::FLOAT) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_float()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_float()
.unwrap_unchecked()
};
ConcreteValue::Float(left - right)
}
_ => panic!("VM Error: Cannot subtract values"),
};
let register = Register::Value(Value::Concrete(difference));
data.set_register(destination, register);
data.next_action = get_next_action(data);
false
}
pub fn multiply(instruction: Instruction, data: &mut ThreadData) -> bool {
let Multiply {
destination,
left,
left_type,
right,
right_type,
} = instruction.into();
let product = match (left_type, right_type) {
(TypeCode::INTEGER, TypeCode::INTEGER) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_integer()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_integer()
.unwrap_unchecked()
};
ConcreteValue::Integer(left * right)
}
(TypeCode::FLOAT, TypeCode::FLOAT) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_float()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_float()
.unwrap_unchecked()
};
ConcreteValue::Float(left * right)
}
_ => panic!("VM Error: Cannot multiply values"),
};
let register = Register::Value(Value::Concrete(product));
data.set_register(destination, register);
data.next_action = get_next_action(data);
false
}
pub fn divide(instruction: Instruction, data: &mut ThreadData) -> bool {
let Divide {
destination,
left,
left_type,
right,
right_type,
} = instruction.into();
let quotient = match (left_type, right_type) {
(TypeCode::INTEGER, TypeCode::INTEGER) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_integer()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_integer()
.unwrap_unchecked()
};
ConcreteValue::Integer(left / right)
}
(TypeCode::FLOAT, TypeCode::FLOAT) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_float()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_float()
.unwrap_unchecked()
};
ConcreteValue::Float(left / right)
}
_ => panic!("VM Error: Cannot divide values"),
};
let register = Register::Value(Value::Concrete(quotient));
data.set_register(destination, register);
data.next_action = get_next_action(data);
false
}
pub fn modulo(instruction: Instruction, data: &mut ThreadData) -> bool {
let Modulo {
destination,
left,
left_type,
right,
right_type,
} = instruction.into();
let remainder = match (left_type, right_type) {
(TypeCode::INTEGER, TypeCode::INTEGER) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_integer()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_integer()
.unwrap_unchecked()
};
ConcreteValue::Integer(left % right)
}
_ => panic!("VM Error: Cannot modulo values"),
};
let register = Register::Value(Value::Concrete(remainder));
data.set_register(destination, register);
data.next_action = get_next_action(data);
false
}
pub fn test(instruction: Instruction, data: &mut ThreadData) -> bool {
let Test {
operand_register,
test_value,
} = instruction.into();
let value = data.open_register_unchecked(operand_register);
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
*boolean
} else {
panic!("VM Error: Expected boolean value for TEST operation",);
};
if boolean == test_value {
let current_call = data.call_stack.last_mut_unchecked();
current_call.ip += 1;
}
data.next_action = get_next_action(data);
false
}
pub fn test_set(instruction: Instruction, data: &mut ThreadData) -> bool {
let TestSet {
destination,
argument,
test_value,
} = instruction.into();
let value = data.get_argument_unchecked(argument);
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
*boolean
} else {
panic!("VM Error: Expected boolean value for TEST_SET operation",);
};
if boolean == test_value {
} else {
let pointer = match argument {
Operand::Constant(constant_index) => Pointer::Constant(constant_index),
Operand::Register(register_index) => Pointer::Register(register_index),
};
let register = Register::Pointer(pointer);
data.set_register(destination, register);
}
data.next_action = get_next_action(data);
false
}
pub fn equal(instruction: Instruction, data: &mut ThreadData) -> bool {
let Equal {
comparator,
left,
left_type,
right,
right_type,
} = instruction.into();
let is_equal = match (left_type, right_type) {
(TypeCode::INTEGER, TypeCode::INTEGER) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_integer()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_integer()
.unwrap_unchecked()
};
left == right
}
(TypeCode::FLOAT, TypeCode::FLOAT) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_float()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_float()
.unwrap_unchecked()
};
left == right
}
(TypeCode::BOOLEAN, TypeCode::BOOLEAN) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_boolean()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_boolean()
.unwrap_unchecked()
};
left == right
}
(TypeCode::STRING, TypeCode::STRING) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_string()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_string()
.unwrap_unchecked()
};
left == right
}
_ => panic!("VM Error: Cannot compare values"),
};
if is_equal == comparator {
let current_call = data.call_stack.last_mut_unchecked();
current_call.ip += 1;
}
data.next_action = get_next_action(data);
false
}
pub fn less(instruction: Instruction, data: &mut ThreadData) -> bool {
let Less {
comparator,
left,
left_type,
right,
right_type,
} = instruction.into();
let is_less = match (left_type, right_type) {
(TypeCode::INTEGER, TypeCode::INTEGER) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_integer()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_integer()
.unwrap_unchecked()
};
left < right
}
(TypeCode::FLOAT, TypeCode::FLOAT) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_float()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_float()
.unwrap_unchecked()
};
left < right
}
_ => panic!("VM Error: Cannot compare values"),
};
if is_less == comparator {
let current_call = data.call_stack.last_mut_unchecked();
current_call.ip += 1;
}
data.next_action = get_next_action(data);
false
}
pub fn less_equal(instruction: Instruction, data: &mut ThreadData) -> bool {
let LessEqual {
comparator,
left,
left_type,
right,
right_type,
} = instruction.into();
let is_less_or_equal = match (left_type, right_type) {
(TypeCode::INTEGER, TypeCode::INTEGER) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_integer()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_integer()
.unwrap_unchecked()
};
left <= right
}
(TypeCode::FLOAT, TypeCode::FLOAT) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_float()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_float()
.unwrap_unchecked()
};
left <= right
}
_ => panic!("VM Error: Cannot compare values"),
};
if is_less_or_equal == comparator {
let current_call = data.call_stack.last_mut_unchecked();
current_call.ip += 1;
}
data.next_action = get_next_action(data);
false
}
pub fn negate(instruction: Instruction, data: &mut ThreadData) -> bool {
let Negate {
destination,
argument,
argument_type,
} = instruction.into();
let argument = data.get_argument_unchecked(argument);
let negated = argument.negate();
let register = Register::Value(negated);
data.set_register(destination, register);
data.next_action = get_next_action(data);
false
}
pub fn not(instruction: Instruction, data: &mut ThreadData) -> bool {
let Not {
destination,
argument,
} = instruction.into();
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));
data.set_register(destination, register);
data.next_action = get_next_action(data);
false
}
pub fn jump(instruction: Instruction, data: &mut ThreadData) -> bool {
let Jump {
offset,
is_positive,
} = instruction.into();
let offset = offset as usize;
let current_call = data.call_stack.last_mut_unchecked();
if is_positive {
current_call.ip += offset;
} else {
current_call.ip -= offset + 1;
}
data.next_action = get_next_action(data);
false
}
pub fn call(instruction: Instruction, data: &mut ThreadData) -> bool {
let Call {
destination: return_register,
function_register,
argument_count,
is_recursive,
} = instruction.into();
let current_call = data.call_stack.last_unchecked();
let first_argument_register = return_register - argument_count;
let prototype = if is_recursive {
current_call.chunk.clone()
} else {
let function = data
.open_register_unchecked(function_register)
.as_function()
.unwrap();
current_call.chunk.prototypes[function.prototype_index as usize].clone()
};
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 = data.open_register_allow_empty_unchecked(register_index);
let argument = if let Some(value) = value_option {
value.clone()
} else {
continue;
};
next_call.registers[argument_index] = Register::Value(argument);
argument_index += 1;
}
data.call_stack.push(next_call);
data.next_action = get_next_action(data);
false
}
pub fn call_native(instruction: Instruction, data: &mut ThreadData) -> bool {
let CallNative {
destination,
function,
argument_count,
} = instruction.into();
let first_argument_index = destination - argument_count;
let argument_range = first_argument_index..destination;
function.call(data, destination, argument_range)
}
pub fn r#return(instruction: Instruction, data: &mut ThreadData) -> bool {
trace!("Returning with call stack:\n{}", data.call_stack);
let Return {
should_return_value,
return_register,
} = instruction.into();
let (destination, return_value) = if data.call_stack.len() == 1 {
if should_return_value {
data.return_value_index = Some(return_register);
};
return true;
} else {
let return_value = data.empty_register_or_clone_constant_unchecked(return_register);
let destination = data.call_stack.pop_unchecked().return_register;
(destination, return_value)
};
if should_return_value {
data.set_register(destination, Register::Value(return_value));
}
data.next_action = get_next_action(data);
false
}
#[cfg(test)]
mod tests {
use crate::Operation;
use super::*;
const ALL_OPERATIONS: [(Operation, RunnerLogic); 24] = [
(Operation::POINT, point),
(Operation::CLOSE, close),
(Operation::LOAD_BOOLEAN, load_boolean),
(Operation::LOAD_CONSTANT, load_constant),
(Operation::LOAD_LIST, load_list),
(Operation::LOAD_SELF, load_self),
(Operation::GET_LOCAL, get_local),
(Operation::SET_LOCAL, set_local),
(Operation::ADD, add),
(Operation::SUBTRACT, subtract),
(Operation::MULTIPLY, multiply),
(Operation::DIVIDE, divide),
(Operation::MODULO, modulo),
(Operation::TEST, test),
(Operation::TEST_SET, test_set),
(Operation::EQUAL, equal),
(Operation::LESS, less),
(Operation::LESS_EQUAL, less_equal),
(Operation::NEGATE, negate),
(Operation::NOT, not),
(Operation::CALL, call),
(Operation::CALL_NATIVE, call_native),
(Operation::JUMP, jump),
(Operation::RETURN, r#return),
];
#[test]
fn operations_map_to_the_correct_runner() {
for (operation, expected_runner) in ALL_OPERATIONS {
let actual_runner = RUNNER_LOGIC_TABLE[operation.0 as usize];
assert_eq!(
expected_runner, actual_runner,
"{operation} runner is incorrect"
);
}
}
}

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,10 +1,10 @@
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, Value};
use super::{Pointer, Register, RunAction, Stack}; use super::{Action, CallFrame};
pub struct Thread { pub struct Thread {
chunk: Arc<Chunk>, chunk: Arc<Chunk>,
@ -24,45 +24,42 @@ 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 call_stack = Vec::with_capacity(self.chunk.prototypes.len() + 1);
let mut main_call = FunctionCall::new(self.chunk.clone(), 0); let mut main_call = CallFrame::new(self.chunk.clone(), 0);
main_call.ip = 1; // The first action is already known
call_stack.push(main_call); call_stack.push(main_call);
let first_action = RunAction::from(*self.chunk.instructions.first().unwrap());
let mut thread_data = ThreadData { let mut thread_data = ThreadData {
call_stack, stack: call_stack,
next_action: first_action, return_value: None,
return_value_index: None, spawned_threads: Vec::with_capacity(0),
spawned_threads: Vec::new(),
}; };
let mut action_sequence = Vec::with_capacity(self.chunk.instructions.len());
for instruction in self.chunk.instructions.iter() {
action_sequence.push(Action::from(instruction));
}
loop { loop {
trace!("Instruction: {}", thread_data.next_action.instruction); let current_frame = thread_data.current_frame_mut();
let current_action = if cfg!(debug_assertions) {
let should_end = (thread_data.next_action.logic)( action_sequence
thread_data.next_action.instruction, .get(current_frame.instruction_pointer)
&mut thread_data, .unwrap()
);
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);
Some(value)
} else { } else {
None unsafe {
action_sequence
.get(current_frame.instruction_pointer)
.unwrap_unchecked()
}
}; };
current_frame.instruction_pointer += 1;
thread_data trace!("Instruction: {}", current_action.fields);
.spawned_threads
.into_iter()
.for_each(|join_handle| {
let _ = join_handle.join();
});
(current_action.logic)(current_action.fields, &mut thread_data);
if let Some(value_option) = thread_data.return_value {
return value_option; return value_option;
} }
} }
@ -71,200 +68,25 @@ impl Thread {
#[derive(Debug)] #[derive(Debug)]
pub struct ThreadData { pub struct ThreadData {
pub call_stack: Stack<FunctionCall>, pub stack: Vec<CallFrame>,
pub next_action: RunAction, pub return_value: Option<Option<Value>>,
pub return_value_index: Option<u16>,
pub spawned_threads: Vec<JoinHandle<()>>, pub spawned_threads: Vec<JoinHandle<()>>,
} }
impl ThreadData { impl ThreadData {
pub fn current_position(&self) -> Span { pub fn current_frame(&self) -> &CallFrame {
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}");
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.stack.last().unwrap()
} else { } else {
unsafe { unsafe { self.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) {
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: u16) -> &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)
}
};
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"),
} }
} }
pub fn open_register_allow_empty_unchecked(&self, register_index: u16) -> Option<&Value> { pub fn current_frame_mut(&mut self) -> &mut CallFrame {
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) { if cfg!(debug_assertions) {
&self.call_stack.last().unwrap().chunk.constants[constant_index] self.stack.last_mut().unwrap()
} else { } else {
unsafe { unsafe { self.stack.last_mut().unwrap_unchecked() }
self.call_stack
.last_unchecked()
.chunk
.constants
.get_unchecked(constant_index)
} }
} }
} }
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;
assert!(
local_index < chunk.locals.len(),
"VM Error: Local index out of bounds"
);
chunk.locals[local_index].register_index
}
}