Crate stack_assembly

Crate stack_assembly 

Source
Expand description

§StackAssembly

StackAssembly is a minimalist, stack-based, assembly-like programming language. Here’s a small taste:

# Push `0` to the stack.
0

increment:
    # Increment the value on the stack by `1`.
    1 +

    # If the value on the stack is smaller than `255`, jump to `increment:`.
    0 copy 255 <
    @increment
        jump_if

# Looks like we didn't jump to `increment:` that last time, so the value
# must be `255` now.
255 = assert

StackAssembly serves as a foundation for my personal research into programming language design and implementation. Even though I want it to be complete enough for real code too, that is not its main purpose. If you’re wondering if it might work for you, the safe answer is probably “no”.

Please check out the repository on GitHub to learn more about StackAssembly. This documentation, while it contains some information about the language itself, is focused on how to use this library, which contains the StackAssembly interpreter.

§Usage

This library contains the interpreter for StackAssembly. It is intentionally minimalist. You provide a script, and the library gives you an API to evaluate it.

use stack_assembly::Eval;

let script = "1 2 +";

let mut eval = Eval::start(script);
eval.run();

assert_eq!(eval.stack.to_i32_slice(), &[3]);

Eval is the main entry point to the library’s API.

§Hosts

Eval evaluates scripts in a sandboxed environment, not giving them any access to the system it itself runs on. StackAssembly scripts by themselves cannot do much.

To change that, we need a host. A host is Rust code that uses this library to drive the evaluation of a StackAssembly script. It can choose to provide additional capabilities to the script.

use stack_assembly::{Effect, Eval};

// A script that seems to want to print the value `3`.
let script = "
    3 @print jump

    print:
        yield
";

// Start the evaluation and advance it until the script triggers an effect.
let mut eval = Eval::start(script);
eval.run();

// `run` has returned, meaning an effect has triggered. Let's make sure that
// went as expected.
assert_eq!(eval.effect, Some(Effect::Yield));
let Ok(value) = eval.stack.pop() else {
    unreachable!("We know that the script pushes a value before yielding.");
};

// The script calls `yield` at a label named `print`. I guess it expects us
// to print the value then.
println!("{value:?}");

When the script triggers the “yield” effect, this host prints the value that’s currently on top of the stack.

This is just a simple example. A more full-featured host would provide more services in addition to printing values. Such a host could determine which service the script means to request by inspecting which other values it put on the stack, or into memory.

Structs§

Eval
The ongoing evaluation of a script
Memory
A linear memory, freely addressable per word
Stack
The operand stack
StackUnderflow
Tried to pop a value from an empty stack
Value
A unit of data

Enums§

Effect
An event triggered by scripts, to signal a specific condition