├── .cargo └── config ├── .github ├── FUNDING.yml └── workflows │ ├── coverage.yml │ ├── docs.yml │ ├── miri.yml │ └── tests.yml ├── .gitignore ├── .rustme ├── config.ron ├── docs.md ├── header.md ├── repo-readme.md ├── vm-docs.md └── vm-header.md ├── Bud-Assembly-Reference.md ├── Bud-Language-Reference.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── benchmarks ├── Cargo.toml └── benches │ └── budmap.rs ├── budlang-cli ├── Cargo.toml ├── src │ └── bud.rs └── tests │ └── ui-tests.rs ├── budlang ├── Cargo.toml ├── README.md ├── examples │ ├── budgeting.rs │ ├── fib-ast.rs │ ├── fib.rs │ └── rust-types.rs └── src │ ├── .crate-docs.md │ ├── ast.rs │ ├── lib.rs │ ├── parser.rs │ └── tests.rs ├── budvm ├── Cargo.toml ├── README.md ├── examples │ ├── fib-asm.rs │ ├── fib-ir.rs │ ├── fib-vm.rs │ └── rust-types-vm.rs └── src │ ├── .crate-docs.md │ ├── budmap.rs │ ├── budmap │ └── tests.rs │ ├── dynamic.rs │ ├── ir.rs │ ├── ir │ └── asm.rs │ ├── lexer_util.rs │ ├── lib.rs │ ├── list.rs │ ├── map.rs │ ├── string.rs │ └── symbol.rs └── xtask ├── Cargo.toml └── src └── main.rs /.cargo/config: -------------------------------------------------------------------------------- 1 | [alias] 2 | xtask = "run --package xtask --" -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [ecton] -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: Coverage 2 | 3 | on: [push] 4 | 5 | jobs: 6 | coverage: 7 | runs-on: ubuntu-latest 8 | timeout-minutes: 30 9 | steps: 10 | - uses: actions/checkout@v2 11 | 12 | - name: Install Rust 13 | uses: hecrj/setup-rust-action@v1 14 | 15 | - name: Run code coverage 16 | run: | 17 | cargo xtask generate-code-coverage-report --install-dependencies 18 | 19 | - name: Deploy Docs 20 | if: github.ref == 'refs/heads/main' 21 | uses: JamesIves/github-pages-deploy-action@releases/v4 22 | with: 23 | branch: gh-pages 24 | folder: coverage/ 25 | git-config-name: kl-botsu 26 | git-config-email: botsu@khonsulabs.com 27 | target-folder: /coverage/ 28 | clean: true 29 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Docs 2 | 3 | on: [push] 4 | 5 | jobs: 6 | docs: 7 | runs-on: ubuntu-latest 8 | if: github.ref == 'refs/heads/main' 9 | steps: 10 | - name: Install Rust 11 | uses: hecrj/setup-rust-action@v1 12 | 13 | - uses: actions/checkout@v2 14 | - name: Generate Docs 15 | run: | 16 | cargo doc --no-deps --all-features 17 | 18 | - name: Deploy Docs 19 | uses: JamesIves/github-pages-deploy-action@releases/v4 20 | with: 21 | branch: gh-pages 22 | folder: target/doc/ 23 | git-config-name: kl-botsu 24 | git-config-email: botsu@khonsulabs.com 25 | target-folder: /main/ 26 | clean: true 27 | -------------------------------------------------------------------------------- /.github/workflows/miri.yml: -------------------------------------------------------------------------------- 1 | # Why run Miri when there is no unsafe code? Because Miri can produce memory 2 | # effects to better test atomic usages. 3 | 4 | name: Miri 5 | 6 | on: [push] 7 | 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | timeout-minutes: 30 12 | steps: 13 | - uses: actions/checkout@v2 14 | 15 | - name: Install Rust 16 | uses: hecrj/setup-rust-action@v1 17 | with: 18 | rust-version: nightly 19 | components: miri 20 | 21 | - name: Run unit tests 22 | run: | 23 | cargo miri test 24 | env: 25 | MIRIFLAGS: -Zmiri-disable-isolation 26 | RUST_BACKTRACE: 1 -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: [push] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | timeout-minutes: 30 9 | steps: 10 | - uses: actions/checkout@v2 11 | 12 | - name: Install Rust 13 | uses: hecrj/setup-rust-action@v1 14 | 15 | - name: Run clippy 16 | run: | 17 | cargo clippy 18 | 19 | - name: Run unit tests 20 | run: | 21 | cargo test --all-features --all-targets 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | perf.data* 3 | Cargo.lock 4 | dist/ -------------------------------------------------------------------------------- /.rustme/config.ron: -------------------------------------------------------------------------------- 1 | Configuration( 2 | files: { 3 | "../README.md": [ 4 | "repo-readme.md", 5 | "https://github.com/khonsulabs/.github/raw/main/snippets/readme-footer.md", 6 | ], 7 | "../budlang/README.md": [ 8 | "header.md", 9 | "docs.md", 10 | "https://github.com/khonsulabs/.github/raw/main/snippets/readme-footer.md", 11 | ], 12 | "../budlang/src/.crate-docs.md": ( 13 | for_docs: true, 14 | sections: [ 15 | "docs.md", 16 | ] 17 | ), 18 | "../budvm/README.md": [ 19 | "vm-header.md", 20 | "vm-docs.md", 21 | "https://github.com/khonsulabs/.github/raw/main/snippets/readme-footer.md", 22 | ], 23 | "../budvm/src/.crate-docs.md": ( 24 | for_docs: true, 25 | sections: [ 26 | "vm-docs.md", 27 | ] 28 | ), 29 | "../CONTRIBUTING.md": [ 30 | "https://github.com/khonsulabs/.github/raw/main/docs/CONTRIBUTING.md", 31 | ], 32 | "../CODE_OF_CONDUCT.md": [ 33 | "https://github.com/khonsulabs/.github/raw/main/docs/CODE_OF_CONDUCT.md", 34 | ], 35 | "../LICENSE-APACHE": [ 36 | "https://github.com/khonsulabs/.github/raw/main/licenses/LICENSE-APACHE", 37 | ], 38 | "../LICENSE-MIT": [ 39 | "https://github.com/khonsulabs/.github/raw/main/licenses/LICENSE-MIT", 40 | ], 41 | }, 42 | glossaries: [ 43 | "https://github.com/khonsulabs/.github/raw/main/snippets/glossary.ron", 44 | { 45 | "docs-base": ( 46 | default: "https://khonsulabs.github.io/budlang/main/budlang", 47 | release: "https://docs.rs/budlang", 48 | ), 49 | "vm-docs-base": ( 50 | default: "https://khonsulabs.github.io/budlang/main/budvm", 51 | release: "https://docs.rs/budvm", 52 | ), 53 | "src-base": ( 54 | default: "https://github.com/khonsulabs/budlang/blob/main", 55 | release: "https://github.com/khonsulabs/budlang/blob/v0.0.0", 56 | ), 57 | "step": ( 58 | default: "https://khonsulabs.github.io/budlang/main/budlang/vm/trait.Environment.html#tymethod.step", 59 | release: "https://docs.rs/budlang/*/budlang/vm/trait.Environment.html#tymethod.step", 60 | for_docs: "crate::vm::Environment::step", 61 | ), 62 | "pause": ( 63 | default: "https://khonsulabs.github.io/budlang/main/budlang/vm/enum.ExecutionBehavior.html#variant.Pause", 64 | release: "https://docs.rs/budlang/*/budlang/vm/enum.ExecutionBehavior.html#variant.Pause", 65 | for_docs: "crate::vm::ExecutionBehavior::Pause", 66 | ), 67 | "vm": ( 68 | default: "https://khonsulabs.github.io/budlang/main/budvm/struct.VirtualMachine.html", 69 | release: "https://docs.rs/budlang/*/budvm/struct.VirtualMachine.html", 70 | for_docs: "crate::VirtualMachine", 71 | ), 72 | "budvm": ( 73 | default: "https://github.com/khonsulabs/budlang/blob/main/budvm", 74 | release: "https://crates.io/crates/budvm", 75 | ), 76 | "budlang": ( 77 | default: "https://github.com/khonsulabs/budlang/blob/main/budlang", 78 | release: "https://crates.io/crates/budlang", 79 | ), 80 | "budlang-cli": ( 81 | default: "https://github.com/khonsulabs/budlang/blob/main/budlang-cli", 82 | release: "https://crates.io/crates/budlang-cli", 83 | ), 84 | } 85 | ], 86 | ) -------------------------------------------------------------------------------- /.rustme/docs.md: -------------------------------------------------------------------------------- 1 | A safe, fast, lightweight embeddable scripting language written in Rust. 2 | 3 | ## Why Bud? 4 | 5 | ### Memory-safe 6 | 7 | This project forbids unsafe code (`#![forbid(unsafe_code)]`), and has only one 8 | dependency: [`budvm`][budvm]. The only unsafe code depended on by this crate is 9 | in Rust's standard library. 10 | 11 | ### Safe to run untrusted code 12 | 13 | The virtual machine invokes [`Environment::step()`]($step$) before each 14 | instruction is exected. The environment can return 15 | [`ExecutionBehavior::Pause`]($pause$) to pause execution, and the state of the 16 | virtual machine will be saved such that it can be resumed again. 17 | 18 | **Work In Progress:** Bud will have various configuration 19 | options including maximum stack size, maximum memory used, and more. 20 | 21 | **Work In Progress:** Bud should never panic. This crate is in early 22 | development, so many instances of `todo!()` and `unwrap()` abound. These will 23 | all be replaced with descriptive errors instead of panics. 24 | 25 | Bud will only support these primitive types: integers, real numbers (floating 26 | point), strings, lists, and maps. Bud will be able to be extended to support 27 | additional features via Rust, placing the developer embedding Bud in full 28 | control of what scripts can do. 29 | 30 | ### Efficient 31 | 32 | Bud is a compiled language powered by its own virtual machine. Currently, Bud 33 | has no optimizer, but the virtual machine code generated by the compiler closely 34 | mirrors the syntax of the language. For example, the repository includes three 35 | examples of a naive [Fibonacci number][fib] function implementation. The [Bud 36 | version][fib-ex] looks like this: 37 | 38 | ```bud 39 | function fibonacci(n) 40 | if n <= 2 41 | 1 42 | else 43 | this(n - 1) + this(n - 2) 44 | end 45 | end 46 | ``` 47 | 48 | Another example [shows an identical implementation][fib-vm] using hand-written 49 | virtual machine instructions. Despite not having an optimizer, the compiled 50 | `fibonacci()` function's code is nearly identical, having one extra (unreachable) 51 | instruction: 52 | 53 | | # | hand-written | # | compiled | 54 | |----|-----------------------|---|----------------------| 55 | | 0 | `lte @0 2 jump 2` | 0 | `lte @0 2 jump 3` | 56 | | 1 | `return 1` | 1 | `return 1` | 57 | | | | 2 | `jump 8` | 58 | | 2 | `sub @0 1 stack` | 3 | `sub @0 1 stack` | 59 | | 3 | `recurse 1 $$0` | 4 | `recurse 1 $$0` | 60 | | 4 | `sub @0 2 stack` | 5 | `sub @0 2 stack` | 61 | | 5 | `recurse 1 $$1` | 6 | `recurse 1 $$1` | 62 | | 6 | `add $$0 $$1 $$$$` | 7 | `add $$0 $$1 $$$$` | 63 | 64 | ## Why not Bud? 65 | 66 | It probably doesn't do what you need (yet): 67 | 68 | - [x] Don't panic in vm 69 | - [ ] Don't panic in compiler 70 | - [ ] Don't panic in parser 71 | - [x] Support parenthesized expressions as terms 72 | - [x] Add variables 73 | - [ ] Add loops 74 | - [x] Add Real (Float) type 75 | - [x] Add String type 76 | - [x] Add List type 77 | - [x] Add Map type 78 | - [x] Ability to write Rust functions 79 | - [x] Implement a REPL 80 | - [ ] Consider static variables for persistent module state. 81 | 82 | Bud is compiled to a virtual machine written using only memory-safe abstractions 83 | in Rust. This yields quite good performance for a dynamically typed language, 84 | but will not be the fastest language. 85 | 86 | ## Goals for Bud 87 | 88 | Aside from the goals outlined above, the use cases it's being designed for are: 89 | 90 | - A [BonsaiDb][bonsaidb] REPL 91 | - Multi-player server-side scripting where user-submitted scripts are allowed. 92 | 93 | [fib]: https://en.wikipedia.org/wiki/Fibonacci_number 94 | [fib-ex]: https://github.com/khonsulabs/budlang/blob/main/budlang/examples/fib.rs 95 | [fib-vm]: https://github.com/khonsulabs/budlang/blob/main/budvm/examples/fib-vm.rs 96 | [bonsaidb]: https://bonsaidb.io/ 97 | [budvm]: $budvm$ 98 | -------------------------------------------------------------------------------- /.rustme/header.md: -------------------------------------------------------------------------------- 1 | # Bud (budlang) 2 | 3 | **WARNING: This crate is not anywhere near being ready to publish.** 4 | 5 | ![budlang forbids unsafe code](https://img.shields.io/badge/unsafe-forbid-success) 6 | [![crate version](https://img.shields.io/crates/v/budlang.svg)](https://crates.io/crates/budlang) 7 | [![Live Build Status](https://img.shields.io/github/actions/workflow/status/khonsulabs/budlang/tests.yml?branch=main)](https://github.com/khonsulabs/budlang/actions?query=workflow:Tests) 8 | [![HTML Coverage Report for `main` branch](https://khonsulabs.github.io/budlang/coverage/badge.svg)](https://khonsulabs.github.io/budlang/coverage/) 9 | [![Documentation](https://img.shields.io/badge/docs-main-informational)]($docs-base$) 10 | -------------------------------------------------------------------------------- /.rustme/repo-readme.md: -------------------------------------------------------------------------------- 1 | # Bud: A Safe Dynamic Language Toolchain 2 | 3 | ![budlang forbids unsafe code](https://img.shields.io/badge/unsafe-forbid-success) 4 | [![Live Build Status](https://img.shields.io/github/workflow/status/khonsulabs/budlang/Tests/main)](https://github.com/khonsulabs/budlang/actions?query=workflow:Tests) 5 | [![HTML Coverage Report for `main` branch](https://khonsulabs.github.io/budlang/coverage/badge.svg)](https://khonsulabs.github.io/budlang/coverage/) 6 | 7 | This repository is where the Bud language is implemented. The virtual machine 8 | and related functionality are implemented separately from the language, making 9 | it language agnostic. 10 | 11 | ## [`budlang`][budlang] 12 | 13 | [![crate version](https://img.shields.io/crates/v/budlang.svg)](https://crates.io/crates/budlang) [![Documentation](https://img.shields.io/badge/docs-main-informational)]($docs-base$) 14 | 15 | The [`budlang`][budlang] crate is where the Bud language is implemented. It is 16 | built using [`budvm`][budvm]. 17 | 18 | ## [`budlang-cli`][budlang-cli] 19 | 20 | [![crate version](https://img.shields.io/crates/v/budlang-cli.svg)](https://crates.io/crates/budlang-cli) 21 | 22 | The [`budlang-cli`][budlang-cli] crate provides utilities to execute Bud scripts 23 | from the command line or using a REPL environment. 24 | 25 | ## [`budvm`][budvm] 26 | 27 | [![crate version](https://img.shields.io/crates/v/budvm.svg)](https://crates.io/crates/budvm) [![Documentation](https://img.shields.io/badge/docs-main-informational)]($vm-docs-base$) 28 | 29 | The [`budvm`][budvm] crate defines a `#[forbid(unsafe_code)]` virtual machine 30 | implementation. 31 | 32 | [budlang]: $budlang$ 33 | [budvm]: $budvm$ 34 | [budlang-cli]: $budlang-cli$ 35 | -------------------------------------------------------------------------------- /.rustme/vm-docs.md: -------------------------------------------------------------------------------- 1 | A safe, dynamically-typed virtual machine 2 | 3 | This crate provides a stack-based virtual machine that can be used to implement 4 | dynamically typed languages. [Bud][budlang] is the language that this crate was 5 | originally designed for. 6 | 7 | ## Safety 8 | 9 | This crate uses `#[forbid(unsafe_code)]` and has no external dependencies. The 10 | only unsafe code blocks are located within Rust's standard library. Any panics 11 | encountered should be considered a bug and reported. 12 | 13 | ## How to use 14 | 15 | [Bud][budlang] can be used as a complete example of how to build a language and 16 | REPL with this crate. There are two simpler examples demonstrating a recursive 17 | fibonacci number function: 18 | 19 | * [fib-vm][fib-vm]: Implemented using virtual machine instructions. 20 | * [fib-ir][fib-ir]: Implemented using intermediate representation (IR) with 21 | conveniences like variable management and labels. 22 | 23 | In all cases, the [`VirtualMachine`][vm] type is used to execute instructions. 24 | Its documentation goes into detail how it operates. 25 | 26 | [budlang]: https://github.com/khonsulabs/budlang 27 | [vm]: $vm$ 28 | [fib-vm]: https://github.com/khonsulabs/budlang/blob/main/budvm/examples/fib-vm.rs 29 | [fib-ir]: https://github.com/khonsulabs/budlang/blob/main/budvm/examples/fib-ir.rs 30 | -------------------------------------------------------------------------------- /.rustme/vm-header.md: -------------------------------------------------------------------------------- 1 | # Bud Virtual Machine (budvm) 2 | 3 | **WARNING: This crate is not anywhere near being ready to publish.** 4 | 5 | ![budvm forbids unsafe code](https://img.shields.io/badge/unsafe-forbid-success) 6 | [![crate version](https://img.shields.io/crates/v/budvm.svg)](https://crates.io/crates/budvm) 7 | [![Live Build Status](https://img.shields.io/github/workflow/status/khonsulabs/budlang/Tests/main)](https://github.com/khonsulabs/budlang/actions?query=workflow:Tests) 8 | [![HTML Coverage Report for `main` branch](https://khonsulabs.github.io/budlang/coverage/badge.svg)](https://khonsulabs.github.io/budlang/coverage/) 9 | [![Documentation](https://img.shields.io/badge/docs-main-informational)]($vm-docs-base$) 10 | -------------------------------------------------------------------------------- /Bud-Assembly-Reference.md: -------------------------------------------------------------------------------- 1 | # Bud Assembly Language Reference 2 | 3 | Bud Assembly is a near perfect mapping to `budvm`'s intermediate representation 4 | (IR). As with most assembly languages, it is very simple. These are the shared 5 | types of arguments instructions may take: 6 | 7 | - `LiteralOrSource`: These parameters can be one of: 8 | - a literal string 9 | - a literal integer 10 | - a literal real number (floating point) 11 | - a literal boolean 12 | - an argument (`@name`) 13 | - a variable (`$name`) 14 | - `Destination`: These parameters can be one of: 15 | - `$`: Store the result on the stack 16 | - `$$`: Store the result in the return register 17 | - `$name`: a variable 18 | - `#label`: A unique-per-function code label 19 | 20 | ## Add 21 | 22 | `add ` 23 | 24 | Adds the value from the first argument and the value from the second argument 25 | and stores the result in the provided destination. 26 | 27 | ## Subtract 28 | 29 | `sub ` 30 | 31 | Subtracts the value from the second argument from the value from the first 32 | argument and stores the result in the provided destination. 33 | 34 | ## Multiplication 35 | 36 | `mul ` 37 | 38 | Multiplies the value from the first argument and the value from the second 39 | argument and stores the result in the provided destination. 40 | 41 | ## Division 42 | 43 | `div ` 44 | 45 | Divides the value from the first argument by the value from the second argument 46 | and stores the result in the provided destination. 47 | 48 | ## Logical And 49 | 50 | `and ` 51 | 52 | Evaluates the truthiness of the first argument and the truthiness of the second 53 | argument, and stores a boolean result of performing a logical and of the two 54 | values. 55 | 56 | This operator short circuits and does not evaluate the second argument if the 57 | first argument is falsey. 58 | 59 | ## Logical Or 60 | 61 | `or ` 62 | 63 | Evaluates the truthiness of the first argument and the truthiness of the second 64 | argument, and stores a boolean result of performing a logical or of the two 65 | values. 66 | 67 | This operator short circuits and does not evaluate the second argument if the 68 | first argument is true. 69 | 70 | ## Logical Xor 71 | 72 | `xor ` 73 | 74 | Evaluates the truthiness of the first argument and the truthiness of the second 75 | argument, and stores a boolean result of performing a logical xor of the two 76 | values. 77 | 78 | ## Logical Not 79 | 80 | `not ` 81 | 82 | The first argument's truthiness is evaluated and the opposite boolean value is 83 | stored in the destination. 84 | 85 | ## Bitwise And 86 | 87 | `bitand ` 88 | 89 | Performs a bitwise and between two integer values. If either argument is not an 90 | integer, a fault will be returned from the virtual machine. 91 | 92 | ## Bitwise Or 93 | 94 | `bitor ` 95 | 96 | Performs a bitwise or between two integer values. If either argument is not an 97 | integer, a fault will be returned from the virtual machine. 98 | 99 | ## Bitwise Xor 100 | 101 | `bitxor ` 102 | 103 | Performs a bitwise exclusive-or between two integer values. If either argument 104 | is not an integer, a fault will be returned from the virtual machine. 105 | 106 | ## Bitwise Not 107 | 108 | `bitnot ` 109 | 110 | The first argument is coerced to an integer and each bit is flipped. The result 111 | is stored in the destination. If the first argument cannot be coerced to an 112 | integer, a fault will be returned from the virtual machine. 113 | 114 | ## Bitwise Shift Left 115 | 116 | `shl ` 117 | 118 | Performs a bitwise shift left of the first argument by the second argument. If 119 | either argument is not an integer, a fault will be returned from the virtual 120 | machine. 121 | 122 | ## Bitwise Shift Right 123 | 124 | `shr ` 125 | 126 | Performs a bitwise shift right of the first argument by the second argument. If 127 | either argument is not an integer, a fault will be returned from the virtual 128 | machine. 129 | 130 | ## Conditional Jump 131 | 132 | `ifnot