Add thread spawning and a random number generator
This commit is contained in:
parent
5caad00f65
commit
de426d814a
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -269,6 +269,15 @@ version = "1.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-channel"
|
||||||
|
version = "0.5.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-deque"
|
name = "crossbeam-deque"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
@ -335,6 +344,7 @@ dependencies = [
|
|||||||
"annotate-snippets",
|
"annotate-snippets",
|
||||||
"colored",
|
"colored",
|
||||||
"criterion",
|
"criterion",
|
||||||
|
"crossbeam-channel",
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"rand",
|
"rand",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -12,7 +12,7 @@ version.workspace = true
|
|||||||
annotate-snippets = "0.11.4"
|
annotate-snippets = "0.11.4"
|
||||||
colored = "2.1.0"
|
colored = "2.1.0"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
serde = { version = "1.0.203", features = ["derive"] }
|
serde = { version = "1.0.203", features = ["derive", "rc"] }
|
||||||
serde_json = "1.0.117"
|
serde_json = "1.0.117"
|
||||||
getrandom = { version = "0.2", features = [
|
getrandom = { version = "0.2", features = [
|
||||||
"js",
|
"js",
|
||||||
@ -21,6 +21,7 @@ smartstring = { version = "1.0.1", features = [
|
|||||||
"serde",
|
"serde",
|
||||||
], default-features = false }
|
], default-features = false }
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
|
crossbeam-channel = "0.5.14"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = { version = "0.3.4", features = ["html_reports"] }
|
criterion = { version = "0.3.4", features = ["html_reports"] }
|
||||||
@ -33,6 +34,10 @@ harness = false
|
|||||||
name = "fibonacci"
|
name = "fibonacci"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "threads"
|
||||||
|
harness = false
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
name = "logic_and"
|
name = "logic_and"
|
||||||
path = "tests/logic/and.rs"
|
path = "tests/logic/and.rs"
|
||||||
|
31
dust-lang/benches/threads.rs
Normal file
31
dust-lang/benches/threads.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use criterion::{Criterion, black_box, criterion_group, criterion_main};
|
||||||
|
use dust_lang::run;
|
||||||
|
|
||||||
|
const SOURCE: &str = r#"
|
||||||
|
let mut i = 0
|
||||||
|
|
||||||
|
while i < 1_000 {
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
spawn(
|
||||||
|
fn () { random_int(0, 10); }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
fn threads(source: &str) {
|
||||||
|
run(source).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn criterion_benchmark(c: &mut Criterion) {
|
||||||
|
let mut group = c.benchmark_group("threads");
|
||||||
|
|
||||||
|
group.measurement_time(Duration::from_secs(15));
|
||||||
|
group.bench_function("threads", |b| b.iter(|| threads(black_box(SOURCE))));
|
||||||
|
group.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(benches, criterion_benchmark);
|
||||||
|
criterion_main!(benches);
|
@ -23,6 +23,7 @@ pub use scope::Scope;
|
|||||||
|
|
||||||
use std::fmt::{self, Debug, Display, Formatter, Write as FmtWrite};
|
use std::fmt::{self, Debug, Display, Formatter, Write as FmtWrite};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
@ -40,7 +41,7 @@ pub struct Chunk {
|
|||||||
pub(crate) positions: Vec<Span>,
|
pub(crate) positions: Vec<Span>,
|
||||||
pub(crate) constants: Vec<Value>,
|
pub(crate) constants: Vec<Value>,
|
||||||
pub(crate) locals: Vec<Local>,
|
pub(crate) locals: Vec<Local>,
|
||||||
pub(crate) prototypes: Vec<Chunk>,
|
pub(crate) prototypes: Vec<Arc<Chunk>>,
|
||||||
|
|
||||||
pub(crate) register_count: usize,
|
pub(crate) register_count: usize,
|
||||||
pub(crate) prototype_index: u8,
|
pub(crate) prototype_index: u8,
|
||||||
@ -55,7 +56,7 @@ impl Chunk {
|
|||||||
positions: impl Into<Vec<Span>>,
|
positions: impl Into<Vec<Span>>,
|
||||||
constants: impl Into<Vec<Value>>,
|
constants: impl Into<Vec<Value>>,
|
||||||
locals: impl Into<Vec<Local>>,
|
locals: impl Into<Vec<Local>>,
|
||||||
prototypes: Vec<Chunk>,
|
prototypes: impl IntoIterator<Item = Chunk>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
@ -64,7 +65,7 @@ impl Chunk {
|
|||||||
positions: positions.into(),
|
positions: positions.into(),
|
||||||
constants: constants.into(),
|
constants: constants.into(),
|
||||||
locals: locals.into(),
|
locals: locals.into(),
|
||||||
prototypes,
|
prototypes: prototypes.into_iter().map(Arc::new).collect(),
|
||||||
register_count: 0,
|
register_count: 0,
|
||||||
prototype_index: 0,
|
prototype_index: 0,
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ use parse_rule::{ParseRule, Precedence};
|
|||||||
use tracing::{Level, debug, info, span};
|
use tracing::{Level, debug, info, span};
|
||||||
use type_checks::{check_math_type, check_math_types};
|
use type_checks::{check_math_type, check_math_types};
|
||||||
|
|
||||||
use std::mem::replace;
|
use std::{mem::replace, sync::Arc};
|
||||||
|
|
||||||
use optimize::control_flow_register_consolidation;
|
use optimize::control_flow_register_consolidation;
|
||||||
|
|
||||||
@ -96,7 +96,7 @@ pub struct Compiler<'src> {
|
|||||||
|
|
||||||
/// 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.
|
||||||
prototypes: Vec<Chunk>,
|
prototypes: Vec<Arc<Chunk>>,
|
||||||
|
|
||||||
/// Maximum stack size required by the chunk. This is assigned to the chunk when
|
/// Maximum stack size required by the chunk. This is assigned to the chunk when
|
||||||
/// [`Compiler::finish`] is called.
|
/// [`Compiler::finish`] is called.
|
||||||
@ -1645,7 +1645,7 @@ impl<'src> Compiler<'src> {
|
|||||||
let chunk = function_compiler.finish();
|
let chunk = function_compiler.finish();
|
||||||
let destination = self.next_register();
|
let destination = self.next_register();
|
||||||
|
|
||||||
self.prototypes.push(chunk);
|
self.prototypes.push(Arc::new(chunk));
|
||||||
|
|
||||||
if let Some(identifier) = identifier {
|
if let Some(identifier) = identifier {
|
||||||
self.declare_local(
|
self.declare_local(
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
//! - [`Lexer`], which lexes the input a token at a time
|
//! - [`Lexer`], which lexes the input a token at a time
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{dust_error::AnnotatedError, CompileError, DustError, Span, Token};
|
use crate::{CompileError, DustError, Span, Token, dust_error::AnnotatedError};
|
||||||
|
|
||||||
/// Lexes the input and returns a vector of tokens and their positions.
|
/// Lexes the input and returns a vector of tokens and their positions.
|
||||||
///
|
///
|
||||||
@ -83,7 +83,7 @@ impl<'src> Lexer<'src> {
|
|||||||
self.skip_whitespace();
|
self.skip_whitespace();
|
||||||
|
|
||||||
let (token, span) = if let Some(character) = self.peek_char() {
|
let (token, span) = if let Some(character) = self.peek_char() {
|
||||||
let lexer = LexRule::from(&character).lexer;
|
let lexer = LexRule::from(&character).lex_action;
|
||||||
|
|
||||||
lexer(self)?
|
lexer(self)?
|
||||||
} else {
|
} else {
|
||||||
@ -613,92 +613,92 @@ impl<'src> Lexer<'src> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type LexerFn<'src> = fn(&mut Lexer<'src>) -> Result<(Token<'src>, Span), LexError>;
|
type LexAction<'src> = fn(&mut Lexer<'src>) -> Result<(Token<'src>, Span), LexError>;
|
||||||
|
|
||||||
pub struct LexRule<'src> {
|
pub struct LexRule<'src> {
|
||||||
lexer: LexerFn<'src>,
|
lex_action: LexAction<'src>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&char> for LexRule<'_> {
|
impl From<&char> for LexRule<'_> {
|
||||||
fn from(char: &char) -> Self {
|
fn from(char: &char) -> Self {
|
||||||
match char {
|
match char {
|
||||||
'0'..='9' => LexRule {
|
'0'..='9' => LexRule {
|
||||||
lexer: Lexer::lex_numeric,
|
lex_action: Lexer::lex_numeric,
|
||||||
},
|
},
|
||||||
char if char.is_alphabetic() => LexRule {
|
char if char.is_alphabetic() => LexRule {
|
||||||
lexer: Lexer::lex_keyword_or_identifier,
|
lex_action: Lexer::lex_keyword_or_identifier,
|
||||||
},
|
},
|
||||||
'"' => LexRule {
|
'"' => LexRule {
|
||||||
lexer: Lexer::lex_string,
|
lex_action: Lexer::lex_string,
|
||||||
},
|
},
|
||||||
'\'' => LexRule {
|
'\'' => LexRule {
|
||||||
lexer: Lexer::lex_char,
|
lex_action: Lexer::lex_char,
|
||||||
},
|
},
|
||||||
'+' => LexRule {
|
'+' => LexRule {
|
||||||
lexer: Lexer::lex_plus,
|
lex_action: Lexer::lex_plus,
|
||||||
},
|
},
|
||||||
'-' => LexRule {
|
'-' => LexRule {
|
||||||
lexer: Lexer::lex_minus,
|
lex_action: Lexer::lex_minus,
|
||||||
},
|
},
|
||||||
'*' => LexRule {
|
'*' => LexRule {
|
||||||
lexer: Lexer::lex_star,
|
lex_action: Lexer::lex_star,
|
||||||
},
|
},
|
||||||
'/' => LexRule {
|
'/' => LexRule {
|
||||||
lexer: Lexer::lex_slash,
|
lex_action: Lexer::lex_slash,
|
||||||
},
|
},
|
||||||
'%' => LexRule {
|
'%' => LexRule {
|
||||||
lexer: Lexer::lex_percent,
|
lex_action: Lexer::lex_percent,
|
||||||
},
|
},
|
||||||
'!' => LexRule {
|
'!' => LexRule {
|
||||||
lexer: Lexer::lex_exclamation_mark,
|
lex_action: Lexer::lex_exclamation_mark,
|
||||||
},
|
},
|
||||||
'=' => LexRule {
|
'=' => LexRule {
|
||||||
lexer: Lexer::lex_equal,
|
lex_action: Lexer::lex_equal,
|
||||||
},
|
},
|
||||||
'<' => LexRule {
|
'<' => LexRule {
|
||||||
lexer: Lexer::lex_less_than,
|
lex_action: Lexer::lex_less_than,
|
||||||
},
|
},
|
||||||
'>' => LexRule {
|
'>' => LexRule {
|
||||||
lexer: Lexer::lex_greater_than,
|
lex_action: Lexer::lex_greater_than,
|
||||||
},
|
},
|
||||||
'&' => LexRule {
|
'&' => LexRule {
|
||||||
lexer: Lexer::lex_ampersand,
|
lex_action: Lexer::lex_ampersand,
|
||||||
},
|
},
|
||||||
'|' => LexRule {
|
'|' => LexRule {
|
||||||
lexer: Lexer::lex_pipe,
|
lex_action: Lexer::lex_pipe,
|
||||||
},
|
},
|
||||||
'(' => LexRule {
|
'(' => LexRule {
|
||||||
lexer: Lexer::lex_left_parenthesis,
|
lex_action: Lexer::lex_left_parenthesis,
|
||||||
},
|
},
|
||||||
')' => LexRule {
|
')' => LexRule {
|
||||||
lexer: Lexer::lex_right_parenthesis,
|
lex_action: Lexer::lex_right_parenthesis,
|
||||||
},
|
},
|
||||||
'[' => LexRule {
|
'[' => LexRule {
|
||||||
lexer: Lexer::lex_left_bracket,
|
lex_action: Lexer::lex_left_bracket,
|
||||||
},
|
},
|
||||||
']' => LexRule {
|
']' => LexRule {
|
||||||
lexer: Lexer::lex_right_bracket,
|
lex_action: Lexer::lex_right_bracket,
|
||||||
},
|
},
|
||||||
'{' => LexRule {
|
'{' => LexRule {
|
||||||
lexer: Lexer::lex_left_brace,
|
lex_action: Lexer::lex_left_brace,
|
||||||
},
|
},
|
||||||
'}' => LexRule {
|
'}' => LexRule {
|
||||||
lexer: Lexer::lex_right_brace,
|
lex_action: Lexer::lex_right_brace,
|
||||||
},
|
},
|
||||||
';' => LexRule {
|
';' => LexRule {
|
||||||
lexer: Lexer::lex_semicolon,
|
lex_action: Lexer::lex_semicolon,
|
||||||
},
|
},
|
||||||
':' => LexRule {
|
':' => LexRule {
|
||||||
lexer: Lexer::lex_colon,
|
lex_action: Lexer::lex_colon,
|
||||||
},
|
},
|
||||||
',' => LexRule {
|
',' => LexRule {
|
||||||
lexer: Lexer::lex_comma,
|
lex_action: Lexer::lex_comma,
|
||||||
},
|
},
|
||||||
'.' => LexRule {
|
'.' => LexRule {
|
||||||
lexer: Lexer::lex_dot,
|
lex_action: Lexer::lex_dot,
|
||||||
},
|
},
|
||||||
_ => LexRule {
|
_ => LexRule {
|
||||||
lexer: Lexer::lex_unexpected,
|
lex_action: Lexer::lex_unexpected,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,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, _: u8, argument_range: Range<u8>) -> bool {
|
||||||
let position = data.current_position();
|
let position = data.current_position();
|
||||||
let mut message = format!("Dust panic at {position}!");
|
let mut message = format!("Dust panic at {position}!");
|
||||||
|
|
||||||
|
@ -1,17 +1,12 @@
|
|||||||
use std::io::{stdin, stdout, Write};
|
use std::io::{Write, stdin, stdout};
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
vm::{get_next_action, Register, ThreadData},
|
|
||||||
ConcreteValue, Value,
|
ConcreteValue, Value,
|
||||||
|
vm::{Register, ThreadData, get_next_action},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn read_line(
|
pub fn read_line(data: &mut ThreadData, destination: u8, _argument_range: Range<u8>) -> bool {
|
||||||
data: &mut ThreadData,
|
|
||||||
destination: Option<u8>,
|
|
||||||
_argument_range: Range<u8>,
|
|
||||||
) -> bool {
|
|
||||||
let destination = destination.unwrap();
|
|
||||||
let mut buffer = String::new();
|
let mut buffer = String::new();
|
||||||
|
|
||||||
if stdin().read_line(&mut buffer).is_ok() {
|
if stdin().read_line(&mut buffer).is_ok() {
|
||||||
@ -29,7 +24,7 @@ pub fn read_line(
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(data: &mut ThreadData, _destination: Option<u8>, argument_range: Range<u8>) -> bool {
|
pub fn write(data: &mut ThreadData, _: u8, argument_range: Range<u8>) -> bool {
|
||||||
let mut stdout = stdout();
|
let mut stdout = stdout();
|
||||||
|
|
||||||
for register_index in argument_range {
|
for register_index in argument_range {
|
||||||
@ -45,11 +40,7 @@ pub fn write(data: &mut ThreadData, _destination: Option<u8>, argument_range: Ra
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_line(
|
pub fn write_line(data: &mut ThreadData, _: u8, argument_range: Range<u8>) -> bool {
|
||||||
data: &mut ThreadData,
|
|
||||||
_destination: Option<u8>,
|
|
||||||
argument_range: Range<u8>,
|
|
||||||
) -> bool {
|
|
||||||
let mut stdout = stdout().lock();
|
let mut stdout = stdout().lock();
|
||||||
|
|
||||||
for register_index in argument_range {
|
for register_index in argument_range {
|
||||||
|
@ -4,7 +4,9 @@
|
|||||||
//! itself or that are more efficient to implement in Rust.
|
//! itself or that are more efficient to implement in Rust.
|
||||||
mod assert;
|
mod assert;
|
||||||
mod io;
|
mod io;
|
||||||
|
mod random;
|
||||||
mod string;
|
mod string;
|
||||||
|
mod thread;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fmt::{self, Display, Formatter},
|
fmt::{self, Display, Formatter},
|
||||||
@ -15,7 +17,7 @@ use std::{
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{vm::ThreadData, AnnotatedError, FunctionType, Span, Type};
|
use crate::{AnnotatedError, FunctionType, Span, Type, vm::ThreadData};
|
||||||
|
|
||||||
macro_rules! define_native_function {
|
macro_rules! define_native_function {
|
||||||
($(($name:ident, $bytes:literal, $str:expr, $type:expr, $function:expr)),*) => {
|
($(($name:ident, $bytes:literal, $str:expr, $type:expr, $function:expr)),*) => {
|
||||||
@ -33,7 +35,7 @@ macro_rules! define_native_function {
|
|||||||
pub fn call(
|
pub fn call(
|
||||||
&self,
|
&self,
|
||||||
data: &mut ThreadData,
|
data: &mut ThreadData,
|
||||||
destination: Option<u8>,
|
destination: u8,
|
||||||
argument_range: Range<u8>,
|
argument_range: Range<u8>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match self {
|
match self {
|
||||||
@ -244,11 +246,42 @@ define_native_function! {
|
|||||||
return_type: Type::None
|
return_type: Type::None
|
||||||
},
|
},
|
||||||
io::write_line
|
io::write_line
|
||||||
)
|
),
|
||||||
|
|
||||||
// // Random
|
// // Random
|
||||||
// (Random, 58_u8, "random", true),
|
(
|
||||||
// (RandomInRange, 59_u8, "random_in_range", true)
|
RandomInteger,
|
||||||
|
58,
|
||||||
|
"random_int",
|
||||||
|
FunctionType {
|
||||||
|
type_parameters: Vec::with_capacity(0),
|
||||||
|
value_parameters: vec![(0, Type::Integer), (1, Type::Integer)],
|
||||||
|
return_type: Type::Integer
|
||||||
|
},
|
||||||
|
random::random_int
|
||||||
|
),
|
||||||
|
|
||||||
|
// Thread
|
||||||
|
(
|
||||||
|
Spawn,
|
||||||
|
60,
|
||||||
|
"spawn",
|
||||||
|
FunctionType {
|
||||||
|
type_parameters: Vec::with_capacity(0),
|
||||||
|
value_parameters: vec![
|
||||||
|
(
|
||||||
|
0,
|
||||||
|
Type::Function(Box::new(FunctionType {
|
||||||
|
type_parameters: Vec::with_capacity(0),
|
||||||
|
value_parameters: Vec::with_capacity(0),
|
||||||
|
return_type: Type::Any
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
],
|
||||||
|
return_type: Type::None
|
||||||
|
},
|
||||||
|
thread::spawn
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
40
dust-lang/src/native_function/random.rs
Normal file
40
dust-lang/src/native_function/random.rs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
use std::ops::Range;
|
||||||
|
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
Value,
|
||||||
|
vm::{Register, ThreadData, get_next_action},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn random_int(data: &mut ThreadData, destination: u8, argument_range: Range<u8>) -> bool {
|
||||||
|
let mut argument_range_iter = argument_range.into_iter();
|
||||||
|
let (min, max) = {
|
||||||
|
let mut min = None;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let register_index = argument_range_iter
|
||||||
|
.next()
|
||||||
|
.unwrap_or_else(|| panic!("No argument was passed to \"random_int\""));
|
||||||
|
let value_option = data.open_register_allow_empty_unchecked(register_index);
|
||||||
|
|
||||||
|
if let Some(argument) = value_option {
|
||||||
|
if let Some(integer) = argument.as_integer() {
|
||||||
|
if min.is_none() {
|
||||||
|
min = Some(integer);
|
||||||
|
} else {
|
||||||
|
break (min, integer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let random_integer = rand::thread_rng().gen_range(min.unwrap()..max);
|
||||||
|
|
||||||
|
data.set_register(destination, Register::Value(Value::integer(random_integer)));
|
||||||
|
|
||||||
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
@ -1,18 +1,13 @@
|
|||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
vm::{get_next_action, Register, ThreadData},
|
|
||||||
ConcreteValue, Value,
|
ConcreteValue, Value,
|
||||||
|
vm::{Register, ThreadData, get_next_action},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn to_string(
|
pub fn to_string(data: &mut ThreadData, destination: u8, argument_range: Range<u8>) -> bool {
|
||||||
data: &mut ThreadData,
|
|
||||||
destination: Option<u8>,
|
|
||||||
argument_range: Range<u8>,
|
|
||||||
) -> bool {
|
|
||||||
let argument_value = data.open_register_unchecked(argument_range.start);
|
let argument_value = data.open_register_unchecked(argument_range.start);
|
||||||
let argument_string = argument_value.display(data);
|
let argument_string = argument_value.display(data);
|
||||||
let destination = destination.unwrap();
|
|
||||||
let register = Register::Value(Value::Concrete(ConcreteValue::string(argument_string)));
|
let register = Register::Value(Value::Concrete(ConcreteValue::string(argument_string)));
|
||||||
|
|
||||||
data.set_register(destination, register);
|
data.set_register(destination, register);
|
||||||
|
64
dust-lang/src/native_function/thread.rs
Normal file
64
dust-lang/src/native_function/thread.rs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
use std::{
|
||||||
|
ops::Range,
|
||||||
|
thread::{Builder, JoinHandle},
|
||||||
|
};
|
||||||
|
|
||||||
|
use tracing::{Level, info, span};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
DustString,
|
||||||
|
vm::{Thread, ThreadData, get_next_action},
|
||||||
|
};
|
||||||
|
|
||||||
|
fn start_thread(data: &mut ThreadData, argument_range: Range<u8>) -> JoinHandle<()> {
|
||||||
|
let mut argument_range_iter = argument_range.into_iter();
|
||||||
|
let function_argument = {
|
||||||
|
loop {
|
||||||
|
let register_index = argument_range_iter
|
||||||
|
.next()
|
||||||
|
.unwrap_or_else(|| panic!("No argument was passed to \"spawn\""));
|
||||||
|
let value_option = data.open_register_allow_empty_unchecked(register_index);
|
||||||
|
|
||||||
|
if let Some(argument) = value_option {
|
||||||
|
break argument;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let function = function_argument.as_function().unwrap();
|
||||||
|
let prototype_index = function.prototype_index as usize;
|
||||||
|
let current_call = data.call_stack.last_unchecked();
|
||||||
|
let prototype = current_call.chunk.prototypes[prototype_index].clone();
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"Spawning thread for \"{}\"",
|
||||||
|
function
|
||||||
|
.name
|
||||||
|
.as_ref()
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_else(|| DustString::from("anonymous"))
|
||||||
|
);
|
||||||
|
|
||||||
|
let thread_name = prototype
|
||||||
|
.name
|
||||||
|
.as_ref()
|
||||||
|
.map(|name| name.to_string())
|
||||||
|
.unwrap_or_else(|| "anonymous".to_string());
|
||||||
|
let mut thread = Thread::new(prototype);
|
||||||
|
|
||||||
|
Builder::new()
|
||||||
|
.name(thread_name)
|
||||||
|
.spawn(move || {
|
||||||
|
let span = span!(Level::INFO, "Spawned thread");
|
||||||
|
let _enter = span.enter();
|
||||||
|
|
||||||
|
thread.run();
|
||||||
|
})
|
||||||
|
.expect("Critical VM Error: Failed to spawn thread")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spawn(data: &mut ThreadData, _: u8, argument_range: Range<u8>) -> bool {
|
||||||
|
let _ = start_thread(data, argument_range);
|
||||||
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
use std::fmt::{self, Debug, Display, Formatter};
|
use std::fmt::{self, Debug, Display, Formatter};
|
||||||
|
|
||||||
use crate::{vm::ThreadData, Type};
|
use crate::{Type, vm::ThreadData};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
@ -50,9 +50,9 @@ impl Value {
|
|||||||
Value::Concrete(ConcreteValue::String(string.into()))
|
Value::Concrete(ConcreteValue::String(string.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_boolean(&self) -> Option<&bool> {
|
pub fn as_boolean(&self) -> Option<bool> {
|
||||||
if let Value::Concrete(ConcreteValue::Boolean(value)) = self {
|
if let Value::Concrete(ConcreteValue::Boolean(boolean)) = self {
|
||||||
Some(value)
|
Some(*boolean)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -66,6 +66,14 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_integer(&self) -> Option<i64> {
|
||||||
|
if let Value::Concrete(ConcreteValue::Integer(integer)) = self {
|
||||||
|
Some(*integer)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn as_string(&self) -> Option<&DustString> {
|
pub fn as_string(&self) -> Option<&DustString> {
|
||||||
if let Value::Concrete(ConcreteValue::String(value)) = self {
|
if let Value::Concrete(ConcreteValue::String(value)) = self {
|
||||||
Some(value)
|
Some(value)
|
||||||
@ -74,6 +82,14 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_string(&self) -> bool {
|
||||||
|
matches!(self, Value::Concrete(ConcreteValue::String(_)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_function(&self) -> bool {
|
||||||
|
matches!(self, Value::Function(_))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn r#type(&self) -> Type {
|
pub fn r#type(&self) -> Type {
|
||||||
match self {
|
match self {
|
||||||
Value::Concrete(concrete_value) => concrete_value.r#type(),
|
Value::Concrete(concrete_value) => concrete_value.r#type(),
|
||||||
|
@ -1,29 +1,34 @@
|
|||||||
use std::fmt::{self, Debug, Display, Formatter};
|
use std::{
|
||||||
|
fmt::{self, Debug, Display, Formatter},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{Chunk, DustString};
|
use crate::{Chunk, DustString};
|
||||||
|
|
||||||
use super::Register;
|
use super::Register;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FunctionCall<'a> {
|
pub struct FunctionCall {
|
||||||
pub chunk: &'a Chunk,
|
pub chunk: Arc<Chunk>,
|
||||||
pub ip: usize,
|
pub ip: usize,
|
||||||
pub return_register: u8,
|
pub return_register: u8,
|
||||||
pub registers: Vec<Register>,
|
pub registers: Vec<Register>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FunctionCall<'a> {
|
impl FunctionCall {
|
||||||
pub fn new(chunk: &'a Chunk, return_register: u8) -> Self {
|
pub fn new(chunk: Arc<Chunk>, return_register: u8) -> Self {
|
||||||
|
let register_count = chunk.register_count;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
chunk,
|
chunk,
|
||||||
ip: 0,
|
ip: 0,
|
||||||
return_register,
|
return_register,
|
||||||
registers: vec![Register::Empty; chunk.register_count],
|
registers: vec![Register::Empty; register_count],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for FunctionCall<'_> {
|
impl Display for FunctionCall {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
|
@ -6,19 +6,20 @@ mod thread;
|
|||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fmt::{self, Debug, Display, Formatter},
|
fmt::{self, Debug, Display, Formatter},
|
||||||
sync::mpsc,
|
sync::Arc,
|
||||||
thread::spawn,
|
thread::Builder,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use function_call::FunctionCall;
|
pub use function_call::FunctionCall;
|
||||||
pub(crate) use run_action::get_next_action;
|
|
||||||
pub use run_action::RunAction;
|
pub use run_action::RunAction;
|
||||||
|
pub(crate) use run_action::get_next_action;
|
||||||
pub use stack::Stack;
|
pub use stack::Stack;
|
||||||
pub use thread::{Thread, ThreadData};
|
pub use thread::{Thread, ThreadData};
|
||||||
|
|
||||||
use tracing::{span, Level};
|
use crossbeam_channel::bounded;
|
||||||
|
use tracing::{Level, span};
|
||||||
|
|
||||||
use crate::{compile, Chunk, DustError, Value};
|
use crate::{Chunk, DustError, Value, compile};
|
||||||
|
|
||||||
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
||||||
let chunk = compile(source)?;
|
let chunk = compile(source)?;
|
||||||
@ -28,41 +29,31 @@ pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Vm {
|
pub struct Vm {
|
||||||
threads: Vec<Thread>,
|
main_chunk: Chunk,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Vm {
|
impl Vm {
|
||||||
pub fn new(chunk: Chunk) -> Self {
|
pub fn new(main_chunk: Chunk) -> Self {
|
||||||
let threads = vec![Thread::new(chunk)];
|
Self { main_chunk }
|
||||||
|
|
||||||
debug_assert_eq!(1, threads.capacity());
|
|
||||||
|
|
||||||
Self { threads }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(mut self) -> Option<Value> {
|
pub fn run(self) -> Option<Value> {
|
||||||
let span = span!(Level::INFO, "Run");
|
let span = span!(Level::INFO, "Run");
|
||||||
let _enter = span.enter();
|
let _enter = span.enter();
|
||||||
|
let thread_name = self
|
||||||
|
.main_chunk
|
||||||
|
.name
|
||||||
|
.as_ref()
|
||||||
|
.map(|name| name.to_string())
|
||||||
|
.unwrap_or_else(|| "anonymous".to_string());
|
||||||
|
let mut main_thread = Thread::new(Arc::new(self.main_chunk));
|
||||||
|
let (tx, rx) = bounded(1);
|
||||||
|
let _ = Builder::new().name(thread_name).spawn(move || {
|
||||||
|
let value_option = main_thread.run();
|
||||||
|
let _ = tx.send(value_option);
|
||||||
|
});
|
||||||
|
|
||||||
if self.threads.len() == 1 {
|
rx.recv().unwrap()
|
||||||
return self.threads[0].run();
|
|
||||||
}
|
|
||||||
|
|
||||||
let (tx, rx) = mpsc::channel();
|
|
||||||
|
|
||||||
for mut thread in self.threads {
|
|
||||||
let tx = tx.clone();
|
|
||||||
|
|
||||||
spawn(move || {
|
|
||||||
let return_value = thread.run();
|
|
||||||
|
|
||||||
if let Some(value) = return_value {
|
|
||||||
tx.send(value).unwrap();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
rx.into_iter().last()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
use tracing::trace;
|
use tracing::trace;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
AbstractList, Argument, ConcreteValue, Instruction, Type, Value,
|
||||||
instruction::{
|
instruction::{
|
||||||
Add, Call, CallNative, Close, Divide, Equal, GetLocal, Jump, Less, LessEqual, LoadBoolean,
|
Add, Call, CallNative, Close, Divide, Equal, GetLocal, Jump, Less, LessEqual, LoadBoolean,
|
||||||
LoadConstant, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not, Point,
|
LoadConstant, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not, Point,
|
||||||
Return, SetLocal, Subtract, Test, TestSet,
|
Return, SetLocal, Subtract, Test, TestSet,
|
||||||
},
|
},
|
||||||
vm::FunctionCall,
|
vm::FunctionCall,
|
||||||
AbstractList, Argument, ConcreteValue, Instruction, Type, Value,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{thread::ThreadData, Pointer, Register};
|
use super::{Pointer, Register, thread::ThreadData};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct RunAction {
|
pub struct RunAction {
|
||||||
@ -525,14 +525,14 @@ pub fn call(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|||||||
let current_call = data.call_stack.last_unchecked();
|
let current_call = data.call_stack.last_unchecked();
|
||||||
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_call.chunk
|
current_call.chunk.clone()
|
||||||
} else {
|
} else {
|
||||||
let function = data
|
let function = data
|
||||||
.open_register_unchecked(function_register)
|
.open_register_unchecked(function_register)
|
||||||
.as_function()
|
.as_function()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
¤t_call.chunk.prototypes[function.prototype_index as usize]
|
current_call.chunk.prototypes[function.prototype_index as usize].clone()
|
||||||
};
|
};
|
||||||
let mut next_call = FunctionCall::new(prototype, return_register);
|
let mut next_call = FunctionCall::new(prototype, return_register);
|
||||||
let mut argument_index = 0;
|
let mut argument_index = 0;
|
||||||
@ -565,7 +565,7 @@ pub fn call_native(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|||||||
let first_argument_index = destination - argument_count;
|
let first_argument_index = destination - argument_count;
|
||||||
let argument_range = first_argument_index..destination;
|
let argument_range = first_argument_index..destination;
|
||||||
|
|
||||||
function.call(data, Some(destination), argument_range)
|
function.call(data, destination, argument_range)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn r#return(instruction: Instruction, data: &mut ThreadData) -> bool {
|
pub fn r#return(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||||
|
@ -124,7 +124,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 -----")?;
|
||||||
|
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
use std::mem::replace;
|
use std::{mem::replace, sync::Arc, thread::JoinHandle};
|
||||||
|
|
||||||
use tracing::{info, trace};
|
use tracing::{info, trace};
|
||||||
|
|
||||||
use crate::{vm::FunctionCall, Argument, Chunk, DustString, Span, Value};
|
use crate::{Argument, Chunk, DustString, Span, Value, vm::FunctionCall};
|
||||||
|
|
||||||
use super::{Pointer, Register, RunAction, Stack};
|
use super::{Pointer, Register, RunAction, Stack};
|
||||||
|
|
||||||
pub struct Thread {
|
pub struct Thread {
|
||||||
chunk: Chunk,
|
chunk: Arc<Chunk>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Thread {
|
impl Thread {
|
||||||
pub fn new(chunk: Chunk) -> Self {
|
pub fn new(chunk: Arc<Chunk>) -> Self {
|
||||||
Thread { chunk }
|
Thread { chunk }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ 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 main_call = FunctionCall::new(&self.chunk, 0);
|
let main_call = FunctionCall::new(self.chunk.clone(), 0);
|
||||||
|
|
||||||
call_stack.push(main_call);
|
call_stack.push(main_call);
|
||||||
|
|
||||||
@ -34,6 +34,7 @@ impl Thread {
|
|||||||
call_stack,
|
call_stack,
|
||||||
next_action: first_action,
|
next_action: first_action,
|
||||||
return_value_index: None,
|
return_value_index: None,
|
||||||
|
spawned_threads: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@ -45,7 +46,7 @@ impl Thread {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if should_end {
|
if should_end {
|
||||||
let return_value = if let Some(register_index) = thread_data.return_value_index {
|
let value_option = if let Some(register_index) = thread_data.return_value_index {
|
||||||
let value =
|
let value =
|
||||||
thread_data.empty_register_or_clone_constant_unchecked(register_index);
|
thread_data.empty_register_or_clone_constant_unchecked(register_index);
|
||||||
|
|
||||||
@ -54,20 +55,28 @@ impl Thread {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
return return_value;
|
thread_data
|
||||||
|
.spawned_threads
|
||||||
|
.into_iter()
|
||||||
|
.for_each(|join_handle| {
|
||||||
|
let _ = join_handle.join();
|
||||||
|
});
|
||||||
|
|
||||||
|
return value_option;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ThreadData<'a> {
|
pub struct ThreadData {
|
||||||
pub call_stack: Stack<FunctionCall<'a>>,
|
pub call_stack: Stack<FunctionCall>,
|
||||||
pub next_action: RunAction,
|
pub next_action: RunAction,
|
||||||
pub return_value_index: Option<u8>,
|
pub return_value_index: Option<u8>,
|
||||||
|
pub spawned_threads: Vec<JoinHandle<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ThreadData<'_> {
|
impl ThreadData {
|
||||||
pub fn current_position(&self) -> Span {
|
pub fn current_position(&self) -> Span {
|
||||||
let current_call = self.call_stack.last_unchecked();
|
let current_call = self.call_stack.last_unchecked();
|
||||||
|
|
||||||
@ -75,7 +84,7 @@ impl ThreadData<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn follow_pointer_unchecked(&self, pointer: Pointer) -> &Value {
|
pub(crate) fn follow_pointer_unchecked(&self, pointer: Pointer) -> &Value {
|
||||||
trace!("Follow pointer {pointer}");
|
trace!("Follow {pointer}");
|
||||||
|
|
||||||
match pointer {
|
match pointer {
|
||||||
Pointer::Register(register_index) => self.open_register_unchecked(register_index),
|
Pointer::Register(register_index) => self.open_register_unchecked(register_index),
|
||||||
@ -97,7 +106,7 @@ impl ThreadData<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_register_unchecked(&self, register_index: u8) -> &Register {
|
pub fn get_register_unchecked(&self, register_index: u8) -> &Register {
|
||||||
trace!("Get register R{register_index}");
|
trace!("Get R{register_index}");
|
||||||
|
|
||||||
let register_index = register_index as usize;
|
let register_index = register_index as usize;
|
||||||
|
|
||||||
@ -133,37 +142,25 @@ impl ThreadData<'_> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
trace!("Open R{register_index} to {register}");
|
||||||
|
|
||||||
match register {
|
match register {
|
||||||
Register::Value(value) => {
|
Register::Value(value) => value,
|
||||||
trace!("Register R{register_index} opened to value {value}");
|
Register::Pointer(pointer) => self.follow_pointer_unchecked(*pointer),
|
||||||
|
|
||||||
value
|
|
||||||
}
|
|
||||||
Register::Pointer(pointer) => {
|
|
||||||
trace!("Open register R{register_index} opened to pointer {pointer}");
|
|
||||||
|
|
||||||
self.follow_pointer_unchecked(*pointer)
|
|
||||||
}
|
|
||||||
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_register_allow_empty_unchecked(&self, register_index: u8) -> Option<&Value> {
|
pub fn open_register_allow_empty_unchecked(&self, register_index: u8) -> Option<&Value> {
|
||||||
trace!("Open register R{register_index}");
|
trace!("Open R{register_index}");
|
||||||
|
|
||||||
let register = self.get_register_unchecked(register_index);
|
let register = self.get_register_unchecked(register_index);
|
||||||
|
|
||||||
|
trace!("Open R{register_index} to {register}");
|
||||||
|
|
||||||
match register {
|
match register {
|
||||||
Register::Value(value) => {
|
Register::Value(value) => Some(value),
|
||||||
trace!("Register R{register_index} openned to value {value}");
|
Register::Pointer(pointer) => Some(self.follow_pointer_unchecked(*pointer)),
|
||||||
|
|
||||||
Some(value)
|
|
||||||
}
|
|
||||||
Register::Pointer(pointer) => {
|
|
||||||
trace!("Open register R{register_index} openned to pointer {pointer}");
|
|
||||||
|
|
||||||
Some(self.follow_pointer_unchecked(*pointer))
|
|
||||||
}
|
|
||||||
Register::Empty => None,
|
Register::Empty => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -260,7 +257,7 @@ impl ThreadData<'_> {
|
|||||||
|
|
||||||
pub fn get_local_register(&self, local_index: u8) -> u8 {
|
pub fn get_local_register(&self, local_index: u8) -> u8 {
|
||||||
let local_index = local_index as usize;
|
let local_index = local_index as usize;
|
||||||
let chunk = self.call_stack.last_unchecked().chunk;
|
let chunk = &self.call_stack.last_unchecked().chunk;
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
local_index < chunk.locals.len(),
|
local_index < chunk.locals.len(),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user