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