1use crate::cell::*;
16use crate::machine::Machine;
17
18#[derive(Debug, Clone, Copy, PartialEq)]
20pub enum ArithValue {
21 Int(i64),
22 Float(f64),
23}
24
25fn overflow(m: &mut Machine, operation: &str) {
28 let ctx = format!("Arithmetic error: integer overflow in {operation}");
29 crate::errors::evaluation(m, "int_overflow", &ctx);
30}
31
32fn zero_divisor(m: &mut Machine, label: &str) {
33 let ctx = format!("Division by zero ({label})");
34 crate::errors::evaluation(m, "zero_divisor", &ctx);
35}
36
37fn int_args_required(m: &mut Machine, op: &str) {
42 let culprit = make_atom(m.atoms.intern("member"));
43 let ctx = format!("{op} requires integer arguments");
44 crate::errors::type_error(m, "integer", culprit, &ctx);
45}
46
47fn shift_undefined(m: &mut Machine, op: &str) {
48 let ctx = format!("Shift {op} requires a non-negative count in [0, 64)");
49 crate::errors::evaluation(m, "undefined", &ctx);
50}
51
52fn check_float(m: &mut Machine, f: f64) -> Result<ArithValue, ()> {
54 if f.is_nan() {
55 crate::errors::evaluation(m, "undefined", "Arithmetic error: NaN result");
56 Err(())
57 } else if f.is_infinite() {
58 crate::errors::evaluation(m, "float_overflow", "Arithmetic error: Infinity result");
59 Err(())
60 } else {
61 Ok(ArithValue::Float(f))
62 }
63}
64
65fn as_f64(v: ArithValue) -> f64 {
66 match v {
67 ArithValue::Int(n) => n as f64,
68 ArithValue::Float(f) => f,
69 }
70}
71
72pub fn arith_lt(a: ArithValue, b: ArithValue) -> bool {
75 use ArithValue::*;
76 match (a, b) {
77 (Int(a), Int(b)) => a < b,
78 (Float(a), Float(b)) => a < b,
79 (Int(a), Float(b)) => (a as f64) < b,
80 (Float(a), Int(b)) => a < (b as f64),
81 }
82}
83
84pub fn arith_gt(a: ArithValue, b: ArithValue) -> bool {
85 arith_lt(b, a)
86}
87
88pub fn arith_eq(a: ArithValue, b: ArithValue) -> bool {
89 use ArithValue::*;
90 match (a, b) {
91 (Int(a), Int(b)) => a == b,
92 (Float(a), Float(b)) => a == b,
93 (Int(a), Float(b)) => (a as f64) == b,
94 (Float(a), Int(b)) => a == (b as f64),
95 }
96}
97
98fn predicate_indicator(m: &mut Machine, name: u32, arity: i64) -> Word {
105 let slash = m.atoms.intern("/");
106 let pi = m.heap.len();
107 m.heap.push(pack_functor(slash, 2));
108 m.heap.push(make_atom(name));
109 m.heap.push(make_int(arity));
110 make(TAG_STR, pi as u64)
111}
112
113#[allow(clippy::result_unit_err)]
118pub fn eval(m: &mut Machine, expr: Word) -> Result<ArithValue, ()> {
119 let w = m.deref(expr);
120 match tag_of(w) {
121 TAG_INT => Ok(ArithValue::Int(int_value(w))),
122 TAG_BIG => Ok(ArithValue::Int(m.heap[payload(w) as usize] as i64)),
123 TAG_FLT => Ok(ArithValue::Float(f64::from_bits(
124 m.heap[payload(w) as usize],
125 ))),
126 TAG_REF => {
127 let ctx = format!("Arithmetic error: unbound variable _{}", payload(w));
128 crate::errors::instantiation(m, &ctx);
129 Err(())
130 }
131 TAG_ATOM => {
132 let culprit = predicate_indicator(m, atom_id(w), 0);
136 crate::errors::type_error(m, "evaluable", culprit, "Cannot evaluate as arithmetic");
137 Err(())
138 }
139 TAG_LST => {
140 crate::errors::type_error(m, "evaluable", w, "Cannot evaluate as arithmetic");
143 Err(())
144 }
145 TAG_STR => eval_struct(m, w),
146 _ => unreachable!("bad tag in arith eval"),
147 }
148}
149
150fn eval_struct(m: &mut Machine, w: Word) -> Result<ArithValue, ()> {
151 let idx = payload(w) as usize;
152 let (functor, arity) = unpack_functor(m.heap[idx]);
153 let name = m.atoms.resolve(functor).to_string();
154 let a0 = m.heap[idx + 1];
156 match (name.as_str(), arity) {
157 ("+", 2) => {
158 let (a, b) = bin(m, idx)?;
159 add(m, a, b)
160 }
161 ("-", 2) => {
162 let (a, b) = bin(m, idx)?;
163 sub(m, a, b)
164 }
165 ("*", 2) => {
166 let (a, b) = bin(m, idx)?;
167 mul(m, a, b)
168 }
169 ("/", 2) => {
170 let (a, b) = bin(m, idx)?;
171 div(m, a, b)
172 }
173 ("//", 2) => {
174 let (a, b) = bin(m, idx)?;
175 int_div(m, a, b)
176 }
177 ("mod", 2) => {
178 let (a, b) = bin(m, idx)?;
179 modulo(m, a, b)
180 }
181 ("rem", 2) => {
182 let (a, b) = bin(m, idx)?;
183 rem(m, a, b)
184 }
185 ("**", 2) => {
186 let (a, b) = bin(m, idx)?;
187 pow_float(m, a, b)
188 }
189 ("^", 2) => {
190 let (a, b) = bin(m, idx)?;
191 pow(m, a, b)
192 }
193 ("<<", 2) => {
194 let (a, b) = bin(m, idx)?;
195 shl(m, a, b)
196 }
197 (">>", 2) => {
198 let (a, b) = bin(m, idx)?;
199 shr(m, a, b)
200 }
201 ("/\\", 2) => {
202 let (a, b) = bin(m, idx)?;
203 bit_and(m, a, b)
204 }
205 ("\\/", 2) => {
206 let (a, b) = bin(m, idx)?;
207 bit_or(m, a, b)
208 }
209 ("xor", 2) => {
210 let (a, b) = bin(m, idx)?;
211 bit_xor(m, a, b)
212 }
213 ("div", 2) => {
214 let (a, b) = bin(m, idx)?;
215 div_floor(m, a, b)
216 }
217 ("min", 2) => {
218 let (a, b) = bin(m, idx)?;
219 Ok(if arith_lt(a, b) { a } else { b })
220 }
221 ("max", 2) => {
222 let (a, b) = bin(m, idx)?;
223 Ok(if arith_lt(a, b) { b } else { a })
224 }
225 ("-", 1) => {
226 let a = eval(m, a0)?;
227 neg(m, a)
228 }
229 ("abs", 1) => {
230 let a = eval(m, a0)?;
231 abs(m, a)
232 }
233 ("sign", 1) => {
234 let a = eval(m, a0)?;
235 Ok(sign(a))
236 }
237 ("\\", 1) => {
238 let a = eval(m, a0)?;
239 bit_not(m, a)
240 }
241 _ => {
242 let name_id = m.atoms.intern(&name);
244 let culprit = predicate_indicator(m, name_id, arity as i64);
245 let ctx = format!("Unknown arithmetic operator: {name}/{arity}");
246 crate::errors::type_error(m, "evaluable", culprit, &ctx);
247 Err(())
248 }
249 }
250}
251
252fn bin(m: &mut Machine, idx: usize) -> Result<(ArithValue, ArithValue), ()> {
254 let a = eval(m, m.heap[idx + 1])?;
255 let b = eval(m, m.heap[idx + 2])?;
256 Ok((a, b))
257}
258
259fn add(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
262 use ArithValue::*;
263 match (a, b) {
264 (Int(a), Int(b)) => a
265 .checked_add(b)
266 .map(Int)
267 .ok_or_else(|| overflow(m, "addition")),
268 (Float(a), Float(b)) => check_float(m, a + b),
269 (Int(a), Float(b)) => check_float(m, a as f64 + b),
270 (Float(a), Int(b)) => check_float(m, a + b as f64),
271 }
272}
273
274fn sub(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
275 use ArithValue::*;
276 match (a, b) {
277 (Int(a), Int(b)) => a
278 .checked_sub(b)
279 .map(Int)
280 .ok_or_else(|| overflow(m, "subtraction")),
281 (Float(a), Float(b)) => check_float(m, a - b),
282 (Int(a), Float(b)) => check_float(m, a as f64 - b),
283 (Float(a), Int(b)) => check_float(m, a - b as f64),
284 }
285}
286
287fn mul(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
288 use ArithValue::*;
289 match (a, b) {
290 (Int(a), Int(b)) => a
291 .checked_mul(b)
292 .map(Int)
293 .ok_or_else(|| overflow(m, "multiplication")),
294 (Float(a), Float(b)) => check_float(m, a * b),
295 (Int(a), Float(b)) => check_float(m, a as f64 * b),
296 (Float(a), Int(b)) => check_float(m, a * b as f64),
297 }
298}
299
300fn div(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
301 use ArithValue::*;
302 match (a, b) {
304 (_, Int(0)) => {
305 zero_divisor(m, "float division");
306 Err(())
307 }
308 (_, Float(0.0)) => {
309 zero_divisor(m, "float division");
310 Err(())
311 }
312 (Int(a), Int(b)) => check_float(m, a as f64 / b as f64),
313 (Float(a), Float(b)) => check_float(m, a / b),
314 (Int(a), Float(b)) => check_float(m, a as f64 / b),
315 (Float(a), Int(b)) => check_float(m, a / b as f64),
316 }
317}
318
319fn modulo(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
320 use ArithValue::*;
321 match (a, b) {
322 (Int(_), Int(0)) => {
323 zero_divisor(m, "modulo");
324 Err(())
325 }
326 (Int(_), Int(i64::MIN)) => {
327 overflow(m, "mod");
328 Err(())
329 }
330 (Int(a), Int(b)) => {
331 let r = a.rem_euclid(b.abs());
333 if b < 0 && r != 0 {
334 Ok(Int(r - b.abs()))
335 } else {
336 Ok(Int(r))
337 }
338 }
339 _ => {
340 int_args_required(m, "mod");
341 Err(())
342 }
343 }
344}
345
346fn rem(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
347 use ArithValue::*;
348 match (a, b) {
349 (Int(_), Int(0)) => {
350 zero_divisor(m, "remainder");
351 Err(())
352 }
353 (Int(a), Int(b)) => a.checked_rem(b).map(Int).ok_or_else(|| overflow(m, "rem")),
354 _ => {
355 int_args_required(m, "rem");
356 Err(())
357 }
358 }
359}
360
361fn int_div(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
362 use ArithValue::*;
363 match (a, b) {
364 (Int(_), Int(0)) => {
365 zero_divisor(m, "integer division");
366 Err(())
367 }
368 (Int(a), Int(b)) => a
369 .checked_div(b)
370 .map(Int)
371 .ok_or_else(|| overflow(m, "division")),
372 _ => {
373 int_args_required(m, "//");
374 Err(())
375 }
376 }
377}
378
379fn div_floor(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
380 use ArithValue::*;
381 match (a, b) {
382 (Int(_), Int(0)) => {
383 zero_divisor(m, "floor division");
384 Err(())
385 }
386 (Int(a), Int(b)) => {
387 let q = match a.checked_div(b) {
388 Some(q) => q,
389 None => {
390 overflow(m, "floor division");
391 return Err(());
392 }
393 };
394 let r = match a.checked_rem(b) {
395 Some(r) => r,
396 None => {
397 overflow(m, "floor division");
398 return Err(());
399 }
400 };
401 if r != 0 && (r < 0) != (b < 0) {
402 q.checked_sub(1)
403 .map(Int)
404 .ok_or_else(|| overflow(m, "floor division"))
405 } else {
406 Ok(Int(q))
407 }
408 }
409 _ => {
410 int_args_required(m, "div");
411 Err(())
412 }
413 }
414}
415
416fn pow_float(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
417 check_float(m, as_f64(a).powf(as_f64(b)))
418}
419
420fn pow(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
421 use ArithValue::*;
422 match (a, b) {
423 (Int(base), Int(exp)) if exp >= 0 => {
424 let exp_u32 = match u32::try_from(exp) {
425 Ok(e) => e,
426 Err(_) => {
427 overflow(m, "integer power");
428 return Err(());
429 }
430 };
431 base.checked_pow(exp_u32)
432 .map(Int)
433 .ok_or_else(|| overflow(m, "integer power"))
434 }
435 _ => check_float(m, as_f64(a).powf(as_f64(b))),
436 }
437}
438
439fn shl(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
440 use ArithValue::*;
441 match (a, b) {
442 (Int(v), Int(n)) => {
443 let bits = match u32::try_from(n) {
444 Ok(b) => b,
445 Err(_) => {
446 shift_undefined(m, "<<");
447 return Err(());
448 }
449 };
450 if bits >= 64 {
451 shift_undefined(m, "<<");
452 return Err(());
453 }
454 v.checked_shl(bits)
455 .map(Int)
456 .ok_or_else(|| overflow(m, "shift_left"))
457 }
458 _ => {
459 int_args_required(m, "<<");
460 Err(())
461 }
462 }
463}
464
465fn shr(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
466 use ArithValue::*;
467 match (a, b) {
468 (Int(v), Int(n)) => {
469 let bits = match u32::try_from(n) {
470 Ok(b) => b,
471 Err(_) => {
472 shift_undefined(m, ">>");
473 return Err(());
474 }
475 };
476 if bits >= 64 {
477 shift_undefined(m, ">>");
478 return Err(());
479 }
480 v.checked_shr(bits)
481 .map(Int)
482 .ok_or_else(|| overflow(m, "shift_right"))
483 }
484 _ => {
485 int_args_required(m, ">>");
486 Err(())
487 }
488 }
489}
490
491fn bit_and(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
492 use ArithValue::*;
493 match (a, b) {
494 (Int(a), Int(b)) => Ok(Int(a & b)),
495 _ => {
496 int_args_required(m, "/\\");
497 Err(())
498 }
499 }
500}
501
502fn bit_or(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
503 use ArithValue::*;
504 match (a, b) {
505 (Int(a), Int(b)) => Ok(Int(a | b)),
506 _ => {
507 int_args_required(m, "\\/");
508 Err(())
509 }
510 }
511}
512
513fn bit_xor(m: &mut Machine, a: ArithValue, b: ArithValue) -> Result<ArithValue, ()> {
514 use ArithValue::*;
515 match (a, b) {
516 (Int(a), Int(b)) => Ok(Int(a ^ b)),
517 _ => {
518 int_args_required(m, "xor");
519 Err(())
520 }
521 }
522}
523
524fn neg(m: &mut Machine, a: ArithValue) -> Result<ArithValue, ()> {
527 use ArithValue::*;
528 match a {
529 Int(n) => n
530 .checked_neg()
531 .map(Int)
532 .ok_or_else(|| overflow(m, "negation")),
533 Float(f) => check_float(m, -f),
534 }
535}
536
537fn abs(m: &mut Machine, a: ArithValue) -> Result<ArithValue, ()> {
538 use ArithValue::*;
539 match a {
540 Int(n) => n.checked_abs().map(Int).ok_or_else(|| overflow(m, "abs")),
541 Float(f) => check_float(m, f.abs()),
542 }
543}
544
545fn bit_not(m: &mut Machine, a: ArithValue) -> Result<ArithValue, ()> {
548 use ArithValue::*;
549 match a {
550 Int(n) => Ok(Int(!n)),
551 Float(_) => {
552 int_args_required(m, "\\");
553 Err(())
554 }
555 }
556}
557
558fn sign(a: ArithValue) -> ArithValue {
559 match a {
560 ArithValue::Int(n) => ArithValue::Int(n.signum()),
561 ArithValue::Float(f) => ArithValue::Float(f.signum()),
562 }
563}
564
565#[cfg(test)]
566mod tests {
567 use super::*;
568 use plg_shared::StringInterner;
569
570 fn machine() -> Box<Machine> {
571 Machine::new(StringInterner::new(), Vec::new())
572 }
573
574 fn bin_str(m: &mut Machine, op: &str, a: Word, b: Word) -> Word {
576 let f = m.atoms.intern(op);
577 let idx = m.heap.len();
578 m.heap.push(pack_functor(f, 2));
579 m.heap.push(a);
580 m.heap.push(b);
581 make(TAG_STR, idx as u64)
582 }
583
584 fn un_str(m: &mut Machine, op: &str, a: Word) -> Word {
585 let f = m.atoms.intern(op);
586 let idx = m.heap.len();
587 m.heap.push(pack_functor(f, 1));
588 m.heap.push(a);
589 make(TAG_STR, idx as u64)
590 }
591
592 fn flt(m: &mut Machine, f: f64) -> Word {
593 let idx = m.heap.len();
594 m.heap.push(f.to_bits());
595 make(TAG_FLT, idx as u64)
596 }
597
598 fn msg(m: &Machine) -> &str {
599 m.error.as_ref().unwrap().message.as_str()
600 }
601
602 #[test]
603 fn happy_paths() {
604 let mut m = machine();
605 let e = bin_str(&mut m, "+", make_int(2), make_int(3));
606 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(5)));
607
608 let e = bin_str(&mut m, "*", make_int(4), make_int(5));
609 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(20)));
610
611 let two = flt(&mut m, 2.0);
613 let e = bin_str(&mut m, "+", two, make_int(3));
614 assert_eq!(eval(&mut m, e), Ok(ArithValue::Float(5.0)));
615
616 let e = bin_str(&mut m, "**", make_int(2), make_int(3));
618 assert_eq!(eval(&mut m, e), Ok(ArithValue::Float(8.0)));
619
620 let e = bin_str(&mut m, "^", make_int(2), make_int(3));
622 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(8)));
623
624 let e = bin_str(&mut m, "mod", make_int(10), make_int(-3));
626 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(-2)));
627 let e = bin_str(&mut m, "mod", make_int(-10), make_int(3));
628 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(2)));
629
630 let e = bin_str(&mut m, "div", make_int(10), make_int(-3));
632 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(-4)));
633
634 let e = un_str(&mut m, "abs", make_int(-5));
635 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(5)));
636 let e = un_str(&mut m, "sign", make_int(-5));
637 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(-1)));
638 let e = un_str(&mut m, "-", make_int(3));
639 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(-3)));
640
641 let e = un_str(&mut m, "\\", make_int(0));
643 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(-1)));
644 let e = un_str(&mut m, "\\", make_int(5));
645 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(-6)));
646
647 let e = bin_str(&mut m, "/\\", make_int(5), make_int(3));
648 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(1)));
649 let e = bin_str(&mut m, "xor", make_int(3), make_int(5));
650 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(6)));
651 let e = bin_str(&mut m, "<<", make_int(5), make_int(1));
652 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(10)));
653
654 let two = flt(&mut m, 2.0);
656 let e = bin_str(&mut m, "max", make_int(1), two);
657 assert_eq!(eval(&mut m, e), Ok(ArithValue::Float(2.0)));
658 let two = flt(&mut m, 2.0);
659 let e = bin_str(&mut m, "min", make_int(1), two);
660 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(1)));
661 }
662
663 #[test]
664 fn err_zero_divisors() {
665 let cases = [
666 ("//", "integer division"),
667 ("mod", "modulo"),
668 ("rem", "remainder"),
669 ("div", "floor division"),
670 ];
671 for (op, label) in cases {
672 let mut m = machine();
673 let e = bin_str(&mut m, op, make_int(1), make_int(0));
674 assert!(eval(&mut m, e).is_err());
675 assert_eq!(
676 msg(&m),
677 format!("error(evaluation_error(zero_divisor), Division by zero ({label}))")
678 );
679 }
680 let mut m = machine();
682 let e = bin_str(&mut m, "/", make_int(1), make_int(0));
683 assert!(eval(&mut m, e).is_err());
684 assert_eq!(
685 msg(&m),
686 "error(evaluation_error(zero_divisor), Division by zero (float division))"
687 );
688 }
689
690 #[test]
691 fn err_int_overflow() {
692 let mut m = machine();
697 let e = bin_str(&mut m, "*", make_int(INT_MAX), make_int(INT_MAX));
698 assert!(eval(&mut m, e).is_err());
699 assert_eq!(
700 msg(&m),
701 "error(evaluation_error(int_overflow), Arithmetic error: integer overflow in multiplication)"
702 );
703
704 let mut m = machine();
708 let e = bin_str(&mut m, "+", make_int(INT_MAX), make_int(INT_MAX));
709 assert_eq!(eval(&mut m, e), Ok(ArithValue::Int(INT_MAX + INT_MAX)));
711 }
712
713 #[test]
714 fn err_type_evaluable_atom_and_compound() {
715 let mut m = machine();
716 let foo = m.atoms.intern("foo");
717 assert!(eval(&mut m, make_atom(foo)).is_err());
718 assert_eq!(
719 msg(&m),
720 "error(type_error(evaluable, /(foo, 0)), Cannot evaluate as arithmetic)"
721 );
722
723 let mut m = machine();
724 let e = un_str(&mut m, "foo", make_int(1));
725 assert!(eval(&mut m, e).is_err());
726 assert_eq!(
727 msg(&m),
728 "error(type_error(evaluable, /(foo, 1)), Unknown arithmetic operator: foo/1)"
729 );
730 }
731
732 #[test]
733 fn err_instantiation() {
734 let mut m = machine();
735 let v = m.new_var();
736 assert!(eval(&mut m, v).is_err());
737 let idx = payload(v);
739 assert_eq!(
740 msg(&m),
741 format!("error(instantiation_error, Arithmetic error: unbound variable _{idx})")
742 );
743 }
744
745 #[test]
746 fn err_nan_and_infinity() {
747 let mut m = machine();
749 let a = flt(&mut m, 0.0);
750 let b = flt(&mut m, 0.0);
751 let e = bin_str(&mut m, "/", a, b);
752 assert!(eval(&mut m, e).is_err());
753 assert_eq!(
754 msg(&m),
755 "error(evaluation_error(zero_divisor), Division by zero (float division))"
756 );
757
758 let mut m = machine();
761 let big = flt(&mut m, 1.0e308);
762 let ten = flt(&mut m, 10.0);
763 let e = bin_str(&mut m, "*", big, ten);
764 assert!(eval(&mut m, e).is_err());
765 assert_eq!(
766 msg(&m),
767 "error(evaluation_error(float_overflow), Arithmetic error: Infinity result)"
768 );
769
770 let mut m = machine();
772 let nan = flt(&mut m, f64::NAN);
773 let one = flt(&mut m, 1.0);
774 let e = bin_str(&mut m, "+", nan, one);
775 assert!(eval(&mut m, e).is_err());
776 assert_eq!(
777 msg(&m),
778 "error(evaluation_error(undefined), Arithmetic error: NaN result)"
779 );
780 }
781
782 #[test]
783 fn err_int_args_required() {
784 let mut m = machine();
785 let two = flt(&mut m, 2.0);
786 let e = bin_str(&mut m, "mod", make_int(5), two);
787 assert!(eval(&mut m, e).is_err());
788 assert_eq!(
789 msg(&m),
790 "error(type_error(integer, member), mod requires integer arguments)"
791 );
792 }
793
794 #[test]
795 fn err_shift_undefined() {
796 let mut m = machine();
797 let e = bin_str(&mut m, "<<", make_int(1), make_int(64));
798 assert!(eval(&mut m, e).is_err());
799 assert_eq!(
800 msg(&m),
801 "error(evaluation_error(undefined), Shift << requires a non-negative count in [0, 64))"
802 );
803 }
804
805 #[test]
806 fn mixed_comparison_helpers() {
807 assert!(arith_eq(ArithValue::Int(1), ArithValue::Float(1.0)));
809 assert!(!arith_lt(ArithValue::Float(1.0), ArithValue::Int(1)));
810 assert!(arith_lt(ArithValue::Int(1), ArithValue::Int(2)));
811 assert!(arith_gt(ArithValue::Float(2.0), ArithValue::Int(1)));
812 }
813}