Skip to main content

sim_kernel/
number_domain.rs

1//! The number-domain contract: the pluggable numeric backend protocol.
2//!
3//! The kernel defines the [`NumberDomain`] protocol, value and operation
4//! contracts, and promotion rules; concrete number domains and arithmetic are
5//! libs loaded against it, never kernel-fixed behavior.
6
7use crate::{
8    env::Cx,
9    error::Result,
10    expr::NumberLiteral,
11    id::Symbol,
12    value::{RuntimeObject, Value},
13};
14
15/// Bounds on the promotion-path search between number domains.
16///
17/// # Examples
18///
19/// ```
20/// use sim_kernel::PromotionSearchLimits;
21///
22/// let limits = PromotionSearchLimits::default();
23/// assert_eq!(limits.max_depth, 8);
24/// assert_eq!(limits.max_states, 256);
25/// ```
26#[derive(Clone, Copy, Debug, PartialEq, Eq)]
27pub struct PromotionSearchLimits {
28    /// Maximum number of conversion hops to chain.
29    pub max_depth: usize,
30    /// Maximum number of intermediate domain states to explore.
31    pub max_states: usize,
32}
33
34impl Default for PromotionSearchLimits {
35    fn default() -> Self {
36        Self {
37            max_depth: 8,
38            max_states: 256,
39        }
40    }
41}
42
43/// A literal-level conversion edge from one number domain to another.
44#[derive(Clone)]
45pub struct PromotionRule {
46    /// Domain the rule converts from.
47    pub from_domain: Symbol,
48    /// Domain the rule converts to.
49    pub to_domain: Symbol,
50    /// Relative cost, used to rank competing promotion paths.
51    pub cost: u16,
52    /// Converts a literal in `from_domain` to one in `to_domain`.
53    pub convert: fn(&mut Cx, NumberLiteral) -> Result<NumberLiteral>,
54}
55
56/// A literal-level binary operator keyed on both operand domains.
57#[derive(Clone)]
58pub struct NumberBinaryOp {
59    /// Operator symbol this rule implements.
60    pub operator: Symbol,
61    /// Required domain of the left operand.
62    pub left_domain: Symbol,
63    /// Required domain of the right operand.
64    pub right_domain: Symbol,
65    /// Relative cost, used to rank competing implementations.
66    pub cost: u16,
67    /// Applies the operator to two domain literals.
68    pub apply: fn(&mut Cx, NumberLiteral, NumberLiteral) -> Result<Value>,
69}
70
71/// A literal-level unary operator keyed on its operand domain.
72#[derive(Clone)]
73pub struct NumberUnaryOp {
74    /// Operator symbol this rule implements.
75    pub operator: Symbol,
76    /// Required domain of the operand.
77    pub operand_domain: Symbol,
78    /// Relative cost, used to rank competing implementations.
79    pub cost: u16,
80    /// Applies the operator to a single domain literal.
81    pub apply: fn(&mut Cx, NumberLiteral) -> Result<Value>,
82}
83
84/// A literal-level reduction over a homogeneous list of operands.
85#[derive(Clone)]
86pub struct NumberReductionOp {
87    /// Operator symbol this rule implements.
88    pub operator: Symbol,
89    /// Required domain of every operand.
90    pub operand_domain: Symbol,
91    /// Relative cost, used to rank competing implementations.
92    pub cost: u16,
93    /// Folds the operator over a vector of domain literals.
94    pub apply: fn(&mut Cx, Vec<NumberLiteral>) -> Result<Value>,
95}
96
97/// A runtime number value paired with its domain and optional literal form.
98#[derive(Clone)]
99pub struct NumberValueRef {
100    /// Domain symbol the value belongs to.
101    pub domain: Symbol,
102    /// The runtime value.
103    pub value: Value,
104    /// Canonical literal form, when one is available.
105    pub literal: Option<NumberLiteral>,
106}
107
108/// Protocol for runtime objects that present themselves as numbers.
109pub trait NumberValue: Send + Sync {
110    /// The number domain this value reports membership in.
111    fn number_domain(&self, cx: &mut Cx) -> Result<Symbol>;
112
113    /// Canonical literal form, or `Ok(None)` when not representable.
114    fn number_literal(&self, _cx: &mut Cx) -> Result<Option<NumberLiteral>> {
115        Ok(None)
116    }
117}
118
119/// A value-level conversion edge from one number domain to another.
120#[derive(Clone)]
121pub struct ValuePromotionRule {
122    /// Domain the rule converts from.
123    pub from_domain: Symbol,
124    /// Domain the rule converts to.
125    pub to_domain: Symbol,
126    /// Relative cost, used to rank competing promotion paths.
127    pub cost: u16,
128    /// Converts a value in `from_domain` to one in `to_domain`.
129    pub convert: fn(&mut Cx, Value) -> Result<Value>,
130}
131
132/// A value-level binary operator keyed on both operand domains.
133#[derive(Clone)]
134pub struct ValueNumberBinaryOp {
135    /// Operator symbol this rule implements.
136    pub operator: Symbol,
137    /// Required domain of the left operand.
138    pub left_domain: Symbol,
139    /// Required domain of the right operand.
140    pub right_domain: Symbol,
141    /// Relative cost, used to rank competing implementations.
142    pub cost: u16,
143    /// Applies the operator to two domain values.
144    pub apply: fn(&mut Cx, Value, Value) -> Result<Value>,
145}
146
147/// A value-level unary operator keyed on its operand domain.
148#[derive(Clone)]
149pub struct ValueNumberUnaryOp {
150    /// Operator symbol this rule implements.
151    pub operator: Symbol,
152    /// Required domain of the operand.
153    pub operand_domain: Symbol,
154    /// Relative cost, used to rank competing implementations.
155    pub cost: u16,
156    /// Applies the operator to a single domain value.
157    pub apply: fn(&mut Cx, Value) -> Result<Value>,
158}
159
160/// A value-level reduction over a homogeneous list of operands.
161#[derive(Clone)]
162pub struct ValueNumberReductionOp {
163    /// Operator symbol this rule implements.
164    pub operator: Symbol,
165    /// Required domain of every operand.
166    pub operand_domain: Symbol,
167    /// Relative cost, used to rank competing implementations.
168    pub cost: u16,
169    /// Folds the operator over a vector of domain values.
170    pub apply: fn(&mut Cx, Vec<Value>) -> Result<Value>,
171}
172
173/// Runtime protocol for a numeric semantic domain.
174///
175/// A number domain owns parsing, canonical encoding, and promotion rules for a
176/// family of number literals. Domains are runtime objects so they can be
177/// registered, reflected, browsed, and exported like other SIM values.
178pub trait NumberDomain: RuntimeObject {
179    /// Symbol uniquely identifying this domain.
180    fn symbol(&self) -> Symbol;
181
182    /// Tie-breaking priority when several domains can parse the same text.
183    fn parse_priority(&self) -> i32 {
184        0
185    }
186
187    /// Parses literal text into a domain value, or `Ok(None)` if not ours.
188    fn parse_literal(&self, cx: &mut Cx, text: &str) -> Result<Option<Value>>;
189
190    /// Encodes a value back to a canonical literal, or `Ok(None)` if not ours.
191    fn encode_literal(&self, cx: &mut Cx, value: Value) -> Result<Option<NumberLiteral>>;
192
193    /// Promotion rules originating from this domain; empty by default.
194    fn promotions(&self) -> Vec<PromotionRule> {
195        Vec::new()
196    }
197}