stack_assembly/effect.rs
1/// # An event triggered by scripts, to signal a specific condition
2///
3/// Effects moderate the communication between script and host. The effect
4/// itself only relays _which_ effect has triggered, but that may signal to
5/// the host that a different communication channel (like operand stack or
6/// memory) is ready to be accessed.
7///
8/// ## Handling Effects
9///
10/// The host may handle effects however it wishes. But since most effects
11/// signal error conditions that the script would not expect to recover
12/// from, a well-behaving host must be careful not to handle effects in
13/// a way that make reasoning about the script's behavior difficult.
14///
15/// Abandoning the evaluation and reporting an error in the appropriate
16/// manner, is the only reasonable way to handle most effects. The
17/// exception to that is [`Effect::Yield`], which does not signal an error
18/// condition. A script would expect to continue afterwards.
19///
20/// To make that possible, the host must clear the effect by calling
21/// [`Eval::clear_effect`].
22///
23/// ### Example
24///
25/// ```
26/// use stack_assembly::{Effect, Eval, Script};
27///
28/// // This script increments a number in a loop, yielding control to the
29/// // host every time it did so.
30/// let script = Script::compile("
31/// 0
32///
33/// increment:
34/// 1 +
35/// yield
36/// @increment jump
37/// ");
38///
39/// let mut eval = Eval::new();
40///
41/// // When running the script for the first time, we expect that it has
42/// // incremented the number once, before yielding.
43/// let (effect, _) = eval.run(&script);
44/// assert_eq!(effect, Effect::Yield);
45/// assert_eq!(eval.operand_stack.to_u32_slice(), &[1]);
46///
47/// // To allow the script to continue, we must clear the effect.
48/// eval.clear_effect();
49///
50/// // Since we handled the effect correctly, we can now assume that the
51/// // script has incremented the number a second time, before yielding
52/// // again.
53/// let (effect, _) = eval.run(&script);
54/// assert_eq!(effect, Effect::Yield);
55/// assert_eq!(eval.operand_stack.to_u32_slice(), &[2]);
56/// ```
57///
58/// [`Eval::clear_effect`]: crate::Eval::clear_effect
59#[derive(Clone, Copy, Debug, Eq, PartialEq)]
60pub enum Effect {
61 /// # An assertion failed
62 ///
63 /// Can trigger when evaluating `assert`, if its input is zero.
64 AssertionFailed,
65
66 /// # Tried to divide by zero
67 ///
68 /// Can trigger when evaluating the `/` operator, if its second input is
69 /// `0`.
70 DivisionByZero,
71
72 /// # Division resulted in integer overflow
73 ///
74 /// Can only trigger when evaluating the `/` operator, if its first input is
75 /// the lowest signed (two's complement) 32-bit integer, and its second
76 /// input is `-1`.
77 ///
78 /// All other arithmetic operators wrap on overflow and don't trigger this
79 /// effect.
80 IntegerOverflow,
81
82 /// # A memory address is out of bounds
83 ///
84 /// Can trigger when evaluating the `read` or `write` operators, if their
85 /// _address_ input (when interpreted as an unsigned 32-bit integer) does
86 /// not refer to an address that is within the bounds of the memory.
87 InvalidAddress,
88
89 /// # Index doesn't refer to valid value on the operand stack
90 ///
91 /// Can trigger when evaluating the `copy` or `drop` operators, if their
92 /// _index_ input is too large to refer to a value on the operand stack.
93 InvalidOperandStackIndex,
94
95 /// # Evaluated a reference that is not paired with a matching label
96 ///
97 /// Can trigger when evaluating a reference, if that reference does not
98 /// refer to a label.
99 InvalidReference,
100
101 /// # Tried popping a value from an empty operand stack
102 ///
103 /// Can trigger when evaluating any operator that has more inputs than the
104 /// number of values currently on the operand stack.
105 OperandStackUnderflow,
106
107 /// # Ran out of operators to evaluate
108 ///
109 /// Triggers when evaluation reaches the end of the script, where no more
110 /// operators are available. This is not an error, which makes it one of the
111 /// ways to signal the regular end of evaluation, alongside
112 /// [`Effect::Return`].
113 OutOfOperators,
114
115 /// # Evaluated `return` while call stack was empty
116 ///
117 /// This is not an error, which makes it one of the ways to signal the
118 /// regular end of evaluation, alongside [`Effect::OutOfOperators`].
119 Return,
120
121 /// # Evaluated an identifier that the language does not recognize
122 ///
123 /// Can trigger when evaluating an identifier, if that identifier does not
124 /// refer to a known operation.
125 UnknownIdentifier,
126
127 /// # The evaluating script yields control to the host
128 ///
129 /// Triggers when evaluating the `yield` operator.
130 Yield,
131}