Skip to main content

sim_kernel/library/registry/
register.rs

1use crate::{
2    error::{Error, Result},
3    id::{
4        CaseId, ClassId, CodecId, FunctionId, LibId, MacroId, NumberDomainId, RuntimeId, ShapeId,
5        SiteId, Symbol,
6    },
7    number_domain::{
8        NumberBinaryOp, NumberReductionOp, NumberUnaryOp, ValueNumberBinaryOp,
9        ValueNumberReductionOp, ValueNumberUnaryOp, ValuePromotionRule,
10    },
11    value::Value,
12};
13
14use super::{Registry, catalog};
15use crate::library::{ExportKind, ExportRecord, ExportState, RegisteredTest, Test};
16
17impl Registry {
18    /// Registers a class value directly, reserving a fresh class id; errors on a
19    /// duplicate class symbol.
20    ///
21    /// # Examples
22    ///
23    /// ```
24    /// use std::sync::Arc;
25    /// use sim_kernel::library::Registry;
26    /// use sim_kernel::{Cx, DefaultFactory, NoopEvalPolicy, Symbol};
27    ///
28    /// let mut cx = Cx::new(
29    ///     Arc::new(NoopEvalPolicy),
30    ///     Arc::new(DefaultFactory),
31    ///     sim_kernel::HandleSeed::new(7),
32    /// );
33    /// let class = cx.factory().bool(true).unwrap();
34    ///
35    /// let mut registry = Registry::default();
36    /// let id = registry
37    ///     .register_class_value(Symbol::new("flag"), class.clone())
38    ///     .unwrap();
39    ///
40    /// assert_eq!(registry.class_by_symbol(&Symbol::new("flag")), Some(&class));
41    /// assert_eq!(registry.class_value(id), Some(&class));
42    ///
43    /// // A duplicate class symbol is rejected.
44    /// assert!(registry.register_class_value(Symbol::new("flag"), class).is_err());
45    /// ```
46    pub fn register_class_value(&mut self, symbol: Symbol, value: Value) -> Result<ClassId> {
47        let kind = ExportKind::named(ExportKind::CLASS);
48        if self.export_row_by_kind_symbol(&kind, &symbol).is_some() {
49            return Err(Error::DuplicateExport {
50                kind: "class",
51                symbol,
52            });
53        }
54        let id = self.try_fresh_class_id()?;
55        self.register_runtime_value(symbol, value, kind, RuntimeId::Class(id))?;
56        Ok(id)
57    }
58
59    /// Registers a function value directly, reserving a fresh function id;
60    /// errors on a duplicate function symbol.
61    pub fn register_function_value(&mut self, symbol: Symbol, value: Value) -> Result<FunctionId> {
62        let kind = ExportKind::named(ExportKind::FUNCTION);
63        if self.export_row_by_kind_symbol(&kind, &symbol).is_some() {
64            return Err(Error::DuplicateExport {
65                kind: "function",
66                symbol,
67            });
68        }
69        let id = self.try_fresh_function_id()?;
70        self.register_runtime_value(symbol, value, kind, RuntimeId::Function(id))?;
71        Ok(id)
72    }
73
74    /// Registers a macro value directly, reserving a fresh macro id; errors on a
75    /// duplicate macro symbol.
76    pub fn register_macro_value(&mut self, symbol: Symbol, value: Value) -> Result<MacroId> {
77        let kind = ExportKind::named(ExportKind::MACRO);
78        if self.export_row_by_kind_symbol(&kind, &symbol).is_some() {
79            return Err(Error::DuplicateExport {
80                kind: "macro",
81                symbol,
82            });
83        }
84        let id = self.try_fresh_macro_id()?;
85        self.register_runtime_value(symbol, value, kind, RuntimeId::Macro(id))?;
86        Ok(id)
87    }
88
89    /// Registers a shape value directly, reserving a fresh shape id; errors on a
90    /// duplicate shape symbol.
91    pub fn register_shape_value(&mut self, symbol: Symbol, value: Value) -> Result<ShapeId> {
92        let kind = ExportKind::named(ExportKind::SHAPE);
93        if self.export_row_by_kind_symbol(&kind, &symbol).is_some() {
94            return Err(Error::DuplicateExport {
95                kind: "shape",
96                symbol,
97            });
98        }
99        let id = self.try_fresh_shape_id()?;
100        self.register_runtime_value(symbol, value, kind, RuntimeId::Shape(id))?;
101        Ok(id)
102    }
103
104    /// Registers a codec value directly, reserving a fresh codec id; errors on a
105    /// duplicate codec symbol.
106    pub fn register_codec_value(&mut self, symbol: Symbol, value: Value) -> Result<CodecId> {
107        let kind = ExportKind::named(ExportKind::CODEC);
108        if self.export_row_by_kind_symbol(&kind, &symbol).is_some() {
109            return Err(Error::DuplicateExport {
110                kind: "codec",
111                symbol,
112            });
113        }
114        let id = self.try_fresh_codec_id()?;
115        self.register_runtime_value(symbol, value, kind, RuntimeId::Codec(id))?;
116        Ok(id)
117    }
118
119    /// Registers a number-domain value directly, reserving a fresh id; errors on
120    /// a duplicate number-domain symbol.
121    pub fn register_number_domain_value(
122        &mut self,
123        symbol: Symbol,
124        value: Value,
125    ) -> Result<NumberDomainId> {
126        let kind = ExportKind::named(ExportKind::NUMBER_DOMAIN);
127        if self.export_row_by_kind_symbol(&kind, &symbol).is_some() {
128            return Err(Error::DuplicateExport {
129                kind: "number-domain",
130                symbol,
131            });
132        }
133        let id = self.try_fresh_number_domain_id()?;
134        self.register_runtime_value(symbol, value, kind, RuntimeId::NumberDomain(id))?;
135        Ok(id)
136    }
137
138    /// Registers an opaque site value directly, reserving a fresh site id;
139    /// errors on a duplicate site symbol.
140    pub fn register_site_value(&mut self, symbol: Symbol, value: Value) -> Result<RuntimeId> {
141        let kind = ExportKind::named(ExportKind::SITE);
142        if self.export_row_by_kind_symbol(&kind, &symbol).is_some() {
143            return Err(Error::DuplicateExport {
144                kind: "site",
145                symbol,
146            });
147        }
148        let id = self.try_fresh_site_id()?;
149        let runtime_id = RuntimeId::Site(id);
150        self.register_runtime_value(symbol, value, kind, runtime_id)?;
151        Ok(runtime_id)
152    }
153
154    /// Returns registered number domains ordered by parse priority (descending),
155    /// then by symbol; the order is computed once and cached.
156    pub fn sorted_number_domains(&mut self) -> Vec<(Symbol, Value)> {
157        if self.number_domain_order.is_none() {
158            self.rebuild_number_domain_order();
159        }
160
161        self.number_domain_order
162            .as_ref()
163            .into_iter()
164            .flatten()
165            .filter_map(|id| {
166                let symbol = self
167                    .number_domain_symbol_cache
168                    .iter()
169                    .find_map(|(symbol, candidate)| (*candidate == *id).then(|| symbol.clone()))?;
170                let value = self.number_domain_value_cache.get(id)?.clone();
171                Some((symbol, value))
172            })
173            .collect()
174    }
175
176    /// Registers a library-supplied test under `symbol`; errors on a duplicate
177    /// test symbol.
178    pub fn register_test(
179        &mut self,
180        symbol: Symbol,
181        lib: Symbol,
182        test: std::sync::Arc<dyn Test>,
183        subjects: Vec<Symbol>,
184    ) -> Result<()> {
185        if self.catalog_test_by_symbol(&symbol).is_some() {
186            return Err(Error::DuplicateExport {
187                kind: "test",
188                symbol,
189            });
190        }
191        self.commit_direct_test_registration(
192            symbol.clone(),
193            lib.clone(),
194            test.clone(),
195            subjects.clone(),
196        )?;
197        self.tests.insert(
198            symbol.clone(),
199            RegisteredTest {
200                symbol: symbol.clone(),
201                lib: lib.clone(),
202                test,
203                subjects,
204            },
205        );
206        self.tests_by_lib
207            .entry(lib)
208            .or_default()
209            .push(symbol.clone());
210        Ok(())
211    }
212
213    /// Registers a plain value export by symbol; errors on a duplicate value
214    /// symbol.
215    pub fn register_value(&mut self, symbol: Symbol, value: Value) -> Result<()> {
216        let kind = ExportKind::named(ExportKind::VALUE);
217        if self.export_row_by_kind_symbol(&kind, &symbol).is_some() {
218            return Err(Error::DuplicateExport {
219                kind: "value",
220                symbol,
221            });
222        }
223        self.register_runtime_value(symbol, value, kind, RuntimeId::Value)?;
224        Ok(())
225    }
226
227    /// Registers a plain value and records it as a resolved export of `lib`.
228    pub fn register_value_for_lib(
229        &mut self,
230        lib: &Symbol,
231        symbol: Symbol,
232        value: Value,
233    ) -> Result<()> {
234        self.register_value(symbol.clone(), value)?;
235        self.append_export_record(
236            lib,
237            ExportRecord {
238                kind: ExportKind::named(ExportKind::VALUE),
239                symbol,
240                state: ExportState::Resolved {
241                    id: RuntimeId::Value,
242                },
243            },
244        )
245    }
246
247    /// Appends an export record to an already-loaded library, indexing it as a
248    /// runtime export when resolved; errors on an unknown lib or duplicate
249    /// kind+symbol.
250    pub fn append_export_record(&mut self, lib: &Symbol, record: ExportRecord) -> Result<()> {
251        let kind = record.kind.clone();
252        let symbol = record.symbol.clone();
253        let runtime_id = match &record.state {
254            ExportState::Resolved { id } => Some(*id),
255            _ => None,
256        };
257
258        let Some(loaded) = self.lib_mut(lib) else {
259            return Err(Error::Lib(format!("unknown lib {lib}")));
260        };
261        if loaded
262            .exports
263            .iter()
264            .any(|existing| existing.kind == kind && existing.symbol == symbol)
265        {
266            return Err(Error::DuplicateExport {
267                kind: kind.duplicate_error_kind(),
268                symbol,
269            });
270        }
271        loaded.exports.push(record);
272        if let Some(runtime_id) = runtime_id {
273            self.insert_runtime_export(kind, symbol, runtime_id);
274        }
275        Ok(())
276    }
277
278    /// Registers a typed number binary operator.
279    pub fn register_number_binary_op(&mut self, op: NumberBinaryOp) {
280        self.number_binary_ops.push(op);
281    }
282
283    /// Registers a value-level number binary operator.
284    pub fn register_value_number_binary_op(&mut self, op: ValueNumberBinaryOp) {
285        self.value_number_binary_ops.push(op);
286    }
287
288    /// Registers a typed number unary operator.
289    pub fn register_number_unary_op(&mut self, op: NumberUnaryOp) {
290        self.number_unary_ops.push(op);
291    }
292
293    /// Registers a value-level number unary operator.
294    pub fn register_value_number_unary_op(&mut self, op: ValueNumberUnaryOp) {
295        self.value_number_unary_ops.push(op);
296    }
297
298    /// Registers a typed number reduction operator.
299    pub fn register_number_reduction_op(&mut self, op: NumberReductionOp) {
300        self.number_reduction_ops.push(op);
301    }
302
303    /// Registers a value-level number reduction operator.
304    pub fn register_value_number_reduction_op(&mut self, op: ValueNumberReductionOp) {
305        self.value_number_reduction_ops.push(op);
306    }
307
308    /// Registers a typed number-domain promotion rule.
309    pub fn register_promotion_rule(&mut self, rule: crate::number_domain::PromotionRule) {
310        self.promotion_rules.push(rule);
311    }
312
313    /// Registers a value-level number-domain promotion rule.
314    pub fn register_value_promotion_rule(&mut self, rule: ValuePromotionRule) {
315        self.value_promotion_rules.push(rule);
316    }
317
318    /// Returns the cheapest promotion rule from `from_domain` to `to_domain`, if
319    /// any.
320    pub fn promotion_rule(
321        &self,
322        from_domain: &Symbol,
323        to_domain: &Symbol,
324    ) -> Option<&crate::number_domain::PromotionRule> {
325        self.promotion_rules
326            .iter()
327            .filter(|rule| &rule.from_domain == from_domain && &rule.to_domain == to_domain)
328            .min_by_key(|rule| rule.cost)
329    }
330
331    /// All registered typed promotion rules.
332    pub fn promotion_rules(&self) -> &[crate::number_domain::PromotionRule] {
333        &self.promotion_rules
334    }
335
336    /// All registered value-level promotion rules.
337    pub fn value_promotion_rules(&self) -> &[ValuePromotionRule] {
338        &self.value_promotion_rules
339    }
340
341    /// All registered typed number binary operators.
342    pub fn number_binary_ops(&self) -> &[NumberBinaryOp] {
343        &self.number_binary_ops
344    }
345
346    /// All registered value-level number binary operators.
347    pub fn value_number_binary_ops(&self) -> &[ValueNumberBinaryOp] {
348        &self.value_number_binary_ops
349    }
350
351    /// All registered typed number unary operators.
352    pub fn number_unary_ops(&self) -> &[NumberUnaryOp] {
353        &self.number_unary_ops
354    }
355
356    /// All registered value-level number unary operators.
357    pub fn value_number_unary_ops(&self) -> &[ValueNumberUnaryOp] {
358        &self.value_number_unary_ops
359    }
360
361    /// All registered typed number reduction operators.
362    pub fn number_reduction_ops(&self) -> &[NumberReductionOp] {
363        &self.number_reduction_ops
364    }
365
366    /// All registered value-level number reduction operators.
367    pub fn value_number_reduction_ops(&self) -> &[ValueNumberReductionOp] {
368        &self.value_number_reduction_ops
369    }
370
371    /// Returns the cheapest typed binary operator matching the operator and
372    /// operand domains, if any.
373    pub fn number_binary_op(
374        &self,
375        operator: &Symbol,
376        left_domain: &Symbol,
377        right_domain: &Symbol,
378    ) -> Option<&NumberBinaryOp> {
379        self.number_binary_ops
380            .iter()
381            .filter(|op| {
382                &op.operator == operator
383                    && &op.left_domain == left_domain
384                    && &op.right_domain == right_domain
385            })
386            .min_by_key(|op| op.cost)
387    }
388
389    /// Reserves a fresh stable library id from the catalog sequence.
390    pub fn fresh_lib_id(&mut self) -> LibId {
391        LibId(self.reserve_catalog_sequence_id(catalog::SEQ_LIB))
392    }
393
394    /// Reserves a fresh stable library id, reporting catalog sequence failures.
395    pub fn try_fresh_lib_id(&mut self) -> Result<LibId> {
396        self.try_reserve_catalog_sequence_id(catalog::SEQ_LIB)
397            .map(LibId)
398    }
399
400    /// Reserves a fresh stable class id from the catalog sequence.
401    pub fn fresh_class_id(&mut self) -> ClassId {
402        ClassId(self.reserve_catalog_sequence_id(catalog::SEQ_CLASS))
403    }
404
405    /// Reserves a fresh stable class id, reporting catalog sequence failures.
406    pub fn try_fresh_class_id(&mut self) -> Result<ClassId> {
407        self.try_reserve_catalog_sequence_id(catalog::SEQ_CLASS)
408            .map(ClassId)
409    }
410
411    pub(crate) fn reserve_class_id(&mut self, id: ClassId) -> Result<()> {
412        self.try_reserve_catalog_sequence_at_least(catalog::SEQ_CLASS, id.0)
413    }
414
415    /// Reserves a fresh stable function id from the catalog sequence.
416    pub fn fresh_function_id(&mut self) -> FunctionId {
417        FunctionId(self.reserve_catalog_sequence_id(catalog::SEQ_FUNCTION))
418    }
419
420    /// Reserves a fresh stable function id, reporting catalog sequence failures.
421    pub fn try_fresh_function_id(&mut self) -> Result<FunctionId> {
422        self.try_reserve_catalog_sequence_id(catalog::SEQ_FUNCTION)
423            .map(FunctionId)
424    }
425
426    pub(crate) fn reserve_function_id(&mut self, id: FunctionId) -> Result<()> {
427        self.try_reserve_catalog_sequence_at_least(catalog::SEQ_FUNCTION, id.0)
428    }
429
430    /// Reserves a fresh stable macro id from the catalog sequence.
431    pub fn fresh_macro_id(&mut self) -> MacroId {
432        MacroId(self.reserve_catalog_sequence_id(catalog::SEQ_MACRO))
433    }
434
435    /// Reserves a fresh stable macro id, reporting catalog sequence failures.
436    pub fn try_fresh_macro_id(&mut self) -> Result<MacroId> {
437        self.try_reserve_catalog_sequence_id(catalog::SEQ_MACRO)
438            .map(MacroId)
439    }
440
441    pub(crate) fn reserve_macro_id(&mut self, id: MacroId) -> Result<()> {
442        self.try_reserve_catalog_sequence_at_least(catalog::SEQ_MACRO, id.0)
443    }
444
445    /// Reserves a fresh stable case id from the catalog sequence.
446    pub fn fresh_case_id(&mut self) -> CaseId {
447        CaseId(self.reserve_catalog_sequence_id(catalog::SEQ_CASE))
448    }
449
450    /// Reserves a fresh stable case id, reporting catalog sequence failures.
451    pub fn try_fresh_case_id(&mut self) -> Result<CaseId> {
452        self.try_reserve_catalog_sequence_id(catalog::SEQ_CASE)
453            .map(CaseId)
454    }
455
456    /// Reserves a fresh stable shape id from the catalog sequence.
457    pub fn fresh_shape_id(&mut self) -> ShapeId {
458        ShapeId(self.reserve_catalog_sequence_id(catalog::SEQ_SHAPE))
459    }
460
461    /// Reserves a fresh stable shape id, reporting catalog sequence failures.
462    pub fn try_fresh_shape_id(&mut self) -> Result<ShapeId> {
463        self.try_reserve_catalog_sequence_id(catalog::SEQ_SHAPE)
464            .map(ShapeId)
465    }
466
467    pub(crate) fn reserve_shape_id(&mut self, id: ShapeId) -> Result<()> {
468        self.try_reserve_catalog_sequence_at_least(catalog::SEQ_SHAPE, id.0)
469    }
470
471    /// Reserves a fresh stable codec id from the catalog sequence.
472    pub fn fresh_codec_id(&mut self) -> CodecId {
473        CodecId(self.reserve_catalog_sequence_id(catalog::SEQ_CODEC))
474    }
475
476    /// Reserves a fresh stable codec id, reporting catalog sequence failures.
477    pub fn try_fresh_codec_id(&mut self) -> Result<CodecId> {
478        self.try_reserve_catalog_sequence_id(catalog::SEQ_CODEC)
479            .map(CodecId)
480    }
481
482    pub(crate) fn reserve_codec_id(&mut self, id: CodecId) -> Result<()> {
483        self.try_reserve_catalog_sequence_at_least(catalog::SEQ_CODEC, id.0)
484    }
485
486    /// Reserves a fresh stable number-domain id from the catalog sequence.
487    pub fn fresh_number_domain_id(&mut self) -> NumberDomainId {
488        NumberDomainId(self.reserve_catalog_sequence_id(catalog::SEQ_NUMBER_DOMAIN))
489    }
490
491    /// Reserves a fresh stable number-domain id, reporting catalog sequence
492    /// failures.
493    pub fn try_fresh_number_domain_id(&mut self) -> Result<NumberDomainId> {
494        self.try_reserve_catalog_sequence_id(catalog::SEQ_NUMBER_DOMAIN)
495            .map(NumberDomainId)
496    }
497
498    pub(crate) fn reserve_number_domain_id(&mut self, id: NumberDomainId) -> Result<()> {
499        self.try_reserve_catalog_sequence_at_least(catalog::SEQ_NUMBER_DOMAIN, id.0)
500    }
501
502    /// Reserves a fresh stable site id from the catalog sequence.
503    pub fn fresh_site_id(&mut self) -> SiteId {
504        SiteId(self.reserve_catalog_sequence_id(catalog::SEQ_SITE))
505    }
506
507    /// Reserves a fresh stable site id, reporting catalog sequence failures.
508    pub fn try_fresh_site_id(&mut self) -> Result<SiteId> {
509        self.try_reserve_catalog_sequence_id(catalog::SEQ_SITE)
510            .map(SiteId)
511    }
512
513    pub(crate) fn reserve_site_id(&mut self, id: SiteId) -> Result<()> {
514        self.try_reserve_catalog_sequence_at_least(catalog::SEQ_SITE, id.0)
515    }
516
517    pub(crate) fn insert_runtime_export(
518        &mut self,
519        kind: ExportKind,
520        symbol: Symbol,
521        id: RuntimeId,
522    ) {
523        self.export_symbols
524            .entry(kind)
525            .or_default()
526            .insert(symbol, id);
527    }
528
529    fn register_runtime_value(
530        &mut self,
531        symbol: Symbol,
532        value: Value,
533        kind: ExportKind,
534        runtime_id: RuntimeId,
535    ) -> Result<()> {
536        self.commit_direct_runtime_registration(
537            kind.clone(),
538            symbol.clone(),
539            runtime_id,
540            value.clone(),
541        )?;
542        match runtime_id {
543            RuntimeId::Class(id) => {
544                self.class_symbol_cache.insert(symbol.clone(), id);
545                self.class_value_cache.insert(id, value);
546            }
547            RuntimeId::Function(id) => {
548                self.function_symbol_cache.insert(symbol.clone(), id);
549                self.function_value_cache.insert(id, value);
550            }
551            RuntimeId::Macro(id) => {
552                self.macro_symbol_cache.insert(symbol.clone(), id);
553                self.macro_value_cache.insert(id, value);
554            }
555            RuntimeId::Shape(id) => {
556                self.shape_symbol_cache.insert(symbol.clone(), id);
557                self.shape_value_cache.insert(id, value);
558            }
559            RuntimeId::Codec(id) => {
560                self.codec_symbol_cache.insert(symbol.clone(), id);
561                self.codec_value_cache.insert(id, value);
562            }
563            RuntimeId::NumberDomain(id) => {
564                self.insert_number_domain_value(symbol.clone(), id, value);
565            }
566            RuntimeId::Site(id) => {
567                self.site_symbol_cache.insert(symbol.clone(), id);
568                self.site_value_cache.insert(id, value);
569            }
570            RuntimeId::Value => {
571                self.plain_value_cache.insert(symbol.clone(), value);
572            }
573        }
574        self.insert_runtime_export(kind.clone(), symbol.clone(), runtime_id);
575        Ok(())
576    }
577
578    pub(crate) fn insert_number_domain_value(
579        &mut self,
580        symbol: Symbol,
581        id: NumberDomainId,
582        value: Value,
583    ) {
584        self.number_domain_symbol_cache.insert(symbol, id);
585        self.number_domain_value_cache.insert(id, value);
586        self.number_domain_order = None;
587    }
588
589    pub(crate) fn rebuild_number_domain_order(&mut self) {
590        let mut order = self
591            .number_domain_symbol_cache
592            .iter()
593            .filter_map(|(symbol, id)| {
594                let value = self.number_domain_value_cache.get(id)?;
595                let priority = value
596                    .object()
597                    .as_number_domain()
598                    .map(|domain| domain.parse_priority())
599                    .unwrap_or(0);
600                Some((priority, symbol.clone(), *id))
601            })
602            .collect::<Vec<_>>();
603        order.sort_by(
604            |(left_priority, left_symbol, _), (right_priority, right_symbol, _)| {
605                right_priority
606                    .cmp(left_priority)
607                    .then_with(|| left_symbol.cmp(right_symbol))
608            },
609        );
610        self.number_domain_order = Some(order.into_iter().map(|(_, _, id)| id).collect());
611    }
612}