1
0

Make runtime improvements

This commit is contained in:
Jeff 2025-01-08 04:21:01 -05:00
parent 4473ea5b23
commit e9bd9b37b0
45 changed files with 523 additions and 916 deletions

View File

@ -46,19 +46,19 @@ write_line(fib(25))
## Goals ## Goals
This project's goal is to deliver a language that not only *works* but that offers genunine value This project's goal is to deliver a language with features that stand out due to a combination of
due to a unique combination of design choices and a high-quality implementation. As mentioned in the design choices and a high-quality implementation. As mentioned in the first sentence, Dust's general
first sentence, Dust's general aspirations are to be **fast**, **safe** and **easy**. aspirations are to be **fast**, **safe** and **easy**.
- **Easy** - **Easy**
- **Simple Syntax** Dust should be easier to learn than most programming languages. Its syntax - **Simple Syntax** Dust should be easier to learn than most programming languages. Its syntax
should be familiar to users of other C-like languages to the point that even a new user can read should be familiar to users of other C-like languages to the point that even a new user can read
Dust code and understand what it does. Rather than being dumbed down by a lack of features, Dust Dust code and understand what it does. Rather than being held back by a lack of features, Dust
should be powerful and elegant in its simplicity, seeking a maximum of capability with a minimum should be powerful and elegant in its simplicity, seeking a maximum of capability with a minimum
of complexity. When advanced features are added, they should never obstruct existing features, of complexity. When advanced features are added, they should never obstruct existing features,
including readability. Even the advanced type system should be clear and unintimidating. including readability. Even the advanced type system should be clear and unintimidating.
- **Excellent Errors** Dust should provide helpful error messages that guide the user to the - **Excellent Errors** Dust should provide helpful error messages that guide the user to the
source of the problem and suggest a solution. Errors should be a helpful learning ressource for source of the problem and suggest a solution. Errors should be a helpful learning resource for
users rather than a source of frustration. users rather than a source of frustration.
- **Relevant Documentation** Users should have the resources they need to learn Dust and write - **Relevant Documentation** Users should have the resources they need to learn Dust and write
code in it. They should know where to look for answers and how to reach out for help. code in it. They should know where to look for answers and how to reach out for help.
@ -68,7 +68,7 @@ first sentence, Dust's general aspirations are to be **fast**, **safe** and **ea
typed language, users should feel confident in the type-consistency of their code and not want typed language, users should feel confident in the type-consistency of their code and not want
to go back to a dynamically typed language. to go back to a dynamically typed language.
- **Memory Safety** Dust should be free of memory bugs. Being implemented in Rust makes this easy - **Memory Safety** Dust should be free of memory bugs. Being implemented in Rust makes this easy
but, to accomodate long-running programs, Dust still requires a memory management strategy. but, to accommodate long-running programs, Dust still requires a memory management strategy.
Dust's design is to use a separate thread for garbage collection, allowing the main thread to Dust's design is to use a separate thread for garbage collection, allowing the main thread to
continue executing code while the garbage collector looks for unused memory. continue executing code while the garbage collector looks for unused memory.
- **Fast** - **Fast**
@ -76,7 +76,7 @@ first sentence, Dust's general aspirations are to be **fast**, **safe** and **ea
executing quickly. The compilation time should feel negligible to the user. executing quickly. The compilation time should feel negligible to the user.
- **Fast Execution** Dust should be generally faster than Python, Ruby and NodeJS. It should be - **Fast Execution** Dust should be generally faster than Python, Ruby and NodeJS. It should be
competitive with highly optimized, modern, register-based VM languages like Lua. Dust should competitive with highly optimized, modern, register-based VM languages like Lua. Dust should
be benchmarked during development to inform decisions about performance. be bench tested during development to inform decisions about performance.
- **Low Resource Usage** Despite its performance, Dust's use of memory and CPU power should be - **Low Resource Usage** Despite its performance, Dust's use of memory and CPU power should be
conservative and predictable enough to accomodate a wide range of devices. conservative and predictable enough to accomodate a wide range of devices.
@ -94,10 +94,10 @@ the family but Rust is its primary point of reference for syntax. Rust was chose
because its imperative code is *obvious by design* and *widely familiar*. Those qualities are because its imperative code is *obvious by design* and *widely familiar*. Those qualities are
aligned with Dust's emphasis on usability. aligned with Dust's emphasis on usability.
However, some differences exist. Dust *evaluates* all of the code in the file while Rust only However, some differences exist. Dust *evaluates* all the code in the file while Rust only initiates
initiates from a "main" function. Dust's execution model is more like one found in a scripting from a "main" function. Dust's execution model is more like one found in a scripting language. If we
language. If we put `42 + 42 == 84` into a file and run it, it will return `true` because the outer put `42 + 42 == 84` into a file and run it, it will return `true` because the outer context is, in a
context is, in a sense, the "main" function. sense, the "main" function.
So while the syntax is by no means compatible, it is superficially similar, even to the point that So while the syntax is by no means compatible, it is superficially similar, even to the point that
syntax highlighting for Rust code works well with Dust code. This is not a design goal but a happy syntax highlighting for Rust code works well with Dust code. This is not a design goal but a happy

View File

@ -1,10 +0,0 @@
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `../../target/release/dust addictive_addition.ds` | 99.1 ± 3.6 | 95.4 | 110.6 | 9.46 ± 0.53 |
| `node addictive_addition.js` | 45.4 ± 0.5 | 44.4 | 46.4 | 4.33 ± 0.19 |
| `deno addictive_addition.js` | 24.0 ± 2.7 | 18.4 | 39.0 | 2.29 ± 0.28 |
| `bun addictive_addition.js` | 10.5 ± 0.5 | 8.9 | 11.6 | 1.00 |
| `python addictive_addition.py` | 231.9 ± 27.2 | 213.6 | 310.9 | 22.14 ± 2.76 |
| `lua addictive_addition.lua` | 21.9 ± 0.3 | 21.7 | 23.7 | 2.09 ± 0.10 |
| `ruby addictive_addition.rb` | 83.5 ± 3.9 | 76.6 | 92.4 | 7.97 ± 0.51 |
| `java addictive_addition.java` | 199.6 ± 5.6 | 194.8 | 214.4 | 19.06 ± 0.98 |

View File

@ -1,6 +0,0 @@
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `../../target/release/dust recursion.ds` | 2.0 ± 0.2 | 1.9 | 2.9 | 1.00 |
| `node recursion.js` | 42.4 ± 0.9 | 41.3 | 45.5 | 21.11 ± 1.68 |
| `deno recursion.js` | 21.2 ± 1.7 | 17.3 | 23.9 | 10.57 ± 1.15 |
| `bun recursion.js` | 8.3 ± 0.3 | 7.3 | 9.7 | 4.13 ± 0.36 |

View File

