1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
//! # StackAssembly
//!
//! StackAssembly is a minimalist, stack-based, assembly-like programming
//! language. Here's a small taste:
//!
//! ```text
//! # 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
//! ```
//!
//! Please check out the [repository on GitHub][repository] 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.
//!
//! [repository]: https://github.com/hannobraun/stack-assembly
//!
//! ## 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, Script};
//!
//! let script = Script::compile("1 2 +");
//!
//! let mut eval = Eval::new();
//! eval.run(&script);
//!
//! assert_eq!(eval.operand_stack.to_i32_slice(), &[3]);
//! ```
//!
//! [`Script`] and [`Eval`] are the main entry points 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, Script};
//!
//! // A script that seems to want to print the value `3`.
//! let script = Script::compile("
//! 3 @print jump
//!
//! print:
//! yield
//! ");
//!
//! // Start the evaluation and advance it until the script triggers an effect.
//! let mut eval = Eval::new();
//! let (effect, _) = eval.run(&script);
//!
//! // `run` has returned, meaning an effect has triggered. Let's make sure that
//! // went as expected.
//! assert_eq!(effect, Effect::Yield);
//! let Ok(value) = eval.operand_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.
pub use ;