Skip to main content

sim_lib_standard_core/
read_construct.rs

1//! Read/construct support exposing profiles and badges as runtime objects.
2
3use std::sync::Arc;
4
5use sim_kernel::{
6    AbiVersion, Args, CORE_CLASS_CLASS_ID, CORE_FUNCTION_CLASS_ID, Callable, Class, ClassId,
7    ClassRef, Cx, DefaultFactory, Export, Expr, Factory, Lib, LibManifest, LibTarget, Linker,
8    Object, ObjectCompat, ObjectEncode, ObjectEncoding, ReadConstructor, ReadConstructorRef, Ref,
9    Result, ShapeRef, Symbol, TableRef, Value, Version,
10};
11
12use crate::{
13    FidelityBadge, LanguageProfile, fidelity_badge_class_symbol, language_profile_class_symbol,
14};
15
16const PROFILE_CLASS_ID: ClassId = ClassId(6100);
17const FIDELITY_BADGE_CLASS_ID: ClassId = ClassId(6101);
18
19/// Runtime object wrapping a [`LanguageProfile`] as the `standard/Profile` class.
20#[derive(Clone)]
21pub struct LanguageProfileValue {
22    profile: LanguageProfile,
23}
24
25impl LanguageProfileValue {
26    /// Wrap `profile` as a runtime value.
27    pub fn new(profile: LanguageProfile) -> Self {
28        Self { profile }
29    }
30
31    /// The wrapped profile.
32    pub fn profile(&self) -> &LanguageProfile {
33        &self.profile
34    }
35}
36
37impl Object for LanguageProfileValue {
38    fn display(&self, _cx: &mut Cx) -> Result<String> {
39        Ok(format!("#<standard-profile {}>", self.profile.symbol))
40    }
41
42    fn as_any(&self) -> &dyn std::any::Any {
43        self
44    }
45}
46
47impl ObjectCompat for LanguageProfileValue {
48    fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
49        class_value_or_stub(cx, PROFILE_CLASS_ID, language_profile_class_symbol())
50    }
51
52    fn as_expr(&self, _cx: &mut Cx) -> Result<Expr> {
53        Ok(Expr::Call {
54            operator: Box::new(Expr::Symbol(language_profile_class_symbol())),
55            args: self.profile.to_constructor_args(),
56        })
57    }
58
59    fn as_object_encoder(&self) -> Option<&dyn ObjectEncode> {
60        Some(self)
61    }
62}
63
64impl ObjectEncode for LanguageProfileValue {
65    fn object_encoding(&self, _cx: &mut Cx) -> Result<ObjectEncoding> {
66        Ok(ObjectEncoding::Constructor {
67            class: language_profile_class_symbol(),
68            args: self.profile.to_constructor_args(),
69        })
70    }
71}
72
73impl sim_citizen::Citizen for LanguageProfileValue {
74    fn citizen_symbol() -> Symbol {
75        language_profile_class_symbol()
76    }
77
78    fn citizen_version() -> u32 {
79        0
80    }
81
82    fn citizen_arity() -> usize {
83        10
84    }
85
86    fn citizen_fields() -> &'static [&'static str] {
87        &[
88            "symbol",
89            "reader",
90            "lowering",
91            "eval_policy",
92            "organs",
93            "numeric_tower",
94            "capabilities",
95            "unsupported_forms",
96            "conformance_tests",
97            "fidelity_badges",
98        ]
99    }
100}
101
102/// Runtime object wrapping a [`FidelityBadge`] as the `standard/FidelityBadge` class.
103#[derive(Clone)]
104pub struct FidelityBadgeValue {
105    badge: FidelityBadge,
106}
107
108impl FidelityBadgeValue {
109    /// Wrap `badge` as a runtime value.
110    pub fn new(badge: FidelityBadge) -> Self {
111        Self { badge }
112    }
113
114    /// The wrapped badge.
115    pub fn badge(&self) -> &FidelityBadge {
116        &self.badge
117    }
118}
119
120impl Object for FidelityBadgeValue {
121    fn display(&self, _cx: &mut Cx) -> Result<String> {
122        Ok(format!("#<standard-fidelity {}>", self.badge.badge))
123    }
124
125    fn as_any(&self) -> &dyn std::any::Any {
126        self
127    }
128}
129
130impl ObjectCompat for FidelityBadgeValue {
131    fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
132        class_value_or_stub(cx, FIDELITY_BADGE_CLASS_ID, fidelity_badge_class_symbol())
133    }
134
135    fn as_expr(&self, _cx: &mut Cx) -> Result<Expr> {
136        Ok(Expr::Call {
137            operator: Box::new(Expr::Symbol(fidelity_badge_class_symbol())),
138            args: self.badge.to_constructor_args(),
139        })
140    }
141
142    fn as_object_encoder(&self) -> Option<&dyn ObjectEncode> {
143        Some(self)
144    }
145}
146
147impl ObjectEncode for FidelityBadgeValue {
148    fn object_encoding(&self, _cx: &mut Cx) -> Result<ObjectEncoding> {
149        Ok(ObjectEncoding::Constructor {
150            class: fidelity_badge_class_symbol(),
151            args: self.badge.to_constructor_args(),
152        })
153    }
154}
155
156impl sim_citizen::Citizen for FidelityBadgeValue {
157    fn citizen_symbol() -> Symbol {
158        fidelity_badge_class_symbol()
159    }
160
161    fn citizen_version() -> u32 {
162        0
163    }
164
165    fn citizen_arity() -> usize {
166        4
167    }
168
169    fn citizen_fields() -> &'static [&'static str] {
170        &["subject", "badge", "level", "evidence"]
171    }
172}
173
174/// Register the `standard/Profile` and `standard/FidelityBadge` classes into the
175/// registry, idempotently.
176///
177/// This installs the read/construct surface that lets the standard distribution
178/// round-trip profiles and badges as runtime objects; classes already present
179/// are left untouched.
180pub fn install_standard_core_classes(cx: &mut Cx) -> Result<()> {
181    sim_lib_core::install_once(cx, &StandardCoreClassesLib).map(|_| ())
182}
183
184/// Manifest id for the standard read/construct class lib.
185pub fn standard_core_classes_lib_symbol() -> Symbol {
186    Symbol::qualified("standard", "classes")
187}
188
189struct StandardCoreClassesLib;
190
191impl Lib for StandardCoreClassesLib {
192    fn manifest(&self) -> LibManifest {
193        LibManifest {
194            id: standard_core_classes_lib_symbol(),
195            version: Version(env!("CARGO_PKG_VERSION").to_owned()),
196            abi: AbiVersion { major: 0, minor: 1 },
197            target: LibTarget::HostRegistered,
198            requires: Vec::new(),
199            capabilities: Vec::new(),
200            exports: [StandardClassKind::Profile, StandardClassKind::FidelityBadge]
201                .into_iter()
202                .map(|kind| Export::Class {
203                    symbol: kind.symbol(),
204                    class_id: Some(kind.id()),
205                })
206                .collect(),
207        }
208    }
209
210    fn load(&self, _cx: &mut sim_kernel::LoadCx, linker: &mut Linker<'_>) -> Result<()> {
211        register_standard_class(linker, StandardClassKind::Profile)?;
212        register_standard_class(linker, StandardClassKind::FidelityBadge)
213    }
214}
215
216#[derive(Clone, Copy)]
217enum StandardClassKind {
218    Profile,
219    FidelityBadge,
220}
221
222impl StandardClassKind {
223    fn id(self) -> ClassId {
224        match self {
225            Self::Profile => PROFILE_CLASS_ID,
226            Self::FidelityBadge => FIDELITY_BADGE_CLASS_ID,
227        }
228    }
229
230    fn symbol(self) -> Symbol {
231        match self {
232            Self::Profile => language_profile_class_symbol(),
233            Self::FidelityBadge => fidelity_badge_class_symbol(),
234        }
235    }
236
237    fn display_name(self) -> &'static str {
238        match self {
239            Self::Profile => "standard/Profile",
240            Self::FidelityBadge => "standard/FidelityBadge",
241        }
242    }
243}
244
245#[derive(Clone)]
246struct StandardClass {
247    kind: StandardClassKind,
248}
249
250impl Object for StandardClass {
251    fn display(&self, _cx: &mut Cx) -> Result<String> {
252        Ok(format!("#<class {}>", self.kind.display_name()))
253    }
254
255    fn as_any(&self) -> &dyn std::any::Any {
256        self
257    }
258}
259
260impl ObjectCompat for StandardClass {
261    fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
262        class_value_or_stub(cx, CORE_CLASS_CLASS_ID, Symbol::qualified("core", "Class"))
263    }
264
265    fn as_expr(&self, _cx: &mut Cx) -> Result<Expr> {
266        Ok(Expr::Symbol(self.kind.symbol()))
267    }
268
269    fn as_callable(&self) -> Option<&dyn Callable> {
270        Some(self)
271    }
272
273    fn as_class(&self) -> Option<&dyn Class> {
274        Some(self)
275    }
276}
277
278impl Callable for StandardClass {
279    fn call(&self, cx: &mut Cx, args: Args) -> Result<Value> {
280        construct_standard_value(cx, self.kind, args.into_vec())
281    }
282}
283
284impl Class for StandardClass {
285    fn id(&self) -> ClassId {
286        self.kind.id()
287    }
288
289    fn symbol(&self) -> Symbol {
290        self.kind.symbol()
291    }
292
293    fn constructor_shape(&self, cx: &mut Cx) -> Result<ShapeRef> {
294        cx.factory().nil()
295    }
296
297    fn instance_shape(&self, cx: &mut Cx) -> Result<ShapeRef> {
298        cx.factory().nil()
299    }
300
301    fn read_constructor(&self, _cx: &mut Cx) -> Result<Option<ReadConstructorRef>> {
302        Ok(Some(DefaultFactory.opaque(Arc::new(
303            StandardReadConstructor { kind: self.kind },
304        ))?))
305    }
306
307    fn members(&self, cx: &mut Cx) -> Result<TableRef> {
308        cx.factory().table(Vec::new())
309    }
310}
311
312#[derive(Clone)]
313struct StandardReadConstructor {
314    kind: StandardClassKind,
315}
316
317impl Object for StandardReadConstructor {
318    fn display(&self, _cx: &mut Cx) -> Result<String> {
319        Ok(format!("#<read-constructor {}>", self.kind.display_name()))
320    }
321
322    fn as_any(&self) -> &dyn std::any::Any {
323        self
324    }
325}
326
327impl ObjectCompat for StandardReadConstructor {
328    fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
329        class_value_or_stub(
330            cx,
331            CORE_FUNCTION_CLASS_ID,
332            Symbol::qualified("core", "Function"),
333        )
334    }
335
336    fn as_read_constructor(&self) -> Option<&dyn ReadConstructor> {
337        Some(self)
338    }
339}
340
341impl ReadConstructor for StandardReadConstructor {
342    fn symbol(&self) -> Symbol {
343        self.kind.symbol()
344    }
345
346    fn args_shape(&self, cx: &mut Cx) -> Result<ShapeRef> {
347        cx.factory().nil()
348    }
349
350    fn construct_read(&self, cx: &mut Cx, args: Vec<Value>) -> Result<Value> {
351        construct_standard_value(cx, self.kind, args)
352    }
353}
354
355fn construct_standard_value(
356    cx: &mut Cx,
357    kind: StandardClassKind,
358    args: Vec<Value>,
359) -> Result<Value> {
360    let exprs = value_exprs(cx, args)?;
361    match kind {
362        StandardClassKind::Profile => cx.factory().opaque(Arc::new(LanguageProfileValue::new(
363            LanguageProfile::from_constructor_args(exprs)?,
364        ))),
365        StandardClassKind::FidelityBadge => cx.factory().opaque(Arc::new(FidelityBadgeValue::new(
366            FidelityBadge::from_constructor_args(exprs)?,
367        ))),
368    }
369}
370
371fn value_exprs(cx: &mut Cx, args: Vec<Value>) -> Result<Vec<Expr>> {
372    let mut exprs = Vec::with_capacity(args.len());
373    for value in args {
374        exprs.push(value.object().as_expr(cx)?);
375    }
376    Ok(exprs)
377}
378
379fn register_standard_class(linker: &mut Linker<'_>, kind: StandardClassKind) -> Result<()> {
380    let class = DefaultFactory
381        .opaque(Arc::new(StandardClass { kind }))
382        .expect("standard class should be boxable");
383    linker.class_value(kind.symbol(), class)?;
384    Ok(())
385}
386
387fn install_language_profile_citizen(linker: &mut Linker<'_>) -> Result<()> {
388    register_standard_class(linker, StandardClassKind::Profile)
389}
390
391fn install_fidelity_badge_citizen(linker: &mut Linker<'_>) -> Result<()> {
392    register_standard_class(linker, StandardClassKind::FidelityBadge)
393}
394
395fn conformance_language_profile_citizen(cx: &mut Cx) -> Result<()> {
396    let profile = LanguageProfile::new(Symbol::qualified("standard-citizen", "profile"));
397    let value = cx
398        .factory()
399        .opaque(Arc::new(LanguageProfileValue::new(profile)))?;
400    sim_citizen::check_value_fixture(cx, value)
401}
402
403fn conformance_fidelity_badge_citizen(cx: &mut Cx) -> Result<()> {
404    let badge = FidelityBadge::new(
405        Ref::Symbol(Symbol::qualified("standard-citizen", "subject")),
406        Symbol::qualified("standard-citizen", "badge"),
407        2,
408        Ref::Symbol(Symbol::qualified("standard-citizen", "evidence")),
409    );
410    let value = cx
411        .factory()
412        .opaque(Arc::new(FidelityBadgeValue::new(badge)))?;
413    sim_citizen::check_value_fixture(cx, value)
414}
415
416sim_citizen::inventory::submit! {
417    sim_citizen::CitizenInfo {
418        symbol: "standard/Profile",
419        version: 0,
420        crate_name: env!("CARGO_PKG_NAME"),
421        arity: 10,
422        install: install_language_profile_citizen,
423        conformance: conformance_language_profile_citizen,
424    }
425}
426
427sim_citizen::inventory::submit! {
428    sim_citizen::CitizenInfo {
429        symbol: "standard/FidelityBadge",
430        version: 0,
431        crate_name: env!("CARGO_PKG_NAME"),
432        arity: 4,
433        install: install_fidelity_badge_citizen,
434        conformance: conformance_fidelity_badge_citizen,
435    }
436}
437
438fn class_value_or_stub(cx: &mut Cx, id: ClassId, symbol: Symbol) -> Result<Value> {
439    if let Some(value) = cx.registry().class_by_symbol(&symbol) {
440        return Ok(value.clone());
441    }
442    cx.factory().class_stub(id, symbol)
443}