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