Optimize; Remove non-working optimizations; Improve CLI
This commit is contained in:
parent
f667716336
commit
2365979561
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -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"
|
||||
|
@ -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#"
|
||||
<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!(
|
||||
const ABOUT: &str = cstr!(
|
||||
r#"
|
||||
<bright-magenta,bold>Dust CLI
|
||||
────────</bright-magenta,bold>
|
||||
────────</>
|
||||
{about}
|
||||
|
||||
<bold>Version:</bold> {version}
|
||||
<bold>Author:</bold> {author}
|
||||
<bold>License:</bold> GPL-3.0
|
||||
<bold>Repository:</bold> git.jeffa.io/jeff/dust
|
||||
<bold>⚙️ Version:</> {version}
|
||||
<bold>🦀 Author:</> {author}
|
||||
<bold>⚖️ License:</> GPL-3.0
|
||||
<bold>🔬 Repository:</> https://git.jeffa.io/jeff/dust
|
||||
"#
|
||||
);
|
||||
|
||||
<bright-magenta,bold>Usage
|
||||
─────</bright-magenta,bold>
|
||||
{usage}
|
||||
const PLAIN_ABOUT: &str = r#"
|
||||
{about}
|
||||
"#;
|
||||
|
||||
<bright-magenta,bold>Options
|
||||
───────</bright-magenta,bold>
|
||||
const USAGE: &str = cstr!(
|
||||
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}
|
||||
"#
|
||||
);
|
||||
|
||||
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()
|
||||
.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);
|
||||
|
@ -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"
|
||||
|
@ -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<DustString>,
|
||||
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<Instruction>,
|
||||
pub(crate) positions: Vec<Span>,
|
||||
pub(crate) constants: Vec<Value>,
|
||||
pub(crate) locals: Vec<Local>,
|
||||
pub(crate) prototypes: Vec<Chunk>,
|
||||
|
||||
pub(crate) register_count: usize,
|
||||
@ -51,10 +50,10 @@ impl Chunk {
|
||||
pub fn with_data(
|
||||
name: Option<DustString>,
|
||||
r#type: FunctionType,
|
||||
instructions: impl Into<SmallVec<[Instruction; 32]>>,
|
||||
positions: impl Into<SmallVec<[Span; 32]>>,
|
||||
constants: impl Into<SmallVec<[Value; 16]>>,
|
||||
locals: impl Into<SmallVec<[Local; 8]>>,
|
||||
instructions: impl Into<Vec<Instruction>>,
|
||||
positions: impl Into<Vec<Span>>,
|
||||
constants: impl Into<Vec<Value>>,
|
||||
locals: impl Into<Vec<Local>>,
|
||||
prototypes: Vec<Chunk>,
|
||||
) -> Self {
|
||||
Self {
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<DustString>, source: &str) -> Result<Chunk, DustError> {
|
||||
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<Value>,
|
||||
|
||||
/// 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<DustString>,
|
||||
is_main: bool,
|
||||
) -> Result<Self, CompileError> {
|
||||
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<Instruction>, Vec<Span>) = self
|
||||
.instructions
|
||||
.into_iter()
|
||||
.map(|(instruction, _, position)| (instruction, position))
|
||||
@ -204,14 +208,14 @@ impl<'src> Compiler<'src> {
|
||||
.locals
|
||||
.into_iter()
|
||||
.map(|(local, _)| local)
|
||||
.collect::<SmallVec<[Local; 8]>>();
|
||||
.collect::<Vec<Local>>();
|
||||
|
||||
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<SmallVec<[(u8, Type); 4]>> = 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)]);
|
||||
};
|
||||
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,
|
||||
};
|
||||
|
@ -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)>;
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,8 +28,6 @@
|
||||
//! println!("{}", report);
|
||||
//! ```
|
||||
|
||||
#![feature(array_repeat)]
|
||||
|
||||
pub mod chunk;
|
||||
pub mod compiler;
|
||||
pub mod dust_error;
|
||||
|
@ -3,7 +3,7 @@ use std::{ops::Range, panic};
|
||||
use crate::vm::ThreadData;
|
||||
|
||||
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 mut message = format!("Dust panic at {position}!");
|
||||
|
@ -11,7 +11,7 @@ pub fn read_line(
|
||||
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 destination = destination.unwrap();
|
||||
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 {
|
||||
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<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().lock();
|
||||
|
||||
for register_index in argument_range {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ pub fn to_string(
|
||||
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 argument_value = record.open_register_unchecked(argument_range.start);
|
||||
let argument_string = argument_value.display(record);
|
||||
let destination = destination.unwrap();
|
||||
|
@ -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<Type>),
|
||||
Map {
|
||||
pairs: Box<SmallVec<[(u8, Type); 8]>>,
|
||||
pairs: Vec<(u8, Type)>,
|
||||
},
|
||||
None,
|
||||
Range {
|
||||
@ -35,7 +34,7 @@ pub enum Type {
|
||||
String,
|
||||
Struct(StructType),
|
||||
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)]
|
||||
pub struct FunctionType {
|
||||
pub type_parameters: Option<SmallVec<[u8; 4]>>,
|
||||
pub value_parameters: Option<SmallVec<[(u8, Type); 4]>>,
|
||||
pub type_parameters: Vec<u8>,
|
||||
pub value_parameters: Vec<(u8, Type)>,
|
||||
pub return_type: Type,
|
||||
}
|
||||
|
||||
impl FunctionType {
|
||||
pub fn new<T: Into<SmallVec<[u8; 4]>>, U: Into<SmallVec<[(u8, Type); 4]>>>(
|
||||
type_parameters: Option<T>,
|
||||
value_parameters: Option<U>,
|
||||
pub fn new<T: Into<Vec<u8>>, U: Into<Vec<(u8, Type)>>>(
|
||||
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, ", ")?;
|
||||
}
|
||||
|
@ -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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ use std::{
|
||||
|
||||
use crate::DustString;
|
||||
|
||||
use super::VmError;
|
||||
use super::Record;
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Stack<T> {
|
||||
@ -35,7 +35,7 @@ impl<T> Stack<T> {
|
||||
|
||||
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<T> Stack<T> {
|
||||
|
||||
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<T> Stack<T> {
|
||||
|
||||
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<T> Stack<T> {
|
||||
|
||||
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<T: Debug> Debug for Stack<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Stack<FunctionCall> {
|
||||
impl Display for Stack<FunctionCall<'_>> {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
writeln!(f, "-- DUST CALL STACK --")?;
|
||||
|
||||
@ -128,14 +128,15 @@ impl Display for Stack<FunctionCall> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct FunctionCall {
|
||||
#[derive(Debug)]
|
||||
pub struct FunctionCall<'a> {
|
||||
pub name: Option<DustString>,
|
||||
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,
|
||||
|
@ -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<FunctionCall>,
|
||||
pub records: Stack<Record<'a>>,
|
||||
pub call_stack: Stack<FunctionCall<'a>>,
|
||||
pub next_action: RunAction,
|
||||
pub return_value_index: Option<u8>,
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user