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}