Merge branch 'dev'
This commit is contained in:
commit
7cfd60d281
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -269,6 +269,15 @@ version = "1.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-channel"
|
||||||
|
version = "0.5.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-deque"
|
name = "crossbeam-deque"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
@ -335,6 +344,7 @@ dependencies = [
|
|||||||
"annotate-snippets",
|
"annotate-snippets",
|
||||||
"colored",
|
"colored",
|
||||||
"criterion",
|
"criterion",
|
||||||
|
"crossbeam-channel",
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"rand",
|
"rand",
|
||||||
"serde",
|
"serde",
|
||||||
|
268
README.md
268
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.
|
**Fast**, **safe** and **easy-to-use** general-purpose programming language.
|
||||||
|
|
||||||
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.**
|
|
||||||
|
|
||||||
```rust
|
```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...")
|
write_line("Enter your name...")
|
||||||
|
|
||||||
let name = read_line()
|
let name = read_line()
|
||||||
@ -38,7 +23,23 @@ fn fib (n: int) -> int {
|
|||||||
write_line(fib(25))
|
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
|
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
|
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
|
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
|
typed language, users should feel confident in the type-consistency of their code and not want
|
||||||
to go back to a dynamically typed language.
|
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
|
- **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.
|
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
|
Dust's design is to use a separate thread for garbage collection, allowing other threads to
|
||||||
continue executing code while the garbage collector looks for unused memory.
|
continue executing instructions while the garbage collector looks for unused memory.
|
||||||
- **Easy**
|
- **Easy**
|
||||||
- **Simple Syntax** Dust should be easier to learn than most programming languages. Its syntax
|
- **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
|
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
|
- **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.
|
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
|
I'm Jeff 🦀 and I started this project as simple expession evaluator. Initially, the project used an
|
||||||
most programmers such as creating variables, using binary operators and printing to the console.
|
external parser and a tree-walking interpreter. After several books, a few papers, countless
|
||||||
Eventually there should be a complete reference for the syntax.
|
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
|
||||||
### 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
|
|
||||||
languages.
|
languages.
|
||||||
|
|
||||||
> I call it my billion-dollar mistake. It was the invention of the null reference in 1965.
|
## Usage
|
||||||
> - Tony Hoare
|
|
||||||
|
|
||||||
Dust *does* have a `none` type, which should not be confused for being `null`-like. Like the `()` or
|
**Dust is under active development and is not yet ready for general use.**
|
||||||
"unit" type in Rust, `none` exists as a type but not as a value. It indicates the lack of a value
|
|
||||||
from a function, expression or statement. A variable cannot be assigned to `none`.
|
|
||||||
|
|
||||||
## Previous Implementations
|
## Installation
|
||||||
|
|
||||||
Dust has gone through several iterations, each with its own design choices. It was originally
|
Eventually, Dust should be available via package managers and as an embeddable library. For now,
|
||||||
implemented with a syntax tree generated by an external parser, then a parser generator, and finally
|
the only way to use Dust is to clone the repository and build it from source.
|
||||||
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.
|
|
||||||
|
|
||||||
## Inspiration
|
## Inspiration
|
||||||
|
|
||||||
[Crafting Interpreters] by Bob Nystrom was a great resource for writing the compiler, especially the
|
*Crafting Interpreters*[^0] by Bob Nystrom was a great resource for writing the compiler, especially
|
||||||
Pratt parser. The book is a great introduction to writing interpreters. Had it been discovered
|
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
|
sooner, some early implementations of Dust would have been both simpler in design and more ambitious
|
||||||
in scope.
|
in scope.
|
||||||
|
|
||||||
[The Implementation of Lua 5.0] by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, and Waldemar
|
*The Implementation of Lua 5.0*[^1] by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, and
|
||||||
Celes was a great resource for understanding register-based virtual machines and their instructions.
|
Waldemar Celes was a great resource for understanding register-based virtual machines and their
|
||||||
This paper was recommended by Bob Nystrom in [Crafting Interpreters].
|
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
|
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
|
essential in the design of Dust's instructions. Dust uses compile-time optimizations that are based
|
||||||
on Lua optimizations covered in this paper.
|
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
|
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.
|
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
|
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
|
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
|
## License
|
||||||
|
|
||||||
Dust is licensed under the GNU General Public License v3.0. See the `LICENSE` file for details.
|
Dust is licensed under the GNU General Public License v3.0. See the `LICENSE` file for details.
|
||||||
|
|
||||||
## References
|
[^0]: [Crafting Interpreters](https://craftinginterpreters.com/)
|
||||||
|
[^1]: [The Implementation of Lua 5.0](https://www.lua.org/doc/jucs05.pdf)
|
||||||
[^1]: [Crafting Interpreters](https://craftinginterpreters.com/)
|
[^2]: [A No-Frills Introduction to Lua 5.1 VM Instructions](https://www.mcours.net/cours/pdf/hasclic3/hasssclic818.pdf)
|
||||||
[^2]: [The Implementation of Lua 5.0](https://www.lua.org/doc/jucs05.pdf)
|
[^3]: [A Performance Survey on Stack-based and Register-based Virtual Machines](https://arxiv.org/abs/1611.00467)
|
||||||
[^3]: [A No-Frills Introduction to Lua 5.1 VM Instructions](https://www.mcours.net/cours/pdf/hasclic3/hasssclic818.pdf)
|
[^4]: [List of C-family programming languages](https://en.wikipedia.org/wiki/List_of_C-family_programming_languages)
|
||||||
[^4]: [A Performance Survey on Stack-based and Register-based Virtual Machines](https://arxiv.org/abs/1611.00467)
|
[^5]: [ripgrep is faster than {grep, ag, git grep, ucg, pt, sift}](https://blog.burntsushi.net/ripgrep/#mechanics)
|
||||||
[^5]: [List of C-family programming languages](https://en.wikipedia.org/wiki/List_of_C-family_programming_languages)
|
[^6]: [Writing a Compiler in Go](https://compilerbook.com/)
|
||||||
[^6]: [ripgrep is faster than {grep, ag, git grep, ucg, pt, sift}](https://blog.burntsushi.net/ripgrep/#mechanics)
|
[^7]: [Structure and Interpretation of Computer Programs, Second Edition](https://mitpress.mit.edu/9780262510875/structure-and-interpretation-of-computer-programs/)
|
||||||
|
@ -12,7 +12,7 @@ version.workspace = true
|
|||||||
annotate-snippets = "0.11.4"
|
annotate-snippets = "0.11.4"
|
||||||
colored = "2.1.0"
|
colored = "2.1.0"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
serde = { version = "1.0.203", features = ["derive"] }
|
serde = { version = "1.0.203", features = ["derive", "rc"] }
|
||||||
serde_json = "1.0.117"
|
serde_json = "1.0.117"
|
||||||
getrandom = { version = "0.2", features = [
|
getrandom = { version = "0.2", features = [
|
||||||
"js",
|
"js",
|
||||||
@ -21,6 +21,7 @@ smartstring = { version = "1.0.1", features = [
|
|||||||
"serde",
|
"serde",
|
||||||
], default-features = false }
|
], default-features = false }
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
|
crossbeam-channel = "0.5.14"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = { version = "0.3.4", features = ["html_reports"] }
|
criterion = { version = "0.3.4", features = ["html_reports"] }
|
||||||
@ -33,6 +34,10 @@ harness = false
|
|||||||
name = "fibonacci"
|
name = "fibonacci"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "threads"
|
||||||
|
harness = false
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
name = "logic_and"
|
name = "logic_and"
|
||||||
path = "tests/logic/and.rs"
|
path = "tests/logic/and.rs"
|
||||||
|
31
dust-lang/benches/threads.rs
Normal file
31
dust-lang/benches/threads.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use criterion::{Criterion, black_box, criterion_group, criterion_main};
|
||||||
|
use dust_lang::run;
|
||||||
|
|
||||||
|
const SOURCE: &str = r#"
|
||||||
|
let mut i = 0
|
||||||
|
|
||||||
|
while i < 1_000 {
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
spawn(
|
||||||
|
fn () { random_int(0, 10); }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
fn threads(source: &str) {
|
||||||
|
run(source).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn criterion_benchmark(c: &mut Criterion) {
|
||||||
|
let mut group = c.benchmark_group("threads");
|
||||||
|
|
||||||
|
group.measurement_time(Duration::from_secs(15));
|
||||||
|
group.bench_function("threads", |b| b.iter(|| threads(black_box(SOURCE))));
|
||||||
|
group.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(benches, criterion_benchmark);
|
||||||
|
criterion_main!(benches);
|
@ -46,11 +46,11 @@ use colored::{ColoredString, Colorize};
|
|||||||
use crate::{Chunk, Local};
|
use crate::{Chunk, Local};
|
||||||
|
|
||||||
const INSTRUCTION_COLUMNS: [(&str, usize); 4] =
|
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 INSTRUCTION_BORDERS: [&str; 3] = [
|
||||||
"╭─────┬────────────┬─────────────────┬────────────────────────╮",
|
"╭─────┬────────────┬─────────────────┬─────────────────────────────────────────╮",
|
||||||
"├─────┼────────────┼─────────────────┼────────────────────────┤",
|
"├─────┼────────────┼─────────────────┼─────────────────────────────────────────┤",
|
||||||
"╰─────┴────────────┴─────────────────┴────────────────────────╯",
|
"╰─────┴────────────┴─────────────────┴─────────────────────────────────────────╯",
|
||||||
];
|
];
|
||||||
|
|
||||||
const LOCAL_COLUMNS: [(&str, usize); 5] = [
|
const LOCAL_COLUMNS: [(&str, usize); 5] = [
|
||||||
@ -286,7 +286,7 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
|||||||
.unwrap_or("stripped".to_string());
|
.unwrap_or("stripped".to_string());
|
||||||
let operation = instruction.operation().to_string();
|
let operation = instruction.operation().to_string();
|
||||||
let info = instruction.disassembly_info();
|
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)?;
|
self.write_center_border(&row)?;
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
|
//! Scoped variable.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::Scope;
|
use crate::Scope;
|
||||||
|
|
||||||
/// A scoped variable.
|
/// Scoped variable.
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub struct Local {
|
pub struct Local {
|
||||||
/// The index of the identifier in the constants table.
|
/// Index of the identifier in the constants list.
|
||||||
pub identifier_index: u8,
|
pub identifier_index: u16,
|
||||||
|
|
||||||
/// Stack index where the local's value is stored.
|
/// Index of the register where the variable's value is stored.
|
||||||
pub register_index: u8,
|
pub register_index: u16,
|
||||||
|
|
||||||
/// Whether the local is mutable.
|
/// Whether the local is mutable.
|
||||||
pub is_mutable: bool,
|
pub is_mutable: bool,
|
||||||
@ -20,7 +22,7 @@ pub struct Local {
|
|||||||
|
|
||||||
impl Local {
|
impl Local {
|
||||||
/// Creates a new Local instance.
|
/// 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 {
|
Self {
|
||||||
identifier_index,
|
identifier_index,
|
||||||
register_index,
|
register_index,
|
||||||
|
@ -23,6 +23,7 @@ pub use scope::Scope;
|
|||||||
|
|
||||||
use std::fmt::{self, Debug, Display, Formatter, Write as FmtWrite};
|
use std::fmt::{self, Debug, Display, Formatter, Write as FmtWrite};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
@ -40,10 +41,10 @@ pub struct Chunk {
|
|||||||
pub(crate) positions: Vec<Span>,
|
pub(crate) positions: Vec<Span>,
|
||||||
pub(crate) constants: Vec<Value>,
|
pub(crate) constants: Vec<Value>,
|
||||||
pub(crate) locals: Vec<Local>,
|
pub(crate) locals: Vec<Local>,
|
||||||
pub(crate) prototypes: Vec<Chunk>,
|
pub(crate) prototypes: Vec<Arc<Chunk>>,
|
||||||
|
|
||||||
pub(crate) register_count: usize,
|
pub(crate) register_count: usize,
|
||||||
pub(crate) prototype_index: u8,
|
pub(crate) prototype_index: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Chunk {
|
impl Chunk {
|
||||||
@ -55,7 +56,7 @@ impl Chunk {
|
|||||||
positions: impl Into<Vec<Span>>,
|
positions: impl Into<Vec<Span>>,
|
||||||
constants: impl Into<Vec<Value>>,
|
constants: impl Into<Vec<Value>>,
|
||||||
locals: impl Into<Vec<Local>>,
|
locals: impl Into<Vec<Local>>,
|
||||||
prototypes: Vec<Chunk>,
|
prototypes: impl IntoIterator<Item = Chunk>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
@ -64,7 +65,7 @@ impl Chunk {
|
|||||||
positions: positions.into(),
|
positions: positions.into(),
|
||||||
constants: constants.into(),
|
constants: constants.into(),
|
||||||
locals: locals.into(),
|
locals: locals.into(),
|
||||||
prototypes,
|
prototypes: prototypes.into_iter().map(Arc::new).collect(),
|
||||||
register_count: 0,
|
register_count: 0,
|
||||||
prototype_index: 0,
|
prototype_index: 0,
|
||||||
}
|
}
|
||||||
|
@ -104,6 +104,14 @@ pub enum CompileError {
|
|||||||
right_type: Type,
|
right_type: Type,
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
|
CannotNegateType {
|
||||||
|
argument_type: Type,
|
||||||
|
position: Span,
|
||||||
|
},
|
||||||
|
CannotNotType {
|
||||||
|
argument_type: Type,
|
||||||
|
position: Span,
|
||||||
|
},
|
||||||
CannotSubtractType {
|
CannotSubtractType {
|
||||||
argument_type: Type,
|
argument_type: Type,
|
||||||
position: Span,
|
position: Span,
|
||||||
@ -182,6 +190,8 @@ impl AnnotatedError for CompileError {
|
|||||||
Self::CannotMutateImmutableVariable { .. } => "Cannot mutate immutable variable",
|
Self::CannotMutateImmutableVariable { .. } => "Cannot mutate immutable variable",
|
||||||
Self::CannotMultiplyArguments { .. } => "Cannot multiply these types",
|
Self::CannotMultiplyArguments { .. } => "Cannot multiply these types",
|
||||||
Self::CannotMultiplyType { .. } => "Cannot multiply this type",
|
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::CannotResolveRegisterType { .. } => "Cannot resolve register type",
|
||||||
Self::CannotResolveVariableType { .. } => "Cannot resolve type",
|
Self::CannotResolveVariableType { .. } => "Cannot resolve type",
|
||||||
Self::CannotSubtractType { .. } => "Cannot subtract from this type",
|
Self::CannotSubtractType { .. } => "Cannot subtract from this type",
|
||||||
@ -251,8 +261,10 @@ impl AnnotatedError for CompileError {
|
|||||||
right_position,
|
right_position,
|
||||||
} => {
|
} => {
|
||||||
vec![(
|
vec![(
|
||||||
format!("Type \"{left_type}\" cannot be added to type \"{right_type}\". Try converting one of the values to the other type."),
|
format!(
|
||||||
Span(left_position.0, right_position.1)
|
"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),
|
_ => Vec::with_capacity(0),
|
||||||
|
@ -28,14 +28,14 @@ use parse_rule::{ParseRule, Precedence};
|
|||||||
use tracing::{Level, debug, info, span};
|
use tracing::{Level, debug, info, span};
|
||||||
use type_checks::{check_math_type, check_math_types};
|
use type_checks::{check_math_type, check_math_types};
|
||||||
|
|
||||||
use std::mem::replace;
|
use std::{mem::replace, sync::Arc};
|
||||||
|
|
||||||
use optimize::control_flow_register_consolidation;
|
use optimize::control_flow_register_consolidation;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Argument, Chunk, ConcreteValue, DustError, DustString, FunctionType, Instruction, Lexer, Local,
|
Chunk, ConcreteValue, DustError, DustString, FunctionType, Instruction, Lexer, Local,
|
||||||
NativeFunction, Operation, Scope, Span, Token, TokenKind, Type, Value,
|
NativeFunction, Operand, Operation, Scope, Span, Token, TokenKind, Type, Value,
|
||||||
instruction::{CallNative, Close, GetLocal, Jump, LoadList, Negate, Not, Return, SetLocal},
|
instruction::{CallNative, Close, GetLocal, Jump, LoadList, Return, SetLocal, TypeCode},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Compiles the input and returns a chunk.
|
/// 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
|
/// Prototypes that have been compiled. These are assigned to the chunk when
|
||||||
/// [`Compiler::finish`] is called.
|
/// [`Compiler::finish`] is called.
|
||||||
prototypes: Vec<Chunk>,
|
prototypes: Vec<Arc<Chunk>>,
|
||||||
|
|
||||||
/// Maximum stack size required by the chunk. This is assigned to the chunk when
|
/// Maximum stack size required by the chunk. This is assigned to the chunk when
|
||||||
/// [`Compiler::finish`] is called.
|
/// [`Compiler::finish`] is called.
|
||||||
@ -104,7 +104,7 @@ pub struct Compiler<'src> {
|
|||||||
|
|
||||||
/// The first register index that the compiler should use. This is used to avoid reusing the
|
/// The first register index that the compiler should use. This is used to avoid reusing the
|
||||||
/// registers that are used for the function's arguments.
|
/// 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
|
/// Index of the current block. This is used to determine the scope of locals and is incremented
|
||||||
/// when a new block is entered.
|
/// 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
|
/// 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.
|
/// 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
|
/// Whether the chunk is the program's main chunk. This is used to prevent recursive calls to
|
||||||
/// the main chunk.
|
/// the main chunk.
|
||||||
@ -227,7 +227,7 @@ impl<'src> Compiler<'src> {
|
|||||||
matches!(self.current_token, Token::Eof)
|
matches!(self.current_token, Token::Eof)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_register(&self) -> u8 {
|
fn next_register(&self) -> u16 {
|
||||||
self.instructions
|
self.instructions
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
@ -260,7 +260,7 @@ impl<'src> Compiler<'src> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_local(&self, index: u8) -> Result<&(Local, Type), CompileError> {
|
fn get_local(&self, index: u16) -> Result<&(Local, Type), CompileError> {
|
||||||
self.locals
|
self.locals
|
||||||
.get(index as usize)
|
.get(index as usize)
|
||||||
.ok_or(CompileError::UndeclaredVariable {
|
.ok_or(CompileError::UndeclaredVariable {
|
||||||
@ -269,7 +269,7 @@ impl<'src> Compiler<'src> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_local_index(&self, identifier_text: &str) -> Result<u8, CompileError> {
|
fn get_local_index(&self, identifier_text: &str) -> Result<u16, CompileError> {
|
||||||
self.locals
|
self.locals
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
@ -284,7 +284,7 @@ impl<'src> Compiler<'src> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if identifier == identifier_text {
|
if identifier == identifier_text {
|
||||||
Some(index as u8)
|
Some(index as u16)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -298,16 +298,16 @@ impl<'src> Compiler<'src> {
|
|||||||
fn declare_local(
|
fn declare_local(
|
||||||
&mut self,
|
&mut self,
|
||||||
identifier: &str,
|
identifier: &str,
|
||||||
register_index: u8,
|
register_index: u16,
|
||||||
r#type: Type,
|
r#type: Type,
|
||||||
is_mutable: bool,
|
is_mutable: bool,
|
||||||
scope: Scope,
|
scope: Scope,
|
||||||
) -> (u8, u8) {
|
) -> (u16, u16) {
|
||||||
info!("Declaring local {identifier}");
|
info!("Declaring local {identifier}");
|
||||||
|
|
||||||
let identifier = Value::Concrete(ConcreteValue::string(identifier));
|
let identifier = Value::Concrete(ConcreteValue::string(identifier));
|
||||||
let identifier_index = self.push_or_get_constant(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((
|
self.locals.push((
|
||||||
Local::new(identifier_index, register_index, is_mutable, scope),
|
Local::new(identifier_index, register_index, is_mutable, scope),
|
||||||
@ -317,7 +317,7 @@ impl<'src> Compiler<'src> {
|
|||||||
(local_index, identifier_index)
|
(local_index, identifier_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_identifier(&self, local_index: u8) -> Option<String> {
|
fn get_identifier(&self, local_index: u16) -> Option<String> {
|
||||||
self.locals
|
self.locals
|
||||||
.get(local_index as usize)
|
.get(local_index as usize)
|
||||||
.and_then(|(local, _)| {
|
.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
|
if let Some(index) = self
|
||||||
.constants
|
.constants
|
||||||
.iter()
|
.iter()
|
||||||
.position(|constant| constant == &value)
|
.position(|constant| constant == &value)
|
||||||
{
|
{
|
||||||
index as u8
|
index as u16
|
||||||
} else {
|
} else {
|
||||||
let index = self.constants.len() as u8;
|
let index = self.constants.len() as u16;
|
||||||
|
|
||||||
self.constants.push(value);
|
self.constants.push(value);
|
||||||
|
|
||||||
@ -393,7 +393,7 @@ impl<'src> Compiler<'src> {
|
|||||||
.unwrap_or(Type::None)
|
.unwrap_or(Type::None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_register_type(&self, register_index: u8) -> Result<Type, CompileError> {
|
fn get_register_type(&self, register_index: u16) -> Result<Type, CompileError> {
|
||||||
if let Some((_, r#type)) = self
|
if let Some((_, r#type)) = self
|
||||||
.locals
|
.locals
|
||||||
.iter()
|
.iter()
|
||||||
@ -651,15 +651,26 @@ impl<'src> Compiler<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let destination = self.next_register();
|
let destination = self.next_register();
|
||||||
let instruction = match operator.kind() {
|
let type_code = match previous_type {
|
||||||
TokenKind::Bang => Instruction::from(Not {
|
Type::Boolean => TypeCode::BOOLEAN,
|
||||||
destination,
|
Type::Byte => TypeCode::BYTE,
|
||||||
argument,
|
Type::Character => TypeCode::CHARACTER,
|
||||||
}),
|
Type::Float => TypeCode::FLOAT,
|
||||||
TokenKind::Minus => Instruction::from(Negate {
|
Type::Integer => TypeCode::INTEGER,
|
||||||
destination,
|
Type::String => TypeCode::STRING,
|
||||||
argument,
|
_ => 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 {
|
return Err(CompileError::ExpectedTokenMultiple {
|
||||||
expected: &[TokenKind::Bang, TokenKind::Minus],
|
expected: &[TokenKind::Bang, TokenKind::Minus],
|
||||||
@ -667,6 +678,12 @@ impl<'src> Compiler<'src> {
|
|||||||
position: operator_position,
|
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);
|
self.emit_instruction(instruction, previous_type, operator_position);
|
||||||
@ -677,14 +694,14 @@ impl<'src> Compiler<'src> {
|
|||||||
fn handle_binary_argument(
|
fn handle_binary_argument(
|
||||||
&mut self,
|
&mut self,
|
||||||
instruction: &Instruction,
|
instruction: &Instruction,
|
||||||
) -> Result<(Argument, bool), CompileError> {
|
) -> Result<(Operand, bool), CompileError> {
|
||||||
let (argument, push_back) = match instruction.operation() {
|
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 => {
|
Operation::GET_LOCAL => {
|
||||||
let local_index = instruction.b_field();
|
let local_index = instruction.b_field();
|
||||||
let (local, _) = self.get_local(local_index)?;
|
let (local, _) = self.get_local(local_index)?;
|
||||||
|
|
||||||
(Argument::Register(local.register_index), false)
|
(Operand::Register(local.register_index), false)
|
||||||
}
|
}
|
||||||
Operation::LOAD_BOOLEAN
|
Operation::LOAD_BOOLEAN
|
||||||
| Operation::LOAD_LIST
|
| Operation::LOAD_LIST
|
||||||
@ -699,12 +716,12 @@ impl<'src> Compiler<'src> {
|
|||||||
| Operation::LESS_EQUAL
|
| Operation::LESS_EQUAL
|
||||||
| Operation::NEGATE
|
| Operation::NEGATE
|
||||||
| Operation::NOT
|
| Operation::NOT
|
||||||
| Operation::CALL => (Argument::Register(instruction.a_field()), true),
|
| Operation::CALL => (Operand::Register(instruction.a_field()), true),
|
||||||
Operation::CALL_NATIVE => {
|
Operation::CALL_NATIVE => {
|
||||||
let function = NativeFunction::from(instruction.b_field());
|
let function = NativeFunction::from(instruction.b_field());
|
||||||
|
|
||||||
if function.returns_value() {
|
if function.returns_value() {
|
||||||
(Argument::Register(instruction.a_field()), true)
|
(Operand::Register(instruction.a_field()), true)
|
||||||
} else {
|
} else {
|
||||||
return Err(CompileError::ExpectedExpression {
|
return Err(CompileError::ExpectedExpression {
|
||||||
found: self.previous_token.to_owned(),
|
found: self.previous_token.to_owned(),
|
||||||
@ -762,6 +779,16 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
check_math_type(&left_type, operator, &left_position)?;
|
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 {
|
if is_assignment && !left_is_mutable_local {
|
||||||
return Err(CompileError::ExpectedMutableVariable {
|
return Err(CompileError::ExpectedMutableVariable {
|
||||||
found: self.previous_token.to_owned(),
|
found: self.previous_token.to_owned(),
|
||||||
@ -784,6 +811,16 @@ impl<'src> Compiler<'src> {
|
|||||||
&right_position,
|
&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 {
|
if push_back_right {
|
||||||
self.instructions
|
self.instructions
|
||||||
.push((right_instruction, right_type, right_position));
|
.push((right_instruction, right_type, right_position));
|
||||||
@ -798,18 +835,28 @@ impl<'src> Compiler<'src> {
|
|||||||
};
|
};
|
||||||
let destination = if is_assignment {
|
let destination = if is_assignment {
|
||||||
match left {
|
match left {
|
||||||
Argument::Register(register) => register,
|
Operand::Register(register) => register,
|
||||||
Argument::Constant(_) => self.next_register(),
|
Operand::Constant(_) => self.next_register(),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.next_register()
|
self.next_register()
|
||||||
};
|
};
|
||||||
let instruction = match operator {
|
let instruction = match operator {
|
||||||
Token::Plus | Token::PlusEqual => Instruction::add(destination, left, right),
|
Token::Plus | Token::PlusEqual => {
|
||||||
Token::Minus | Token::MinusEqual => Instruction::subtract(destination, left, right),
|
Instruction::add(destination, left, left_type_code, right, right_type_code)
|
||||||
Token::Star | Token::StarEqual => Instruction::multiply(destination, left, right),
|
}
|
||||||
Token::Slash | Token::SlashEqual => Instruction::divide(destination, left, right),
|
Token::Minus | Token::MinusEqual => {
|
||||||
Token::Percent | Token::PercentEqual => Instruction::modulo(destination, left, right),
|
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 {
|
return Err(CompileError::ExpectedTokenMultiple {
|
||||||
expected: &[
|
expected: &[
|
||||||
@ -861,6 +908,18 @@ impl<'src> Compiler<'src> {
|
|||||||
let operator_position = self.current_position;
|
let operator_position = self.current_position;
|
||||||
let rule = ParseRule::from(&operator);
|
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 {
|
if push_back_left {
|
||||||
self.instructions
|
self.instructions
|
||||||
.push((left_instruction, left_type, left_position));
|
.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)?;
|
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 {
|
if push_back_right {
|
||||||
self.instructions
|
self.instructions
|
||||||
.push((right_instruction, right_type, right_position));
|
.push((right_instruction, right_type, right_position));
|
||||||
@ -885,12 +957,22 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
let destination = self.next_register();
|
let destination = self.next_register();
|
||||||
let comparison = match operator {
|
let comparison = match operator {
|
||||||
Token::DoubleEqual => Instruction::equal(true, left, right),
|
Token::DoubleEqual => {
|
||||||
Token::BangEqual => Instruction::equal(false, left, right),
|
Instruction::equal(true, left, left_type_code, right, right_type_code)
|
||||||
Token::Less => Instruction::less(true, left, right),
|
}
|
||||||
Token::LessEqual => Instruction::less_equal(true, left, right),
|
Token::BangEqual => {
|
||||||
Token::Greater => Instruction::less_equal(false, left, right),
|
Instruction::equal(false, left, left_type_code, right, right_type_code)
|
||||||
Token::GreaterEqual => Instruction::less(false, left, right),
|
}
|
||||||
|
Token::Less => Instruction::less(true, left, left_type_code, right, right_type_code),
|
||||||
|
Token::LessEqual => {
|
||||||
|
Instruction::less_equal(true, left, left_type_code, right, right_type_code)
|
||||||
|
}
|
||||||
|
Token::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 {
|
return Err(CompileError::ExpectedTokenMultiple {
|
||||||
expected: &[
|
expected: &[
|
||||||
@ -982,7 +1064,7 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
let old_jump = &mut instructions[1].0;
|
let old_jump = &mut instructions[1].0;
|
||||||
let jump_index = instructions_length - group_index * 3 - 1;
|
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);
|
*old_jump = Instruction::jump(short_circuit_distance, true);
|
||||||
}
|
}
|
||||||
@ -1009,7 +1091,7 @@ impl<'src> Compiler<'src> {
|
|||||||
return self.parse_call_native(native_function);
|
return self.parse_call_native(native_function);
|
||||||
} else if self.function_name.as_deref() == Some(identifier) && !self.is_main {
|
} else if self.function_name.as_deref() == Some(identifier) && !self.is_main {
|
||||||
let destination = self.next_register();
|
let destination = self.next_register();
|
||||||
let load_self = Instruction::load_self(destination);
|
let load_self = Instruction::load_self(destination, false);
|
||||||
|
|
||||||
self.emit_instruction(load_self, Type::SelfFunction, start_position);
|
self.emit_instruction(load_self, Type::SelfFunction, start_position);
|
||||||
|
|
||||||
@ -1147,10 +1229,7 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
let destination = self.next_register();
|
let destination = self.next_register();
|
||||||
let end = self.previous_position.1;
|
let end = self.previous_position.1;
|
||||||
let load_list = Instruction::from(LoadList {
|
let load_list = Instruction::load_list(destination, start_register, false);
|
||||||
destination,
|
|
||||||
start_register,
|
|
||||||
});
|
|
||||||
|
|
||||||
self.emit_instruction(load_list, Type::List(Box::new(item_type)), Span(start, end));
|
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 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();
|
let if_block_type = self.get_last_instruction_type();
|
||||||
|
|
||||||
if let Token::Else = self.current_token {
|
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_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();
|
let else_block_type = self.get_last_instruction_type();
|
||||||
|
|
||||||
if let Err(conflict) = if_block_type.check(&else_block_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();
|
let (loader, _, _) = self.instructions.last_mut().unwrap();
|
||||||
|
|
||||||
loader.set_c_field(true as u8);
|
loader.set_c_field(true as u16);
|
||||||
} else {
|
} else {
|
||||||
if_block_distance += 1;
|
if_block_distance += 1;
|
||||||
let jump = Instruction::from(Jump {
|
let jump = Instruction::from(Jump {
|
||||||
@ -1270,7 +1349,7 @@ impl<'src> Compiler<'src> {
|
|||||||
fn parse_while(&mut self) -> Result<(), CompileError> {
|
fn parse_while(&mut self) -> Result<(), CompileError> {
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
let expression_start = self.instructions.len() as u8;
|
let expression_start = self.instructions.len();
|
||||||
|
|
||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
|
|
||||||
@ -1297,8 +1376,8 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
self.parse_block()?;
|
self.parse_block()?;
|
||||||
|
|
||||||
let block_end = self.instructions.len() as u8;
|
let block_end = self.instructions.len();
|
||||||
let jump_distance = block_end - block_start as u8 + 1;
|
let jump_distance = (block_end - block_start + 1) as u16;
|
||||||
let jump = Instruction::from(Jump {
|
let jump = Instruction::from(Jump {
|
||||||
offset: jump_distance,
|
offset: jump_distance,
|
||||||
is_positive: true,
|
is_positive: true,
|
||||||
@ -1307,7 +1386,7 @@ impl<'src> Compiler<'src> {
|
|||||||
self.instructions
|
self.instructions
|
||||||
.insert(block_start, (jump, Type::None, self.current_position));
|
.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 {
|
let jump_back = Instruction::from(Jump {
|
||||||
offset: jump_back_distance,
|
offset: jump_back_distance,
|
||||||
is_positive: false,
|
is_positive: false,
|
||||||
@ -1433,7 +1512,7 @@ impl<'src> Compiler<'src> {
|
|||||||
let offset = offset as usize;
|
let offset = offset as usize;
|
||||||
|
|
||||||
if is_positive && offset + index == instruction_length - 1 {
|
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;
|
let offset = offset as usize;
|
||||||
|
|
||||||
if is_positive && offset + index == instruction_length - 1 {
|
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)? {
|
while !function_compiler.allow(Token::RightParenthesis)? {
|
||||||
let is_mutable = function_compiler.allow(Token::Mut)?;
|
let is_mutable = function_compiler.allow(Token::Mut)?;
|
||||||
@ -1645,7 +1724,7 @@ impl<'src> Compiler<'src> {
|
|||||||
let chunk = function_compiler.finish();
|
let chunk = function_compiler.finish();
|
||||||
let destination = self.next_register();
|
let destination = self.next_register();
|
||||||
|
|
||||||
self.prototypes.push(chunk);
|
self.prototypes.push(Arc::new(chunk));
|
||||||
|
|
||||||
if let Some(identifier) = identifier {
|
if let Some(identifier) = identifier {
|
||||||
self.declare_local(
|
self.declare_local(
|
||||||
@ -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(
|
self.emit_instruction(
|
||||||
load_function,
|
load_function,
|
||||||
|
@ -26,7 +26,7 @@ use crate::{Compiler, Instruction, Operation};
|
|||||||
/// a `POINT` instruction to prevent the VM from encountering an empty register.
|
/// a `POINT` instruction to prevent the VM from encountering an empty register.
|
||||||
///
|
///
|
||||||
/// The instructions must be in the following order:
|
/// 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`
|
/// - `JUMP`
|
||||||
/// - `LOAD_BOOLEAN` or `LOAD_CONSTANT`
|
/// - `LOAD_BOOLEAN` or `LOAD_CONSTANT`
|
||||||
/// - `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!(
|
if !matches!(
|
||||||
compiler.get_last_operations(),
|
compiler.get_last_operations(),
|
||||||
Some([
|
Some([
|
||||||
Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL | Operation::TEST,
|
Operation::TEST | Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL,
|
||||||
Operation::JUMP,
|
Operation::JUMP,
|
||||||
Operation::LOAD_BOOLEAN | Operation::LOAD_CONSTANT,
|
Operation::LOAD_BOOLEAN | Operation::LOAD_CONSTANT,
|
||||||
Operation::LOAD_BOOLEAN | Operation::LOAD_CONSTANT,
|
Operation::LOAD_BOOLEAN | Operation::LOAD_CONSTANT,
|
||||||
|
@ -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 struct Add {
|
||||||
pub destination: u8,
|
pub destination: u16,
|
||||||
pub left: Argument,
|
pub left: Operand,
|
||||||
pub right: Argument,
|
pub left_type: TypeCode,
|
||||||
|
pub right: Operand,
|
||||||
|
pub right_type: TypeCode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Instruction> for Add {
|
impl From<Instruction> for Add {
|
||||||
fn from(instruction: Instruction) -> Self {
|
fn from(instruction: Instruction) -> Self {
|
||||||
let destination = instruction.a_field();
|
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 {
|
Add {
|
||||||
destination,
|
destination,
|
||||||
left,
|
left,
|
||||||
|
left_type,
|
||||||
right,
|
right,
|
||||||
|
right_type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -22,10 +30,40 @@ impl From<Instruction> for Add {
|
|||||||
impl From<Add> for Instruction {
|
impl From<Add> for Instruction {
|
||||||
fn from(add: Add) -> Self {
|
fn from(add: Add) -> Self {
|
||||||
let operation = Operation::ADD;
|
let operation = Operation::ADD;
|
||||||
let a = add.destination;
|
let a_field = add.destination;
|
||||||
let (b, b_is_constant) = add.left.as_index_and_constant_flag();
|
let (b_field, b_is_constant) = add.left.as_index_and_constant_flag();
|
||||||
let (c, c_is_constant) = add.right.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})",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
use crate::{Instruction, Operation};
|
use crate::{Instruction, Operation};
|
||||||
|
|
||||||
|
use super::InstructionBuilder;
|
||||||
|
|
||||||
pub struct Call {
|
pub struct Call {
|
||||||
pub destination: u8,
|
pub destination: u16,
|
||||||
pub function_register: u8,
|
pub function_register: u16,
|
||||||
pub argument_count: u8,
|
pub argument_count: u16,
|
||||||
pub is_recursive: bool,
|
pub is_recursive: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,11 +29,45 @@ impl From<Instruction> for Call {
|
|||||||
|
|
||||||
impl From<Call> for Instruction {
|
impl From<Call> for Instruction {
|
||||||
fn from(call: Call) -> Self {
|
fn from(call: Call) -> Self {
|
||||||
let a = call.destination;
|
let a_field = call.destination;
|
||||||
let b = call.function_register;
|
let b_field = call.function_register;
|
||||||
let c = call.argument_count;
|
let c_field = call.argument_count;
|
||||||
let d = call.is_recursive;
|
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})"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
use crate::{Instruction, NativeFunction, Operation};
|
use crate::{Instruction, NativeFunction, Operation};
|
||||||
|
|
||||||
|
use super::InstructionBuilder;
|
||||||
|
|
||||||
pub struct CallNative {
|
pub struct CallNative {
|
||||||
pub destination: u8,
|
pub destination: u16,
|
||||||
pub function: NativeFunction,
|
pub function: NativeFunction,
|
||||||
pub argument_count: u8,
|
pub argument_count: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Instruction> for CallNative {
|
impl From<Instruction> for CallNative {
|
||||||
@ -22,10 +26,41 @@ impl From<Instruction> for CallNative {
|
|||||||
impl From<CallNative> for Instruction {
|
impl From<CallNative> for Instruction {
|
||||||
fn from(call_native: CallNative) -> Self {
|
fn from(call_native: CallNative) -> Self {
|
||||||
let operation = Operation::CALL_NATIVE;
|
let operation = Operation::CALL_NATIVE;
|
||||||
let a = call_native.destination;
|
let a_field = call_native.destination;
|
||||||
let b = call_native.function as u8;
|
let b_field = call_native.function as u16;
|
||||||
let c = call_native.argument_count;
|
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})"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
use crate::{Instruction, Operation};
|
use crate::{Instruction, Operation};
|
||||||
|
|
||||||
|
use super::InstructionBuilder;
|
||||||
|
|
||||||
pub struct Close {
|
pub struct Close {
|
||||||
pub from: u8,
|
pub from: u16,
|
||||||
pub to: u8,
|
pub to: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Instruction> for Close {
|
impl From<Instruction> for Close {
|
||||||
@ -17,8 +21,23 @@ impl From<Instruction> for Close {
|
|||||||
impl From<Close> for Instruction {
|
impl From<Close> for Instruction {
|
||||||
fn from(close: Close) -> Self {
|
fn from(close: Close) -> Self {
|
||||||
let operation = Operation::CLOSE;
|
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}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 struct Divide {
|
||||||
pub destination: u8,
|
pub destination: u16,
|
||||||
pub left: Argument,
|
pub left: Operand,
|
||||||
pub right: Argument,
|
pub left_type: TypeCode,
|
||||||
|
pub right: Operand,
|
||||||
|
pub right_type: TypeCode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Instruction> for Divide {
|
impl From<Instruction> for Divide {
|
||||||
fn from(instruction: Instruction) -> Self {
|
fn from(instruction: Instruction) -> Self {
|
||||||
let destination = instruction.a_field();
|
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 {
|
Divide {
|
||||||
destination,
|
destination,
|
||||||
left,
|
left,
|
||||||
|
left_type,
|
||||||
right,
|
right,
|
||||||
|
right_type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -22,10 +30,40 @@ impl From<Instruction> for Divide {
|
|||||||
impl From<Divide> for Instruction {
|
impl From<Divide> for Instruction {
|
||||||
fn from(divide: Divide) -> Self {
|
fn from(divide: Divide) -> Self {
|
||||||
let operation = Operation::DIVIDE;
|
let operation = Operation::DIVIDE;
|
||||||
let a = divide.destination;
|
let a_field = divide.destination;
|
||||||
let (b, b_is_constant) = divide.left.as_index_and_constant_flag();
|
let (b_field, b_is_constant) = divide.left.as_index_and_constant_flag();
|
||||||
let (c, c_is_constant) = divide.right.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})",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 struct Equal {
|
||||||
pub value: bool,
|
pub comparator: bool,
|
||||||
pub left: Argument,
|
pub left: Operand,
|
||||||
pub right: Argument,
|
pub left_type: TypeCode,
|
||||||
|
pub right: Operand,
|
||||||
|
pub right_type: TypeCode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Instruction> for Equal {
|
impl From<Instruction> for Equal {
|
||||||
fn from(instruction: Instruction) -> Self {
|
fn from(instruction: Instruction) -> Self {
|
||||||
let value = instruction.d_field();
|
let comparator = instruction.d_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();
|
||||||
|
|
||||||
Equal { value, left, right }
|
Equal {
|
||||||
|
comparator,
|
||||||
|
left,
|
||||||
|
left_type,
|
||||||
|
right,
|
||||||
|
right_type,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Equal> for Instruction {
|
impl From<Equal> for Instruction {
|
||||||
fn from(equal: Equal) -> Self {
|
fn from(equal_bool: Equal) -> Self {
|
||||||
let operation = Operation::EQUAL;
|
let operation = Operation::EQUAL;
|
||||||
let (b, b_is_constant) = equal.left.as_index_and_constant_flag();
|
let (b_field, b_is_constant) = equal_bool.left.as_index_and_constant_flag();
|
||||||
let (c, c_is_constant) = equal.right.as_index_and_constant_flag();
|
let (c_field, c_is_constant) = equal_bool.right.as_index_and_constant_flag();
|
||||||
let d = equal.value;
|
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 }}"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
use crate::{Instruction, Operation};
|
use crate::{Instruction, Operation};
|
||||||
|
|
||||||
|
use super::InstructionBuilder;
|
||||||
|
|
||||||
pub struct GetLocal {
|
pub struct GetLocal {
|
||||||
pub destination: u8,
|
pub destination: u16,
|
||||||
pub local_index: u8,
|
pub local_index: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Instruction> for GetLocal {
|
impl From<Instruction> for GetLocal {
|
||||||
@ -20,9 +24,26 @@ impl From<Instruction> for GetLocal {
|
|||||||
impl From<GetLocal> for Instruction {
|
impl From<GetLocal> for Instruction {
|
||||||
fn from(get_local: GetLocal) -> Self {
|
fn from(get_local: GetLocal) -> Self {
|
||||||
let operation = Operation::GET_LOCAL;
|
let operation = Operation::GET_LOCAL;
|
||||||
let a = get_local.destination;
|
let a_field = get_local.destination;
|
||||||
let b = get_local.local_index;
|
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}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
use crate::{Instruction, Operation};
|
use crate::{Instruction, Operation};
|
||||||
|
|
||||||
|
use super::InstructionBuilder;
|
||||||
|
|
||||||
pub struct Jump {
|
pub struct Jump {
|
||||||
pub offset: u8,
|
pub offset: u16,
|
||||||
pub is_positive: bool,
|
pub is_positive: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -17,9 +21,27 @@ impl From<Instruction> for Jump {
|
|||||||
impl From<Jump> for Instruction {
|
impl From<Jump> for Instruction {
|
||||||
fn from(jump: Jump) -> Self {
|
fn from(jump: Jump) -> Self {
|
||||||
let operation = Operation::JUMP;
|
let operation = Operation::JUMP;
|
||||||
let b = jump.offset;
|
let b_field = jump.offset;
|
||||||
let c = jump.is_positive as u8;
|
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}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 struct Less {
|
||||||
pub value: bool,
|
pub comparator: bool,
|
||||||
pub left: Argument,
|
pub left: Operand,
|
||||||
pub right: Argument,
|
pub left_type: TypeCode,
|
||||||
|
pub right: Operand,
|
||||||
|
pub right_type: TypeCode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Instruction> for Less {
|
impl From<Instruction> for Less {
|
||||||
fn from(instruction: Instruction) -> Self {
|
fn from(instruction: Instruction) -> Self {
|
||||||
let value = instruction.d_field();
|
let comparator = instruction.d_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();
|
||||||
|
|
||||||
Less { value, left, right }
|
Less {
|
||||||
|
comparator,
|
||||||
|
left,
|
||||||
|
left_type,
|
||||||
|
right,
|
||||||
|
right_type,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Less> for Instruction {
|
impl From<Less> for Instruction {
|
||||||
fn from(less: Less) -> Self {
|
fn from(less: Less) -> Self {
|
||||||
let operation = Operation::LESS;
|
let operation = Operation::LESS;
|
||||||
let (b, b_is_constant) = less.left.as_index_and_constant_flag();
|
let (b_field, b_is_constant) = less.left.as_index_and_constant_flag();
|
||||||
let (c, c_is_constant) = less.right.as_index_and_constant_flag();
|
let (c_field, c_is_constant) = less.right.as_index_and_constant_flag();
|
||||||
let d = less.value;
|
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 }}"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 struct LessEqual {
|
||||||
pub value: bool,
|
pub comparator: bool,
|
||||||
pub left: Argument,
|
pub left: Operand,
|
||||||
pub right: Argument,
|
pub left_type: TypeCode,
|
||||||
|
pub right: Operand,
|
||||||
|
pub right_type: TypeCode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Instruction> for LessEqual {
|
impl From<Instruction> for LessEqual {
|
||||||
fn from(instruction: Instruction) -> Self {
|
fn from(instruction: Instruction) -> Self {
|
||||||
let value = instruction.d_field();
|
let comparator = instruction.d_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();
|
||||||
|
|
||||||
LessEqual { value, left, right }
|
LessEqual {
|
||||||
|
comparator,
|
||||||
|
left,
|
||||||
|
left_type,
|
||||||
|
right,
|
||||||
|
right_type,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<LessEqual> for Instruction {
|
impl From<LessEqual> for Instruction {
|
||||||
fn from(less_equal: LessEqual) -> Self {
|
fn from(less_equal_byte: LessEqual) -> Self {
|
||||||
let operation = Operation::LESS_EQUAL;
|
let operation = Operation::LESS_EQUAL;
|
||||||
let (b, b_options) = less_equal.left.as_index_and_constant_flag();
|
let (b_field, b_is_constant) = less_equal_byte.left.as_index_and_constant_flag();
|
||||||
let (c, c_options) = less_equal.right.as_index_and_constant_flag();
|
let (c_field, c_is_constant) = less_equal_byte.right.as_index_and_constant_flag();
|
||||||
let d = less_equal.value;
|
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 }}"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
use crate::{Instruction, Operation};
|
use crate::{Instruction, Operation};
|
||||||
|
|
||||||
|
use super::InstructionBuilder;
|
||||||
|
|
||||||
pub struct LoadBoolean {
|
pub struct LoadBoolean {
|
||||||
pub destination: u8,
|
pub destination: u16,
|
||||||
pub value: bool,
|
pub value: bool,
|
||||||
pub jump_next: bool,
|
pub jump_next: bool,
|
||||||
}
|
}
|
||||||
@ -19,10 +23,35 @@ impl From<Instruction> for LoadBoolean {
|
|||||||
impl From<LoadBoolean> for Instruction {
|
impl From<LoadBoolean> for Instruction {
|
||||||
fn from(load_boolean: LoadBoolean) -> Self {
|
fn from(load_boolean: LoadBoolean) -> Self {
|
||||||
let operation = Operation::LOAD_BOOLEAN;
|
let operation = Operation::LOAD_BOOLEAN;
|
||||||
let a = load_boolean.destination;
|
let a_field = load_boolean.destination;
|
||||||
let b = load_boolean.value as u8;
|
let b_field = load_boolean.value as u16;
|
||||||
let c = load_boolean.jump_next as u8;
|
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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,11 @@ use std::fmt::{self, Display, Formatter};
|
|||||||
|
|
||||||
use crate::{Instruction, Operation};
|
use crate::{Instruction, Operation};
|
||||||
|
|
||||||
|
use super::InstructionBuilder;
|
||||||
|
|
||||||
pub struct LoadConstant {
|
pub struct LoadConstant {
|
||||||
pub destination: u8,
|
pub destination: u16,
|
||||||
pub constant_index: u8,
|
pub constant_index: u16,
|
||||||
pub jump_next: bool,
|
pub jump_next: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,12 +26,14 @@ impl From<Instruction> for LoadConstant {
|
|||||||
|
|
||||||
impl From<LoadConstant> for Instruction {
|
impl From<LoadConstant> for Instruction {
|
||||||
fn from(load_constant: LoadConstant) -> Self {
|
fn from(load_constant: LoadConstant) -> Self {
|
||||||
let operation = Operation::LOAD_CONSTANT;
|
InstructionBuilder {
|
||||||
let a = load_constant.destination;
|
operation: Operation::LOAD_CONSTANT,
|
||||||
let b = load_constant.constant_index;
|
a_field: load_constant.destination,
|
||||||
let c = load_constant.jump_next as u8;
|
b_field: load_constant.constant_index,
|
||||||
|
c_field: load_constant.jump_next as u16,
|
||||||
Instruction::new(operation, a, b, c, false, false, false)
|
..Default::default()
|
||||||
|
}
|
||||||
|
.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,12 +45,12 @@ impl Display for LoadConstant {
|
|||||||
jump_next,
|
jump_next,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
write!(f, "R{destination} = Constant {constant_index}")?;
|
write!(f, "R{destination} = C{constant_index}")?;
|
||||||
|
|
||||||
if *jump_next {
|
if *jump_next {
|
||||||
write!(f, " JUMP +1")
|
write!(f, " JUMP +1")?;
|
||||||
} else {
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -1,40 +1,54 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
use super::{Instruction, Operation};
|
use super::{Instruction, InstructionBuilder, Operation};
|
||||||
|
|
||||||
pub struct LoadFunction {
|
pub struct LoadFunction {
|
||||||
pub destination: u8,
|
pub destination: u16,
|
||||||
pub prototype_index: u8,
|
pub prototype_index: u16,
|
||||||
|
pub jump_next: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Instruction> for LoadFunction {
|
impl From<Instruction> for LoadFunction {
|
||||||
fn from(instruction: Instruction) -> Self {
|
fn from(instruction: Instruction) -> Self {
|
||||||
let destination = instruction.a_field();
|
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 {
|
LoadFunction {
|
||||||
destination,
|
destination,
|
||||||
prototype_index: record_index,
|
prototype_index,
|
||||||
|
jump_next,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<LoadFunction> for Instruction {
|
impl From<LoadFunction> for Instruction {
|
||||||
fn from(load_function: LoadFunction) -> Self {
|
fn from(load_function: LoadFunction) -> Self {
|
||||||
Instruction::new(
|
InstructionBuilder {
|
||||||
Operation::LOAD_FUNCTION,
|
operation: Operation::LOAD_FUNCTION,
|
||||||
load_function.destination,
|
a_field: load_function.destination,
|
||||||
load_function.prototype_index,
|
b_field: load_function.prototype_index,
|
||||||
0,
|
c_field: load_function.jump_next as u16,
|
||||||
false,
|
..Default::default()
|
||||||
false,
|
}
|
||||||
false,
|
.build()
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for LoadFunction {
|
impl Display for LoadFunction {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,56 @@
|
|||||||
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
use crate::{Instruction, Operation};
|
use crate::{Instruction, Operation};
|
||||||
|
|
||||||
|
use super::InstructionBuilder;
|
||||||
|
|
||||||
pub struct LoadList {
|
pub struct LoadList {
|
||||||
pub destination: u8,
|
pub destination: u16,
|
||||||
pub start_register: u8,
|
pub start_register: u16,
|
||||||
|
pub jump_next: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Instruction> for LoadList {
|
impl From<Instruction> for LoadList {
|
||||||
fn from(instruction: Instruction) -> Self {
|
fn from(instruction: Instruction) -> Self {
|
||||||
let destination = instruction.a_field();
|
let destination = instruction.a_field();
|
||||||
let start_register = instruction.b_field();
|
let start_register = instruction.b_field();
|
||||||
|
let jump_next = instruction.c_field() != 0;
|
||||||
|
|
||||||
LoadList {
|
LoadList {
|
||||||
destination,
|
destination,
|
||||||
start_register,
|
start_register,
|
||||||
|
jump_next,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<LoadList> for Instruction {
|
impl From<LoadList> for Instruction {
|
||||||
fn from(load_list: LoadList) -> Self {
|
fn from(load_list: LoadList) -> Self {
|
||||||
let operation = Operation::LOAD_LIST;
|
InstructionBuilder {
|
||||||
let a = load_list.destination;
|
operation: Operation::LOAD_LIST,
|
||||||
let b = load_list.start_register;
|
a_field: load_list.destination,
|
||||||
|
b_field: load_list.start_register,
|
||||||
|
c_field: load_list.jump_next as u16,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Instruction::new(operation, a, b, 0, false, false, false)
|
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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,51 @@
|
|||||||
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
use crate::{Instruction, Operation};
|
use crate::{Instruction, Operation};
|
||||||
|
|
||||||
|
use super::InstructionBuilder;
|
||||||
|
|
||||||
pub struct LoadSelf {
|
pub struct LoadSelf {
|
||||||
pub destination: u8,
|
pub destination: u16,
|
||||||
|
pub jump_next: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Instruction> for LoadSelf {
|
impl From<Instruction> for LoadSelf {
|
||||||
fn from(instruction: Instruction) -> Self {
|
fn from(instruction: Instruction) -> Self {
|
||||||
let destination = instruction.a_field();
|
let destination = instruction.a_field();
|
||||||
|
let jump_next = instruction.c_field() != 0;
|
||||||
|
|
||||||
LoadSelf { destination }
|
LoadSelf {
|
||||||
|
destination,
|
||||||
|
jump_next,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<LoadSelf> for Instruction {
|
impl From<LoadSelf> for Instruction {
|
||||||
fn from(load_self: LoadSelf) -> Self {
|
fn from(load_self: LoadSelf) -> Self {
|
||||||
let operation = Operation::LOAD_SELF;
|
InstructionBuilder {
|
||||||
let a = load_self.destination;
|
operation: Operation::LOAD_SELF,
|
||||||
|
a_field: load_self.destination,
|
||||||
|
c_field: load_self.jump_next as u16,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Instruction::new(operation, a, 0, 0, false, false, false)
|
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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -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 struct Modulo {
|
||||||
pub destination: u8,
|
pub destination: u16,
|
||||||
pub left: Argument,
|
pub left: Operand,
|
||||||
pub right: Argument,
|
pub left_type: TypeCode,
|
||||||
|
pub right: Operand,
|
||||||
|
pub right_type: TypeCode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Instruction> for Modulo {
|
impl From<Instruction> for Modulo {
|
||||||
fn from(instruction: Instruction) -> Self {
|
fn from(instruction: Instruction) -> Self {
|
||||||
let destination = instruction.a_field();
|
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 {
|
Modulo {
|
||||||
destination,
|
destination,
|
||||||
left,
|
left,
|
||||||
|
left_type,
|
||||||
right,
|
right,
|
||||||
|
right_type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -22,10 +30,40 @@ impl From<Instruction> for Modulo {
|
|||||||
impl From<Modulo> for Instruction {
|
impl From<Modulo> for Instruction {
|
||||||
fn from(modulo: Modulo) -> Self {
|
fn from(modulo: Modulo) -> Self {
|
||||||
let operation = Operation::MODULO;
|
let operation = Operation::MODULO;
|
||||||
let a = modulo.destination;
|
let a_field = modulo.destination;
|
||||||
let (b, b_is_constant) = modulo.left.as_index_and_constant_flag();
|
let (b_field, b_is_constant) = modulo.left.as_index_and_constant_flag();
|
||||||
let (c, c_is_constant) = modulo.right.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})",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 struct Multiply {
|
||||||
pub destination: u8,
|
pub destination: u16,
|
||||||
pub left: Argument,
|
pub left: Operand,
|
||||||
pub right: Argument,
|
pub left_type: TypeCode,
|
||||||
|
pub right: Operand,
|
||||||
|
pub right_type: TypeCode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Instruction> for Multiply {
|
impl From<Instruction> for Multiply {
|
||||||
fn from(instruction: Instruction) -> Self {
|
fn from(instruction: Instruction) -> Self {
|
||||||
let destination = instruction.a_field();
|
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 {
|
Multiply {
|
||||||
destination,
|
destination,
|
||||||
left,
|
left,
|
||||||
|
left_type,
|
||||||
right,
|
right,
|
||||||
|
right_type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -22,10 +30,40 @@ impl From<Instruction> for Multiply {
|
|||||||
impl From<Multiply> for Instruction {
|
impl From<Multiply> for Instruction {
|
||||||
fn from(multiply: Multiply) -> Self {
|
fn from(multiply: Multiply) -> Self {
|
||||||
let operation = Operation::MULTIPLY;
|
let operation = Operation::MULTIPLY;
|
||||||
let a = multiply.destination;
|
let a_field = multiply.destination;
|
||||||
let (b, b_options) = multiply.left.as_index_and_constant_flag();
|
let (b_field, b_is_constant) = multiply.left.as_index_and_constant_flag();
|
||||||
let (c, c_options) = multiply.right.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})",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 struct Negate {
|
||||||
pub destination: u8,
|
pub destination: u16,
|
||||||
pub argument: Argument,
|
pub argument: Operand,
|
||||||
|
pub argument_type: TypeCode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Instruction> for Negate {
|
impl From<Instruction> for Negate {
|
||||||
fn from(instruction: Instruction) -> Self {
|
fn from(instruction: Instruction) -> Self {
|
||||||
let destination = instruction.a_field();
|
let destination = instruction.a_field();
|
||||||
let argument = instruction.b_as_argument();
|
let argument = instruction.b_as_argument();
|
||||||
|
let argument_type = instruction.b_type();
|
||||||
|
|
||||||
Negate {
|
Negate {
|
||||||
destination,
|
destination,
|
||||||
argument,
|
argument,
|
||||||
|
argument_type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -20,10 +25,30 @@ impl From<Instruction> for Negate {
|
|||||||
impl From<Negate> for Instruction {
|
impl From<Negate> for Instruction {
|
||||||
fn from(negate: Negate) -> Self {
|
fn from(negate: Negate) -> Self {
|
||||||
let operation = Operation::NEGATE;
|
let operation = Operation::NEGATE;
|
||||||
let a = negate.destination;
|
let a_field = negate.destination;
|
||||||
let (b, b_is_constant) = negate.argument.as_index_and_constant_flag();
|
let (b_field, b_is_constant) = negate.argument.as_index_and_constant_flag();
|
||||||
let c = 0;
|
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})")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 struct Not {
|
||||||
pub destination: u8,
|
pub destination: u16,
|
||||||
pub argument: Argument,
|
pub argument: Operand,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Instruction> for Not {
|
impl From<Instruction> for Not {
|
||||||
@ -20,9 +24,27 @@ impl From<Instruction> for Not {
|
|||||||
impl From<Not> for Instruction {
|
impl From<Not> for Instruction {
|
||||||
fn from(not: Not) -> Self {
|
fn from(not: Not) -> Self {
|
||||||
let operation = Operation::NOT;
|
let operation = Operation::NOT;
|
||||||
let a = not.destination;
|
let a_field = not.destination;
|
||||||
let (b, b_is_constant) = not.argument.as_index_and_constant_flag();
|
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}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,29 +9,46 @@ use serde::{Deserialize, Serialize};
|
|||||||
pub struct Operation(pub u8);
|
pub struct Operation(pub u8);
|
||||||
|
|
||||||
impl Operation {
|
impl Operation {
|
||||||
|
// Stack manipulation
|
||||||
pub const POINT: Operation = Operation(0);
|
pub const POINT: Operation = Operation(0);
|
||||||
pub const CLOSE: Operation = Operation(1);
|
pub const CLOSE: Operation = Operation(1);
|
||||||
|
|
||||||
|
// Loaders
|
||||||
pub const LOAD_BOOLEAN: Operation = Operation(2);
|
pub const LOAD_BOOLEAN: Operation = Operation(2);
|
||||||
pub const LOAD_CONSTANT: Operation = Operation(3);
|
pub const LOAD_CONSTANT: Operation = Operation(3);
|
||||||
pub const LOAD_FUNCTION: Operation = Operation(4);
|
pub const LOAD_FUNCTION: Operation = Operation(4);
|
||||||
pub const LOAD_LIST: Operation = Operation(5);
|
pub const LOAD_LIST: Operation = Operation(5);
|
||||||
pub const LOAD_SELF: Operation = Operation(6);
|
pub const LOAD_SELF: Operation = Operation(6);
|
||||||
|
|
||||||
|
// Locals
|
||||||
pub const GET_LOCAL: Operation = Operation(7);
|
pub const GET_LOCAL: Operation = Operation(7);
|
||||||
pub const SET_LOCAL: Operation = Operation(8);
|
pub const SET_LOCAL: Operation = Operation(8);
|
||||||
|
|
||||||
|
// Arithmetic
|
||||||
pub const ADD: Operation = Operation(9);
|
pub const ADD: Operation = Operation(9);
|
||||||
pub const SUBTRACT: Operation = Operation(10);
|
pub const SUBTRACT: Operation = Operation(10);
|
||||||
pub const MULTIPLY: Operation = Operation(11);
|
pub const MULTIPLY: Operation = Operation(11);
|
||||||
pub const DIVIDE: Operation = Operation(12);
|
pub const DIVIDE: Operation = Operation(12);
|
||||||
pub const MODULO: Operation = Operation(13);
|
pub const MODULO: Operation = Operation(13);
|
||||||
pub const TEST: Operation = Operation(14);
|
|
||||||
pub const TEST_SET: Operation = Operation(15);
|
// Comparison
|
||||||
pub const EQUAL: Operation = Operation(16);
|
pub const EQUAL: Operation = Operation(14);
|
||||||
pub const LESS: Operation = Operation(17);
|
pub const LESS: Operation = Operation(15);
|
||||||
pub const LESS_EQUAL: Operation = Operation(18);
|
pub const LESS_EQUAL: Operation = Operation(16);
|
||||||
pub const NEGATE: Operation = Operation(19);
|
|
||||||
pub const NOT: Operation = Operation(20);
|
// 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: Operation = Operation(21);
|
||||||
pub const CALL_NATIVE: Operation = Operation(22);
|
pub const CALL_NATIVE: Operation = Operation(22);
|
||||||
|
|
||||||
|
// Control flow
|
||||||
pub const JUMP: Operation = Operation(23);
|
pub const JUMP: Operation = Operation(23);
|
||||||
pub const RETURN: Operation = Operation(24);
|
pub const RETURN: Operation = Operation(24);
|
||||||
}
|
}
|
||||||
@ -53,23 +70,41 @@ impl Operation {
|
|||||||
Self::MULTIPLY => "MULTIPLY",
|
Self::MULTIPLY => "MULTIPLY",
|
||||||
Self::DIVIDE => "DIVIDE",
|
Self::DIVIDE => "DIVIDE",
|
||||||
Self::MODULO => "MODULO",
|
Self::MODULO => "MODULO",
|
||||||
Self::TEST => "TEST",
|
|
||||||
Self::TEST_SET => "TEST_SET",
|
|
||||||
Self::EQUAL => "EQUAL",
|
Self::EQUAL => "EQUAL",
|
||||||
Self::LESS => "LESS",
|
Self::LESS => "LESS",
|
||||||
Self::LESS_EQUAL => "LESS_EQUAL",
|
Self::LESS_EQUAL => "LESS_EQUAL",
|
||||||
Self::NEGATE => "NEGATE",
|
Self::NEGATE => "NEGATE",
|
||||||
Self::NOT => "NOT",
|
Self::NOT => "NOT",
|
||||||
|
Self::TEST => "TEST",
|
||||||
|
Self::TEST_SET => "TEST_SET",
|
||||||
Self::CALL => "CALL",
|
Self::CALL => "CALL",
|
||||||
Self::CALL_NATIVE => "CALL_NATIVE",
|
Self::CALL_NATIVE => "CALL_NATIVE",
|
||||||
Self::JUMP => "JUMP",
|
Self::JUMP => "JUMP",
|
||||||
Self::RETURN => "RETURN",
|
Self::RETURN => "RETURN",
|
||||||
_ => Self::panic_from_unknown_code(self.0),
|
_ => self.panic_from_unknown_code(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn panic_from_unknown_code(code: u8) -> ! {
|
pub fn is_math(self) -> bool {
|
||||||
panic!("Unknown operation code: {code}");
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,9 +2,11 @@ use std::fmt::{self, Display, Formatter};
|
|||||||
|
|
||||||
use crate::{Instruction, Operation};
|
use crate::{Instruction, Operation};
|
||||||
|
|
||||||
|
use super::InstructionBuilder;
|
||||||
|
|
||||||
pub struct Point {
|
pub struct Point {
|
||||||
pub from: u8,
|
pub from: u16,
|
||||||
pub to: u8,
|
pub to: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Instruction> for Point {
|
impl From<Instruction> for Point {
|
||||||
@ -16,6 +18,22 @@ impl From<Instruction> for Point {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Point> 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 {
|
impl Display for Point {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
let Point { from, to } = self;
|
let Point { from, to } = self;
|
||||||
@ -23,13 +41,3 @@ impl Display for Point {
|
|||||||
write!(f, "{from} -> {to}")
|
write!(f, "{from} -> {to}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Point> 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
use crate::{Instruction, Operation};
|
use crate::{Instruction, Operation};
|
||||||
|
|
||||||
|
use super::InstructionBuilder;
|
||||||
|
|
||||||
pub struct Return {
|
pub struct Return {
|
||||||
pub should_return_value: bool,
|
pub should_return_value: bool,
|
||||||
pub return_register: u8,
|
pub return_register: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Instruction> for Return {
|
impl From<Instruction> for Return {
|
||||||
@ -20,9 +24,30 @@ impl From<Instruction> for Return {
|
|||||||
impl From<Return> for Instruction {
|
impl From<Return> for Instruction {
|
||||||
fn from(r#return: Return) -> Self {
|
fn from(r#return: Return) -> Self {
|
||||||
let operation = Operation::RETURN;
|
let operation = Operation::RETURN;
|
||||||
let b = r#return.should_return_value as u8;
|
let b_field = r#return.should_return_value as u16;
|
||||||
let c = r#return.return_register;
|
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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
use crate::{Instruction, Operation};
|
use crate::{Instruction, Operation};
|
||||||
|
|
||||||
|
use super::InstructionBuilder;
|
||||||
|
|
||||||
pub struct SetLocal {
|
pub struct SetLocal {
|
||||||
pub register_index: u8,
|
pub register_index: u16,
|
||||||
pub local_index: u8,
|
pub local_index: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Instruction> for SetLocal {
|
impl From<Instruction> for SetLocal {
|
||||||
@ -20,9 +24,26 @@ impl From<Instruction> for SetLocal {
|
|||||||
impl From<SetLocal> for Instruction {
|
impl From<SetLocal> for Instruction {
|
||||||
fn from(set_local: SetLocal) -> Self {
|
fn from(set_local: SetLocal) -> Self {
|
||||||
let operation = Operation::SET_LOCAL;
|
let operation = Operation::SET_LOCAL;
|
||||||
let b = set_local.register_index;
|
let b_field = set_local.register_index;
|
||||||
let c = set_local.local_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}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 struct Subtract {
|
||||||
pub destination: u8,
|
pub destination: u16,
|
||||||
pub left: Argument,
|
pub left: Operand,
|
||||||
pub right: Argument,
|
pub left_type: TypeCode,
|
||||||
|
pub right: Operand,
|
||||||
|
pub right_type: TypeCode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Instruction> for Subtract {
|
impl From<Instruction> for Subtract {
|
||||||
fn from(instruction: Instruction) -> Self {
|
fn from(instruction: Instruction) -> Self {
|
||||||
let destination = instruction.a_field();
|
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 {
|
Subtract {
|
||||||
destination,
|
destination,
|
||||||
left,
|
left,
|
||||||
|
left_type,
|
||||||
right,
|
right,
|
||||||
|
right_type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -22,10 +30,40 @@ impl From<Instruction> for Subtract {
|
|||||||
impl From<Subtract> for Instruction {
|
impl From<Subtract> for Instruction {
|
||||||
fn from(subtract: Subtract) -> Self {
|
fn from(subtract: Subtract) -> Self {
|
||||||
let operation = Operation::SUBTRACT;
|
let operation = Operation::SUBTRACT;
|
||||||
let a = subtract.destination;
|
let a_field = subtract.destination;
|
||||||
let (b, b_is_constant) = subtract.left.as_index_and_constant_flag();
|
let (b_field, b_is_constant) = subtract.left.as_index_and_constant_flag();
|
||||||
let (c, c_is_constant) = subtract.right.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})",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
use crate::{Instruction, Operation};
|
use crate::{Instruction, Operation};
|
||||||
|
|
||||||
|
use super::InstructionBuilder;
|
||||||
|
|
||||||
pub struct Test {
|
pub struct Test {
|
||||||
pub operand_register: u8,
|
pub operand_register: u16,
|
||||||
pub test_value: bool,
|
pub test_value: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,14 +23,27 @@ impl From<Instruction> for Test {
|
|||||||
|
|
||||||
impl From<Test> for Instruction {
|
impl From<Test> for Instruction {
|
||||||
fn from(test: Test) -> Self {
|
fn from(test: Test) -> Self {
|
||||||
Instruction::new(
|
let b_field = test.operand_register;
|
||||||
Operation::TEST,
|
let c_field = test.test_value as u16;
|
||||||
0,
|
|
||||||
test.operand_register,
|
InstructionBuilder {
|
||||||
test.test_value as u8,
|
operation: Operation::TEST,
|
||||||
false,
|
b_field,
|
||||||
false,
|
c_field,
|
||||||
false,
|
..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 }}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 struct TestSet {
|
||||||
pub destination: u8,
|
pub destination: u16,
|
||||||
pub argument: Argument,
|
pub argument: Operand,
|
||||||
pub test_value: bool,
|
pub test_value: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,10 +27,34 @@ impl From<Instruction> for TestSet {
|
|||||||
impl From<TestSet> for Instruction {
|
impl From<TestSet> for Instruction {
|
||||||
fn from(test_set: TestSet) -> Self {
|
fn from(test_set: TestSet) -> Self {
|
||||||
let operation = Operation::TEST;
|
let operation = Operation::TEST;
|
||||||
let a = test_set.destination;
|
let a_field = test_set.destination;
|
||||||
let (b, b_is_constant) = test_set.argument.as_index_and_constant_flag();
|
let (b_field, b_is_constant) = test_set.argument.as_index_and_constant_flag();
|
||||||
let c = test_set.test_value as u8;
|
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} }}"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
31
dust-lang/src/instruction/type_code.rs
Normal file
31
dust-lang/src/instruction/type_code.rs
Normal file
@ -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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@
|
|||||||
//! - [`Lexer`], which lexes the input a token at a time
|
//! - [`Lexer`], which lexes the input a token at a time
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{dust_error::AnnotatedError, CompileError, DustError, Span, Token};
|
use crate::{CompileError, DustError, Span, Token, dust_error::AnnotatedError};
|
||||||
|
|
||||||
/// Lexes the input and returns a vector of tokens and their positions.
|
/// Lexes the input and returns a vector of tokens and their positions.
|
||||||
///
|
///
|
||||||
@ -83,7 +83,7 @@ impl<'src> Lexer<'src> {
|
|||||||
self.skip_whitespace();
|
self.skip_whitespace();
|
||||||
|
|
||||||
let (token, span) = if let Some(character) = self.peek_char() {
|
let (token, span) = if let Some(character) = self.peek_char() {
|
||||||
let lexer = LexRule::from(&character).lexer;
|
let lexer = LexRule::from(&character).lex_action;
|
||||||
|
|
||||||
lexer(self)?
|
lexer(self)?
|
||||||
} else {
|
} else {
|
||||||
@ -613,92 +613,92 @@ impl<'src> Lexer<'src> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type LexerFn<'src> = fn(&mut Lexer<'src>) -> Result<(Token<'src>, Span), LexError>;
|
type LexAction<'src> = fn(&mut Lexer<'src>) -> Result<(Token<'src>, Span), LexError>;
|
||||||
|
|
||||||
pub struct LexRule<'src> {
|
pub struct LexRule<'src> {
|
||||||
lexer: LexerFn<'src>,
|
lex_action: LexAction<'src>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&char> for LexRule<'_> {
|
impl From<&char> for LexRule<'_> {
|
||||||
fn from(char: &char) -> Self {
|
fn from(char: &char) -> Self {
|
||||||
match char {
|
match char {
|
||||||
'0'..='9' => LexRule {
|
'0'..='9' => LexRule {
|
||||||
lexer: Lexer::lex_numeric,
|
lex_action: Lexer::lex_numeric,
|
||||||
},
|
},
|
||||||
char if char.is_alphabetic() => LexRule {
|
char if char.is_alphabetic() => LexRule {
|
||||||
lexer: Lexer::lex_keyword_or_identifier,
|
lex_action: Lexer::lex_keyword_or_identifier,
|
||||||
},
|
},
|
||||||
'"' => LexRule {
|
'"' => LexRule {
|
||||||
lexer: Lexer::lex_string,
|
lex_action: Lexer::lex_string,
|
||||||
},
|
},
|
||||||
'\'' => LexRule {
|
'\'' => LexRule {
|
||||||
lexer: Lexer::lex_char,
|
lex_action: Lexer::lex_char,
|
||||||
},
|
},
|
||||||
'+' => LexRule {
|
'+' => LexRule {
|
||||||
lexer: Lexer::lex_plus,
|
lex_action: Lexer::lex_plus,
|
||||||
},
|
},
|
||||||
'-' => LexRule {
|
'-' => LexRule {
|
||||||
lexer: Lexer::lex_minus,
|
lex_action: Lexer::lex_minus,
|
||||||
},
|
},
|
||||||
'*' => LexRule {
|
'*' => LexRule {
|
||||||
lexer: Lexer::lex_star,
|
lex_action: Lexer::lex_star,
|
||||||
},
|
},
|
||||||
'/' => LexRule {
|
'/' => LexRule {
|
||||||
lexer: Lexer::lex_slash,
|
lex_action: Lexer::lex_slash,
|
||||||
},
|
},
|
||||||
'%' => LexRule {
|
'%' => LexRule {
|
||||||
lexer: Lexer::lex_percent,
|
lex_action: Lexer::lex_percent,
|
||||||
},
|
},
|
||||||
'!' => LexRule {
|
'!' => LexRule {
|
||||||
lexer: Lexer::lex_exclamation_mark,
|
lex_action: Lexer::lex_exclamation_mark,
|
||||||
},
|
},
|
||||||
'=' => LexRule {
|
'=' => LexRule {
|
||||||
lexer: Lexer::lex_equal,
|
lex_action: Lexer::lex_equal,
|
||||||
},
|
},
|
||||||
'<' => LexRule {
|
'<' => LexRule {
|
||||||
lexer: Lexer::lex_less_than,
|
lex_action: Lexer::lex_less_than,
|
||||||
},
|
},
|
||||||
'>' => LexRule {
|
'>' => LexRule {
|
||||||
lexer: Lexer::lex_greater_than,
|
lex_action: Lexer::lex_greater_than,
|
||||||
},
|
},
|
||||||
'&' => LexRule {
|
'&' => LexRule {
|
||||||
lexer: Lexer::lex_ampersand,
|
lex_action: Lexer::lex_ampersand,
|
||||||
},
|
},
|
||||||
'|' => LexRule {
|
'|' => LexRule {
|
||||||
lexer: Lexer::lex_pipe,
|
lex_action: Lexer::lex_pipe,
|
||||||
},
|
},
|
||||||
'(' => LexRule {
|
'(' => LexRule {
|
||||||
lexer: Lexer::lex_left_parenthesis,
|
lex_action: Lexer::lex_left_parenthesis,
|
||||||
},
|
},
|
||||||
')' => LexRule {
|
')' => LexRule {
|
||||||
lexer: Lexer::lex_right_parenthesis,
|
lex_action: Lexer::lex_right_parenthesis,
|
||||||
},
|
},
|
||||||
'[' => LexRule {
|
'[' => LexRule {
|
||||||
lexer: Lexer::lex_left_bracket,
|
lex_action: Lexer::lex_left_bracket,
|
||||||
},
|
},
|
||||||
']' => LexRule {
|
']' => LexRule {
|
||||||
lexer: Lexer::lex_right_bracket,
|
lex_action: Lexer::lex_right_bracket,
|
||||||
},
|
},
|
||||||
'{' => LexRule {
|
'{' => LexRule {
|
||||||
lexer: Lexer::lex_left_brace,
|
lex_action: Lexer::lex_left_brace,
|
||||||
},
|
},
|
||||||
'}' => LexRule {
|
'}' => LexRule {
|
||||||
lexer: Lexer::lex_right_brace,
|
lex_action: Lexer::lex_right_brace,
|
||||||
},
|
},
|
||||||
';' => LexRule {
|
';' => LexRule {
|
||||||
lexer: Lexer::lex_semicolon,
|
lex_action: Lexer::lex_semicolon,
|
||||||
},
|
},
|
||||||
':' => LexRule {
|
':' => LexRule {
|
||||||
lexer: Lexer::lex_colon,
|
lex_action: Lexer::lex_colon,
|
||||||
},
|
},
|
||||||
',' => LexRule {
|
',' => LexRule {
|
||||||
lexer: Lexer::lex_comma,
|
lex_action: Lexer::lex_comma,
|
||||||
},
|
},
|
||||||
'.' => LexRule {
|
'.' => LexRule {
|
||||||
lexer: Lexer::lex_dot,
|
lex_action: Lexer::lex_dot,
|
||||||
},
|
},
|
||||||
_ => LexRule {
|
_ => LexRule {
|
||||||
lexer: Lexer::lex_unexpected,
|
lex_action: Lexer::lex_unexpected,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,17 +40,17 @@ pub mod value;
|
|||||||
pub mod vm;
|
pub mod vm;
|
||||||
|
|
||||||
pub use crate::chunk::{Chunk, Disassembler, Local, Scope};
|
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::dust_error::{AnnotatedError, DustError};
|
||||||
pub use crate::instruction::{Argument, Instruction, InstructionData, Operation};
|
pub use crate::instruction::{Operand, Instruction, Operation};
|
||||||
pub use crate::lexer::{lex, LexError, Lexer};
|
pub use crate::lexer::{LexError, Lexer, lex};
|
||||||
pub use crate::native_function::{NativeFunction, NativeFunctionError};
|
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::token::{Token, TokenKind, TokenOwned};
|
||||||
|
pub use crate::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict};
|
||||||
pub use crate::value::{
|
pub use crate::value::{
|
||||||
AbstractList, ConcreteValue, DustString, Function, RangeValue, Value, ValueError,
|
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;
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ use std::{ops::Range, panic};
|
|||||||
|
|
||||||
use crate::vm::ThreadData;
|
use crate::vm::ThreadData;
|
||||||
|
|
||||||
pub fn panic(data: &mut ThreadData, _: Option<u8>, argument_range: Range<u8>) -> bool {
|
pub fn panic(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
|
||||||
let position = data.current_position();
|
let position = data.current_position();
|
||||||
let mut message = format!("Dust panic at {position}!");
|
let mut message = format!("Dust panic at {position}!");
|
||||||
|
|
||||||
|
@ -1,17 +1,12 @@
|
|||||||
use std::io::{stdin, stdout, Write};
|
use std::io::{Write, stdin, stdout};
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
vm::{get_next_action, Register, ThreadData},
|
|
||||||
ConcreteValue, Value,
|
ConcreteValue, Value,
|
||||||
|
vm::{Register, ThreadData, get_next_action},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn read_line(
|
pub fn read_line(data: &mut ThreadData, destination: u16, _argument_range: Range<u16>) -> bool {
|
||||||
data: &mut ThreadData,
|
|
||||||
destination: Option<u8>,
|
|
||||||
_argument_range: Range<u8>,
|
|
||||||
) -> bool {
|
|
||||||
let destination = destination.unwrap();
|
|
||||||
let mut buffer = String::new();
|
let mut buffer = String::new();
|
||||||
|
|
||||||
if stdin().read_line(&mut buffer).is_ok() {
|
if stdin().read_line(&mut buffer).is_ok() {
|
||||||
@ -29,7 +24,7 @@ pub fn read_line(
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(data: &mut ThreadData, _destination: Option<u8>, argument_range: Range<u8>) -> bool {
|
pub fn write(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
|
||||||
let mut stdout = stdout();
|
let mut stdout = stdout();
|
||||||
|
|
||||||
for register_index in argument_range {
|
for register_index in argument_range {
|
||||||
@ -45,11 +40,7 @@ pub fn write(data: &mut ThreadData, _destination: Option<u8>, argument_range: Ra
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_line(
|
pub fn write_line(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
|
||||||
data: &mut ThreadData,
|
|
||||||
_destination: Option<u8>,
|
|
||||||
argument_range: Range<u8>,
|
|
||||||
) -> bool {
|
|
||||||
let mut stdout = stdout().lock();
|
let mut stdout = stdout().lock();
|
||||||
|
|
||||||
for register_index in argument_range {
|
for register_index in argument_range {
|
||||||
|
@ -4,7 +4,9 @@
|
|||||||
//! itself or that are more efficient to implement in Rust.
|
//! itself or that are more efficient to implement in Rust.
|
||||||
mod assert;
|
mod assert;
|
||||||
mod io;
|
mod io;
|
||||||
|
mod random;
|
||||||
mod string;
|
mod string;
|
||||||
|
mod thread;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fmt::{self, Display, Formatter},
|
fmt::{self, Display, Formatter},
|
||||||
@ -15,7 +17,7 @@ use std::{
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{vm::ThreadData, AnnotatedError, FunctionType, Span, Type};
|
use crate::{AnnotatedError, FunctionType, Span, Type, vm::ThreadData};
|
||||||
|
|
||||||
macro_rules! define_native_function {
|
macro_rules! define_native_function {
|
||||||
($(($name:ident, $bytes:literal, $str:expr, $type:expr, $function:expr)),*) => {
|
($(($name:ident, $bytes:literal, $str:expr, $type:expr, $function:expr)),*) => {
|
||||||
@ -33,8 +35,8 @@ macro_rules! define_native_function {
|
|||||||
pub fn call(
|
pub fn call(
|
||||||
&self,
|
&self,
|
||||||
data: &mut ThreadData,
|
data: &mut ThreadData,
|
||||||
destination: Option<u8>,
|
destination: u16,
|
||||||
argument_range: Range<u8>,
|
argument_range: Range<u16>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match self {
|
match self {
|
||||||
$(
|
$(
|
||||||
@ -78,8 +80,8 @@ macro_rules! define_native_function {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<u8> for NativeFunction {
|
impl From<u16> for NativeFunction {
|
||||||
fn from(bytes: u8) -> Self {
|
fn from(bytes: u16) -> Self {
|
||||||
match bytes {
|
match bytes {
|
||||||
$(
|
$(
|
||||||
$bytes => NativeFunction::$name,
|
$bytes => NativeFunction::$name,
|
||||||
@ -244,11 +246,42 @@ define_native_function! {
|
|||||||
return_type: Type::None
|
return_type: Type::None
|
||||||
},
|
},
|
||||||
io::write_line
|
io::write_line
|
||||||
)
|
),
|
||||||
|
|
||||||
// // Random
|
// // Random
|
||||||
// (Random, 58_u8, "random", true),
|
(
|
||||||
// (RandomInRange, 59_u8, "random_in_range", true)
|
RandomInteger,
|
||||||
|
58,
|
||||||
|
"random_int",
|
||||||
|
FunctionType {
|
||||||
|
type_parameters: Vec::with_capacity(0),
|
||||||
|
value_parameters: vec![(0, Type::Integer), (1, Type::Integer)],
|
||||||
|
return_type: Type::Integer
|
||||||
|
},
|
||||||
|
random::random_int
|
||||||
|
),
|
||||||
|
|
||||||
|
// Thread
|
||||||
|
(
|
||||||
|
Spawn,
|
||||||
|
60,
|
||||||
|
"spawn",
|
||||||
|
FunctionType {
|
||||||
|
type_parameters: Vec::with_capacity(0),
|
||||||
|
value_parameters: vec![
|
||||||
|
(
|
||||||
|
0,
|
||||||
|
Type::Function(Box::new(FunctionType {
|
||||||
|
type_parameters: Vec::with_capacity(0),
|
||||||
|
value_parameters: Vec::with_capacity(0),
|
||||||
|
return_type: Type::Any
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
],
|
||||||
|
return_type: Type::None
|
||||||
|
},
|
||||||
|
thread::spawn
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
40
dust-lang/src/native_function/random.rs
Normal file
40
dust-lang/src/native_function/random.rs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
use std::ops::Range;
|
||||||
|
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
Value,
|
||||||
|
vm::{Register, ThreadData, get_next_action},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn random_int(data: &mut ThreadData, destination: u16, argument_range: Range<u16>) -> bool {
|
||||||
|
let mut argument_range_iter = argument_range.into_iter();
|
||||||
|
let (min, max) = {
|
||||||
|
let mut min = None;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let register_index = argument_range_iter
|
||||||
|
.next()
|
||||||
|
.unwrap_or_else(|| panic!("No argument was passed to \"random_int\""));
|
||||||
|
let value_option = data.open_register_allow_empty_unchecked(register_index);
|
||||||
|
|
||||||
|
if let Some(argument) = value_option {
|
||||||
|
if let Some(integer) = argument.as_integer() {
|
||||||
|
if min.is_none() {
|
||||||
|
min = Some(integer);
|
||||||
|
} else {
|
||||||
|
break (min, integer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let random_integer = rand::thread_rng().gen_range(min.unwrap()..max);
|
||||||
|
|
||||||
|
data.set_register(destination, Register::Value(Value::integer(random_integer)));
|
||||||
|
|
||||||
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
@ -1,18 +1,13 @@
|
|||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
vm::{get_next_action, Register, ThreadData},
|
|
||||||
ConcreteValue, Value,
|
ConcreteValue, Value,
|
||||||
|
vm::{Register, ThreadData, get_next_action},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn to_string(
|
pub fn to_string(data: &mut ThreadData, destination: u16, argument_range: Range<u16>) -> bool {
|
||||||
data: &mut ThreadData,
|
|
||||||
destination: Option<u8>,
|
|
||||||
argument_range: Range<u8>,
|
|
||||||
) -> bool {
|
|
||||||
let argument_value = data.open_register_unchecked(argument_range.start);
|
let argument_value = data.open_register_unchecked(argument_range.start);
|
||||||
let argument_string = argument_value.display(data);
|
let argument_string = argument_value.display(data);
|
||||||
let destination = destination.unwrap();
|
|
||||||
let register = Register::Value(Value::Concrete(ConcreteValue::string(argument_string)));
|
let register = Register::Value(Value::Concrete(ConcreteValue::string(argument_string)));
|
||||||
|
|
||||||
data.set_register(destination, register);
|
data.set_register(destination, register);
|
||||||
|
64
dust-lang/src/native_function/thread.rs
Normal file
64
dust-lang/src/native_function/thread.rs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
use std::{
|
||||||
|
ops::Range,
|
||||||
|
thread::{Builder, JoinHandle},
|
||||||
|
};
|
||||||
|
|
||||||
|
use tracing::{Level, info, span};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
DustString,
|
||||||
|
vm::{Thread, ThreadData, get_next_action},
|
||||||
|
};
|
||||||
|
|
||||||
|
fn start_thread(data: &mut ThreadData, argument_range: Range<u16>) -> 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<u16>) -> bool {
|
||||||
|
let _ = start_thread(data, argument_range);
|
||||||
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
@ -273,13 +273,13 @@ impl Ord for Type {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub struct FunctionType {
|
pub struct FunctionType {
|
||||||
pub type_parameters: Vec<u8>,
|
pub type_parameters: Vec<u16>,
|
||||||
pub value_parameters: Vec<(u8, Type)>,
|
pub value_parameters: Vec<(u16, Type)>,
|
||||||
pub return_type: Type,
|
pub return_type: Type,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FunctionType {
|
impl FunctionType {
|
||||||
pub fn new<T: Into<Vec<u8>>, U: Into<Vec<(u8, Type)>>>(
|
pub fn new<T: Into<Vec<u16>>, U: Into<Vec<(u16, Type)>>>(
|
||||||
type_parameters: T,
|
type_parameters: T,
|
||||||
value_parameters: U,
|
value_parameters: U,
|
||||||
return_type: Type,
|
return_type: Type,
|
||||||
|
@ -8,7 +8,7 @@ use super::DustString;
|
|||||||
pub struct Function {
|
pub struct Function {
|
||||||
pub name: Option<DustString>,
|
pub name: Option<DustString>,
|
||||||
pub r#type: FunctionType,
|
pub r#type: FunctionType,
|
||||||
pub prototype_index: u8,
|
pub prototype_index: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Function {
|
impl Display for Function {
|
||||||
|
@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
use std::fmt::{self, Debug, Display, Formatter};
|
use std::fmt::{self, Debug, Display, Formatter};
|
||||||
|
|
||||||
use crate::{vm::ThreadData, Type};
|
use crate::{Type, vm::ThreadData};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
@ -50,9 +50,33 @@ impl Value {
|
|||||||
Value::Concrete(ConcreteValue::String(string.into()))
|
Value::Concrete(ConcreteValue::String(string.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_boolean(&self) -> Option<&bool> {
|
pub fn as_boolean(&self) -> Option<bool> {
|
||||||
if let Value::Concrete(ConcreteValue::Boolean(value)) = self {
|
if let Value::Concrete(ConcreteValue::Boolean(boolean)) = self {
|
||||||
Some(value)
|
Some(*boolean)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_byte(&self) -> Option<u8> {
|
||||||
|
if let Value::Concrete(ConcreteValue::Byte(byte)) = self {
|
||||||
|
Some(*byte)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_character(&self) -> Option<char> {
|
||||||
|
if let Value::Concrete(ConcreteValue::Character(character)) = self {
|
||||||
|
Some(*character)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_float(&self) -> Option<f64> {
|
||||||
|
if let Value::Concrete(ConcreteValue::Float(float)) = self {
|
||||||
|
Some(*float)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -66,6 +90,14 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_integer(&self) -> Option<i64> {
|
||||||
|
if let Value::Concrete(ConcreteValue::Integer(integer)) = self {
|
||||||
|
Some(*integer)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn as_string(&self) -> Option<&DustString> {
|
pub fn as_string(&self) -> Option<&DustString> {
|
||||||
if let Value::Concrete(ConcreteValue::String(value)) = self {
|
if let Value::Concrete(ConcreteValue::String(value)) = self {
|
||||||
Some(value)
|
Some(value)
|
||||||
@ -74,6 +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 {
|
pub fn r#type(&self) -> Type {
|
||||||
match self {
|
match self {
|
||||||
Value::Concrete(concrete_value) => concrete_value.r#type(),
|
Value::Concrete(concrete_value) => concrete_value.r#type(),
|
||||||
|
@ -1,29 +1,34 @@
|
|||||||
use std::fmt::{self, Debug, Display, Formatter};
|
use std::{
|
||||||
|
fmt::{self, Debug, Display, Formatter},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{Chunk, DustString};
|
use crate::{Chunk, DustString};
|
||||||
|
|
||||||
use super::Register;
|
use super::Register;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FunctionCall<'a> {
|
pub struct FunctionCall {
|
||||||
pub chunk: &'a Chunk,
|
pub chunk: Arc<Chunk>,
|
||||||
pub ip: usize,
|
pub ip: usize,
|
||||||
pub return_register: u8,
|
pub return_register: u16,
|
||||||
pub registers: Vec<Register>,
|
pub registers: Vec<Register>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FunctionCall<'a> {
|
impl FunctionCall {
|
||||||
pub fn new(chunk: &'a Chunk, return_register: u8) -> Self {
|
pub fn new(chunk: Arc<Chunk>, return_register: u16) -> Self {
|
||||||
|
let register_count = chunk.register_count;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
chunk,
|
chunk,
|
||||||
ip: 0,
|
ip: 0,
|
||||||
return_register,
|
return_register,
|
||||||
registers: vec![Register::Empty; chunk.register_count],
|
registers: vec![Register::Empty; register_count],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for FunctionCall<'_> {
|
impl Display for FunctionCall {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
|
@ -6,19 +6,20 @@ mod thread;
|
|||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fmt::{self, Debug, Display, Formatter},
|
fmt::{self, Debug, Display, Formatter},
|
||||||
sync::mpsc,
|
sync::Arc,
|
||||||
thread::spawn,
|
thread::Builder,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use function_call::FunctionCall;
|
pub use function_call::FunctionCall;
|
||||||
pub(crate) use run_action::get_next_action;
|
|
||||||
pub use run_action::RunAction;
|
pub use run_action::RunAction;
|
||||||
|
pub(crate) use run_action::get_next_action;
|
||||||
pub use stack::Stack;
|
pub use stack::Stack;
|
||||||
pub use thread::{Thread, ThreadData};
|
pub use thread::{Thread, ThreadData};
|
||||||
|
|
||||||
use tracing::{span, Level};
|
use crossbeam_channel::bounded;
|
||||||
|
use tracing::{Level, span};
|
||||||
|
|
||||||
use crate::{compile, Chunk, DustError, Value};
|
use crate::{Chunk, DustError, Value, compile};
|
||||||
|
|
||||||
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
||||||
let chunk = compile(source)?;
|
let chunk = compile(source)?;
|
||||||
@ -28,41 +29,37 @@ pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Vm {
|
pub struct Vm {
|
||||||
threads: Vec<Thread>,
|
main_chunk: Chunk,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Vm {
|
impl Vm {
|
||||||
pub fn new(chunk: Chunk) -> Self {
|
pub fn new(main_chunk: Chunk) -> Self {
|
||||||
let threads = vec![Thread::new(chunk)];
|
Self { main_chunk }
|
||||||
|
|
||||||
debug_assert_eq!(1, threads.capacity());
|
|
||||||
|
|
||||||
Self { threads }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(mut self) -> Option<Value> {
|
pub fn run(self) -> Option<Value> {
|
||||||
let span = span!(Level::INFO, "Run");
|
let span = span!(Level::INFO, "Run");
|
||||||
let _enter = span.enter();
|
let _enter = span.enter();
|
||||||
|
let thread_name = self
|
||||||
|
.main_chunk
|
||||||
|
.name
|
||||||
|
.as_ref()
|
||||||
|
.map(|name| name.to_string())
|
||||||
|
.unwrap_or_else(|| "anonymous".to_string());
|
||||||
|
let mut main_thread = Thread::new(Arc::new(self.main_chunk));
|
||||||
|
let (tx, rx) = bounded(1);
|
||||||
|
|
||||||
if self.threads.len() == 1 {
|
Builder::new()
|
||||||
return self.threads[0].run();
|
.name(thread_name)
|
||||||
}
|
.spawn(move || {
|
||||||
|
let value_option = main_thread.run();
|
||||||
|
let _ = tx.send(value_option);
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
.join()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let (tx, rx) = mpsc::channel();
|
rx.recv().unwrap_or(None)
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,9 +82,9 @@ impl Display for Register {
|
|||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub enum Pointer {
|
pub enum Pointer {
|
||||||
Register(u8),
|
Register(u16),
|
||||||
Constant(u8),
|
Constant(u16),
|
||||||
Stack(usize, u8),
|
Stack(usize, u16),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Pointer {
|
impl Display for Pointer {
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
use tracing::trace;
|
use tracing::trace;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
AbstractList, ConcreteValue, Instruction, Operand, Type, Value,
|
||||||
instruction::{
|
instruction::{
|
||||||
Add, Call, CallNative, Close, Divide, Equal, GetLocal, Jump, Less, LessEqual, LoadBoolean,
|
Add, Call, CallNative, Close, Divide, Equal, GetLocal, Jump, Less, LessEqual, LoadBoolean,
|
||||||
LoadConstant, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not, Point,
|
LoadConstant, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not, Point,
|
||||||
Return, SetLocal, Subtract, Test, TestSet,
|
Return, SetLocal, Subtract, Test, TestSet, TypeCode,
|
||||||
},
|
},
|
||||||
vm::FunctionCall,
|
vm::FunctionCall,
|
||||||
AbstractList, Argument, ConcreteValue, Instruction, Type, Value,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{thread::ThreadData, Pointer, Register};
|
use super::{Pointer, Register, thread::ThreadData};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct RunAction {
|
pub struct RunAction {
|
||||||
@ -44,13 +44,13 @@ pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [
|
|||||||
multiply,
|
multiply,
|
||||||
divide,
|
divide,
|
||||||
modulo,
|
modulo,
|
||||||
test,
|
|
||||||
test_set,
|
|
||||||
equal,
|
equal,
|
||||||
less,
|
less,
|
||||||
less_equal,
|
less_equal,
|
||||||
negate,
|
negate,
|
||||||
not,
|
not,
|
||||||
|
test,
|
||||||
|
test_set,
|
||||||
call,
|
call,
|
||||||
call_native,
|
call_native,
|
||||||
jump,
|
jump,
|
||||||
@ -145,6 +145,7 @@ pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|||||||
let LoadList {
|
let LoadList {
|
||||||
destination,
|
destination,
|
||||||
start_register,
|
start_register,
|
||||||
|
jump_next,
|
||||||
} = instruction.into();
|
} = instruction.into();
|
||||||
let mut item_pointers = Vec::with_capacity((destination - start_register) as usize);
|
let mut item_pointers = Vec::with_capacity((destination - start_register) as usize);
|
||||||
let mut item_type = Type::Any;
|
let mut item_type = Type::Any;
|
||||||
@ -186,6 +187,7 @@ pub fn load_function(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|||||||
let LoadFunction {
|
let LoadFunction {
|
||||||
destination,
|
destination,
|
||||||
prototype_index,
|
prototype_index,
|
||||||
|
jump_next,
|
||||||
} = instruction.into();
|
} = instruction.into();
|
||||||
let prototype_index = prototype_index as usize;
|
let prototype_index = prototype_index as usize;
|
||||||
let current_call = data.call_stack.last_mut_unchecked();
|
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 {
|
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 current_call = data.call_stack.last_mut_unchecked();
|
||||||
let prototype = ¤t_call.chunk;
|
let prototype = ¤t_call.chunk;
|
||||||
let function = prototype.as_function();
|
let function = prototype.as_function();
|
||||||
@ -248,12 +253,42 @@ pub fn add(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|||||||
let Add {
|
let Add {
|
||||||
destination,
|
destination,
|
||||||
left,
|
left,
|
||||||
|
left_type,
|
||||||
right,
|
right,
|
||||||
|
right_type,
|
||||||
} = instruction.into();
|
} = instruction.into();
|
||||||
let left = data.get_argument_unchecked(left);
|
let sum = match (left_type, right_type) {
|
||||||
let right = data.get_argument_unchecked(right);
|
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
||||||
let sum = left.add(right);
|
let left = unsafe {
|
||||||
let register = Register::Value(sum);
|
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);
|
data.set_register(destination, register);
|
||||||
|
|
||||||
@ -266,12 +301,42 @@ pub fn subtract(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|||||||
let Subtract {
|
let Subtract {
|
||||||
destination,
|
destination,
|
||||||
left,
|
left,
|
||||||
|
left_type,
|
||||||
right,
|
right,
|
||||||
|
right_type,
|
||||||
} = instruction.into();
|
} = instruction.into();
|
||||||
let left = data.get_argument_unchecked(left);
|
let difference = match (left_type, right_type) {
|
||||||
let right = data.get_argument_unchecked(right);
|
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
||||||
let difference = left.subtract(right);
|
let left = unsafe {
|
||||||
let register = Register::Value(difference);
|
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);
|
data.set_register(destination, register);
|
||||||
|
|
||||||
@ -284,20 +349,42 @@ pub fn multiply(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|||||||
let Multiply {
|
let Multiply {
|
||||||
destination,
|
destination,
|
||||||
left,
|
left,
|
||||||
|
left_type,
|
||||||
right,
|
right,
|
||||||
|
right_type,
|
||||||
} = instruction.into();
|
} = instruction.into();
|
||||||
let left = data.get_argument_unchecked(left);
|
let product = match (left_type, right_type) {
|
||||||
let right = data.get_argument_unchecked(right);
|
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
||||||
let product = match (left, right) {
|
let left = unsafe {
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => match (left, right) {
|
data.get_argument_unchecked(left)
|
||||||
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
.as_integer()
|
||||||
ConcreteValue::Integer(left * right).to_value()
|
.unwrap_unchecked()
|
||||||
}
|
|
||||||
_ => panic!("Value Error: Cannot multiply values"),
|
|
||||||
},
|
|
||||||
_ => panic!("Value Error: Cannot multiply values"),
|
|
||||||
};
|
};
|
||||||
let register = Register::Value(product);
|
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(Value::Concrete(product));
|
||||||
|
|
||||||
data.set_register(destination, register);
|
data.set_register(destination, register);
|
||||||
|
|
||||||
@ -310,20 +397,42 @@ pub fn divide(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|||||||
let Divide {
|
let Divide {
|
||||||
destination,
|
destination,
|
||||||
left,
|
left,
|
||||||
|
left_type,
|
||||||
right,
|
right,
|
||||||
|
right_type,
|
||||||
} = instruction.into();
|
} = instruction.into();
|
||||||
let left = data.get_argument_unchecked(left);
|
let quotient = match (left_type, right_type) {
|
||||||
let right = data.get_argument_unchecked(right);
|
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
||||||
let quotient = match (left, right) {
|
let left = unsafe {
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => match (left, right) {
|
data.get_argument_unchecked(left)
|
||||||
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
.as_integer()
|
||||||
ConcreteValue::Integer(left / right).to_value()
|
.unwrap_unchecked()
|
||||||
}
|
|
||||||
_ => panic!("Value Error: Cannot divide values"),
|
|
||||||
},
|
|
||||||
_ => panic!("Value Error: Cannot divide values"),
|
|
||||||
};
|
};
|
||||||
let register = Register::Value(quotient);
|
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(Value::Concrete(quotient));
|
||||||
|
|
||||||
data.set_register(destination, register);
|
data.set_register(destination, register);
|
||||||
|
|
||||||
@ -336,20 +445,28 @@ pub fn modulo(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|||||||
let Modulo {
|
let Modulo {
|
||||||
destination,
|
destination,
|
||||||
left,
|
left,
|
||||||
|
left_type,
|
||||||
right,
|
right,
|
||||||
|
right_type,
|
||||||
} = instruction.into();
|
} = instruction.into();
|
||||||
let left = data.get_argument_unchecked(left);
|
let remainder = match (left_type, right_type) {
|
||||||
let right = data.get_argument_unchecked(right);
|
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
||||||
let remainder = match (left, right) {
|
let left = unsafe {
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => match (left, right) {
|
data.get_argument_unchecked(left)
|
||||||
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
.as_integer()
|
||||||
ConcreteValue::Integer(left % right).to_value()
|
.unwrap_unchecked()
|
||||||
}
|
|
||||||
_ => panic!("Value Error: Cannot modulo values"),
|
|
||||||
},
|
|
||||||
_ => panic!("Value Error: Cannot modulo values"),
|
|
||||||
};
|
};
|
||||||
let register = Register::Value(remainder);
|
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(Value::Concrete(remainder));
|
||||||
|
|
||||||
data.set_register(destination, register);
|
data.set_register(destination, register);
|
||||||
|
|
||||||
@ -397,8 +514,8 @@ pub fn test_set(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|||||||
if boolean == test_value {
|
if boolean == test_value {
|
||||||
} else {
|
} else {
|
||||||
let pointer = match argument {
|
let pointer = match argument {
|
||||||
Argument::Constant(constant_index) => Pointer::Constant(constant_index),
|
Operand::Constant(constant_index) => Pointer::Constant(constant_index),
|
||||||
Argument::Register(register_index) => Pointer::Register(register_index),
|
Operand::Register(register_index) => Pointer::Register(register_index),
|
||||||
};
|
};
|
||||||
let register = Register::Pointer(pointer);
|
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 {
|
pub fn equal(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||||
let Equal { value, left, right } = instruction.into();
|
let Equal {
|
||||||
let left = data.get_argument_unchecked(left);
|
comparator,
|
||||||
let right = data.get_argument_unchecked(right);
|
left,
|
||||||
let is_equal = left.equals(right);
|
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();
|
let current_call = data.call_stack.last_mut_unchecked();
|
||||||
|
|
||||||
current_call.ip += 1;
|
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 {
|
pub fn less(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||||
let Less { value, left, right } = instruction.into();
|
let Less {
|
||||||
let left = data.get_argument_unchecked(left);
|
comparator,
|
||||||
let right = data.get_argument_unchecked(right);
|
left,
|
||||||
let is_less = left < right;
|
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();
|
let current_call = data.call_stack.last_mut_unchecked();
|
||||||
|
|
||||||
current_call.ip += 1;
|
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 {
|
pub fn less_equal(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||||
let LessEqual { value, left, right } = instruction.into();
|
let LessEqual {
|
||||||
let left = data.get_argument_unchecked(left);
|
comparator,
|
||||||
let right = data.get_argument_unchecked(right);
|
left,
|
||||||
let is_less_or_equal = left <= right;
|
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();
|
let current_call = data.call_stack.last_mut_unchecked();
|
||||||
|
|
||||||
current_call.ip += 1;
|
current_call.ip += 1;
|
||||||
@ -465,6 +712,7 @@ pub fn negate(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|||||||
let Negate {
|
let Negate {
|
||||||
destination,
|
destination,
|
||||||
argument,
|
argument,
|
||||||
|
argument_type,
|
||||||
} = instruction.into();
|
} = instruction.into();
|
||||||
let argument = data.get_argument_unchecked(argument);
|
let argument = data.get_argument_unchecked(argument);
|
||||||
let negated = argument.negate();
|
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 current_call = data.call_stack.last_unchecked();
|
||||||
let first_argument_register = return_register - argument_count;
|
let first_argument_register = return_register - argument_count;
|
||||||
let prototype = if is_recursive {
|
let prototype = if is_recursive {
|
||||||
current_call.chunk
|
current_call.chunk.clone()
|
||||||
} else {
|
} else {
|
||||||
let function = data
|
let function = data
|
||||||
.open_register_unchecked(function_register)
|
.open_register_unchecked(function_register)
|
||||||
.as_function()
|
.as_function()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
¤t_call.chunk.prototypes[function.prototype_index as usize]
|
current_call.chunk.prototypes[function.prototype_index as usize].clone()
|
||||||
};
|
};
|
||||||
let mut next_call = FunctionCall::new(prototype, return_register);
|
let mut next_call = FunctionCall::new(prototype, return_register);
|
||||||
let mut argument_index = 0;
|
let mut argument_index = 0;
|
||||||
@ -565,7 +813,7 @@ pub fn call_native(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|||||||
let first_argument_index = destination - argument_count;
|
let first_argument_index = destination - argument_count;
|
||||||
let argument_range = first_argument_index..destination;
|
let argument_range = first_argument_index..destination;
|
||||||
|
|
||||||
function.call(data, Some(destination), argument_range)
|
function.call(data, destination, argument_range)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn r#return(instruction: Instruction, data: &mut ThreadData) -> bool {
|
pub fn r#return(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||||
|
@ -124,7 +124,7 @@ impl<T: Debug> Debug for Stack<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Stack<FunctionCall<'_>> {
|
impl Display for Stack<FunctionCall> {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
writeln!(f, "----- DUST CALL STACK -----")?;
|
writeln!(f, "----- DUST CALL STACK -----")?;
|
||||||
|
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
use std::mem::replace;
|
use std::{mem::replace, sync::Arc, thread::JoinHandle};
|
||||||
|
|
||||||
use tracing::{info, trace};
|
use tracing::{info, trace};
|
||||||
|
|
||||||
use crate::{vm::FunctionCall, Argument, Chunk, DustString, Span, Value};
|
use crate::{Chunk, DustString, Operand, Span, Value, vm::FunctionCall};
|
||||||
|
|
||||||
use super::{Pointer, Register, RunAction, Stack};
|
use super::{Pointer, Register, RunAction, Stack};
|
||||||
|
|
||||||
pub struct Thread {
|
pub struct Thread {
|
||||||
chunk: Chunk,
|
chunk: Arc<Chunk>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Thread {
|
impl Thread {
|
||||||
pub fn new(chunk: Chunk) -> Self {
|
pub fn new(chunk: Arc<Chunk>) -> Self {
|
||||||
Thread { chunk }
|
Thread { chunk }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,7 +25,8 @@ impl Thread {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let mut call_stack = Stack::with_capacity(self.chunk.prototypes.len() + 1);
|
let mut call_stack = Stack::with_capacity(self.chunk.prototypes.len() + 1);
|
||||||
let main_call = FunctionCall::new(&self.chunk, 0);
|
let mut main_call = FunctionCall::new(self.chunk.clone(), 0);
|
||||||
|
main_call.ip = 1; // The first action is already known
|
||||||
|
|
||||||
call_stack.push(main_call);
|
call_stack.push(main_call);
|
||||||
|
|
||||||
@ -34,6 +35,7 @@ impl Thread {
|
|||||||
call_stack,
|
call_stack,
|
||||||
next_action: first_action,
|
next_action: first_action,
|
||||||
return_value_index: None,
|
return_value_index: None,
|
||||||
|
spawned_threads: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@ -45,7 +47,7 @@ impl Thread {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if should_end {
|
if should_end {
|
||||||
let return_value = if let Some(register_index) = thread_data.return_value_index {
|
let value_option = if let Some(register_index) = thread_data.return_value_index {
|
||||||
let value =
|
let value =
|
||||||
thread_data.empty_register_or_clone_constant_unchecked(register_index);
|
thread_data.empty_register_or_clone_constant_unchecked(register_index);
|
||||||
|
|
||||||
@ -54,20 +56,28 @@ impl Thread {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
return return_value;
|
thread_data
|
||||||
|
.spawned_threads
|
||||||
|
.into_iter()
|
||||||
|
.for_each(|join_handle| {
|
||||||
|
let _ = join_handle.join();
|
||||||
|
});
|
||||||
|
|
||||||
|
return value_option;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ThreadData<'a> {
|
pub struct ThreadData {
|
||||||
pub call_stack: Stack<FunctionCall<'a>>,
|
pub call_stack: Stack<FunctionCall>,
|
||||||
pub next_action: RunAction,
|
pub next_action: RunAction,
|
||||||
pub return_value_index: Option<u8>,
|
pub return_value_index: Option<u16>,
|
||||||
|
pub spawned_threads: Vec<JoinHandle<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ThreadData<'_> {
|
impl ThreadData {
|
||||||
pub fn current_position(&self) -> Span {
|
pub fn current_position(&self) -> Span {
|
||||||
let current_call = self.call_stack.last_unchecked();
|
let current_call = self.call_stack.last_unchecked();
|
||||||
|
|
||||||
@ -75,7 +85,7 @@ impl ThreadData<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn follow_pointer_unchecked(&self, pointer: Pointer) -> &Value {
|
pub(crate) fn follow_pointer_unchecked(&self, pointer: Pointer) -> &Value {
|
||||||
trace!("Follow pointer {pointer}");
|
trace!("Follow {pointer}");
|
||||||
|
|
||||||
match pointer {
|
match pointer {
|
||||||
Pointer::Register(register_index) => self.open_register_unchecked(register_index),
|
Pointer::Register(register_index) => self.open_register_unchecked(register_index),
|
||||||
@ -96,8 +106,8 @@ impl ThreadData<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_register_unchecked(&self, register_index: u8) -> &Register {
|
pub fn get_register_unchecked(&self, register_index: u16) -> &Register {
|
||||||
trace!("Get register R{register_index}");
|
trace!("Get R{register_index}");
|
||||||
|
|
||||||
let register_index = register_index as usize;
|
let register_index = register_index as usize;
|
||||||
|
|
||||||
@ -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;
|
let to_register = to_register as usize;
|
||||||
|
|
||||||
self.call_stack.last_mut_unchecked().registers[to_register] = register;
|
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_index = register_index as usize;
|
||||||
|
|
||||||
let register = if cfg!(debug_assertions) {
|
let register = if cfg!(debug_assertions) {
|
||||||
@ -133,42 +143,30 @@ impl ThreadData<'_> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
trace!("Open R{register_index} to {register}");
|
||||||
|
|
||||||
match register {
|
match register {
|
||||||
Register::Value(value) => {
|
Register::Value(value) => value,
|
||||||
trace!("Register R{register_index} opened to value {value}");
|
Register::Pointer(pointer) => self.follow_pointer_unchecked(*pointer),
|
||||||
|
|
||||||
value
|
|
||||||
}
|
|
||||||
Register::Pointer(pointer) => {
|
|
||||||
trace!("Open register R{register_index} opened to pointer {pointer}");
|
|
||||||
|
|
||||||
self.follow_pointer_unchecked(*pointer)
|
|
||||||
}
|
|
||||||
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_register_allow_empty_unchecked(&self, register_index: u8) -> Option<&Value> {
|
pub fn open_register_allow_empty_unchecked(&self, register_index: u16) -> Option<&Value> {
|
||||||
trace!("Open register R{register_index}");
|
trace!("Open R{register_index}");
|
||||||
|
|
||||||
let register = self.get_register_unchecked(register_index);
|
let register = self.get_register_unchecked(register_index);
|
||||||
|
|
||||||
|
trace!("Open R{register_index} to {register}");
|
||||||
|
|
||||||
match register {
|
match register {
|
||||||
Register::Value(value) => {
|
Register::Value(value) => Some(value),
|
||||||
trace!("Register R{register_index} openned to value {value}");
|
Register::Pointer(pointer) => Some(self.follow_pointer_unchecked(*pointer)),
|
||||||
|
|
||||||
Some(value)
|
|
||||||
}
|
|
||||||
Register::Pointer(pointer) => {
|
|
||||||
trace!("Open register R{register_index} openned to pointer {pointer}");
|
|
||||||
|
|
||||||
Some(self.follow_pointer_unchecked(*pointer))
|
|
||||||
}
|
|
||||||
Register::Empty => None,
|
Register::Empty => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 register_index = register_index as usize;
|
||||||
let old_register = replace(
|
let old_register = replace(
|
||||||
&mut self.call_stack.last_mut_unchecked().registers[register_index],
|
&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);
|
let register = self.get_register_unchecked(register_index);
|
||||||
|
|
||||||
match register {
|
match register {
|
||||||
@ -235,14 +233,14 @@ impl ThreadData<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// DRY helper to get a value from an Argument
|
/// 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 {
|
match argument {
|
||||||
Argument::Constant(constant_index) => self.get_constant_unchecked(constant_index),
|
Operand::Constant(constant_index) => self.get_constant_unchecked(constant_index),
|
||||||
Argument::Register(register_index) => self.open_register_unchecked(register_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;
|
let constant_index = constant_index as usize;
|
||||||
|
|
||||||
if cfg!(debug_assertions) {
|
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 local_index = local_index as usize;
|
||||||
let chunk = self.call_stack.last_unchecked().chunk;
|
let chunk = &self.call_stack.last_unchecked().chunk;
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
local_index < chunk.locals.len(),
|
local_index < chunk.locals.len(),
|
||||||
|
@ -61,11 +61,11 @@ fn parentheses_precedence() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
Instruction::add(0, Argument::Constant(0), Argument::Constant(1)),
|
Instruction::add(0, Operand::Constant(0), Operand::Constant(1)),
|
||||||
Span(3, 4)
|
Span(3, 4)
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
Instruction::multiply(1, Argument::Register(0), Argument::Constant(2)),
|
Instruction::multiply(1, Operand::Register(0), Operand::Constant(2)),
|
||||||
Span(8, 9)
|
Span(8, 9)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(11, 11)),
|
(Instruction::r#return(true), Span(11, 11)),
|
||||||
@ -97,19 +97,19 @@ fn math_operator_precedence() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
Instruction::add(0, Argument::Constant(0), Argument::Constant(1)),
|
Instruction::add(0, Operand::Constant(0), Operand::Constant(1)),
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
Instruction::multiply(1, Argument::Constant(2), Argument::Constant(3)),
|
Instruction::multiply(1, Operand::Constant(2), Operand::Constant(3)),
|
||||||
Span(10, 11)
|
Span(10, 11)
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
Instruction::divide(2, Argument::Register(1), Argument::Constant(4)),
|
Instruction::divide(2, Operand::Register(1), Operand::Constant(4)),
|
||||||
Span(14, 15)
|
Span(14, 15)
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
Instruction::subtract(3, Argument::Register(0), Argument::Register(2)),
|
Instruction::subtract(3, Operand::Register(0), Operand::Register(2)),
|
||||||
Span(6, 7)
|
Span(6, 7)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(17, 17)),
|
(Instruction::r#return(true), Span(17, 17)),
|
||||||
|
@ -15,7 +15,7 @@ fn equal() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
Instruction::equal(0, true, Argument::Constant(0), Argument::Constant(1)),
|
Instruction::equal(0, true, Operand::Constant(0), Operand::Constant(1)),
|
||||||
Span(2, 4)
|
Span(2, 4)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(6, 6)),
|
(Instruction::r#return(true), Span(6, 6)),
|
||||||
@ -43,7 +43,7 @@ fn greater() {
|
|||||||
},
|
},
|
||||||
vec![
|
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)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(5, 5)),
|
(Instruction::r#return(true), Span(5, 5)),
|
||||||
@ -71,7 +71,7 @@ fn greater_than_or_equal() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
Instruction::less(0, false, Argument::Constant(0), Argument::Constant(1)),
|
Instruction::less(0, false, Operand::Constant(0), Operand::Constant(1)),
|
||||||
Span(2, 4)
|
Span(2, 4)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(6, 6)),
|
(Instruction::r#return(true), Span(6, 6)),
|
||||||
@ -99,7 +99,7 @@ fn less_than() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
Instruction::less(0, true, Argument::Constant(0), Argument::Constant(1)),
|
Instruction::less(0, true, Operand::Constant(0), Operand::Constant(1)),
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(5, 5)),
|
(Instruction::r#return(true), Span(5, 5)),
|
||||||
@ -127,7 +127,7 @@ fn less_than_or_equal() {
|
|||||||
},
|
},
|
||||||
vec![
|
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)
|
Span(2, 4)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(6, 6)),
|
(Instruction::r#return(true), Span(6, 6)),
|
||||||
@ -155,7 +155,7 @@ fn not_equal() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
Instruction::equal(0, false, Argument::Constant(0), Argument::Constant(1)),
|
Instruction::equal(0, false, Operand::Constant(0), Operand::Constant(1)),
|
||||||
Span(2, 4)
|
Span(2, 4)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(6, 6)),
|
(Instruction::r#return(true), Span(6, 6)),
|
||||||
|
@ -20,7 +20,7 @@ fn function() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
Instruction::add(2, Argument::Register(0), Argument::Register(1)),
|
Instruction::add(2, Operand::Register(0), Operand::Register(1)),
|
||||||
Span(30, 31)
|
Span(30, 31)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(34, 35)),
|
(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(0, 0, false), Span(0, 35)),
|
||||||
(Instruction::load_constant(1, 1, false), Span(36, 37)),
|
(Instruction::load_constant(1, 1, false), Span(36, 37)),
|
||||||
(Instruction::load_constant(2, 2, false), Span(39, 40)),
|
(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)),
|
(Instruction::r#return(true), Span(41, 41)),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
@ -64,7 +64,7 @@ fn function_call() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
Instruction::add(2, Argument::Register(0), Argument::Register(1)),
|
Instruction::add(2, Operand::Register(0), Operand::Register(1)),
|
||||||
Span(30, 31)
|
Span(30, 31)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(34, 35)),
|
(Instruction::r#return(true), Span(34, 35)),
|
||||||
@ -112,7 +112,7 @@ fn function_declaration() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
Instruction::add(2, Argument::Register(0), Argument::Register(1)),
|
Instruction::add(2, Operand::Register(0), Operand::Register(1)),
|
||||||
Span(35, 36)
|
Span(35, 36)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(39, 40)),
|
(Instruction::r#return(true), Span(39, 40)),
|
||||||
|
@ -80,15 +80,15 @@ fn list_with_complex_expression() {
|
|||||||
vec![
|
vec![
|
||||||
(Instruction::load_constant(0, 0, false), Span(1, 2)),
|
(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)
|
Span(6, 7)
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
Instruction::multiply(2, Argument::Constant(3), Argument::Constant(4)),
|
Instruction::multiply(2, Operand::Constant(3), Operand::Constant(4)),
|
||||||
Span(14, 15)
|
Span(14, 15)
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
Instruction::subtract(3, Argument::Register(1), Argument::Register(2)),
|
Instruction::subtract(3, Operand::Register(1), Operand::Register(2)),
|
||||||
Span(10, 11)
|
Span(10, 11)
|
||||||
),
|
),
|
||||||
(Instruction::close(1, 3), Span(17, 18)),
|
(Instruction::close(1, 3), Span(17, 18)),
|
||||||
@ -131,7 +131,7 @@ fn list_with_simple_expression() {
|
|||||||
vec![
|
vec![
|
||||||
(Instruction::load_constant(0, 0, false), Span(1, 2)),
|
(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)
|
Span(6, 7)
|
||||||
),
|
),
|
||||||
(Instruction::load_constant(2, 3, false), Span(11, 12)),
|
(Instruction::load_constant(2, 3, false), Span(11, 12)),
|
||||||
|
@ -16,12 +16,12 @@ fn r#while() {
|
|||||||
vec![
|
vec![
|
||||||
(Instruction::load_constant(0, 0, false), Span(12, 13)),
|
(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)
|
Span(23, 24)
|
||||||
),
|
),
|
||||||
(Instruction::jump(2, true), Span(41, 42)),
|
(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)
|
Span(35, 36)
|
||||||
),
|
),
|
||||||
(Instruction::jump(3, false), Span(41, 42)),
|
(Instruction::jump(3, false), Span(41, 42)),
|
||||||
|
@ -15,7 +15,7 @@ fn divide_bytes() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
Instruction::divide(0, Argument::Constant(0), Argument::Constant(1)),
|
Instruction::divide(0, Operand::Constant(0), Operand::Constant(1)),
|
||||||
Span(5, 6)
|
Span(5, 6)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(11, 11))
|
(Instruction::r#return(true), Span(11, 11))
|
||||||
@ -43,7 +43,7 @@ fn divide_floats() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
Instruction::divide(0, Argument::Constant(0), Argument::Constant(0)),
|
Instruction::divide(0, Operand::Constant(0), Operand::Constant(0)),
|
||||||
Span(4, 5)
|
Span(4, 5)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(9, 9))
|
(Instruction::r#return(true), Span(9, 9))
|
||||||
@ -71,7 +71,7 @@ fn divide_integers() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
Instruction::divide(0, Argument::Constant(0), Argument::Constant(0)),
|
Instruction::divide(0, Operand::Constant(0), Operand::Constant(0)),
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(5, 5))
|
(Instruction::r#return(true), Span(5, 5))
|
||||||
|
@ -16,7 +16,7 @@ fn divide_assign() {
|
|||||||
vec![
|
vec![
|
||||||
(Instruction::load_constant(0, 0, false), Span(12, 13)),
|
(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)
|
Span(17, 19)
|
||||||
),
|
),
|
||||||
(Instruction::get_local(1, 0), Span(23, 24)),
|
(Instruction::get_local(1, 0), Span(23, 24)),
|
||||||
|
@ -15,7 +15,7 @@ fn modulo_floats() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
Instruction::modulo(0, Argument::Constant(0), Argument::Constant(0)),
|
Instruction::modulo(0, Operand::Constant(0), Operand::Constant(0)),
|
||||||
Span(4, 5)
|
Span(4, 5)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(9, 9))
|
(Instruction::r#return(true), Span(9, 9))
|
||||||
@ -43,7 +43,7 @@ fn modulo_integers() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
Instruction::modulo(0, Argument::Constant(0), Argument::Constant(0)),
|
Instruction::modulo(0, Operand::Constant(0), Operand::Constant(0)),
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(5, 5))
|
(Instruction::r#return(true), Span(5, 5))
|
||||||
|
@ -15,7 +15,7 @@ fn multiply_floats() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
Instruction::multiply(0, Argument::Constant(0), Argument::Constant(0)),
|
Instruction::multiply(0, Operand::Constant(0), Operand::Constant(0)),
|
||||||
Span(4, 5)
|
Span(4, 5)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(9, 9)),
|
(Instruction::r#return(true), Span(9, 9)),
|
||||||
@ -43,7 +43,7 @@ fn multiply_integers() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
Instruction::multiply(0, Argument::Constant(0), Argument::Constant(1)),
|
Instruction::multiply(0, Operand::Constant(0), Operand::Constant(1)),
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(5, 5)),
|
(Instruction::r#return(true), Span(5, 5)),
|
||||||
|
@ -16,7 +16,7 @@ fn multiply_assign() {
|
|||||||
vec![
|
vec![
|
||||||
(Instruction::load_constant(0, 0, false), Span(12, 13)),
|
(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)
|
Span(17, 19)
|
||||||
),
|
),
|
||||||
(Instruction::get_local(1, 0), Span(22, 23)),
|
(Instruction::get_local(1, 0), Span(22, 23)),
|
||||||
|
@ -15,7 +15,7 @@ fn subtract_floats() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
Instruction::subtract(0, Argument::Constant(0), Argument::Constant(0)),
|
Instruction::subtract(0, Operand::Constant(0), Operand::Constant(0)),
|
||||||
Span(4, 5)
|
Span(4, 5)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(9, 9)),
|
(Instruction::r#return(true), Span(9, 9)),
|
||||||
@ -43,7 +43,7 @@ fn subtract_floats_saturate() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
Instruction::subtract(0, Argument::Constant(0), Argument::Constant(1)),
|
Instruction::subtract(0, Operand::Constant(0), Operand::Constant(1)),
|
||||||
Span(25, 26)
|
Span(25, 26)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(36, 36)),
|
(Instruction::r#return(true), Span(36, 36)),
|
||||||
@ -74,7 +74,7 @@ fn subtract_integers() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
Instruction::subtract(0, Argument::Constant(0), Argument::Constant(1)),
|
Instruction::subtract(0, Operand::Constant(0), Operand::Constant(1)),
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(5, 5)),
|
(Instruction::r#return(true), Span(5, 5)),
|
||||||
@ -102,7 +102,7 @@ fn subtract_integers_saturate() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
Instruction::subtract(0, Argument::Constant(0), Argument::Constant(1)),
|
Instruction::subtract(0, Operand::Constant(0), Operand::Constant(1)),
|
||||||
Span(21, 22)
|
Span(21, 22)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(24, 24)),
|
(Instruction::r#return(true), Span(24, 24)),
|
||||||
|
@ -16,7 +16,7 @@ fn subtract_assign() {
|
|||||||
vec![
|
vec![
|
||||||
(Instruction::load_constant(0, 0, false), Span(12, 14)),
|
(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)
|
Span(18, 20)
|
||||||
),
|
),
|
||||||
(Instruction::get_local(1, 0), Span(24, 25)),
|
(Instruction::get_local(1, 0), Span(24, 25)),
|
||||||
|
@ -14,7 +14,7 @@ fn negate() {
|
|||||||
return_type: Type::Integer,
|
return_type: Type::Integer,
|
||||||
},
|
},
|
||||||
vec![
|
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)),
|
(Instruction::r#return(true), Span(5, 5)),
|
||||||
],
|
],
|
||||||
vec![ConcreteValue::Integer(42)],
|
vec![ConcreteValue::Integer(42)],
|
||||||
@ -40,7 +40,7 @@ fn not() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(Instruction::load_boolean(0, true, false), Span(1, 5)),
|
(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)),
|
(Instruction::r#return(true), Span(5, 5)),
|
||||||
],
|
],
|
||||||
vec![],
|
vec![],
|
||||||
|
Loading…
x
Reference in New Issue
Block a user