stack_assembly/
lib.rs

1//! # StackAssembly
2//!
3//! StackAssembly is a minimalist, stack-based, assembly-like programming
4//! language. Here's a small taste:
5//!
6//! ```text
7//! # Push `0` to the stack.
8//! 0
9//!
10//! increment:
11//!     # Increment the value on the stack by `1`.
12//!     1 +
13//!
14//!     # If the value on the stack is smaller than `255`, jump to `increment:`.
15//!     0 copy 255 <
16//!     @increment
17//!         jump_if
18//!
19//! # Looks like we didn't jump to `increment:` that last time, so the value
20//! # must be `255` now.
21//! 255 = assert
22//! ```
23//!
24//! StackAssembly serves as a foundation for my personal research into
25//! programming language design and implementation. Even though I want it to be
26//! complete enough for real code too, that is not its main purpose. If you're
27//! wondering if it might work for you, the safe answer is probably "no".
28//!
29//! Please check out the [repository on GitHub][repository] to learn more about
30//! StackAssembly. This documentation, while it contains some information about
31//! the language itself, is focused on how to use this library, which contains
32//! the StackAssembly interpreter.
33//!
34//! [repository]: https://github.com/hannobraun/stack-assembly
35//!
36//! ## Usage
37//!
38//! This library contains the interpreter for StackAssembly. It is intentionally
39//! minimalist. You provide a **script**, and the library gives you an API to
40//! evaluate it.
41//!
42//! ```
43//! use stack_assembly::Eval;
44//!
45//! let script = "1 2 +";
46//!
47//! let mut eval = Eval::start(script);
48//! eval.run();
49//!
50//! assert_eq!(eval.stack.to_i32_slice(), &[3]);
51//! ```
52//!
53//! [`Eval`] is the main entry point to the library's API.
54//!
55//! ### Hosts
56//!
57//! [`Eval`] evaluates scripts in a sandboxed environment, not giving them any
58//! access to the system it itself runs on. StackAssembly scripts by themselves
59//! cannot do much.
60//!
61//! To change that, we need a **host**. A host is Rust code that uses this
62//! library to drive the evaluation of a StackAssembly script. It can choose to
63//! provide additional capabilities to the script.
64//!
65//! ```
66//! use stack_assembly::{Effect, Eval};
67//!
68//! // A script that seems to want to print the value `3`.
69//! let script = "
70//!     3 @print jump
71//!
72//!     print:
73//!         yield
74//! ";
75//!
76//! // Start the evaluation and advance it until the script triggers an effect.
77//! let mut eval = Eval::start(script);
78//! eval.run();
79//!
80//! // `run` has returned, meaning an effect has triggered. Let's make sure that
81//! // went as expected.
82//! assert_eq!(eval.effect, Some(Effect::Yield));
83//! let Ok(value) = eval.stack.pop() else {
84//!     unreachable!("We know that the script pushes a value before yielding.");
85//! };
86//!
87//! // The script calls `yield` at a label named `print`. I guess it expects us
88//! // to print the value then.
89//! println!("{value:?}");
90//! ```
91//!
92//! When the script triggers the "yield" effect, this host prints the value
93//! that's currently on top of the stack.
94//!
95//! This is just a simple example. A more full-featured host would provide more
96//! services in addition to printing values. Such a host could determine which
97//! service the script means to request by inspecting which other values it put
98//! on the stack, or into memory.
99
100#![warn(missing_debug_implementations)]
101#![warn(missing_docs)]
102
103mod effect;
104mod eval;
105mod memory;
106mod stack;
107mod value;
108
109#[cfg(test)]
110mod tests;
111
112pub use self::{
113    effect::Effect,
114    eval::Eval,
115    memory::Memory,
116    stack::{Stack, StackUnderflow},
117    value::Value,
118};