Compare commits
82 Commits
Author | SHA1 | Date | |
---|---|---|---|
a08a1e560a | |||
1f62391b58 | |||
0aee607961 | |||
a53e0018cf | |||
4c019410e8 | |||
4a169bc515 | |||
5de44b58ee | |||
0292a01162 | |||
b0f7ca7992 | |||
7153cc16f2 | |||
7f939b693e | |||
07f8b36c99 | |||
5030171bb6 | |||
afcc1874fe | |||
720f006d8c | |||
4145499e0c | |||
794e556c7a | |||
d21bb2cbdc | |||
fb5eac1307 | |||
728eb071e8 | |||
8940f37654 | |||
69ef1b3b06 | |||
6a61947476 | |||
77148cbf3f | |||
d53521c0f9 | |||
5aefb29a95 | |||
3599643fad | |||
00f35bd3ae | |||
e1e259141c | |||
59f64c9afd | |||
833ee30930 | |||
fe59024bf4 | |||
d2ee33c7df | |||
cc93d8e345 | |||
a53cfb9cf6 | |||
f2ee01d66f | |||
dfa5f6858c | |||
c7a18b1d39 | |||
63312cf08a | |||
a5d2e7d869 | |||
3af1b64820 | |||
07001a03e7 | |||
6e9d5a49d2 | |||
1bb87de70f | |||
dcea611a0b | |||
dc2acbfe46 | |||
95cfaecd17 | |||
e19ddbe2f3 | |||
e53ecd93fe | |||
cd03e06ade | |||
62fcdd7e8c | |||
71a92c078b | |||
e387579a81 | |||
d3addbe183 | |||
ac11ad5674 | |||
3d48558b94 | |||
1d0824165d | |||
8cc5661944 | |||
4b38a93409 | |||
72421bf510 | |||
1155b5fff8 | |||
820ead0c02 | |||
25b4230aa4 | |||
b90adcb9f0 | |||
788e3d4a2b | |||
75d6948e82 | |||
bd273035aa | |||
6f0955c29a | |||
6d17ba9a2c | |||
5662ba3d08 | |||
4775d425a0 | |||
03103e4fa1 | |||
12092c30f4 | |||
93f8f31b6d | |||
371a061b1c | |||
c1fe54ccd5 | |||
3fcbde59e5 | |||
7cfd60d281 | |||
6eac92f4ce | |||
0d39d91cc7 | |||
dc002023cd | |||
8e09d487c3 |
19
.github/workflows/rust.yml
vendored
Normal file
19
.github/workflows/rust.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
name: Cargo Build & Test
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
build_and_test:
|
||||
name: Dust Build & Test
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: rustup update nightly && rustup default nightly
|
||||
- run: cargo build --verbose
|
||||
- run: cargo test --verbose --no-fail-fast
|
545
Cargo.lock
generated
545
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -5,11 +5,11 @@ resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
authors = ["Jeff Anderson"]
|
||||
edition = "2024"
|
||||
license = "GPL-3.0"
|
||||
readme = "README.md"
|
||||
repository = "https://git.jeffa.io/jeff/dust.git"
|
||||
version = "0.5.0"
|
||||
edition = "2021"
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 1
|
||||
|
191
README.md
191
README.md
@ -1,4 +1,10 @@
|
||||
# ✭ Dust Programming Language
|
||||
# Dust   [![test_shield]][actions] [![License: GPL v3][gpl3_shield]][gpl3] [![rust_shield]](#)
|
||||
|
||||
[test_shield]: https://github.com/solaeus/dust/actions/workflows/rust.yml/badge.svg
|
||||
[actions]: https://github.com/solaeus/dust/actions
|
||||
[gpl3_shield]: https://img.shields.io/badge/License-GPLv3-blue.svg
|
||||
[gpl3]: https://www.gnu.org/licenses/gpl-3.0
|
||||
[rust_shield]: https://img.shields.io/badge/Rust-%23000000.svg?e&logo=rust&logoColor=white
|
||||
|
||||
**Fast**, **safe** and **easy-to-use** general-purpose programming language.
|
||||
|
||||
@ -23,12 +29,17 @@ fn fib (n: int) -> int {
|
||||
write_line(fib(25))
|
||||
```
|
||||
|
||||
> [!IMPORTANT]
|
||||
> 🧪 💡 ⚗️
|
||||
> Dust is still experimental. Currently, development is more focused on exploring ideas for
|
||||
> optimization and performance than on stability or feature completeness. This will change as the
|
||||
> project matures.
|
||||
|
||||
## 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
|
||||
- Multi-threaded register-based virtual machine
|
||||
- Beautiful, helpful error messages from the compiler
|
||||
- Safe execution, runtime errors are treated as bugs
|
||||
|
||||
@ -45,40 +56,40 @@ This project's goal is to deliver a language with features that stand out due to
|
||||
design choices and a high-quality implementation. As mentioned in the first sentence, Dust's general
|
||||
aspirations are to be **fast**, **safe** and **easy**.
|
||||
|
||||
- **Fast**
|
||||
- **Fast** 🚀
|
||||
- **Fast Compilation** Despite its compile-time abstractions, Dust should compile and start
|
||||
executing quickly. The compilation time should feel negligible to the user.
|
||||
- **Fast Execution** Dust should be competitive with highly optimized, modern, register-based VM
|
||||
languages like Lua. Dust should be bench tested during development to inform decisions about
|
||||
performance.
|
||||
- **Low Resource Usage** Memory and CPU power should be used conservatively and predictably.
|
||||
- **Safe**
|
||||
- **Safe** 🛡️
|
||||
- **Static Types** Typing should prevent runtime errors and improve code quality, offering a
|
||||
superior development experience despite some additional constraints. Like any good statically
|
||||
typed language, users should feel confident in the type-consistency of their code and not want
|
||||
to go back to a dynamically typed language.
|
||||
superior development experience despite some additional constraints.
|
||||
- **Null-Free** Dust has no "null" or "undefined" values. All values are initialized and have a
|
||||
type. This eliminates a whole class of bugs that are common in other languages.
|
||||
- **Memory Safety** Dust should be free of memory bugs. Being implemented in Rust makes this easy
|
||||
but, to accommodate long-running programs, Dust still requires a memory management strategy.
|
||||
Dust's design is to use a separate thread for garbage collection, allowing other threads to
|
||||
continue executing instructions while the garbage collector looks for unused memory.
|
||||
- **Easy**
|
||||
- **Memory Safety** Dust should be free of memory bugs, using both safe Rust and sound, correct
|
||||
"unsafe" Rust to maximize performance. Dust should employ a concurrent mark-and-sweep garbage
|
||||
collecter, allowing other threads to continue executing instructions while the garbage collector
|
||||
looks for freeable memory.
|
||||
- **Easy** 🎂
|
||||
- **Simple Syntax** Dust should be easier to learn than most programming languages. Its syntax
|
||||
should be familiar to users of other C-like languages to the point that even a new user can read
|
||||
Dust code and understand what it does. Rather than being held back by a lack of features, Dust
|
||||
should be powerful and elegant in its simplicity, seeking a maximum of capability with a minimum
|
||||
of complexity.
|
||||
- **Practical Tooling** Shipped as a single binary, Dust should provide logging and tools for
|
||||
disassembly and tokenization that make the lexer, compiler and runtime as transparent as
|
||||
possible. Dust should also include an official formatter through the same binary. Additional
|
||||
tools such as a language server and linter should be adopted when possible.
|
||||
- **Excellent Errors** Dust should provide helpful error messages that guide the user to the
|
||||
source of the problem and suggest a solution. Errors should be a helpful learning resource for
|
||||
users rather than a source of frustration.
|
||||
- **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.
|
||||
|
||||
### Author
|
||||
|
||||
I'm Jeff 🦀 and I started this project as simple expession evaluator. Initially, the project used an
|
||||
external parser and a tree-walking interpreter. After several books, a few papers, countless
|
||||
I'm Jeff 🦀 and I started this project as a simple expession evaluator. Initially, the project used
|
||||
an external parser and a tree-walking interpreter. After several books, a few papers, countless
|
||||
articles and a lot of experimentation, Dust has evolved to an ambitious project that aims to
|
||||
implement lucrative features with a high-quality implementation that competes with established
|
||||
languages.
|
||||
@ -87,6 +98,150 @@ languages.
|
||||
|
||||
**Dust is under active development and is not yet ready for general use.**
|
||||
|
||||
### CLI
|
||||
|
||||
The Dust CLI has commands to run, disassemble or tokenize Dust code. It can also provide logging at
|
||||
different levels and measure the time taken for compilation and execution.
|
||||
|
||||
<details>
|
||||
<summary>Show CLI help</summary>
|
||||
|
||||
```text
|
||||
Command line interface for the Dust programming language
|
||||
|
||||
Usage: dust [OPTIONS] [FILE]
|
||||
dust {run|-r} [OPTIONS] [FILE]
|
||||
dust {compile|-c} [OPTIONS] [FILE]
|
||||
dust {tokenize|-t} [OPTIONS] [FILE]
|
||||
dust help [COMMAND]...
|
||||
|
||||
Arguments:
|
||||
[FILE] Path to a source code file
|
||||
|
||||
Options:
|
||||
-l, --log-level <LOG_LEVEL> Overrides the DUST_LOG environment variable
|
||||
--time Print the time taken for compilation and execution
|
||||
--no-output Do not print the program's return value
|
||||
--name <NAME> Custom program name, overrides the file name
|
||||
-e, --eval <INPUT> Source code to run instead of a file
|
||||
--stdin Read source code from stdin
|
||||
-i, --input <INPUT> Input format [default: dust] [possible values: dust, json, ron, yaml]
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
|
||||
dust {run|-r}:
|
||||
Compile and run the program (default)
|
||||
--time Print the time taken for compilation and execution
|
||||
--no-output Do not print the program's return value
|
||||
--name <NAME> Custom program name, overrides the file name
|
||||
-e, --eval <INPUT> Source code to run instead of a file
|
||||
--stdin Read source code from stdin
|
||||
-i, --input <INPUT> Input format [default: dust] [possible values: dust, json, ron, yaml]
|
||||
-h, --help Print help
|
||||
[FILE] Path to a source code file
|
||||
|
||||
dust {compile|-c}:
|
||||
Compile and print the input
|
||||
-s, --style Style disassembly output
|
||||
-n, --name <NAME> Custom program name, overrides the file name
|
||||
-e, --eval <INPUT> Source code to run instead of a file
|
||||
--stdin Read source code from stdin
|
||||
-o, --output <FORMAT> [default: cli] [possible values: cli, json, ron, yaml]
|
||||
-h, --help Print help
|
||||
[FILE] Path to a source code file
|
||||
|
||||
dust {tokenize|-t}:
|
||||
Lex the source code and print the tokens
|
||||
-s, --style Style token output
|
||||
-e, --eval <INPUT> Source code to run instead of a file
|
||||
--stdin Read source code from stdin
|
||||
-h, --help Print help
|
||||
[FILE] Path to a source code file
|
||||
|
||||
dust help:
|
||||
Print this message or the help of the given subcommand(s)
|
||||
[COMMAND]... Print help for the subcommand(s)
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
#### Run a program
|
||||
|
||||
If not specified, the CLI will use `run` command. This mode compiles and executes the Dust program,
|
||||
printing the return value to the console. You can also run Dust code directly from the command line
|
||||
using the `--eval` or `-e` flag.
|
||||
|
||||
```sh
|
||||
dust foobar.ds
|
||||
dust -e 'let x = 42; x'
|
||||
```
|
||||
|
||||
#### Compile and disassemble
|
||||
|
||||
Dust's disassembly output is a detailed, human-readable representation of the internal
|
||||
representation of the Dust program. It shows every piece of information that the compiler sends to
|
||||
the virtual machine and explains what each instruction does and what data it uses.
|
||||
|
||||
```sh
|
||||
dust compile example.ds
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Show disassembly</summary>
|
||||
|
||||
```text
|
||||
╭──────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ example.ds │
|
||||
│ │
|
||||
│ let mut i = 0; while i < 10 { i += 1 } │
|
||||
│ │
|
||||
│ 6 instructions, 4 constants, 1 locals, returns none │
|
||||
│ │
|
||||
│ Instructions │
|
||||
│ ╭─────┬────────────┬─────────────────┬─────────────────────────────────────────╮ │
|
||||
│ │ i │ POSITION │ OPERATION │ INFO │ │
|
||||
│ ├─────┼────────────┼─────────────────┼─────────────────────────────────────────┤ │
|
||||
│ │ 0 │ (12, 13) │ LOAD_CONSTANT │ R_INT_0 = C0 │ │
|
||||
│ │ 1 │ (23, 24) │ LESS │ if R_INT_0 < C_2 { JUMP +1 } │ │
|
||||
│ │ 2 │ (38, 38) │ JUMP │ JUMP +2 │ │
|
||||
│ │ 3 │ (30, 36) │ ADD │ R_INT_0 = R_INT_0 + C_3 │ │
|
||||
│ │ 4 │ (38, 38) │ JUMP │ JUMP -3 │ │
|
||||
│ │ 5 │ (38, 38) │ RETURN │ RETURN │ │
|
||||
│ ╰─────┴────────────┴─────────────────┴─────────────────────────────────────────╯ │
|
||||
│ Locals │
|
||||
│ ╭─────┬────────────────┬──────────────────────────┬────────────┬───────┬───────╮ │
|
||||
│ │ i │ IDENTIFIER │ TYPE │ REGISTER │ SCOPE │MUTABLE│ │
|
||||
│ ├─────┼────────────────┼──────────────────────────┼────────────┼───────┼───────┤ │
|
||||
│ │ 0 │ i │ int │ R_INT_0 │ 0.0 │ true │ │
|
||||
│ ╰─────┴────────────────┴──────────────────────────┴────────────┴───────┴───────╯ │
|
||||
│ Constants │
|
||||
│ ╭─────┬──────────────────────────┬──────────────────────────╮ │
|
||||
│ │ i │ TYPE │ VALUE │ │
|
||||
│ ├─────┼──────────────────────────┼──────────────────────────┤ │
|
||||
│ │ 0 │ int │ 0 │ │
|
||||
│ │ 1 │ str │ i │ │
|
||||
│ │ 2 │ int │ 10 │ │
|
||||
│ │ 3 │ int │ 1 │ │
|
||||
│ ╰─────┴──────────────────────────┴──────────────────────────╯ │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
The instruction notation reflects the Dust VM's register-based architecture. Values are referred to
|
||||
by their address in the register or constant table. For example, `R_INT_42` refers to the
|
||||
forty-second integer register, and `C_0` refers to the first constant.
|
||||
|
||||
```text
|
||||
R_INT_0 = R_INT_0 + C_3
|
||||
```
|
||||
|
||||
The info section for the ADD instruction shows what the instruction does: it adds the value at
|
||||
`R_INT_0` to the value at `C_3` and stores the result in `R_INT_0`. As the "Constants" section
|
||||
shows, `C_3` is the integer constant `1`. This means that this add instruction increments the value
|
||||
in `R_INT_0` by `1`. In the "Locals" section, we can see that `R_INT_0` is the register used by the
|
||||
`i` variable.
|
||||
|
||||
## Installation
|
||||
|
||||
Eventually, Dust should be available via package managers and as an embeddable library. For now,
|
||||
@ -101,7 +256,7 @@ in scope.
|
||||
|
||||
*The Implementation of Lua 5.0*[^1] by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, and
|
||||
Waldemar Celes was a great resource for understanding register-based virtual machines and their
|
||||
instructions. This paper was recommended by Bob Nystrom in [Crafting Interpreters].
|
||||
instructions. This paper was recommended by Bob Nystrom in *Crafting Interpreters*.
|
||||
|
||||
*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
|
||||
|
@ -1,4 +1,4 @@
|
||||
local i = 1
|
||||
local i = 0
|
||||
|
||||
while i < 5000000 do
|
||||
i = i + 1
|
||||
|
@ -1,4 +1,4 @@
|
||||
i = 1
|
||||
i = 0
|
||||
|
||||
while i < 5_000_000:
|
||||
i += 1
|
||||
|
@ -1,4 +1,4 @@
|
||||
|
||||
#!/bin/sh
|
||||
hyperfine \
|
||||
--shell none \
|
||||
--prepare 'sync' \
|
||||
@ -9,5 +9,6 @@ hyperfine \
|
||||
'bun addictive_addition.js' \
|
||||
'python addictive_addition.py' \
|
||||
'lua addictive_addition.lua' \
|
||||
'luajit addictive_addition.lua' \
|
||||
'ruby addictive_addition.rb' \
|
||||
'java addictive_addition.java'
|
||||
|
@ -13,16 +13,17 @@ name = "dust"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.14", features = [
|
||||
clap = { version = "4.5.29", features = [
|
||||
"cargo",
|
||||
"color",
|
||||
"derive",
|
||||
"help",
|
||||
"wrap_help",
|
||||
] }
|
||||
color-print = "0.3.7"
|
||||
dust-lang = { path = "../dust-lang" }
|
||||
postcard = "1.0.10"
|
||||
serde_json = "1.0.133"
|
||||
postcard = "1.1.1"
|
||||
ron = "0.8.1"
|
||||
serde_json = "1.0.138"
|
||||
serde_yaml = "0.9.34"
|
||||
tracing = "0.1.41"
|
||||
tracing-subscriber = "0.3.19"
|
||||
tracing-subscriber = { version = "0.3.19", features = ["time"] }
|
||||
|
@ -9,66 +9,18 @@ use clap::{
|
||||
builder::{styling::AnsiColor, Styles},
|
||||
crate_authors, crate_description, crate_version,
|
||||
error::ErrorKind,
|
||||
Args, ColorChoice, Error, Parser, Subcommand, ValueHint,
|
||||
Args, ColorChoice, Error, Parser, Subcommand, ValueEnum, ValueHint,
|
||||
};
|
||||
use color_print::{cformat, cstr};
|
||||
use dust_lang::{CompileError, Compiler, DustError, DustString, Lexer, Span, Token, Vm};
|
||||
use tracing::{subscriber::set_global_default, Level};
|
||||
use tracing_subscriber::FmtSubscriber;
|
||||
|
||||
const ABOUT: &str = cstr!(
|
||||
r#"
|
||||
<bright-magenta,bold>Dust CLI
|
||||
────────</>
|
||||
{about}
|
||||
|
||||
<bold>⚙️ Version:</> {version}
|
||||
<bold>🦀 Author:</> {author}
|
||||
<bold>⚖️ License:</> GPL-3.0
|
||||
<bold>🔬 Repository:</> https://git.jeffa.io/jeff/dust
|
||||
"#
|
||||
);
|
||||
|
||||
const PLAIN_ABOUT: &str = r#"
|
||||
{about}
|
||||
"#;
|
||||
|
||||
const USAGE: &str = cstr!(
|
||||
r#"
|
||||
<bright-magenta,bold>Usage:</> {usage}
|
||||
"#
|
||||
);
|
||||
|
||||
const SUBCOMMANDS: &str = cstr!(
|
||||
r#"
|
||||
<bright-magenta,bold>Modes:</>
|
||||
{subcommands}
|
||||
"#
|
||||
);
|
||||
|
||||
const OPTIONS: &str = cstr!(
|
||||
r#"
|
||||
<bright-magenta,bold>Options:</>
|
||||
{options}
|
||||
"#
|
||||
);
|
||||
|
||||
const CREATE_MAIN_HELP_TEMPLATE: fn() -> String =
|
||||
|| cformat!("{ABOUT}{USAGE}{SUBCOMMANDS}{OPTIONS}");
|
||||
|
||||
const CREATE_MODE_HELP_TEMPLATE: fn(&str) -> String = |title| {
|
||||
cformat!(
|
||||
"\
|
||||
<bright-magenta,bold>{title}\n────────</>\
|
||||
{PLAIN_ABOUT}{USAGE}{OPTIONS}
|
||||
"
|
||||
)
|
||||
};
|
||||
use tracing_subscriber::{fmt::time::Uptime, FmtSubscriber};
|
||||
|
||||
const STYLES: Styles = Styles::styled()
|
||||
.literal(AnsiColor::Cyan.on_default())
|
||||
.placeholder(AnsiColor::Cyan.on_default())
|
||||
.valid(AnsiColor::BrightCyan.on_default())
|
||||
.header(AnsiColor::BrightMagenta.on_default().bold().underline())
|
||||
.usage(AnsiColor::BrightMagenta.on_default().bold().underline())
|
||||
.literal(AnsiColor::BrightCyan.on_default().bold())
|
||||
.placeholder(AnsiColor::BrightCyan.on_default().bold())
|
||||
.valid(AnsiColor::BrightGreen.on_default())
|
||||
.invalid(AnsiColor::BrightYellow.on_default())
|
||||
.error(AnsiColor::BrightRed.on_default());
|
||||
|
||||
@ -78,7 +30,6 @@ const STYLES: Styles = Styles::styled()
|
||||
author = crate_authors!(),
|
||||
about = crate_description!(),
|
||||
color = ColorChoice::Auto,
|
||||
help_template = CREATE_MAIN_HELP_TEMPLATE(),
|
||||
styles = STYLES,
|
||||
)]
|
||||
struct Cli {
|
||||
@ -98,17 +49,17 @@ struct Cli {
|
||||
log_level: Option<Level>,
|
||||
|
||||
#[command(subcommand)]
|
||||
mode: Option<Mode>,
|
||||
mode: Option<Command>,
|
||||
|
||||
#[command(flatten)]
|
||||
run: Run,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
struct Input {
|
||||
struct Source {
|
||||
/// Source code to run instead of a file
|
||||
#[arg(short, long, value_hint = ValueHint::Other, value_name = "INPUT")]
|
||||
command: Option<String>,
|
||||
eval: Option<String>,
|
||||
|
||||
/// Read source code from stdin
|
||||
#[arg(long)]
|
||||
@ -121,10 +72,7 @@ struct Input {
|
||||
|
||||
/// Compile and run the program (default)
|
||||
#[derive(Args)]
|
||||
#[command(
|
||||
short_flag = 'r',
|
||||
help_template = CREATE_MODE_HELP_TEMPLATE("Run Mode")
|
||||
)]
|
||||
#[command(short_flag = 'r')]
|
||||
struct Run {
|
||||
/// Print the time taken for compilation and execution
|
||||
#[arg(long)]
|
||||
@ -139,49 +87,66 @@ struct Run {
|
||||
name: Option<DustString>,
|
||||
|
||||
#[command(flatten)]
|
||||
input: Input,
|
||||
source: Source,
|
||||
|
||||
/// Input format
|
||||
#[arg(short, long, default_value = "dust")]
|
||||
input: InputFormat,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
#[clap(subcommand_value_name = "MODE", flatten_help = true)]
|
||||
enum Mode {
|
||||
#[clap(subcommand_value_name = "COMMAND", flatten_help = true)]
|
||||
enum Command {
|
||||
Run(Run),
|
||||
|
||||
/// Compile and print the bytecode disassembly
|
||||
#[command(
|
||||
short_flag = 'd',
|
||||
help_template = CREATE_MODE_HELP_TEMPLATE("Disassemble Mode")
|
||||
)]
|
||||
Disassemble {
|
||||
/// Compile and print the input
|
||||
#[command(short_flag = 'c')]
|
||||
Compile {
|
||||
/// Style disassembly output
|
||||
#[arg(short, long, default_value = "true")]
|
||||
style: bool,
|
||||
|
||||
/// Custom program name, overrides the file name
|
||||
#[arg(long)]
|
||||
#[arg(short, long)]
|
||||
name: Option<DustString>,
|
||||
|
||||
#[command(flatten)]
|
||||
input: Input,
|
||||
source: Source,
|
||||
|
||||
#[arg(short, long, default_value = "cli", value_name = "FORMAT")]
|
||||
output: OutputFormat,
|
||||
},
|
||||
|
||||
/// Lex the source code and print the tokens
|
||||
#[command(
|
||||
short_flag = 't',
|
||||
help_template = CREATE_MODE_HELP_TEMPLATE("Tokenize Mode")
|
||||
)]
|
||||
#[command(short_flag = 't')]
|
||||
Tokenize {
|
||||
/// Style token output
|
||||
#[arg(short, long, default_value = "true")]
|
||||
style: bool,
|
||||
|
||||
#[command(flatten)]
|
||||
input: Input,
|
||||
source: Source,
|
||||
},
|
||||
}
|
||||
|
||||
fn get_source_and_file_name(input: Input) -> (String, Option<DustString>) {
|
||||
if let Some(path) = input.file {
|
||||
#[derive(ValueEnum, Clone, Copy)]
|
||||
enum OutputFormat {
|
||||
Cli,
|
||||
Json,
|
||||
Ron,
|
||||
Yaml,
|
||||
}
|
||||
|
||||
#[derive(ValueEnum, Clone, Copy)]
|
||||
enum InputFormat {
|
||||
Dust,
|
||||
Json,
|
||||
Ron,
|
||||
Yaml,
|
||||
}
|
||||
|
||||
fn get_source_and_file_name(source: Source) -> (String, Option<DustString>) {
|
||||
if let Some(path) = source.file {
|
||||
let source = read_to_string(&path).expect("Failed to read source file");
|
||||
let file_name = path
|
||||
.file_name()
|
||||
@ -191,7 +156,7 @@ fn get_source_and_file_name(input: Input) -> (String, Option<DustString>) {
|
||||
return (source, file_name);
|
||||
}
|
||||
|
||||
if input.stdin {
|
||||
if source.stdin {
|
||||
let mut source = String::new();
|
||||
io::stdin()
|
||||
.read_to_string(&mut source)
|
||||
@ -200,7 +165,7 @@ fn get_source_and_file_name(input: Input) -> (String, Option<DustString>) {
|
||||
return (source, None);
|
||||
}
|
||||
|
||||
let source = input.command.expect("No source code provided");
|
||||
let source = source.eval.expect("No source code provided");
|
||||
|
||||
(source, None)
|
||||
}
|
||||
@ -212,17 +177,93 @@ fn main() {
|
||||
mode,
|
||||
run,
|
||||
} = Cli::parse();
|
||||
let mode = mode.unwrap_or(Mode::Run(run));
|
||||
let mode = mode.unwrap_or(Command::Run(run));
|
||||
let subscriber = FmtSubscriber::builder()
|
||||
.with_max_level(log_level)
|
||||
.with_thread_names(true)
|
||||
.with_ansi(true)
|
||||
.with_file(false)
|
||||
.with_line_number(false)
|
||||
.with_thread_names(true)
|
||||
.with_timer(Uptime::from(start_time))
|
||||
.finish();
|
||||
|
||||
set_global_default(subscriber).expect("Failed to set tracing subscriber");
|
||||
|
||||
if let Mode::Disassemble { style, name, input } = mode {
|
||||
let (source, file_name) = get_source_and_file_name(input);
|
||||
if let Command::Run(Run {
|
||||
time,
|
||||
no_output,
|
||||
name,
|
||||
source,
|
||||
input,
|
||||
}) = mode
|
||||
{
|
||||
let (source, file_name) = get_source_and_file_name(source);
|
||||
let lexer = Lexer::new(&source);
|
||||
let program_name = name.or(file_name);
|
||||
let chunk = match input {
|
||||
InputFormat::Dust => {
|
||||
let mut compiler = match Compiler::new(lexer, program_name, true) {
|
||||
Ok(compiler) => compiler,
|
||||
Err(error) => {
|
||||
handle_compile_error(error, &source);
|
||||
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
match compiler.compile() {
|
||||
Ok(()) => {}
|
||||
Err(error) => {
|
||||
handle_compile_error(error, &source);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
compiler.finish()
|
||||
}
|
||||
InputFormat::Json => {
|
||||
serde_json::from_str(&source).expect("Failed to deserialize JSON into chunk")
|
||||
}
|
||||
InputFormat::Ron => {
|
||||
ron::de::from_str(&source).expect("Failed to deserialize RON into chunk")
|
||||
}
|
||||
InputFormat::Yaml => {
|
||||
serde_yaml::from_str(&source).expect("Failed to deserialize YAML into chunk")
|
||||
}
|
||||
};
|
||||
let compile_end = start_time.elapsed();
|
||||
|
||||
let vm = Vm::new(chunk);
|
||||
let return_value = vm.run();
|
||||
let run_end = start_time.elapsed();
|
||||
|
||||
if !no_output {
|
||||
if let Some(value) = return_value {
|
||||
println!("{}", value)
|
||||
}
|
||||
}
|
||||
|
||||
if time {
|
||||
let run_time = run_end - compile_end;
|
||||
let total_time = compile_end + run_time;
|
||||
|
||||
print_time("Compile Time", compile_end);
|
||||
print_time("Run Time", run_time);
|
||||
print_time("Total Time", total_time);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if let Command::Compile {
|
||||
style,
|
||||
name,
|
||||
source,
|
||||
output,
|
||||
} = mode
|
||||
{
|
||||
let (source, file_name) = get_source_and_file_name(source);
|
||||
let lexer = Lexer::new(&source);
|
||||
let program_name = name.or(file_name);
|
||||
let mut compiler = match Compiler::new(lexer, program_name, true) {
|
||||
@ -246,6 +287,8 @@ fn main() {
|
||||
let chunk = compiler.finish();
|
||||
let mut stdout = stdout().lock();
|
||||
|
||||
match output {
|
||||
OutputFormat::Cli => {
|
||||
chunk
|
||||
.disassembler(&mut stdout)
|
||||
.width(65)
|
||||
@ -253,12 +296,32 @@ fn main() {
|
||||
.source(&source)
|
||||
.disassemble()
|
||||
.expect("Failed to write disassembly to stdout");
|
||||
}
|
||||
OutputFormat::Json => {
|
||||
let json = serde_json::to_string_pretty(&chunk)
|
||||
.expect("Failed to serialize chunk to JSON");
|
||||
|
||||
println!("{json}");
|
||||
}
|
||||
OutputFormat::Ron => {
|
||||
let ron = ron::ser::to_string_pretty(&chunk, Default::default())
|
||||
.expect("Failed to serialize chunk to RON");
|
||||
|
||||
println!("{ron}");
|
||||
}
|
||||
OutputFormat::Yaml => {
|
||||
let yaml =
|
||||
serde_yaml::to_string(&chunk).expect("Failed to serialize chunk to YAML");
|
||||
|
||||
println!("{yaml}");
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if let Mode::Tokenize { input, .. } = mode {
|
||||
let (source, _) = get_source_and_file_name(input);
|
||||
if let Command::Tokenize { source, .. } = mode {
|
||||
let (source, _) = get_source_and_file_name(source);
|
||||
let mut lexer = Lexer::new(&source);
|
||||
let mut next_token = || -> Option<(Token, Span, bool)> {
|
||||
match lexer.next_token() {
|
||||
@ -299,59 +362,6 @@ fn main() {
|
||||
|
||||
println!("{token_kind:^21}|{token:^22}|{position:^22}");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if let Mode::Run(Run {
|
||||
time,
|
||||
no_output,
|
||||
name,
|
||||
input,
|
||||
}) = mode
|
||||
{
|
||||
let (source, file_name) = get_source_and_file_name(input);
|
||||
let lexer = Lexer::new(&source);
|
||||
let program_name = name.or(file_name);
|
||||
let mut compiler = match Compiler::new(lexer, program_name, true) {
|
||||
Ok(compiler) => compiler,
|
||||
Err(error) => {
|
||||
handle_compile_error(error, &source);
|
||||
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
match compiler.compile() {
|
||||
Ok(()) => {}
|
||||
Err(error) => {
|
||||
handle_compile_error(error, &source);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let chunk = compiler.finish();
|
||||
let compile_end = start_time.elapsed();
|
||||
|
||||
let vm = Vm::new(chunk);
|
||||
let return_value = vm.run();
|
||||
let run_end = start_time.elapsed();
|
||||
|
||||
if let Some(value) = return_value {
|
||||
if !no_output {
|
||||
println!("{}", value)
|
||||
}
|
||||
}
|
||||
|
||||
if time {
|
||||
let run_time = run_end - compile_end;
|
||||
let total_time = compile_end + run_time;
|
||||
|
||||
print_time("Compile Time", compile_end);
|
||||
print_time("Run Time", run_time);
|
||||
print_time("Total Time", total_time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,11 +9,11 @@ repository.workspace = true
|
||||
version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
annotate-snippets = "0.11.4"
|
||||
colored = "2.1.0"
|
||||
annotate-snippets = "0.11.5"
|
||||
colored = "2.2.0"
|
||||
rand = "0.8.5"
|
||||
serde = { version = "1.0.203", features = ["derive", "rc"] }
|
||||
serde_json = "1.0.117"
|
||||
serde = { version = "1.0.217", features = ["derive", "rc"] }
|
||||
serde_json = "1.0.138"
|
||||
getrandom = { version = "0.2", features = [
|
||||
"js",
|
||||
] } # Indirect dependency, for WASM builds
|
||||
@ -22,14 +22,27 @@ smartstring = { version = "1.0.1", features = [
|
||||
], default-features = false }
|
||||
tracing = "0.1.41"
|
||||
crossbeam-channel = "0.5.14"
|
||||
smallvec = { version = "1.14.0", features = ["serde", "const_generics"] }
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { version = "0.3.4", features = ["html_reports"] }
|
||||
criterion = { version = "0.5.1", features = ["html_reports"] }
|
||||
|
||||
[[bench]]
|
||||
name = "addictive_addition"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "addictive_subtraction"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "addictive_multiplication"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "addictive_division"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "fibonacci"
|
||||
harness = false
|
||||
@ -39,77 +52,73 @@ name = "threads"
|
||||
harness = false
|
||||
|
||||
[[test]]
|
||||
name = "logic_and"
|
||||
path = "tests/logic/and.rs"
|
||||
|
||||
[[test]]
|
||||
name = "logic_and_and"
|
||||
path = "tests/logic/and_and.rs"
|
||||
|
||||
[[test]]
|
||||
name = "logic_or"
|
||||
path = "tests/logic/or.rs"
|
||||
|
||||
[[test]]
|
||||
name = "logic_variables"
|
||||
path = "tests/logic/variables.rs"
|
||||
|
||||
[[test]]
|
||||
name = "math_add"
|
||||
name = "add"
|
||||
path = "tests/math/add.rs"
|
||||
|
||||
[[test]]
|
||||
name = "math_add_assign"
|
||||
path = "tests/math/add_assign.rs"
|
||||
|
||||
[[test]]
|
||||
name = "math_add_errors"
|
||||
path = "tests/math/add_errors.rs"
|
||||
|
||||
[[test]]
|
||||
name = "math_divide"
|
||||
path = "tests/math/divide.rs"
|
||||
|
||||
[[test]]
|
||||
name = "math_divide_assign"
|
||||
path = "tests/math/divide_assign.rs"
|
||||
|
||||
[[test]]
|
||||
name = "math_divide_erros"
|
||||
path = "tests/math/divide_errors.rs"
|
||||
|
||||
[[test]]
|
||||
name = "math_modulo"
|
||||
path = "tests/math/modulo.rs"
|
||||
|
||||
[[test]]
|
||||
name = "math_modulo_assign"
|
||||
path = "tests/math/modulo_assign.rs"
|
||||
|
||||
[[test]]
|
||||
name = "math_modulo_errors"
|
||||
path = "tests/math/modulo_errors.rs"
|
||||
|
||||
[[test]]
|
||||
name = "math_multiply"
|
||||
path = "tests/math/multiply.rs"
|
||||
|
||||
[[test]]
|
||||
name = "math_multiply_assign"
|
||||
path = "tests/math/multiply_assign.rs"
|
||||
|
||||
[[test]]
|
||||
name = "math_multiply_errors"
|
||||
path = "tests/math/multiply_errors.rs"
|
||||
|
||||
[[test]]
|
||||
name = "math_subtract"
|
||||
name = "subtract"
|
||||
path = "tests/math/subtract.rs"
|
||||
|
||||
[[test]]
|
||||
name = "math_subtract_assign"
|
||||
path = "tests/math/subtract_assign.rs"
|
||||
name = "multiply"
|
||||
path = "tests/math/multiply.rs"
|
||||
|
||||
[[test]]
|
||||
name = "math_subtract_errors"
|
||||
path = "tests/math/subtract_errors.rs"
|
||||
name = "divide"
|
||||
path = "tests/math/divide.rs"
|
||||
|
||||
[[test]]
|
||||
name = "modulo"
|
||||
path = "tests/math/modulo.rs"
|
||||
|
||||
[[test]]
|
||||
name = "less"
|
||||
path = "tests/comparison/less.rs"
|
||||
|
||||
[[test]]
|
||||
name = "equal"
|
||||
path = "tests/comparison/equal.rs"
|
||||
|
||||
[[test]]
|
||||
name = "less_equal"
|
||||
path = "tests/comparison/less_equal.rs"
|
||||
|
||||
[[test]]
|
||||
name = "greater"
|
||||
path = "tests/comparison/greater.rs"
|
||||
|
||||
[[test]]
|
||||
name = "not_equal"
|
||||
path = "tests/comparison/not_equal.rs"
|
||||
|
||||
[[test]]
|
||||
name = "greater_equal"
|
||||
path = "tests/comparison/greater_equal.rs"
|
||||
|
||||
[[test]]
|
||||
name = "and"
|
||||
path = "tests/logic/and.rs"
|
||||
|
||||
[[test]]
|
||||
name = "or"
|
||||
path = "tests/logic/or.rs"
|
||||
|
||||
[[test]]
|
||||
name = "and_and"
|
||||
path = "tests/logic/and_and.rs"
|
||||
|
||||
[[test]]
|
||||
name = "or_or"
|
||||
path = "tests/logic/or_or.rs"
|
||||
|
||||
[[test]]
|
||||
name = "and_or"
|
||||
path = "tests/logic/and_or.rs"
|
||||
|
||||
[[test]]
|
||||
name = "or_and"
|
||||
path = "tests/logic/or_and.rs"
|
||||
|
||||
[[test]]
|
||||
name = "assignment_errors"
|
||||
path = "tests/compiler_errors/assignment_errors.rs"
|
||||
|
29
dust-lang/benches/addictive_division.rs
Normal file
29
dust-lang/benches/addictive_division.rs
Normal file
@ -0,0 +1,29 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use dust_lang::run;
|
||||
|
||||
const SOURCE: &str = r"
|
||||
let mut i = 1.7976931348623157e308
|
||||
|
||||
while i > 1.0 {
|
||||
i /= 1.00014196662;
|
||||
}
|
||||
";
|
||||
|
||||
fn addictive_division(source: &str) {
|
||||
run(source).unwrap();
|
||||
}
|
||||
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("addictive_division");
|
||||
|
||||
group.measurement_time(Duration::from_secs(15));
|
||||
group.bench_function("addictive_division", |b| {
|
||||
b.iter(|| addictive_division(black_box(SOURCE)))
|
||||
});
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
criterion_main!(benches);
|
29
dust-lang/benches/addictive_multiplication.rs
Normal file
29
dust-lang/benches/addictive_multiplication.rs
Normal file
@ -0,0 +1,29 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use dust_lang::run;
|
||||
|
||||
const SOURCE: &str = r"
|
||||
let mut i = 1.0
|
||||
|
||||
while i < 1.7976931348623157e308 {
|
||||
i *= 1.00014196662
|
||||
}
|
||||
";
|
||||
|
||||
fn addictive_multiplication(source: &str) {
|
||||
run(source).unwrap();
|
||||
}
|
||||
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("addictive_multiplication");
|
||||
|
||||
group.measurement_time(Duration::from_secs(15));
|
||||
group.bench_function("addictive_multiplication", |b| {
|
||||
b.iter(|| addictive_multiplication(black_box(SOURCE)))
|
||||
});
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
criterion_main!(benches);
|
29
dust-lang/benches/addictive_subtraction.rs
Normal file
29
dust-lang/benches/addictive_subtraction.rs
Normal file
@ -0,0 +1,29 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use dust_lang::run;
|
||||
|
||||
const SOURCE: &str = r"
|
||||
let mut i = 5_000_000
|
||||
|
||||
while i > 0 {
|
||||
i -= 1
|
||||
}
|
||||
";
|
||||
|
||||
fn addictive_subtraction(source: &str) {
|
||||
run(source).unwrap();
|
||||
}
|
||||
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("addictive_subtraction");
|
||||
|
||||
group.measurement_time(Duration::from_secs(15));
|
||||
group.bench_function("addictive_subtraction", |b| {
|
||||
b.iter(|| addictive_subtraction(black_box(SOURCE)))
|
||||
});
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
criterion_main!(benches);
|
@ -1,6 +1,6 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use criterion::{Criterion, black_box, criterion_group, criterion_main};
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use dust_lang::run;
|
||||
|
||||
const SOURCE: &str = r#"
|
||||
@ -9,9 +9,13 @@ const SOURCE: &str = r#"
|
||||
while i < 1_000 {
|
||||
i += 1
|
||||
|
||||
spawn(
|
||||
fn () { random_int(0, 10); }
|
||||
)
|
||||
spawn(fn () {
|
||||
let mut j = 0
|
||||
|
||||
while j < 5_000_000 {
|
||||
j += 1
|
||||
}
|
||||
})
|
||||
}
|
||||
"#;
|
||||
|
||||
|
@ -16,34 +16,32 @@
|
||||
//! that implements the [Write][] trait.
|
||||
//!
|
||||
//! ```text
|
||||
//! ╭─────────────────────────────────────────────────────────────────╮
|
||||
//! │ dust │
|
||||
//! │ │
|
||||
//! │ write_line("Hello world!") │
|
||||
//! ╭──────────────────────────────────────────────────────────────────────────────────╮
|
||||
//! │ write_line("hello world") │
|
||||
//! │ │
|
||||
//! │ 3 instructions, 1 constants, 0 locals, returns none │
|
||||
//! │ │
|
||||
//! │ Instructions │
|
||||
//! │ ╭─────┬────────────┬─────────────────┬────────────────────────╮ │
|
||||
//! │ ╭─────┬────────────┬─────────────────┬─────────────────────────────────────────╮ │
|
||||
//! │ │ i │ POSITION │ OPERATION │ INFO │ │
|
||||
//! │ ├─────┼────────────┼─────────────────┼────────────────────────┤ │
|
||||
//! │ │ 0 │ (11, 25) │ LOAD_CONSTANT │ R0 = C0 │ │
|
||||
//! │ │ 1 │ (0, 26) │ CALL_NATIVE │ write_line(R0) │ │
|
||||
//! │ │ 2 │ (26, 26) │ RETURN │ RETURN │ │
|
||||
//! │ ╰─────┴────────────┴─────────────────┴────────────────────────╯ │
|
||||
//! │ ├─────┼────────────┼─────────────────┼─────────────────────────────────────────┤ │
|
||||
//! │ │ 0 │ (11, 24) │ LOAD_CONSTANT │ R_STR_0 = C0 │ │
|
||||
//! │ │ 1 │ (0, 25) │ CALL_NATIVE │ write_line(R0) │ │
|
||||
//! │ │ 2 │ (25, 25) │ RETURN │ RETURN │ │
|
||||
//! │ ╰─────┴────────────┴─────────────────┴─────────────────────────────────────────╯ │
|
||||
//! │ Constants │
|
||||
//! │ ╭─────┬──────────────────────────┬──────────────────────────╮ │
|
||||
//! │ │ i │ TYPE │ VALUE │ │
|
||||
//! │ ├─────┼──────────────────────────┼──────────────────────────┤ │
|
||||
//! │ │ 0 │ str │ Hello world! │ │
|
||||
//! │ │ 0 │ str │ hello world │ │
|
||||
//! │ ╰─────┴──────────────────────────┴──────────────────────────╯ │
|
||||
//! ╰─────────────────────────────────────────────────────────────────╯
|
||||
//! ╰──────────────────────────────────────────────────────────────────────────────────╯
|
||||
//! ```
|
||||
use std::io::{self, Write};
|
||||
|
||||
use colored::{ColoredString, Colorize};
|
||||
|
||||
use crate::{Chunk, Local};
|
||||
use crate::{Chunk, Local, Type};
|
||||
|
||||
const INSTRUCTION_COLUMNS: [(&str, usize); 4] =
|
||||
[("i", 5), ("POSITION", 12), ("OPERATION", 17), ("INFO", 41)];
|
||||
@ -53,17 +51,18 @@ const INSTRUCTION_BORDERS: [&str; 3] = [
|
||||
"╰─────┴────────────┴─────────────────┴─────────────────────────────────────────╯",
|
||||
];
|
||||
|
||||
const LOCAL_COLUMNS: [(&str, usize); 5] = [
|
||||
const LOCAL_COLUMNS: [(&str, usize); 6] = [
|
||||
("i", 5),
|
||||
("IDENTIFIER", 16),
|
||||
("REGISTER", 10),
|
||||
("TYPE", 26),
|
||||
("REGISTER", 12),
|
||||
("SCOPE", 7),
|
||||
("MUTABLE", 7),
|
||||
];
|
||||
const LOCAL_BORDERS: [&str; 3] = [
|
||||
"╭─────┬────────────────┬──────────┬───────┬───────╮",
|
||||
"├─────┼────────────────┼──────────┼───────┼───────┤",
|
||||
"╰─────┴────────────────┴──────────┴───────┴───────╯",
|
||||
"╭─────┬────────────────┬──────────────────────────┬────────────┬───────┬───────╮",
|
||||
"├─────┼────────────────┼──────────────────────────┼────────────┼───────┼───────┤",
|
||||
"╰─────┴────────────────┴──────────────────────────┴────────────┴───────┴───────╯",
|
||||
];
|
||||
|
||||
const CONSTANT_COLUMNS: [(&str, usize); 3] = [("i", 5), ("TYPE", 26), ("VALUE", 26)];
|
||||
@ -314,6 +313,7 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
||||
Local {
|
||||
identifier_index,
|
||||
register_index,
|
||||
r#type,
|
||||
scope,
|
||||
is_mutable,
|
||||
},
|
||||
@ -321,14 +321,16 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
||||
{
|
||||
let identifier_display = self
|
||||
.chunk
|
||||
.constants
|
||||
.string_constants
|
||||
.get(*identifier_index as usize)
|
||||
.map(|value| value.to_string())
|
||||
.unwrap_or_else(|| "unknown".to_string());
|
||||
let register_display = format!("R{register_index}");
|
||||
let type_display = r#type.to_string();
|
||||
let type_caps = type_display.to_uppercase();
|
||||
let register_display = format!("R_{type_caps}_{register_index}");
|
||||
let scope = scope.to_string();
|
||||
let row = format!(
|
||||
"│{index:^5}│{identifier_display:^16}│{register_display:^10}│{scope:^7}│{is_mutable:^7}│"
|
||||
"│{index:^5}│{identifier_display:^16}│{type_display:^26}│{register_display:^12}│{scope:^7}│{is_mutable:^7}│"
|
||||
);
|
||||
|
||||
self.write_center_border(&row)?;
|
||||
@ -352,8 +354,56 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
||||
self.write_center_border(&column_name_line)?;
|
||||
self.write_center_border(CONSTANT_BORDERS[1])?;
|
||||
|
||||
for (index, value) in self.chunk.constants.iter().enumerate() {
|
||||
let type_display = value.r#type().to_string();
|
||||
for (index, value) in self.chunk.character_constants.iter().enumerate() {
|
||||
let type_display = Type::Character.to_string();
|
||||
let value_display = {
|
||||
let mut value_string = value.to_string();
|
||||
|
||||
if value_string.len() > 26 {
|
||||
value_string = format!("{value_string:.23}...");
|
||||
}
|
||||
|
||||
value_string
|
||||
};
|
||||
let constant_display = format!("│{index:^5}│{type_display:^26}│{value_display:^26}│");
|
||||
|
||||
self.write_center_border(&constant_display)?;
|
||||
}
|
||||
|
||||
for (index, value) in self.chunk.float_constants.iter().enumerate() {
|
||||
let type_display = Type::Float.to_string();
|
||||
let value_display = {
|
||||
let mut value_string = value.to_string();
|
||||
|
||||
if value_string.len() > 26 {
|
||||
value_string = format!("{value_string:.23}...");
|
||||
}
|
||||
|
||||
value_string
|
||||
};
|
||||
let constant_display = format!("│{index:^5}│{type_display:^26}│{value_display:^26}│");
|
||||
|
||||
self.write_center_border(&constant_display)?;
|
||||
}
|
||||
|
||||
for (index, value) in self.chunk.integer_constants.iter().enumerate() {
|
||||
let type_display = Type::Integer.to_string();
|
||||
let value_display = {
|
||||
let mut value_string = value.to_string();
|
||||
|
||||
if value_string.len() > 26 {
|
||||
value_string = format!("{value_string:.23}...");
|
||||
}
|
||||
|
||||
value_string
|
||||
};
|
||||
let constant_display = format!("│{index:^5}│{type_display:^26}│{value_display:^26}│");
|
||||
|
||||
self.write_center_border(&constant_display)?;
|
||||
}
|
||||
|
||||
for (index, value) in self.chunk.string_constants.iter().enumerate() {
|
||||
let type_display = Type::String.to_string();
|
||||
let value_display = {
|
||||
let mut value_string = value.to_string();
|
||||
|
||||
@ -374,7 +424,7 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
||||
}
|
||||
|
||||
pub fn write_prototype_section(&mut self) -> Result<(), io::Error> {
|
||||
self.write_center_border_bold("Functions")?;
|
||||
self.write_center_border_bold("Prototypes")?;
|
||||
|
||||
for chunk in &self.chunk.prototypes {
|
||||
chunk
|
||||
@ -407,7 +457,10 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
||||
if let Some(source) = self.source {
|
||||
let lazily_formatted = source.split_whitespace().collect::<Vec<&str>>().join(" ");
|
||||
|
||||
if self.chunk.name.is_some() {
|
||||
self.write_center_border("")?;
|
||||
}
|
||||
|
||||
self.write_center_border(&lazily_formatted)?;
|
||||
self.write_center_border("")?;
|
||||
}
|
||||
@ -415,7 +468,10 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
||||
let info_line = format!(
|
||||
"{} instructions, {} constants, {} locals, returns {}",
|
||||
self.chunk.instructions.len(),
|
||||
self.chunk.constants.len(),
|
||||
self.chunk.character_constants.len()
|
||||
+ self.chunk.float_constants.len()
|
||||
+ self.chunk.integer_constants.len()
|
||||
+ self.chunk.string_constants.len(),
|
||||
self.chunk.locals.len(),
|
||||
self.chunk.r#type.return_type
|
||||
);
|
||||
@ -431,7 +487,11 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
||||
self.write_local_section()?;
|
||||
}
|
||||
|
||||
if !self.chunk.constants.is_empty() {
|
||||
if !self.chunk.character_constants.is_empty()
|
||||
|| !self.chunk.float_constants.is_empty()
|
||||
|| !self.chunk.integer_constants.is_empty()
|
||||
|| !self.chunk.string_constants.is_empty()
|
||||
{
|
||||
self.write_constant_section()?;
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::Scope;
|
||||
use crate::{Scope, Type};
|
||||
|
||||
/// Scoped variable.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
@ -16,16 +16,26 @@ pub struct Local {
|
||||
/// Whether the local is mutable.
|
||||
pub is_mutable: bool,
|
||||
|
||||
/// Type of the variable's value.
|
||||
pub r#type: Type,
|
||||
|
||||
/// Scope where the variable was declared.
|
||||
pub scope: Scope,
|
||||
}
|
||||
|
||||
impl Local {
|
||||
/// Creates a new Local instance.
|
||||
pub fn new(identifier_index: u16, register_index: u16, is_mutable: bool, scope: Scope) -> Self {
|
||||
pub fn new(
|
||||
identifier_index: u16,
|
||||
register_index: u16,
|
||||
r#type: Type,
|
||||
is_mutable: bool,
|
||||
scope: Scope,
|
||||
) -> Self {
|
||||
Self {
|
||||
identifier_index,
|
||||
register_index,
|
||||
r#type,
|
||||
is_mutable,
|
||||
scope,
|
||||
}
|
||||
|
@ -1,18 +1,15 @@
|
||||
//! Representation of a Dust program or function.
|
||||
//!
|
||||
//! **Except for testing purposes, a chunk should not be created directly. Instead, use the compiler
|
||||
//! to generate a chunk from Dust source code.**
|
||||
//!
|
||||
//! A chunk is output by the compiler to represent all the information needed to execute a Dust
|
||||
//! program. In addition to the program itself, each function in the source is compiled into its own
|
||||
//! chunk and stored in the `prototypes` field of its parent. Thus, a chunk can also represent a
|
||||
//! function prototype.
|
||||
//!
|
||||
//! Chunks have a name when they belong to a named function. They also have a type, so the input
|
||||
//! parameters and the type of the return value are statically known. The [`Chunk::stack_size`]
|
||||
//! field can provide the necessary stack size that will be needed by the virtual machine. Chunks
|
||||
//! cannot be instantiated directly and must be created by the compiler. However, when the Rust
|
||||
//! compiler is in the "test" or "debug_assertions" configuration (used for all types of test),
|
||||
//! [`Chunk::with_data`] can be used to create a chunk for comparison to the compiler output. Do not
|
||||
//! try to run these chunks in a virtual machine. Due to their missing stack size and record index,
|
||||
//! they will cause a panic or undefined behavior.
|
||||
//! parameters and the type of the return value are statically known.
|
||||
mod disassembler;
|
||||
mod local;
|
||||
mod scope;
|
||||
@ -27,50 +24,37 @@ use std::sync::Arc;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{DustString, Function, FunctionType, Instruction, Span, Value};
|
||||
use crate::{DustString, Function, FunctionType, Instruction, Span};
|
||||
|
||||
/// Representation of a Dust program or function.
|
||||
///
|
||||
/// See the [module-level documentation](index.html) for more information.
|
||||
#[derive(Clone, PartialOrd, Serialize, Deserialize)]
|
||||
#[derive(Clone, Default, PartialOrd, Serialize, Deserialize)]
|
||||
pub struct Chunk {
|
||||
pub(crate) name: Option<DustString>,
|
||||
pub(crate) r#type: FunctionType,
|
||||
pub name: Option<DustString>,
|
||||
pub r#type: FunctionType,
|
||||
|
||||
pub(crate) instructions: Vec<Instruction>,
|
||||
pub(crate) positions: Vec<Span>,
|
||||
pub(crate) constants: Vec<Value>,
|
||||
pub(crate) locals: Vec<Local>,
|
||||
pub(crate) prototypes: Vec<Arc<Chunk>>,
|
||||
pub instructions: Vec<Instruction>,
|
||||
pub positions: Vec<Span>,
|
||||
pub character_constants: Vec<char>,
|
||||
pub float_constants: Vec<f64>,
|
||||
pub integer_constants: Vec<i64>,
|
||||
pub string_constants: Vec<DustString>,
|
||||
pub locals: Vec<Local>,
|
||||
pub prototypes: Vec<Arc<Chunk>>,
|
||||
|
||||
pub(crate) register_count: usize,
|
||||
pub(crate) prototype_index: u16,
|
||||
pub boolean_register_count: u16,
|
||||
pub byte_register_count: u16,
|
||||
pub character_register_count: u16,
|
||||
pub float_register_count: u16,
|
||||
pub integer_register_count: u16,
|
||||
pub string_register_count: u16,
|
||||
pub list_register_count: u16,
|
||||
pub function_register_count: u16,
|
||||
pub prototype_index: u16,
|
||||
}
|
||||
|
||||
impl Chunk {
|
||||
#[cfg(any(test, debug_assertions))]
|
||||
pub fn with_data(
|
||||
name: Option<DustString>,
|
||||
r#type: FunctionType,
|
||||
instructions: impl Into<Vec<Instruction>>,
|
||||
positions: impl Into<Vec<Span>>,
|
||||
constants: impl Into<Vec<Value>>,
|
||||
locals: impl Into<Vec<Local>>,
|
||||
prototypes: impl IntoIterator<Item = Chunk>,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
r#type,
|
||||
instructions: instructions.into(),
|
||||
positions: positions.into(),
|
||||
constants: constants.into(),
|
||||
locals: locals.into(),
|
||||
prototypes: prototypes.into_iter().map(Arc::new).collect(),
|
||||
register_count: 0,
|
||||
prototype_index: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_function(&self) -> Function {
|
||||
Function {
|
||||
name: self.name.clone(),
|
||||
@ -125,7 +109,11 @@ impl PartialEq for Chunk {
|
||||
self.name == other.name
|
||||
&& self.r#type == other.r#type
|
||||
&& self.instructions == other.instructions
|
||||
&& self.constants == other.constants
|
||||
&& self.positions == other.positions
|
||||
&& self.character_constants == other.character_constants
|
||||
&& self.float_constants == other.float_constants
|
||||
&& self.integer_constants == other.integer_constants
|
||||
&& self.string_constants == other.string_constants
|
||||
&& self.locals == other.locals
|
||||
&& self.prototypes == other.prototypes
|
||||
}
|
||||
|
@ -11,8 +11,7 @@ use serde::{Deserialize, Serialize};
|
||||
/// The `block index` is a unique identifier for a block within a chunk. It is used to differentiate
|
||||
/// between blocks that are not nested together but have the same depth, i.e. sibling scopes. If the
|
||||
/// `block_index` is 0, then the scope is the root scope of the chunk. The `block_index` is always 0
|
||||
/// when the `depth` is 0. See [Chunk::begin_scope][] and [Chunk::end_scope][] to see how scopes are
|
||||
/// incremented and decremented.
|
||||
/// when the `depth` is 0.
|
||||
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct Scope {
|
||||
/// Level of block nesting.
|
||||
|
@ -181,7 +181,7 @@ impl AnnotatedError for CompileError {
|
||||
fn description(&self) -> &'static str {
|
||||
match self {
|
||||
Self::CannotAddArguments { .. } => "Cannot add these types",
|
||||
Self::CannotAddType { .. } => "Cannot add to this type",
|
||||
Self::CannotAddType { .. } => "Cannot add this type",
|
||||
Self::ComparisonChain { .. } => "Cannot chain comparison operations",
|
||||
Self::CannotDivideArguments { .. } => "Cannot divide these types",
|
||||
Self::CannotDivideType { .. } => "Cannot divide this type",
|
||||
@ -222,6 +222,12 @@ impl AnnotatedError for CompileError {
|
||||
|
||||
fn detail_snippets(&self) -> Vec<(String, Span)> {
|
||||
match self {
|
||||
Self::CannotAddType {
|
||||
argument_type,
|
||||
position,
|
||||
} => {
|
||||
vec![(format!("Cannot add type `{}`", argument_type), *position)]
|
||||
}
|
||||
Self::CannotAddArguments {
|
||||
left_type,
|
||||
left_position,
|
||||
@ -229,12 +235,9 @@ impl AnnotatedError for CompileError {
|
||||
right_position,
|
||||
} => {
|
||||
vec![
|
||||
(format!("`{left_type}` value was used here"), *left_position),
|
||||
(
|
||||
format!("A value of type \"{left_type}\" was used here."),
|
||||
*left_position,
|
||||
),
|
||||
(
|
||||
format!("A value of type \"{right_type}\" was used here."),
|
||||
format!("`{right_type}` value was used here"),
|
||||
*right_position,
|
||||
),
|
||||
]
|
||||
@ -242,7 +245,7 @@ impl AnnotatedError for CompileError {
|
||||
Self::ReturnTypeConflict { conflict, position } => {
|
||||
vec![(
|
||||
format!(
|
||||
"Expected type {} but found type {}",
|
||||
"Expected type `{}` but found type `{}`",
|
||||
conflict.expected, conflict.actual
|
||||
),
|
||||
*position,
|
||||
@ -262,7 +265,7 @@ impl AnnotatedError for CompileError {
|
||||
} => {
|
||||
vec![(
|
||||
format!(
|
||||
"Type \"{left_type}\" cannot be, added to type \"{right_type}\". Try converting one of the values to the other type."
|
||||
"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),
|
||||
)]
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,64 +0,0 @@
|
||||
//! Functions used by the compiler to optimize a chunk's bytecode during compilation.
|
||||
|
||||
use tracing::debug;
|
||||
|
||||
use crate::{Compiler, Instruction, Operation};
|
||||
|
||||
/// Optimizes a control flow pattern to use fewer registers and avoid using a `POINT` instruction.
|
||||
/// Use this after parsing an if/else statement.
|
||||
///
|
||||
/// This makes the following examples compile to the same bytecode:
|
||||
///
|
||||
/// ```dust
|
||||
/// 4 == 4
|
||||
/// ```
|
||||
///
|
||||
/// ```dust
|
||||
/// if 4 == 4 { true } else { false }
|
||||
/// ```
|
||||
///
|
||||
/// When they occur in the sequence shown below, instructions can be optimized by taking advantage
|
||||
/// of the loaders' ability to skip an instruction after loading a value. If these instructions are
|
||||
/// the result of a binary expression, this will not change anything because they were already
|
||||
/// emitted optimally. Control flow patterns, however, can be optimized because the load
|
||||
/// instructions are from seperate expressions that each uses its own register. Since only one of
|
||||
/// the two branches will be executed, this is wasteful. It would also require the compiler to emit
|
||||
/// a `POINT` instruction to prevent the VM from encountering an empty register.
|
||||
///
|
||||
/// The instructions must be in the following order:
|
||||
/// - `TEST` or any of the `EQUAL`, `LESS` or `LESS_EQUAL` instructions
|
||||
/// - `JUMP`
|
||||
/// - `LOAD_BOOLEAN` or `LOAD_CONSTANT`
|
||||
/// - `LOAD_BOOLEAN` or `LOAD_CONSTANT`
|
||||
///
|
||||
/// This optimization was taken from `A No-Frills Introduction to Lua 5.1 VM Instructions` by
|
||||
/// Kein-Hong Man.
|
||||
pub fn control_flow_register_consolidation(compiler: &mut Compiler) {
|
||||
if !matches!(
|
||||
compiler.get_last_operations(),
|
||||
Some([
|
||||
Operation::TEST | Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL,
|
||||
Operation::JUMP,
|
||||
Operation::LOAD_BOOLEAN | Operation::LOAD_CONSTANT,
|
||||
Operation::LOAD_BOOLEAN | Operation::LOAD_CONSTANT,
|
||||
])
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
debug!("Consolidating registers for control flow optimization");
|
||||
|
||||
let first_loader_index = compiler.instructions.len() - 2;
|
||||
let (first_loader, _, _) = &mut compiler.instructions.get_mut(first_loader_index).unwrap();
|
||||
let first_loader_destination = first_loader.a_field();
|
||||
*first_loader =
|
||||
Instruction::load_boolean(first_loader.a_field(), first_loader.b_field() != 0, true);
|
||||
|
||||
let second_loader_index = compiler.instructions.len() - 1;
|
||||
let (second_loader, _, _) = &mut compiler.instructions.get_mut(second_loader_index).unwrap();
|
||||
*second_loader = Instruction::load_boolean(
|
||||
first_loader_destination,
|
||||
second_loader.b_field() != 0,
|
||||
false,
|
||||
);
|
||||
}
|
@ -72,7 +72,7 @@ impl From<&Token<'_>> for ParseRule<'_> {
|
||||
Token::DoubleAmpersand => ParseRule {
|
||||
prefix: None,
|
||||
infix: Some(Compiler::parse_logical_binary),
|
||||
precedence: Precedence::LogicalAnd,
|
||||
precedence: Precedence::Logic,
|
||||
},
|
||||
Token::DoubleEqual => ParseRule {
|
||||
prefix: None,
|
||||
@ -82,7 +82,7 @@ impl From<&Token<'_>> for ParseRule<'_> {
|
||||
Token::DoublePipe => ParseRule {
|
||||
prefix: None,
|
||||
infix: Some(Compiler::parse_logical_binary),
|
||||
precedence: Precedence::LogicalOr,
|
||||
precedence: Precedence::Logic,
|
||||
},
|
||||
Token::DoubleDot => ParseRule {
|
||||
prefix: Some(Compiler::expect_expression),
|
||||
@ -284,14 +284,13 @@ impl From<&Token<'_>> for ParseRule<'_> {
|
||||
/// Operator precedence levels.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Precedence {
|
||||
Primary = 9,
|
||||
Call = 8,
|
||||
Unary = 7,
|
||||
Factor = 6,
|
||||
Term = 5,
|
||||
Comparison = 4,
|
||||
LogicalAnd = 3,
|
||||
LogicalOr = 2,
|
||||
Primary = 8,
|
||||
Call = 7,
|
||||
Unary = 6,
|
||||
Factor = 5,
|
||||
Term = 4,
|
||||
Comparison = 3,
|
||||
Logic = 2,
|
||||
Assignment = 1,
|
||||
None = 0,
|
||||
}
|
||||
@ -300,9 +299,8 @@ impl Precedence {
|
||||
pub fn increment(&self) -> Self {
|
||||
match self {
|
||||
Precedence::None => Precedence::Assignment,
|
||||
Precedence::Assignment => Precedence::LogicalOr,
|
||||
Precedence::LogicalOr => Precedence::LogicalAnd,
|
||||
Precedence::LogicalAnd => Precedence::Comparison,
|
||||
Precedence::Assignment => Precedence::Logic,
|
||||
Precedence::Logic => Precedence::Comparison,
|
||||
Precedence::Comparison => Precedence::Term,
|
||||
Precedence::Term => Precedence::Factor,
|
||||
Precedence::Factor => Precedence::Unary,
|
||||
|
@ -1,28 +1,22 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode};
|
||||
use super::{Instruction, InstructionFields, Operand, Operation, TypeCode};
|
||||
|
||||
pub struct Add {
|
||||
pub destination: u16,
|
||||
pub left: Operand,
|
||||
pub left_type: TypeCode,
|
||||
pub right: Operand,
|
||||
pub right_type: TypeCode,
|
||||
}
|
||||
|
||||
impl From<Instruction> for Add {
|
||||
fn from(instruction: Instruction) -> Self {
|
||||
impl From<&Instruction> for Add {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a_field();
|
||||
let (left, right) = instruction.b_and_c_as_operands();
|
||||
let left_type = instruction.b_type();
|
||||
let right_type = instruction.c_type();
|
||||
|
||||
Add {
|
||||
destination,
|
||||
left,
|
||||
left_type,
|
||||
right,
|
||||
right_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -33,10 +27,10 @@ impl From<Add> for Instruction {
|
||||
let a_field = add.destination;
|
||||
let (b_field, b_is_constant) = add.left.as_index_and_constant_flag();
|
||||
let (c_field, c_is_constant) = add.right.as_index_and_constant_flag();
|
||||
let b_type = add.left_type;
|
||||
let c_type = add.right_type;
|
||||
let b_type = add.left.as_type();
|
||||
let c_type = add.right.as_type();
|
||||
|
||||
InstructionBuilder {
|
||||
InstructionFields {
|
||||
operation,
|
||||
a_field,
|
||||
b_field,
|
||||
@ -56,14 +50,19 @@ impl Display for Add {
|
||||
let Add {
|
||||
destination,
|
||||
left,
|
||||
left_type,
|
||||
right,
|
||||
right_type,
|
||||
} = self;
|
||||
|
||||
write!(
|
||||
f,
|
||||
"R{destination} = {left}({left_type}) + {right}({right_type})",
|
||||
)
|
||||
match left.as_type() {
|
||||
TypeCode::BOOLEAN => write!(f, "R_BOOL_{destination}")?,
|
||||
TypeCode::BYTE => write!(f, "R_BYTE_{destination}")?,
|
||||
TypeCode::CHARACTER => write!(f, "R_STR_{destination}")?,
|
||||
TypeCode::FLOAT => write!(f, "R_FLOAT_{destination}")?,
|
||||
TypeCode::INTEGER => write!(f, "R_INT_{destination}")?,
|
||||
TypeCode::STRING => write!(f, "R_STR_{destination}")?,
|
||||
_ => todo!(),
|
||||
}
|
||||
|
||||
write!(f, " = {left} + {right}",)
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
use super::InstructionBuilder;
|
||||
use super::InstructionFields;
|
||||
|
||||
pub struct Call {
|
||||
pub destination: u16,
|
||||
@ -34,7 +34,7 @@ impl From<Call> for Instruction {
|
||||
let c_field = call.argument_count;
|
||||
let d_field = call.is_recursive;
|
||||
|
||||
InstructionBuilder {
|
||||
InstructionFields {
|
||||
operation: Operation::CALL,
|
||||
a_field,
|
||||
b_field,
|
||||
|
@ -1,24 +1,25 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use crate::{Instruction, NativeFunction, Operation};
|
||||
use crate::{Instruction, NativeFunction, Operation, Type};
|
||||
|
||||
use super::InstructionBuilder;
|
||||
use super::InstructionFields;
|
||||
|
||||
pub struct CallNative {
|
||||
pub destination: u16,
|
||||
pub function: NativeFunction,
|
||||
pub argument_count: u16,
|
||||
pub first_argument_index: u16,
|
||||
}
|
||||
|
||||
impl From<Instruction> for CallNative {
|
||||
fn from(instruction: Instruction) -> Self {
|
||||
let destination = instruction.a_field();
|
||||
let function = NativeFunction::from(instruction.b_field());
|
||||
let first_argument_index = instruction.c_field();
|
||||
|
||||
CallNative {
|
||||
destination,
|
||||
function,
|
||||
argument_count: instruction.c_field(),
|
||||
first_argument_index,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -28,9 +29,9 @@ impl From<CallNative> for Instruction {
|
||||
let operation = Operation::CALL_NATIVE;
|
||||
let a_field = call_native.destination;
|
||||
let b_field = call_native.function as u16;
|
||||
let c_field = call_native.argument_count;
|
||||
let c_field = call_native.first_argument_index;
|
||||
|
||||
InstructionBuilder {
|
||||
InstructionFields {
|
||||
operation,
|
||||
a_field,
|
||||
b_field,
|
||||
@ -46,21 +47,57 @@ impl Display for CallNative {
|
||||
let CallNative {
|
||||
destination,
|
||||
function,
|
||||
argument_count,
|
||||
first_argument_index,
|
||||
} = self;
|
||||
let arguments_start = destination.saturating_sub(*argument_count);
|
||||
let arguments_end = arguments_start + argument_count;
|
||||
let argument_count = function.r#type().value_parameters.len() as u16;
|
||||
|
||||
if function.returns_value() {
|
||||
write!(f, "R{destination} = ")?;
|
||||
}
|
||||
|
||||
write!(f, "{function}")?;
|
||||
|
||||
match argument_count {
|
||||
0 => {
|
||||
write!(f, "{function}()")
|
||||
write!(f, "()")
|
||||
}
|
||||
_ => {
|
||||
let arguments_end = first_argument_index + argument_count - 1;
|
||||
let arguments_index_range = *first_argument_index..=arguments_end;
|
||||
let function_type = function.r#type();
|
||||
let argument_types = function_type.value_parameters.iter();
|
||||
|
||||
write!(f, "(")?;
|
||||
|
||||
for (index, r#type) in arguments_index_range.zip(argument_types) {
|
||||
match r#type {
|
||||
Type::Boolean => {
|
||||
write!(f, "R_BOOL_{index}")
|
||||
}
|
||||
Type::Byte => {
|
||||
write!(f, "R_BYTE_{index}")
|
||||
}
|
||||
Type::Float => {
|
||||
write!(f, "R_FLOAT_{index}")
|
||||
}
|
||||
Type::Integer => {
|
||||
write!(f, "R_INT_{index}")
|
||||
}
|
||||
Type::String => {
|
||||
write!(f, "R_STR_{index}")
|
||||
}
|
||||
unsupported => {
|
||||
todo!("Support for {unsupported:?} arguments")
|
||||
}
|
||||
}?;
|
||||
|
||||
if index != arguments_end {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
|
||||
write!(f, ")")
|
||||
}
|
||||
1 => write!(f, "{function}(R{arguments_start})"),
|
||||
_ => write!(f, "{function}(R{arguments_start}..R{arguments_end})"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,18 +2,20 @@ use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
use super::InstructionBuilder;
|
||||
use super::{InstructionFields, TypeCode};
|
||||
|
||||
pub struct Close {
|
||||
pub from: u16,
|
||||
pub to: u16,
|
||||
pub r#type: TypeCode,
|
||||
}
|
||||
|
||||
impl From<Instruction> for Close {
|
||||
fn from(instruction: Instruction) -> Self {
|
||||
impl From<&Instruction> for Close {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
Close {
|
||||
from: instruction.b_field(),
|
||||
to: instruction.c_field(),
|
||||
r#type: instruction.b_type(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -22,11 +24,13 @@ impl From<Close> for Instruction {
|
||||
fn from(close: Close) -> Self {
|
||||
let operation = Operation::CLOSE;
|
||||
let b_field = close.from;
|
||||
let b_type = close.r#type;
|
||||
let c_field = close.to;
|
||||
|
||||
InstructionBuilder {
|
||||
InstructionFields {
|
||||
operation,
|
||||
b_field,
|
||||
b_type,
|
||||
c_field,
|
||||
..Default::default()
|
||||
}
|
||||
@ -36,8 +40,17 @@ impl From<Close> for Instruction {
|
||||
|
||||
impl Display for Close {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
let Close { from, to } = self;
|
||||
let Close { from, to, r#type } = self;
|
||||
|
||||
write!(f, "{from}..={to}")
|
||||
match *r#type {
|
||||
TypeCode::BOOLEAN => write!(f, "R_BOOL_{from}..=R_BOOL_{to}"),
|
||||
TypeCode::BYTE => write!(f, "R_BYTE_{from}..=R_BYTE_{to}"),
|
||||
TypeCode::CHARACTER => write!(f, "R_CHAR_{from}..=R_CHAR_{to}"),
|
||||
TypeCode::FLOAT => write!(f, "R_FLOAT_{from}..=R_FLOAT_{to}"),
|
||||
TypeCode::INTEGER => write!(f, "R_INT_{from}..=R_INT_{to}"),
|
||||
TypeCode::STRING => write!(f, "R_STR_{from}..=R_STR_{to}"),
|
||||
TypeCode::LIST => write!(f, "R_LIST_{from}..=R_LIST_{to}"),
|
||||
unsupported => panic!("Unsupported type code: {:?}", unsupported),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +1,22 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode};
|
||||
use super::{Instruction, InstructionFields, Operand, Operation, TypeCode};
|
||||
|
||||
pub struct Divide {
|
||||
pub destination: u16,
|
||||
pub left: Operand,
|
||||
pub left_type: TypeCode,
|
||||
pub right: Operand,
|
||||
pub right_type: TypeCode,
|
||||
}
|
||||
|
||||
impl From<Instruction> for Divide {
|
||||
fn from(instruction: Instruction) -> Self {
|
||||
let destination = instruction.a_field();
|
||||
let (left, right) = instruction.b_and_c_as_operands();
|
||||
let left_type = instruction.b_type();
|
||||
let right_type = instruction.c_type();
|
||||
|
||||
Divide {
|
||||
destination,
|
||||
left,
|
||||
left_type,
|
||||
right,
|
||||
right_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -33,10 +27,10 @@ impl From<Divide> for Instruction {
|
||||
let a_field = divide.destination;
|
||||
let (b_field, b_is_constant) = divide.left.as_index_and_constant_flag();
|
||||
let (c_field, c_is_constant) = divide.right.as_index_and_constant_flag();
|
||||
let b_type = divide.left_type;
|
||||
let c_type = divide.right_type;
|
||||
let b_type = divide.left.as_type();
|
||||
let c_type = divide.right.as_type();
|
||||
|
||||
InstructionBuilder {
|
||||
InstructionFields {
|
||||
operation,
|
||||
a_field,
|
||||
b_field,
|
||||
@ -56,14 +50,19 @@ impl Display for Divide {
|
||||
let Divide {
|
||||
destination,
|
||||
left,
|
||||
left_type,
|
||||
right,
|
||||
right_type,
|
||||
} = self;
|
||||
|
||||
write!(
|
||||
f,
|
||||
"R{destination} = {left_type}({left}) ÷ {right_type}({right})",
|
||||
)
|
||||
match left.as_type() {
|
||||
TypeCode::BOOLEAN => write!(f, "R_BOOL_{destination}")?,
|
||||
TypeCode::BYTE => write!(f, "R_BYTE_{destination}")?,
|
||||
TypeCode::CHARACTER => write!(f, "R_STR_{destination}")?,
|
||||
TypeCode::FLOAT => write!(f, "R_FLOAT_{destination}")?,
|
||||
TypeCode::INTEGER => write!(f, "R_INT_{destination}")?,
|
||||
TypeCode::STRING => write!(f, "R_STR_{destination}")?,
|
||||
_ => todo!(),
|
||||
}
|
||||
|
||||
write!(f, " = {left} ÷ {right}",)
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +1,22 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode};
|
||||
use super::{Instruction, InstructionFields, Operand, Operation};
|
||||
|
||||
pub struct Equal {
|
||||
pub comparator: bool,
|
||||
pub left: Operand,
|
||||
pub left_type: TypeCode,
|
||||
pub right: Operand,
|
||||
pub right_type: TypeCode,
|
||||
}
|
||||
|
||||
impl From<Instruction> for Equal {
|
||||
fn from(instruction: Instruction) -> Self {
|
||||
let comparator = instruction.d_field();
|
||||
let (left, right) = instruction.b_and_c_as_operands();
|
||||
let left_type = instruction.b_type();
|
||||
let right_type = instruction.c_type();
|
||||
|
||||
Equal {
|
||||
comparator,
|
||||
left,
|
||||
left_type,
|
||||
right,
|
||||
right_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -33,10 +27,10 @@ impl From<Equal> for Instruction {
|
||||
let (b_field, b_is_constant) = equal_bool.left.as_index_and_constant_flag();
|
||||
let (c_field, c_is_constant) = equal_bool.right.as_index_and_constant_flag();
|
||||
let d_field = equal_bool.comparator;
|
||||
let b_type = equal_bool.left_type;
|
||||
let c_type = equal_bool.right_type;
|
||||
let b_type = equal_bool.left.as_type();
|
||||
let c_type = equal_bool.right.as_type();
|
||||
|
||||
InstructionBuilder {
|
||||
InstructionFields {
|
||||
operation,
|
||||
b_field,
|
||||
c_field,
|
||||
@ -56,15 +50,10 @@ impl Display for Equal {
|
||||
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 }}"
|
||||
)
|
||||
write!(f, "if {left} {operator} {right} {{ JUMP +1 }}")
|
||||
}
|
||||
}
|
||||
|
@ -1,49 +0,0 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
use super::InstructionBuilder;
|
||||
|
||||
pub struct GetLocal {
|
||||
pub destination: u16,
|
||||
pub local_index: u16,
|
||||
}
|
||||
|
||||
impl From<Instruction> for GetLocal {
|
||||
fn from(instruction: Instruction) -> Self {
|
||||
let destination = instruction.a_field();
|
||||
let local_index = instruction.b_field();
|
||||
|
||||
GetLocal {
|
||||
destination,
|
||||
local_index,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GetLocal> for Instruction {
|
||||
fn from(get_local: GetLocal) -> Self {
|
||||
let operation = Operation::GET_LOCAL;
|
||||
let a_field = get_local.destination;
|
||||
let b_field = get_local.local_index;
|
||||
|
||||
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}")
|
||||
}
|
||||
}
|
@ -2,15 +2,15 @@ use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
use super::InstructionBuilder;
|
||||
use super::InstructionFields;
|
||||
|
||||
pub struct Jump {
|
||||
pub offset: u16,
|
||||
pub is_positive: bool,
|
||||
}
|
||||
|
||||
impl From<Instruction> for Jump {
|
||||
fn from(instruction: Instruction) -> Self {
|
||||
impl From<&Instruction> for Jump {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
Jump {
|
||||
offset: instruction.b_field(),
|
||||
is_positive: instruction.c_field() != 0,
|
||||
@ -24,7 +24,7 @@ impl From<Jump> for Instruction {
|
||||
let b_field = jump.offset;
|
||||
let c_field = jump.is_positive as u16;
|
||||
|
||||
InstructionBuilder {
|
||||
InstructionFields {
|
||||
operation,
|
||||
b_field,
|
||||
c_field,
|
||||
|
@ -1,28 +1,22 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode};
|
||||
use super::{Instruction, InstructionFields, Operand, Operation};
|
||||
|
||||
pub struct Less {
|
||||
pub comparator: bool,
|
||||
pub left: Operand,
|
||||
pub left_type: TypeCode,
|
||||
pub right: Operand,
|
||||
pub right_type: TypeCode,
|
||||
}
|
||||
|
||||
impl From<Instruction> for Less {
|
||||
fn from(instruction: Instruction) -> Self {
|
||||
let comparator = instruction.d_field();
|
||||
let (left, right) = instruction.b_and_c_as_operands();
|
||||
let left_type = instruction.b_type();
|
||||
let right_type = instruction.c_type();
|
||||
|
||||
Less {
|
||||
comparator,
|
||||
left,
|
||||
left_type,
|
||||
right,
|
||||
right_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -33,10 +27,10 @@ impl From<Less> for Instruction {
|
||||
let (b_field, b_is_constant) = less.left.as_index_and_constant_flag();
|
||||
let (c_field, c_is_constant) = less.right.as_index_and_constant_flag();
|
||||
let d_field = less.comparator;
|
||||
let b_type = less.left_type;
|
||||
let c_type = less.right_type;
|
||||
let b_type = less.left.as_type();
|
||||
let c_type = less.right.as_type();
|
||||
|
||||
InstructionBuilder {
|
||||
InstructionFields {
|
||||
operation,
|
||||
b_field,
|
||||
c_field,
|
||||
@ -56,15 +50,10 @@ impl Display for Less {
|
||||
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 }}"
|
||||
)
|
||||
write!(f, "if {left} {operator} {right} {{ JUMP +1 }}")
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +1,22 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode};
|
||||
use super::{Instruction, InstructionFields, Operand, Operation};
|
||||
|
||||
pub struct LessEqual {
|
||||
pub comparator: bool,
|
||||
pub left: Operand,
|
||||
pub left_type: TypeCode,
|
||||
pub right: Operand,
|
||||
pub right_type: TypeCode,
|
||||
}
|
||||
|
||||
impl From<Instruction> for LessEqual {
|
||||
fn from(instruction: Instruction) -> Self {
|
||||
let comparator = instruction.d_field();
|
||||
let (left, right) = instruction.b_and_c_as_operands();
|
||||
let left_type = instruction.b_type();
|
||||
let right_type = instruction.c_type();
|
||||
|
||||
LessEqual {
|
||||
comparator,
|
||||
left,
|
||||
left_type,
|
||||
right,
|
||||
right_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -33,10 +27,10 @@ impl From<LessEqual> for Instruction {
|
||||
let (b_field, b_is_constant) = less_equal_byte.left.as_index_and_constant_flag();
|
||||
let (c_field, c_is_constant) = less_equal_byte.right.as_index_and_constant_flag();
|
||||
let d_field = less_equal_byte.comparator;
|
||||
let b_type = less_equal_byte.left_type;
|
||||
let c_type = less_equal_byte.right_type;
|
||||
let b_type = less_equal_byte.left.as_type();
|
||||
let c_type = less_equal_byte.right.as_type();
|
||||
|
||||
InstructionBuilder {
|
||||
InstructionFields {
|
||||
operation,
|
||||
b_field,
|
||||
c_field,
|
||||
@ -56,15 +50,10 @@ impl Display for LessEqual {
|
||||
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 }}"
|
||||
)
|
||||
write!(f, "if {left} {operator} {right} {{ JUMP +1 }}")
|
||||
}
|
||||
}
|
||||
|
@ -1,57 +0,0 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
use super::InstructionBuilder;
|
||||
|
||||
pub struct LoadBoolean {
|
||||
pub destination: u16,
|
||||
pub value: bool,
|
||||
pub jump_next: bool,
|
||||
}
|
||||
|
||||
impl From<Instruction> for LoadBoolean {
|
||||
fn from(instruction: Instruction) -> Self {
|
||||
LoadBoolean {
|
||||
destination: instruction.a_field(),
|
||||
value: instruction.b_field() != 0,
|
||||
jump_next: instruction.c_field() != 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LoadBoolean> for Instruction {
|
||||
fn from(load_boolean: LoadBoolean) -> Self {
|
||||
let operation = Operation::LOAD_BOOLEAN;
|
||||
let a_field = load_boolean.destination;
|
||||
let b_field = load_boolean.value as u16;
|
||||
let c_field = load_boolean.jump_next as u16;
|
||||
|
||||
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,11 +2,12 @@ use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
use super::InstructionBuilder;
|
||||
use super::{InstructionFields, TypeCode};
|
||||
|
||||
pub struct LoadConstant {
|
||||
pub destination: u16,
|
||||
pub constant_index: u16,
|
||||
pub constant_type: TypeCode,
|
||||
pub jump_next: bool,
|
||||
}
|
||||
|
||||
@ -14,11 +15,13 @@ impl From<Instruction> for LoadConstant {
|
||||
fn from(instruction: Instruction) -> Self {
|
||||
let destination = instruction.a_field();
|
||||
let constant_index = instruction.b_field();
|
||||
let constant_type = instruction.b_type();
|
||||
let jump_next = instruction.c_field() != 0;
|
||||
|
||||
LoadConstant {
|
||||
destination,
|
||||
constant_index,
|
||||
constant_type,
|
||||
jump_next,
|
||||
}
|
||||
}
|
||||
@ -26,10 +29,11 @@ impl From<Instruction> for LoadConstant {
|
||||
|
||||
impl From<LoadConstant> for Instruction {
|
||||
fn from(load_constant: LoadConstant) -> Self {
|
||||
InstructionBuilder {
|
||||
InstructionFields {
|
||||
operation: Operation::LOAD_CONSTANT,
|
||||
a_field: load_constant.destination,
|
||||
b_field: load_constant.constant_index,
|
||||
b_type: load_constant.constant_type,
|
||||
c_field: load_constant.jump_next as u16,
|
||||
..Default::default()
|
||||
}
|
||||
@ -42,10 +46,21 @@ impl Display for LoadConstant {
|
||||
let LoadConstant {
|
||||
destination,
|
||||
constant_index,
|
||||
constant_type,
|
||||
jump_next,
|
||||
} = self;
|
||||
|
||||
write!(f, "R{destination} = C{constant_index}")?;
|
||||
match *constant_type {
|
||||
TypeCode::BOOLEAN => write!(f, "R_BOOL_{destination}")?,
|
||||
TypeCode::BYTE => write!(f, "R_BYTE_{destination}")?,
|
||||
TypeCode::CHARACTER => write!(f, "R_CHAR_{destination}")?,
|
||||
TypeCode::FLOAT => write!(f, "R_FLOAT_{destination}")?,
|
||||
TypeCode::INTEGER => write!(f, "R_INT_{destination}")?,
|
||||
TypeCode::STRING => write!(f, "R_STR_{destination}")?,
|
||||
unsupported => panic!("Unsupported type code: {}", unsupported.0),
|
||||
}
|
||||
|
||||
write!(f, " = C{constant_index}")?;
|
||||
|
||||
if *jump_next {
|
||||
write!(f, " JUMP +1")?;
|
||||
|
70
dust-lang/src/instruction/load_encoded.rs
Normal file
70
dust-lang/src/instruction/load_encoded.rs
Normal file
@ -0,0 +1,70 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
use super::{InstructionFields, TypeCode};
|
||||
|
||||
pub struct LoadEncoded {
|
||||
pub destination: u16,
|
||||
pub value: u8,
|
||||
pub value_type: TypeCode,
|
||||
pub jump_next: bool,
|
||||
}
|
||||
|
||||
impl From<Instruction> for LoadEncoded {
|
||||
fn from(instruction: Instruction) -> Self {
|
||||
LoadEncoded {
|
||||
destination: instruction.a_field(),
|
||||
value: instruction.b_field() as u8,
|
||||
value_type: instruction.b_type(),
|
||||
jump_next: instruction.c_field() != 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LoadEncoded> for Instruction {
|
||||
fn from(load_boolean: LoadEncoded) -> Self {
|
||||
let operation = Operation::LOAD_ENCODED;
|
||||
let a_field = load_boolean.destination;
|
||||
let b_field = load_boolean.value as u16;
|
||||
let b_type = load_boolean.value_type;
|
||||
let c_field = load_boolean.jump_next as u16;
|
||||
|
||||
InstructionFields {
|
||||
operation,
|
||||
a_field,
|
||||
b_field,
|
||||
b_type,
|
||||
c_field,
|
||||
..Default::default()
|
||||
}
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for LoadEncoded {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
let LoadEncoded {
|
||||
destination,
|
||||
value,
|
||||
value_type,
|
||||
jump_next,
|
||||
} = self;
|
||||
|
||||
match *value_type {
|
||||
TypeCode::BOOLEAN => {
|
||||
let boolean = *value != 0;
|
||||
|
||||
write!(f, "R_BOOL_{destination} = {boolean}")?
|
||||
}
|
||||
TypeCode::BYTE => write!(f, "R_BYTE_{destination} = 0x{value:0X}")?,
|
||||
_ => panic!("Invalid type code {value_type} for LoadEncoded instruction"),
|
||||
}
|
||||
|
||||
if *jump_next {
|
||||
write!(f, " JUMP +1")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use super::{Instruction, InstructionBuilder, Operation};
|
||||
use super::{Instruction, InstructionFields, Operation};
|
||||
|
||||
pub struct LoadFunction {
|
||||
pub destination: u16,
|
||||
@ -24,7 +24,7 @@ impl From<Instruction> for LoadFunction {
|
||||
|
||||
impl From<LoadFunction> for Instruction {
|
||||
fn from(load_function: LoadFunction) -> Self {
|
||||
InstructionBuilder {
|
||||
InstructionFields {
|
||||
operation: Operation::LOAD_FUNCTION,
|
||||
a_field: load_function.destination,
|
||||
b_field: load_function.prototype_index,
|
||||
|
@ -2,11 +2,13 @@ use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
use super::InstructionBuilder;
|
||||
use super::{InstructionFields, TypeCode};
|
||||
|
||||
pub struct LoadList {
|
||||
pub destination: u16,
|
||||
pub item_type: TypeCode,
|
||||
pub start_register: u16,
|
||||
pub end_register: u16,
|
||||
pub jump_next: bool,
|
||||
}
|
||||
|
||||
@ -14,11 +16,15 @@ impl From<Instruction> for LoadList {
|
||||
fn from(instruction: Instruction) -> Self {
|
||||
let destination = instruction.a_field();
|
||||
let start_register = instruction.b_field();
|
||||
let jump_next = instruction.c_field() != 0;
|
||||
let item_type = instruction.b_type();
|
||||
let end_register = instruction.c_field();
|
||||
let jump_next = instruction.d_field();
|
||||
|
||||
LoadList {
|
||||
destination,
|
||||
item_type,
|
||||
start_register,
|
||||
end_register,
|
||||
jump_next,
|
||||
}
|
||||
}
|
||||
@ -26,11 +32,13 @@ impl From<Instruction> for LoadList {
|
||||
|
||||
impl From<LoadList> for Instruction {
|
||||
fn from(load_list: LoadList) -> Self {
|
||||
InstructionBuilder {
|
||||
InstructionFields {
|
||||
operation: Operation::LOAD_LIST,
|
||||
a_field: load_list.destination,
|
||||
b_field: load_list.start_register,
|
||||
c_field: load_list.jump_next as u16,
|
||||
b_type: load_list.item_type,
|
||||
c_field: load_list.end_register,
|
||||
d_field: load_list.jump_next,
|
||||
..Default::default()
|
||||
}
|
||||
.build()
|
||||
@ -41,11 +49,43 @@ impl Display for LoadList {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
let LoadList {
|
||||
destination,
|
||||
item_type,
|
||||
start_register,
|
||||
end_register,
|
||||
jump_next,
|
||||
} = self;
|
||||
|
||||
write!(f, "R{destination} = [R{start_register}..R{destination}]")?;
|
||||
write!(f, "R_LIST_{destination} = [")?;
|
||||
|
||||
match *item_type {
|
||||
TypeCode::BOOLEAN => {
|
||||
write!(f, "R_BOOL_{start_register}..=R_BOOL_{end_register}")?;
|
||||
}
|
||||
TypeCode::BYTE => {
|
||||
write!(f, "R_BYTE_{start_register}..=R_BYTE_{end_register}")?;
|
||||
}
|
||||
TypeCode::CHARACTER => {
|
||||
write!(f, "R_CHAR_{start_register}..=R_CHAR_{end_register}")?;
|
||||
}
|
||||
TypeCode::FLOAT => {
|
||||
write!(f, "R_FLOAT_{start_register}..=R_FLOAT_{end_register}")?;
|
||||
}
|
||||
TypeCode::INTEGER => {
|
||||
write!(f, "R_INT_{start_register}..=R_INT_{end_register}")?;
|
||||
}
|
||||
TypeCode::STRING => {
|
||||
write!(f, "R_STR_{start_register}..=R_STR_{end_register}")?;
|
||||
}
|
||||
TypeCode::LIST => {
|
||||
write!(f, "R_LIST_{start_register}..=R_LIST_{end_register}")?;
|
||||
}
|
||||
TypeCode::FUNCTION => {
|
||||
write!(f, "R_FN_{start_register}..=R_FN_{end_register}")?;
|
||||
}
|
||||
unknown => panic!("Unknown type code: {}", unknown.0),
|
||||
}
|
||||
|
||||
write!(f, "]")?;
|
||||
|
||||
if *jump_next {
|
||||
write!(f, " JUMP +1")?;
|
||||
|
@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
use super::InstructionBuilder;
|
||||
use super::InstructionFields;
|
||||
|
||||
pub struct LoadSelf {
|
||||
pub destination: u16,
|
||||
@ -23,7 +23,7 @@ impl From<Instruction> for LoadSelf {
|
||||
|
||||
impl From<LoadSelf> for Instruction {
|
||||
fn from(load_self: LoadSelf) -> Self {
|
||||
InstructionBuilder {
|
||||
InstructionFields {
|
||||
operation: Operation::LOAD_SELF,
|
||||
a_field: load_self.destination,
|
||||
c_field: load_self.jump_next as u16,
|
||||
|
@ -1,59 +1,45 @@
|
||||
//! The Dust instruction set.
|
||||
//!
|
||||
//! Each instruction is 64 bits and uses up to seven distinct fields.
|
||||
//! Each instruction is 64 bits and uses up to nine distinct fields.
|
||||
//!
|
||||
//! # Layout
|
||||
//!
|
||||
//! Bits | Description
|
||||
//! ----- | -----------
|
||||
//! 0-6 | Operation
|
||||
//! 7 | Flag indicating if the B field is a constant
|
||||
//! 8 | Flag indicating if the C field is a constant
|
||||
//! 9 | D field (boolean)
|
||||
//! 10-12 | B field type
|
||||
//! 13-15 | C field type
|
||||
//! 0-4 | Operation
|
||||
//! 5-8 | B field type
|
||||
//! 9 | Flag indicating if the B field is a constant
|
||||
//! 10-13 | C field type
|
||||
//! 14 | Flag indicating if the C field is a constant
|
||||
//! 15 | D field (boolean)
|
||||
//! 16-31 | A field (unsigned 16-bit integer)
|
||||
//! 32-47 | B field (unsigned 16-bit integer)
|
||||
//! 48-63 | C field (unsigned 16-bit integer)
|
||||
//!
|
||||
//! **Be careful when working with instructions directly**. When modifying an instruction's fields,
|
||||
//! you may also need to modify its flags. It is usually best to remove instructions and insert new
|
||||
//! ones in their place instead of mutating them.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ## Creating Instructions
|
||||
//! # Creating Instructions
|
||||
//!
|
||||
//! For each operation, there are two ways to create an instruction:
|
||||
//!
|
||||
//! - Use the associated function on `Instruction`
|
||||
//! - Use the corresponding struct and call `Instruction::from`
|
||||
//!
|
||||
//! Both produce the same result, but the first is more concise. The structs are more useful when
|
||||
//! reading instructions, as shown below.
|
||||
//! Both produce the same result, but the first is usuall more concise. The structs are more useful
|
||||
//! when reading instructions, as shown below.
|
||||
//!
|
||||
//! Use the [`Operand`][] type when creating instructions. In addition to being easy to read and
|
||||
//! write, this ensures that the instruction has the correct flags to represent the operands.
|
||||
//!
|
||||
//! ```
|
||||
//! # use dust_lang::instruction::{Instruction, Move};
|
||||
//! let move_1 = Instruction::r#move(42, 4);
|
||||
//! let move_2 = Instruction::from(Move { from: 42, to: 4 });
|
||||
//!
|
||||
//! assert_eq!(move_1, move_2);
|
||||
//! ```
|
||||
//!
|
||||
//! Use the [`Argument`][] type when creating instructions. In addition to being easy to read and
|
||||
//! write, this ensures that the instruction has the correct flags to represent the arguments.
|
||||
//!
|
||||
//! ```
|
||||
//! # use dust_lang::instruction::{Instruction, Add, Argument};
|
||||
//! # use dust_lang::instruction::{Instruction, Add, Operand, TypeCode};
|
||||
//! let add_1 = Instruction::add(
|
||||
//! 0,
|
||||
//! Argument::Register(1),
|
||||
//! Argument::Constant(2)
|
||||
//! Operand::Register(1, TypeCode::INTEGER),
|
||||
//! Operand::Constant(2, TypeCode::INTEGER)
|
||||
//! );
|
||||
//! let add_2 = Instruction::from(Add {
|
||||
//! destination: 0,
|
||||
//! left: Argument::Register(1),
|
||||
//! right: Argument::Constant(2),
|
||||
//! left: Operand::Register(1, TypeCode::INTEGER),
|
||||
//! right: Operand::Constant(2, TypeCode::INTEGER),
|
||||
//! });
|
||||
//!
|
||||
//! assert_eq!(add_1, add_2);
|
||||
@ -64,28 +50,27 @@
|
||||
//! To read an instruction, check its operation with [`Instruction::operation`], then convert the
|
||||
//! instruction to the struct that corresponds to that operation. Like the example above, this
|
||||
//! removes the burden of dealing with the options directly and automatically casts the A, B, C and
|
||||
//! D fields as `u16`, `bool` or `Argument` values.
|
||||
//! D fields as `u16`, `bool` or `Operand` values.
|
||||
//!
|
||||
//! ```
|
||||
//! # use dust_lang::instruction::{Instruction, Add, Argument, Operation};
|
||||
//! # use dust_lang::instruction::{Instruction, Add, Operand, Operation, TypeCode};
|
||||
//! # let mystery_instruction = Instruction::add(
|
||||
//! # 1,
|
||||
//! # Argument::Register(1),
|
||||
//! # Argument::Constant(2)
|
||||
//! # Operand::Register(1, TypeCode::INTEGER),
|
||||
//! # Operand::Constant(2, TypeCode::INTEGER)
|
||||
//! # );
|
||||
//! // Let's read an instruction and see if it performs addition-assignment,
|
||||
//! // like in one of the following examples:
|
||||
//! // - `a += 2`
|
||||
//! // - `a = a + 2`
|
||||
//! // - `a = 2 + a`
|
||||
//!
|
||||
//! let operation = mystery_instruction.operation();
|
||||
//! let is_add_assign = match operation {
|
||||
//! Operation::Add => {
|
||||
//! Operation::ADD => {
|
||||
//! let Add { destination, left, right } = Add::from(&mystery_instruction);
|
||||
//!
|
||||
//! left == Argument::Register(destination)
|
||||
//! || right == Argument::Register(destination);
|
||||
//! left == Operand::Register(destination, TypeCode::INTEGER)
|
||||
//! || right == Operand::Register(destination, TypeCode::INTEGER)
|
||||
//!
|
||||
//! }
|
||||
//! _ => false,
|
||||
@ -99,23 +84,21 @@ mod call_native;
|
||||
mod close;
|
||||
mod divide;
|
||||
mod equal;
|
||||
mod get_local;
|
||||
mod jump;
|
||||
mod less;
|
||||
mod less_equal;
|
||||
mod load_boolean;
|
||||
mod load_constant;
|
||||
mod load_encoded;
|
||||
mod load_function;
|
||||
mod load_list;
|
||||
mod load_self;
|
||||
mod modulo;
|
||||
mod r#move;
|
||||
mod multiply;
|
||||
mod negate;
|
||||
mod not;
|
||||
mod operation;
|
||||
mod point;
|
||||
mod r#return;
|
||||
mod set_local;
|
||||
mod subtract;
|
||||
mod test;
|
||||
mod test_set;
|
||||
@ -127,12 +110,11 @@ pub use call_native::CallNative;
|
||||
pub use close::Close;
|
||||
pub use divide::Divide;
|
||||
pub use equal::Equal;
|
||||
pub use get_local::GetLocal;
|
||||
pub use jump::Jump;
|
||||
pub use less::Less;
|
||||
pub use less_equal::LessEqual;
|
||||
pub use load_boolean::LoadBoolean;
|
||||
pub use load_constant::LoadConstant;
|
||||
pub use load_encoded::LoadEncoded;
|
||||
pub use load_function::LoadFunction;
|
||||
pub use load_list::LoadList;
|
||||
pub use load_self::LoadSelf;
|
||||
@ -141,9 +123,8 @@ pub use multiply::Multiply;
|
||||
pub use negate::Negate;
|
||||
pub use not::Not;
|
||||
pub use operation::Operation;
|
||||
pub use point::Point;
|
||||
pub use r#move::Move;
|
||||
pub use r#return::Return;
|
||||
pub use set_local::SetLocal;
|
||||
pub use subtract::Subtract;
|
||||
pub use test::Test;
|
||||
pub use test_set::TestSet;
|
||||
@ -154,7 +135,8 @@ pub use type_code::TypeCode;
|
||||
|
||||
use crate::NativeFunction;
|
||||
|
||||
pub struct InstructionBuilder {
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct InstructionFields {
|
||||
pub operation: Operation,
|
||||
pub a_field: u16,
|
||||
pub b_field: u16,
|
||||
@ -166,14 +148,14 @@ pub struct InstructionBuilder {
|
||||
pub c_type: TypeCode,
|
||||
}
|
||||
|
||||
impl InstructionBuilder {
|
||||
impl InstructionFields {
|
||||
pub fn build(self) -> Instruction {
|
||||
let bits = ((self.operation.0 as u64) << 57)
|
||||
| ((self.b_is_constant as u64) << 56)
|
||||
| ((self.c_is_constant as u64) << 55)
|
||||
| ((self.d_field as u64) << 54)
|
||||
| ((self.b_type.0 as u64) << 51)
|
||||
| ((self.c_type.0 as u64) << 48)
|
||||
let bits = ((self.operation.0 as u64) << 59)
|
||||
| ((self.b_type.0 as u64) << 54)
|
||||
| ((self.b_is_constant as u64) << 53)
|
||||
| ((self.c_type.0 as u64) << 49)
|
||||
| ((self.c_is_constant as u64) << 48)
|
||||
| ((self.d_field as u64) << 47)
|
||||
| ((self.a_field as u64) << 32)
|
||||
| ((self.b_field as u64) << 16)
|
||||
| (self.c_field as u64);
|
||||
@ -182,10 +164,26 @@ impl InstructionBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for InstructionBuilder {
|
||||
impl From<&Instruction> for InstructionFields {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
InstructionFields {
|
||||
operation: instruction.operation(),
|
||||
a_field: instruction.a_field(),
|
||||
b_field: instruction.b_field(),
|
||||
c_field: instruction.c_field(),
|
||||
d_field: instruction.d_field(),
|
||||
b_is_constant: instruction.b_is_constant(),
|
||||
c_is_constant: instruction.c_is_constant(),
|
||||
b_type: instruction.b_type(),
|
||||
c_type: instruction.c_type(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for InstructionFields {
|
||||
fn default() -> Self {
|
||||
InstructionBuilder {
|
||||
operation: Operation::POINT,
|
||||
InstructionFields {
|
||||
operation: Operation::MOVE,
|
||||
a_field: 0,
|
||||
b_field: 0,
|
||||
c_field: 0,
|
||||
@ -206,79 +204,207 @@ pub struct Instruction(u64);
|
||||
|
||||
impl Instruction {
|
||||
pub fn operation(&self) -> Operation {
|
||||
let first_7_bits = (self.0 >> 57) as u8;
|
||||
let first_5_bits = (self.0 >> 59) as u8;
|
||||
|
||||
Operation(first_7_bits)
|
||||
}
|
||||
|
||||
pub fn b_is_constant(&self) -> bool {
|
||||
(self.0 >> 56) & 1 != 0
|
||||
}
|
||||
|
||||
pub fn c_is_constant(&self) -> bool {
|
||||
(self.0 >> 55) & 1 != 0
|
||||
}
|
||||
|
||||
pub fn d_field(&self) -> bool {
|
||||
(self.0 >> 54) & 1 != 0
|
||||
Operation(first_5_bits)
|
||||
}
|
||||
|
||||
pub fn b_type(&self) -> TypeCode {
|
||||
let byte = ((self.0 >> 51) & 0b111) as u8;
|
||||
let bits_5_to_8 = (self.0 >> 54) & 0b1111;
|
||||
|
||||
TypeCode(byte)
|
||||
TypeCode(bits_5_to_8 as u8)
|
||||
}
|
||||
|
||||
pub fn b_is_constant(&self) -> bool {
|
||||
let bit_9 = (self.0 >> 53) & 1;
|
||||
|
||||
bit_9 != 0
|
||||
}
|
||||
|
||||
pub fn c_type(&self) -> TypeCode {
|
||||
let byte = ((self.0 >> 48) & 0b111) as u8;
|
||||
let bits_10_to_13 = (self.0 >> 49) & 0b1111;
|
||||
|
||||
TypeCode(byte)
|
||||
TypeCode(bits_10_to_13 as u8)
|
||||
}
|
||||
|
||||
pub fn c_is_constant(&self) -> bool {
|
||||
let bit_14 = (self.0 >> 48) & 1;
|
||||
|
||||
bit_14 != 0
|
||||
}
|
||||
|
||||
pub fn d_field(&self) -> bool {
|
||||
let bit_15 = (self.0 >> 47) & 1;
|
||||
|
||||
bit_15 != 0
|
||||
}
|
||||
|
||||
pub fn a_field(&self) -> u16 {
|
||||
((self.0 >> 32) & 0xFFFF) as u16
|
||||
let bits_16_to_31 = (self.0 >> 32) & 0xFFFF;
|
||||
|
||||
bits_16_to_31 as u16
|
||||
}
|
||||
|
||||
pub fn b_field(&self) -> u16 {
|
||||
((self.0 >> 16) & 0xFFFF) as u16
|
||||
let bits_32_to_47 = (self.0 >> 16) & 0xFFFF;
|
||||
|
||||
bits_32_to_47 as u16
|
||||
}
|
||||
|
||||
pub fn c_field(&self) -> u16 {
|
||||
(self.0 & 0xFFFF) as u16
|
||||
let bits_48_to_63 = self.0 & 0xFFFF;
|
||||
|
||||
bits_48_to_63 as u16
|
||||
}
|
||||
|
||||
pub fn set_a_field(&mut self, bits: u16) {
|
||||
self.0 = (bits as u64) << 31;
|
||||
let mut fields = InstructionFields::from(&*self);
|
||||
fields.a_field = bits;
|
||||
*self = fields.build();
|
||||
}
|
||||
|
||||
pub fn set_b_field(&mut self, bits: u16) {
|
||||
self.0 = (bits as u64) << 47;
|
||||
let mut fields = InstructionFields::from(&*self);
|
||||
fields.b_field = bits;
|
||||
*self = fields.build();
|
||||
}
|
||||
|
||||
pub fn set_c_field(&mut self, bits: u16) {
|
||||
self.0 = (bits as u64) << 63;
|
||||
let mut fields = InstructionFields::from(&*self);
|
||||
fields.c_field = bits;
|
||||
*self = fields.build();
|
||||
}
|
||||
|
||||
pub fn point(from: u16, to: u16) -> Instruction {
|
||||
Instruction::from(Point { from, to })
|
||||
pub fn as_operand(&self) -> Operand {
|
||||
match self.operation() {
|
||||
Operation::MOVE => {
|
||||
let Move { operand: to, .. } = Move::from(self);
|
||||
|
||||
Operand::Register(to.index(), to.as_type())
|
||||
}
|
||||
Operation::LOAD_ENCODED => {
|
||||
let LoadEncoded {
|
||||
destination,
|
||||
value_type,
|
||||
..
|
||||
} = LoadEncoded::from(*self);
|
||||
|
||||
Operand::Register(destination, value_type)
|
||||
}
|
||||
Operation::LOAD_CONSTANT => {
|
||||
let LoadConstant {
|
||||
constant_type,
|
||||
constant_index,
|
||||
..
|
||||
} = LoadConstant::from(*self);
|
||||
|
||||
Operand::Constant(constant_index, constant_type)
|
||||
}
|
||||
Operation::LOAD_LIST => {
|
||||
let LoadList { destination, .. } = LoadList::from(*self);
|
||||
|
||||
Operand::Register(destination, TypeCode::LIST)
|
||||
}
|
||||
Operation::LOAD_FUNCTION => {
|
||||
let LoadFunction { destination, .. } = LoadFunction::from(*self);
|
||||
|
||||
Operand::Register(destination, TypeCode::FUNCTION)
|
||||
}
|
||||
Operation::LOAD_SELF => {
|
||||
let LoadSelf { destination, .. } = LoadSelf::from(*self);
|
||||
|
||||
Operand::Register(destination, TypeCode::FUNCTION)
|
||||
}
|
||||
Operation::ADD => {
|
||||
let Add {
|
||||
destination, left, ..
|
||||
} = Add::from(self);
|
||||
|
||||
let register_type = match left.as_type() {
|
||||
TypeCode::BOOLEAN => TypeCode::BOOLEAN,
|
||||
TypeCode::BYTE => TypeCode::BYTE,
|
||||
TypeCode::CHARACTER => TypeCode::STRING, // Adding characters concatenates them
|
||||
TypeCode::INTEGER => TypeCode::INTEGER,
|
||||
TypeCode::FLOAT => TypeCode::FLOAT,
|
||||
TypeCode::STRING => TypeCode::STRING,
|
||||
TypeCode::LIST => TypeCode::LIST,
|
||||
TypeCode::FUNCTION => TypeCode::FUNCTION,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
Operand::Register(destination, register_type)
|
||||
}
|
||||
Operation::SUBTRACT => {
|
||||
let Subtract {
|
||||
destination, left, ..
|
||||
} = Subtract::from(*self);
|
||||
|
||||
Operand::Register(destination, left.as_type())
|
||||
}
|
||||
Operation::MULTIPLY => {
|
||||
let Multiply {
|
||||
destination, left, ..
|
||||
} = Multiply::from(*self);
|
||||
|
||||
Operand::Register(destination, left.as_type())
|
||||
}
|
||||
Operation::DIVIDE => {
|
||||
let Divide {
|
||||
destination, left, ..
|
||||
} = Divide::from(*self);
|
||||
|
||||
Operand::Register(destination, left.as_type())
|
||||
}
|
||||
Operation::MODULO => {
|
||||
let Modulo {
|
||||
destination, left, ..
|
||||
} = Modulo::from(*self);
|
||||
|
||||
Operand::Register(destination, left.as_type())
|
||||
}
|
||||
unsupported => todo!("Support {unsupported}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn close(from: u16, to: u16) -> Instruction {
|
||||
Instruction::from(Close { from, to })
|
||||
pub fn no_op() -> Instruction {
|
||||
Instruction(Operation::NO_OP.0 as u64)
|
||||
}
|
||||
|
||||
pub fn load_boolean(destination: u16, value: bool, jump_next: bool) -> Instruction {
|
||||
Instruction::from(LoadBoolean {
|
||||
pub fn r#move(destination: u16, to: Operand) -> Instruction {
|
||||
Instruction::from(Move {
|
||||
destination,
|
||||
operand: to,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn close(from: u16, to: u16, r#type: TypeCode) -> Instruction {
|
||||
Instruction::from(Close { from, to, r#type })
|
||||
}
|
||||
|
||||
pub fn load_encoded(
|
||||
destination: u16,
|
||||
value: u8,
|
||||
value_type: TypeCode,
|
||||
jump_next: bool,
|
||||
) -> Instruction {
|
||||
Instruction::from(LoadEncoded {
|
||||
destination,
|
||||
value,
|
||||
value_type,
|
||||
jump_next,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn load_constant(destination: u16, constant_index: u16, jump_next: bool) -> Instruction {
|
||||
pub fn load_constant(
|
||||
destination: u16,
|
||||
constant_index: u16,
|
||||
constant_type: TypeCode,
|
||||
jump_next: bool,
|
||||
) -> Instruction {
|
||||
Instruction::from(LoadConstant {
|
||||
destination,
|
||||
constant_index,
|
||||
constant_type,
|
||||
jump_next,
|
||||
})
|
||||
}
|
||||
@ -291,10 +417,18 @@ impl Instruction {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn load_list(destination: u16, start_register: u16, jump_next: bool) -> Instruction {
|
||||
pub fn load_list(
|
||||
destination: u16,
|
||||
item_type: TypeCode,
|
||||
start_register: u16,
|
||||
end_register: u16,
|
||||
jump_next: bool,
|
||||
) -> Instruction {
|
||||
Instruction::from(LoadList {
|
||||
destination,
|
||||
item_type,
|
||||
start_register,
|
||||
end_register,
|
||||
jump_next,
|
||||
})
|
||||
}
|
||||
@ -306,145 +440,67 @@ impl Instruction {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_local(destination: u16, local_index: u16) -> Instruction {
|
||||
Instruction::from(GetLocal {
|
||||
destination,
|
||||
local_index,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_local(register: u16, local_index: u16) -> Instruction {
|
||||
Instruction::from(SetLocal {
|
||||
local_index,
|
||||
register_index: register,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add(
|
||||
destination: u16,
|
||||
left: Operand,
|
||||
left_type: TypeCode,
|
||||
right: Operand,
|
||||
right_type: TypeCode,
|
||||
) -> Instruction {
|
||||
pub fn add(destination: u16, left: Operand, right: Operand) -> Instruction {
|
||||
Instruction::from(Add {
|
||||
destination,
|
||||
left,
|
||||
left_type,
|
||||
right,
|
||||
right_type,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn subtract(
|
||||
destination: u16,
|
||||
left: Operand,
|
||||
left_type: TypeCode,
|
||||
right: Operand,
|
||||
right_type: TypeCode,
|
||||
) -> Instruction {
|
||||
pub fn subtract(destination: u16, left: Operand, right: Operand) -> Instruction {
|
||||
Instruction::from(Subtract {
|
||||
destination,
|
||||
left,
|
||||
left_type,
|
||||
right,
|
||||
right_type,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn multiply(
|
||||
destination: u16,
|
||||
left: Operand,
|
||||
left_type: TypeCode,
|
||||
right: Operand,
|
||||
right_type: TypeCode,
|
||||
) -> Instruction {
|
||||
pub fn multiply(destination: u16, left: Operand, right: Operand) -> Instruction {
|
||||
Instruction::from(Multiply {
|
||||
destination,
|
||||
left,
|
||||
left_type,
|
||||
right,
|
||||
right_type,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn divide(
|
||||
destination: u16,
|
||||
left: Operand,
|
||||
left_type: TypeCode,
|
||||
right: Operand,
|
||||
right_type: TypeCode,
|
||||
) -> Instruction {
|
||||
pub fn divide(destination: u16, left: Operand, right: Operand) -> Instruction {
|
||||
Instruction::from(Divide {
|
||||
destination,
|
||||
left,
|
||||
left_type,
|
||||
right,
|
||||
right_type,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn modulo(
|
||||
destination: u16,
|
||||
left: Operand,
|
||||
left_type: TypeCode,
|
||||
right: Operand,
|
||||
right_type: TypeCode,
|
||||
) -> Instruction {
|
||||
pub fn modulo(destination: u16, left: Operand, right: Operand) -> Instruction {
|
||||
Instruction::from(Modulo {
|
||||
destination,
|
||||
left,
|
||||
left_type,
|
||||
right,
|
||||
right_type,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn equal(
|
||||
comparator: bool,
|
||||
left: Operand,
|
||||
left_type: TypeCode,
|
||||
right: Operand,
|
||||
right_type: TypeCode,
|
||||
) -> Instruction {
|
||||
pub fn equal(comparator: bool, left: Operand, right: Operand) -> Instruction {
|
||||
Instruction::from(Equal {
|
||||
comparator,
|
||||
left,
|
||||
left_type,
|
||||
right,
|
||||
right_type,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn less(
|
||||
comparator: bool,
|
||||
left: Operand,
|
||||
left_type: TypeCode,
|
||||
right: Operand,
|
||||
right_type: TypeCode,
|
||||
) -> Instruction {
|
||||
pub fn less(comparator: bool, left: Operand, right: Operand) -> Instruction {
|
||||
Instruction::from(Less {
|
||||
comparator,
|
||||
left,
|
||||
left_type,
|
||||
right,
|
||||
right_type,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn less_equal(
|
||||
comparator: bool,
|
||||
left: Operand,
|
||||
left_type: TypeCode,
|
||||
right: Operand,
|
||||
right_type: TypeCode,
|
||||
) -> Instruction {
|
||||
pub fn less_equal(comparator: bool, left: Operand, right: Operand) -> Instruction {
|
||||
Instruction::from(LessEqual {
|
||||
comparator,
|
||||
left,
|
||||
left_type,
|
||||
right,
|
||||
right_type,
|
||||
})
|
||||
}
|
||||
|
||||
@ -502,19 +558,24 @@ impl Instruction {
|
||||
pub fn call_native(
|
||||
destination: u16,
|
||||
function: NativeFunction,
|
||||
argument_count: u16,
|
||||
first_argument_index: u16,
|
||||
) -> Instruction {
|
||||
Instruction::from(CallNative {
|
||||
destination,
|
||||
function,
|
||||
argument_count,
|
||||
first_argument_index,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn r#return(should_return_value: bool, return_register: u16) -> Instruction {
|
||||
pub fn r#return(
|
||||
should_return_value: bool,
|
||||
return_register: u16,
|
||||
r#type: TypeCode,
|
||||
) -> Instruction {
|
||||
Instruction::from(Return {
|
||||
should_return_value,
|
||||
return_register,
|
||||
r#type,
|
||||
})
|
||||
}
|
||||
|
||||
@ -526,55 +587,20 @@ impl Instruction {
|
||||
self.operation().is_comparison()
|
||||
}
|
||||
|
||||
pub fn as_argument(&self) -> Option<Operand> {
|
||||
match self.operation() {
|
||||
Operation::LOAD_CONSTANT => Some(Operand::Constant(self.b_field())),
|
||||
Operation::LOAD_BOOLEAN
|
||||
| Operation::LOAD_LIST
|
||||
| Operation::LOAD_SELF
|
||||
| Operation::GET_LOCAL
|
||||
| Operation::ADD
|
||||
| Operation::SUBTRACT
|
||||
| Operation::MULTIPLY
|
||||
| Operation::DIVIDE
|
||||
| Operation::MODULO
|
||||
| Operation::EQUAL
|
||||
| Operation::LESS
|
||||
| Operation::LESS_EQUAL
|
||||
| Operation::NEGATE
|
||||
| Operation::NOT
|
||||
| Operation::CALL => Some(Operand::Register(self.a_field())),
|
||||
Operation::CALL_NATIVE => {
|
||||
let function = NativeFunction::from(self.b_field());
|
||||
|
||||
if function.returns_value() {
|
||||
Some(Operand::Register(self.a_field()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn b_as_argument(&self) -> Operand {
|
||||
pub fn b_as_operand(&self) -> Operand {
|
||||
if self.b_is_constant() {
|
||||
Operand::Constant(self.b_field())
|
||||
Operand::Constant(self.b_field(), self.b_type())
|
||||
} else {
|
||||
Operand::Register(self.b_field())
|
||||
Operand::Register(self.b_field(), self.b_type())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn b_and_c_as_operands(&self) -> (Operand, Operand) {
|
||||
let left = if self.b_is_constant() {
|
||||
Operand::Constant(self.b_field())
|
||||
} else {
|
||||
Operand::Register(self.b_field())
|
||||
};
|
||||
let left = self.b_as_operand();
|
||||
let right = if self.c_is_constant() {
|
||||
Operand::Constant(self.c_field())
|
||||
Operand::Constant(self.c_field(), self.c_type())
|
||||
} else {
|
||||
Operand::Register(self.c_field())
|
||||
Operand::Register(self.c_field(), self.c_type())
|
||||
};
|
||||
|
||||
(left, right)
|
||||
@ -582,13 +608,12 @@ impl Instruction {
|
||||
|
||||
pub fn yields_value(&self) -> bool {
|
||||
match self.operation() {
|
||||
Operation::POINT
|
||||
| Operation::LOAD_BOOLEAN
|
||||
Operation::MOVE
|
||||
| Operation::LOAD_ENCODED
|
||||
| Operation::LOAD_CONSTANT
|
||||
| Operation::LOAD_FUNCTION
|
||||
| Operation::LOAD_LIST
|
||||
| Operation::LOAD_SELF
|
||||
| Operation::GET_LOCAL
|
||||
| Operation::ADD
|
||||
| Operation::SUBTRACT
|
||||
| Operation::MULTIPLY
|
||||
@ -603,7 +628,6 @@ impl Instruction {
|
||||
function.returns_value()
|
||||
}
|
||||
Operation::CLOSE
|
||||
| Operation::SET_LOCAL
|
||||
| Operation::EQUAL
|
||||
| Operation::LESS
|
||||
| Operation::LESS_EQUAL
|
||||
@ -611,7 +635,7 @@ impl Instruction {
|
||||
| Operation::TEST_SET
|
||||
| Operation::JUMP
|
||||
| Operation::RETURN => false,
|
||||
_ => self.operation().panic_from_unknown_code(),
|
||||
unknown => panic!("Unknown operation: {}", unknown.0),
|
||||
}
|
||||
}
|
||||
|
||||
@ -619,16 +643,14 @@ impl Instruction {
|
||||
let operation = self.operation();
|
||||
|
||||
match operation {
|
||||
Operation::POINT => Point::from(*self).to_string(),
|
||||
Operation::CLOSE => Close::from(*self).to_string(),
|
||||
Operation::LOAD_BOOLEAN => LoadBoolean::from(*self).to_string(),
|
||||
Operation::MOVE => Move::from(self).to_string(),
|
||||
Operation::CLOSE => Close::from(self).to_string(),
|
||||
Operation::LOAD_ENCODED => LoadEncoded::from(*self).to_string(),
|
||||
Operation::LOAD_CONSTANT => LoadConstant::from(*self).to_string(),
|
||||
Operation::LOAD_FUNCTION => LoadFunction::from(*self).to_string(),
|
||||
Operation::LOAD_LIST => LoadList::from(*self).to_string(),
|
||||
Operation::LOAD_SELF => LoadSelf::from(*self).to_string(),
|
||||
Operation::GET_LOCAL => GetLocal::from(*self).to_string(),
|
||||
Operation::SET_LOCAL => SetLocal::from(*self).to_string(),
|
||||
Operation::ADD => Add::from(*self).to_string(),
|
||||
Operation::ADD => Add::from(self).to_string(),
|
||||
Operation::SUBTRACT => Subtract::from(*self).to_string(),
|
||||
Operation::MULTIPLY => Multiply::from(*self).to_string(),
|
||||
Operation::DIVIDE => Divide::from(*self).to_string(),
|
||||
@ -638,14 +660,13 @@ impl Instruction {
|
||||
Operation::EQUAL => Equal::from(*self).to_string(),
|
||||
Operation::LESS => Less::from(*self).to_string(),
|
||||
Operation::LESS_EQUAL => LessEqual::from(*self).to_string(),
|
||||
Operation::TEST => Test::from(*self).to_string(),
|
||||
Operation::TEST => Test::from(self).to_string(),
|
||||
Operation::TEST_SET => TestSet::from(*self).to_string(),
|
||||
Operation::CALL => Call::from(*self).to_string(),
|
||||
Operation::CALL_NATIVE => CallNative::from(*self).to_string(),
|
||||
Operation::JUMP => Jump::from(*self).to_string(),
|
||||
Operation::JUMP => Jump::from(self).to_string(),
|
||||
Operation::RETURN => Return::from(*self).to_string(),
|
||||
|
||||
_ => operation.panic_from_unknown_code(),
|
||||
unknown => panic!("Unknown operation: {}", unknown.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -664,30 +685,37 @@ impl Display for Instruction {
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub enum Operand {
|
||||
Constant(u16),
|
||||
Register(u16),
|
||||
Constant(u16, TypeCode),
|
||||
Register(u16, TypeCode),
|
||||
}
|
||||
|
||||
impl Operand {
|
||||
pub fn index(&self) -> u16 {
|
||||
match self {
|
||||
Operand::Constant(index) => *index,
|
||||
Operand::Register(index) => *index,
|
||||
Operand::Constant(index, _) => *index,
|
||||
Operand::Register(index, _) => *index,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_constant(&self) -> bool {
|
||||
matches!(self, Operand::Constant(_))
|
||||
matches!(self, Operand::Constant(_, _))
|
||||
}
|
||||
|
||||
pub fn is_register(&self) -> bool {
|
||||
matches!(self, Operand::Register(_))
|
||||
matches!(self, Operand::Register(_, _))
|
||||
}
|
||||
|
||||
pub fn as_index_and_constant_flag(&self) -> (u16, bool) {
|
||||
match self {
|
||||
Operand::Constant(index) => (*index, true),
|
||||
Operand::Register(index) => (*index, false),
|
||||
Operand::Constant(index, _) => (*index, true),
|
||||
Operand::Register(index, _) => (*index, false),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_type(&self) -> TypeCode {
|
||||
match self {
|
||||
Operand::Constant(_, r#type) => *r#type,
|
||||
Operand::Register(_, r#type) => *r#type,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -695,8 +723,28 @@ impl Operand {
|
||||
impl Display for Operand {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Operand::Constant(index) => write!(f, "C{index}"),
|
||||
Operand::Register(index) => write!(f, "R{index}"),
|
||||
Operand::Constant(index, r#type) => match *r#type {
|
||||
TypeCode::BOOLEAN => write!(f, "C_{}", index),
|
||||
TypeCode::BYTE => write!(f, "C_{}", index),
|
||||
TypeCode::CHARACTER => write!(f, "C_{}", index),
|
||||
TypeCode::INTEGER => write!(f, "C_{}", index),
|
||||
TypeCode::FLOAT => write!(f, "C_{}", index),
|
||||
TypeCode::STRING => write!(f, "C_{}", index),
|
||||
TypeCode::LIST => write!(f, "C_{}", index),
|
||||
TypeCode::FUNCTION => write!(f, "C_{}", index),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Operand::Register(index, r#type) => match *r#type {
|
||||
TypeCode::BOOLEAN => write!(f, "R_BOOL_{}", index),
|
||||
TypeCode::BYTE => write!(f, "R_BYTE_{}", index),
|
||||
TypeCode::CHARACTER => write!(f, "R_CHAR_{}", index),
|
||||
TypeCode::INTEGER => write!(f, "R_INT_{}", index),
|
||||
TypeCode::FLOAT => write!(f, "R_FLOAT_{}", index),
|
||||
TypeCode::STRING => write!(f, "R_STR_{}", index),
|
||||
TypeCode::LIST => write!(f, "R_LIST_{}", index),
|
||||
TypeCode::FUNCTION => write!(f, "R_FN_{}", index),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -709,23 +757,19 @@ mod tests {
|
||||
fn decode_operation() {
|
||||
let instruction = Instruction::add(
|
||||
42,
|
||||
Operand::Constant(4),
|
||||
TypeCode::STRING,
|
||||
Operand::Register(2),
|
||||
TypeCode::CHARACTER,
|
||||
Operand::Constant(4, TypeCode::STRING),
|
||||
Operand::Register(2, TypeCode::CHARACTER),
|
||||
);
|
||||
|
||||
assert_eq!(instruction.operation(), Operation::ADD);
|
||||
assert_eq!(Operation::ADD, instruction.operation());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_a_field() {
|
||||
let instruction = Instruction::add(
|
||||
42,
|
||||
Operand::Constant(4),
|
||||
TypeCode::STRING,
|
||||
Operand::Register(2),
|
||||
TypeCode::CHARACTER,
|
||||
Operand::Constant(4, TypeCode::STRING),
|
||||
Operand::Register(2, TypeCode::CHARACTER),
|
||||
);
|
||||
|
||||
assert_eq!(42, instruction.a_field());
|
||||
@ -735,10 +779,8 @@ mod tests {
|
||||
fn decode_b_field() {
|
||||
let instruction = Instruction::add(
|
||||
42,
|
||||
Operand::Constant(4),
|
||||
TypeCode::STRING,
|
||||
Operand::Register(2),
|
||||
TypeCode::CHARACTER,
|
||||
Operand::Constant(4, TypeCode::STRING),
|
||||
Operand::Register(2, TypeCode::CHARACTER),
|
||||
);
|
||||
|
||||
assert_eq!(4, instruction.b_field());
|
||||
@ -748,10 +790,8 @@ mod tests {
|
||||
fn decode_c_field() {
|
||||
let instruction = Instruction::add(
|
||||
42,
|
||||
Operand::Constant(4),
|
||||
TypeCode::STRING,
|
||||
Operand::Register(2),
|
||||
TypeCode::CHARACTER,
|
||||
Operand::Constant(4, TypeCode::STRING),
|
||||
Operand::Register(2, TypeCode::CHARACTER),
|
||||
);
|
||||
|
||||
assert_eq!(2, instruction.c_field());
|
||||
@ -761,23 +801,19 @@ mod tests {
|
||||
fn decode_d_field() {
|
||||
let instruction = Instruction::add(
|
||||
42,
|
||||
Operand::Constant(4),
|
||||
TypeCode::STRING,
|
||||
Operand::Register(2),
|
||||
TypeCode::CHARACTER,
|
||||
Operand::Constant(4, TypeCode::STRING),
|
||||
Operand::Register(2, TypeCode::CHARACTER),
|
||||
);
|
||||
|
||||
assert!(instruction.d_field());
|
||||
assert!(!instruction.d_field());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_b_is_constant() {
|
||||
let instruction = Instruction::add(
|
||||
42,
|
||||
Operand::Constant(4),
|
||||
TypeCode::STRING,
|
||||
Operand::Register(2),
|
||||
TypeCode::CHARACTER,
|
||||
Operand::Constant(4, TypeCode::STRING),
|
||||
Operand::Register(2, TypeCode::CHARACTER),
|
||||
);
|
||||
|
||||
assert!(instruction.b_is_constant());
|
||||
@ -787,10 +823,8 @@ mod tests {
|
||||
fn decode_c_is_constant() {
|
||||
let instruction = Instruction::add(
|
||||
42,
|
||||
Operand::Register(2),
|
||||
TypeCode::STRING,
|
||||
Operand::Constant(4),
|
||||
TypeCode::CHARACTER,
|
||||
Operand::Register(2, TypeCode::STRING),
|
||||
Operand::Constant(4, TypeCode::CHARACTER),
|
||||
);
|
||||
|
||||
assert!(instruction.c_is_constant());
|
||||
@ -800,10 +834,8 @@ mod tests {
|
||||
fn decode_b_type() {
|
||||
let instruction = Instruction::add(
|
||||
42,
|
||||
Operand::Constant(4),
|
||||
TypeCode::STRING,
|
||||
Operand::Register(2),
|
||||
TypeCode::CHARACTER,
|
||||
Operand::Constant(4, TypeCode::STRING),
|
||||
Operand::Register(2, TypeCode::CHARACTER),
|
||||
);
|
||||
|
||||
assert_eq!(TypeCode::STRING, instruction.b_type());
|
||||
@ -813,10 +845,8 @@ mod tests {
|
||||
fn decode_c_type() {
|
||||
let instruction = Instruction::add(
|
||||
42,
|
||||
Operand::Constant(4),
|
||||
TypeCode::STRING,
|
||||
Operand::Register(2),
|
||||
TypeCode::CHARACTER,
|
||||
Operand::Constant(4, TypeCode::STRING),
|
||||
Operand::Register(2, TypeCode::CHARACTER),
|
||||
);
|
||||
|
||||
assert_eq!(TypeCode::CHARACTER, instruction.c_type());
|
||||
|
@ -1,28 +1,22 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode};
|
||||
use super::{Instruction, InstructionFields, Operand, Operation, TypeCode};
|
||||
|
||||
pub struct Modulo {
|
||||
pub destination: u16,
|
||||
pub left: Operand,
|
||||
pub left_type: TypeCode,
|
||||
pub right: Operand,
|
||||
pub right_type: TypeCode,
|
||||
}
|
||||
|
||||
impl From<Instruction> for Modulo {
|
||||
fn from(instruction: Instruction) -> Self {
|
||||
let destination = instruction.a_field();
|
||||
let (left, right) = instruction.b_and_c_as_operands();
|
||||
let left_type = instruction.b_type();
|
||||
let right_type = instruction.c_type();
|
||||
|
||||
Modulo {
|
||||
destination,
|
||||
left,
|
||||
left_type,
|
||||
right,
|
||||
right_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -33,10 +27,10 @@ impl From<Modulo> for Instruction {
|
||||
let a_field = modulo.destination;
|
||||
let (b_field, b_is_constant) = modulo.left.as_index_and_constant_flag();
|
||||
let (c_field, c_is_constant) = modulo.right.as_index_and_constant_flag();
|
||||
let b_type = modulo.left_type;
|
||||
let c_type = modulo.right_type;
|
||||
let b_type = modulo.left.as_type();
|
||||
let c_type = modulo.right.as_type();
|
||||
|
||||
InstructionBuilder {
|
||||
InstructionFields {
|
||||
operation,
|
||||
a_field,
|
||||
b_field,
|
||||
@ -56,14 +50,19 @@ impl Display for Modulo {
|
||||
let Modulo {
|
||||
destination,
|
||||
left,
|
||||
left_type,
|
||||
right,
|
||||
right_type,
|
||||
} = self;
|
||||
|
||||
write!(
|
||||
f,
|
||||
"R{destination} = {left_type}({left}) % {right_type}({right})",
|
||||
)
|
||||
match left.as_type() {
|
||||
TypeCode::BOOLEAN => write!(f, "R_BOOL_{destination}")?,
|
||||
TypeCode::BYTE => write!(f, "R_BYTE_{destination}")?,
|
||||
TypeCode::CHARACTER => write!(f, "R_STR_{destination}")?,
|
||||
TypeCode::FLOAT => write!(f, "R_FLOAT_{destination}")?,
|
||||
TypeCode::INTEGER => write!(f, "R_INT_{destination}")?,
|
||||
TypeCode::STRING => write!(f, "R_STR_{destination}")?,
|
||||
_ => todo!(),
|
||||
}
|
||||
|
||||
write!(f, " = {left} % {right}",)
|
||||
}
|
||||
}
|
||||
|
60
dust-lang/src/instruction/move.rs
Normal file
60
dust-lang/src/instruction/move.rs
Normal file
@ -0,0 +1,60 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
use super::{InstructionFields, Operand, TypeCode};
|
||||
|
||||
pub struct Move {
|
||||
pub destination: u16,
|
||||
pub operand: Operand,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for Move {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
Move {
|
||||
destination: instruction.a_field(),
|
||||
operand: instruction.b_as_operand(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Move> for Instruction {
|
||||
fn from(r#move: Move) -> Self {
|
||||
let operation = Operation::MOVE;
|
||||
let a_field = r#move.destination;
|
||||
let (b_field, b_is_constant) = r#move.operand.as_index_and_constant_flag();
|
||||
let b_type = r#move.operand.as_type();
|
||||
|
||||
InstructionFields {
|
||||
operation,
|
||||
a_field,
|
||||
b_field,
|
||||
b_type,
|
||||
b_is_constant,
|
||||
..Default::default()
|
||||
}
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Move {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
let Move {
|
||||
destination,
|
||||
operand: to,
|
||||
} = self;
|
||||
|
||||
match to.as_type() {
|
||||
TypeCode::BOOLEAN => write!(f, "R_BOOL_{destination} -> {to}"),
|
||||
TypeCode::BYTE => write!(f, "R_BYTE_{destination} -> {to}"),
|
||||
TypeCode::CHARACTER => write!(f, "R_CHAR_{destination} -> {to}"),
|
||||
TypeCode::FLOAT => write!(f, "R_FLOAT_{destination} -> {to}"),
|
||||
TypeCode::INTEGER => write!(f, "R_INT_{destination} -> {to}"),
|
||||
TypeCode::STRING => write!(f, "R_STR_{destination} -> {to}"),
|
||||
unsupported => write!(
|
||||
f,
|
||||
"Unsupported type code: {unsupported} for MOVE instruction"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +1,22 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode};
|
||||
use super::{Instruction, InstructionFields, Operand, Operation, TypeCode};
|
||||
|
||||
pub struct Multiply {
|
||||
pub destination: u16,
|
||||
pub left: Operand,
|
||||
pub left_type: TypeCode,
|
||||
pub right: Operand,
|
||||
pub right_type: TypeCode,
|
||||
}
|
||||
|
||||
impl From<Instruction> for Multiply {
|
||||
fn from(instruction: Instruction) -> Self {
|
||||
let destination = instruction.a_field();
|
||||
let (left, right) = instruction.b_and_c_as_operands();
|
||||
let left_type = instruction.b_type();
|
||||
let right_type = instruction.c_type();
|
||||
|
||||
Multiply {
|
||||
destination,
|
||||
left,
|
||||
left_type,
|
||||
right,
|
||||
right_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -33,10 +27,10 @@ impl From<Multiply> for Instruction {
|
||||
let a_field = multiply.destination;
|
||||
let (b_field, b_is_constant) = multiply.left.as_index_and_constant_flag();
|
||||
let (c_field, c_is_constant) = multiply.right.as_index_and_constant_flag();
|
||||
let b_type = multiply.left_type;
|
||||
let c_type = multiply.right_type;
|
||||
let b_type = multiply.left.as_type();
|
||||
let c_type = multiply.right.as_type();
|
||||
|
||||
InstructionBuilder {
|
||||
InstructionFields {
|
||||
operation,
|
||||
a_field,
|
||||
b_field,
|
||||
@ -56,14 +50,19 @@ impl Display for Multiply {
|
||||
let Multiply {
|
||||
destination,
|
||||
left,
|
||||
left_type,
|
||||
right,
|
||||
right_type,
|
||||
} = self;
|
||||
|
||||
write!(
|
||||
f,
|
||||
"R{destination} = {left_type}({left}) ✕ {right_type}({right})",
|
||||
)
|
||||
match left.as_type() {
|
||||
TypeCode::BOOLEAN => write!(f, "R_BOOL_{destination}")?,
|
||||
TypeCode::BYTE => write!(f, "R_BYTE_{destination}")?,
|
||||
TypeCode::CHARACTER => write!(f, "R_STR_{destination}")?,
|
||||
TypeCode::FLOAT => write!(f, "R_FLOAT_{destination}")?,
|
||||
TypeCode::INTEGER => write!(f, "R_INT_{destination}")?,
|
||||
TypeCode::STRING => write!(f, "R_STR_{destination}")?,
|
||||
_ => todo!(),
|
||||
}
|
||||
|
||||
write!(f, " = {left} ✕ {right}",)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode};
|
||||
use super::{Instruction, InstructionFields, Operand, Operation, TypeCode};
|
||||
|
||||
pub struct Negate {
|
||||
pub destination: u16,
|
||||
@ -11,7 +11,7 @@ pub struct Negate {
|
||||
impl From<Instruction> for Negate {
|
||||
fn from(instruction: Instruction) -> Self {
|
||||
let destination = instruction.a_field();
|
||||
let argument = instruction.b_as_argument();
|
||||
let argument = instruction.b_as_operand();
|
||||
let argument_type = instruction.b_type();
|
||||
|
||||
Negate {
|
||||
@ -29,7 +29,7 @@ impl From<Negate> for Instruction {
|
||||
let (b_field, b_is_constant) = negate.argument.as_index_and_constant_flag();
|
||||
let b_type = negate.argument_type;
|
||||
|
||||
InstructionBuilder {
|
||||
InstructionFields {
|
||||
operation,
|
||||
a_field,
|
||||
b_field,
|
||||
@ -46,9 +46,9 @@ impl Display for Negate {
|
||||
let Negate {
|
||||
destination,
|
||||
argument,
|
||||
argument_type,
|
||||
..
|
||||
} = self;
|
||||
|
||||
write!(f, "R{destination} = -{argument_type}({argument})")
|
||||
write!(f, "R{destination} = -{argument}")
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use std::fmt::Display;
|
||||
|
||||
use crate::{Instruction, Operand, Operation};
|
||||
|
||||
use super::InstructionBuilder;
|
||||
use super::InstructionFields;
|
||||
|
||||
pub struct Not {
|
||||
pub destination: u16,
|
||||
@ -12,7 +12,7 @@ pub struct Not {
|
||||
impl From<Instruction> for Not {
|
||||
fn from(instruction: Instruction) -> Self {
|
||||
let destination = instruction.a_field();
|
||||
let argument = instruction.b_as_argument();
|
||||
let argument = instruction.b_as_operand();
|
||||
|
||||
Not {
|
||||
destination,
|
||||
@ -27,7 +27,7 @@ impl From<Not> for Instruction {
|
||||
let a_field = not.destination;
|
||||
let (b_field, b_is_constant) = not.argument.as_index_and_constant_flag();
|
||||
|
||||
InstructionBuilder {
|
||||
InstructionFields {
|
||||
operation,
|
||||
a_field,
|
||||
b_field,
|
||||
|
@ -9,62 +9,59 @@ use serde::{Deserialize, Serialize};
|
||||
pub struct Operation(pub u8);
|
||||
|
||||
impl Operation {
|
||||
// Stack manipulation
|
||||
pub const POINT: Operation = Operation(0);
|
||||
pub const CLOSE: Operation = Operation(1);
|
||||
pub const NO_OP: Operation = Operation(0);
|
||||
|
||||
// Register manipulation
|
||||
pub const MOVE: Operation = Operation(1);
|
||||
pub const CLOSE: Operation = Operation(2);
|
||||
|
||||
// Loaders
|
||||
pub const LOAD_BOOLEAN: Operation = Operation(2);
|
||||
pub const LOAD_CONSTANT: Operation = Operation(3);
|
||||
pub const LOAD_FUNCTION: Operation = Operation(4);
|
||||
pub const LOAD_ENCODED: Operation = Operation(3);
|
||||
pub const LOAD_CONSTANT: Operation = Operation(4);
|
||||
pub const LOAD_LIST: Operation = Operation(5);
|
||||
pub const LOAD_SELF: Operation = Operation(6);
|
||||
|
||||
// Locals
|
||||
pub const GET_LOCAL: Operation = Operation(7);
|
||||
pub const SET_LOCAL: Operation = Operation(8);
|
||||
pub const LOAD_FUNCTION: Operation = Operation(6);
|
||||
pub const LOAD_SELF: Operation = Operation(7);
|
||||
|
||||
// Arithmetic
|
||||
pub const ADD: Operation = Operation(9);
|
||||
pub const SUBTRACT: Operation = Operation(10);
|
||||
pub const MULTIPLY: Operation = Operation(11);
|
||||
pub const DIVIDE: Operation = Operation(12);
|
||||
pub const MODULO: Operation = Operation(13);
|
||||
pub const ADD: Operation = Operation(8);
|
||||
pub const SUBTRACT: Operation = Operation(9);
|
||||
pub const MULTIPLY: Operation = Operation(10);
|
||||
pub const DIVIDE: Operation = Operation(11);
|
||||
pub const MODULO: Operation = Operation(12);
|
||||
|
||||
// Comparison
|
||||
pub const EQUAL: Operation = Operation(14);
|
||||
pub const LESS: Operation = Operation(15);
|
||||
pub const LESS_EQUAL: Operation = Operation(16);
|
||||
pub const EQUAL: Operation = Operation(13);
|
||||
pub const LESS: Operation = Operation(14);
|
||||
pub const LESS_EQUAL: Operation = Operation(15);
|
||||
|
||||
// Unary operations
|
||||
pub const NEGATE: Operation = Operation(17);
|
||||
pub const NOT: Operation = Operation(18);
|
||||
pub const NEGATE: Operation = Operation(16);
|
||||
pub const NOT: Operation = Operation(17);
|
||||
|
||||
// Logical operations
|
||||
pub const TEST: Operation = Operation(19);
|
||||
pub const TEST_SET: Operation = Operation(20);
|
||||
pub const TEST: Operation = Operation(18);
|
||||
pub const TEST_SET: Operation = Operation(19);
|
||||
|
||||
// Function calls
|
||||
pub const CALL: Operation = Operation(21);
|
||||
pub const CALL_NATIVE: Operation = Operation(22);
|
||||
pub const CALL: Operation = Operation(20);
|
||||
pub const CALL_NATIVE: Operation = Operation(21);
|
||||
|
||||
// Control flow
|
||||
pub const JUMP: Operation = Operation(23);
|
||||
pub const RETURN: Operation = Operation(24);
|
||||
pub const JUMP: Operation = Operation(22);
|
||||
pub const RETURN: Operation = Operation(23);
|
||||
}
|
||||
|
||||
impl Operation {
|
||||
pub fn name(&self) -> &'static str {
|
||||
match *self {
|
||||
Self::POINT => "POINT",
|
||||
Self::NO_OP => "NO_OP",
|
||||
Self::MOVE => "MOVE",
|
||||
Self::CLOSE => "CLOSE",
|
||||
Self::LOAD_BOOLEAN => "LOAD_BOOLEAN",
|
||||
Self::LOAD_ENCODED => "LOAD_ENCODED",
|
||||
Self::LOAD_CONSTANT => "LOAD_CONSTANT",
|
||||
Self::LOAD_FUNCTION => "LOAD_FUNCTION",
|
||||
Self::LOAD_LIST => "LOAD_LIST",
|
||||
Self::LOAD_SELF => "LOAD_SELF",
|
||||
Self::GET_LOCAL => "GET_LOCAL",
|
||||
Self::SET_LOCAL => "SET_LOCAL",
|
||||
Self::ADD => "ADD",
|
||||
Self::SUBTRACT => "SUBTRACT",
|
||||
Self::MULTIPLY => "MULTIPLY",
|
||||
@ -81,7 +78,7 @@ impl Operation {
|
||||
Self::CALL_NATIVE => "CALL_NATIVE",
|
||||
Self::JUMP => "JUMP",
|
||||
Self::RETURN => "RETURN",
|
||||
_ => self.panic_from_unknown_code(),
|
||||
unknown => panic!("Unknown operation: {}", unknown.0),
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,10 +99,6 @@ impl Operation {
|
||||
Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL
|
||||
)
|
||||
}
|
||||
|
||||
pub fn panic_from_unknown_code(self) -> ! {
|
||||
panic!("Unknown operation code: {}", self.0);
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Operation {
|
||||
|
@ -1,43 +0,0 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
use super::InstructionBuilder;
|
||||
|
||||
pub struct Point {
|
||||
pub from: u16,
|
||||
pub to: u16,
|
||||
}
|
||||
|
||||
impl From<Instruction> for Point {
|
||||
fn from(instruction: Instruction) -> Self {
|
||||
Point {
|
||||
from: instruction.b_field(),
|
||||
to: instruction.c_field(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
let Point { from, to } = self;
|
||||
|
||||
write!(f, "{from} -> {to}")
|
||||
}
|
||||
}
|
@ -2,21 +2,24 @@ use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
use super::InstructionBuilder;
|
||||
use super::{InstructionFields, TypeCode};
|
||||
|
||||
pub struct Return {
|
||||
pub should_return_value: bool,
|
||||
pub return_register: u16,
|
||||
pub r#type: TypeCode,
|
||||
}
|
||||
|
||||
impl From<Instruction> for Return {
|
||||
fn from(instruction: Instruction) -> Self {
|
||||
let should_return_value = instruction.b_field() != 0;
|
||||
let return_register = instruction.c_field();
|
||||
let r#type = instruction.b_type();
|
||||
|
||||
Return {
|
||||
should_return_value,
|
||||
return_register,
|
||||
r#type,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -25,11 +28,13 @@ impl From<Return> for Instruction {
|
||||
fn from(r#return: Return) -> Self {
|
||||
let operation = Operation::RETURN;
|
||||
let b_field = r#return.should_return_value as u16;
|
||||
let b_type = r#return.r#type;
|
||||
let c_field = r#return.return_register;
|
||||
|
||||
InstructionBuilder {
|
||||
InstructionFields {
|
||||
operation,
|
||||
b_field,
|
||||
b_type,
|
||||
c_field,
|
||||
..Default::default()
|
||||
}
|
||||
@ -42,10 +47,23 @@ impl Display for Return {
|
||||
let Return {
|
||||
should_return_value,
|
||||
return_register,
|
||||
r#type,
|
||||
} = self;
|
||||
|
||||
if *should_return_value {
|
||||
write!(f, "RETURN R{return_register}")
|
||||
write!(f, "RETURN ")?;
|
||||
|
||||
match *r#type {
|
||||
TypeCode::BOOLEAN => write!(f, "R_BOOL_{return_register}"),
|
||||
TypeCode::BYTE => write!(f, "R_BYTE_{return_register}"),
|
||||
TypeCode::CHARACTER => write!(f, "R_CHAR_{return_register}"),
|
||||
TypeCode::FLOAT => write!(f, "R_FLOAT_{return_register}"),
|
||||
TypeCode::INTEGER => write!(f, "R_INT_{return_register}"),
|
||||
TypeCode::STRING => write!(f, "R_STR_{return_register}"),
|
||||
TypeCode::LIST => write!(f, "R_LIST_{return_register}"),
|
||||
TypeCode::FUNCTION => write!(f, "R_FN_{return_register}"),
|
||||
unsupported => unreachable!("Unsupported return type: {}", unsupported),
|
||||
}
|
||||
} else {
|
||||
write!(f, "RETURN")
|
||||
}
|
||||
|
@ -1,49 +0,0 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
use super::InstructionBuilder;
|
||||
|
||||
pub struct SetLocal {
|
||||
pub register_index: u16,
|
||||
pub local_index: u16,
|
||||
}
|
||||
|
||||
impl From<Instruction> for SetLocal {
|
||||
fn from(instruction: Instruction) -> Self {
|
||||
let register_index = instruction.b_field();
|
||||
let local_index = instruction.c_field();
|
||||
|
||||
SetLocal {
|
||||
register_index,
|
||||
local_index,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SetLocal> for Instruction {
|
||||
fn from(set_local: SetLocal) -> Self {
|
||||
let operation = Operation::SET_LOCAL;
|
||||
let b_field = set_local.register_index;
|
||||
let c_field = set_local.local_index;
|
||||
|
||||
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,28 +1,22 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use super::{Instruction, InstructionBuilder, Operand, Operation, TypeCode};
|
||||
use super::{Instruction, InstructionFields, Operand, Operation, TypeCode};
|
||||
|
||||
pub struct Subtract {
|
||||
pub destination: u16,
|
||||
pub left: Operand,
|
||||
pub left_type: TypeCode,
|
||||
pub right: Operand,
|
||||
pub right_type: TypeCode,
|
||||
}
|
||||
|
||||
impl From<Instruction> for Subtract {
|
||||
fn from(instruction: Instruction) -> Self {
|
||||
let destination = instruction.a_field();
|
||||
let (left, right) = instruction.b_and_c_as_operands();
|
||||
let left_type = instruction.b_type();
|
||||
let right_type = instruction.c_type();
|
||||
|
||||
Subtract {
|
||||
destination,
|
||||
left,
|
||||
left_type,
|
||||
right,
|
||||
right_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -33,10 +27,10 @@ impl From<Subtract> for Instruction {
|
||||
let a_field = subtract.destination;
|
||||
let (b_field, b_is_constant) = subtract.left.as_index_and_constant_flag();
|
||||
let (c_field, c_is_constant) = subtract.right.as_index_and_constant_flag();
|
||||
let b_type = subtract.left_type;
|
||||
let c_type = subtract.right_type;
|
||||
let b_type = subtract.left.as_type();
|
||||
let c_type = subtract.right.as_type();
|
||||
|
||||
InstructionBuilder {
|
||||
InstructionFields {
|
||||
operation,
|
||||
a_field,
|
||||
b_field,
|
||||
@ -56,14 +50,19 @@ impl Display for Subtract {
|
||||
let Subtract {
|
||||
destination,
|
||||
left,
|
||||
left_type,
|
||||
right,
|
||||
right_type,
|
||||
} = self;
|
||||
|
||||
write!(
|
||||
f,
|
||||
"R{destination} = {left_type}({left}) - {right_type}({right})",
|
||||
)
|
||||
match left.as_type() {
|
||||
TypeCode::BOOLEAN => write!(f, "R_BOOL_{destination}")?,
|
||||
TypeCode::BYTE => write!(f, "R_BYTE_{destination}")?,
|
||||
TypeCode::CHARACTER => write!(f, "R_STR_{destination}")?,
|
||||
TypeCode::FLOAT => write!(f, "R_FLOAT_{destination}")?,
|
||||
TypeCode::INTEGER => write!(f, "R_INT_{destination}")?,
|
||||
TypeCode::STRING => write!(f, "R_STR_{destination}")?,
|
||||
_ => todo!(),
|
||||
}
|
||||
|
||||
write!(f, " = {left} - {right}",)
|
||||
}
|
||||
}
|
||||
|
@ -2,15 +2,15 @@ use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
use super::InstructionBuilder;
|
||||
use super::InstructionFields;
|
||||
|
||||
pub struct Test {
|
||||
pub operand_register: u16,
|
||||
pub test_value: bool,
|
||||
}
|
||||
|
||||
impl From<Instruction> for Test {
|
||||
fn from(instruction: Instruction) -> Self {
|
||||
impl From<&Instruction> for Test {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let operand_register = instruction.b_field();
|
||||
let test_value = instruction.c_field() != 0;
|
||||
|
||||
@ -26,7 +26,7 @@ impl From<Test> for Instruction {
|
||||
let b_field = test.operand_register;
|
||||
let c_field = test.test_value as u16;
|
||||
|
||||
InstructionBuilder {
|
||||
InstructionFields {
|
||||
operation: Operation::TEST,
|
||||
b_field,
|
||||
c_field,
|
||||
@ -44,6 +44,6 @@ impl Display for Test {
|
||||
} = self;
|
||||
let bang = if *test_value { "" } else { "!" };
|
||||
|
||||
write!(f, "if {bang}R{operand_register} {{ JUMP +1 }}")
|
||||
write!(f, "if {bang}R_BOOL_{operand_register} {{ JUMP +1 }}")
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use crate::{Instruction, Operand, Operation};
|
||||
|
||||
use super::InstructionBuilder;
|
||||
use super::InstructionFields;
|
||||
|
||||
pub struct TestSet {
|
||||
pub destination: u16,
|
||||
@ -13,7 +13,7 @@ pub struct TestSet {
|
||||
impl From<Instruction> for TestSet {
|
||||
fn from(instruction: Instruction) -> Self {
|
||||
let destination = instruction.a_field();
|
||||
let argument = instruction.b_as_argument();
|
||||
let argument = instruction.b_as_operand();
|
||||
let test_value = instruction.c_field() != 0;
|
||||
|
||||
TestSet {
|
||||
@ -31,7 +31,7 @@ impl From<TestSet> for Instruction {
|
||||
let (b_field, b_is_constant) = test_set.argument.as_index_and_constant_flag();
|
||||
let c_field = test_set.test_value as u16;
|
||||
|
||||
InstructionBuilder {
|
||||
InstructionFields {
|
||||
operation,
|
||||
a_field,
|
||||
b_field,
|
||||
|
@ -1,15 +1,20 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
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 const NONE: TypeCode = TypeCode(0);
|
||||
pub const BOOLEAN: TypeCode = TypeCode(1);
|
||||
pub const BYTE: TypeCode = TypeCode(2);
|
||||
pub const CHARACTER: TypeCode = TypeCode(3);
|
||||
pub const FLOAT: TypeCode = TypeCode(4);
|
||||
pub const INTEGER: TypeCode = TypeCode(5);
|
||||
pub const STRING: TypeCode = TypeCode(6);
|
||||
pub const LIST: TypeCode = TypeCode(7);
|
||||
pub const FUNCTION: TypeCode = TypeCode(8);
|
||||
|
||||
pub fn panic_from_unknown_code(self) -> ! {
|
||||
panic!("Unknown type code: {}", self.0);
|
||||
@ -25,6 +30,8 @@ impl Display for TypeCode {
|
||||
TypeCode::FLOAT => write!(f, "float"),
|
||||
TypeCode::INTEGER => write!(f, "int"),
|
||||
TypeCode::STRING => write!(f, "str"),
|
||||
TypeCode::LIST => write!(f, "list"),
|
||||
TypeCode::FUNCTION => write!(f, "fn"),
|
||||
_ => self.panic_from_unknown_code(),
|
||||
}
|
||||
}
|
||||
|
@ -14,10 +14,10 @@
|
||||
//! ## Examples
|
||||
//!
|
||||
//! ```rust
|
||||
//! # use dust_lang::{run, ConcreteValue};
|
||||
//! # use dust_lang::{run, Value};
|
||||
//! let result = run("21 * 2").unwrap();
|
||||
//!
|
||||
//! assert_eq!(result, Some(ConcreteValue::Integer(42)));
|
||||
//! assert_eq!(result, Some(Value::integer(42)));
|
||||
//! ```
|
||||
//!
|
||||
//! ```rust
|
||||
@ -40,17 +40,17 @@ pub mod value;
|
||||
pub mod vm;
|
||||
|
||||
pub use crate::chunk::{Chunk, Disassembler, Local, Scope};
|
||||
pub use crate::compiler::{CompileError, Compiler, compile};
|
||||
pub use crate::compiler::{compile, CompileError, Compiler};
|
||||
pub use crate::dust_error::{AnnotatedError, DustError};
|
||||
pub use crate::instruction::{Operand, Instruction, Operation};
|
||||
pub use crate::lexer::{LexError, Lexer, lex};
|
||||
pub use crate::instruction::{Instruction, Operand, Operation};
|
||||
pub use crate::lexer::{lex, LexError, Lexer};
|
||||
pub use crate::native_function::{NativeFunction, NativeFunctionError};
|
||||
pub use crate::token::{Token, TokenKind, TokenOwned};
|
||||
pub use crate::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict};
|
||||
pub use crate::token::{Token, TokenKind, TokenOwned};
|
||||
pub use crate::value::{
|
||||
AbstractList, ConcreteValue, DustString, Function, RangeValue, Value, ValueError,
|
||||
};
|
||||
pub use crate::vm::{Pointer, Vm, run};
|
||||
pub use crate::vm::{run, Vm};
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
|
@ -1,20 +1,20 @@
|
||||
use std::{ops::Range, panic};
|
||||
|
||||
use crate::vm::ThreadData;
|
||||
use crate::vm::Thread;
|
||||
|
||||
pub fn panic(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
|
||||
pub fn panic(data: &mut Thread, _: usize, argument_range: Range<usize>) {
|
||||
let current_frame = data.current_frame();
|
||||
let position = data.current_position();
|
||||
let mut message = format!("Dust panic at {position}!");
|
||||
|
||||
for register_index in argument_range {
|
||||
let value_option = data.open_register_allow_empty_unchecked(register_index);
|
||||
let value = match value_option {
|
||||
Some(value) => value,
|
||||
None => continue,
|
||||
};
|
||||
let string = value.display(data);
|
||||
let string = current_frame
|
||||
.registers
|
||||
.strings
|
||||
.get(register_index)
|
||||
.as_value();
|
||||
|
||||
message.push_str(&string);
|
||||
message.push_str(string);
|
||||
message.push('\n');
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,11 @@
|
||||
use std::io::{Write, stdin, stdout};
|
||||
use std::io::{stdin, stdout, Write};
|
||||
use std::ops::Range;
|
||||
|
||||
use crate::{
|
||||
ConcreteValue, Value,
|
||||
vm::{Register, ThreadData, get_next_action},
|
||||
};
|
||||
use crate::vm::Thread;
|
||||
use crate::DustString;
|
||||
|
||||
pub fn read_line(data: &mut ThreadData, destination: u16, _argument_range: Range<u16>) -> bool {
|
||||
pub fn read_line(data: &mut Thread, destination: usize, _argument_range: Range<usize>) {
|
||||
let current_frame = data.current_frame_mut();
|
||||
let mut buffer = String::new();
|
||||
|
||||
if stdin().read_line(&mut buffer).is_ok() {
|
||||
@ -14,45 +13,44 @@ pub fn read_line(data: &mut ThreadData, destination: u16, _argument_range: Range
|
||||
|
||||
buffer.truncate(length.saturating_sub(1));
|
||||
|
||||
let register = Register::Value(Value::Concrete(ConcreteValue::string(buffer)));
|
||||
let string = DustString::from(buffer);
|
||||
|
||||
data.set_register(destination, register);
|
||||
current_frame
|
||||
.registers
|
||||
.strings
|
||||
.set_to_new_register(destination, string);
|
||||
}
|
||||
}
|
||||
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn write(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
|
||||
pub fn write(data: &mut Thread, _: usize, argument_range: Range<usize>) {
|
||||
let current_frame = data.current_frame_mut();
|
||||
let mut stdout = stdout();
|
||||
|
||||
for register_index in argument_range {
|
||||
if let Some(value) = data.open_register_allow_empty_unchecked(register_index) {
|
||||
let string = value.display(data);
|
||||
let string = current_frame
|
||||
.registers
|
||||
.strings
|
||||
.get(register_index)
|
||||
.as_value();
|
||||
let _ = stdout.write(string.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
let _ = stdout.flush();
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn write_line(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
|
||||
pub fn write_line(data: &mut Thread, _: usize, argument_range: Range<usize>) {
|
||||
let current_frame = data.current_frame_mut();
|
||||
let mut stdout = stdout().lock();
|
||||
|
||||
for register_index in argument_range {
|
||||
if let Some(value) = data.open_register_allow_empty_unchecked(register_index) {
|
||||
let string = value.display(data);
|
||||
let string = current_frame
|
||||
.registers
|
||||
.strings
|
||||
.get(register_index)
|
||||
.as_value();
|
||||
let _ = stdout.write(string.as_bytes());
|
||||
}
|
||||
|
||||
let _ = stdout.write(b"\n");
|
||||
}
|
||||
}
|
||||
|
||||
let _ = stdout.flush();
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
}
|
||||
|
@ -5,7 +5,6 @@
|
||||
mod assert;
|
||||
mod io;
|
||||
mod random;
|
||||
mod string;
|
||||
mod thread;
|
||||
|
||||
use std::{
|
||||
@ -17,7 +16,7 @@ use std::{
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{AnnotatedError, FunctionType, Span, Type, vm::ThreadData};
|
||||
use crate::{AnnotatedError, FunctionType, Span, Type, vm::Thread};
|
||||
|
||||
macro_rules! define_native_function {
|
||||
($(($name:ident, $bytes:literal, $str:expr, $type:expr, $function:expr)),*) => {
|
||||
@ -34,13 +33,13 @@ macro_rules! define_native_function {
|
||||
impl NativeFunction {
|
||||
pub fn call(
|
||||
&self,
|
||||
data: &mut ThreadData,
|
||||
destination: u16,
|
||||
argument_range: Range<u16>,
|
||||
) -> bool {
|
||||
thread: &mut Thread,
|
||||
destination: usize,
|
||||
argument_range: Range<usize>,
|
||||
) {
|
||||
match self {
|
||||
$(
|
||||
NativeFunction::$name => $function(data, destination, argument_range),
|
||||
NativeFunction::$name => $function(thread, destination, argument_range),
|
||||
)*
|
||||
}
|
||||
}
|
||||
@ -74,7 +73,7 @@ macro_rules! define_native_function {
|
||||
pub fn returns_value(&self) -> bool {
|
||||
match self {
|
||||
$(
|
||||
NativeFunction::$name => $type.return_type != Type::None,
|
||||
NativeFunction::$name => $type.return_type.as_ref() != &Type::None,
|
||||
)*
|
||||
}
|
||||
}
|
||||
@ -134,11 +133,7 @@ define_native_function! {
|
||||
Panic,
|
||||
3,
|
||||
"panic",
|
||||
FunctionType {
|
||||
type_parameters: Vec::with_capacity(0),
|
||||
value_parameters: Vec::with_capacity(0),
|
||||
return_type: Type::None
|
||||
},
|
||||
FunctionType::new([], [], Type::None),
|
||||
assert::panic
|
||||
),
|
||||
|
||||
@ -147,17 +142,13 @@ define_native_function! {
|
||||
// (ToByte, 5_u8, "to_byte", true),
|
||||
// (ToFloat, 6_u8, "to_float", true),
|
||||
// (ToInteger, 7_u8, "to_integer", true),
|
||||
(
|
||||
ToString,
|
||||
8,
|
||||
"to_string",
|
||||
FunctionType {
|
||||
type_parameters: Vec::with_capacity(0),
|
||||
value_parameters: vec![(0, Type::Any)],
|
||||
return_type: Type::String
|
||||
},
|
||||
string::to_string
|
||||
),
|
||||
// (
|
||||
// ToString,
|
||||
// 8,
|
||||
// "to_string",
|
||||
// FunctionType::new([], [Type::Any], Type::String),
|
||||
// string::to_string
|
||||
// ),
|
||||
|
||||
// // List and string
|
||||
// (All, 9_u8, "all", true),
|
||||
@ -212,11 +203,7 @@ define_native_function! {
|
||||
ReadLine,
|
||||
50,
|
||||
"read_line",
|
||||
FunctionType {
|
||||
type_parameters: Vec::with_capacity(0),
|
||||
value_parameters: Vec::with_capacity(0),
|
||||
return_type: Type::String
|
||||
},
|
||||
FunctionType::new([], [], Type::String),
|
||||
io::read_line
|
||||
),
|
||||
// (ReadTo, 51_u8, "read_to", false),
|
||||
@ -228,11 +215,7 @@ define_native_function! {
|
||||
Write,
|
||||
55,
|
||||
"write",
|
||||
FunctionType {
|
||||
type_parameters: Vec::with_capacity(0),
|
||||
value_parameters: vec![(0, Type::String)],
|
||||
return_type: Type::None
|
||||
},
|
||||
FunctionType::new([], [Type::String], Type::None),
|
||||
io::write
|
||||
),
|
||||
// (WriteFile, 56_u8, "write_file", false),
|
||||
@ -240,11 +223,7 @@ define_native_function! {
|
||||
WriteLine,
|
||||
57,
|
||||
"write_line",
|
||||
FunctionType {
|
||||
type_parameters: Vec::with_capacity(0),
|
||||
value_parameters: vec![(0, Type::String)],
|
||||
return_type: Type::None
|
||||
},
|
||||
FunctionType::new([], [Type::String], Type::None),
|
||||
io::write_line
|
||||
),
|
||||
|
||||
@ -253,11 +232,7 @@ define_native_function! {
|
||||
RandomInteger,
|
||||
58,
|
||||
"random_int",
|
||||
FunctionType {
|
||||
type_parameters: Vec::with_capacity(0),
|
||||
value_parameters: vec![(0, Type::Integer), (1, Type::Integer)],
|
||||
return_type: Type::Integer
|
||||
},
|
||||
FunctionType::new([], [Type::Integer, Type::Integer], Type::Integer),
|
||||
random::random_int
|
||||
),
|
||||
|
||||
@ -266,20 +241,7 @@ define_native_function! {
|
||||
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
|
||||
},
|
||||
FunctionType::new([], [ Type::function([], [], Type::None)], Type::None),
|
||||
thread::spawn
|
||||
)
|
||||
}
|
||||
|
@ -2,12 +2,10 @@ use std::ops::Range;
|
||||
|
||||
use rand::Rng;
|
||||
|
||||
use crate::{
|
||||
Value,
|
||||
vm::{Register, ThreadData, get_next_action},
|
||||
};
|
||||
use crate::vm::Thread;
|
||||
|
||||
pub fn random_int(data: &mut ThreadData, destination: u16, argument_range: Range<u16>) -> bool {
|
||||
pub fn random_int(data: &mut Thread, destination: usize, argument_range: Range<usize>) {
|
||||
let current_frame = data.current_frame_mut();
|
||||
let mut argument_range_iter = argument_range.into_iter();
|
||||
let (min, max) = {
|
||||
let mut min = None;
|
||||
@ -16,25 +14,24 @@ pub fn random_int(data: &mut ThreadData, destination: u16, argument_range: Range
|
||||
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);
|
||||
let integer = current_frame
|
||||
.registers
|
||||
.integers
|
||||
.get(register_index)
|
||||
.copy_value();
|
||||
|
||||
if let Some(argument) = value_option {
|
||||
if let Some(integer) = argument.as_integer() {
|
||||
if min.is_none() {
|
||||
min = Some(integer);
|
||||
} else {
|
||||
if let Some(min) = min {
|
||||
break (min, integer);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
min = Some(integer);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let random_integer = rand::thread_rng().gen_range(min.unwrap()..max);
|
||||
let random_integer = rand::thread_rng().gen_range(min..max);
|
||||
|
||||
data.set_register(destination, Register::Value(Value::integer(random_integer)));
|
||||
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
current_frame
|
||||
.registers
|
||||
.integers
|
||||
.set_to_new_register(destination, random_integer);
|
||||
}
|
||||
|
@ -1,18 +0,0 @@
|
||||
use std::ops::Range;
|
||||
|
||||
use crate::{
|
||||
ConcreteValue, Value,
|
||||
vm::{Register, ThreadData, get_next_action},
|
||||
};
|
||||
|
||||
pub fn to_string(data: &mut ThreadData, destination: u16, argument_range: Range<u16>) -> bool {
|
||||
let argument_value = data.open_register_unchecked(argument_range.start);
|
||||
let argument_string = argument_value.display(data);
|
||||
let register = Register::Value(Value::Concrete(ConcreteValue::string(argument_string)));
|
||||
|
||||
data.set_register(destination, register);
|
||||
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
}
|
@ -1,64 +1,11 @@
|
||||
use std::{
|
||||
ops::Range,
|
||||
thread::{Builder, JoinHandle},
|
||||
};
|
||||
use std::{ops::Range, thread::JoinHandle};
|
||||
|
||||
use tracing::{Level, info, span};
|
||||
use crate::vm::Thread;
|
||||
|
||||
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")
|
||||
fn start_thread(_thread: &mut Thread, _argument_range: Range<usize>) -> JoinHandle<()> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
pub fn spawn(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
|
||||
pub fn spawn(data: &mut Thread, _: usize, argument_range: Range<usize>) {
|
||||
let _ = start_thread(data, argument_range);
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
}
|
||||
|
@ -4,25 +4,22 @@ use std::fmt::{self, Display, Formatter};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
macro_rules! define_tokens {
|
||||
($($variant:ident $(($data_type:ty))?),+ $(,)?) => {
|
||||
($($variant:ident $(($data_type:ty))?),+) => {
|
||||
/// Source token.
|
||||
///
|
||||
/// This is a borrowed type, i.e. some variants contain references to the source text.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Default, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub enum Token<'src> {
|
||||
#[default]
|
||||
Eof,
|
||||
$(
|
||||
$variant $(($data_type))?,
|
||||
)*
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
/// Data-less representation of a source token.
|
||||
///
|
||||
/// If a [Token] borrows from the source text, its TokenKind omits the data.
|
||||
pub enum TokenKind {
|
||||
Eof,
|
||||
$(
|
||||
$variant,
|
||||
)*
|
||||
@ -31,6 +28,8 @@ macro_rules! define_tokens {
|
||||
}
|
||||
|
||||
define_tokens! {
|
||||
Eof,
|
||||
|
||||
// Hard-coded values
|
||||
Boolean(&'src str),
|
||||
Byte(&'src str),
|
||||
@ -90,7 +89,7 @@ define_tokens! {
|
||||
Slash,
|
||||
SlashEqual,
|
||||
Star,
|
||||
StarEqual,
|
||||
StarEqual
|
||||
}
|
||||
|
||||
impl Token<'_> {
|
||||
@ -448,7 +447,7 @@ impl Display for Token<'_> {
|
||||
/// Owned representation of a source token.
|
||||
///
|
||||
/// If a [Token] borrows from the source text, its TokenOwned omits the data.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub enum TokenOwned {
|
||||
Eof,
|
||||
|
||||
|
@ -7,8 +7,11 @@ use std::{
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::instruction::TypeCode;
|
||||
|
||||
/// Description of a kind of value.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Clone, Default, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(tag = "Type", content = "Value")]
|
||||
pub enum Type {
|
||||
Any,
|
||||
Boolean,
|
||||
@ -16,43 +19,54 @@ pub enum Type {
|
||||
Character,
|
||||
Enum(EnumType),
|
||||
Float,
|
||||
Function(Box<FunctionType>),
|
||||
Generic {
|
||||
identifier_index: u8,
|
||||
concrete_type: Option<Box<Type>>,
|
||||
},
|
||||
Function(FunctionType),
|
||||
Generic(GenericType),
|
||||
Integer,
|
||||
List(Box<Type>),
|
||||
Map {
|
||||
pairs: Vec<(u8, Type)>,
|
||||
},
|
||||
List(TypeCode),
|
||||
Map(Vec<Type>),
|
||||
#[default]
|
||||
None,
|
||||
Range {
|
||||
r#type: Box<Type>,
|
||||
},
|
||||
Range(Box<Type>),
|
||||
SelfFunction,
|
||||
String,
|
||||
Struct(StructType),
|
||||
Tuple {
|
||||
fields: Vec<Type>,
|
||||
},
|
||||
Tuple(Vec<Type>),
|
||||
}
|
||||
|
||||
impl Type {
|
||||
pub fn function(function_type: FunctionType) -> Self {
|
||||
Type::Function(Box::new(function_type))
|
||||
pub fn function<T: Into<Vec<u16>>, U: Into<Vec<Type>>>(
|
||||
type_parameters: T,
|
||||
value_parameters: U,
|
||||
return_type: Type,
|
||||
) -> Self {
|
||||
Type::Function(FunctionType {
|
||||
type_parameters: type_parameters.into(),
|
||||
value_parameters: value_parameters.into(),
|
||||
return_type: Box::new(return_type),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn list(element_type: Type) -> Self {
|
||||
Type::List(Box::new(element_type))
|
||||
pub fn type_code(&self) -> TypeCode {
|
||||
match self {
|
||||
Type::Boolean => TypeCode::BOOLEAN,
|
||||
Type::Byte => TypeCode::BYTE,
|
||||
Type::Character => TypeCode::CHARACTER,
|
||||
Type::Float => TypeCode::FLOAT,
|
||||
Type::Integer => TypeCode::INTEGER,
|
||||
Type::None => TypeCode::NONE,
|
||||
Type::String => TypeCode::STRING,
|
||||
Type::List { .. } => TypeCode::LIST,
|
||||
Type::Function { .. } => TypeCode::FUNCTION,
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a concrete type, either the type itself or the concrete type of a generic type.
|
||||
pub fn concrete_type(&self) -> &Type {
|
||||
if let Type::Generic {
|
||||
if let Type::Generic(GenericType {
|
||||
concrete_type: Some(concrete_type),
|
||||
..
|
||||
} = self
|
||||
}) = self
|
||||
{
|
||||
concrete_type.concrete_type()
|
||||
} else {
|
||||
@ -63,24 +77,22 @@ impl Type {
|
||||
/// Checks that the type is compatible with another type.
|
||||
pub fn check(&self, other: &Type) -> Result<(), TypeConflict> {
|
||||
match (self.concrete_type(), other.concrete_type()) {
|
||||
(Type::Any, _)
|
||||
| (_, Type::Any)
|
||||
| (Type::Boolean, Type::Boolean)
|
||||
(Type::Boolean, Type::Boolean)
|
||||
| (Type::Byte, Type::Byte)
|
||||
| (Type::Character, Type::Character)
|
||||
| (Type::Float, Type::Float)
|
||||
| (Type::Integer, Type::Integer)
|
||||
| (Type::None, Type::None)
|
||||
| (Type::String { .. }, Type::String { .. }) => return Ok(()),
|
||||
| (Type::String, Type::String) => return Ok(()),
|
||||
(
|
||||
Type::Generic {
|
||||
Type::Generic(GenericType {
|
||||
concrete_type: left,
|
||||
..
|
||||
},
|
||||
Type::Generic {
|
||||
}),
|
||||
Type::Generic(GenericType {
|
||||
concrete_type: right,
|
||||
..
|
||||
},
|
||||
}),
|
||||
) => match (left, right) {
|
||||
(Some(left), Some(right)) => {
|
||||
if left.check(right).is_ok() {
|
||||
@ -92,8 +104,8 @@ impl Type {
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
(Type::Generic { concrete_type, .. }, other)
|
||||
| (other, Type::Generic { concrete_type, .. }) => {
|
||||
(Type::Generic(GenericType { concrete_type, .. }), other)
|
||||
| (other, Type::Generic(GenericType { concrete_type, .. })) => {
|
||||
if let Some(concrete_type) = concrete_type {
|
||||
if other == concrete_type.as_ref() {
|
||||
return Ok(());
|
||||
@ -106,7 +118,7 @@ impl Type {
|
||||
}
|
||||
}
|
||||
(Type::List(left_type), Type::List(right_type)) => {
|
||||
if left_type.check(right_type).is_err() {
|
||||
if left_type != right_type {
|
||||
return Err(TypeConflict {
|
||||
actual: other.clone(),
|
||||
expected: self.clone(),
|
||||
@ -120,12 +132,12 @@ impl Type {
|
||||
type_parameters: left_type_parameters,
|
||||
value_parameters: left_value_parameters,
|
||||
return_type: left_return,
|
||||
} = left_function_type.as_ref();
|
||||
} = left_function_type;
|
||||
let FunctionType {
|
||||
type_parameters: right_type_parameters,
|
||||
value_parameters: right_value_parameters,
|
||||
return_type: right_return,
|
||||
} = right_function_type.as_ref();
|
||||
} = right_function_type;
|
||||
|
||||
if left_return != right_return
|
||||
|| left_type_parameters != right_type_parameters
|
||||
@ -139,7 +151,7 @@ impl Type {
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
(Type::Range { r#type: left_type }, Type::Range { r#type: right_type }) => {
|
||||
(Type::Range(left_type), Type::Range(right_type)) => {
|
||||
if left_type == right_type {
|
||||
return Ok(());
|
||||
}
|
||||
@ -164,25 +176,24 @@ impl Display for Type {
|
||||
Type::Enum(EnumType { name, .. }) => write!(f, "{name}"),
|
||||
Type::Float => write!(f, "float"),
|
||||
Type::Function(function_type) => write!(f, "{function_type}"),
|
||||
Type::Generic { concrete_type, .. } => {
|
||||
Type::Generic(GenericType { concrete_type, .. }) => {
|
||||
match concrete_type.clone().map(|r#box| *r#box) {
|
||||
Some(Type::Generic {
|
||||
identifier_index: identifier,
|
||||
..
|
||||
}) => write!(f, "{identifier}"),
|
||||
Some(Type::Generic(GenericType {
|
||||
identifier_index, ..
|
||||
})) => write!(f, "C_{identifier_index}"),
|
||||
Some(concrete_type) => write!(f, "implied to be {concrete_type}"),
|
||||
None => write!(f, "unknown"),
|
||||
}
|
||||
}
|
||||
Type::Integer => write!(f, "int"),
|
||||
Type::List(item_type) => write!(f, "[{item_type}]"),
|
||||
Type::Map { pairs } => {
|
||||
Type::Map(pairs) => {
|
||||
write!(f, "map ")?;
|
||||
|
||||
write!(f, "{{")?;
|
||||
|
||||
for (index, (key, value)) in pairs.iter().enumerate() {
|
||||
write!(f, "{key}: {value}")?;
|
||||
for (index, r#type) in pairs.iter().enumerate() {
|
||||
write!(f, "???: {type}")?;
|
||||
|
||||
if index != pairs.len() - 1 {
|
||||
write!(f, ", ")?;
|
||||
@ -192,11 +203,11 @@ impl Display for Type {
|
||||
write!(f, "}}")
|
||||
}
|
||||
Type::None => write!(f, "none"),
|
||||
Type::Range { r#type } => write!(f, "{type} range"),
|
||||
Type::Range(r#type) => write!(f, "{type} range"),
|
||||
Type::SelfFunction => write!(f, "self"),
|
||||
Type::String => write!(f, "str"),
|
||||
Type::Struct(struct_type) => write!(f, "{struct_type}"),
|
||||
Type::Tuple { fields } => {
|
||||
Type::Tuple(fields) => {
|
||||
write!(f, "(")?;
|
||||
|
||||
for (index, r#type) in fields.iter().enumerate() {
|
||||
@ -231,13 +242,13 @@ impl Ord for Type {
|
||||
(Type::Character, Type::Character) => Ordering::Equal,
|
||||
(Type::Character, _) => Ordering::Greater,
|
||||
(Type::Enum(left_enum), Type::Enum(right_enum)) => left_enum.cmp(right_enum),
|
||||
(Type::Enum(_), _) => Ordering::Greater,
|
||||
(Type::Enum { .. }, _) => Ordering::Greater,
|
||||
(Type::Float, Type::Float) => Ordering::Equal,
|
||||
(Type::Float, _) => Ordering::Greater,
|
||||
(Type::Function(left_function), Type::Function(right_function)) => {
|
||||
left_function.cmp(right_function)
|
||||
}
|
||||
(Type::Function(_), _) => Ordering::Greater,
|
||||
(Type::Function { .. }, _) => Ordering::Greater,
|
||||
(Type::Generic { .. }, Type::Generic { .. }) => Ordering::Equal,
|
||||
(Type::Generic { .. }, _) => Ordering::Greater,
|
||||
(Type::Integer, Type::Integer) => Ordering::Equal,
|
||||
@ -246,26 +257,24 @@ impl Ord for Type {
|
||||
left_item_type.cmp(right_item_type)
|
||||
}
|
||||
(Type::List { .. }, _) => Ordering::Greater,
|
||||
(Type::Map { pairs: left_pairs }, Type::Map { pairs: right_pairs }) => {
|
||||
(Type::Map(left_pairs), Type::Map(right_pairs)) => {
|
||||
left_pairs.iter().cmp(right_pairs.iter())
|
||||
}
|
||||
(Type::Map { .. }, _) => Ordering::Greater,
|
||||
(Type::None, Type::None) => Ordering::Equal,
|
||||
(Type::None, _) => Ordering::Greater,
|
||||
(Type::Range { r#type: left_type }, Type::Range { r#type: right_type }) => {
|
||||
left_type.cmp(right_type)
|
||||
}
|
||||
(Type::Range(left_type), Type::Range(right_type)) => left_type.cmp(right_type),
|
||||
(Type::Range { .. }, _) => Ordering::Greater,
|
||||
(Type::SelfFunction, Type::SelfFunction) => Ordering::Equal,
|
||||
(Type::SelfFunction, _) => Ordering::Greater,
|
||||
(Type::String, Type::String) => Ordering::Equal,
|
||||
(Type::String { .. }, _) => Ordering::Greater,
|
||||
(Type::String, _) => Ordering::Greater,
|
||||
(Type::Struct(left_struct), Type::Struct(right_struct)) => {
|
||||
left_struct.cmp(right_struct)
|
||||
}
|
||||
(Type::Struct(_), _) => Ordering::Greater,
|
||||
(Type::Struct { .. }, _) => Ordering::Greater,
|
||||
|
||||
(Type::Tuple { fields: left }, Type::Tuple { fields: right }) => left.cmp(right),
|
||||
(Type::Tuple(left), Type::Tuple(right)) => left.cmp(right),
|
||||
(Type::Tuple { .. }, _) => Ordering::Greater,
|
||||
}
|
||||
}
|
||||
@ -274,12 +283,12 @@ impl Ord for Type {
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct FunctionType {
|
||||
pub type_parameters: Vec<u16>,
|
||||
pub value_parameters: Vec<(u16, Type)>,
|
||||
pub return_type: Type,
|
||||
pub value_parameters: Vec<Type>,
|
||||
pub return_type: Box<Type>,
|
||||
}
|
||||
|
||||
impl FunctionType {
|
||||
pub fn new<T: Into<Vec<u16>>, U: Into<Vec<(u16, Type)>>>(
|
||||
pub fn new<T: Into<Vec<u16>>, U: Into<Vec<Type>>>(
|
||||
type_parameters: T,
|
||||
value_parameters: U,
|
||||
return_type: Type,
|
||||
@ -287,7 +296,17 @@ impl FunctionType {
|
||||
FunctionType {
|
||||
type_parameters: type_parameters.into(),
|
||||
value_parameters: value_parameters.into(),
|
||||
return_type,
|
||||
return_type: Box::new(return_type),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FunctionType {
|
||||
fn default() -> Self {
|
||||
FunctionType {
|
||||
type_parameters: Vec::new(),
|
||||
value_parameters: Vec::new(),
|
||||
return_type: Box::new(Type::None),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -313,7 +332,7 @@ impl Display for FunctionType {
|
||||
write!(f, "(")?;
|
||||
|
||||
if !self.value_parameters.is_empty() {
|
||||
for (index, (_, r#type)) in self.value_parameters.iter().enumerate() {
|
||||
for (index, r#type) in self.value_parameters.iter().enumerate() {
|
||||
if index > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
@ -324,7 +343,7 @@ impl Display for FunctionType {
|
||||
|
||||
write!(f, ")")?;
|
||||
|
||||
if self.return_type != Type::None {
|
||||
if self.return_type.as_ref() != &Type::None {
|
||||
write!(f, " -> {}", self.return_type)?;
|
||||
}
|
||||
|
||||
@ -468,6 +487,12 @@ impl Display for EnumType {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct GenericType {
|
||||
pub identifier_index: u8,
|
||||
pub concrete_type: Option<Box<Type>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct TypeConflict {
|
||||
pub expected: Type,
|
||||
|
@ -1,27 +1,41 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use crate::{vm::ThreadData, Pointer, Type};
|
||||
use crate::{
|
||||
instruction::TypeCode,
|
||||
vm::{Pointer, Thread},
|
||||
};
|
||||
|
||||
use super::DustString;
|
||||
use super::{ConcreteValue, DustString};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||
pub struct AbstractList {
|
||||
pub item_type: Type,
|
||||
pub item_type: TypeCode,
|
||||
pub item_pointers: Vec<Pointer>,
|
||||
}
|
||||
|
||||
impl AbstractList {
|
||||
pub fn display(&self, data: &ThreadData) -> DustString {
|
||||
pub fn display(&self, thread: &Thread) -> DustString {
|
||||
let current_frame = thread.current_frame();
|
||||
let mut display = DustString::new();
|
||||
|
||||
display.push('[');
|
||||
|
||||
for (i, item) in self.item_pointers.iter().enumerate() {
|
||||
for (i, pointer) in self.item_pointers.iter().copied().enumerate() {
|
||||
if i > 0 {
|
||||
display.push_str(", ");
|
||||
}
|
||||
|
||||
let item_display = data.follow_pointer_unchecked(*item).display(data);
|
||||
let item_display = match self.item_type {
|
||||
TypeCode::BOOLEAN => current_frame.get_boolean_from_pointer(pointer).to_string(),
|
||||
TypeCode::BYTE => current_frame.get_byte_from_pointer(pointer).to_string(),
|
||||
TypeCode::CHARACTER => current_frame
|
||||
.get_character_from_pointer(pointer)
|
||||
.to_string(),
|
||||
TypeCode::FLOAT => current_frame.get_float_from_pointer(pointer).to_string(),
|
||||
TypeCode::INTEGER => current_frame.get_integer_from_pointer(pointer).to_string(),
|
||||
TypeCode::STRING => current_frame.get_string_from_pointer(pointer).to_string(),
|
||||
_ => todo!(),
|
||||
};
|
||||
|
||||
display.push_str(&item_display);
|
||||
}
|
||||
@ -30,6 +44,80 @@ impl AbstractList {
|
||||
|
||||
display
|
||||
}
|
||||
|
||||
pub fn to_concrete(&self, thread: &Thread) -> ConcreteValue {
|
||||
let mut concrete_list = Vec::with_capacity(self.item_pointers.len());
|
||||
|
||||
match self.item_type {
|
||||
TypeCode::BOOLEAN => {
|
||||
for pointer in &self.item_pointers {
|
||||
let boolean = thread.current_frame().get_boolean_from_pointer(*pointer);
|
||||
|
||||
concrete_list.push(ConcreteValue::Boolean(boolean));
|
||||
}
|
||||
}
|
||||
TypeCode::BYTE => {
|
||||
for pointer in &self.item_pointers {
|
||||
let byte = thread.current_frame().get_byte_from_pointer(*pointer);
|
||||
|
||||
concrete_list.push(ConcreteValue::Byte(byte));
|
||||
}
|
||||
}
|
||||
TypeCode::CHARACTER => {
|
||||
for pointer in &self.item_pointers {
|
||||
let character = thread.current_frame().get_character_from_pointer(*pointer);
|
||||
|
||||
concrete_list.push(ConcreteValue::Character(character));
|
||||
}
|
||||
}
|
||||
TypeCode::FLOAT => {
|
||||
for pointer in &self.item_pointers {
|
||||
let float = thread.current_frame().get_float_from_pointer(*pointer);
|
||||
|
||||
concrete_list.push(ConcreteValue::Float(float));
|
||||
}
|
||||
}
|
||||
TypeCode::INTEGER => {
|
||||
for pointer in &self.item_pointers {
|
||||
let integer = thread.current_frame().get_integer_from_pointer(*pointer);
|
||||
|
||||
concrete_list.push(ConcreteValue::Integer(integer));
|
||||
}
|
||||
}
|
||||
TypeCode::STRING => {
|
||||
for pointer in &self.item_pointers {
|
||||
let string = thread
|
||||
.current_frame()
|
||||
.get_string_from_pointer(*pointer)
|
||||
.clone();
|
||||
|
||||
concrete_list.push(ConcreteValue::String(string));
|
||||
}
|
||||
}
|
||||
TypeCode::LIST => {
|
||||
for pointer in &self.item_pointers {
|
||||
let list = thread
|
||||
.current_frame()
|
||||
.get_list_from_pointer(pointer)
|
||||
.to_concrete(thread);
|
||||
|
||||
concrete_list.push(list);
|
||||
}
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
|
||||
ConcreteValue::List(concrete_list)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AbstractList {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
item_type: TypeCode::NONE,
|
||||
item_pointers: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for AbstractList {
|
||||
@ -37,7 +125,7 @@ impl Display for AbstractList {
|
||||
write!(f, "[")?;
|
||||
|
||||
for pointer in &self.item_pointers {
|
||||
write!(f, "{}", pointer)?;
|
||||
write!(f, "{:?}", pointer)?;
|
||||
}
|
||||
|
||||
write!(f, "]")
|
||||
|
@ -4,13 +4,14 @@ use serde::{Deserialize, Serialize};
|
||||
use smartstring::{LazyCompact, SmartString};
|
||||
use tracing::trace;
|
||||
|
||||
use crate::{Type, Value, ValueError};
|
||||
use crate::{Type, Value};
|
||||
|
||||
use super::RangeValue;
|
||||
|
||||
pub type DustString = SmartString<LazyCompact>;
|
||||
|
||||
#[derive(Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
#[serde(tag = "type", content = "value")]
|
||||
pub enum ConcreteValue {
|
||||
Boolean(bool),
|
||||
Byte(u8),
|
||||
@ -27,14 +28,54 @@ impl ConcreteValue {
|
||||
Value::Concrete(self)
|
||||
}
|
||||
|
||||
pub fn list<T: Into<Vec<ConcreteValue>>>(into_list: T) -> Self {
|
||||
ConcreteValue::List(into_list.into())
|
||||
pub fn list<T: Into<Vec<ConcreteValue>>>(into_items: T) -> Self {
|
||||
ConcreteValue::List(into_items.into())
|
||||
}
|
||||
|
||||
pub fn string<T: Into<SmartString<LazyCompact>>>(to_string: T) -> Self {
|
||||
ConcreteValue::String(to_string.into())
|
||||
}
|
||||
|
||||
pub fn as_boolean(&self) -> Option<&bool> {
|
||||
if let ConcreteValue::Boolean(boolean) = self {
|
||||
Some(boolean)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_byte(&self) -> Option<&u8> {
|
||||
if let ConcreteValue::Byte(byte) = self {
|
||||
Some(byte)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_character(&self) -> Option<&char> {
|
||||
if let ConcreteValue::Character(character) = self {
|
||||
Some(character)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_float(&self) -> Option<&f64> {
|
||||
if let ConcreteValue::Float(float) = self {
|
||||
Some(float)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_integer(&self) -> Option<&i64> {
|
||||
if let ConcreteValue::Integer(integer) = self {
|
||||
Some(integer)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_string(&self) -> Option<&DustString> {
|
||||
if let ConcreteValue::String(string) = self {
|
||||
Some(string)
|
||||
@ -43,6 +84,22 @@ impl ConcreteValue {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_list(&self) -> Option<&Vec<ConcreteValue>> {
|
||||
if let ConcreteValue::List(list) = self {
|
||||
Some(list)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_range(&self) -> Option<&RangeValue> {
|
||||
if let ConcreteValue::Range(range) = self {
|
||||
Some(range)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn display(&self) -> DustString {
|
||||
DustString::from(self.to_string())
|
||||
}
|
||||
@ -54,236 +111,11 @@ impl ConcreteValue {
|
||||
ConcreteValue::Character(_) => Type::Character,
|
||||
ConcreteValue::Float(_) => Type::Float,
|
||||
ConcreteValue::Integer(_) => Type::Integer,
|
||||
ConcreteValue::List(list) => {
|
||||
let item_type = list.first().map_or(Type::Any, |item| item.r#type());
|
||||
|
||||
Type::List(Box::new(item_type))
|
||||
}
|
||||
ConcreteValue::List(items) => items.first().map_or(Type::Any, |item| item.r#type()),
|
||||
ConcreteValue::Range(range) => range.r#type(),
|
||||
ConcreteValue::String(_) => Type::String,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&self, other: &Self) -> ConcreteValue {
|
||||
use ConcreteValue::*;
|
||||
|
||||
match (self, other) {
|
||||
(Byte(left), Byte(right)) => {
|
||||
let sum = left.saturating_add(*right);
|
||||
|
||||
Byte(sum)
|
||||
}
|
||||
(Character(left), Character(right)) => {
|
||||
let mut concatenated = DustString::new();
|
||||
|
||||
concatenated.push(*left);
|
||||
concatenated.push(*right);
|
||||
|
||||
String(concatenated)
|
||||
}
|
||||
(Character(left), String(right)) => {
|
||||
let mut concatenated = DustString::new();
|
||||
|
||||
concatenated.push(*left);
|
||||
concatenated.push_str(right);
|
||||
|
||||
String(concatenated)
|
||||
}
|
||||
(Float(left), Float(right)) => {
|
||||
let sum = left + right;
|
||||
|
||||
Float(sum)
|
||||
}
|
||||
(Integer(left), Integer(right)) => {
|
||||
let sum = left.saturating_add(*right);
|
||||
|
||||
Integer(sum)
|
||||
}
|
||||
(String(left), Character(right)) => {
|
||||
let concatenated = format!("{}{}", left, right);
|
||||
|
||||
String(DustString::from(concatenated))
|
||||
}
|
||||
(String(left), String(right)) => {
|
||||
let concatenated = format!("{}{}", left, right);
|
||||
|
||||
String(DustString::from(concatenated))
|
||||
}
|
||||
_ => panic!(
|
||||
"{}",
|
||||
ValueError::CannotAdd(
|
||||
Value::Concrete(self.clone()),
|
||||
Value::Concrete(other.clone())
|
||||
)
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn subtract(&self, other: &Self) -> ConcreteValue {
|
||||
use ConcreteValue::*;
|
||||
|
||||
match (self, other) {
|
||||
(Byte(left), Byte(right)) => ConcreteValue::Byte(left.saturating_sub(*right)),
|
||||
(Float(left), Float(right)) => ConcreteValue::Float(left - right),
|
||||
(Integer(left), Integer(right)) => ConcreteValue::Integer(left.saturating_sub(*right)),
|
||||
_ => panic!(
|
||||
"{}",
|
||||
ValueError::CannotSubtract(
|
||||
Value::Concrete(self.clone()),
|
||||
Value::Concrete(other.clone())
|
||||
)
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn multiply(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
|
||||
use ConcreteValue::*;
|
||||
|
||||
let product = match (self, other) {
|
||||
(Byte(left), Byte(right)) => ConcreteValue::Byte(left.saturating_mul(*right)),
|
||||
(Float(left), Float(right)) => ConcreteValue::Float(left * right),
|
||||
(Integer(left), Integer(right)) => ConcreteValue::Integer(left.saturating_mul(*right)),
|
||||
_ => {
|
||||
return Err(ValueError::CannotMultiply(
|
||||
self.clone().to_value(),
|
||||
other.clone().to_value(),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(product)
|
||||
}
|
||||
|
||||
pub fn divide(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
|
||||
use ConcreteValue::*;
|
||||
|
||||
let quotient = match (self, other) {
|
||||
(Byte(left), Byte(right)) => ConcreteValue::Byte(left.saturating_div(*right)),
|
||||
(Float(left), Float(right)) => ConcreteValue::Float(left / right),
|
||||
(Integer(left), Integer(right)) => ConcreteValue::Integer(left.saturating_div(*right)),
|
||||
_ => {
|
||||
return Err(ValueError::CannotMultiply(
|
||||
self.clone().to_value(),
|
||||
other.clone().to_value(),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(quotient)
|
||||
}
|
||||
|
||||
pub fn modulo(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
|
||||
use ConcreteValue::*;
|
||||
|
||||
let product = match (self, other) {
|
||||
(Byte(left), Byte(right)) => ConcreteValue::Byte(left.wrapping_rem(*right)),
|
||||
(Float(left), Float(right)) => ConcreteValue::Float(left % right),
|
||||
(Integer(left), Integer(right)) => {
|
||||
ConcreteValue::Integer(left.wrapping_rem_euclid(*right))
|
||||
}
|
||||
_ => {
|
||||
return Err(ValueError::CannotMultiply(
|
||||
self.clone().to_value(),
|
||||
other.clone().to_value(),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(product)
|
||||
}
|
||||
|
||||
pub fn negate(&self) -> ConcreteValue {
|
||||
use ConcreteValue::*;
|
||||
|
||||
match self {
|
||||
Boolean(value) => ConcreteValue::Boolean(!value),
|
||||
Byte(value) => ConcreteValue::Byte(value.wrapping_neg()),
|
||||
Float(value) => ConcreteValue::Float(-value),
|
||||
Integer(value) => ConcreteValue::Integer(value.wrapping_neg()),
|
||||
_ => panic!("{}", ValueError::CannotNegate(self.clone().to_value())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn not(&self) -> Result<ConcreteValue, ValueError> {
|
||||
use ConcreteValue::*;
|
||||
|
||||
let not = match self {
|
||||
Boolean(value) => ConcreteValue::Boolean(!value),
|
||||
_ => return Err(ValueError::CannotNot(self.clone().to_value())),
|
||||
};
|
||||
|
||||
Ok(not)
|
||||
}
|
||||
|
||||
pub fn equals(&self, other: &ConcreteValue) -> bool {
|
||||
use ConcreteValue::*;
|
||||
|
||||
match (self, other) {
|
||||
(Boolean(left), Boolean(right)) => left == right,
|
||||
(Byte(left), Byte(right)) => left == right,
|
||||
(Character(left), Character(right)) => left == right,
|
||||
(Float(left), Float(right)) => left == right,
|
||||
(Integer(left), Integer(right)) => left == right,
|
||||
(List(left), List(right)) => left == right,
|
||||
(Range(left), Range(right)) => left == right,
|
||||
(String(left), String(right)) => left == right,
|
||||
_ => {
|
||||
panic!(
|
||||
"{}",
|
||||
ValueError::CannotCompare(
|
||||
Value::Concrete(self.clone()),
|
||||
Value::Concrete(other.clone())
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn less_than(&self, other: &ConcreteValue) -> Result<ConcreteValue, ValueError> {
|
||||
use ConcreteValue::*;
|
||||
|
||||
let less_than = match (self, other) {
|
||||
(Boolean(left), Boolean(right)) => ConcreteValue::Boolean(left < right),
|
||||
(Byte(left), Byte(right)) => ConcreteValue::Boolean(left < right),
|
||||
(Character(left), Character(right)) => ConcreteValue::Boolean(left < right),
|
||||
(Float(left), Float(right)) => ConcreteValue::Boolean(left < right),
|
||||
(Integer(left), Integer(right)) => ConcreteValue::Boolean(left < right),
|
||||
(List(left), List(right)) => ConcreteValue::Boolean(left < right),
|
||||
(Range(left), Range(right)) => ConcreteValue::Boolean(left < right),
|
||||
(String(left), String(right)) => ConcreteValue::Boolean(left < right),
|
||||
_ => {
|
||||
return Err(ValueError::CannotCompare(
|
||||
self.clone().to_value(),
|
||||
other.clone().to_value(),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(less_than)
|
||||
}
|
||||
|
||||
pub fn less_than_or_equals(&self, other: &ConcreteValue) -> Result<ConcreteValue, ValueError> {
|
||||
use ConcreteValue::*;
|
||||
|
||||
let less_than_or_equal = match (self, other) {
|
||||
(Boolean(left), Boolean(right)) => ConcreteValue::Boolean(left <= right),
|
||||
(Byte(left), Byte(right)) => ConcreteValue::Boolean(left <= right),
|
||||
(Character(left), Character(right)) => ConcreteValue::Boolean(left <= right),
|
||||
(Float(left), Float(right)) => ConcreteValue::Boolean(left <= right),
|
||||
(Integer(left), Integer(right)) => ConcreteValue::Boolean(left <= right),
|
||||
(List(left), List(right)) => ConcreteValue::Boolean(left <= right),
|
||||
(Range(left), Range(right)) => ConcreteValue::Boolean(left <= right),
|
||||
(String(left), String(right)) => ConcreteValue::Boolean(left <= right),
|
||||
_ => {
|
||||
return Err(ValueError::CannotCompare(
|
||||
self.clone().to_value(),
|
||||
other.clone().to_value(),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(less_than_or_equal)
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for ConcreteValue {
|
||||
@ -296,7 +128,7 @@ impl Clone for ConcreteValue {
|
||||
ConcreteValue::Character(character) => ConcreteValue::Character(*character),
|
||||
ConcreteValue::Float(float) => ConcreteValue::Float(*float),
|
||||
ConcreteValue::Integer(integer) => ConcreteValue::Integer(*integer),
|
||||
ConcreteValue::List(list) => ConcreteValue::List(list.clone()),
|
||||
ConcreteValue::List(items) => ConcreteValue::List(items.clone()),
|
||||
ConcreteValue::Range(range) => ConcreteValue::Range(*range),
|
||||
ConcreteValue::String(string) => ConcreteValue::String(string.clone()),
|
||||
}
|
||||
@ -319,10 +151,10 @@ impl Display for ConcreteValue {
|
||||
Ok(())
|
||||
}
|
||||
ConcreteValue::Integer(integer) => write!(f, "{integer}"),
|
||||
ConcreteValue::List(list) => {
|
||||
ConcreteValue::List(items) => {
|
||||
write!(f, "[")?;
|
||||
|
||||
for (index, item) in list.iter().enumerate() {
|
||||
for (index, item) in items.iter().enumerate() {
|
||||
if index > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use crate::FunctionType;
|
||||
|
||||
use super::DustString;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||
#[derive(Clone, Debug, Default, PartialEq, PartialOrd)]
|
||||
pub struct Function {
|
||||
pub name: Option<DustString>,
|
||||
pub r#type: FunctionType,
|
||||
|
@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use std::fmt::{self, Debug, Display, Formatter};
|
||||
|
||||
use crate::{Type, vm::ThreadData};
|
||||
use crate::{Type, vm::Thread};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub enum Value {
|
||||
@ -50,6 +50,14 @@ impl Value {
|
||||
Value::Concrete(ConcreteValue::String(string.into()))
|
||||
}
|
||||
|
||||
pub fn as_concrete(&self) -> Option<&ConcreteValue> {
|
||||
if let Value::Concrete(concrete_value) = self {
|
||||
Some(concrete_value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_boolean(&self) -> Option<bool> {
|
||||
if let Value::Concrete(ConcreteValue::Boolean(boolean)) = self {
|
||||
Some(*boolean)
|
||||
@ -117,109 +125,12 @@ impl Value {
|
||||
pub fn r#type(&self) -> Type {
|
||||
match self {
|
||||
Value::Concrete(concrete_value) => concrete_value.r#type(),
|
||||
Value::AbstractList(AbstractList { item_type, .. }) => {
|
||||
Type::List(Box::new(item_type.clone()))
|
||||
}
|
||||
Value::Function(Function { r#type, .. }) => Type::Function(Box::new(r#type.clone())),
|
||||
Value::AbstractList(AbstractList { item_type, .. }) => Type::List(*item_type),
|
||||
Value::Function(Function { r#type, .. }) => Type::Function(r#type.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&self, other: &Value) -> Value {
|
||||
let sum = match (self, other) {
|
||||
(Value::Concrete(left), Value::Concrete(right)) => left.add(right),
|
||||
_ => panic!("{}", ValueError::CannotAdd(self.clone(), other.clone())),
|
||||
};
|
||||
|
||||
Value::Concrete(sum)
|
||||
}
|
||||
|
||||
pub fn subtract(&self, other: &Value) -> Value {
|
||||
let difference = match (self, other) {
|
||||
(Value::Concrete(left), Value::Concrete(right)) => left.subtract(right),
|
||||
_ => panic!(
|
||||
"{}",
|
||||
ValueError::CannotSubtract(self.clone(), other.clone())
|
||||
),
|
||||
};
|
||||
|
||||
Value::Concrete(difference)
|
||||
}
|
||||
|
||||
pub fn multiply(&self, other: &Value) -> Result<Value, ValueError> {
|
||||
match (self, other) {
|
||||
(Value::Concrete(left), Value::Concrete(right)) => {
|
||||
left.multiply(right).map(Value::Concrete)
|
||||
}
|
||||
_ => Err(ValueError::CannotMultiply(
|
||||
self.to_owned(),
|
||||
other.to_owned(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn divide(&self, other: &Value) -> Result<Value, ValueError> {
|
||||
match (self, other) {
|
||||
(Value::Concrete(left), Value::Concrete(right)) => {
|
||||
left.divide(right).map(Value::Concrete)
|
||||
}
|
||||
_ => Err(ValueError::CannotDivide(self.to_owned(), other.to_owned())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn modulo(&self, other: &Value) -> Result<Value, ValueError> {
|
||||
match (self, other) {
|
||||
(Value::Concrete(left), Value::Concrete(right)) => {
|
||||
left.modulo(right).map(Value::Concrete)
|
||||
}
|
||||
_ => Err(ValueError::CannotModulo(self.to_owned(), other.to_owned())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn negate(&self) -> Value {
|
||||
let concrete = match self {
|
||||
Value::Concrete(concrete_value) => concrete_value.negate(),
|
||||
_ => panic!("{}", ValueError::CannotNegate(self.clone())),
|
||||
};
|
||||
|
||||
Value::Concrete(concrete)
|
||||
}
|
||||
|
||||
pub fn not(&self) -> Result<Value, ValueError> {
|
||||
match self {
|
||||
Value::Concrete(concrete_value) => concrete_value.not().map(Value::Concrete),
|
||||
_ => Err(ValueError::CannotNot(self.to_owned())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn equals(&self, other: &Value) -> bool {
|
||||
match (self, other) {
|
||||
(Value::Concrete(left), Value::Concrete(right)) => left.equals(right),
|
||||
_ => panic!(
|
||||
"{}",
|
||||
ValueError::CannotCompare(self.to_owned(), other.to_owned())
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn less(&self, other: &Value) -> Result<Value, ValueError> {
|
||||
match (self, other) {
|
||||
(Value::Concrete(left), Value::Concrete(right)) => {
|
||||
left.less_than(right).map(Value::Concrete)
|
||||
}
|
||||
_ => Err(ValueError::CannotCompare(self.to_owned(), other.to_owned())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn less_than_or_equals(&self, other: &Value) -> Result<Value, ValueError> {
|
||||
match (self, other) {
|
||||
(Value::Concrete(left), Value::Concrete(right)) => {
|
||||
left.less_than_or_equals(right).map(Value::Concrete)
|
||||
}
|
||||
_ => Err(ValueError::CannotCompare(self.to_owned(), other.to_owned())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn display(&self, data: &ThreadData) -> DustString {
|
||||
pub fn display(&self, data: &Thread) -> DustString {
|
||||
match self {
|
||||
Value::AbstractList(list) => list.display(data),
|
||||
Value::Concrete(concrete_value) => concrete_value.display(),
|
||||
|
@ -32,9 +32,7 @@ impl RangeValue {
|
||||
}
|
||||
};
|
||||
|
||||
Type::Range {
|
||||
r#type: Box::new(inner_type),
|
||||
}
|
||||
Type::Range(Box::new(inner_type))
|
||||
}
|
||||
}
|
||||
|
||||
|
287
dust-lang/src/vm/action/add.rs
Normal file
287
dust-lang/src/vm/action/add.rs
Normal file
@ -0,0 +1,287 @@
|
||||
use std::ops::Add;
|
||||
|
||||
use tracing::trace;
|
||||
|
||||
use crate::{
|
||||
vm::{call_frame::RuntimeValue, Thread},
|
||||
DustString, Instruction,
|
||||
};
|
||||
|
||||
use super::Cache;
|
||||
|
||||
pub fn add_bytes(_: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
|
||||
let destination_index = instruction.a_field() as usize;
|
||||
let left_index = instruction.b_field() as usize;
|
||||
let right_index = instruction.c_field() as usize;
|
||||
|
||||
let current_frame = thread.current_frame_mut();
|
||||
let left_value = current_frame.get_byte_from_register(left_index).clone();
|
||||
let right_value = current_frame.get_byte_from_register(right_index).clone();
|
||||
let sum = left_value.add(&right_value);
|
||||
|
||||
current_frame
|
||||
.registers
|
||||
.bytes
|
||||
.get_mut(destination_index)
|
||||
.as_value_mut()
|
||||
.set_inner(sum);
|
||||
}
|
||||
|
||||
pub fn add_characters(
|
||||
_: &mut usize,
|
||||
instruction: &Instruction,
|
||||
thread: &mut Thread,
|
||||
_: &mut Cache,
|
||||
) {
|
||||
let destination_index = instruction.a_field() as usize;
|
||||
let left_index = instruction.b_field() as usize;
|
||||
let left_is_constant = instruction.b_is_constant();
|
||||
let right = instruction.c_field() as usize;
|
||||
let right_is_constant = instruction.c_is_constant();
|
||||
|
||||
let current_frame = thread.current_frame_mut();
|
||||
let left_value = if left_is_constant {
|
||||
current_frame.get_character_constant(left_index)
|
||||
} else {
|
||||
current_frame.get_character_from_register(left_index)
|
||||
};
|
||||
let right_value = if right_is_constant {
|
||||
current_frame.get_character_constant(right)
|
||||
} else {
|
||||
current_frame.get_character_from_register(right)
|
||||
};
|
||||
let concatenated = {
|
||||
let mut concatenated = DustString::from(String::with_capacity(2));
|
||||
|
||||
concatenated.push(left_value.clone_inner());
|
||||
concatenated.push(right_value.clone_inner());
|
||||
|
||||
RuntimeValue::Raw(concatenated)
|
||||
};
|
||||
|
||||
current_frame
|
||||
.registers
|
||||
.strings
|
||||
.get_mut(destination_index)
|
||||
.set(concatenated);
|
||||
}
|
||||
|
||||
pub fn add_floats(_: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
|
||||
let destination = instruction.a_field() as usize;
|
||||
let left = instruction.b_field() as usize;
|
||||
let left_is_constant = instruction.b_is_constant();
|
||||
let right = instruction.c_field() as usize;
|
||||
let right_is_constant = instruction.c_is_constant();
|
||||
|
||||
let current_frame = thread.current_frame_mut();
|
||||
let left_value = if left_is_constant {
|
||||
current_frame.get_float_constant(left)
|
||||
} else {
|
||||
current_frame.get_float_from_register(left)
|
||||
}
|
||||
.clone();
|
||||
let right_value = if right_is_constant {
|
||||
current_frame.get_float_constant(right)
|
||||
} else {
|
||||
current_frame.get_float_from_register(right)
|
||||
}
|
||||
.clone();
|
||||
let sum = left_value.add(&right_value);
|
||||
|
||||
current_frame
|
||||
.registers
|
||||
.floats
|
||||
.get_mut(destination)
|
||||
.as_value_mut()
|
||||
.set_inner(sum);
|
||||
}
|
||||
|
||||
pub fn add_integers(_: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
|
||||
let destination = instruction.a_field() as usize;
|
||||
let left = instruction.b_field() as usize;
|
||||
let left_is_constant = instruction.b_is_constant();
|
||||
let right = instruction.c_field() as usize;
|
||||
let right_is_constant = instruction.c_is_constant();
|
||||
|
||||
let current_frame = thread.current_frame_mut();
|
||||
let left_value = if left_is_constant {
|
||||
current_frame.get_integer_constant(left)
|
||||
} else {
|
||||
current_frame.get_integer_from_register(left)
|
||||
}
|
||||
.clone();
|
||||
let right_value = if right_is_constant {
|
||||
current_frame.get_integer_constant(right)
|
||||
} else {
|
||||
current_frame.get_integer_from_register(right)
|
||||
}
|
||||
.clone();
|
||||
let sum = left_value.add(&right_value);
|
||||
|
||||
current_frame
|
||||
.registers
|
||||
.integers
|
||||
.get_mut(destination)
|
||||
.as_value_mut()
|
||||
.set_inner(sum);
|
||||
}
|
||||
|
||||
pub fn add_strings(_: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
|
||||
let destination = instruction.a_field() as usize;
|
||||
let left = instruction.b_field() as usize;
|
||||
let left_is_constant = instruction.b_is_constant();
|
||||
let right = instruction.c_field() as usize;
|
||||
let right_is_constant = instruction.c_is_constant();
|
||||
|
||||
let current_frame = thread.current_frame_mut();
|
||||
let left_value = if left_is_constant {
|
||||
current_frame.get_string_constant(left)
|
||||
} else {
|
||||
current_frame.get_string_from_register(left)
|
||||
};
|
||||
let right_value = if right_is_constant {
|
||||
current_frame.get_string_constant(right)
|
||||
} else {
|
||||
current_frame.get_string_from_register(right)
|
||||
};
|
||||
let concatenated = DustString::from(format!("{left_value}{right_value}"));
|
||||
|
||||
current_frame
|
||||
.registers
|
||||
.strings
|
||||
.get_mut(destination)
|
||||
.as_value_mut()
|
||||
.set_inner(concatenated);
|
||||
}
|
||||
|
||||
pub fn add_character_string(
|
||||
_: &mut usize,
|
||||
instruction: &Instruction,
|
||||
thread: &mut Thread,
|
||||
_: &mut Cache,
|
||||
) {
|
||||
let destination = instruction.a_field() as usize;
|
||||
let left = instruction.b_field() as usize;
|
||||
let left_is_constant = instruction.b_is_constant();
|
||||
let right = instruction.c_field() as usize;
|
||||
let right_is_constant = instruction.c_is_constant();
|
||||
|
||||
let current_frame = thread.current_frame_mut();
|
||||
let left_value = if left_is_constant {
|
||||
current_frame.get_character_constant(left)
|
||||
} else {
|
||||
current_frame.get_character_from_register(left)
|
||||
};
|
||||
let right_value = if right_is_constant {
|
||||
current_frame.get_string_constant(right)
|
||||
} else {
|
||||
current_frame.get_string_from_register(right)
|
||||
};
|
||||
let concatenated = DustString::from(format!("{left_value}{right_value}"));
|
||||
|
||||
current_frame
|
||||
.registers
|
||||
.strings
|
||||
.get_mut(destination)
|
||||
.as_value_mut()
|
||||
.set_inner(concatenated);
|
||||
}
|
||||
|
||||
pub fn add_string_character(
|
||||
_: &mut usize,
|
||||
instruction: &Instruction,
|
||||
thread: &mut Thread,
|
||||
_: &mut Cache,
|
||||
) {
|
||||
let destination = instruction.a_field() as usize;
|
||||
let left = instruction.b_field() as usize;
|
||||
let left_is_constant = instruction.b_is_constant();
|
||||
let right = instruction.c_field() as usize;
|
||||
let right_is_constant = instruction.c_is_constant();
|
||||
|
||||
let current_frame = thread.current_frame_mut();
|
||||
let left_value = if left_is_constant {
|
||||
current_frame.get_string_constant(left)
|
||||
} else {
|
||||
current_frame.get_string_from_register(left)
|
||||
};
|
||||
let right_value = if right_is_constant {
|
||||
current_frame.get_character_constant(right)
|
||||
} else {
|
||||
current_frame.get_character_from_register(right)
|
||||
};
|
||||
let concatenated = DustString::from(format!("{left_value}{right_value}"));
|
||||
|
||||
current_frame
|
||||
.registers
|
||||
.strings
|
||||
.get_mut(destination)
|
||||
.as_value_mut()
|
||||
.set_inner(concatenated);
|
||||
}
|
||||
|
||||
pub fn optimized_add_integer(
|
||||
_: &mut usize,
|
||||
instruction: &Instruction,
|
||||
thread: &mut Thread,
|
||||
cache: &mut Cache,
|
||||
) {
|
||||
if let Cache::IntegerMath([destination, left, right]) = cache {
|
||||
trace!("OPTIMIZED_ADD using integer cache");
|
||||
|
||||
let sum = left.add(right);
|
||||
|
||||
*destination.borrow_mut() = sum;
|
||||
} else {
|
||||
let destination_index = instruction.a_field() as usize;
|
||||
let left_index = instruction.b_field() as usize;
|
||||
let left_is_constant = instruction.b_is_constant();
|
||||
let right_index = instruction.c_field() as usize;
|
||||
let right_is_constant = instruction.c_is_constant();
|
||||
let current_frame = thread.current_frame_mut();
|
||||
let left_value = if left_is_constant {
|
||||
let value = current_frame.get_integer_constant_mut(left_index).to_rc();
|
||||
|
||||
current_frame.constants.integers[left_index] = value.clone();
|
||||
|
||||
value
|
||||
} else {
|
||||
let value = current_frame
|
||||
.get_integer_from_register_mut(left_index)
|
||||
.to_ref_cell();
|
||||
|
||||
current_frame.registers.integers[left_index].set(value.clone());
|
||||
|
||||
value
|
||||
};
|
||||
let right_value = if right_is_constant {
|
||||
let value = current_frame.get_integer_constant_mut(right_index).to_rc();
|
||||
|
||||
current_frame.constants.integers[right_index] = value.clone();
|
||||
|
||||
value
|
||||
} else {
|
||||
let value = current_frame
|
||||
.get_integer_from_register_mut(right_index)
|
||||
.to_ref_cell();
|
||||
|
||||
current_frame.registers.integers[right_index].set(value.clone());
|
||||
|
||||
value
|
||||
};
|
||||
let sum = left_value.add(&right_value);
|
||||
let destination = {
|
||||
let mut value = current_frame
|
||||
.get_integer_from_register_mut(destination_index)
|
||||
.to_ref_cell();
|
||||
|
||||
value.set_inner(sum);
|
||||
|
||||
current_frame.registers.integers[destination_index].set(value.clone());
|
||||
|
||||
value
|
||||
};
|
||||
|
||||
*cache = Cache::IntegerMath([destination, left_value, right_value]);
|
||||
}
|
||||
}
|
217
dust-lang/src/vm/action/equal.rs
Normal file
217
dust-lang/src/vm/action/equal.rs
Normal file
@ -0,0 +1,217 @@
|
||||
use tracing::trace;
|
||||
|
||||
use crate::{vm::Thread, Instruction};
|
||||
|
||||
use super::Cache;
|
||||
|
||||
pub fn equal_booleans(
|
||||
ip: &mut usize,
|
||||
instruction: &Instruction,
|
||||
thread: &mut Thread,
|
||||
_: &mut Cache,
|
||||
) {
|
||||
let left_index = instruction.b_field() as usize;
|
||||
let right_index = instruction.c_field() as usize;
|
||||
let comparator = instruction.d_field();
|
||||
|
||||
let current_frame = thread.current_frame_mut();
|
||||
let left_value = current_frame.get_boolean_from_register(left_index);
|
||||
let right_value = current_frame.get_boolean_from_register(right_index);
|
||||
let is_equal = left_value == right_value;
|
||||
|
||||
if is_equal == comparator {
|
||||
*ip += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn equal_bytes(ip: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
|
||||
let left = instruction.b_field() as usize;
|
||||
let right = instruction.c_field() as usize;
|
||||
let comparator = instruction.d_field();
|
||||
|
||||
let current_frame = thread.current_frame_mut();
|
||||
let left_value = current_frame.get_byte_from_register(left);
|
||||
let right_value = current_frame.get_byte_from_register(right);
|
||||
let is_equal = left_value == right_value;
|
||||
|
||||
if is_equal == comparator {
|
||||
*ip += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn equal_characters(
|
||||
ip: &mut usize,
|
||||
instruction: &Instruction,
|
||||
thread: &mut Thread,
|
||||
_: &mut Cache,
|
||||
) {
|
||||
let left_index = instruction.b_field() as usize;
|
||||
let left_is_constant = instruction.b_is_constant();
|
||||
let right_index = instruction.c_field() as usize;
|
||||
let right_is_constant = instruction.c_is_constant();
|
||||
let comparator = instruction.d_field();
|
||||
|
||||
let current_frame = thread.current_frame_mut();
|
||||
let left_value = if left_is_constant {
|
||||
current_frame.get_character_constant(left_index)
|
||||
} else {
|
||||
current_frame.get_character_from_register(left_index)
|
||||
};
|
||||
let right_value = if right_is_constant {
|
||||
current_frame.get_character_constant(right_index)
|
||||
} else {
|
||||
current_frame.get_character_from_register(right_index)
|
||||
};
|
||||
let is_equal = left_value == right_value;
|
||||
|
||||
if is_equal == comparator {
|
||||
*ip += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn equal_floats(ip: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
|
||||
let left = instruction.b_field() as usize;
|
||||
let left_is_constant = instruction.b_is_constant();
|
||||
let right = instruction.c_field() as usize;
|
||||
let right_is_constant = instruction.c_is_constant();
|
||||
let comparator = instruction.d_field();
|
||||
|
||||
let current_frame = thread.current_frame_mut();
|
||||
let left_value = if left_is_constant {
|
||||
current_frame.get_float_constant(left)
|
||||
} else {
|
||||
current_frame.get_float_from_register(left)
|
||||
};
|
||||
let right_value = if right_is_constant {
|
||||
current_frame.get_float_constant(right)
|
||||
} else {
|
||||
current_frame.get_float_from_register(right)
|
||||
};
|
||||
let is_equal = left_value == right_value;
|
||||
|
||||
if is_equal == comparator {
|
||||
*ip += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn equal_integers(
|
||||
ip: &mut usize,
|
||||
instruction: &Instruction,
|
||||
thread: &mut Thread,
|
||||
_: &mut Cache,
|
||||
) {
|
||||
let left = instruction.b_field() as usize;
|
||||
let left_is_constant = instruction.b_is_constant();
|
||||
let right = instruction.c_field() as usize;
|
||||
let right_is_constant = instruction.c_is_constant();
|
||||
let comparator = instruction.d_field();
|
||||
|
||||
let current_frame = thread.current_frame_mut();
|
||||
let left_value = if left_is_constant {
|
||||
current_frame.get_integer_constant(left)
|
||||
} else {
|
||||
current_frame.get_integer_from_register(left)
|
||||
};
|
||||
let right_value = if right_is_constant {
|
||||
current_frame.get_integer_constant(right)
|
||||
} else {
|
||||
current_frame.get_integer_from_register(right)
|
||||
};
|
||||
let is_equal = left_value == right_value;
|
||||
|
||||
if is_equal == comparator {
|
||||
*ip += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn equal_strings(
|
||||
ip: &mut usize,
|
||||
instruction: &Instruction,
|
||||
thread: &mut Thread,
|
||||
_: &mut Cache,
|
||||
) {
|
||||
let left = instruction.b_field() as usize;
|
||||
let left_is_constant = instruction.b_is_constant();
|
||||
let right = instruction.c_field() as usize;
|
||||
let right_is_constant = instruction.c_is_constant();
|
||||
let comparator = instruction.d_field();
|
||||
|
||||
let current_frame = thread.current_frame_mut();
|
||||
let left_value = if left_is_constant {
|
||||
current_frame.get_string_constant(left)
|
||||
} else {
|
||||
current_frame.get_string_from_register(left)
|
||||
};
|
||||
let right_value = if right_is_constant {
|
||||
current_frame.get_string_constant(right)
|
||||
} else {
|
||||
current_frame.get_string_from_register(right)
|
||||
};
|
||||
let is_equal = left_value == right_value;
|
||||
|
||||
if is_equal == comparator {
|
||||
*ip += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn optimized_equal_integers(
|
||||
ip: &mut usize,
|
||||
instruction: &Instruction,
|
||||
thread: &mut Thread,
|
||||
cache: &mut Cache,
|
||||
) {
|
||||
if let Cache::IntegerComparison([left, right]) = cache {
|
||||
trace!("equal_INTEGERS_OPTIMIZED using cache");
|
||||
|
||||
let is_equal = left == right;
|
||||
|
||||
if is_equal {
|
||||
*ip += 1;
|
||||
}
|
||||
} else {
|
||||
let left_index = instruction.b_field() as usize;
|
||||
let left_is_constant = instruction.b_is_constant();
|
||||
let right_index = instruction.c_field() as usize;
|
||||
let right_is_constant = instruction.c_is_constant();
|
||||
let comparator = instruction.d_field();
|
||||
|
||||
let current_frame = thread.current_frame_mut();
|
||||
let left_value = if left_is_constant {
|
||||
let value = current_frame.get_integer_constant_mut(left_index).to_rc();
|
||||
|
||||
current_frame.constants.integers[left_index] = value.clone();
|
||||
|
||||
value
|
||||
} else {
|
||||
let value = current_frame
|
||||
.get_integer_from_register_mut(left_index)
|
||||
.to_ref_cell();
|
||||
|
||||
current_frame.registers.integers[left_index].set(value.clone());
|
||||
|
||||
value
|
||||
};
|
||||
let right_value = if right_is_constant {
|
||||
let value = current_frame.get_integer_constant_mut(right_index).to_rc();
|
||||
|
||||
current_frame.constants.integers[right_index] = value.clone();
|
||||
|
||||
value
|
||||
} else {
|
||||
let value = current_frame
|
||||
.get_integer_from_register_mut(right_index)
|
||||
.to_ref_cell();
|
||||
|
||||
current_frame.registers.integers[right_index].set(value.clone());
|
||||
|
||||
value
|
||||
};
|
||||
let is_equal = left_value == right_value;
|
||||
|
||||
if is_equal == comparator {
|
||||
*ip += 1;
|
||||
}
|
||||
|
||||
*cache = Cache::IntegerComparison([left_value, right_value]);
|
||||
}
|
||||
}
|
48
dust-lang/src/vm/action/jump.rs
Normal file
48
dust-lang/src/vm/action/jump.rs
Normal file
@ -0,0 +1,48 @@
|
||||
use tracing::trace;
|
||||
|
||||
use crate::{vm::Thread, Instruction};
|
||||
|
||||
use super::Cache;
|
||||
|
||||
pub fn jump(ip: &mut usize, instruction: &Instruction, _: &mut Thread, _: &mut Cache) {
|
||||
let offset = instruction.b_field() as usize;
|
||||
let is_positive = instruction.c_field() != 0;
|
||||
|
||||
if is_positive {
|
||||
trace!("JUMP +{}", offset);
|
||||
} else {
|
||||
trace!("JUMP -{}", offset);
|
||||
}
|
||||
|
||||
if is_positive {
|
||||
*ip += offset;
|
||||
} else {
|
||||
*ip -= offset + 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn optimized_jump_forward(
|
||||
ip: &mut usize,
|
||||
instruction: &Instruction,
|
||||
_: &mut Thread,
|
||||
_: &mut Cache,
|
||||
) {
|
||||
let offset = instruction.b_field() as usize;
|
||||
|
||||
trace!("JUMP +{}", offset);
|
||||
|
||||
*ip += offset;
|
||||
}
|
||||
|
||||
pub fn optimized_jump_backward(
|
||||
ip: &mut usize,
|
||||
instruction: &Instruction,
|
||||
_: &mut Thread,
|
||||
_: &mut Cache,
|
||||
) {
|
||||
let offset = instruction.b_field() as usize;
|
||||
|
||||
trace!("JUMP -{}", offset);
|
||||
|
||||
*ip -= offset + 1;
|
||||
}
|
212
dust-lang/src/vm/action/less.rs
Normal file
212
dust-lang/src/vm/action/less.rs
Normal file
@ -0,0 +1,212 @@
|
||||
use tracing::trace;
|
||||
|
||||
use crate::{vm::Thread, Instruction};
|
||||
|
||||
use super::Cache;
|
||||
|
||||
pub fn less_booleans(
|
||||
ip: &mut usize,
|
||||
instruction: &Instruction,
|
||||
thread: &mut Thread,
|
||||
_: &mut Cache,
|
||||
) {
|
||||
let left_index = instruction.b_field() as usize;
|
||||
let right_index = instruction.c_field() as usize;
|
||||
let comparator = instruction.d_field();
|
||||
|
||||
let current_frame = thread.current_frame_mut();
|
||||
let left_value = current_frame.get_boolean_from_register(left_index);
|
||||
let right_value = current_frame.get_boolean_from_register(right_index);
|
||||
let is_less_than = left_value < right_value;
|
||||
|
||||
if is_less_than == comparator {
|
||||
*ip += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn less_bytes(ip: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
|
||||
let left = instruction.b_field() as usize;
|
||||
let right = instruction.c_field() as usize;
|
||||
let comparator = instruction.d_field();
|
||||
|
||||
let current_frame = thread.current_frame_mut();
|
||||
let left_value = current_frame.get_byte_from_register(left);
|
||||
let right_value = current_frame.get_byte_from_register(right);
|
||||
let is_less_than = left_value < right_value;
|
||||
|
||||
if is_less_than == comparator {
|
||||
*ip += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn less_characters(
|
||||
ip: &mut usize,
|
||||
instruction: &Instruction,
|
||||
thread: &mut Thread,
|
||||
_: &mut Cache,
|
||||
) {
|
||||
let left_index = instruction.b_field() as usize;
|
||||
let left_is_constant = instruction.b_is_constant();
|
||||
let right_index = instruction.c_field() as usize;
|
||||
let right_is_constant = instruction.c_is_constant();
|
||||
let comparator = instruction.d_field();
|
||||
|
||||
let current_frame = thread.current_frame_mut();
|
||||
let left_value = if left_is_constant {
|
||||
current_frame.get_character_constant(left_index)
|
||||
} else {
|
||||
current_frame.get_character_from_register(left_index)
|
||||
};
|
||||
let right_value = if right_is_constant {
|
||||
current_frame.get_character_constant(right_index)
|
||||
} else {
|
||||
current_frame.get_character_from_register(right_index)
|
||||
};
|
||||
let is_less_than = left_value < right_value;
|
||||
|
||||
if is_less_than == comparator {
|
||||
*ip += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn less_floats(ip: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
|
||||
let left = instruction.b_field() as usize;
|
||||
let left_is_constant = instruction.b_is_constant();
|
||||
let right = instruction.c_field() as usize;
|
||||
let right_is_constant = instruction.c_is_constant();
|
||||
let comparator = instruction.d_field();
|
||||
|
||||
let current_frame = thread.current_frame_mut();
|
||||
let left_value = if left_is_constant {
|
||||
current_frame.get_float_constant(left)
|
||||
} else {
|
||||
current_frame.get_float_from_register(left)
|
||||
};
|
||||
let right_value = if right_is_constant {
|
||||
current_frame.get_float_constant(right)
|
||||
} else {
|
||||
current_frame.get_float_from_register(right)
|
||||
};
|
||||
let is_less_than = left_value < right_value;
|
||||
|
||||
if is_less_than == comparator {
|
||||
*ip += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn less_integers(
|
||||
ip: &mut usize,
|
||||
instruction: &Instruction,
|
||||
thread: &mut Thread,
|
||||
_: &mut Cache,
|
||||
) {
|
||||
let left = instruction.b_field() as usize;
|
||||
let left_is_constant = instruction.b_is_constant();
|
||||
let right = instruction.c_field() as usize;
|
||||
let right_is_constant = instruction.c_is_constant();
|
||||
let comparator = instruction.d_field();
|
||||
|
||||
let current_frame = thread.current_frame_mut();
|
||||
let left_value = if left_is_constant {
|
||||
current_frame.get_integer_constant(left)
|
||||
} else {
|
||||
current_frame.get_integer_from_register(left)
|
||||
};
|
||||
let right_value = if right_is_constant {
|
||||
current_frame.get_integer_constant(right)
|
||||
} else {
|
||||
current_frame.get_integer_from_register(right)
|
||||
};
|
||||
let is_less_than = left_value < right_value;
|
||||
|
||||
if is_less_than == comparator {
|
||||
*ip += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn less_strings(ip: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
|
||||
let left = instruction.b_field() as usize;
|
||||
let left_is_constant = instruction.b_is_constant();
|
||||
let right = instruction.c_field() as usize;
|
||||
let right_is_constant = instruction.c_is_constant();
|
||||
let comparator = instruction.d_field();
|
||||
|
||||
let current_frame = thread.current_frame_mut();
|
||||
let left_value = if left_is_constant {
|
||||
current_frame.get_string_constant(left)
|
||||
} else {
|
||||
current_frame.get_string_from_register(left)
|
||||
};
|
||||
let right_value = if right_is_constant {
|
||||
current_frame.get_string_constant(right)
|
||||
} else {
|
||||
current_frame.get_string_from_register(right)
|
||||
};
|
||||
let is_less_than = left_value < right_value;
|
||||
|
||||
if is_less_than == comparator {
|
||||
*ip += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn optimized_less_integers(
|
||||
ip: &mut usize,
|
||||
instruction: &Instruction,
|
||||
thread: &mut Thread,
|
||||
cache: &mut Cache,
|
||||
) {
|
||||
if let Cache::IntegerComparison([left, right]) = cache {
|
||||
trace!("OPTIMIZED_LESS using integer cache");
|
||||
|
||||
let is_less_than = left < right;
|
||||
|
||||
if is_less_than {
|
||||
*ip += 1;
|
||||
}
|
||||
} else {
|
||||
let left_index = instruction.b_field() as usize;
|
||||
let left_is_constant = instruction.b_is_constant();
|
||||
let right_index = instruction.c_field() as usize;
|
||||
let right_is_constant = instruction.c_is_constant();
|
||||
let comparator = instruction.d_field();
|
||||
|
||||
let current_frame = thread.current_frame_mut();
|
||||
let left_value = if left_is_constant {
|
||||
let value = current_frame.get_integer_constant_mut(left_index).to_rc();
|
||||
|
||||
current_frame.constants.integers[left_index] = value.clone();
|
||||
|
||||
value
|
||||
} else {
|
||||
let value = current_frame
|
||||
.get_integer_from_register_mut(left_index)
|
||||
.to_ref_cell();
|
||||
|
||||
current_frame.registers.integers[left_index].set(value.clone());
|
||||
|
||||
value
|
||||
};
|
||||
let right_value = if right_is_constant {
|
||||
let value = current_frame.get_integer_constant_mut(right_index).to_rc();
|
||||
|
||||
current_frame.constants.integers[right_index] = value.clone();
|
||||
|
||||
value
|
||||
} else {
|
||||
let value = current_frame
|
||||
.get_integer_from_register_mut(right_index)
|
||||
.to_ref_cell();
|
||||
|
||||
current_frame.registers.integers[right_index].set(value.clone());
|
||||
|
||||
value
|
||||
};
|
||||
let is_less_than = left_value < right_value;
|
||||
|
||||
if is_less_than == comparator {
|
||||
*ip += 1;
|
||||
}
|
||||
|
||||
*cache = Cache::IntegerComparison([left_value, right_value]);
|
||||
}
|
||||
}
|
227
dust-lang/src/vm/action/less_equal.rs
Normal file
227
dust-lang/src/vm/action/less_equal.rs
Normal file
@ -0,0 +1,227 @@
|
||||
use tracing::trace;
|
||||
|
||||
use crate::{vm::Thread, Instruction};
|
||||
|
||||
use super::Cache;
|
||||
|
||||
pub fn less_equal_booleans(
|
||||
ip: &mut usize,
|
||||
instruction: &Instruction,
|
||||
thread: &mut Thread,
|
||||
_: &mut Cache,
|
||||
) {
|
||||
let left_index = instruction.b_field() as usize;
|
||||
let right_index = instruction.c_field() as usize;
|
||||
let comparator = instruction.d_field();
|
||||
|
||||
let current_frame = thread.current_frame_mut();
|
||||
let left_value = current_frame.get_boolean_from_register(left_index);
|
||||
let right_value = current_frame.get_boolean_from_register(right_index);
|
||||
let is_less_than_or_equal = left_value <= right_value;
|
||||
|
||||
if is_less_than_or_equal == comparator {
|
||||
*ip += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn less_equal_bytes(
|
||||
ip: &mut usize,
|
||||
instruction: &Instruction,
|
||||
thread: &mut Thread,
|
||||
_: &mut Cache,
|
||||
) {
|
||||
let left = instruction.b_field() as usize;
|
||||
let right = instruction.c_field() as usize;
|
||||
let comparator = instruction.d_field();
|
||||
|
||||
let current_frame = thread.current_frame_mut();
|
||||
let left_value = current_frame.get_byte_from_register(left);
|
||||
let right_value = current_frame.get_byte_from_register(right);
|
||||
let is_less_than_or_equal = left_value <= right_value;
|
||||
|
||||
if is_less_than_or_equal == comparator {
|
||||
*ip += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn less_equal_characters(
|
||||
ip: &mut usize,
|
||||
instruction: &Instruction,
|
||||
thread: &mut Thread,
|
||||
_: &mut Cache,
|
||||
) {
|
||||
let left_index = instruction.b_field() as usize;
|
||||
let left_is_constant = instruction.b_is_constant();
|
||||
let right_index = instruction.c_field() as usize;
|
||||
let right_is_constant = instruction.c_is_constant();
|
||||
let comparator = instruction.d_field();
|
||||
|
||||
let current_frame = thread.current_frame_mut();
|
||||
let left_value = if left_is_constant {
|
||||
current_frame.get_character_constant(left_index)
|
||||
} else {
|
||||
current_frame.get_character_from_register(left_index)
|
||||
};
|
||||
let right_value = if right_is_constant {
|
||||
current_frame.get_character_constant(right_index)
|
||||
} else {
|
||||
current_frame.get_character_from_register(right_index)
|
||||
};
|
||||
let is_less_than_or_equal = left_value <= right_value;
|
||||
|
||||
if is_less_than_or_equal == comparator {
|
||||
*ip += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn less_equal_floats(
|
||||
ip: &mut usize,
|
||||
instruction: &Instruction,
|
||||
thread: &mut Thread,
|
||||
_: &mut Cache,
|
||||
) {
|
||||
let left = instruction.b_field() as usize;
|
||||
let left_is_constant = instruction.b_is_constant();
|
||||
let right = instruction.c_field() as usize;
|
||||
let right_is_constant = instruction.c_is_constant();
|
||||
let comparator = instruction.d_field();
|
||||
|
||||
let current_frame = thread.current_frame_mut();
|
||||
let left_value = if left_is_constant {
|
||||
current_frame.get_float_constant(left)
|
||||
} else {
|
||||
current_frame.get_float_from_register(left)
|
||||
};
|
||||
let right_value = if right_is_constant {
|
||||
current_frame.get_float_constant(right)
|
||||
} else {
|
||||
current_frame.get_float_from_register(right)
|
||||
};
|
||||
let is_less_than_or_equal = left_value <= right_value;
|
||||
|
||||
if is_less_than_or_equal == comparator {
|
||||
*ip += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn less_equal_integers(
|
||||
ip: &mut usize,
|
||||
instruction: &Instruction,
|
||||
thread: &mut Thread,
|
||||
_: &mut Cache,
|
||||
) {
|
||||
let left = instruction.b_field() as usize;
|
||||
let left_is_constant = instruction.b_is_constant();
|
||||
let right = instruction.c_field() as usize;
|
||||
let right_is_constant = instruction.c_is_constant();
|
||||
let comparator = instruction.d_field();
|
||||
|
||||
let current_frame = thread.current_frame_mut();
|
||||
let left_value = if left_is_constant {
|
||||
current_frame.get_integer_constant(left)
|
||||
} else {
|
||||
current_frame.get_integer_from_register(left)
|
||||
};
|
||||
let right_value = if right_is_constant {
|
||||
current_frame.get_integer_constant(right)
|
||||
} else {
|
||||
current_frame.get_integer_from_register(right)
|
||||
};
|
||||
let is_less_than_or_equal = left_value <= right_value;
|
||||
|
||||
if is_less_than_or_equal == comparator {
|
||||
*ip += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn less_equal_strings(
|
||||
ip: &mut usize,
|
||||
instruction: &Instruction,
|
||||
thread: &mut Thread,
|
||||
_: &mut Cache,
|
||||
) {
|
||||
let left = instruction.b_field() as usize;
|
||||
let left_is_constant = instruction.b_is_constant();
|
||||
let right = instruction.c_field() as usize;
|
||||
let right_is_constant = instruction.c_is_constant();
|
||||
let comparator = instruction.d_field();
|
||||
|
||||
let current_frame = thread.current_frame_mut();
|
||||
let left_value = if left_is_constant {
|
||||
current_frame.get_string_constant(left)
|
||||
} else {
|
||||
current_frame.get_string_from_register(left)
|
||||
};
|
||||
let right_value = if right_is_constant {
|
||||
current_frame.get_string_constant(right)
|
||||
} else {
|
||||
current_frame.get_string_from_register(right)
|
||||
};
|
||||
let is_less_than_or_equal = left_value <= right_value;
|
||||
|
||||
if is_less_than_or_equal == comparator {
|
||||
*ip += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn optimized_less_equal_integers(
|
||||
ip: &mut usize,
|
||||
instruction: &Instruction,
|
||||
thread: &mut Thread,
|
||||
cache: &mut Cache,
|
||||
) {
|
||||
if let Cache::IntegerComparison([left, right]) = cache {
|
||||
trace!("LESS_INTEGERS_OPTIMIZED using cache");
|
||||
|
||||
let is_less_than_or_equal = left <= right;
|
||||
|
||||
if is_less_than_or_equal {
|
||||
*ip += 1;
|
||||
}
|
||||
} else {
|
||||
let left_index = instruction.b_field() as usize;
|
||||
let left_is_constant = instruction.b_is_constant();
|
||||
let right_index = instruction.c_field() as usize;
|
||||
let right_is_constant = instruction.c_is_constant();
|
||||
let comparator = instruction.d_field();
|
||||
|
||||
let current_frame = thread.current_frame_mut();
|
||||
let left_value = if left_is_constant {
|
||||
let value = current_frame.get_integer_constant_mut(left_index).to_rc();
|
||||
|
||||
current_frame.constants.integers[left_index] = value.clone();
|
||||
|
||||
value
|
||||
} else {
|
||||
let value = current_frame
|
||||
.get_integer_from_register_mut(left_index)
|
||||
.to_ref_cell();
|
||||
|
||||
current_frame.registers.integers[left_index].set(value.clone());
|
||||
|
||||
value
|
||||
};
|
||||
let right_value = if right_is_constant {
|
||||
let value = current_frame.get_integer_constant_mut(right_index).to_rc();
|
||||
|
||||
current_frame.constants.integers[right_index] = value.clone();
|
||||
|
||||
value
|
||||
} else {
|
||||
let value = current_frame
|
||||
.get_integer_from_register_mut(right_index)
|
||||
.to_ref_cell();
|
||||
|
||||
current_frame.registers.integers[right_index].set(value.clone());
|
||||
|
||||
value
|
||||
};
|
||||
let is_less_than_or_equal = left_value <= right_value;
|
||||
|
||||
if is_less_than_or_equal == comparator {
|
||||
*ip += 1;
|
||||
}
|
||||
|
||||
*cache = Cache::IntegerComparison([left_value, right_value]);
|
||||
}
|
||||
}
|
623
dust-lang/src/vm/action/mod.rs
Normal file
623
dust-lang/src/vm/action/mod.rs
Normal file
@ -0,0 +1,623 @@
|
||||
mod add;
|
||||
mod equal;
|
||||
mod jump;
|
||||
mod less;
|
||||
mod less_equal;
|
||||
|
||||
use add::{
|
||||
add_bytes, add_character_string, add_characters, add_floats, add_integers,
|
||||
add_string_character, add_strings, optimized_add_integer,
|
||||
};
|
||||
use equal::{
|
||||
equal_booleans, equal_bytes, equal_characters, equal_floats, equal_integers, equal_strings,
|
||||
optimized_equal_integers,
|
||||
};
|
||||
use jump::{jump, optimized_jump_backward, optimized_jump_forward};
|
||||
use less::{
|
||||
less_booleans, less_bytes, less_characters, less_floats, less_integers, less_strings,
|
||||
optimized_less_integers,
|
||||
};
|
||||
|
||||
use less_equal::{
|
||||
less_equal_booleans, less_equal_bytes, less_equal_characters, less_equal_floats,
|
||||
less_equal_integers, less_equal_strings, optimized_less_equal_integers,
|
||||
};
|
||||
use tracing::info;
|
||||
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use crate::{instruction::TypeCode, AbstractList, ConcreteValue, Instruction, Operation, Value};
|
||||
|
||||
use super::{call_frame::RuntimeValue, thread::Thread, Pointer};
|
||||
|
||||
pub type ActionLogic = fn(&mut usize, &Instruction, &mut Thread, &mut Cache);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ActionSequence {
|
||||
actions: Vec<Action>,
|
||||
}
|
||||
|
||||
impl ActionSequence {
|
||||
pub fn new<T: IntoIterator<Item = Instruction> + DoubleEndedIterator<Item = Instruction>>(
|
||||
instructions: T,
|
||||
) -> Self {
|
||||
let mut actions = Vec::new();
|
||||
let mut instructions_reversed = instructions.rev();
|
||||
|
||||
while let Some(instruction) = instructions_reversed.next() {
|
||||
if instruction.operation() == Operation::JUMP {
|
||||
let backward_offset = instruction.b_field() as usize;
|
||||
let is_positive = instruction.c_field() != 0;
|
||||
|
||||
if !is_positive {
|
||||
let mut loop_actions = Vec::with_capacity(backward_offset);
|
||||
let jump_action = Action::optimized(instruction);
|
||||
|
||||
loop_actions.push(jump_action);
|
||||
|
||||
for _ in 0..backward_offset {
|
||||
let instruction = instructions_reversed.next().unwrap();
|
||||
let action = Action::optimized(instruction);
|
||||
|
||||
loop_actions.push(action);
|
||||
}
|
||||
|
||||
loop_actions.reverse();
|
||||
|
||||
let cache = Cache::LoopActions(ActionSequence {
|
||||
actions: loop_actions,
|
||||
});
|
||||
|
||||
let action = Action {
|
||||
instruction,
|
||||
logic: r#loop,
|
||||
cache,
|
||||
};
|
||||
|
||||
actions.push(action);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let action = Action::unoptimized(instruction);
|
||||
|
||||
actions.push(action);
|
||||
}
|
||||
|
||||
actions.reverse();
|
||||
|
||||
ActionSequence { actions }
|
||||
}
|
||||
|
||||
pub fn run(&mut self, thread: &mut Thread) {
|
||||
let mut local_ip = 0;
|
||||
|
||||
while local_ip < self.actions.len() {
|
||||
let action = if cfg!(debug_assertions) {
|
||||
self.actions.get_mut(local_ip).unwrap()
|
||||
} else {
|
||||
unsafe { self.actions.get_unchecked_mut(local_ip) }
|
||||
};
|
||||
local_ip += 1;
|
||||
|
||||
info!("Run {action}");
|
||||
|
||||
(action.logic)(
|
||||
&mut local_ip,
|
||||
&action.instruction,
|
||||
thread,
|
||||
&mut action.cache,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ActionSequence {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "[")?;
|
||||
|
||||
for (index, action) in self.actions.iter().enumerate() {
|
||||
if index > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
|
||||
write!(f, "{action}")?;
|
||||
}
|
||||
|
||||
write!(f, "]")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Action {
|
||||
instruction: Instruction,
|
||||
logic: ActionLogic,
|
||||
cache: Cache,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Cache {
|
||||
Empty,
|
||||
IntegerMath([RuntimeValue<i64>; 3]),
|
||||
IntegerComparison([RuntimeValue<i64>; 2]),
|
||||
LoopActions(ActionSequence),
|
||||
}
|
||||
|
||||
impl Action {
|
||||
pub fn unoptimized(instruction: Instruction) -> Self {
|
||||
let logic = match instruction.operation() {
|
||||
Operation::POINT => point,
|
||||
Operation::CLOSE => close,
|
||||
Operation::LOAD_ENCODED => load_encoded,
|
||||
Operation::LOAD_CONSTANT => load_constant,
|
||||
Operation::LOAD_LIST => load_list,
|
||||
Operation::LOAD_FUNCTION => load_function,
|
||||
Operation::LOAD_SELF => load_self,
|
||||
Operation::ADD => match (instruction.b_type(), instruction.c_type()) {
|
||||
(TypeCode::INTEGER, TypeCode::INTEGER) => add_integers,
|
||||
(TypeCode::FLOAT, TypeCode::FLOAT) => add_floats,
|
||||
(TypeCode::BYTE, TypeCode::BYTE) => add_bytes,
|
||||
(TypeCode::STRING, TypeCode::STRING) => add_strings,
|
||||
(TypeCode::CHARACTER, TypeCode::CHARACTER) => add_characters,
|
||||
(TypeCode::STRING, TypeCode::CHARACTER) => add_string_character,
|
||||
(TypeCode::CHARACTER, TypeCode::STRING) => add_character_string,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Operation::SUBTRACT => subtract,
|
||||
Operation::MULTIPLY => multiply,
|
||||
Operation::DIVIDE => divide,
|
||||
Operation::MODULO => modulo,
|
||||
Operation::NEGATE => negate,
|
||||
Operation::NOT => not,
|
||||
Operation::EQUAL => match (instruction.b_type(), instruction.c_type()) {
|
||||
(TypeCode::BOOLEAN, TypeCode::BOOLEAN) => equal_booleans,
|
||||
(TypeCode::BYTE, TypeCode::BYTE) => equal_bytes,
|
||||
(TypeCode::CHARACTER, TypeCode::CHARACTER) => equal_characters,
|
||||
(TypeCode::FLOAT, TypeCode::FLOAT) => equal_floats,
|
||||
(TypeCode::INTEGER, TypeCode::INTEGER) => equal_integers,
|
||||
(TypeCode::STRING, TypeCode::STRING) => equal_strings,
|
||||
_ => todo!(),
|
||||
},
|
||||
Operation::LESS => match (instruction.b_type(), instruction.c_type()) {
|
||||
(TypeCode::BOOLEAN, TypeCode::BOOLEAN) => less_booleans,
|
||||
(TypeCode::BYTE, TypeCode::BYTE) => less_bytes,
|
||||
(TypeCode::CHARACTER, TypeCode::CHARACTER) => less_characters,
|
||||
(TypeCode::FLOAT, TypeCode::FLOAT) => less_floats,
|
||||
(TypeCode::INTEGER, TypeCode::INTEGER) => less_integers,
|
||||
(TypeCode::STRING, TypeCode::STRING) => less_strings,
|
||||
_ => todo!(),
|
||||
},
|
||||
Operation::LESS_EQUAL => match (instruction.b_type(), instruction.c_type()) {
|
||||
(TypeCode::BOOLEAN, TypeCode::BOOLEAN) => less_equal_booleans,
|
||||
(TypeCode::BYTE, TypeCode::BYTE) => less_equal_bytes,
|
||||
(TypeCode::CHARACTER, TypeCode::CHARACTER) => less_equal_characters,
|
||||
(TypeCode::FLOAT, TypeCode::FLOAT) => less_equal_floats,
|
||||
(TypeCode::INTEGER, TypeCode::INTEGER) => less_equal_integers,
|
||||
(TypeCode::STRING, TypeCode::STRING) => less_equal_strings,
|
||||
_ => todo!(),
|
||||
},
|
||||
Operation::TEST => test,
|
||||
Operation::TEST_SET => test_set,
|
||||
Operation::CALL => call,
|
||||
Operation::CALL_NATIVE => call_native,
|
||||
Operation::JUMP => jump,
|
||||
Operation::RETURN => r#return,
|
||||
_ => todo!(),
|
||||
};
|
||||
|
||||
Action {
|
||||
instruction,
|
||||
logic,
|
||||
cache: Cache::Empty,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn optimized(instruction: Instruction) -> Self {
|
||||
let logic = match instruction.operation() {
|
||||
Operation::JUMP => match instruction.c_field() {
|
||||
0 => optimized_jump_backward,
|
||||
_ => optimized_jump_forward,
|
||||
},
|
||||
Operation::ADD => match (instruction.b_type(), instruction.c_type()) {
|
||||
(TypeCode::INTEGER, TypeCode::INTEGER) => optimized_add_integer,
|
||||
_ => todo!(),
|
||||
},
|
||||
Operation::EQUAL => match (instruction.b_type(), instruction.c_type()) {
|
||||
(TypeCode::INTEGER, TypeCode::INTEGER) => optimized_equal_integers,
|
||||
_ => todo!(),
|
||||
},
|
||||
Operation::LESS => match (instruction.b_type(), instruction.c_type()) {
|
||||
(TypeCode::INTEGER, TypeCode::INTEGER) => optimized_less_integers,
|
||||
_ => todo!(),
|
||||
},
|
||||
Operation::LESS_EQUAL => match (instruction.b_type(), instruction.c_type()) {
|
||||
(TypeCode::INTEGER, TypeCode::INTEGER) => optimized_less_equal_integers,
|
||||
_ => todo!(),
|
||||
},
|
||||
_ => todo!(),
|
||||
};
|
||||
|
||||
Action {
|
||||
instruction,
|
||||
logic,
|
||||
cache: Cache::Empty,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Action {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
if let Cache::LoopActions(actions) = &self.cache {
|
||||
write!(f, "LOOP: {actions}")?;
|
||||
} else {
|
||||
write!(f, "{}", self.instruction.operation())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn r#loop(_: &mut usize, _: &Instruction, thread: &mut Thread, cache: &mut Cache) {
|
||||
if let Cache::LoopActions(actions) = cache {
|
||||
actions.run(thread);
|
||||
}
|
||||
}
|
||||
|
||||
fn point(_: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn close(_: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn load_encoded(ip: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
|
||||
let destination = instruction.a_field();
|
||||
let value_type = instruction.b_type();
|
||||
let jump_next = instruction.c_field() != 0;
|
||||
|
||||
match value_type {
|
||||
TypeCode::BOOLEAN => {
|
||||
let value = instruction.b_field() != 0;
|
||||
|
||||
thread
|
||||
.current_frame_mut()
|
||||
.registers
|
||||
.booleans
|
||||
.get_mut(destination as usize)
|
||||
.as_value_mut()
|
||||
.set_inner(value);
|
||||
}
|
||||
TypeCode::BYTE => {
|
||||
let value = instruction.b_field() as u8;
|
||||
|
||||
thread
|
||||
.current_frame_mut()
|
||||
.registers
|
||||
.bytes
|
||||
.get_mut(destination as usize)
|
||||
.as_value_mut()
|
||||
.set_inner(value);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
if jump_next {
|
||||
*ip += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn load_constant(ip: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
|
||||
let destination = instruction.a_field() as usize;
|
||||
let constant_index = instruction.b_field() as usize;
|
||||
let constant_type = instruction.b_type();
|
||||
let jump_next = instruction.c_field() != 0;
|
||||
let current_frame = thread.current_frame_mut();
|
||||
|
||||
match constant_type {
|
||||
TypeCode::CHARACTER => {
|
||||
let constant = current_frame.get_character_constant(constant_index).clone();
|
||||
|
||||
current_frame
|
||||
.registers
|
||||
.characters
|
||||
.get_mut(destination)
|
||||
.set(constant);
|
||||
}
|
||||
TypeCode::FLOAT => {
|
||||
let constant = current_frame.get_float_constant(constant_index).clone();
|
||||
|
||||
current_frame
|
||||
.registers
|
||||
.floats
|
||||
.get_mut(destination)
|
||||
.set(constant);
|
||||
}
|
||||
TypeCode::INTEGER => {
|
||||
let constant = current_frame.get_integer_constant(constant_index).clone();
|
||||
|
||||
current_frame
|
||||
.registers
|
||||
.integers
|
||||
.get_mut(destination)
|
||||
.set(constant);
|
||||
}
|
||||
TypeCode::STRING => {
|
||||
let constant = current_frame.get_string_constant(constant_index).clone();
|
||||
|
||||
current_frame
|
||||
.registers
|
||||
.strings
|
||||
.get_mut(destination)
|
||||
.set(constant);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
if jump_next {
|
||||
*ip += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn load_list(ip: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
|
||||
let destination = instruction.a_field() as usize;
|
||||
let start_register = instruction.b_field() as usize;
|
||||
let item_type = instruction.b_type();
|
||||
let end_register = instruction.c_field() as usize;
|
||||
let jump_next = instruction.d_field();
|
||||
let current_frame = thread.current_frame_mut();
|
||||
|
||||
let mut item_pointers = Vec::with_capacity(end_register - start_register + 1);
|
||||
|
||||
match item_type {
|
||||
TypeCode::BOOLEAN => {
|
||||
for register_index in start_register..=end_register {
|
||||
let register_is_closed = current_frame.registers.booleans.is_closed(register_index);
|
||||
|
||||
if register_is_closed {
|
||||
continue;
|
||||
}
|
||||
|
||||
item_pointers.push(Pointer::Register(register_index));
|
||||
}
|
||||
}
|
||||
TypeCode::BYTE => {
|
||||
for register_index in start_register..=end_register {
|
||||
let register_is_closed = current_frame.registers.bytes.is_closed(register_index);
|
||||
|
||||
if register_is_closed {
|
||||
continue;
|
||||
}
|
||||
|
||||
item_pointers.push(Pointer::Register(register_index));
|
||||
}
|
||||
}
|
||||
TypeCode::CHARACTER => {
|
||||
for register_index in start_register..=end_register {
|
||||
let register_is_closed =
|
||||
current_frame.registers.characters.is_closed(register_index);
|
||||
|
||||
if register_is_closed {
|
||||
continue;
|
||||
}
|
||||
|
||||
item_pointers.push(Pointer::Register(register_index));
|
||||
}
|
||||
}
|
||||
TypeCode::FLOAT => {
|
||||
for register_index in start_register..=end_register {
|
||||
let register_is_closed = current_frame.registers.floats.is_closed(register_index);
|
||||
|
||||
if register_is_closed {
|
||||
continue;
|
||||
}
|
||||
|
||||
item_pointers.push(Pointer::Register(register_index));
|
||||
}
|
||||
}
|
||||
TypeCode::INTEGER => {
|
||||
for register_index in start_register..=end_register {
|
||||
let register_is_closed = current_frame.registers.integers.is_closed(register_index);
|
||||
|
||||
if register_is_closed {
|
||||
continue;
|
||||
}
|
||||
|
||||
item_pointers.push(Pointer::Register(register_index));
|
||||
}
|
||||
}
|
||||
TypeCode::STRING => {
|
||||
for register_index in start_register..=end_register {
|
||||
let register_is_closed = current_frame.registers.strings.is_closed(register_index);
|
||||
|
||||
if register_is_closed {
|
||||
continue;
|
||||
}
|
||||
|
||||
item_pointers.push(Pointer::Register(register_index));
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
let list = RuntimeValue::Raw(AbstractList {
|
||||
item_type,
|
||||
item_pointers,
|
||||
});
|
||||
|
||||
current_frame.registers.lists.get_mut(destination).set(list);
|
||||
|
||||
if jump_next {
|
||||
*ip += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn load_function(_: &mut usize, _: &Instruction, _: &mut Thread, _: &mut Cache) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn load_self(_: &mut usize, _: &Instruction, _: &mut Thread, _: &mut Cache) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn subtract(_: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn multiply(_: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn divide(_: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn modulo(_: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn test(ip: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn test_set(_: &mut usize, _: &Instruction, _: &mut Thread, _: &mut Cache) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn negate(_: &mut usize, _: &Instruction, _: &mut Thread, _: &mut Cache) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn not(_: &mut usize, _: &Instruction, _: &mut Thread, _: &mut Cache) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn call(_: &mut usize, _: &Instruction, _: &mut Thread, _: &mut Cache) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn call_native(_: &mut usize, _: &Instruction, _: &mut Thread, _: &mut Cache) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn r#return(_: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
|
||||
let should_return_value = instruction.b_field() != 0;
|
||||
let return_register = instruction.c_field() as usize;
|
||||
let return_type = instruction.b_type();
|
||||
let current_frame = thread.current_frame();
|
||||
|
||||
// if should_return_value {
|
||||
// match return_type {
|
||||
// TypeCode::BOOLEAN => {
|
||||
// let return_value = current_frame
|
||||
// .get_boolean_from_register(return_register)
|
||||
// .clone_inner();
|
||||
// thread.return_value = Some(Value::boolean(return_value));
|
||||
// }
|
||||
// TypeCode::BYTE => {
|
||||
// let return_value = current_frame
|
||||
// .get_byte_from_register(return_register)
|
||||
// .clone_inner();
|
||||
// thread.return_value = Some(Value::byte(return_value));
|
||||
// }
|
||||
// TypeCode::CHARACTER => {
|
||||
// let return_value = current_frame
|
||||
// .get_character_from_register(return_register)
|
||||
// .clone_inner();
|
||||
// thread.return_value = Some(Value::character(return_value));
|
||||
// }
|
||||
// TypeCode::FLOAT => {
|
||||
// let return_value = current_frame
|
||||
// .get_float_from_register(return_register)
|
||||
// .clone_inner();
|
||||
// thread.return_value = Some(Value::float(return_value));
|
||||
// }
|
||||
// TypeCode::INTEGER => {
|
||||
// let return_value = current_frame
|
||||
// .get_integer_from_register(return_register)
|
||||
// .clone_inner();
|
||||
// thread.return_value = Some(Value::integer(return_value));
|
||||
// }
|
||||
// TypeCode::STRING => {
|
||||
// let return_value = current_frame
|
||||
// .get_string_from_register(return_register)
|
||||
// .clone_inner();
|
||||
// thread.return_value = Some(Value::string(return_value));
|
||||
// }
|
||||
// TypeCode::LIST => {
|
||||
// let abstract_list = current_frame
|
||||
// .get_list_from_register(return_register)
|
||||
// .clone_inner();
|
||||
// let mut concrete_list = Vec::with_capacity(abstract_list.item_pointers.len());
|
||||
|
||||
// match abstract_list.item_type {
|
||||
// TypeCode::BOOLEAN => {
|
||||
// for pointer in abstract_list.item_pointers {
|
||||
// let boolean = current_frame
|
||||
// .get_boolean_from_pointer(&pointer)
|
||||
// .clone_inner();
|
||||
// let value = ConcreteValue::Boolean(boolean);
|
||||
|
||||
// concrete_list.push(value);
|
||||
// }
|
||||
// }
|
||||
// TypeCode::BYTE => {
|
||||
// for pointer in abstract_list.item_pointers {
|
||||
// let byte = current_frame.get_byte_from_pointer(&pointer).clone_inner();
|
||||
// let value = ConcreteValue::Byte(byte);
|
||||
|
||||
// concrete_list.push(value);
|
||||
// }
|
||||
// }
|
||||
// TypeCode::CHARACTER => {
|
||||
// for pointer in abstract_list.item_pointers {
|
||||
// let character = current_frame
|
||||
// .get_character_from_pointer(&pointer)
|
||||
// .clone_inner();
|
||||
// let value = ConcreteValue::Character(character);
|
||||
|
||||
// concrete_list.push(value);
|
||||
// }
|
||||
// }
|
||||
// TypeCode::FLOAT => {
|
||||
// for pointer in abstract_list.item_pointers {
|
||||
// let float =
|
||||
// current_frame.get_float_from_pointer(&pointer).clone_inner();
|
||||
// let value = ConcreteValue::Float(float);
|
||||
|
||||
// concrete_list.push(value);
|
||||
// }
|
||||
// }
|
||||
// TypeCode::INTEGER => {
|
||||
// for pointer in abstract_list.item_pointers {
|
||||
// let integer = current_frame
|
||||
// .get_integer_from_pointer(&pointer)
|
||||
// .clone_inner();
|
||||
// let value = ConcreteValue::Integer(integer);
|
||||
|
||||
// concrete_list.push(value);
|
||||
// }
|
||||
// }
|
||||
// TypeCode::STRING => {
|
||||
// for pointer in abstract_list.item_pointers {
|
||||
// let string = current_frame
|
||||
// .get_string_from_pointer(&pointer)
|
||||
// .clone_inner();
|
||||
// let value = ConcreteValue::String(string);
|
||||
|
||||
// concrete_list.push(value);
|
||||
// }
|
||||
// }
|
||||
// _ => todo!(),
|
||||
// }
|
||||
|
||||
// thread.return_value = Some(Value::Concrete(ConcreteValue::list(
|
||||
// concrete_list,
|
||||
// abstract_list.item_type,
|
||||
// )));
|
||||
// }
|
||||
// _ => unreachable!(),
|
||||
// }
|
||||
// }
|
||||
}
|
317
dust-lang/src/vm/call_frame.rs
Normal file
317
dust-lang/src/vm/call_frame.rs
Normal file
@ -0,0 +1,317 @@
|
||||
use std::{
|
||||
fmt::{self, Debug, Display, Formatter},
|
||||
ops::{Index, IndexMut, RangeInclusive},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::{AbstractList, Chunk, DustString, Function};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CallFrame {
|
||||
pub chunk: Arc<Chunk>,
|
||||
pub ip: usize,
|
||||
pub return_register: u16,
|
||||
pub registers: RegisterTable,
|
||||
}
|
||||
|
||||
impl CallFrame {
|
||||
pub fn new(chunk: Arc<Chunk>, return_register: u16) -> Self {
|
||||
let registers = RegisterTable {
|
||||
booleans: RegisterList::new(chunk.boolean_register_count as usize),
|
||||
bytes: RegisterList::new(chunk.byte_register_count as usize),
|
||||
characters: RegisterList::new(chunk.character_register_count as usize),
|
||||
floats: RegisterList::new(chunk.float_register_count as usize),
|
||||
integers: RegisterList::new(chunk.integer_register_count as usize),
|
||||
strings: RegisterList::new(chunk.string_register_count as usize),
|
||||
lists: RegisterList::new(chunk.list_register_count as usize),
|
||||
functions: RegisterList::new(chunk.function_register_count as usize),
|
||||
};
|
||||
|
||||
Self {
|
||||
chunk,
|
||||
ip: 0,
|
||||
return_register,
|
||||
registers,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_boolean_from_pointer(&self, pointer: Pointer) -> bool {
|
||||
match pointer {
|
||||
Pointer::Register(register_index) => {
|
||||
*self.registers.booleans.get(register_index).as_value()
|
||||
}
|
||||
Pointer::Constant(_) => panic!("Attempted to get boolean from constant pointer"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_byte_from_pointer(&self, pointer: Pointer) -> u8 {
|
||||
match pointer {
|
||||
Pointer::Register(register_index) => {
|
||||
*self.registers.bytes.get(register_index).as_value()
|
||||
}
|
||||
Pointer::Constant(_) => panic!("Attempted to get byte from constant pointer"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_character_from_pointer(&self, pointer: Pointer) -> char {
|
||||
match pointer {
|
||||
Pointer::Register(register_index) => {
|
||||
*self.registers.characters.get(register_index).as_value()
|
||||
}
|
||||
Pointer::Constant(constant_index) => self.get_character_constant(constant_index),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_character_constant(&self, constant_index: usize) -> char {
|
||||
if cfg!(debug_assertions) {
|
||||
*self.chunk.character_constants.get(constant_index).unwrap()
|
||||
} else {
|
||||
unsafe { *self.chunk.character_constants.get_unchecked(constant_index) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_float_from_pointer(&self, pointer: Pointer) -> f64 {
|
||||
match pointer {
|
||||
Pointer::Register(register_index) => {
|
||||
*self.registers.floats.get(register_index).as_value()
|
||||
}
|
||||
Pointer::Constant(constant_index) => self.get_float_constant(constant_index),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_float_constant(&self, constant_index: usize) -> f64 {
|
||||
if cfg!(debug_assertions) {
|
||||
*self.chunk.float_constants.get(constant_index).unwrap()
|
||||
} else {
|
||||
unsafe { *self.chunk.float_constants.get_unchecked(constant_index) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_integer_from_pointer(&self, pointer: Pointer) -> i64 {
|
||||
match pointer {
|
||||
Pointer::Register(register_index) => {
|
||||
*self.registers.integers.get(register_index).as_value()
|
||||
}
|
||||
Pointer::Constant(constant_index) => self.get_integer_constant(constant_index),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_integer_constant(&self, constant_index: usize) -> i64 {
|
||||
if cfg!(debug_assertions) {
|
||||
*self.chunk.integer_constants.get(constant_index).unwrap()
|
||||
} else {
|
||||
unsafe { *self.chunk.integer_constants.get_unchecked(constant_index) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_string_from_pointer(&self, pointer: Pointer) -> &DustString {
|
||||
match pointer {
|
||||
Pointer::Register(register_index) => {
|
||||
self.registers.strings.get(register_index).as_value()
|
||||
}
|
||||
Pointer::Constant(constant_index) => self.get_string_constant(constant_index),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_string_constant(&self, constant_index: usize) -> &DustString {
|
||||
if cfg!(debug_assertions) {
|
||||
self.chunk.string_constants.get(constant_index).unwrap()
|
||||
} else {
|
||||
unsafe { self.chunk.string_constants.get_unchecked(constant_index) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_list_from_pointer(&self, pointer: &Pointer) -> &AbstractList {
|
||||
match pointer {
|
||||
Pointer::Register(register_index) => {
|
||||
self.registers.lists.get(*register_index).as_value()
|
||||
}
|
||||
Pointer::Constant(_) => panic!("Attempted to get list from constant pointer"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_function_from_pointer(&self, pointer: &Pointer) -> &Function {
|
||||
match pointer {
|
||||
Pointer::Register(register_index) => {
|
||||
self.registers.functions.get(*register_index).as_value()
|
||||
}
|
||||
Pointer::Constant(_) => panic!("Attempted to get function from constant pointer"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for CallFrame {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"FunctionCall: {} | IP: {}",
|
||||
self.chunk
|
||||
.name
|
||||
.as_ref()
|
||||
.unwrap_or(&DustString::from("anonymous")),
|
||||
self.ip,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RegisterTable {
|
||||
pub booleans: RegisterList<bool>,
|
||||
pub bytes: RegisterList<u8>,
|
||||
pub characters: RegisterList<char>,
|
||||
pub floats: RegisterList<f64>,
|
||||
pub integers: RegisterList<i64>,
|
||||
pub strings: RegisterList<DustString>,
|
||||
pub lists: RegisterList<AbstractList>,
|
||||
pub functions: RegisterList<Function>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RegisterList<T, const STACK_LEN: usize = 64> {
|
||||
pub registers: SmallVec<[Register<T>; STACK_LEN]>,
|
||||
}
|
||||
|
||||
impl<T, const STACK_LEN: usize> RegisterList<T, STACK_LEN>
|
||||
where
|
||||
T: Clone + Default,
|
||||
{
|
||||
pub fn new(length: usize) -> Self {
|
||||
let mut registers = SmallVec::with_capacity(length);
|
||||
|
||||
for _ in 0..length {
|
||||
registers.push(Register::default());
|
||||
}
|
||||
|
||||
Self { registers }
|
||||
}
|
||||
|
||||
pub fn get(&self, index: usize) -> &Register<T> {
|
||||
if cfg!(debug_assertions) {
|
||||
self.registers.get(index).unwrap()
|
||||
} else {
|
||||
unsafe { self.registers.get_unchecked(index) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_many_mut(&mut self, indices: RangeInclusive<usize>) -> &mut [Register<T>] {
|
||||
let registers = if cfg!(debug_assertions) {
|
||||
self.registers.get_disjoint_mut([indices]).unwrap()
|
||||
} else {
|
||||
unsafe { self.registers.get_disjoint_unchecked_mut([indices]) }
|
||||
};
|
||||
|
||||
registers[0]
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, index: usize) -> &mut Register<T> {
|
||||
if cfg!(debug_assertions) {
|
||||
let length = self.registers.len();
|
||||
|
||||
self.registers
|
||||
.get_mut(index)
|
||||
.unwrap_or_else(|| panic!("Index out of bounds: {index}. Length is {length}"))
|
||||
} else {
|
||||
unsafe { self.registers.get_unchecked_mut(index) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_to_new_register(&mut self, index: usize, new_value: T) {
|
||||
assert!(index < self.registers.len(), "Register index out of bounds");
|
||||
|
||||
self.registers[index] = Register::value(new_value)
|
||||
}
|
||||
|
||||
pub fn close(&mut self, index: usize) {
|
||||
if cfg!(debug_assertions) {
|
||||
self.registers.get_mut(index).unwrap().close()
|
||||
} else {
|
||||
unsafe { self.registers.get_unchecked_mut(index).close() }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_closed(&self, index: usize) -> bool {
|
||||
if cfg!(debug_assertions) {
|
||||
self.registers.get(index).unwrap().is_closed()
|
||||
} else {
|
||||
unsafe { self.registers.get_unchecked(index).is_closed() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Index<usize> for RegisterList<T> {
|
||||
type Output = Register<T>;
|
||||
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
&self.registers[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IndexMut<usize> for RegisterList<T> {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
&mut self.registers[index]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Register<T> {
|
||||
value: T,
|
||||
is_closed: bool,
|
||||
}
|
||||
|
||||
impl<T> Register<T> {
|
||||
pub fn value(value: T) -> Self {
|
||||
Self {
|
||||
value,
|
||||
is_closed: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_closed(&self) -> bool {
|
||||
self.is_closed
|
||||
}
|
||||
|
||||
pub fn close(&mut self) {
|
||||
self.is_closed = true;
|
||||
}
|
||||
|
||||
pub fn set(&mut self, new_value: T) {
|
||||
self.value = new_value;
|
||||
}
|
||||
|
||||
pub fn as_value(&self) -> &T {
|
||||
&self.value
|
||||
}
|
||||
|
||||
pub fn as_value_mut(&mut self) -> &mut T {
|
||||
&mut self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy> Register<T> {
|
||||
pub fn copy_value(&self) -> T {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> Register<T> {
|
||||
pub fn clone_value(&self) -> T {
|
||||
self.value.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default> Default for Register<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
value: Default::default(),
|
||||
is_closed: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub enum Pointer {
|
||||
Register(usize),
|
||||
Constant(usize),
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
use std::{
|
||||
fmt::{self, Debug, Display, Formatter},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use crate::{Chunk, DustString};
|
||||
|
||||
use super::Register;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FunctionCall {
|
||||
pub chunk: Arc<Chunk>,
|
||||
pub ip: usize,
|
||||
pub return_register: u16,
|
||||
pub registers: Vec<Register>,
|
||||
}
|
||||
|
||||
impl FunctionCall {
|
||||
pub fn new(chunk: Arc<Chunk>, return_register: u16) -> Self {
|
||||
let register_count = chunk.register_count;
|
||||
|
||||
Self {
|
||||
chunk,
|
||||
ip: 0,
|
||||
return_register,
|
||||
registers: vec![Register::Empty; register_count],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for FunctionCall {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"FunctionCall: {} | IP: {} | Registers: {}",
|
||||
self.chunk
|
||||
.name
|
||||
.as_ref()
|
||||
.unwrap_or(&DustString::from("anonymous")),
|
||||
self.ip,
|
||||
self.registers.len()
|
||||
)
|
||||
}
|
||||
}
|
@ -1,25 +1,17 @@
|
||||
//! Virtual machine and errors
|
||||
mod function_call;
|
||||
mod run_action;
|
||||
mod stack;
|
||||
// mod action;
|
||||
mod call_frame;
|
||||
mod thread;
|
||||
|
||||
use std::{
|
||||
fmt::{self, Debug, Display, Formatter},
|
||||
sync::Arc,
|
||||
thread::Builder,
|
||||
};
|
||||
use std::{sync::Arc, thread::Builder};
|
||||
|
||||
pub use function_call::FunctionCall;
|
||||
pub use run_action::RunAction;
|
||||
pub(crate) use run_action::get_next_action;
|
||||
pub use stack::Stack;
|
||||
pub use thread::{Thread, ThreadData};
|
||||
pub use call_frame::{CallFrame, Pointer, Register, RegisterTable};
|
||||
pub use thread::Thread;
|
||||
|
||||
use crossbeam_channel::bounded;
|
||||
use tracing::{Level, span};
|
||||
use tracing::{span, Level};
|
||||
|
||||
use crate::{Chunk, DustError, Value, compile};
|
||||
use crate::{compile, Chunk, DustError, Value};
|
||||
|
||||
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
||||
let chunk = compile(source)?;
|
||||
@ -40,20 +32,22 @@ impl Vm {
|
||||
pub fn run(self) -> Option<Value> {
|
||||
let span = span!(Level::INFO, "Run");
|
||||
let _enter = span.enter();
|
||||
|
||||
let thread_name = self
|
||||
.main_chunk
|
||||
.name
|
||||
.as_ref()
|
||||
.map(|name| name.to_string())
|
||||
.unwrap_or_else(|| "anonymous".to_string());
|
||||
let mut main_thread = Thread::new(Arc::new(self.main_chunk));
|
||||
let (tx, rx) = bounded(1);
|
||||
|
||||
Builder::new()
|
||||
.name(thread_name)
|
||||
.spawn(move || {
|
||||
let value_option = main_thread.run();
|
||||
let _ = tx.send(value_option);
|
||||
let main_chunk = Arc::new(self.main_chunk);
|
||||
let main_thread = Thread::new(main_chunk);
|
||||
let return_value = main_thread.run();
|
||||
let _ = tx.send(return_value);
|
||||
})
|
||||
.unwrap()
|
||||
.join()
|
||||
@ -62,39 +56,3 @@ impl Vm {
|
||||
rx.recv().unwrap_or(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Register {
|
||||
Empty,
|
||||
Value(Value),
|
||||
Pointer(Pointer),
|
||||
}
|
||||
|
||||
impl Display for Register {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::Empty => write!(f, "empty"),
|
||||
Self::Value(value) => write!(f, "{}", value),
|
||||
Self::Pointer(pointer) => write!(f, "{}", pointer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub enum Pointer {
|
||||
Register(u16),
|
||||
Constant(u16),
|
||||
Stack(usize, u16),
|
||||
}
|
||||
|
||||
impl Display for Pointer {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::Register(index) => write!(f, "PR{}", index),
|
||||
Self::Constant(index) => write!(f, "PC{}", index),
|
||||
Self::Stack(call_index, register_index) => {
|
||||
write!(f, "PS{}R{}", call_index, register_index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,893 +0,0 @@
|
||||
use tracing::trace;
|
||||
|
||||
use crate::{
|
||||
AbstractList, ConcreteValue, Instruction, Operand, Type, Value,
|
||||
instruction::{
|
||||
Add, Call, CallNative, Close, Divide, Equal, GetLocal, Jump, Less, LessEqual, LoadBoolean,
|
||||
LoadConstant, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not, Point,
|
||||
Return, SetLocal, Subtract, Test, TestSet, TypeCode,
|
||||
},
|
||||
vm::FunctionCall,
|
||||
};
|
||||
|
||||
use super::{Pointer, Register, thread::ThreadData};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct RunAction {
|
||||
pub logic: RunnerLogic,
|
||||
pub instruction: Instruction,
|
||||
}
|
||||
|
||||
impl From<Instruction> for RunAction {
|
||||
fn from(instruction: Instruction) -> Self {
|
||||
let operation = instruction.operation();
|
||||
let logic = RUNNER_LOGIC_TABLE[operation.0 as usize];
|
||||
|
||||
RunAction { logic, instruction }
|
||||
}
|
||||
}
|
||||
|
||||
pub type RunnerLogic = fn(Instruction, &mut ThreadData) -> bool;
|
||||
|
||||
pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [
|
||||
point,
|
||||
close,
|
||||
load_boolean,
|
||||
load_constant,
|
||||
load_function,
|
||||
load_list,
|
||||
load_self,
|
||||
get_local,
|
||||
set_local,
|
||||
add,
|
||||
subtract,
|
||||
multiply,
|
||||
divide,
|
||||
modulo,
|
||||
equal,
|
||||
less,
|
||||
less_equal,
|
||||
negate,
|
||||
not,
|
||||
test,
|
||||
test_set,
|
||||
call,
|
||||
call_native,
|
||||
jump,
|
||||
r#return,
|
||||
];
|
||||
|
||||
pub(crate) fn get_next_action(data: &mut ThreadData) -> RunAction {
|
||||
let current_call = data.call_stack.last_mut_unchecked();
|
||||
let instruction = current_call.chunk.instructions[current_call.ip];
|
||||
let operation = instruction.operation();
|
||||
let logic = RUNNER_LOGIC_TABLE[operation.0 as usize];
|
||||
|
||||
current_call.ip += 1;
|
||||
|
||||
RunAction { logic, instruction }
|
||||
}
|
||||
|
||||
pub fn point(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||
let Point { from, to } = instruction.into();
|
||||
let from_register = data.get_register_unchecked(from);
|
||||
let from_register_is_empty = matches!(from_register, Register::Empty);
|
||||
|
||||
if !from_register_is_empty {
|
||||
let register = Register::Pointer(Pointer::Register(to));
|
||||
|
||||
data.set_register(from, register);
|
||||
}
|
||||
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn close(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||
let Close { from, to } = instruction.into();
|
||||
|
||||
for register_index in from..to {
|
||||
data.set_register(register_index, Register::Empty);
|
||||
}
|
||||
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn load_boolean(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||
let LoadBoolean {
|
||||
destination,
|
||||
value,
|
||||
jump_next,
|
||||
} = instruction.into();
|
||||
let boolean = Value::Concrete(ConcreteValue::Boolean(value));
|
||||
let register = Register::Value(boolean);
|
||||
|
||||
data.set_register(destination, register);
|
||||
|
||||
if jump_next {
|
||||
let current_call = data.call_stack.last_mut_unchecked();
|
||||
|
||||
current_call.ip += 1;
|
||||
}
|
||||
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn load_constant(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||
let LoadConstant {
|
||||
destination,
|
||||
constant_index,
|
||||
jump_next,
|
||||
} = instruction.into();
|
||||
let register = Register::Pointer(Pointer::Constant(constant_index));
|
||||
|
||||
trace!("Load constant {constant_index} into R{destination}");
|
||||
|
||||
data.set_register(destination, register);
|
||||
|
||||
if jump_next {
|
||||
let current_call = data.call_stack.last_mut_unchecked();
|
||||
|
||||
current_call.ip += 1;
|
||||
}
|
||||
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||
let LoadList {
|
||||
destination,
|
||||
start_register,
|
||||
jump_next,
|
||||
} = instruction.into();
|
||||
let mut item_pointers = Vec::with_capacity((destination - start_register) as usize);
|
||||
let mut item_type = Type::Any;
|
||||
|
||||
for register_index in start_register..destination {
|
||||
match data.get_register_unchecked(register_index) {
|
||||
Register::Empty => continue,
|
||||
Register::Value(value) => {
|
||||
if item_type == Type::Any {
|
||||
item_type = value.r#type();
|
||||
}
|
||||
}
|
||||
Register::Pointer(pointer) => {
|
||||
if item_type == Type::Any {
|
||||
item_type = data.follow_pointer_unchecked(*pointer).r#type();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let pointer = Pointer::Register(register_index);
|
||||
|
||||
item_pointers.push(pointer);
|
||||
}
|
||||
|
||||
let list_value = Value::AbstractList(AbstractList {
|
||||
item_type,
|
||||
item_pointers,
|
||||
});
|
||||
let register = Register::Value(list_value);
|
||||
|
||||
data.set_register(destination, register);
|
||||
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn load_function(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||
let LoadFunction {
|
||||
destination,
|
||||
prototype_index,
|
||||
jump_next,
|
||||
} = instruction.into();
|
||||
let prototype_index = prototype_index as usize;
|
||||
let current_call = data.call_stack.last_mut_unchecked();
|
||||
let prototype = ¤t_call.chunk.prototypes[prototype_index];
|
||||
let function = prototype.as_function();
|
||||
let register = Register::Value(Value::Function(function));
|
||||
|
||||
data.set_register(destination, register);
|
||||
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn load_self(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||
let LoadSelf {
|
||||
destination,
|
||||
jump_next,
|
||||
} = instruction.into();
|
||||
let current_call = data.call_stack.last_mut_unchecked();
|
||||
let prototype = ¤t_call.chunk;
|
||||
let function = prototype.as_function();
|
||||
let register = Register::Value(Value::Function(function));
|
||||
|
||||
data.set_register(destination, register);
|
||||
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn get_local(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||
let GetLocal {
|
||||
destination,
|
||||
local_index,
|
||||
} = instruction.into();
|
||||
let local_register_index = data.get_local_register(local_index);
|
||||
let register = Register::Pointer(Pointer::Register(local_register_index));
|
||||
|
||||
data.set_register(destination, register);
|
||||
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn set_local(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||
let SetLocal {
|
||||
register_index,
|
||||
local_index,
|
||||
} = instruction.into();
|
||||
let local_register_index = data.get_local_register(local_index);
|
||||
let register = Register::Pointer(Pointer::Register(register_index));
|
||||
|
||||
data.set_register(local_register_index, register);
|
||||
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn add(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||
let Add {
|
||||
destination,
|
||||
left,
|
||||
left_type,
|
||||
right,
|
||||
right_type,
|
||||
} = instruction.into();
|
||||
let sum = match (left_type, right_type) {
|
||||
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
||||
let left = unsafe {
|
||||
data.get_argument_unchecked(left)
|
||||
.as_integer()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
let right = unsafe {
|
||||
data.get_argument_unchecked(right)
|
||||
.as_integer()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
|
||||
ConcreteValue::Integer(left + right)
|
||||
}
|
||||
(TypeCode::FLOAT, TypeCode::FLOAT) => {
|
||||
let left = unsafe {
|
||||
data.get_argument_unchecked(left)
|
||||
.as_float()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
let right = unsafe {
|
||||
data.get_argument_unchecked(right)
|
||||
.as_float()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
|
||||
ConcreteValue::Float(left + right)
|
||||
}
|
||||
_ => panic!("VM Error: Cannot add values"),
|
||||
};
|
||||
let register = Register::Value(Value::Concrete(sum));
|
||||
|
||||
data.set_register(destination, register);
|
||||
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn subtract(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||
let Subtract {
|
||||
destination,
|
||||
left,
|
||||
left_type,
|
||||
right,
|
||||
right_type,
|
||||
} = instruction.into();
|
||||
let difference = match (left_type, right_type) {
|
||||
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
||||
let left = unsafe {
|
||||
data.get_argument_unchecked(left)
|
||||
.as_integer()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
let right = unsafe {
|
||||
data.get_argument_unchecked(right)
|
||||
.as_integer()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
|
||||
ConcreteValue::Integer(left - right)
|
||||
}
|
||||
(TypeCode::FLOAT, TypeCode::FLOAT) => {
|
||||
let left = unsafe {
|
||||
data.get_argument_unchecked(left)
|
||||
.as_float()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
let right = unsafe {
|
||||
data.get_argument_unchecked(right)
|
||||
.as_float()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
|
||||
ConcreteValue::Float(left - right)
|
||||
}
|
||||
_ => panic!("VM Error: Cannot subtract values"),
|
||||
};
|
||||
let register = Register::Value(Value::Concrete(difference));
|
||||
|
||||
data.set_register(destination, register);
|
||||
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn multiply(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||
let Multiply {
|
||||
destination,
|
||||
left,
|
||||
left_type,
|
||||
right,
|
||||
right_type,
|
||||
} = instruction.into();
|
||||
let product = match (left_type, right_type) {
|
||||
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
||||
let left = unsafe {
|
||||
data.get_argument_unchecked(left)
|
||||
.as_integer()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
let right = unsafe {
|
||||
data.get_argument_unchecked(right)
|
||||
.as_integer()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
|
||||
ConcreteValue::Integer(left * right)
|
||||
}
|
||||
(TypeCode::FLOAT, TypeCode::FLOAT) => {
|
||||
let left = unsafe {
|
||||
data.get_argument_unchecked(left)
|
||||
.as_float()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
let right = unsafe {
|
||||
data.get_argument_unchecked(right)
|
||||
.as_float()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
|
||||
ConcreteValue::Float(left * right)
|
||||
}
|
||||
_ => panic!("VM Error: Cannot multiply values"),
|
||||
};
|
||||
let register = Register::Value(Value::Concrete(product));
|
||||
|
||||
data.set_register(destination, register);
|
||||
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn divide(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||
let Divide {
|
||||
destination,
|
||||
left,
|
||||
left_type,
|
||||
right,
|
||||
right_type,
|
||||
} = instruction.into();
|
||||
let quotient = match (left_type, right_type) {
|
||||
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
||||
let left = unsafe {
|
||||
data.get_argument_unchecked(left)
|
||||
.as_integer()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
let right = unsafe {
|
||||
data.get_argument_unchecked(right)
|
||||
.as_integer()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
|
||||
ConcreteValue::Integer(left / right)
|
||||
}
|
||||
(TypeCode::FLOAT, TypeCode::FLOAT) => {
|
||||
let left = unsafe {
|
||||
data.get_argument_unchecked(left)
|
||||
.as_float()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
let right = unsafe {
|
||||
data.get_argument_unchecked(right)
|
||||
.as_float()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
|
||||
ConcreteValue::Float(left / right)
|
||||
}
|
||||
_ => panic!("VM Error: Cannot divide values"),
|
||||
};
|
||||
let register = Register::Value(Value::Concrete(quotient));
|
||||
|
||||
data.set_register(destination, register);
|
||||
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn modulo(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||
let Modulo {
|
||||
destination,
|
||||
left,
|
||||
left_type,
|
||||
right,
|
||||
right_type,
|
||||
} = instruction.into();
|
||||
let remainder = match (left_type, right_type) {
|
||||
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
||||
let left = unsafe {
|
||||
data.get_argument_unchecked(left)
|
||||
.as_integer()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
let right = unsafe {
|
||||
data.get_argument_unchecked(right)
|
||||
.as_integer()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
|
||||
ConcreteValue::Integer(left % right)
|
||||
}
|
||||
_ => panic!("VM Error: Cannot modulo values"),
|
||||
};
|
||||
let register = Register::Value(Value::Concrete(remainder));
|
||||
|
||||
data.set_register(destination, register);
|
||||
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn test(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||
let Test {
|
||||
operand_register,
|
||||
test_value,
|
||||
} = instruction.into();
|
||||
let value = data.open_register_unchecked(operand_register);
|
||||
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
|
||||
*boolean
|
||||
} else {
|
||||
panic!("VM Error: Expected boolean value for TEST operation",);
|
||||
};
|
||||
|
||||
if boolean == test_value {
|
||||
let current_call = data.call_stack.last_mut_unchecked();
|
||||
|
||||
current_call.ip += 1;
|
||||
}
|
||||
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn test_set(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||
let TestSet {
|
||||
destination,
|
||||
argument,
|
||||
test_value,
|
||||
} = instruction.into();
|
||||
let value = data.get_argument_unchecked(argument);
|
||||
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
|
||||
*boolean
|
||||
} else {
|
||||
panic!("VM Error: Expected boolean value for TEST_SET operation",);
|
||||
};
|
||||
|
||||
if boolean == test_value {
|
||||
} else {
|
||||
let pointer = match argument {
|
||||
Operand::Constant(constant_index) => Pointer::Constant(constant_index),
|
||||
Operand::Register(register_index) => Pointer::Register(register_index),
|
||||
};
|
||||
let register = Register::Pointer(pointer);
|
||||
|
||||
data.set_register(destination, register);
|
||||
}
|
||||
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn equal(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||
let Equal {
|
||||
comparator,
|
||||
left,
|
||||
left_type,
|
||||
right,
|
||||
right_type,
|
||||
} = instruction.into();
|
||||
let is_equal = match (left_type, right_type) {
|
||||
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
||||
let left = unsafe {
|
||||
data.get_argument_unchecked(left)
|
||||
.as_integer()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
let right = unsafe {
|
||||
data.get_argument_unchecked(right)
|
||||
.as_integer()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
|
||||
left == right
|
||||
}
|
||||
(TypeCode::FLOAT, TypeCode::FLOAT) => {
|
||||
let left = unsafe {
|
||||
data.get_argument_unchecked(left)
|
||||
.as_float()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
let right = unsafe {
|
||||
data.get_argument_unchecked(right)
|
||||
.as_float()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
|
||||
left == right
|
||||
}
|
||||
(TypeCode::BOOLEAN, TypeCode::BOOLEAN) => {
|
||||
let left = unsafe {
|
||||
data.get_argument_unchecked(left)
|
||||
.as_boolean()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
let right = unsafe {
|
||||
data.get_argument_unchecked(right)
|
||||
.as_boolean()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
|
||||
left == right
|
||||
}
|
||||
(TypeCode::STRING, TypeCode::STRING) => {
|
||||
let left = unsafe {
|
||||
data.get_argument_unchecked(left)
|
||||
.as_string()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
let right = unsafe {
|
||||
data.get_argument_unchecked(right)
|
||||
.as_string()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
|
||||
left == right
|
||||
}
|
||||
_ => panic!("VM Error: Cannot compare values"),
|
||||
};
|
||||
|
||||
if is_equal == comparator {
|
||||
let current_call = data.call_stack.last_mut_unchecked();
|
||||
|
||||
current_call.ip += 1;
|
||||
}
|
||||
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn less(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||
let Less {
|
||||
comparator,
|
||||
left,
|
||||
left_type,
|
||||
right,
|
||||
right_type,
|
||||
} = instruction.into();
|
||||
let is_less = match (left_type, right_type) {
|
||||
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
||||
let left = unsafe {
|
||||
data.get_argument_unchecked(left)
|
||||
.as_integer()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
let right = unsafe {
|
||||
data.get_argument_unchecked(right)
|
||||
.as_integer()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
|
||||
left < right
|
||||
}
|
||||
(TypeCode::FLOAT, TypeCode::FLOAT) => {
|
||||
let left = unsafe {
|
||||
data.get_argument_unchecked(left)
|
||||
.as_float()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
let right = unsafe {
|
||||
data.get_argument_unchecked(right)
|
||||
.as_float()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
|
||||
left < right
|
||||
}
|
||||
_ => panic!("VM Error: Cannot compare values"),
|
||||
};
|
||||
|
||||
if is_less == comparator {
|
||||
let current_call = data.call_stack.last_mut_unchecked();
|
||||
|
||||
current_call.ip += 1;
|
||||
}
|
||||
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn less_equal(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||
let LessEqual {
|
||||
comparator,
|
||||
left,
|
||||
left_type,
|
||||
right,
|
||||
right_type,
|
||||
} = instruction.into();
|
||||
let is_less_or_equal = match (left_type, right_type) {
|
||||
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
||||
let left = unsafe {
|
||||
data.get_argument_unchecked(left)
|
||||
.as_integer()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
let right = unsafe {
|
||||
data.get_argument_unchecked(right)
|
||||
.as_integer()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
|
||||
left <= right
|
||||
}
|
||||
(TypeCode::FLOAT, TypeCode::FLOAT) => {
|
||||
let left = unsafe {
|
||||
data.get_argument_unchecked(left)
|
||||
.as_float()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
let right = unsafe {
|
||||
data.get_argument_unchecked(right)
|
||||
.as_float()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
|
||||
left <= right
|
||||
}
|
||||
_ => panic!("VM Error: Cannot compare values"),
|
||||
};
|
||||
|
||||
if is_less_or_equal == comparator {
|
||||
let current_call = data.call_stack.last_mut_unchecked();
|
||||
|
||||
current_call.ip += 1;
|
||||
}
|
||||
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn negate(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||
let Negate {
|
||||
destination,
|
||||
argument,
|
||||
argument_type,
|
||||
} = instruction.into();
|
||||
let argument = data.get_argument_unchecked(argument);
|
||||
let negated = argument.negate();
|
||||
let register = Register::Value(negated);
|
||||
|
||||
data.set_register(destination, register);
|
||||
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn not(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||
let Not {
|
||||
destination,
|
||||
argument,
|
||||
} = instruction.into();
|
||||
let argument = data.get_argument_unchecked(argument);
|
||||
let not = match argument {
|
||||
Value::Concrete(ConcreteValue::Boolean(boolean)) => ConcreteValue::Boolean(!boolean),
|
||||
_ => panic!("VM Error: Expected boolean value for NOT operation"),
|
||||
};
|
||||
let register = Register::Value(Value::Concrete(not));
|
||||
|
||||
data.set_register(destination, register);
|
||||
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn jump(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||
let Jump {
|
||||
offset,
|
||||
is_positive,
|
||||
} = instruction.into();
|
||||
let offset = offset as usize;
|
||||
let current_call = data.call_stack.last_mut_unchecked();
|
||||
|
||||
if is_positive {
|
||||
current_call.ip += offset;
|
||||
} else {
|
||||
current_call.ip -= offset + 1;
|
||||
}
|
||||
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn call(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||
let Call {
|
||||
destination: return_register,
|
||||
function_register,
|
||||
argument_count,
|
||||
is_recursive,
|
||||
} = instruction.into();
|
||||
let current_call = data.call_stack.last_unchecked();
|
||||
let first_argument_register = return_register - argument_count;
|
||||
let prototype = if is_recursive {
|
||||
current_call.chunk.clone()
|
||||
} else {
|
||||
let function = data
|
||||
.open_register_unchecked(function_register)
|
||||
.as_function()
|
||||
.unwrap();
|
||||
|
||||
current_call.chunk.prototypes[function.prototype_index as usize].clone()
|
||||
};
|
||||
let mut next_call = FunctionCall::new(prototype, return_register);
|
||||
let mut argument_index = 0;
|
||||
|
||||
for register_index in first_argument_register..return_register {
|
||||
let value_option = data.open_register_allow_empty_unchecked(register_index);
|
||||
let argument = if let Some(value) = value_option {
|
||||
value.clone()
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
next_call.registers[argument_index] = Register::Value(argument);
|
||||
argument_index += 1;
|
||||
}
|
||||
|
||||
data.call_stack.push(next_call);
|
||||
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn call_native(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||
let CallNative {
|
||||
destination,
|
||||
function,
|
||||
argument_count,
|
||||
} = instruction.into();
|
||||
let first_argument_index = destination - argument_count;
|
||||
let argument_range = first_argument_index..destination;
|
||||
|
||||
function.call(data, destination, argument_range)
|
||||
}
|
||||
|
||||
pub fn r#return(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||
trace!("Returning with call stack:\n{}", data.call_stack);
|
||||
|
||||
let Return {
|
||||
should_return_value,
|
||||
return_register,
|
||||
} = instruction.into();
|
||||
let (destination, return_value) = if data.call_stack.len() == 1 {
|
||||
if should_return_value {
|
||||
data.return_value_index = Some(return_register);
|
||||
};
|
||||
|
||||
return true;
|
||||
} else {
|
||||
let return_value = data.empty_register_or_clone_constant_unchecked(return_register);
|
||||
let destination = data.call_stack.pop_unchecked().return_register;
|
||||
|
||||
(destination, return_value)
|
||||
};
|
||||
|
||||
if should_return_value {
|
||||
data.set_register(destination, Register::Value(return_value));
|
||||
}
|
||||
|
||||
data.next_action = get_next_action(data);
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use crate::Operation;
|
||||
|
||||
use super::*;
|
||||
|
||||
const ALL_OPERATIONS: [(Operation, RunnerLogic); 24] = [
|
||||
(Operation::POINT, point),
|
||||
(Operation::CLOSE, close),
|
||||
(Operation::LOAD_BOOLEAN, load_boolean),
|
||||
(Operation::LOAD_CONSTANT, load_constant),
|
||||
(Operation::LOAD_LIST, load_list),
|
||||
(Operation::LOAD_SELF, load_self),
|
||||
(Operation::GET_LOCAL, get_local),
|
||||
(Operation::SET_LOCAL, set_local),
|
||||
(Operation::ADD, add),
|
||||
(Operation::SUBTRACT, subtract),
|
||||
(Operation::MULTIPLY, multiply),
|
||||
(Operation::DIVIDE, divide),
|
||||
(Operation::MODULO, modulo),
|
||||
(Operation::TEST, test),
|
||||
(Operation::TEST_SET, test_set),
|
||||
(Operation::EQUAL, equal),
|
||||
(Operation::LESS, less),
|
||||
(Operation::LESS_EQUAL, less_equal),
|
||||
(Operation::NEGATE, negate),
|
||||
(Operation::NOT, not),
|
||||
(Operation::CALL, call),
|
||||
(Operation::CALL_NATIVE, call_native),
|
||||
(Operation::JUMP, jump),
|
||||
(Operation::RETURN, r#return),
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn operations_map_to_the_correct_runner() {
|
||||
for (operation, expected_runner) in ALL_OPERATIONS {
|
||||
let actual_runner = RUNNER_LOGIC_TABLE[operation.0 as usize];
|
||||
|
||||
assert_eq!(
|
||||
expected_runner, actual_runner,
|
||||
"{operation} runner is incorrect"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,137 +0,0 @@
|
||||
use std::{
|
||||
fmt::{self, Debug, Display, Formatter},
|
||||
ops::{Index, IndexMut, Range},
|
||||
};
|
||||
|
||||
use super::FunctionCall;
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Stack<T> {
|
||||
items: Vec<T>,
|
||||
}
|
||||
|
||||
impl<T> Stack<T> {
|
||||
pub fn new() -> Self {
|
||||
Stack {
|
||||
items: Vec::with_capacity(1),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
Stack {
|
||||
items: Vec::with_capacity(capacity),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.items.is_empty()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.items.len()
|
||||
}
|
||||
|
||||
pub fn get_unchecked(&self, index: usize) -> &T {
|
||||
if cfg!(debug_assertions) {
|
||||
assert!(index < self.len(), "Stack underflow");
|
||||
|
||||
&self.items[index]
|
||||
} else {
|
||||
unsafe { self.items.get_unchecked(index) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_unchecked_mut(&mut self, index: usize) -> &mut T {
|
||||
if cfg!(debug_assertions) {
|
||||
assert!(index < self.len(), "Stack underflow");
|
||||
|
||||
&mut self.items[index]
|
||||
} else {
|
||||
unsafe { self.items.get_unchecked_mut(index) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&mut self, item: T) {
|
||||
self.items.push(item);
|
||||
}
|
||||
|
||||
pub fn pop(&mut self) -> Option<T> {
|
||||
self.items.pop()
|
||||
}
|
||||
|
||||
pub fn last(&self) -> Option<&T> {
|
||||
self.items.last()
|
||||
}
|
||||
|
||||
pub fn last_mut(&mut self) -> Option<&mut T> {
|
||||
self.items.last_mut()
|
||||
}
|
||||
|
||||
pub fn pop_unchecked(&mut self) -> T {
|
||||
if cfg!(debug_assertions) {
|
||||
assert!(!self.is_empty(), "Stack underflow");
|
||||
|
||||
self.items.pop().unwrap()
|
||||
} else {
|
||||
unsafe { self.items.pop().unwrap_unchecked() }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn last_unchecked(&self) -> &T {
|
||||
if cfg!(debug_assertions) {
|
||||
assert!(!self.is_empty(), "Stack underflow");
|
||||
|
||||
self.items.last().unwrap()
|
||||
} else {
|
||||
unsafe { self.items.last().unwrap_unchecked() }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn last_mut_unchecked(&mut self) -> &mut T {
|
||||
if cfg!(debug_assertions) {
|
||||
assert!(!self.is_empty(), "Stack underflow");
|
||||
|
||||
self.items.last_mut().unwrap()
|
||||
} else {
|
||||
unsafe { self.items.last_mut().unwrap_unchecked() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for Stack<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Index<Range<usize>> for Stack<T> {
|
||||
type Output = [T];
|
||||
|
||||
fn index(&self, index: Range<usize>) -> &Self::Output {
|
||||
&self.items[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IndexMut<Range<usize>> for Stack<T> {
|
||||
fn index_mut(&mut self, index: Range<usize>) -> &mut Self::Output {
|
||||
&mut self.items[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Debug> Debug for Stack<T> {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}", self.items)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Stack<FunctionCall> {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
writeln!(f, "----- DUST CALL STACK -----")?;
|
||||
|
||||
for (index, function_call) in self.items.iter().enumerate().rev() {
|
||||
writeln!(f, "{index:02} | {function_call}")?;
|
||||
}
|
||||
|
||||
write!(f, "---------------------------")
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,129 +0,0 @@
|
||||
use dust_lang::*;
|
||||
|
||||
#[test]
|
||||
fn constant() {
|
||||
let source = "42";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::Integer
|
||||
},
|
||||
vec![
|
||||
(Instruction::load_constant(0, 0, false), Span(0, 2)),
|
||||
(Instruction::r#return(true), Span(2, 2))
|
||||
],
|
||||
vec![ConcreteValue::Integer(42)],
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
|
||||
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(42))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty() {
|
||||
let source = "";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::None
|
||||
},
|
||||
vec![(Instruction::r#return(false), Span(0, 0))],
|
||||
vec![],
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
assert_eq!(run(source), Ok(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parentheses_precedence() {
|
||||
let source = "(1 + 2) * 3";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::Integer
|
||||
},
|
||||
vec![
|
||||
(
|
||||
Instruction::add(0, Operand::Constant(0), Operand::Constant(1)),
|
||||
Span(3, 4)
|
||||
),
|
||||
(
|
||||
Instruction::multiply(1, Operand::Register(0), Operand::Constant(2)),
|
||||
Span(8, 9)
|
||||
),
|
||||
(Instruction::r#return(true), Span(11, 11)),
|
||||
],
|
||||
vec![
|
||||
ConcreteValue::Integer(1),
|
||||
ConcreteValue::Integer(2),
|
||||
ConcreteValue::Integer(3)
|
||||
],
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
|
||||
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(9))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn math_operator_precedence() {
|
||||
let source = "1 + 2 - 3 * 4 / 5";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::Integer,
|
||||
},
|
||||
vec![
|
||||
(
|
||||
Instruction::add(0, Operand::Constant(0), Operand::Constant(1)),
|
||||
Span(2, 3)
|
||||
),
|
||||
(
|
||||
Instruction::multiply(1, Operand::Constant(2), Operand::Constant(3)),
|
||||
Span(10, 11)
|
||||
),
|
||||
(
|
||||
Instruction::divide(2, Operand::Register(1), Operand::Constant(4)),
|
||||
Span(14, 15)
|
||||
),
|
||||
(
|
||||
Instruction::subtract(3, Operand::Register(0), Operand::Register(2)),
|
||||
Span(6, 7)
|
||||
),
|
||||
(Instruction::r#return(true), Span(17, 17)),
|
||||
],
|
||||
vec![
|
||||
ConcreteValue::Integer(1),
|
||||
ConcreteValue::Integer(2),
|
||||
ConcreteValue::Integer(3),
|
||||
ConcreteValue::Integer(4),
|
||||
ConcreteValue::Integer(5),
|
||||
],
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
|
||||
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(1))));
|
||||
}
|
@ -1,169 +0,0 @@
|
||||
use dust_lang::*;
|
||||
|
||||
#[test]
|
||||
fn equal() {
|
||||
let source = "1 == 2";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::Boolean
|
||||
},
|
||||
vec![
|
||||
(
|
||||
Instruction::equal(0, true, Operand::Constant(0), Operand::Constant(1)),
|
||||
Span(2, 4)
|
||||
),
|
||||
(Instruction::r#return(true), Span(6, 6)),
|
||||
],
|
||||
vec![ConcreteValue::Integer(1), ConcreteValue::Integer(2)],
|
||||
vec![]
|
||||
)),
|
||||
);
|
||||
|
||||
assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(false))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn greater() {
|
||||
let source = "1 > 2";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::Boolean
|
||||
},
|
||||
vec![
|
||||
(
|
||||
Instruction::less_equal(0, false, Operand::Constant(0), Operand::Constant(1)),
|
||||
Span(2, 3)
|
||||
),
|
||||
(Instruction::r#return(true), Span(5, 5)),
|
||||
],
|
||||
vec![ConcreteValue::Integer(1), ConcreteValue::Integer(2)],
|
||||
vec![]
|
||||
)),
|
||||
);
|
||||
|
||||
assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(false))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn greater_than_or_equal() {
|
||||
let source = "1 >= 2";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::Boolean
|
||||
},
|
||||
vec![
|
||||
(
|
||||
Instruction::less(0, false, Operand::Constant(0), Operand::Constant(1)),
|
||||
Span(2, 4)
|
||||
),
|
||||
(Instruction::r#return(true), Span(6, 6)),
|
||||
],
|
||||
vec![ConcreteValue::Integer(1), ConcreteValue::Integer(2)],
|
||||
vec![]
|
||||
)),
|
||||
);
|
||||
|
||||
assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(false))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn less_than() {
|
||||
let source = "1 < 2";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::Boolean
|
||||
},
|
||||
vec![
|
||||
(
|
||||
Instruction::less(0, true, Operand::Constant(0), Operand::Constant(1)),
|
||||
Span(2, 3)
|
||||
),
|
||||
(Instruction::r#return(true), Span(5, 5)),
|
||||
],
|
||||
vec![ConcreteValue::Integer(1), ConcreteValue::Integer(2)],
|
||||
vec![]
|
||||
)),
|
||||
);
|
||||
|
||||
assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(true))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn less_than_or_equal() {
|
||||
let source = "1 <= 2";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::Boolean
|
||||
},
|
||||
vec![
|
||||
(
|
||||
Instruction::less_equal(0, true, Operand::Constant(0), Operand::Constant(1)),
|
||||
Span(2, 4)
|
||||
),
|
||||
(Instruction::r#return(true), Span(6, 6)),
|
||||
],
|
||||
vec![ConcreteValue::Integer(1), ConcreteValue::Integer(2)],
|
||||
vec![]
|
||||
)),
|
||||
);
|
||||
|
||||
assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(true))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_equal() {
|
||||
let source = "1 != 2";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::Boolean
|
||||
},
|
||||
vec![
|
||||
(
|
||||
Instruction::equal(0, false, Operand::Constant(0), Operand::Constant(1)),
|
||||
Span(2, 4)
|
||||
),
|
||||
(Instruction::r#return(true), Span(6, 6)),
|
||||
],
|
||||
vec![ConcreteValue::Integer(1), ConcreteValue::Integer(2)],
|
||||
vec![]
|
||||
)),
|
||||
);
|
||||
|
||||
assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(true))));
|
||||
}
|
209
dust-lang/tests/comparison/equal.rs
Normal file
209
dust-lang/tests/comparison/equal.rs
Normal file
@ -0,0 +1,209 @@
|
||||
use dust_lang::{
|
||||
compile, instruction::TypeCode, run, Chunk, DustString, FunctionType, Instruction, Operand,
|
||||
Span, Type, Value,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn equal_bytes() {
|
||||
let source = "0x0A == 0x03";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, 0x0A, TypeCode::BYTE, false),
|
||||
Instruction::load_encoded(1, 0x03, TypeCode::BYTE, false),
|
||||
Instruction::equal(
|
||||
true,
|
||||
Operand::Register(0, TypeCode::BYTE),
|
||||
Operand::Register(1, TypeCode::BYTE),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(8, 12),
|
||||
Span(0, 12),
|
||||
Span(0, 12),
|
||||
Span(0, 12),
|
||||
Span(0, 12),
|
||||
Span(12, 12),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equal_characters() {
|
||||
let source = "'a' == 'b'";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::equal(
|
||||
true,
|
||||
Operand::Constant(0, TypeCode::CHARACTER),
|
||||
Operand::Constant(1, TypeCode::CHARACTER),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 10),
|
||||
Span(0, 10),
|
||||
Span(0, 10),
|
||||
Span(0, 10),
|
||||
Span(10, 10),
|
||||
],
|
||||
character_constants: vec!['a', 'b'],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equal_floats() {
|
||||
let source = "10.0 == 3.0";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::equal(
|
||||
true,
|
||||
Operand::Constant(0, TypeCode::FLOAT),
|
||||
Operand::Constant(1, TypeCode::FLOAT),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 11),
|
||||
Span(0, 11),
|
||||
Span(0, 11),
|
||||
Span(0, 11),
|
||||
Span(11, 11),
|
||||
],
|
||||
float_constants: vec![10.0, 3.0],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equal_integers() {
|
||||
let source = "10 == 3";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::equal(
|
||||
true,
|
||||
Operand::Constant(0, TypeCode::INTEGER),
|
||||
Operand::Constant(1, TypeCode::INTEGER),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![Span(0, 7), Span(0, 7), Span(0, 7), Span(0, 7), Span(7, 7)],
|
||||
integer_constants: vec![10, 3],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equal_strings() {
|
||||
let source = "\"abc\" == \"def\"";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::equal(
|
||||
true,
|
||||
Operand::Constant(0, TypeCode::STRING),
|
||||
Operand::Constant(1, TypeCode::STRING),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 14),
|
||||
Span(0, 14),
|
||||
Span(0, 14),
|
||||
Span(0, 14),
|
||||
Span(14, 14),
|
||||
],
|
||||
string_constants: vec![DustString::from("abc"), DustString::from("def")],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equal_lists() {
|
||||
let source = "[1, 2, 3] == [4, 5, 6]";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_constant(0, 0, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(1, 1, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(2, 2, TypeCode::INTEGER, false),
|
||||
Instruction::load_list(0, TypeCode::INTEGER, 0, 2, false),
|
||||
Instruction::load_constant(3, 3, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(4, 4, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(5, 5, TypeCode::INTEGER, false),
|
||||
Instruction::load_list(1, TypeCode::INTEGER, 3, 5, false),
|
||||
Instruction::equal(
|
||||
true,
|
||||
Operand::Register(0, TypeCode::LIST),
|
||||
Operand::Register(1, TypeCode::LIST),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(1, 2),
|
||||
Span(4, 5),
|
||||
Span(7, 8),
|
||||
Span(0, 9),
|
||||
Span(14, 15),
|
||||
Span(17, 18),
|
||||
Span(20, 21),
|
||||
Span(13, 22),
|
||||
Span(0, 22),
|
||||
Span(0, 22),
|
||||
Span(0, 22),
|
||||
Span(0, 22),
|
||||
Span(22, 22),
|
||||
],
|
||||
integer_constants: vec![1, 2, 3, 4, 5, 6],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
203
dust-lang/tests/comparison/greater.rs
Normal file
203
dust-lang/tests/comparison/greater.rs
Normal file
@ -0,0 +1,203 @@
|
||||
use dust_lang::{
|
||||
compile, instruction::TypeCode, run, Chunk, DustString, FunctionType, Instruction, Operand,
|
||||
Span, Type, Value,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn greater_bytes() {
|
||||
let source = "0x0A > 0x03";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, 0x0A, TypeCode::BYTE, false),
|
||||
Instruction::load_encoded(1, 0x03, TypeCode::BYTE, false),
|
||||
Instruction::less_equal(
|
||||
false,
|
||||
Operand::Register(0, TypeCode::BYTE),
|
||||
Operand::Register(1, TypeCode::BYTE),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(7, 11),
|
||||
Span(0, 11),
|
||||
Span(0, 11),
|
||||
Span(0, 11),
|
||||
Span(0, 11),
|
||||
Span(11, 11),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn greater_characters() {
|
||||
let source = "'a' > 'b'";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::less_equal(
|
||||
false,
|
||||
Operand::Constant(0, TypeCode::CHARACTER),
|
||||
Operand::Constant(1, TypeCode::CHARACTER),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![Span(0, 9), Span(0, 9), Span(0, 9), Span(0, 9), Span(9, 9)],
|
||||
character_constants: vec!['a', 'b'],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn greater_floats() {
|
||||
let source = "10.0 > 3.0";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::less_equal(
|
||||
false,
|
||||
Operand::Constant(0, TypeCode::FLOAT),
|
||||
Operand::Constant(1, TypeCode::FLOAT),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 10),
|
||||
Span(0, 10),
|
||||
Span(0, 10),
|
||||
Span(0, 10),
|
||||
Span(10, 10),
|
||||
],
|
||||
float_constants: vec![10.0, 3.0],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn greater_integers() {
|
||||
let source = "10 > 3";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::less_equal(
|
||||
false,
|
||||
Operand::Constant(0, TypeCode::INTEGER),
|
||||
Operand::Constant(1, TypeCode::INTEGER),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![Span(0, 6), Span(0, 6), Span(0, 6), Span(0, 6), Span(6, 6)],
|
||||
integer_constants: vec![10, 3],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn greater_strings() {
|
||||
let source = "\"abc\" > \"def\"";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::less_equal(
|
||||
false,
|
||||
Operand::Constant(0, TypeCode::STRING),
|
||||
Operand::Constant(1, TypeCode::STRING),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 13),
|
||||
Span(0, 13),
|
||||
Span(0, 13),
|
||||
Span(0, 13),
|
||||
Span(13, 13),
|
||||
],
|
||||
string_constants: vec![DustString::from("abc"), DustString::from("def")],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn greater_lists() {
|
||||
let source = "[1, 2, 3] > [4, 5, 6]";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_constant(0, 0, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(1, 1, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(2, 2, TypeCode::INTEGER, false),
|
||||
Instruction::load_list(0, TypeCode::INTEGER, 0, 2, false),
|
||||
Instruction::load_constant(3, 3, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(4, 4, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(5, 5, TypeCode::INTEGER, false),
|
||||
Instruction::load_list(1, TypeCode::INTEGER, 3, 5, false),
|
||||
Instruction::less_equal(
|
||||
false,
|
||||
Operand::Register(0, TypeCode::LIST),
|
||||
Operand::Register(1, TypeCode::LIST),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(1, 2),
|
||||
Span(4, 5),
|
||||
Span(7, 8),
|
||||
Span(0, 9),
|
||||
Span(13, 14),
|
||||
Span(16, 17),
|
||||
Span(19, 20),
|
||||
Span(12, 21),
|
||||
Span(0, 21),
|
||||
Span(0, 21),
|
||||
Span(0, 21),
|
||||
Span(0, 21),
|
||||
Span(21, 21),
|
||||
],
|
||||
integer_constants: vec![1, 2, 3, 4, 5, 6],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
244
dust-lang/tests/comparison/greater_equal.rs
Normal file
244
dust-lang/tests/comparison/greater_equal.rs
Normal file
@ -0,0 +1,244 @@
|
||||
use dust_lang::{
|
||||
compile, instruction::TypeCode, run, Chunk, DustString, FunctionType, Instruction, Operand,
|
||||
Span, Type, Value,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn great_equal_booleans() {
|
||||
let source = "true >= false";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::load_encoded(1, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::less(
|
||||
false,
|
||||
Operand::Register(0, TypeCode::BOOLEAN),
|
||||
Operand::Register(1, TypeCode::BOOLEAN),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(2, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(2, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 2, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(8, 13),
|
||||
Span(0, 13),
|
||||
Span(0, 13),
|
||||
Span(0, 13),
|
||||
Span(0, 13),
|
||||
Span(13, 13),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn greater_equal_bytes() {
|
||||
let source = "0x0A >= 0x03";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, 0x0A, TypeCode::BYTE, false),
|
||||
Instruction::load_encoded(1, 0x03, TypeCode::BYTE, false),
|
||||
Instruction::less(
|
||||
false,
|
||||
Operand::Register(0, TypeCode::BYTE),
|
||||
Operand::Register(1, TypeCode::BYTE),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(8, 12),
|
||||
Span(0, 12),
|
||||
Span(0, 12),
|
||||
Span(0, 12),
|
||||
Span(0, 12),
|
||||
Span(12, 12),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn greater_equal_characters() {
|
||||
let source = "'a' >= 'b'";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::less(
|
||||
false,
|
||||
Operand::Constant(0, TypeCode::CHARACTER),
|
||||
Operand::Constant(1, TypeCode::CHARACTER),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 10),
|
||||
Span(0, 10),
|
||||
Span(0, 10),
|
||||
Span(0, 10),
|
||||
Span(10, 10),
|
||||
],
|
||||
character_constants: vec!['a', 'b'],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn greater_equal_floats() {
|
||||
let source = "10.0 >= 3.0";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::less(
|
||||
false,
|
||||
Operand::Constant(0, TypeCode::FLOAT),
|
||||
Operand::Constant(1, TypeCode::FLOAT),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 11),
|
||||
Span(0, 11),
|
||||
Span(0, 11),
|
||||
Span(0, 11),
|
||||
Span(11, 11),
|
||||
],
|
||||
float_constants: vec![10.0, 3.0],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn greater_equal_integers() {
|
||||
let source = "10 >= 3";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::less(
|
||||
false,
|
||||
Operand::Constant(0, TypeCode::INTEGER),
|
||||
Operand::Constant(1, TypeCode::INTEGER),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![Span(0, 7), Span(0, 7), Span(0, 7), Span(0, 7), Span(7, 7)],
|
||||
integer_constants: vec![10, 3],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn greater_equal_strings() {
|
||||
let source = "\"abc\" >= \"def\"";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::less(
|
||||
false,
|
||||
Operand::Constant(0, TypeCode::STRING),
|
||||
Operand::Constant(1, TypeCode::STRING),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 14),
|
||||
Span(0, 14),
|
||||
Span(0, 14),
|
||||
Span(0, 14),
|
||||
Span(14, 14),
|
||||
],
|
||||
string_constants: vec![DustString::from("abc"), DustString::from("def")],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn greater_equal_lists() {
|
||||
let source = "[1, 2, 3] >= [4, 5, 6]";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_constant(0, 0, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(1, 1, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(2, 2, TypeCode::INTEGER, false),
|
||||
Instruction::load_list(0, TypeCode::INTEGER, 0, 2, false),
|
||||
Instruction::load_constant(3, 3, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(4, 4, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(5, 5, TypeCode::INTEGER, false),
|
||||
Instruction::load_list(1, TypeCode::INTEGER, 3, 5, false),
|
||||
Instruction::less(
|
||||
false,
|
||||
Operand::Register(0, TypeCode::LIST),
|
||||
Operand::Register(1, TypeCode::LIST),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(1, 2),
|
||||
Span(4, 5),
|
||||
Span(7, 8),
|
||||
Span(0, 9),
|
||||
Span(14, 15),
|
||||
Span(17, 18),
|
||||
Span(20, 21),
|
||||
Span(13, 22),
|
||||
Span(0, 22),
|
||||
Span(0, 22),
|
||||
Span(0, 22),
|
||||
Span(0, 22),
|
||||
Span(22, 22),
|
||||
],
|
||||
integer_constants: vec![1, 2, 3, 4, 5, 6],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
238
dust-lang/tests/comparison/less.rs
Normal file
238
dust-lang/tests/comparison/less.rs
Normal file
@ -0,0 +1,238 @@
|
||||
use dust_lang::{
|
||||
compile, instruction::TypeCode, run, Chunk, DustString, FunctionType, Instruction, Operand,
|
||||
Span, Type, Value,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn less_booleans() {
|
||||
let source = "true < false";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::load_encoded(1, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::less(
|
||||
true,
|
||||
Operand::Register(0, TypeCode::BOOLEAN),
|
||||
Operand::Register(1, TypeCode::BOOLEAN),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(2, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(2, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 2, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(7, 12),
|
||||
Span(0, 12),
|
||||
Span(0, 12),
|
||||
Span(0, 12),
|
||||
Span(0, 12),
|
||||
Span(12, 12),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn less_bytes() {
|
||||
let source = "0x0A < 0x03";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, 0x0A, TypeCode::BYTE, false),
|
||||
Instruction::load_encoded(1, 0x03, TypeCode::BYTE, false),
|
||||
Instruction::less(
|
||||
true,
|
||||
Operand::Register(0, TypeCode::BYTE),
|
||||
Operand::Register(1, TypeCode::BYTE),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(7, 11),
|
||||
Span(0, 11),
|
||||
Span(0, 11),
|
||||
Span(0, 11),
|
||||
Span(0, 11),
|
||||
Span(11, 11),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn less_characters() {
|
||||
let source = "'a' < 'b'";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::less(
|
||||
true,
|
||||
Operand::Constant(0, TypeCode::CHARACTER),
|
||||
Operand::Constant(1, TypeCode::CHARACTER),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![Span(0, 9), Span(0, 9), Span(0, 9), Span(0, 9), Span(9, 9)],
|
||||
character_constants: vec!['a', 'b'],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn less_floats() {
|
||||
let source = "10.0 < 3.0";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::less(
|
||||
true,
|
||||
Operand::Constant(0, TypeCode::FLOAT),
|
||||
Operand::Constant(1, TypeCode::FLOAT),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 10),
|
||||
Span(0, 10),
|
||||
Span(0, 10),
|
||||
Span(0, 10),
|
||||
Span(10, 10),
|
||||
],
|
||||
float_constants: vec![10.0, 3.0],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn less_integers() {
|
||||
let source = "10 < 3";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::less(
|
||||
true,
|
||||
Operand::Constant(0, TypeCode::INTEGER),
|
||||
Operand::Constant(1, TypeCode::INTEGER),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![Span(0, 6), Span(0, 6), Span(0, 6), Span(0, 6), Span(6, 6)],
|
||||
integer_constants: vec![10, 3],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn less_strings() {
|
||||
let source = "\"abc\" < \"def\"";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::less(
|
||||
true,
|
||||
Operand::Constant(0, TypeCode::STRING),
|
||||
Operand::Constant(1, TypeCode::STRING),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 13),
|
||||
Span(0, 13),
|
||||
Span(0, 13),
|
||||
Span(0, 13),
|
||||
Span(13, 13),
|
||||
],
|
||||
string_constants: vec![DustString::from("abc"), DustString::from("def")],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn less_lists() {
|
||||
let source = "[1, 2, 3] < [4, 5, 6]";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_constant(0, 0, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(1, 1, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(2, 2, TypeCode::INTEGER, false),
|
||||
Instruction::load_list(0, TypeCode::INTEGER, 0, 2, false),
|
||||
Instruction::load_constant(3, 3, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(4, 4, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(5, 5, TypeCode::INTEGER, false),
|
||||
Instruction::load_list(1, TypeCode::INTEGER, 3, 5, false),
|
||||
Instruction::less(
|
||||
true,
|
||||
Operand::Register(0, TypeCode::LIST),
|
||||
Operand::Register(1, TypeCode::LIST),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(1, 2),
|
||||
Span(4, 5),
|
||||
Span(7, 8),
|
||||
Span(0, 9),
|
||||
Span(13, 14),
|
||||
Span(16, 17),
|
||||
Span(19, 20),
|
||||
Span(12, 21),
|
||||
Span(0, 21),
|
||||
Span(0, 21),
|
||||
Span(0, 21),
|
||||
Span(0, 21),
|
||||
Span(21, 21),
|
||||
],
|
||||
integer_constants: vec![1, 2, 3, 4, 5, 6],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
244
dust-lang/tests/comparison/less_equal.rs
Normal file
244
dust-lang/tests/comparison/less_equal.rs
Normal file
@ -0,0 +1,244 @@
|
||||
use dust_lang::{
|
||||
compile, instruction::TypeCode, run, Chunk, DustString, FunctionType, Instruction, Operand,
|
||||
Span, Type, Value,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn less_equal_booleans() {
|
||||
let source = "true <= false";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::load_encoded(1, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::less_equal(
|
||||
true,
|
||||
Operand::Register(0, TypeCode::BOOLEAN),
|
||||
Operand::Register(1, TypeCode::BOOLEAN),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(2, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(2, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 2, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(8, 13),
|
||||
Span(0, 13),
|
||||
Span(0, 13),
|
||||
Span(0, 13),
|
||||
Span(0, 13),
|
||||
Span(13, 13),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn less_equal_bytes() {
|
||||
let source = "0x0A <= 0x03";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, 0x0A, TypeCode::BYTE, false),
|
||||
Instruction::load_encoded(1, 0x03, TypeCode::BYTE, false),
|
||||
Instruction::less_equal(
|
||||
true,
|
||||
Operand::Register(0, TypeCode::BYTE),
|
||||
Operand::Register(1, TypeCode::BYTE),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(8, 12),
|
||||
Span(0, 12),
|
||||
Span(0, 12),
|
||||
Span(0, 12),
|
||||
Span(0, 12),
|
||||
Span(12, 12),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn less_equal_characters() {
|
||||
let source = "'a' <= 'b'";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::less_equal(
|
||||
true,
|
||||
Operand::Constant(0, TypeCode::CHARACTER),
|
||||
Operand::Constant(1, TypeCode::CHARACTER),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 10),
|
||||
Span(0, 10),
|
||||
Span(0, 10),
|
||||
Span(0, 10),
|
||||
Span(10, 10),
|
||||
],
|
||||
character_constants: vec!['a', 'b'],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn less_equal_floats() {
|
||||
let source = "10.0 <= 3.0";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::less_equal(
|
||||
true,
|
||||
Operand::Constant(0, TypeCode::FLOAT),
|
||||
Operand::Constant(1, TypeCode::FLOAT),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 11),
|
||||
Span(0, 11),
|
||||
Span(0, 11),
|
||||
Span(0, 11),
|
||||
Span(11, 11),
|
||||
],
|
||||
float_constants: vec![10.0, 3.0],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn less_equal_integers() {
|
||||
let source = "10 <= 3";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::less_equal(
|
||||
true,
|
||||
Operand::Constant(0, TypeCode::INTEGER),
|
||||
Operand::Constant(1, TypeCode::INTEGER),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![Span(0, 7), Span(0, 7), Span(0, 7), Span(0, 7), Span(7, 7)],
|
||||
integer_constants: vec![10, 3],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn less_equal_strings() {
|
||||
let source = "\"abc\" <= \"def\"";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::less_equal(
|
||||
true,
|
||||
Operand::Constant(0, TypeCode::STRING),
|
||||
Operand::Constant(1, TypeCode::STRING),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 14),
|
||||
Span(0, 14),
|
||||
Span(0, 14),
|
||||
Span(0, 14),
|
||||
Span(14, 14),
|
||||
],
|
||||
string_constants: vec![DustString::from("abc"), DustString::from("def")],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn less_equal_lists() {
|
||||
let source = "[1, 2, 3] <= [4, 5, 6]";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_constant(0, 0, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(1, 1, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(2, 2, TypeCode::INTEGER, false),
|
||||
Instruction::load_list(0, TypeCode::INTEGER, 0, 2, false),
|
||||
Instruction::load_constant(3, 3, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(4, 4, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(5, 5, TypeCode::INTEGER, false),
|
||||
Instruction::load_list(1, TypeCode::INTEGER, 3, 5, false),
|
||||
Instruction::less_equal(
|
||||
true,
|
||||
Operand::Register(0, TypeCode::LIST),
|
||||
Operand::Register(1, TypeCode::LIST),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(1, 2),
|
||||
Span(4, 5),
|
||||
Span(7, 8),
|
||||
Span(0, 9),
|
||||
Span(14, 15),
|
||||
Span(17, 18),
|
||||
Span(20, 21),
|
||||
Span(13, 22),
|
||||
Span(0, 22),
|
||||
Span(0, 22),
|
||||
Span(0, 22),
|
||||
Span(0, 22),
|
||||
Span(22, 22),
|
||||
],
|
||||
integer_constants: vec![1, 2, 3, 4, 5, 6],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
244
dust-lang/tests/comparison/not_equal.rs
Normal file
244
dust-lang/tests/comparison/not_equal.rs
Normal file
@ -0,0 +1,244 @@
|
||||
use dust_lang::{
|
||||
compile, instruction::TypeCode, run, Chunk, DustString, FunctionType, Instruction, Operand,
|
||||
Span, Type, Value,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn not_equal_booleans() {
|
||||
let source = "true != false";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::load_encoded(1, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::equal(
|
||||
false,
|
||||
Operand::Register(0, TypeCode::BOOLEAN),
|
||||
Operand::Register(1, TypeCode::BOOLEAN),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(2, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(2, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 2, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(8, 13),
|
||||
Span(0, 13),
|
||||
Span(0, 13),
|
||||
Span(0, 13),
|
||||
Span(0, 13),
|
||||
Span(13, 13),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_equal_bytes() {
|
||||
let source = "0x0A != 0x03";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, 0x0A, TypeCode::BYTE, false),
|
||||
Instruction::load_encoded(1, 0x03, TypeCode::BYTE, false),
|
||||
Instruction::equal(
|
||||
false,
|
||||
Operand::Register(0, TypeCode::BYTE),
|
||||
Operand::Register(1, TypeCode::BYTE),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(8, 12),
|
||||
Span(0, 12),
|
||||
Span(0, 12),
|
||||
Span(0, 12),
|
||||
Span(0, 12),
|
||||
Span(12, 12),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_equal_characters() {
|
||||
let source = "'a' != 'b'";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::equal(
|
||||
false,
|
||||
Operand::Constant(0, TypeCode::CHARACTER),
|
||||
Operand::Constant(1, TypeCode::CHARACTER),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 10),
|
||||
Span(0, 10),
|
||||
Span(0, 10),
|
||||
Span(0, 10),
|
||||
Span(10, 10),
|
||||
],
|
||||
character_constants: vec!['a', 'b'],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_equal_floats() {
|
||||
let source = "10.0 != 3.0";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::equal(
|
||||
false,
|
||||
Operand::Constant(0, TypeCode::FLOAT),
|
||||
Operand::Constant(1, TypeCode::FLOAT),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 11),
|
||||
Span(0, 11),
|
||||
Span(0, 11),
|
||||
Span(0, 11),
|
||||
Span(11, 11),
|
||||
],
|
||||
float_constants: vec![10.0, 3.0],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_equal_integers() {
|
||||
let source = "10 != 3";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::equal(
|
||||
false,
|
||||
Operand::Constant(0, TypeCode::INTEGER),
|
||||
Operand::Constant(1, TypeCode::INTEGER),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![Span(0, 7), Span(0, 7), Span(0, 7), Span(0, 7), Span(7, 7)],
|
||||
integer_constants: vec![10, 3],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_equal_strings() {
|
||||
let source = "\"abc\" != \"def\"";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::equal(
|
||||
false,
|
||||
Operand::Constant(0, TypeCode::STRING),
|
||||
Operand::Constant(1, TypeCode::STRING),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 14),
|
||||
Span(0, 14),
|
||||
Span(0, 14),
|
||||
Span(0, 14),
|
||||
Span(14, 14),
|
||||
],
|
||||
string_constants: vec![DustString::from("abc"), DustString::from("def")],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_equal_lists() {
|
||||
let source = "[1, 2, 3] != [4, 5, 6]";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_constant(0, 0, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(1, 1, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(2, 2, TypeCode::INTEGER, false),
|
||||
Instruction::load_list(0, TypeCode::INTEGER, 0, 2, false),
|
||||
Instruction::load_constant(3, 3, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(4, 4, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(5, 5, TypeCode::INTEGER, false),
|
||||
Instruction::load_list(1, TypeCode::INTEGER, 3, 5, false),
|
||||
Instruction::equal(
|
||||
false,
|
||||
Operand::Register(0, TypeCode::LIST),
|
||||
Operand::Register(1, TypeCode::LIST),
|
||||
),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(1, 2),
|
||||
Span(4, 5),
|
||||
Span(7, 8),
|
||||
Span(0, 9),
|
||||
Span(14, 15),
|
||||
Span(17, 18),
|
||||
Span(20, 21),
|
||||
Span(13, 22),
|
||||
Span(0, 22),
|
||||
Span(0, 22),
|
||||
Span(0, 22),
|
||||
Span(0, 22),
|
||||
Span(22, 22),
|
||||
],
|
||||
integer_constants: vec![1, 2, 3, 4, 5, 6],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
@ -1,133 +0,0 @@
|
||||
use dust_lang::*;
|
||||
use smallvec::smallvec;
|
||||
|
||||
#[test]
|
||||
fn function() {
|
||||
let source = "fn(a: int, b: int) -> int { a + b }";
|
||||
|
||||
assert_eq!(
|
||||
run(source),
|
||||
Ok(Some(ConcreteValue::function(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::function(FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: Some(smallvec![(0, Type::Integer), (1, Type::Integer)]),
|
||||
return_type: Type::Integer,
|
||||
})
|
||||
},
|
||||
vec![
|
||||
(
|
||||
Instruction::add(2, Operand::Register(0), Operand::Register(1)),
|
||||
Span(30, 31)
|
||||
),
|
||||
(Instruction::r#return(true), Span(34, 35)),
|
||||
],
|
||||
vec![ConcreteValue::string("a"), ConcreteValue::string("b"),],
|
||||
vec![
|
||||
Local::new(0, 0, false, Scope::default()),
|
||||
Local::new(1, 1, false, Scope::default())
|
||||
]
|
||||
))))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_call() {
|
||||
let source = "fn(a: int, b: int) -> int { a + b }(1, 2)";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::Integer
|
||||
},
|
||||
vec![
|
||||
(Instruction::load_constant(0, 0, false), Span(0, 35)),
|
||||
(Instruction::load_constant(1, 1, false), Span(36, 37)),
|
||||
(Instruction::load_constant(2, 2, false), Span(39, 40)),
|
||||
(Instruction::call(3, Operand::Constant(0), 2), Span(35, 41)),
|
||||
(Instruction::r#return(true), Span(41, 41)),
|
||||
],
|
||||
vec![
|
||||
ConcreteValue::function(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: Some(smallvec![(0, Type::Integer), (1, Type::Integer)]),
|
||||
return_type: Type::Integer
|
||||
},
|
||||
vec![
|
||||
(
|
||||
Instruction::add(2, Operand::Register(0), Operand::Register(1)),
|
||||
Span(30, 31)
|
||||
),
|
||||
(Instruction::r#return(true), Span(34, 35)),
|
||||
],
|
||||
vec![ConcreteValue::string("a"), ConcreteValue::string("b"),],
|
||||
vec![
|
||||
Local::new(0, 0, false, Scope::default()),
|
||||
Local::new(1, 1, false, Scope::default())
|
||||
]
|
||||
)),
|
||||
ConcreteValue::Integer(1),
|
||||
ConcreteValue::Integer(2)
|
||||
],
|
||||
vec![]
|
||||
)),
|
||||
);
|
||||
|
||||
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(3))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_declaration() {
|
||||
let source = "fn add (a: int, b: int) -> int { a + b }";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::None
|
||||
},
|
||||
vec![
|
||||
(Instruction::load_constant(0, 0, false), Span(0, 40)),
|
||||
(Instruction::r#return(false), Span(40, 40))
|
||||
],
|
||||
vec![
|
||||
ConcreteValue::function(Chunk::with_data(
|
||||
Some("add".into()),
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: Some(smallvec![(0, Type::Integer), (1, Type::Integer)]),
|
||||
return_type: Type::Integer
|
||||
},
|
||||
vec![
|
||||
(
|
||||
Instruction::add(2, Operand::Register(0), Operand::Register(1)),
|
||||
Span(35, 36)
|
||||
),
|
||||
(Instruction::r#return(true), Span(39, 40)),
|
||||
],
|
||||
vec![ConcreteValue::string("a"), ConcreteValue::string("b")],
|
||||
vec![
|
||||
Local::new(0, 0, false, Scope::default()),
|
||||
Local::new(1, 1, false, Scope::default())
|
||||
]
|
||||
)),
|
||||
ConcreteValue::string("add"),
|
||||
],
|
||||
vec![Local::new(1, 0, false, Scope::default(),),],
|
||||
)),
|
||||
);
|
||||
|
||||
assert_eq!(run(source), Ok(None));
|
||||
}
|
@ -1,159 +0,0 @@
|
||||
use dust_lang::*;
|
||||
|
||||
#[test]
|
||||
fn empty_list() {
|
||||
let source = "[]";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::List(Box::new(Type::Any)),
|
||||
},
|
||||
vec![
|
||||
(Instruction::load_list(0, 0), Span(0, 2)),
|
||||
(Instruction::r#return(true), Span(2, 2)),
|
||||
],
|
||||
vec![],
|
||||
vec![]
|
||||
)),
|
||||
);
|
||||
|
||||
assert_eq!(run(source), Ok(Some(ConcreteValue::list([]))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list() {
|
||||
let source = "[1, 2, 3]";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::List(Box::new(Type::Integer)),
|
||||
},
|
||||
vec![
|
||||
(Instruction::load_constant(0, 0, false), Span(1, 2)),
|
||||
(Instruction::load_constant(1, 1, false), Span(4, 5)),
|
||||
(Instruction::load_constant(2, 2, false), Span(7, 8)),
|
||||
(Instruction::load_list(3, 0), Span(0, 9)),
|
||||
(Instruction::r#return(true), Span(9, 9)),
|
||||
],
|
||||
vec![
|
||||
ConcreteValue::Integer(1),
|
||||
ConcreteValue::Integer(2),
|
||||
ConcreteValue::Integer(3)
|
||||
],
|
||||
vec![]
|
||||
)),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
run(source),
|
||||
Ok(Some(ConcreteValue::list([
|
||||
ConcreteValue::Integer(1),
|
||||
ConcreteValue::Integer(2),
|
||||
ConcreteValue::Integer(3)
|
||||
])))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_with_complex_expression() {
|
||||
let source = "[1, 2 + 3 - 4 * 5]";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::List(Box::new(Type::Integer)),
|
||||
},
|
||||
vec![
|
||||
(Instruction::load_constant(0, 0, false), Span(1, 2)),
|
||||
(
|
||||
Instruction::add(1, Operand::Constant(1), Operand::Constant(2)),
|
||||
Span(6, 7)
|
||||
),
|
||||
(
|
||||
Instruction::multiply(2, Operand::Constant(3), Operand::Constant(4)),
|
||||
Span(14, 15)
|
||||
),
|
||||
(
|
||||
Instruction::subtract(3, Operand::Register(1), Operand::Register(2)),
|
||||
Span(10, 11)
|
||||
),
|
||||
(Instruction::close(1, 3), Span(17, 18)),
|
||||
(Instruction::load_list(4, 0), Span(0, 18)),
|
||||
(Instruction::r#return(true), Span(18, 18)),
|
||||
],
|
||||
vec![
|
||||
ConcreteValue::Integer(1),
|
||||
ConcreteValue::Integer(2),
|
||||
ConcreteValue::Integer(3),
|
||||
ConcreteValue::Integer(4),
|
||||
ConcreteValue::Integer(5)
|
||||
],
|
||||
vec![]
|
||||
)),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
run(source),
|
||||
Ok(Some(ConcreteValue::list([
|
||||
ConcreteValue::Integer(1),
|
||||
ConcreteValue::Integer(-15)
|
||||
])))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_with_simple_expression() {
|
||||
let source = "[1, 2 + 3, 4]";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::List(Box::new(Type::Integer)),
|
||||
},
|
||||
vec![
|
||||
(Instruction::load_constant(0, 0, false), Span(1, 2)),
|
||||
(
|
||||
Instruction::add(1, Operand::Constant(1), Operand::Constant(2)),
|
||||
Span(6, 7)
|
||||
),
|
||||
(Instruction::load_constant(2, 3, false), Span(11, 12)),
|
||||
(Instruction::load_list(3, 0), Span(0, 13)),
|
||||
(Instruction::r#return(true), Span(13, 13)),
|
||||
],
|
||||
vec![
|
||||
ConcreteValue::Integer(1),
|
||||
ConcreteValue::Integer(2),
|
||||
ConcreteValue::Integer(3),
|
||||
ConcreteValue::Integer(4),
|
||||
],
|
||||
vec![]
|
||||
)),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
run(source),
|
||||
Ok(Some(ConcreteValue::list([
|
||||
ConcreteValue::Integer(1),
|
||||
ConcreteValue::Integer(5),
|
||||
ConcreteValue::Integer(4),
|
||||
])))
|
||||
);
|
||||
}
|
@ -1,146 +1,111 @@
|
||||
use dust_lang::*;
|
||||
use smallvec::smallvec;
|
||||
use dust_lang::{
|
||||
Chunk, FunctionType, Instruction, Span, Type, Value, compile, instruction::TypeCode, run,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn true_and_true() {
|
||||
let source = "true && true";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::Boolean,
|
||||
},
|
||||
smallvec![
|
||||
Instruction::load_boolean(0, true, false),
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_boolean(1, true, false),
|
||||
Instruction::r#return(true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
smallvec![
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(5, 7),
|
||||
Span(5, 7),
|
||||
Span(8, 12),
|
||||
Span(12, 12),
|
||||
],
|
||||
smallvec![],
|
||||
smallvec![],
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(run(source), Ok(Some(Value::boolean(true))));
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn true_and_false() {
|
||||
let source = "true && false";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(5, 7),
|
||||
Span(5, 7),
|
||||
Span(8, 13),
|
||||
Span(13, 13),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn false_and_true() {
|
||||
let source = "false && true";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 5),
|
||||
Span(6, 8),
|
||||
Span(6, 8),
|
||||
Span(9, 13),
|
||||
Span(13, 13),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn false_and_false() {
|
||||
let source = "false && false";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::Boolean,
|
||||
},
|
||||
smallvec![
|
||||
Instruction::load_boolean(0, false, false),
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_boolean(1, false, false),
|
||||
Instruction::r#return(true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
smallvec![
|
||||
positions: vec![
|
||||
Span(0, 5),
|
||||
Span(6, 8),
|
||||
Span(6, 8),
|
||||
Span(9, 14),
|
||||
Span(14, 14),
|
||||
],
|
||||
smallvec![],
|
||||
smallvec![],
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(run(source), Ok(Some(Value::boolean(false))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn false_and_true() {
|
||||
let source = "false && true";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::Boolean,
|
||||
},
|
||||
smallvec![
|
||||
Instruction::load_boolean(0, false, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_boolean(1, true, false),
|
||||
Instruction::r#return(true),
|
||||
],
|
||||
smallvec![
|
||||
Span(0, 5),
|
||||
Span(6, 8),
|
||||
Span(6, 8),
|
||||
Span(9, 13),
|
||||
Span(13, 13)
|
||||
],
|
||||
smallvec![],
|
||||
smallvec![],
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
|
||||
assert_eq!(run(source), Ok(Some(Value::boolean(false))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn true_and_false() {
|
||||
let source = "true && false";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::Boolean,
|
||||
},
|
||||
smallvec![
|
||||
Instruction::load_boolean(0, true, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_boolean(1, false, false),
|
||||
Instruction::r#return(true),
|
||||
],
|
||||
smallvec![
|
||||
Span(0, 4),
|
||||
Span(5, 7),
|
||||
Span(5, 7),
|
||||
Span(8, 13),
|
||||
Span(13, 13)
|
||||
],
|
||||
smallvec![],
|
||||
smallvec![],
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
|
||||
assert_eq!(run(source), Ok(Some(Value::boolean(false))));
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
@ -1,30 +1,23 @@
|
||||
use dust_lang::*;
|
||||
use smallvec::smallvec;
|
||||
use dust_lang::{
|
||||
Chunk, FunctionType, Instruction, Span, Type, Value, compile, instruction::TypeCode, run,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn true_and_true_and_true() {
|
||||
let source = "true && true && true";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::Boolean,
|
||||
},
|
||||
smallvec![
|
||||
Instruction::load_boolean(0, true, false),
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_boolean(1, true, false),
|
||||
Instruction::test(1, true),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_boolean(2, true, false),
|
||||
Instruction::r#return(true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
smallvec![
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(5, 7),
|
||||
Span(5, 7),
|
||||
@ -32,11 +25,243 @@ fn true_and_true_and_true() {
|
||||
Span(13, 15),
|
||||
Span(13, 15),
|
||||
Span(16, 20),
|
||||
Span(20, 20)
|
||||
Span(20, 20),
|
||||
],
|
||||
smallvec![],
|
||||
smallvec![],
|
||||
vec![],
|
||||
))
|
||||
);
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn true_and_true_and_false() {
|
||||
let source = "true && true && false";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(5, 7),
|
||||
Span(5, 7),
|
||||
Span(8, 12),
|
||||
Span(13, 15),
|
||||
Span(13, 15),
|
||||
Span(16, 21),
|
||||
Span(21, 21),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn true_and_false_and_true() {
|
||||
let source = "true && false && true";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(5, 7),
|
||||
Span(5, 7),
|
||||
Span(8, 13),
|
||||
Span(14, 16),
|
||||
Span(14, 16),
|
||||
Span(17, 21),
|
||||
Span(21, 21),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn true_and_false_and_false() {
|
||||
let source = "true && false && false";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(5, 7),
|
||||
Span(5, 7),
|
||||
Span(8, 13),
|
||||
Span(14, 16),
|
||||
Span(14, 16),
|
||||
Span(17, 22),
|
||||
Span(22, 22),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn false_and_true_and_true() {
|
||||
let source = "false && true && true";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 5),
|
||||
Span(6, 8),
|
||||
Span(6, 8),
|
||||
Span(9, 13),
|
||||
Span(14, 16),
|
||||
Span(14, 16),
|
||||
Span(17, 21),
|
||||
Span(21, 21),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn false_and_true_and_false() {
|
||||
let source = "false && true && false";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 5),
|
||||
Span(6, 8),
|
||||
Span(6, 8),
|
||||
Span(9, 13),
|
||||
Span(14, 16),
|
||||
Span(14, 16),
|
||||
Span(17, 22),
|
||||
Span(22, 22),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn false_and_false_and_true() {
|
||||
let source = "false && false && true";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 5),
|
||||
Span(6, 8),
|
||||
Span(6, 8),
|
||||
Span(9, 14),
|
||||
Span(15, 17),
|
||||
Span(15, 17),
|
||||
Span(18, 22),
|
||||
Span(22, 22),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn false_and_false_and_false() {
|
||||
let source = "false && false && false";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 5),
|
||||
Span(6, 8),
|
||||
Span(6, 8),
|
||||
Span(9, 14),
|
||||
Span(15, 17),
|
||||
Span(15, 17),
|
||||
Span(18, 23),
|
||||
Span(23, 23),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
267
dust-lang/tests/logic/and_or.rs
Normal file
267
dust-lang/tests/logic/and_or.rs
Normal file
@ -0,0 +1,267 @@
|
||||
use dust_lang::{
|
||||
Chunk, FunctionType, Instruction, Span, Type, Value, compile, instruction::TypeCode, run,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn true_and_true_or_true() {
|
||||
let source = "true && true || true";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(5, 7),
|
||||
Span(5, 7),
|
||||
Span(8, 12),
|
||||
Span(13, 15),
|
||||
Span(13, 15),
|
||||
Span(16, 20),
|
||||
Span(20, 20),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn true_and_false_or_true() {
|
||||
let source = "true && false || true";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(5, 7),
|
||||
Span(5, 7),
|
||||
Span(8, 13),
|
||||
Span(14, 16),
|
||||
Span(14, 16),
|
||||
Span(17, 21),
|
||||
Span(21, 21),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn true_and_true_or_false() {
|
||||
let source = "true && true || false";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(5, 7),
|
||||
Span(5, 7),
|
||||
Span(8, 12),
|
||||
Span(13, 15),
|
||||
Span(13, 15),
|
||||
Span(16, 21),
|
||||
Span(21, 21),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn true_and_false_or_false() {
|
||||
let source = "true && false || false";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(5, 7),
|
||||
Span(5, 7),
|
||||
Span(8, 13),
|
||||
Span(14, 16),
|
||||
Span(14, 16),
|
||||
Span(17, 22),
|
||||
Span(22, 22),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn false_and_true_or_true() {
|
||||
let source = "false && true || true";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 5),
|
||||
Span(6, 8),
|
||||
Span(6, 8),
|
||||
Span(9, 13),
|
||||
Span(14, 16),
|
||||
Span(14, 16),
|
||||
Span(17, 21),
|
||||
Span(21, 21),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn false_and_false_or_true() {
|
||||
let source = "false && false || true";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 5),
|
||||
Span(6, 8),
|
||||
Span(6, 8),
|
||||
Span(9, 14),
|
||||
Span(15, 17),
|
||||
Span(15, 17),
|
||||
Span(18, 22),
|
||||
Span(22, 22),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn false_and_true_or_false() {
|
||||
let source = "false && true || false";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 5),
|
||||
Span(6, 8),
|
||||
Span(6, 8),
|
||||
Span(9, 13),
|
||||
Span(14, 16),
|
||||
Span(14, 16),
|
||||
Span(17, 22),
|
||||
Span(22, 22),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn false_and_false_or_false() {
|
||||
let source = "false && false || false";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 5),
|
||||
Span(6, 8),
|
||||
Span(6, 8),
|
||||
Span(9, 14),
|
||||
Span(15, 17),
|
||||
Span(15, 17),
|
||||
Span(18, 23),
|
||||
Span(23, 23),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
@ -1,38 +1,111 @@
|
||||
use dust_lang::*;
|
||||
use smallvec::smallvec;
|
||||
use dust_lang::{
|
||||
Chunk, FunctionType, Instruction, Span, Type, Value, compile, instruction::TypeCode, run,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn true_or_true() {
|
||||
let source = "true || true";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(5, 7),
|
||||
Span(5, 7),
|
||||
Span(8, 12),
|
||||
Span(12, 12),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn true_or_false() {
|
||||
let source = "true || false";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::Boolean,
|
||||
},
|
||||
smallvec![
|
||||
Instruction::load_boolean(0, true, false),
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_boolean(1, false, false),
|
||||
Instruction::r#return(true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
smallvec![
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(5, 7),
|
||||
Span(5, 7),
|
||||
Span(8, 13),
|
||||
Span(13, 13),
|
||||
],
|
||||
smallvec![],
|
||||
smallvec![],
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(run(source), Ok(Some(Value::boolean(true))));
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn false_or_true() {
|
||||
let source = "false || true";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 5),
|
||||
Span(6, 8),
|
||||
Span(6, 8),
|
||||
Span(9, 13),
|
||||
Span(13, 13),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn false_or_false() {
|
||||
let source = "false || false";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 5),
|
||||
Span(6, 8),
|
||||
Span(6, 8),
|
||||
Span(9, 14),
|
||||
Span(14, 14),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
266
dust-lang/tests/logic/or_and.rs
Normal file
266
dust-lang/tests/logic/or_and.rs
Normal file
@ -0,0 +1,266 @@
|
||||
use dust_lang::{
|
||||
Chunk, FunctionType, Instruction, Span, Type, Value, compile, instruction::TypeCode, run,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn true_or_true_and_true() {
|
||||
let source = "true || true && true";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(5, 7),
|
||||
Span(5, 7),
|
||||
Span(8, 12),
|
||||
Span(13, 15),
|
||||
Span(13, 15),
|
||||
Span(16, 20),
|
||||
Span(20, 20),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn true_or_false_or_true() {
|
||||
let source = "true || false || true";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(5, 7),
|
||||
Span(5, 7),
|
||||
Span(8, 13),
|
||||
Span(14, 16),
|
||||
Span(14, 16),
|
||||
Span(17, 21),
|
||||
Span(21, 21),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn true_or_true_and_false() {
|
||||
let source = "true || true && false";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(5, 7),
|
||||
Span(5, 7),
|
||||
Span(8, 12),
|
||||
Span(13, 15),
|
||||
Span(13, 15),
|
||||
Span(16, 21),
|
||||
Span(21, 21),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn true_or_false_and_false() {
|
||||
let source = "true || false && false";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(5, 7),
|
||||
Span(5, 7),
|
||||
Span(8, 13),
|
||||
Span(14, 16),
|
||||
Span(14, 16),
|
||||
Span(17, 22),
|
||||
Span(22, 22),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
#[test]
|
||||
fn false_or_true_and_true() {
|
||||
let source = "false || true && true";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 5),
|
||||
Span(6, 8),
|
||||
Span(6, 8),
|
||||
Span(9, 13),
|
||||
Span(14, 16),
|
||||
Span(14, 16),
|
||||
Span(17, 21),
|
||||
Span(21, 21),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn false_or_false_and_true() {
|
||||
let source = "false || false && true";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 5),
|
||||
Span(6, 8),
|
||||
Span(6, 8),
|
||||
Span(9, 14),
|
||||
Span(15, 17),
|
||||
Span(15, 17),
|
||||
Span(18, 22),
|
||||
Span(22, 22),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn false_or_true_and_false() {
|
||||
let source = "false || true && false";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 5),
|
||||
Span(6, 8),
|
||||
Span(6, 8),
|
||||
Span(9, 13),
|
||||
Span(14, 16),
|
||||
Span(14, 16),
|
||||
Span(17, 22),
|
||||
Span(22, 22),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn false_or_false_and_false() {
|
||||
let source = "false || false && false";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, true),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 5),
|
||||
Span(6, 8),
|
||||
Span(6, 8),
|
||||
Span(9, 14),
|
||||
Span(15, 17),
|
||||
Span(15, 17),
|
||||
Span(18, 23),
|
||||
Span(23, 23),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
267
dust-lang/tests/logic/or_or.rs
Normal file
267
dust-lang/tests/logic/or_or.rs
Normal file
@ -0,0 +1,267 @@
|
||||
use dust_lang::{
|
||||
Chunk, FunctionType, Instruction, Span, Type, Value, compile, instruction::TypeCode, run,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn true_or_true_or_true() {
|
||||
let source = "true || true || true";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(5, 7),
|
||||
Span(5, 7),
|
||||
Span(8, 12),
|
||||
Span(13, 15),
|
||||
Span(13, 15),
|
||||
Span(16, 20),
|
||||
Span(20, 20),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn true_or_true_or_false() {
|
||||
let source = "true || true || false";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(5, 7),
|
||||
Span(5, 7),
|
||||
Span(8, 12),
|
||||
Span(13, 15),
|
||||
Span(13, 15),
|
||||
Span(16, 21),
|
||||
Span(21, 21),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn true_or_false_or_true() {
|
||||
let source = "true || false || true";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(5, 7),
|
||||
Span(5, 7),
|
||||
Span(8, 13),
|
||||
Span(14, 16),
|
||||
Span(14, 16),
|
||||
Span(17, 21),
|
||||
Span(21, 21),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn true_or_false_or_false() {
|
||||
let source = "true || false || false";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(5, 7),
|
||||
Span(5, 7),
|
||||
Span(8, 13),
|
||||
Span(14, 16),
|
||||
Span(14, 16),
|
||||
Span(17, 22),
|
||||
Span(22, 22),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn false_or_true_or_true() {
|
||||
let source = "false || true || true";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 5),
|
||||
Span(6, 8),
|
||||
Span(6, 8),
|
||||
Span(9, 13),
|
||||
Span(14, 16),
|
||||
Span(14, 16),
|
||||
Span(17, 21),
|
||||
Span(21, 21),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn false_or_true_or_false() {
|
||||
let source = "false || true || false";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 5),
|
||||
Span(6, 8),
|
||||
Span(6, 8),
|
||||
Span(9, 13),
|
||||
Span(14, 16),
|
||||
Span(14, 16),
|
||||
Span(17, 22),
|
||||
Span(22, 22),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn false_or_false_or_true() {
|
||||
let source = "false || false || true";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 5),
|
||||
Span(6, 8),
|
||||
Span(6, 8),
|
||||
Span(9, 14),
|
||||
Span(15, 17),
|
||||
Span(15, 17),
|
||||
Span(18, 22),
|
||||
Span(22, 22),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(true));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn false_or_false_or_false() {
|
||||
let source = "false || false || false";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Boolean),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(4, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::test(0, false),
|
||||
Instruction::jump(1, true),
|
||||
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 5),
|
||||
Span(6, 8),
|
||||
Span(6, 8),
|
||||
Span(9, 14),
|
||||
Span(15, 17),
|
||||
Span(15, 17),
|
||||
Span(18, 23),
|
||||
Span(23, 23),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::boolean(false));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
use dust_lang::*;
|
||||
|
||||
#[test]
|
||||
fn r#while() {
|
||||
let source = "let mut x = 0; while x < 5 { x = x + 1 } x";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::Integer,
|
||||
},
|
||||
vec![
|
||||
(Instruction::load_constant(0, 0, false), Span(12, 13)),
|
||||
(
|
||||
Instruction::less(0, true, Operand::Register(0), Operand::Constant(2)),
|
||||
Span(23, 24)
|
||||
),
|
||||
(Instruction::jump(2, true), Span(41, 42)),
|
||||
(
|
||||
Instruction::add(0, Operand::Register(0), Operand::Constant(3)),
|
||||
Span(35, 36)
|
||||
),
|
||||
(Instruction::jump(3, false), Span(41, 42)),
|
||||
(Instruction::get_local(1, 0), Span(41, 42)),
|
||||
(Instruction::r#return(true), Span(42, 42)),
|
||||
],
|
||||
vec![
|
||||
ConcreteValue::Integer(0),
|
||||
ConcreteValue::string("x"),
|
||||
ConcreteValue::Integer(5),
|
||||
ConcreteValue::Integer(1),
|
||||
],
|
||||
vec![Local::new(1, 0, true, Scope::default())]
|
||||
)),
|
||||
);
|
||||
|
||||
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(5))));
|
||||
}
|
@ -1,270 +1,302 @@
|
||||
use dust_lang::*;
|
||||
use smallvec::smallvec;
|
||||
use dust_lang::{
|
||||
compile, instruction::TypeCode, run, Chunk, DustString, FunctionType, Instruction, Operand,
|
||||
Span, Type, Value,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn add_bytes() {
|
||||
let source = "0xfe + 0x01";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::Byte,
|
||||
},
|
||||
smallvec![
|
||||
Instruction::add(0, Argument::Constant(0), Argument::Constant(1)),
|
||||
Instruction::r#return(true),
|
||||
let source = "0x28 + 0x02";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Byte),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, 40, TypeCode::BYTE, false),
|
||||
Instruction::load_encoded(1, 2, TypeCode::BYTE, false),
|
||||
Instruction::add(
|
||||
2,
|
||||
Operand::Register(0, TypeCode::BYTE),
|
||||
Operand::Register(1, TypeCode::BYTE),
|
||||
),
|
||||
Instruction::r#return(true, 2, TypeCode::BYTE),
|
||||
],
|
||||
smallvec![Span(5, 6), Span(11, 11),],
|
||||
smallvec![Value::byte(0xfe), Value::byte(0x01)],
|
||||
smallvec![],
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
positions: vec![Span(0, 4), Span(7, 11), Span(0, 11), Span(11, 11)],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::byte(0x2A));
|
||||
|
||||
assert_eq!(run(source), Ok(Some(Value::byte(0xff))));
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_bytes_saturate() {
|
||||
let source = "0xff + 0x01";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::Byte,
|
||||
},
|
||||
smallvec![
|
||||
Instruction::add(0, Argument::Constant(0), Argument::Constant(1)),
|
||||
Instruction::r#return(true),
|
||||
fn add_many_bytes() {
|
||||
let source = "0x28 + 0x02 + 0x02 + 0x02";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Byte),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, 40, TypeCode::BYTE, false),
|
||||
Instruction::load_encoded(1, 2, TypeCode::BYTE, false),
|
||||
Instruction::add(
|
||||
2,
|
||||
Operand::Register(0, TypeCode::BYTE),
|
||||
Operand::Register(1, TypeCode::BYTE),
|
||||
),
|
||||
Instruction::load_encoded(3, 2, TypeCode::BYTE, false),
|
||||
Instruction::add(
|
||||
4,
|
||||
Operand::Register(2, TypeCode::BYTE),
|
||||
Operand::Register(3, TypeCode::BYTE),
|
||||
),
|
||||
Instruction::load_encoded(5, 2, TypeCode::BYTE, false),
|
||||
Instruction::add(
|
||||
6,
|
||||
Operand::Register(4, TypeCode::BYTE),
|
||||
Operand::Register(5, TypeCode::BYTE),
|
||||
),
|
||||
Instruction::r#return(true, 6, TypeCode::BYTE),
|
||||
],
|
||||
smallvec![Span(5, 6), Span(11, 11)],
|
||||
smallvec![Value::byte(0xff), Value::byte(0x01)],
|
||||
smallvec![],
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(7, 11),
|
||||
Span(0, 11),
|
||||
Span(14, 18),
|
||||
Span(0, 18),
|
||||
Span(21, 25),
|
||||
Span(0, 25),
|
||||
Span(25, 25),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::byte(46));
|
||||
|
||||
assert_eq!(run(source), Ok(Some(Value::byte(0xff))));
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_characters() {
|
||||
let source = "'a' + 'b'";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::String,
|
||||
},
|
||||
smallvec![
|
||||
Instruction::add(0, Argument::Constant(0), Argument::Constant(1)),
|
||||
Instruction::r#return(true)
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::String),
|
||||
instructions: vec![
|
||||
Instruction::add(
|
||||
0,
|
||||
Operand::Constant(0, TypeCode::CHARACTER),
|
||||
Operand::Constant(1, TypeCode::CHARACTER),
|
||||
),
|
||||
Instruction::r#return(true, 0, TypeCode::STRING),
|
||||
],
|
||||
smallvec![Span(4, 5), Span(11, 11)],
|
||||
smallvec![Value::character('a'), Value::character('b')],
|
||||
smallvec![],
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
positions: vec![Span(0, 9), Span(9, 9)],
|
||||
character_constants: vec!['a', 'b'],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::string("ab"));
|
||||
|
||||
assert_eq!(run(source), Ok(Some(Value::string("ab"))));
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_character_and_string() {
|
||||
let source = "'a' + \"b\"";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::String,
|
||||
},
|
||||
smallvec![
|
||||
Instruction::add(0, Argument::Constant(0), Argument::Constant(1)),
|
||||
Instruction::r#return(true),
|
||||
fn add_many_characters() {
|
||||
let source = "'a' + 'b' + 'c' + 'd'";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::String),
|
||||
instructions: vec![
|
||||
Instruction::add(
|
||||
0,
|
||||
Operand::Constant(0, TypeCode::CHARACTER),
|
||||
Operand::Constant(1, TypeCode::CHARACTER),
|
||||
),
|
||||
Instruction::add(
|
||||
1,
|
||||
Operand::Register(0, TypeCode::STRING),
|
||||
Operand::Constant(2, TypeCode::CHARACTER),
|
||||
),
|
||||
Instruction::add(
|
||||
2,
|
||||
Operand::Register(1, TypeCode::STRING),
|
||||
Operand::Constant(3, TypeCode::CHARACTER),
|
||||
),
|
||||
Instruction::r#return(true, 2, TypeCode::STRING),
|
||||
],
|
||||
smallvec![Span(4, 5), Span(9, 9),],
|
||||
smallvec![Value::character('a'), Value::string("b")],
|
||||
smallvec![],
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
positions: vec![Span(0, 9), Span(0, 15), Span(0, 21), Span(21, 21)],
|
||||
character_constants: vec!['a', 'b', 'c', 'd'],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::string("abcd"));
|
||||
|
||||
assert_eq!(run(source), Ok(Some(Value::string("ab"))));
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_floats() {
|
||||
let source = "1.0 + 2.0";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::Float,
|
||||
},
|
||||
smallvec![
|
||||
Instruction::add(0, Argument::Constant(0), Argument::Constant(1)),
|
||||
Instruction::r#return(true),
|
||||
let source = "2.40 + 40.02";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Float),
|
||||
instructions: vec![
|
||||
Instruction::add(
|
||||
0,
|
||||
Operand::Constant(0, TypeCode::FLOAT),
|
||||
Operand::Constant(1, TypeCode::FLOAT),
|
||||
),
|
||||
Instruction::r#return(true, 0, TypeCode::FLOAT),
|
||||
],
|
||||
smallvec![Span(4, 5), Span(9, 9),],
|
||||
smallvec![Value::float(1.0), Value::float(2.0)],
|
||||
smallvec![],
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
positions: vec![Span(0, 12), Span(12, 12)],
|
||||
float_constants: vec![2.40, 40.02],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::float(42.42));
|
||||
|
||||
assert_eq!(run(source), Ok(Some(Value::float(3.0))));
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_floats_saturatate() {
|
||||
let source = "1.7976931348623157E+308 + 0.00000001";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::Float,
|
||||
},
|
||||
smallvec![
|
||||
Instruction::add(0, Argument::Constant(0), Argument::Constant(1)),
|
||||
Instruction::r#return(true),
|
||||
fn add_many_floats() {
|
||||
let source = "2.40 + 40.02 + 2.40 + 40.02";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Float),
|
||||
instructions: vec![
|
||||
Instruction::add(
|
||||
0,
|
||||
Operand::Constant(0, TypeCode::FLOAT),
|
||||
Operand::Constant(1, TypeCode::FLOAT),
|
||||
),
|
||||
Instruction::add(
|
||||
1,
|
||||
Operand::Register(0, TypeCode::FLOAT),
|
||||
Operand::Constant(0, TypeCode::FLOAT),
|
||||
),
|
||||
Instruction::add(
|
||||
2,
|
||||
Operand::Register(1, TypeCode::FLOAT),
|
||||
Operand::Constant(1, TypeCode::FLOAT),
|
||||
),
|
||||
Instruction::r#return(true, 2, TypeCode::FLOAT),
|
||||
],
|
||||
smallvec![Span(24, 25), Span(36, 36),],
|
||||
smallvec![Value::float(f64::MAX), Value::float(0.00000001)],
|
||||
smallvec![],
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
positions: vec![Span(0, 12), Span(0, 19), Span(0, 27), Span(27, 27)],
|
||||
float_constants: vec![2.40, 40.02],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::float(84.84));
|
||||
|
||||
assert_eq!(run(source), Ok(Some(Value::float(f64::MAX))));
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_integers() {
|
||||
let source = "1 + 2";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::Integer,
|
||||
},
|
||||
smallvec![
|
||||
Instruction::add(0, Argument::Constant(0), Argument::Constant(1)),
|
||||
Instruction::r#return(true)
|
||||
let source = "40 + 2";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Integer),
|
||||
instructions: vec![
|
||||
Instruction::add(
|
||||
0,
|
||||
Operand::Constant(0, TypeCode::INTEGER),
|
||||
Operand::Constant(1, TypeCode::INTEGER),
|
||||
),
|
||||
Instruction::r#return(true, 0, TypeCode::INTEGER),
|
||||
],
|
||||
smallvec![Span(2, 3), Span(5, 5),],
|
||||
smallvec![Value::integer(1), Value::integer(2)],
|
||||
smallvec![],
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
positions: vec![Span(0, 6), Span(6, 6)],
|
||||
integer_constants: vec![40, 2],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::integer(42));
|
||||
|
||||
assert_eq!(run(source), Ok(Some(Value::integer(3))));
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_integers_saturate() {
|
||||
let source = "9223372036854775807 + 1";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::Integer,
|
||||
},
|
||||
smallvec![
|
||||
Instruction::add(0, Argument::Constant(0), Argument::Constant(1)),
|
||||
Instruction::r#return(true)
|
||||
fn add_many_integers() {
|
||||
let source = "40 + 2 + 40 + 2";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Integer),
|
||||
instructions: vec![
|
||||
Instruction::add(
|
||||
0,
|
||||
Operand::Constant(0, TypeCode::INTEGER),
|
||||
Operand::Constant(1, TypeCode::INTEGER),
|
||||
),
|
||||
Instruction::add(
|
||||
1,
|
||||
Operand::Register(0, TypeCode::INTEGER),
|
||||
Operand::Constant(0, TypeCode::INTEGER),
|
||||
),
|
||||
Instruction::add(
|
||||
2,
|
||||
Operand::Register(1, TypeCode::INTEGER),
|
||||
Operand::Constant(1, TypeCode::INTEGER),
|
||||
),
|
||||
Instruction::r#return(true, 2, TypeCode::INTEGER),
|
||||
],
|
||||
smallvec![Span(20, 21), Span(23, 23),],
|
||||
smallvec![Value::integer(i64::MAX), Value::integer(1)],
|
||||
smallvec![],
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
positions: vec![Span(0, 6), Span(0, 11), Span(0, 15), Span(15, 15)],
|
||||
integer_constants: vec![40, 2],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::integer(84));
|
||||
|
||||
assert_eq!(run(source), Ok(Some(Value::integer(i64::MAX))));
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_strings() {
|
||||
let source = "\"Hello, \" + \"world!\"";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::String,
|
||||
},
|
||||
smallvec![
|
||||
Instruction::add(0, Argument::Constant(0), Argument::Constant(1)),
|
||||
Instruction::r#return(true),
|
||||
let source = "\"Hello, \" + \"World!\"";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::String),
|
||||
instructions: vec![
|
||||
Instruction::add(
|
||||
0,
|
||||
Operand::Constant(0, TypeCode::STRING),
|
||||
Operand::Constant(1, TypeCode::STRING),
|
||||
),
|
||||
Instruction::r#return(true, 0, TypeCode::STRING),
|
||||
],
|
||||
smallvec![Span(10, 11), Span(20, 20)],
|
||||
smallvec![Value::string("Hello, "), Value::string("world!")],
|
||||
smallvec![],
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
positions: vec![Span(0, 20), Span(20, 20)],
|
||||
string_constants: vec![DustString::from("Hello, "), DustString::from("World!")],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::string("Hello, World!"));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_string_and_character() {
|
||||
let source = "\"a\" + 'b'";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::String,
|
||||
},
|
||||
smallvec![
|
||||
Instruction::add(0, Argument::Constant(0), Argument::Constant(1)),
|
||||
Instruction::r#return(true),
|
||||
fn add_many_strings() {
|
||||
let source = "\"foo\" + \"bar\" + \"baz\" + \"buzz\"";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::String),
|
||||
instructions: vec![
|
||||
Instruction::add(
|
||||
0,
|
||||
Operand::Constant(0, TypeCode::STRING),
|
||||
Operand::Constant(1, TypeCode::STRING),
|
||||
),
|
||||
Instruction::add(
|
||||
1,
|
||||
Operand::Register(0, TypeCode::STRING),
|
||||
Operand::Constant(2, TypeCode::STRING),
|
||||
),
|
||||
Instruction::add(
|
||||
2,
|
||||
Operand::Register(1, TypeCode::STRING),
|
||||
Operand::Constant(3, TypeCode::STRING),
|
||||
),
|
||||
Instruction::r#return(true, 2, TypeCode::STRING),
|
||||
],
|
||||
smallvec![Span(4, 5), Span(9, 9),],
|
||||
smallvec![Value::string("a"), Value::character('b')],
|
||||
smallvec![],
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
positions: vec![Span(0, 13), Span(0, 21), Span(0, 30), Span(30, 30)],
|
||||
string_constants: vec![
|
||||
DustString::from("foo"),
|
||||
DustString::from("bar"),
|
||||
DustString::from("baz"),
|
||||
DustString::from("buzz"),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::string("foobarbazbuzz"));
|
||||
|
||||
assert_eq!(run(source), Ok(Some(Value::string("ab"))));
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
@ -1,31 +0,0 @@
|
||||
use dust_lang::*;
|
||||
use smallvec::smallvec;
|
||||
|
||||
#[test]
|
||||
fn add_assign() {
|
||||
let source = "let mut a = 1; a += 2; a";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::Integer,
|
||||
},
|
||||
smallvec![
|
||||
Instruction::load_constant(0, 0, false),
|
||||
Instruction::add(0, Argument::Register(0), Argument::Constant(2)),
|
||||
Instruction::get_local(1, 0),
|
||||
Instruction::r#return(true)
|
||||
],
|
||||
smallvec![Span(12, 13), Span(17, 19), Span(23, 24), Span(24, 24)],
|
||||
smallvec![Value::integer(1), Value::string("a"), Value::integer(2)],
|
||||
smallvec![Local::new(1, 0, true, Scope::default())],
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
|
||||
assert_eq!(run(source), Ok(Some(Value::integer(3))));
|
||||
}
|
@ -1,405 +0,0 @@
|
||||
use dust_lang::*;
|
||||
|
||||
#[test]
|
||||
fn add_boolean_left() {
|
||||
let source = "true + 1";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Err(DustError::Compile {
|
||||
error: CompileError::CannotAddType {
|
||||
argument_type: Type::Boolean,
|
||||
position: Span(0, 4)
|
||||
},
|
||||
source,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_boolean_right() {
|
||||
let source = "1 + true";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Err(DustError::Compile {
|
||||
error: CompileError::CannotAddType {
|
||||
argument_type: Type::Boolean,
|
||||
position: Span(4, 8)
|
||||
},
|
||||
source,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_function_left() {
|
||||
let source = "fn(){} + 1";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Err(DustError::Compile {
|
||||
error: CompileError::CannotAddType {
|
||||
argument_type: Type::function(FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::None
|
||||
}),
|
||||
position: Span(0, 6)
|
||||
},
|
||||
source,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_function_right() {
|
||||
let source = "1 + fn(){}";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Err(DustError::Compile {
|
||||
error: CompileError::CannotAddType {
|
||||
argument_type: Type::function(FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::None
|
||||
}),
|
||||
position: Span(4, 10)
|
||||
},
|
||||
source,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_list_left() {
|
||||
let source = "[1, 2] + 1";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Err(DustError::Compile {
|
||||
error: CompileError::CannotAddType {
|
||||
argument_type: Type::List(Box::new(Type::Integer)),
|
||||
position: Span(0, 6)
|
||||
},
|
||||
source,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_list_right() {
|
||||
let source = "1 + [1, 2]";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Err(DustError::Compile {
|
||||
error: CompileError::CannotAddType {
|
||||
argument_type: Type::List(Box::new(Type::Integer)),
|
||||
position: Span(4, 10)
|
||||
},
|
||||
source,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn add_range_left() {
|
||||
// todo!("Add ranges")
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn add_range_right() {
|
||||
// todo!("Add ranges")
|
||||
// }
|
||||
//
|
||||
|
||||
#[test]
|
||||
fn add_byte_and_character() {
|
||||
let source = "0xff + 'a'";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Err(DustError::Compile {
|
||||
error: CompileError::CannotAddArguments {
|
||||
left_type: Type::Byte,
|
||||
right_type: Type::Character,
|
||||
position: Span(0, 10)
|
||||
},
|
||||
source,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_byte_and_integer() {
|
||||
let source = "0xff + 1";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Err(DustError::Compile {
|
||||
error: CompileError::CannotAddArguments {
|
||||
left_type: Type::Byte,
|
||||
right_type: Type::Integer,
|
||||
position: Span(0, 8)
|
||||
},
|
||||
source,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_byte_and_string() {
|
||||
let source = "0xff + \"hello\"";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Err(DustError::Compile {
|
||||
error: CompileError::CannotAddArguments {
|
||||
left_type: Type::Byte,
|
||||
right_type: Type::String,
|
||||
position: Span(0, 14)
|
||||
},
|
||||
source,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_character_and_byte() {
|
||||
let source = "'a' + 0xff";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Err(DustError::Compile {
|
||||
error: CompileError::CannotAddArguments {
|
||||
left_type: Type::Character,
|
||||
right_type: Type::Byte,
|
||||
position: Span(0, 10)
|
||||
},
|
||||
source,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_character_and_float() {
|
||||
let source = "'a' + 1.0";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Err(DustError::Compile {
|
||||
error: CompileError::CannotAddArguments {
|
||||
left_type: Type::Character,
|
||||
right_type: Type::Float,
|
||||
position: Span(0, 9)
|
||||
},
|
||||
source,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_character_and_integer() {
|
||||
let source = "'a' + 1";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Err(DustError::Compile {
|
||||
error: CompileError::CannotAddArguments {
|
||||
left_type: Type::Character,
|
||||
right_type: Type::Integer,
|
||||
position: Span(0, 7)
|
||||
},
|
||||
source,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_float_and_byte() {
|
||||
let source = "1.0 + 0xff";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Err(DustError::Compile {
|
||||
error: CompileError::CannotAddArguments {
|
||||
left_type: Type::Float,
|
||||
right_type: Type::Byte,
|
||||
position: Span(0, 10)
|
||||
},
|
||||
source,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_float_and_character() {
|
||||
let source = "1.0 + 'a'";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Err(DustError::Compile {
|
||||
error: CompileError::CannotAddArguments {
|
||||
left_type: Type::Float,
|
||||
right_type: Type::Character,
|
||||
position: Span(0, 9)
|
||||
},
|
||||
source,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_float_and_integer() {
|
||||
let source = "1.0 + 1";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Err(DustError::Compile {
|
||||
error: CompileError::CannotAddArguments {
|
||||
left_type: Type::Float,
|
||||
right_type: Type::Integer,
|
||||
position: Span(0, 7)
|
||||
},
|
||||
source,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_float_and_string() {
|
||||
let source = "1.0 + \"hello\"";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Err(DustError::Compile {
|
||||
error: CompileError::CannotAddArguments {
|
||||
left_type: Type::Float,
|
||||
right_type: Type::String,
|
||||
position: Span(0, 13)
|
||||
},
|
||||
source,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_integer_and_byte() {
|
||||
let source = "1 + 0xff";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Err(DustError::Compile {
|
||||
error: CompileError::CannotAddArguments {
|
||||
left_type: Type::Integer,
|
||||
right_type: Type::Byte,
|
||||
position: Span(0, 8)
|
||||
},
|
||||
source,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_integer_and_character() {
|
||||
let source = "1 + 'a'";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Err(DustError::Compile {
|
||||
error: CompileError::CannotAddArguments {
|
||||
left_type: Type::Integer,
|
||||
right_type: Type::Character,
|
||||
position: Span(0, 7)
|
||||
},
|
||||
source,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_integer_and_float() {
|
||||
let source = "1 + 1.0";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Err(DustError::Compile {
|
||||
error: CompileError::CannotAddArguments {
|
||||
left_type: Type::Integer,
|
||||
right_type: Type::Float,
|
||||
position: Span(0, 7)
|
||||
},
|
||||
source,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_integer_and_string() {
|
||||
let source = "1 + \"hello\"";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Err(DustError::Compile {
|
||||
error: CompileError::CannotAddArguments {
|
||||
left_type: Type::Integer,
|
||||
right_type: Type::String,
|
||||
position: Span(0, 11)
|
||||
},
|
||||
source,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_string_and_byte() {
|
||||
let source = "\"hello\" + 0xff";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Err(DustError::Compile {
|
||||
error: CompileError::CannotAddArguments {
|
||||
left_type: Type::String,
|
||||
right_type: Type::Byte,
|
||||
position: Span(0, 14)
|
||||
},
|
||||
source,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_string_and_float() {
|
||||
let source = "\"hello\" + 1.0";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Err(DustError::Compile {
|
||||
error: CompileError::CannotAddArguments {
|
||||
left_type: Type::String,
|
||||
right_type: Type::Float,
|
||||
position: Span(0, 13)
|
||||
},
|
||||
source,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_string_and_integer() {
|
||||
let source = "\"hello\" + 1";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Err(DustError::Compile {
|
||||
error: CompileError::CannotAddArguments {
|
||||
left_type: Type::String,
|
||||
right_type: Type::Integer,
|
||||
position: Span(0, 11)
|
||||
},
|
||||
source,
|
||||
})
|
||||
);
|
||||
}
|
@ -1,85 +1,167 @@
|
||||
use dust_lang::*;
|
||||
use dust_lang::{
|
||||
compile, instruction::TypeCode, run, Chunk, FunctionType, Instruction, Operand, Span, Type,
|
||||
Value,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn divide_bytes() {
|
||||
let source = "0xff / 0x01";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::Byte,
|
||||
},
|
||||
vec![
|
||||
(
|
||||
Instruction::divide(0, Operand::Constant(0), Operand::Constant(1)),
|
||||
Span(5, 6)
|
||||
let source = "0x0A / 0x02";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Byte),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, 10, TypeCode::BYTE, false),
|
||||
Instruction::load_encoded(1, 2, TypeCode::BYTE, false),
|
||||
Instruction::divide(
|
||||
2,
|
||||
Operand::Register(0, TypeCode::BYTE),
|
||||
Operand::Register(1, TypeCode::BYTE),
|
||||
),
|
||||
(Instruction::r#return(true), Span(11, 11))
|
||||
Instruction::r#return(true, 2, TypeCode::BYTE),
|
||||
],
|
||||
vec![ConcreteValue::Byte(0xff), ConcreteValue::Byte(0x01)],
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
positions: vec![Span(0, 4), Span(7, 11), Span(0, 11), Span(11, 11)],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::byte(0x05));
|
||||
|
||||
assert_eq!(run(source), Ok(Some(ConcreteValue::Byte(0xff))));
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn divide_many_bytes() {
|
||||
let source = "0x0A / 0x02 / 0x02";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Byte),
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, 10, TypeCode::BYTE, false),
|
||||
Instruction::load_encoded(1, 2, TypeCode::BYTE, false),
|
||||
Instruction::divide(
|
||||
2,
|
||||
Operand::Register(0, TypeCode::BYTE),
|
||||
Operand::Register(1, TypeCode::BYTE),
|
||||
),
|
||||
Instruction::load_encoded(3, 2, TypeCode::BYTE, false),
|
||||
Instruction::divide(
|
||||
4,
|
||||
Operand::Register(2, TypeCode::BYTE),
|
||||
Operand::Register(3, TypeCode::BYTE),
|
||||
),
|
||||
Instruction::r#return(true, 4, TypeCode::BYTE),
|
||||
],
|
||||
positions: vec![
|
||||
Span(0, 4),
|
||||
Span(7, 11),
|
||||
Span(0, 11),
|
||||
Span(14, 18),
|
||||
Span(0, 18),
|
||||
Span(18, 18),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::byte(0x02));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn divide_floats() {
|
||||
let source = "2.0 / 2.0";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::Float,
|
||||
},
|
||||
vec![
|
||||
(
|
||||
Instruction::divide(0, Operand::Constant(0), Operand::Constant(0)),
|
||||
Span(4, 5)
|
||||
let source = "1.0 / 0.25";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Float),
|
||||
instructions: vec![
|
||||
Instruction::divide(
|
||||
0,
|
||||
Operand::Constant(0, TypeCode::FLOAT),
|
||||
Operand::Constant(1, TypeCode::FLOAT),
|
||||
),
|
||||
(Instruction::r#return(true), Span(9, 9))
|
||||
Instruction::r#return(true, 0, TypeCode::FLOAT),
|
||||
],
|
||||
vec![ConcreteValue::Float(2.0)],
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
positions: vec![Span(0, 10), Span(10, 10)],
|
||||
float_constants: vec![1.0, 0.25],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::float(4.0));
|
||||
|
||||
assert_eq!(run(source), Ok(Some(ConcreteValue::Float(1.0))));
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn divide_many_floats() {
|
||||
let source = "1.0 / 0.25 / 0.5";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Float),
|
||||
instructions: vec![
|
||||
Instruction::divide(
|
||||
0,
|
||||
Operand::Constant(0, TypeCode::FLOAT),
|
||||
Operand::Constant(1, TypeCode::FLOAT),
|
||||
),
|
||||
Instruction::divide(
|
||||
1,
|
||||
Operand::Register(0, TypeCode::FLOAT),
|
||||
Operand::Constant(2, TypeCode::FLOAT),
|
||||
),
|
||||
Instruction::r#return(true, 1, TypeCode::FLOAT),
|
||||
],
|
||||
positions: vec![Span(0, 10), Span(0, 16), Span(16, 16)],
|
||||
float_constants: vec![1.0, 0.25, 0.5],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::float(8.0));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn divide_integers() {
|
||||
let source = "2 / 2";
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Ok(Chunk::with_data(
|
||||
None,
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::Integer,
|
||||
},
|
||||
vec![
|
||||
(
|
||||
Instruction::divide(0, Operand::Constant(0), Operand::Constant(0)),
|
||||
Span(2, 3)
|
||||
let source = "10 / 2";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Integer),
|
||||
instructions: vec![
|
||||
Instruction::divide(
|
||||
0,
|
||||
Operand::Constant(0, TypeCode::INTEGER),
|
||||
Operand::Constant(1, TypeCode::INTEGER),
|
||||
),
|
||||
(Instruction::r#return(true), Span(5, 5))
|
||||
Instruction::r#return(true, 0, TypeCode::INTEGER),
|
||||
],
|
||||
vec![ConcreteValue::Integer(2)],
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
positions: vec![Span(0, 6), Span(6, 6)],
|
||||
integer_constants: vec![10, 2],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::integer(5));
|
||||
|
||||
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(1))));
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn divide_many_integers() {
|
||||
let source = "10 / 2 / 2";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType::new([], [], Type::Integer),
|
||||
instructions: vec![
|
||||
Instruction::divide(
|
||||
0,
|
||||
Operand::Constant(0, TypeCode::INTEGER),
|
||||
Operand::Constant(1, TypeCode::INTEGER),
|
||||
),
|
||||
Instruction::divide(
|
||||
1,
|
||||
Operand::Register(0, TypeCode::INTEGER),
|
||||
Operand::Constant(1, TypeCode::INTEGER),
|
||||
),
|
||||
Instruction::r#return(true, 1, TypeCode::INTEGER),
|
||||
],
|
||||
positions: vec![Span(0, 6), Span(0, 10), Span(10, 10)],
|
||||
integer_constants: vec![10, 2],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::integer(2));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user