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/README.md b/README.md index b4cdfa4..eed371e 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,211 +75,70 @@ 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. - -### Syntax and Evaluation - -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. - -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. - -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 +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. -> I call it my billion-dollar mistake. It was the invention of the null reference in 1965. -> - Tony Hoare +## Usage -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`. +**Dust is under active development and is not yet ready for general use.** -## Previous Implementations +## Installation -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 -[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/) 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/disassembler.rs b/dust-lang/src/chunk/disassembler.rs index ac0b62a..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", 24)]; + [("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:^24}โ”‚"); + let row = format!("โ”‚{index:^5}โ”‚{position:^12}โ”‚{operation:^17}โ”‚{info:^41}โ”‚"); self.write_center_border(&row)?; } 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 07c7eae..2944767 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,10 +41,10 @@ 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, + pub(crate) prototype_index: u16, } impl Chunk { @@ -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/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 edbb040..8df9d3a 100644 --- a/dust-lang/src/compiler/mod.rs +++ b/dust-lang/src/compiler/mod.rs @@ -28,14 +28,14 @@ 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; 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, + instruction::{CallNative, Close, GetLocal, Jump, LoadList, Return, SetLocal, TypeCode}, }; /// Compiles the input and returns a chunk. @@ -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. @@ -104,7 +104,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 +115,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. @@ -227,7 +227,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 +260,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,7 +269,7 @@ 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() @@ -284,7 +284,7 @@ impl<'src> Compiler<'src> { }; if identifier == identifier_text { - Some(index as u8) + Some(index as u16) } else { None } @@ -298,16 +298,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 local_index = self.locals.len() as u16; self.locals.push(( Local::new(identifier_index, register_index, is_mutable, scope), @@ -317,7 +317,7 @@ 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, _)| { @@ -327,15 +327,15 @@ impl<'src> Compiler<'src> { }) } - fn push_or_get_constant(&mut self, value: Value) -> u8 { + fn push_or_get_constant(&mut self, value: Value) -> u16 { if let Some(index) = self .constants .iter() .position(|constant| constant == &value) { - index as u8 + index as u16 } else { - let index = self.constants.len() as u8; + let index = self.constants.len() as u16; self.constants.push(value); @@ -393,7 +393,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() @@ -651,22 +651,39 @@ 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, - }), - _ => { - 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); @@ -677,14 +694,14 @@ 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 @@ -699,12 +716,12 @@ impl<'src> Compiler<'src> { | Operation::LESS_EQUAL | Operation::NEGATE | 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(), @@ -762,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(), @@ -784,6 +811,16 @@ 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, right_position)); @@ -798,18 +835,28 @@ 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), + Token::Plus | Token::PlusEqual => { + Instruction::add(destination, left, left_type_code, right, right_type_code) + } + Token::Minus | Token::MinusEqual => { + Instruction::subtract(destination, left, left_type_code, right, right_type_code) + } + Token::Star | Token::StarEqual => { + Instruction::multiply(destination, left, left_type_code, right, right_type_code) + } + Token::Slash | Token::SlashEqual => { + Instruction::divide(destination, left, left_type_code, right, right_type_code) + } + Token::Percent | Token::PercentEqual => { + Instruction::modulo(destination, left, left_type_code, right, right_type_code) + } _ => { return Err(CompileError::ExpectedTokenMultiple { expected: &[ @@ -861,6 +908,18 @@ 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, left_position)); @@ -878,6 +937,19 @@ 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, right_position)); @@ -885,12 +957,22 @@ impl<'src> Compiler<'src> { 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), + Token::DoubleEqual => { + Instruction::equal(true, left, left_type_code, right, right_type_code) + } + Token::BangEqual => { + Instruction::equal(false, left, left_type_code, right, right_type_code) + } + 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::Greater => { + Instruction::less_equal(false, left, left_type_code, right, right_type_code) + } + Token::GreaterEqual => { + Instruction::less(false, left, left_type_code, right, right_type_code) + } _ => { return Err(CompileError::ExpectedTokenMultiple { expected: &[ @@ -982,7 +1064,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 +1091,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); @@ -1147,10 +1229,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)); @@ -1194,7 +1273,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 +1295,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,7 +1313,7 @@ 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 { @@ -1270,7 +1349,7 @@ 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(); self.parse_expression()?; @@ -1297,8 +1376,8 @@ 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 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, @@ -1307,7 +1386,7 @@ impl<'src> Compiler<'src> { self.instructions .insert(block_start, (jump, Type::None, self.current_position)); - let jump_back_distance = block_end - expression_start + 1; + let jump_back_distance = (block_end - expression_start + 1) as u16; let jump_back = Instruction::from(Jump { offset: jump_back_distance, is_positive: false, @@ -1433,7 +1512,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 +1566,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 +1646,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)?; @@ -1645,7 +1724,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( @@ -1657,7 +1736,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, diff --git a/dust-lang/src/compiler/optimize.rs b/dust-lang/src/compiler/optimize.rs index 83b529c..bf22d34 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,7 @@ 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 | 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 index d8648c2..94230bb 100644 --- a/dust-lang/src/instruction/add.rs +++ b/dust-lang/src/instruction/add.rs @@ -1,20 +1,28 @@ -use crate::{Argument, Instruction, Operation}; +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; pub struct Add { - pub destination: u8, - pub left: Argument, - pub right: Argument, + 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_arguments(); + 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, } } } @@ -22,10 +30,40 @@ impl From for Add { 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(); + 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; - Instruction::new(operation, a, b, c, b_is_constant, c_is_constant, false) + 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/call.rs b/dust-lang/src/instruction/call.rs index dff0679..ff69533 100644 --- a/dust-lang/src/instruction/call.rs +++ b/dust-lang/src/instruction/call.rs @@ -1,9 +1,13 @@ +use std::fmt::{self, Display, Formatter}; + 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 +29,45 @@ 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() + } +} + +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 d44a637..f66fac4 100644 --- a/dust-lang/src/instruction/call_native.rs +++ b/dust-lang/src/instruction/call_native.rs @@ -1,9 +1,13 @@ +use std::fmt::Display; + 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 +26,41 @@ 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() + } +} + +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/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 index 55a3263..d039f90 100644 --- a/dust-lang/src/instruction/divide.rs +++ b/dust-lang/src/instruction/divide.rs @@ -1,20 +1,28 @@ -use crate::{Argument, Instruction, Operation}; +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; pub struct Divide { - pub destination: u8, - pub left: Argument, - pub right: Argument, + 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_arguments(); + 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, } } } @@ -22,10 +30,40 @@ impl From for Divide { 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(); + 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; - Instruction::new(operation, a, b, c, b_is_constant, c_is_constant, false) + 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/equal.rs b/dust-lang/src/instruction/equal.rs index 3b42c2e..6d1aed6 100644 --- a/dust-lang/src/instruction/equal.rs +++ b/dust-lang/src/instruction/equal.rs @@ -1,27 +1,70 @@ -use crate::{Argument, Instruction, Operation}; +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; pub struct Equal { - pub value: bool, - pub left: Argument, - pub right: Argument, + pub comparator: bool, + pub left: Operand, + pub left_type: TypeCode, + pub right: Operand, + pub right_type: TypeCode, } impl From for Equal { fn from(instruction: Instruction) -> Self { - let value = instruction.d_field(); - let (left, right) = instruction.b_and_c_as_arguments(); + 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(); - Equal { value, left, right } + Equal { + comparator, + left, + left_type, + right, + right_type, + } } } impl From for Instruction { - fn from(equal: Equal) -> Self { + fn from(equal_bool: 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; + 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; - Instruction::new(operation, 0, b, c, b_is_constant, c_is_constant, d) + InstructionBuilder { + operation, + b_field, + c_field, + d_field, + b_is_constant, + c_is_constant, + b_type, + c_type, + ..Default::default() + } + .build() + } +} + +impl Display for Equal { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let Equal { + comparator, + left, + left_type, + right, + right_type, + } = self; + let operator = if *comparator { "==" } else { "โ‰ " }; + + write!( + f, + "if {left}({left_type}) {operator} {right}({right_type}) {{ 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..23b82ce 100644 --- a/dust-lang/src/instruction/jump.rs +++ b/dust-lang/src/instruction/jump.rs @@ -1,7 +1,11 @@ +use std::fmt::{self, Display, Formatter}; + use crate::{Instruction, Operation}; +use super::InstructionBuilder; + pub struct Jump { - pub offset: u8, + pub offset: u16, pub is_positive: bool, } @@ -17,9 +21,27 @@ 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() + } +} + +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 index f8a868e..67fa53b 100644 --- a/dust-lang/src/instruction/less.rs +++ b/dust-lang/src/instruction/less.rs @@ -1,27 +1,70 @@ -use crate::{Argument, Instruction, Operation}; +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; pub struct Less { - pub value: bool, - pub left: Argument, - pub right: Argument, + 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 value = instruction.d_field(); - let (left, right) = instruction.b_and_c_as_arguments(); + 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 { value, left, right } + Less { + comparator, + left, + left_type, + right, + right_type, + } } } 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; + 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; - Instruction::new(operation, 0, b, c, b_is_constant, c_is_constant, d) + 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_equal.rs b/dust-lang/src/instruction/less_equal.rs index 6707804..fc3238f 100644 --- a/dust-lang/src/instruction/less_equal.rs +++ b/dust-lang/src/instruction/less_equal.rs @@ -1,27 +1,70 @@ -use crate::{Argument, Instruction, Operation}; +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; pub struct LessEqual { - pub value: bool, - pub left: Argument, - pub right: Argument, + pub comparator: bool, + pub left: Operand, + pub left_type: TypeCode, + pub right: Operand, + pub right_type: TypeCode, } impl From for LessEqual { fn from(instruction: Instruction) -> Self { - let value = instruction.d_field(); - let (left, right) = instruction.b_and_c_as_arguments(); + 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(); - LessEqual { value, left, right } + LessEqual { + comparator, + left, + left_type, + right, + right_type, + } } } impl From for Instruction { - fn from(less_equal: LessEqual) -> Self { + fn from(less_equal_byte: 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; + 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; - Instruction::new(operation, 0, b, c, b_options, c_options, d) + InstructionBuilder { + operation, + b_field, + c_field, + d_field, + b_is_constant, + c_is_constant, + b_type, + c_type, + ..Default::default() + } + .build() + } +} + +impl Display for LessEqual { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let LessEqual { + 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/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 8cb0234..424819c 100644 --- a/dust-lang/src/instruction/mod.rs +++ b/dust-lang/src/instruction/mod.rs @@ -1,16 +1,20 @@ -//! 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 seven distinct fields. //! -//! Bit | Description +//! # 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 | A field (unsigned 8-bit integer) -//! 16-23 | B field (unsigned 8-bit integer) -//! 24-31 | C field (unsigned 8-bit integer) +//! 0-6 | Operation +//! 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) //! //! **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 @@ -60,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}; @@ -71,9 +75,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 +119,7 @@ mod set_local; mod subtract; mod test; mod test_set; +mod type_code; pub use add::Add; pub use call::Call; @@ -145,101 +150,124 @@ pub use test_set::TestSet; use serde::{Deserialize, Serialize}; use std::fmt::{self, Debug, Display, Formatter}; +pub use type_code::TypeCode; use crate::NativeFunction; -/// An operation and its arguments for the Dust virtual machine. -/// -/// 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 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, + pub b_type: TypeCode, + pub c_type: TypeCode, +} -impl Instruction { - pub fn new( - operation: Operation, - a: u8, - b: u8, - c: u8, - 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); +impl InstructionBuilder { + pub fn build(self) -> Instruction { + 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) } +} +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, + b_type: TypeCode::BOOLEAN, + c_type: TypeCode::BOOLEAN, + } + } +} + +/// 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)] +pub struct Instruction(u64); + +impl Instruction { pub fn operation(&self) -> Operation { - let operation_bits = self.0 & 0b0001_1111; + let first_7_bits = (self.0 >> 57) as u8; - Operation(operation_bits as u8) + Operation(first_7_bits) } pub fn b_is_constant(&self) -> bool { - (self.0 >> 5) & 1 == 1 + (self.0 >> 56) & 1 != 0 } pub fn c_is_constant(&self) -> bool { - (self.0 >> 6) & 1 == 1 + (self.0 >> 55) & 1 != 0 } pub fn d_field(&self) -> bool { - (self.0 >> 7) & 1 == 1 + (self.0 >> 54) & 1 != 0 } - pub fn a_field(&self) -> u8 { - (self.0 >> 8) as u8 + pub fn b_type(&self) -> TypeCode { + let byte = ((self.0 >> 51) & 0b111) as u8; + + TypeCode(byte) } - pub fn b_field(&self) -> u8 { - (self.0 >> 16) as u8 + pub fn c_type(&self) -> TypeCode { + let byte = ((self.0 >> 48) & 0b111) as u8; + + TypeCode(byte) } - pub fn c_field(&self) -> u8 { - (self.0 >> 24) as u8 + pub fn a_field(&self) -> u16 { + ((self.0 >> 32) & 0xFFFF) as u16 } - pub fn set_a_field(&mut self, bits: u8) { - self.0 = (self.0 & 0xFFFF00FF) | ((bits as u32) << 8); + pub fn b_field(&self) -> u16 { + ((self.0 >> 16) & 0xFFFF) as u16 } - pub fn set_b_field(&mut self, bits: u8) { - self.0 = (self.0 & 0xFFFF00FF) | ((bits as u32) << 16); + pub fn c_field(&self) -> u16 { + (self.0 & 0xFFFF) as u16 } - pub fn set_c_field(&mut self, bits: u8) { - self.0 = (self.0 & 0xFF00FFFF) | ((bits as u32) << 24); + pub fn set_a_field(&mut self, bits: u16) { + self.0 = (bits as u64) << 31; } - 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_b_field(&mut self, bits: u16) { + self.0 = (bits as u64) << 47; } - pub fn point(from: u8, to: u8) -> Instruction { + pub fn set_c_field(&mut self, bits: u16) { + self.0 = (bits as u64) << 63; + } + + 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, @@ -247,7 +275,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, @@ -255,86 +283,194 @@ 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 { + 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 subtract(destination: u8, left: Argument, right: Argument) -> Instruction { + 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 multiply(destination: u8, left: Argument, right: Argument) -> Instruction { + 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 divide(destination: u8, left: Argument, right: Argument) -> Instruction { + 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 modulo(destination: u8, left: Argument, right: Argument) -> Instruction { + 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 test(operand_register: u8, value: bool) -> Instruction { + 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 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 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 negate(destination: u16, argument: Operand, argument_type: TypeCode) -> Instruction { + Instruction::from(Negate { + destination, + argument, + argument_type, + }) + } + + 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, @@ -342,33 +478,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, @@ -376,9 +486,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 { @@ -390,9 +500,9 @@ impl Instruction { } pub fn call_native( - destination: u8, + destination: u16, function: NativeFunction, - argument_count: u8, + argument_count: u16, ) -> Instruction { Instruction::from(CallNative { destination, @@ -401,7 +511,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, @@ -409,26 +519,16 @@ 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 @@ -443,12 +543,12 @@ impl Instruction { | Operation::LESS_EQUAL | Operation::NEGATE | 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 } @@ -457,24 +557,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) @@ -511,7 +611,7 @@ impl Instruction { | Operation::TEST_SET | Operation::JUMP | Operation::RETURN => false, - _ => Operation::panic_from_unknown_code(self.operation().0), + _ => self.operation().panic_from_unknown_code(), } } @@ -520,242 +620,32 @@ 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 => 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(), - 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(), } } } @@ -772,57 +662,163 @@ 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( + 42, + Operand::Constant(4), + TypeCode::STRING, + Operand::Register(2), + TypeCode::CHARACTER, + ); + + assert_eq!(instruction.operation(), Operation::ADD); + } + + #[test] + fn decode_a_field() { + 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( + 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( + 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::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( + 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( + 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 index 9ba0f5b..64d3548 100644 --- a/dust-lang/src/instruction/modulo.rs +++ b/dust-lang/src/instruction/modulo.rs @@ -1,20 +1,28 @@ -use crate::{Argument, Instruction, Operation}; +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; pub struct Modulo { - pub destination: u8, - pub left: Argument, - pub right: Argument, + 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_arguments(); + 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, } } } @@ -22,10 +30,40 @@ impl From for Modulo { 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(); + 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; - Instruction::new(operation, a, b, c, b_is_constant, c_is_constant, false) + 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/multiply.rs b/dust-lang/src/instruction/multiply.rs index fd73ff3..62fa34e 100644 --- a/dust-lang/src/instruction/multiply.rs +++ b/dust-lang/src/instruction/multiply.rs @@ -1,20 +1,28 @@ -use crate::{Argument, Instruction, Operation}; +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; pub struct Multiply { - pub destination: u8, - pub left: Argument, - pub right: Argument, + 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_arguments(); + 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, } } } @@ -22,10 +30,40 @@ impl From for Multiply { 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(); + 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; - Instruction::new(operation, a, b, c, b_options, c_options, false) + 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/negate.rs b/dust-lang/src/instruction/negate.rs index 405db8d..e2a4aa4 100644 --- a/dust-lang/src/instruction/negate.rs +++ b/dust-lang/src/instruction/negate.rs @@ -1,18 +1,23 @@ -use crate::{Argument, Instruction, Operation}; +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; pub struct Negate { - pub destination: u8, - pub argument: Argument, + 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, } } } @@ -20,10 +25,30 @@ impl From for Negate { 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; + let a_field = negate.destination; + let (b_field, b_is_constant) = negate.argument.as_index_and_constant_flag(); + let b_type = negate.argument_type; - Instruction::new(operation, a, b, c, b_is_constant, false, false) + 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/not.rs b/dust-lang/src/instruction/not.rs index 70d7dd5..51a06db 100644 --- a/dust-lang/src/instruction/not.rs +++ b/dust-lang/src/instruction/not.rs @@ -1,8 +1,12 @@ -use crate::{Argument, Instruction, Operation}; +use std::fmt::Display; + +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 +24,27 @@ 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() + } +} + +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 0ab3851..5617f9c 100644 --- a/dust-lang/src/instruction/operation.rs +++ b/dust-lang/src/instruction/operation.rs @@ -9,29 +9,46 @@ 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); + + // Arithmetic 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); + + // Comparison + 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(17); + pub const NOT: Operation = Operation(18); + + // Logical operations + pub const TEST: Operation = Operation(19); + pub const TEST_SET: Operation = Operation(20); + + // Function calls pub const CALL: Operation = Operation(21); pub const CALL_NATIVE: Operation = Operation(22); + + // Control flow pub const JUMP: Operation = Operation(23); pub const RETURN: Operation = Operation(24); } @@ -53,23 +70,41 @@ impl Operation { Self::MULTIPLY => "MULTIPLY", Self::DIVIDE => "DIVIDE", Self::MODULO => "MODULO", - 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::TEST => "TEST", + Self::TEST_SET => "TEST_SET", Self::CALL => "CALL", 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 panic_from_unknown_code(code: u8) -> ! { - panic!("Unknown operation code: {code}"); + pub fn is_math(self) -> bool { + matches!( + self, + Operation::ADD + | Operation::SUBTRACT + | Operation::MULTIPLY + | Operation::DIVIDE + | Operation::MODULO + ) + } + + pub fn is_comparison(self) -> bool { + matches!( + self, + Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL + ) + } + + pub fn panic_from_unknown_code(self) -> ! { + panic!("Unknown operation code: {}", self.0); } } 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..b1c6e76 100644 --- a/dust-lang/src/instruction/return.rs +++ b/dust-lang/src/instruction/return.rs @@ -1,8 +1,12 @@ +use std::fmt::{self, Display, Formatter}; + 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 +24,30 @@ 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() + } +} + +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/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 index 3923719..df3f68f 100644 --- a/dust-lang/src/instruction/subtract.rs +++ b/dust-lang/src/instruction/subtract.rs @@ -1,20 +1,28 @@ -use crate::{Argument, Instruction, Operation}; +use std::fmt::{self, Display, Formatter}; + +use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode}; pub struct Subtract { - pub destination: u8, - pub left: Argument, - pub right: Argument, + 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_arguments(); + 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, } } } @@ -22,10 +30,40 @@ impl From for Subtract { 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(); + 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; - Instruction::new(operation, a, b, c, b_is_constant, c_is_constant, false) + 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/test.rs b/dust-lang/src/instruction/test.rs index 138483a..e826ed3 100644 --- a/dust-lang/src/instruction/test.rs +++ b/dust-lang/src/instruction/test.rs @@ -1,7 +1,11 @@ +use std::fmt::{self, Display, Formatter}; + use crate::{Instruction, Operation}; +use super::InstructionBuilder; + pub struct Test { - pub operand_register: u8, + pub operand_register: u16, pub test_value: bool, } @@ -19,14 +23,27 @@ 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() + } +} + +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 27a15e5..c946767 100644 --- a/dust-lang/src/instruction/test_set.rs +++ b/dust-lang/src/instruction/test_set.rs @@ -1,8 +1,12 @@ -use crate::{Argument, Instruction, Operation}; +use std::fmt::{self, Display, Formatter}; + +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 +27,34 @@ 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() + } +} + +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/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/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 b2704a4..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, _: Option, 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 0267009..89bf2b7 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: u16, _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, _: u16, 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, _: 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 e56f54a..1926143 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,8 +35,8 @@ macro_rules! define_native_function { pub fn call( &self, data: &mut ThreadData, - destination: Option, - argument_range: Range, + destination: u16, + argument_range: Range, ) -> bool { match self { $( @@ -78,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, @@ -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..1eae46a --- /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: u16, 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..9e3546c 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: u16, 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..4b0cf85 --- /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, _: u16, argument_range: Range) -> bool { + let _ = start_thread(data, argument_range); + data.next_action = get_next_action(data); + + false +} 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/value/mod.rs b/dust-lang/src/value/mod.rs index 6da63fb..b9b7d91 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,33 @@ 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 + } + } + + 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 } @@ -66,6 +90,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 +106,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..7db37aa 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 return_register: u16, 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: u16) -> 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..0e44c78 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,37 @@ 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); - if self.threads.len() == 1 { - return self.threads[0].run(); - } + Builder::new() + .name(thread_name) + .spawn(move || { + let value_option = main_thread.run(); + let _ = tx.send(value_option); + }) + .unwrap() + .join() + .unwrap(); - 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_or(None) } } @@ -85,9 +82,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 3af2630..167419a 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, 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, + Return, SetLocal, Subtract, Test, TestSet, TypeCode, }, 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 { @@ -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, @@ -145,6 +145,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 +187,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 +203,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(); @@ -248,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); @@ -266,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); @@ -284,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); @@ -310,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); @@ -336,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); @@ -397,8 +514,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); @@ -411,12 +528,74 @@ 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 left = data.get_argument_unchecked(left); - let right = data.get_argument_unchecked(right); - let is_equal = left.equals(right); + let Equal { + comparator, + left, + left_type, + right, + right_type, + } = instruction.into(); + 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() + }; - if is_equal == value { + 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(); current_call.ip += 1; @@ -428,12 +607,46 @@ 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 left = data.get_argument_unchecked(left); - let right = data.get_argument_unchecked(right); - let is_less = left < right; + let Less { + comparator, + left, + left_type, + right, + right_type, + } = instruction.into(); + 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() + }; - if is_less == value { + 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(); current_call.ip += 1; @@ -445,12 +658,46 @@ 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 left = data.get_argument_unchecked(left); - let right = data.get_argument_unchecked(right); - let is_less_or_equal = left <= right; + let LessEqual { + comparator, + left, + left_type, + right, + right_type, + } = instruction.into(); + 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() + }; - if is_less_or_equal == value { + 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(); current_call.ip += 1; @@ -465,6 +712,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(); @@ -525,14 +773,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 +813,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..c605066 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::{Chunk, DustString, Operand, 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,8 @@ impl Thread { ); let mut call_stack = Stack::with_capacity(self.chunk.prototypes.len() + 1); - let main_call = FunctionCall::new(&self.chunk, 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); @@ -34,6 +35,7 @@ impl Thread { call_stack, next_action: first_action, return_value_index: None, + spawned_threads: Vec::new(), }; loop { @@ -45,7 +47,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 +56,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 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 +85,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), @@ -96,8 +106,8 @@ impl ThreadData<'_> { } } - pub fn get_register_unchecked(&self, register_index: u8) -> &Register { - trace!("Get register R{register_index}"); + pub fn get_register_unchecked(&self, register_index: u16) -> &Register { + trace!("Get R{register_index}"); let register_index = register_index as usize; @@ -113,13 +123,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) { @@ -133,42 +143,30 @@ 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}"); + 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); + 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, } } - 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], @@ -205,7 +203,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 { @@ -235,14 +233,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) { @@ -258,9 +256,9 @@ 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; + let chunk = &self.call_stack.last_unchecked().chunk; assert!( local_index < chunk.locals.len(), 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![],