Skip to main content

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}