@ -223,7 +223,8 @@ fn main() {
if let Mode::Disassemble { style, name, input } = mode { if let Mode::Disassemble { style, name, input } = mode {
let (source, file_name) = get_source_and_file_name(input); let (source, file_name) = get_source_and_file_name(input);
let lexer = Lexer::new(&source); let lexer = Lexer::new(&source);
let mut compiler = match Compiler::new(lexer) { let program_name = name.or(file_name);
let mut compiler = match Compiler::new(lexer, program_name) {
Ok(compiler) => compiler, Ok(compiler) => compiler,
Err(error) => { Err(error) => {
handle_compile_error(error, &source); handle_compile_error(error, &source);
@ -241,7 +242,7 @@ fn main() {
} }
} }
let chunk = compiler.finish(name.or(file_name)); let chunk = compiler.finish();
let mut stdout = stdout().lock(); let mut stdout = stdout().lock();
chunk chunk
@ -310,7 +311,8 @@ fn main() {
{ {
let (source, file_name) = get_source_and_file_name(input); let (source, file_name) = get_source_and_file_name(input);
let lexer = Lexer::new(&source); let lexer = Lexer::new(&source);
let mut compiler = match Compiler::new(lexer) { let program_name = name.or(file_name);
let mut compiler = match Compiler::new(lexer, program_name) {
Ok(compiler) => compiler, Ok(compiler) => compiler,
Err(error) => { Err(error) => {
handle_compile_error(error, &source); handle_compile_error(error, &source);
@ -328,7 +330,7 @@ fn main() {
} }
} }
let chunk = compiler.finish(name.or(file_name)); let chunk = compiler.finish();
let compile_end = start_time.elapsed(); let compile_end = start_time.elapsed();
let vm = Vm::new(chunk); let vm = Vm::new(chunk);

View File

@ -1,7 +1,7 @@
use std::time::Duration; use std::time::Duration;
use criterion::{black_box, criterion_group, criterion_main, Criterion}; use criterion::{black_box, criterion_group, criterion_main, Criterion};
use dust_lang::run; use dust_lang::{run, DustString};
const SOURCE: &str = r" const SOURCE: &str = r"
let mut i = 0 let mut i = 0
@ -12,7 +12,7 @@ const SOURCE: &str = r"
"; ";
fn addictive_addition(source: &str) { fn addictive_addition(source: &str) {
let _ = run(source).unwrap(); run(Some(DustString::from("addictive_addition")), source).unwrap();
} }
fn criterion_benchmark(c: &mut Criterion) { fn criterion_benchmark(c: &mut Criterion) {

View File

@ -1,7 +1,7 @@
use std::time::Duration; use std::time::Duration;
use criterion::{black_box, criterion_group, criterion_main, Criterion}; use criterion::{black_box, criterion_group, criterion_main, Criterion};
use dust_lang::run; use dust_lang::{run, DustString};
const SOURCE: &str = r" const SOURCE: &str = r"
fn fib (n: int) -> int { fn fib (n: int) -> int {
@ -15,7 +15,7 @@ const SOURCE: &str = r"
"; ";
fn addictive_addition(source: &str) { fn addictive_addition(source: &str) {
let _ = run(source).unwrap(); run(Some(DustString::from("fibonacci")), source).unwrap();
} }
fn criterion_benchmark(c: &mut Criterion) { fn criterion_benchmark(c: &mut Criterion) {

View File

@ -277,10 +277,10 @@ impl<'a, W: Write> Disassembler<'a, W> {
self.write_center_border(&column_name_line)?; self.write_center_border(&column_name_line)?;
self.write_center_border(INSTRUCTION_BORDERS[1])?; self.write_center_border(INSTRUCTION_BORDERS[1])?;
for (index, instruction) in self.chunk.instructions().iter().enumerate() { for (index, instruction) in self.chunk.instructions.iter().enumerate() {
let position = self let position = self
.chunk .chunk
.positions() .positions
.get(index) .get(index)
.map(|position| position.to_string()) .map(|position| position.to_string())
.unwrap_or("stripped".to_string()); .unwrap_or("stripped".to_string());
@ -317,11 +317,11 @@ impl<'a, W: Write> Disassembler<'a, W> {
scope, scope,
is_mutable, is_mutable,
}, },
) in self.chunk.locals().iter().enumerate() ) in self.chunk.locals.iter().enumerate()
{ {
let identifier_display = self let identifier_display = self
.chunk .chunk
.constants() .constants
.get(*identifier_index as usize) .get(*identifier_index as usize)
.map(|value| value.to_string()) .map(|value| value.to_string())
.unwrap_or_else(|| "unknown".to_string()); .unwrap_or_else(|| "unknown".to_string());
@ -352,7 +352,7 @@ impl<'a, W: Write> Disassembler<'a, W> {
self.write_center_border(&column_name_line)?; self.write_center_border(&column_name_line)?;
self.write_center_border(CONSTANT_BORDERS[1])?; self.write_center_border(CONSTANT_BORDERS[1])?;
for (index, value) in self.chunk.constants().iter().enumerate() { for (index, value) in self.chunk.constants.iter().enumerate() {
let type_display = value.r#type().to_string(); let type_display = value.r#type().to_string();
let value_display = { let value_display = {
let mut value_string = value.to_string(); let mut value_string = value.to_string();

View File

@ -1,14 +1,13 @@
//! Representation of a Dust program or function. //! Representation of a Dust program or function.
//! //!
//! A chunk is output by the compiler to represent all of the information needed to execute a Dust //! A chunk is output by the compiler to represent all the information needed to execute a Dust
//! program. In addition to the program itself, each function in the source is compiled into its own //! program. In addition to the program itself, each function in the source is compiled into its own
//! chunk and stored in the `prototypes` field of its parent. Thus, a chunk is also the //! chunk and stored in the `prototypes` field of its parent. Thus, a chunk can also represent a
//! representation of a function prototype, i.e. a function declaration, as opposed to an individual //! function prototype.
//! instance.
//! //!
//! Chunks have a name when they belong to a named function. They also have a type, so the input //! Chunks have a name when they belong to a named function. They also have a type, so the input
//! parameters and the type of the return value are statically known. The [`Chunk::stack_size`] //! parameters and the type of the return value are statically known. The [`Chunk::stack_size`]
//! method can provide the necessary stack size that will be needed by the virtual machine. Chunks //! field can provide the necessary stack size that will be needed by the virtual machine. Chunks
//! cannot be instantiated directly and must be created by the compiler. However, when the Rust //! cannot be instantiated directly and must be created by the compiler. However, when the Rust
//! compiler is in the "test" configuration (used for all types of test), [`Chunk::with_data`] can //! compiler is in the "test" configuration (used for all types of test), [`Chunk::with_data`] can
//! be used to create a chunk for comparison to the compiler output. Do not try to run these chunks //! be used to create a chunk for comparison to the compiler output. Do not try to run these chunks
@ -27,7 +26,6 @@ use std::io::Write;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use smallvec::SmallVec; use smallvec::SmallVec;
use crate::vm::{Record, RunAction};
use crate::{DustString, Function, FunctionType, Instruction, Span, Value}; use crate::{DustString, Function, FunctionType, Instruction, Span, Value};
/// Representation of a Dust program or function. /// Representation of a Dust program or function.
@ -35,45 +33,20 @@ use crate::{DustString, Function, FunctionType, Instruction, Span, Value};
/// See the [module-level documentation](index.html) for more information. /// See the [module-level documentation](index.html) for more information.
#[derive(Clone, PartialOrd, Serialize, Deserialize)] #[derive(Clone, PartialOrd, Serialize, Deserialize)]
pub struct Chunk { pub struct Chunk {
name: Option<DustString>, pub(crate) name: Option<DustString>,
r#type: FunctionType, pub(crate) r#type: FunctionType,
instructions: SmallVec<[Instruction; 32]>, pub(crate) instructions: SmallVec<[Instruction; 32]>,
positions: SmallVec<[Span; 32]>, pub(crate) positions: SmallVec<[Span; 32]>,
constants: SmallVec<[Value; 16]>, pub(crate) constants: SmallVec<[Value; 16]>,
locals: SmallVec<[Local; 8]>, pub(crate) locals: SmallVec<[Local; 8]>,
prototypes: Vec<Chunk>, pub(crate) prototypes: Vec<Chunk>,
stack_size: usize, pub(crate) stack_size: usize,
record_index: u8, pub(crate) prototype_index: u8,
} }
impl Chunk { impl Chunk {
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
name: Option<DustString>,
r#type: FunctionType,
instructions: impl Into<SmallVec<[Instruction; 32]>>,
positions: impl Into<SmallVec<[Span; 32]>>,
constants: impl Into<SmallVec<[Value; 16]>>,
locals: impl Into<SmallVec<[Local; 8]>>,
prototypes: impl Into<Vec<Chunk>>,
stack_size: usize,
record_index: u8,
) -> Self {
Self {
name,
r#type,
instructions: instructions.into(),
positions: positions.into(),
constants: constants.into(),
locals: locals.into(),
prototypes: prototypes.into(),
stack_size,
record_index,
}
}
#[cfg(any(test, debug_assertions))] #[cfg(any(test, debug_assertions))]
pub fn with_data( pub fn with_data(
name: Option<DustString>, name: Option<DustString>,
@ -93,7 +66,7 @@ impl Chunk {
locals: locals.into(), locals: locals.into(),
prototypes, prototypes,
stack_size: 0, stack_size: 0,
record_index: 0, prototype_index: 0,
} }
} }
@ -101,63 +74,10 @@ impl Chunk {
Function { Function {
name: self.name.clone(), name: self.name.clone(),
r#type: self.r#type.clone(), r#type: self.r#type.clone(),
record_index: self.record_index, prototype_index: self.prototype_index,
} }
} }
pub fn into_records(self, records: &mut Vec<Record>) {
let actions = self.instructions().iter().map(RunAction::from).collect();
let record = Record::new(
actions,
None,
self.name,
self.r#type,
self.positions,
self.constants,
self.locals,
self.stack_size,
self.record_index,
);
records.push(record);
for chunk in self.prototypes {
chunk.into_records(records);
}
}
pub fn name(&self) -> Option<&DustString> {
self.name.as_ref()
}
pub fn r#type(&self) -> &FunctionType {
&self.r#type
}
pub fn instructions(&self) -> &SmallVec<[Instruction; 32]> {
&self.instructions
}
pub fn positions(&self) -> &SmallVec<[Span; 32]> {
&self.positions
}
pub fn constants(&self) -> &SmallVec<[Value; 16]> {
&self.constants
}
pub fn locals(&self) -> &SmallVec<[Local; 8]> {
&self.locals
}
pub fn prototypes(&self) -> &Vec<Chunk> {
&self.prototypes
}
pub fn stack_size(&self) -> usize {
self.stack_size
}
pub fn disassembler<'a, W: Write>(&'a self, writer: &'a mut W) -> Disassembler<'a, W> { pub fn disassembler<'a, W: Write>(&'a self, writer: &'a mut W) -> Disassembler<'a, W> {
Disassembler::new(self, writer) Disassembler::new(self, writer)
} }

View File

@ -231,6 +231,15 @@ impl AnnotatedError for CompileError {
) )
] ]
} }
Self::ReturnTypeConflict { conflict, position } => {
smallvec![(
format!(
"Expected type {} but found type {}",
conflict.expected, conflict.actual
),
*position
)]
}
_ => SmallVec::new(), _ => SmallVec::new(),
} }
} }

View File

@ -50,16 +50,16 @@ use crate::{
/// ///
/// assert_eq!(chunk.instructions().len(), 3); /// assert_eq!(chunk.instructions().len(), 3);
/// ``` /// ```
pub fn compile(source: &str) -> Result<Chunk, DustError> { pub fn compile(program_name: Option<DustString>, source: &str) -> Result<Chunk, DustError> {
let lexer = Lexer::new(source); let lexer = Lexer::new(source);
let mut compiler = Compiler::new(lexer).map_err(|error| DustError::compile(error, source))?; let mut compiler =
compiler.is_main_chunk = true; Compiler::new(lexer, program_name).map_err(|error| DustError::compile(error, source))?;
compiler compiler
.compile() .compile()
.map_err(|error| DustError::compile(error, source))?; .map_err(|error| DustError::compile(error, source))?;
let chunk = compiler.finish(None::<DustString>); let chunk = compiler.finish();
Ok(chunk) Ok(chunk)
} }
@ -112,21 +112,12 @@ pub struct Compiler<'src> {
/// incremented when a new block is entered. /// incremented when a new block is entered.
block_index: u8, block_index: u8,
/// The current scope of the compiler. This is used to test if a variable is in scope. /// The current block scope of the compiler. This is used to test if a variable is in scope.
current_scope: Scope, current_scope: Scope,
/// Index of the [`Record`] that the VM will use when calling the function. This is a /// Index of the Chunk in its parent's prototype list. This is set to 0 for the main chunk but
/// depth-first index. /// that value is never read because the main chunk is not a callable function.
record_index: u8, prototype_index: u8,
/// Record index for the next nested chunk that is compiled. When a function is compiled, its
/// `record_index` is assigned from this value. Then `next_record_index` is incremented to
/// maintain the depth-first index. After the function is compiled, its `next_record_index`
/// is assigned back to this chunk.
next_record_index: u8,
/// Whether the compiler is compiling the main chunk.
is_main_chunk: bool,
current_token: Token<'src>, current_token: Token<'src>,
current_position: Span, current_position: Span,
@ -135,12 +126,15 @@ pub struct Compiler<'src> {
} }
impl<'src> Compiler<'src> { impl<'src> Compiler<'src> {
/// Creates a new compiler with the given lexer. /// Creates a new top-level compiler with the given lexer.
pub fn new(mut lexer: Lexer<'src>) -> Result<Self, CompileError> { pub fn new(
mut lexer: Lexer<'src>,
function_name: Option<DustString>,
) -> Result<Self, CompileError> {
let (current_token, current_position) = lexer.next_token()?; let (current_token, current_position) = lexer.next_token()?;
Ok(Compiler { Ok(Compiler {
function_name: None, function_name,
r#type: FunctionType { r#type: FunctionType {
type_parameters: None, type_parameters: None,
value_parameters: None, value_parameters: None,
@ -155,9 +149,7 @@ impl<'src> Compiler<'src> {
minimum_register: 0, minimum_register: 0,
block_index: 0, block_index: 0,
current_scope: Scope::default(), current_scope: Scope::default(),
record_index: 0, prototype_index: 0,
next_record_index: 1,
is_main_chunk: true,
current_token, current_token,
current_position, current_position,
previous_token: Token::Eof, previous_token: Token::Eof,
@ -165,39 +157,6 @@ impl<'src> Compiler<'src> {
}) })
} }
/// Creates a new chunk with the compiled data, optionally assigning a name to the chunk.
///
/// Note for maintainers: Do not give a name when compiling functions, only the main chunk. This
/// will allow [`Compiler::function_name`] to be both the name used for recursive calls and the
/// name of the function when it is compiled. The name can later be seen in the VM's call stack.
pub fn finish(self, name: Option<impl Into<DustString>>) -> Chunk {
let (instructions, positions): (SmallVec<[Instruction; 32]>, SmallVec<[Span; 32]>) = self
.instructions
.into_iter()
.map(|(instruction, _, position)| (instruction, position))
.unzip();
let locals = self
.locals
.into_iter()
.map(|(local, _)| local)
.collect::<SmallVec<[Local; 8]>>();
let chunk_name = name
.map(|into_name| into_name.into())
.or(self.function_name);
Chunk::new(
chunk_name,
self.r#type,
instructions,
positions,
self.constants,
locals,
self.prototypes,
self.stack_size,
self.record_index,
)
}
/// Compiles the source (which is in the lexer) while checking for errors and returning a /// Compiles the source (which is in the lexer) while checking for errors and returning a
/// [`CompileError`] if any are found. After calling this function, check its return value for /// [`CompileError`] if any are found. After calling this function, check its return value for
/// an error, then call [`Compiler::finish`] to get the compiled chunk. /// an error, then call [`Compiler::finish`] to get the compiled chunk.
@ -230,6 +189,36 @@ impl<'src> Compiler<'src> {
Ok(()) Ok(())
} }
/// Creates a new chunk with the compiled data, optionally assigning a name to the chunk.
///
/// Note for maintainers: Do not give a name when compiling functions, only the main chunk. This
/// will allow [`Compiler::function_name`] to be both the name used for recursive calls and the
/// name of the function when it is compiled. The name can later be seen in the VM's call stack.
pub fn finish(self) -> Chunk {
let (instructions, positions): (SmallVec<[Instruction; 32]>, SmallVec<[Span; 32]>) = self
.instructions
.into_iter()
.map(|(instruction, _, position)| (instruction, position))
.unzip();
let locals = self
.locals
.into_iter()
.map(|(local, _)| local)
.collect::<SmallVec<[Local; 8]>>();
Chunk {
name: self.function_name,
r#type: self.r#type,
instructions,
positions,
constants: self.constants,
locals,
prototypes: self.prototypes,
stack_size: self.stack_size,
prototype_index: self.prototype_index,
}
}
fn is_eof(&self) -> bool { fn is_eof(&self) -> bool {
matches!(self.current_token, Token::Eof) matches!(self.current_token, Token::Eof)
} }
@ -417,7 +406,7 @@ impl<'src> Compiler<'src> {
let operation = instruction.operation(); let operation = instruction.operation();
if let Operation::LOAD_LIST = operation { if let Operation::LOAD_LIST = operation {
let LoadList { start_register, .. } = LoadList::from(instruction); let LoadList { start_register, .. } = LoadList::from(*instruction);
let item_type = self.get_register_type(start_register)?; let item_type = self.get_register_type(start_register)?;
return Ok(Type::List(Box::new(item_type))); return Ok(Type::List(Box::new(item_type)));
@ -442,22 +431,13 @@ impl<'src> Compiler<'src> {
/// ///
/// If [`Self::type`] is already set, it will check if the given [Type] is compatible. /// If [`Self::type`] is already set, it will check if the given [Type] is compatible.
fn update_return_type(&mut self, new_return_type: Type) -> Result<(), CompileError> { fn update_return_type(&mut self, new_return_type: Type) -> Result<(), CompileError> {
if !self.is_main_chunk {
Type::function(self.r#type.clone())
.check(&new_return_type)
.map_err(|conflict| CompileError::ReturnTypeConflict {
conflict,
position: self.current_position,
})?;
}
if self.r#type.return_type != Type::None { if self.r#type.return_type != Type::None {
self.r#type self.r#type
.return_type .return_type
.check(&new_return_type) .check(&new_return_type)
.map_err(|conflict| CompileError::ReturnTypeConflict { .map_err(|conflict| CompileError::ReturnTypeConflict {
conflict, conflict,
position: self.current_position, position: self.previous_position,
})?; })?;
} }
@ -749,7 +729,7 @@ impl<'src> Compiler<'src> {
})?; })?;
let (left, push_back_left) = self.handle_binary_argument(&left_instruction)?; let (left, push_back_left) = self.handle_binary_argument(&left_instruction)?;
let left_is_mutable_local = if let Operation::GET_LOCAL = left_instruction.operation() { let left_is_mutable_local = if let Operation::GET_LOCAL = left_instruction.operation() {
let GetLocal { local_index, .. } = GetLocal::from(&left_instruction); let GetLocal { local_index, .. } = GetLocal::from(left_instruction);
self.locals self.locals
.get(local_index as usize) .get(local_index as usize)
@ -1020,7 +1000,7 @@ impl<'src> Compiler<'src> {
return self.parse_call_native(native_function); return self.parse_call_native(native_function);
} else if self.function_name.as_deref() == Some(identifier) { } else if self.function_name.as_deref() == Some(identifier) {
let destination = self.next_register(); let destination = self.next_register();
let load_function = Instruction::load_function(destination, self.record_index); let load_function = Instruction::load_function(destination, self.prototype_index);
self.emit_instruction(load_function, Type::SelfFunction, start_position); self.emit_instruction(load_function, Type::SelfFunction, start_position);
@ -1172,7 +1152,7 @@ impl<'src> Compiler<'src> {
self.advance()?; self.advance()?;
self.parse_expression()?; self.parse_expression()?;
let emit_test_and_jump = if matches!( if matches!(
self.get_last_operations(), self.get_last_operations(),
Some([ Some([
Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL, Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL,
@ -1183,13 +1163,8 @@ impl<'src> Compiler<'src> {
) { ) {
self.instructions.pop(); self.instructions.pop();
self.instructions.pop(); self.instructions.pop();
self.instructions.pop();
false
} else { } else {
true
};
if emit_test_and_jump {
let operand_register = self.next_register() - 1; let operand_register = self.next_register() - 1;
let test = Instruction::test(operand_register, true); let test = Instruction::test(operand_register, true);
@ -1212,7 +1187,6 @@ impl<'src> Compiler<'src> {
let if_block_end = self.instructions.len(); let if_block_end = self.instructions.len();
let mut if_block_distance = (if_block_end - if_block_start) as u8; let mut if_block_distance = (if_block_end - if_block_start) as u8;
let if_block_type = self.get_last_instruction_type(); let if_block_type = self.get_last_instruction_type();
// let if_last_register = self.next_register().saturating_sub(1);
if let Token::Else = self.current_token { if let Token::Else = self.current_token {
self.advance()?; self.advance()?;
@ -1275,22 +1249,12 @@ impl<'src> Compiler<'src> {
} }
} }
if emit_test_and_jump {
let jump = Instruction::jump(if_block_distance, true); let jump = Instruction::jump(if_block_distance, true);
self.instructions self.instructions
.insert(if_block_start, (jump, Type::None, if_block_start_position)); .insert(if_block_start, (jump, Type::None, if_block_start_position));
}
control_flow_register_consolidation(self); control_flow_register_consolidation(self);
// let else_last_register = self.next_register().saturating_sub(1);
// let point = Instruction::point(else_last_register, if_last_register);
// if if_last_register < else_last_register {
// self.emit_instruction(point, else_block_type, self.current_position);
// }
Ok(()) Ok(())
} }
@ -1456,10 +1420,10 @@ impl<'src> Compiler<'src> {
let Jump { let Jump {
offset, offset,
is_positive, is_positive,
} = Jump::from(&*instruction); } = Jump::from(*instruction);
let offset = offset as usize; let offset = offset as usize;
if is_positive && offset + index == instruction_length - 2 { if is_positive && offset + index == instruction_length - 1 {
*instruction = Instruction::jump((offset + 1) as u8, true); *instruction = Instruction::jump((offset + 1) as u8, true);
} }
} }
@ -1469,7 +1433,14 @@ impl<'src> Compiler<'src> {
} }
fn parse_implicit_return(&mut self) -> Result<(), CompileError> { fn parse_implicit_return(&mut self) -> Result<(), CompileError> {
if self.allow(Token::Semicolon)? { if matches!(self.get_last_operation(), Some(Operation::RETURN))
|| matches!(
self.get_last_operations(),
Some([Operation::RETURN, Operation::JUMP])
)
{
// 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, 0);
self.emit_instruction(r#return, Type::None, self.current_position); self.emit_instruction(r#return, Type::None, self.current_position);
@ -1503,10 +1474,10 @@ impl<'src> Compiler<'src> {
let Jump { let Jump {
offset, offset,
is_positive, is_positive,
} = Jump::from(&*instruction); } = Jump::from(*instruction);
let offset = offset as usize; let offset = offset as usize;
if is_positive && offset + index == instruction_length - 2 { if is_positive && offset + index == instruction_length - 1 {
*instruction = Instruction::jump((offset + 1) as u8, true); *instruction = Instruction::jump((offset + 1) as u8, true);
} }
} }
@ -1564,24 +1535,30 @@ impl<'src> Compiler<'src> {
fn parse_function(&mut self) -> Result<(), CompileError> { fn parse_function(&mut self) -> Result<(), CompileError> {
let function_start = self.current_position.0; let function_start = self.current_position.0;
let mut function_compiler = Compiler::new(self.lexer)?;
function_compiler.record_index = self.next_record_index; self.advance()?;
function_compiler.next_record_index = self.next_record_index + 1;
let identifier_info = if let Token::Identifier(text) = function_compiler.current_token { let identifier = if let Token::Identifier(text) = self.current_token {
let position = function_compiler.current_position; self.advance()?;
function_compiler.advance()?; Some(text)
function_compiler.function_name = Some(text.into());
Some((text, position))
} else { } else {
None None
}; };
function_compiler.expect(Token::LeftParenthesis)?; let mut function_compiler = if self.current_token == Token::LeftParenthesis {
let function_name = identifier.map(DustString::from);
Compiler::new(self.lexer, function_name)? // This will consume the left parenthesis
} else {
return Err(CompileError::ExpectedToken {
expected: TokenKind::LeftParenthesis,
found: self.current_token.to_owned(),
position: self.current_position,
});
};
function_compiler.prototype_index = self.prototypes.len() as u8;
let mut value_parameters: Option<SmallVec<[(u8, Type); 4]>> = None; let mut value_parameters: Option<SmallVec<[(u8, Type); 4]>> = None;
@ -1656,18 +1633,17 @@ impl<'src> Compiler<'src> {
self.previous_position = function_compiler.previous_position; self.previous_position = function_compiler.previous_position;
self.current_token = function_compiler.current_token; self.current_token = function_compiler.current_token;
self.current_position = function_compiler.current_position; self.current_position = function_compiler.current_position;
self.next_record_index = function_compiler.next_record_index;
self.lexer.skip_to(self.current_position.1); self.lexer.skip_to(self.current_position.1);
let function_end = function_compiler.previous_position.1; let function_end = function_compiler.previous_position.1;
let record_index = function_compiler.record_index; let prototype_index = function_compiler.prototype_index;
let chunk = function_compiler.finish(None::<&str>); let chunk = function_compiler.finish();
let destination = self.next_register(); let destination = self.next_register();
self.prototypes.push(chunk); self.prototypes.push(chunk);
if let Some((identifier, _)) = identifier_info { if let Some(identifier) = identifier {
self.declare_local( self.declare_local(
identifier, identifier,
destination, destination,
@ -1677,7 +1653,7 @@ impl<'src> Compiler<'src> {
); );
} }
let load_function = Instruction::load_function(destination, record_index); let load_function = Instruction::load_function(destination, prototype_index);
self.emit_instruction( self.emit_instruction(
load_function, load_function,

View File

@ -6,8 +6,8 @@ pub struct Add {
pub right: Argument, pub right: Argument,
} }
impl From<&Instruction> for Add { impl From<Instruction> for Add {
fn from(instruction: &Instruction) -> Self { fn from(instruction: Instruction) -> Self {
let destination = instruction.a_field(); let destination = instruction.a_field();
let (left, right) = instruction.b_and_c_as_arguments(); let (left, right) = instruction.b_and_c_as_arguments();

View File

@ -1,15 +1,13 @@
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::InstructionData;
pub struct Call { pub struct Call {
pub destination: u8, pub destination: u8,
pub function_register: u8, pub function_register: u8,
pub argument_count: u8, pub argument_count: u8,
} }
impl From<&Instruction> for Call { impl From<Instruction> for Call {
fn from(instruction: &Instruction) -> Self { fn from(instruction: Instruction) -> Self {
let destination = instruction.a_field(); let destination = instruction.a_field();
let function_register = instruction.b_field(); let function_register = instruction.b_field();
let argument_count = instruction.c_field(); let argument_count = instruction.c_field();
@ -22,20 +20,6 @@ impl From<&Instruction> for Call {
} }
} }
impl From<InstructionData> for Call {
fn from(instruction: InstructionData) -> Self {
let destination = instruction.a_field;
let function_register = instruction.b_field;
let argument_count = instruction.c_field;
Call {
destination,
function_register,
argument_count,
}
}
}
impl From<Call> for Instruction { impl From<Call> for Instruction {
fn from(call: Call) -> Self { fn from(call: Call) -> Self {
let a = call.destination; let a = call.destination;

View File

@ -1,15 +1,13 @@
use crate::{Instruction, NativeFunction, Operation}; use crate::{Instruction, NativeFunction, Operation};
use super::InstructionData;
pub struct CallNative { pub struct CallNative {
pub destination: u8, pub destination: u8,
pub function: NativeFunction, pub function: NativeFunction,
pub argument_count: u8, pub argument_count: u8,
} }
impl From<&Instruction> for CallNative { impl From<Instruction> for CallNative {
fn from(instruction: &Instruction) -> Self { fn from(instruction: Instruction) -> Self {
let destination = instruction.a_field(); let destination = instruction.a_field();
let function = NativeFunction::from(instruction.b_field()); let function = NativeFunction::from(instruction.b_field());
@ -21,16 +19,6 @@ impl From<&Instruction> for CallNative {
} }
} }
impl From<InstructionData> for CallNative {
fn from(data: InstructionData) -> Self {
CallNative {
destination: data.a_field,
function: NativeFunction::from(data.b_field),
argument_count: data.c_field,
}
}
}
impl From<CallNative> for Instruction { impl From<CallNative> for Instruction {
fn from(call_native: CallNative) -> Self { fn from(call_native: CallNative) -> Self {
let operation = Operation::CALL_NATIVE; let operation = Operation::CALL_NATIVE;

View File

@ -1,14 +1,12 @@
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::InstructionData;
pub struct Close { pub struct Close {
pub from: u8, pub from: u8,
pub to: u8, pub to: u8,
} }
impl From<&Instruction> for Close { impl From<Instruction> for Close {
fn from(instruction: &Instruction) -> Self { fn from(instruction: Instruction) -> Self {
Close { Close {
from: instruction.b_field(), from: instruction.b_field(),
to: instruction.c_field(), to: instruction.c_field(),
@ -16,15 +14,6 @@ impl From<&Instruction> for Close {
} }
} }
impl From<InstructionData> for Close {
fn from(instruction: InstructionData) -> Self {
Close {
from: instruction.b_field,
to: instruction.c_field,
}
}
}
impl From<Close> for Instruction { impl From<Close> for Instruction {
fn from(close: Close) -> Self { fn from(close: Close) -> Self {
let operation = Operation::CLOSE; let operation = Operation::CLOSE;

View File

@ -6,8 +6,8 @@ pub struct Divide {
pub right: Argument, pub right: Argument,
} }
impl From<&Instruction> for Divide { impl From<Instruction> for Divide {
fn from(instruction: &Instruction) -> Self { fn from(instruction: Instruction) -> Self {
let destination = instruction.a_field(); let destination = instruction.a_field();
let (left, right) = instruction.b_and_c_as_arguments(); let (left, right) = instruction.b_and_c_as_arguments();

View File

@ -6,8 +6,8 @@ pub struct Equal {
pub right: Argument, pub right: Argument,
} }
impl From<&Instruction> for Equal { impl From<Instruction> for Equal {
fn from(instruction: &Instruction) -> Self { fn from(instruction: Instruction) -> Self {
let value = instruction.d_field(); let value = instruction.d_field();
let (left, right) = instruction.b_and_c_as_arguments(); let (left, right) = instruction.b_and_c_as_arguments();

View File

@ -1,27 +1,12 @@
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::InstructionData;
pub struct GetLocal { pub struct GetLocal {
pub destination: u8, pub destination: u8,
pub local_index: u8, pub local_index: u8,
} }
impl From<InstructionData> for GetLocal { impl From<Instruction> for GetLocal {
fn from(data: InstructionData) -> Self { fn from(instruction: Instruction) -> Self {
let InstructionData {
a_field, b_field, ..
} = data;
GetLocal {
destination: a_field,
local_index: b_field,
}
}
}
impl From<&Instruction> for GetLocal {
fn from(instruction: &Instruction) -> Self {
let destination = instruction.a_field(); let destination = instruction.a_field();
let local_index = instruction.b_field(); let local_index = instruction.b_field();

View File

@ -5,8 +5,8 @@ pub struct Jump {
pub is_positive: bool, pub is_positive: bool,
} }
impl From<&Instruction> for Jump { impl From<Instruction> for Jump {
fn from(instruction: &Instruction) -> Self { fn from(instruction: Instruction) -> Self {
Jump { Jump {
offset: instruction.b_field(), offset: instruction.b_field(),
is_positive: instruction.c_field() != 0, is_positive: instruction.c_field() != 0,

View File

@ -6,8 +6,8 @@ pub struct Less {
pub right: Argument, pub right: Argument,
} }
impl From<&Instruction> for Less { impl From<Instruction> for Less {
fn from(instruction: &Instruction) -> Self { fn from(instruction: Instruction) -> Self {
let value = instruction.d_field(); let value = instruction.d_field();
let (left, right) = instruction.b_and_c_as_arguments(); let (left, right) = instruction.b_and_c_as_arguments();

View File

@ -6,8 +6,8 @@ pub struct LessEqual {
pub right: Argument, pub right: Argument,
} }
impl From<&Instruction> for LessEqual { impl From<Instruction> for LessEqual {
fn from(instruction: &Instruction) -> Self { fn from(instruction: Instruction) -> Self {
let value = instruction.d_field(); let value = instruction.d_field();
let (left, right) = instruction.b_and_c_as_arguments(); let (left, right) = instruction.b_and_c_as_arguments();

View File

@ -1,23 +1,17 @@
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::InstructionData;
pub struct LoadBoolean { pub struct LoadBoolean {
pub destination: u8, pub destination: u8,
pub value: bool, pub value: bool,
pub jump_next: bool, pub jump_next: bool,
} }
impl From<InstructionData> for LoadBoolean { impl From<Instruction> for LoadBoolean {
fn from(instruction: InstructionData) -> Self { fn from(instruction: Instruction) -> Self {
let destination = instruction.a_field;
let value = instruction.b_field != 0;
let jump_next = instruction.c_field != 0;
LoadBoolean { LoadBoolean {
destination, destination: instruction.a_field(),
value, value: instruction.b_field() != 0,
jump_next, jump_next: instruction.c_field() != 0,
} }
} }
} }

View File

@ -2,16 +2,14 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::InstructionData;
pub struct LoadConstant { pub struct LoadConstant {
pub destination: u8, pub destination: u8,
pub constant_index: u8, pub constant_index: u8,
pub jump_next: bool, pub jump_next: bool,
} }
impl From<&Instruction> for LoadConstant { impl From<Instruction> for LoadConstant {
fn from(instruction: &Instruction) -> Self { fn from(instruction: Instruction) -> Self {
let destination = instruction.a_field(); let destination = instruction.a_field();
let constant_index = instruction.b_field(); let constant_index = instruction.b_field();
let jump_next = instruction.c_field() != 0; let jump_next = instruction.c_field() != 0;
@ -24,20 +22,6 @@ impl From<&Instruction> for LoadConstant {
} }
} }
impl From<InstructionData> for LoadConstant {
fn from(instruction: InstructionData) -> Self {
let destination = instruction.a_field;
let constant_index = instruction.b_field;
let jump_next = instruction.c_field != 0;
LoadConstant {
destination,
constant_index,
jump_next,
}
}
}
impl From<LoadConstant> for Instruction { impl From<LoadConstant> for Instruction {
fn from(load_constant: LoadConstant) -> Self { fn from(load_constant: LoadConstant) -> Self {
let operation = Operation::LOAD_CONSTANT; let operation = Operation::LOAD_CONSTANT;

View File

@ -1,29 +1,20 @@
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use super::{Instruction, InstructionData, Operation}; use super::{Instruction, Operation};
pub struct LoadFunction { pub struct LoadFunction {
pub destination: u8, pub destination: u8,
pub record_index: u8, pub prototype_index: u8,
} }
impl From<&Instruction> for LoadFunction { impl From<Instruction> for LoadFunction {
fn from(instruction: &Instruction) -> Self { fn from(instruction: Instruction) -> Self {
let destination = instruction.a_field(); let destination = instruction.a_field();
let record_index = instruction.b_field(); let record_index = instruction.b_field();
LoadFunction { LoadFunction {
destination, destination,
record_index, prototype_index: record_index,
}
}
}
impl From<InstructionData> for LoadFunction {
fn from(instruction: InstructionData) -> Self {
LoadFunction {
destination: instruction.a_field,
record_index: instruction.b_field,
} }
} }
} }
@ -33,7 +24,7 @@ impl From<LoadFunction> for Instruction {
Instruction::new( Instruction::new(
Operation::LOAD_FUNCTION, Operation::LOAD_FUNCTION,
load_function.destination, load_function.destination,
load_function.record_index, load_function.prototype_index,
0, 0,
false, false,
false, false,
@ -44,6 +35,6 @@ impl From<LoadFunction> for Instruction {
impl Display for LoadFunction { impl Display for LoadFunction {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "R{} = F{}", self.destination, self.record_index) write!(f, "R{} = F{}", self.destination, self.prototype_index)
} }
} }

View File

@ -1,14 +1,12 @@
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::InstructionData;
pub struct LoadList { pub struct LoadList {
pub destination: u8, pub destination: u8,
pub start_register: u8, pub start_register: u8,
} }
impl From<&Instruction> for LoadList { impl From<Instruction> for LoadList {
fn from(instruction: &Instruction) -> Self { fn from(instruction: Instruction) -> Self {
let destination = instruction.a_field(); let destination = instruction.a_field();
let start_register = instruction.b_field(); let start_register = instruction.b_field();
@ -19,18 +17,6 @@ impl From<&Instruction> for LoadList {
} }
} }
impl From<InstructionData> for LoadList {
fn from(instruction: InstructionData) -> Self {
let destination = instruction.a_field;
let start_register = instruction.b_field;
LoadList {
destination,
start_register,
}
}
}
impl From<LoadList> for Instruction { impl From<LoadList> for Instruction {
fn from(load_list: LoadList) -> Self { fn from(load_list: LoadList) -> Self {
let operation = Operation::LOAD_LIST; let operation = Operation::LOAD_LIST;

View File

@ -1,27 +1,17 @@
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::InstructionData;
pub struct LoadSelf { pub struct LoadSelf {
pub destination: u8, pub destination: u8,
} }
impl From<&Instruction> for LoadSelf { impl From<Instruction> for LoadSelf {
fn from(instruction: &Instruction) -> Self { fn from(instruction: Instruction) -> Self {
let destination = instruction.a_field(); let destination = instruction.a_field();
LoadSelf { destination } LoadSelf { destination }
} }
} }
impl From<InstructionData> for LoadSelf {
fn from(instruction: InstructionData) -> Self {
let destination = instruction.a_field;
LoadSelf { destination }
}
}
impl From<LoadSelf> for Instruction { impl From<LoadSelf> for Instruction {
fn from(load_self: LoadSelf) -> Self { fn from(load_self: LoadSelf) -> Self {
let operation = Operation::LOAD_SELF; let operation = Operation::LOAD_SELF;

View File

@ -255,10 +255,10 @@ impl Instruction {
}) })
} }
pub fn load_function(destination: u8, record_index: u8) -> Instruction { pub fn load_function(destination: u8, prototype_index: u8) -> Instruction {
Instruction::from(LoadFunction { Instruction::from(LoadFunction {
destination, destination,
record_index, prototype_index,
}) })
} }
@ -510,12 +510,12 @@ impl Instruction {
} }
pub fn disassembly_info(&self) -> String { pub fn disassembly_info(&self) -> String {
let (operation, data) = self.decode(); let operation = self.operation();
match operation { match operation {
Operation::POINT => Point::from(data).to_string(), Operation::POINT => Point::from(*self).to_string(),
Operation::CLOSE => { Operation::CLOSE => {
let Close { from, to } = Close::from(data); let Close { from, to } = Close::from(*self);
format!("R{from}..R{to}") format!("R{from}..R{to}")
} }
@ -524,7 +524,7 @@ impl Instruction {
destination, destination,
value, value,
jump_next, jump_next,
} = LoadBoolean::from(data); } = LoadBoolean::from(*self);
if jump_next { if jump_next {
format!("R{destination} = {value} && JUMP +1") format!("R{destination} = {value} && JUMP +1")
@ -537,7 +537,7 @@ impl Instruction {
destination, destination,
constant_index, constant_index,
jump_next, jump_next,
} = LoadConstant::from(self); } = LoadConstant::from(*self);
if jump_next { if jump_next {
format!("R{destination} = C{constant_index} JUMP +1") format!("R{destination} = C{constant_index} JUMP +1")
@ -545,18 +545,18 @@ impl Instruction {
format!("R{destination} = C{constant_index}") format!("R{destination} = C{constant_index}")
} }
} }
Operation::LOAD_FUNCTION => LoadFunction::from(self).to_string(), Operation::LOAD_FUNCTION => LoadFunction::from(*self).to_string(),
Operation::LOAD_LIST => { Operation::LOAD_LIST => {
let LoadList { let LoadList {
destination, destination,
start_register, start_register,
} = LoadList::from(self); } = LoadList::from(*self);
let end_register = destination.saturating_sub(1); let end_register = destination.saturating_sub(1);
format!("R{destination} = [R{start_register}..=R{end_register}]",) format!("R{destination} = [R{start_register}..=R{end_register}]",)
} }
Operation::LOAD_SELF => { Operation::LOAD_SELF => {
let LoadSelf { destination } = LoadSelf::from(self); let LoadSelf { destination } = LoadSelf::from(*self);
format!("R{destination} = self") format!("R{destination} = self")
} }
@ -564,7 +564,7 @@ impl Instruction {
let GetLocal { let GetLocal {
destination, destination,
local_index, local_index,
} = GetLocal::from(self); } = GetLocal::from(*self);
format!("R{destination} = L{local_index}") format!("R{destination} = L{local_index}")
} }
@ -572,7 +572,7 @@ impl Instruction {
let SetLocal { let SetLocal {
register_index, register_index,
local_index, local_index,
} = SetLocal::from(self); } = SetLocal::from(*self);
format!("L{local_index} = R{register_index}") format!("L{local_index} = R{register_index}")
} }
@ -581,7 +581,7 @@ impl Instruction {
destination, destination,
left, left,
right, right,
} = Add::from(self); } = Add::from(*self);
format!("R{destination} = {left} + {right}") format!("R{destination} = {left} + {right}")
} }
@ -590,7 +590,7 @@ impl Instruction {
destination, destination,
left, left,
right, right,
} = Subtract::from(self); } = Subtract::from(*self);
format!("R{destination} = {left} - {right}") format!("R{destination} = {left} - {right}")
} }
@ -599,7 +599,7 @@ impl Instruction {
destination, destination,
left, left,
right, right,
} = Multiply::from(self); } = Multiply::from(*self);
format!("R{destination} = {left} * {right}") format!("R{destination} = {left} * {right}")
} }
@ -608,7 +608,7 @@ impl Instruction {
destination, destination,
left, left,
right, right,
} = Divide::from(self); } = Divide::from(*self);
format!("R{destination} = {left} / {right}") format!("R{destination} = {left} / {right}")
} }
@ -617,7 +617,7 @@ impl Instruction {
destination, destination,
left, left,
right, right,
} = Modulo::from(self); } = Modulo::from(*self);
format!("R{destination} = {left} % {right}") format!("R{destination} = {left} % {right}")
} }
@ -625,7 +625,7 @@ impl Instruction {
let Test { let Test {
operand_register, operand_register,
test_value, test_value,
} = Test::from(self); } = Test::from(*self);
let bang = if test_value { "" } else { "!" }; let bang = if test_value { "" } else { "!" };
format!("if {bang}R{operand_register} {{ JUMP +1 }}",) format!("if {bang}R{operand_register} {{ JUMP +1 }}",)
@ -635,25 +635,25 @@ impl Instruction {
destination, destination,
argument, argument,
test_value, test_value,
} = TestSet::from(self); } = TestSet::from(*self);
let bang = if test_value { "" } else { "!" }; let bang = if test_value { "" } else { "!" };
format!("if {bang}{argument} {{ JUMP +1 }} else {{ R{destination} = {argument} }}") format!("if {bang}{argument} {{ JUMP +1 }} else {{ R{destination} = {argument} }}")
} }
Operation::EQUAL => { Operation::EQUAL => {
let Equal { value, left, right } = Equal::from(self); let Equal { value, left, right } = Equal::from(*self);
let comparison_symbol = if value { "==" } else { "!=" }; let comparison_symbol = if value { "==" } else { "!=" };
format!("if {left} {comparison_symbol} {right} {{ JUMP +1 }}") format!("if {left} {comparison_symbol} {right} {{ JUMP +1 }}")
} }
Operation::LESS => { Operation::LESS => {
let Less { value, left, right } = Less::from(self); let Less { value, left, right } = Less::from(*self);
let comparison_symbol = if value { "<" } else { ">=" }; let comparison_symbol = if value { "<" } else { ">=" };
format!("if {left} {comparison_symbol} {right} {{ JUMP +1 }}") format!("if {left} {comparison_symbol} {right} {{ JUMP +1 }}")
} }
Operation::LESS_EQUAL => { Operation::LESS_EQUAL => {
let LessEqual { value, left, right } = LessEqual::from(self); let LessEqual { value, left, right } = LessEqual::from(*self);
let comparison_symbol = if value { "<=" } else { ">" }; let comparison_symbol = if value { "<=" } else { ">" };
format!("if {left} {comparison_symbol} {right} {{ JUMP +1 }}") format!("if {left} {comparison_symbol} {right} {{ JUMP +1 }}")
@ -662,7 +662,7 @@ impl Instruction {
let Negate { let Negate {
destination, destination,
argument, argument,
} = Negate::from(self); } = Negate::from(*self);
format!("R{destination} = -{argument}") format!("R{destination} = -{argument}")
} }
@ -670,7 +670,7 @@ impl Instruction {
let Not { let Not {
destination, destination,
argument, argument,
} = Not::from(self); } = Not::from(*self);
format!("R{destination} = !{argument}") format!("R{destination} = !{argument}")
} }
@ -678,7 +678,7 @@ impl Instruction {
let Jump { let Jump {
offset, offset,
is_positive, is_positive,
} = Jump::from(self); } = Jump::from(*self);
if is_positive { if is_positive {
format!("JUMP +{offset}") format!("JUMP +{offset}")
@ -691,7 +691,7 @@ impl Instruction {
destination, destination,
function_register, function_register,
argument_count, argument_count,
} = Call::from(self); } = Call::from(*self);
let arguments_start = destination.saturating_sub(argument_count); let arguments_start = destination.saturating_sub(argument_count);
match argument_count { match argument_count {
@ -709,7 +709,7 @@ impl Instruction {
destination, destination,
function, function,
argument_count, argument_count,
} = CallNative::from(self); } = CallNative::from(*self);
let arguments_start = destination.saturating_sub(argument_count); let arguments_start = destination.saturating_sub(argument_count);
let arguments_end = arguments_start + argument_count; let arguments_end = arguments_start + argument_count;
let mut info_string = if function.returns_value() { let mut info_string = if function.returns_value() {
@ -734,7 +734,7 @@ impl Instruction {
let Return { let Return {
should_return_value, should_return_value,
return_register, return_register,
} = Return::from(self); } = Return::from(*self);
if should_return_value { if should_return_value {
format!("RETURN R{return_register}") format!("RETURN R{return_register}")

View File

@ -6,8 +6,8 @@ pub struct Modulo {
pub right: Argument, pub right: Argument,
} }
impl From<&Instruction> for Modulo { impl From<Instruction> for Modulo {
fn from(instruction: &Instruction) -> Self { fn from(instruction: Instruction) -> Self {
let destination = instruction.a_field(); let destination = instruction.a_field();
let (left, right) = instruction.b_and_c_as_arguments(); let (left, right) = instruction.b_and_c_as_arguments();

View File

@ -6,8 +6,8 @@ pub struct Multiply {
pub right: Argument, pub right: Argument,
} }
impl From<&Instruction> for Multiply { impl From<Instruction> for Multiply {
fn from(instruction: &Instruction) -> Self { fn from(instruction: Instruction) -> Self {
let destination = instruction.a_field(); let destination = instruction.a_field();
let (left, right) = instruction.b_and_c_as_arguments(); let (left, right) = instruction.b_and_c_as_arguments();

View File

@ -5,8 +5,8 @@ pub struct Negate {
pub argument: Argument, pub argument: Argument,
} }
impl From<&Instruction> for Negate { impl From<Instruction> for Negate {
fn from(instruction: &Instruction) -> Self { fn from(instruction: Instruction) -> Self {
let destination = instruction.a_field(); let destination = instruction.a_field();
let argument = instruction.b_as_argument(); let argument = instruction.b_as_argument();

View File

@ -5,8 +5,8 @@ pub struct Not {
pub argument: Argument, pub argument: Argument,
} }
impl From<&Instruction> for Not { impl From<Instruction> for Not {
fn from(instruction: &Instruction) -> Self { fn from(instruction: Instruction) -> Self {
let destination = instruction.a_field(); let destination = instruction.a_field();
let argument = instruction.b_as_argument(); let argument = instruction.b_as_argument();

View File

@ -2,15 +2,13 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::InstructionData;
pub struct Point { pub struct Point {
pub from: u8, pub from: u8,
pub to: u8, pub to: u8,
} }
impl From<&Instruction> for Point { impl From<Instruction> for Point {
fn from(instruction: &Instruction) -> Self { fn from(instruction: Instruction) -> Self {
Point { Point {
from: instruction.b_field(), from: instruction.b_field(),
to: instruction.c_field(), to: instruction.c_field(),
@ -18,15 +16,6 @@ impl From<&Instruction> for Point {
} }
} }
impl From<InstructionData> for Point {
fn from(instruction: InstructionData) -> Self {
Point {
from: instruction.b_field,
to: instruction.c_field,
}
}
}
impl Display for Point { impl Display for Point {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let Point { from, to } = self; let Point { from, to } = self;

View File

@ -1,27 +1,12 @@
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::InstructionData;
pub struct Return { pub struct Return {
pub should_return_value: bool, pub should_return_value: bool,
pub return_register: u8, pub return_register: u8,
} }
impl From<InstructionData> for Return { impl From<Instruction> for Return {
fn from(data: InstructionData) -> Self { fn from(instruction: Instruction) -> Self {
let InstructionData {
b_field, c_field, ..
} = data;
Return {
should_return_value: b_field != 0,
return_register: c_field,
}
}
}
impl From<&Instruction> for Return {
fn from(instruction: &Instruction) -> Self {
let should_return_value = instruction.b_field() != 0; let should_return_value = instruction.b_field() != 0;
let return_register = instruction.c_field(); let return_register = instruction.c_field();

View File

@ -5,8 +5,8 @@ pub struct SetLocal {
pub local_index: u8, pub local_index: u8,
} }
impl From<&Instruction> for SetLocal { impl From<Instruction> for SetLocal {
fn from(instruction: &Instruction) -> Self { fn from(instruction: Instruction) -> Self {
let register_index = instruction.b_field(); let register_index = instruction.b_field();
let local_index = instruction.c_field(); let local_index = instruction.c_field();

View File

@ -6,8 +6,8 @@ pub struct Subtract {
pub right: Argument, pub right: Argument,
} }
impl From<&Instruction> for Subtract { impl From<Instruction> for Subtract {
fn from(instruction: &Instruction) -> Self { fn from(instruction: Instruction) -> Self {
let destination = instruction.a_field(); let destination = instruction.a_field();
let (left, right) = instruction.b_and_c_as_arguments(); let (left, right) = instruction.b_and_c_as_arguments();

View File

@ -5,8 +5,8 @@ pub struct Test {
pub test_value: bool, pub test_value: bool,
} }
impl From<&Instruction> for Test { impl From<Instruction> for Test {
fn from(instruction: &Instruction) -> Self { fn from(instruction: Instruction) -> Self {
let operand_register = instruction.b_field(); let operand_register = instruction.b_field();
let test_value = instruction.c_field() != 0; let test_value = instruction.c_field() != 0;

View File

@ -6,8 +6,8 @@ pub struct TestSet {
pub test_value: bool, pub test_value: bool,
} }
impl From<&Instruction> for TestSet { impl From<Instruction> for TestSet {
fn from(instruction: &Instruction) -> Self { fn from(instruction: Instruction) -> Self {
let destination = instruction.a_field(); let destination = instruction.a_field();
let argument = instruction.b_as_argument(); let argument = instruction.b_as_argument();
let test_value = instruction.c_field() != 0; let test_value = instruction.c_field() != 0;

View File

@ -64,7 +64,6 @@ impl ConcreteValue {
} }
} }
#[inline(always)]
pub fn add(&self, other: &Self) -> ConcreteValue { pub fn add(&self, other: &Self) -> ConcreteValue {
use ConcreteValue::*; use ConcreteValue::*;
@ -120,22 +119,21 @@ impl ConcreteValue {
} }
} }
pub fn subtract(&self, other: &Self) -> Result<ConcreteValue, ValueError> { pub fn subtract(&self, other: &Self) -> ConcreteValue {
use ConcreteValue::*; use ConcreteValue::*;
let difference = match (self, other) { match (self, other) {
(Byte(left), Byte(right)) => ConcreteValue::Byte(left.saturating_sub(*right)), (Byte(left), Byte(right)) => ConcreteValue::Byte(left.saturating_sub(*right)),
(Float(left), Float(right)) => ConcreteValue::Float(left - right), (Float(left), Float(right)) => ConcreteValue::Float(left - right),
(Integer(left), Integer(right)) => ConcreteValue::Integer(left.saturating_sub(*right)), (Integer(left), Integer(right)) => ConcreteValue::Integer(left.saturating_sub(*right)),
_ => { _ => panic!(
return Err(ValueError::CannotSubtract( "{}",
self.clone().to_value(), ValueError::CannotSubtract(
other.clone().to_value(), Value::Concrete(self.clone()),
)) Value::Concrete(other.clone())
)
),
} }
};
Ok(difference)
} }
pub fn multiply(&self, other: &Self) -> Result<ConcreteValue, ValueError> { pub fn multiply(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
@ -217,30 +215,30 @@ impl ConcreteValue {
Ok(not) Ok(not)
} }
pub fn equal(&self, other: &ConcreteValue) -> Result<ConcreteValue, ValueError> { pub fn equals(&self, other: &ConcreteValue) -> bool {
use ConcreteValue::*; use ConcreteValue::*;
let equal = match (self, other) { match (self, other) {
(Boolean(left), Boolean(right)) => ConcreteValue::Boolean(left == right), (Boolean(left), Boolean(right)) => left == right,
(Byte(left), Byte(right)) => ConcreteValue::Boolean(left == right), (Byte(left), Byte(right)) => left == right,
(Character(left), Character(right)) => ConcreteValue::Boolean(left == right), (Character(left), Character(right)) => left == right,
(Float(left), Float(right)) => ConcreteValue::Boolean(left == right), (Float(left), Float(right)) => left == right,
(Integer(left), Integer(right)) => ConcreteValue::Boolean(left == right), (Integer(left), Integer(right)) => left == right,
(List(left), List(right)) => ConcreteValue::Boolean(left == right), (List(left), List(right)) => left == right,
(Range(left), Range(right)) => ConcreteValue::Boolean(left == right), (Range(left), Range(right)) => left == right,
(String(left), String(right)) => ConcreteValue::Boolean(left == right), (String(left), String(right)) => left == right,
_ => { _ => {
return Err(ValueError::CannotCompare( panic!(
self.clone().to_value(), "{}",
other.clone().to_value(), ValueError::CannotCompare(
)) Value::Concrete(self.clone()),
Value::Concrete(other.clone())
)
)
}
} }
};
Ok(equal)
} }
#[inline(always)]
pub fn less_than(&self, other: &ConcreteValue) -> Result<ConcreteValue, ValueError> { pub fn less_than(&self, other: &ConcreteValue) -> Result<ConcreteValue, ValueError> {
use ConcreteValue::*; use ConcreteValue::*;
@ -264,7 +262,7 @@ impl ConcreteValue {
Ok(less_than) Ok(less_than)
} }
pub fn less_than_or_equal(&self, other: &ConcreteValue) -> Result<ConcreteValue, ValueError> { pub fn less_than_or_equals(&self, other: &ConcreteValue) -> Result<ConcreteValue, ValueError> {
use ConcreteValue::*; use ConcreteValue::*;
let less_than_or_equal = match (self, other) { let less_than_or_equal = match (self, other) {

View File

@ -8,7 +8,7 @@ use super::DustString;
pub struct Function { pub struct Function {
pub name: Option<DustString>, pub name: Option<DustString>,
pub r#type: FunctionType, pub r#type: FunctionType,
pub record_index: u8, pub prototype_index: u8,
} }
impl Display for Function { impl Display for Function {

View File

@ -85,24 +85,24 @@ impl Value {
} }
pub fn add(&self, other: &Value) -> Value { pub fn add(&self, other: &Value) -> Value {
let concrete = match (self, other) { let sum = match (self, other) {
(Value::Concrete(left), Value::Concrete(right)) => left.add(right), (Value::Concrete(left), Value::Concrete(right)) => left.add(right),
_ => panic!("{}", ValueError::CannotAdd(self.clone(), other.clone())), _ => panic!("{}", ValueError::CannotAdd(self.clone(), other.clone())),
}; };
Value::Concrete(concrete) Value::Concrete(sum)
} }
pub fn subtract(&self, other: &Value) -> Result<Value, ValueError> { pub fn subtract(&self, other: &Value) -> Value {
match (self, other) { let difference = match (self, other) {
(Value::Concrete(left), Value::Concrete(right)) => { (Value::Concrete(left), Value::Concrete(right)) => left.subtract(right),
left.subtract(right).map(Value::Concrete) _ => panic!(
} "{}",
_ => Err(ValueError::CannotSubtract( ValueError::CannotSubtract(self.clone(), other.clone())
self.to_owned(), ),
other.to_owned(), };
)),
} Value::Concrete(difference)
} }
pub fn multiply(&self, other: &Value) -> Result<Value, ValueError> { pub fn multiply(&self, other: &Value) -> Result<Value, ValueError> {
@ -151,12 +151,13 @@ impl Value {
} }
} }
pub fn equal(&self, other: &Value) -> Result<Value, ValueError> { pub fn equals(&self, other: &Value) -> bool {
match (self, other) { match (self, other) {
(Value::Concrete(left), Value::Concrete(right)) => { (Value::Concrete(left), Value::Concrete(right)) => left.equals(right),
left.equal(right).map(Value::Concrete) _ => panic!(
} "{}",
_ => Err(ValueError::CannotCompare(self.to_owned(), other.to_owned())), ValueError::CannotCompare(self.to_owned(), other.to_owned())
),
} }
} }
@ -169,10 +170,10 @@ impl Value {
} }
} }
pub fn less_equal(&self, other: &Value) -> Result<Value, ValueError> { pub fn less_than_or_equals(&self, other: &Value) -> Result<Value, ValueError> {
match (self, other) { match (self, other) {
(Value::Concrete(left), Value::Concrete(right)) => { (Value::Concrete(left), Value::Concrete(right)) => {
left.less_than_or_equal(right).map(Value::Concrete) left.less_than_or_equals(right).map(Value::Concrete)
} }
_ => Err(ValueError::CannotCompare(self.to_owned(), other.to_owned())), _ => Err(ValueError::CannotCompare(self.to_owned(), other.to_owned())),
} }

View File

@ -81,18 +81,17 @@ impl Display for CallStack {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
writeln!(f, "-- DUST CALL STACK --")?; writeln!(f, "-- DUST CALL STACK --")?;
for function_call in &self.calls { for function_call in self.calls.iter().rev() {
writeln!(f, "{function_call}")?; writeln!(f, "{function_call}")?;
} }
Ok(()) writeln!(f, "--")
} }
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct FunctionCall { pub struct FunctionCall {
pub name: Option<DustString>, pub name: Option<DustString>,
pub record_index: u8,
pub return_register: u8, pub return_register: u8,
pub ip: usize, pub ip: usize,
} }
@ -101,7 +100,6 @@ impl Display for FunctionCall {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let FunctionCall { let FunctionCall {
name, name,
record_index,
return_register, return_register,
.. ..
} = self; } = self;
@ -110,9 +108,6 @@ impl Display for FunctionCall {
.map(|name| name.as_str()) .map(|name| name.as_str())
.unwrap_or("anonymous"); .unwrap_or("anonymous");
write!( write!(f, "{name} (Return register: {return_register})")
f,
"{name} (Record: {record_index}, Return register: {return_register})"
)
} }
} }

View File

@ -18,10 +18,10 @@ pub use run_action::RunAction;
pub use thread::{Thread, ThreadSignal}; pub use thread::{Thread, ThreadSignal};
use tracing::{span, Level}; use tracing::{span, Level};
use crate::{compile, Chunk, DustError, Value}; use crate::{compile, Chunk, DustError, DustString, Value};
pub fn run(source: &str) -> Result<Option<Value>, DustError> { pub fn run(program_name: Option<DustString>, source: &str) -> Result<Option<Value>, DustError> {
let chunk = compile(source)?; let chunk = compile(program_name, source)?;
let vm = Vm::new(chunk); let vm = Vm::new(chunk);
Ok(vm.run()) Ok(vm.run())

View File

@ -1,63 +1,30 @@
use std::mem::replace; use std::mem::replace;
use smallvec::SmallVec;
use tracing::trace; use tracing::trace;
use crate::{DustString, Function, FunctionType, Local, Span, Value}; use crate::{Argument, Chunk, DustString, Function, Span, Value};
use super::{run_action::RunAction, Pointer, Register}; use super::{Pointer, Register};
#[derive(Debug)] #[derive(Debug)]
pub struct Record { pub struct Record<'a> {
pub ip: usize, pub ip: usize,
pub actions: SmallVec<[RunAction; 32]>, pub chunk: &'a Chunk,
stack: Vec<Register>, stack: Vec<Register>,
last_assigned_register: Option<u8>,
name: Option<DustString>,
r#type: FunctionType,
positions: SmallVec<[Span; 32]>,
constants: SmallVec<[Value; 16]>,
locals: SmallVec<[Local; 8]>,
index: u8,
} }
impl Record { impl<'a> Record<'a> {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(chunk: &'a Chunk) -> Self {
actions: SmallVec<[RunAction; 32]>,
last_assigned_register: Option<u8>,
name: Option<DustString>,
r#type: FunctionType,
positions: SmallVec<[Span; 32]>,
constants: SmallVec<[Value; 16]>,
locals: SmallVec<[Local; 8]>,
stack_size: usize,
index: u8,
) -> Self {
Self { Self {
ip: 0, ip: 0,
actions, stack: vec![Register::Empty; chunk.stack_size],
stack: vec![Register::Empty; stack_size], chunk,
last_assigned_register,
name,
r#type,
positions,
constants,
locals,
index,
} }
} }
pub fn name(&self) -> Option<&DustString> { pub fn name(&self) -> Option<&DustString> {
self.name.as_ref() self.chunk.name.as_ref()
}
pub fn index(&self) -> u8 {
self.index
} }
pub fn stack_size(&self) -> usize { pub fn stack_size(&self) -> usize {
@ -65,19 +32,11 @@ impl Record {
} }
pub fn current_position(&self) -> Span { pub fn current_position(&self) -> Span {
self.positions[self.ip] self.chunk.positions[self.ip]
}
pub fn last_assigned_register(&self) -> Option<u8> {
self.last_assigned_register
} }
pub fn as_function(&self) -> Function { pub fn as_function(&self) -> Function {
Function { self.chunk.as_function()
name: self.name.clone(),
r#type: self.r#type.clone(),
record_index: self.index,
}
} }
pub(crate) fn follow_pointer(&self, pointer: Pointer) -> &Value { pub(crate) fn follow_pointer(&self, pointer: Pointer) -> &Value {
@ -103,7 +62,6 @@ impl Record {
} }
pub fn set_register(&mut self, to_register: u8, register: Register) { pub fn set_register(&mut self, to_register: u8, register: Register) {
self.last_assigned_register = Some(to_register);
let to_register = to_register as usize; let to_register = to_register as usize;
assert!( assert!(
@ -215,11 +173,10 @@ impl Record {
} }
/// DRY helper to get a value from an Argument /// DRY helper to get a value from an Argument
pub fn get_argument(&self, index: u8, is_constant: bool) -> &Value { pub fn get_argument(&self, argument: Argument) -> &Value {
if is_constant { match argument {
self.get_constant(index) Argument::Constant(constant_index) => self.get_constant(constant_index),
} else { Argument::Register(register_index) => self.open_register(register_index),
self.open_register(index)
} }
} }
@ -227,21 +184,21 @@ impl Record {
let constant_index = constant_index as usize; let constant_index = constant_index as usize;
assert!( assert!(
constant_index < self.constants.len(), constant_index < self.chunk.constants.len(),
"VM Error: Constant index out of bounds" "VM Error: Constant index out of bounds"
); );
&self.constants[constant_index] &self.chunk.constants[constant_index]
} }
pub fn get_local_register(&self, local_index: u8) -> u8 { pub fn get_local_register(&self, local_index: u8) -> u8 {
let local_index = local_index as usize; let local_index = local_index as usize;
assert!( assert!(
local_index < self.locals.len(), local_index < self.chunk.locals.len(),
"VM Error: Local index out of bounds" "VM Error: Local index out of bounds"
); );
self.locals[local_index].register_index self.chunk.locals[local_index].register_index
} }
} }

View File

@ -2,11 +2,11 @@ use tracing::trace;
use crate::{ use crate::{
instruction::{ instruction::{
Call, CallNative, Close, GetLocal, LoadBoolean, LoadConstant, LoadFunction, LoadList, Add, Call, CallNative, Close, Divide, Equal, GetLocal, Jump, Less, LessEqual, LoadBoolean,
LoadSelf, Point, Return, LoadConstant, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not, Point,
Return, SetLocal, Subtract, Test, TestSet,
}, },
vm::VmError, AbstractList, Argument, ConcreteValue, Instruction, Type, Value,
AbstractList, ConcreteValue, Instruction, InstructionData, Type, Value,
}; };
use super::{thread::ThreadSignal, Pointer, Record, Register}; use super::{thread::ThreadSignal, Pointer, Record, Register};
@ -14,31 +14,22 @@ use super::{thread::ThreadSignal, Pointer, Record, Register};
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct RunAction { pub struct RunAction {
pub logic: RunnerLogic, pub logic: RunnerLogic,
pub data: InstructionData, pub instruction: Instruction,
}
impl From<&Instruction> for RunAction {
fn from(instruction: &Instruction) -> Self {
let (operation, data) = instruction.decode();
let logic = RUNNER_LOGIC_TABLE[operation.0 as usize];
RunAction { logic, data }
}
} }
impl From<Instruction> for RunAction { impl From<Instruction> for RunAction {
fn from(instruction: Instruction) -> Self { fn from(instruction: Instruction) -> Self {
let (operation, data) = instruction.decode(); let operation = instruction.operation();
let logic = RUNNER_LOGIC_TABLE[operation.0 as usize]; let logic = RUNNER_LOGIC_TABLE[operation.0 as usize];
RunAction { logic, data } RunAction { logic, instruction }
} }
} }
pub type RunnerLogic = fn(InstructionData, &mut Record) -> ThreadSignal; pub type RunnerLogic = fn(Instruction, &mut Record) -> ThreadSignal;
pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [ pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [
r#move, point,
close, close,
load_boolean, load_boolean,
load_constant, load_constant,
@ -65,8 +56,8 @@ pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [
r#return, r#return,
]; ];
pub fn r#move(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn point(instruction: Instruction, record: &mut Record) -> ThreadSignal {
let Point { from, to } = instruction_data.into(); let Point { from, to } = instruction.into();
let from_register = record.get_register(from); let from_register = record.get_register(from);
let from_register_is_empty = matches!(from_register, Register::Empty); let from_register_is_empty = matches!(from_register, Register::Empty);
@ -79,8 +70,8 @@ pub fn r#move(instruction_data: InstructionData, record: &mut Record) -> ThreadS
ThreadSignal::Continue ThreadSignal::Continue
} }
pub fn close(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn close(instruction: Instruction, record: &mut Record) -> ThreadSignal {
let Close { from, to } = instruction_data.into(); let Close { from, to } = instruction.into();
assert!(from < to, "Runtime Error: Malformed instruction"); assert!(from < to, "Runtime Error: Malformed instruction");
@ -96,12 +87,12 @@ pub fn close(instruction_data: InstructionData, record: &mut Record) -> ThreadSi
ThreadSignal::Continue ThreadSignal::Continue
} }
pub fn load_boolean(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn load_boolean(instruction: Instruction, record: &mut Record) -> ThreadSignal {
let LoadBoolean { let LoadBoolean {
destination, destination,
value, value,
jump_next, jump_next,
} = instruction_data.into(); } = instruction.into();
let boolean = Value::Concrete(ConcreteValue::Boolean(value)); let boolean = Value::Concrete(ConcreteValue::Boolean(value));
let register = Register::Value(boolean); let register = Register::Value(boolean);
@ -114,12 +105,12 @@ pub fn load_boolean(instruction_data: InstructionData, record: &mut Record) -> T
ThreadSignal::Continue ThreadSignal::Continue
} }
pub fn load_constant(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn load_constant(instruction: Instruction, record: &mut Record) -> ThreadSignal {
let LoadConstant { let LoadConstant {
destination, destination,
constant_index, constant_index,
jump_next, jump_next,
} = instruction_data.into(); } = instruction.into();
let register = Register::Pointer(Pointer::Constant(constant_index)); let register = Register::Pointer(Pointer::Constant(constant_index));
trace!("Load constant {constant_index} into R{destination}"); trace!("Load constant {constant_index} into R{destination}");
@ -133,11 +124,11 @@ pub fn load_constant(instruction_data: InstructionData, record: &mut Record) ->
ThreadSignal::Continue ThreadSignal::Continue
} }
pub fn load_list(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn load_list(instruction: Instruction, record: &mut Record) -> ThreadSignal {
let LoadList { let LoadList {
destination, destination,
start_register, start_register,
} = instruction_data.into(); } = instruction.into();
let mut item_pointers = Vec::with_capacity((destination - start_register) as usize); let mut item_pointers = Vec::with_capacity((destination - start_register) as usize);
let mut item_type = Type::Any; let mut item_type = Type::Any;
@ -172,20 +163,20 @@ pub fn load_list(instruction_data: InstructionData, record: &mut Record) -> Thre
ThreadSignal::Continue ThreadSignal::Continue
} }
pub fn load_function(instruction_data: InstructionData, _: &mut Record) -> ThreadSignal { pub fn load_function(instruction: Instruction, _: &mut Record) -> ThreadSignal {
let LoadFunction { let LoadFunction {
destination, destination,
record_index, prototype_index,
} = instruction_data.into(); } = instruction.into();
ThreadSignal::LoadFunction { ThreadSignal::LoadFunction {
from_record_index: record_index, destination,
to_register_index: destination, prototype_index,
} }
} }
pub fn load_self(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn load_self(instruction: Instruction, record: &mut Record) -> ThreadSignal {
let LoadSelf { destination } = instruction_data.into(); let LoadSelf { destination } = instruction.into();
let function = record.as_function(); let function = record.as_function();
let register = Register::Value(Value::Function(function)); let register = Register::Value(Value::Function(function));
@ -194,11 +185,11 @@ pub fn load_self(instruction_data: InstructionData, record: &mut Record) -> Thre
ThreadSignal::Continue ThreadSignal::Continue
} }
pub fn get_local(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn get_local(instruction: Instruction, record: &mut Record) -> ThreadSignal {
let GetLocal { let GetLocal {
destination, destination,
local_index, local_index,
} = instruction_data.into(); } = instruction.into();
let local_register_index = record.get_local_register(local_index); let local_register_index = record.get_local_register(local_index);
let register = Register::Pointer(Pointer::Stack(local_register_index)); let register = Register::Pointer(Pointer::Stack(local_register_index));
@ -207,77 +198,59 @@ pub fn get_local(instruction_data: InstructionData, record: &mut Record) -> Thre
ThreadSignal::Continue ThreadSignal::Continue
} }
pub fn set_local(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn set_local(instruction: Instruction, record: &mut Record) -> ThreadSignal {
let InstructionData { let SetLocal {
b_field: b, register_index,
c_field: c, local_index,
.. } = instruction.into();
} = instruction_data; let local_register_index = record.get_local_register(local_index);
let local_register_index = record.get_local_register(c); let register = Register::Pointer(Pointer::Stack(register_index));
let register = Register::Pointer(Pointer::Stack(b));
record.set_register(local_register_index, register); record.set_register(local_register_index, register);
ThreadSignal::Continue ThreadSignal::Continue
} }
pub fn add(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn add(instruction: Instruction, record: &mut Record) -> ThreadSignal {
let InstructionData { let Add {
a_field: a, destination,
b_field: b, left,
c_field: c, right,
b_is_constant, } = instruction.into();
c_is_constant, let left = record.get_argument(left);
.. let right = record.get_argument(right);
} = instruction_data;
let left = record.get_argument(b, b_is_constant);
let right = record.get_argument(c, c_is_constant);
let sum = left.add(right); let sum = left.add(right);
let register = Register::Value(sum); let register = Register::Value(sum);
record.set_register(a, register); record.set_register(destination, register);
ThreadSignal::Continue ThreadSignal::Continue
} }
pub fn subtract(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn subtract(instruction: Instruction, record: &mut Record) -> ThreadSignal {
let InstructionData { let Subtract {
a_field: a, destination,
b_field: b, left,
c_field: c, right,
b_is_constant, } = instruction.into();
c_is_constant, let left = record.get_argument(left);
.. let right = record.get_argument(right);
} = instruction_data; let difference = left.subtract(right);
let left = record.get_argument(b, b_is_constant);
let right = record.get_argument(c, c_is_constant);
let difference = match (left, right) {
(Value::Concrete(left), Value::Concrete(right)) => match (left, right) {
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
ConcreteValue::Integer(left - right).to_value()
}
_ => panic!("Value Error: Cannot subtract values {left} and {right}"),
},
_ => panic!("Value Error: Cannot subtract values {left} and {right}"),
};
let register = Register::Value(difference); let register = Register::Value(difference);
record.set_register(a, register); record.set_register(destination, register);
ThreadSignal::Continue ThreadSignal::Continue
} }
pub fn multiply(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn multiply(instruction: Instruction, record: &mut Record) -> ThreadSignal {
let InstructionData { let Multiply {
a_field: a, destination,
b_field: b, left,
c_field: c, right,
b_is_constant, } = instruction.into();
c_is_constant, let left = record.get_argument(left);
.. let right = record.get_argument(right);
} = instruction_data;
let left = record.get_argument(b, b_is_constant);
let right = record.get_argument(c, c_is_constant);
let product = match (left, right) { let product = match (left, right) {
(Value::Concrete(left), Value::Concrete(right)) => match (left, right) { (Value::Concrete(left), Value::Concrete(right)) => match (left, right) {
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => { (ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
@ -289,22 +262,19 @@ pub fn multiply(instruction_data: InstructionData, record: &mut Record) -> Threa
}; };
let register = Register::Value(product); let register = Register::Value(product);
record.set_register(a, register); record.set_register(destination, register);
ThreadSignal::Continue ThreadSignal::Continue
} }
pub fn divide(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn divide(instruction: Instruction, record: &mut Record) -> ThreadSignal {
let InstructionData { let Divide {
a_field: a, destination,
b_field: b, left,
c_field: c, right,
b_is_constant, } = instruction.into();
c_is_constant, let left = record.get_argument(left);
.. let right = record.get_argument(right);
} = instruction_data;
let left = record.get_argument(b, b_is_constant);
let right = record.get_argument(c, c_is_constant);
let quotient = match (left, right) { let quotient = match (left, right) {
(Value::Concrete(left), Value::Concrete(right)) => match (left, right) { (Value::Concrete(left), Value::Concrete(right)) => match (left, right) {
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => { (ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
@ -316,22 +286,19 @@ pub fn divide(instruction_data: InstructionData, record: &mut Record) -> ThreadS
}; };
let register = Register::Value(quotient); let register = Register::Value(quotient);
record.set_register(a, register); record.set_register(destination, register);
ThreadSignal::Continue ThreadSignal::Continue
} }
pub fn modulo(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn modulo(instruction: Instruction, record: &mut Record) -> ThreadSignal {
let InstructionData { let Modulo {
a_field: a, destination,
b_field: b, left,
c_field: c, right,
b_is_constant, } = instruction.into();
c_is_constant, let left = record.get_argument(left);
.. let right = record.get_argument(right);
} = instruction_data;
let left = record.get_argument(b, b_is_constant);
let right = record.get_argument(c, c_is_constant);
let remainder = match (left, right) { let remainder = match (left, right) {
(Value::Concrete(left), Value::Concrete(right)) => match (left, right) { (Value::Concrete(left), Value::Concrete(right)) => match (left, right) {
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => { (ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
@ -343,22 +310,22 @@ pub fn modulo(instruction_data: InstructionData, record: &mut Record) -> ThreadS
}; };
let register = Register::Value(remainder); let register = Register::Value(remainder);
record.set_register(a, register); record.set_register(destination, register);
ThreadSignal::Continue ThreadSignal::Continue
} }
pub fn test(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn test(instruction: Instruction, record: &mut Record) -> ThreadSignal {
let InstructionData { let Test {
b_field, c_field, .. operand_register,
} = instruction_data; test_value,
let value = record.open_register(b_field); } = instruction.into();
let value = record.open_register(operand_register);
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value { let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
*boolean *boolean
} else { } else {
panic!("VM Error: Expected boolean value for TEST operation",); panic!("VM Error: Expected boolean value for TEST operation",);
}; };
let test_value = c_field != 0;
if boolean == test_value { if boolean == test_value {
record.ip += 1; record.ip += 1;
@ -367,141 +334,110 @@ pub fn test(instruction_data: InstructionData, record: &mut Record) -> ThreadSig
ThreadSignal::Continue ThreadSignal::Continue
} }
pub fn test_set(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn test_set(instruction: Instruction, record: &mut Record) -> ThreadSignal {
let InstructionData { let TestSet {
a_field: a, destination,
b_field: b, argument,
c_field: c, test_value,
b_is_constant, } = instruction.into();
.. let value = record.get_argument(argument);
} = instruction_data;
let value = record.get_argument(b, b_is_constant);
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value { let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
*boolean *boolean
} else { } else {
panic!("VM Error: Expected boolean value for TEST_SET operation",); panic!("VM Error: Expected boolean value for TEST_SET operation",);
}; };
let test_value = c != 0;
if boolean == test_value { if boolean == test_value {
record.ip += 1; record.ip += 1;
} else { } else {
let pointer = if b_is_constant { let pointer = match argument {
Pointer::Constant(b) Argument::Constant(constant_index) => Pointer::Constant(constant_index),
} else { Argument::Register(register_index) => Pointer::Stack(register_index),
Pointer::Stack(b)
}; };
let register = Register::Pointer(pointer); let register = Register::Pointer(pointer);
record.set_register(a, register); record.set_register(destination, register);
} }
ThreadSignal::Continue ThreadSignal::Continue
} }
pub fn equal(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn equal(instruction: Instruction, record: &mut Record) -> ThreadSignal {
let InstructionData { let Equal { value, left, right } = instruction.into();
b_field: b, let left = record.get_argument(left);
c_field: c, let right = record.get_argument(right);
b_is_constant, let is_equal = left.equals(right);
c_is_constant,
d_field: d,
..
} = instruction_data;
let left = record.get_argument(b, b_is_constant);
let right = record.get_argument(c, c_is_constant);
let is_equal = left == right;
if is_equal == d { if is_equal == value {
record.ip += 1; record.ip += 1;
} }
ThreadSignal::Continue ThreadSignal::Continue
} }
pub fn less(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn less(instruction: Instruction, record: &mut Record) -> ThreadSignal {
let InstructionData { let Less { value, left, right } = instruction.into();
b_field: b, let left = record.get_argument(left);
c_field: c, let right = record.get_argument(right);
b_is_constant,
c_is_constant,
d_field: d,
..
} = instruction_data;
let left = record.get_argument(b, b_is_constant);
let right = record.get_argument(c, c_is_constant);
let is_less = left < right; let is_less = left < right;
if is_less == d { if is_less == value {
record.ip += 1; record.ip += 1;
} }
ThreadSignal::Continue ThreadSignal::Continue
} }
pub fn less_equal(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn less_equal(instruction: Instruction, record: &mut Record) -> ThreadSignal {
let InstructionData { let LessEqual { value, left, right } = instruction.into();
b_field: b, let left = record.get_argument(left);
c_field: c, let right = record.get_argument(right);
b_is_constant,
c_is_constant,
d_field: d,
..
} = instruction_data;
let left = record.get_argument(b, b_is_constant);
let right = record.get_argument(c, c_is_constant);
let is_less_or_equal = left <= right; let is_less_or_equal = left <= right;
if is_less_or_equal == d { if is_less_or_equal == value {
record.ip += 1; record.ip += 1;
} }
ThreadSignal::Continue ThreadSignal::Continue
} }
pub fn negate(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn negate(instruction: Instruction, record: &mut Record) -> ThreadSignal {
let InstructionData { let Negate {
a_field: a, destination,
b_field: b, argument,
b_is_constant, } = instruction.into();
.. let argument = record.get_argument(argument);
} = instruction_data;
let argument = record.get_argument(b, b_is_constant);
let negated = argument.negate(); let negated = argument.negate();
let register = Register::Value(negated); let register = Register::Value(negated);
record.set_register(a, register); record.set_register(destination, register);
ThreadSignal::Continue ThreadSignal::Continue
} }
pub fn not(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn not(instruction: Instruction, record: &mut Record) -> ThreadSignal {
let InstructionData { let Not {
a_field: a, destination,
b_field: b, argument,
b_is_constant, } = instruction.into();
.. let argument = record.get_argument(argument);
} = instruction_data;
let argument = record.get_argument(b, b_is_constant);
let not = match argument { let not = match argument {
Value::Concrete(ConcreteValue::Boolean(boolean)) => ConcreteValue::Boolean(!boolean), Value::Concrete(ConcreteValue::Boolean(boolean)) => ConcreteValue::Boolean(!boolean),
_ => panic!("VM Error: Expected boolean value for NOT operation"), _ => panic!("VM Error: Expected boolean value for NOT operation"),
}; };
let register = Register::Value(Value::Concrete(not)); let register = Register::Value(Value::Concrete(not));
record.set_register(a, register); record.set_register(destination, register);
ThreadSignal::Continue ThreadSignal::Continue
} }
pub fn jump(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn jump(instruction: Instruction, record: &mut Record) -> ThreadSignal {
let InstructionData { let Jump {
b_field: b, offset,
c_field: c, is_positive,
.. } = instruction.into();
} = instruction_data; let offset = offset as usize;
let offset = b as usize;
let is_positive = c != 0;
if is_positive { if is_positive {
record.ip += offset; record.ip += offset;
@ -512,36 +448,26 @@ pub fn jump(instruction_data: InstructionData, record: &mut Record) -> ThreadSig
ThreadSignal::Continue ThreadSignal::Continue
} }
pub fn call(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn call(instruction: Instruction, _: &mut Record) -> ThreadSignal {
let Call { let Call {
destination, destination: return_register,
function_register, function_register,
argument_count, argument_count,
} = instruction_data.into(); } = instruction.into();
let function_value = record.open_register(function_register);
let function = match function_value {
Value::Function(function) => function,
_ => panic!(
"{}",
VmError::ExpectedFunction {
value: function_value.clone()
}
),
};
ThreadSignal::Call { ThreadSignal::Call {
record_index: function.record_index, function_register,
return_register: destination, return_register,
argument_count, argument_count,
} }
} }
pub fn call_native(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn call_native(instruction: Instruction, record: &mut Record) -> ThreadSignal {
let CallNative { let CallNative {
destination, destination,
function, function,
argument_count, argument_count,
} = instruction_data.into(); } = instruction.into();
let first_argument_index = destination - argument_count; let first_argument_index = destination - argument_count;
let argument_range = first_argument_index..destination; let argument_range = first_argument_index..destination;
@ -550,11 +476,11 @@ pub fn call_native(instruction_data: InstructionData, record: &mut Record) -> Th
.unwrap_or_else(|error| panic!("{error:?}")) .unwrap_or_else(|error| panic!("{error:?}"))
} }
pub fn r#return(instruction_data: InstructionData, _: &mut Record) -> ThreadSignal { pub fn r#return(instruction: Instruction, _: &mut Record) -> ThreadSignal {
let Return { let Return {
should_return_value, should_return_value,
return_register, return_register,
} = instruction_data.into(); } = instruction.into();
ThreadSignal::Return { ThreadSignal::Return {
should_return_value, should_return_value,
@ -570,7 +496,7 @@ mod tests {
use super::*; use super::*;
const ALL_OPERATIONS: [(Operation, RunnerLogic); 24] = [ const ALL_OPERATIONS: [(Operation, RunnerLogic); 24] = [
(Operation::POINT, r#move), (Operation::POINT, point),
(Operation::CLOSE, close), (Operation::CLOSE, close),
(Operation::LOAD_BOOLEAN, load_boolean), (Operation::LOAD_BOOLEAN, load_boolean),
(Operation::LOAD_CONSTANT, load_constant), (Operation::LOAD_CONSTANT, load_constant),

View File

@ -1,3 +1,5 @@
use std::fmt::{self, Display, Formatter};
use tracing::{info, trace}; use tracing::{info, trace};
use crate::{ use crate::{
@ -5,38 +7,36 @@ use crate::{
Chunk, DustString, Value, Chunk, DustString, Value,
}; };
use super::{record::Record, CallStack}; use super::{record::Record, CallStack, RunAction};
pub struct Thread { pub struct Thread {
call_stack: CallStack, chunk: Chunk,
records: Vec<Record>,
} }
impl Thread { impl Thread {
pub fn new(chunk: Chunk) -> Self { pub fn new(chunk: Chunk) -> Self {
let call_stack = CallStack::with_capacity(chunk.prototypes().len() + 1); Thread { chunk }
let mut records = Vec::with_capacity(chunk.prototypes().len() + 1);
chunk.into_records(&mut records);
Thread {
call_stack,
records,
}
} }
pub fn run(&mut self) -> Option<Value> { pub fn run(&mut self) -> Option<Value> {
let mut active = &mut self.records[0]; let mut call_stack = CallStack::with_capacity(self.chunk.prototypes.len() + 1);
let mut current_call = FunctionCall { let mut records = Vec::with_capacity(self.chunk.prototypes.len() + 1);
name: active.name().cloned(),
record_index: active.index(), let main_call = FunctionCall {
return_register: active.stack_size() as u8 - 1, name: self.chunk.name.clone(),
ip: active.ip, return_register: 0,
ip: 0,
}; };
let main_record = Record::new(&self.chunk);
call_stack.push(main_call);
records.push(main_record);
let mut active_record = &mut records[0];
info!( info!(
"Starting thread with {}", "Starting thread with {}",
active active_record
.as_function() .as_function()
.name .name
.unwrap_or_else(|| DustString::from("anonymous")) .unwrap_or_else(|| DustString::from("anonymous"))
@ -44,138 +44,106 @@ impl Thread {
loop { loop {
trace!( trace!(
"Run \"{}\" | Record = {} | IP = {}", "Run \"{}\" | IP = {}",
active active_record
.name() .name()
.cloned() .cloned()
.unwrap_or_else(|| DustString::from("anonymous")), .unwrap_or_else(|| DustString::from("anonymous")),
active.index(), active_record.ip
active.ip
); );
if active.ip >= active.actions.len() { let instruction = active_record.chunk.instructions[active_record.ip];
return None; let action = RunAction::from(instruction);
} let signal = (action.logic)(action.instruction, active_record);
let action = active.actions[active.ip]; trace!("Thread Signal: {}", signal);
let signal = (action.logic)(action.data, active);
trace!("Thread Signal: {:?}", signal); active_record.ip += 1;
active.ip += 1;
match signal { match signal {
ThreadSignal::Continue => {} ThreadSignal::Continue => {}
ThreadSignal::Call { ThreadSignal::Call {
record_index, function_register,
return_register, return_register,
argument_count, argument_count,
} => { } => {
let record_index = record_index as usize; let function = active_record
.open_register(function_register)
.as_function()
.unwrap();
let first_argument_register = return_register - argument_count; let first_argument_register = return_register - argument_count;
let mut arguments = Vec::with_capacity(argument_count as usize); let mut arguments = Vec::with_capacity(argument_count as usize);
for register_index in first_argument_register..return_register { for register_index in first_argument_register..return_register {
let value = active.clone_register_value_or_constant(register_index); let value = active_record.clone_register_value_or_constant(register_index);
arguments.push(value); arguments.push(value);
} }
if record_index == active.index() as usize { trace!("Passing arguments: {arguments:?}");
trace!("Recursion detected");
if let Some(record) = self.call_stack.last_mut() {
record.ip = active.ip;
}
active.ip = 0;
}
let prototype = &self.chunk.prototypes[function.prototype_index as usize];
let next_record = Record::new(prototype);
let next_call = FunctionCall { let next_call = FunctionCall {
name: active.name().cloned(), name: next_record.name().cloned(),
record_index: active.index(),
return_register, return_register,
ip: active.ip, ip: active_record.ip,
}; };
if self call_stack.push(next_call);
.call_stack records.push(next_record);
.last()
.is_some_and(|call| call != &next_call)
|| self.call_stack.is_empty()
{
self.call_stack.push(current_call);
}
current_call = next_call; active_record = records.last_mut().unwrap();
active = &mut self.records[record_index];
for (index, argument) in arguments.into_iter().enumerate() { for (index, argument) in arguments.into_iter().enumerate() {
active.set_register(index as u8, Register::Value(argument)); active_record.set_register(index as u8, Register::Value(argument));
} }
} }
ThreadSignal::LoadFunction { ThreadSignal::LoadFunction {
from_record_index, destination,
to_register_index, prototype_index,
} => { } => {
let function_record_index = from_record_index as usize; let function_record_index = prototype_index as usize;
let original_record_index = active.index() as usize; let function = self.chunk.prototypes[function_record_index].as_function();
active = &mut self.records[function_record_index];
let function = active.as_function();
let register = Register::Value(Value::Function(function)); let register = Register::Value(Value::Function(function));
active = &mut self.records[original_record_index]; active_record.set_register(destination, register);
active.set_register(to_register_index, register);
} }
ThreadSignal::Return { ThreadSignal::Return {
should_return_value, should_return_value,
return_register, return_register,
} => { } => {
trace!("\n{:#?}{}", self.call_stack, current_call); trace!("Returning with call stack:\n{call_stack}");
let outer_call = if let Some(call) = self.call_stack.pop() { let return_value = if should_return_value {
call Some(
} else if should_return_value { active_record
let return_value = active .empty_register_or_clone_constant(return_register, Register::Empty),
.empty_register_or_clone_constant(return_register, Register::Empty); )
let next_call = self.call_stack.pop();
if next_call.is_none() {
return Some(return_value);
}
let next_index = active.index() as usize - 1;
let next_record = &mut self.records[next_index];
next_record.set_register(
current_call.return_register,
Register::Value(return_value),
);
current_call = next_call.unwrap();
active = next_record;
continue;
} else { } else {
return None; None
}; };
let record_index = outer_call.record_index as usize;
let current_call = call_stack.pop_or_panic();
let _current_record = records.pop().unwrap();
let destination = current_call.return_register; let destination = current_call.return_register;
if should_return_value { if call_stack.is_empty() {
let return_value = active return if should_return_value {
.empty_register_or_clone_constant(return_register, Register::Empty); Some(return_value.unwrap())
let outer_record = &mut self.records[record_index]; } else {
None
outer_record.set_register(destination, Register::Value(return_value)); };
} }
active = &mut self.records[record_index]; let outer_record = records.last_mut().unwrap();
current_call = outer_call;
if should_return_value {
outer_record
.set_register(destination, Register::Value(return_value.unwrap()));
}
active_record = outer_record;
} }
} }
} }
@ -186,7 +154,7 @@ impl Thread {
pub enum ThreadSignal { pub enum ThreadSignal {
Continue, Continue,
Call { Call {
record_index: u8, function_register: u8,
return_register: u8, return_register: u8,
argument_count: u8, argument_count: u8,
}, },
@ -195,7 +163,13 @@ pub enum ThreadSignal {
return_register: u8, return_register: u8,
}, },
LoadFunction { LoadFunction {
from_record_index: u8, prototype_index: u8,
to_register_index: u8, destination: u8,
}, },
} }
impl Display for ThreadSignal {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}

View File

@ -5,4 +5,4 @@ fn fib (n: int) -> int {
fib(n - 1) + fib(n - 2) fib(n - 1) + fib(n - 2)
} }
write_line(fib(10)) write_line(fib(25))