From e0579c6536eb74fb60015becf8b77615fe4eefba Mon Sep 17 00:00:00 2001 From: Jeff Date: Sun, 19 Jan 2025 23:37:43 -0500 Subject: [PATCH] Begin refactor of how registers and values are handled in the VM --- .vscode/launch.json | 674 ++++++++++++++++ Cargo.lock | 4 + dust-lang/Cargo.toml | 1 + dust-lang/src/chunk/constant_table.rs | 137 ++++ dust-lang/src/chunk/disassembler.rs | 81 +- dust-lang/src/chunk/mod.rs | 10 +- dust-lang/src/compiler/mod.rs | 142 +++- dust-lang/src/instruction/load_constant.rs | 17 +- dust-lang/src/instruction/mod.rs | 28 +- dust-lang/src/instruction/return.rs | 16 +- dust-lang/src/instruction/type_code.rs | 2 +- dust-lang/src/lib.rs | 9 +- dust-lang/src/native_function/assert.rs | 17 +- dust-lang/src/native_function/io.rs | 41 +- dust-lang/src/native_function/random.rs | 53 +- dust-lang/src/native_function/string.rs | 18 +- dust-lang/src/native_function/thread.rs | 88 +- dust-lang/src/value/abstract_list.rs | 23 +- dust-lang/src/value/concrete_value.rs | 341 -------- dust-lang/src/value/mod.rs | 253 ++---- dust-lang/src/vm/action.rs | 254 ++++++ dust-lang/src/vm/call_frame.rs | 41 + dust-lang/src/vm/function_call.rs | 44 - dust-lang/src/vm/mod.rs | 57 +- dust-lang/src/vm/pointer.rs | 69 ++ dust-lang/src/vm/register_table.rs | 185 +++++ dust-lang/src/vm/run_action.rs | 893 --------------------- dust-lang/src/vm/stack.rs | 137 ---- dust-lang/src/vm/thread.rs | 252 +----- 29 files changed, 1816 insertions(+), 2071 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 dust-lang/src/chunk/constant_table.rs delete mode 100644 dust-lang/src/value/concrete_value.rs create mode 100644 dust-lang/src/vm/action.rs create mode 100644 dust-lang/src/vm/call_frame.rs delete mode 100644 dust-lang/src/vm/function_call.rs create mode 100644 dust-lang/src/vm/pointer.rs create mode 100644 dust-lang/src/vm/register_table.rs delete mode 100644 dust-lang/src/vm/run_action.rs delete mode 100644 dust-lang/src/vm/stack.rs diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..112315c --- /dev/null +++ b/.vscode/launch.json @@ -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}" + } + ] +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index f62c495..b599be5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -349,6 +349,7 @@ dependencies = [ "rand", "serde", "serde_json", + "smallvec", "smartstring", "tracing", ] @@ -805,6 +806,9 @@ name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +dependencies = [ + "serde", +] [[package]] name = "smartstring" diff --git a/dust-lang/Cargo.toml b/dust-lang/Cargo.toml index 96fedb5..5d01ec0 100644 --- a/dust-lang/Cargo.toml +++ b/dust-lang/Cargo.toml @@ -22,6 +22,7 @@ smartstring = { version = "1.0.1", features = [ ], default-features = false } tracing = "0.1.41" crossbeam-channel = "0.5.14" +smallvec = { version = "1.13.2", features = ["serde"] } [dev-dependencies] criterion = { version = "0.3.4", features = ["html_reports"] } diff --git a/dust-lang/src/chunk/constant_table.rs b/dust-lang/src/chunk/constant_table.rs new file mode 100644 index 0000000..702745c --- /dev/null +++ b/dust-lang/src/chunk/constant_table.rs @@ -0,0 +1,137 @@ +use serde::{Deserialize, Serialize}; + +use crate::DustString; + +#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize, Deserialize)] +pub struct ConstantTable { + pub bytes: Vec, + pub characters: Vec, + pub floats: Vec, + pub integers: Vec, + pub strings: Vec, +} + +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>, + characters: impl Into>, + floats: impl Into>, + integers: impl Into>, + strings: impl Into>, + ) -> 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 { + self.bytes.get(index as usize).copied() + } + + pub fn get_character(&self, index: u16) -> Option { + self.characters.get(index as usize).copied() + } + + pub fn get_float(&self, index: u16) -> Option { + self.floats.get(index as usize).copied() + } + + pub fn get_integer(&self, index: u16) -> Option { + self.integers.get(index as usize).copied() + } + + pub fn get_string(&self, index: u16) -> Option<&DustString> { + self.strings.get(index as usize) + } +} diff --git a/dust-lang/src/chunk/disassembler.rs b/dust-lang/src/chunk/disassembler.rs index ac209cd..8f0e624 100644 --- a/dust-lang/src/chunk/disassembler.rs +++ b/dust-lang/src/chunk/disassembler.rs @@ -43,7 +43,7 @@ use std::io::{self, Write}; use colored::{ColoredString, Colorize}; -use crate::{Chunk, Local}; +use crate::{Chunk, Local, Type}; const INSTRUCTION_COLUMNS: [(&str, usize); 4] = [("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 INDENTATION: &str = "│ "; @@ -322,6 +322,7 @@ impl<'a, W: Write> Disassembler<'a, W> { let identifier_display = self .chunk .constants + .strings .get(*identifier_index as usize) .map(|value| value.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(CONSTANT_BORDERS[1])?; - for (index, value) in self.chunk.constants.iter().enumerate() { - let type_display = value.r#type().to_string(); + for (index, value) in self.chunk.constants.bytes.iter().enumerate() { + 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 mut value_string = value.to_string(); diff --git a/dust-lang/src/chunk/mod.rs b/dust-lang/src/chunk/mod.rs index 2944767..29f68b7 100644 --- a/dust-lang/src/chunk/mod.rs +++ b/dust-lang/src/chunk/mod.rs @@ -13,10 +13,12 @@ //! [`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, //! they will cause a panic or undefined behavior. +mod constant_table; mod disassembler; mod local; mod scope; +pub use constant_table::ConstantTable; pub use disassembler::Disassembler; pub use local::Local; pub use scope::Scope; @@ -27,7 +29,7 @@ use std::sync::Arc; use serde::{Deserialize, Serialize}; -use crate::{DustString, Function, FunctionType, Instruction, Span, Value}; +use crate::{DustString, Function, FunctionType, Instruction, Span}; /// Representation of a Dust program or function. /// @@ -39,7 +41,7 @@ pub struct Chunk { pub(crate) instructions: Vec, pub(crate) positions: Vec, - pub(crate) constants: Vec, + pub(crate) constants: ConstantTable, pub(crate) locals: Vec, pub(crate) prototypes: Vec>, @@ -54,7 +56,7 @@ impl Chunk { r#type: FunctionType, instructions: impl Into>, positions: impl Into>, - constants: impl Into>, + constants: ConstantTable, locals: impl Into>, prototypes: impl IntoIterator, ) -> Self { @@ -63,7 +65,7 @@ impl Chunk { r#type, instructions: instructions.into(), positions: positions.into(), - constants: constants.into(), + constants, locals: locals.into(), prototypes: prototypes.into_iter().map(Arc::new).collect(), register_count: 0, diff --git a/dust-lang/src/compiler/mod.rs b/dust-lang/src/compiler/mod.rs index 8df9d3a..f3f4bad 100644 --- a/dust-lang/src/compiler/mod.rs +++ b/dust-lang/src/compiler/mod.rs @@ -33,8 +33,9 @@ use std::{mem::replace, sync::Arc}; use optimize::control_flow_register_consolidation; use crate::{ - Chunk, ConcreteValue, DustError, DustString, FunctionType, Instruction, Lexer, Local, - NativeFunction, Operand, Operation, Scope, Span, Token, TokenKind, Type, Value, + Chunk, DustError, DustString, FunctionType, Instruction, Lexer, Local, NativeFunction, Operand, + Operation, Scope, Span, Token, TokenKind, Type, + chunk::ConstantTable, 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`] /// is called. - constants: Vec, + constants: ConstantTable, /// Block-local variables and their types. The locals are assigned to the chunk when /// [`Compiler::finish`] is called. The types are discarded after compilation. @@ -144,7 +145,7 @@ impl<'src> Compiler<'src> { return_type: Type::None, }, instructions: Vec::new(), - constants: Vec::new(), + constants: ConstantTable::new(), locals: Vec::new(), prototypes: Vec::new(), stack_size: 0, @@ -215,7 +216,7 @@ impl<'src> Compiler<'src> { r#type: self.r#type, instructions, positions, - constants: self.constants.to_vec(), + constants: self.constants, locals, prototypes: self.prototypes, register_count: self.stack_size, @@ -275,13 +276,10 @@ impl<'src> Compiler<'src> { .enumerate() .rev() .find_map(|(index, (local, _))| { - let constant = self.constants.get(local.identifier_index as usize)?; - let identifier = - if let Value::Concrete(ConcreteValue::String(identifier)) = constant { - identifier - } else { - return None; - }; + let identifier = self + .constants + .strings + .get(local.identifier_index as usize)?; if identifier == identifier_text { Some(index as u16) @@ -305,8 +303,8 @@ impl<'src> Compiler<'src> { ) -> (u16, u16) { info!("Declaring local {identifier}"); - let identifier = Value::Concrete(ConcreteValue::string(identifier)); - let identifier_index = self.push_or_get_constant(identifier); + let identifier = DustString::from(identifier); + let identifier_index = self.push_or_get_constant_string(identifier); let local_index = self.locals.len() as u16; self.locals.push(( @@ -322,22 +320,24 @@ impl<'src> Compiler<'src> { .get(local_index as usize) .and_then(|(local, _)| { self.constants + .strings .get(local.identifier_index as usize) .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 .constants + .strings .iter() - .position(|constant| constant == &value) + .position(|constant| constant == &string) { index as u16 } else { let index = self.constants.len() as u16; - self.constants.push(value); + self.constants.strings.push(string); index } @@ -466,17 +466,65 @@ impl<'src> Compiler<'src> { 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, - constant: ConcreteValue, + character: char, position: Span, ) -> Result<(), CompileError> { - let r#type = constant.r#type(); - let constant_index = self.push_or_get_constant(Value::Concrete(constant)); + let constant_index = self.constants.insert_character(character); 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(()) } @@ -511,9 +559,8 @@ impl<'src> Compiler<'src> { let byte = u8::from_str_radix(&text[2..], 16) .map_err(|error| CompileError::ParseIntError { error, position })?; - let value = ConcreteValue::Byte(byte); - self.emit_constant(value, position)?; + self.emit_byte_constant(byte, position)?; Ok(()) } else { @@ -530,10 +577,7 @@ impl<'src> Compiler<'src> { if let Token::Character(character) = self.current_token { self.advance()?; - - let value = ConcreteValue::Character(character); - - self.emit_constant(value, position)?; + self.emit_character_constant(character, position)?; Ok(()) } else { @@ -557,9 +601,8 @@ impl<'src> Compiler<'src> { error, position: self.previous_position, })?; - let value = ConcreteValue::Float(float); - self.emit_constant(value, position)?; + self.emit_float_constant(float, position)?; Ok(()) } else { @@ -589,9 +632,7 @@ impl<'src> Compiler<'src> { integer_value = integer_value * 10 + digit; } - let value = ConcreteValue::Integer(integer_value); - - self.emit_constant(value, position)?; + self.emit_integer_constant(integer_value, position)?; Ok(()) } else { @@ -609,9 +650,9 @@ impl<'src> Compiler<'src> { if let Token::String(text) = self.current_token { self.advance()?; - let value = ConcreteValue::string(text); + let string = DustString::from(text); - self.emit_constant(value, position)?; + self.emit_string_constant(string, position)?; Ok(()) } else { @@ -1478,24 +1519,34 @@ impl<'src> Compiler<'src> { self.advance()?; - let should_return_value = + let (should_return_value, return_type) = if matches!(self.current_token, Token::Semicolon | Token::RightBrace) { self.update_return_type(Type::None)?; - false + (false, TypeCode(0)) } else { self.parse_expression()?; 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)?; - true + (true, type_code) }; let end = self.current_position.1; let return_register = self.next_register() - 1; let r#return = Instruction::from(Return { should_return_value, + return_type, 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 } 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); } else { @@ -1549,7 +1600,16 @@ impl<'src> Compiler<'src> { })?; 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.emit_instruction(r#return, Type::None, self.current_position); diff --git a/dust-lang/src/instruction/load_constant.rs b/dust-lang/src/instruction/load_constant.rs index f26b4c2..a0a7c10 100644 --- a/dust-lang/src/instruction/load_constant.rs +++ b/dust-lang/src/instruction/load_constant.rs @@ -2,10 +2,11 @@ use std::fmt::{self, Display, Formatter}; use crate::{Instruction, Operation}; -use super::InstructionBuilder; +use super::{InstructionBuilder, TypeCode}; pub struct LoadConstant { pub destination: u16, + pub type_code: TypeCode, pub constant_index: u16, pub jump_next: bool, } @@ -13,11 +14,13 @@ pub struct LoadConstant { impl From for LoadConstant { fn from(instruction: Instruction) -> Self { let destination = instruction.a_field(); + let type_code = instruction.b_type(); let constant_index = instruction.b_field(); let jump_next = instruction.c_field() != 0; LoadConstant { destination, + type_code, constant_index, jump_next, } @@ -29,6 +32,7 @@ impl From for Instruction { InstructionBuilder { operation: Operation::LOAD_CONSTANT, a_field: load_constant.destination, + b_type: load_constant.type_code, b_field: load_constant.constant_index, c_field: load_constant.jump_next as u16, ..Default::default() @@ -41,11 +45,20 @@ impl Display for LoadConstant { fn fmt(&self, f: &mut Formatter) -> fmt::Result { let LoadConstant { destination, + type_code, constant_index, jump_next, } = 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 { write!(f, " JUMP +1")?; diff --git a/dust-lang/src/instruction/mod.rs b/dust-lang/src/instruction/mod.rs index 424819c..2324133 100644 --- a/dust-lang/src/instruction/mod.rs +++ b/dust-lang/src/instruction/mod.rs @@ -154,6 +154,7 @@ pub use type_code::TypeCode; use crate::NativeFunction; +#[derive(Clone, Copy, PartialEq, PartialOrd)] pub struct InstructionBuilder { pub operation: Operation, 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. /// /// 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 { destination, + type_code, constant_index, 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 { should_return_value, + return_type, return_register, }) } diff --git a/dust-lang/src/instruction/return.rs b/dust-lang/src/instruction/return.rs index b1c6e76..f108094 100644 --- a/dust-lang/src/instruction/return.rs +++ b/dust-lang/src/instruction/return.rs @@ -2,20 +2,23 @@ use std::fmt::{self, Display, Formatter}; use crate::{Instruction, Operation}; -use super::InstructionBuilder; +use super::{InstructionBuilder, TypeCode}; pub struct Return { pub should_return_value: bool, + pub return_type: TypeCode, pub return_register: u16, } impl From for Return { fn from(instruction: Instruction) -> Self { let should_return_value = instruction.b_field() != 0; + let return_type = instruction.b_type(); let return_register = instruction.c_field(); Return { should_return_value, + return_type, return_register, } } @@ -25,11 +28,13 @@ impl From for Instruction { fn from(r#return: Return) -> Self { let operation = Operation::RETURN; let b_field = r#return.should_return_value as u16; + let b_type = r#return.return_type; let c_field = r#return.return_register; InstructionBuilder { operation, b_field, + b_type, c_field, ..Default::default() } @@ -41,11 +46,18 @@ impl Display for Return { fn fmt(&self, f: &mut Formatter) -> fmt::Result { let Return { should_return_value, + return_type, return_register, } = self; 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 { write!(f, "RETURN") } diff --git a/dust-lang/src/instruction/type_code.rs b/dust-lang/src/instruction/type_code.rs index daf6e82..ddf213d 100644 --- a/dust-lang/src/instruction/type_code.rs +++ b/dust-lang/src/instruction/type_code.rs @@ -1,6 +1,6 @@ use std::fmt::Display; -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] pub struct TypeCode(pub u8); impl TypeCode { diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index bc6cb03..3fbaaf1 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -27,6 +27,7 @@ //! //! println!("{}", report); //! ``` +#![feature(get_many_mut)] pub mod chunk; pub mod compiler; @@ -42,15 +43,13 @@ pub mod vm; pub use crate::chunk::{Chunk, Disassembler, Local, Scope}; pub use crate::compiler::{CompileError, Compiler, compile}; pub use crate::dust_error::{AnnotatedError, DustError}; -pub use crate::instruction::{Operand, Instruction, Operation}; +pub use crate::instruction::{Instruction, Operand, Operation}; pub use crate::lexer::{LexError, Lexer, lex}; pub use crate::native_function::{NativeFunction, NativeFunctionError}; pub use crate::token::{Token, TokenKind, TokenOwned}; pub use crate::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict}; -pub use crate::value::{ - AbstractList, ConcreteValue, DustString, Function, RangeValue, Value, ValueError, -}; -pub use crate::vm::{Pointer, Vm, run}; +pub use crate::value::{AbstractList, DustString, Function, RangeValue, Value}; +pub use crate::vm::{Vm, run}; use std::fmt::Display; diff --git a/dust-lang/src/native_function/assert.rs b/dust-lang/src/native_function/assert.rs index 3cd7045..bdcd145 100644 --- a/dust-lang/src/native_function/assert.rs +++ b/dust-lang/src/native_function/assert.rs @@ -3,20 +3,5 @@ use std::{ops::Range, panic}; use crate::vm::ThreadData; pub fn panic(data: &mut ThreadData, _: u16, argument_range: Range) -> bool { - let position = data.current_position(); - 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) + todo!() } diff --git a/dust-lang/src/native_function/io.rs b/dust-lang/src/native_function/io.rs index 89bf2b7..327ca00 100644 --- a/dust-lang/src/native_function/io.rs +++ b/dust-lang/src/native_function/io.rs @@ -1,12 +1,14 @@ use std::io::{Write, stdin, stdout}; use std::ops::Range; +use crate::DustString; use crate::{ - ConcreteValue, Value, - vm::{Register, ThreadData, get_next_action}, + Value, + vm::{Register, ThreadData}, }; pub fn read_line(data: &mut ThreadData, destination: u16, _argument_range: Range) -> bool { + let current_frame = data.current_frame_mut(); let mut buffer = String::new(); 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)); - 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 } pub fn write(data: &mut ThreadData, _: u16, argument_range: Range) -> bool { - let mut stdout = stdout(); - - 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 + todo!() } pub fn write_line(data: &mut ThreadData, _: u16, argument_range: Range) -> bool { - let mut stdout = stdout().lock(); - - 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 + todo!() } diff --git a/dust-lang/src/native_function/random.rs b/dust-lang/src/native_function/random.rs index 1eae46a..cee0008 100644 --- a/dust-lang/src/native_function/random.rs +++ b/dust-lang/src/native_function/random.rs @@ -2,39 +2,38 @@ use std::ops::Range; use rand::Rng; -use crate::{ - Value, - vm::{Register, ThreadData, get_next_action}, -}; +use crate::vm::ThreadData; pub fn random_int(data: &mut ThreadData, destination: u16, argument_range: Range) -> bool { - let mut argument_range_iter = argument_range.into_iter(); - let (min, max) = { - let mut min = None; + todo!() - loop { - let register_index = argument_range_iter - .next() - .unwrap_or_else(|| panic!("No argument was passed to \"random_int\"")); - let value_option = data.open_register_allow_empty_unchecked(register_index); + // let mut argument_range_iter = argument_range.into_iter(); + // let (min, max) = { + // let mut min = None; - if let Some(argument) = value_option { - if let Some(integer) = argument.as_integer() { - if min.is_none() { - min = Some(integer); - } else { - break (min, integer); - } - } - } - } - }; + // loop { + // let register_index = argument_range_iter + // .next() + // .unwrap_or_else(|| panic!("No argument was passed to \"random_int\"")); + // let value_option = data.open_register_allow_empty_unchecked(register_index); - let random_integer = rand::thread_rng().gen_range(min.unwrap()..max); + // if let Some(argument) = value_option { + // if let Some(integer) = argument.as_integer() { + // if min.is_none() { + // min = Some(integer); + // } else { + // break (min, integer); + // } + // } + // } + // } + // }; - data.set_register(destination, Register::Value(Value::integer(random_integer))); + // let random_integer = rand::thread_rng().gen_range(min.unwrap()..max); - data.next_action = get_next_action(data); + // data.set_register(destination, Register::Value(Value::integer(random_integer))); - false + // data.next_action = get_next_action(data); + + // false } diff --git a/dust-lang/src/native_function/string.rs b/dust-lang/src/native_function/string.rs index 9e3546c..ea0ba3e 100644 --- a/dust-lang/src/native_function/string.rs +++ b/dust-lang/src/native_function/string.rs @@ -1,18 +1,20 @@ use std::ops::Range; use crate::{ - ConcreteValue, Value, - vm::{Register, ThreadData, get_next_action}, + Value, + vm::{Register, ThreadData}, }; pub fn to_string(data: &mut ThreadData, destination: u16, argument_range: Range) -> bool { - 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))); + todo!() - 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 } diff --git a/dust-lang/src/native_function/thread.rs b/dust-lang/src/native_function/thread.rs index 4b0cf85..9ecbd8f 100644 --- a/dust-lang/src/native_function/thread.rs +++ b/dust-lang/src/native_function/thread.rs @@ -7,58 +7,62 @@ use tracing::{Level, info, span}; use crate::{ DustString, - vm::{Thread, ThreadData, get_next_action}, + vm::{Thread, ThreadData}, }; fn start_thread(data: &mut ThreadData, argument_range: Range) -> JoinHandle<()> { - let mut argument_range_iter = argument_range.into_iter(); - 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); + todo!() - if let Some(argument) = value_option { - break argument; - } - } - }; - let function = function_argument.as_function().unwrap(); - let prototype_index = function.prototype_index as usize; - let current_call = data.call_stack.last_unchecked(); - let prototype = current_call.chunk.prototypes[prototype_index].clone(); + // let mut argument_range_iter = argument_range.into_iter(); + // 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); - info!( - "Spawning thread for \"{}\"", - function - .name - .as_ref() - .cloned() - .unwrap_or_else(|| DustString::from("anonymous")) - ); + // if let Some(argument) = value_option { + // break argument; + // } + // } + // }; + // let function = function_argument.as_function().unwrap(); + // 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 - .name - .as_ref() - .map(|name| name.to_string()) - .unwrap_or_else(|| "anonymous".to_string()); - let mut thread = Thread::new(prototype); + // info!( + // "Spawning thread for \"{}\"", + // function + // .name + // .as_ref() + // .cloned() + // .unwrap_or_else(|| DustString::from("anonymous")) + // ); - Builder::new() - .name(thread_name) - .spawn(move || { - let span = span!(Level::INFO, "Spawned thread"); - let _enter = span.enter(); + // let thread_name = prototype + // .name + // .as_ref() + // .map(|name| name.to_string()) + // .unwrap_or_else(|| "anonymous".to_string()); + // let mut thread = Thread::new(prototype); - thread.run(); - }) - .expect("Critical VM Error: Failed to spawn thread") + // Builder::new() + // .name(thread_name) + // .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) -> bool { - let _ = start_thread(data, argument_range); - data.next_action = get_next_action(data); + todo!() - false + // let _ = start_thread(data, argument_range); + // data.next_action = get_next_action(data); + + // false } diff --git a/dust-lang/src/value/abstract_list.rs b/dust-lang/src/value/abstract_list.rs index f678d77..0d0d844 100644 --- a/dust-lang/src/value/abstract_list.rs +++ b/dust-lang/src/value/abstract_list.rs @@ -1,6 +1,9 @@ use std::fmt::{self, Display, Formatter}; -use crate::{vm::ThreadData, Pointer, Type}; +use crate::{ + Type, + vm::{Pointer, ThreadData}, +}; use super::DustString; @@ -12,23 +15,7 @@ pub struct AbstractList { impl AbstractList { pub fn display(&self, data: &ThreadData) -> DustString { - let mut display = DustString::new(); - - 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 + todo!() } } diff --git a/dust-lang/src/value/concrete_value.rs b/dust-lang/src/value/concrete_value.rs deleted file mode 100644 index f923d2a..0000000 --- a/dust-lang/src/value/concrete_value.rs +++ /dev/null @@ -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; - -#[derive(Debug, PartialEq, PartialOrd, Serialize, Deserialize)] -pub enum ConcreteValue { - Boolean(bool), - Byte(u8), - Character(char), - Float(f64), - Integer(i64), - List(Vec), - Range(RangeValue), - String(DustString), -} - -impl ConcreteValue { - pub fn to_value(self) -> Value { - Value::Concrete(self) - } - - pub fn list>>(into_list: T) -> Self { - ConcreteValue::List(into_list.into()) - } - - pub fn string>>(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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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}"), - } - } -} diff --git a/dust-lang/src/value/mod.rs b/dust-lang/src/value/mod.rs index b9b7d91..6bdb381 100644 --- a/dust-lang/src/value/mod.rs +++ b/dust-lang/src/value/mod.rs @@ -1,57 +1,40 @@ //! Runtime values used by the VM. mod abstract_list; -mod concrete_value; mod function; mod range_value; pub use abstract_list::AbstractList; -pub use concrete_value::{ConcreteValue, DustString}; pub use function::Function; pub use range_value::RangeValue; use serde::{Deserialize, Serialize}; +use smartstring::{LazyCompact, SmartString}; use std::fmt::{self, Debug, Display, Formatter}; use crate::{Type, vm::ThreadData}; +pub type DustString = SmartString; + #[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)] pub enum Value { - Concrete(ConcreteValue), + Boolean(bool), + Byte(u8), + Character(char), + Float(f64), + Integer(i64), + String(DustString), + + List(Vec), #[serde(skip)] AbstractList(AbstractList), - #[serde(skip)] Function(Function), } impl Value { - pub fn boolean(boolean: bool) -> Self { - Value::Concrete(ConcreteValue::Boolean(boolean)) - } - - pub fn byte(byte: u8) -> Self { - Value::Concrete(ConcreteValue::Byte(byte)) - } - - pub fn character(character: char) -> Self { - Value::Concrete(ConcreteValue::Character(character)) - } - - pub fn float(float: f64) -> Self { - Value::Concrete(ConcreteValue::Float(float)) - } - - pub fn integer(integer: i64) -> Self { - Value::Concrete(ConcreteValue::Integer(integer)) - } - - pub fn string(string: impl Into) -> Self { - Value::Concrete(ConcreteValue::String(string.into())) - } - pub fn as_boolean(&self) -> Option { - if let Value::Concrete(ConcreteValue::Boolean(boolean)) = self { + if let Value::Boolean(boolean) = self { Some(*boolean) } else { None @@ -59,7 +42,7 @@ impl Value { } pub fn as_byte(&self) -> Option { - if let Value::Concrete(ConcreteValue::Byte(byte)) = self { + if let Value::Byte(byte) = self { Some(*byte) } else { None @@ -67,7 +50,7 @@ impl Value { } pub fn as_character(&self) -> Option { - if let Value::Concrete(ConcreteValue::Character(character)) = self { + if let Value::Character(character) = self { Some(*character) } else { None @@ -75,7 +58,7 @@ impl Value { } pub fn as_float(&self) -> Option { - if let Value::Concrete(ConcreteValue::Float(float)) = self { + if let Value::Float(float) = self { Some(*float) } else { None @@ -91,7 +74,7 @@ impl Value { } pub fn as_integer(&self) -> Option { - if let Value::Concrete(ConcreteValue::Integer(integer)) = self { + if let Value::Integer(integer) = self { Some(*integer) } else { None @@ -99,15 +82,15 @@ impl Value { } pub fn as_string(&self) -> Option<&DustString> { - if let Value::Concrete(ConcreteValue::String(value)) = self { - Some(value) + if let Value::String(string) = self { + Some(string) } else { None } } pub fn is_string(&self) -> bool { - matches!(self, Value::Concrete(ConcreteValue::String(_))) + matches!(self, Value::String(_)) } pub fn is_function(&self) -> bool { @@ -116,7 +99,13 @@ impl Value { pub fn r#type(&self) -> Type { 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, .. }) => { 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - match (self, other) { - (Value::Concrete(left), Value::Concrete(right)) => { - left.less_than_or_equals(right).map(Value::Concrete) - } - _ => Err(ValueError::CannotCompare(self.to_owned(), other.to_owned())), - } - } - pub fn display(&self, data: &ThreadData) -> DustString { match self { + Value::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::Concrete(concrete_value) => concrete_value.display(), Value::Function(function) => DustString::from(function.to_string()), } } @@ -231,60 +147,27 @@ impl Value { impl Display for Value { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 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::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}") - } - } - } -} diff --git a/dust-lang/src/vm/action.rs b/dust-lang/src/vm/action.rs new file mode 100644 index 0000000..c796096 --- /dev/null +++ b/dust-lang/src/vm/action.rs @@ -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::(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::(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!(), + } +} diff --git a/dust-lang/src/vm/call_frame.rs b/dust-lang/src/vm/call_frame.rs new file mode 100644 index 0000000..bf89316 --- /dev/null +++ b/dust-lang/src/vm/call_frame.rs @@ -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, + pub registers: RegisterTable, + pub return_register: u16, + pub instruction_pointer: usize, +} + +impl CallFrame { + pub fn new(prototype: Arc, 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, + ) + } +} diff --git a/dust-lang/src/vm/function_call.rs b/dust-lang/src/vm/function_call.rs deleted file mode 100644 index 7db37aa..0000000 --- a/dust-lang/src/vm/function_call.rs +++ /dev/null @@ -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, - pub ip: usize, - pub return_register: u16, - pub registers: Vec, -} - -impl FunctionCall { - pub fn new(chunk: Arc, 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() - ) - } -} diff --git a/dust-lang/src/vm/mod.rs b/dust-lang/src/vm/mod.rs index 0e44c78..1d84dd7 100644 --- a/dust-lang/src/vm/mod.rs +++ b/dust-lang/src/vm/mod.rs @@ -1,19 +1,16 @@ //! Virtual machine and errors -mod function_call; -mod run_action; -mod stack; +mod action; +mod call_frame; +mod pointer; +mod register_table; mod thread; -use std::{ - fmt::{self, Debug, Display, Formatter}, - sync::Arc, - thread::Builder, -}; +use std::{sync::Arc, thread::Builder}; -pub use function_call::FunctionCall; -pub use run_action::RunAction; -pub(crate) use run_action::get_next_action; -pub use stack::Stack; +pub use action::Action; +pub use call_frame::CallFrame; +pub use pointer::Pointer; +pub use register_table::{Register, RegisterTable}; pub use thread::{Thread, ThreadData}; use crossbeam_channel::bounded; @@ -62,39 +59,3 @@ impl Vm { rx.recv().unwrap_or(None) } } - -#[derive(Clone, Debug, PartialEq)] -pub enum Register { - Empty, - Value(Value), - Pointer(Pointer), -} - -impl Display for Register { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - Self::Empty => write!(f, "empty"), - Self::Value(value) => write!(f, "{}", value), - Self::Pointer(pointer) => write!(f, "{}", pointer), - } - } -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub enum Pointer { - Register(u16), - Constant(u16), - Stack(usize, u16), -} - -impl Display for Pointer { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - Self::Register(index) => write!(f, "PR{}", index), - Self::Constant(index) => write!(f, "PC{}", index), - Self::Stack(call_index, register_index) => { - write!(f, "PS{}R{}", call_index, register_index) - } - } - } -} diff --git a/dust-lang/src/vm/pointer.rs b/dust-lang/src/vm/pointer.rs new file mode 100644 index 0000000..7558c3a --- /dev/null +++ b/dust-lang/src/vm/pointer.rs @@ -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})"), + } + } +} diff --git a/dust-lang/src/vm/register_table.rs b/dust-lang/src/vm/register_table.rs new file mode 100644 index 0000000..ebbef75 --- /dev/null +++ b/dust-lang/src/vm/register_table.rs @@ -0,0 +1,185 @@ +use smallvec::{SmallVec, smallvec}; +use tracing::trace; + +use crate::DustString; + +use super::Pointer; + +#[derive(Debug, Clone)] +pub enum Register { + Empty, + Value(T), + Pointer(Pointer), +} + +impl Register { + 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; 64]>, + bytes: SmallVec<[Register; 64]>, + characters: SmallVec<[Register; 64]>, + floats: SmallVec<[Register; 64]>, + integers: SmallVec<[Register; 64]>, + strings: SmallVec<[Register; 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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] { + 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 { + 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 { + let index = index as usize; + + if cfg!(debug_assertions) { + self.strings.get_mut(index).unwrap() + } else { + unsafe { self.strings.get_mut(index).unwrap_unchecked() } + } + } +} diff --git a/dust-lang/src/vm/run_action.rs b/dust-lang/src/vm/run_action.rs deleted file mode 100644 index 167419a..0000000 --- a/dust-lang/src/vm/run_action.rs +++ /dev/null @@ -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 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 = ¤t_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 = ¤t_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" - ); - } - } -} diff --git a/dust-lang/src/vm/stack.rs b/dust-lang/src/vm/stack.rs deleted file mode 100644 index dd43253..0000000 --- a/dust-lang/src/vm/stack.rs +++ /dev/null @@ -1,137 +0,0 @@ -use std::{ - fmt::{self, Debug, Display, Formatter}, - ops::{Index, IndexMut, Range}, -}; - -use super::FunctionCall; - -#[derive(Clone, PartialEq)] -pub struct Stack { - items: Vec, -} - -impl Stack { - 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 { - 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 Default for Stack { - fn default() -> Self { - Self::new() - } -} - -impl Index> for Stack { - type Output = [T]; - - fn index(&self, index: Range) -> &Self::Output { - &self.items[index] - } -} - -impl IndexMut> for Stack { - fn index_mut(&mut self, index: Range) -> &mut Self::Output { - &mut self.items[index] - } -} - -impl Debug for Stack { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{:?}", self.items) - } -} - -impl Display for Stack { - 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, "---------------------------") - } -} diff --git a/dust-lang/src/vm/thread.rs b/dust-lang/src/vm/thread.rs index c605066..13a7e91 100644 --- a/dust-lang/src/vm/thread.rs +++ b/dust-lang/src/vm/thread.rs @@ -1,10 +1,10 @@ -use std::{mem::replace, sync::Arc, thread::JoinHandle}; +use std::{sync::Arc, thread::JoinHandle}; use tracing::{info, trace}; -use crate::{Chunk, DustString, Operand, Span, Value, vm::FunctionCall}; +use crate::{Chunk, DustString, Value}; -use super::{Pointer, Register, RunAction, Stack}; +use super::{Action, CallFrame}; pub struct Thread { chunk: Arc, @@ -24,45 +24,42 @@ impl Thread { .unwrap_or_else(|| DustString::from("anonymous")) ); - let mut call_stack = Stack::with_capacity(self.chunk.prototypes.len() + 1); - let mut main_call = FunctionCall::new(self.chunk.clone(), 0); - main_call.ip = 1; // The first action is already known + let mut call_stack = Vec::with_capacity(self.chunk.prototypes.len() + 1); + let mut main_call = CallFrame::new(self.chunk.clone(), 0); call_stack.push(main_call); - let first_action = RunAction::from(*self.chunk.instructions.first().unwrap()); let mut thread_data = ThreadData { - call_stack, - next_action: first_action, - return_value_index: None, - spawned_threads: Vec::new(), + stack: call_stack, + return_value: None, + spawned_threads: Vec::with_capacity(0), }; + 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 { - trace!("Instruction: {}", thread_data.next_action.instruction); + let current_frame = thread_data.current_frame_mut(); + let current_action = if cfg!(debug_assertions) { + action_sequence + .get(current_frame.instruction_pointer) + .unwrap() + } else { + unsafe { + action_sequence + .get(current_frame.instruction_pointer) + .unwrap_unchecked() + } + }; + current_frame.instruction_pointer += 1; - let should_end = (thread_data.next_action.logic)( - thread_data.next_action.instruction, - &mut thread_data, - ); + trace!("Instruction: {}", current_action.fields); - 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 { - None - }; - - thread_data - .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; } } @@ -71,200 +68,25 @@ impl Thread { #[derive(Debug)] pub struct ThreadData { - pub call_stack: Stack, - pub next_action: RunAction, - pub return_value_index: Option, + pub stack: Vec, + pub return_value: Option>, pub spawned_threads: Vec>, } impl ThreadData { - pub fn current_position(&self) -> Span { - let current_call = self.call_stack.last_unchecked(); - - current_call.chunk.positions[current_call.ip] - } - - pub(crate) fn follow_pointer_unchecked(&self, pointer: Pointer) -> &Value { - trace!("Follow {pointer}"); - - match pointer { - Pointer::Register(register_index) => self.open_register_unchecked(register_index), - Pointer::Constant(constant_index) => self.get_constant_unchecked(constant_index), - Pointer::Stack(stack_index, register_index) => unsafe { - let register = self - .call_stack - .get_unchecked(stack_index) - .registers - .get_unchecked(register_index as usize); - - match register { - Register::Value(value) => value, - Register::Pointer(pointer) => self.follow_pointer_unchecked(*pointer), - Register::Empty => panic!("VM Error: Register {register_index} is empty"), - } - }, - } - } - - pub fn get_register_unchecked(&self, register_index: u16) -> &Register { - trace!("Get R{register_index}"); - - let register_index = register_index as usize; - + pub fn current_frame(&self) -> &CallFrame { if cfg!(debug_assertions) { - &self.call_stack.last_unchecked().registers[register_index] + self.stack.last().unwrap() } else { - unsafe { - self.call_stack - .last_unchecked() - .registers - .get_unchecked(register_index) - } + unsafe { self.stack.last().unwrap_unchecked() } } } - pub fn set_register(&mut self, to_register: u16, register: Register) { - let to_register = to_register as usize; - - self.call_stack.last_mut_unchecked().registers[to_register] = register; - } - - pub fn 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> { - 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; - + pub fn current_frame_mut(&mut self) -> &mut CallFrame { if cfg!(debug_assertions) { - &self.call_stack.last().unwrap().chunk.constants[constant_index] + self.stack.last_mut().unwrap() } else { - unsafe { - self.call_stack - .last_unchecked() - .chunk - .constants - .get_unchecked(constant_index) - } + unsafe { self.stack.last_mut().unwrap_unchecked() } } } - - 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 - } }