1
0

Optimize; Remove non-working optimizations; Improve CLI

This commit is contained in:
Jeff 2025-01-09 01:44:07 -05:00
parent f667716336
commit 2365979561
19 changed files with 211 additions and 259 deletions

4
Cargo.lock generated
View File

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

View File

@ -6,70 +6,74 @@ use std::{
}; };
use clap::{ use clap::{
builder::{styling::AnsiColor, StyledStr, Styles}, builder::{
styling::{AnsiColor, Color},
StyledStr, Styles,
},
crate_authors, crate_description, crate_version, crate_authors, crate_description, crate_version,
error::ErrorKind, error::ErrorKind,
Args, ColorChoice, Error, Parser, Subcommand, ValueHint, Args, ColorChoice, Error, Parser, Subcommand, ValueHint,
}; };
use color_print::cstr; use color_print::{cformat, cstr};
use dust_lang::{CompileError, Compiler, DustError, DustString, Lexer, Span, Token, Vm}; use dust_lang::{CompileError, Compiler, DustError, DustString, Lexer, Span, Token, Vm};
use tracing::{subscriber::set_global_default, Level}; use tracing::{subscriber::set_global_default, Level};
use tracing_subscriber::FmtSubscriber; use tracing_subscriber::FmtSubscriber;
const CLI_HELP_TEMPLATE: &str = cstr!( const ABOUT: &str = cstr!(
r#"
<bright-magenta><bold>Dust CLI
</bold></bright-magenta>
{about}
<bold>Version:</bold> {version}
<bold>Author:</bold> {author}
<bold>License:</bold> GPL-3.0
<bold>Repository:</bold> git.jeffa.io/jeff/dust
<bright-magenta,bold>Usage
</bright-magenta,bold>
{tab}{usage}
<bright-magenta,bold>Modes
</bright-magenta,bold>
{subcommands}
<bright-magenta,bold>Options
</bright-magenta,bold>
{options}
"#
);
const MODE_HELP_TEMPLATE: &str = cstr!(
r#" r#"
<bright-magenta,bold>Dust CLI <bright-magenta,bold>Dust CLI
</bright-magenta,bold> </>
{about} {about}
<bold>Version:</bold> {version} <bold> Version:</> {version}
<bold>Author:</bold> {author} <bold>🦀 Author:</> {author}
<bold>License:</bold> GPL-3.0 <bold> License:</> GPL-3.0
<bold>Repository:</bold> git.jeffa.io/jeff/dust <bold>🔬 Repository:</> https://git.jeffa.io/jeff/dust
"#
);
<bright-magenta,bold>Usage const PLAIN_ABOUT: &str = r#"
</bright-magenta,bold> {about}
{usage} "#;
<bright-magenta,bold>Options const USAGE: &str = cstr!(
</bright-magenta,bold> r#"
<bright-magenta,bold>Usage:</> {usage}
"#
);
const SUBCOMMANDS: &str = cstr!(
r#"
<bright-magenta,bold>Modes:</>
{subcommands}
"#
);
const OPTIONS: &str = cstr!(
r#"
<bright-magenta,bold>Options:</>
{options} {options}
"# "#
); );
const CREATE_MAIN_HELP_TEMPLATE: fn() -> String =
|| cformat!("{ABOUT}{USAGE}{SUBCOMMANDS}{OPTIONS}");
const CREATE_MODE_HELP_TEMPLATE: fn(&str) -> String = |title| {
cformat!(
"\
<bright-magenta,bold>{title}\n</>\
{PLAIN_ABOUT}{USAGE}{OPTIONS}
"
)
};
const STYLES: Styles = Styles::styled() const STYLES: Styles = Styles::styled()
.header(AnsiColor::BrightMagenta.on_default().bold()) .literal(AnsiColor::Cyan.on_default())
.usage(AnsiColor::BrightCyan.on_default().bold()) .placeholder(AnsiColor::Cyan.on_default())
.literal(AnsiColor::BrightCyan.on_default()) .valid(AnsiColor::BrightCyan.on_default())
.placeholder(AnsiColor::BrightMagenta.on_default()) .invalid(AnsiColor::BrightYellow.on_default())
.error(AnsiColor::BrightRed.on_default().bold()) .error(AnsiColor::BrightRed.on_default());
.valid(AnsiColor::Blue.on_default())
.invalid(AnsiColor::BrightRed.on_default());
#[derive(Parser)] #[derive(Parser)]
#[clap( #[clap(
@ -77,7 +81,7 @@ const STYLES: Styles = Styles::styled()
author = crate_authors!(), author = crate_authors!(),
about = crate_description!(), about = crate_description!(),
color = ColorChoice::Auto, color = ColorChoice::Auto,
help_template = StyledStr::from(CLI_HELP_TEMPLATE), help_template = CREATE_MAIN_HELP_TEMPLATE(),
styles = STYLES, styles = STYLES,
)] )]
struct Cli { struct Cli {
@ -122,7 +126,7 @@ struct Input {
#[derive(Args)] #[derive(Args)]
#[command( #[command(
short_flag = 'r', short_flag = 'r',
help_template = MODE_HELP_TEMPLATE help_template = CREATE_MODE_HELP_TEMPLATE("Run Mode")
)] )]
struct Run { struct Run {
/// Print the time taken for compilation and execution /// Print the time taken for compilation and execution
@ -149,7 +153,7 @@ enum Mode {
/// Compile and print the bytecode disassembly /// Compile and print the bytecode disassembly
#[command( #[command(
short_flag = 'd', short_flag = 'd',
help_template = MODE_HELP_TEMPLATE help_template = CREATE_MODE_HELP_TEMPLATE("Disassemble Mode")
)] )]
Disassemble { Disassemble {
/// Style disassembly output /// Style disassembly output
@ -167,7 +171,7 @@ enum Mode {
/// Lex the source code and print the tokens /// Lex the source code and print the tokens
#[command( #[command(
short_flag = 't', short_flag = 't',
help_template = MODE_HELP_TEMPLATE help_template = CREATE_MODE_HELP_TEMPLATE("Tokenize Mode")
)] )]
Tokenize { Tokenize {
/// Style token output /// Style token output
@ -224,7 +228,7 @@ 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 program_name = name.or(file_name); let program_name = name.or(file_name);
let mut compiler = match Compiler::new(lexer, program_name) { let mut compiler = match Compiler::new(lexer, program_name, true) {
Ok(compiler) => compiler, Ok(compiler) => compiler,
Err(error) => { Err(error) => {
handle_compile_error(error, &source); handle_compile_error(error, &source);
@ -312,7 +316,7 @@ 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 program_name = name.or(file_name); let program_name = name.or(file_name);
let mut compiler = match Compiler::new(lexer, program_name) { let mut compiler = match Compiler::new(lexer, program_name, true) {
Ok(compiler) => compiler, Ok(compiler) => compiler,
Err(error) => { Err(error) => {
handle_compile_error(error, &source); handle_compile_error(error, &source);

View File

@ -1,6 +1,6 @@
[package] [package]
name = "dust-lang" name = "dust-lang"
description = "Interpreter library for the Dust programming language" description = "Dust programming language library"
authors.workspace = true authors.workspace = true
edition.workspace = true edition.workspace = true
license.workspace = true license.workspace = true
@ -17,7 +17,6 @@ serde_json = "1.0.117"
getrandom = { version = "0.2", features = [ getrandom = { version = "0.2", features = [
"js", "js",
] } # Indirect dependency, for wasm builds ] } # Indirect dependency, for wasm builds
smallvec = { version = "1.13.2", features = ["const_generics", "serde"] }
smartstring = { version = "1.0.1", features = [ smartstring = { version = "1.0.1", features = [
"serde", "serde",
], default-features = false } ], default-features = false }
@ -25,7 +24,6 @@ tracing = "0.1.41"
[dev-dependencies] [dev-dependencies]
criterion = { version = "0.3.4", features = ["html_reports"] } criterion = { version = "0.3.4", features = ["html_reports"] }
smallvec = { version = "1.13.2", features = ["const_generics"] }
[[bench]] [[bench]]
name = "addictive_addition" name = "addictive_addition"

View File

@ -24,7 +24,6 @@ use std::fmt::{self, Debug, Display, Formatter, Write as FmtWrite};
use std::io::Write; use std::io::Write;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
use crate::{DustString, Function, FunctionType, Instruction, Span, Value}; use crate::{DustString, Function, FunctionType, Instruction, Span, Value};
@ -36,10 +35,10 @@ pub struct Chunk {
pub(crate) name: Option<DustString>, pub(crate) name: Option<DustString>,
pub(crate) r#type: FunctionType, pub(crate) r#type: FunctionType,
pub(crate) instructions: SmallVec<[Instruction; 32]>, pub(crate) instructions: Vec<Instruction>,
pub(crate) positions: SmallVec<[Span; 32]>, pub(crate) positions: Vec<Span>,
pub(crate) constants: SmallVec<[Value; 16]>, pub(crate) constants: Vec<Value>,
pub(crate) locals: SmallVec<[Local; 8]>, pub(crate) locals: Vec<Local>,
pub(crate) prototypes: Vec<Chunk>, pub(crate) prototypes: Vec<Chunk>,
pub(crate) register_count: usize, pub(crate) register_count: usize,
@ -51,10 +50,10 @@ impl Chunk {
pub fn with_data( pub fn with_data(
name: Option<DustString>, name: Option<DustString>,
r#type: FunctionType, r#type: FunctionType,
instructions: impl Into<SmallVec<[Instruction; 32]>>, instructions: impl Into<Vec<Instruction>>,
positions: impl Into<SmallVec<[Span; 32]>>, positions: impl Into<Vec<Span>>,
constants: impl Into<SmallVec<[Value; 16]>>, constants: impl Into<Vec<Value>>,
locals: impl Into<SmallVec<[Local; 8]>>, locals: impl Into<Vec<Local>>,
prototypes: Vec<Chunk>, prototypes: Vec<Chunk>,
) -> Self { ) -> Self {
Self { Self {

View File

@ -1,7 +1,5 @@
use std::num::{ParseFloatError, ParseIntError}; use std::num::{ParseFloatError, ParseIntError};
use smallvec::{smallvec, SmallVec};
use crate::{AnnotatedError, LexError, Scope, Span, TokenKind, TokenOwned, Type, TypeConflict}; use crate::{AnnotatedError, LexError, Scope, Span, TokenKind, TokenOwned, Type, TypeConflict};
/// Compilation errors /// Compilation errors
@ -212,7 +210,7 @@ impl AnnotatedError for CompileError {
} }
} }
fn detail_snippets(&self) -> SmallVec<[(String, Span); 2]> { fn detail_snippets(&self) -> Vec<(String, Span)> {
match self { match self {
Self::CannotAddArguments { Self::CannotAddArguments {
left_type, left_type,
@ -220,31 +218,31 @@ impl AnnotatedError for CompileError {
right_type, right_type,
right_position, right_position,
} => { } => {
smallvec![ vec![
( (
format!("A value of type \"{left_type}\" was used here."), format!("A value of type \"{left_type}\" was used here."),
*left_position *left_position,
), ),
( (
format!("A value of type \"{right_type}\" was used here."), format!("A value of type \"{right_type}\" was used here."),
*right_position *right_position,
) ),
] ]
} }
Self::ReturnTypeConflict { conflict, position } => { Self::ReturnTypeConflict { conflict, position } => {
smallvec![( vec![(
format!( format!(
"Expected type {} but found type {}", "Expected type {} but found type {}",
conflict.expected, conflict.actual conflict.expected, conflict.actual
), ),
*position *position,
)] )]
} }
_ => SmallVec::new(), _ => Vec::with_capacity(0),
} }
} }
fn help_snippets(&self) -> SmallVec<[(String, Span); 2]> { fn help_snippets(&self) -> Vec<(String, Span)> {
match self { match self {
Self::CannotAddArguments { Self::CannotAddArguments {
left_type, left_type,
@ -252,12 +250,12 @@ impl AnnotatedError for CompileError {
right_type, right_type,
right_position, right_position,
} => { } => {
smallvec![( vec![(
format!("Type \"{left_type}\" cannot be added to type \"{right_type}\". Try converting one of the values to the other type."), format!("Type \"{left_type}\" cannot be added to type \"{right_type}\". Try converting one of the values to the other type."),
Span(left_position.0, right_position.1) Span(left_position.0, right_position.1)
)] )]
} }
_ => SmallVec::new(), _ => Vec::with_capacity(0),
} }
} }
} }

View File

@ -31,7 +31,6 @@ use type_checks::{check_math_type, check_math_types};
use std::mem::replace; use std::mem::replace;
use optimize::control_flow_register_consolidation; use optimize::control_flow_register_consolidation;
use smallvec::{smallvec, SmallVec};
use crate::{ use crate::{
instruction::{CallNative, Close, GetLocal, Jump, LoadList, Negate, Not, Return, SetLocal}, instruction::{CallNative, Close, GetLocal, Jump, LoadList, Negate, Not, Return, SetLocal},
@ -52,8 +51,8 @@ use crate::{
/// ``` /// ```
pub fn compile(program_name: Option<DustString>, 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 = let mut compiler = Compiler::new(lexer, program_name, true)
Compiler::new(lexer, program_name).map_err(|error| DustError::compile(error, source))?; .map_err(|error| DustError::compile(error, source))?;
compiler compiler
.compile() .compile()
@ -85,15 +84,15 @@ pub struct Compiler<'src> {
/// Instructions, along with their types and positions, that have been compiled. The /// Instructions, along with their types and positions, that have been compiled. The
/// instructions and positions are assigned to the chunk when [`Compiler::finish`] is called. /// instructions and positions are assigned to the chunk when [`Compiler::finish`] is called.
/// The types are discarded after compilation. /// The types are discarded after compilation.
instructions: SmallVec<[(Instruction, Type, Span); 32]>, instructions: Vec<(Instruction, Type, Span)>,
/// Constants that have been compiled. These are assigned to the chunk when [`Compiler::finish`] /// Constants that have been compiled. These are assigned to the chunk when [`Compiler::finish`]
/// is called. /// is called.
constants: SmallVec<[Value; 16]>, constants: Vec<Value>,
/// Block-local variables and their types. The locals are assigned to the chunk when /// Block-local variables and their types. The locals are assigned to the chunk when
/// [`Compiler::finish`] is called. The types are discarded after compilation. /// [`Compiler::finish`] is called. The types are discarded after compilation.
locals: SmallVec<[(Local, Type); 8]>, locals: Vec<(Local, Type)>,
/// Prototypes that have been compiled. These are assigned to the chunk when /// Prototypes that have been compiled. These are assigned to the chunk when
/// [`Compiler::finish`] is called. /// [`Compiler::finish`] is called.
@ -104,12 +103,11 @@ pub struct Compiler<'src> {
stack_size: usize, stack_size: usize,
/// The first register index that the compiler should use. This is used to avoid reusing the /// The first register index that the compiler should use. This is used to avoid reusing the
/// registers that are used for the function's arguments, thus it is zero in the program's main /// registers that are used for the function's arguments.
/// chunk.
minimum_register: u8, minimum_register: u8,
/// Index of the current block. This is used to determine the scope of the locals and is /// Index of the current block. This is used to determine the scope of locals and is incremented
/// incremented when a new block is entered. /// when a new block is entered.
block_index: u8, block_index: u8,
/// The current block 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.
@ -119,6 +117,10 @@ pub struct Compiler<'src> {
/// that value is never read because the main chunk is not a callable function. /// that value is never read because the main chunk is not a callable function.
prototype_index: u8, prototype_index: u8,
/// Whether the chunk is the program's main chunk. This is used to prevent recursive calls to
/// the main chunk.
is_main: bool,
current_token: Token<'src>, current_token: Token<'src>,
current_position: Span, current_position: Span,
previous_token: Token<'src>, previous_token: Token<'src>,
@ -126,23 +128,24 @@ pub struct Compiler<'src> {
} }
impl<'src> Compiler<'src> { impl<'src> Compiler<'src> {
/// Creates a new top-level compiler with the given lexer. /// Creates a new compiler.
pub fn new( pub fn new(
mut lexer: Lexer<'src>, mut lexer: Lexer<'src>,
function_name: Option<DustString>, function_name: Option<DustString>,
is_main: bool,
) -> Result<Self, CompileError> { ) -> 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, function_name,
r#type: FunctionType { r#type: FunctionType {
type_parameters: None, type_parameters: Vec::with_capacity(0),
value_parameters: None, value_parameters: Vec::with_capacity(0),
return_type: Type::None, return_type: Type::None,
}, },
instructions: SmallVec::new(), instructions: Vec::new(),
constants: SmallVec::new(), constants: Vec::new(),
locals: SmallVec::new(), locals: Vec::new(),
prototypes: Vec::new(), prototypes: Vec::new(),
stack_size: 0, stack_size: 0,
lexer, lexer,
@ -150,6 +153,7 @@ impl<'src> Compiler<'src> {
block_index: 0, block_index: 0,
current_scope: Scope::default(), current_scope: Scope::default(),
prototype_index: 0, prototype_index: 0,
is_main,
current_token, current_token,
current_position, current_position,
previous_token: Token::Eof, previous_token: Token::Eof,
@ -195,7 +199,7 @@ impl<'src> Compiler<'src> {
/// will allow [`Compiler::function_name`] to be both the name used for recursive calls and the /// 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. /// 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 { pub fn finish(self) -> Chunk {
let (instructions, positions): (SmallVec<[Instruction; 32]>, SmallVec<[Span; 32]>) = self let (instructions, positions): (Vec<Instruction>, Vec<Span>) = self
.instructions .instructions
.into_iter() .into_iter()
.map(|(instruction, _, position)| (instruction, position)) .map(|(instruction, _, position)| (instruction, position))
@ -204,14 +208,14 @@ impl<'src> Compiler<'src> {
.locals .locals
.into_iter() .into_iter()
.map(|(local, _)| local) .map(|(local, _)| local)
.collect::<SmallVec<[Local; 8]>>(); .collect::<Vec<Local>>();
Chunk { Chunk {
name: self.function_name, name: self.function_name,
r#type: self.r#type, r#type: self.r#type,
instructions, instructions,
positions, positions,
constants: self.constants, constants: self.constants.to_vec(),
locals, locals,
prototypes: self.prototypes, prototypes: self.prototypes,
register_count: self.stack_size, register_count: self.stack_size,
@ -998,7 +1002,7 @@ impl<'src> Compiler<'src> {
local_index local_index
} else if let Some(native_function) = NativeFunction::from_str(identifier) { } else if let Some(native_function) = NativeFunction::from_str(identifier) {
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) && !self.is_main {
let destination = self.next_register(); let destination = self.next_register();
let load_self = Instruction::load_self(destination); let load_self = Instruction::load_self(destination);
@ -1549,7 +1553,7 @@ impl<'src> Compiler<'src> {
let mut function_compiler = if self.current_token == Token::LeftParenthesis { let mut function_compiler = if self.current_token == Token::LeftParenthesis {
let function_name = identifier.map(DustString::from); let function_name = identifier.map(DustString::from);
Compiler::new(self.lexer, function_name)? // This will consume the left parenthesis Compiler::new(self.lexer, function_name, false)? // This will consume the parenthesis
} else { } else {
return Err(CompileError::ExpectedToken { return Err(CompileError::ExpectedToken {
expected: TokenKind::LeftParenthesis, expected: TokenKind::LeftParenthesis,
@ -1560,7 +1564,7 @@ impl<'src> Compiler<'src> {
function_compiler.prototype_index = self.prototypes.len() as u8; function_compiler.prototype_index = self.prototypes.len() as u8;
let mut value_parameters: Option<SmallVec<[(u8, Type); 4]>> = None; let mut value_parameters: Vec<(u8, Type)> = Vec::with_capacity(3);
while !function_compiler.allow(Token::RightParenthesis)? { while !function_compiler.allow(Token::RightParenthesis)? {
let is_mutable = function_compiler.allow(Token::Mut)?; let is_mutable = function_compiler.allow(Token::Mut)?;
@ -1594,15 +1598,10 @@ impl<'src> Compiler<'src> {
function_compiler.current_scope, function_compiler.current_scope,
); );
if let Some(value_parameters) = value_parameters.as_mut() { value_parameters.push((identifier_index, r#type));
value_parameters.push((identifier_index, r#type)); function_compiler.allow(Token::Comma)?;
} else {
value_parameters = Some(smallvec![(identifier_index, r#type)]);
};
function_compiler.minimum_register += 1; function_compiler.minimum_register += 1;
function_compiler.allow(Token::Comma)?;
} }
let return_type = if function_compiler.allow(Token::ArrowThin)? { let return_type = if function_compiler.allow(Token::ArrowThin)? {
@ -1618,7 +1617,7 @@ impl<'src> Compiler<'src> {
Type::None Type::None
}; };
let function_type = FunctionType { let function_type = FunctionType {
type_parameters: None, type_parameters: Vec::with_capacity(0),
value_parameters, value_parameters,
return_type, return_type,
}; };

View File

@ -3,7 +3,6 @@
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use annotate_snippets::{Level, Renderer, Snippet}; use annotate_snippets::{Level, Renderer, Snippet};
use smallvec::SmallVec;
use crate::{CompileError, NativeFunctionError, Span}; use crate::{CompileError, NativeFunctionError, Span};
@ -76,6 +75,6 @@ impl Display for DustError<'_> {
pub trait AnnotatedError { pub trait AnnotatedError {
fn title() -> &'static str; fn title() -> &'static str;
fn description(&self) -> &'static str; fn description(&self) -> &'static str;
fn detail_snippets(&self) -> SmallVec<[(String, Span); 2]>; fn detail_snippets(&self) -> Vec<(String, Span)>;
fn help_snippets(&self) -> SmallVec<[(String, Span); 2]>; fn help_snippets(&self) -> Vec<(String, Span)>;
} }

View File

@ -744,12 +744,12 @@ impl AnnotatedError for LexError {
} }
} }
fn detail_snippets(&self) -> smallvec::SmallVec<[(String, Span); 2]> { fn detail_snippets(&self) -> Vec<(String, Span)> {
todo!() Vec::with_capacity(0)
} }
fn help_snippets(&self) -> smallvec::SmallVec<[(String, Span); 2]> { fn help_snippets(&self) -> Vec<(String, Span)> {
todo!() Vec::with_capacity(0)
} }
} }

View File

@ -28,8 +28,6 @@
//! println!("{}", report); //! println!("{}", report);
//! ``` //! ```
#![feature(array_repeat)]
pub mod chunk; pub mod chunk;
pub mod compiler; pub mod compiler;
pub mod dust_error; pub mod dust_error;

View File

@ -3,7 +3,7 @@ use std::{ops::Range, panic};
use crate::vm::ThreadData; use crate::vm::ThreadData;
pub fn panic(data: &mut ThreadData, _: Option<u8>, argument_range: Range<u8>) -> bool { pub fn panic(data: &mut ThreadData, _: Option<u8>, argument_range: Range<u8>) -> bool {
let record = data.records.last_mut_unchecked(); let record = &mut data.call_stack.last_mut_unchecked().record;
let position = record.current_position(); let position = record.current_position();
let mut message = format!("Dust panic at {position}!"); let mut message = format!("Dust panic at {position}!");

View File

@ -11,7 +11,7 @@ pub fn read_line(
destination: Option<u8>, destination: Option<u8>,
_argument_range: Range<u8>, _argument_range: Range<u8>,
) -> bool { ) -> bool {
let record = data.records.last_mut_unchecked(); let record = &mut data.call_stack.last_mut_unchecked().record;
let destination = destination.unwrap(); let destination = destination.unwrap();
let mut buffer = String::new(); let mut buffer = String::new();
@ -31,7 +31,7 @@ pub fn read_line(
} }
pub fn write(data: &mut ThreadData, _destination: Option<u8>, argument_range: Range<u8>) -> bool { pub fn write(data: &mut ThreadData, _destination: Option<u8>, argument_range: Range<u8>) -> bool {
let record = data.records.last_mut_unchecked(); let record = &mut data.call_stack.last_mut_unchecked().record;
let mut stdout = stdout(); let mut stdout = stdout();
for register_index in argument_range { for register_index in argument_range {
@ -52,7 +52,7 @@ pub fn write_line(
_destination: Option<u8>, _destination: Option<u8>,
argument_range: Range<u8>, argument_range: Range<u8>,
) -> bool { ) -> bool {
let record = data.records.last_mut_unchecked(); let record = &mut data.call_stack.last_mut_unchecked().record;
let mut stdout = stdout().lock(); let mut stdout = stdout().lock();
for register_index in argument_range { for register_index in argument_range {

View File

@ -2,7 +2,7 @@
//! //!
//! Native functions are used to implement features that are not possible to implement in Dust //! Native functions are used to implement features that are not possible to implement in Dust
//! itself or that are more efficient to implement in Rust. //! itself or that are more efficient to implement in Rust.
mod assertion; mod assert;
mod io; mod io;
mod string; mod string;
@ -14,7 +14,6 @@ use std::{
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use smallvec::{smallvec, SmallVec};
use crate::{vm::ThreadData, AnnotatedError, FunctionType, Span, Type}; use crate::{vm::ThreadData, AnnotatedError, FunctionType, Span, Type};
@ -134,11 +133,11 @@ define_native_function! {
3, 3,
"panic", "panic",
FunctionType { FunctionType {
type_parameters: None, type_parameters: Vec::with_capacity(0),
value_parameters: None, value_parameters: Vec::with_capacity(0),
return_type: Type::None return_type: Type::None
}, },
assertion::panic assert::panic
), ),
// // Type conversion // // Type conversion
@ -151,8 +150,8 @@ define_native_function! {
8, 8,
"to_string", "to_string",
FunctionType { FunctionType {
type_parameters: None, type_parameters: Vec::with_capacity(0),
value_parameters: Some(smallvec![(0, Type::Any)]), value_parameters: vec![(0, Type::Any)],
return_type: Type::String return_type: Type::String
}, },
string::to_string string::to_string
@ -212,8 +211,8 @@ define_native_function! {
50, 50,
"read_line", "read_line",
FunctionType { FunctionType {
type_parameters: None, type_parameters: Vec::with_capacity(0),
value_parameters: None, value_parameters: Vec::with_capacity(0),
return_type: Type::String return_type: Type::String
}, },
io::read_line io::read_line
@ -228,8 +227,8 @@ define_native_function! {
55, 55,
"write", "write",
FunctionType { FunctionType {
type_parameters: None, type_parameters: Vec::with_capacity(0),
value_parameters: Some(smallvec![(0, Type::String)]), value_parameters: vec![(0, Type::String)],
return_type: Type::None return_type: Type::None
}, },
io::write io::write
@ -240,8 +239,8 @@ define_native_function! {
57, 57,
"write_line", "write_line",
FunctionType { FunctionType {
type_parameters: None, type_parameters: Vec::with_capacity(0),
value_parameters: Some(smallvec![(0, Type::String)]), value_parameters: vec![(0, Type::String)],
return_type: Type::None return_type: Type::None
}, },
io::write_line io::write_line
@ -289,29 +288,29 @@ impl AnnotatedError for NativeFunctionError {
} }
} }
fn detail_snippets(&self) -> SmallVec<[(String, Span); 2]> { fn detail_snippets(&self) -> Vec<(String, Span)> {
match self { match self {
NativeFunctionError::ExpectedArgumentCount { NativeFunctionError::ExpectedArgumentCount {
expected, expected,
found, found,
position, position,
} => smallvec![( } => vec![(
format!("Expected {expected} arguments, found {found}"), format!("Expected {expected} arguments, found {found}"),
*position *position,
)], )],
NativeFunctionError::Panic { message, position } => { NativeFunctionError::Panic { message, position } => {
smallvec![(format!("Dust panic!\n{message}"), *position)] vec![(format!("Dust panic!\n{message}"), *position)]
} }
NativeFunctionError::Parse { error, position } => { NativeFunctionError::Parse { error, position } => {
smallvec![(format!("{error}"), *position)] vec![(format!("{error}"), *position)]
} }
NativeFunctionError::Io { error, position } => { NativeFunctionError::Io { error, position } => {
smallvec![(format!("{error}"), *position)] vec![(format!("{error}"), *position)]
} }
} }
} }
fn help_snippets(&self) -> SmallVec<[(String, Span); 2]> { fn help_snippets(&self) -> Vec<(String, Span)> {
SmallVec::new() Vec::with_capacity(0)
} }
} }

View File

@ -10,7 +10,7 @@ pub fn to_string(
destination: Option<u8>, destination: Option<u8>,
argument_range: Range<u8>, argument_range: Range<u8>,
) -> bool { ) -> bool {
let record = data.records.last_mut_unchecked(); let record = &mut data.call_stack.last_mut_unchecked().record;
let argument_value = record.open_register_unchecked(argument_range.start); let argument_value = record.open_register_unchecked(argument_range.start);
let argument_string = argument_value.display(record); let argument_string = argument_value.display(record);
let destination = destination.unwrap(); let destination = destination.unwrap();

View File

@ -6,7 +6,6 @@ use std::{
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
/// Description of a kind of value. /// Description of a kind of value.
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
@ -25,7 +24,7 @@ pub enum Type {
Integer, Integer,
List(Box<Type>), List(Box<Type>),
Map { Map {
pairs: Box<SmallVec<[(u8, Type); 8]>>, pairs: Vec<(u8, Type)>,
}, },
None, None,
Range { Range {
@ -35,7 +34,7 @@ pub enum Type {
String, String,
Struct(StructType), Struct(StructType),
Tuple { Tuple {
fields: Box<SmallVec<[Type; 4]>>, fields: Vec<Type>,
}, },
} }
@ -274,20 +273,20 @@ impl Ord for Type {
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct FunctionType { pub struct FunctionType {
pub type_parameters: Option<SmallVec<[u8; 4]>>, pub type_parameters: Vec<u8>,
pub value_parameters: Option<SmallVec<[(u8, Type); 4]>>, pub value_parameters: Vec<(u8, Type)>,
pub return_type: Type, pub return_type: Type,
} }
impl FunctionType { impl FunctionType {
pub fn new<T: Into<SmallVec<[u8; 4]>>, U: Into<SmallVec<[(u8, Type); 4]>>>( pub fn new<T: Into<Vec<u8>>, U: Into<Vec<(u8, Type)>>>(
type_parameters: Option<T>, type_parameters: T,
value_parameters: Option<U>, value_parameters: U,
return_type: Type, return_type: Type,
) -> Self { ) -> Self {
FunctionType { FunctionType {
type_parameters: type_parameters.map(|into_types| into_types.into()), type_parameters: type_parameters.into(),
value_parameters: value_parameters.map(|into_values| into_values.into()), value_parameters: value_parameters.into(),
return_type, return_type,
} }
} }
@ -297,10 +296,10 @@ impl Display for FunctionType {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "fn ")?; write!(f, "fn ")?;
if let Some(type_parameters) = &self.type_parameters { if !self.type_parameters.is_empty() {
write!(f, "<")?; write!(f, "<")?;
for (index, type_parameter) in type_parameters.iter().enumerate() { for (index, type_parameter) in self.type_parameters.iter().enumerate() {
if index > 0 { if index > 0 {
write!(f, ", ")?; write!(f, ", ")?;
} }
@ -313,8 +312,8 @@ impl Display for FunctionType {
write!(f, "(")?; write!(f, "(")?;
if let Some(value_parameters) = &self.value_parameters { if !self.value_parameters.is_empty() {
for (index, (_, r#type)) in value_parameters.iter().enumerate() { for (index, (_, r#type)) in self.value_parameters.iter().enumerate() {
if index > 0 { if index > 0 {
write!(f, ", ")?; write!(f, ", ")?;
} }

View File

@ -1,39 +0,0 @@
use std::fmt::{self, Display, Formatter};
use crate::{InstructionData, Value};
use super::{stack::Stack, FunctionCall};
#[derive(Clone, Debug, PartialEq)]
pub enum VmError {
StackUnderflow,
ExpectedFunction {
value: Value,
},
InstructionIndexOutOfBounds {
call_stack: Stack<FunctionCall>,
ip: usize,
},
MalformedInstruction {
instruction: InstructionData,
},
}
impl Display for VmError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::StackUnderflow => {
write!(f, "Call stack underflow")
}
Self::ExpectedFunction { value } => {
write!(f, "Expected function, found {value}")
}
Self::InstructionIndexOutOfBounds { call_stack, ip } => {
write!(f, "Instruction index {} out of bounds\n{call_stack}", ip)
}
Self::MalformedInstruction { instruction } => {
write!(f, "Malformed instruction {instruction}")
}
}
}
}

View File

@ -1,5 +1,4 @@
//! Virtual machine and errors //! Virtual machine and errors
mod error;
mod record; mod record;
mod run_action; mod run_action;
mod stack; mod stack;
@ -11,7 +10,6 @@ use std::{
thread::spawn, thread::spawn,
}; };
pub use error::VmError;
pub use record::Record; pub use record::Record;
pub(crate) use run_action::get_next_action; pub(crate) use run_action::get_next_action;
pub use run_action::RunAction; pub use run_action::RunAction;

View File

@ -68,7 +68,7 @@ pub(crate) fn get_next_action(record: &mut Record) -> RunAction {
} }
pub fn point(instruction: Instruction, data: &mut ThreadData) -> bool { pub fn point(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = data.records.last_mut_unchecked(); let record = &mut data.call_stack.last_mut_unchecked().record;
let Point { from, to } = instruction.into(); let Point { from, to } = instruction.into();
let from_register = record.get_register_unchecked(from); let from_register = record.get_register_unchecked(from);
let from_register_is_empty = matches!(from_register, Register::Empty); let from_register_is_empty = matches!(from_register, Register::Empty);
@ -85,7 +85,7 @@ pub fn point(instruction: Instruction, data: &mut ThreadData) -> bool {
} }
pub fn close(instruction: Instruction, data: &mut ThreadData) -> bool { pub fn close(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = data.records.last_mut_unchecked(); let record = &mut data.call_stack.last_mut_unchecked().record;
let Close { from, to } = instruction.into(); let Close { from, to } = instruction.into();
for register_index in from..to { for register_index in from..to {
@ -98,7 +98,7 @@ pub fn close(instruction: Instruction, data: &mut ThreadData) -> bool {
} }
pub fn load_boolean(instruction: Instruction, data: &mut ThreadData) -> bool { pub fn load_boolean(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = data.records.last_mut_unchecked(); let record = &mut data.call_stack.last_mut_unchecked().record;
let LoadBoolean { let LoadBoolean {
destination, destination,
value, value,
@ -119,7 +119,7 @@ pub fn load_boolean(instruction: Instruction, data: &mut ThreadData) -> bool {
} }
pub fn load_constant(instruction: Instruction, data: &mut ThreadData) -> bool { pub fn load_constant(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = data.records.last_mut_unchecked(); let record = &mut data.call_stack.last_mut_unchecked().record;
let LoadConstant { let LoadConstant {
destination, destination,
constant_index, constant_index,
@ -141,7 +141,7 @@ pub fn load_constant(instruction: Instruction, data: &mut ThreadData) -> bool {
} }
pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> bool { pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = data.records.last_mut_unchecked(); let record = &mut data.call_stack.last_mut_unchecked().record;
let LoadList { let LoadList {
destination, destination,
start_register, start_register,
@ -183,7 +183,7 @@ pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> bool {
} }
pub fn load_function(instruction: Instruction, data: &mut ThreadData) -> bool { pub fn load_function(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = data.records.last_mut_unchecked(); let record = &mut data.call_stack.last_mut_unchecked().record;
let LoadFunction { let LoadFunction {
destination, destination,
prototype_index, prototype_index,
@ -200,7 +200,7 @@ pub fn load_function(instruction: Instruction, data: &mut ThreadData) -> bool {
} }
pub fn load_self(instruction: Instruction, data: &mut ThreadData) -> bool { pub fn load_self(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = data.records.last_mut_unchecked(); let record = &mut data.call_stack.last_mut_unchecked().record;
let LoadSelf { destination } = instruction.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));
@ -213,7 +213,7 @@ pub fn load_self(instruction: Instruction, data: &mut ThreadData) -> bool {
} }
pub fn get_local(instruction: Instruction, data: &mut ThreadData) -> bool { pub fn get_local(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = data.records.last_mut_unchecked(); let record = &mut data.call_stack.last_mut_unchecked().record;
let GetLocal { let GetLocal {
destination, destination,
local_index, local_index,
@ -229,7 +229,7 @@ pub fn get_local(instruction: Instruction, data: &mut ThreadData) -> bool {
} }
pub fn set_local(instruction: Instruction, data: &mut ThreadData) -> bool { pub fn set_local(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = data.records.last_mut_unchecked(); let record = &mut data.call_stack.last_mut_unchecked().record;
let SetLocal { let SetLocal {
register_index, register_index,
local_index, local_index,
@ -245,7 +245,7 @@ pub fn set_local(instruction: Instruction, data: &mut ThreadData) -> bool {
} }
pub fn add(instruction: Instruction, data: &mut ThreadData) -> bool { pub fn add(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = data.records.last_mut_unchecked(); let record = &mut data.call_stack.last_mut_unchecked().record;
let Add { let Add {
destination, destination,
left, left,
@ -264,7 +264,7 @@ pub fn add(instruction: Instruction, data: &mut ThreadData) -> bool {
} }
pub fn subtract(instruction: Instruction, data: &mut ThreadData) -> bool { pub fn subtract(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = data.records.last_mut_unchecked(); let record = &mut data.call_stack.last_mut_unchecked().record;
let Subtract { let Subtract {
destination, destination,
left, left,
@ -283,7 +283,7 @@ pub fn subtract(instruction: Instruction, data: &mut ThreadData) -> bool {
} }
pub fn multiply(instruction: Instruction, data: &mut ThreadData) -> bool { pub fn multiply(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = data.records.last_mut_unchecked(); let record = &mut data.call_stack.last_mut_unchecked().record;
let Multiply { let Multiply {
destination, destination,
left, left,
@ -310,7 +310,7 @@ pub fn multiply(instruction: Instruction, data: &mut ThreadData) -> bool {
} }
pub fn divide(instruction: Instruction, data: &mut ThreadData) -> bool { pub fn divide(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = data.records.last_mut_unchecked(); let record = &mut data.call_stack.last_mut_unchecked().record;
let Divide { let Divide {
destination, destination,
left, left,
@ -337,7 +337,7 @@ pub fn divide(instruction: Instruction, data: &mut ThreadData) -> bool {
} }
pub fn modulo(instruction: Instruction, data: &mut ThreadData) -> bool { pub fn modulo(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = data.records.last_mut_unchecked(); let record = &mut data.call_stack.last_mut_unchecked().record;
let Modulo { let Modulo {
destination, destination,
left, left,
@ -364,7 +364,7 @@ pub fn modulo(instruction: Instruction, data: &mut ThreadData) -> bool {
} }
pub fn test(instruction: Instruction, data: &mut ThreadData) -> bool { pub fn test(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = data.records.last_mut_unchecked(); let record = &mut data.call_stack.last_mut_unchecked().record;
let Test { let Test {
operand_register, operand_register,
test_value, test_value,
@ -386,7 +386,7 @@ pub fn test(instruction: Instruction, data: &mut ThreadData) -> bool {
} }
pub fn test_set(instruction: Instruction, data: &mut ThreadData) -> bool { pub fn test_set(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = data.records.last_mut_unchecked(); let record = &mut data.call_stack.last_mut_unchecked().record;
let TestSet { let TestSet {
destination, destination,
argument, argument,
@ -416,7 +416,7 @@ pub fn test_set(instruction: Instruction, data: &mut ThreadData) -> bool {
} }
pub fn equal(instruction: Instruction, data: &mut ThreadData) -> bool { pub fn equal(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = data.records.last_mut_unchecked(); let record = &mut data.call_stack.last_mut_unchecked().record;
let Equal { value, left, right } = instruction.into(); let Equal { value, left, right } = instruction.into();
let left = record.get_argument_unchecked(left); let left = record.get_argument_unchecked(left);
let right = record.get_argument_unchecked(right); let right = record.get_argument_unchecked(right);
@ -432,7 +432,7 @@ pub fn equal(instruction: Instruction, data: &mut ThreadData) -> bool {
} }
pub fn less(instruction: Instruction, data: &mut ThreadData) -> bool { pub fn less(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = data.records.last_mut_unchecked(); let record = &mut data.call_stack.last_mut_unchecked().record;
let Less { value, left, right } = instruction.into(); let Less { value, left, right } = instruction.into();
let left = record.get_argument_unchecked(left); let left = record.get_argument_unchecked(left);
let right = record.get_argument_unchecked(right); let right = record.get_argument_unchecked(right);
@ -448,7 +448,7 @@ pub fn less(instruction: Instruction, data: &mut ThreadData) -> bool {
} }
pub fn less_equal(instruction: Instruction, data: &mut ThreadData) -> bool { pub fn less_equal(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = data.records.last_mut_unchecked(); let record = &mut data.call_stack.last_mut_unchecked().record;
let LessEqual { value, left, right } = instruction.into(); let LessEqual { value, left, right } = instruction.into();
let left = record.get_argument_unchecked(left); let left = record.get_argument_unchecked(left);
let right = record.get_argument_unchecked(right); let right = record.get_argument_unchecked(right);
@ -464,7 +464,7 @@ pub fn less_equal(instruction: Instruction, data: &mut ThreadData) -> bool {
} }
pub fn negate(instruction: Instruction, data: &mut ThreadData) -> bool { pub fn negate(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = data.records.last_mut_unchecked(); let record = &mut data.call_stack.last_mut_unchecked().record;
let Negate { let Negate {
destination, destination,
argument, argument,
@ -481,7 +481,7 @@ pub fn negate(instruction: Instruction, data: &mut ThreadData) -> bool {
} }
pub fn not(instruction: Instruction, data: &mut ThreadData) -> bool { pub fn not(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = data.records.last_mut_unchecked(); let record = &mut data.call_stack.last_mut_unchecked().record;
let Not { let Not {
destination, destination,
argument, argument,
@ -501,7 +501,7 @@ pub fn not(instruction: Instruction, data: &mut ThreadData) -> bool {
} }
pub fn jump(instruction: Instruction, data: &mut ThreadData) -> bool { pub fn jump(instruction: Instruction, data: &mut ThreadData) -> bool {
let record = data.records.last_mut_unchecked(); let record = &mut data.call_stack.last_mut_unchecked().record;
let Jump { let Jump {
offset, offset,
is_positive, is_positive,
@ -520,32 +520,35 @@ pub fn jump(instruction: Instruction, data: &mut ThreadData) -> bool {
} }
pub fn call(instruction: Instruction, data: &mut ThreadData) -> bool { pub fn call(instruction: Instruction, data: &mut ThreadData) -> bool {
let current_record = data.records.pop_unchecked(); let current_call = data.call_stack.pop_unchecked();
let Call { let Call {
destination: return_register, destination: return_register,
function_register, function_register,
argument_count, argument_count,
is_recursive, is_recursive,
} = instruction.into(); } = instruction.into();
let function = current_record let function = current_call
.record
.open_register_unchecked(function_register) .open_register_unchecked(function_register)
.as_function() .as_function()
.unwrap(); .unwrap();
let first_argument_register = return_register - argument_count; let first_argument_register = return_register - argument_count;
let prototype = if is_recursive { let prototype = if is_recursive {
current_record.chunk current_call.record.chunk
} else { } else {
&current_record.chunk.prototypes[function.prototype_index as usize] &current_call.record.chunk.prototypes[function.prototype_index as usize]
}; };
let mut next_record = Record::new(prototype); let mut next_call = FunctionCall {
let next_call = FunctionCall { name: prototype.name.clone(),
name: next_record.name().cloned(),
return_register, return_register,
ip: current_record.ip, ip: current_call.ip,
record: Record::new(prototype),
}; };
for (argument_index, register_index) in (first_argument_register..return_register).enumerate() { for (argument_index, register_index) in (first_argument_register..return_register).enumerate() {
let argument = current_record.clone_register_value_or_constant_unchecked(register_index); let argument = current_call
.record
.clone_register_value_or_constant_unchecked(register_index);
trace!( trace!(
"Passing argument \"{argument}\" to {}", "Passing argument \"{argument}\" to {}",
@ -555,14 +558,15 @@ pub fn call(instruction: Instruction, data: &mut ThreadData) -> bool {
.unwrap_or_else(|| DustString::from("anonymous")) .unwrap_or_else(|| DustString::from("anonymous"))
); );
next_record.set_register(argument_index as u8, Register::Value(argument)); next_call
.record
.set_register(argument_index as u8, Register::Value(argument));
} }
data.next_action = get_next_action(&mut next_record); data.next_action = get_next_action(&mut next_call.record);
data.call_stack.push(current_call);
data.call_stack.push(next_call); data.call_stack.push(next_call);
data.records.push(current_record);
data.records.push(next_record);
false false
} }
@ -586,28 +590,30 @@ pub fn r#return(instruction: Instruction, data: &mut ThreadData) -> bool {
should_return_value, should_return_value,
return_register, return_register,
} = instruction.into(); } = instruction.into();
let current_call = data.call_stack.pop_unchecked(); let mut current_call = if data.call_stack.len() == 1 {
let mut current_record = if data.call_stack.is_empty() {
if should_return_value { if should_return_value {
data.return_value_index = Some(return_register); data.return_value_index = Some(return_register);
}; };
return true; return true;
} else { } else {
data.records.pop_unchecked() data.call_stack.pop_unchecked()
}; };
let outer_record = data.records.last_mut_unchecked(); let outer_call = data.call_stack.last_mut_unchecked();
let destination = current_call.return_register; let destination = current_call.return_register;
if should_return_value { if should_return_value {
let return_value = let return_value = current_call
current_record.empty_register_or_clone_constant_unchecked(return_register); .record
.empty_register_or_clone_constant_unchecked(return_register);
outer_record.set_register(destination, Register::Value(return_value)); outer_call
.record
.set_register(destination, Register::Value(return_value));
} }
data.next_action = get_next_action(outer_record); data.next_action = get_next_action(&mut outer_call.record);
false false
} }

View File

@ -5,7 +5,7 @@ use std::{
use crate::DustString; use crate::DustString;
use super::VmError; use super::Record;
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub struct Stack<T> { pub struct Stack<T> {
@ -35,7 +35,7 @@ impl<T> Stack<T> {
pub fn get_unchecked(&self, index: usize) -> &T { pub fn get_unchecked(&self, index: usize) -> &T {
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
assert!(index < self.len(), "{}", VmError::StackUnderflow); assert!(index < self.len(), "Stack underflow");
&self.items[index] &self.items[index]
} else { } else {
@ -61,7 +61,7 @@ impl<T> Stack<T> {
pub fn pop_unchecked(&mut self) -> T { pub fn pop_unchecked(&mut self) -> T {
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
assert!(!self.is_empty(), "{}", VmError::StackUnderflow); assert!(!self.is_empty(), "Stack underflow");
self.items.pop().unwrap() self.items.pop().unwrap()
} else { } else {
@ -71,7 +71,7 @@ impl<T> Stack<T> {
pub fn last_unchecked(&self) -> &T { pub fn last_unchecked(&self) -> &T {
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
assert!(!self.is_empty(), "{}", VmError::StackUnderflow); assert!(!self.is_empty(), "Stack underflow");
self.items.last().unwrap() self.items.last().unwrap()
} else { } else {
@ -81,7 +81,7 @@ impl<T> Stack<T> {
pub fn last_mut_unchecked(&mut self) -> &mut T { pub fn last_mut_unchecked(&mut self) -> &mut T {
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
assert!(!self.is_empty(), "{}", VmError::StackUnderflow); assert!(!self.is_empty(), "Stack underflow");
self.items.last_mut().unwrap() self.items.last_mut().unwrap()
} else { } else {
@ -116,7 +116,7 @@ impl<T: Debug> Debug for Stack<T> {
} }
} }
impl Display for Stack<FunctionCall> { impl Display for Stack<FunctionCall<'_>> {
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 --")?;
@ -128,14 +128,15 @@ impl Display for Stack<FunctionCall> {
} }
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Debug)]
pub struct FunctionCall { pub struct FunctionCall<'a> {
pub name: Option<DustString>, pub name: Option<DustString>,
pub return_register: u8, pub return_register: u8,
pub ip: usize, pub ip: usize,
pub record: Record<'a>,
} }
impl Display for FunctionCall { 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,

View File

@ -23,21 +23,18 @@ impl Thread {
); );
let mut call_stack = Stack::with_capacity(self.chunk.prototypes.len() + 1); let mut call_stack = Stack::with_capacity(self.chunk.prototypes.len() + 1);
let mut records = Stack::with_capacity(self.chunk.prototypes.len() + 1);
let main_call = FunctionCall { let main_call = FunctionCall {
name: self.chunk.name.clone(), name: self.chunk.name.clone(),
return_register: 0, // Never used, the main function's return is the thread's return return_register: 0, // Never used, the main function's return is the thread's return
ip: 0, ip: 0,
record: Record::new(&self.chunk),
}; };
let main_record = Record::new(&self.chunk);
call_stack.push(main_call); call_stack.push(main_call);
records.push(main_record);
let first_action = RunAction::from(*self.chunk.instructions.first().unwrap()); let first_action = RunAction::from(*self.chunk.instructions.first().unwrap());
let mut thread_data = ThreadData { let mut thread_data = ThreadData {
call_stack, call_stack,
records,
next_action: first_action, next_action: first_action,
return_value_index: None, return_value_index: None,
}; };
@ -53,8 +50,9 @@ impl Thread {
if should_end { if should_end {
let return_value = if let Some(register_index) = thread_data.return_value_index { let return_value = if let Some(register_index) = thread_data.return_value_index {
let value = thread_data let value = thread_data
.records .call_stack
.last_mut_unchecked() .last_mut_unchecked()
.record
.empty_register_or_clone_constant_unchecked(register_index); .empty_register_or_clone_constant_unchecked(register_index);
Some(value) Some(value)
@ -70,8 +68,7 @@ impl Thread {
#[derive(Debug)] #[derive(Debug)]
pub struct ThreadData<'a> { pub struct ThreadData<'a> {
pub call_stack: Stack<FunctionCall>, pub call_stack: Stack<FunctionCall<'a>>,
pub records: Stack<Record<'a>>,
pub next_action: RunAction, pub next_action: RunAction,
pub return_value_index: Option<u8>, pub return_value_index: Option<u8>,
} }