1use crate::prelude::*;
2use std::collections::HashMap;
3
4enum PendingRight {
6 Add(Obj),
7 Sub(Obj),
8 Mul(Obj),
9 Div(Obj),
10}
11
12#[derive(Copy, Clone)]
13enum BinaryCombineOp {
14 Add,
15 Sub,
16 Mul,
17 Div,
18}
19
20impl Runtime {
21 fn object_supported_by_eval_stmt(obj: &Obj) -> bool {
22 matches!(
23 obj,
24 Obj::Number(_)
25 | Obj::FnObj(_)
26 | Obj::Add(_)
27 | Obj::Sub(_)
28 | Obj::Mul(_)
29 | Obj::Div(_)
30 | Obj::Pow(_)
31 | Obj::Sum(_)
32 | Obj::Product(_)
33 | Obj::MatrixListObj(_)
34 | Obj::MatrixAdd(_)
35 | Obj::MatrixSub(_)
36 | Obj::MatrixMul(_)
37 | Obj::MatrixScalarMul(_)
38 | Obj::MatrixPow(_)
39 | Obj::Atom(AtomObj::Identifier(_))
40 )
41 }
42
43 fn summand_as_unary_anonymous_fn_cloned(obj: &Obj) -> Option<AnonymousFn> {
45 match obj {
46 Obj::AnonymousFn(af) => Some(af.clone()),
47 Obj::FnObj(fo) => {
48 if !fo.body.is_empty() {
49 return None;
50 }
51 match fo.head.as_ref() {
52 FnObjHead::AnonymousFnLiteral(a) => Some((**a).clone()),
53 _ => None,
54 }
55 }
56 _ => None,
57 }
58 }
59
60 fn eval_reduce_nested_sum_product_in_obj(
63 &mut self,
64 obj: Obj,
65 eval_stmt: &EvalStmt,
66 ) -> Result<Obj, RuntimeError> {
67 match obj {
68 Obj::Sum(s) => self.eval_sum_or_product_for_eval_stmt(
69 s.start.as_ref(),
70 s.end.as_ref(),
71 s.func.as_ref(),
72 false,
73 eval_stmt,
74 ),
75 Obj::Product(p) => self.eval_sum_or_product_for_eval_stmt(
76 p.start.as_ref(),
77 p.end.as_ref(),
78 p.func.as_ref(),
79 true,
80 eval_stmt,
81 ),
82 Obj::Add(b) => {
83 let l = self.eval_reduce_nested_sum_product_in_obj((*b.left).clone(), eval_stmt)?;
84 let r =
85 self.eval_reduce_nested_sum_product_in_obj((*b.right).clone(), eval_stmt)?;
86 Ok(Add::new(l, r).into())
87 }
88 Obj::Sub(b) => {
89 let l = self.eval_reduce_nested_sum_product_in_obj((*b.left).clone(), eval_stmt)?;
90 let r =
91 self.eval_reduce_nested_sum_product_in_obj((*b.right).clone(), eval_stmt)?;
92 Ok(Sub::new(l, r).into())
93 }
94 Obj::Mul(b) => {
95 let l = self.eval_reduce_nested_sum_product_in_obj((*b.left).clone(), eval_stmt)?;
96 let r =
97 self.eval_reduce_nested_sum_product_in_obj((*b.right).clone(), eval_stmt)?;
98 Ok(Mul::new(l, r).into())
99 }
100 Obj::Div(b) => {
101 let l = self.eval_reduce_nested_sum_product_in_obj((*b.left).clone(), eval_stmt)?;
102 let r =
103 self.eval_reduce_nested_sum_product_in_obj((*b.right).clone(), eval_stmt)?;
104 Ok(Div::new(l, r).into())
105 }
106 Obj::Mod(m) => {
107 let l = self.eval_reduce_nested_sum_product_in_obj((*m.left).clone(), eval_stmt)?;
108 let r =
109 self.eval_reduce_nested_sum_product_in_obj((*m.right).clone(), eval_stmt)?;
110 Ok(Mod::new(l, r).into())
111 }
112 Obj::Pow(p) => {
113 let base =
114 self.eval_reduce_nested_sum_product_in_obj((*p.base).clone(), eval_stmt)?;
115 let exp =
116 self.eval_reduce_nested_sum_product_in_obj((*p.exponent).clone(), eval_stmt)?;
117 Ok(Pow::new(base, exp).into())
118 }
119 Obj::Abs(a) => {
120 let arg =
121 self.eval_reduce_nested_sum_product_in_obj((*a.arg).clone(), eval_stmt)?;
122 Ok(Abs::new(arg).into())
123 }
124 Obj::Log(l) => {
125 let b = self.eval_reduce_nested_sum_product_in_obj((*l.base).clone(), eval_stmt)?;
126 let x = self.eval_reduce_nested_sum_product_in_obj((*l.arg).clone(), eval_stmt)?;
127 Ok(Log::new(b, x).into())
128 }
129 Obj::Max(m) => {
130 let l = self.eval_reduce_nested_sum_product_in_obj((*m.left).clone(), eval_stmt)?;
131 let r =
132 self.eval_reduce_nested_sum_product_in_obj((*m.right).clone(), eval_stmt)?;
133 Ok(Max::new(l, r).into())
134 }
135 Obj::Min(m) => {
136 let l = self.eval_reduce_nested_sum_product_in_obj((*m.left).clone(), eval_stmt)?;
137 let r =
138 self.eval_reduce_nested_sum_product_in_obj((*m.right).clone(), eval_stmt)?;
139 Ok(Min::new(l, r).into())
140 }
141 other => Ok(other),
142 }
143 }
144
145 fn eval_sum_or_product_for_eval_stmt(
147 &mut self,
148 start: &Obj,
149 end: &Obj,
150 func: &Obj,
151 is_product: bool,
152 eval_stmt: &EvalStmt,
153 ) -> Result<Obj, RuntimeError> {
154 let start_ev = self.evaluate_symbol_obj_iterative(start.clone(), eval_stmt)?;
155 let end_ev = self.evaluate_symbol_obj_iterative(end.clone(), eval_stmt)?;
156 let Some(a_num) = self.resolve_obj_to_number(&start_ev) else {
157 return Err(short_exec_error(
158 eval_stmt.clone().into(),
159 "eval: sum/product start must resolve to a number".to_string(),
160 None,
161 vec![],
162 ));
163 };
164 let Some(b_num) = self.resolve_obj_to_number(&end_ev) else {
165 return Err(short_exec_error(
166 eval_stmt.clone().into(),
167 "eval: sum/product end must resolve to a number".to_string(),
168 None,
169 vec![],
170 ));
171 };
172 let as_ = a_num.normalized_value.trim();
173 let bs = b_num.normalized_value.trim();
174 if !is_number_string_literally_integer_without_dot(as_.to_string())
175 || !is_number_string_literally_integer_without_dot(bs.to_string())
176 {
177 return Err(short_exec_error(
178 eval_stmt.clone().into(),
179 "eval: sum/product need integer (no fractional part) start and end for iteration"
180 .to_string(),
181 None,
182 vec![],
183 ));
184 }
185 let ai = as_.parse::<i128>().map_err(|_| {
186 short_exec_error(
187 eval_stmt.clone().into(),
188 "eval: sum/product could not parse integer bounds".to_string(),
189 None,
190 vec![],
191 )
192 })?;
193 let bi = bs.parse::<i128>().map_err(|_| {
194 short_exec_error(
195 eval_stmt.clone().into(),
196 "eval: sum/product could not parse integer bounds".to_string(),
197 None,
198 vec![],
199 )
200 })?;
201 if ai > bi {
202 return Err(short_exec_error(
203 eval_stmt.clone().into(),
204 "eval: sum/product need start <= end (integer range)".to_string(),
205 None,
206 vec![],
207 ));
208 }
209 let Some(af) = Self::summand_as_unary_anonymous_fn_cloned(func) else {
210 return Err(short_exec_error(
211 eval_stmt.clone().into(),
212 "eval: sum/product third argument must be a unary anonymous function (no calls)"
213 .to_string(),
214 None,
215 vec![],
216 ));
217 };
218 if ParamGroupWithSet::number_of_params(&af.body.params_def_with_set) != 1 {
219 return Err(short_exec_error(
220 eval_stmt.clone().into(),
221 "eval: sum/product index function must be unary".to_string(),
222 None,
223 vec![],
224 ));
225 }
226 let param_names = ParamGroupWithSet::collect_param_names(&af.body.params_def_with_set);
227 let pname = param_names[0].clone();
228 let mut acc_num = if is_product {
229 Number::new("1".to_string())
230 } else {
231 Number::new("0".to_string())
232 };
233 for k in ai..=bi {
234 let mut param_to_arg_map: HashMap<String, Obj> = HashMap::new();
235 param_to_arg_map.insert(pname.clone(), Number::new(k.to_string()).into());
236 let inst =
237 self.inst_obj(af.equal_to.as_ref(), ¶m_to_arg_map, ParamObjType::FnSet)?;
238 let term = self.resolve_obj(&inst);
239 let term = self.eval_reduce_nested_sum_product_in_obj(term, eval_stmt)?;
240 let Some(n) = term.evaluate_to_normalized_decimal_number() else {
241 return Err(short_exec_error(
242 eval_stmt.clone().into(),
243 format!(
244 "eval: could not reduce sum/product body to a number at index {}",
245 k
246 ),
247 None,
248 vec![],
249 ));
250 };
251 if is_product {
252 let step: Obj = Mul::new(acc_num.into(), n.into()).into();
253 acc_num = step
254 .evaluate_to_normalized_decimal_number()
255 .ok_or_else(|| {
256 short_exec_error(
257 eval_stmt.clone().into(),
258 "eval: product accumulation failed to normalize".to_string(),
259 None,
260 vec![],
261 )
262 })?;
263 } else {
264 let step: Obj = Add::new(acc_num.into(), n.into()).into();
265 acc_num = step
266 .evaluate_to_normalized_decimal_number()
267 .ok_or_else(|| {
268 short_exec_error(
269 eval_stmt.clone().into(),
270 "eval: sum accumulation failed to normalize".to_string(),
271 None,
272 vec![],
273 )
274 })?;
275 }
276 }
277 Ok(acc_num.into())
278 }
279
280 fn eval_matrix_list_cells_for_eval_stmt(
281 &mut self,
282 m: MatrixListObj,
283 eval_stmt: &EvalStmt,
284 ) -> Result<MatrixListObj, RuntimeError> {
285 let mut rows_out = Vec::with_capacity(m.rows.len());
286 for row in m.rows {
287 let mut out_row = Vec::with_capacity(row.len());
288 for cell in row {
289 let v = self.evaluate_symbol_obj_iterative(*cell, eval_stmt)?;
290 out_row.push(Box::new(v));
291 }
292 rows_out.push(out_row);
293 }
294 Ok(MatrixListObj { rows: rows_out })
295 }
296
297 fn add_matrix_lists_under_eval(
298 &self,
299 left: MatrixListObj,
300 right: MatrixListObj,
301 eval_stmt: &EvalStmt,
302 ) -> Result<MatrixListObj, RuntimeError> {
303 if left.rows.len() != right.rows.len() {
304 return Err(short_exec_error(
305 eval_stmt.clone().into(),
306 "eval: matrix ++ row count mismatch".to_string(),
307 None,
308 vec![],
309 ));
310 }
311 let mut rows = Vec::with_capacity(left.rows.len());
312 for (lr, rr) in left.rows.into_iter().zip(right.rows.into_iter()) {
313 if lr.len() != rr.len() {
314 return Err(short_exec_error(
315 eval_stmt.clone().into(),
316 "eval: matrix ++ column count mismatch".to_string(),
317 None,
318 vec![],
319 ));
320 }
321 let mut row = Vec::with_capacity(lr.len());
322 for (a, b) in lr.into_iter().zip(rr.into_iter()) {
323 let sum_obj: Obj = Add::new(*a, *b).into();
324 let Some(n) = sum_obj.evaluate_to_normalized_decimal_number() else {
325 return Err(short_exec_error(
326 eval_stmt.clone().into(),
327 "eval: matrix ++ needs numeric cells".to_string(),
328 None,
329 vec![],
330 ));
331 };
332 row.push(Box::new(n.into()));
333 }
334 rows.push(row);
335 }
336 Ok(MatrixListObj { rows })
337 }
338
339 fn sub_matrix_lists_under_eval(
340 &self,
341 left: MatrixListObj,
342 right: MatrixListObj,
343 eval_stmt: &EvalStmt,
344 ) -> Result<MatrixListObj, RuntimeError> {
345 if left.rows.len() != right.rows.len() {
346 return Err(short_exec_error(
347 eval_stmt.clone().into(),
348 "eval: matrix -- row count mismatch".to_string(),
349 None,
350 vec![],
351 ));
352 }
353 let mut rows = Vec::with_capacity(left.rows.len());
354 for (lr, rr) in left.rows.into_iter().zip(right.rows.into_iter()) {
355 if lr.len() != rr.len() {
356 return Err(short_exec_error(
357 eval_stmt.clone().into(),
358 "eval: matrix -- column count mismatch".to_string(),
359 None,
360 vec![],
361 ));
362 }
363 let mut row = Vec::with_capacity(lr.len());
364 for (a, b) in lr.into_iter().zip(rr.into_iter()) {
365 let diff_obj: Obj = Sub::new(*a, *b).into();
366 let Some(n) = diff_obj.evaluate_to_normalized_decimal_number() else {
367 return Err(short_exec_error(
368 eval_stmt.clone().into(),
369 "eval: matrix -- needs numeric cells".to_string(),
370 None,
371 vec![],
372 ));
373 };
374 row.push(Box::new(n.into()));
375 }
376 rows.push(row);
377 }
378 Ok(MatrixListObj { rows })
379 }
380
381 fn multiply_matrix_lists_under_eval(
382 &self,
383 left: MatrixListObj,
384 right: MatrixListObj,
385 eval_stmt: &EvalStmt,
386 ) -> Result<MatrixListObj, RuntimeError> {
387 let r1 = left.rows.len();
388 let c1 = if r1 == 0 { 0 } else { left.rows[0].len() };
389 let r2 = right.rows.len();
390 let c2 = if r2 == 0 { 0 } else { right.rows[0].len() };
391 if c1 != r2 {
392 return Err(short_exec_error(
393 eval_stmt.clone().into(),
394 "eval: matrix ** inner dimension mismatch".to_string(),
395 None,
396 vec![],
397 ));
398 }
399 let mut rows: Vec<Vec<Box<Obj>>> = Vec::with_capacity(r1);
400 for i in 0..r1 {
401 let mut row: Vec<Box<Obj>> = Vec::with_capacity(c2);
402 for k in 0..c2 {
403 let mut acc_num = Number::new("0".to_string());
404 for j in 0..c1 {
405 let prod_obj: Obj =
406 Mul::new((*left.rows[i][j]).clone(), (*right.rows[j][k]).clone()).into();
407 let Some(p) = prod_obj.evaluate_to_normalized_decimal_number() else {
408 return Err(short_exec_error(
409 eval_stmt.clone().into(),
410 "eval: matrix ** cell multiply failed".to_string(),
411 None,
412 vec![],
413 ));
414 };
415 let sum_obj: Obj = Add::new(acc_num.into(), p.into()).into();
416 let Some(s) = sum_obj.evaluate_to_normalized_decimal_number() else {
417 return Err(short_exec_error(
418 eval_stmt.clone().into(),
419 "eval: matrix ** accumulation failed".to_string(),
420 None,
421 vec![],
422 ));
423 };
424 acc_num = s;
425 }
426 row.push(Box::new(acc_num.into()));
427 }
428 rows.push(row);
429 }
430 Ok(MatrixListObj { rows })
431 }
432
433 fn scalar_matrix_mul_under_eval(
434 &self,
435 scalar: Obj,
436 matrix: MatrixListObj,
437 eval_stmt: &EvalStmt,
438 ) -> Result<MatrixListObj, RuntimeError> {
439 let mut rows_out = Vec::with_capacity(matrix.rows.len());
440 for row in matrix.rows {
441 let mut out_row = Vec::with_capacity(row.len());
442 for cell in row {
443 let prod_obj: Obj = Mul::new(scalar.clone(), (*cell).clone()).into();
444 let Some(n) = prod_obj.evaluate_to_normalized_decimal_number() else {
445 return Err(short_exec_error(
446 eval_stmt.clone().into(),
447 "eval: *. needs scalar and numeric matrix cells".to_string(),
448 None,
449 vec![],
450 ));
451 };
452 out_row.push(Box::new(n.into()));
453 }
454 rows_out.push(out_row);
455 }
456 Ok(MatrixListObj { rows: rows_out })
457 }
458
459 fn matrix_pow_under_eval(
460 &self,
461 base: MatrixListObj,
462 exponent: usize,
463 eval_stmt: &EvalStmt,
464 ) -> Result<MatrixListObj, RuntimeError> {
465 if exponent == 0 {
466 return Err(short_exec_error(
467 eval_stmt.clone().into(),
468 "eval: matrix ^^ exponent must be at least 1".to_string(),
469 None,
470 vec![],
471 ));
472 }
473 let mut acc = base.clone();
474 for _ in 1..exponent {
475 acc = self.multiply_matrix_lists_under_eval(acc, base.clone(), eval_stmt)?;
476 }
477 Ok(acc)
478 }
479
480 fn eval_to_matrix_list_for_eval_stmt(
481 &mut self,
482 obj: Obj,
483 eval_stmt: &EvalStmt,
484 ) -> Result<MatrixListObj, RuntimeError> {
485 let cur = self.peel_fn_obj_dispatch_loop(obj, eval_stmt)?;
486 match cur {
487 Obj::MatrixListObj(m) => self.eval_matrix_list_cells_for_eval_stmt(m, eval_stmt),
488 Obj::MatrixAdd(ma) => {
489 let l = self.eval_to_matrix_list_for_eval_stmt((*ma.left).clone(), eval_stmt)?;
490 let r = self.eval_to_matrix_list_for_eval_stmt((*ma.right).clone(), eval_stmt)?;
491 self.add_matrix_lists_under_eval(l, r, eval_stmt)
492 }
493 Obj::MatrixSub(ms) => {
494 let l = self.eval_to_matrix_list_for_eval_stmt((*ms.left).clone(), eval_stmt)?;
495 let r = self.eval_to_matrix_list_for_eval_stmt((*ms.right).clone(), eval_stmt)?;
496 self.sub_matrix_lists_under_eval(l, r, eval_stmt)
497 }
498 Obj::MatrixMul(mm) => {
499 let l = self.eval_to_matrix_list_for_eval_stmt((*mm.left).clone(), eval_stmt)?;
500 let r = self.eval_to_matrix_list_for_eval_stmt((*mm.right).clone(), eval_stmt)?;
501 self.multiply_matrix_lists_under_eval(l, r, eval_stmt)
502 }
503 Obj::MatrixScalarMul(m) => {
504 let s = self.evaluate_symbol_obj_iterative((*m.scalar).clone(), eval_stmt)?;
505 let mat = self.eval_to_matrix_list_for_eval_stmt((*m.matrix).clone(), eval_stmt)?;
506 self.scalar_matrix_mul_under_eval(s, mat, eval_stmt)
507 }
508 Obj::MatrixPow(mp) => {
509 let base = self.eval_to_matrix_list_for_eval_stmt((*mp.base).clone(), eval_stmt)?;
510 let exp_obj =
511 self.evaluate_symbol_obj_iterative((*mp.exponent).clone(), eval_stmt)?;
512 let Some(exp_num) = exp_obj.evaluate_to_normalized_decimal_number() else {
513 return Err(short_exec_error(
514 eval_stmt.clone().into(),
515 "eval: matrix ^^ exponent must evaluate to a number".to_string(),
516 None,
517 vec![],
518 ));
519 };
520 let exp_u = exp_num.normalized_value.parse::<usize>().map_err(|_| {
521 short_exec_error(
522 eval_stmt.clone().into(),
523 "eval: matrix ^^ exponent must be a non-negative integer".to_string(),
524 None,
525 vec![],
526 )
527 })?;
528 self.matrix_pow_under_eval(base, exp_u, eval_stmt)
529 }
530 other => {
531 let lookup_key = match &other {
532 Obj::Atom(AtomObj::Identifier(id)) => id.name.clone(),
533 _ => other.to_string(),
534 };
535 let Some(ml) = self.get_obj_equal_to_matrix_list(&lookup_key) else {
536 return Err(short_exec_error(
537 eval_stmt.clone().into(),
538 format!("eval: `{}` is not a matrix list", lookup_key),
539 None,
540 vec![],
541 ));
542 };
543 self.eval_to_matrix_list_for_eval_stmt(ml.into(), eval_stmt)
544 }
545 }
546 }
547
548 fn finish_numeric_accumulator_with_pending_rights(
549 &mut self,
550 acc: Obj,
551 pending: &mut Vec<PendingRight>,
552 eval_stmt: &EvalStmt,
553 ) -> Result<Obj, RuntimeError> {
554 let mut acc = acc;
555 while let Some(pend) = pending.pop() {
556 let (combine_op, right_obj) = match pend {
557 PendingRight::Add(o) => (BinaryCombineOp::Add, o),
558 PendingRight::Sub(o) => (BinaryCombineOp::Sub, o),
559 PendingRight::Mul(o) => (BinaryCombineOp::Mul, o),
560 PendingRight::Div(o) => (BinaryCombineOp::Div, o),
561 };
562 let right_eval = self.evaluate_symbol_obj_iterative(right_obj, eval_stmt)?;
563 acc = self.combine_two_numeric_objs(acc, right_eval, combine_op, eval_stmt)?;
564 }
565 Ok(acc)
566 }
567
568 fn evaluate_symbol_obj_iterative(
571 &mut self,
572 initial: Obj,
573 eval_stmt: &EvalStmt,
574 ) -> Result<Obj, RuntimeError> {
575 let mut pending: Vec<PendingRight> = Vec::new();
576 let mut cur = initial;
577
578 loop {
579 cur = self.peel_fn_obj_dispatch_loop(cur, eval_stmt)?;
580
581 match cur {
582 Obj::Add(add) => {
583 pending.push(PendingRight::Add(*add.right));
584 cur = *add.left;
585 continue;
586 }
587 Obj::Sub(sub) => {
588 pending.push(PendingRight::Sub(*sub.right));
589 cur = *sub.left;
590 continue;
591 }
592 Obj::Mul(mul) => {
593 pending.push(PendingRight::Mul(*mul.right));
594 cur = *mul.left;
595 continue;
596 }
597 Obj::Div(div) => {
598 pending.push(PendingRight::Div(*div.right));
599 cur = *div.left;
600 continue;
601 }
602 Obj::Number(acc_num) => {
603 return self.finish_numeric_accumulator_with_pending_rights(
604 acc_num.into(),
605 &mut pending,
606 eval_stmt,
607 );
608 }
609 Obj::Pow(pow) => {
610 let left =
611 self.evaluate_symbol_obj_iterative((*pow.base).clone(), eval_stmt)?;
612 let right =
613 self.evaluate_symbol_obj_iterative((*pow.exponent).clone(), eval_stmt)?;
614 let combined: Obj = Pow::new(left, right).into();
615 match combined.evaluate_to_normalized_decimal_number() {
616 Some(acc_num) => {
617 return self.finish_numeric_accumulator_with_pending_rights(
618 acc_num.into(),
619 &mut pending,
620 eval_stmt,
621 );
622 }
623 None => {
624 if pending.is_empty() {
625 return Ok(combined);
626 }
627 return Err(short_exec_error(
628 eval_stmt.clone().into(),
629 "eval: non-numeric power with pending binary operation".to_string(),
630 None,
631 vec![],
632 ));
633 }
634 }
635 }
636 Obj::Sum(sum) => {
637 if !pending.is_empty() {
638 return Err(short_exec_error(
639 eval_stmt.clone().into(),
640 "eval: sum with pending binary operation".to_string(),
641 None,
642 vec![],
643 ));
644 }
645 let v = self.eval_sum_or_product_for_eval_stmt(
646 sum.start.as_ref(),
647 sum.end.as_ref(),
648 sum.func.as_ref(),
649 false,
650 eval_stmt,
651 )?;
652 return self.finish_numeric_accumulator_with_pending_rights(
653 v,
654 &mut pending,
655 eval_stmt,
656 );
657 }
658 Obj::Product(prod) => {
659 if !pending.is_empty() {
660 return Err(short_exec_error(
661 eval_stmt.clone().into(),
662 "eval: product with pending binary operation".to_string(),
663 None,
664 vec![],
665 ));
666 }
667 let v = self.eval_sum_or_product_for_eval_stmt(
668 prod.start.as_ref(),
669 prod.end.as_ref(),
670 prod.func.as_ref(),
671 true,
672 eval_stmt,
673 )?;
674 return self.finish_numeric_accumulator_with_pending_rights(
675 v,
676 &mut pending,
677 eval_stmt,
678 );
679 }
680 Obj::MatrixListObj(m) => {
681 if !pending.is_empty() {
682 return Err(short_exec_error(
683 eval_stmt.clone().into(),
684 "eval: matrix value with pending binary operation".to_string(),
685 None,
686 vec![],
687 ));
688 }
689 let done = self.eval_matrix_list_cells_for_eval_stmt(m, eval_stmt)?;
690 return Ok(done.into());
691 }
692 Obj::MatrixAdd(ma) => {
693 if !pending.is_empty() {
694 return Err(short_exec_error(
695 eval_stmt.clone().into(),
696 "eval: matrix ++ with pending binary operation".to_string(),
697 None,
698 vec![],
699 ));
700 }
701 let done =
702 self.eval_to_matrix_list_for_eval_stmt(Obj::MatrixAdd(ma), eval_stmt)?;
703 return Ok(done.into());
704 }
705 Obj::MatrixSub(ms) => {
706 if !pending.is_empty() {
707 return Err(short_exec_error(
708 eval_stmt.clone().into(),
709 "eval: matrix -- with pending binary operation".to_string(),
710 None,
711 vec![],
712 ));
713 }
714 let done =
715 self.eval_to_matrix_list_for_eval_stmt(Obj::MatrixSub(ms), eval_stmt)?;
716 return Ok(done.into());
717 }
718 Obj::MatrixMul(mm) => {
719 if !pending.is_empty() {
720 return Err(short_exec_error(
721 eval_stmt.clone().into(),
722 "eval: matrix ** with pending binary operation".to_string(),
723 None,
724 vec![],
725 ));
726 }
727 let done =
728 self.eval_to_matrix_list_for_eval_stmt(Obj::MatrixMul(mm), eval_stmt)?;
729 return Ok(done.into());
730 }
731 Obj::MatrixScalarMul(m) => {
732 if !pending.is_empty() {
733 return Err(short_exec_error(
734 eval_stmt.clone().into(),
735 "eval: *. with pending binary operation".to_string(),
736 None,
737 vec![],
738 ));
739 }
740 let done =
741 self.eval_to_matrix_list_for_eval_stmt(Obj::MatrixScalarMul(m), eval_stmt)?;
742 return Ok(done.into());
743 }
744 Obj::MatrixPow(mp) => {
745 if !pending.is_empty() {
746 return Err(short_exec_error(
747 eval_stmt.clone().into(),
748 "eval: matrix ^^ with pending binary operation".to_string(),
749 None,
750 vec![],
751 ));
752 }
753 let done =
754 self.eval_to_matrix_list_for_eval_stmt(Obj::MatrixPow(mp), eval_stmt)?;
755 return Ok(done.into());
756 }
757 _ => {
758 if pending.is_empty() {
759 return Ok(cur);
760 }
761 return Err(short_exec_error(
762 eval_stmt.clone().into(),
763 "eval: non-numeric intermediate with pending binary operation".to_string(),
764 None,
765 vec![],
766 ));
767 }
768 }
769 }
770 }
771
772 fn combine_two_numeric_objs(
773 &mut self,
774 left: Obj,
775 right: Obj,
776 combine_op: BinaryCombineOp,
777 eval_stmt: &EvalStmt,
778 ) -> Result<Obj, RuntimeError> {
779 let combined: Obj = match combine_op {
780 BinaryCombineOp::Add => Add::new(left, right).into(),
781 BinaryCombineOp::Sub => Sub::new(left, right).into(),
782 BinaryCombineOp::Mul => Mul::new(left, right).into(),
783 BinaryCombineOp::Div => Div::new(left, right).into(),
784 };
785 let calculated = combined.evaluate_to_normalized_decimal_number();
786 match calculated {
787 Some(number) => Ok(number.into()),
788 None => Err(short_exec_error(
789 eval_stmt.clone().into(),
790 "eval: failed to combine numeric sub-expression".to_string(),
791 None,
792 vec![],
793 )),
794 }
795 }
796
797 fn peel_fn_obj_dispatch_loop(
799 &mut self,
800 mut cur: Obj,
801 eval_stmt: &EvalStmt,
802 ) -> Result<Obj, RuntimeError> {
803 while let Obj::FnObj(ref fn_obj) = cur {
804 cur = self.dispatch_algo_one_return_expr(fn_obj, eval_stmt)?;
805 }
806 Ok(cur)
807 }
808
809 fn dispatch_algo_one_return_expr(
811 &mut self,
812 fn_obj_to_evaluate: &FnObj,
813 eval_stmt: &EvalStmt,
814 ) -> Result<Obj, RuntimeError> {
815 let fn_name = fn_obj_to_evaluate.head.to_string();
816 let mut flattened_number_args: Vec<Obj> = Vec::new();
817 for arg_group in fn_obj_to_evaluate.body.iter() {
818 for arg in arg_group.iter() {
819 let evaluated_arg_obj =
820 self.evaluate_symbol_obj_iterative((**arg).clone(), eval_stmt)?;
821 match evaluated_arg_obj {
822 Obj::Number(number) => {
823 flattened_number_args.push(number.into());
824 }
825 _ => {
826 return Err(short_exec_error(
827 eval_stmt.clone().into(),
828 "eval: function arguments must evaluate to Number".to_string(),
829 None,
830 vec![],
831 ));
832 }
833 }
834 }
835 }
836
837 let algo_definition = match self.get_algo_definition_by_name(&fn_name) {
838 Some(definition) => definition.clone(),
839 None => {
840 return Err(short_exec_error(
841 eval_stmt.clone().into(),
842 format!("eval: algorithm `{}` is not defined", fn_name),
843 None,
844 vec![],
845 ));
846 }
847 };
848
849 if flattened_number_args.len() != algo_definition.params.len() {
850 return Err(short_exec_error(
851 eval_stmt.clone().into(),
852 format!(
853 "eval: argument count mismatch (expected {}, got {})",
854 algo_definition.params.len(),
855 flattened_number_args.len()
856 ),
857 None,
858 vec![],
859 ));
860 }
861
862 let mut param_to_arg_map: HashMap<String, Obj> = HashMap::new();
863 for (param_name, arg_obj) in algo_definition
864 .params
865 .iter()
866 .zip(flattened_number_args.iter())
867 {
868 param_to_arg_map.insert(param_name.clone(), arg_obj.clone());
869 }
870
871 for algo_case in algo_definition.cases.iter() {
872 let instantiated_case_condition = self.inst_atomic_fact(
873 &algo_case.condition,
874 ¶m_to_arg_map,
875 ParamObjType::DefAlgo,
876 None,
877 )?;
878 let verify_result = self
879 .verify_atomic_fact(&instantiated_case_condition, &VerifyState::new(0, false))
880 .map_err(|verify_error| {
881 short_exec_error(
882 eval_stmt.clone().into(),
883 "eval: failed to verify case condition".to_string(),
884 Some(verify_error),
885 vec![],
886 )
887 })?;
888
889 if verify_result.is_true() {
890 return self.inst_obj(
891 &algo_case.return_stmt.value,
892 ¶m_to_arg_map,
893 ParamObjType::DefAlgo,
894 );
895 }
896 if verify_result.is_unknown() {
897 let reversed_case_condition = instantiated_case_condition.make_reversed();
898 let verify_reversed_result = self
899 .verify_atomic_fact(&reversed_case_condition, &VerifyState::new(0, false))
900 .map_err(|verify_error| {
901 short_exec_error(
902 eval_stmt.clone().into(),
903 "eval: failed to verify reversed case condition".to_string(),
904 Some(verify_error),
905 vec![],
906 )
907 })?;
908 if verify_reversed_result.is_unknown() {
909 return Err(short_exec_error(
910 eval_stmt.clone().into(),
911 format!(
912 "eval: case `{}` is unknown and its reverse is also unknown",
913 instantiated_case_condition
914 ),
915 None,
916 vec![],
917 ));
918 }
919 }
920 }
921
922 if let Some(default_return_stmt) = &algo_definition.default_return {
923 self.inst_obj(
924 &default_return_stmt.value,
925 ¶m_to_arg_map,
926 ParamObjType::DefAlgo,
927 )
928 } else {
929 Err(short_exec_error(
930 eval_stmt.clone().into(),
931 "eval: no case matched and no default return".to_string(),
932 None,
933 vec![],
934 ))
935 }
936 }
937
938 pub fn exec_eval_stmt(&mut self, stmt: &EvalStmt) -> Result<StmtResult, RuntimeError> {
939 self.verify_obj_well_defined_and_store_cache(
940 &stmt.obj_to_eval,
941 &VerifyState::new(0, false),
942 )?;
943
944 let resolved_obj = self.resolve_obj(&stmt.obj_to_eval);
945 let eval_result = self.run_in_local_env(|rt| {
946 if !Self::object_supported_by_eval_stmt(&resolved_obj) {
947 return Err(short_exec_error(
948 stmt.clone().into(),
949 "eval: need a function call, numeric expression (+ - * / ^), sum/product over a unary anonymous body, or matrix ++ -- ** *. ^^ / matrix literal"
950 .to_string(),
951 None,
952 vec![],
953 ));
954 }
955 rt.evaluate_symbol_obj_iterative(resolved_obj.clone(), stmt)
956 });
957
958 let evaluated_obj = eval_result?;
959 let evaluated_equal_fact = EqualFact::new(
960 stmt.obj_to_eval.clone(),
961 evaluated_obj,
962 stmt.line_file.clone(),
963 )
964 .into();
965
966 let mut infer_result = InferResult::new();
967 infer_result.new_fact(&evaluated_equal_fact);
968 self.verify_well_defined_and_store_and_infer_with_default_verify_state(
969 evaluated_equal_fact,
970 )?;
971
972 Ok((NonFactualStmtSuccess::new(stmt.clone().into(), infer_result, vec![])).into())
973 }
974}