diff --git a/Cargo.lock b/Cargo.lock
index eecbb0c..6811bcd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -339,7 +339,6 @@ dependencies = [
"rand",
"serde",
"serde_json",
- "smallvec",
"smartstring",
"tracing",
]
@@ -796,9 +795,6 @@ name = "smallvec"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
-dependencies = [
- "serde",
-]
[[package]]
name = "smartstring"
diff --git a/dust-cli/src/main.rs b/dust-cli/src/main.rs
index 3d6412a..47a794f 100644
--- a/dust-cli/src/main.rs
+++ b/dust-cli/src/main.rs
@@ -6,70 +6,74 @@ use std::{
};
use clap::{
- builder::{styling::AnsiColor, StyledStr, Styles},
+ builder::{
+ styling::{AnsiColor, Color},
+ StyledStr, Styles,
+ },
crate_authors, crate_description, crate_version,
error::ErrorKind,
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 tracing::{subscriber::set_global_default, Level};
use tracing_subscriber::FmtSubscriber;
-const CLI_HELP_TEMPLATE: &str = cstr!(
- r#"
-Dust CLI
-────────
-{about}
-
-Version: {version}
-Author: {author}
-License: GPL-3.0
-Repository: git.jeffa.io/jeff/dust
-
-Usage
-─────
-{tab}{usage}
-
-Modes
-─────
-{subcommands}
-
-Options
-───────
-{options}
-"#
-);
-
-const MODE_HELP_TEMPLATE: &str = cstr!(
+const ABOUT: &str = cstr!(
r#"
Dust CLI
-────────
+────────>
{about}
-Version: {version}
-Author: {author}
-License: GPL-3.0
-Repository: git.jeffa.io/jeff/dust
+⚙️ Version:> {version}
+🦀 Author:> {author}
+⚖️ License:> GPL-3.0
+🔬 Repository:> https://git.jeffa.io/jeff/dust
+"#
+);
-Usage
-─────
-{usage}
+const PLAIN_ABOUT: &str = r#"
+{about}
+"#;
-Options
-───────
+const USAGE: &str = cstr!(
+ r#"
+Usage:> {usage}
+"#
+);
+
+const SUBCOMMANDS: &str = cstr!(
+ r#"
+Modes:>
+{subcommands}
+"#
+);
+
+const OPTIONS: &str = cstr!(
+ r#"
+Options:>
{options}
"#
);
+const CREATE_MAIN_HELP_TEMPLATE: fn() -> String =
+ || cformat!("{ABOUT}{USAGE}{SUBCOMMANDS}{OPTIONS}");
+
+const CREATE_MODE_HELP_TEMPLATE: fn(&str) -> String = |title| {
+ cformat!(
+ "\
+ {title}\n────────>\
+ {PLAIN_ABOUT}{USAGE}{OPTIONS}
+ "
+ )
+};
+
const STYLES: Styles = Styles::styled()
- .header(AnsiColor::BrightMagenta.on_default().bold())
- .usage(AnsiColor::BrightCyan.on_default().bold())
- .literal(AnsiColor::BrightCyan.on_default())
- .placeholder(AnsiColor::BrightMagenta.on_default())
- .error(AnsiColor::BrightRed.on_default().bold())
- .valid(AnsiColor::Blue.on_default())
- .invalid(AnsiColor::BrightRed.on_default());
+ .literal(AnsiColor::Cyan.on_default())
+ .placeholder(AnsiColor::Cyan.on_default())
+ .valid(AnsiColor::BrightCyan.on_default())
+ .invalid(AnsiColor::BrightYellow.on_default())
+ .error(AnsiColor::BrightRed.on_default());
#[derive(Parser)]
#[clap(
@@ -77,7 +81,7 @@ const STYLES: Styles = Styles::styled()
author = crate_authors!(),
about = crate_description!(),
color = ColorChoice::Auto,
- help_template = StyledStr::from(CLI_HELP_TEMPLATE),
+ help_template = CREATE_MAIN_HELP_TEMPLATE(),
styles = STYLES,
)]
struct Cli {
@@ -122,7 +126,7 @@ struct Input {
#[derive(Args)]
#[command(
short_flag = 'r',
- help_template = MODE_HELP_TEMPLATE
+ help_template = CREATE_MODE_HELP_TEMPLATE("Run Mode")
)]
struct Run {
/// Print the time taken for compilation and execution
@@ -149,7 +153,7 @@ enum Mode {
/// Compile and print the bytecode disassembly
#[command(
short_flag = 'd',
- help_template = MODE_HELP_TEMPLATE
+ help_template = CREATE_MODE_HELP_TEMPLATE("Disassemble Mode")
)]
Disassemble {
/// Style disassembly output
@@ -167,7 +171,7 @@ enum Mode {
/// Lex the source code and print the tokens
#[command(
short_flag = 't',
- help_template = MODE_HELP_TEMPLATE
+ help_template = CREATE_MODE_HELP_TEMPLATE("Tokenize Mode")
)]
Tokenize {
/// Style token output
@@ -224,7 +228,7 @@ fn main() {
let (source, file_name) = get_source_and_file_name(input);
let lexer = Lexer::new(&source);
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,
Err(error) => {
handle_compile_error(error, &source);
@@ -312,7 +316,7 @@ fn main() {
let (source, file_name) = get_source_and_file_name(input);
let lexer = Lexer::new(&source);
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,
Err(error) => {
handle_compile_error(error, &source);
diff --git a/dust-lang/Cargo.toml b/dust-lang/Cargo.toml
index 28c86ac..87b0557 100644
--- a/dust-lang/Cargo.toml
+++ b/dust-lang/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "dust-lang"
-description = "Interpreter library for the Dust programming language"
+description = "Dust programming language library"
authors.workspace = true
edition.workspace = true
license.workspace = true
@@ -17,7 +17,6 @@ serde_json = "1.0.117"
getrandom = { version = "0.2", features = [
"js",
] } # Indirect dependency, for wasm builds
-smallvec = { version = "1.13.2", features = ["const_generics", "serde"] }
smartstring = { version = "1.0.1", features = [
"serde",
], default-features = false }
@@ -25,7 +24,6 @@ tracing = "0.1.41"
[dev-dependencies]
criterion = { version = "0.3.4", features = ["html_reports"] }
-smallvec = { version = "1.13.2", features = ["const_generics"] }
[[bench]]
name = "addictive_addition"
diff --git a/dust-lang/src/chunk/mod.rs b/dust-lang/src/chunk/mod.rs
index 05eb799..8ad2e84 100644
--- a/dust-lang/src/chunk/mod.rs
+++ b/dust-lang/src/chunk/mod.rs
@@ -24,7 +24,6 @@ use std::fmt::{self, Debug, Display, Formatter, Write as FmtWrite};
use std::io::Write;
use serde::{Deserialize, Serialize};
-use smallvec::SmallVec;
use crate::{DustString, Function, FunctionType, Instruction, Span, Value};
@@ -36,10 +35,10 @@ pub struct Chunk {
pub(crate) name: Option,
pub(crate) r#type: FunctionType,
- pub(crate) instructions: SmallVec<[Instruction; 32]>,
- pub(crate) positions: SmallVec<[Span; 32]>,
- pub(crate) constants: SmallVec<[Value; 16]>,
- pub(crate) locals: SmallVec<[Local; 8]>,
+ pub(crate) instructions: Vec,
+ pub(crate) positions: Vec,
+ pub(crate) constants: Vec,
+ pub(crate) locals: Vec,
pub(crate) prototypes: Vec,
pub(crate) register_count: usize,
@@ -51,10 +50,10 @@ impl Chunk {
pub fn with_data(
name: Option,
r#type: FunctionType,
- instructions: impl Into>,
- positions: impl Into>,
- constants: impl Into>,
- locals: impl Into>,
+ instructions: impl Into>,
+ positions: impl Into>,
+ constants: impl Into>,
+ locals: impl Into>,
prototypes: Vec,
) -> Self {
Self {
diff --git a/dust-lang/src/compiler/error.rs b/dust-lang/src/compiler/error.rs
index 02d0e4b..63aee4f 100644
--- a/dust-lang/src/compiler/error.rs
+++ b/dust-lang/src/compiler/error.rs
@@ -1,7 +1,5 @@
use std::num::{ParseFloatError, ParseIntError};
-use smallvec::{smallvec, SmallVec};
-
use crate::{AnnotatedError, LexError, Scope, Span, TokenKind, TokenOwned, Type, TypeConflict};
/// 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 {
Self::CannotAddArguments {
left_type,
@@ -220,31 +218,31 @@ impl AnnotatedError for CompileError {
right_type,
right_position,
} => {
- smallvec![
+ vec![
(
format!("A value of type \"{left_type}\" was used here."),
- *left_position
+ *left_position,
),
(
format!("A value of type \"{right_type}\" was used here."),
- *right_position
- )
+ *right_position,
+ ),
]
}
Self::ReturnTypeConflict { conflict, position } => {
- smallvec![(
+ vec![(
format!(
"Expected type {} but found type {}",
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 {
Self::CannotAddArguments {
left_type,
@@ -252,12 +250,12 @@ impl AnnotatedError for CompileError {
right_type,
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."),
Span(left_position.0, right_position.1)
)]
}
- _ => SmallVec::new(),
+ _ => Vec::with_capacity(0),
}
}
}
diff --git a/dust-lang/src/compiler/mod.rs b/dust-lang/src/compiler/mod.rs
index 2e19050..d9f88f1 100644
--- a/dust-lang/src/compiler/mod.rs
+++ b/dust-lang/src/compiler/mod.rs
@@ -31,7 +31,6 @@ use type_checks::{check_math_type, check_math_types};
use std::mem::replace;
use optimize::control_flow_register_consolidation;
-use smallvec::{smallvec, SmallVec};
use crate::{
instruction::{CallNative, Close, GetLocal, Jump, LoadList, Negate, Not, Return, SetLocal},
@@ -52,8 +51,8 @@ use crate::{
/// ```
pub fn compile(program_name: Option, source: &str) -> Result {
let lexer = Lexer::new(source);
- let mut compiler =
- Compiler::new(lexer, program_name).map_err(|error| DustError::compile(error, source))?;
+ let mut compiler = Compiler::new(lexer, program_name, true)
+ .map_err(|error| DustError::compile(error, source))?;
compiler
.compile()
@@ -85,15 +84,15 @@ pub struct Compiler<'src> {
/// 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.
/// 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`]
/// is called.
- constants: SmallVec<[Value; 16]>,
+ constants: Vec,
/// Block-local variables and their types. The locals are assigned to the chunk when
/// [`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
/// [`Compiler::finish`] is called.
@@ -104,12 +103,11 @@ pub struct Compiler<'src> {
stack_size: usize,
/// 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
- /// chunk.
+ /// registers that are used for the function's arguments.
minimum_register: u8,
- /// Index of the current block. This is used to determine the scope of the locals and is
- /// incremented when a new block is entered.
+ /// Index of the current block. This is used to determine the scope of locals and is incremented
+ /// when a new block is entered.
block_index: u8,
/// 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.
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_position: Span,
previous_token: Token<'src>,
@@ -126,23 +128,24 @@ pub struct Compiler<'src> {
}
impl<'src> Compiler<'src> {
- /// Creates a new top-level compiler with the given lexer.
+ /// Creates a new compiler.
pub fn new(
mut lexer: Lexer<'src>,
function_name: Option,
+ is_main: bool,
) -> Result {
let (current_token, current_position) = lexer.next_token()?;
Ok(Compiler {
function_name,
r#type: FunctionType {
- type_parameters: None,
- value_parameters: None,
+ type_parameters: Vec::with_capacity(0),
+ value_parameters: Vec::with_capacity(0),
return_type: Type::None,
},
- instructions: SmallVec::new(),
- constants: SmallVec::new(),
- locals: SmallVec::new(),
+ instructions: Vec::new(),
+ constants: Vec::new(),
+ locals: Vec::new(),
prototypes: Vec::new(),
stack_size: 0,
lexer,
@@ -150,6 +153,7 @@ impl<'src> Compiler<'src> {
block_index: 0,
current_scope: Scope::default(),
prototype_index: 0,
+ is_main,
current_token,
current_position,
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
/// 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
+ let (instructions, positions): (Vec, Vec) = self
.instructions
.into_iter()
.map(|(instruction, _, position)| (instruction, position))
@@ -204,14 +208,14 @@ impl<'src> Compiler<'src> {
.locals
.into_iter()
.map(|(local, _)| local)
- .collect::>();
+ .collect::>();
Chunk {
name: self.function_name,
r#type: self.r#type,
instructions,
positions,
- constants: self.constants,
+ constants: self.constants.to_vec(),
locals,
prototypes: self.prototypes,
register_count: self.stack_size,
@@ -998,7 +1002,7 @@ impl<'src> Compiler<'src> {
local_index
} else if let Some(native_function) = NativeFunction::from_str(identifier) {
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 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 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 {
return Err(CompileError::ExpectedToken {
expected: TokenKind::LeftParenthesis,
@@ -1560,7 +1564,7 @@ impl<'src> Compiler<'src> {
function_compiler.prototype_index = self.prototypes.len() as u8;
- let mut value_parameters: Option> = None;
+ let mut value_parameters: Vec<(u8, Type)> = Vec::with_capacity(3);
while !function_compiler.allow(Token::RightParenthesis)? {
let is_mutable = function_compiler.allow(Token::Mut)?;
@@ -1594,15 +1598,10 @@ impl<'src> Compiler<'src> {
function_compiler.current_scope,
);
- if let Some(value_parameters) = value_parameters.as_mut() {
- value_parameters.push((identifier_index, r#type));
- } else {
- value_parameters = Some(smallvec![(identifier_index, r#type)]);
- };
+ value_parameters.push((identifier_index, r#type));
+ function_compiler.allow(Token::Comma)?;
function_compiler.minimum_register += 1;
-
- function_compiler.allow(Token::Comma)?;
}
let return_type = if function_compiler.allow(Token::ArrowThin)? {
@@ -1618,7 +1617,7 @@ impl<'src> Compiler<'src> {
Type::None
};
let function_type = FunctionType {
- type_parameters: None,
+ type_parameters: Vec::with_capacity(0),
value_parameters,
return_type,
};
diff --git a/dust-lang/src/dust_error.rs b/dust-lang/src/dust_error.rs
index 300a296..da94b40 100644
--- a/dust-lang/src/dust_error.rs
+++ b/dust-lang/src/dust_error.rs
@@ -3,7 +3,6 @@
use std::fmt::{self, Display, Formatter};
use annotate_snippets::{Level, Renderer, Snippet};
-use smallvec::SmallVec;
use crate::{CompileError, NativeFunctionError, Span};
@@ -76,6 +75,6 @@ impl Display for DustError<'_> {
pub trait AnnotatedError {
fn title() -> &'static str;
fn description(&self) -> &'static str;
- fn detail_snippets(&self) -> SmallVec<[(String, Span); 2]>;
- fn help_snippets(&self) -> SmallVec<[(String, Span); 2]>;
+ fn detail_snippets(&self) -> Vec<(String, Span)>;
+ fn help_snippets(&self) -> Vec<(String, Span)>;
}
diff --git a/dust-lang/src/lexer.rs b/dust-lang/src/lexer.rs
index 02e7b07..0c09983 100644
--- a/dust-lang/src/lexer.rs
+++ b/dust-lang/src/lexer.rs
@@ -744,12 +744,12 @@ impl AnnotatedError for LexError {
}
}
- fn detail_snippets(&self) -> smallvec::SmallVec<[(String, Span); 2]> {
- todo!()
+ fn detail_snippets(&self) -> Vec<(String, Span)> {
+ Vec::with_capacity(0)
}
- fn help_snippets(&self) -> smallvec::SmallVec<[(String, Span); 2]> {
- todo!()
+ fn help_snippets(&self) -> Vec<(String, Span)> {
+ Vec::with_capacity(0)
}
}
diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs
index 14c00a9..f1e8dbd 100644
--- a/dust-lang/src/lib.rs
+++ b/dust-lang/src/lib.rs
@@ -28,8 +28,6 @@
//! println!("{}", report);
//! ```
-#![feature(array_repeat)]
-
pub mod chunk;
pub mod compiler;
pub mod dust_error;
diff --git a/dust-lang/src/native_function/assertion.rs b/dust-lang/src/native_function/assert.rs
similarity index 88%
rename from dust-lang/src/native_function/assertion.rs
rename to dust-lang/src/native_function/assert.rs
index 7592eee..d2e3cef 100644
--- a/dust-lang/src/native_function/assertion.rs
+++ b/dust-lang/src/native_function/assert.rs
@@ -3,7 +3,7 @@ use std::{ops::Range, panic};
use crate::vm::ThreadData;
pub fn panic(data: &mut ThreadData, _: Option, argument_range: Range) -> bool {
- let record = data.records.last_mut_unchecked();
+ let record = &mut data.call_stack.last_mut_unchecked().record;
let position = record.current_position();
let mut message = format!("Dust panic at {position}!");
diff --git a/dust-lang/src/native_function/io.rs b/dust-lang/src/native_function/io.rs
index 5cc0fe3..d4e571b 100644
--- a/dust-lang/src/native_function/io.rs
+++ b/dust-lang/src/native_function/io.rs
@@ -11,7 +11,7 @@ pub fn read_line(
destination: Option,
_argument_range: Range,
) -> bool {
- let record = data.records.last_mut_unchecked();
+ let record = &mut data.call_stack.last_mut_unchecked().record;
let destination = destination.unwrap();
let mut buffer = String::new();
@@ -31,7 +31,7 @@ pub fn read_line(
}
pub fn write(data: &mut ThreadData, _destination: Option, argument_range: Range) -> bool {
- let record = data.records.last_mut_unchecked();
+ let record = &mut data.call_stack.last_mut_unchecked().record;
let mut stdout = stdout();
for register_index in argument_range {
@@ -52,7 +52,7 @@ pub fn write_line(
_destination: Option,
argument_range: Range,
) -> bool {
- let record = data.records.last_mut_unchecked();
+ let record = &mut data.call_stack.last_mut_unchecked().record;
let mut stdout = stdout().lock();
for register_index in argument_range {
diff --git a/dust-lang/src/native_function/mod.rs b/dust-lang/src/native_function/mod.rs
index c7b6c29..e56f54a 100644
--- a/dust-lang/src/native_function/mod.rs
+++ b/dust-lang/src/native_function/mod.rs
@@ -2,7 +2,7 @@
//!
//! 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.
-mod assertion;
+mod assert;
mod io;
mod string;
@@ -14,7 +14,6 @@ use std::{
};
use serde::{Deserialize, Serialize};
-use smallvec::{smallvec, SmallVec};
use crate::{vm::ThreadData, AnnotatedError, FunctionType, Span, Type};
@@ -134,11 +133,11 @@ define_native_function! {
3,
"panic",
FunctionType {
- type_parameters: None,
- value_parameters: None,
+ type_parameters: Vec::with_capacity(0),
+ value_parameters: Vec::with_capacity(0),
return_type: Type::None
},
- assertion::panic
+ assert::panic
),
// // Type conversion
@@ -151,8 +150,8 @@ define_native_function! {
8,
"to_string",
FunctionType {
- type_parameters: None,
- value_parameters: Some(smallvec![(0, Type::Any)]),
+ type_parameters: Vec::with_capacity(0),
+ value_parameters: vec![(0, Type::Any)],
return_type: Type::String
},
string::to_string
@@ -212,8 +211,8 @@ define_native_function! {
50,
"read_line",
FunctionType {
- type_parameters: None,
- value_parameters: None,
+ type_parameters: Vec::with_capacity(0),
+ value_parameters: Vec::with_capacity(0),
return_type: Type::String
},
io::read_line
@@ -228,8 +227,8 @@ define_native_function! {
55,
"write",
FunctionType {
- type_parameters: None,
- value_parameters: Some(smallvec![(0, Type::String)]),
+ type_parameters: Vec::with_capacity(0),
+ value_parameters: vec![(0, Type::String)],
return_type: Type::None
},
io::write
@@ -240,8 +239,8 @@ define_native_function! {
57,
"write_line",
FunctionType {
- type_parameters: None,
- value_parameters: Some(smallvec![(0, Type::String)]),
+ type_parameters: Vec::with_capacity(0),
+ value_parameters: vec![(0, Type::String)],
return_type: Type::None
},
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 {
NativeFunctionError::ExpectedArgumentCount {
expected,
found,
position,
- } => smallvec![(
+ } => vec![(
format!("Expected {expected} arguments, found {found}"),
- *position
+ *position,
)],
NativeFunctionError::Panic { message, position } => {
- smallvec![(format!("Dust panic!\n{message}"), *position)]
+ vec![(format!("Dust panic!\n{message}"), *position)]
}
NativeFunctionError::Parse { error, position } => {
- smallvec![(format!("{error}"), *position)]
+ vec![(format!("{error}"), *position)]
}
NativeFunctionError::Io { error, position } => {
- smallvec![(format!("{error}"), *position)]
+ vec![(format!("{error}"), *position)]
}
}
}
- fn help_snippets(&self) -> SmallVec<[(String, Span); 2]> {
- SmallVec::new()
+ fn help_snippets(&self) -> Vec<(String, Span)> {
+ Vec::with_capacity(0)
}
}
diff --git a/dust-lang/src/native_function/string.rs b/dust-lang/src/native_function/string.rs
index 525761e..48cd8b9 100644
--- a/dust-lang/src/native_function/string.rs
+++ b/dust-lang/src/native_function/string.rs
@@ -10,7 +10,7 @@ pub fn to_string(
destination: Option,
argument_range: Range,
) -> 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_string = argument_value.display(record);
let destination = destination.unwrap();
diff --git a/dust-lang/src/type.rs b/dust-lang/src/type.rs
index d9a9150..e6264c4 100644
--- a/dust-lang/src/type.rs
+++ b/dust-lang/src/type.rs
@@ -6,7 +6,6 @@ use std::{
};
use serde::{Deserialize, Serialize};
-use smallvec::SmallVec;
/// Description of a kind of value.
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
@@ -25,7 +24,7 @@ pub enum Type {
Integer,
List(Box),
Map {
- pairs: Box>,
+ pairs: Vec<(u8, Type)>,
},
None,
Range {
@@ -35,7 +34,7 @@ pub enum Type {
String,
Struct(StructType),
Tuple {
- fields: Box>,
+ fields: Vec,
},
}
@@ -274,20 +273,20 @@ impl Ord for Type {
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct FunctionType {
- pub type_parameters: Option>,
- pub value_parameters: Option>,
+ pub type_parameters: Vec,
+ pub value_parameters: Vec<(u8, Type)>,
pub return_type: Type,
}
impl FunctionType {
- pub fn new>, U: Into>>(
- type_parameters: Option,
- value_parameters: Option,
+ pub fn new>, U: Into>>(
+ type_parameters: T,
+ value_parameters: U,
return_type: Type,
) -> Self {
FunctionType {
- type_parameters: type_parameters.map(|into_types| into_types.into()),
- value_parameters: value_parameters.map(|into_values| into_values.into()),
+ type_parameters: type_parameters.into(),
+ value_parameters: value_parameters.into(),
return_type,
}
}
@@ -297,10 +296,10 @@ impl Display for FunctionType {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "fn ")?;
- if let Some(type_parameters) = &self.type_parameters {
+ if !self.type_parameters.is_empty() {
write!(f, "<")?;
- for (index, type_parameter) in type_parameters.iter().enumerate() {
+ for (index, type_parameter) in self.type_parameters.iter().enumerate() {
if index > 0 {
write!(f, ", ")?;
}
@@ -313,8 +312,8 @@ impl Display for FunctionType {
write!(f, "(")?;
- if let Some(value_parameters) = &self.value_parameters {
- for (index, (_, r#type)) in value_parameters.iter().enumerate() {
+ if !self.value_parameters.is_empty() {
+ for (index, (_, r#type)) in self.value_parameters.iter().enumerate() {
if index > 0 {
write!(f, ", ")?;
}
diff --git a/dust-lang/src/vm/error.rs b/dust-lang/src/vm/error.rs
deleted file mode 100644
index 1c71da4..0000000
--- a/dust-lang/src/vm/error.rs
+++ /dev/null
@@ -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,
- 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}")
- }
- }
- }
-}
diff --git a/dust-lang/src/vm/mod.rs b/dust-lang/src/vm/mod.rs
index f39ad72..cb5336f 100644
--- a/dust-lang/src/vm/mod.rs
+++ b/dust-lang/src/vm/mod.rs
@@ -1,5 +1,4 @@
//! Virtual machine and errors
-mod error;
mod record;
mod run_action;
mod stack;
@@ -11,7 +10,6 @@ use std::{
thread::spawn,
};
-pub use error::VmError;
pub use record::Record;
pub(crate) use run_action::get_next_action;
pub use run_action::RunAction;
diff --git a/dust-lang/src/vm/run_action.rs b/dust-lang/src/vm/run_action.rs
index f636133..6cf5234 100644
--- a/dust-lang/src/vm/run_action.rs
+++ b/dust-lang/src/vm/run_action.rs
@@ -68,7 +68,7 @@ pub(crate) fn get_next_action(record: &mut Record) -> RunAction {
}
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 from_register = record.get_register_unchecked(from);
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 {
- let record = data.records.last_mut_unchecked();
+ let record = &mut data.call_stack.last_mut_unchecked().record;
let Close { from, to } = instruction.into();
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 {
- let record = data.records.last_mut_unchecked();
+ let record = &mut data.call_stack.last_mut_unchecked().record;
let LoadBoolean {
destination,
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 {
- let record = data.records.last_mut_unchecked();
+ let record = &mut data.call_stack.last_mut_unchecked().record;
let LoadConstant {
destination,
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 {
- let record = data.records.last_mut_unchecked();
+ let record = &mut data.call_stack.last_mut_unchecked().record;
let LoadList {
destination,
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 {
- let record = data.records.last_mut_unchecked();
+ let record = &mut data.call_stack.last_mut_unchecked().record;
let LoadFunction {
destination,
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 {
- let record = data.records.last_mut_unchecked();
+ let record = &mut data.call_stack.last_mut_unchecked().record;
let LoadSelf { destination } = instruction.into();
let function = record.as_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 {
- let record = data.records.last_mut_unchecked();
+ let record = &mut data.call_stack.last_mut_unchecked().record;
let GetLocal {
destination,
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 {
- let record = data.records.last_mut_unchecked();
+ let record = &mut data.call_stack.last_mut_unchecked().record;
let SetLocal {
register_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 {
- let record = data.records.last_mut_unchecked();
+ let record = &mut data.call_stack.last_mut_unchecked().record;
let Add {
destination,
left,
@@ -264,7 +264,7 @@ pub fn add(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 {
destination,
left,
@@ -283,7 +283,7 @@ pub fn subtract(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 {
destination,
left,
@@ -310,7 +310,7 @@ pub fn multiply(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 {
destination,
left,
@@ -337,7 +337,7 @@ pub fn divide(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 {
destination,
left,
@@ -364,7 +364,7 @@ pub fn modulo(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 {
operand_register,
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 {
- let record = data.records.last_mut_unchecked();
+ let record = &mut data.call_stack.last_mut_unchecked().record;
let TestSet {
destination,
argument,
@@ -416,7 +416,7 @@ pub fn test_set(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 left = record.get_argument_unchecked(left);
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 {
- 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 left = record.get_argument_unchecked(left);
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 {
- 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 left = record.get_argument_unchecked(left);
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 {
- let record = data.records.last_mut_unchecked();
+ let record = &mut data.call_stack.last_mut_unchecked().record;
let Negate {
destination,
argument,
@@ -481,7 +481,7 @@ pub fn negate(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 {
destination,
argument,
@@ -501,7 +501,7 @@ pub fn not(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 {
offset,
is_positive,
@@ -520,32 +520,35 @@ pub fn jump(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 {
destination: return_register,
function_register,
argument_count,
is_recursive,
} = instruction.into();
- let function = current_record
+ let function = current_call
+ .record
.open_register_unchecked(function_register)
.as_function()
.unwrap();
let first_argument_register = return_register - argument_count;
let prototype = if is_recursive {
- current_record.chunk
+ current_call.record.chunk
} else {
- ¤t_record.chunk.prototypes[function.prototype_index as usize]
+ ¤t_call.record.chunk.prototypes[function.prototype_index as usize]
};
- let mut next_record = Record::new(prototype);
- let next_call = FunctionCall {
- name: next_record.name().cloned(),
+ let mut next_call = FunctionCall {
+ name: prototype.name.clone(),
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() {
- 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!(
"Passing argument \"{argument}\" to {}",
@@ -555,14 +558,15 @@ pub fn call(instruction: Instruction, data: &mut ThreadData) -> bool {
.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.records.push(current_record);
- data.records.push(next_record);
false
}
@@ -586,28 +590,30 @@ pub fn r#return(instruction: Instruction, data: &mut ThreadData) -> bool {
should_return_value,
return_register,
} = instruction.into();
- let current_call = data.call_stack.pop_unchecked();
- let mut current_record = if data.call_stack.is_empty() {
+ let mut current_call = if data.call_stack.len() == 1 {
if should_return_value {
data.return_value_index = Some(return_register);
};
return true;
} 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;
if should_return_value {
- let return_value =
- current_record.empty_register_or_clone_constant_unchecked(return_register);
+ let return_value = current_call
+ .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
}
diff --git a/dust-lang/src/vm/stack.rs b/dust-lang/src/vm/stack.rs
index 82e8562..752dcd8 100644
--- a/dust-lang/src/vm/stack.rs
+++ b/dust-lang/src/vm/stack.rs
@@ -5,7 +5,7 @@ use std::{
use crate::DustString;
-use super::VmError;
+use super::Record;
#[derive(Clone, PartialEq)]
pub struct Stack {
@@ -35,7 +35,7 @@ impl Stack {
pub fn get_unchecked(&self, index: usize) -> &T {
if cfg!(debug_assertions) {
- assert!(index < self.len(), "{}", VmError::StackUnderflow);
+ assert!(index < self.len(), "Stack underflow");
&self.items[index]
} else {
@@ -61,7 +61,7 @@ impl Stack {
pub fn pop_unchecked(&mut self) -> T {
if cfg!(debug_assertions) {
- assert!(!self.is_empty(), "{}", VmError::StackUnderflow);
+ assert!(!self.is_empty(), "Stack underflow");
self.items.pop().unwrap()
} else {
@@ -71,7 +71,7 @@ impl Stack {
pub fn last_unchecked(&self) -> &T {
if cfg!(debug_assertions) {
- assert!(!self.is_empty(), "{}", VmError::StackUnderflow);
+ assert!(!self.is_empty(), "Stack underflow");
self.items.last().unwrap()
} else {
@@ -81,7 +81,7 @@ impl Stack {
pub fn last_mut_unchecked(&mut self) -> &mut T {
if cfg!(debug_assertions) {
- assert!(!self.is_empty(), "{}", VmError::StackUnderflow);
+ assert!(!self.is_empty(), "Stack underflow");
self.items.last_mut().unwrap()
} else {
@@ -116,7 +116,7 @@ impl Debug for Stack {
}
}
-impl Display for Stack {
+impl Display for Stack> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
writeln!(f, "-- DUST CALL STACK --")?;
@@ -128,14 +128,15 @@ impl Display for Stack {
}
}
-#[derive(Clone, Debug, PartialEq)]
-pub struct FunctionCall {
+#[derive(Debug)]
+pub struct FunctionCall<'a> {
pub name: Option,
pub return_register: u8,
pub ip: usize,
+ pub record: Record<'a>,
}
-impl Display for FunctionCall {
+impl Display for FunctionCall<'_> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let FunctionCall {
name,
diff --git a/dust-lang/src/vm/thread.rs b/dust-lang/src/vm/thread.rs
index 3adaaa2..462479c 100644
--- a/dust-lang/src/vm/thread.rs
+++ b/dust-lang/src/vm/thread.rs
@@ -23,21 +23,18 @@ impl Thread {
);
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 {
name: self.chunk.name.clone(),
return_register: 0, // Never used, the main function's return is the thread's return
ip: 0,
+ record: Record::new(&self.chunk),
};
- let main_record = Record::new(&self.chunk);
call_stack.push(main_call);
- records.push(main_record);
let first_action = RunAction::from(*self.chunk.instructions.first().unwrap());
let mut thread_data = ThreadData {
call_stack,
- records,
next_action: first_action,
return_value_index: None,
};
@@ -53,8 +50,9 @@ impl Thread {
if should_end {
let return_value = if let Some(register_index) = thread_data.return_value_index {
let value = thread_data
- .records
+ .call_stack
.last_mut_unchecked()
+ .record
.empty_register_or_clone_constant_unchecked(register_index);
Some(value)
@@ -70,8 +68,7 @@ impl Thread {
#[derive(Debug)]
pub struct ThreadData<'a> {
- pub call_stack: Stack,
- pub records: Stack>,
+ pub call_stack: Stack>,
pub next_action: RunAction,
pub return_value_index: Option,
}