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",
|
"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"
|
||||||
|
@ -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);
|
||||||
|
@ -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"
|
||||||
|
@ -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 {
|
||||||
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
@ -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)>;
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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}!");
|
||||||
|
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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, ", ")?;
|
||||||
}
|
}
|
||||||
|
@ -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
|
//! 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;
|
||||||
|
@ -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 {
|
||||||
¤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 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
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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>,
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user