vihaco_module/stack.rs
1// SPDX-FileCopyrightText: 2026 The vihaco Authors
2// SPDX-License-Identifier: MIT
3
4//! Typed access to individual logical stack values.
5//!
6//! These traits describe storage access, independently of a CPU, composite, or
7//! backing representation. Available with `program-image-execution`.
8//!
9//! These are storage extension contracts for the framework, not requirements
10//! for ordinary component/composite authors. `vihaco_stdlib::ExecutionState`
11//! supplies access; instructions declare operand/result types.
12
13use vihaco_abi::word::{
14 Word, decode_bool, decode_i32, decode_i64, decode_u32, encode_bool, encode_i32, encode_i64,
15 encode_u32,
16};
17
18/// Low-level typed operand access, excluding protected frame locals.
19///
20/// To be implemented by vihaco's execution storage. Component authors choose
21/// `PopFromStack` messages instead of implementing stack access. This public
22/// extension point also permits custom storage implementations.
23///
24/// The type parameter selects the representation: word-backed storage can
25/// decode floats, while an optimized cursor can consume native float values.
26pub trait PopStack<T> {
27 /// Consume exactly one logical operand as `T`.
28 ///
29 /// # Errors
30 /// Returns an error if no operand is available. Earlier successful reads
31 /// remain consumed when a subsequent read fails.
32 fn pop(&mut self) -> eyre::Result<T>;
33}
34
35/// Low-level typed insertion into logical stack storage.
36///
37/// To be implemented by vihaco's execution storage. Component authors return
38/// `PushToStack` effects instead of implementing stack insertion. This public
39/// extension point also permits custom storage implementations.
40///
41/// Each insertion adds exactly one value above existing values. Word-backed
42/// implementations encode floats with [`vihaco_abi::word::encode_f32`] and
43/// [`vihaco_abi::word::encode_f64`]; a
44/// later cached representation can retain native floating-point values instead.
45/// Floating-point insertion must preserve exact IEEE-754 bits.
46pub trait PushStack<T> {
47 /// Insert one value in this representation.
48 fn push(&mut self, value: T);
49}
50
51/// One copied logical stack value and its storage-representation conversions.
52///
53/// Separates the type a component computes with from the representation used to
54/// access program storage. A component can declare operands such as
55/// `PopFromStack<(i64, f64)>` and receive ordinary Rust values, while
56/// `vihaco_stdlib::ExecutionState` decides how to store them. A later optimized
57/// cursor can keep a floating-point value in a native cache without changing
58/// that component's messages or effects.
59/// This trait is framework plumbing; ordinary component authors do not implement
60/// it or any stack access logic.
61///
62/// Conversions describe one logical value and have no stack access. The default
63/// readers/writers consume/produce exactly one value through those conversions.
64/// Custom items need not have a Word encoding. This trait does not select an
65/// output storage bank or require native float insertion to pass through Word.
66///
67/// Booleans decode zero as false and every nonzero word as true. Insertion
68/// normalizes them to `0` or `1`; no canonical-input validation is performed.
69pub trait StackValue: Copy {
70 /// The value representation exchanged with the program's storage machinery.
71 ///
72 /// Built-in integers and booleans use [`Word`]; floats use their native
73 /// types. For example, components compute with `bool`, while its storage
74 /// access uses an encoded Word. An `f64` stays an `f64` at this interface:
75 /// word-backed storage encodes its bits, while an optimized cursor can retain
76 /// the native value and avoid conversions between instructions.
77 ///
78 /// "Storage" describes this internal access representation, not a physical
79 /// memory location, allocation, or bytecode encoding. It lets program storage
80 /// evolve independently of the component-facing operand/result types. The
81 /// selected runtime cache bank remains the storage implementation's concern.
82 type Storage;
83
84 /// Convert one stored representation into a component-facing value.
85 ///
86 /// # Errors
87 /// Returns any value-validation error required by a custom item. Built-in
88 /// conversions accept every input, including truthy Boolean words.
89 fn from_storage(value: Self::Storage) -> eyre::Result<Self>;
90
91 /// Convert this value into the representation accepted by program storage.
92 fn into_storage(self) -> Self::Storage;
93
94 /// Consume one operand and convert it to this item.
95 ///
96 /// # Errors
97 /// Propagates source-access or conversion errors. If conversion fails, the
98 /// operand has already been consumed; previous pops are not rolled back.
99 #[inline]
100 fn pop_from(source: &mut impl PopStack<Self::Storage>) -> eyre::Result<Self> {
101 source.pop().and_then(Self::from_storage)
102 }
103
104 /// Convert and insert this item using the destination's typed writer.
105 #[inline]
106 fn push_to(self, destination: &mut impl PushStack<Self::Storage>) {
107 destination.push(self.into_storage());
108 }
109}
110
111macro_rules! identity_item {
112 ($ty:ty) => {
113 impl StackValue for $ty {
114 type Storage = Self;
115
116 #[inline]
117 fn from_storage(value: Self::Storage) -> eyre::Result<Self> {
118 Ok(value)
119 }
120
121 #[inline]
122 fn into_storage(self) -> Self::Storage {
123 self
124 }
125
126 // Preserve direct Result forwarding for identity readers.
127 #[inline]
128 fn pop_from(source: &mut impl PopStack<Self::Storage>) -> eyre::Result<Self> {
129 source.pop()
130 }
131 }
132 };
133}
134
135identity_item!(Word);
136identity_item!(f32);
137identity_item!(f64);
138
139macro_rules! word_item {
140 ($ty:ty, $decode:path, $encode:path) => {
141 impl StackValue for $ty {
142 type Storage = Word;
143
144 #[inline]
145 fn from_storage(value: Self::Storage) -> eyre::Result<Self> {
146 Ok($decode(value))
147 }
148
149 #[inline]
150 fn into_storage(self) -> Self::Storage {
151 $encode(self)
152 }
153 }
154 };
155}
156
157word_item!(i32, decode_i32, encode_i32);
158word_item!(i64, decode_i64, encode_i64);
159word_item!(u32, decode_u32, encode_u32);
160word_item!(bool, decode_bool, encode_bool);