sim_kernel/object.rs
1//! The object contract: the runtime object surface and argument forms.
2//!
3//! The kernel defines the [`Object`] header, the class/shape/constructor
4//! reference types, and the checked [`Args`] forms passed to callables; the
5//! libraries supply the concrete object implementations.
6
7use std::any::Any;
8use std::sync::OnceLock;
9
10use crate::{
11 callable::Callable,
12 capability::TrustLevel,
13 claim::{Claim, ClaimPattern},
14 class::Class,
15 datum::Datum,
16 encode::{ObjectEncode, ReadConstructor},
17 env::Cx,
18 error::Result,
19 expr::Expr,
20 id::Symbol,
21 number_domain::{NumberDomain, NumberValue},
22 ref_id::Ref,
23 term::OpKey,
24 value::Value,
25};
26
27/// Runtime value carrying a class object.
28pub type ClassRef = Value;
29/// Runtime value carrying a read-constructor object.
30pub type ReadConstructorRef = Value;
31/// Runtime value carrying a shape object.
32pub type ShapeRef = Value;
33/// Runtime value carrying a table object.
34pub type TableRef = Value;
35
36/// Identity and trust header attached to every runtime object.
37#[derive(Clone, Debug, PartialEq, Eq)]
38pub struct ObjectHeader {
39 /// Stable reference identifying the object.
40 pub id: Ref,
41 /// Symbol naming the object's kind.
42 pub kind: Symbol,
43 /// Trust level governing the object's capabilities.
44 pub trust: TrustLevel,
45}
46
47/// Sink that collects claims emitted while reflecting over an object.
48pub trait ClaimSink {
49 /// Accept one emitted claim.
50 fn claim(&mut self, claim: Claim) -> Result<()>;
51}
52
53/// Checked, already-evaluated positional arguments passed to a callable.
54#[derive(Clone, Default)]
55pub struct Args {
56 values: Vec<Value>,
57}
58
59impl Args {
60 /// Build an argument list from evaluated values.
61 pub fn new(values: Vec<Value>) -> Self {
62 Self { values }
63 }
64
65 /// Borrow the argument values in order.
66 pub fn values(&self) -> &[Value] {
67 &self.values
68 }
69
70 /// Consume the argument list, returning the owned values.
71 pub fn into_vec(self) -> Vec<Value> {
72 self.values
73 }
74}
75
76/// Unevaluated argument expressions passed to a raw callable.
77#[derive(Clone, Default)]
78pub struct RawArgs {
79 exprs: Vec<Expr>,
80}
81
82impl RawArgs {
83 /// Build a raw argument list from unevaluated expressions.
84 pub fn new(exprs: Vec<Expr>) -> Self {
85 Self { exprs }
86 }
87
88 /// Borrow the argument expressions in order.
89 pub fn exprs(&self) -> &[Expr] {
90 &self.exprs
91 }
92
93 /// Consume the raw argument list, returning the owned expressions.
94 pub fn into_exprs(self) -> Vec<Expr> {
95 self.exprs
96 }
97}
98
99/// Base protocol implemented by every runtime value.
100///
101/// SIM uses object-style protocol views instead of a closed value enum for
102/// extensible runtime behavior. The root trait stays small: behavior is
103/// exposed through headers, operations, claims, snapshots, display, and Rust
104/// downcasting.
105pub trait Object: Send + Sync {
106 /// Identity and trust header for the object; defaults to the shared
107 /// anonymous header.
108 fn header(&self) -> &ObjectHeader {
109 default_object_header()
110 }
111
112 /// Resolve the operation registered under `key`, if any.
113 fn op(&self, _key: &OpKey) -> Option<&dyn crate::op::Op> {
114 None
115 }
116
117 /// Emit the object's claims matching `pattern` into `sink`.
118 fn claims(
119 &self,
120 _cx: &mut Cx,
121 _pattern: &ClaimPattern,
122 _sink: &mut dyn ClaimSink,
123 ) -> Result<()> {
124 Ok(())
125 }
126
127 /// Optional content-addressable snapshot of the object's state.
128 fn snapshot(&self, _cx: &mut Cx) -> Result<Option<Datum>> {
129 Ok(None)
130 }
131
132 /// Render the object as a human-readable display string.
133 fn display(&self, _cx: &mut Cx) -> Result<String>;
134
135 /// Expose the object for Rust downcasting.
136 fn as_any(&self) -> &dyn Any;
137}
138
139/// Bridge that exposes typed protocol views (class, callable, shape, encoder,
140/// number, fabric, stream, and related accessors) for objects that do not
141/// resolve them through operations.
142///
143/// Callers should resolve operations, claims, refs, or snapshots instead of
144/// calling this trait. It is kept outside [`Object`] so the root object trait
145/// stays minimal while library objects expose this behavior through
146/// compatibility adapters.
147pub trait ObjectCompat: Object {
148 /// Class object this value belongs to; defaults to nil.
149 fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
150 cx.factory().nil()
151 }
152
153 /// Callable view, if the object can be invoked.
154 fn as_callable(&self) -> Option<&dyn Callable> {
155 None
156 }
157
158 /// Class view, if the object is a class.
159 fn as_class(&self) -> Option<&dyn Class> {
160 None
161 }
162
163 /// Shape view, if the object is a shape.
164 fn as_shape(&self) -> Option<&dyn crate::shape::Shape> {
165 None
166 }
167
168 /// Object-encoder view, if the object encodes other objects.
169 fn as_object_encoder(&self) -> Option<&dyn ObjectEncode> {
170 None
171 }
172
173 /// Read-constructor view, if the object decodes data forms.
174 fn as_read_constructor(&self) -> Option<&dyn ReadConstructor> {
175 None
176 }
177
178 /// Number-domain view, if the object is a number domain.
179 fn as_number_domain(&self) -> Option<&dyn NumberDomain> {
180 None
181 }
182
183 /// Number-value view, if the object is a domain number.
184 fn as_number_value(&self) -> Option<&dyn NumberValue> {
185 None
186 }
187
188 /// Eval-fabric view, if the object is a distributed eval surface.
189 fn as_eval_fabric(&self) -> Option<&dyn crate::eval::EvalFabric> {
190 None
191 }
192
193 /// Stream view, if the object is a stream.
194 fn as_stream(&self) -> Option<&dyn crate::stream::Stream> {
195 None
196 }
197
198 /// Sequence view, if the object is a sequence.
199 fn as_sequence(&self) -> Option<&dyn crate::seq::Sequence> {
200 None
201 }
202
203 /// Thunk view, if the object is a deferred computation.
204 fn as_thunk(&self) -> Option<&dyn crate::eval::Thunk> {
205 None
206 }
207
208 /// List view, if the object is a list value.
209 fn as_list(&self) -> Option<&dyn crate::list::ListValue> {
210 None
211 }
212
213 /// Table-implementation view, if the object is a table.
214 fn as_table_impl(&self) -> Option<&dyn crate::table::Table> {
215 None
216 }
217
218 /// Directory view, if the object is a directory.
219 fn as_dir(&self) -> Option<&dyn crate::table::Dir> {
220 None
221 }
222
223 /// Expression form of the object; defaults to an opaque extension node.
224 fn as_expr(&self, cx: &mut Cx) -> Result<Expr> {
225 Ok(Expr::Extension {
226 tag: Symbol::qualified("core", "opaque-object"),
227 payload: Box::new(Expr::String(Object::display(self, cx)?)),
228 })
229 }
230
231 /// Truthiness of the object; defaults to true.
232 fn truth(&self, _cx: &mut Cx) -> Result<bool> {
233 Ok(true)
234 }
235
236 /// Publish claims asserting that the object satisfies `shape`; returns
237 /// whether any were published.
238 fn publish_shape_satisfaction_claims(&self, _cx: &mut Cx, _shape: &Ref) -> Result<bool> {
239 Ok(false)
240 }
241
242 /// Project the object into a table value; the default exposes its display.
243 fn as_table(&self, cx: &mut Cx) -> Result<Value> {
244 let display = Object::display(self, cx)?;
245 cx.factory().table(vec![(
246 Symbol::new("display"),
247 cx.factory().string(display)?,
248 )])
249 }
250}
251
252impl dyn Object {
253 /// Downcast the object to a concrete type `T`, if it is that type.
254 pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
255 self.as_any().downcast_ref::<T>()
256 }
257}
258
259impl ClaimSink for Vec<Claim> {
260 /// Collect the claim by pushing it onto the vector.
261 fn claim(&mut self, claim: Claim) -> Result<()> {
262 self.push(claim);
263 Ok(())
264 }
265}
266
267/// The shared anonymous [`ObjectHeader`] used by objects without their own.
268pub fn default_object_header() -> &'static ObjectHeader {
269 static HEADER: OnceLock<ObjectHeader> = OnceLock::new();
270 HEADER.get_or_init(|| ObjectHeader {
271 id: default_object_ref(),
272 kind: Symbol::qualified("core", "Object"),
273 trust: TrustLevel::HostInternal,
274 })
275}
276
277/// Whether `header` is the shared anonymous [`default_object_header`].
278pub fn is_default_object_header(header: &ObjectHeader) -> bool {
279 header == default_object_header()
280}
281
282fn default_object_ref() -> Ref {
283 Ref::Symbol(Symbol::qualified("core", "anonymous-object"))
284}