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