1use crate::{
2 error::{Error, Result},
3 id::Symbol,
4 number_domain::{
5 NumberBinaryOp, NumberReductionOp, NumberUnaryOp, NumberValueRef, ValueNumberBinaryOp,
6 ValueNumberReductionOp, ValueNumberUnaryOp,
7 },
8 value::Value,
9};
10
11use super::{
12 Cx,
13 selection::{CandidateSelection, ScoredCandidate, choose_best_candidate},
14};
15
16#[derive(Clone)]
17struct PreparedBinaryOp<T> {
18 op: T,
19 left: Value,
20 right: Value,
21}
22
23#[derive(Clone)]
24struct PreparedUnaryOp<T> {
25 op: T,
26 operand: Value,
27}
28
29#[derive(Clone)]
30struct PreparedReductionOp<T> {
31 op: T,
32 operands: Vec<Value>,
33}
34
35enum BinaryCandidate {
36 Literal(PreparedBinaryOp<NumberBinaryOp>),
37 Value(PreparedBinaryOp<ValueNumberBinaryOp>),
38}
39
40enum UnaryCandidate {
41 Literal(PreparedUnaryOp<NumberUnaryOp>),
42 Value(PreparedUnaryOp<ValueNumberUnaryOp>),
43}
44
45enum ReductionCandidate {
46 Literal(PreparedReductionOp<NumberReductionOp>),
47 Value(PreparedReductionOp<ValueNumberReductionOp>),
48}
49
50impl Cx {
51 pub fn parse_number_literal(&mut self, text: &str) -> Result<Option<crate::NumberLiteral>> {
58 let domains = self.registry_mut().sorted_number_domains();
59 for (_symbol, value) in domains {
60 let Some(domain) = value.object().as_number_domain() else {
61 continue;
62 };
63 let Some(parsed) = domain.parse_literal(self, text)? else {
64 continue;
65 };
66 let Some(number) = domain.encode_literal(self, parsed)? else {
67 return Err(Error::Eval(format!(
68 "number domain {} parsed {text} but cannot encode it back to a literal",
69 domain.symbol()
70 )));
71 };
72 return Ok(Some(number));
73 }
74
75 Ok(None)
76 }
77
78 pub fn encode_number_value(&mut self, value: Value) -> Result<Option<crate::NumberLiteral>> {
80 let domains = self.registry_mut().sorted_number_domains();
81 for (_symbol, domain_value) in domains {
82 let Some(domain) = domain_value.object().as_number_domain() else {
83 continue;
84 };
85 if let Some(number) = domain.encode_literal(self, value.clone())? {
86 return Ok(Some(number));
87 }
88 }
89 Ok(None)
90 }
91
92 pub fn number_value_ref(&mut self, value: Value) -> Result<Option<NumberValueRef>> {
94 if let Some(number) = value.object().as_number_value() {
95 let domain = number.number_domain(self)?;
96 let literal = number.number_literal(self)?;
97 return Ok(Some(NumberValueRef {
98 domain,
99 value,
100 literal,
101 }));
102 }
103
104 let Some(literal) = self.encode_number_value(value.clone())? else {
105 return Ok(None);
106 };
107 Ok(Some(NumberValueRef {
108 domain: literal.domain.clone(),
109 value,
110 literal: Some(literal),
111 }))
112 }
113
114 pub fn apply_value_number_binary_op(
119 &mut self,
120 operator: &Symbol,
121 left: Value,
122 right: Value,
123 ) -> Result<Value> {
124 let left_ref = self.require_number_value(operator, left.clone(), "left")?;
125 let right_ref = self.require_number_value(operator, right.clone(), "right")?;
126 let literal_candidates = self
127 .registry()
128 .number_binary_ops()
129 .iter()
130 .filter(|op| &op.operator == operator)
131 .cloned()
132 .collect::<Vec<_>>();
133 let value_candidates = self
134 .registry()
135 .value_number_binary_ops()
136 .iter()
137 .filter(|op| &op.operator == operator)
138 .cloned()
139 .collect::<Vec<_>>();
140 if literal_candidates.is_empty() && value_candidates.is_empty() {
141 return Err(Error::Eval(format!(
142 "operator {operator} has no registered number rules"
143 )));
144 }
145
146 let mut scored = Vec::new();
147 for op in literal_candidates {
148 let Some((cost, promoted_left, promoted_right)) =
149 self.promote_literal_operands(&op, left_ref.clone(), right_ref.clone())?
150 else {
151 continue;
152 };
153 scored.push(ScoredCandidate {
154 cost,
155 candidate: BinaryCandidate::Literal(PreparedBinaryOp {
156 op,
157 left: promoted_left,
158 right: promoted_right,
159 }),
160 });
161 }
162
163 for op in value_candidates {
164 let Some((cost, promoted_left, promoted_right)) =
165 self.promote_value_operands(&op, left_ref.clone(), right_ref.clone())?
166 else {
167 continue;
168 };
169 scored.push(ScoredCandidate {
170 cost,
171 candidate: BinaryCandidate::Value(PreparedBinaryOp {
172 op,
173 left: promoted_left,
174 right: promoted_right,
175 }),
176 });
177 }
178
179 match choose_best_candidate(scored) {
180 CandidateSelection::None => Err(Error::NoPromotionPath {
181 operator: operator.clone(),
182 left_domain: left_ref.domain,
183 right_domain: right_ref.domain,
184 }),
185 CandidateSelection::Unique(BinaryCandidate::Literal(best)) => {
186 let left = self.expect_literal(best.left)?;
187 let right = self.expect_literal(best.right)?;
188 (best.op.apply)(self, left, right)
189 }
190 CandidateSelection::Unique(BinaryCandidate::Value(best)) => {
191 (best.op.apply)(self, best.left, best.right)
192 }
193 CandidateSelection::Ambiguous(best) => Err(Error::AmbiguousNumberDispatch {
194 operator: operator.clone(),
195 candidates: best
196 .into_iter()
197 .map(|candidate| match candidate {
198 BinaryCandidate::Literal(candidate) => {
199 (candidate.op.left_domain, candidate.op.right_domain)
200 }
201 BinaryCandidate::Value(candidate) => {
202 (candidate.op.left_domain, candidate.op.right_domain)
203 }
204 })
205 .collect(),
206 }),
207 }
208 }
209
210 pub fn apply_number_binary_op(
214 &mut self,
215 operator: &Symbol,
216 left: crate::NumberLiteral,
217 right: crate::NumberLiteral,
218 ) -> Result<Value> {
219 let left_value = self.factory().number_literal(left.domain, left.canonical)?;
220 let right_value = self
221 .factory()
222 .number_literal(right.domain, right.canonical)?;
223 self.apply_value_number_binary_op(operator, left_value, right_value)
224 }
225
226 pub fn apply_value_number_unary_op(
229 &mut self,
230 operator: &Symbol,
231 operand: Value,
232 ) -> Result<Value> {
233 let operand_ref = self.require_number_value(operator, operand.clone(), "operand")?;
234 let literal_candidates = self
235 .registry()
236 .number_unary_ops()
237 .iter()
238 .filter(|op| &op.operator == operator)
239 .cloned()
240 .collect::<Vec<_>>();
241 let value_candidates = self
242 .registry()
243 .value_number_unary_ops()
244 .iter()
245 .filter(|op| &op.operator == operator)
246 .cloned()
247 .collect::<Vec<_>>();
248 if literal_candidates.is_empty() && value_candidates.is_empty() {
249 return Err(Error::Eval(format!(
250 "operator {operator} has no registered number rules"
251 )));
252 }
253
254 let mut scored = Vec::new();
255 for op in literal_candidates {
256 let Some((cost, promoted_operand)) =
257 self.promote_literal_operand(operand_ref.clone(), &op.operand_domain)?
258 else {
259 continue;
260 };
261 scored.push(ScoredCandidate {
262 cost: cost as u32 + op.cost as u32,
263 candidate: UnaryCandidate::Literal(PreparedUnaryOp {
264 op,
265 operand: promoted_operand,
266 }),
267 });
268 }
269
270 for op in value_candidates {
271 let Some((cost, promoted_operand)) =
272 self.promote_number_value(operand_ref.clone(), &op.operand_domain)?
273 else {
274 continue;
275 };
276 scored.push(ScoredCandidate {
277 cost: cost as u32 + op.cost as u32,
278 candidate: UnaryCandidate::Value(PreparedUnaryOp {
279 op,
280 operand: promoted_operand.value,
281 }),
282 });
283 }
284
285 match choose_best_candidate(scored) {
286 CandidateSelection::None => Err(Error::Eval(format!(
287 "operator {operator} has no registered number rule for {}",
288 operand_ref.domain
289 ))),
290 CandidateSelection::Unique(UnaryCandidate::Literal(best)) => {
291 let operand = self.expect_literal(best.operand)?;
292 (best.op.apply)(self, operand)
293 }
294 CandidateSelection::Unique(UnaryCandidate::Value(best)) => {
295 (best.op.apply)(self, best.operand)
296 }
297 CandidateSelection::Ambiguous(best) => Err(Error::AmbiguousNumberDispatch {
298 operator: operator.clone(),
299 candidates: best
300 .into_iter()
301 .map(|candidate| match candidate {
302 UnaryCandidate::Literal(candidate) => (
303 candidate.op.operand_domain.clone(),
304 candidate.op.operand_domain,
305 ),
306 UnaryCandidate::Value(candidate) => (
307 candidate.op.operand_domain.clone(),
308 candidate.op.operand_domain,
309 ),
310 })
311 .collect(),
312 }),
313 }
314 }
315
316 pub fn apply_number_unary_op(
320 &mut self,
321 operator: &Symbol,
322 operand: crate::NumberLiteral,
323 ) -> Result<Value> {
324 let operand = self
325 .factory()
326 .number_literal(operand.domain, operand.canonical)?;
327 self.apply_value_number_unary_op(operator, operand)
328 }
329
330 pub fn apply_value_number_reduction_op(
333 &mut self,
334 operator: &Symbol,
335 operands: Vec<Value>,
336 ) -> Result<Value> {
337 let operand_refs = operands
338 .into_iter()
339 .map(|operand| self.require_number_value(operator, operand, "operand"))
340 .collect::<Result<Vec<_>>>()?;
341 let literal_candidates = self
342 .registry()
343 .number_reduction_ops()
344 .iter()
345 .filter(|op| &op.operator == operator)
346 .cloned()
347 .collect::<Vec<_>>();
348 let value_candidates = self
349 .registry()
350 .value_number_reduction_ops()
351 .iter()
352 .filter(|op| &op.operator == operator)
353 .cloned()
354 .collect::<Vec<_>>();
355 if literal_candidates.is_empty() && value_candidates.is_empty() {
356 return Err(Error::Eval(format!(
357 "operator {operator} has no registered number rules"
358 )));
359 }
360
361 let mut scored = Vec::new();
362 for op in literal_candidates {
363 let Some((cost, promoted_operands)) = self
364 .promote_literal_operands_for_reduction(operand_refs.clone(), &op.operand_domain)?
365 else {
366 continue;
367 };
368 scored.push(ScoredCandidate {
369 cost: cost as u32 + op.cost as u32,
370 candidate: ReductionCandidate::Literal(PreparedReductionOp {
371 op,
372 operands: promoted_operands,
373 }),
374 });
375 }
376
377 for op in value_candidates {
378 let Some((cost, promoted_operands)) =
379 self.promote_number_values(operand_refs.clone(), &op.operand_domain)?
380 else {
381 continue;
382 };
383 scored.push(ScoredCandidate {
384 cost: cost as u32 + op.cost as u32,
385 candidate: ReductionCandidate::Value(PreparedReductionOp {
386 op,
387 operands: promoted_operands
388 .into_iter()
389 .map(|item| item.value)
390 .collect(),
391 }),
392 });
393 }
394
395 let domains = operand_refs
396 .iter()
397 .map(|operand| operand.domain.to_string())
398 .collect::<Vec<_>>()
399 .join(", ");
400 match choose_best_candidate(scored) {
401 CandidateSelection::None => Err(Error::Eval(format!(
402 "operator {operator} has no registered number rule for [{domains}]"
403 ))),
404 CandidateSelection::Unique(ReductionCandidate::Literal(best)) => {
405 let operands = best
406 .operands
407 .into_iter()
408 .map(|operand| self.expect_literal(operand))
409 .collect::<Result<Vec<_>>>()?;
410 (best.op.apply)(self, operands)
411 }
412 CandidateSelection::Unique(ReductionCandidate::Value(best)) => {
413 (best.op.apply)(self, best.operands)
414 }
415 CandidateSelection::Ambiguous(best) => Err(Error::AmbiguousNumberDispatch {
416 operator: operator.clone(),
417 candidates: best
418 .into_iter()
419 .map(|candidate| match candidate {
420 ReductionCandidate::Literal(candidate) => (
421 candidate.op.operand_domain.clone(),
422 candidate.op.operand_domain,
423 ),
424 ReductionCandidate::Value(candidate) => (
425 candidate.op.operand_domain.clone(),
426 candidate.op.operand_domain,
427 ),
428 })
429 .collect(),
430 }),
431 }
432 }
433
434 pub fn apply_number_reduction_op(
438 &mut self,
439 operator: &Symbol,
440 operands: Vec<crate::NumberLiteral>,
441 ) -> Result<Value> {
442 let operands = operands
443 .into_iter()
444 .map(|operand| {
445 self.factory()
446 .number_literal(operand.domain, operand.canonical)
447 })
448 .collect::<Result<Vec<_>>>()?;
449 self.apply_value_number_reduction_op(operator, operands)
450 }
451
452 fn require_number_value(
453 &mut self,
454 operator: &Symbol,
455 value: Value,
456 side: &str,
457 ) -> Result<NumberValueRef> {
458 self.number_value_ref(value)?.ok_or_else(|| {
459 Error::Eval(format!(
460 "operator {operator} {side} operand is not a registered number"
461 ))
462 })
463 }
464
465 fn expect_literal(&mut self, value: Value) -> Result<crate::NumberLiteral> {
466 self.number_value_ref(value)?.and_then(|number| number.literal).ok_or_else(|| {
467 Error::Eval("value-level numeric dispatch selected a literal-only rule for a non-literal value".to_owned())
468 })
469 }
470}