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.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.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.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.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.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.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.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 class id from the catalog sequence.
391    pub fn fresh_class_id(&mut self) -> ClassId {
392        ClassId(self.reserve_catalog_sequence_id(catalog::SEQ_CLASS))
393    }
394
395    pub(crate) fn reserve_class_id(&mut self, id: ClassId) {
396        self.reserve_catalog_sequence_at_least(catalog::SEQ_CLASS, id.0);
397    }
398
399    /// Reserves a fresh stable function id from the catalog sequence.
400    pub fn fresh_function_id(&mut self) -> FunctionId {
401        FunctionId(self.reserve_catalog_sequence_id(catalog::SEQ_FUNCTION))
402    }
403
404    /// Reserves a fresh stable macro id from the catalog sequence.
405    pub fn fresh_macro_id(&mut self) -> MacroId {
406        MacroId(self.reserve_catalog_sequence_id(catalog::SEQ_MACRO))
407    }
408
409    /// Reserves a fresh stable case id from the catalog sequence.
410    pub fn fresh_case_id(&mut self) -> CaseId {
411        CaseId(self.reserve_catalog_sequence_id(catalog::SEQ_CASE))
412    }
413
414    /// Reserves a fresh stable shape id from the catalog sequence.
415    pub fn fresh_shape_id(&mut self) -> ShapeId {
416        ShapeId(self.reserve_catalog_sequence_id(catalog::SEQ_SHAPE))
417    }
418
419    /// Reserves a fresh stable codec id from the catalog sequence.
420    pub fn fresh_codec_id(&mut self) -> CodecId {
421        CodecId(self.reserve_catalog_sequence_id(catalog::SEQ_CODEC))
422    }
423
424    /// Reserves a fresh stable number-domain id from the catalog sequence.
425    pub fn fresh_number_domain_id(&mut self) -> NumberDomainId {
426        NumberDomainId(self.reserve_catalog_sequence_id(catalog::SEQ_NUMBER_DOMAIN))
427    }
428
429    /// Reserves a fresh stable site id from the catalog sequence.
430    pub fn fresh_site_id(&mut self) -> SiteId {
431        SiteId(self.reserve_catalog_sequence_id(catalog::SEQ_SITE))
432    }
433
434    pub(crate) fn insert_runtime_export(
435        &mut self,
436        kind: ExportKind,
437        symbol: Symbol,
438        id: RuntimeId,
439    ) {
440        self.export_symbols
441            .entry(kind)
442            .or_default()
443            .insert(symbol, id);
444    }
445
446    fn register_runtime_value(
447        &mut self,
448        symbol: Symbol,
449        value: Value,
450        kind: ExportKind,
451        runtime_id: RuntimeId,
452    ) -> Result<()> {
453        self.commit_direct_runtime_registration(
454            kind.clone(),
455            symbol.clone(),
456            runtime_id,
457            value.clone(),
458        )?;
459        match runtime_id {
460            RuntimeId::Class(id) => {
461                self.class_symbol_cache.insert(symbol.clone(), id);
462                self.class_value_cache.insert(id, value);
463            }
464            RuntimeId::Function(id) => {
465                self.function_symbol_cache.insert(symbol.clone(), id);
466                self.function_value_cache.insert(id, value);
467            }
468            RuntimeId::Macro(id) => {
469                self.macro_symbol_cache.insert(symbol.clone(), id);
470                self.macro_value_cache.insert(id, value);
471            }
472            RuntimeId::Shape(id) => {
473                self.shape_symbol_cache.insert(symbol.clone(), id);
474                self.shape_value_cache.insert(id, value);
475            }
476            RuntimeId::Codec(id) => {
477                self.codec_symbol_cache.insert(symbol.clone(), id);
478                self.codec_value_cache.insert(id, value);
479            }
480            RuntimeId::NumberDomain(id) => {
481                self.insert_number_domain_value(symbol.clone(), id, value);
482            }
483            RuntimeId::Site(id) => {
484                self.site_symbol_cache.insert(symbol.clone(), id);
485                self.site_value_cache.insert(id, value);
486            }
487            RuntimeId::Value => {
488                self.plain_value_cache.insert(symbol.clone(), value);
489            }
490        }
491        self.insert_runtime_export(kind.clone(), symbol.clone(), runtime_id);
492        Ok(())
493    }
494
495    pub(crate) fn insert_number_domain_value(
496        &mut self,
497        symbol: Symbol,
498        id: NumberDomainId,
499        value: Value,
500    ) {
501        self.number_domain_symbol_cache.insert(symbol, id);
502        self.number_domain_value_cache.insert(id, value);
503        self.number_domain_order = None;
504    }
505
506    pub(crate) fn rebuild_number_domain_order(&mut self) {
507        let mut order = self
508            .number_domain_symbol_cache
509            .iter()
510            .filter_map(|(symbol, id)| {
511                let value = self.number_domain_value_cache.get(id)?;
512                let priority = value
513                    .object()
514                    .as_number_domain()
515                    .map(|domain| domain.parse_priority())
516                    .unwrap_or(0);
517                Some((priority, symbol.clone(), *id))
518            })
519            .collect::<Vec<_>>();
520        order.sort_by(
521            |(left_priority, left_symbol, _), (right_priority, right_symbol, _)| {
522                right_priority
523                    .cmp(left_priority)
524                    .then_with(|| left_symbol.cmp(right_symbol))
525            },
526        );
527        self.number_domain_order = Some(order.into_iter().map(|(_, _, id)| id).collect());
528    }
529}