From de426d814adde9db4cc114a05f00ed9479cadcbb Mon Sep 17 00:00:00 2001 From: Jeff Date: Thu, 9 Jan 2025 19:56:36 -0500 Subject: [PATCH 1/7] Add thread spawning and a random number generator --- Cargo.lock | 10 ++++ dust-lang/Cargo.toml | 7 ++- dust-lang/benches/threads.rs | 31 ++++++++++++ dust-lang/src/chunk/mod.rs | 7 +-- dust-lang/src/compiler/mod.rs | 6 +-- dust-lang/src/lexer.rs | 60 +++++++++++------------ dust-lang/src/native_function/assert.rs | 2 +- dust-lang/src/native_function/io.rs | 19 ++------ dust-lang/src/native_function/mod.rs | 43 ++++++++++++++-- dust-lang/src/native_function/random.rs | 40 +++++++++++++++ dust-lang/src/native_function/string.rs | 9 +--- dust-lang/src/native_function/thread.rs | 64 ++++++++++++++++++++++++ dust-lang/src/value/mod.rs | 24 +++++++-- dust-lang/src/vm/function_call.rs | 19 +++++--- dust-lang/src/vm/mod.rs | 55 +++++++++------------ dust-lang/src/vm/run_action.rs | 10 ++-- dust-lang/src/vm/stack.rs | 2 +- dust-lang/src/vm/thread.rs | 65 ++++++++++++------------- 18 files changed, 326 insertions(+), 147 deletions(-) create mode 100644 dust-lang/benches/threads.rs create mode 100644 dust-lang/src/native_function/random.rs create mode 100644 dust-lang/src/native_function/thread.rs diff --git a/Cargo.lock b/Cargo.lock index 6811bcd..f62c495 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -269,6 +269,15 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "crossbeam-deque" version = "0.8.5" @@ -335,6 +344,7 @@ dependencies = [ "annotate-snippets", "colored", "criterion", + "crossbeam-channel", "getrandom", "rand", "serde", diff --git a/dust-lang/Cargo.toml b/dust-lang/Cargo.toml index bef5fb8..96fedb5 100644 --- a/dust-lang/Cargo.toml +++ b/dust-lang/Cargo.toml @@ -12,7 +12,7 @@ version.workspace = true annotate-snippets = "0.11.4" colored = "2.1.0" rand = "0.8.5" -serde = { version = "1.0.203", features = ["derive"] } +serde = { version = "1.0.203", features = ["derive", "rc"] } serde_json = "1.0.117" getrandom = { version = "0.2", features = [ "js", @@ -21,6 +21,7 @@ smartstring = { version = "1.0.1", features = [ "serde", ], default-features = false } tracing = "0.1.41" +crossbeam-channel = "0.5.14" [dev-dependencies] criterion = { version = "0.3.4", features = ["html_reports"] } @@ -33,6 +34,10 @@ harness = false name = "fibonacci" harness = false +[[bench]] +name = "threads" +harness = false + [[test]] name = "logic_and" path = "tests/logic/and.rs" diff --git a/dust-lang/benches/threads.rs b/dust-lang/benches/threads.rs new file mode 100644 index 0000000..8c4ef2e --- /dev/null +++ b/dust-lang/benches/threads.rs @@ -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); diff --git a/dust-lang/src/chunk/mod.rs b/dust-lang/src/chunk/mod.rs index 07c7eae..d9a7364 100644 --- a/dust-lang/src/chunk/mod.rs +++ b/dust-lang/src/chunk/mod.rs @@ -23,6 +23,7 @@ pub use scope::Scope; use std::fmt::{self, Debug, Display, Formatter, Write as FmtWrite}; use std::io::Write; +use std::sync::Arc; use serde::{Deserialize, Serialize}; @@ -40,7 +41,7 @@ pub struct Chunk { pub(crate) positions: Vec, pub(crate) constants: Vec, pub(crate) locals: Vec, - pub(crate) prototypes: Vec, + pub(crate) prototypes: Vec>, pub(crate) register_count: usize, pub(crate) prototype_index: u8, @@ -55,7 +56,7 @@ impl Chunk { positions: impl Into>, constants: impl Into>, locals: impl Into>, - prototypes: Vec, + prototypes: impl IntoIterator, ) -> Self { Self { name, @@ -64,7 +65,7 @@ impl Chunk { positions: positions.into(), constants: constants.into(), locals: locals.into(), - prototypes, + prototypes: prototypes.into_iter().map(Arc::new).collect(), register_count: 0, prototype_index: 0, } diff --git a/dust-lang/src/compiler/mod.rs b/dust-lang/src/compiler/mod.rs index edbb040..270fc81 100644 --- a/dust-lang/src/compiler/mod.rs +++ b/dust-lang/src/compiler/mod.rs @@ -28,7 +28,7 @@ use parse_rule::{ParseRule, Precedence}; use tracing::{Level, debug, info, span}; 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; @@ -96,7 +96,7 @@ pub struct Compiler<'src> { /// Prototypes that have been compiled. These are assigned to the chunk when /// [`Compiler::finish`] is called. - prototypes: Vec, + prototypes: Vec>, /// Maximum stack size required by the chunk. This is assigned to the chunk when /// [`Compiler::finish`] is called. @@ -1645,7 +1645,7 @@ impl<'src> Compiler<'src> { let chunk = function_compiler.finish(); let destination = self.next_register(); - self.prototypes.push(chunk); + self.prototypes.push(Arc::new(chunk)); if let Some(identifier) = identifier { self.declare_local( diff --git a/dust-lang/src/lexer.rs b/dust-lang/src/lexer.rs index 0c09983..df03324 100644 --- a/dust-lang/src/lexer.rs +++ b/dust-lang/src/lexer.rs @@ -5,7 +5,7 @@ //! - [`Lexer`], which lexes the input a token at a time 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. /// @@ -83,7 +83,7 @@ impl<'src> Lexer<'src> { self.skip_whitespace(); 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)? } 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> { - lexer: LexerFn<'src>, + lex_action: LexAction<'src>, } impl From<&char> for LexRule<'_> { fn from(char: &char) -> Self { match char { '0'..='9' => LexRule { - lexer: Lexer::lex_numeric, + lex_action: Lexer::lex_numeric, }, char if char.is_alphabetic() => LexRule { - lexer: Lexer::lex_keyword_or_identifier, + lex_action: Lexer::lex_keyword_or_identifier, }, '"' => LexRule { - lexer: Lexer::lex_string, + lex_action: Lexer::lex_string, }, '\'' => LexRule { - lexer: Lexer::lex_char, + lex_action: Lexer::lex_char, }, '+' => LexRule { - lexer: Lexer::lex_plus, + lex_action: Lexer::lex_plus, }, '-' => LexRule { - lexer: Lexer::lex_minus, + lex_action: Lexer::lex_minus, }, '*' => LexRule { - lexer: Lexer::lex_star, + lex_action: Lexer::lex_star, }, '/' => LexRule { - lexer: Lexer::lex_slash, + lex_action: Lexer::lex_slash, }, '%' => LexRule { - lexer: Lexer::lex_percent, + lex_action: Lexer::lex_percent, }, '!' => LexRule { - lexer: Lexer::lex_exclamation_mark, + lex_action: Lexer::lex_exclamation_mark, }, '=' => LexRule { - lexer: Lexer::lex_equal, + lex_action: Lexer::lex_equal, }, '<' => LexRule { - lexer: Lexer::lex_less_than, + lex_action: Lexer::lex_less_than, }, '>' => LexRule { - lexer: Lexer::lex_greater_than, + lex_action: Lexer::lex_greater_than, }, '&' => LexRule { - lexer: Lexer::lex_ampersand, + lex_action: Lexer::lex_ampersand, }, '|' => LexRule { - lexer: Lexer::lex_pipe, + lex_action: Lexer::lex_pipe, }, '(' => LexRule { - lexer: Lexer::lex_left_parenthesis, + lex_action: Lexer::lex_left_parenthesis, }, ')' => LexRule { - lexer: Lexer::lex_right_parenthesis, + lex_action: Lexer::lex_right_parenthesis, }, '[' => LexRule { - lexer: Lexer::lex_left_bracket, + lex_action: Lexer::lex_left_bracket, }, ']' => LexRule { - lexer: Lexer::lex_right_bracket, + lex_action: Lexer::lex_right_bracket, }, '{' => LexRule { - lexer: Lexer::lex_left_brace, + lex_action: Lexer::lex_left_brace, }, '}' => LexRule { - lexer: Lexer::lex_right_brace, + lex_action: Lexer::lex_right_brace, }, ';' => LexRule { - lexer: Lexer::lex_semicolon, + lex_action: Lexer::lex_semicolon, }, ':' => LexRule { - lexer: Lexer::lex_colon, + lex_action: Lexer::lex_colon, }, ',' => LexRule { - lexer: Lexer::lex_comma, + lex_action: Lexer::lex_comma, }, '.' => LexRule { - lexer: Lexer::lex_dot, + lex_action: Lexer::lex_dot, }, _ => LexRule { - lexer: Lexer::lex_unexpected, + lex_action: Lexer::lex_unexpected, }, } } diff --git a/dust-lang/src/native_function/assert.rs b/dust-lang/src/native_function/assert.rs index b2704a4..b748ee7 100644 --- a/dust-lang/src/native_function/assert.rs +++ b/dust-lang/src/native_function/assert.rs @@ -2,7 +2,7 @@ use std::{ops::Range, panic}; use crate::vm::ThreadData; -pub fn panic(data: &mut ThreadData, _: Option, argument_range: Range) -> bool { +pub fn panic(data: &mut ThreadData, _: u8, argument_range: Range) -> bool { let position = data.current_position(); let mut message = format!("Dust panic at {position}!"); diff --git a/dust-lang/src/native_function/io.rs b/dust-lang/src/native_function/io.rs index 0267009..696e5d5 100644 --- a/dust-lang/src/native_function/io.rs +++ b/dust-lang/src/native_function/io.rs @@ -1,17 +1,12 @@ -use std::io::{stdin, stdout, Write}; +use std::io::{Write, stdin, stdout}; use std::ops::Range; use crate::{ - vm::{get_next_action, Register, ThreadData}, ConcreteValue, Value, + vm::{Register, ThreadData, get_next_action}, }; -pub fn read_line( - data: &mut ThreadData, - destination: Option, - _argument_range: Range, -) -> bool { - let destination = destination.unwrap(); +pub fn read_line(data: &mut ThreadData, destination: u8, _argument_range: Range) -> bool { let mut buffer = String::new(); if stdin().read_line(&mut buffer).is_ok() { @@ -29,7 +24,7 @@ pub fn read_line( false } -pub fn write(data: &mut ThreadData, _destination: Option, argument_range: Range) -> bool { +pub fn write(data: &mut ThreadData, _: u8, argument_range: Range) -> bool { let mut stdout = stdout(); for register_index in argument_range { @@ -45,11 +40,7 @@ pub fn write(data: &mut ThreadData, _destination: Option, argument_range: Ra false } -pub fn write_line( - data: &mut ThreadData, - _destination: Option, - argument_range: Range, -) -> bool { +pub fn write_line(data: &mut ThreadData, _: u8, argument_range: Range) -> bool { let mut stdout = stdout().lock(); for register_index in argument_range { diff --git a/dust-lang/src/native_function/mod.rs b/dust-lang/src/native_function/mod.rs index e56f54a..4917843 100644 --- a/dust-lang/src/native_function/mod.rs +++ b/dust-lang/src/native_function/mod.rs @@ -4,7 +4,9 @@ //! itself or that are more efficient to implement in Rust. mod assert; mod io; +mod random; mod string; +mod thread; use std::{ fmt::{self, Display, Formatter}, @@ -15,7 +17,7 @@ use std::{ 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 { ($(($name:ident, $bytes:literal, $str:expr, $type:expr, $function:expr)),*) => { @@ -33,7 +35,7 @@ macro_rules! define_native_function { pub fn call( &self, data: &mut ThreadData, - destination: Option, + destination: u8, argument_range: Range, ) -> bool { match self { @@ -244,11 +246,42 @@ define_native_function! { return_type: Type::None }, io::write_line - ) + ), // // 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)] diff --git a/dust-lang/src/native_function/random.rs b/dust-lang/src/native_function/random.rs new file mode 100644 index 0000000..f743b0b --- /dev/null +++ b/dust-lang/src/native_function/random.rs @@ -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) -> 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 +} diff --git a/dust-lang/src/native_function/string.rs b/dust-lang/src/native_function/string.rs index f368185..8d03c96 100644 --- a/dust-lang/src/native_function/string.rs +++ b/dust-lang/src/native_function/string.rs @@ -1,18 +1,13 @@ use std::ops::Range; use crate::{ - vm::{get_next_action, Register, ThreadData}, ConcreteValue, Value, + vm::{Register, ThreadData, get_next_action}, }; -pub fn to_string( - data: &mut ThreadData, - destination: Option, - argument_range: Range, -) -> bool { +pub fn to_string(data: &mut ThreadData, destination: u8, argument_range: Range) -> bool { let argument_value = data.open_register_unchecked(argument_range.start); let argument_string = argument_value.display(data); - let destination = destination.unwrap(); let register = Register::Value(Value::Concrete(ConcreteValue::string(argument_string))); data.set_register(destination, register); diff --git a/dust-lang/src/native_function/thread.rs b/dust-lang/src/native_function/thread.rs new file mode 100644 index 0000000..4786065 --- /dev/null +++ b/dust-lang/src/native_function/thread.rs @@ -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) -> 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) -> bool { + let _ = start_thread(data, argument_range); + data.next_action = get_next_action(data); + + false +} diff --git a/dust-lang/src/value/mod.rs b/dust-lang/src/value/mod.rs index 6da63fb..6a37fbf 100644 --- a/dust-lang/src/value/mod.rs +++ b/dust-lang/src/value/mod.rs @@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize}; use std::fmt::{self, Debug, Display, Formatter}; -use crate::{vm::ThreadData, Type}; +use crate::{Type, vm::ThreadData}; #[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)] pub enum Value { @@ -50,9 +50,9 @@ impl Value { Value::Concrete(ConcreteValue::String(string.into())) } - pub fn as_boolean(&self) -> Option<&bool> { - if let Value::Concrete(ConcreteValue::Boolean(value)) = self { - Some(value) + pub fn as_boolean(&self) -> Option { + if let Value::Concrete(ConcreteValue::Boolean(boolean)) = self { + Some(*boolean) } else { None } @@ -66,6 +66,14 @@ impl Value { } } + pub fn as_integer(&self) -> Option { + if let Value::Concrete(ConcreteValue::Integer(integer)) = self { + Some(*integer) + } else { + None + } + } + pub fn as_string(&self) -> Option<&DustString> { if let Value::Concrete(ConcreteValue::String(value)) = self { 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 { match self { Value::Concrete(concrete_value) => concrete_value.r#type(), diff --git a/dust-lang/src/vm/function_call.rs b/dust-lang/src/vm/function_call.rs index 86a0991..c9cbe84 100644 --- a/dust-lang/src/vm/function_call.rs +++ b/dust-lang/src/vm/function_call.rs @@ -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 super::Register; #[derive(Debug)] -pub struct FunctionCall<'a> { - pub chunk: &'a Chunk, +pub struct FunctionCall { + pub chunk: Arc, pub ip: usize, pub return_register: u8, pub registers: Vec, } -impl<'a> FunctionCall<'a> { - pub fn new(chunk: &'a Chunk, return_register: u8) -> Self { +impl FunctionCall { + pub fn new(chunk: Arc, return_register: u8) -> Self { + let register_count = chunk.register_count; + Self { chunk, ip: 0, 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 { write!( f, diff --git a/dust-lang/src/vm/mod.rs b/dust-lang/src/vm/mod.rs index 4f16b3e..9e0ec9b 100644 --- a/dust-lang/src/vm/mod.rs +++ b/dust-lang/src/vm/mod.rs @@ -6,19 +6,20 @@ mod thread; use std::{ fmt::{self, Debug, Display, Formatter}, - sync::mpsc, - thread::spawn, + sync::Arc, + thread::Builder, }; pub use function_call::FunctionCall; -pub(crate) use run_action::get_next_action; pub use run_action::RunAction; +pub(crate) use run_action::get_next_action; pub use stack::Stack; 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, DustError> { let chunk = compile(source)?; @@ -28,41 +29,31 @@ pub fn run(source: &str) -> Result, DustError> { } pub struct Vm { - threads: Vec, + main_chunk: Chunk, } impl Vm { - pub fn new(chunk: Chunk) -> Self { - let threads = vec![Thread::new(chunk)]; - - debug_assert_eq!(1, threads.capacity()); - - Self { threads } + pub fn new(main_chunk: Chunk) -> Self { + Self { main_chunk } } - pub fn run(mut self) -> Option { + pub fn run(self) -> Option { let span = span!(Level::INFO, "Run"); 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 { - 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() + rx.recv().unwrap() } } diff --git a/dust-lang/src/vm/run_action.rs b/dust-lang/src/vm/run_action.rs index 3af2630..5bb6cb4 100644 --- a/dust-lang/src/vm/run_action.rs +++ b/dust-lang/src/vm/run_action.rs @@ -1,16 +1,16 @@ use tracing::trace; use crate::{ + AbstractList, Argument, ConcreteValue, Instruction, Type, Value, instruction::{ Add, Call, CallNative, Close, Divide, Equal, GetLocal, Jump, Less, LessEqual, LoadBoolean, LoadConstant, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not, Point, Return, SetLocal, Subtract, Test, TestSet, }, 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)] 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 first_argument_register = return_register - argument_count; let prototype = if is_recursive { - current_call.chunk + current_call.chunk.clone() } else { let function = data .open_register_unchecked(function_register) .as_function() .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 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 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 { diff --git a/dust-lang/src/vm/stack.rs b/dust-lang/src/vm/stack.rs index ac8d2e9..dd43253 100644 --- a/dust-lang/src/vm/stack.rs +++ b/dust-lang/src/vm/stack.rs @@ -124,7 +124,7 @@ impl Debug for Stack { } } -impl Display for Stack> { +impl Display for Stack { fn fmt(&self, f: &mut Formatter) -> fmt::Result { writeln!(f, "----- DUST CALL STACK -----")?; diff --git a/dust-lang/src/vm/thread.rs b/dust-lang/src/vm/thread.rs index 8152a5b..35c9811 100644 --- a/dust-lang/src/vm/thread.rs +++ b/dust-lang/src/vm/thread.rs @@ -1,17 +1,17 @@ -use std::mem::replace; +use std::{mem::replace, sync::Arc, thread::JoinHandle}; 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}; pub struct Thread { - chunk: Chunk, + chunk: Arc, } impl Thread { - pub fn new(chunk: Chunk) -> Self { + pub fn new(chunk: Arc) -> Self { Thread { chunk } } @@ -25,7 +25,7 @@ impl Thread { ); 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); @@ -34,6 +34,7 @@ impl Thread { call_stack, next_action: first_action, return_value_index: None, + spawned_threads: Vec::new(), }; loop { @@ -45,7 +46,7 @@ impl Thread { ); 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 = thread_data.empty_register_or_clone_constant_unchecked(register_index); @@ -54,20 +55,28 @@ impl Thread { None }; - return return_value; + thread_data + .spawned_threads + .into_iter() + .for_each(|join_handle| { + let _ = join_handle.join(); + }); + + return value_option; } } } } #[derive(Debug)] -pub struct ThreadData<'a> { - pub call_stack: Stack>, +pub struct ThreadData { + pub call_stack: Stack, pub next_action: RunAction, pub return_value_index: Option, + pub spawned_threads: Vec>, } -impl ThreadData<'_> { +impl ThreadData { pub fn current_position(&self) -> Span { let current_call = self.call_stack.last_unchecked(); @@ -75,7 +84,7 @@ impl ThreadData<'_> { } pub(crate) fn follow_pointer_unchecked(&self, pointer: Pointer) -> &Value { - trace!("Follow pointer {pointer}"); + trace!("Follow {pointer}"); match pointer { 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 { - trace!("Get register R{register_index}"); + trace!("Get R{register_index}"); let register_index = register_index as usize; @@ -133,37 +142,25 @@ impl ThreadData<'_> { } }; + trace!("Open R{register_index} to {register}"); + match register { - Register::Value(value) => { - trace!("Register R{register_index} opened to value {value}"); - - value - } - Register::Pointer(pointer) => { - trace!("Open register R{register_index} opened to pointer {pointer}"); - - self.follow_pointer_unchecked(*pointer) - } + Register::Value(value) => value, + Register::Pointer(pointer) => self.follow_pointer_unchecked(*pointer), Register::Empty => panic!("VM Error: Register {register_index} is empty"), } } 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); + trace!("Open R{register_index} to {register}"); + match register { - Register::Value(value) => { - trace!("Register R{register_index} openned to value {value}"); - - Some(value) - } - Register::Pointer(pointer) => { - trace!("Open register R{register_index} openned to pointer {pointer}"); - - Some(self.follow_pointer_unchecked(*pointer)) - } + Register::Value(value) => Some(value), + Register::Pointer(pointer) => Some(self.follow_pointer_unchecked(*pointer)), Register::Empty => None, } } @@ -260,7 +257,7 @@ impl ThreadData<'_> { pub fn get_local_register(&self, local_index: u8) -> u8 { let local_index = local_index as usize; - let chunk = self.call_stack.last_unchecked().chunk; + let chunk = &self.call_stack.last_unchecked().chunk; assert!( local_index < chunk.locals.len(), From 61f4093da0f43f10ebfc3b6ca5ddb41418d66230 Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 10 Jan 2025 12:54:33 -0500 Subject: [PATCH 2/7] Edit README; Begin 64-bit instruction set --- README.md | 223 ++++--------------------- dust-lang/src/instruction/mod.rs | 73 ++++---- dust-lang/src/instruction/type_code.rs | 10 ++ 3 files changed, 81 insertions(+), 225 deletions(-) create mode 100644 dust-lang/src/instruction/type_code.rs diff --git a/README.md b/README.md index b4cdfa4..1b15a6a 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,9 @@ -# The Dust Programming Language +# ✭ Dust Programming Language -A **fast**, **safe** and **easy to use** language for general-purpose programming. - -Dust is **statically typed** to ensure that each program is valid before it is run. Compiling is -fast due to the purpose-built lexer and parser. Execution is fast because Dust uses a custom -bytecode that runs in a multi-threaded VM. Dust combines compile-time safety guarantees and -optimizations with negligible compile times and satisfying runtime speed to deliver a unique set of -features. It offers the best qualities of two disparate categories of programming language: the -highly optimized but slow-to-compile languages like Rust and C++ and the quick-to-start but often -slow and error-prone languages like Python and JavaScript. - -Dust's syntax, safety features and evaluation model are based on Rust. Its instruction set, -optimization strategies and virtual machine are based on Lua. Unlike Rust and other languages that -compile to machine code, Dust has a very low time to execution. Unlike Lua and most other -interpreted languages, Dust enforces static typing to improve clarity and prevent bugs. - -**Dust is under active development and is not yet ready for general use.** +**Fast**, **safe** and **easy-to-use** general-purpose programming language. ```rust -// "Hello, world" using Dust's built-in I/O functions +// An interactive "Hello, world" using Dust's built-in I/O functions write_line("Enter your name...") let name = read_line() @@ -38,7 +23,23 @@ fn fib (n: int) -> int { write_line(fib(25)) ``` -## Goals +## 🌣 Highlights + +- Easy to read and write +- Single-pass, self-optimizing compiler +- Static typing with extensive type inference +- Multi-threaded register-based virtual machine with concurrent garbage collection +- Beautiful, helpful error messages from the compiler +- Safe execution, runtime errors are treated as bugs + +## 🛈 Overview + +Dust's syntax, safety features and evaluation model are based on Rust. Its instruction set +and optimization strategies are based on Lua. Unlike Rust and other languages that compile to +machine code, Dust has a very low time to execution. Unlike Lua and most other interpreted +languages, Dust enforces static typing to improve clarity and prevent bugs. + +### Project Goals This project's goal is to deliver a language with features that stand out due to a combination of design choices and a high-quality implementation. As mentioned in the first sentence, Dust's general @@ -56,10 +57,12 @@ aspirations are to be **fast**, **safe** and **easy**. superior development experience despite some additional constraints. Like any good statically typed language, users should feel confident in the type-consistency of their code and not want to go back to a dynamically typed language. + - **Null-Free** Dust has no "null" or "undefined" values. All values are initialized and have a + type. This eliminates a whole class of bugs that are common in other languages. - **Memory Safety** Dust should be free of memory bugs. Being implemented in Rust makes this easy but, to accommodate long-running programs, Dust still requires a memory management strategy. - Dust's design is to use a separate thread for garbage collection, allowing the main thread to - continue executing code while the garbage collector looks for unused memory. + Dust's design is to use a separate thread for garbage collection, allowing other threads to + continue executing instructions while the garbage collector looks for unused memory. - **Easy** - **Simple Syntax** Dust should be easier to learn than most programming languages. Its syntax should be familiar to users of other C-like languages to the point that even a new user can read @@ -72,178 +75,22 @@ aspirations are to be **fast**, **safe** and **easy**. - **Relevant Documentation** Users should have the resources they need to learn Dust and write code in it. They should know where to look for answers and how to reach out for help. -## Language Overview +### Author -This is a quick overview of Dust's syntax features. It skips over the aspects that are familiar to -most programmers such as creating variables, using binary operators and printing to the console. -Eventually there should be a complete reference for the syntax. +I'm Jeff and I started this project to learn more about programming languages by implementing a +simple expession evaluator. Initially, the project used an external parser and a tree-walking +interpreter. After several books, papers and a lot of experimentation, Dust has evolved to an +ambitious project that aims to implement lucrative features with a high-quality implementation that +competes with established languages. -### Syntax and Evaluation +## Usage -Dust belongs to the C-like family of languages[^5], with an imperative syntax that will be familiar -to many programmers. Dust code looks a lot like Ruby, JavaScript, TypeScript and other members of -the family but Rust is its primary point of reference for syntax. Rust was chosen as a syntax model -because its imperative code is *obvious by design* and *widely familiar*. Those qualities are -aligned with Dust's emphasis on usability. +**Dust is under active development and is not yet ready for general use.** -However, some differences exist. Dust *evaluates* all the code in the file while Rust only initiates -from a "main" function. Dust's execution model is more like one found in a scripting language. If we -put `42 + 42 == 84` into a file and run it, it will return `true` because the outer context is, in a -sense, the "main" function. +## Installation -So while the syntax is by no means compatible, it is superficially similar, even to the point that -syntax highlighting for Rust code works well with Dust code. This is not a design goal but a happy -coincidence. - -### Statements and Expressions - -Dust is composed of statements and expressions. If a statement ends in an expression without a -trailing semicolon, the statement evaluates to the value produced by that expression. However, if -the expression's value is suppressed with a semicolon, the statement does not evaluate to a value. -This is identical to Rust's evaluation model. That means that the following code will not compile: - -```rust -// !!! Compile Error !!! -let a = { 40 + 2; } -``` - -The `a` variable is assigned to the value produced by a block. The block contains an expression that -is suppressed by a semicolon, so the block does not evaluate to a value. Therefore, the `a` variable -would have to be uninitialized (which Dust does not allow) or result in a runtime error (which Dust -avoids at all costs). We can fix this code by moving the semicolon to the end of the block. In this -position it suppresses the value of the entire `let` statement. As we saw above, a `let` statement -never evaluates to a value, so the semicolon has no effect on the program's behavior and could be -omitted altogether. - -```rust -let a = { 40 + 2 }; // This is fine -let a = { 40 + 2 } // This is also fine -``` - -Only the final expression in a block is returned. When a `let` statement is combined with an -`if/else` statement, the program can perform conditional side effects before assigning the variable. - -```rust -let random: int = random(0..100) -let is_even = if random == 99 { - write_line("We got a 99!") - - false -} else { - random % 2 == 0 -} - -is_even -``` - -If the above example were passed to Dust as a complete program, it would return a boolean value and -might print a message to the console (if the user is especially lucky). However, note that the -program could be modified to return no value by simply adding a semicolon at the end. - -Compared to JavaScript, Dust's evaluation model is more predictable, less error-prone and will never -trap the user into a frustating hunt for a missing semicolon. Compared to Rust, Dust's evaluation -model is more accomodating without sacrificing expressiveness. In Rust, semicolons are *required* -and *meaningful*, which provides excellent consistency but lacks flexibility. In JavaScript, -semicolons are *required* and *meaningless*, which is a source of confusion for many developers. - -Dust borrowed Rust's approach to semicolons and their effect on evaluation and relaxed the rules to -accommodate different styles of coding. Rust isn't designed for command lines or REPLs but Dust is -well-suited to those applications. Dust needs to work in a source file or in an ad-hoc one-liner -sent to the CLI. Thus, semicolons are optional in most cases. - -There are two things you need to know about semicolons in Dust: - -- Semicolons suppress the value of whatever they follow. The preceding statement or expression will - have the type `none` and will not evaluate to a value. -- If a semicolon does not change how the program runs, it is optional. - -This example shows three statements with semicolons. The compiler knows that a `let` statement -cannot produce a value and will always have the type `none`. Thanks to static typing, it also knows -that the `write_line` function has no return value so the function call also has the type `none`. -Therefore, these semicolons are optional. - -```rust -let a = 40; -let b = 2; - -write_line("The answer is ", a + b); -``` - -Removing the semicolons does not alter the execution pattern or the return value. - -```rust -let x = 10 -let y = 3 - -write_line("The remainder is ", x % y) -``` - -### Type System - -All variables have a type that is established when the variable is declared. This usually does not -require that the type be explicitly stated, Dust can infer the type from the value. - -The next example produces a compiler error because the `if` block evaluates to and `int` but the -`else` block evaluates to a `str`. Dust does not allow branches of the same `if/else` statement to -have different types. - -```rust -// !!! Compile Error !!! -let input = read_line() -let reward = if input == "42" { - write_line("You got it! Here's your reward.") - - 777 // <- This is an int -} else { - write_line(input, " is not the answer.") - - "777" // <- This is a string -} -``` - -### Basic Values - -Dust supports the following basic values: - -- Boolean: `true` or `false` -- Byte: An unsigned 8-bit integer -- Character: A Unicode scalar value -- Float: A 64-bit floating-point number -- Function: An executable chunk of code -- Integer: A signed 64-bit integer -- String: A UTF-8 encoded byte sequence - -Dust's "basic" values are conceptually similar because they are singular as opposed to composite. -Most of these values are stored on the stack but some are heap-allocated. A Dust string is a -sequence of bytes that are encoded in UTF-8. Even though it could be seen as a composite of byte -values, strings are considered "basic" because they are parsed directly from tokens and behave as -singular values. Shorter strings are stored on the stack while longer strings are heap-allocated. -Dust offers built-in native functions that can manipulate strings by accessing their bytes or -reading them as a sequence of characters. - -There is no `null` or `undefined` value in Dust. All values and variables must be initialized to one -of the supported value types. This eliminates a whole class of bugs that permeate many other -languages. - -> I call it my billion-dollar mistake. It was the invention of the null reference in 1965. -> - Tony Hoare - -Dust *does* have a `none` type, which should not be confused for being `null`-like. Like the `()` or -"unit" type in Rust, `none` exists as a type but not as a value. It indicates the lack of a value -from a function, expression or statement. A variable cannot be assigned to `none`. - -## Previous Implementations - -Dust has gone through several iterations, each with its own design choices. It was originally -implemented with a syntax tree generated by an external parser, then a parser generator, and finally -a custom parser. Eventually the language was rewritten to use bytecode instructions and a virtual -machine. The current implementation: compiling to bytecode with custom lexing and parsing for a -register-based VM, is by far the most performant and the general design is unlikely to change. - -Dust previously had a more complex type system with type arguments (or "generics") and a simple -model for asynchronous execution of statements. Both of these features were removed to simplify the -language when it was rewritten to use bytecode instructions. Both features are planned to be -reintroduced in the future. +Eventually, Dust should be available via package managers and as an embeddable library. For now, +the only way to use Dust is to clone the repository and build it from source. ## Inspiration diff --git a/dust-lang/src/instruction/mod.rs b/dust-lang/src/instruction/mod.rs index 8cb0234..d1e6afc 100644 --- a/dust-lang/src/instruction/mod.rs +++ b/dust-lang/src/instruction/mod.rs @@ -1,16 +1,17 @@ -//! Instructions for the Dust virtual machine. +//! The Dust instruction set. //! -//! Each instruction is 32 bits and uses up to seven distinct fields: +//! Each instruction is 64 bits and uses up to eight distinct fields. The instruction's layout is: //! -//! Bit | Description +//! Bits | Description //! ----- | ----------- //! 0-4 | Operation code //! 5 | Flag indicating if the B field is a constant //! 6 | Flag indicating if the C field is a constant //! 7 | D field (boolean) -//! 8-15 | A field (unsigned 8-bit integer) -//! 16-23 | B field (unsigned 8-bit integer) -//! 24-31 | C field (unsigned 8-bit integer) +//! 8-15 | Type specifier +//! 16-31 | A field (unsigned 16-bit integer) +//! 32-47 | B field (unsigned 16-bit integer) +//! 48-63 | C field (unsigned 16-bit integer) //! //! **Be careful when working with instructions directly**. When modifying an instruction's fields, //! you may also need to modify its flags. It is usually best to remove instructions and insert new @@ -71,9 +72,9 @@ //! # ); //! // Let's read an instruction and see if it performs addition-assignment, //! // like in one of the following examples: -//! // - `a += 2` -//! // - `a = a + 2` -//! // - `a = 2 + a` +//! // - `a += 2` +//! // - `a = a + 2` +//! // - `a = 2 + a` //! //! let operation = mystery_instruction.operation(); //! let is_add_assign = match operation { @@ -115,6 +116,7 @@ mod set_local; mod subtract; mod test; mod test_set; +mod type_code; pub use add::Add; pub use call::Call; @@ -152,25 +154,27 @@ use crate::NativeFunction; /// /// See the [module-level documentation](index.html) for more information. #[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] -pub struct Instruction(u32); +pub struct Instruction(u64); impl Instruction { pub fn new( operation: Operation, - a: u8, - b: u8, - c: u8, + type_specifier: u8, + a: u16, + b: u16, + c: u16, + d: bool, b_is_constant: bool, c_is_constant: bool, - d: bool, ) -> Instruction { - let bits = operation.0 as u32 - | ((b_is_constant as u32) << 5) - | ((c_is_constant as u32) << 6) - | ((d as u32) << 7) - | ((a as u32) << 8) - | ((b as u32) << 16) - | ((c as u32) << 24); + let bits = operation.0 as u64 + | ((b_is_constant as u64) << 5) + | ((c_is_constant as u64) << 6) + | ((d as u64) << 7) + | ((type_specifier as u64) << 15) + | ((a as u64) << 31) + | ((b as u64) << 47) + | ((c as u64) << 63); Instruction(bits) } @@ -206,29 +210,24 @@ impl Instruction { } pub fn set_a_field(&mut self, bits: u8) { - self.0 = (self.0 & 0xFFFF00FF) | ((bits as u32) << 8); + self.0 &= 0xFFFFFFFF00000000 | ((bits as u64) << 31); } pub fn set_b_field(&mut self, bits: u8) { - self.0 = (self.0 & 0xFFFF00FF) | ((bits as u32) << 16); + self.0 &= 0xFFFF0000FFFFFFFF | ((bits as u64) << 47); } - pub fn set_c_field(&mut self, bits: u8) { - self.0 = (self.0 & 0xFF00FFFF) | ((bits as u32) << 24); - } + pub fn set_c_field(&mut self, bits: u8) {} pub fn decode(self) -> (Operation, InstructionData) { - ( - self.operation(), - InstructionData { - a_field: self.a_field(), - b_field: self.b_field(), - c_field: self.c_field(), - b_is_constant: self.b_is_constant(), - c_is_constant: self.c_is_constant(), - d_field: self.d_field(), - }, - ) + (self.operation(), InstructionData { + a_field: self.a_field(), + b_field: self.b_field(), + c_field: self.c_field(), + b_is_constant: self.b_is_constant(), + c_is_constant: self.c_is_constant(), + d_field: self.d_field(), + }) } pub fn point(from: u8, to: u8) -> Instruction { diff --git a/dust-lang/src/instruction/type_code.rs b/dust-lang/src/instruction/type_code.rs new file mode 100644 index 0000000..0e038ee --- /dev/null +++ b/dust-lang/src/instruction/type_code.rs @@ -0,0 +1,10 @@ +pub struct TypeCode(pub u8); + +impl TypeCode { + const INTEGER: u8 = 0; + const FLOAT: u8 = 1; + const STRING: u8 = 2; + const BOOLEAN: u8 = 3; + const CHARACTER: u8 = 4; + const BYTE: u8 = 5; +} From 22d46e7b6dba70568a8a5f535e4729984fa48f7c Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 10 Jan 2025 13:39:09 -0500 Subject: [PATCH 3/7] Edit README.md --- README.md | 61 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 1b15a6a..c4bd339 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ fn fib (n: int) -> int { write_line(fib(25)) ``` -## 🌣 Highlights +## Highlights - Easy to read and write - Single-pass, self-optimizing compiler @@ -32,7 +32,7 @@ write_line(fib(25)) - Beautiful, helpful error messages from the compiler - Safe execution, runtime errors are treated as bugs -## 🛈 Overview +## Overview Dust's syntax, safety features and evaluation model are based on Rust. Its instruction set and optimization strategies are based on Lua. Unlike Rust and other languages that compile to @@ -77,11 +77,11 @@ aspirations are to be **fast**, **safe** and **easy**. ### Author -I'm Jeff and I started this project to learn more about programming languages by implementing a -simple expession evaluator. Initially, the project used an external parser and a tree-walking -interpreter. After several books, papers and a lot of experimentation, Dust has evolved to an -ambitious project that aims to implement lucrative features with a high-quality implementation that -competes with established languages. +I'm Jeff 🦀 and I started this project as simple expession evaluator. Initially, the project used an +external parser and a tree-walking interpreter. After several books, a few papers, countless +articles and a lot of experimentation, Dust has evolved to an ambitious project that aims to +implement lucrative features with a high-quality implementation that competes with established +languages. ## Usage @@ -94,36 +94,51 @@ the only way to use Dust is to clone the repository and build it from source. ## Inspiration -[Crafting Interpreters] by Bob Nystrom was a great resource for writing the compiler, especially the -Pratt parser. The book is a great introduction to writing interpreters. Had it been discovered +*Crafting Interpreters*[^0] by Bob Nystrom was a great resource for writing the compiler, especially +the Pratt parser. The book is a great introduction to writing interpreters. Had it been discovered sooner, some early implementations of Dust would have been both simpler in design and more ambitious in scope. -[The Implementation of Lua 5.0] by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, and Waldemar -Celes was a great resource for understanding register-based virtual machines and their instructions. -This paper was recommended by Bob Nystrom in [Crafting Interpreters]. +*The Implementation of Lua 5.0*[^1] by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, and +Waldemar Celes was a great resource for understanding register-based virtual machines and their +instructions. This paper was recommended by Bob Nystrom in [Crafting Interpreters]. -[A No-Frills Introduction to Lua 5.1 VM Instructions] by Kein-Hong Man has a wealth of detailed +*A No-Frills Introduction to Lua 5.1 VM Instructions*[^2] by Kein-Hong Man has a wealth of detailed information on how Lua uses terse instructions to create dense chunks that execute quickly. This was essential in the design of Dust's instructions. Dust uses compile-time optimizations that are based on Lua optimizations covered in this paper. -[A Performance Survey on Stack-based and Register-based Virtual Machines] by Ruijie Fang and Siqi +"A Performance Survey on Stack-based and Register-based Virtual Machines"[^3] by Ruijie Fang and Siqi Liup was helpful for a quick yet efficient primer on getting stack-based and register-based virtual machines up and running. The included code examples show how to implement both types of VMs in C. The performance comparison between the two types of VMs is worth reading for anyone who is trying to choose between the two. Some of the benchmarks described in the paper inspired similar benchmarks -used in this project to compare Dust to other languages. +used in this project to compare Dust to other languages and inform design decisions. + +*Writing a Compiler in Go*[^6] by Thorsten Ball is a lot like [Crafting Interpreters][], they are the +where I look for a generalized approach to solving a problem. Filled with code examples, this book +helps the reader make the turn from evaluating a syntax tree to thinking about how problems are +solved on physical hardware and how that informs the design of a virtual machine. + +> Let me get straight to the point: a virtual machine is a computer built with software. +> -- Thorsten Ball, *Writing a Compiler in Go* + +*Structure and Interpretation of Computer Programs, Second Edition*[^7] by Harold Abelson and Gerald +Jay Sussman with Julie Sussman is a classic text on computer science. It encourages an abstract +view of programming, sometimes using diagrams to explain programs as though they were physical +devices. It requires more effort than the books that immediately show you how to write a program, +but the takeaway is a deep understanding of the the process a computer (or a VM) goes through to +execute a program. ## License Dust is licensed under the GNU General Public License v3.0. See the `LICENSE` file for details. -## References - -[^1]: [Crafting Interpreters](https://craftinginterpreters.com/) -[^2]: [The Implementation of Lua 5.0](https://www.lua.org/doc/jucs05.pdf) -[^3]: [A No-Frills Introduction to Lua 5.1 VM Instructions](https://www.mcours.net/cours/pdf/hasclic3/hasssclic818.pdf) -[^4]: [A Performance Survey on Stack-based and Register-based Virtual Machines](https://arxiv.org/abs/1611.00467) -[^5]: [List of C-family programming languages](https://en.wikipedia.org/wiki/List_of_C-family_programming_languages) -[^6]: [ripgrep is faster than {grep, ag, git grep, ucg, pt, sift}](https://blog.burntsushi.net/ripgrep/#mechanics) +[^0]: [Crafting Interpreters](https://craftinginterpreters.com/) +[^1]: [The Implementation of Lua 5.0](https://www.lua.org/doc/jucs05.pdf) +[^2]: [A No-Frills Introduction to Lua 5.1 VM Instructions](https://www.mcours.net/cours/pdf/hasclic3/hasssclic818.pdf) +[^3]: [A Performance Survey on Stack-based and Register-based Virtual Machines](https://arxiv.org/abs/1611.00467) +[^4]: [List of C-family programming languages](https://en.wikipedia.org/wiki/List_of_C-family_programming_languages) +[^5]: [ripgrep is faster than {grep, ag, git grep, ucg, pt, sift}](https://blog.burntsushi.net/ripgrep/#mechanics) +[^6]: [Writing a Compiler in Go](https://compilerbook.com/) +[^7]: [Structure and Interpretation of Computer Programs, Second Edition](https://mitpress.mit.edu/9780262510875/structure-and-interpretation-of-computer-programs/) From 0510e18060f48ff03536a799a7870ae2db100657 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 13 Jan 2025 06:01:38 -0500 Subject: [PATCH 4/7] Begin implementing typed 64-bit instructions --- dust-lang/src/chunk/constant_table.rs | 62 + dust-lang/src/chunk/disassembler.rs | 89 +- dust-lang/src/chunk/local.rs | 14 +- dust-lang/src/chunk/mod.rs | 12 +- dust-lang/src/compiler/mod.rs | 550 ++++++--- dust-lang/src/compiler/optimize.rs | 20 +- dust-lang/src/instruction/add.rs | 31 - dust-lang/src/instruction/add_byte.rs | 54 + dust-lang/src/instruction/add_char.rs | 54 + dust-lang/src/instruction/add_char_str.rs | 54 + dust-lang/src/instruction/add_float.rs | 54 + dust-lang/src/instruction/add_int.rs | 56 + dust-lang/src/instruction/add_str.rs | 54 + dust-lang/src/instruction/add_str_char.rs | 54 + dust-lang/src/instruction/call.rs | 26 +- dust-lang/src/instruction/call_native.rs | 21 +- dust-lang/src/instruction/close.rs | 27 +- dust-lang/src/instruction/divide.rs | 31 - dust-lang/src/instruction/divide_byte.rs | 54 + dust-lang/src/instruction/divide_float.rs | 54 + dust-lang/src/instruction/divide_int.rs | 54 + dust-lang/src/instruction/equal.rs | 27 - dust-lang/src/instruction/equal_bool.rs | 55 + dust-lang/src/instruction/equal_byte.rs | 55 + dust-lang/src/instruction/equal_char.rs | 55 + dust-lang/src/instruction/equal_char_str.rs | 55 + dust-lang/src/instruction/equal_float.rs | 55 + dust-lang/src/instruction/equal_int.rs | 55 + dust-lang/src/instruction/equal_str.rs | 55 + dust-lang/src/instruction/equal_str_char.rs | 55 + dust-lang/src/instruction/get_local.rs | 31 +- dust-lang/src/instruction/jump.rs | 16 +- dust-lang/src/instruction/less.rs | 27 - dust-lang/src/instruction/less_byte.rs | 55 + dust-lang/src/instruction/less_char.rs | 55 + dust-lang/src/instruction/less_equal.rs | 27 - dust-lang/src/instruction/less_equal_byte.rs | 55 + dust-lang/src/instruction/less_equal_char.rs | 55 + dust-lang/src/instruction/less_equal_float.rs | 55 + dust-lang/src/instruction/less_equal_int.rs | 55 + dust-lang/src/instruction/less_equal_str.rs | 55 + dust-lang/src/instruction/less_float.rs | 55 + dust-lang/src/instruction/less_int.rs | 55 + dust-lang/src/instruction/less_str.rs | 55 + dust-lang/src/instruction/load_boolean.rs | 39 +- dust-lang/src/instruction/load_constant.rs | 28 +- dust-lang/src/instruction/load_function.rs | 44 +- dust-lang/src/instruction/load_list.rs | 42 +- dust-lang/src/instruction/load_self.rs | 41 +- dust-lang/src/instruction/mod.rs | 1060 ++++++++++------- dust-lang/src/instruction/modulo.rs | 31 - dust-lang/src/instruction/modulo_byte.rs | 54 + dust-lang/src/instruction/modulo_float.rs | 54 + dust-lang/src/instruction/modulo_int.rs | 54 + dust-lang/src/instruction/multiply.rs | 31 - dust-lang/src/instruction/multiply_byte.rs | 54 + dust-lang/src/instruction/multiply_float.rs | 54 + dust-lang/src/instruction/multiply_int.rs | 54 + dust-lang/src/instruction/negate.rs | 29 - dust-lang/src/instruction/negate_float.rs | 48 + dust-lang/src/instruction/negate_int.rs | 48 + dust-lang/src/instruction/not.rs | 21 +- dust-lang/src/instruction/operation.rs | 186 ++- dust-lang/src/instruction/point.rs | 32 +- dust-lang/src/instruction/return.rs | 16 +- dust-lang/src/instruction/set_local.rs | 31 +- dust-lang/src/instruction/subtract.rs | 31 - dust-lang/src/instruction/subtract_byte.rs | 54 + dust-lang/src/instruction/subtract_float.rs | 54 + dust-lang/src/instruction/subtract_int.rs | 54 + dust-lang/src/instruction/test.rs | 23 +- dust-lang/src/instruction/test_set.rs | 24 +- dust-lang/src/instruction/type_code.rs | 10 - dust-lang/src/lib.rs | 10 +- dust-lang/src/native_function/assert.rs | 2 +- dust-lang/src/native_function/io.rs | 6 +- dust-lang/src/native_function/mod.rs | 8 +- dust-lang/src/native_function/random.rs | 2 +- dust-lang/src/native_function/string.rs | 2 +- dust-lang/src/native_function/thread.rs | 4 +- dust-lang/src/type.rs | 6 +- dust-lang/src/value/function.rs | 2 +- dust-lang/src/vm/function_call.rs | 4 +- dust-lang/src/vm/mod.rs | 8 +- dust-lang/src/vm/run_action.rs | 18 +- dust-lang/src/vm/thread.rs | 26 +- dust-lang/tests/basic.rs | 12 +- dust-lang/tests/comparison.rs | 12 +- dust-lang/tests/functions.rs | 8 +- dust-lang/tests/lists.rs | 8 +- dust-lang/tests/loops.rs | 4 +- dust-lang/tests/math/divide.rs | 6 +- dust-lang/tests/math/divide_assign.rs | 2 +- dust-lang/tests/math/modulo.rs | 4 +- dust-lang/tests/math/multiply.rs | 4 +- dust-lang/tests/math/multiply_assign.rs | 2 +- dust-lang/tests/math/subtract.rs | 8 +- dust-lang/tests/math/subtract_assign.rs | 2 +- dust-lang/tests/unary_operations.rs | 4 +- 99 files changed, 3918 insertions(+), 1100 deletions(-) create mode 100644 dust-lang/src/chunk/constant_table.rs delete mode 100644 dust-lang/src/instruction/add.rs create mode 100644 dust-lang/src/instruction/add_byte.rs create mode 100644 dust-lang/src/instruction/add_char.rs create mode 100644 dust-lang/src/instruction/add_char_str.rs create mode 100644 dust-lang/src/instruction/add_float.rs create mode 100644 dust-lang/src/instruction/add_int.rs create mode 100644 dust-lang/src/instruction/add_str.rs create mode 100644 dust-lang/src/instruction/add_str_char.rs delete mode 100644 dust-lang/src/instruction/divide.rs create mode 100644 dust-lang/src/instruction/divide_byte.rs create mode 100644 dust-lang/src/instruction/divide_float.rs create mode 100644 dust-lang/src/instruction/divide_int.rs delete mode 100644 dust-lang/src/instruction/equal.rs create mode 100644 dust-lang/src/instruction/equal_bool.rs create mode 100644 dust-lang/src/instruction/equal_byte.rs create mode 100644 dust-lang/src/instruction/equal_char.rs create mode 100644 dust-lang/src/instruction/equal_char_str.rs create mode 100644 dust-lang/src/instruction/equal_float.rs create mode 100644 dust-lang/src/instruction/equal_int.rs create mode 100644 dust-lang/src/instruction/equal_str.rs create mode 100644 dust-lang/src/instruction/equal_str_char.rs delete mode 100644 dust-lang/src/instruction/less.rs create mode 100644 dust-lang/src/instruction/less_byte.rs create mode 100644 dust-lang/src/instruction/less_char.rs delete mode 100644 dust-lang/src/instruction/less_equal.rs create mode 100644 dust-lang/src/instruction/less_equal_byte.rs create mode 100644 dust-lang/src/instruction/less_equal_char.rs create mode 100644 dust-lang/src/instruction/less_equal_float.rs create mode 100644 dust-lang/src/instruction/less_equal_int.rs create mode 100644 dust-lang/src/instruction/less_equal_str.rs create mode 100644 dust-lang/src/instruction/less_float.rs create mode 100644 dust-lang/src/instruction/less_int.rs create mode 100644 dust-lang/src/instruction/less_str.rs delete mode 100644 dust-lang/src/instruction/modulo.rs create mode 100644 dust-lang/src/instruction/modulo_byte.rs create mode 100644 dust-lang/src/instruction/modulo_float.rs create mode 100644 dust-lang/src/instruction/modulo_int.rs delete mode 100644 dust-lang/src/instruction/multiply.rs create mode 100644 dust-lang/src/instruction/multiply_byte.rs create mode 100644 dust-lang/src/instruction/multiply_float.rs create mode 100644 dust-lang/src/instruction/multiply_int.rs delete mode 100644 dust-lang/src/instruction/negate.rs create mode 100644 dust-lang/src/instruction/negate_float.rs create mode 100644 dust-lang/src/instruction/negate_int.rs delete mode 100644 dust-lang/src/instruction/subtract.rs create mode 100644 dust-lang/src/instruction/subtract_byte.rs create mode 100644 dust-lang/src/instruction/subtract_float.rs create mode 100644 dust-lang/src/instruction/subtract_int.rs delete mode 100644 dust-lang/src/instruction/type_code.rs diff --git a/dust-lang/src/chunk/constant_table.rs b/dust-lang/src/chunk/constant_table.rs new file mode 100644 index 0000000..4c0c1ed --- /dev/null +++ b/dust-lang/src/chunk/constant_table.rs @@ -0,0 +1,62 @@ +use serde::{Deserialize, Serialize}; + +use crate::DustString; + +#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)] +pub struct ConstantTable { + pub r#true: bool, + pub r#false: bool, + pub bytes: Vec, + pub characters: Vec, + pub floats: Vec, + pub integers: Vec, + pub strings: Vec, +} + +impl ConstantTable { + pub fn new() -> Self { + Self { + r#true: false, + r#false: false, + bytes: Vec::new(), + characters: Vec::new(), + floats: Vec::new(), + integers: Vec::new(), + strings: Vec::new(), + } + } + + #[cfg(debug_assertions)] + pub fn with_data( + booleans: (bool, bool), + bytes: impl Into>, + characters: impl Into>, + floats: impl Into>, + integers: impl Into>, + strings: impl Into>, + ) -> Self { + Self { + r#true: booleans.0, + r#false: booleans.1, + bytes: bytes.into(), + characters: characters.into(), + floats: floats.into(), + integers: integers.into(), + strings: strings.into(), + } + } + + pub fn len(&self) -> usize { + (if self.r#true { 1 } else { 0 }) + + (if self.r#false { 1 } else { 0 }) + + self.bytes.len() + + self.characters.len() + + self.floats.len() + + self.integers.len() + + self.strings.len() + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} diff --git a/dust-lang/src/chunk/disassembler.rs b/dust-lang/src/chunk/disassembler.rs index ac0b62a..b9dd48c 100644 --- a/dust-lang/src/chunk/disassembler.rs +++ b/dust-lang/src/chunk/disassembler.rs @@ -43,7 +43,7 @@ use std::io::{self, Write}; use colored::{ColoredString, Colorize}; -use crate::{Chunk, Local}; +use crate::{Chunk, Local, Type}; const INSTRUCTION_COLUMNS: [(&str, usize); 4] = [("i", 5), ("POSITION", 12), ("OPERATION", 17), ("INFO", 24)]; @@ -322,6 +322,7 @@ impl<'a, W: Write> Disassembler<'a, W> { let identifier_display = self .chunk .constants + .strings .get(*identifier_index as usize) .map(|value| value.to_string()) .unwrap_or_else(|| "unknown".to_string()); @@ -352,8 +353,88 @@ impl<'a, W: Write> Disassembler<'a, W> { self.write_center_border(&column_name_line)?; self.write_center_border(CONSTANT_BORDERS[1])?; - for (index, value) in self.chunk.constants.iter().enumerate() { - let type_display = value.r#type().to_string(); + if self.chunk.constants.r#true { + let type_display = Type::Boolean.to_string(); + let value_display = "true"; + let constant_display = format!("│{:^5}│{:^26}│{:^26}│", 0, type_display, value_display); + + self.write_center_border(&constant_display)?; + } + + if self.chunk.constants.r#false { + let type_display = Type::Boolean.to_string(); + let value_display = "false"; + let constant_display = format!("│{:^5}│{:^26}│{:^26}│", 1, type_display, value_display); + + self.write_center_border(&constant_display)?; + } + + for (index, value) in self.chunk.constants.bytes.iter().enumerate() { + let type_display = Type::Byte.to_string(); + let value_display = { + let mut value_string = value.to_string(); + + if value_string.len() > 26 { + value_string = format!("{value_string:.23}..."); + } + + value_string + }; + let constant_display = format!("│{index:^5}│{type_display:^26}│{value_display:^26}│"); + + self.write_center_border(&constant_display)?; + } + + for (index, value) in self.chunk.constants.characters.iter().enumerate() { + let type_display = Type::Character.to_string(); + let value_display = { + let mut value_string = value.to_string(); + + if value_string.len() > 26 { + value_string = format!("{value_string:.23}..."); + } + + value_string + }; + let constant_display = format!("│{index:^5}│{type_display:^26}│{value_display:^26}│"); + + self.write_center_border(&constant_display)?; + } + + for (index, value) in self.chunk.constants.floats.iter().enumerate() { + let type_display = Type::Float.to_string(); + let value_display = { + let mut value_string = value.to_string(); + + if value_string.len() > 26 { + value_string = format!("{value_string:.23}..."); + } + + value_string + }; + let constant_display = format!("│{index:^5}│{type_display:^26}│{value_display:^26}│"); + + self.write_center_border(&constant_display)?; + } + + for (index, value) in self.chunk.constants.integers.iter().enumerate() { + let type_display = Type::Integer.to_string(); + let value_display = { + let mut value_string = value.to_string(); + + if value_string.len() > 26 { + value_string = format!("{value_string:.23}..."); + } + + value_string + }; + let constant_display = format!("│{index:^5}│{type_display:^26}│{value_display:^26}│"); + + self.write_center_border(&constant_display)?; + } + + for (index, value) in self.chunk.constants.strings.iter().enumerate() { + let type_display = Type::String.to_string(); let value_display = { let mut value_string = value.to_string(); @@ -374,7 +455,7 @@ impl<'a, W: Write> Disassembler<'a, W> { } pub fn write_prototype_section(&mut self) -> Result<(), io::Error> { - self.write_center_border_bold("Functions")?; + self.write_center_border_bold("Prototypes")?; for chunk in &self.chunk.prototypes { chunk diff --git a/dust-lang/src/chunk/local.rs b/dust-lang/src/chunk/local.rs index 67f9d68..30cf47b 100644 --- a/dust-lang/src/chunk/local.rs +++ b/dust-lang/src/chunk/local.rs @@ -1,15 +1,17 @@ +//! Scoped variable. + use serde::{Deserialize, Serialize}; use crate::Scope; -/// A scoped variable. +/// Scoped variable. #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct Local { - /// The index of the identifier in the constants table. - pub identifier_index: u8, + /// Index of the identifier in the constants list. + pub identifier_index: u16, - /// Stack index where the local's value is stored. - pub register_index: u8, + /// Index of the register where the variable's value is stored. + pub register_index: u16, /// Whether the local is mutable. pub is_mutable: bool, @@ -20,7 +22,7 @@ pub struct Local { impl Local { /// Creates a new Local instance. - pub fn new(identifier_index: u8, register_index: u8, is_mutable: bool, scope: Scope) -> Self { + pub fn new(identifier_index: u16, register_index: u16, is_mutable: bool, scope: Scope) -> Self { Self { identifier_index, register_index, diff --git a/dust-lang/src/chunk/mod.rs b/dust-lang/src/chunk/mod.rs index d9a7364..29f68b7 100644 --- a/dust-lang/src/chunk/mod.rs +++ b/dust-lang/src/chunk/mod.rs @@ -13,10 +13,12 @@ //! [`Chunk::with_data`] can be used to create a chunk for comparison to the compiler output. Do not //! try to run these chunks in a virtual machine. Due to their missing stack size and record index, //! they will cause a panic or undefined behavior. +mod constant_table; mod disassembler; mod local; mod scope; +pub use constant_table::ConstantTable; pub use disassembler::Disassembler; pub use local::Local; pub use scope::Scope; @@ -27,7 +29,7 @@ use std::sync::Arc; use serde::{Deserialize, Serialize}; -use crate::{DustString, Function, FunctionType, Instruction, Span, Value}; +use crate::{DustString, Function, FunctionType, Instruction, Span}; /// Representation of a Dust program or function. /// @@ -39,12 +41,12 @@ pub struct Chunk { pub(crate) instructions: Vec, pub(crate) positions: Vec, - pub(crate) constants: Vec, + pub(crate) constants: ConstantTable, pub(crate) locals: Vec, pub(crate) prototypes: Vec>, pub(crate) register_count: usize, - pub(crate) prototype_index: u8, + pub(crate) prototype_index: u16, } impl Chunk { @@ -54,7 +56,7 @@ impl Chunk { r#type: FunctionType, instructions: impl Into>, positions: impl Into>, - constants: impl Into>, + constants: ConstantTable, locals: impl Into>, prototypes: impl IntoIterator, ) -> Self { @@ -63,7 +65,7 @@ impl Chunk { r#type, instructions: instructions.into(), positions: positions.into(), - constants: constants.into(), + constants, locals: locals.into(), prototypes: prototypes.into_iter().map(Arc::new).collect(), register_count: 0, diff --git a/dust-lang/src/compiler/mod.rs b/dust-lang/src/compiler/mod.rs index 270fc81..cb007b1 100644 --- a/dust-lang/src/compiler/mod.rs +++ b/dust-lang/src/compiler/mod.rs @@ -33,9 +33,10 @@ use std::{mem::replace, sync::Arc}; use optimize::control_flow_register_consolidation; use crate::{ - Argument, Chunk, ConcreteValue, DustError, DustString, FunctionType, Instruction, Lexer, Local, - NativeFunction, Operation, Scope, Span, Token, TokenKind, Type, Value, - instruction::{CallNative, Close, GetLocal, Jump, LoadList, Negate, Not, Return, SetLocal}, + Chunk, ConcreteValue, DustError, DustString, FunctionType, Instruction, Lexer, Local, + NativeFunction, Operand, Operation, Scope, Span, Token, TokenKind, Type, Value, + chunk::ConstantTable, + instruction::{GetLocal, Jump, LoadList}, }; /// Compiles the input and returns a chunk. @@ -88,7 +89,7 @@ pub struct Compiler<'src> { /// Constants that have been compiled. These are assigned to the chunk when [`Compiler::finish`] /// is called. - constants: Vec, + constants: ConstantTable, /// Block-local variables and their types. The locals are assigned to the chunk when /// [`Compiler::finish`] is called. The types are discarded after compilation. @@ -104,7 +105,7 @@ pub struct Compiler<'src> { /// 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. - minimum_register: u8, + minimum_register: u16, /// Index of the current block. This is used to determine the scope of locals and is incremented /// when a new block is entered. @@ -115,7 +116,7 @@ pub struct Compiler<'src> { /// Index of the Chunk in its parent's prototype list. This is set to 0 for the main chunk but /// that value is never read because the main chunk is not a callable function. - prototype_index: u8, + prototype_index: u16, /// Whether the chunk is the program's main chunk. This is used to prevent recursive calls to /// the main chunk. @@ -144,7 +145,7 @@ impl<'src> Compiler<'src> { return_type: Type::None, }, instructions: Vec::new(), - constants: Vec::new(), + constants: ConstantTable::new(), locals: Vec::new(), prototypes: Vec::new(), stack_size: 0, @@ -215,7 +216,7 @@ impl<'src> Compiler<'src> { r#type: self.r#type, instructions, positions, - constants: self.constants.to_vec(), + constants: self.constants, locals, prototypes: self.prototypes, register_count: self.stack_size, @@ -227,7 +228,7 @@ impl<'src> Compiler<'src> { matches!(self.current_token, Token::Eof) } - fn next_register(&self) -> u8 { + fn next_register(&self) -> u16 { self.instructions .iter() .rev() @@ -260,7 +261,7 @@ impl<'src> Compiler<'src> { Ok(()) } - fn get_local(&self, index: u8) -> Result<&(Local, Type), CompileError> { + fn get_local(&self, index: u16) -> Result<&(Local, Type), CompileError> { self.locals .get(index as usize) .ok_or(CompileError::UndeclaredVariable { @@ -269,22 +270,19 @@ impl<'src> Compiler<'src> { }) } - fn get_local_index(&self, identifier_text: &str) -> Result { + fn get_local_index(&self, identifier_text: &str) -> Result { self.locals .iter() .enumerate() .rev() .find_map(|(index, (local, _))| { - let constant = self.constants.get(local.identifier_index as usize)?; - let identifier = - if let Value::Concrete(ConcreteValue::String(identifier)) = constant { - identifier - } else { - return None; - }; + let identifier = self + .constants + .strings + .get(local.identifier_index as usize)?; if identifier == identifier_text { - Some(index as u8) + Some(index as u16) } else { None } @@ -298,16 +296,16 @@ impl<'src> Compiler<'src> { fn declare_local( &mut self, identifier: &str, - register_index: u8, + register_index: u16, r#type: Type, is_mutable: bool, scope: Scope, - ) -> (u8, u8) { + ) -> (u16, u16) { info!("Declaring local {identifier}"); - let identifier = Value::Concrete(ConcreteValue::string(identifier)); - let identifier_index = self.push_or_get_constant(identifier); - let local_index = self.locals.len() as u8; + let identifier = DustString::from(identifier); + let identifier_index = self.push_or_get_constant_string(identifier); + let local_index = self.locals.len() as u16; self.locals.push(( Local::new(identifier_index, register_index, is_mutable, scope), @@ -317,29 +315,107 @@ impl<'src> Compiler<'src> { (local_index, identifier_index) } - fn get_identifier(&self, local_index: u8) -> Option { + fn get_identifier(&self, local_index: u16) -> Option { self.locals .get(local_index as usize) .and_then(|(local, _)| { self.constants + .strings .get(local.identifier_index as usize) .map(|value| value.to_string()) }) } - fn push_or_get_constant(&mut self, value: Value) -> u8 { + fn push_or_get_constant_bool(&mut self, boolean: bool) -> u16 { + if boolean { + if self.constants.r#true { + 0 + } else { + self.constants.r#true = true; + + 0 + } + } else if self.constants.r#false { + 1 + } else { + self.constants.r#false = true; + + 1 + } + } + + fn push_or_get_constant_byte(&mut self, new_byte: u8) -> u16 { if let Some(index) = self .constants + .bytes .iter() - .position(|constant| constant == &value) + .position(|&byte| byte == new_byte) { - index as u8 + index as u16 } else { - let index = self.constants.len() as u8; + self.constants.bytes.push(new_byte); - self.constants.push(value); + (self.constants.bytes.len() - 1) as u16 + } + } - index + fn push_or_get_constant_character(&mut self, new_character: char) -> u16 { + if let Some(index) = self + .constants + .characters + .iter() + .position(|&character| character == new_character) + { + index as u16 + } else { + self.constants.characters.push(new_character); + + (self.constants.characters.len() - 1) as u16 + } + } + + fn push_or_get_constant_float(&mut self, new_float: f64) -> u16 { + if let Some(index) = self + .constants + .floats + .iter() + .position(|&float| float == new_float) + { + index as u16 + } else { + self.constants.floats.push(new_float); + + (self.constants.floats.len() - 1) as u16 + } + } + + fn push_or_get_constant_integer(&mut self, new_integer: i64) -> u16 { + if let Some(index) = self + .constants + .integers + .iter() + .position(|&integer| integer == new_integer) + { + index as u16 + } else { + self.constants.integers.push(new_integer); + + (self.constants.integers.len() - 1) as u16 + } + } + + fn push_or_get_constant_string(&mut self, new_string: DustString) -> u16 { + if let Some(index) = self + .constants + .strings + .iter() + .position(|string| *string == new_string) + { + index as u16 + } else { + self.constants.strings.push(new_string); + + (self.constants.strings.len() - 1) as u16 } } @@ -393,7 +469,7 @@ impl<'src> Compiler<'src> { .unwrap_or(Type::None) } - fn get_register_type(&self, register_index: u8) -> Result { + fn get_register_type(&self, register_index: u16) -> Result { if let Some((_, r#type)) = self .locals .iter() @@ -481,6 +557,14 @@ impl<'src> Compiler<'src> { Ok(()) } + fn emit_boolean_constant(&mut self, boolean: bool, position: Span) { + let constant_index = self.push_or_get_constant_bool(boolean); + let destination = self.next_register(); + let load_boolean = Instruction::load_constant(destination, constant_index, jump_next); + + self.emit_instruction(load_boolean, Type::Boolean, position); + } + fn parse_boolean(&mut self) -> Result<(), CompileError> { let position = self.current_position; @@ -651,15 +735,10 @@ impl<'src> Compiler<'src> { } let destination = self.next_register(); - let instruction = match operator.kind() { - TokenKind::Bang => Instruction::from(Not { - destination, - argument, - }), - TokenKind::Minus => Instruction::from(Negate { - destination, - argument, - }), + let instruction = match (operator, &previous_type) { + (Token::Bang, Type::Boolean) => Instruction::not(destination, argument), + (Token::Minus, Type::Float) => Instruction::negate_float(destination, argument), + (Token::Minus, Type::Integer) => Instruction::negate_int(destination, argument), _ => { return Err(CompileError::ExpectedTokenMultiple { expected: &[TokenKind::Bang, TokenKind::Minus], @@ -677,34 +756,62 @@ impl<'src> Compiler<'src> { fn handle_binary_argument( &mut self, instruction: &Instruction, - ) -> Result<(Argument, bool), CompileError> { + ) -> Result<(Operand, bool), CompileError> { let (argument, push_back) = match instruction.operation() { - Operation::LOAD_CONSTANT => (Argument::Constant(instruction.b_field()), false), + Operation::LOAD_CONSTANT => (Operand::Constant(instruction.b_field()), false), Operation::GET_LOCAL => { let local_index = instruction.b_field(); let (local, _) = self.get_local(local_index)?; - (Argument::Register(local.register_index), false) + (Operand::Register(local.register_index), false) } Operation::LOAD_BOOLEAN | Operation::LOAD_LIST | Operation::LOAD_SELF - | Operation::ADD - | Operation::SUBTRACT - | Operation::MULTIPLY - | Operation::DIVIDE - | Operation::MODULO - | Operation::EQUAL - | Operation::LESS - | Operation::LESS_EQUAL - | Operation::NEGATE + | Operation::ADD_BYTE + | Operation::ADD_CHAR + | Operation::ADD_CHAR_STR + | Operation::ADD_FLOAT + | Operation::ADD_INT + | Operation::ADD_STR + | Operation::ADD_STR_CHAR + | Operation::SUBTRACT_BYTE + | Operation::SUBTRACT_FLOAT + | Operation::SUBTRACT_INT + | Operation::MULTIPLY_BYTE + | Operation::MULTIPLY_FLOAT + | Operation::MULTIPLY_INT + | Operation::DIVIDE_BYTE + | Operation::DIVIDE_FLOAT + | Operation::DIVIDE_INT + | Operation::MODULO_BYTE + | Operation::MODULO_INT + | Operation::MODULO_FLOAT + | Operation::EQUAL_INT + | Operation::EQUAL_FLOAT + | Operation::EQUAL_CHAR + | Operation::EQUAL_STR + | Operation::EQUAL_BOOL + | Operation::EQUAL_BYTE + | Operation::LESS_INT + | Operation::LESS_FLOAT + | Operation::LESS_CHAR + | Operation::LESS_STR + | Operation::LESS_BYTE + | Operation::LESS_EQUAL_INT + | Operation::LESS_EQUAL_FLOAT + | Operation::LESS_EQUAL_CHAR + | Operation::LESS_EQUAL_STR + | Operation::LESS_EQUAL_BYTE + | Operation::NEGATE_INT + | Operation::NEGATE_FLOAT | Operation::NOT - | Operation::CALL => (Argument::Register(instruction.a_field()), true), + | Operation::CALL => (Operand::Register(instruction.a_field()), true), Operation::CALL_NATIVE => { let function = NativeFunction::from(instruction.b_field()); if function.returns_value() { - (Argument::Register(instruction.a_field()), true) + (Operand::Register(instruction.a_field()), true) } else { return Err(CompileError::ExpectedExpression { found: self.previous_token.to_owned(), @@ -786,7 +893,7 @@ impl<'src> Compiler<'src> { if push_back_right { self.instructions - .push((right_instruction, right_type, right_position)); + .push((right_instruction, right_type.clone(), right_position)); } let r#type = if is_assignment { @@ -798,18 +905,70 @@ impl<'src> Compiler<'src> { }; let destination = if is_assignment { match left { - Argument::Register(register) => register, - Argument::Constant(_) => self.next_register(), + Operand::Register(register) => register, + Operand::Constant(_) => self.next_register(), } } else { self.next_register() }; - let instruction = match operator { - Token::Plus | Token::PlusEqual => Instruction::add(destination, left, right), - Token::Minus | Token::MinusEqual => Instruction::subtract(destination, left, right), - Token::Star | Token::StarEqual => Instruction::multiply(destination, left, right), - Token::Slash | Token::SlashEqual => Instruction::divide(destination, left, right), - Token::Percent | Token::PercentEqual => Instruction::modulo(destination, left, right), + let instruction = match (operator, left_type, right_type) { + (Token::Plus, Type::Byte, Type::Byte) => { + Instruction::add_byte(destination, left, right) + } + (Token::Plus, Type::Character, Type::Character) => { + Instruction::add_char(destination, left, right) + } + (Token::Plus, Type::Character, Type::String) => { + Instruction::add_char_str(destination, left, right) + } + (Token::Plus, Type::Float, Type::Float) => { + Instruction::add_float(destination, left, right) + } + (Token::Plus, Type::Integer, Type::Integer) => { + Instruction::add_int(destination, left, right) + } + (Token::Plus, Type::String, Type::Character) => { + Instruction::add_str_char(destination, left, right) + } + (Token::Plus, Type::String, Type::String) => { + Instruction::add_str(destination, left, right) + } + (Token::Minus, Type::Byte, Type::Byte) => { + Instruction::subtract_byte(destination, left, right) + } + (Token::Minus, Type::Float, Type::Float) => { + Instruction::subtract_float(destination, left, right) + } + (Token::Minus, Type::Integer, Type::Integer) => { + Instruction::subtract_int(destination, left, right) + } + (Token::Star, Type::Byte, Type::Byte) => { + Instruction::multiply_byte(destination, left, right) + } + (Token::Star, Type::Float, Type::Float) => { + Instruction::multiply_float(destination, left, right) + } + (Token::Star, Type::Integer, Type::Integer) => { + Instruction::multiply_int(destination, left, right) + } + (Token::Slash, Type::Byte, Type::Byte) => { + Instruction::divide_byte(destination, left, right) + } + (Token::Slash, Type::Float, Type::Float) => { + Instruction::divide_float(destination, left, right) + } + (Token::Slash, Type::Integer, Type::Integer) => { + Instruction::divide_int(destination, left, right) + } + (Token::Percent, Type::Byte, Type::Byte) => { + Instruction::modulo_byte(destination, left, right) + } + (Token::Percent, Type::Integer, Type::Integer) => { + Instruction::modulo_int(destination, left, right) + } + (Token::Percent, Type::Float, Type::Float) => { + Instruction::modulo_float(destination, left, right) + } _ => { return Err(CompileError::ExpectedTokenMultiple { expected: &[ @@ -836,13 +995,9 @@ impl<'src> Compiler<'src> { } fn parse_comparison_binary(&mut self) -> Result<(), CompileError> { - if let Some( - [ - Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL, - _, - _, - ], - ) = self.get_last_operations() + if self + .get_last_operation() + .is_some_and(|operation| operation.is_comparison()) { return Err(CompileError::ComparisonChain { position: self.current_position, @@ -863,7 +1018,7 @@ impl<'src> Compiler<'src> { if push_back_left { self.instructions - .push((left_instruction, left_type, left_position)); + .push((left_instruction, left_type.clone(), left_position)); } self.advance()?; @@ -880,17 +1035,111 @@ impl<'src> Compiler<'src> { if push_back_right { self.instructions - .push((right_instruction, right_type, right_position)); + .push((right_instruction, right_type.clone(), right_position)); } let destination = self.next_register(); - let comparison = match operator { - Token::DoubleEqual => Instruction::equal(true, left, right), - Token::BangEqual => Instruction::equal(false, left, right), - Token::Less => Instruction::less(true, left, right), - Token::LessEqual => Instruction::less_equal(true, left, right), - Token::Greater => Instruction::less_equal(false, left, right), - Token::GreaterEqual => Instruction::less(false, left, right), + let comparison = match (operator, left_type, right_type) { + (Token::DoubleEqual, Type::Boolean, Type::Boolean) => { + Instruction::equal_bool(true, left, right) + } + (Token::DoubleEqual, Type::Byte, Type::Byte) => { + Instruction::equal_byte(true, left, right) + } + (Token::DoubleEqual, Type::Character, Type::Character) => { + Instruction::equal_char(true, left, right) + } + (Token::DoubleEqual, Type::Character, Type::String) => { + Instruction::equal_char_str(true, left, right) + } + (Token::DoubleEqual, Type::Float, Type::Float) => { + Instruction::equal_float(true, left, right) + } + (Token::DoubleEqual, Type::Integer, Type::Integer) => { + Instruction::equal_int(true, left, right) + } + (Token::DoubleEqual, Type::String, Type::String) => { + Instruction::equal_str(true, left, right) + } + (Token::DoubleEqual, Type::String, Type::Character) => { + Instruction::equal_str_char(true, left, right) + } + (Token::BangEqual, Type::Boolean, Type::Boolean) => { + Instruction::equal_bool(false, left, right) + } + (Token::BangEqual, Type::Byte, Type::Byte) => { + Instruction::equal_byte(false, left, right) + } + (Token::BangEqual, Type::Character, Type::Character) => { + Instruction::equal_char(false, left, right) + } + (Token::BangEqual, Type::Character, Type::String) => { + Instruction::equal_char_str(false, left, right) + } + (Token::BangEqual, Type::Float, Type::Float) => { + Instruction::equal_float(false, left, right) + } + (Token::BangEqual, Type::Integer, Type::Integer) => { + Instruction::equal_int(false, left, right) + } + (Token::BangEqual, Type::String, Type::String) => { + Instruction::equal_str(false, left, right) + } + (Token::BangEqual, Type::String, Type::Character) => { + Instruction::equal_str_char(false, left, right) + } + (Token::Less, Type::Byte, Type::Byte) => Instruction::less_byte(true, left, right), + (Token::Less, Type::Character, Type::Character) => { + Instruction::less_char(true, left, right) + } + (Token::Less, Type::Float, Type::Float) => Instruction::less_float(true, left, right), + (Token::Less, Type::Integer, Type::Integer) => Instruction::less_int(true, left, right), + (Token::Less, Type::String, Type::String) => Instruction::less_str(true, left, right), + (Token::GreaterEqual, Type::Byte, Type::Byte) => { + Instruction::less_byte(false, left, right) + } + (Token::GreaterEqual, Type::Character, Type::Character) => { + Instruction::less_char(false, left, right) + } + (Token::GreaterEqual, Type::Float, Type::Float) => { + Instruction::less_float(false, left, right) + } + (Token::GreaterEqual, Type::Integer, Type::Integer) => { + Instruction::less_int(false, left, right) + } + (Token::GreaterEqual, Type::String, Type::String) => { + Instruction::less_str(false, left, right) + } + (Token::LessEqual, Type::Byte, Type::Byte) => { + Instruction::less_equal_byte(true, left, right) + } + (Token::LessEqual, Type::Character, Type::Character) => { + Instruction::less_equal_char(true, left, right) + } + (Token::LessEqual, Type::Float, Type::Float) => { + Instruction::less_equal_float(true, left, right) + } + (Token::LessEqual, Type::Integer, Type::Integer) => { + Instruction::less_equal_int(true, left, right) + } + (Token::LessEqual, Type::String, Type::String) => { + Instruction::less_equal_str(true, left, right) + } + (Token::Greater, Type::Byte, Type::Byte) => { + Instruction::less_equal_byte(false, left, right) + } + (Token::Greater, Type::Character, Type::Character) => { + Instruction::less_equal_char(false, left, right) + } + (Token::Greater, Type::Float, Type::Float) => { + Instruction::less_equal_float(false, left, right) + } + (Token::Greater, Type::Integer, Type::Integer) => { + Instruction::less_equal_int(false, left, right) + } + (Token::Greater, Type::String, Type::String) => { + Instruction::less_equal_str(false, left, right) + } _ => { return Err(CompileError::ExpectedTokenMultiple { expected: &[ @@ -974,7 +1223,23 @@ impl<'src> Compiler<'src> { if !matches!( instructions[0].0.operation(), - Operation::TEST | Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL + Operation::TEST + | Operation::EQUAL_INT + | Operation::EQUAL_STR + | Operation::EQUAL_BOOL + | Operation::EQUAL_BYTE + | Operation::EQUAL_CHAR + | Operation::EQUAL_FLOAT + | Operation::LESS_INT + | Operation::LESS_STR + | Operation::LESS_BYTE + | Operation::LESS_CHAR + | Operation::LESS_FLOAT + | Operation::LESS_EQUAL_INT + | Operation::LESS_EQUAL_STR + | Operation::LESS_EQUAL_BYTE + | Operation::LESS_EQUAL_CHAR + | Operation::LESS_EQUAL_FLOAT ) || !matches!(instructions[1].0.operation(), Operation::JUMP) { continue; @@ -982,7 +1247,7 @@ impl<'src> Compiler<'src> { let old_jump = &mut instructions[1].0; let jump_index = instructions_length - group_index * 3 - 1; - let short_circuit_distance = (instructions_length - jump_index) as u8; + let short_circuit_distance = (instructions_length - jump_index) as u16; *old_jump = Instruction::jump(short_circuit_distance, true); } @@ -1009,7 +1274,7 @@ impl<'src> Compiler<'src> { return self.parse_call_native(native_function); } else if self.function_name.as_deref() == Some(identifier) && !self.is_main { let destination = self.next_register(); - let load_self = Instruction::load_self(destination); + let load_self = Instruction::load_self(destination, false); self.emit_instruction(load_self, Type::SelfFunction, start_position); @@ -1056,10 +1321,7 @@ impl<'src> Compiler<'src> { math_instruction.set_a_field(local_register_index); } else { let register = self.next_register() - 1; - let set_local = Instruction::from(SetLocal { - register_index: register, - local_index, - }); + let set_local = Instruction::set_local(register, local_index); self.emit_instruction(set_local, Type::None, start_position); } @@ -1134,10 +1396,7 @@ impl<'src> Compiler<'src> { } if expected_register < actual_register { - let close = Instruction::from(Close { - from: expected_register, - to: actual_register, - }); + let close = Instruction::close(expected_register, actual_register); self.emit_instruction(close, Type::None, self.current_position); } @@ -1147,10 +1406,7 @@ impl<'src> Compiler<'src> { let destination = self.next_register(); let end = self.previous_position.1; - let load_list = Instruction::from(LoadList { - destination, - start_register, - }); + let load_list = Instruction::load_list(destination, start_register, false); self.emit_instruction(load_list, Type::List(Box::new(item_type)), Span(start, end)); @@ -1164,7 +1420,22 @@ impl<'src> Compiler<'src> { if matches!( self.get_last_operations(), Some([ - Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL, + Operation::EQUAL_INT + | Operation::EQUAL_STR + | Operation::EQUAL_BOOL + | Operation::EQUAL_BYTE + | Operation::EQUAL_CHAR + | Operation::EQUAL_FLOAT + | Operation::LESS_INT + | Operation::LESS_STR + | Operation::LESS_BYTE + | Operation::LESS_CHAR + | Operation::LESS_FLOAT + | Operation::LESS_EQUAL_INT + | Operation::LESS_EQUAL_STR + | Operation::LESS_EQUAL_BYTE + | Operation::LESS_EQUAL_CHAR + | Operation::LESS_EQUAL_FLOAT, Operation::JUMP, Operation::LOAD_BOOLEAN, Operation::LOAD_BOOLEAN @@ -1194,7 +1465,7 @@ impl<'src> Compiler<'src> { } let if_block_end = self.instructions.len(); - let mut if_block_distance = (if_block_end - if_block_start) as u8; + let mut if_block_distance = (if_block_end - if_block_start) as u16; let if_block_type = self.get_last_instruction_type(); if let Token::Else = self.current_token { @@ -1216,7 +1487,7 @@ impl<'src> Compiler<'src> { } let else_block_end = self.instructions.len(); - let else_block_distance = (else_block_end - if_block_end) as u8; + let else_block_distance = (else_block_end - if_block_end) as u16; let else_block_type = self.get_last_instruction_type(); if let Err(conflict) = if_block_type.check(&else_block_type) { @@ -1234,13 +1505,10 @@ impl<'src> Compiler<'src> { { let (loader, _, _) = self.instructions.last_mut().unwrap(); - loader.set_c_field(true as u8); + loader.set_c_field(true as u16); } else { if_block_distance += 1; - let jump = Instruction::from(Jump { - offset: else_block_distance, - is_positive: true, - }); + let jump = Instruction::jump(else_block_distance, true); self.instructions .insert(if_block_end, (jump, Type::None, self.current_position)); @@ -1248,10 +1516,7 @@ impl<'src> Compiler<'src> { } 2.. => { if_block_distance += 1; - let jump = Instruction::from(Jump { - offset: else_block_distance, - is_positive: true, - }); + let jump = Instruction::jump(else_block_distance, true); self.instructions .insert(if_block_end, (jump, Type::None, self.current_position)); @@ -1270,14 +1535,29 @@ impl<'src> Compiler<'src> { fn parse_while(&mut self) -> Result<(), CompileError> { self.advance()?; - let expression_start = self.instructions.len() as u8; + let expression_start = self.instructions.len() as u16; self.parse_expression()?; if matches!( self.get_last_operations(), Some([ - Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL, + Operation::EQUAL_INT + | Operation::EQUAL_STR + | Operation::EQUAL_BOOL + | Operation::EQUAL_BYTE + | Operation::EQUAL_CHAR + | Operation::EQUAL_FLOAT + | Operation::LESS_INT + | Operation::LESS_STR + | Operation::LESS_BYTE + | Operation::LESS_CHAR + | Operation::LESS_FLOAT + | Operation::LESS_EQUAL_INT + | Operation::LESS_EQUAL_STR + | Operation::LESS_EQUAL_BYTE + | Operation::LESS_EQUAL_CHAR + | Operation::LESS_EQUAL_FLOAT, Operation::JUMP, Operation::LOAD_BOOLEAN, Operation::LOAD_BOOLEAN @@ -1297,21 +1577,15 @@ impl<'src> Compiler<'src> { self.parse_block()?; - let block_end = self.instructions.len() as u8; - let jump_distance = block_end - block_start as u8 + 1; - let jump = Instruction::from(Jump { - offset: jump_distance, - is_positive: true, - }); + let block_end = self.instructions.len() as u16; + let jump_distance = block_end - block_start as u16 + 1; + let jump = Instruction::jump(jump_distance, true); self.instructions .insert(block_start, (jump, Type::None, self.current_position)); let jump_back_distance = block_end - expression_start + 1; - let jump_back = Instruction::from(Jump { - offset: jump_back_distance, - is_positive: false, - }); + let jump_back = Instruction::jump(jump_back_distance, false); self.emit_instruction(jump_back, Type::None, self.current_position); @@ -1330,13 +1604,9 @@ impl<'src> Compiler<'src> { self.parse_expression()?; let actual_register = self.next_register() - 1; - let registers_to_close = actual_register - expected_register; - if registers_to_close > 0 { - let close = Instruction::from(Close { - from: expected_register, - to: actual_register, - }); + if expected_register < actual_register { + let close = Instruction::close(expected_register, actual_register); self.emit_instruction(close, Type::None, self.current_position); } @@ -1348,11 +1618,7 @@ impl<'src> Compiler<'src> { let destination = self.next_register(); let argument_count = destination - start_register; let return_type = function.r#type().return_type; - let call_native = Instruction::from(CallNative { - destination, - function, - argument_count, - }); + let call_native = Instruction::call_native(destination, function, argument_count); self.emit_instruction(call_native, return_type, Span(start, end)); @@ -1415,10 +1681,7 @@ impl<'src> Compiler<'src> { }; let end = self.current_position.1; let return_register = self.next_register() - 1; - let r#return = Instruction::from(Return { - should_return_value, - return_register, - }); + let r#return = Instruction::r#return(should_return_value, return_register); self.emit_instruction(r#return, Type::None, Span(start, end)); @@ -1433,7 +1696,7 @@ impl<'src> Compiler<'src> { let offset = offset as usize; if is_positive && offset + index == instruction_length - 1 { - *instruction = Instruction::jump((offset + 1) as u8, true); + *instruction = Instruction::jump((offset + 1) as u16, true); } } } @@ -1487,7 +1750,7 @@ impl<'src> Compiler<'src> { let offset = offset as usize; if is_positive && offset + index == instruction_length - 1 { - *instruction = Instruction::jump((offset + 1) as u8, true); + *instruction = Instruction::jump((offset + 1) as u16, true); } } } @@ -1567,9 +1830,9 @@ impl<'src> Compiler<'src> { }); }; - function_compiler.prototype_index = self.prototypes.len() as u8; + function_compiler.prototype_index = self.prototypes.len() as u16; - let mut value_parameters: Vec<(u8, Type)> = Vec::with_capacity(3); + let mut value_parameters: Vec<(u16, Type)> = Vec::with_capacity(3); while !function_compiler.allow(Token::RightParenthesis)? { let is_mutable = function_compiler.allow(Token::Mut)?; @@ -1657,7 +1920,7 @@ impl<'src> Compiler<'src> { ); } - let load_function = Instruction::load_function(destination, prototype_index); + let load_function = Instruction::load_function(destination, prototype_index, false); self.emit_instruction( load_function, @@ -1717,15 +1980,12 @@ impl<'src> Compiler<'src> { let registers_to_close = (actual_register - expected_register).saturating_sub(1); if registers_to_close > 0 { - let close = Instruction::from(Close { - from: expected_register, - to: actual_register, - }); + let close = Instruction::close(expected_register, actual_register); self.emit_instruction(close, Type::None, self.current_position); - } - argument_count += registers_to_close + 1; + argument_count += registers_to_close + 1; + } self.allow(Token::Comma)?; } diff --git a/dust-lang/src/compiler/optimize.rs b/dust-lang/src/compiler/optimize.rs index 83b529c..3d45bad 100644 --- a/dust-lang/src/compiler/optimize.rs +++ b/dust-lang/src/compiler/optimize.rs @@ -26,7 +26,7 @@ use crate::{Compiler, Instruction, Operation}; /// a `POINT` instruction to prevent the VM from encountering an empty register. /// /// The instructions must be in the following order: -/// - `EQUAL` | `LESS` | `LESS_EQUAL` | `TEST` +/// - `TEST` or any of the `EQUAL`, `LESS` or `LESS_EQUAL` instructions /// - `JUMP` /// - `LOAD_BOOLEAN` or `LOAD_CONSTANT` /// - `LOAD_BOOLEAN` or `LOAD_CONSTANT` @@ -37,7 +37,23 @@ pub fn control_flow_register_consolidation(compiler: &mut Compiler) { if !matches!( compiler.get_last_operations(), Some([ - Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL | Operation::TEST, + Operation::TEST + | Operation::EQUAL_INT + | Operation::EQUAL_STR + | Operation::EQUAL_BOOL + | Operation::EQUAL_BYTE + | Operation::EQUAL_CHAR + | Operation::EQUAL_FLOAT + | Operation::LESS_INT + | Operation::LESS_STR + | Operation::LESS_BYTE + | Operation::LESS_CHAR + | Operation::LESS_FLOAT + | Operation::LESS_EQUAL_INT + | Operation::LESS_EQUAL_STR + | Operation::LESS_EQUAL_BYTE + | Operation::LESS_EQUAL_CHAR + | Operation::LESS_EQUAL_FLOAT, Operation::JUMP, Operation::LOAD_BOOLEAN | Operation::LOAD_CONSTANT, Operation::LOAD_BOOLEAN | Operation::LOAD_CONSTANT, diff --git a/dust-lang/src/instruction/add.rs b/dust-lang/src/instruction/add.rs deleted file mode 100644 index d8648c2..0000000 --- a/dust-lang/src/instruction/add.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::{Argument, Instruction, Operation}; - -pub struct Add { - pub destination: u8, - pub left: Argument, - pub right: Argument, -} - -impl From for Add { - fn from(instruction: Instruction) -> Self { - let destination = instruction.a_field(); - let (left, right) = instruction.b_and_c_as_arguments(); - - Add { - destination, - left, - right, - } - } -} - -impl From for Instruction { - fn from(add: Add) -> Self { - let operation = Operation::ADD; - let a = add.destination; - let (b, b_is_constant) = add.left.as_index_and_constant_flag(); - let (c, c_is_constant) = add.right.as_index_and_constant_flag(); - - Instruction::new(operation, a, b, c, b_is_constant, c_is_constant, false) - } -} diff --git a/dust-lang/src/instruction/add_byte.rs b/dust-lang/src/instruction/add_byte.rs new file mode 100644 index 0000000..764badd --- /dev/null +++ b/dust-lang/src/instruction/add_byte.rs @@ -0,0 +1,54 @@ +use std::fmt::Display; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct AddByte { + pub destination: u16, + pub left: Operand, + pub right: Operand, +} + +impl From for AddByte { + fn from(instruction: Instruction) -> Self { + let destination = instruction.a_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + AddByte { + destination, + left, + right, + } + } +} + +impl From for Instruction { + fn from(add_byte: AddByte) -> Self { + let operation = Operation::ADD_BYTE; + let a_field = add_byte.destination; + let (b_field, b_is_constant) = add_byte.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = add_byte.right.as_index_and_constant_flag(); + + InstructionBuilder { + operation, + a_field, + b_field, + c_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for AddByte { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let AddByte { + destination, + left, + right, + } = self; + + write!(f, "R{} = {} + {}", destination, left, right) + } +} diff --git a/dust-lang/src/instruction/add_char.rs b/dust-lang/src/instruction/add_char.rs new file mode 100644 index 0000000..f0260ea --- /dev/null +++ b/dust-lang/src/instruction/add_char.rs @@ -0,0 +1,54 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct AddChar { + pub destination: u16, + pub left: Operand, + pub right: Operand, +} + +impl From for AddChar { + fn from(instruction: Instruction) -> Self { + let destination = instruction.a_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + AddChar { + destination, + left, + right, + } + } +} + +impl From for Instruction { + fn from(add_char: AddChar) -> Self { + let operation = Operation::ADD_CHAR; + let a_field = add_char.destination; + let (b_field, b_is_constant) = add_char.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = add_char.right.as_index_and_constant_flag(); + + InstructionBuilder { + operation, + a_field, + b_field, + c_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for AddChar { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let AddChar { + destination, + left, + right, + } = self; + + write!(f, "R{} = {} + {}", destination, left, right) + } +} diff --git a/dust-lang/src/instruction/add_char_str.rs b/dust-lang/src/instruction/add_char_str.rs new file mode 100644 index 0000000..4ffe95e --- /dev/null +++ b/dust-lang/src/instruction/add_char_str.rs @@ -0,0 +1,54 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct AddCharStr { + pub destination: u16, + pub left: Operand, + pub right: Operand, +} + +impl From for AddCharStr { + fn from(instruction: Instruction) -> Self { + let destination = instruction.a_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + AddCharStr { + destination, + left, + right, + } + } +} + +impl From for Instruction { + fn from(add_char_str: AddCharStr) -> Self { + let operation = Operation::ADD_CHAR_STR; + let a_field = add_char_str.destination; + let (b_field, b_is_constant) = add_char_str.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = add_char_str.right.as_index_and_constant_flag(); + + InstructionBuilder { + operation, + a_field, + b_field, + c_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for AddCharStr { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let AddCharStr { + destination, + left, + right, + } = self; + + write!(f, "R{} = {} + {}", destination, left, right) + } +} diff --git a/dust-lang/src/instruction/add_float.rs b/dust-lang/src/instruction/add_float.rs new file mode 100644 index 0000000..a8292f6 --- /dev/null +++ b/dust-lang/src/instruction/add_float.rs @@ -0,0 +1,54 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct AddFloat { + pub destination: u16, + pub left: Operand, + pub right: Operand, +} + +impl From for AddFloat { + fn from(instruction: Instruction) -> Self { + let destination = instruction.a_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + AddFloat { + destination, + left, + right, + } + } +} + +impl From for Instruction { + fn from(add_float: AddFloat) -> Self { + let operation = Operation::ADD_FLOAT; + let a_field = add_float.destination; + let (b_field, b_is_constant) = add_float.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = add_float.right.as_index_and_constant_flag(); + + InstructionBuilder { + operation, + a_field, + b_field, + c_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for AddFloat { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let AddFloat { + destination, + left, + right, + } = self; + + write!(f, "R{} = {} + {}", destination, left, right) + } +} diff --git a/dust-lang/src/instruction/add_int.rs b/dust-lang/src/instruction/add_int.rs new file mode 100644 index 0000000..21f2bf7 --- /dev/null +++ b/dust-lang/src/instruction/add_int.rs @@ -0,0 +1,56 @@ +use std::fmt::{self, Display, Formatter}; + +use crate::{Instruction, Operand, Operation}; + +use super::InstructionBuilder; + +pub struct AddInt { + pub destination: u16, + pub left: Operand, + pub right: Operand, +} + +impl From for AddInt { + fn from(instruction: Instruction) -> Self { + let destination = instruction.a_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + AddInt { + destination, + left, + right, + } + } +} + +impl From for Instruction { + fn from(add_int: AddInt) -> Self { + let operation = Operation::ADD_INT; + let a_field = add_int.destination; + let (b_field, b_is_constant) = add_int.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = add_int.right.as_index_and_constant_flag(); + + InstructionBuilder { + operation, + a_field, + b_field, + c_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for AddInt { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let AddInt { + destination, + left, + right, + } = self; + + write!(f, "R{} = {} + {}", destination, left, right) + } +} diff --git a/dust-lang/src/instruction/add_str.rs b/dust-lang/src/instruction/add_str.rs new file mode 100644 index 0000000..a43a3c9 --- /dev/null +++ b/dust-lang/src/instruction/add_str.rs @@ -0,0 +1,54 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct AddStr { + pub destination: u16, + pub left: Operand, + pub right: Operand, +} + +impl From for AddStr { + fn from(instruction: Instruction) -> Self { + let destination = instruction.a_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + AddStr { + destination, + left, + right, + } + } +} + +impl From for Instruction { + fn from(add_str: AddStr) -> Self { + let operation = Operation::ADD_STR; + let a_field = add_str.destination; + let (b_field, b_is_constant) = add_str.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = add_str.right.as_index_and_constant_flag(); + + InstructionBuilder { + operation, + a_field, + b_field, + c_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for AddStr { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let AddStr { + destination, + left, + right, + } = self; + + write!(f, "R{} = {} + {}", destination, left, right) + } +} diff --git a/dust-lang/src/instruction/add_str_char.rs b/dust-lang/src/instruction/add_str_char.rs new file mode 100644 index 0000000..8638fd8 --- /dev/null +++ b/dust-lang/src/instruction/add_str_char.rs @@ -0,0 +1,54 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct AddStrChar { + pub destination: u16, + pub left: Operand, + pub right: Operand, +} + +impl From for AddStrChar { + fn from(instruction: Instruction) -> Self { + let destination = instruction.a_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + AddStrChar { + destination, + left, + right, + } + } +} + +impl From for Instruction { + fn from(add_str_char: AddStrChar) -> Self { + let operation = Operation::ADD_STR_CHAR; + let a_field = add_str_char.destination; + let (b_field, b_is_constant) = add_str_char.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = add_str_char.right.as_index_and_constant_flag(); + + InstructionBuilder { + operation, + a_field, + b_field, + c_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for AddStrChar { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let AddStrChar { + destination, + left, + right, + } = self; + + write!(f, "R{} = {} + {}", destination, left, right) + } +} diff --git a/dust-lang/src/instruction/call.rs b/dust-lang/src/instruction/call.rs index dff0679..571bbb8 100644 --- a/dust-lang/src/instruction/call.rs +++ b/dust-lang/src/instruction/call.rs @@ -1,9 +1,11 @@ use crate::{Instruction, Operation}; +use super::InstructionBuilder; + pub struct Call { - pub destination: u8, - pub function_register: u8, - pub argument_count: u8, + pub destination: u16, + pub function_register: u16, + pub argument_count: u16, pub is_recursive: bool, } @@ -25,11 +27,19 @@ impl From for Call { impl From for Instruction { fn from(call: Call) -> Self { - let a = call.destination; - let b = call.function_register; - let c = call.argument_count; - let d = call.is_recursive; + let a_field = call.destination; + let b_field = call.function_register; + let c_field = call.argument_count; + let d_field = call.is_recursive; - Instruction::new(Operation::CALL, a, b, c, false, false, d) + InstructionBuilder { + operation: Operation::CALL, + a_field, + b_field, + c_field, + d_field, + ..Default::default() + } + .build() } } diff --git a/dust-lang/src/instruction/call_native.rs b/dust-lang/src/instruction/call_native.rs index d44a637..c40733a 100644 --- a/dust-lang/src/instruction/call_native.rs +++ b/dust-lang/src/instruction/call_native.rs @@ -1,9 +1,11 @@ use crate::{Instruction, NativeFunction, Operation}; +use super::InstructionBuilder; + pub struct CallNative { - pub destination: u8, + pub destination: u16, pub function: NativeFunction, - pub argument_count: u8, + pub argument_count: u16, } impl From for CallNative { @@ -22,10 +24,17 @@ impl From for CallNative { impl From for Instruction { fn from(call_native: CallNative) -> Self { let operation = Operation::CALL_NATIVE; - let a = call_native.destination; - let b = call_native.function as u8; - let c = call_native.argument_count; + let a_field = call_native.destination; + let b_field = call_native.function as u16; + let c_field = call_native.argument_count; - Instruction::new(operation, a, b, c, false, false, false) + InstructionBuilder { + operation, + a_field, + b_field, + c_field, + ..Default::default() + } + .build() } } diff --git a/dust-lang/src/instruction/close.rs b/dust-lang/src/instruction/close.rs index 304d88c..9174438 100644 --- a/dust-lang/src/instruction/close.rs +++ b/dust-lang/src/instruction/close.rs @@ -1,8 +1,12 @@ +use std::fmt::{self, Display, Formatter}; + use crate::{Instruction, Operation}; +use super::InstructionBuilder; + pub struct Close { - pub from: u8, - pub to: u8, + pub from: u16, + pub to: u16, } impl From for Close { @@ -17,8 +21,23 @@ impl From for Close { impl From for Instruction { fn from(close: Close) -> Self { let operation = Operation::CLOSE; - let (a, b, c) = (0, close.from, close.to); + let b_field = close.from; + let c_field = close.to; - Instruction::new(operation, a, b, c, false, false, false) + InstructionBuilder { + operation, + b_field, + c_field, + ..Default::default() + } + .build() + } +} + +impl Display for Close { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let Close { from, to } = self; + + write!(f, "{from}..={to}") } } diff --git a/dust-lang/src/instruction/divide.rs b/dust-lang/src/instruction/divide.rs deleted file mode 100644 index 55a3263..0000000 --- a/dust-lang/src/instruction/divide.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::{Argument, Instruction, Operation}; - -pub struct Divide { - pub destination: u8, - pub left: Argument, - pub right: Argument, -} - -impl From for Divide { - fn from(instruction: Instruction) -> Self { - let destination = instruction.a_field(); - let (left, right) = instruction.b_and_c_as_arguments(); - - Divide { - destination, - left, - right, - } - } -} - -impl From for Instruction { - fn from(divide: Divide) -> Self { - let operation = Operation::DIVIDE; - let a = divide.destination; - let (b, b_is_constant) = divide.left.as_index_and_constant_flag(); - let (c, c_is_constant) = divide.right.as_index_and_constant_flag(); - - Instruction::new(operation, a, b, c, b_is_constant, c_is_constant, false) - } -} diff --git a/dust-lang/src/instruction/divide_byte.rs b/dust-lang/src/instruction/divide_byte.rs new file mode 100644 index 0000000..ce2a4c4 --- /dev/null +++ b/dust-lang/src/instruction/divide_byte.rs @@ -0,0 +1,54 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct DivideByte { + pub destination: u16, + pub left: Operand, + pub right: Operand, +} + +impl From for DivideByte { + fn from(instruction: Instruction) -> Self { + let destination = instruction.a_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + DivideByte { + destination, + left, + right, + } + } +} + +impl From for Instruction { + fn from(divide_byte: DivideByte) -> Self { + let operation = Operation::DIVIDE_BYTE; + let a_field = divide_byte.destination; + let (b_field, b_is_constant) = divide_byte.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = divide_byte.right.as_index_and_constant_flag(); + + InstructionBuilder { + operation, + a_field, + b_field, + c_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for DivideByte { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let DivideByte { + destination, + left, + right, + } = self; + + write!(f, "R{} = {} ÷ {}", destination, left, right) + } +} diff --git a/dust-lang/src/instruction/divide_float.rs b/dust-lang/src/instruction/divide_float.rs new file mode 100644 index 0000000..aaaef22 --- /dev/null +++ b/dust-lang/src/instruction/divide_float.rs @@ -0,0 +1,54 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct DivideFloat { + pub destination: u16, + pub left: Operand, + pub right: Operand, +} + +impl From for DivideFloat { + fn from(instruction: Instruction) -> Self { + let destination = instruction.a_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + DivideFloat { + destination, + left, + right, + } + } +} + +impl From for Instruction { + fn from(divide_float: DivideFloat) -> Self { + let operation = Operation::DIVIDE_FLOAT; + let a_field = divide_float.destination; + let (b_field, b_is_constant) = divide_float.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = divide_float.right.as_index_and_constant_flag(); + + InstructionBuilder { + operation, + a_field, + b_field, + c_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for DivideFloat { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let DivideFloat { + destination, + left, + right, + } = self; + + write!(f, "R{} = {} ÷ {}", destination, left, right) + } +} diff --git a/dust-lang/src/instruction/divide_int.rs b/dust-lang/src/instruction/divide_int.rs new file mode 100644 index 0000000..b52ced0 --- /dev/null +++ b/dust-lang/src/instruction/divide_int.rs @@ -0,0 +1,54 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct DivideInt { + pub destination: u16, + pub left: Operand, + pub right: Operand, +} + +impl From for DivideInt { + fn from(instruction: Instruction) -> Self { + let destination = instruction.a_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + DivideInt { + destination, + left, + right, + } + } +} + +impl From for Instruction { + fn from(divide_int: DivideInt) -> Self { + let operation = Operation::DIVIDE_INT; + let a_field = divide_int.destination; + let (b_field, b_is_constant) = divide_int.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = divide_int.right.as_index_and_constant_flag(); + + InstructionBuilder { + operation, + a_field, + b_field, + c_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for DivideInt { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let DivideInt { + destination, + left, + right, + } = self; + + write!(f, "R{} = {} ÷ {}", destination, left, right) + } +} diff --git a/dust-lang/src/instruction/equal.rs b/dust-lang/src/instruction/equal.rs deleted file mode 100644 index 3b42c2e..0000000 --- a/dust-lang/src/instruction/equal.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::{Argument, Instruction, Operation}; - -pub struct Equal { - pub value: bool, - pub left: Argument, - pub right: Argument, -} - -impl From for Equal { - fn from(instruction: Instruction) -> Self { - let value = instruction.d_field(); - let (left, right) = instruction.b_and_c_as_arguments(); - - Equal { value, left, right } - } -} - -impl From for Instruction { - fn from(equal: Equal) -> Self { - let operation = Operation::EQUAL; - let (b, b_is_constant) = equal.left.as_index_and_constant_flag(); - let (c, c_is_constant) = equal.right.as_index_and_constant_flag(); - let d = equal.value; - - Instruction::new(operation, 0, b, c, b_is_constant, c_is_constant, d) - } -} diff --git a/dust-lang/src/instruction/equal_bool.rs b/dust-lang/src/instruction/equal_bool.rs new file mode 100644 index 0000000..03a0997 --- /dev/null +++ b/dust-lang/src/instruction/equal_bool.rs @@ -0,0 +1,55 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct EqualBool { + pub comparator: bool, + pub left: Operand, + pub right: Operand, +} + +impl From for EqualBool { + fn from(instruction: Instruction) -> Self { + let comparator = instruction.d_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + EqualBool { + comparator, + left, + right, + } + } +} + +impl From for Instruction { + fn from(equal_bool: EqualBool) -> Self { + let operation = Operation::EQUAL_BOOL; + let (b_field, b_is_constant) = equal_bool.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = equal_bool.right.as_index_and_constant_flag(); + let d_field = equal_bool.comparator; + + InstructionBuilder { + operation, + b_field, + c_field, + d_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for EqualBool { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let EqualBool { + comparator, + left, + right, + } = self; + let operator = if *comparator { "==" } else { "≠" }; + + write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") + } +} diff --git a/dust-lang/src/instruction/equal_byte.rs b/dust-lang/src/instruction/equal_byte.rs new file mode 100644 index 0000000..3be0ddb --- /dev/null +++ b/dust-lang/src/instruction/equal_byte.rs @@ -0,0 +1,55 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct EqualByte { + pub comparator: bool, + pub left: Operand, + pub right: Operand, +} + +impl From for EqualByte { + fn from(instruction: Instruction) -> Self { + let comparator = instruction.d_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + EqualByte { + comparator, + left, + right, + } + } +} + +impl From for Instruction { + fn from(equal_byte: EqualByte) -> Self { + let operation = Operation::EQUAL_BYTE; + let (b_field, b_is_constant) = equal_byte.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = equal_byte.right.as_index_and_constant_flag(); + let d_field = equal_byte.comparator; + + InstructionBuilder { + operation, + b_field, + c_field, + d_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for EqualByte { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let EqualByte { + comparator, + left, + right, + } = self; + let operator = if *comparator { "==" } else { "≠" }; + + write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") + } +} diff --git a/dust-lang/src/instruction/equal_char.rs b/dust-lang/src/instruction/equal_char.rs new file mode 100644 index 0000000..4e60a77 --- /dev/null +++ b/dust-lang/src/instruction/equal_char.rs @@ -0,0 +1,55 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct EqualChar { + pub comparator: bool, + pub left: Operand, + pub right: Operand, +} + +impl From for EqualChar { + fn from(instruction: Instruction) -> Self { + let comparator = instruction.d_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + EqualChar { + comparator, + left, + right, + } + } +} + +impl From for Instruction { + fn from(equal_char: EqualChar) -> Self { + let operation = Operation::EQUAL_CHAR; + let (b_field, b_is_constant) = equal_char.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = equal_char.right.as_index_and_constant_flag(); + let d_field = equal_char.comparator; + + InstructionBuilder { + operation, + b_field, + c_field, + d_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for EqualChar { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let EqualChar { + comparator, + left, + right, + } = self; + let operator = if *comparator { "==" } else { "≠" }; + + write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") + } +} diff --git a/dust-lang/src/instruction/equal_char_str.rs b/dust-lang/src/instruction/equal_char_str.rs new file mode 100644 index 0000000..eb15ae4 --- /dev/null +++ b/dust-lang/src/instruction/equal_char_str.rs @@ -0,0 +1,55 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct EqualCharStr { + pub comparator: bool, + pub left: Operand, + pub right: Operand, +} + +impl From for EqualCharStr { + fn from(instruction: Instruction) -> Self { + let comparator = instruction.d_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + EqualCharStr { + comparator, + left, + right, + } + } +} + +impl From for Instruction { + fn from(equal_char_str: EqualCharStr) -> Self { + let operation = Operation::EQUAL_CHAR_STR; + let (b_field, b_is_constant) = equal_char_str.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = equal_char_str.right.as_index_and_constant_flag(); + let d_field = equal_char_str.comparator; + + InstructionBuilder { + operation, + b_field, + c_field, + d_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for EqualCharStr { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let EqualCharStr { + comparator, + left, + right, + } = self; + let operator = if *comparator { "==" } else { "≠" }; + + write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") + } +} diff --git a/dust-lang/src/instruction/equal_float.rs b/dust-lang/src/instruction/equal_float.rs new file mode 100644 index 0000000..6149c2c --- /dev/null +++ b/dust-lang/src/instruction/equal_float.rs @@ -0,0 +1,55 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct EqualFloat { + pub comparator: bool, + pub left: Operand, + pub right: Operand, +} + +impl From for EqualFloat { + fn from(instruction: Instruction) -> Self { + let comparator = instruction.d_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + EqualFloat { + comparator, + left, + right, + } + } +} + +impl From for Instruction { + fn from(equal_float: EqualFloat) -> Self { + let operation = Operation::EQUAL_FLOAT; + let (b_field, b_is_constant) = equal_float.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = equal_float.right.as_index_and_constant_flag(); + let d_field = equal_float.comparator; + + InstructionBuilder { + operation, + b_field, + c_field, + d_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for EqualFloat { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let EqualFloat { + comparator, + left, + right, + } = self; + let operator = if *comparator { "==" } else { "≠" }; + + write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") + } +} diff --git a/dust-lang/src/instruction/equal_int.rs b/dust-lang/src/instruction/equal_int.rs new file mode 100644 index 0000000..cebc38d --- /dev/null +++ b/dust-lang/src/instruction/equal_int.rs @@ -0,0 +1,55 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct EqualInt { + pub comparator: bool, + pub left: Operand, + pub right: Operand, +} + +impl From for EqualInt { + fn from(instruction: Instruction) -> Self { + let comparator = instruction.d_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + EqualInt { + comparator, + left, + right, + } + } +} + +impl From for Instruction { + fn from(equal_int: EqualInt) -> Self { + let operation = Operation::EQUAL_INT; + let (b_field, b_is_constant) = equal_int.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = equal_int.right.as_index_and_constant_flag(); + let d_field = equal_int.comparator; + + InstructionBuilder { + operation, + b_field, + c_field, + d_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for EqualInt { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let EqualInt { + comparator, + left, + right, + } = self; + let operator = if *comparator { "==" } else { "≠" }; + + write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") + } +} diff --git a/dust-lang/src/instruction/equal_str.rs b/dust-lang/src/instruction/equal_str.rs new file mode 100644 index 0000000..f4cc0c4 --- /dev/null +++ b/dust-lang/src/instruction/equal_str.rs @@ -0,0 +1,55 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct EqualStr { + pub comparator: bool, + pub left: Operand, + pub right: Operand, +} + +impl From for EqualStr { + fn from(instruction: Instruction) -> Self { + let comparator = instruction.d_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + EqualStr { + comparator, + left, + right, + } + } +} + +impl From for Instruction { + fn from(equal_str: EqualStr) -> Self { + let operation = Operation::EQUAL_STR; + let (b_field, b_is_constant) = equal_str.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = equal_str.right.as_index_and_constant_flag(); + let d_field = equal_str.comparator; + + InstructionBuilder { + operation, + b_field, + c_field, + d_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for EqualStr { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let EqualStr { + comparator, + left, + right, + } = self; + let operator = if *comparator { "==" } else { "≠" }; + + write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") + } +} diff --git a/dust-lang/src/instruction/equal_str_char.rs b/dust-lang/src/instruction/equal_str_char.rs new file mode 100644 index 0000000..ccd0dd4 --- /dev/null +++ b/dust-lang/src/instruction/equal_str_char.rs @@ -0,0 +1,55 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct EqualStrChar { + pub comparator: bool, + pub left: Operand, + pub right: Operand, +} + +impl From for EqualStrChar { + fn from(instruction: Instruction) -> Self { + let comparator = instruction.d_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + EqualStrChar { + comparator, + left, + right, + } + } +} + +impl From for Instruction { + fn from(equal_str_char: EqualStrChar) -> Self { + let operation = Operation::EQUAL_STR_CHAR; + let (b_field, b_is_constant) = equal_str_char.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = equal_str_char.right.as_index_and_constant_flag(); + let d_field = equal_str_char.comparator; + + InstructionBuilder { + operation, + b_field, + c_field, + d_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for EqualStrChar { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let EqualStrChar { + comparator, + left, + right, + } = self; + let operator = if *comparator { "==" } else { "≠" }; + + write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") + } +} diff --git a/dust-lang/src/instruction/get_local.rs b/dust-lang/src/instruction/get_local.rs index ec8be9d..8aba567 100644 --- a/dust-lang/src/instruction/get_local.rs +++ b/dust-lang/src/instruction/get_local.rs @@ -1,8 +1,12 @@ +use std::fmt::{self, Display, Formatter}; + use crate::{Instruction, Operation}; +use super::InstructionBuilder; + pub struct GetLocal { - pub destination: u8, - pub local_index: u8, + pub destination: u16, + pub local_index: u16, } impl From for GetLocal { @@ -20,9 +24,26 @@ impl From for GetLocal { impl From for Instruction { fn from(get_local: GetLocal) -> Self { let operation = Operation::GET_LOCAL; - let a = get_local.destination; - let b = get_local.local_index; + let a_field = get_local.destination; + let b_field = get_local.local_index; - Instruction::new(operation, a, b, 0, false, false, false) + InstructionBuilder { + operation, + a_field, + b_field, + ..Default::default() + } + .build() + } +} + +impl Display for GetLocal { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let GetLocal { + destination, + local_index, + } = self; + + write!(f, "R{destination} = L{local_index}") } } diff --git a/dust-lang/src/instruction/jump.rs b/dust-lang/src/instruction/jump.rs index 8a4c150..cf55b7c 100644 --- a/dust-lang/src/instruction/jump.rs +++ b/dust-lang/src/instruction/jump.rs @@ -1,7 +1,9 @@ use crate::{Instruction, Operation}; +use super::InstructionBuilder; + pub struct Jump { - pub offset: u8, + pub offset: u16, pub is_positive: bool, } @@ -17,9 +19,15 @@ impl From for Jump { impl From for Instruction { fn from(jump: Jump) -> Self { let operation = Operation::JUMP; - let b = jump.offset; - let c = jump.is_positive as u8; + let b_field = jump.offset; + let c_field = jump.is_positive as u16; - Instruction::new(operation, 0, b, c, false, false, false) + InstructionBuilder { + operation, + b_field, + c_field, + ..Default::default() + } + .build() } } diff --git a/dust-lang/src/instruction/less.rs b/dust-lang/src/instruction/less.rs deleted file mode 100644 index f8a868e..0000000 --- a/dust-lang/src/instruction/less.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::{Argument, Instruction, Operation}; - -pub struct Less { - pub value: bool, - pub left: Argument, - pub right: Argument, -} - -impl From for Less { - fn from(instruction: Instruction) -> Self { - let value = instruction.d_field(); - let (left, right) = instruction.b_and_c_as_arguments(); - - Less { value, left, right } - } -} - -impl From for Instruction { - fn from(less: Less) -> Self { - let operation = Operation::LESS; - let (b, b_is_constant) = less.left.as_index_and_constant_flag(); - let (c, c_is_constant) = less.right.as_index_and_constant_flag(); - let d = less.value; - - Instruction::new(operation, 0, b, c, b_is_constant, c_is_constant, d) - } -} diff --git a/dust-lang/src/instruction/less_byte.rs b/dust-lang/src/instruction/less_byte.rs new file mode 100644 index 0000000..7b657f0 --- /dev/null +++ b/dust-lang/src/instruction/less_byte.rs @@ -0,0 +1,55 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct LessByte { + pub comparator: bool, + pub left: Operand, + pub right: Operand, +} + +impl From for LessByte { + fn from(instruction: Instruction) -> Self { + let comparator = instruction.d_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + LessByte { + comparator, + left, + right, + } + } +} + +impl From for Instruction { + fn from(less_byte: LessByte) -> Self { + let operation = Operation::LESS_BYTE; + let (b_field, b_is_constant) = less_byte.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = less_byte.right.as_index_and_constant_flag(); + let d_field = less_byte.comparator; + + InstructionBuilder { + operation, + b_field, + c_field, + d_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for LessByte { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let LessByte { + comparator, + left, + right, + } = self; + let operator = if *comparator { "<" } else { "≥" }; + + write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") + } +} diff --git a/dust-lang/src/instruction/less_char.rs b/dust-lang/src/instruction/less_char.rs new file mode 100644 index 0000000..0f26ccf --- /dev/null +++ b/dust-lang/src/instruction/less_char.rs @@ -0,0 +1,55 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct LessChar { + pub comparator: bool, + pub left: Operand, + pub right: Operand, +} + +impl From for LessChar { + fn from(instruction: Instruction) -> Self { + let comparator = instruction.d_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + LessChar { + comparator, + left, + right, + } + } +} + +impl From for Instruction { + fn from(less_char: LessChar) -> Self { + let operation = Operation::LESS_CHAR; + let (b_field, b_is_constant) = less_char.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = less_char.right.as_index_and_constant_flag(); + let d_field = less_char.comparator; + + InstructionBuilder { + operation, + b_field, + c_field, + d_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for LessChar { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let LessChar { + comparator, + left, + right, + } = self; + let operator = if *comparator { "<" } else { "≥" }; + + write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") + } +} diff --git a/dust-lang/src/instruction/less_equal.rs b/dust-lang/src/instruction/less_equal.rs deleted file mode 100644 index 6707804..0000000 --- a/dust-lang/src/instruction/less_equal.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::{Argument, Instruction, Operation}; - -pub struct LessEqual { - pub value: bool, - pub left: Argument, - pub right: Argument, -} - -impl From for LessEqual { - fn from(instruction: Instruction) -> Self { - let value = instruction.d_field(); - let (left, right) = instruction.b_and_c_as_arguments(); - - LessEqual { value, left, right } - } -} - -impl From for Instruction { - fn from(less_equal: LessEqual) -> Self { - let operation = Operation::LESS_EQUAL; - let (b, b_options) = less_equal.left.as_index_and_constant_flag(); - let (c, c_options) = less_equal.right.as_index_and_constant_flag(); - let d = less_equal.value; - - Instruction::new(operation, 0, b, c, b_options, c_options, d) - } -} diff --git a/dust-lang/src/instruction/less_equal_byte.rs b/dust-lang/src/instruction/less_equal_byte.rs new file mode 100644 index 0000000..08b1cac --- /dev/null +++ b/dust-lang/src/instruction/less_equal_byte.rs @@ -0,0 +1,55 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct LessEqualByte { + pub comparator: bool, + pub left: Operand, + pub right: Operand, +} + +impl From for LessEqualByte { + fn from(instruction: Instruction) -> Self { + let comparator = instruction.d_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + LessEqualByte { + comparator, + left, + right, + } + } +} + +impl From for Instruction { + fn from(less_equal_byte: LessEqualByte) -> Self { + let operation = Operation::LESS_EQUAL_BYTE; + let (b_field, b_is_constant) = less_equal_byte.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = less_equal_byte.right.as_index_and_constant_flag(); + let d_field = less_equal_byte.comparator; + + InstructionBuilder { + operation, + b_field, + c_field, + d_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for LessEqualByte { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let LessEqualByte { + comparator, + left, + right, + } = self; + let operator = if *comparator { "≤" } else { ">" }; + + write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") + } +} diff --git a/dust-lang/src/instruction/less_equal_char.rs b/dust-lang/src/instruction/less_equal_char.rs new file mode 100644 index 0000000..bd0777e --- /dev/null +++ b/dust-lang/src/instruction/less_equal_char.rs @@ -0,0 +1,55 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct LessEqualChar { + pub comparator: bool, + pub left: Operand, + pub right: Operand, +} + +impl From for LessEqualChar { + fn from(instruction: Instruction) -> Self { + let comparator = instruction.d_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + LessEqualChar { + comparator, + left, + right, + } + } +} + +impl From for Instruction { + fn from(less_equal_char: LessEqualChar) -> Self { + let operation = Operation::LESS_EQUAL_CHAR; + let (b_field, b_is_constant) = less_equal_char.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = less_equal_char.right.as_index_and_constant_flag(); + let d_field = less_equal_char.comparator; + + InstructionBuilder { + operation, + b_field, + c_field, + d_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for LessEqualChar { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let LessEqualChar { + comparator, + left, + right, + } = self; + let operator = if *comparator { "≤" } else { ">" }; + + write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") + } +} diff --git a/dust-lang/src/instruction/less_equal_float.rs b/dust-lang/src/instruction/less_equal_float.rs new file mode 100644 index 0000000..f552f0b --- /dev/null +++ b/dust-lang/src/instruction/less_equal_float.rs @@ -0,0 +1,55 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct LessEqualFloat { + pub comparator: bool, + pub left: Operand, + pub right: Operand, +} + +impl From for LessEqualFloat { + fn from(instruction: Instruction) -> Self { + let comparator = instruction.d_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + LessEqualFloat { + comparator, + left, + right, + } + } +} + +impl From for Instruction { + fn from(less_equal_float: LessEqualFloat) -> Self { + let operation = Operation::LESS_EQUAL_FLOAT; + let (b_field, b_is_constant) = less_equal_float.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = less_equal_float.right.as_index_and_constant_flag(); + let d_field = less_equal_float.comparator; + + InstructionBuilder { + operation, + b_field, + c_field, + d_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for LessEqualFloat { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let LessEqualFloat { + comparator, + left, + right, + } = self; + let operator = if *comparator { "≤" } else { ">" }; + + write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") + } +} diff --git a/dust-lang/src/instruction/less_equal_int.rs b/dust-lang/src/instruction/less_equal_int.rs new file mode 100644 index 0000000..ede40c8 --- /dev/null +++ b/dust-lang/src/instruction/less_equal_int.rs @@ -0,0 +1,55 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct LessEqualInt { + pub comparator: bool, + pub left: Operand, + pub right: Operand, +} + +impl From for LessEqualInt { + fn from(instruction: Instruction) -> Self { + let comparator = instruction.d_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + LessEqualInt { + comparator, + left, + right, + } + } +} + +impl From for Instruction { + fn from(less_equal_int: LessEqualInt) -> Self { + let operation = Operation::LESS_EQUAL_INT; + let (b_field, b_is_constant) = less_equal_int.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = less_equal_int.right.as_index_and_constant_flag(); + let d_field = less_equal_int.comparator; + + InstructionBuilder { + operation, + b_field, + c_field, + d_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for LessEqualInt { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let LessEqualInt { + comparator, + left, + right, + } = self; + let operator = if *comparator { "≤" } else { ">" }; + + write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") + } +} diff --git a/dust-lang/src/instruction/less_equal_str.rs b/dust-lang/src/instruction/less_equal_str.rs new file mode 100644 index 0000000..4b8ae2b --- /dev/null +++ b/dust-lang/src/instruction/less_equal_str.rs @@ -0,0 +1,55 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct LessEqualStr { + pub comparator: bool, + pub left: Operand, + pub right: Operand, +} + +impl From for LessEqualStr { + fn from(instruction: Instruction) -> Self { + let comparator = instruction.d_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + LessEqualStr { + comparator, + left, + right, + } + } +} + +impl From for Instruction { + fn from(less_equal_str: LessEqualStr) -> Self { + let operation = Operation::LESS_EQUAL_STR; + let (b_field, b_is_constant) = less_equal_str.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = less_equal_str.right.as_index_and_constant_flag(); + let d_field = less_equal_str.comparator; + + InstructionBuilder { + operation, + b_field, + c_field, + d_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for LessEqualStr { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let LessEqualStr { + comparator, + left, + right, + } = self; + let operator = if *comparator { "≤" } else { ">" }; + + write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") + } +} diff --git a/dust-lang/src/instruction/less_float.rs b/dust-lang/src/instruction/less_float.rs new file mode 100644 index 0000000..a5722fa --- /dev/null +++ b/dust-lang/src/instruction/less_float.rs @@ -0,0 +1,55 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct LessFloat { + pub comparator: bool, + pub left: Operand, + pub right: Operand, +} + +impl From for LessFloat { + fn from(instruction: Instruction) -> Self { + let comparator = instruction.d_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + LessFloat { + comparator, + left, + right, + } + } +} + +impl From for Instruction { + fn from(less_float: LessFloat) -> Self { + let operation = Operation::LESS_FLOAT; + let (b_field, b_is_constant) = less_float.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = less_float.right.as_index_and_constant_flag(); + let d_field = less_float.comparator; + + InstructionBuilder { + operation, + b_field, + c_field, + d_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for LessFloat { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let LessFloat { + comparator, + left, + right, + } = self; + let operator = if *comparator { "<" } else { "≥" }; + + write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") + } +} diff --git a/dust-lang/src/instruction/less_int.rs b/dust-lang/src/instruction/less_int.rs new file mode 100644 index 0000000..c693d94 --- /dev/null +++ b/dust-lang/src/instruction/less_int.rs @@ -0,0 +1,55 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct LessInt { + pub comparator: bool, + pub left: Operand, + pub right: Operand, +} + +impl From for LessInt { + fn from(instruction: Instruction) -> Self { + let comparator = instruction.d_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + LessInt { + comparator, + left, + right, + } + } +} + +impl From for Instruction { + fn from(less_int: LessInt) -> Self { + let operation = Operation::LESS_INT; + let (b_field, b_is_constant) = less_int.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = less_int.right.as_index_and_constant_flag(); + let d_field = less_int.comparator; + + InstructionBuilder { + operation, + b_field, + c_field, + d_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for LessInt { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let LessInt { + comparator, + left, + right, + } = self; + let operator = if *comparator { "<" } else { "≥" }; + + write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") + } +} diff --git a/dust-lang/src/instruction/less_str.rs b/dust-lang/src/instruction/less_str.rs new file mode 100644 index 0000000..c26b494 --- /dev/null +++ b/dust-lang/src/instruction/less_str.rs @@ -0,0 +1,55 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct LessStr { + pub comparator: bool, + pub left: Operand, + pub right: Operand, +} + +impl From for LessStr { + fn from(instruction: Instruction) -> Self { + let comparator = instruction.d_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + LessStr { + comparator, + left, + right, + } + } +} + +impl From for Instruction { + fn from(less_str: LessStr) -> Self { + let operation = Operation::LESS_STR; + let (b_field, b_is_constant) = less_str.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = less_str.right.as_index_and_constant_flag(); + let d_field = less_str.comparator; + + InstructionBuilder { + operation, + b_field, + c_field, + d_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for LessStr { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let LessStr { + comparator, + left, + right, + } = self; + let operator = if *comparator { "<" } else { "≥" }; + + write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") + } +} diff --git a/dust-lang/src/instruction/load_boolean.rs b/dust-lang/src/instruction/load_boolean.rs index 9742d70..f14ff3c 100644 --- a/dust-lang/src/instruction/load_boolean.rs +++ b/dust-lang/src/instruction/load_boolean.rs @@ -1,7 +1,11 @@ +use std::fmt::{self, Display, Formatter}; + use crate::{Instruction, Operation}; +use super::InstructionBuilder; + pub struct LoadBoolean { - pub destination: u8, + pub destination: u16, pub value: bool, pub jump_next: bool, } @@ -19,10 +23,35 @@ impl From for LoadBoolean { impl From for Instruction { fn from(load_boolean: LoadBoolean) -> Self { let operation = Operation::LOAD_BOOLEAN; - let a = load_boolean.destination; - let b = load_boolean.value as u8; - let c = load_boolean.jump_next as u8; + let a_field = load_boolean.destination; + let b_field = load_boolean.value as u16; + let c_field = load_boolean.jump_next as u16; - Instruction::new(operation, a, b, c, false, false, false) + InstructionBuilder { + operation, + a_field, + b_field, + c_field, + ..Default::default() + } + .build() + } +} + +impl Display for LoadBoolean { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let LoadBoolean { + destination, + value, + jump_next, + } = self; + + write!(f, "R{destination} = {value}")?; + + if *jump_next { + write!(f, " JUMP +1")?; + } + + Ok(()) } } diff --git a/dust-lang/src/instruction/load_constant.rs b/dust-lang/src/instruction/load_constant.rs index 2940a6c..f26b4c2 100644 --- a/dust-lang/src/instruction/load_constant.rs +++ b/dust-lang/src/instruction/load_constant.rs @@ -2,9 +2,11 @@ use std::fmt::{self, Display, Formatter}; use crate::{Instruction, Operation}; +use super::InstructionBuilder; + pub struct LoadConstant { - pub destination: u8, - pub constant_index: u8, + pub destination: u16, + pub constant_index: u16, pub jump_next: bool, } @@ -24,12 +26,14 @@ impl From for LoadConstant { impl From for Instruction { fn from(load_constant: LoadConstant) -> Self { - let operation = Operation::LOAD_CONSTANT; - let a = load_constant.destination; - let b = load_constant.constant_index; - let c = load_constant.jump_next as u8; - - Instruction::new(operation, a, b, c, false, false, false) + InstructionBuilder { + operation: Operation::LOAD_CONSTANT, + a_field: load_constant.destination, + b_field: load_constant.constant_index, + c_field: load_constant.jump_next as u16, + ..Default::default() + } + .build() } } @@ -41,12 +45,12 @@ impl Display for LoadConstant { jump_next, } = self; - write!(f, "R{destination} = Constant {constant_index}")?; + write!(f, "R{destination} = C{constant_index}")?; if *jump_next { - write!(f, " JUMP +1") - } else { - Ok(()) + write!(f, " JUMP +1")?; } + + Ok(()) } } diff --git a/dust-lang/src/instruction/load_function.rs b/dust-lang/src/instruction/load_function.rs index d1ec669..1692e02 100644 --- a/dust-lang/src/instruction/load_function.rs +++ b/dust-lang/src/instruction/load_function.rs @@ -1,40 +1,54 @@ use std::fmt::{self, Display, Formatter}; -use super::{Instruction, Operation}; +use super::{Instruction, InstructionBuilder, Operation}; pub struct LoadFunction { - pub destination: u8, - pub prototype_index: u8, + pub destination: u16, + pub prototype_index: u16, + pub jump_next: bool, } impl From for LoadFunction { fn from(instruction: Instruction) -> Self { let destination = instruction.a_field(); - let record_index = instruction.b_field(); + let prototype_index = instruction.b_field(); + let jump_next = instruction.c_field() != 0; LoadFunction { destination, - prototype_index: record_index, + prototype_index, + jump_next, } } } impl From for Instruction { fn from(load_function: LoadFunction) -> Self { - Instruction::new( - Operation::LOAD_FUNCTION, - load_function.destination, - load_function.prototype_index, - 0, - false, - false, - false, - ) + InstructionBuilder { + operation: Operation::LOAD_FUNCTION, + a_field: load_function.destination, + b_field: load_function.prototype_index, + c_field: load_function.jump_next as u16, + ..Default::default() + } + .build() } } impl Display for LoadFunction { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "R{} = F{}", self.destination, self.prototype_index) + let LoadFunction { + destination, + prototype_index, + jump_next, + } = self; + + write!(f, "R{destination} = P{prototype_index}")?; + + if *jump_next { + write!(f, " JUMP +1")?; + } + + Ok(()) } } diff --git a/dust-lang/src/instruction/load_list.rs b/dust-lang/src/instruction/load_list.rs index 2812cb4..d891ec5 100644 --- a/dust-lang/src/instruction/load_list.rs +++ b/dust-lang/src/instruction/load_list.rs @@ -1,28 +1,56 @@ +use std::fmt::{self, Display, Formatter}; + use crate::{Instruction, Operation}; +use super::InstructionBuilder; + pub struct LoadList { - pub destination: u8, - pub start_register: u8, + pub destination: u16, + pub start_register: u16, + pub jump_next: bool, } impl From for LoadList { fn from(instruction: Instruction) -> Self { let destination = instruction.a_field(); let start_register = instruction.b_field(); + let jump_next = instruction.c_field() != 0; LoadList { destination, start_register, + jump_next, } } } impl From for Instruction { fn from(load_list: LoadList) -> Self { - let operation = Operation::LOAD_LIST; - let a = load_list.destination; - let b = load_list.start_register; - - Instruction::new(operation, a, b, 0, false, false, false) + InstructionBuilder { + operation: Operation::LOAD_LIST, + a_field: load_list.destination, + b_field: load_list.start_register, + c_field: load_list.jump_next as u16, + ..Default::default() + } + .build() + } +} + +impl Display for LoadList { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let LoadList { + destination, + start_register, + jump_next, + } = self; + + write!(f, "R{destination} = [R{start_register}..R{destination}]")?; + + if *jump_next { + write!(f, " JUMP +1")?; + } + + Ok(()) } } diff --git a/dust-lang/src/instruction/load_self.rs b/dust-lang/src/instruction/load_self.rs index 9e5b999..1cb10e2 100644 --- a/dust-lang/src/instruction/load_self.rs +++ b/dust-lang/src/instruction/load_self.rs @@ -1,22 +1,51 @@ +use std::fmt::{self, Display, Formatter}; + use crate::{Instruction, Operation}; +use super::InstructionBuilder; + pub struct LoadSelf { - pub destination: u8, + pub destination: u16, + pub jump_next: bool, } impl From for LoadSelf { fn from(instruction: Instruction) -> Self { let destination = instruction.a_field(); + let jump_next = instruction.c_field() != 0; - LoadSelf { destination } + LoadSelf { + destination, + jump_next, + } } } impl From for Instruction { fn from(load_self: LoadSelf) -> Self { - let operation = Operation::LOAD_SELF; - let a = load_self.destination; - - Instruction::new(operation, a, 0, 0, false, false, false) + InstructionBuilder { + operation: Operation::LOAD_SELF, + a_field: load_self.destination, + c_field: load_self.jump_next as u16, + ..Default::default() + } + .build() + } +} + +impl Display for LoadSelf { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let LoadSelf { + destination, + jump_next, + } = self; + + write!(f, "R{destination} = SELF")?; + + if *jump_next { + write!(f, " JUMP +1")?; + } + + Ok(()) } } diff --git a/dust-lang/src/instruction/mod.rs b/dust-lang/src/instruction/mod.rs index d1e6afc..e99bc21 100644 --- a/dust-lang/src/instruction/mod.rs +++ b/dust-lang/src/instruction/mod.rs @@ -1,14 +1,17 @@ //! The Dust instruction set. //! -//! Each instruction is 64 bits and uses up to eight distinct fields. The instruction's layout is: +//! Each instruction is 64 bits and uses up to seven distinct fields. +//! +//! # Layout //! //! Bits | Description //! ----- | ----------- -//! 0-4 | Operation code -//! 5 | Flag indicating if the B field is a constant -//! 6 | Flag indicating if the C field is a constant -//! 7 | D field (boolean) -//! 8-15 | Type specifier +//! 0-6 | Operation +//! 7 | Unused +//! 8 | Flag indicating if the B field is a constant +//! 9 | Flag indicating if the C field is a constant +//! 10 | D field (boolean) +//! 11-15 | Unused //! 16-31 | A field (unsigned 16-bit integer) //! 32-47 | B field (unsigned 16-bit integer) //! 48-63 | C field (unsigned 16-bit integer) @@ -61,7 +64,7 @@ //! To read an instruction, check its operation with [`Instruction::operation`], then convert the //! instruction to the struct that corresponds to that operation. Like the example above, this //! removes the burden of dealing with the options directly and automatically casts the A, B, C and -//! D fields as `u8`, `bool` or `Argument` values. +//! D fields as `u16`, `bool` or `Argument` values. //! //! ``` //! # use dust_lang::instruction::{Instruction, Add, Argument, Operation}; @@ -90,58 +93,117 @@ //! //! assert!(is_add_assign); //! ``` -mod add; +mod add_byte; +mod add_char; +mod add_char_str; +mod add_float; +mod add_int; +mod add_str; +mod add_str_char; mod call; mod call_native; mod close; -mod divide; -mod equal; +mod divide_byte; +mod divide_float; +mod divide_int; +mod equal_bool; +mod equal_byte; +mod equal_char; +mod equal_char_str; +mod equal_float; +mod equal_int; +mod equal_str; +mod equal_str_char; mod get_local; mod jump; -mod less; -mod less_equal; +mod less_byte; +mod less_char; +mod less_equal_byte; +mod less_equal_char; +mod less_equal_float; +mod less_equal_int; +mod less_equal_str; +mod less_float; +mod less_int; +mod less_str; mod load_boolean; mod load_constant; mod load_function; mod load_list; mod load_self; -mod modulo; -mod multiply; -mod negate; +mod modulo_byte; +mod modulo_float; +mod modulo_int; +mod multiply_byte; +mod multiply_float; +mod multiply_int; +mod negate_float; +mod negate_int; mod not; mod operation; mod point; mod r#return; mod set_local; -mod subtract; +mod subtract_byte; +mod subtract_float; +mod subtract_int; mod test; mod test_set; -mod type_code; -pub use add::Add; +pub use add_byte::AddByte; +pub use add_char::AddChar; +pub use add_char_str::AddCharStr; +pub use add_float::AddFloat; +pub use add_int::AddInt; +pub use add_str::AddStr; +pub use add_str_char::AddStrChar; pub use call::Call; pub use call_native::CallNative; pub use close::Close; -pub use divide::Divide; -pub use equal::Equal; +pub use divide_byte::DivideByte; +pub use divide_float::DivideFloat; +pub use divide_int::DivideInt; +pub use equal_bool::EqualBool; +pub use equal_byte::EqualByte; +pub use equal_char::EqualChar; +pub use equal_char_str::EqualCharStr; +pub use equal_float::EqualFloat; +pub use equal_int::EqualInt; +pub use equal_str::EqualStr; +pub use equal_str_char::EqualStrChar; pub use get_local::GetLocal; pub use jump::Jump; -pub use less::Less; -pub use less_equal::LessEqual; +pub use less_byte::LessByte; +pub use less_char::LessChar; +pub use less_equal_byte::LessEqualByte; +pub use less_equal_char::LessEqualChar; +pub use less_equal_float::LessEqualFloat; +pub use less_equal_int::LessEqualInt; +pub use less_equal_str::LessEqualStr; +pub use less_float::LessFloat; +pub use less_int::LessInt; +pub use less_str::LessStr; pub use load_boolean::LoadBoolean; pub use load_constant::LoadConstant; pub use load_function::LoadFunction; pub use load_list::LoadList; pub use load_self::LoadSelf; -pub use modulo::Modulo; -pub use multiply::Multiply; -pub use negate::Negate; +pub use modulo_byte::ModuloByte; +pub use modulo_float::ModuloFloat; +pub use modulo_int::ModuloInt; +pub use multiply_byte::MultiplyByte; +pub use multiply_float::MultiplyFloat; +pub use multiply_int::MultiplyInt; +pub use negate_float::NegateFloat; +pub use negate_int::NegateInt; pub use not::Not; pub use operation::Operation; pub use point::Point; pub use r#return::Return; pub use set_local::SetLocal; -pub use subtract::Subtract; +pub use subtract_byte::SubtractByte; +pub use subtract_float::SubtractFloat; +pub use subtract_int::SubtractInt; pub use test::Test; pub use test_set::TestSet; @@ -150,6 +212,44 @@ use std::fmt::{self, Debug, Display, Formatter}; use crate::NativeFunction; +pub struct InstructionBuilder { + pub operation: Operation, + pub a_field: u16, + pub b_field: u16, + pub c_field: u16, + pub d_field: bool, + pub b_is_constant: bool, + pub c_is_constant: bool, +} + +impl InstructionBuilder { + pub fn build(self) -> Instruction { + let bits = self.operation.0 as u64 + | ((self.b_is_constant as u64) << 7) + | ((self.c_is_constant as u64) << 8) + | ((self.d_field as u64) << 9) + | ((self.a_field as u64) << 31) + | ((self.b_field as u64) << 47) + | ((self.c_field as u64) << 63); + + Instruction(bits) + } +} + +impl Default for InstructionBuilder { + fn default() -> Self { + InstructionBuilder { + operation: Operation::POINT, + a_field: 0, + b_field: 0, + c_field: 0, + d_field: false, + b_is_constant: false, + c_is_constant: false, + } + } +} + /// An operation and its arguments for the Dust virtual machine. /// /// See the [module-level documentation](index.html) for more information. @@ -157,88 +257,57 @@ use crate::NativeFunction; pub struct Instruction(u64); impl Instruction { - pub fn new( - operation: Operation, - type_specifier: u8, - a: u16, - b: u16, - c: u16, - d: bool, - b_is_constant: bool, - c_is_constant: bool, - ) -> Instruction { - let bits = operation.0 as u64 - | ((b_is_constant as u64) << 5) - | ((c_is_constant as u64) << 6) - | ((d as u64) << 7) - | ((type_specifier as u64) << 15) - | ((a as u64) << 31) - | ((b as u64) << 47) - | ((c as u64) << 63); - - Instruction(bits) - } - pub fn operation(&self) -> Operation { - let operation_bits = self.0 & 0b0001_1111; + let first_byte = (self.0 & 0b0111_1111) as u8; - Operation(operation_bits as u8) + Operation(first_byte) } pub fn b_is_constant(&self) -> bool { - (self.0 >> 5) & 1 == 1 + (self.0 >> 8) & 1 == 0 } pub fn c_is_constant(&self) -> bool { - (self.0 >> 6) & 1 == 1 + (self.0 >> 9) & 1 == 0 } pub fn d_field(&self) -> bool { - (self.0 >> 7) & 1 == 1 + (self.0 >> 10) & 1 == 0 } - pub fn a_field(&self) -> u8 { - (self.0 >> 8) as u8 + pub fn a_field(&self) -> u16 { + ((self.0 >> 31) & 0xFFFF) as u16 } - pub fn b_field(&self) -> u8 { - (self.0 >> 16) as u8 + pub fn b_field(&self) -> u16 { + ((self.0 >> 47) & 0xFFFF) as u16 } - pub fn c_field(&self) -> u8 { - (self.0 >> 24) as u8 + pub fn c_field(&self) -> u16 { + (self.0 >> 48) as u16 } - pub fn set_a_field(&mut self, bits: u8) { - self.0 &= 0xFFFFFFFF00000000 | ((bits as u64) << 31); + pub fn set_a_field(&mut self, bits: u16) { + self.0 = (bits as u64) << 31; } - pub fn set_b_field(&mut self, bits: u8) { - self.0 &= 0xFFFF0000FFFFFFFF | ((bits as u64) << 47); + pub fn set_b_field(&mut self, bits: u16) { + self.0 = (bits as u64) << 47; } - pub fn set_c_field(&mut self, bits: u8) {} - - pub fn decode(self) -> (Operation, InstructionData) { - (self.operation(), InstructionData { - a_field: self.a_field(), - b_field: self.b_field(), - c_field: self.c_field(), - b_is_constant: self.b_is_constant(), - c_is_constant: self.c_is_constant(), - d_field: self.d_field(), - }) + pub fn set_c_field(&mut self, bits: u16) { + self.0 = (bits as u64) << 63; } - pub fn point(from: u8, to: u8) -> Instruction { + pub fn point(from: u16, to: u16) -> Instruction { Instruction::from(Point { from, to }) } - pub fn close(from: u8, to: u8) -> Instruction { + pub fn close(from: u16, to: u16) -> Instruction { Instruction::from(Close { from, to }) } - pub fn load_boolean(destination: u8, value: bool, jump_next: bool) -> Instruction { + pub fn load_boolean(destination: u16, value: bool, jump_next: bool) -> Instruction { Instruction::from(LoadBoolean { destination, value, @@ -246,7 +315,7 @@ impl Instruction { }) } - pub fn load_constant(destination: u8, constant_index: u8, jump_next: bool) -> Instruction { + pub fn load_constant(destination: u16, constant_index: u16, jump_next: bool) -> Instruction { Instruction::from(LoadConstant { destination, constant_index, @@ -254,86 +323,368 @@ impl Instruction { }) } - pub fn load_function(destination: u8, prototype_index: u8) -> Instruction { + pub fn load_function(destination: u16, prototype_index: u16, jump_next: bool) -> Instruction { Instruction::from(LoadFunction { destination, prototype_index, + jump_next, }) } - pub fn load_list(destination: u8, start_register: u8) -> Instruction { + pub fn load_list(destination: u16, start_register: u16, jump_next: bool) -> Instruction { Instruction::from(LoadList { destination, start_register, + jump_next, }) } - pub fn load_self(destination: u8) -> Instruction { - Instruction::from(LoadSelf { destination }) + pub fn load_self(destination: u16, jump_next: bool) -> Instruction { + Instruction::from(LoadSelf { + destination, + jump_next, + }) } - pub fn get_local(destination: u8, local_index: u8) -> Instruction { + pub fn get_local(destination: u16, local_index: u16) -> Instruction { Instruction::from(GetLocal { destination, local_index, }) } - pub fn set_local(register: u8, local_index: u8) -> Instruction { + pub fn set_local(register: u16, local_index: u16) -> Instruction { Instruction::from(SetLocal { local_index, register_index: register, }) } - pub fn add(destination: u8, left: Argument, right: Argument) -> Instruction { - Instruction::from(Add { + pub fn add_int(destination: u16, left: Operand, right: Operand) -> Instruction { + Instruction::from(AddInt { destination, left, right, }) } - pub fn subtract(destination: u8, left: Argument, right: Argument) -> Instruction { - Instruction::from(Subtract { + pub fn add_float(destination: u16, left: Operand, right: Operand) -> Instruction { + Instruction::from(AddFloat { destination, left, right, }) } - pub fn multiply(destination: u8, left: Argument, right: Argument) -> Instruction { - Instruction::from(Multiply { + pub fn add_byte(destination: u16, left: Operand, right: Operand) -> Instruction { + Instruction::from(AddByte { destination, left, right, }) } - pub fn divide(destination: u8, left: Argument, right: Argument) -> Instruction { - Instruction::from(Divide { + pub fn add_str(destination: u16, left: Operand, right: Operand) -> Instruction { + Instruction::from(AddStr { destination, left, right, }) } - pub fn modulo(destination: u8, left: Argument, right: Argument) -> Instruction { - Instruction::from(Modulo { + pub fn add_char(destination: u16, left: Operand, right: Operand) -> Instruction { + Instruction::from(AddChar { destination, left, right, }) } - pub fn test(operand_register: u8, value: bool) -> Instruction { + pub fn add_str_char(destination: u16, left: Operand, right: Operand) -> Instruction { + Instruction::from(AddStrChar { + destination, + left, + right, + }) + } + + pub fn add_char_str(destination: u16, left: Operand, right: Operand) -> Instruction { + Instruction::from(AddCharStr { + destination, + left, + right, + }) + } + + pub fn subtract_int(destination: u16, left: Operand, right: Operand) -> Instruction { + Instruction::from(SubtractInt { + destination, + left, + right, + }) + } + + pub fn subtract_float(destination: u16, left: Operand, right: Operand) -> Instruction { + Instruction::from(SubtractFloat { + destination, + left, + right, + }) + } + + pub fn subtract_byte(destination: u16, left: Operand, right: Operand) -> Instruction { + Instruction::from(SubtractByte { + destination, + left, + right, + }) + } + + pub fn multiply_int(destination: u16, left: Operand, right: Operand) -> Instruction { + Instruction::from(MultiplyInt { + destination, + left, + right, + }) + } + + pub fn multiply_float(destination: u16, left: Operand, right: Operand) -> Instruction { + Instruction::from(MultiplyFloat { + destination, + left, + right, + }) + } + + pub fn multiply_byte(destination: u16, left: Operand, right: Operand) -> Instruction { + Instruction::from(MultiplyByte { + destination, + left, + right, + }) + } + + pub fn divide_int(destination: u16, left: Operand, right: Operand) -> Instruction { + Instruction::from(DivideInt { + destination, + left, + right, + }) + } + + pub fn divide_float(destination: u16, left: Operand, right: Operand) -> Instruction { + Instruction::from(DivideFloat { + destination, + left, + right, + }) + } + + pub fn divide_byte(destination: u16, left: Operand, right: Operand) -> Instruction { + Instruction::from(DivideByte { + destination, + left, + right, + }) + } + + pub fn modulo_int(destination: u16, left: Operand, right: Operand) -> Instruction { + Instruction::from(ModuloInt { + destination, + left, + right, + }) + } + + pub fn modulo_float(destination: u16, left: Operand, right: Operand) -> Instruction { + Instruction::from(ModuloFloat { + destination, + left, + right, + }) + } + + pub fn modulo_byte(destination: u16, left: Operand, right: Operand) -> Instruction { + Instruction::from(ModuloByte { + destination, + left, + right, + }) + } + + pub fn equal_int(comparator: bool, left: Operand, right: Operand) -> Instruction { + Instruction::from(EqualInt { + comparator, + left, + right, + }) + } + + pub fn equal_float(comparator: bool, left: Operand, right: Operand) -> Instruction { + Instruction::from(EqualFloat { + comparator, + left, + right, + }) + } + + pub fn equal_byte(comparator: bool, left: Operand, right: Operand) -> Instruction { + Instruction::from(EqualByte { + comparator, + left, + right, + }) + } + + pub fn equal_str(comparator: bool, left: Operand, right: Operand) -> Instruction { + Instruction::from(EqualStr { + comparator, + left, + right, + }) + } + + pub fn equal_char(comparator: bool, left: Operand, right: Operand) -> Instruction { + Instruction::from(EqualChar { + comparator, + left, + right, + }) + } + + pub fn equal_str_char(comparator: bool, left: Operand, right: Operand) -> Instruction { + Instruction::from(EqualStrChar { + comparator, + left, + right, + }) + } + + pub fn equal_char_str(comparator: bool, left: Operand, right: Operand) -> Instruction { + Instruction::from(EqualCharStr { + comparator, + left, + right, + }) + } + + pub fn equal_bool(comparator: bool, left: Operand, right: Operand) -> Instruction { + Instruction::from(EqualBool { + comparator, + left, + right, + }) + } + + pub fn less_int(comparator: bool, left: Operand, right: Operand) -> Instruction { + Instruction::from(LessInt { + comparator, + left, + right, + }) + } + + pub fn less_float(comparator: bool, left: Operand, right: Operand) -> Instruction { + Instruction::from(LessFloat { + comparator, + left, + right, + }) + } + + pub fn less_byte(comparator: bool, left: Operand, right: Operand) -> Instruction { + Instruction::from(LessByte { + comparator, + left, + right, + }) + } + + pub fn less_str(comparator: bool, left: Operand, right: Operand) -> Instruction { + Instruction::from(LessStr { + comparator, + left, + right, + }) + } + + pub fn less_char(comparator: bool, left: Operand, right: Operand) -> Instruction { + Instruction::from(LessChar { + comparator, + left, + right, + }) + } + + pub fn less_equal_int(comparator: bool, left: Operand, right: Operand) -> Instruction { + Instruction::from(LessEqualInt { + comparator, + left, + right, + }) + } + + pub fn less_equal_float(comparator: bool, left: Operand, right: Operand) -> Instruction { + Instruction::from(LessEqualFloat { + comparator, + left, + right, + }) + } + + pub fn less_equal_byte(comparator: bool, left: Operand, right: Operand) -> Instruction { + Instruction::from(LessEqualByte { + comparator, + left, + right, + }) + } + + pub fn less_equal_str(comparator: bool, left: Operand, right: Operand) -> Instruction { + Instruction::from(LessEqualStr { + comparator, + left, + right, + }) + } + + pub fn less_equal_char(comparator: bool, left: Operand, right: Operand) -> Instruction { + Instruction::from(LessEqualChar { + comparator, + left, + right, + }) + } + + pub fn negate_int(destination: u16, argument: Operand) -> Instruction { + Instruction::from(NegateInt { + destination, + argument, + }) + } + + pub fn negate_float(destination: u16, argument: Operand) -> Instruction { + Instruction::from(NegateFloat { + destination, + argument, + }) + } + + pub fn not(destination: u16, argument: Operand) -> Instruction { + Instruction::from(Not { + destination, + argument, + }) + } + + pub fn test(operand_register: u16, value: bool) -> Instruction { Instruction::from(Test { operand_register, test_value: value, }) } - pub fn test_set(destination: u8, argument: Argument, value: bool) -> Instruction { + pub fn test_set(destination: u16, argument: Operand, value: bool) -> Instruction { Instruction::from(TestSet { destination, argument, @@ -341,33 +692,7 @@ impl Instruction { }) } - pub fn equal(value: bool, left: Argument, right: Argument) -> Instruction { - Instruction::from(Equal { value, left, right }) - } - - pub fn less(value: bool, left: Argument, right: Argument) -> Instruction { - Instruction::from(Less { value, left, right }) - } - - pub fn less_equal(value: bool, left: Argument, right: Argument) -> Instruction { - Instruction::from(LessEqual { value, left, right }) - } - - pub fn negate(destination: u8, argument: Argument) -> Instruction { - Instruction::from(Negate { - destination, - argument, - }) - } - - pub fn not(destination: u8, argument: Argument) -> Instruction { - Instruction::from(Not { - destination, - argument, - }) - } - - pub fn jump(offset: u8, is_positive: bool) -> Instruction { + pub fn jump(offset: u16, is_positive: bool) -> Instruction { Instruction::from(Jump { offset, is_positive, @@ -375,9 +700,9 @@ impl Instruction { } pub fn call( - destination: u8, - function_register: u8, - argument_count: u8, + destination: u16, + function_register: u16, + argument_count: u16, is_recursive: bool, ) -> Instruction { Instruction::from(Call { @@ -389,9 +714,9 @@ impl Instruction { } pub fn call_native( - destination: u8, + destination: u16, function: NativeFunction, - argument_count: u8, + argument_count: u16, ) -> Instruction { Instruction::from(CallNative { destination, @@ -400,7 +725,7 @@ impl Instruction { }) } - pub fn r#return(should_return_value: bool, return_register: u8) -> Instruction { + pub fn r#return(should_return_value: bool, return_register: u16) -> Instruction { Instruction::from(Return { should_return_value, return_register, @@ -408,46 +733,66 @@ impl Instruction { } pub fn is_math(&self) -> bool { - matches!( - self.operation(), - Operation::ADD - | Operation::SUBTRACT - | Operation::MULTIPLY - | Operation::DIVIDE - | Operation::MODULO - ) + self.operation().is_math() } pub fn is_comparison(&self) -> bool { - matches!( - self.operation(), - Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL - ) + self.operation().is_comparison() } - pub fn as_argument(&self) -> Option { + pub fn as_argument(&self) -> Option { match self.operation() { - Operation::LOAD_CONSTANT => Some(Argument::Constant(self.b_field())), + Operation::LOAD_CONSTANT => Some(Operand::Constant(self.b_field())), Operation::LOAD_BOOLEAN | Operation::LOAD_LIST | Operation::LOAD_SELF | Operation::GET_LOCAL - | Operation::ADD - | Operation::SUBTRACT - | Operation::MULTIPLY - | Operation::DIVIDE - | Operation::MODULO - | Operation::EQUAL - | Operation::LESS - | Operation::LESS_EQUAL - | Operation::NEGATE + | Operation::ADD_INT + | Operation::ADD_FLOAT + | Operation::ADD_BYTE + | Operation::ADD_STR + | Operation::ADD_CHAR + | Operation::ADD_STR_CHAR + | Operation::ADD_CHAR_STR + | Operation::SUBTRACT_INT + | Operation::SUBTRACT_FLOAT + | Operation::SUBTRACT_BYTE + | Operation::MULTIPLY_INT + | Operation::MULTIPLY_FLOAT + | Operation::MULTIPLY_BYTE + | Operation::DIVIDE_INT + | Operation::DIVIDE_FLOAT + | Operation::DIVIDE_BYTE + | Operation::MODULO_INT + | Operation::MODULO_FLOAT + | Operation::MODULO_BYTE + | Operation::EQUAL_INT + | Operation::EQUAL_FLOAT + | Operation::EQUAL_BYTE + | Operation::EQUAL_STR + | Operation::EQUAL_CHAR + | Operation::EQUAL_STR_CHAR + | Operation::EQUAL_CHAR_STR + | Operation::EQUAL_BOOL + | Operation::LESS_INT + | Operation::LESS_FLOAT + | Operation::LESS_BYTE + | Operation::LESS_STR + | Operation::LESS_CHAR + | Operation::LESS_EQUAL_INT + | Operation::LESS_EQUAL_FLOAT + | Operation::LESS_EQUAL_BYTE + | Operation::LESS_EQUAL_STR + | Operation::LESS_EQUAL_CHAR + | Operation::NEGATE_INT + | Operation::NEGATE_FLOAT | Operation::NOT - | Operation::CALL => Some(Argument::Register(self.a_field())), + | Operation::CALL => Some(Operand::Register(self.a_field())), Operation::CALL_NATIVE => { let function = NativeFunction::from(self.b_field()); if function.returns_value() { - Some(Argument::Register(self.a_field())) + Some(Operand::Register(self.a_field())) } else { None } @@ -456,24 +801,24 @@ impl Instruction { } } - pub fn b_as_argument(&self) -> Argument { + pub fn b_as_argument(&self) -> Operand { if self.b_is_constant() { - Argument::Constant(self.b_field()) + Operand::Constant(self.b_field()) } else { - Argument::Register(self.b_field()) + Operand::Register(self.b_field()) } } - pub fn b_and_c_as_arguments(&self) -> (Argument, Argument) { + pub fn b_and_c_as_operands(&self) -> (Operand, Operand) { let left = if self.b_is_constant() { - Argument::Constant(self.b_field()) + Operand::Constant(self.b_field()) } else { - Argument::Register(self.b_field()) + Operand::Register(self.b_field()) }; let right = if self.c_is_constant() { - Argument::Constant(self.c_field()) + Operand::Constant(self.c_field()) } else { - Argument::Register(self.c_field()) + Operand::Register(self.c_field()) }; (left, right) @@ -488,12 +833,27 @@ impl Instruction { | Operation::LOAD_LIST | Operation::LOAD_SELF | Operation::GET_LOCAL - | Operation::ADD - | Operation::SUBTRACT - | Operation::MULTIPLY - | Operation::DIVIDE - | Operation::MODULO - | Operation::NEGATE + | Operation::ADD_INT + | Operation::ADD_FLOAT + | Operation::ADD_BYTE + | Operation::ADD_STR + | Operation::ADD_CHAR + | Operation::ADD_STR_CHAR + | Operation::ADD_CHAR_STR + | Operation::SUBTRACT_INT + | Operation::SUBTRACT_FLOAT + | Operation::SUBTRACT_BYTE + | Operation::MULTIPLY_INT + | Operation::MULTIPLY_FLOAT + | Operation::MULTIPLY_BYTE + | Operation::DIVIDE_INT + | Operation::DIVIDE_FLOAT + | Operation::DIVIDE_BYTE + | Operation::MODULO_INT + | Operation::MODULO_FLOAT + | Operation::MODULO_BYTE + | Operation::NEGATE_INT + | Operation::NEGATE_FLOAT | Operation::NOT | Operation::CALL => true, Operation::CALL_NATIVE => { @@ -503,9 +863,24 @@ impl Instruction { } Operation::CLOSE | Operation::SET_LOCAL - | Operation::EQUAL - | Operation::LESS - | Operation::LESS_EQUAL + | Operation::EQUAL_INT + | Operation::EQUAL_FLOAT + | Operation::EQUAL_BYTE + | Operation::EQUAL_STR + | Operation::EQUAL_CHAR + | Operation::EQUAL_STR_CHAR + | Operation::EQUAL_CHAR_STR + | Operation::EQUAL_BOOL + | Operation::LESS_INT + | Operation::LESS_FLOAT + | Operation::LESS_BYTE + | Operation::LESS_STR + | Operation::LESS_CHAR + | Operation::LESS_EQUAL_INT + | Operation::LESS_EQUAL_FLOAT + | Operation::LESS_EQUAL_BYTE + | Operation::LESS_EQUAL_STR + | Operation::LESS_EQUAL_CHAR | Operation::TEST | Operation::TEST_SET | Operation::JUMP @@ -519,242 +894,17 @@ impl Instruction { match operation { Operation::POINT => Point::from(*self).to_string(), - Operation::CLOSE => { - let Close { from, to } = Close::from(*self); - - format!("R{from}..R{to}") - } - Operation::LOAD_BOOLEAN => { - let LoadBoolean { - destination, - value, - jump_next, - } = LoadBoolean::from(*self); - - if jump_next { - format!("R{destination} = {value} && JUMP +1") - } else { - format!("R{destination} = {value}") - } - } - Operation::LOAD_CONSTANT => { - let LoadConstant { - destination, - constant_index, - jump_next, - } = LoadConstant::from(*self); - - if jump_next { - format!("R{destination} = C{constant_index} JUMP +1") - } else { - format!("R{destination} = C{constant_index}") - } - } + Operation::CLOSE => Close::from(*self).to_string(), + Operation::LOAD_BOOLEAN => LoadBoolean::from(*self).to_string(), + Operation::LOAD_CONSTANT => LoadConstant::from(*self).to_string(), Operation::LOAD_FUNCTION => LoadFunction::from(*self).to_string(), - Operation::LOAD_LIST => { - let LoadList { - destination, - start_register, - } = LoadList::from(*self); - let end_register = destination.saturating_sub(1); + Operation::LOAD_LIST => LoadList::from(*self).to_string(), + Operation::LOAD_SELF => LoadSelf::from(*self).to_string(), + Operation::GET_LOCAL => GetLocal::from(*self).to_string(), + Operation::SET_LOCAL => SetLocal::from(*self).to_string(), + Operation::ADD_INT => AddInt::from(*self).to_string(), - format!("R{destination} = [R{start_register}..=R{end_register}]",) - } - Operation::LOAD_SELF => { - let LoadSelf { destination } = LoadSelf::from(*self); - - format!("R{destination} = self") - } - Operation::GET_LOCAL => { - let GetLocal { - destination, - local_index, - } = GetLocal::from(*self); - - format!("R{destination} = L{local_index}") - } - Operation::SET_LOCAL => { - let SetLocal { - register_index, - local_index, - } = SetLocal::from(*self); - - format!("L{local_index} = R{register_index}") - } - Operation::ADD => { - let Add { - destination, - left, - right, - } = Add::from(*self); - - format!("R{destination} = {left} + {right}") - } - Operation::SUBTRACT => { - let Subtract { - destination, - left, - right, - } = Subtract::from(*self); - - format!("R{destination} = {left} - {right}") - } - Operation::MULTIPLY => { - let Multiply { - destination, - left, - right, - } = Multiply::from(*self); - - format!("R{destination} = {left} * {right}") - } - Operation::DIVIDE => { - let Divide { - destination, - left, - right, - } = Divide::from(*self); - - format!("R{destination} = {left} / {right}") - } - Operation::MODULO => { - let Modulo { - destination, - left, - right, - } = Modulo::from(*self); - - format!("R{destination} = {left} % {right}") - } - Operation::TEST => { - let Test { - operand_register, - test_value, - } = Test::from(*self); - let bang = if test_value { "" } else { "!" }; - - format!("if {bang}R{operand_register} {{ JUMP +1 }}",) - } - Operation::TEST_SET => { - let TestSet { - destination, - argument, - test_value, - } = TestSet::from(*self); - let bang = if test_value { "" } else { "!" }; - - format!("if {bang}{argument} {{ JUMP +1 }} else {{ R{destination} = {argument} }}") - } - Operation::EQUAL => { - let Equal { value, left, right } = Equal::from(*self); - let comparison_symbol = if value { "==" } else { "!=" }; - - format!("if {left} {comparison_symbol} {right} {{ JUMP +1 }}") - } - Operation::LESS => { - let Less { value, left, right } = Less::from(*self); - let comparison_symbol = if value { "<" } else { ">=" }; - - format!("if {left} {comparison_symbol} {right} {{ JUMP +1 }}") - } - Operation::LESS_EQUAL => { - let LessEqual { value, left, right } = LessEqual::from(*self); - let comparison_symbol = if value { "<=" } else { ">" }; - - format!("if {left} {comparison_symbol} {right} {{ JUMP +1 }}") - } - Operation::NEGATE => { - let Negate { - destination, - argument, - } = Negate::from(*self); - - format!("R{destination} = -{argument}") - } - Operation::NOT => { - let Not { - destination, - argument, - } = Not::from(*self); - - format!("R{destination} = !{argument}") - } - Operation::JUMP => { - let Jump { - offset, - is_positive, - } = Jump::from(*self); - - if is_positive { - format!("JUMP +{offset}") - } else { - format!("JUMP -{offset}") - } - } - Operation::CALL => { - let Call { - destination, - function_register, - argument_count, - .. - } = Call::from(*self); - let arguments_start = destination.saturating_sub(argument_count); - - match argument_count { - 0 => format!("R{destination} = R{function_register}()"), - 1 => format!("R{destination} = R{function_register}(R{arguments_start})"), - _ => { - format!( - "R{destination} = R{function_register}(R{arguments_start}..R{destination})" - ) - } - } - } - Operation::CALL_NATIVE => { - let CallNative { - destination, - function, - argument_count, - } = CallNative::from(*self); - let arguments_start = destination.saturating_sub(argument_count); - let arguments_end = arguments_start + argument_count; - let mut info_string = if function.returns_value() { - format!("R{destination} = ") - } else { - String::new() - }; - - match argument_count { - 0 => { - info_string.push_str(function.as_str()); - info_string.push_str("()"); - } - 1 => info_string.push_str(&format!("{function}(R{arguments_start})")), - _ => info_string - .push_str(&format!("{function}(R{arguments_start}..R{arguments_end})")), - } - - info_string - } - Operation::RETURN => { - let Return { - should_return_value, - return_register, - } = Return::from(*self); - - if should_return_value { - format!("RETURN R{return_register}") - } else { - String::new() - } - } - _ => { - if cfg!(debug_assertions) { - panic!("Unknown operation {}", self.operation()); - } else { - "RETURN".to_string() - } - } + _ => Operation::panic_from_unknown_code(operation.0), } } } @@ -771,57 +921,95 @@ impl Display for Instruction { } } -#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] -pub struct InstructionData { - pub a_field: u8, - pub b_field: u8, - pub c_field: u8, - pub d_field: bool, - pub b_is_constant: bool, - pub c_is_constant: bool, -} - -impl Display for InstructionData { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{self:?}") - } -} - #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub enum Argument { - Constant(u8), - Register(u8), +pub enum Operand { + Constant(u16), + Register(u16), } -impl Argument { - pub fn index(&self) -> u8 { +impl Operand { + pub fn index(&self) -> u16 { match self { - Argument::Constant(index) => *index, - Argument::Register(index) => *index, + Operand::Constant(index) => *index, + Operand::Register(index) => *index, } } pub fn is_constant(&self) -> bool { - matches!(self, Argument::Constant(_)) + matches!(self, Operand::Constant(_)) } pub fn is_register(&self) -> bool { - matches!(self, Argument::Register(_)) + matches!(self, Operand::Register(_)) } - pub fn as_index_and_constant_flag(&self) -> (u8, bool) { + pub fn as_index_and_constant_flag(&self) -> (u16, bool) { match self { - Argument::Constant(index) => (*index, true), - Argument::Register(index) => (*index, false), + Operand::Constant(index) => (*index, true), + Operand::Register(index) => (*index, false), } } } -impl Display for Argument { +impl Display for Operand { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { - Argument::Constant(index) => write!(f, "C{index}"), - Argument::Register(index) => write!(f, "R{index}"), + Operand::Constant(index) => write!(f, "C{index}"), + Operand::Register(index) => write!(f, "R{index}"), } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn decode_operation() { + let instruction = Instruction::add_int(42, Operand::Constant(4), Operand::Register(2)); + + assert_eq!(instruction.operation(), Operation::ADD_INT); + } + + #[test] + fn decode_a_field() { + let instruction = Instruction::add_int(42, Operand::Constant(4), Operand::Register(2)); + + assert_eq!(42, instruction.a_field()); + } + + #[test] + fn decode_b_field() { + let instruction = Instruction::add_int(42, Operand::Constant(4), Operand::Register(2)); + + assert_eq!(4, instruction.b_field()); + } + + #[test] + fn decode_c_field() { + let instruction = Instruction::add_int(42, Operand::Constant(4), Operand::Register(2)); + + assert_eq!(2, instruction.c_field()); + } + + #[test] + fn decode_d_field() { + let instruction = Instruction::call(42, 4, 2, true); + + assert!(instruction.d_field()); + } + + #[test] + fn decode_b_is_constant() { + let instruction = Instruction::add_int(42, Operand::Constant(4), Operand::Register(2)); + + assert!(instruction.b_is_constant()); + } + + #[test] + fn decode_c_is_constant() { + let instruction = Instruction::add_int(42, Operand::Register(2), Operand::Constant(4)); + + assert!(instruction.c_is_constant()); + } +} diff --git a/dust-lang/src/instruction/modulo.rs b/dust-lang/src/instruction/modulo.rs deleted file mode 100644 index 9ba0f5b..0000000 --- a/dust-lang/src/instruction/modulo.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::{Argument, Instruction, Operation}; - -pub struct Modulo { - pub destination: u8, - pub left: Argument, - pub right: Argument, -} - -impl From for Modulo { - fn from(instruction: Instruction) -> Self { - let destination = instruction.a_field(); - let (left, right) = instruction.b_and_c_as_arguments(); - - Modulo { - destination, - left, - right, - } - } -} - -impl From for Instruction { - fn from(modulo: Modulo) -> Self { - let operation = Operation::MODULO; - let a = modulo.destination; - let (b, b_is_constant) = modulo.left.as_index_and_constant_flag(); - let (c, c_is_constant) = modulo.right.as_index_and_constant_flag(); - - Instruction::new(operation, a, b, c, b_is_constant, c_is_constant, false) - } -} diff --git a/dust-lang/src/instruction/modulo_byte.rs b/dust-lang/src/instruction/modulo_byte.rs new file mode 100644 index 0000000..9e969d3 --- /dev/null +++ b/dust-lang/src/instruction/modulo_byte.rs @@ -0,0 +1,54 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct ModuloByte { + pub destination: u16, + pub left: Operand, + pub right: Operand, +} + +impl From for ModuloByte { + fn from(instruction: Instruction) -> Self { + let destination = instruction.a_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + ModuloByte { + destination, + left, + right, + } + } +} + +impl From for Instruction { + fn from(modulo_byte: ModuloByte) -> Self { + let operation = Operation::MODULO_BYTE; + let a_field = modulo_byte.destination; + let (b_field, b_is_constant) = modulo_byte.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = modulo_byte.right.as_index_and_constant_flag(); + + InstructionBuilder { + operation, + a_field, + b_field, + c_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for ModuloByte { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let ModuloByte { + destination, + left, + right, + } = self; + + write!(f, "R{} = {} % {}", destination, left, right) + } +} diff --git a/dust-lang/src/instruction/modulo_float.rs b/dust-lang/src/instruction/modulo_float.rs new file mode 100644 index 0000000..7b33022 --- /dev/null +++ b/dust-lang/src/instruction/modulo_float.rs @@ -0,0 +1,54 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct ModuloFloat { + pub destination: u16, + pub left: Operand, + pub right: Operand, +} + +impl From for ModuloFloat { + fn from(instruction: Instruction) -> Self { + let destination = instruction.a_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + ModuloFloat { + destination, + left, + right, + } + } +} + +impl From for Instruction { + fn from(modulo_float: ModuloFloat) -> Self { + let operation = Operation::MODULO_FLOAT; + let a_field = modulo_float.destination; + let (b_field, b_is_constant) = modulo_float.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = modulo_float.right.as_index_and_constant_flag(); + + InstructionBuilder { + operation, + a_field, + b_field, + c_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for ModuloFloat { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let ModuloFloat { + destination, + left, + right, + } = self; + + write!(f, "R{} = {} % {}", destination, left, right) + } +} diff --git a/dust-lang/src/instruction/modulo_int.rs b/dust-lang/src/instruction/modulo_int.rs new file mode 100644 index 0000000..e913893 --- /dev/null +++ b/dust-lang/src/instruction/modulo_int.rs @@ -0,0 +1,54 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct ModuloInt { + pub destination: u16, + pub left: Operand, + pub right: Operand, +} + +impl From for ModuloInt { + fn from(instruction: Instruction) -> Self { + let destination = instruction.a_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + ModuloInt { + destination, + left, + right, + } + } +} + +impl From for Instruction { + fn from(modulo_int: ModuloInt) -> Self { + let operation = Operation::MODULO_INT; + let a_field = modulo_int.destination; + let (b_field, b_is_constant) = modulo_int.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = modulo_int.right.as_index_and_constant_flag(); + + InstructionBuilder { + operation, + a_field, + b_field, + c_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for ModuloInt { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let ModuloInt { + destination, + left, + right, + } = self; + + write!(f, "R{} = {} % {}", destination, left, right) + } +} diff --git a/dust-lang/src/instruction/multiply.rs b/dust-lang/src/instruction/multiply.rs deleted file mode 100644 index fd73ff3..0000000 --- a/dust-lang/src/instruction/multiply.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::{Argument, Instruction, Operation}; - -pub struct Multiply { - pub destination: u8, - pub left: Argument, - pub right: Argument, -} - -impl From for Multiply { - fn from(instruction: Instruction) -> Self { - let destination = instruction.a_field(); - let (left, right) = instruction.b_and_c_as_arguments(); - - Multiply { - destination, - left, - right, - } - } -} - -impl From for Instruction { - fn from(multiply: Multiply) -> Self { - let operation = Operation::MULTIPLY; - let a = multiply.destination; - let (b, b_options) = multiply.left.as_index_and_constant_flag(); - let (c, c_options) = multiply.right.as_index_and_constant_flag(); - - Instruction::new(operation, a, b, c, b_options, c_options, false) - } -} diff --git a/dust-lang/src/instruction/multiply_byte.rs b/dust-lang/src/instruction/multiply_byte.rs new file mode 100644 index 0000000..501776e --- /dev/null +++ b/dust-lang/src/instruction/multiply_byte.rs @@ -0,0 +1,54 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct MultiplyByte { + pub destination: u16, + pub left: Operand, + pub right: Operand, +} + +impl From for MultiplyByte { + fn from(instruction: Instruction) -> Self { + let destination = instruction.a_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + MultiplyByte { + destination, + left, + right, + } + } +} + +impl From for Instruction { + fn from(multiply_byte: MultiplyByte) -> Self { + let operation = Operation::MODULO_BYTE; + let a_field = multiply_byte.destination; + let (b_field, b_is_constant) = multiply_byte.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = multiply_byte.right.as_index_and_constant_flag(); + + InstructionBuilder { + operation, + a_field, + b_field, + c_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for MultiplyByte { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let MultiplyByte { + destination, + left, + right, + } = self; + + write!(f, "R{} = {} ✕ {}", destination, left, right) + } +} diff --git a/dust-lang/src/instruction/multiply_float.rs b/dust-lang/src/instruction/multiply_float.rs new file mode 100644 index 0000000..19f8292 --- /dev/null +++ b/dust-lang/src/instruction/multiply_float.rs @@ -0,0 +1,54 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct MultiplyFloat { + pub destination: u16, + pub left: Operand, + pub right: Operand, +} + +impl From for MultiplyFloat { + fn from(instruction: Instruction) -> Self { + let destination = instruction.a_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + MultiplyFloat { + destination, + left, + right, + } + } +} + +impl From for Instruction { + fn from(multiply_float: MultiplyFloat) -> Self { + let operation = Operation::MULTIPLY_FLOAT; + let a_field = multiply_float.destination; + let (b_field, b_is_constant) = multiply_float.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = multiply_float.right.as_index_and_constant_flag(); + + InstructionBuilder { + operation, + a_field, + b_field, + c_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for MultiplyFloat { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let MultiplyFloat { + destination, + left, + right, + } = self; + + write!(f, "R{} = {} ✕ {}", destination, left, right) + } +} diff --git a/dust-lang/src/instruction/multiply_int.rs b/dust-lang/src/instruction/multiply_int.rs new file mode 100644 index 0000000..00fbdf5 --- /dev/null +++ b/dust-lang/src/instruction/multiply_int.rs @@ -0,0 +1,54 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct MultiplyInt { + pub destination: u16, + pub left: Operand, + pub right: Operand, +} + +impl From for MultiplyInt { + fn from(instruction: Instruction) -> Self { + let destination = instruction.a_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + MultiplyInt { + destination, + left, + right, + } + } +} + +impl From for Instruction { + fn from(multiply_int: MultiplyInt) -> Self { + let operation = Operation::MULTIPLY_INT; + let a_field = multiply_int.destination; + let (b_field, b_is_constant) = multiply_int.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = multiply_int.right.as_index_and_constant_flag(); + + InstructionBuilder { + operation, + a_field, + b_field, + c_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for MultiplyInt { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let MultiplyInt { + destination, + left, + right, + } = self; + + write!(f, "R{} = {} ✕ {}", destination, left, right) + } +} diff --git a/dust-lang/src/instruction/negate.rs b/dust-lang/src/instruction/negate.rs deleted file mode 100644 index 405db8d..0000000 --- a/dust-lang/src/instruction/negate.rs +++ /dev/null @@ -1,29 +0,0 @@ -use crate::{Argument, Instruction, Operation}; - -pub struct Negate { - pub destination: u8, - pub argument: Argument, -} - -impl From for Negate { - fn from(instruction: Instruction) -> Self { - let destination = instruction.a_field(); - let argument = instruction.b_as_argument(); - - Negate { - destination, - argument, - } - } -} - -impl From for Instruction { - fn from(negate: Negate) -> Self { - let operation = Operation::NEGATE; - let a = negate.destination; - let (b, b_is_constant) = negate.argument.as_index_and_constant_flag(); - let c = 0; - - Instruction::new(operation, a, b, c, b_is_constant, false, false) - } -} diff --git a/dust-lang/src/instruction/negate_float.rs b/dust-lang/src/instruction/negate_float.rs new file mode 100644 index 0000000..c0ed4d3 --- /dev/null +++ b/dust-lang/src/instruction/negate_float.rs @@ -0,0 +1,48 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct NegateFloat { + pub destination: u16, + pub argument: Operand, +} + +impl From for NegateFloat { + fn from(instruction: Instruction) -> Self { + let destination = instruction.a_field(); + let argument = instruction.b_as_argument(); + + NegateFloat { + destination, + argument, + } + } +} + +impl From for Instruction { + fn from(negate_float: NegateFloat) -> Self { + let operation = Operation::NEGATE_FLOAT; + let a_field = negate_float.destination; + let (b_field, b_is_constant) = negate_float.argument.as_index_and_constant_flag(); + + InstructionBuilder { + operation, + a_field, + b_field, + b_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for NegateFloat { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let NegateFloat { + destination, + argument, + } = self; + + write!(f, "R{destination} = -{argument}") + } +} diff --git a/dust-lang/src/instruction/negate_int.rs b/dust-lang/src/instruction/negate_int.rs new file mode 100644 index 0000000..6f1f901 --- /dev/null +++ b/dust-lang/src/instruction/negate_int.rs @@ -0,0 +1,48 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct NegateInt { + pub destination: u16, + pub argument: Operand, +} + +impl From for NegateInt { + fn from(instruction: Instruction) -> Self { + let destination = instruction.a_field(); + let argument = instruction.b_as_argument(); + + NegateInt { + destination, + argument, + } + } +} + +impl From for Instruction { + fn from(negate_int: NegateInt) -> Self { + let operation = Operation::NEGATE_INT; + let a_field = negate_int.destination; + let (b_field, b_is_constant) = negate_int.argument.as_index_and_constant_flag(); + + InstructionBuilder { + operation, + a_field, + b_field, + b_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for NegateInt { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let NegateInt { + destination, + argument, + } = self; + + write!(f, "R{destination} = -{argument}") + } +} diff --git a/dust-lang/src/instruction/not.rs b/dust-lang/src/instruction/not.rs index 70d7dd5..f26c4ea 100644 --- a/dust-lang/src/instruction/not.rs +++ b/dust-lang/src/instruction/not.rs @@ -1,8 +1,10 @@ -use crate::{Argument, Instruction, Operation}; +use crate::{Instruction, Operand, Operation}; + +use super::InstructionBuilder; pub struct Not { - pub destination: u8, - pub argument: Argument, + pub destination: u16, + pub argument: Operand, } impl From for Not { @@ -20,9 +22,16 @@ impl From for Not { impl From for Instruction { fn from(not: Not) -> Self { let operation = Operation::NOT; - let a = not.destination; - let (b, b_is_constant) = not.argument.as_index_and_constant_flag(); + let a_field = not.destination; + let (b_field, b_is_constant) = not.argument.as_index_and_constant_flag(); - Instruction::new(operation, a, b, 0, b_is_constant, false, false) + InstructionBuilder { + operation, + a_field, + b_field, + b_is_constant, + ..Default::default() + } + .build() } } diff --git a/dust-lang/src/instruction/operation.rs b/dust-lang/src/instruction/operation.rs index 0ab3851..ddded47 100644 --- a/dust-lang/src/instruction/operation.rs +++ b/dust-lang/src/instruction/operation.rs @@ -9,31 +9,90 @@ use serde::{Deserialize, Serialize}; pub struct Operation(pub u8); impl Operation { + // Stack manipulation pub const POINT: Operation = Operation(0); pub const CLOSE: Operation = Operation(1); + + // Loaders pub const LOAD_BOOLEAN: Operation = Operation(2); pub const LOAD_CONSTANT: Operation = Operation(3); pub const LOAD_FUNCTION: Operation = Operation(4); pub const LOAD_LIST: Operation = Operation(5); pub const LOAD_SELF: Operation = Operation(6); + + // Locals pub const GET_LOCAL: Operation = Operation(7); pub const SET_LOCAL: Operation = Operation(8); - pub const ADD: Operation = Operation(9); - pub const SUBTRACT: Operation = Operation(10); - pub const MULTIPLY: Operation = Operation(11); - pub const DIVIDE: Operation = Operation(12); - pub const MODULO: Operation = Operation(13); - pub const TEST: Operation = Operation(14); - pub const TEST_SET: Operation = Operation(15); - pub const EQUAL: Operation = Operation(16); - pub const LESS: Operation = Operation(17); - pub const LESS_EQUAL: Operation = Operation(18); - pub const NEGATE: Operation = Operation(19); - pub const NOT: Operation = Operation(20); - pub const CALL: Operation = Operation(21); - pub const CALL_NATIVE: Operation = Operation(22); - pub const JUMP: Operation = Operation(23); - pub const RETURN: Operation = Operation(24); + + // Addition + pub const ADD_INT: Operation = Operation(9); + pub const ADD_FLOAT: Operation = Operation(10); + pub const ADD_BYTE: Operation = Operation(11); + pub const ADD_STR: Operation = Operation(12); + pub const ADD_CHAR: Operation = Operation(13); + pub const ADD_STR_CHAR: Operation = Operation(14); + pub const ADD_CHAR_STR: Operation = Operation(15); + + // Subtraction + pub const SUBTRACT_INT: Operation = Operation(16); + pub const SUBTRACT_FLOAT: Operation = Operation(17); + pub const SUBTRACT_BYTE: Operation = Operation(18); + + // Multiplication + pub const MULTIPLY_INT: Operation = Operation(19); + pub const MULTIPLY_FLOAT: Operation = Operation(20); + pub const MULTIPLY_BYTE: Operation = Operation(21); + + // Division + pub const DIVIDE_INT: Operation = Operation(22); + pub const DIVIDE_FLOAT: Operation = Operation(23); + pub const DIVIDE_BYTE: Operation = Operation(24); + + // Modulo + pub const MODULO_INT: Operation = Operation(25); + pub const MODULO_FLOAT: Operation = Operation(26); + pub const MODULO_BYTE: Operation = Operation(27); + + // Equality + pub const EQUAL_INT: Operation = Operation(28); + pub const EQUAL_FLOAT: Operation = Operation(29); + pub const EQUAL_BYTE: Operation = Operation(30); + pub const EQUAL_STR: Operation = Operation(31); + pub const EQUAL_CHAR: Operation = Operation(32); + pub const EQUAL_STR_CHAR: Operation = Operation(33); + pub const EQUAL_CHAR_STR: Operation = Operation(34); + pub const EQUAL_BOOL: Operation = Operation(35); + + // < or >= comparison + pub const LESS_INT: Operation = Operation(36); + pub const LESS_FLOAT: Operation = Operation(37); + pub const LESS_BYTE: Operation = Operation(38); + pub const LESS_STR: Operation = Operation(39); + pub const LESS_CHAR: Operation = Operation(40); + + // <= or > comparison + pub const LESS_EQUAL_INT: Operation = Operation(41); + pub const LESS_EQUAL_FLOAT: Operation = Operation(42); + pub const LESS_EQUAL_BYTE: Operation = Operation(43); + pub const LESS_EQUAL_STR: Operation = Operation(44); + pub const LESS_EQUAL_CHAR: Operation = Operation(45); + + // Unary operations + pub const NEGATE_INT: Operation = Operation(46); + pub const NEGATE_FLOAT: Operation = Operation(47); + pub const NOT: Operation = Operation(48); + + // Logical operations + pub const TEST: Operation = Operation(49); + pub const TEST_SET: Operation = Operation(50); + + // Function calls + pub const CALL: Operation = Operation(51); + pub const CALL_NATIVE: Operation = Operation(52); + + // Control flow + pub const JUMP: Operation = Operation(53); + pub const RETURN: Operation = Operation(54); } impl Operation { @@ -48,18 +107,48 @@ impl Operation { Self::LOAD_SELF => "LOAD_SELF", Self::GET_LOCAL => "GET_LOCAL", Self::SET_LOCAL => "SET_LOCAL", - Self::ADD => "ADD", - Self::SUBTRACT => "SUBTRACT", - Self::MULTIPLY => "MULTIPLY", - Self::DIVIDE => "DIVIDE", - Self::MODULO => "MODULO", + Self::ADD_INT => "ADD_INT", + Self::ADD_FLOAT => "ADD_FLOAT", + Self::ADD_BYTE => "ADD_BYTE", + Self::ADD_STR => "ADD_STR", + Self::ADD_CHAR => "ADD_CHAR", + Self::ADD_STR_CHAR => "ADD_STR_CHAR", + Self::ADD_CHAR_STR => "ADD_CHAR_STR", + Self::SUBTRACT_INT => "SUBTRACT_INT", + Self::SUBTRACT_FLOAT => "SUBTRACT_FLOAT", + Self::SUBTRACT_BYTE => "SUBTRACT_BYTE", + Self::MULTIPLY_INT => "MULTIPLY_INT", + Self::MULTIPLY_FLOAT => "MULTIPLY_FLOAT", + Self::MULTIPLY_BYTE => "MULTIPLY_BYTE", + Self::DIVIDE_INT => "DIVIDE_INT", + Self::DIVIDE_FLOAT => "DIVIDE_FLOAT", + Self::DIVIDE_BYTE => "DIVIDE_BYTE", + Self::MODULO_INT => "MODULO_INT", + Self::MODULO_FLOAT => "MODULO_FLOAT", + Self::MODULO_BYTE => "MODULO_BYTE", + Self::EQUAL_INT => "EQUAL_INT", + Self::EQUAL_FLOAT => "EQUAL_FLOAT", + Self::EQUAL_BYTE => "EQUAL_BYTE", + Self::EQUAL_STR => "EQUAL_STR", + Self::EQUAL_CHAR => "EQUAL_CHAR", + Self::EQUAL_STR_CHAR => "EQUAL_STR_CHAR", + Self::EQUAL_CHAR_STR => "EQUAL_CHAR_STR", + Self::EQUAL_BOOL => "EQUAL_BOOL", + Self::LESS_INT => "LESS_INT", + Self::LESS_FLOAT => "LESS_FLOAT", + Self::LESS_BYTE => "LESS_BYTE", + Self::LESS_STR => "LESS_STR", + Self::LESS_CHAR => "LESS_CHAR", + Self::LESS_EQUAL_INT => "LESS_EQUAL_INT", + Self::LESS_EQUAL_FLOAT => "LESS_EQUAL_FLOAT", + Self::LESS_EQUAL_BYTE => "LESS_EQUAL_BYTE", + Self::LESS_EQUAL_STR => "LESS_EQUAL_STR", + Self::LESS_EQUAL_CHAR => "LESS_EQUAL_CHAR", + Self::NEGATE_INT => "NEGATE_INT", + Self::NEGATE_FLOAT => "NEGATE_FLOAT", + Self::NOT => "NOT", Self::TEST => "TEST", Self::TEST_SET => "TEST_SET", - Self::EQUAL => "EQUAL", - Self::LESS => "LESS", - Self::LESS_EQUAL => "LESS_EQUAL", - Self::NEGATE => "NEGATE", - Self::NOT => "NOT", Self::CALL => "CALL", Self::CALL_NATIVE => "CALL_NATIVE", Self::JUMP => "JUMP", @@ -68,6 +157,51 @@ impl Operation { } } + pub fn is_math(self) -> bool { + matches!( + self, + Operation::ADD_INT + | Operation::ADD_FLOAT + | Operation::ADD_BYTE + | Operation::SUBTRACT_INT + | Operation::SUBTRACT_FLOAT + | Operation::SUBTRACT_BYTE + | Operation::MULTIPLY_INT + | Operation::MULTIPLY_FLOAT + | Operation::MULTIPLY_BYTE + | Operation::DIVIDE_INT + | Operation::DIVIDE_FLOAT + | Operation::DIVIDE_BYTE + | Operation::MODULO_INT + | Operation::MODULO_FLOAT + | Operation::MODULO_BYTE + ) + } + + pub fn is_comparison(self) -> bool { + matches!( + self, + Operation::EQUAL_INT + | Operation::EQUAL_FLOAT + | Operation::EQUAL_BYTE + | Operation::EQUAL_STR + | Operation::EQUAL_CHAR + | Operation::EQUAL_STR_CHAR + | Operation::EQUAL_CHAR_STR + | Operation::EQUAL_BOOL + | Operation::LESS_INT + | Operation::LESS_FLOAT + | Operation::LESS_BYTE + | Operation::LESS_STR + | Operation::LESS_CHAR + | Operation::LESS_EQUAL_INT + | Operation::LESS_EQUAL_FLOAT + | Operation::LESS_EQUAL_BYTE + | Operation::LESS_EQUAL_STR + | Operation::LESS_EQUAL_CHAR + ) + } + pub fn panic_from_unknown_code(code: u8) -> ! { panic!("Unknown operation code: {code}"); } diff --git a/dust-lang/src/instruction/point.rs b/dust-lang/src/instruction/point.rs index fe4d43b..eb6e459 100644 --- a/dust-lang/src/instruction/point.rs +++ b/dust-lang/src/instruction/point.rs @@ -2,9 +2,11 @@ use std::fmt::{self, Display, Formatter}; use crate::{Instruction, Operation}; +use super::InstructionBuilder; + pub struct Point { - pub from: u8, - pub to: u8, + pub from: u16, + pub to: u16, } impl From for Point { @@ -16,6 +18,22 @@ impl From for Point { } } +impl From for Instruction { + fn from(r#move: Point) -> Self { + let operation = Operation::POINT; + let b_field = r#move.from; + let c_field = r#move.to; + + InstructionBuilder { + operation, + b_field, + c_field, + ..Default::default() + } + .build() + } +} + impl Display for Point { fn fmt(&self, f: &mut Formatter) -> fmt::Result { let Point { from, to } = self; @@ -23,13 +41,3 @@ impl Display for Point { write!(f, "{from} -> {to}") } } - -impl From for Instruction { - fn from(r#move: Point) -> Self { - let operation = Operation::POINT; - let b = r#move.from; - let c = r#move.to; - - Instruction::new(operation, 0, b, c, false, false, false) - } -} diff --git a/dust-lang/src/instruction/return.rs b/dust-lang/src/instruction/return.rs index b3c52c5..e1c9040 100644 --- a/dust-lang/src/instruction/return.rs +++ b/dust-lang/src/instruction/return.rs @@ -1,8 +1,10 @@ use crate::{Instruction, Operation}; +use super::InstructionBuilder; + pub struct Return { pub should_return_value: bool, - pub return_register: u8, + pub return_register: u16, } impl From for Return { @@ -20,9 +22,15 @@ impl From for Return { impl From for Instruction { fn from(r#return: Return) -> Self { let operation = Operation::RETURN; - let b = r#return.should_return_value as u8; - let c = r#return.return_register; + let b_field = r#return.should_return_value as u16; + let c_field = r#return.return_register; - Instruction::new(operation, 0, b, c, false, false, false) + InstructionBuilder { + operation, + b_field, + c_field, + ..Default::default() + } + .build() } } diff --git a/dust-lang/src/instruction/set_local.rs b/dust-lang/src/instruction/set_local.rs index 6e6ebf1..57b48a2 100644 --- a/dust-lang/src/instruction/set_local.rs +++ b/dust-lang/src/instruction/set_local.rs @@ -1,8 +1,12 @@ +use std::fmt::{self, Display, Formatter}; + use crate::{Instruction, Operation}; +use super::InstructionBuilder; + pub struct SetLocal { - pub register_index: u8, - pub local_index: u8, + pub register_index: u16, + pub local_index: u16, } impl From for SetLocal { @@ -20,9 +24,26 @@ impl From for SetLocal { impl From for Instruction { fn from(set_local: SetLocal) -> Self { let operation = Operation::SET_LOCAL; - let b = set_local.register_index; - let c = set_local.local_index; + let b_field = set_local.register_index; + let c_field = set_local.local_index; - Instruction::new(operation, 0, b, c, false, false, false) + InstructionBuilder { + operation, + b_field, + c_field, + ..Default::default() + } + .build() + } +} + +impl Display for SetLocal { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let SetLocal { + register_index, + local_index, + } = self; + + write!(f, "L{local_index} = R{register_index}") } } diff --git a/dust-lang/src/instruction/subtract.rs b/dust-lang/src/instruction/subtract.rs deleted file mode 100644 index 3923719..0000000 --- a/dust-lang/src/instruction/subtract.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::{Argument, Instruction, Operation}; - -pub struct Subtract { - pub destination: u8, - pub left: Argument, - pub right: Argument, -} - -impl From for Subtract { - fn from(instruction: Instruction) -> Self { - let destination = instruction.a_field(); - let (left, right) = instruction.b_and_c_as_arguments(); - - Subtract { - destination, - left, - right, - } - } -} - -impl From for Instruction { - fn from(subtract: Subtract) -> Self { - let operation = Operation::SUBTRACT; - let a = subtract.destination; - let (b, b_is_constant) = subtract.left.as_index_and_constant_flag(); - let (c, c_is_constant) = subtract.right.as_index_and_constant_flag(); - - Instruction::new(operation, a, b, c, b_is_constant, c_is_constant, false) - } -} diff --git a/dust-lang/src/instruction/subtract_byte.rs b/dust-lang/src/instruction/subtract_byte.rs new file mode 100644 index 0000000..76de9a7 --- /dev/null +++ b/dust-lang/src/instruction/subtract_byte.rs @@ -0,0 +1,54 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct SubtractByte { + pub destination: u16, + pub left: Operand, + pub right: Operand, +} + +impl From for SubtractByte { + fn from(instruction: Instruction) -> Self { + let destination = instruction.a_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + SubtractByte { + destination, + left, + right, + } + } +} + +impl From for Instruction { + fn from(subtract_byte: SubtractByte) -> Self { + let operation = Operation::SUBTRACT_BYTE; + let a_field = subtract_byte.destination; + let (b_field, b_is_constant) = subtract_byte.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = subtract_byte.right.as_index_and_constant_flag(); + + InstructionBuilder { + operation, + a_field, + b_field, + c_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for SubtractByte { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let SubtractByte { + destination, + left, + right, + } = self; + + write!(f, "R{} = {} - {}", destination, left, right) + } +} diff --git a/dust-lang/src/instruction/subtract_float.rs b/dust-lang/src/instruction/subtract_float.rs new file mode 100644 index 0000000..d806a49 --- /dev/null +++ b/dust-lang/src/instruction/subtract_float.rs @@ -0,0 +1,54 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct SubtractFloat { + pub destination: u16, + pub left: Operand, + pub right: Operand, +} + +impl From for SubtractFloat { + fn from(instruction: Instruction) -> Self { + let destination = instruction.a_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + SubtractFloat { + destination, + left, + right, + } + } +} + +impl From for Instruction { + fn from(subtract_float: SubtractFloat) -> Self { + let operation = Operation::SUBTRACT_FLOAT; + let a_field = subtract_float.destination; + let (b_field, b_is_constant) = subtract_float.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = subtract_float.right.as_index_and_constant_flag(); + + InstructionBuilder { + operation, + a_field, + b_field, + c_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for SubtractFloat { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let SubtractFloat { + destination, + left, + right, + } = self; + + write!(f, "R{} = {} - {}", destination, left, right) + } +} diff --git a/dust-lang/src/instruction/subtract_int.rs b/dust-lang/src/instruction/subtract_int.rs new file mode 100644 index 0000000..b9b2ffd --- /dev/null +++ b/dust-lang/src/instruction/subtract_int.rs @@ -0,0 +1,54 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation}; + +pub struct SubtractInt { + pub destination: u16, + pub left: Operand, + pub right: Operand, +} + +impl From for SubtractInt { + fn from(instruction: Instruction) -> Self { + let destination = instruction.a_field(); + let (left, right) = instruction.b_and_c_as_operands(); + + SubtractInt { + destination, + left, + right, + } + } +} + +impl From for Instruction { + fn from(subtract_int: SubtractInt) -> Self { + let operation = Operation::SUBTRACT_INT; + let a_field = subtract_int.destination; + let (b_field, b_is_constant) = subtract_int.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = subtract_int.right.as_index_and_constant_flag(); + + InstructionBuilder { + operation, + a_field, + b_field, + c_field, + b_is_constant, + c_is_constant, + ..Default::default() + } + .build() + } +} + +impl Display for SubtractInt { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let SubtractInt { + destination, + left, + right, + } = self; + + write!(f, "R{} = {} - {}", destination, left, right) + } +} diff --git a/dust-lang/src/instruction/test.rs b/dust-lang/src/instruction/test.rs index 138483a..62124dd 100644 --- a/dust-lang/src/instruction/test.rs +++ b/dust-lang/src/instruction/test.rs @@ -1,7 +1,9 @@ use crate::{Instruction, Operation}; +use super::InstructionBuilder; + pub struct Test { - pub operand_register: u8, + pub operand_register: u16, pub test_value: bool, } @@ -19,14 +21,15 @@ impl From for Test { impl From for Instruction { fn from(test: Test) -> Self { - Instruction::new( - Operation::TEST, - 0, - test.operand_register, - test.test_value as u8, - false, - false, - false, - ) + let b_field = test.operand_register; + let c_field = test.test_value as u16; + + InstructionBuilder { + operation: Operation::TEST, + b_field, + c_field, + ..Default::default() + } + .build() } } diff --git a/dust-lang/src/instruction/test_set.rs b/dust-lang/src/instruction/test_set.rs index 27a15e5..4ae8191 100644 --- a/dust-lang/src/instruction/test_set.rs +++ b/dust-lang/src/instruction/test_set.rs @@ -1,8 +1,10 @@ -use crate::{Argument, Instruction, Operation}; +use crate::{Instruction, Operand, Operation}; + +use super::InstructionBuilder; pub struct TestSet { - pub destination: u8, - pub argument: Argument, + pub destination: u16, + pub argument: Operand, pub test_value: bool, } @@ -23,10 +25,18 @@ impl From for TestSet { impl From for Instruction { fn from(test_set: TestSet) -> Self { let operation = Operation::TEST; - let a = test_set.destination; - let (b, b_is_constant) = test_set.argument.as_index_and_constant_flag(); - let c = test_set.test_value as u8; + let a_field = test_set.destination; + let (b_field, b_is_constant) = test_set.argument.as_index_and_constant_flag(); + let c_field = test_set.test_value as u16; - Instruction::new(operation, a, b, c, b_is_constant, false, false) + InstructionBuilder { + operation, + a_field, + b_field, + c_field, + b_is_constant, + ..Default::default() + } + .build() } } diff --git a/dust-lang/src/instruction/type_code.rs b/dust-lang/src/instruction/type_code.rs deleted file mode 100644 index 0e038ee..0000000 --- a/dust-lang/src/instruction/type_code.rs +++ /dev/null @@ -1,10 +0,0 @@ -pub struct TypeCode(pub u8); - -impl TypeCode { - const INTEGER: u8 = 0; - const FLOAT: u8 = 1; - const STRING: u8 = 2; - const BOOLEAN: u8 = 3; - const CHARACTER: u8 = 4; - const BYTE: u8 = 5; -} diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index f1e8dbd..bc6cb03 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -40,17 +40,17 @@ pub mod value; pub mod vm; pub use crate::chunk::{Chunk, Disassembler, Local, Scope}; -pub use crate::compiler::{compile, CompileError, Compiler}; +pub use crate::compiler::{CompileError, Compiler, compile}; pub use crate::dust_error::{AnnotatedError, DustError}; -pub use crate::instruction::{Argument, Instruction, InstructionData, Operation}; -pub use crate::lexer::{lex, LexError, Lexer}; +pub use crate::instruction::{Operand, Instruction, Operation}; +pub use crate::lexer::{LexError, Lexer, lex}; pub use crate::native_function::{NativeFunction, NativeFunctionError}; -pub use crate::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict}; pub use crate::token::{Token, TokenKind, TokenOwned}; +pub use crate::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict}; pub use crate::value::{ AbstractList, ConcreteValue, DustString, Function, RangeValue, Value, ValueError, }; -pub use crate::vm::{run, Pointer, Vm}; +pub use crate::vm::{Pointer, Vm, run}; use std::fmt::Display; diff --git a/dust-lang/src/native_function/assert.rs b/dust-lang/src/native_function/assert.rs index b748ee7..3cd7045 100644 --- a/dust-lang/src/native_function/assert.rs +++ b/dust-lang/src/native_function/assert.rs @@ -2,7 +2,7 @@ use std::{ops::Range, panic}; use crate::vm::ThreadData; -pub fn panic(data: &mut ThreadData, _: u8, argument_range: Range) -> bool { +pub fn panic(data: &mut ThreadData, _: u16, argument_range: Range) -> bool { let position = data.current_position(); let mut message = format!("Dust panic at {position}!"); diff --git a/dust-lang/src/native_function/io.rs b/dust-lang/src/native_function/io.rs index 696e5d5..89bf2b7 100644 --- a/dust-lang/src/native_function/io.rs +++ b/dust-lang/src/native_function/io.rs @@ -6,7 +6,7 @@ use crate::{ vm::{Register, ThreadData, get_next_action}, }; -pub fn read_line(data: &mut ThreadData, destination: u8, _argument_range: Range) -> bool { +pub fn read_line(data: &mut ThreadData, destination: u16, _argument_range: Range) -> bool { let mut buffer = String::new(); if stdin().read_line(&mut buffer).is_ok() { @@ -24,7 +24,7 @@ pub fn read_line(data: &mut ThreadData, destination: u8, _argument_range: Range< false } -pub fn write(data: &mut ThreadData, _: u8, argument_range: Range) -> bool { +pub fn write(data: &mut ThreadData, _: u16, argument_range: Range) -> bool { let mut stdout = stdout(); for register_index in argument_range { @@ -40,7 +40,7 @@ pub fn write(data: &mut ThreadData, _: u8, argument_range: Range) -> bool { false } -pub fn write_line(data: &mut ThreadData, _: u8, argument_range: Range) -> bool { +pub fn write_line(data: &mut ThreadData, _: u16, argument_range: Range) -> bool { let mut stdout = stdout().lock(); for register_index in argument_range { diff --git a/dust-lang/src/native_function/mod.rs b/dust-lang/src/native_function/mod.rs index 4917843..1926143 100644 --- a/dust-lang/src/native_function/mod.rs +++ b/dust-lang/src/native_function/mod.rs @@ -35,8 +35,8 @@ macro_rules! define_native_function { pub fn call( &self, data: &mut ThreadData, - destination: u8, - argument_range: Range, + destination: u16, + argument_range: Range, ) -> bool { match self { $( @@ -80,8 +80,8 @@ macro_rules! define_native_function { } } - impl From for NativeFunction { - fn from(bytes: u8) -> Self { + impl From for NativeFunction { + fn from(bytes: u16) -> Self { match bytes { $( $bytes => NativeFunction::$name, diff --git a/dust-lang/src/native_function/random.rs b/dust-lang/src/native_function/random.rs index f743b0b..1eae46a 100644 --- a/dust-lang/src/native_function/random.rs +++ b/dust-lang/src/native_function/random.rs @@ -7,7 +7,7 @@ use crate::{ vm::{Register, ThreadData, get_next_action}, }; -pub fn random_int(data: &mut ThreadData, destination: u8, argument_range: Range) -> bool { +pub fn random_int(data: &mut ThreadData, destination: u16, argument_range: Range) -> bool { let mut argument_range_iter = argument_range.into_iter(); let (min, max) = { let mut min = None; diff --git a/dust-lang/src/native_function/string.rs b/dust-lang/src/native_function/string.rs index 8d03c96..9e3546c 100644 --- a/dust-lang/src/native_function/string.rs +++ b/dust-lang/src/native_function/string.rs @@ -5,7 +5,7 @@ use crate::{ vm::{Register, ThreadData, get_next_action}, }; -pub fn to_string(data: &mut ThreadData, destination: u8, argument_range: Range) -> bool { +pub fn to_string(data: &mut ThreadData, destination: u16, argument_range: Range) -> bool { let argument_value = data.open_register_unchecked(argument_range.start); let argument_string = argument_value.display(data); let register = Register::Value(Value::Concrete(ConcreteValue::string(argument_string))); diff --git a/dust-lang/src/native_function/thread.rs b/dust-lang/src/native_function/thread.rs index 4786065..4b0cf85 100644 --- a/dust-lang/src/native_function/thread.rs +++ b/dust-lang/src/native_function/thread.rs @@ -10,7 +10,7 @@ use crate::{ vm::{Thread, ThreadData, get_next_action}, }; -fn start_thread(data: &mut ThreadData, argument_range: Range) -> JoinHandle<()> { +fn start_thread(data: &mut ThreadData, argument_range: Range) -> JoinHandle<()> { let mut argument_range_iter = argument_range.into_iter(); let function_argument = { loop { @@ -56,7 +56,7 @@ fn start_thread(data: &mut ThreadData, argument_range: Range) -> JoinHandle< .expect("Critical VM Error: Failed to spawn thread") } -pub fn spawn(data: &mut ThreadData, _: u8, argument_range: Range) -> bool { +pub fn spawn(data: &mut ThreadData, _: u16, argument_range: Range) -> bool { let _ = start_thread(data, argument_range); data.next_action = get_next_action(data); diff --git a/dust-lang/src/type.rs b/dust-lang/src/type.rs index e6264c4..1258526 100644 --- a/dust-lang/src/type.rs +++ b/dust-lang/src/type.rs @@ -273,13 +273,13 @@ impl Ord for Type { #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct FunctionType { - pub type_parameters: Vec, - pub value_parameters: Vec<(u8, Type)>, + pub type_parameters: Vec, + pub value_parameters: Vec<(u16, Type)>, pub return_type: Type, } impl FunctionType { - pub fn new>, U: Into>>( + pub fn new>, U: Into>>( type_parameters: T, value_parameters: U, return_type: Type, diff --git a/dust-lang/src/value/function.rs b/dust-lang/src/value/function.rs index fc74575..f331dcf 100644 --- a/dust-lang/src/value/function.rs +++ b/dust-lang/src/value/function.rs @@ -8,7 +8,7 @@ use super::DustString; pub struct Function { pub name: Option, pub r#type: FunctionType, - pub prototype_index: u8, + pub prototype_index: u16, } impl Display for Function { diff --git a/dust-lang/src/vm/function_call.rs b/dust-lang/src/vm/function_call.rs index c9cbe84..7db37aa 100644 --- a/dust-lang/src/vm/function_call.rs +++ b/dust-lang/src/vm/function_call.rs @@ -11,12 +11,12 @@ use super::Register; pub struct FunctionCall { pub chunk: Arc, pub ip: usize, - pub return_register: u8, + pub return_register: u16, pub registers: Vec, } impl FunctionCall { - pub fn new(chunk: Arc, return_register: u8) -> Self { + pub fn new(chunk: Arc, return_register: u16) -> Self { let register_count = chunk.register_count; Self { diff --git a/dust-lang/src/vm/mod.rs b/dust-lang/src/vm/mod.rs index 9e0ec9b..4cb6b1d 100644 --- a/dust-lang/src/vm/mod.rs +++ b/dust-lang/src/vm/mod.rs @@ -53,7 +53,7 @@ impl Vm { let _ = tx.send(value_option); }); - rx.recv().unwrap() + rx.recv().unwrap_or(None) } } @@ -76,9 +76,9 @@ impl Display for Register { #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum Pointer { - Register(u8), - Constant(u8), - Stack(usize, u8), + Register(u16), + Constant(u16), + Stack(usize, u16), } impl Display for Pointer { diff --git a/dust-lang/src/vm/run_action.rs b/dust-lang/src/vm/run_action.rs index 5bb6cb4..d97f0d7 100644 --- a/dust-lang/src/vm/run_action.rs +++ b/dust-lang/src/vm/run_action.rs @@ -1,11 +1,10 @@ use tracing::trace; use crate::{ - AbstractList, Argument, ConcreteValue, Instruction, Type, Value, + AbstractList, ConcreteValue, Instruction, Operand, Type, Value, instruction::{ - Add, Call, CallNative, Close, Divide, Equal, GetLocal, Jump, Less, LessEqual, LoadBoolean, - LoadConstant, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not, Point, - Return, SetLocal, Subtract, Test, TestSet, + Call, CallNative, Close, GetLocal, Jump, LoadBoolean, LoadConstant, LoadFunction, LoadList, + LoadSelf, Not, Point, Return, SetLocal, Test, TestSet, }, vm::FunctionCall, }; @@ -145,6 +144,7 @@ pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> bool { let LoadList { destination, start_register, + jump_next, } = instruction.into(); let mut item_pointers = Vec::with_capacity((destination - start_register) as usize); let mut item_type = Type::Any; @@ -186,6 +186,7 @@ pub fn load_function(instruction: Instruction, data: &mut ThreadData) -> bool { let LoadFunction { destination, prototype_index, + jump_next, } = instruction.into(); let prototype_index = prototype_index as usize; let current_call = data.call_stack.last_mut_unchecked(); @@ -201,7 +202,10 @@ pub fn load_function(instruction: Instruction, data: &mut ThreadData) -> bool { } pub fn load_self(instruction: Instruction, data: &mut ThreadData) -> bool { - let LoadSelf { destination } = instruction.into(); + let LoadSelf { + destination, + jump_next, + } = instruction.into(); let current_call = data.call_stack.last_mut_unchecked(); let prototype = ¤t_call.chunk; let function = prototype.as_function(); @@ -397,8 +401,8 @@ pub fn test_set(instruction: Instruction, data: &mut ThreadData) -> bool { if boolean == test_value { } else { let pointer = match argument { - Argument::Constant(constant_index) => Pointer::Constant(constant_index), - Argument::Register(register_index) => Pointer::Register(register_index), + Operand::Constant(constant_index) => Pointer::Constant(constant_index), + Operand::Register(register_index) => Pointer::Register(register_index), }; let register = Register::Pointer(pointer); diff --git a/dust-lang/src/vm/thread.rs b/dust-lang/src/vm/thread.rs index 35c9811..30b84a6 100644 --- a/dust-lang/src/vm/thread.rs +++ b/dust-lang/src/vm/thread.rs @@ -2,7 +2,7 @@ use std::{mem::replace, sync::Arc, thread::JoinHandle}; use tracing::{info, trace}; -use crate::{Argument, Chunk, DustString, Span, Value, vm::FunctionCall}; +use crate::{Chunk, DustString, Operand, Span, Value, vm::FunctionCall}; use super::{Pointer, Register, RunAction, Stack}; @@ -72,7 +72,7 @@ impl Thread { pub struct ThreadData { pub call_stack: Stack, pub next_action: RunAction, - pub return_value_index: Option, + pub return_value_index: Option, pub spawned_threads: Vec>, } @@ -105,7 +105,7 @@ impl ThreadData { } } - pub fn get_register_unchecked(&self, register_index: u8) -> &Register { + pub fn get_register_unchecked(&self, register_index: u16) -> &Register { trace!("Get R{register_index}"); let register_index = register_index as usize; @@ -122,13 +122,13 @@ impl ThreadData { } } - pub fn set_register(&mut self, to_register: u8, register: Register) { + pub fn set_register(&mut self, to_register: u16, register: Register) { let to_register = to_register as usize; self.call_stack.last_mut_unchecked().registers[to_register] = register; } - pub fn open_register_unchecked(&self, register_index: u8) -> &Value { + pub fn open_register_unchecked(&self, register_index: u16) -> &Value { let register_index = register_index as usize; let register = if cfg!(debug_assertions) { @@ -151,7 +151,7 @@ impl ThreadData { } } - pub fn open_register_allow_empty_unchecked(&self, register_index: u8) -> Option<&Value> { + pub fn open_register_allow_empty_unchecked(&self, register_index: u16) -> Option<&Value> { trace!("Open R{register_index}"); let register = self.get_register_unchecked(register_index); @@ -165,7 +165,7 @@ impl ThreadData { } } - pub fn empty_register_or_clone_constant_unchecked(&mut self, register_index: u8) -> Value { + pub fn empty_register_or_clone_constant_unchecked(&mut self, register_index: u16) -> Value { let register_index = register_index as usize; let old_register = replace( &mut self.call_stack.last_mut_unchecked().registers[register_index], @@ -202,7 +202,7 @@ impl ThreadData { } } - pub fn clone_register_value_or_constant_unchecked(&self, register_index: u8) -> Value { + pub fn clone_register_value_or_constant_unchecked(&self, register_index: u16) -> Value { let register = self.get_register_unchecked(register_index); match register { @@ -232,14 +232,14 @@ impl ThreadData { } /// DRY helper to get a value from an Argument - pub fn get_argument_unchecked(&self, argument: Argument) -> &Value { + pub fn get_argument_unchecked(&self, argument: Operand) -> &Value { match argument { - Argument::Constant(constant_index) => self.get_constant_unchecked(constant_index), - Argument::Register(register_index) => self.open_register_unchecked(register_index), + Operand::Constant(constant_index) => self.get_constant_unchecked(constant_index), + Operand::Register(register_index) => self.open_register_unchecked(register_index), } } - pub fn get_constant_unchecked(&self, constant_index: u8) -> &Value { + pub fn get_constant_unchecked(&self, constant_index: u16) -> &Value { let constant_index = constant_index as usize; if cfg!(debug_assertions) { @@ -255,7 +255,7 @@ impl ThreadData { } } - pub fn get_local_register(&self, local_index: u8) -> u8 { + pub fn get_local_register(&self, local_index: u16) -> u16 { let local_index = local_index as usize; let chunk = &self.call_stack.last_unchecked().chunk; diff --git a/dust-lang/tests/basic.rs b/dust-lang/tests/basic.rs index 442592c..ec728e6 100644 --- a/dust-lang/tests/basic.rs +++ b/dust-lang/tests/basic.rs @@ -61,11 +61,11 @@ fn parentheses_precedence() { }, vec![ ( - Instruction::add(0, Argument::Constant(0), Argument::Constant(1)), + Instruction::add(0, Operand::Constant(0), Operand::Constant(1)), Span(3, 4) ), ( - Instruction::multiply(1, Argument::Register(0), Argument::Constant(2)), + Instruction::multiply(1, Operand::Register(0), Operand::Constant(2)), Span(8, 9) ), (Instruction::r#return(true), Span(11, 11)), @@ -97,19 +97,19 @@ fn math_operator_precedence() { }, vec![ ( - Instruction::add(0, Argument::Constant(0), Argument::Constant(1)), + Instruction::add(0, Operand::Constant(0), Operand::Constant(1)), Span(2, 3) ), ( - Instruction::multiply(1, Argument::Constant(2), Argument::Constant(3)), + Instruction::multiply(1, Operand::Constant(2), Operand::Constant(3)), Span(10, 11) ), ( - Instruction::divide(2, Argument::Register(1), Argument::Constant(4)), + Instruction::divide(2, Operand::Register(1), Operand::Constant(4)), Span(14, 15) ), ( - Instruction::subtract(3, Argument::Register(0), Argument::Register(2)), + Instruction::subtract(3, Operand::Register(0), Operand::Register(2)), Span(6, 7) ), (Instruction::r#return(true), Span(17, 17)), diff --git a/dust-lang/tests/comparison.rs b/dust-lang/tests/comparison.rs index ae19d6c..677a646 100644 --- a/dust-lang/tests/comparison.rs +++ b/dust-lang/tests/comparison.rs @@ -15,7 +15,7 @@ fn equal() { }, vec![ ( - Instruction::equal(0, true, Argument::Constant(0), Argument::Constant(1)), + Instruction::equal(0, true, Operand::Constant(0), Operand::Constant(1)), Span(2, 4) ), (Instruction::r#return(true), Span(6, 6)), @@ -43,7 +43,7 @@ fn greater() { }, vec![ ( - Instruction::less_equal(0, false, Argument::Constant(0), Argument::Constant(1)), + Instruction::less_equal(0, false, Operand::Constant(0), Operand::Constant(1)), Span(2, 3) ), (Instruction::r#return(true), Span(5, 5)), @@ -71,7 +71,7 @@ fn greater_than_or_equal() { }, vec![ ( - Instruction::less(0, false, Argument::Constant(0), Argument::Constant(1)), + Instruction::less(0, false, Operand::Constant(0), Operand::Constant(1)), Span(2, 4) ), (Instruction::r#return(true), Span(6, 6)), @@ -99,7 +99,7 @@ fn less_than() { }, vec![ ( - Instruction::less(0, true, Argument::Constant(0), Argument::Constant(1)), + Instruction::less(0, true, Operand::Constant(0), Operand::Constant(1)), Span(2, 3) ), (Instruction::r#return(true), Span(5, 5)), @@ -127,7 +127,7 @@ fn less_than_or_equal() { }, vec![ ( - Instruction::less_equal(0, true, Argument::Constant(0), Argument::Constant(1)), + Instruction::less_equal(0, true, Operand::Constant(0), Operand::Constant(1)), Span(2, 4) ), (Instruction::r#return(true), Span(6, 6)), @@ -155,7 +155,7 @@ fn not_equal() { }, vec![ ( - Instruction::equal(0, false, Argument::Constant(0), Argument::Constant(1)), + Instruction::equal(0, false, Operand::Constant(0), Operand::Constant(1)), Span(2, 4) ), (Instruction::r#return(true), Span(6, 6)), diff --git a/dust-lang/tests/functions.rs b/dust-lang/tests/functions.rs index 1ffdcfa..181529d 100644 --- a/dust-lang/tests/functions.rs +++ b/dust-lang/tests/functions.rs @@ -20,7 +20,7 @@ fn function() { }, vec![ ( - Instruction::add(2, Argument::Register(0), Argument::Register(1)), + Instruction::add(2, Operand::Register(0), Operand::Register(1)), Span(30, 31) ), (Instruction::r#return(true), Span(34, 35)), @@ -51,7 +51,7 @@ fn function_call() { (Instruction::load_constant(0, 0, false), Span(0, 35)), (Instruction::load_constant(1, 1, false), Span(36, 37)), (Instruction::load_constant(2, 2, false), Span(39, 40)), - (Instruction::call(3, Argument::Constant(0), 2), Span(35, 41)), + (Instruction::call(3, Operand::Constant(0), 2), Span(35, 41)), (Instruction::r#return(true), Span(41, 41)), ], vec![ @@ -64,7 +64,7 @@ fn function_call() { }, vec![ ( - Instruction::add(2, Argument::Register(0), Argument::Register(1)), + Instruction::add(2, Operand::Register(0), Operand::Register(1)), Span(30, 31) ), (Instruction::r#return(true), Span(34, 35)), @@ -112,7 +112,7 @@ fn function_declaration() { }, vec![ ( - Instruction::add(2, Argument::Register(0), Argument::Register(1)), + Instruction::add(2, Operand::Register(0), Operand::Register(1)), Span(35, 36) ), (Instruction::r#return(true), Span(39, 40)), diff --git a/dust-lang/tests/lists.rs b/dust-lang/tests/lists.rs index b8d8aac..adb7ba4 100644 --- a/dust-lang/tests/lists.rs +++ b/dust-lang/tests/lists.rs @@ -80,15 +80,15 @@ fn list_with_complex_expression() { vec![ (Instruction::load_constant(0, 0, false), Span(1, 2)), ( - Instruction::add(1, Argument::Constant(1), Argument::Constant(2)), + Instruction::add(1, Operand::Constant(1), Operand::Constant(2)), Span(6, 7) ), ( - Instruction::multiply(2, Argument::Constant(3), Argument::Constant(4)), + Instruction::multiply(2, Operand::Constant(3), Operand::Constant(4)), Span(14, 15) ), ( - Instruction::subtract(3, Argument::Register(1), Argument::Register(2)), + Instruction::subtract(3, Operand::Register(1), Operand::Register(2)), Span(10, 11) ), (Instruction::close(1, 3), Span(17, 18)), @@ -131,7 +131,7 @@ fn list_with_simple_expression() { vec![ (Instruction::load_constant(0, 0, false), Span(1, 2)), ( - Instruction::add(1, Argument::Constant(1), Argument::Constant(2)), + Instruction::add(1, Operand::Constant(1), Operand::Constant(2)), Span(6, 7) ), (Instruction::load_constant(2, 3, false), Span(11, 12)), diff --git a/dust-lang/tests/loops.rs b/dust-lang/tests/loops.rs index 252478c..5c7d121 100644 --- a/dust-lang/tests/loops.rs +++ b/dust-lang/tests/loops.rs @@ -16,12 +16,12 @@ fn r#while() { vec![ (Instruction::load_constant(0, 0, false), Span(12, 13)), ( - Instruction::less(0, true, Argument::Register(0), Argument::Constant(2)), + Instruction::less(0, true, Operand::Register(0), Operand::Constant(2)), Span(23, 24) ), (Instruction::jump(2, true), Span(41, 42)), ( - Instruction::add(0, Argument::Register(0), Argument::Constant(3)), + Instruction::add(0, Operand::Register(0), Operand::Constant(3)), Span(35, 36) ), (Instruction::jump(3, false), Span(41, 42)), diff --git a/dust-lang/tests/math/divide.rs b/dust-lang/tests/math/divide.rs index d81dfb8..9f40492 100644 --- a/dust-lang/tests/math/divide.rs +++ b/dust-lang/tests/math/divide.rs @@ -15,7 +15,7 @@ fn divide_bytes() { }, vec![ ( - Instruction::divide(0, Argument::Constant(0), Argument::Constant(1)), + Instruction::divide(0, Operand::Constant(0), Operand::Constant(1)), Span(5, 6) ), (Instruction::r#return(true), Span(11, 11)) @@ -43,7 +43,7 @@ fn divide_floats() { }, vec![ ( - Instruction::divide(0, Argument::Constant(0), Argument::Constant(0)), + Instruction::divide(0, Operand::Constant(0), Operand::Constant(0)), Span(4, 5) ), (Instruction::r#return(true), Span(9, 9)) @@ -71,7 +71,7 @@ fn divide_integers() { }, vec![ ( - Instruction::divide(0, Argument::Constant(0), Argument::Constant(0)), + Instruction::divide(0, Operand::Constant(0), Operand::Constant(0)), Span(2, 3) ), (Instruction::r#return(true), Span(5, 5)) diff --git a/dust-lang/tests/math/divide_assign.rs b/dust-lang/tests/math/divide_assign.rs index 943feee..7e44d83 100644 --- a/dust-lang/tests/math/divide_assign.rs +++ b/dust-lang/tests/math/divide_assign.rs @@ -16,7 +16,7 @@ fn divide_assign() { vec![ (Instruction::load_constant(0, 0, false), Span(12, 13)), ( - Instruction::divide(0, Argument::Register(0), Argument::Constant(0)), + Instruction::divide(0, Operand::Register(0), Operand::Constant(0)), Span(17, 19) ), (Instruction::get_local(1, 0), Span(23, 24)), diff --git a/dust-lang/tests/math/modulo.rs b/dust-lang/tests/math/modulo.rs index b1ee83e..d803338 100644 --- a/dust-lang/tests/math/modulo.rs +++ b/dust-lang/tests/math/modulo.rs @@ -15,7 +15,7 @@ fn modulo_floats() { }, vec![ ( - Instruction::modulo(0, Argument::Constant(0), Argument::Constant(0)), + Instruction::modulo(0, Operand::Constant(0), Operand::Constant(0)), Span(4, 5) ), (Instruction::r#return(true), Span(9, 9)) @@ -43,7 +43,7 @@ fn modulo_integers() { }, vec![ ( - Instruction::modulo(0, Argument::Constant(0), Argument::Constant(0)), + Instruction::modulo(0, Operand::Constant(0), Operand::Constant(0)), Span(2, 3) ), (Instruction::r#return(true), Span(5, 5)) diff --git a/dust-lang/tests/math/multiply.rs b/dust-lang/tests/math/multiply.rs index a3241c0..f54f044 100644 --- a/dust-lang/tests/math/multiply.rs +++ b/dust-lang/tests/math/multiply.rs @@ -15,7 +15,7 @@ fn multiply_floats() { }, vec![ ( - Instruction::multiply(0, Argument::Constant(0), Argument::Constant(0)), + Instruction::multiply(0, Operand::Constant(0), Operand::Constant(0)), Span(4, 5) ), (Instruction::r#return(true), Span(9, 9)), @@ -43,7 +43,7 @@ fn multiply_integers() { }, vec![ ( - Instruction::multiply(0, Argument::Constant(0), Argument::Constant(1)), + Instruction::multiply(0, Operand::Constant(0), Operand::Constant(1)), Span(2, 3) ), (Instruction::r#return(true), Span(5, 5)), diff --git a/dust-lang/tests/math/multiply_assign.rs b/dust-lang/tests/math/multiply_assign.rs index ec6f87d..065bd24 100644 --- a/dust-lang/tests/math/multiply_assign.rs +++ b/dust-lang/tests/math/multiply_assign.rs @@ -16,7 +16,7 @@ fn multiply_assign() { vec![ (Instruction::load_constant(0, 0, false), Span(12, 13)), ( - Instruction::multiply(0, Argument::Register(0), Argument::Constant(2)), + Instruction::multiply(0, Operand::Register(0), Operand::Constant(2)), Span(17, 19) ), (Instruction::get_local(1, 0), Span(22, 23)), diff --git a/dust-lang/tests/math/subtract.rs b/dust-lang/tests/math/subtract.rs index 2fc5b84..44f7b2d 100644 --- a/dust-lang/tests/math/subtract.rs +++ b/dust-lang/tests/math/subtract.rs @@ -15,7 +15,7 @@ fn subtract_floats() { }, vec![ ( - Instruction::subtract(0, Argument::Constant(0), Argument::Constant(0)), + Instruction::subtract(0, Operand::Constant(0), Operand::Constant(0)), Span(4, 5) ), (Instruction::r#return(true), Span(9, 9)), @@ -43,7 +43,7 @@ fn subtract_floats_saturate() { }, vec![ ( - Instruction::subtract(0, Argument::Constant(0), Argument::Constant(1)), + Instruction::subtract(0, Operand::Constant(0), Operand::Constant(1)), Span(25, 26) ), (Instruction::r#return(true), Span(36, 36)), @@ -74,7 +74,7 @@ fn subtract_integers() { }, vec![ ( - Instruction::subtract(0, Argument::Constant(0), Argument::Constant(1)), + Instruction::subtract(0, Operand::Constant(0), Operand::Constant(1)), Span(2, 3) ), (Instruction::r#return(true), Span(5, 5)), @@ -102,7 +102,7 @@ fn subtract_integers_saturate() { }, vec![ ( - Instruction::subtract(0, Argument::Constant(0), Argument::Constant(1)), + Instruction::subtract(0, Operand::Constant(0), Operand::Constant(1)), Span(21, 22) ), (Instruction::r#return(true), Span(24, 24)), diff --git a/dust-lang/tests/math/subtract_assign.rs b/dust-lang/tests/math/subtract_assign.rs index 733eded..7b20cbf 100644 --- a/dust-lang/tests/math/subtract_assign.rs +++ b/dust-lang/tests/math/subtract_assign.rs @@ -16,7 +16,7 @@ fn subtract_assign() { vec![ (Instruction::load_constant(0, 0, false), Span(12, 14)), ( - Instruction::subtract(0, Argument::Register(0), Argument::Constant(2)), + Instruction::subtract(0, Operand::Register(0), Operand::Constant(2)), Span(18, 20) ), (Instruction::get_local(1, 0), Span(24, 25)), diff --git a/dust-lang/tests/unary_operations.rs b/dust-lang/tests/unary_operations.rs index c04486e..3b44024 100644 --- a/dust-lang/tests/unary_operations.rs +++ b/dust-lang/tests/unary_operations.rs @@ -14,7 +14,7 @@ fn negate() { return_type: Type::Integer, }, vec![ - (Instruction::negate(0, Argument::Constant(0)), Span(0, 1)), + (Instruction::negate(0, Operand::Constant(0)), Span(0, 1)), (Instruction::r#return(true), Span(5, 5)), ], vec![ConcreteValue::Integer(42)], @@ -40,7 +40,7 @@ fn not() { }, vec![ (Instruction::load_boolean(0, true, false), Span(1, 5)), - (Instruction::not(1, Argument::Register(0)), Span(0, 1)), + (Instruction::not(1, Operand::Register(0)), Span(0, 1)), (Instruction::r#return(true), Span(5, 5)), ], vec![], From ac1ee793ab985448446e822d6fc7baac11457caa Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 13 Jan 2025 09:49:08 -0500 Subject: [PATCH 5/7] Use fewer operations and encode operand types in the instruction --- dust-lang/src/chunk/constant_table.rs | 62 -- dust-lang/src/chunk/disassembler.rs | 99 +-- dust-lang/src/chunk/mod.rs | 10 +- dust-lang/src/compiler/error.rs | 16 +- dust-lang/src/compiler/mod.rs | 561 +++++--------- dust-lang/src/compiler/optimize.rs | 18 +- dust-lang/src/instruction/add.rs | 69 ++ dust-lang/src/instruction/add_byte.rs | 54 -- dust-lang/src/instruction/add_char.rs | 54 -- dust-lang/src/instruction/add_char_str.rs | 54 -- dust-lang/src/instruction/add_float.rs | 54 -- dust-lang/src/instruction/add_int.rs | 56 -- dust-lang/src/instruction/add_str.rs | 54 -- dust-lang/src/instruction/add_str_char.rs | 54 -- dust-lang/src/instruction/call.rs | 28 + dust-lang/src/instruction/call_native.rs | 26 + dust-lang/src/instruction/divide.rs | 69 ++ dust-lang/src/instruction/divide_byte.rs | 54 -- dust-lang/src/instruction/divide_float.rs | 54 -- dust-lang/src/instruction/divide_int.rs | 54 -- .../instruction/{equal_bool.rs => equal.rs} | 35 +- dust-lang/src/instruction/equal_byte.rs | 55 -- dust-lang/src/instruction/equal_char.rs | 55 -- dust-lang/src/instruction/equal_char_str.rs | 55 -- dust-lang/src/instruction/equal_float.rs | 55 -- dust-lang/src/instruction/equal_int.rs | 55 -- dust-lang/src/instruction/equal_str.rs | 55 -- dust-lang/src/instruction/equal_str_char.rs | 55 -- dust-lang/src/instruction/jump.rs | 14 + dust-lang/src/instruction/less.rs | 70 ++ dust-lang/src/instruction/less_byte.rs | 55 -- dust-lang/src/instruction/less_char.rs | 55 -- .../{less_equal_byte.rs => less_equal.rs} | 35 +- dust-lang/src/instruction/less_equal_char.rs | 55 -- dust-lang/src/instruction/less_equal_float.rs | 55 -- dust-lang/src/instruction/less_equal_int.rs | 55 -- dust-lang/src/instruction/less_equal_str.rs | 55 -- dust-lang/src/instruction/less_float.rs | 55 -- dust-lang/src/instruction/less_int.rs | 55 -- dust-lang/src/instruction/less_str.rs | 55 -- dust-lang/src/instruction/mod.rs | 699 +++++++----------- dust-lang/src/instruction/modulo.rs | 69 ++ dust-lang/src/instruction/modulo_byte.rs | 54 -- dust-lang/src/instruction/modulo_float.rs | 54 -- dust-lang/src/instruction/modulo_int.rs | 54 -- dust-lang/src/instruction/multiply.rs | 69 ++ dust-lang/src/instruction/multiply_byte.rs | 54 -- dust-lang/src/instruction/multiply_float.rs | 54 -- dust-lang/src/instruction/multiply_int.rs | 54 -- dust-lang/src/instruction/negate.rs | 54 ++ dust-lang/src/instruction/negate_float.rs | 48 -- dust-lang/src/instruction/negate_int.rs | 48 -- dust-lang/src/instruction/not.rs | 13 + dust-lang/src/instruction/operation.rs | 157 +--- dust-lang/src/instruction/return.rs | 17 + dust-lang/src/instruction/subtract.rs | 69 ++ dust-lang/src/instruction/subtract_byte.rs | 54 -- dust-lang/src/instruction/subtract_float.rs | 54 -- dust-lang/src/instruction/subtract_int.rs | 54 -- dust-lang/src/instruction/test.rs | 14 + dust-lang/src/instruction/test_set.rs | 18 + dust-lang/src/instruction/type_code.rs | 31 + dust-lang/src/value/mod.rs | 24 + dust-lang/src/vm/run_action.rs | 230 ++++-- 64 files changed, 1386 insertions(+), 3194 deletions(-) delete mode 100644 dust-lang/src/chunk/constant_table.rs create mode 100644 dust-lang/src/instruction/add.rs delete mode 100644 dust-lang/src/instruction/add_byte.rs delete mode 100644 dust-lang/src/instruction/add_char.rs delete mode 100644 dust-lang/src/instruction/add_char_str.rs delete mode 100644 dust-lang/src/instruction/add_float.rs delete mode 100644 dust-lang/src/instruction/add_int.rs delete mode 100644 dust-lang/src/instruction/add_str.rs delete mode 100644 dust-lang/src/instruction/add_str_char.rs create mode 100644 dust-lang/src/instruction/divide.rs delete mode 100644 dust-lang/src/instruction/divide_byte.rs delete mode 100644 dust-lang/src/instruction/divide_float.rs delete mode 100644 dust-lang/src/instruction/divide_int.rs rename dust-lang/src/instruction/{equal_bool.rs => equal.rs} (55%) delete mode 100644 dust-lang/src/instruction/equal_byte.rs delete mode 100644 dust-lang/src/instruction/equal_char.rs delete mode 100644 dust-lang/src/instruction/equal_char_str.rs delete mode 100644 dust-lang/src/instruction/equal_float.rs delete mode 100644 dust-lang/src/instruction/equal_int.rs delete mode 100644 dust-lang/src/instruction/equal_str.rs delete mode 100644 dust-lang/src/instruction/equal_str_char.rs create mode 100644 dust-lang/src/instruction/less.rs delete mode 100644 dust-lang/src/instruction/less_byte.rs delete mode 100644 dust-lang/src/instruction/less_char.rs rename dust-lang/src/instruction/{less_equal_byte.rs => less_equal.rs} (54%) delete mode 100644 dust-lang/src/instruction/less_equal_char.rs delete mode 100644 dust-lang/src/instruction/less_equal_float.rs delete mode 100644 dust-lang/src/instruction/less_equal_int.rs delete mode 100644 dust-lang/src/instruction/less_equal_str.rs delete mode 100644 dust-lang/src/instruction/less_float.rs delete mode 100644 dust-lang/src/instruction/less_int.rs delete mode 100644 dust-lang/src/instruction/less_str.rs create mode 100644 dust-lang/src/instruction/modulo.rs delete mode 100644 dust-lang/src/instruction/modulo_byte.rs delete mode 100644 dust-lang/src/instruction/modulo_float.rs delete mode 100644 dust-lang/src/instruction/modulo_int.rs create mode 100644 dust-lang/src/instruction/multiply.rs delete mode 100644 dust-lang/src/instruction/multiply_byte.rs delete mode 100644 dust-lang/src/instruction/multiply_float.rs delete mode 100644 dust-lang/src/instruction/multiply_int.rs create mode 100644 dust-lang/src/instruction/negate.rs delete mode 100644 dust-lang/src/instruction/negate_float.rs delete mode 100644 dust-lang/src/instruction/negate_int.rs create mode 100644 dust-lang/src/instruction/subtract.rs delete mode 100644 dust-lang/src/instruction/subtract_byte.rs delete mode 100644 dust-lang/src/instruction/subtract_float.rs delete mode 100644 dust-lang/src/instruction/subtract_int.rs create mode 100644 dust-lang/src/instruction/type_code.rs diff --git a/dust-lang/src/chunk/constant_table.rs b/dust-lang/src/chunk/constant_table.rs deleted file mode 100644 index 4c0c1ed..0000000 --- a/dust-lang/src/chunk/constant_table.rs +++ /dev/null @@ -1,62 +0,0 @@ -use serde::{Deserialize, Serialize}; - -use crate::DustString; - -#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)] -pub struct ConstantTable { - pub r#true: bool, - pub r#false: bool, - pub bytes: Vec, - pub characters: Vec, - pub floats: Vec, - pub integers: Vec, - pub strings: Vec, -} - -impl ConstantTable { - pub fn new() -> Self { - Self { - r#true: false, - r#false: false, - bytes: Vec::new(), - characters: Vec::new(), - floats: Vec::new(), - integers: Vec::new(), - strings: Vec::new(), - } - } - - #[cfg(debug_assertions)] - pub fn with_data( - booleans: (bool, bool), - bytes: impl Into>, - characters: impl Into>, - floats: impl Into>, - integers: impl Into>, - strings: impl Into>, - ) -> Self { - Self { - r#true: booleans.0, - r#false: booleans.1, - bytes: bytes.into(), - characters: characters.into(), - floats: floats.into(), - integers: integers.into(), - strings: strings.into(), - } - } - - pub fn len(&self) -> usize { - (if self.r#true { 1 } else { 0 }) - + (if self.r#false { 1 } else { 0 }) - + self.bytes.len() - + self.characters.len() - + self.floats.len() - + self.integers.len() - + self.strings.len() - } - - pub fn is_empty(&self) -> bool { - self.len() == 0 - } -} diff --git a/dust-lang/src/chunk/disassembler.rs b/dust-lang/src/chunk/disassembler.rs index b9dd48c..817f4e2 100644 --- a/dust-lang/src/chunk/disassembler.rs +++ b/dust-lang/src/chunk/disassembler.rs @@ -43,14 +43,14 @@ use std::io::{self, Write}; use colored::{ColoredString, Colorize}; -use crate::{Chunk, Local, Type}; +use crate::{Chunk, Local}; const INSTRUCTION_COLUMNS: [(&str, usize); 4] = - [("i", 5), ("POSITION", 12), ("OPERATION", 17), ("INFO", 24)]; + [("i", 5), ("POSITION", 12), ("OPERATION", 17), ("INFO", 36)]; const INSTRUCTION_BORDERS: [&str; 3] = [ - "╭─────┬────────────┬─────────────────┬────────────────────────╮", - "├─────┼────────────┼─────────────────┼────────────────────────┤", - "╰─────┴────────────┴─────────────────┴────────────────────────╯", + "╭─────┬────────────┬─────────────────┬────────────────────────────────────╮", + "├─────┼────────────┼─────────────────┼────────────────────────────────────┤", + "╰─────┴────────────┴─────────────────┴────────────────────────────────────╯", ]; const LOCAL_COLUMNS: [(&str, usize); 5] = [ @@ -286,7 +286,7 @@ impl<'a, W: Write> Disassembler<'a, W> { .unwrap_or("stripped".to_string()); let operation = instruction.operation().to_string(); let info = instruction.disassembly_info(); - let row = format!("│{index:^5}│{position:^12}│{operation:^17}│{info:^24}│"); + let row = format!("│{index:^5}│{position:^12}│{operation:^17}│{info:^36}│"); self.write_center_border(&row)?; } @@ -322,7 +322,6 @@ impl<'a, W: Write> Disassembler<'a, W> { let identifier_display = self .chunk .constants - .strings .get(*identifier_index as usize) .map(|value| value.to_string()) .unwrap_or_else(|| "unknown".to_string()); @@ -353,88 +352,8 @@ impl<'a, W: Write> Disassembler<'a, W> { self.write_center_border(&column_name_line)?; self.write_center_border(CONSTANT_BORDERS[1])?; - if self.chunk.constants.r#true { - let type_display = Type::Boolean.to_string(); - let value_display = "true"; - let constant_display = format!("│{:^5}│{:^26}│{:^26}│", 0, type_display, value_display); - - self.write_center_border(&constant_display)?; - } - - if self.chunk.constants.r#false { - let type_display = Type::Boolean.to_string(); - let value_display = "false"; - let constant_display = format!("│{:^5}│{:^26}│{:^26}│", 1, type_display, value_display); - - self.write_center_border(&constant_display)?; - } - - for (index, value) in self.chunk.constants.bytes.iter().enumerate() { - let type_display = Type::Byte.to_string(); - let value_display = { - let mut value_string = value.to_string(); - - if value_string.len() > 26 { - value_string = format!("{value_string:.23}..."); - } - - value_string - }; - let constant_display = format!("│{index:^5}│{type_display:^26}│{value_display:^26}│"); - - self.write_center_border(&constant_display)?; - } - - for (index, value) in self.chunk.constants.characters.iter().enumerate() { - let type_display = Type::Character.to_string(); - let value_display = { - let mut value_string = value.to_string(); - - if value_string.len() > 26 { - value_string = format!("{value_string:.23}..."); - } - - value_string - }; - let constant_display = format!("│{index:^5}│{type_display:^26}│{value_display:^26}│"); - - self.write_center_border(&constant_display)?; - } - - for (index, value) in self.chunk.constants.floats.iter().enumerate() { - let type_display = Type::Float.to_string(); - let value_display = { - let mut value_string = value.to_string(); - - if value_string.len() > 26 { - value_string = format!("{value_string:.23}..."); - } - - value_string - }; - let constant_display = format!("│{index:^5}│{type_display:^26}│{value_display:^26}│"); - - self.write_center_border(&constant_display)?; - } - - for (index, value) in self.chunk.constants.integers.iter().enumerate() { - let type_display = Type::Integer.to_string(); - let value_display = { - let mut value_string = value.to_string(); - - if value_string.len() > 26 { - value_string = format!("{value_string:.23}..."); - } - - value_string - }; - let constant_display = format!("│{index:^5}│{type_display:^26}│{value_display:^26}│"); - - self.write_center_border(&constant_display)?; - } - - for (index, value) in self.chunk.constants.strings.iter().enumerate() { - let type_display = Type::String.to_string(); + for (index, value) in self.chunk.constants.iter().enumerate() { + let type_display = value.r#type().to_string(); let value_display = { let mut value_string = value.to_string(); @@ -455,7 +374,7 @@ impl<'a, W: Write> Disassembler<'a, W> { } pub fn write_prototype_section(&mut self) -> Result<(), io::Error> { - self.write_center_border_bold("Prototypes")?; + self.write_center_border_bold("Functions")?; for chunk in &self.chunk.prototypes { chunk diff --git a/dust-lang/src/chunk/mod.rs b/dust-lang/src/chunk/mod.rs index 29f68b7..2944767 100644 --- a/dust-lang/src/chunk/mod.rs +++ b/dust-lang/src/chunk/mod.rs @@ -13,12 +13,10 @@ //! [`Chunk::with_data`] can be used to create a chunk for comparison to the compiler output. Do not //! try to run these chunks in a virtual machine. Due to their missing stack size and record index, //! they will cause a panic or undefined behavior. -mod constant_table; mod disassembler; mod local; mod scope; -pub use constant_table::ConstantTable; pub use disassembler::Disassembler; pub use local::Local; pub use scope::Scope; @@ -29,7 +27,7 @@ use std::sync::Arc; use serde::{Deserialize, Serialize}; -use crate::{DustString, Function, FunctionType, Instruction, Span}; +use crate::{DustString, Function, FunctionType, Instruction, Span, Value}; /// Representation of a Dust program or function. /// @@ -41,7 +39,7 @@ pub struct Chunk { pub(crate) instructions: Vec, pub(crate) positions: Vec, - pub(crate) constants: ConstantTable, + pub(crate) constants: Vec, pub(crate) locals: Vec, pub(crate) prototypes: Vec>, @@ -56,7 +54,7 @@ impl Chunk { r#type: FunctionType, instructions: impl Into>, positions: impl Into>, - constants: ConstantTable, + constants: impl Into>, locals: impl Into>, prototypes: impl IntoIterator, ) -> Self { @@ -65,7 +63,7 @@ impl Chunk { r#type, instructions: instructions.into(), positions: positions.into(), - constants, + constants: constants.into(), locals: locals.into(), prototypes: prototypes.into_iter().map(Arc::new).collect(), register_count: 0, diff --git a/dust-lang/src/compiler/error.rs b/dust-lang/src/compiler/error.rs index 63aee4f..98eb619 100644 --- a/dust-lang/src/compiler/error.rs +++ b/dust-lang/src/compiler/error.rs @@ -104,6 +104,14 @@ pub enum CompileError { right_type: Type, position: Span, }, + CannotNegateType { + argument_type: Type, + position: Span, + }, + CannotNotType { + argument_type: Type, + position: Span, + }, CannotSubtractType { argument_type: Type, position: Span, @@ -182,6 +190,8 @@ impl AnnotatedError for CompileError { Self::CannotMutateImmutableVariable { .. } => "Cannot mutate immutable variable", Self::CannotMultiplyArguments { .. } => "Cannot multiply these types", Self::CannotMultiplyType { .. } => "Cannot multiply this type", + Self::CannotNegateType { .. } => "Cannot negate this type", + Self::CannotNotType { .. } => "Cannot apply \"not\" operation to this type", Self::CannotResolveRegisterType { .. } => "Cannot resolve register type", Self::CannotResolveVariableType { .. } => "Cannot resolve type", Self::CannotSubtractType { .. } => "Cannot subtract from this type", @@ -251,8 +261,10 @@ impl AnnotatedError for CompileError { right_position, } => { vec![( - format!("Type \"{left_type}\" cannot be added to type \"{right_type}\". Try converting one of the values to the other type."), - Span(left_position.0, right_position.1) + 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), )] } _ => Vec::with_capacity(0), diff --git a/dust-lang/src/compiler/mod.rs b/dust-lang/src/compiler/mod.rs index cb007b1..8df9d3a 100644 --- a/dust-lang/src/compiler/mod.rs +++ b/dust-lang/src/compiler/mod.rs @@ -35,8 +35,7 @@ use optimize::control_flow_register_consolidation; use crate::{ Chunk, ConcreteValue, DustError, DustString, FunctionType, Instruction, Lexer, Local, NativeFunction, Operand, Operation, Scope, Span, Token, TokenKind, Type, Value, - chunk::ConstantTable, - instruction::{GetLocal, Jump, LoadList}, + instruction::{CallNative, Close, GetLocal, Jump, LoadList, Return, SetLocal, TypeCode}, }; /// Compiles the input and returns a chunk. @@ -89,7 +88,7 @@ pub struct Compiler<'src> { /// Constants that have been compiled. These are assigned to the chunk when [`Compiler::finish`] /// is called. - constants: ConstantTable, + constants: Vec, /// Block-local variables and their types. The locals are assigned to the chunk when /// [`Compiler::finish`] is called. The types are discarded after compilation. @@ -145,7 +144,7 @@ impl<'src> Compiler<'src> { return_type: Type::None, }, instructions: Vec::new(), - constants: ConstantTable::new(), + constants: Vec::new(), locals: Vec::new(), prototypes: Vec::new(), stack_size: 0, @@ -216,7 +215,7 @@ impl<'src> Compiler<'src> { r#type: self.r#type, instructions, positions, - constants: self.constants, + constants: self.constants.to_vec(), locals, prototypes: self.prototypes, register_count: self.stack_size, @@ -276,10 +275,13 @@ impl<'src> Compiler<'src> { .enumerate() .rev() .find_map(|(index, (local, _))| { - let identifier = self - .constants - .strings - .get(local.identifier_index as usize)?; + let constant = self.constants.get(local.identifier_index as usize)?; + let identifier = + if let Value::Concrete(ConcreteValue::String(identifier)) = constant { + identifier + } else { + return None; + }; if identifier == identifier_text { Some(index as u16) @@ -303,8 +305,8 @@ impl<'src> Compiler<'src> { ) -> (u16, u16) { info!("Declaring local {identifier}"); - let identifier = DustString::from(identifier); - let identifier_index = self.push_or_get_constant_string(identifier); + let identifier = Value::Concrete(ConcreteValue::string(identifier)); + let identifier_index = self.push_or_get_constant(identifier); let local_index = self.locals.len() as u16; self.locals.push(( @@ -320,102 +322,24 @@ impl<'src> Compiler<'src> { .get(local_index as usize) .and_then(|(local, _)| { self.constants - .strings .get(local.identifier_index as usize) .map(|value| value.to_string()) }) } - fn push_or_get_constant_bool(&mut self, boolean: bool) -> u16 { - if boolean { - if self.constants.r#true { - 0 - } else { - self.constants.r#true = true; - - 0 - } - } else if self.constants.r#false { - 1 - } else { - self.constants.r#false = true; - - 1 - } - } - - fn push_or_get_constant_byte(&mut self, new_byte: u8) -> u16 { + fn push_or_get_constant(&mut self, value: Value) -> u16 { if let Some(index) = self .constants - .bytes .iter() - .position(|&byte| byte == new_byte) + .position(|constant| constant == &value) { index as u16 } else { - self.constants.bytes.push(new_byte); + let index = self.constants.len() as u16; - (self.constants.bytes.len() - 1) as u16 - } - } + self.constants.push(value); - fn push_or_get_constant_character(&mut self, new_character: char) -> u16 { - if let Some(index) = self - .constants - .characters - .iter() - .position(|&character| character == new_character) - { - index as u16 - } else { - self.constants.characters.push(new_character); - - (self.constants.characters.len() - 1) as u16 - } - } - - fn push_or_get_constant_float(&mut self, new_float: f64) -> u16 { - if let Some(index) = self - .constants - .floats - .iter() - .position(|&float| float == new_float) - { - index as u16 - } else { - self.constants.floats.push(new_float); - - (self.constants.floats.len() - 1) as u16 - } - } - - fn push_or_get_constant_integer(&mut self, new_integer: i64) -> u16 { - if let Some(index) = self - .constants - .integers - .iter() - .position(|&integer| integer == new_integer) - { - index as u16 - } else { - self.constants.integers.push(new_integer); - - (self.constants.integers.len() - 1) as u16 - } - } - - fn push_or_get_constant_string(&mut self, new_string: DustString) -> u16 { - if let Some(index) = self - .constants - .strings - .iter() - .position(|string| *string == new_string) - { - index as u16 - } else { - self.constants.strings.push(new_string); - - (self.constants.strings.len() - 1) as u16 + index } } @@ -557,14 +481,6 @@ impl<'src> Compiler<'src> { Ok(()) } - fn emit_boolean_constant(&mut self, boolean: bool, position: Span) { - let constant_index = self.push_or_get_constant_bool(boolean); - let destination = self.next_register(); - let load_boolean = Instruction::load_constant(destination, constant_index, jump_next); - - self.emit_instruction(load_boolean, Type::Boolean, position); - } - fn parse_boolean(&mut self) -> Result<(), CompileError> { let position = self.current_position; @@ -735,17 +651,39 @@ impl<'src> Compiler<'src> { } let destination = self.next_register(); - let instruction = match (operator, &previous_type) { - (Token::Bang, Type::Boolean) => Instruction::not(destination, argument), - (Token::Minus, Type::Float) => Instruction::negate_float(destination, argument), - (Token::Minus, Type::Integer) => Instruction::negate_int(destination, argument), - _ => { - return Err(CompileError::ExpectedTokenMultiple { - expected: &[TokenKind::Bang, TokenKind::Minus], - found: operator.to_owned(), - position: operator_position, - }); - } + let type_code = match previous_type { + Type::Boolean => TypeCode::BOOLEAN, + Type::Byte => TypeCode::BYTE, + Type::Character => TypeCode::CHARACTER, + Type::Float => TypeCode::FLOAT, + Type::Integer => TypeCode::INTEGER, + Type::String => TypeCode::STRING, + _ => match operator { + Token::Minus => { + return Err(CompileError::CannotNegateType { + argument_type: previous_type, + position: previous_position, + }); + } + Token::Bang => { + return Err(CompileError::CannotNotType { + argument_type: previous_type, + position: previous_position, + }); + } + _ => { + return Err(CompileError::ExpectedTokenMultiple { + expected: &[TokenKind::Bang, TokenKind::Minus], + found: operator.to_owned(), + position: operator_position, + }); + } + }, + }; + let instruction = match operator { + Token::Bang => Instruction::not(destination, argument), + Token::Minus => Instruction::negate(destination, argument, type_code), + _ => unreachable!(), }; self.emit_instruction(instruction, previous_type, operator_position); @@ -768,43 +706,15 @@ impl<'src> Compiler<'src> { Operation::LOAD_BOOLEAN | Operation::LOAD_LIST | Operation::LOAD_SELF - | Operation::ADD_BYTE - | Operation::ADD_CHAR - | Operation::ADD_CHAR_STR - | Operation::ADD_FLOAT - | Operation::ADD_INT - | Operation::ADD_STR - | Operation::ADD_STR_CHAR - | Operation::SUBTRACT_BYTE - | Operation::SUBTRACT_FLOAT - | Operation::SUBTRACT_INT - | Operation::MULTIPLY_BYTE - | Operation::MULTIPLY_FLOAT - | Operation::MULTIPLY_INT - | Operation::DIVIDE_BYTE - | Operation::DIVIDE_FLOAT - | Operation::DIVIDE_INT - | Operation::MODULO_BYTE - | Operation::MODULO_INT - | Operation::MODULO_FLOAT - | Operation::EQUAL_INT - | Operation::EQUAL_FLOAT - | Operation::EQUAL_CHAR - | Operation::EQUAL_STR - | Operation::EQUAL_BOOL - | Operation::EQUAL_BYTE - | Operation::LESS_INT - | Operation::LESS_FLOAT - | Operation::LESS_CHAR - | Operation::LESS_STR - | Operation::LESS_BYTE - | Operation::LESS_EQUAL_INT - | Operation::LESS_EQUAL_FLOAT - | Operation::LESS_EQUAL_CHAR - | Operation::LESS_EQUAL_STR - | Operation::LESS_EQUAL_BYTE - | Operation::NEGATE_INT - | Operation::NEGATE_FLOAT + | Operation::ADD + | Operation::SUBTRACT + | Operation::MULTIPLY + | Operation::DIVIDE + | Operation::MODULO + | Operation::EQUAL + | Operation::LESS + | Operation::LESS_EQUAL + | Operation::NEGATE | Operation::NOT | Operation::CALL => (Operand::Register(instruction.a_field()), true), Operation::CALL_NATIVE => { @@ -869,6 +779,16 @@ impl<'src> Compiler<'src> { check_math_type(&left_type, operator, &left_position)?; + let left_type_code = match left_type { + Type::Boolean => TypeCode::BOOLEAN, + Type::Byte => TypeCode::BYTE, + Type::Character => TypeCode::CHARACTER, + Type::Float => TypeCode::FLOAT, + Type::Integer => TypeCode::INTEGER, + Type::String => TypeCode::STRING, + _ => unreachable!(), + }; + if is_assignment && !left_is_mutable_local { return Err(CompileError::ExpectedMutableVariable { found: self.previous_token.to_owned(), @@ -891,9 +811,19 @@ impl<'src> Compiler<'src> { &right_position, )?; + let right_type_code = match right_type { + Type::Boolean => TypeCode::BOOLEAN, + Type::Byte => TypeCode::BYTE, + Type::Character => TypeCode::CHARACTER, + Type::Float => TypeCode::FLOAT, + Type::Integer => TypeCode::INTEGER, + Type::String => TypeCode::STRING, + _ => unreachable!(), + }; + if push_back_right { self.instructions - .push((right_instruction, right_type.clone(), right_position)); + .push((right_instruction, right_type, right_position)); } let r#type = if is_assignment { @@ -911,63 +841,21 @@ impl<'src> Compiler<'src> { } else { self.next_register() }; - let instruction = match (operator, left_type, right_type) { - (Token::Plus, Type::Byte, Type::Byte) => { - Instruction::add_byte(destination, left, right) + let instruction = match operator { + Token::Plus | Token::PlusEqual => { + Instruction::add(destination, left, left_type_code, right, right_type_code) } - (Token::Plus, Type::Character, Type::Character) => { - Instruction::add_char(destination, left, right) + Token::Minus | Token::MinusEqual => { + Instruction::subtract(destination, left, left_type_code, right, right_type_code) } - (Token::Plus, Type::Character, Type::String) => { - Instruction::add_char_str(destination, left, right) + Token::Star | Token::StarEqual => { + Instruction::multiply(destination, left, left_type_code, right, right_type_code) } - (Token::Plus, Type::Float, Type::Float) => { - Instruction::add_float(destination, left, right) + Token::Slash | Token::SlashEqual => { + Instruction::divide(destination, left, left_type_code, right, right_type_code) } - (Token::Plus, Type::Integer, Type::Integer) => { - Instruction::add_int(destination, left, right) - } - (Token::Plus, Type::String, Type::Character) => { - Instruction::add_str_char(destination, left, right) - } - (Token::Plus, Type::String, Type::String) => { - Instruction::add_str(destination, left, right) - } - (Token::Minus, Type::Byte, Type::Byte) => { - Instruction::subtract_byte(destination, left, right) - } - (Token::Minus, Type::Float, Type::Float) => { - Instruction::subtract_float(destination, left, right) - } - (Token::Minus, Type::Integer, Type::Integer) => { - Instruction::subtract_int(destination, left, right) - } - (Token::Star, Type::Byte, Type::Byte) => { - Instruction::multiply_byte(destination, left, right) - } - (Token::Star, Type::Float, Type::Float) => { - Instruction::multiply_float(destination, left, right) - } - (Token::Star, Type::Integer, Type::Integer) => { - Instruction::multiply_int(destination, left, right) - } - (Token::Slash, Type::Byte, Type::Byte) => { - Instruction::divide_byte(destination, left, right) - } - (Token::Slash, Type::Float, Type::Float) => { - Instruction::divide_float(destination, left, right) - } - (Token::Slash, Type::Integer, Type::Integer) => { - Instruction::divide_int(destination, left, right) - } - (Token::Percent, Type::Byte, Type::Byte) => { - Instruction::modulo_byte(destination, left, right) - } - (Token::Percent, Type::Integer, Type::Integer) => { - Instruction::modulo_int(destination, left, right) - } - (Token::Percent, Type::Float, Type::Float) => { - Instruction::modulo_float(destination, left, right) + Token::Percent | Token::PercentEqual => { + Instruction::modulo(destination, left, left_type_code, right, right_type_code) } _ => { return Err(CompileError::ExpectedTokenMultiple { @@ -995,9 +883,13 @@ impl<'src> Compiler<'src> { } fn parse_comparison_binary(&mut self) -> Result<(), CompileError> { - if self - .get_last_operation() - .is_some_and(|operation| operation.is_comparison()) + if let Some( + [ + Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL, + _, + _, + ], + ) = self.get_last_operations() { return Err(CompileError::ComparisonChain { position: self.current_position, @@ -1016,9 +908,21 @@ impl<'src> Compiler<'src> { let operator_position = self.current_position; let rule = ParseRule::from(&operator); + // TODO: Check if the left type is a valid type for comparison + + let left_type_code = match left_type { + Type::Boolean => TypeCode::BOOLEAN, + Type::Byte => TypeCode::BYTE, + Type::Character => TypeCode::CHARACTER, + Type::Float => TypeCode::FLOAT, + Type::Integer => TypeCode::INTEGER, + Type::String => TypeCode::STRING, + _ => unreachable!(), + }; + if push_back_left { self.instructions - .push((left_instruction, left_type.clone(), left_position)); + .push((left_instruction, left_type, left_position)); } self.advance()?; @@ -1033,112 +937,41 @@ impl<'src> Compiler<'src> { })?; let (right, push_back_right) = self.handle_binary_argument(&right_instruction)?; + // TODO: Check if the right type is a valid type for comparison + // TODO: Check if the left and right types are compatible + + let right_type_code = match right_type { + Type::Boolean => TypeCode::BOOLEAN, + Type::Byte => TypeCode::BYTE, + Type::Character => TypeCode::CHARACTER, + Type::Float => TypeCode::FLOAT, + Type::Integer => TypeCode::INTEGER, + Type::String => TypeCode::STRING, + _ => unreachable!(), + }; + if push_back_right { self.instructions - .push((right_instruction, right_type.clone(), right_position)); + .push((right_instruction, right_type, right_position)); } let destination = self.next_register(); - let comparison = match (operator, left_type, right_type) { - (Token::DoubleEqual, Type::Boolean, Type::Boolean) => { - Instruction::equal_bool(true, left, right) + let comparison = match operator { + Token::DoubleEqual => { + Instruction::equal(true, left, left_type_code, right, right_type_code) } - (Token::DoubleEqual, Type::Byte, Type::Byte) => { - Instruction::equal_byte(true, left, right) + Token::BangEqual => { + Instruction::equal(false, left, left_type_code, right, right_type_code) } - (Token::DoubleEqual, Type::Character, Type::Character) => { - Instruction::equal_char(true, left, right) + Token::Less => Instruction::less(true, left, left_type_code, right, right_type_code), + Token::LessEqual => { + Instruction::less_equal(true, left, left_type_code, right, right_type_code) } - (Token::DoubleEqual, Type::Character, Type::String) => { - Instruction::equal_char_str(true, left, right) + Token::Greater => { + Instruction::less_equal(false, left, left_type_code, right, right_type_code) } - (Token::DoubleEqual, Type::Float, Type::Float) => { - Instruction::equal_float(true, left, right) - } - (Token::DoubleEqual, Type::Integer, Type::Integer) => { - Instruction::equal_int(true, left, right) - } - (Token::DoubleEqual, Type::String, Type::String) => { - Instruction::equal_str(true, left, right) - } - (Token::DoubleEqual, Type::String, Type::Character) => { - Instruction::equal_str_char(true, left, right) - } - (Token::BangEqual, Type::Boolean, Type::Boolean) => { - Instruction::equal_bool(false, left, right) - } - (Token::BangEqual, Type::Byte, Type::Byte) => { - Instruction::equal_byte(false, left, right) - } - (Token::BangEqual, Type::Character, Type::Character) => { - Instruction::equal_char(false, left, right) - } - (Token::BangEqual, Type::Character, Type::String) => { - Instruction::equal_char_str(false, left, right) - } - (Token::BangEqual, Type::Float, Type::Float) => { - Instruction::equal_float(false, left, right) - } - (Token::BangEqual, Type::Integer, Type::Integer) => { - Instruction::equal_int(false, left, right) - } - (Token::BangEqual, Type::String, Type::String) => { - Instruction::equal_str(false, left, right) - } - (Token::BangEqual, Type::String, Type::Character) => { - Instruction::equal_str_char(false, left, right) - } - (Token::Less, Type::Byte, Type::Byte) => Instruction::less_byte(true, left, right), - (Token::Less, Type::Character, Type::Character) => { - Instruction::less_char(true, left, right) - } - (Token::Less, Type::Float, Type::Float) => Instruction::less_float(true, left, right), - (Token::Less, Type::Integer, Type::Integer) => Instruction::less_int(true, left, right), - (Token::Less, Type::String, Type::String) => Instruction::less_str(true, left, right), - (Token::GreaterEqual, Type::Byte, Type::Byte) => { - Instruction::less_byte(false, left, right) - } - (Token::GreaterEqual, Type::Character, Type::Character) => { - Instruction::less_char(false, left, right) - } - (Token::GreaterEqual, Type::Float, Type::Float) => { - Instruction::less_float(false, left, right) - } - (Token::GreaterEqual, Type::Integer, Type::Integer) => { - Instruction::less_int(false, left, right) - } - (Token::GreaterEqual, Type::String, Type::String) => { - Instruction::less_str(false, left, right) - } - (Token::LessEqual, Type::Byte, Type::Byte) => { - Instruction::less_equal_byte(true, left, right) - } - (Token::LessEqual, Type::Character, Type::Character) => { - Instruction::less_equal_char(true, left, right) - } - (Token::LessEqual, Type::Float, Type::Float) => { - Instruction::less_equal_float(true, left, right) - } - (Token::LessEqual, Type::Integer, Type::Integer) => { - Instruction::less_equal_int(true, left, right) - } - (Token::LessEqual, Type::String, Type::String) => { - Instruction::less_equal_str(true, left, right) - } - (Token::Greater, Type::Byte, Type::Byte) => { - Instruction::less_equal_byte(false, left, right) - } - (Token::Greater, Type::Character, Type::Character) => { - Instruction::less_equal_char(false, left, right) - } - (Token::Greater, Type::Float, Type::Float) => { - Instruction::less_equal_float(false, left, right) - } - (Token::Greater, Type::Integer, Type::Integer) => { - Instruction::less_equal_int(false, left, right) - } - (Token::Greater, Type::String, Type::String) => { - Instruction::less_equal_str(false, left, right) + Token::GreaterEqual => { + Instruction::less(false, left, left_type_code, right, right_type_code) } _ => { return Err(CompileError::ExpectedTokenMultiple { @@ -1223,23 +1056,7 @@ impl<'src> Compiler<'src> { if !matches!( instructions[0].0.operation(), - Operation::TEST - | Operation::EQUAL_INT - | Operation::EQUAL_STR - | Operation::EQUAL_BOOL - | Operation::EQUAL_BYTE - | Operation::EQUAL_CHAR - | Operation::EQUAL_FLOAT - | Operation::LESS_INT - | Operation::LESS_STR - | Operation::LESS_BYTE - | Operation::LESS_CHAR - | Operation::LESS_FLOAT - | Operation::LESS_EQUAL_INT - | Operation::LESS_EQUAL_STR - | Operation::LESS_EQUAL_BYTE - | Operation::LESS_EQUAL_CHAR - | Operation::LESS_EQUAL_FLOAT + Operation::TEST | Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL ) || !matches!(instructions[1].0.operation(), Operation::JUMP) { continue; @@ -1321,7 +1138,10 @@ impl<'src> Compiler<'src> { math_instruction.set_a_field(local_register_index); } else { let register = self.next_register() - 1; - let set_local = Instruction::set_local(register, local_index); + let set_local = Instruction::from(SetLocal { + register_index: register, + local_index, + }); self.emit_instruction(set_local, Type::None, start_position); } @@ -1396,7 +1216,10 @@ impl<'src> Compiler<'src> { } if expected_register < actual_register { - let close = Instruction::close(expected_register, actual_register); + let close = Instruction::from(Close { + from: expected_register, + to: actual_register, + }); self.emit_instruction(close, Type::None, self.current_position); } @@ -1420,22 +1243,7 @@ impl<'src> Compiler<'src> { if matches!( self.get_last_operations(), Some([ - Operation::EQUAL_INT - | Operation::EQUAL_STR - | Operation::EQUAL_BOOL - | Operation::EQUAL_BYTE - | Operation::EQUAL_CHAR - | Operation::EQUAL_FLOAT - | Operation::LESS_INT - | Operation::LESS_STR - | Operation::LESS_BYTE - | Operation::LESS_CHAR - | Operation::LESS_FLOAT - | Operation::LESS_EQUAL_INT - | Operation::LESS_EQUAL_STR - | Operation::LESS_EQUAL_BYTE - | Operation::LESS_EQUAL_CHAR - | Operation::LESS_EQUAL_FLOAT, + Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL, Operation::JUMP, Operation::LOAD_BOOLEAN, Operation::LOAD_BOOLEAN @@ -1508,7 +1316,10 @@ impl<'src> Compiler<'src> { loader.set_c_field(true as u16); } else { if_block_distance += 1; - let jump = Instruction::jump(else_block_distance, true); + let jump = Instruction::from(Jump { + offset: else_block_distance, + is_positive: true, + }); self.instructions .insert(if_block_end, (jump, Type::None, self.current_position)); @@ -1516,7 +1327,10 @@ impl<'src> Compiler<'src> { } 2.. => { if_block_distance += 1; - let jump = Instruction::jump(else_block_distance, true); + let jump = Instruction::from(Jump { + offset: else_block_distance, + is_positive: true, + }); self.instructions .insert(if_block_end, (jump, Type::None, self.current_position)); @@ -1535,29 +1349,14 @@ impl<'src> Compiler<'src> { fn parse_while(&mut self) -> Result<(), CompileError> { self.advance()?; - let expression_start = self.instructions.len() as u16; + let expression_start = self.instructions.len(); self.parse_expression()?; if matches!( self.get_last_operations(), Some([ - Operation::EQUAL_INT - | Operation::EQUAL_STR - | Operation::EQUAL_BOOL - | Operation::EQUAL_BYTE - | Operation::EQUAL_CHAR - | Operation::EQUAL_FLOAT - | Operation::LESS_INT - | Operation::LESS_STR - | Operation::LESS_BYTE - | Operation::LESS_CHAR - | Operation::LESS_FLOAT - | Operation::LESS_EQUAL_INT - | Operation::LESS_EQUAL_STR - | Operation::LESS_EQUAL_BYTE - | Operation::LESS_EQUAL_CHAR - | Operation::LESS_EQUAL_FLOAT, + Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL, Operation::JUMP, Operation::LOAD_BOOLEAN, Operation::LOAD_BOOLEAN @@ -1577,15 +1376,21 @@ impl<'src> Compiler<'src> { self.parse_block()?; - let block_end = self.instructions.len() as u16; - let jump_distance = block_end - block_start as u16 + 1; - let jump = Instruction::jump(jump_distance, true); + let block_end = self.instructions.len(); + let jump_distance = (block_end - block_start + 1) as u16; + let jump = Instruction::from(Jump { + offset: jump_distance, + is_positive: true, + }); self.instructions .insert(block_start, (jump, Type::None, self.current_position)); - let jump_back_distance = block_end - expression_start + 1; - let jump_back = Instruction::jump(jump_back_distance, false); + let jump_back_distance = (block_end - expression_start + 1) as u16; + let jump_back = Instruction::from(Jump { + offset: jump_back_distance, + is_positive: false, + }); self.emit_instruction(jump_back, Type::None, self.current_position); @@ -1604,9 +1409,13 @@ impl<'src> Compiler<'src> { self.parse_expression()?; let actual_register = self.next_register() - 1; + let registers_to_close = actual_register - expected_register; - if expected_register < actual_register { - let close = Instruction::close(expected_register, actual_register); + if registers_to_close > 0 { + let close = Instruction::from(Close { + from: expected_register, + to: actual_register, + }); self.emit_instruction(close, Type::None, self.current_position); } @@ -1618,7 +1427,11 @@ impl<'src> Compiler<'src> { let destination = self.next_register(); let argument_count = destination - start_register; let return_type = function.r#type().return_type; - let call_native = Instruction::call_native(destination, function, argument_count); + let call_native = Instruction::from(CallNative { + destination, + function, + argument_count, + }); self.emit_instruction(call_native, return_type, Span(start, end)); @@ -1681,7 +1494,10 @@ impl<'src> Compiler<'src> { }; let end = self.current_position.1; let return_register = self.next_register() - 1; - let r#return = Instruction::r#return(should_return_value, return_register); + let r#return = Instruction::from(Return { + should_return_value, + return_register, + }); self.emit_instruction(r#return, Type::None, Span(start, end)); @@ -1980,13 +1796,16 @@ impl<'src> Compiler<'src> { let registers_to_close = (actual_register - expected_register).saturating_sub(1); if registers_to_close > 0 { - let close = Instruction::close(expected_register, actual_register); + let close = Instruction::from(Close { + from: expected_register, + to: actual_register, + }); self.emit_instruction(close, Type::None, self.current_position); - - argument_count += registers_to_close + 1; } + argument_count += registers_to_close + 1; + self.allow(Token::Comma)?; } diff --git a/dust-lang/src/compiler/optimize.rs b/dust-lang/src/compiler/optimize.rs index 3d45bad..bf22d34 100644 --- a/dust-lang/src/compiler/optimize.rs +++ b/dust-lang/src/compiler/optimize.rs @@ -37,23 +37,7 @@ pub fn control_flow_register_consolidation(compiler: &mut Compiler) { if !matches!( compiler.get_last_operations(), Some([ - Operation::TEST - | Operation::EQUAL_INT - | Operation::EQUAL_STR - | Operation::EQUAL_BOOL - | Operation::EQUAL_BYTE - | Operation::EQUAL_CHAR - | Operation::EQUAL_FLOAT - | Operation::LESS_INT - | Operation::LESS_STR - | Operation::LESS_BYTE - | Operation::LESS_CHAR - | Operation::LESS_FLOAT - | Operation::LESS_EQUAL_INT - | Operation::LESS_EQUAL_STR - | Operation::LESS_EQUAL_BYTE - | Operation::LESS_EQUAL_CHAR - | Operation::LESS_EQUAL_FLOAT, + Operation::TEST | Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL, Operation::JUMP, Operation::LOAD_BOOLEAN | Operation::LOAD_CONSTANT, Operation::LOAD_BOOLEAN | Operation::LOAD_CONSTANT, diff --git a/dust-lang/src/instruction/add.rs b/dust-lang/src/instruction/add.rs new file mode 100644 index 0000000..94230bb --- /dev/null +++ b/dust-lang/src/instruction/add.rs @@ -0,0 +1,69 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; + +pub struct Add { + pub destination: u16, + pub left: Operand, + pub left_type: TypeCode, + pub right: Operand, + pub right_type: TypeCode, +} + +impl From for Add { + fn from(instruction: Instruction) -> Self { + let destination = instruction.a_field(); + let (left, right) = instruction.b_and_c_as_operands(); + let left_type = instruction.b_type(); + let right_type = instruction.c_type(); + + Add { + destination, + left, + left_type, + right, + right_type, + } + } +} + +impl From for Instruction { + fn from(add: Add) -> Self { + let operation = Operation::ADD; + let a_field = add.destination; + let (b_field, b_is_constant) = add.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = add.right.as_index_and_constant_flag(); + let b_type = add.left_type; + let c_type = add.right_type; + + InstructionBuilder { + operation, + a_field, + b_field, + c_field, + b_is_constant, + c_is_constant, + b_type, + c_type, + ..Default::default() + } + .build() + } +} + +impl Display for Add { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let Add { + destination, + left, + left_type, + right, + right_type, + } = self; + + write!( + f, + "R{destination} = {left}({left_type}) + {right}({right_type})", + ) + } +} diff --git a/dust-lang/src/instruction/add_byte.rs b/dust-lang/src/instruction/add_byte.rs deleted file mode 100644 index 764badd..0000000 --- a/dust-lang/src/instruction/add_byte.rs +++ /dev/null @@ -1,54 +0,0 @@ -use std::fmt::Display; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct AddByte { - pub destination: u16, - pub left: Operand, - pub right: Operand, -} - -impl From for AddByte { - fn from(instruction: Instruction) -> Self { - let destination = instruction.a_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - AddByte { - destination, - left, - right, - } - } -} - -impl From for Instruction { - fn from(add_byte: AddByte) -> Self { - let operation = Operation::ADD_BYTE; - let a_field = add_byte.destination; - let (b_field, b_is_constant) = add_byte.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = add_byte.right.as_index_and_constant_flag(); - - InstructionBuilder { - operation, - a_field, - b_field, - c_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for AddByte { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let AddByte { - destination, - left, - right, - } = self; - - write!(f, "R{} = {} + {}", destination, left, right) - } -} diff --git a/dust-lang/src/instruction/add_char.rs b/dust-lang/src/instruction/add_char.rs deleted file mode 100644 index f0260ea..0000000 --- a/dust-lang/src/instruction/add_char.rs +++ /dev/null @@ -1,54 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct AddChar { - pub destination: u16, - pub left: Operand, - pub right: Operand, -} - -impl From for AddChar { - fn from(instruction: Instruction) -> Self { - let destination = instruction.a_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - AddChar { - destination, - left, - right, - } - } -} - -impl From for Instruction { - fn from(add_char: AddChar) -> Self { - let operation = Operation::ADD_CHAR; - let a_field = add_char.destination; - let (b_field, b_is_constant) = add_char.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = add_char.right.as_index_and_constant_flag(); - - InstructionBuilder { - operation, - a_field, - b_field, - c_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for AddChar { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let AddChar { - destination, - left, - right, - } = self; - - write!(f, "R{} = {} + {}", destination, left, right) - } -} diff --git a/dust-lang/src/instruction/add_char_str.rs b/dust-lang/src/instruction/add_char_str.rs deleted file mode 100644 index 4ffe95e..0000000 --- a/dust-lang/src/instruction/add_char_str.rs +++ /dev/null @@ -1,54 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct AddCharStr { - pub destination: u16, - pub left: Operand, - pub right: Operand, -} - -impl From for AddCharStr { - fn from(instruction: Instruction) -> Self { - let destination = instruction.a_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - AddCharStr { - destination, - left, - right, - } - } -} - -impl From for Instruction { - fn from(add_char_str: AddCharStr) -> Self { - let operation = Operation::ADD_CHAR_STR; - let a_field = add_char_str.destination; - let (b_field, b_is_constant) = add_char_str.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = add_char_str.right.as_index_and_constant_flag(); - - InstructionBuilder { - operation, - a_field, - b_field, - c_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for AddCharStr { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let AddCharStr { - destination, - left, - right, - } = self; - - write!(f, "R{} = {} + {}", destination, left, right) - } -} diff --git a/dust-lang/src/instruction/add_float.rs b/dust-lang/src/instruction/add_float.rs deleted file mode 100644 index a8292f6..0000000 --- a/dust-lang/src/instruction/add_float.rs +++ /dev/null @@ -1,54 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct AddFloat { - pub destination: u16, - pub left: Operand, - pub right: Operand, -} - -impl From for AddFloat { - fn from(instruction: Instruction) -> Self { - let destination = instruction.a_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - AddFloat { - destination, - left, - right, - } - } -} - -impl From for Instruction { - fn from(add_float: AddFloat) -> Self { - let operation = Operation::ADD_FLOAT; - let a_field = add_float.destination; - let (b_field, b_is_constant) = add_float.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = add_float.right.as_index_and_constant_flag(); - - InstructionBuilder { - operation, - a_field, - b_field, - c_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for AddFloat { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let AddFloat { - destination, - left, - right, - } = self; - - write!(f, "R{} = {} + {}", destination, left, right) - } -} diff --git a/dust-lang/src/instruction/add_int.rs b/dust-lang/src/instruction/add_int.rs deleted file mode 100644 index 21f2bf7..0000000 --- a/dust-lang/src/instruction/add_int.rs +++ /dev/null @@ -1,56 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use crate::{Instruction, Operand, Operation}; - -use super::InstructionBuilder; - -pub struct AddInt { - pub destination: u16, - pub left: Operand, - pub right: Operand, -} - -impl From for AddInt { - fn from(instruction: Instruction) -> Self { - let destination = instruction.a_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - AddInt { - destination, - left, - right, - } - } -} - -impl From for Instruction { - fn from(add_int: AddInt) -> Self { - let operation = Operation::ADD_INT; - let a_field = add_int.destination; - let (b_field, b_is_constant) = add_int.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = add_int.right.as_index_and_constant_flag(); - - InstructionBuilder { - operation, - a_field, - b_field, - c_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for AddInt { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let AddInt { - destination, - left, - right, - } = self; - - write!(f, "R{} = {} + {}", destination, left, right) - } -} diff --git a/dust-lang/src/instruction/add_str.rs b/dust-lang/src/instruction/add_str.rs deleted file mode 100644 index a43a3c9..0000000 --- a/dust-lang/src/instruction/add_str.rs +++ /dev/null @@ -1,54 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct AddStr { - pub destination: u16, - pub left: Operand, - pub right: Operand, -} - -impl From for AddStr { - fn from(instruction: Instruction) -> Self { - let destination = instruction.a_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - AddStr { - destination, - left, - right, - } - } -} - -impl From for Instruction { - fn from(add_str: AddStr) -> Self { - let operation = Operation::ADD_STR; - let a_field = add_str.destination; - let (b_field, b_is_constant) = add_str.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = add_str.right.as_index_and_constant_flag(); - - InstructionBuilder { - operation, - a_field, - b_field, - c_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for AddStr { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let AddStr { - destination, - left, - right, - } = self; - - write!(f, "R{} = {} + {}", destination, left, right) - } -} diff --git a/dust-lang/src/instruction/add_str_char.rs b/dust-lang/src/instruction/add_str_char.rs deleted file mode 100644 index 8638fd8..0000000 --- a/dust-lang/src/instruction/add_str_char.rs +++ /dev/null @@ -1,54 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct AddStrChar { - pub destination: u16, - pub left: Operand, - pub right: Operand, -} - -impl From for AddStrChar { - fn from(instruction: Instruction) -> Self { - let destination = instruction.a_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - AddStrChar { - destination, - left, - right, - } - } -} - -impl From for Instruction { - fn from(add_str_char: AddStrChar) -> Self { - let operation = Operation::ADD_STR_CHAR; - let a_field = add_str_char.destination; - let (b_field, b_is_constant) = add_str_char.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = add_str_char.right.as_index_and_constant_flag(); - - InstructionBuilder { - operation, - a_field, - b_field, - c_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for AddStrChar { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let AddStrChar { - destination, - left, - right, - } = self; - - write!(f, "R{} = {} + {}", destination, left, right) - } -} diff --git a/dust-lang/src/instruction/call.rs b/dust-lang/src/instruction/call.rs index 571bbb8..ff69533 100644 --- a/dust-lang/src/instruction/call.rs +++ b/dust-lang/src/instruction/call.rs @@ -1,3 +1,5 @@ +use std::fmt::{self, Display, Formatter}; + use crate::{Instruction, Operation}; use super::InstructionBuilder; @@ -43,3 +45,29 @@ impl From for Instruction { .build() } } + +impl Display for Call { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let Call { + destination, + function_register, + argument_count, + .. + } = self; + let arguments_start = destination.saturating_sub(*argument_count); + + match argument_count { + 0 => write!(f, "R{destination} = R{function_register}()"), + 1 => write!( + f, + "R{destination} = R{function_register}(R{arguments_start})" + ), + _ => { + write!( + f, + "R{destination} = R{function_register}(R{arguments_start}..R{destination})" + ) + } + } + } +} diff --git a/dust-lang/src/instruction/call_native.rs b/dust-lang/src/instruction/call_native.rs index c40733a..f66fac4 100644 --- a/dust-lang/src/instruction/call_native.rs +++ b/dust-lang/src/instruction/call_native.rs @@ -1,3 +1,5 @@ +use std::fmt::Display; + use crate::{Instruction, NativeFunction, Operation}; use super::InstructionBuilder; @@ -38,3 +40,27 @@ impl From for Instruction { .build() } } + +impl Display for CallNative { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let CallNative { + destination, + function, + argument_count, + } = self; + let arguments_start = destination.saturating_sub(*argument_count); + let arguments_end = arguments_start + argument_count; + + if function.returns_value() { + write!(f, "R{destination} = ")?; + } + + match argument_count { + 0 => { + write!(f, "{function}()") + } + 1 => write!(f, "{function}(R{arguments_start})"), + _ => write!(f, "{function}(R{arguments_start}..R{arguments_end})"), + } + } +} diff --git a/dust-lang/src/instruction/divide.rs b/dust-lang/src/instruction/divide.rs new file mode 100644 index 0000000..d039f90 --- /dev/null +++ b/dust-lang/src/instruction/divide.rs @@ -0,0 +1,69 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; + +pub struct Divide { + pub destination: u16, + pub left: Operand, + pub left_type: TypeCode, + pub right: Operand, + pub right_type: TypeCode, +} + +impl From for Divide { + fn from(instruction: Instruction) -> Self { + let destination = instruction.a_field(); + let (left, right) = instruction.b_and_c_as_operands(); + let left_type = instruction.b_type(); + let right_type = instruction.c_type(); + + Divide { + destination, + left, + left_type, + right, + right_type, + } + } +} + +impl From for Instruction { + fn from(divide: Divide) -> Self { + let operation = Operation::DIVIDE; + let a_field = divide.destination; + let (b_field, b_is_constant) = divide.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = divide.right.as_index_and_constant_flag(); + let b_type = divide.left_type; + let c_type = divide.right_type; + + InstructionBuilder { + operation, + a_field, + b_field, + c_field, + b_is_constant, + c_is_constant, + b_type, + c_type, + ..Default::default() + } + .build() + } +} + +impl Display for Divide { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let Divide { + destination, + left, + left_type, + right, + right_type, + } = self; + + write!( + f, + "R{destination} = {left_type}({left}) ÷ {right_type}({right})", + ) + } +} diff --git a/dust-lang/src/instruction/divide_byte.rs b/dust-lang/src/instruction/divide_byte.rs deleted file mode 100644 index ce2a4c4..0000000 --- a/dust-lang/src/instruction/divide_byte.rs +++ /dev/null @@ -1,54 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct DivideByte { - pub destination: u16, - pub left: Operand, - pub right: Operand, -} - -impl From for DivideByte { - fn from(instruction: Instruction) -> Self { - let destination = instruction.a_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - DivideByte { - destination, - left, - right, - } - } -} - -impl From for Instruction { - fn from(divide_byte: DivideByte) -> Self { - let operation = Operation::DIVIDE_BYTE; - let a_field = divide_byte.destination; - let (b_field, b_is_constant) = divide_byte.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = divide_byte.right.as_index_and_constant_flag(); - - InstructionBuilder { - operation, - a_field, - b_field, - c_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for DivideByte { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let DivideByte { - destination, - left, - right, - } = self; - - write!(f, "R{} = {} ÷ {}", destination, left, right) - } -} diff --git a/dust-lang/src/instruction/divide_float.rs b/dust-lang/src/instruction/divide_float.rs deleted file mode 100644 index aaaef22..0000000 --- a/dust-lang/src/instruction/divide_float.rs +++ /dev/null @@ -1,54 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct DivideFloat { - pub destination: u16, - pub left: Operand, - pub right: Operand, -} - -impl From for DivideFloat { - fn from(instruction: Instruction) -> Self { - let destination = instruction.a_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - DivideFloat { - destination, - left, - right, - } - } -} - -impl From for Instruction { - fn from(divide_float: DivideFloat) -> Self { - let operation = Operation::DIVIDE_FLOAT; - let a_field = divide_float.destination; - let (b_field, b_is_constant) = divide_float.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = divide_float.right.as_index_and_constant_flag(); - - InstructionBuilder { - operation, - a_field, - b_field, - c_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for DivideFloat { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let DivideFloat { - destination, - left, - right, - } = self; - - write!(f, "R{} = {} ÷ {}", destination, left, right) - } -} diff --git a/dust-lang/src/instruction/divide_int.rs b/dust-lang/src/instruction/divide_int.rs deleted file mode 100644 index b52ced0..0000000 --- a/dust-lang/src/instruction/divide_int.rs +++ /dev/null @@ -1,54 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct DivideInt { - pub destination: u16, - pub left: Operand, - pub right: Operand, -} - -impl From for DivideInt { - fn from(instruction: Instruction) -> Self { - let destination = instruction.a_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - DivideInt { - destination, - left, - right, - } - } -} - -impl From for Instruction { - fn from(divide_int: DivideInt) -> Self { - let operation = Operation::DIVIDE_INT; - let a_field = divide_int.destination; - let (b_field, b_is_constant) = divide_int.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = divide_int.right.as_index_and_constant_flag(); - - InstructionBuilder { - operation, - a_field, - b_field, - c_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for DivideInt { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let DivideInt { - destination, - left, - right, - } = self; - - write!(f, "R{} = {} ÷ {}", destination, left, right) - } -} diff --git a/dust-lang/src/instruction/equal_bool.rs b/dust-lang/src/instruction/equal.rs similarity index 55% rename from dust-lang/src/instruction/equal_bool.rs rename to dust-lang/src/instruction/equal.rs index 03a0997..6d1aed6 100644 --- a/dust-lang/src/instruction/equal_bool.rs +++ b/dust-lang/src/instruction/equal.rs @@ -1,32 +1,40 @@ use std::fmt::{self, Display, Formatter}; -use super::{Instruction, InstructionBuilder, Operand, Operation}; +use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; -pub struct EqualBool { +pub struct Equal { pub comparator: bool, pub left: Operand, + pub left_type: TypeCode, pub right: Operand, + pub right_type: TypeCode, } -impl From for EqualBool { +impl From for Equal { fn from(instruction: Instruction) -> Self { let comparator = instruction.d_field(); let (left, right) = instruction.b_and_c_as_operands(); + let left_type = instruction.b_type(); + let right_type = instruction.c_type(); - EqualBool { + Equal { comparator, left, + left_type, right, + right_type, } } } -impl From for Instruction { - fn from(equal_bool: EqualBool) -> Self { - let operation = Operation::EQUAL_BOOL; +impl From for Instruction { + fn from(equal_bool: Equal) -> Self { + let operation = Operation::EQUAL; let (b_field, b_is_constant) = equal_bool.left.as_index_and_constant_flag(); let (c_field, c_is_constant) = equal_bool.right.as_index_and_constant_flag(); let d_field = equal_bool.comparator; + let b_type = equal_bool.left_type; + let c_type = equal_bool.right_type; InstructionBuilder { operation, @@ -35,21 +43,28 @@ impl From for Instruction { d_field, b_is_constant, c_is_constant, + b_type, + c_type, ..Default::default() } .build() } } -impl Display for EqualBool { +impl Display for Equal { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let EqualBool { + let Equal { comparator, left, + left_type, right, + right_type, } = self; let operator = if *comparator { "==" } else { "≠" }; - write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") + write!( + f, + "if {left}({left_type}) {operator} {right}({right_type}) {{ JUMP +1 }}" + ) } } diff --git a/dust-lang/src/instruction/equal_byte.rs b/dust-lang/src/instruction/equal_byte.rs deleted file mode 100644 index 3be0ddb..0000000 --- a/dust-lang/src/instruction/equal_byte.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct EqualByte { - pub comparator: bool, - pub left: Operand, - pub right: Operand, -} - -impl From for EqualByte { - fn from(instruction: Instruction) -> Self { - let comparator = instruction.d_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - EqualByte { - comparator, - left, - right, - } - } -} - -impl From for Instruction { - fn from(equal_byte: EqualByte) -> Self { - let operation = Operation::EQUAL_BYTE; - let (b_field, b_is_constant) = equal_byte.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = equal_byte.right.as_index_and_constant_flag(); - let d_field = equal_byte.comparator; - - InstructionBuilder { - operation, - b_field, - c_field, - d_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for EqualByte { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let EqualByte { - comparator, - left, - right, - } = self; - let operator = if *comparator { "==" } else { "≠" }; - - write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") - } -} diff --git a/dust-lang/src/instruction/equal_char.rs b/dust-lang/src/instruction/equal_char.rs deleted file mode 100644 index 4e60a77..0000000 --- a/dust-lang/src/instruction/equal_char.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct EqualChar { - pub comparator: bool, - pub left: Operand, - pub right: Operand, -} - -impl From for EqualChar { - fn from(instruction: Instruction) -> Self { - let comparator = instruction.d_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - EqualChar { - comparator, - left, - right, - } - } -} - -impl From for Instruction { - fn from(equal_char: EqualChar) -> Self { - let operation = Operation::EQUAL_CHAR; - let (b_field, b_is_constant) = equal_char.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = equal_char.right.as_index_and_constant_flag(); - let d_field = equal_char.comparator; - - InstructionBuilder { - operation, - b_field, - c_field, - d_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for EqualChar { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let EqualChar { - comparator, - left, - right, - } = self; - let operator = if *comparator { "==" } else { "≠" }; - - write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") - } -} diff --git a/dust-lang/src/instruction/equal_char_str.rs b/dust-lang/src/instruction/equal_char_str.rs deleted file mode 100644 index eb15ae4..0000000 --- a/dust-lang/src/instruction/equal_char_str.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct EqualCharStr { - pub comparator: bool, - pub left: Operand, - pub right: Operand, -} - -impl From for EqualCharStr { - fn from(instruction: Instruction) -> Self { - let comparator = instruction.d_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - EqualCharStr { - comparator, - left, - right, - } - } -} - -impl From for Instruction { - fn from(equal_char_str: EqualCharStr) -> Self { - let operation = Operation::EQUAL_CHAR_STR; - let (b_field, b_is_constant) = equal_char_str.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = equal_char_str.right.as_index_and_constant_flag(); - let d_field = equal_char_str.comparator; - - InstructionBuilder { - operation, - b_field, - c_field, - d_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for EqualCharStr { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let EqualCharStr { - comparator, - left, - right, - } = self; - let operator = if *comparator { "==" } else { "≠" }; - - write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") - } -} diff --git a/dust-lang/src/instruction/equal_float.rs b/dust-lang/src/instruction/equal_float.rs deleted file mode 100644 index 6149c2c..0000000 --- a/dust-lang/src/instruction/equal_float.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct EqualFloat { - pub comparator: bool, - pub left: Operand, - pub right: Operand, -} - -impl From for EqualFloat { - fn from(instruction: Instruction) -> Self { - let comparator = instruction.d_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - EqualFloat { - comparator, - left, - right, - } - } -} - -impl From for Instruction { - fn from(equal_float: EqualFloat) -> Self { - let operation = Operation::EQUAL_FLOAT; - let (b_field, b_is_constant) = equal_float.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = equal_float.right.as_index_and_constant_flag(); - let d_field = equal_float.comparator; - - InstructionBuilder { - operation, - b_field, - c_field, - d_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for EqualFloat { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let EqualFloat { - comparator, - left, - right, - } = self; - let operator = if *comparator { "==" } else { "≠" }; - - write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") - } -} diff --git a/dust-lang/src/instruction/equal_int.rs b/dust-lang/src/instruction/equal_int.rs deleted file mode 100644 index cebc38d..0000000 --- a/dust-lang/src/instruction/equal_int.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct EqualInt { - pub comparator: bool, - pub left: Operand, - pub right: Operand, -} - -impl From for EqualInt { - fn from(instruction: Instruction) -> Self { - let comparator = instruction.d_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - EqualInt { - comparator, - left, - right, - } - } -} - -impl From for Instruction { - fn from(equal_int: EqualInt) -> Self { - let operation = Operation::EQUAL_INT; - let (b_field, b_is_constant) = equal_int.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = equal_int.right.as_index_and_constant_flag(); - let d_field = equal_int.comparator; - - InstructionBuilder { - operation, - b_field, - c_field, - d_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for EqualInt { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let EqualInt { - comparator, - left, - right, - } = self; - let operator = if *comparator { "==" } else { "≠" }; - - write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") - } -} diff --git a/dust-lang/src/instruction/equal_str.rs b/dust-lang/src/instruction/equal_str.rs deleted file mode 100644 index f4cc0c4..0000000 --- a/dust-lang/src/instruction/equal_str.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct EqualStr { - pub comparator: bool, - pub left: Operand, - pub right: Operand, -} - -impl From for EqualStr { - fn from(instruction: Instruction) -> Self { - let comparator = instruction.d_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - EqualStr { - comparator, - left, - right, - } - } -} - -impl From for Instruction { - fn from(equal_str: EqualStr) -> Self { - let operation = Operation::EQUAL_STR; - let (b_field, b_is_constant) = equal_str.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = equal_str.right.as_index_and_constant_flag(); - let d_field = equal_str.comparator; - - InstructionBuilder { - operation, - b_field, - c_field, - d_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for EqualStr { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let EqualStr { - comparator, - left, - right, - } = self; - let operator = if *comparator { "==" } else { "≠" }; - - write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") - } -} diff --git a/dust-lang/src/instruction/equal_str_char.rs b/dust-lang/src/instruction/equal_str_char.rs deleted file mode 100644 index ccd0dd4..0000000 --- a/dust-lang/src/instruction/equal_str_char.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct EqualStrChar { - pub comparator: bool, - pub left: Operand, - pub right: Operand, -} - -impl From for EqualStrChar { - fn from(instruction: Instruction) -> Self { - let comparator = instruction.d_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - EqualStrChar { - comparator, - left, - right, - } - } -} - -impl From for Instruction { - fn from(equal_str_char: EqualStrChar) -> Self { - let operation = Operation::EQUAL_STR_CHAR; - let (b_field, b_is_constant) = equal_str_char.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = equal_str_char.right.as_index_and_constant_flag(); - let d_field = equal_str_char.comparator; - - InstructionBuilder { - operation, - b_field, - c_field, - d_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for EqualStrChar { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let EqualStrChar { - comparator, - left, - right, - } = self; - let operator = if *comparator { "==" } else { "≠" }; - - write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") - } -} diff --git a/dust-lang/src/instruction/jump.rs b/dust-lang/src/instruction/jump.rs index cf55b7c..23b82ce 100644 --- a/dust-lang/src/instruction/jump.rs +++ b/dust-lang/src/instruction/jump.rs @@ -1,3 +1,5 @@ +use std::fmt::{self, Display, Formatter}; + use crate::{Instruction, Operation}; use super::InstructionBuilder; @@ -31,3 +33,15 @@ impl From for Instruction { .build() } } + +impl Display for Jump { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let Jump { + offset, + is_positive, + } = self; + let sign = if *is_positive { "+" } else { "-" }; + + write!(f, "JUMP {sign}{offset}") + } +} diff --git a/dust-lang/src/instruction/less.rs b/dust-lang/src/instruction/less.rs new file mode 100644 index 0000000..67fa53b --- /dev/null +++ b/dust-lang/src/instruction/less.rs @@ -0,0 +1,70 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; + +pub struct Less { + pub comparator: bool, + pub left: Operand, + pub left_type: TypeCode, + pub right: Operand, + pub right_type: TypeCode, +} + +impl From for Less { + fn from(instruction: Instruction) -> Self { + let comparator = instruction.d_field(); + let (left, right) = instruction.b_and_c_as_operands(); + let left_type = instruction.b_type(); + let right_type = instruction.c_type(); + + Less { + comparator, + left, + left_type, + right, + right_type, + } + } +} + +impl From for Instruction { + fn from(less: Less) -> Self { + let operation = Operation::LESS; + let (b_field, b_is_constant) = less.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = less.right.as_index_and_constant_flag(); + let d_field = less.comparator; + let b_type = less.left_type; + let c_type = less.right_type; + + InstructionBuilder { + operation, + b_field, + c_field, + d_field, + b_is_constant, + c_is_constant, + b_type, + c_type, + ..Default::default() + } + .build() + } +} + +impl Display for Less { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let Less { + comparator, + left, + left_type, + right, + right_type, + } = self; + let operator = if *comparator { "<" } else { "≥" }; + + write!( + f, + "if {left_type}({left}) {operator} {right_type}({right}) {{ JUMP +1 }}" + ) + } +} diff --git a/dust-lang/src/instruction/less_byte.rs b/dust-lang/src/instruction/less_byte.rs deleted file mode 100644 index 7b657f0..0000000 --- a/dust-lang/src/instruction/less_byte.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct LessByte { - pub comparator: bool, - pub left: Operand, - pub right: Operand, -} - -impl From for LessByte { - fn from(instruction: Instruction) -> Self { - let comparator = instruction.d_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - LessByte { - comparator, - left, - right, - } - } -} - -impl From for Instruction { - fn from(less_byte: LessByte) -> Self { - let operation = Operation::LESS_BYTE; - let (b_field, b_is_constant) = less_byte.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = less_byte.right.as_index_and_constant_flag(); - let d_field = less_byte.comparator; - - InstructionBuilder { - operation, - b_field, - c_field, - d_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for LessByte { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let LessByte { - comparator, - left, - right, - } = self; - let operator = if *comparator { "<" } else { "≥" }; - - write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") - } -} diff --git a/dust-lang/src/instruction/less_char.rs b/dust-lang/src/instruction/less_char.rs deleted file mode 100644 index 0f26ccf..0000000 --- a/dust-lang/src/instruction/less_char.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct LessChar { - pub comparator: bool, - pub left: Operand, - pub right: Operand, -} - -impl From for LessChar { - fn from(instruction: Instruction) -> Self { - let comparator = instruction.d_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - LessChar { - comparator, - left, - right, - } - } -} - -impl From for Instruction { - fn from(less_char: LessChar) -> Self { - let operation = Operation::LESS_CHAR; - let (b_field, b_is_constant) = less_char.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = less_char.right.as_index_and_constant_flag(); - let d_field = less_char.comparator; - - InstructionBuilder { - operation, - b_field, - c_field, - d_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for LessChar { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let LessChar { - comparator, - left, - right, - } = self; - let operator = if *comparator { "<" } else { "≥" }; - - write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") - } -} diff --git a/dust-lang/src/instruction/less_equal_byte.rs b/dust-lang/src/instruction/less_equal.rs similarity index 54% rename from dust-lang/src/instruction/less_equal_byte.rs rename to dust-lang/src/instruction/less_equal.rs index 08b1cac..fc3238f 100644 --- a/dust-lang/src/instruction/less_equal_byte.rs +++ b/dust-lang/src/instruction/less_equal.rs @@ -1,32 +1,40 @@ use std::fmt::{self, Display, Formatter}; -use super::{Instruction, InstructionBuilder, Operand, Operation}; +use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; -pub struct LessEqualByte { +pub struct LessEqual { pub comparator: bool, pub left: Operand, + pub left_type: TypeCode, pub right: Operand, + pub right_type: TypeCode, } -impl From for LessEqualByte { +impl From for LessEqual { fn from(instruction: Instruction) -> Self { let comparator = instruction.d_field(); let (left, right) = instruction.b_and_c_as_operands(); + let left_type = instruction.b_type(); + let right_type = instruction.c_type(); - LessEqualByte { + LessEqual { comparator, left, + left_type, right, + right_type, } } } -impl From for Instruction { - fn from(less_equal_byte: LessEqualByte) -> Self { - let operation = Operation::LESS_EQUAL_BYTE; +impl From for Instruction { + fn from(less_equal_byte: LessEqual) -> Self { + let operation = Operation::LESS_EQUAL; let (b_field, b_is_constant) = less_equal_byte.left.as_index_and_constant_flag(); let (c_field, c_is_constant) = less_equal_byte.right.as_index_and_constant_flag(); let d_field = less_equal_byte.comparator; + let b_type = less_equal_byte.left_type; + let c_type = less_equal_byte.right_type; InstructionBuilder { operation, @@ -35,21 +43,28 @@ impl From for Instruction { d_field, b_is_constant, c_is_constant, + b_type, + c_type, ..Default::default() } .build() } } -impl Display for LessEqualByte { +impl Display for LessEqual { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let LessEqualByte { + let LessEqual { comparator, left, + left_type, right, + right_type, } = self; let operator = if *comparator { "≤" } else { ">" }; - write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") + write!( + f, + "if {left_type}({left}) {operator} {right_type}({right}) {{ JUMP +1 }}" + ) } } diff --git a/dust-lang/src/instruction/less_equal_char.rs b/dust-lang/src/instruction/less_equal_char.rs deleted file mode 100644 index bd0777e..0000000 --- a/dust-lang/src/instruction/less_equal_char.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct LessEqualChar { - pub comparator: bool, - pub left: Operand, - pub right: Operand, -} - -impl From for LessEqualChar { - fn from(instruction: Instruction) -> Self { - let comparator = instruction.d_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - LessEqualChar { - comparator, - left, - right, - } - } -} - -impl From for Instruction { - fn from(less_equal_char: LessEqualChar) -> Self { - let operation = Operation::LESS_EQUAL_CHAR; - let (b_field, b_is_constant) = less_equal_char.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = less_equal_char.right.as_index_and_constant_flag(); - let d_field = less_equal_char.comparator; - - InstructionBuilder { - operation, - b_field, - c_field, - d_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for LessEqualChar { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let LessEqualChar { - comparator, - left, - right, - } = self; - let operator = if *comparator { "≤" } else { ">" }; - - write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") - } -} diff --git a/dust-lang/src/instruction/less_equal_float.rs b/dust-lang/src/instruction/less_equal_float.rs deleted file mode 100644 index f552f0b..0000000 --- a/dust-lang/src/instruction/less_equal_float.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct LessEqualFloat { - pub comparator: bool, - pub left: Operand, - pub right: Operand, -} - -impl From for LessEqualFloat { - fn from(instruction: Instruction) -> Self { - let comparator = instruction.d_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - LessEqualFloat { - comparator, - left, - right, - } - } -} - -impl From for Instruction { - fn from(less_equal_float: LessEqualFloat) -> Self { - let operation = Operation::LESS_EQUAL_FLOAT; - let (b_field, b_is_constant) = less_equal_float.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = less_equal_float.right.as_index_and_constant_flag(); - let d_field = less_equal_float.comparator; - - InstructionBuilder { - operation, - b_field, - c_field, - d_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for LessEqualFloat { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let LessEqualFloat { - comparator, - left, - right, - } = self; - let operator = if *comparator { "≤" } else { ">" }; - - write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") - } -} diff --git a/dust-lang/src/instruction/less_equal_int.rs b/dust-lang/src/instruction/less_equal_int.rs deleted file mode 100644 index ede40c8..0000000 --- a/dust-lang/src/instruction/less_equal_int.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct LessEqualInt { - pub comparator: bool, - pub left: Operand, - pub right: Operand, -} - -impl From for LessEqualInt { - fn from(instruction: Instruction) -> Self { - let comparator = instruction.d_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - LessEqualInt { - comparator, - left, - right, - } - } -} - -impl From for Instruction { - fn from(less_equal_int: LessEqualInt) -> Self { - let operation = Operation::LESS_EQUAL_INT; - let (b_field, b_is_constant) = less_equal_int.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = less_equal_int.right.as_index_and_constant_flag(); - let d_field = less_equal_int.comparator; - - InstructionBuilder { - operation, - b_field, - c_field, - d_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for LessEqualInt { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let LessEqualInt { - comparator, - left, - right, - } = self; - let operator = if *comparator { "≤" } else { ">" }; - - write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") - } -} diff --git a/dust-lang/src/instruction/less_equal_str.rs b/dust-lang/src/instruction/less_equal_str.rs deleted file mode 100644 index 4b8ae2b..0000000 --- a/dust-lang/src/instruction/less_equal_str.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct LessEqualStr { - pub comparator: bool, - pub left: Operand, - pub right: Operand, -} - -impl From for LessEqualStr { - fn from(instruction: Instruction) -> Self { - let comparator = instruction.d_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - LessEqualStr { - comparator, - left, - right, - } - } -} - -impl From for Instruction { - fn from(less_equal_str: LessEqualStr) -> Self { - let operation = Operation::LESS_EQUAL_STR; - let (b_field, b_is_constant) = less_equal_str.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = less_equal_str.right.as_index_and_constant_flag(); - let d_field = less_equal_str.comparator; - - InstructionBuilder { - operation, - b_field, - c_field, - d_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for LessEqualStr { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let LessEqualStr { - comparator, - left, - right, - } = self; - let operator = if *comparator { "≤" } else { ">" }; - - write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") - } -} diff --git a/dust-lang/src/instruction/less_float.rs b/dust-lang/src/instruction/less_float.rs deleted file mode 100644 index a5722fa..0000000 --- a/dust-lang/src/instruction/less_float.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct LessFloat { - pub comparator: bool, - pub left: Operand, - pub right: Operand, -} - -impl From for LessFloat { - fn from(instruction: Instruction) -> Self { - let comparator = instruction.d_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - LessFloat { - comparator, - left, - right, - } - } -} - -impl From for Instruction { - fn from(less_float: LessFloat) -> Self { - let operation = Operation::LESS_FLOAT; - let (b_field, b_is_constant) = less_float.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = less_float.right.as_index_and_constant_flag(); - let d_field = less_float.comparator; - - InstructionBuilder { - operation, - b_field, - c_field, - d_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for LessFloat { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let LessFloat { - comparator, - left, - right, - } = self; - let operator = if *comparator { "<" } else { "≥" }; - - write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") - } -} diff --git a/dust-lang/src/instruction/less_int.rs b/dust-lang/src/instruction/less_int.rs deleted file mode 100644 index c693d94..0000000 --- a/dust-lang/src/instruction/less_int.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct LessInt { - pub comparator: bool, - pub left: Operand, - pub right: Operand, -} - -impl From for LessInt { - fn from(instruction: Instruction) -> Self { - let comparator = instruction.d_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - LessInt { - comparator, - left, - right, - } - } -} - -impl From for Instruction { - fn from(less_int: LessInt) -> Self { - let operation = Operation::LESS_INT; - let (b_field, b_is_constant) = less_int.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = less_int.right.as_index_and_constant_flag(); - let d_field = less_int.comparator; - - InstructionBuilder { - operation, - b_field, - c_field, - d_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for LessInt { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let LessInt { - comparator, - left, - right, - } = self; - let operator = if *comparator { "<" } else { "≥" }; - - write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") - } -} diff --git a/dust-lang/src/instruction/less_str.rs b/dust-lang/src/instruction/less_str.rs deleted file mode 100644 index c26b494..0000000 --- a/dust-lang/src/instruction/less_str.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct LessStr { - pub comparator: bool, - pub left: Operand, - pub right: Operand, -} - -impl From for LessStr { - fn from(instruction: Instruction) -> Self { - let comparator = instruction.d_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - LessStr { - comparator, - left, - right, - } - } -} - -impl From for Instruction { - fn from(less_str: LessStr) -> Self { - let operation = Operation::LESS_STR; - let (b_field, b_is_constant) = less_str.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = less_str.right.as_index_and_constant_flag(); - let d_field = less_str.comparator; - - InstructionBuilder { - operation, - b_field, - c_field, - d_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for LessStr { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let LessStr { - comparator, - left, - right, - } = self; - let operator = if *comparator { "<" } else { "≥" }; - - write!(f, "if {left} {operator} {right} {{ JUMP +1 }}") - } -} diff --git a/dust-lang/src/instruction/mod.rs b/dust-lang/src/instruction/mod.rs index e99bc21..6b55724 100644 --- a/dust-lang/src/instruction/mod.rs +++ b/dust-lang/src/instruction/mod.rs @@ -7,11 +7,11 @@ //! Bits | Description //! ----- | ----------- //! 0-6 | Operation -//! 7 | Unused -//! 8 | Flag indicating if the B field is a constant -//! 9 | Flag indicating if the C field is a constant -//! 10 | D field (boolean) -//! 11-15 | Unused +//! 7 | Flag indicating if the B field is a constant +//! 8 | Flag indicating if the C field is a constant +//! 9 | D field (boolean) +//! 10-12 | B field type +//! 13-15 | C field type //! 16-31 | A field (unsigned 16-bit integer) //! 32-47 | B field (unsigned 16-bit integer) //! 48-63 | C field (unsigned 16-bit integer) @@ -93,122 +93,64 @@ //! //! assert!(is_add_assign); //! ``` -mod add_byte; -mod add_char; -mod add_char_str; -mod add_float; -mod add_int; -mod add_str; -mod add_str_char; +mod add; mod call; mod call_native; mod close; -mod divide_byte; -mod divide_float; -mod divide_int; -mod equal_bool; -mod equal_byte; -mod equal_char; -mod equal_char_str; -mod equal_float; -mod equal_int; -mod equal_str; -mod equal_str_char; +mod divide; +mod equal; mod get_local; mod jump; -mod less_byte; -mod less_char; -mod less_equal_byte; -mod less_equal_char; -mod less_equal_float; -mod less_equal_int; -mod less_equal_str; -mod less_float; -mod less_int; -mod less_str; +mod less; +mod less_equal; mod load_boolean; mod load_constant; mod load_function; mod load_list; mod load_self; -mod modulo_byte; -mod modulo_float; -mod modulo_int; -mod multiply_byte; -mod multiply_float; -mod multiply_int; -mod negate_float; -mod negate_int; +mod modulo; +mod multiply; +mod negate; mod not; mod operation; mod point; mod r#return; mod set_local; -mod subtract_byte; -mod subtract_float; -mod subtract_int; +mod subtract; mod test; mod test_set; +mod type_code; -pub use add_byte::AddByte; -pub use add_char::AddChar; -pub use add_char_str::AddCharStr; -pub use add_float::AddFloat; -pub use add_int::AddInt; -pub use add_str::AddStr; -pub use add_str_char::AddStrChar; +pub use add::Add; pub use call::Call; pub use call_native::CallNative; pub use close::Close; -pub use divide_byte::DivideByte; -pub use divide_float::DivideFloat; -pub use divide_int::DivideInt; -pub use equal_bool::EqualBool; -pub use equal_byte::EqualByte; -pub use equal_char::EqualChar; -pub use equal_char_str::EqualCharStr; -pub use equal_float::EqualFloat; -pub use equal_int::EqualInt; -pub use equal_str::EqualStr; -pub use equal_str_char::EqualStrChar; +pub use divide::Divide; +pub use equal::Equal; pub use get_local::GetLocal; pub use jump::Jump; -pub use less_byte::LessByte; -pub use less_char::LessChar; -pub use less_equal_byte::LessEqualByte; -pub use less_equal_char::LessEqualChar; -pub use less_equal_float::LessEqualFloat; -pub use less_equal_int::LessEqualInt; -pub use less_equal_str::LessEqualStr; -pub use less_float::LessFloat; -pub use less_int::LessInt; -pub use less_str::LessStr; +pub use less::Less; +pub use less_equal::LessEqual; pub use load_boolean::LoadBoolean; pub use load_constant::LoadConstant; pub use load_function::LoadFunction; pub use load_list::LoadList; pub use load_self::LoadSelf; -pub use modulo_byte::ModuloByte; -pub use modulo_float::ModuloFloat; -pub use modulo_int::ModuloInt; -pub use multiply_byte::MultiplyByte; -pub use multiply_float::MultiplyFloat; -pub use multiply_int::MultiplyInt; -pub use negate_float::NegateFloat; -pub use negate_int::NegateInt; +pub use modulo::Modulo; +pub use multiply::Multiply; +pub use negate::Negate; pub use not::Not; pub use operation::Operation; pub use point::Point; pub use r#return::Return; pub use set_local::SetLocal; -pub use subtract_byte::SubtractByte; -pub use subtract_float::SubtractFloat; -pub use subtract_int::SubtractInt; +pub use subtract::Subtract; pub use test::Test; pub use test_set::TestSet; use serde::{Deserialize, Serialize}; use std::fmt::{self, Debug, Display, Formatter}; +pub use type_code::TypeCode; use crate::NativeFunction; @@ -220,17 +162,21 @@ pub struct InstructionBuilder { pub d_field: bool, pub b_is_constant: bool, pub c_is_constant: bool, + pub b_type: TypeCode, + pub c_type: TypeCode, } impl InstructionBuilder { pub fn build(self) -> Instruction { - let bits = self.operation.0 as u64 - | ((self.b_is_constant as u64) << 7) - | ((self.c_is_constant as u64) << 8) - | ((self.d_field as u64) << 9) - | ((self.a_field as u64) << 31) - | ((self.b_field as u64) << 47) - | ((self.c_field as u64) << 63); + let bits = ((self.operation.0 as u64) << 57) + | ((self.b_is_constant as u64) << 56) + | ((self.c_is_constant as u64) << 55) + | ((self.d_field as u64) << 54) + | ((self.b_type.0 as u64) << 51) + | ((self.c_type.0 as u64) << 48) + | ((self.a_field as u64) << 32) + | ((self.b_field as u64) << 16) + | (self.c_field as u64); Instruction(bits) } @@ -246,11 +192,13 @@ impl Default for InstructionBuilder { d_field: false, b_is_constant: false, c_is_constant: false, + b_type: TypeCode::BOOLEAN, + c_type: TypeCode::BOOLEAN, } } } -/// An operation and its arguments for the Dust virtual machine. +/// An instruction for the Dust virtual machine. /// /// See the [module-level documentation](index.html) for more information. #[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] @@ -258,33 +206,45 @@ pub struct Instruction(u64); impl Instruction { pub fn operation(&self) -> Operation { - let first_byte = (self.0 & 0b0111_1111) as u8; + let first_7_bits = (self.0 >> 57) as u8; - Operation(first_byte) + Operation(first_7_bits) } pub fn b_is_constant(&self) -> bool { - (self.0 >> 8) & 1 == 0 + (self.0 >> 56) & 1 != 0 } pub fn c_is_constant(&self) -> bool { - (self.0 >> 9) & 1 == 0 + (self.0 >> 55) & 1 != 0 } pub fn d_field(&self) -> bool { - (self.0 >> 10) & 1 == 0 + (self.0 >> 54) & 1 == 0 + } + + pub fn b_type(&self) -> TypeCode { + let byte = ((self.0 >> 51) & 0b111) as u8; + + TypeCode(byte) + } + + pub fn c_type(&self) -> TypeCode { + let byte = ((self.0 >> 48) & 0b111) as u8; + + TypeCode(byte) } pub fn a_field(&self) -> u16 { - ((self.0 >> 31) & 0xFFFF) as u16 + ((self.0 >> 32) & 0xFFFF) as u16 } pub fn b_field(&self) -> u16 { - ((self.0 >> 47) & 0xFFFF) as u16 + ((self.0 >> 16) & 0xFFFF) as u16 } pub fn c_field(&self) -> u16 { - (self.0 >> 48) as u16 + (self.0 & 0xFFFF) as u16 } pub fn set_a_field(&mut self, bits: u16) { @@ -360,313 +320,139 @@ impl Instruction { }) } - pub fn add_int(destination: u16, left: Operand, right: Operand) -> Instruction { - Instruction::from(AddInt { + pub fn add( + destination: u16, + left: Operand, + left_type: TypeCode, + right: Operand, + right_type: TypeCode, + ) -> Instruction { + Instruction::from(Add { destination, left, + left_type, right, + right_type, }) } - pub fn add_float(destination: u16, left: Operand, right: Operand) -> Instruction { - Instruction::from(AddFloat { + pub fn subtract( + destination: u16, + left: Operand, + left_type: TypeCode, + right: Operand, + right_type: TypeCode, + ) -> Instruction { + Instruction::from(Subtract { destination, left, + left_type, right, + right_type, }) } - pub fn add_byte(destination: u16, left: Operand, right: Operand) -> Instruction { - Instruction::from(AddByte { + pub fn multiply( + destination: u16, + left: Operand, + left_type: TypeCode, + right: Operand, + right_type: TypeCode, + ) -> Instruction { + Instruction::from(Multiply { destination, left, + left_type, right, + right_type, }) } - pub fn add_str(destination: u16, left: Operand, right: Operand) -> Instruction { - Instruction::from(AddStr { + pub fn divide( + destination: u16, + left: Operand, + left_type: TypeCode, + right: Operand, + right_type: TypeCode, + ) -> Instruction { + Instruction::from(Divide { destination, left, + left_type, right, + right_type, }) } - pub fn add_char(destination: u16, left: Operand, right: Operand) -> Instruction { - Instruction::from(AddChar { + pub fn modulo( + destination: u16, + left: Operand, + left_type: TypeCode, + right: Operand, + right_type: TypeCode, + ) -> Instruction { + Instruction::from(Modulo { destination, left, + left_type, right, + right_type, }) } - pub fn add_str_char(destination: u16, left: Operand, right: Operand) -> Instruction { - Instruction::from(AddStrChar { - destination, - left, - right, - }) - } - - pub fn add_char_str(destination: u16, left: Operand, right: Operand) -> Instruction { - Instruction::from(AddCharStr { - destination, - left, - right, - }) - } - - pub fn subtract_int(destination: u16, left: Operand, right: Operand) -> Instruction { - Instruction::from(SubtractInt { - destination, - left, - right, - }) - } - - pub fn subtract_float(destination: u16, left: Operand, right: Operand) -> Instruction { - Instruction::from(SubtractFloat { - destination, - left, - right, - }) - } - - pub fn subtract_byte(destination: u16, left: Operand, right: Operand) -> Instruction { - Instruction::from(SubtractByte { - destination, - left, - right, - }) - } - - pub fn multiply_int(destination: u16, left: Operand, right: Operand) -> Instruction { - Instruction::from(MultiplyInt { - destination, - left, - right, - }) - } - - pub fn multiply_float(destination: u16, left: Operand, right: Operand) -> Instruction { - Instruction::from(MultiplyFloat { - destination, - left, - right, - }) - } - - pub fn multiply_byte(destination: u16, left: Operand, right: Operand) -> Instruction { - Instruction::from(MultiplyByte { - destination, - left, - right, - }) - } - - pub fn divide_int(destination: u16, left: Operand, right: Operand) -> Instruction { - Instruction::from(DivideInt { - destination, - left, - right, - }) - } - - pub fn divide_float(destination: u16, left: Operand, right: Operand) -> Instruction { - Instruction::from(DivideFloat { - destination, - left, - right, - }) - } - - pub fn divide_byte(destination: u16, left: Operand, right: Operand) -> Instruction { - Instruction::from(DivideByte { - destination, - left, - right, - }) - } - - pub fn modulo_int(destination: u16, left: Operand, right: Operand) -> Instruction { - Instruction::from(ModuloInt { - destination, - left, - right, - }) - } - - pub fn modulo_float(destination: u16, left: Operand, right: Operand) -> Instruction { - Instruction::from(ModuloFloat { - destination, - left, - right, - }) - } - - pub fn modulo_byte(destination: u16, left: Operand, right: Operand) -> Instruction { - Instruction::from(ModuloByte { - destination, - left, - right, - }) - } - - pub fn equal_int(comparator: bool, left: Operand, right: Operand) -> Instruction { - Instruction::from(EqualInt { + pub fn equal( + comparator: bool, + left: Operand, + left_type: TypeCode, + right: Operand, + right_type: TypeCode, + ) -> Instruction { + Instruction::from(Equal { comparator, left, + left_type, right, + right_type, }) } - pub fn equal_float(comparator: bool, left: Operand, right: Operand) -> Instruction { - Instruction::from(EqualFloat { + pub fn less( + comparator: bool, + left: Operand, + left_type: TypeCode, + right: Operand, + right_type: TypeCode, + ) -> Instruction { + Instruction::from(Less { comparator, left, + left_type, right, + right_type, }) } - pub fn equal_byte(comparator: bool, left: Operand, right: Operand) -> Instruction { - Instruction::from(EqualByte { + pub fn less_equal( + comparator: bool, + left: Operand, + left_type: TypeCode, + right: Operand, + right_type: TypeCode, + ) -> Instruction { + Instruction::from(LessEqual { comparator, left, + left_type, right, + right_type, }) } - pub fn equal_str(comparator: bool, left: Operand, right: Operand) -> Instruction { - Instruction::from(EqualStr { - comparator, - left, - right, - }) - } - - pub fn equal_char(comparator: bool, left: Operand, right: Operand) -> Instruction { - Instruction::from(EqualChar { - comparator, - left, - right, - }) - } - - pub fn equal_str_char(comparator: bool, left: Operand, right: Operand) -> Instruction { - Instruction::from(EqualStrChar { - comparator, - left, - right, - }) - } - - pub fn equal_char_str(comparator: bool, left: Operand, right: Operand) -> Instruction { - Instruction::from(EqualCharStr { - comparator, - left, - right, - }) - } - - pub fn equal_bool(comparator: bool, left: Operand, right: Operand) -> Instruction { - Instruction::from(EqualBool { - comparator, - left, - right, - }) - } - - pub fn less_int(comparator: bool, left: Operand, right: Operand) -> Instruction { - Instruction::from(LessInt { - comparator, - left, - right, - }) - } - - pub fn less_float(comparator: bool, left: Operand, right: Operand) -> Instruction { - Instruction::from(LessFloat { - comparator, - left, - right, - }) - } - - pub fn less_byte(comparator: bool, left: Operand, right: Operand) -> Instruction { - Instruction::from(LessByte { - comparator, - left, - right, - }) - } - - pub fn less_str(comparator: bool, left: Operand, right: Operand) -> Instruction { - Instruction::from(LessStr { - comparator, - left, - right, - }) - } - - pub fn less_char(comparator: bool, left: Operand, right: Operand) -> Instruction { - Instruction::from(LessChar { - comparator, - left, - right, - }) - } - - pub fn less_equal_int(comparator: bool, left: Operand, right: Operand) -> Instruction { - Instruction::from(LessEqualInt { - comparator, - left, - right, - }) - } - - pub fn less_equal_float(comparator: bool, left: Operand, right: Operand) -> Instruction { - Instruction::from(LessEqualFloat { - comparator, - left, - right, - }) - } - - pub fn less_equal_byte(comparator: bool, left: Operand, right: Operand) -> Instruction { - Instruction::from(LessEqualByte { - comparator, - left, - right, - }) - } - - pub fn less_equal_str(comparator: bool, left: Operand, right: Operand) -> Instruction { - Instruction::from(LessEqualStr { - comparator, - left, - right, - }) - } - - pub fn less_equal_char(comparator: bool, left: Operand, right: Operand) -> Instruction { - Instruction::from(LessEqualChar { - comparator, - left, - right, - }) - } - - pub fn negate_int(destination: u16, argument: Operand) -> Instruction { - Instruction::from(NegateInt { - destination, - argument, - }) - } - - pub fn negate_float(destination: u16, argument: Operand) -> Instruction { - Instruction::from(NegateFloat { + pub fn negate(destination: u16, argument: Operand, argument_type: TypeCode) -> Instruction { + Instruction::from(Negate { destination, argument, + argument_type, }) } @@ -747,45 +533,15 @@ impl Instruction { | Operation::LOAD_LIST | Operation::LOAD_SELF | Operation::GET_LOCAL - | Operation::ADD_INT - | Operation::ADD_FLOAT - | Operation::ADD_BYTE - | Operation::ADD_STR - | Operation::ADD_CHAR - | Operation::ADD_STR_CHAR - | Operation::ADD_CHAR_STR - | Operation::SUBTRACT_INT - | Operation::SUBTRACT_FLOAT - | Operation::SUBTRACT_BYTE - | Operation::MULTIPLY_INT - | Operation::MULTIPLY_FLOAT - | Operation::MULTIPLY_BYTE - | Operation::DIVIDE_INT - | Operation::DIVIDE_FLOAT - | Operation::DIVIDE_BYTE - | Operation::MODULO_INT - | Operation::MODULO_FLOAT - | Operation::MODULO_BYTE - | Operation::EQUAL_INT - | Operation::EQUAL_FLOAT - | Operation::EQUAL_BYTE - | Operation::EQUAL_STR - | Operation::EQUAL_CHAR - | Operation::EQUAL_STR_CHAR - | Operation::EQUAL_CHAR_STR - | Operation::EQUAL_BOOL - | Operation::LESS_INT - | Operation::LESS_FLOAT - | Operation::LESS_BYTE - | Operation::LESS_STR - | Operation::LESS_CHAR - | Operation::LESS_EQUAL_INT - | Operation::LESS_EQUAL_FLOAT - | Operation::LESS_EQUAL_BYTE - | Operation::LESS_EQUAL_STR - | Operation::LESS_EQUAL_CHAR - | Operation::NEGATE_INT - | Operation::NEGATE_FLOAT + | Operation::ADD + | Operation::SUBTRACT + | Operation::MULTIPLY + | Operation::DIVIDE + | Operation::MODULO + | Operation::EQUAL + | Operation::LESS + | Operation::LESS_EQUAL + | Operation::NEGATE | Operation::NOT | Operation::CALL => Some(Operand::Register(self.a_field())), Operation::CALL_NATIVE => { @@ -833,27 +589,12 @@ impl Instruction { | Operation::LOAD_LIST | Operation::LOAD_SELF | Operation::GET_LOCAL - | Operation::ADD_INT - | Operation::ADD_FLOAT - | Operation::ADD_BYTE - | Operation::ADD_STR - | Operation::ADD_CHAR - | Operation::ADD_STR_CHAR - | Operation::ADD_CHAR_STR - | Operation::SUBTRACT_INT - | Operation::SUBTRACT_FLOAT - | Operation::SUBTRACT_BYTE - | Operation::MULTIPLY_INT - | Operation::MULTIPLY_FLOAT - | Operation::MULTIPLY_BYTE - | Operation::DIVIDE_INT - | Operation::DIVIDE_FLOAT - | Operation::DIVIDE_BYTE - | Operation::MODULO_INT - | Operation::MODULO_FLOAT - | Operation::MODULO_BYTE - | Operation::NEGATE_INT - | Operation::NEGATE_FLOAT + | Operation::ADD + | Operation::SUBTRACT + | Operation::MULTIPLY + | Operation::DIVIDE + | Operation::MODULO + | Operation::NEGATE | Operation::NOT | Operation::CALL => true, Operation::CALL_NATIVE => { @@ -863,29 +604,14 @@ impl Instruction { } Operation::CLOSE | Operation::SET_LOCAL - | Operation::EQUAL_INT - | Operation::EQUAL_FLOAT - | Operation::EQUAL_BYTE - | Operation::EQUAL_STR - | Operation::EQUAL_CHAR - | Operation::EQUAL_STR_CHAR - | Operation::EQUAL_CHAR_STR - | Operation::EQUAL_BOOL - | Operation::LESS_INT - | Operation::LESS_FLOAT - | Operation::LESS_BYTE - | Operation::LESS_STR - | Operation::LESS_CHAR - | Operation::LESS_EQUAL_INT - | Operation::LESS_EQUAL_FLOAT - | Operation::LESS_EQUAL_BYTE - | Operation::LESS_EQUAL_STR - | Operation::LESS_EQUAL_CHAR + | Operation::EQUAL + | Operation::LESS + | Operation::LESS_EQUAL | Operation::TEST | Operation::TEST_SET | Operation::JUMP | Operation::RETURN => false, - _ => Operation::panic_from_unknown_code(self.operation().0), + _ => self.operation().panic_from_unknown_code(), } } @@ -902,9 +628,24 @@ impl Instruction { Operation::LOAD_SELF => LoadSelf::from(*self).to_string(), Operation::GET_LOCAL => GetLocal::from(*self).to_string(), Operation::SET_LOCAL => SetLocal::from(*self).to_string(), - Operation::ADD_INT => AddInt::from(*self).to_string(), + Operation::ADD => Add::from(*self).to_string(), + Operation::SUBTRACT => Subtract::from(*self).to_string(), + Operation::MULTIPLY => Multiply::from(*self).to_string(), + Operation::DIVIDE => Divide::from(*self).to_string(), + Operation::MODULO => Modulo::from(*self).to_string(), + Operation::NEGATE => Negate::from(*self).to_string(), + Operation::NOT => Not::from(*self).to_string(), + Operation::EQUAL => Equal::from(*self).to_string(), + Operation::LESS => Less::from(*self).to_string(), + Operation::LESS_EQUAL => LessEqual::from(*self).to_string(), + Operation::TEST => Test::from(*self).to_string(), + Operation::TEST_SET => TestSet::from(*self).to_string(), + Operation::CALL => Call::from(*self).to_string(), + Operation::CALL_NATIVE => CallNative::from(*self).to_string(), + Operation::JUMP => Jump::from(*self).to_string(), + Operation::RETURN => Return::from(*self).to_string(), - _ => Operation::panic_from_unknown_code(operation.0), + _ => operation.panic_from_unknown_code(), } } } @@ -966,50 +707,118 @@ mod tests { #[test] fn decode_operation() { - let instruction = Instruction::add_int(42, Operand::Constant(4), Operand::Register(2)); + let instruction = Instruction::add( + 42, + Operand::Constant(4), + TypeCode::STRING, + Operand::Register(2), + TypeCode::CHARACTER, + ); - assert_eq!(instruction.operation(), Operation::ADD_INT); + assert_eq!(instruction.operation(), Operation::ADD); } #[test] fn decode_a_field() { - let instruction = Instruction::add_int(42, Operand::Constant(4), Operand::Register(2)); + let instruction = Instruction::add( + 42, + Operand::Constant(4), + TypeCode::STRING, + Operand::Register(2), + TypeCode::CHARACTER, + ); assert_eq!(42, instruction.a_field()); } #[test] fn decode_b_field() { - let instruction = Instruction::add_int(42, Operand::Constant(4), Operand::Register(2)); + let instruction = Instruction::add( + 42, + Operand::Constant(4), + TypeCode::STRING, + Operand::Register(2), + TypeCode::CHARACTER, + ); assert_eq!(4, instruction.b_field()); } #[test] fn decode_c_field() { - let instruction = Instruction::add_int(42, Operand::Constant(4), Operand::Register(2)); + let instruction = Instruction::add( + 42, + Operand::Constant(4), + TypeCode::STRING, + Operand::Register(2), + TypeCode::CHARACTER, + ); assert_eq!(2, instruction.c_field()); } #[test] fn decode_d_field() { - let instruction = Instruction::call(42, 4, 2, true); + let instruction = Instruction::add( + 42, + Operand::Constant(4), + TypeCode::STRING, + Operand::Register(2), + TypeCode::CHARACTER, + ); assert!(instruction.d_field()); } #[test] fn decode_b_is_constant() { - let instruction = Instruction::add_int(42, Operand::Constant(4), Operand::Register(2)); + let instruction = Instruction::add( + 42, + Operand::Constant(4), + TypeCode::STRING, + Operand::Register(2), + TypeCode::CHARACTER, + ); assert!(instruction.b_is_constant()); } #[test] fn decode_c_is_constant() { - let instruction = Instruction::add_int(42, Operand::Register(2), Operand::Constant(4)); + let instruction = Instruction::add( + 42, + Operand::Register(2), + TypeCode::STRING, + Operand::Constant(4), + TypeCode::CHARACTER, + ); assert!(instruction.c_is_constant()); } + + #[test] + fn decode_b_type() { + let instruction = Instruction::add( + 42, + Operand::Constant(4), + TypeCode::STRING, + Operand::Register(2), + TypeCode::CHARACTER, + ); + + assert_eq!(TypeCode::STRING, instruction.b_type()); + } + + #[test] + fn decode_c_type() { + let instruction = Instruction::add( + 42, + Operand::Constant(4), + TypeCode::STRING, + Operand::Register(2), + TypeCode::CHARACTER, + ); + + assert_eq!(TypeCode::CHARACTER, instruction.c_type()); + } } diff --git a/dust-lang/src/instruction/modulo.rs b/dust-lang/src/instruction/modulo.rs new file mode 100644 index 0000000..64d3548 --- /dev/null +++ b/dust-lang/src/instruction/modulo.rs @@ -0,0 +1,69 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; + +pub struct Modulo { + pub destination: u16, + pub left: Operand, + pub left_type: TypeCode, + pub right: Operand, + pub right_type: TypeCode, +} + +impl From for Modulo { + fn from(instruction: Instruction) -> Self { + let destination = instruction.a_field(); + let (left, right) = instruction.b_and_c_as_operands(); + let left_type = instruction.b_type(); + let right_type = instruction.c_type(); + + Modulo { + destination, + left, + left_type, + right, + right_type, + } + } +} + +impl From for Instruction { + fn from(modulo: Modulo) -> Self { + let operation = Operation::MODULO; + let a_field = modulo.destination; + let (b_field, b_is_constant) = modulo.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = modulo.right.as_index_and_constant_flag(); + let b_type = modulo.left_type; + let c_type = modulo.right_type; + + InstructionBuilder { + operation, + a_field, + b_field, + c_field, + b_is_constant, + c_is_constant, + b_type, + c_type, + ..Default::default() + } + .build() + } +} + +impl Display for Modulo { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let Modulo { + destination, + left, + left_type, + right, + right_type, + } = self; + + write!( + f, + "R{destination} = {left_type}({left}) % {right_type}({right})", + ) + } +} diff --git a/dust-lang/src/instruction/modulo_byte.rs b/dust-lang/src/instruction/modulo_byte.rs deleted file mode 100644 index 9e969d3..0000000 --- a/dust-lang/src/instruction/modulo_byte.rs +++ /dev/null @@ -1,54 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct ModuloByte { - pub destination: u16, - pub left: Operand, - pub right: Operand, -} - -impl From for ModuloByte { - fn from(instruction: Instruction) -> Self { - let destination = instruction.a_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - ModuloByte { - destination, - left, - right, - } - } -} - -impl From for Instruction { - fn from(modulo_byte: ModuloByte) -> Self { - let operation = Operation::MODULO_BYTE; - let a_field = modulo_byte.destination; - let (b_field, b_is_constant) = modulo_byte.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = modulo_byte.right.as_index_and_constant_flag(); - - InstructionBuilder { - operation, - a_field, - b_field, - c_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for ModuloByte { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let ModuloByte { - destination, - left, - right, - } = self; - - write!(f, "R{} = {} % {}", destination, left, right) - } -} diff --git a/dust-lang/src/instruction/modulo_float.rs b/dust-lang/src/instruction/modulo_float.rs deleted file mode 100644 index 7b33022..0000000 --- a/dust-lang/src/instruction/modulo_float.rs +++ /dev/null @@ -1,54 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct ModuloFloat { - pub destination: u16, - pub left: Operand, - pub right: Operand, -} - -impl From for ModuloFloat { - fn from(instruction: Instruction) -> Self { - let destination = instruction.a_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - ModuloFloat { - destination, - left, - right, - } - } -} - -impl From for Instruction { - fn from(modulo_float: ModuloFloat) -> Self { - let operation = Operation::MODULO_FLOAT; - let a_field = modulo_float.destination; - let (b_field, b_is_constant) = modulo_float.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = modulo_float.right.as_index_and_constant_flag(); - - InstructionBuilder { - operation, - a_field, - b_field, - c_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for ModuloFloat { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let ModuloFloat { - destination, - left, - right, - } = self; - - write!(f, "R{} = {} % {}", destination, left, right) - } -} diff --git a/dust-lang/src/instruction/modulo_int.rs b/dust-lang/src/instruction/modulo_int.rs deleted file mode 100644 index e913893..0000000 --- a/dust-lang/src/instruction/modulo_int.rs +++ /dev/null @@ -1,54 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct ModuloInt { - pub destination: u16, - pub left: Operand, - pub right: Operand, -} - -impl From for ModuloInt { - fn from(instruction: Instruction) -> Self { - let destination = instruction.a_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - ModuloInt { - destination, - left, - right, - } - } -} - -impl From for Instruction { - fn from(modulo_int: ModuloInt) -> Self { - let operation = Operation::MODULO_INT; - let a_field = modulo_int.destination; - let (b_field, b_is_constant) = modulo_int.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = modulo_int.right.as_index_and_constant_flag(); - - InstructionBuilder { - operation, - a_field, - b_field, - c_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for ModuloInt { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let ModuloInt { - destination, - left, - right, - } = self; - - write!(f, "R{} = {} % {}", destination, left, right) - } -} diff --git a/dust-lang/src/instruction/multiply.rs b/dust-lang/src/instruction/multiply.rs new file mode 100644 index 0000000..62fa34e --- /dev/null +++ b/dust-lang/src/instruction/multiply.rs @@ -0,0 +1,69 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; + +pub struct Multiply { + pub destination: u16, + pub left: Operand, + pub left_type: TypeCode, + pub right: Operand, + pub right_type: TypeCode, +} + +impl From for Multiply { + fn from(instruction: Instruction) -> Self { + let destination = instruction.a_field(); + let (left, right) = instruction.b_and_c_as_operands(); + let left_type = instruction.b_type(); + let right_type = instruction.c_type(); + + Multiply { + destination, + left, + left_type, + right, + right_type, + } + } +} + +impl From for Instruction { + fn from(multiply: Multiply) -> Self { + let operation = Operation::MULTIPLY; + let a_field = multiply.destination; + let (b_field, b_is_constant) = multiply.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = multiply.right.as_index_and_constant_flag(); + let b_type = multiply.left_type; + let c_type = multiply.right_type; + + InstructionBuilder { + operation, + a_field, + b_field, + c_field, + b_is_constant, + c_is_constant, + b_type, + c_type, + ..Default::default() + } + .build() + } +} + +impl Display for Multiply { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let Multiply { + destination, + left, + left_type, + right, + right_type, + } = self; + + write!( + f, + "R{destination} = {left_type}({left}) ✕ {right_type}({right})", + ) + } +} diff --git a/dust-lang/src/instruction/multiply_byte.rs b/dust-lang/src/instruction/multiply_byte.rs deleted file mode 100644 index 501776e..0000000 --- a/dust-lang/src/instruction/multiply_byte.rs +++ /dev/null @@ -1,54 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct MultiplyByte { - pub destination: u16, - pub left: Operand, - pub right: Operand, -} - -impl From for MultiplyByte { - fn from(instruction: Instruction) -> Self { - let destination = instruction.a_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - MultiplyByte { - destination, - left, - right, - } - } -} - -impl From for Instruction { - fn from(multiply_byte: MultiplyByte) -> Self { - let operation = Operation::MODULO_BYTE; - let a_field = multiply_byte.destination; - let (b_field, b_is_constant) = multiply_byte.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = multiply_byte.right.as_index_and_constant_flag(); - - InstructionBuilder { - operation, - a_field, - b_field, - c_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for MultiplyByte { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let MultiplyByte { - destination, - left, - right, - } = self; - - write!(f, "R{} = {} ✕ {}", destination, left, right) - } -} diff --git a/dust-lang/src/instruction/multiply_float.rs b/dust-lang/src/instruction/multiply_float.rs deleted file mode 100644 index 19f8292..0000000 --- a/dust-lang/src/instruction/multiply_float.rs +++ /dev/null @@ -1,54 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct MultiplyFloat { - pub destination: u16, - pub left: Operand, - pub right: Operand, -} - -impl From for MultiplyFloat { - fn from(instruction: Instruction) -> Self { - let destination = instruction.a_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - MultiplyFloat { - destination, - left, - right, - } - } -} - -impl From for Instruction { - fn from(multiply_float: MultiplyFloat) -> Self { - let operation = Operation::MULTIPLY_FLOAT; - let a_field = multiply_float.destination; - let (b_field, b_is_constant) = multiply_float.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = multiply_float.right.as_index_and_constant_flag(); - - InstructionBuilder { - operation, - a_field, - b_field, - c_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for MultiplyFloat { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let MultiplyFloat { - destination, - left, - right, - } = self; - - write!(f, "R{} = {} ✕ {}", destination, left, right) - } -} diff --git a/dust-lang/src/instruction/multiply_int.rs b/dust-lang/src/instruction/multiply_int.rs deleted file mode 100644 index 00fbdf5..0000000 --- a/dust-lang/src/instruction/multiply_int.rs +++ /dev/null @@ -1,54 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct MultiplyInt { - pub destination: u16, - pub left: Operand, - pub right: Operand, -} - -impl From for MultiplyInt { - fn from(instruction: Instruction) -> Self { - let destination = instruction.a_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - MultiplyInt { - destination, - left, - right, - } - } -} - -impl From for Instruction { - fn from(multiply_int: MultiplyInt) -> Self { - let operation = Operation::MULTIPLY_INT; - let a_field = multiply_int.destination; - let (b_field, b_is_constant) = multiply_int.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = multiply_int.right.as_index_and_constant_flag(); - - InstructionBuilder { - operation, - a_field, - b_field, - c_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for MultiplyInt { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let MultiplyInt { - destination, - left, - right, - } = self; - - write!(f, "R{} = {} ✕ {}", destination, left, right) - } -} diff --git a/dust-lang/src/instruction/negate.rs b/dust-lang/src/instruction/negate.rs new file mode 100644 index 0000000..e2a4aa4 --- /dev/null +++ b/dust-lang/src/instruction/negate.rs @@ -0,0 +1,54 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; + +pub struct Negate { + pub destination: u16, + pub argument: Operand, + pub argument_type: TypeCode, +} + +impl From for Negate { + fn from(instruction: Instruction) -> Self { + let destination = instruction.a_field(); + let argument = instruction.b_as_argument(); + let argument_type = instruction.b_type(); + + Negate { + destination, + argument, + argument_type, + } + } +} + +impl From for Instruction { + fn from(negate: Negate) -> Self { + let operation = Operation::NEGATE; + let a_field = negate.destination; + let (b_field, b_is_constant) = negate.argument.as_index_and_constant_flag(); + let b_type = negate.argument_type; + + InstructionBuilder { + operation, + a_field, + b_field, + b_is_constant, + b_type, + ..Default::default() + } + .build() + } +} + +impl Display for Negate { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let Negate { + destination, + argument, + argument_type, + } = self; + + write!(f, "R{destination} = -{argument_type}({argument})") + } +} diff --git a/dust-lang/src/instruction/negate_float.rs b/dust-lang/src/instruction/negate_float.rs deleted file mode 100644 index c0ed4d3..0000000 --- a/dust-lang/src/instruction/negate_float.rs +++ /dev/null @@ -1,48 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct NegateFloat { - pub destination: u16, - pub argument: Operand, -} - -impl From for NegateFloat { - fn from(instruction: Instruction) -> Self { - let destination = instruction.a_field(); - let argument = instruction.b_as_argument(); - - NegateFloat { - destination, - argument, - } - } -} - -impl From for Instruction { - fn from(negate_float: NegateFloat) -> Self { - let operation = Operation::NEGATE_FLOAT; - let a_field = negate_float.destination; - let (b_field, b_is_constant) = negate_float.argument.as_index_and_constant_flag(); - - InstructionBuilder { - operation, - a_field, - b_field, - b_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for NegateFloat { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let NegateFloat { - destination, - argument, - } = self; - - write!(f, "R{destination} = -{argument}") - } -} diff --git a/dust-lang/src/instruction/negate_int.rs b/dust-lang/src/instruction/negate_int.rs deleted file mode 100644 index 6f1f901..0000000 --- a/dust-lang/src/instruction/negate_int.rs +++ /dev/null @@ -1,48 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct NegateInt { - pub destination: u16, - pub argument: Operand, -} - -impl From for NegateInt { - fn from(instruction: Instruction) -> Self { - let destination = instruction.a_field(); - let argument = instruction.b_as_argument(); - - NegateInt { - destination, - argument, - } - } -} - -impl From for Instruction { - fn from(negate_int: NegateInt) -> Self { - let operation = Operation::NEGATE_INT; - let a_field = negate_int.destination; - let (b_field, b_is_constant) = negate_int.argument.as_index_and_constant_flag(); - - InstructionBuilder { - operation, - a_field, - b_field, - b_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for NegateInt { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let NegateInt { - destination, - argument, - } = self; - - write!(f, "R{destination} = -{argument}") - } -} diff --git a/dust-lang/src/instruction/not.rs b/dust-lang/src/instruction/not.rs index f26c4ea..51a06db 100644 --- a/dust-lang/src/instruction/not.rs +++ b/dust-lang/src/instruction/not.rs @@ -1,3 +1,5 @@ +use std::fmt::Display; + use crate::{Instruction, Operand, Operation}; use super::InstructionBuilder; @@ -35,3 +37,14 @@ impl From for Instruction { .build() } } + +impl Display for Not { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Not { + destination, + argument, + } = self; + + write!(f, "R{destination} = !{argument}") + } +} diff --git a/dust-lang/src/instruction/operation.rs b/dust-lang/src/instruction/operation.rs index ddded47..4c4a708 100644 --- a/dust-lang/src/instruction/operation.rs +++ b/dust-lang/src/instruction/operation.rs @@ -24,62 +24,20 @@ impl Operation { pub const GET_LOCAL: Operation = Operation(7); pub const SET_LOCAL: Operation = Operation(8); - // Addition - pub const ADD_INT: Operation = Operation(9); - pub const ADD_FLOAT: Operation = Operation(10); - pub const ADD_BYTE: Operation = Operation(11); - pub const ADD_STR: Operation = Operation(12); - pub const ADD_CHAR: Operation = Operation(13); - pub const ADD_STR_CHAR: Operation = Operation(14); - pub const ADD_CHAR_STR: Operation = Operation(15); + // Arithmetic + pub const ADD: Operation = Operation(9); + pub const SUBTRACT: Operation = Operation(16); + pub const MULTIPLY: Operation = Operation(19); + pub const DIVIDE: Operation = Operation(22); + pub const MODULO: Operation = Operation(25); - // Subtraction - pub const SUBTRACT_INT: Operation = Operation(16); - pub const SUBTRACT_FLOAT: Operation = Operation(17); - pub const SUBTRACT_BYTE: Operation = Operation(18); - - // Multiplication - pub const MULTIPLY_INT: Operation = Operation(19); - pub const MULTIPLY_FLOAT: Operation = Operation(20); - pub const MULTIPLY_BYTE: Operation = Operation(21); - - // Division - pub const DIVIDE_INT: Operation = Operation(22); - pub const DIVIDE_FLOAT: Operation = Operation(23); - pub const DIVIDE_BYTE: Operation = Operation(24); - - // Modulo - pub const MODULO_INT: Operation = Operation(25); - pub const MODULO_FLOAT: Operation = Operation(26); - pub const MODULO_BYTE: Operation = Operation(27); - - // Equality - pub const EQUAL_INT: Operation = Operation(28); - pub const EQUAL_FLOAT: Operation = Operation(29); - pub const EQUAL_BYTE: Operation = Operation(30); - pub const EQUAL_STR: Operation = Operation(31); - pub const EQUAL_CHAR: Operation = Operation(32); - pub const EQUAL_STR_CHAR: Operation = Operation(33); - pub const EQUAL_CHAR_STR: Operation = Operation(34); - pub const EQUAL_BOOL: Operation = Operation(35); - - // < or >= comparison - pub const LESS_INT: Operation = Operation(36); - pub const LESS_FLOAT: Operation = Operation(37); - pub const LESS_BYTE: Operation = Operation(38); - pub const LESS_STR: Operation = Operation(39); - pub const LESS_CHAR: Operation = Operation(40); - - // <= or > comparison - pub const LESS_EQUAL_INT: Operation = Operation(41); - pub const LESS_EQUAL_FLOAT: Operation = Operation(42); - pub const LESS_EQUAL_BYTE: Operation = Operation(43); - pub const LESS_EQUAL_STR: Operation = Operation(44); - pub const LESS_EQUAL_CHAR: Operation = Operation(45); + // Comparison + pub const EQUAL: Operation = Operation(28); + pub const LESS: Operation = Operation(36); + pub const LESS_EQUAL: Operation = Operation(41); // Unary operations - pub const NEGATE_INT: Operation = Operation(46); - pub const NEGATE_FLOAT: Operation = Operation(47); + pub const NEGATE: Operation = Operation(46); pub const NOT: Operation = Operation(48); // Logical operations @@ -107,45 +65,15 @@ impl Operation { Self::LOAD_SELF => "LOAD_SELF", Self::GET_LOCAL => "GET_LOCAL", Self::SET_LOCAL => "SET_LOCAL", - Self::ADD_INT => "ADD_INT", - Self::ADD_FLOAT => "ADD_FLOAT", - Self::ADD_BYTE => "ADD_BYTE", - Self::ADD_STR => "ADD_STR", - Self::ADD_CHAR => "ADD_CHAR", - Self::ADD_STR_CHAR => "ADD_STR_CHAR", - Self::ADD_CHAR_STR => "ADD_CHAR_STR", - Self::SUBTRACT_INT => "SUBTRACT_INT", - Self::SUBTRACT_FLOAT => "SUBTRACT_FLOAT", - Self::SUBTRACT_BYTE => "SUBTRACT_BYTE", - Self::MULTIPLY_INT => "MULTIPLY_INT", - Self::MULTIPLY_FLOAT => "MULTIPLY_FLOAT", - Self::MULTIPLY_BYTE => "MULTIPLY_BYTE", - Self::DIVIDE_INT => "DIVIDE_INT", - Self::DIVIDE_FLOAT => "DIVIDE_FLOAT", - Self::DIVIDE_BYTE => "DIVIDE_BYTE", - Self::MODULO_INT => "MODULO_INT", - Self::MODULO_FLOAT => "MODULO_FLOAT", - Self::MODULO_BYTE => "MODULO_BYTE", - Self::EQUAL_INT => "EQUAL_INT", - Self::EQUAL_FLOAT => "EQUAL_FLOAT", - Self::EQUAL_BYTE => "EQUAL_BYTE", - Self::EQUAL_STR => "EQUAL_STR", - Self::EQUAL_CHAR => "EQUAL_CHAR", - Self::EQUAL_STR_CHAR => "EQUAL_STR_CHAR", - Self::EQUAL_CHAR_STR => "EQUAL_CHAR_STR", - Self::EQUAL_BOOL => "EQUAL_BOOL", - Self::LESS_INT => "LESS_INT", - Self::LESS_FLOAT => "LESS_FLOAT", - Self::LESS_BYTE => "LESS_BYTE", - Self::LESS_STR => "LESS_STR", - Self::LESS_CHAR => "LESS_CHAR", - Self::LESS_EQUAL_INT => "LESS_EQUAL_INT", - Self::LESS_EQUAL_FLOAT => "LESS_EQUAL_FLOAT", - Self::LESS_EQUAL_BYTE => "LESS_EQUAL_BYTE", - Self::LESS_EQUAL_STR => "LESS_EQUAL_STR", - Self::LESS_EQUAL_CHAR => "LESS_EQUAL_CHAR", - Self::NEGATE_INT => "NEGATE_INT", - Self::NEGATE_FLOAT => "NEGATE_FLOAT", + Self::ADD => "ADD", + Self::SUBTRACT => "SUBTRACT", + Self::MULTIPLY => "MULTIPLY", + Self::DIVIDE => "DIVIDE", + Self::MODULO => "MODULO", + Self::EQUAL => "EQUAL", + Self::LESS => "LESS", + Self::LESS_EQUAL => "LESS_EQUAL", + Self::NEGATE => "NEGATE", Self::NOT => "NOT", Self::TEST => "TEST", Self::TEST_SET => "TEST_SET", @@ -153,57 +81,30 @@ impl Operation { Self::CALL_NATIVE => "CALL_NATIVE", Self::JUMP => "JUMP", Self::RETURN => "RETURN", - _ => Self::panic_from_unknown_code(self.0), + _ => self.panic_from_unknown_code(), } } pub fn is_math(self) -> bool { matches!( self, - Operation::ADD_INT - | Operation::ADD_FLOAT - | Operation::ADD_BYTE - | Operation::SUBTRACT_INT - | Operation::SUBTRACT_FLOAT - | Operation::SUBTRACT_BYTE - | Operation::MULTIPLY_INT - | Operation::MULTIPLY_FLOAT - | Operation::MULTIPLY_BYTE - | Operation::DIVIDE_INT - | Operation::DIVIDE_FLOAT - | Operation::DIVIDE_BYTE - | Operation::MODULO_INT - | Operation::MODULO_FLOAT - | Operation::MODULO_BYTE + Operation::ADD + | Operation::SUBTRACT + | Operation::MULTIPLY + | Operation::DIVIDE + | Operation::MODULO ) } pub fn is_comparison(self) -> bool { matches!( self, - Operation::EQUAL_INT - | Operation::EQUAL_FLOAT - | Operation::EQUAL_BYTE - | Operation::EQUAL_STR - | Operation::EQUAL_CHAR - | Operation::EQUAL_STR_CHAR - | Operation::EQUAL_CHAR_STR - | Operation::EQUAL_BOOL - | Operation::LESS_INT - | Operation::LESS_FLOAT - | Operation::LESS_BYTE - | Operation::LESS_STR - | Operation::LESS_CHAR - | Operation::LESS_EQUAL_INT - | Operation::LESS_EQUAL_FLOAT - | Operation::LESS_EQUAL_BYTE - | Operation::LESS_EQUAL_STR - | Operation::LESS_EQUAL_CHAR + Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL ) } - pub fn panic_from_unknown_code(code: u8) -> ! { - panic!("Unknown operation code: {code}"); + pub fn panic_from_unknown_code(self) -> ! { + panic!("Unknown operation code: {}", self.0); } } diff --git a/dust-lang/src/instruction/return.rs b/dust-lang/src/instruction/return.rs index e1c9040..b1c6e76 100644 --- a/dust-lang/src/instruction/return.rs +++ b/dust-lang/src/instruction/return.rs @@ -1,3 +1,5 @@ +use std::fmt::{self, Display, Formatter}; + use crate::{Instruction, Operation}; use super::InstructionBuilder; @@ -34,3 +36,18 @@ impl From for Instruction { .build() } } + +impl Display for Return { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let Return { + should_return_value, + return_register, + } = self; + + if *should_return_value { + write!(f, "RETURN R{return_register}") + } else { + write!(f, "RETURN") + } + } +} diff --git a/dust-lang/src/instruction/subtract.rs b/dust-lang/src/instruction/subtract.rs new file mode 100644 index 0000000..df3f68f --- /dev/null +++ b/dust-lang/src/instruction/subtract.rs @@ -0,0 +1,69 @@ +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; + +pub struct Subtract { + pub destination: u16, + pub left: Operand, + pub left_type: TypeCode, + pub right: Operand, + pub right_type: TypeCode, +} + +impl From for Subtract { + fn from(instruction: Instruction) -> Self { + let destination = instruction.a_field(); + let (left, right) = instruction.b_and_c_as_operands(); + let left_type = instruction.b_type(); + let right_type = instruction.c_type(); + + Subtract { + destination, + left, + left_type, + right, + right_type, + } + } +} + +impl From for Instruction { + fn from(subtract: Subtract) -> Self { + let operation = Operation::SUBTRACT; + let a_field = subtract.destination; + let (b_field, b_is_constant) = subtract.left.as_index_and_constant_flag(); + let (c_field, c_is_constant) = subtract.right.as_index_and_constant_flag(); + let b_type = subtract.left_type; + let c_type = subtract.right_type; + + InstructionBuilder { + operation, + a_field, + b_field, + c_field, + b_is_constant, + c_is_constant, + b_type, + c_type, + ..Default::default() + } + .build() + } +} + +impl Display for Subtract { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let Subtract { + destination, + left, + left_type, + right, + right_type, + } = self; + + write!( + f, + "R{destination} = {left_type}({left}) - {right_type}({right})", + ) + } +} diff --git a/dust-lang/src/instruction/subtract_byte.rs b/dust-lang/src/instruction/subtract_byte.rs deleted file mode 100644 index 76de9a7..0000000 --- a/dust-lang/src/instruction/subtract_byte.rs +++ /dev/null @@ -1,54 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct SubtractByte { - pub destination: u16, - pub left: Operand, - pub right: Operand, -} - -impl From for SubtractByte { - fn from(instruction: Instruction) -> Self { - let destination = instruction.a_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - SubtractByte { - destination, - left, - right, - } - } -} - -impl From for Instruction { - fn from(subtract_byte: SubtractByte) -> Self { - let operation = Operation::SUBTRACT_BYTE; - let a_field = subtract_byte.destination; - let (b_field, b_is_constant) = subtract_byte.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = subtract_byte.right.as_index_and_constant_flag(); - - InstructionBuilder { - operation, - a_field, - b_field, - c_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for SubtractByte { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let SubtractByte { - destination, - left, - right, - } = self; - - write!(f, "R{} = {} - {}", destination, left, right) - } -} diff --git a/dust-lang/src/instruction/subtract_float.rs b/dust-lang/src/instruction/subtract_float.rs deleted file mode 100644 index d806a49..0000000 --- a/dust-lang/src/instruction/subtract_float.rs +++ /dev/null @@ -1,54 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct SubtractFloat { - pub destination: u16, - pub left: Operand, - pub right: Operand, -} - -impl From for SubtractFloat { - fn from(instruction: Instruction) -> Self { - let destination = instruction.a_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - SubtractFloat { - destination, - left, - right, - } - } -} - -impl From for Instruction { - fn from(subtract_float: SubtractFloat) -> Self { - let operation = Operation::SUBTRACT_FLOAT; - let a_field = subtract_float.destination; - let (b_field, b_is_constant) = subtract_float.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = subtract_float.right.as_index_and_constant_flag(); - - InstructionBuilder { - operation, - a_field, - b_field, - c_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for SubtractFloat { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let SubtractFloat { - destination, - left, - right, - } = self; - - write!(f, "R{} = {} - {}", destination, left, right) - } -} diff --git a/dust-lang/src/instruction/subtract_int.rs b/dust-lang/src/instruction/subtract_int.rs deleted file mode 100644 index b9b2ffd..0000000 --- a/dust-lang/src/instruction/subtract_int.rs +++ /dev/null @@ -1,54 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use super::{Instruction, InstructionBuilder, Operand, Operation}; - -pub struct SubtractInt { - pub destination: u16, - pub left: Operand, - pub right: Operand, -} - -impl From for SubtractInt { - fn from(instruction: Instruction) -> Self { - let destination = instruction.a_field(); - let (left, right) = instruction.b_and_c_as_operands(); - - SubtractInt { - destination, - left, - right, - } - } -} - -impl From for Instruction { - fn from(subtract_int: SubtractInt) -> Self { - let operation = Operation::SUBTRACT_INT; - let a_field = subtract_int.destination; - let (b_field, b_is_constant) = subtract_int.left.as_index_and_constant_flag(); - let (c_field, c_is_constant) = subtract_int.right.as_index_and_constant_flag(); - - InstructionBuilder { - operation, - a_field, - b_field, - c_field, - b_is_constant, - c_is_constant, - ..Default::default() - } - .build() - } -} - -impl Display for SubtractInt { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let SubtractInt { - destination, - left, - right, - } = self; - - write!(f, "R{} = {} - {}", destination, left, right) - } -} diff --git a/dust-lang/src/instruction/test.rs b/dust-lang/src/instruction/test.rs index 62124dd..e826ed3 100644 --- a/dust-lang/src/instruction/test.rs +++ b/dust-lang/src/instruction/test.rs @@ -1,3 +1,5 @@ +use std::fmt::{self, Display, Formatter}; + use crate::{Instruction, Operation}; use super::InstructionBuilder; @@ -33,3 +35,15 @@ impl From for Instruction { .build() } } + +impl Display for Test { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let Test { + operand_register, + test_value, + } = self; + let bang = if *test_value { "" } else { "!" }; + + write!(f, "if {bang}R{operand_register} {{ JUMP +1 }}") + } +} diff --git a/dust-lang/src/instruction/test_set.rs b/dust-lang/src/instruction/test_set.rs index 4ae8191..c946767 100644 --- a/dust-lang/src/instruction/test_set.rs +++ b/dust-lang/src/instruction/test_set.rs @@ -1,3 +1,5 @@ +use std::fmt::{self, Display, Formatter}; + use crate::{Instruction, Operand, Operation}; use super::InstructionBuilder; @@ -40,3 +42,19 @@ impl From for Instruction { .build() } } + +impl Display for TestSet { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let TestSet { + destination, + argument, + test_value, + } = self; + let bang = if *test_value { "" } else { "!" }; + + write!( + f, + "if {bang}{argument} {{ JUMP +1 }} else {{ R{destination} = {argument} }}" + ) + } +} diff --git a/dust-lang/src/instruction/type_code.rs b/dust-lang/src/instruction/type_code.rs new file mode 100644 index 0000000..daf6e82 --- /dev/null +++ b/dust-lang/src/instruction/type_code.rs @@ -0,0 +1,31 @@ +use std::fmt::Display; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct TypeCode(pub u8); + +impl TypeCode { + pub const BOOLEAN: TypeCode = TypeCode(0); + pub const BYTE: TypeCode = TypeCode(1); + pub const CHARACTER: TypeCode = TypeCode(2); + pub const FLOAT: TypeCode = TypeCode(3); + pub const INTEGER: TypeCode = TypeCode(4); + pub const STRING: TypeCode = TypeCode(5); + + pub fn panic_from_unknown_code(self) -> ! { + panic!("Unknown type code: {}", self.0); + } +} + +impl Display for TypeCode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match *self { + TypeCode::BOOLEAN => write!(f, "bool"), + TypeCode::BYTE => write!(f, "byte"), + TypeCode::CHARACTER => write!(f, "char"), + TypeCode::FLOAT => write!(f, "float"), + TypeCode::INTEGER => write!(f, "int"), + TypeCode::STRING => write!(f, "str"), + _ => self.panic_from_unknown_code(), + } + } +} diff --git a/dust-lang/src/value/mod.rs b/dust-lang/src/value/mod.rs index 6a37fbf..b9b7d91 100644 --- a/dust-lang/src/value/mod.rs +++ b/dust-lang/src/value/mod.rs @@ -58,6 +58,30 @@ impl Value { } } + pub fn as_byte(&self) -> Option { + if let Value::Concrete(ConcreteValue::Byte(byte)) = self { + Some(*byte) + } else { + None + } + } + + pub fn as_character(&self) -> Option { + if let Value::Concrete(ConcreteValue::Character(character)) = self { + Some(*character) + } else { + None + } + } + + pub fn as_float(&self) -> Option { + if let Value::Concrete(ConcreteValue::Float(float)) = self { + Some(*float) + } else { + None + } + } + pub fn as_function(&self) -> Option<&Function> { if let Value::Function(function) = self { Some(function) diff --git a/dust-lang/src/vm/run_action.rs b/dust-lang/src/vm/run_action.rs index d97f0d7..25a4527 100644 --- a/dust-lang/src/vm/run_action.rs +++ b/dust-lang/src/vm/run_action.rs @@ -3,8 +3,9 @@ use tracing::trace; use crate::{ AbstractList, ConcreteValue, Instruction, Operand, Type, Value, instruction::{ - Call, CallNative, Close, GetLocal, Jump, LoadBoolean, LoadConstant, LoadFunction, LoadList, - LoadSelf, Not, Point, Return, SetLocal, Test, TestSet, + Add, Call, CallNative, Close, Divide, Equal, GetLocal, Jump, Less, LessEqual, LoadBoolean, + LoadConstant, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not, Point, + Return, SetLocal, Subtract, Test, TestSet, TypeCode, }, vm::FunctionCall, }; @@ -252,12 +253,42 @@ pub fn add(instruction: Instruction, data: &mut ThreadData) -> bool { let Add { destination, left, + left_type, right, + right_type, } = instruction.into(); - let left = data.get_argument_unchecked(left); - let right = data.get_argument_unchecked(right); - let sum = left.add(right); - let register = Register::Value(sum); + let sum = match (left_type, right_type) { + (TypeCode::INTEGER, TypeCode::INTEGER) => { + let left = unsafe { + data.get_argument_unchecked(left) + .as_integer() + .unwrap_unchecked() + }; + let right = unsafe { + data.get_argument_unchecked(right) + .as_integer() + .unwrap_unchecked() + }; + + ConcreteValue::Integer(left + right) + } + (TypeCode::FLOAT, TypeCode::FLOAT) => { + let left = unsafe { + data.get_argument_unchecked(left) + .as_float() + .unwrap_unchecked() + }; + let right = unsafe { + data.get_argument_unchecked(right) + .as_float() + .unwrap_unchecked() + }; + + ConcreteValue::Float(left + right) + } + _ => panic!("VM Error: Cannot add values"), + }; + let register = Register::Value(Value::Concrete(sum)); data.set_register(destination, register); @@ -270,12 +301,42 @@ pub fn subtract(instruction: Instruction, data: &mut ThreadData) -> bool { let Subtract { destination, left, + left_type, right, + right_type, } = instruction.into(); - let left = data.get_argument_unchecked(left); - let right = data.get_argument_unchecked(right); - let difference = left.subtract(right); - let register = Register::Value(difference); + let difference = match (left_type, right_type) { + (TypeCode::INTEGER, TypeCode::INTEGER) => { + let left = unsafe { + data.get_argument_unchecked(left) + .as_integer() + .unwrap_unchecked() + }; + let right = unsafe { + data.get_argument_unchecked(right) + .as_integer() + .unwrap_unchecked() + }; + + ConcreteValue::Integer(left - right) + } + (TypeCode::FLOAT, TypeCode::FLOAT) => { + let left = unsafe { + data.get_argument_unchecked(left) + .as_float() + .unwrap_unchecked() + }; + let right = unsafe { + data.get_argument_unchecked(right) + .as_float() + .unwrap_unchecked() + }; + + ConcreteValue::Float(left - right) + } + _ => panic!("VM Error: Cannot subtract values"), + }; + let register = Register::Value(Value::Concrete(difference)); data.set_register(destination, register); @@ -288,20 +349,42 @@ pub fn multiply(instruction: Instruction, data: &mut ThreadData) -> bool { let Multiply { destination, left, + left_type, right, + right_type, } = instruction.into(); - let left = data.get_argument_unchecked(left); - let right = data.get_argument_unchecked(right); - let product = match (left, right) { - (Value::Concrete(left), Value::Concrete(right)) => match (left, right) { - (ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => { - ConcreteValue::Integer(left * right).to_value() - } - _ => panic!("Value Error: Cannot multiply values"), - }, - _ => panic!("Value Error: Cannot multiply values"), + let product = match (left_type, right_type) { + (TypeCode::INTEGER, TypeCode::INTEGER) => { + let left = unsafe { + data.get_argument_unchecked(left) + .as_integer() + .unwrap_unchecked() + }; + let right = unsafe { + data.get_argument_unchecked(right) + .as_integer() + .unwrap_unchecked() + }; + + ConcreteValue::Integer(left * right) + } + (TypeCode::FLOAT, TypeCode::FLOAT) => { + let left = unsafe { + data.get_argument_unchecked(left) + .as_float() + .unwrap_unchecked() + }; + let right = unsafe { + data.get_argument_unchecked(right) + .as_float() + .unwrap_unchecked() + }; + + ConcreteValue::Float(left * right) + } + _ => panic!("VM Error: Cannot multiply values"), }; - let register = Register::Value(product); + let register = Register::Value(Value::Concrete(product)); data.set_register(destination, register); @@ -314,20 +397,42 @@ pub fn divide(instruction: Instruction, data: &mut ThreadData) -> bool { let Divide { destination, left, + left_type, right, + right_type, } = instruction.into(); - let left = data.get_argument_unchecked(left); - let right = data.get_argument_unchecked(right); - let quotient = match (left, right) { - (Value::Concrete(left), Value::Concrete(right)) => match (left, right) { - (ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => { - ConcreteValue::Integer(left / right).to_value() - } - _ => panic!("Value Error: Cannot divide values"), - }, - _ => panic!("Value Error: Cannot divide values"), + let quotient = match (left_type, right_type) { + (TypeCode::INTEGER, TypeCode::INTEGER) => { + let left = unsafe { + data.get_argument_unchecked(left) + .as_integer() + .unwrap_unchecked() + }; + let right = unsafe { + data.get_argument_unchecked(right) + .as_integer() + .unwrap_unchecked() + }; + + ConcreteValue::Integer(left / right) + } + (TypeCode::FLOAT, TypeCode::FLOAT) => { + let left = unsafe { + data.get_argument_unchecked(left) + .as_float() + .unwrap_unchecked() + }; + let right = unsafe { + data.get_argument_unchecked(right) + .as_float() + .unwrap_unchecked() + }; + + ConcreteValue::Float(left / right) + } + _ => panic!("VM Error: Cannot divide values"), }; - let register = Register::Value(quotient); + let register = Register::Value(Value::Concrete(quotient)); data.set_register(destination, register); @@ -340,20 +445,28 @@ pub fn modulo(instruction: Instruction, data: &mut ThreadData) -> bool { let Modulo { destination, left, + left_type, right, + right_type, } = instruction.into(); - let left = data.get_argument_unchecked(left); - let right = data.get_argument_unchecked(right); - let remainder = match (left, right) { - (Value::Concrete(left), Value::Concrete(right)) => match (left, right) { - (ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => { - ConcreteValue::Integer(left % right).to_value() - } - _ => panic!("Value Error: Cannot modulo values"), - }, - _ => panic!("Value Error: Cannot modulo values"), + let remainder = match (left_type, right_type) { + (TypeCode::INTEGER, TypeCode::INTEGER) => { + let left = unsafe { + data.get_argument_unchecked(left) + .as_integer() + .unwrap_unchecked() + }; + let right = unsafe { + data.get_argument_unchecked(right) + .as_integer() + .unwrap_unchecked() + }; + + ConcreteValue::Integer(left % right) + } + _ => panic!("VM Error: Cannot modulo values"), }; - let register = Register::Value(remainder); + let register = Register::Value(Value::Concrete(remainder)); data.set_register(destination, register); @@ -415,12 +528,18 @@ pub fn test_set(instruction: Instruction, data: &mut ThreadData) -> bool { } pub fn equal(instruction: Instruction, data: &mut ThreadData) -> bool { - let Equal { value, left, right } = instruction.into(); + let Equal { + comparator, + left, + left_type, + right, + right_type, + } = instruction.into(); let left = data.get_argument_unchecked(left); let right = data.get_argument_unchecked(right); let is_equal = left.equals(right); - if is_equal == value { + if is_equal == comparator { let current_call = data.call_stack.last_mut_unchecked(); current_call.ip += 1; @@ -432,12 +551,18 @@ pub fn equal(instruction: Instruction, data: &mut ThreadData) -> bool { } pub fn less(instruction: Instruction, data: &mut ThreadData) -> bool { - let Less { value, left, right } = instruction.into(); + let Less { + comparator, + left, + left_type, + right, + right_type, + } = instruction.into(); let left = data.get_argument_unchecked(left); let right = data.get_argument_unchecked(right); let is_less = left < right; - if is_less == value { + if is_less == comparator { let current_call = data.call_stack.last_mut_unchecked(); current_call.ip += 1; @@ -449,12 +574,18 @@ pub fn less(instruction: Instruction, data: &mut ThreadData) -> bool { } pub fn less_equal(instruction: Instruction, data: &mut ThreadData) -> bool { - let LessEqual { value, left, right } = instruction.into(); + let LessEqual { + comparator, + left, + left_type, + right, + right_type, + } = instruction.into(); let left = data.get_argument_unchecked(left); let right = data.get_argument_unchecked(right); let is_less_or_equal = left <= right; - if is_less_or_equal == value { + if is_less_or_equal == comparator { let current_call = data.call_stack.last_mut_unchecked(); current_call.ip += 1; @@ -469,6 +600,7 @@ pub fn negate(instruction: Instruction, data: &mut ThreadData) -> bool { let Negate { destination, argument, + argument_type, } = instruction.into(); let argument = data.get_argument_unchecked(argument); let negated = argument.negate(); From 1409698fdd0a9ec7bbd54f917edfd393c0cf81c4 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 13 Jan 2025 10:37:54 -0500 Subject: [PATCH 6/7] Fix some bugs to get language working with new 64-bit instructions --- dust-lang/src/chunk/disassembler.rs | 10 +- dust-lang/src/instruction/mod.rs | 2 +- dust-lang/src/instruction/operation.rs | 30 +++--- dust-lang/src/vm/mod.rs | 14 ++- dust-lang/src/vm/run_action.rs | 134 +++++++++++++++++++++++-- dust-lang/src/vm/thread.rs | 3 +- 6 files changed, 156 insertions(+), 37 deletions(-) diff --git a/dust-lang/src/chunk/disassembler.rs b/dust-lang/src/chunk/disassembler.rs index 817f4e2..ac209cd 100644 --- a/dust-lang/src/chunk/disassembler.rs +++ b/dust-lang/src/chunk/disassembler.rs @@ -46,11 +46,11 @@ use colored::{ColoredString, Colorize}; use crate::{Chunk, Local}; const INSTRUCTION_COLUMNS: [(&str, usize); 4] = - [("i", 5), ("POSITION", 12), ("OPERATION", 17), ("INFO", 36)]; + [("i", 5), ("POSITION", 12), ("OPERATION", 17), ("INFO", 41)]; const INSTRUCTION_BORDERS: [&str; 3] = [ - "╭─────┬────────────┬─────────────────┬────────────────────────────────────╮", - "├─────┼────────────┼─────────────────┼────────────────────────────────────┤", - "╰─────┴────────────┴─────────────────┴────────────────────────────────────╯", + "╭─────┬────────────┬─────────────────┬─────────────────────────────────────────╮", + "├─────┼────────────┼─────────────────┼─────────────────────────────────────────┤", + "╰─────┴────────────┴─────────────────┴─────────────────────────────────────────╯", ]; const LOCAL_COLUMNS: [(&str, usize); 5] = [ @@ -286,7 +286,7 @@ impl<'a, W: Write> Disassembler<'a, W> { .unwrap_or("stripped".to_string()); let operation = instruction.operation().to_string(); let info = instruction.disassembly_info(); - let row = format!("│{index:^5}│{position:^12}│{operation:^17}│{info:^36}│"); + let row = format!("│{index:^5}│{position:^12}│{operation:^17}│{info:^41}│"); self.write_center_border(&row)?; } diff --git a/dust-lang/src/instruction/mod.rs b/dust-lang/src/instruction/mod.rs index 6b55724..424819c 100644 --- a/dust-lang/src/instruction/mod.rs +++ b/dust-lang/src/instruction/mod.rs @@ -220,7 +220,7 @@ impl Instruction { } pub fn d_field(&self) -> bool { - (self.0 >> 54) & 1 == 0 + (self.0 >> 54) & 1 != 0 } pub fn b_type(&self) -> TypeCode { diff --git a/dust-lang/src/instruction/operation.rs b/dust-lang/src/instruction/operation.rs index 4c4a708..5617f9c 100644 --- a/dust-lang/src/instruction/operation.rs +++ b/dust-lang/src/instruction/operation.rs @@ -26,31 +26,31 @@ impl Operation { // Arithmetic pub const ADD: Operation = Operation(9); - pub const SUBTRACT: Operation = Operation(16); - pub const MULTIPLY: Operation = Operation(19); - pub const DIVIDE: Operation = Operation(22); - pub const MODULO: Operation = Operation(25); + pub const SUBTRACT: Operation = Operation(10); + pub const MULTIPLY: Operation = Operation(11); + pub const DIVIDE: Operation = Operation(12); + pub const MODULO: Operation = Operation(13); // Comparison - pub const EQUAL: Operation = Operation(28); - pub const LESS: Operation = Operation(36); - pub const LESS_EQUAL: Operation = Operation(41); + pub const EQUAL: Operation = Operation(14); + pub const LESS: Operation = Operation(15); + pub const LESS_EQUAL: Operation = Operation(16); // Unary operations - pub const NEGATE: Operation = Operation(46); - pub const NOT: Operation = Operation(48); + pub const NEGATE: Operation = Operation(17); + pub const NOT: Operation = Operation(18); // Logical operations - pub const TEST: Operation = Operation(49); - pub const TEST_SET: Operation = Operation(50); + pub const TEST: Operation = Operation(19); + pub const TEST_SET: Operation = Operation(20); // Function calls - pub const CALL: Operation = Operation(51); - pub const CALL_NATIVE: Operation = Operation(52); + pub const CALL: Operation = Operation(21); + pub const CALL_NATIVE: Operation = Operation(22); // Control flow - pub const JUMP: Operation = Operation(53); - pub const RETURN: Operation = Operation(54); + pub const JUMP: Operation = Operation(23); + pub const RETURN: Operation = Operation(24); } impl Operation { diff --git a/dust-lang/src/vm/mod.rs b/dust-lang/src/vm/mod.rs index 4cb6b1d..0e44c78 100644 --- a/dust-lang/src/vm/mod.rs +++ b/dust-lang/src/vm/mod.rs @@ -48,10 +48,16 @@ impl Vm { .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); - }); + + Builder::new() + .name(thread_name) + .spawn(move || { + let value_option = main_thread.run(); + let _ = tx.send(value_option); + }) + .unwrap() + .join() + .unwrap(); rx.recv().unwrap_or(None) } diff --git a/dust-lang/src/vm/run_action.rs b/dust-lang/src/vm/run_action.rs index 25a4527..167419a 100644 --- a/dust-lang/src/vm/run_action.rs +++ b/dust-lang/src/vm/run_action.rs @@ -44,13 +44,13 @@ pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [ multiply, divide, modulo, - test, - test_set, equal, less, less_equal, negate, not, + test, + test_set, call, call_native, jump, @@ -535,9 +535,65 @@ pub fn equal(instruction: Instruction, data: &mut ThreadData) -> bool { right, right_type, } = instruction.into(); - let left = data.get_argument_unchecked(left); - let right = data.get_argument_unchecked(right); - let is_equal = left.equals(right); + let is_equal = match (left_type, right_type) { + (TypeCode::INTEGER, TypeCode::INTEGER) => { + let left = unsafe { + data.get_argument_unchecked(left) + .as_integer() + .unwrap_unchecked() + }; + let right = unsafe { + data.get_argument_unchecked(right) + .as_integer() + .unwrap_unchecked() + }; + + left == right + } + (TypeCode::FLOAT, TypeCode::FLOAT) => { + let left = unsafe { + data.get_argument_unchecked(left) + .as_float() + .unwrap_unchecked() + }; + let right = unsafe { + data.get_argument_unchecked(right) + .as_float() + .unwrap_unchecked() + }; + + left == right + } + (TypeCode::BOOLEAN, TypeCode::BOOLEAN) => { + let left = unsafe { + data.get_argument_unchecked(left) + .as_boolean() + .unwrap_unchecked() + }; + let right = unsafe { + data.get_argument_unchecked(right) + .as_boolean() + .unwrap_unchecked() + }; + + left == right + } + (TypeCode::STRING, TypeCode::STRING) => { + let left = unsafe { + data.get_argument_unchecked(left) + .as_string() + .unwrap_unchecked() + }; + let right = unsafe { + data.get_argument_unchecked(right) + .as_string() + .unwrap_unchecked() + }; + + left == right + } + _ => panic!("VM Error: Cannot compare values"), + }; if is_equal == comparator { let current_call = data.call_stack.last_mut_unchecked(); @@ -558,9 +614,37 @@ pub fn less(instruction: Instruction, data: &mut ThreadData) -> bool { right, right_type, } = instruction.into(); - let left = data.get_argument_unchecked(left); - let right = data.get_argument_unchecked(right); - let is_less = left < right; + let is_less = match (left_type, right_type) { + (TypeCode::INTEGER, TypeCode::INTEGER) => { + let left = unsafe { + data.get_argument_unchecked(left) + .as_integer() + .unwrap_unchecked() + }; + let right = unsafe { + data.get_argument_unchecked(right) + .as_integer() + .unwrap_unchecked() + }; + + left < right + } + (TypeCode::FLOAT, TypeCode::FLOAT) => { + let left = unsafe { + data.get_argument_unchecked(left) + .as_float() + .unwrap_unchecked() + }; + let right = unsafe { + data.get_argument_unchecked(right) + .as_float() + .unwrap_unchecked() + }; + + left < right + } + _ => panic!("VM Error: Cannot compare values"), + }; if is_less == comparator { let current_call = data.call_stack.last_mut_unchecked(); @@ -581,9 +665,37 @@ pub fn less_equal(instruction: Instruction, data: &mut ThreadData) -> bool { right, right_type, } = instruction.into(); - let left = data.get_argument_unchecked(left); - let right = data.get_argument_unchecked(right); - let is_less_or_equal = left <= right; + let is_less_or_equal = match (left_type, right_type) { + (TypeCode::INTEGER, TypeCode::INTEGER) => { + let left = unsafe { + data.get_argument_unchecked(left) + .as_integer() + .unwrap_unchecked() + }; + let right = unsafe { + data.get_argument_unchecked(right) + .as_integer() + .unwrap_unchecked() + }; + + left <= right + } + (TypeCode::FLOAT, TypeCode::FLOAT) => { + let left = unsafe { + data.get_argument_unchecked(left) + .as_float() + .unwrap_unchecked() + }; + let right = unsafe { + data.get_argument_unchecked(right) + .as_float() + .unwrap_unchecked() + }; + + left <= right + } + _ => panic!("VM Error: Cannot compare values"), + }; if is_less_or_equal == comparator { let current_call = data.call_stack.last_mut_unchecked(); diff --git a/dust-lang/src/vm/thread.rs b/dust-lang/src/vm/thread.rs index 30b84a6..c605066 100644 --- a/dust-lang/src/vm/thread.rs +++ b/dust-lang/src/vm/thread.rs @@ -25,7 +25,8 @@ impl Thread { ); let mut call_stack = Stack::with_capacity(self.chunk.prototypes.len() + 1); - let main_call = FunctionCall::new(self.chunk.clone(), 0); + let mut main_call = FunctionCall::new(self.chunk.clone(), 0); + main_call.ip = 1; // The first action is already known call_stack.push(main_call); From b96395c224d2f746811f6a68b5fa5d0d6ec8b561 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 13 Jan 2025 10:43:54 -0500 Subject: [PATCH 7/7] Fix typo in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c4bd339..eed371e 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,7 @@ The performance comparison between the two types of VMs is worth reading for any choose between the two. Some of the benchmarks described in the paper inspired similar benchmarks used in this project to compare Dust to other languages and inform design decisions. -*Writing a Compiler in Go*[^6] by Thorsten Ball is a lot like [Crafting Interpreters][], they are the +*Writing a Compiler in Go*[^6] by Thorsten Ball is a lot like *Crafting Interpreters*, they are the where I look for a generalized approach to solving a problem. Filled with code examples, this book helps the reader make the turn from evaluating a syntax tree to thinking about how problems are solved on physical hardware and how that informs the design of a virtual machine.