1use super::*;
4
5pub(crate) fn values_equal(a: &Value, b: &Value) -> bool {
6 match (a, b) {
7 (Value::Num(n1), Value::Num(n2)) => n1 == n2,
8 (Value::Str(s1), Value::Str(s2)) => s1 == s2,
9 (Value::Symbol(s1), Value::Symbol(s2)) => s1 == s2,
10 (Value::Nil, Value::Nil) => true,
11 (Value::Ratio(r1), Value::Ratio(r2)) => r1 == r2,
12 (Value::Bool(b1), Value::Bool(b2)) => b1 == b2,
13 (Value::Complex(c1), Value::Complex(c2)) => c1 == c2,
14 _ => false,
16 }
17}
18
19pub(crate) fn values_cmp(a: &Value, b: &Value) -> Option<core::cmp::Ordering> {
20 match (a, b) {
21 (Value::Num(n1), Value::Num(n2)) => n1.partial_cmp(n2),
22 (Value::Str(s1), Value::Str(s2)) => s1.partial_cmp(s2),
23 (Value::Ratio(r1), Value::Ratio(r2)) => {
24 let left = r1.num.mul(&r2.den);
25 let right = r2.num.mul(&r1.den);
26 left.partial_cmp(&right)
27 }
28 (Value::Num(n1), Value::Ratio(r2)) => {
29 let left = n1.mul(&r2.den);
30 let right = r2.num.clone();
31 left.partial_cmp(&right)
32 }
33 (Value::Ratio(r1), Value::Num(n2)) => {
34 let left = r1.num.clone();
35 let right = n2.mul(&r1.den);
36 left.partial_cmp(&right)
37 }
38 _ => None,
39 }
40}
41
42pub(crate) fn builtin_eq(env: &mut Env, args: &[AST]) -> Result<Value, String> {
43 let vals = eval_args(env, args)?;
44 if vals.len() < 2 {
45 return Ok(Value::Bool(true));
46 }
47 for i in 0..vals.len() - 1 {
48 if !values_equal(&vals[i], &vals[i + 1]) {
49 return Ok(Value::Bool(false));
50 }
51 }
52 Ok(Value::Bool(true))
53}
54
55pub(crate) fn builtin_neq(env: &mut Env, args: &[AST]) -> Result<Value, String> {
56 let vals = eval_args(env, args)?;
57 if vals.len() < 2 {
58 return Ok(Value::Bool(false));
59 }
60 for i in 0..vals.len() - 1 {
61 if values_equal(&vals[i], &vals[i + 1]) {
62 return Ok(Value::Bool(false));
63 }
64 }
65 Ok(Value::Bool(true))
66}
67
68pub(crate) fn builtin_lt(env: &mut Env, args: &[AST]) -> Result<Value, String> {
69 let vals = eval_args(env, args)?;
70 if vals.len() < 2 {
71 return Ok(Value::Bool(true));
72 }
73 for i in 0..vals.len() - 1 {
74 match values_cmp(&vals[i], &vals[i + 1]) {
75 Some(core::cmp::Ordering::Less) => {}
76 _ => return Ok(Value::Bool(false)),
77 }
78 }
79 Ok(Value::Bool(true))
80}
81
82pub(crate) fn builtin_le(env: &mut Env, args: &[AST]) -> Result<Value, String> {
83 let vals = eval_args(env, args)?;
84 if vals.len() < 2 {
85 return Ok(Value::Bool(true));
86 }
87 for i in 0..vals.len() - 1 {
88 match values_cmp(&vals[i], &vals[i + 1]) {
89 Some(core::cmp::Ordering::Less) | Some(core::cmp::Ordering::Equal) => {}
90 _ => return Ok(Value::Bool(false)),
91 }
92 }
93 Ok(Value::Bool(true))
94}
95
96pub(crate) fn builtin_gt(env: &mut Env, args: &[AST]) -> Result<Value, String> {
97 let vals = eval_args(env, args)?;
98 if vals.len() < 2 {
99 return Ok(Value::Bool(true));
100 }
101 for i in 0..vals.len() - 1 {
102 match values_cmp(&vals[i], &vals[i + 1]) {
103 Some(core::cmp::Ordering::Greater) => {}
104 _ => return Ok(Value::Bool(false)),
105 }
106 }
107 Ok(Value::Bool(true))
108}
109
110pub(crate) fn builtin_ge(env: &mut Env, args: &[AST]) -> Result<Value, String> {
111 let vals = eval_args(env, args)?;
112 if vals.len() < 2 {
113 return Ok(Value::Bool(true));
114 }
115 for i in 0..vals.len() - 1 {
116 match values_cmp(&vals[i], &vals[i + 1]) {
117 Some(core::cmp::Ordering::Greater) | Some(core::cmp::Ordering::Equal) => {}
118 _ => return Ok(Value::Bool(false)),
119 }
120 }
121 Ok(Value::Bool(true))
122}
123
124pub(crate) fn builtin_and(env: &mut Env, args: &[AST]) -> Result<Value, String> {
125 if args.is_empty() {
126 return Ok(Value::Bool(true));
127 }
128 let mut last_val = Value::Bool(true);
129 for arg in args {
130 let evaluated = eval(env, arg)?;
131 let val = force_eval(env, evaluated)?;
132 if !super::super::eval::is_truthy(&val)? {
133 return Ok(val);
134 }
135 last_val = val;
136 }
137 Ok(last_val)
138}
139
140pub(crate) fn builtin_or(env: &mut Env, args: &[AST]) -> Result<Value, String> {
141 if args.is_empty() {
142 return Ok(Value::Bool(false));
143 }
144 let mut last_val = Value::Bool(false);
145 for arg in args {
146 let evaluated = eval(env, arg)?;
147 let val = force_eval(env, evaluated)?;
148 if super::super::eval::is_truthy(&val)? {
149 return Ok(val);
150 }
151 last_val = val;
152 }
153 Ok(last_val)
154}
155
156pub(crate) fn builtin_not(env: &mut Env, args: &[AST]) -> Result<Value, String> {
157 let vals = eval_args(env, args)?;
158 if vals.len() != 1 {
159 return Err(String::from("not requires exactly 1 argument"));
160 }
161 if super::super::eval::is_truthy(&vals[0])? {
162 Ok(Value::Bool(false))
163 } else {
164 Ok(Value::Bool(true))
165 }
166}
167
168pub(crate) fn builtin_xor(env: &mut Env, args: &[AST]) -> Result<Value, String> {
169 let vals = eval_args(env, args)?;
170 let mut count = 0;
171 for val in vals {
172 if super::super::eval::is_truthy(&val)? {
173 count += 1;
174 }
175 }
176 if count % 2 == 1 {
177 Ok(Value::Bool(true))
178 } else {
179 Ok(Value::Bool(false))
180 }
181}
182
183pub(crate) fn builtin_lambda(env: &mut Env, args: &[AST]) -> Result<Value, String> {
184 if args.len() != 2 {
185 return Err(String::from(
186 "lambda requires exactly 2 arguments: lambda((args...) body)",
187 ));
188 }
189
190 let mut arg_names = Vec::new();
191 if let AST::List(arg_list) = &args[0] {
192 for arg in arg_list {
193 if let AST::Symbol(sym) = arg {
194 arg_names.push(sym.clone());
195 } else {
196 return Err(String::from("lambda arguments must be symbols"));
197 }
198 }
199 } else {
200 return Err(String::from(
201 "lambda first argument must be a list of symbols",
202 ));
203 }
204
205 let body_ast = args[1].clone();
206 let closure_env = Box::new(env.clone());
207
208 Ok(Value::Lambda(arg_names, body_ast, closure_env))
209}
210
211pub(crate) fn builtin_while(env: &mut Env, args: &[AST]) -> Result<Value, String> {
212 if args.len() < 2 {
213 return Err(String::from(
214 "while requires at least 2 arguments: while(cond, body...)",
215 ));
216 }
217 let cond_ast = &args[0];
218 let body_asts = &args[1..];
219 let mut last_val = Value::Nil;
220
221 loop {
222 crate::kernel::scheduler::yield_now();
223 let eval_cond = eval(env, cond_ast)?;
224 let cond_val = force_eval(env, eval_cond)?;
225 if !super::super::eval::is_truthy(&cond_val)? {
226 break;
227 }
228 for body_ast in body_asts {
229 let eval_body = eval(env, body_ast)?;
230 last_val = force_eval(env, eval_body)?;
231 }
232 }
233 Ok(last_val)
234}
235
236pub(crate) fn builtin_for(env: &mut Env, args: &[AST]) -> Result<Value, String> {
237 if args.len() < 3 {
238 return Err(String::from(
239 "for requires at least 3 arguments: for(var, list, body...)",
240 ));
241 }
242 let var_name = if let AST::Symbol(sym) = &args[0] {
243 sym.clone()
244 } else {
245 return Err(String::from("for first argument must be a variable symbol"));
246 };
247
248 let eval_list = eval(env, &args[1])?;
249 let list_val = force_eval(env, eval_list)?;
250 let body_asts = &args[2..];
251 let mut last_val = Value::Nil;
252
253 if let Value::List(items) = list_val {
254 for item in items {
255 crate::kernel::scheduler::yield_now();
256 env.set(var_name.clone(), item);
257 for body_ast in body_asts {
258 let eval_body = eval(env, body_ast)?;
259 last_val = force_eval(env, eval_body)?;
260 }
261 }
262 } else {
263 return Err(String::from("for second argument must evaluate to a list"));
264 }
265 Ok(last_val)
266}
267
268pub(crate) fn builtin_list(env: &mut Env, args: &[AST]) -> Result<Value, String> {
269 let mut items = Vec::new();
270 for arg in args {
271 let eval_arg = eval(env, arg)?;
272 items.push(force_eval(env, eval_arg)?);
273 }
274 Ok(Value::List(items))
275}
276
277pub(crate) fn builtin_cons(env: &mut Env, args: &[AST]) -> Result<Value, String> {
278 if args.len() != 2 {
279 return Err(String::from("cons requires exactly 2 arguments"));
280 }
281 let eval_head = eval(env, &args[0])?;
282 let head = force_eval(env, eval_head)?;
283 let eval_tail = eval(env, &args[1])?;
284 let tail = force_eval(env, eval_tail)?;
285
286 if let Value::List(mut list) = tail {
287 list.insert(0, head);
288 Ok(Value::List(list))
289 } else {
290 Err(String::from("cons second argument must be a list"))
291 }
292}
293
294pub(crate) fn builtin_car(env: &mut Env, args: &[AST]) -> Result<Value, String> {
295 if args.len() != 1 {
296 return Err(String::from("car requires exactly 1 argument"));
297 }
298 let eval_arg = eval(env, &args[0])?;
299 let val = force_eval(env, eval_arg)?;
300 if let Value::List(list) = val {
301 if list.is_empty() {
302 Ok(Value::Nil)
303 } else {
304 Ok(list[0].clone())
305 }
306 } else {
307 Err(String::from("car argument must be a list"))
308 }
309}
310
311pub(crate) fn builtin_cdr(env: &mut Env, args: &[AST]) -> Result<Value, String> {
312 if args.len() != 1 {
313 return Err(String::from("cdr requires exactly 1 argument"));
314 }
315 let eval_arg = eval(env, &args[0])?;
316 let val = force_eval(env, eval_arg)?;
317 if let Value::List(list) = val {
318 if list.is_empty() {
319 Ok(Value::List(Vec::new()))
320 } else {
321 Ok(Value::List(list[1..].to_vec()))
322 }
323 } else {
324 Err(String::from("cdr argument must be a list"))
325 }
326}
327
328pub(crate) fn builtin_map(env: &mut Env, args: &[AST]) -> Result<Value, String> {
329 if args.len() != 2 {
330 return Err(String::from("map requires exactly 2 arguments"));
331 }
332 let eval_func = eval(env, &args[0])?;
333 let func_val = force_eval(env, eval_func)?;
334 let eval_list = eval(env, &args[1])?;
335 let list_val = force_eval(env, eval_list)?;
336
337 if let Value::Stream(_) = &list_val {
338 return Ok(Value::Stream(alloc::rc::Rc::new(core::cell::RefCell::new(
339 super::super::eval::StreamCore::Map {
340 source: Box::new(list_val.clone()),
341 func: Box::new(func_val),
342 env: Box::new(env.clone()),
343 },
344 ))));
345 }
346
347 let items = if let Value::List(items) = list_val {
348 items
349 } else {
350 return Err(String::from("map second argument must be a list or stream"));
351 };
352
353 let mut result = Vec::new();
354 match func_val {
355 Value::Lambda(arg_names, body_ast, closure_env) => {
356 if arg_names.len() != 1 {
357 return Err(String::from("map function must take exactly 1 argument"));
358 }
359 for item in items {
360 let mut local_env = Env::with_parent(*closure_env.clone());
361 local_env.set(arg_names[0].clone(), item);
362 let evaluated = eval(&mut local_env, &body_ast)?;
363 let res = force_eval(&mut local_env, evaluated)?;
364 result.push(res);
365 }
366 }
367 _ => return Err(String::from("map first argument must be a lambda function")),
368 }
369 Ok(Value::List(result))
370}
371
372pub(crate) fn builtin_filter(env: &mut Env, args: &[AST]) -> Result<Value, String> {
373 if args.len() != 2 {
374 return Err(String::from("filter requires exactly 2 arguments"));
375 }
376 let eval_func = eval(env, &args[0])?;
377 let func_val = force_eval(env, eval_func)?;
378 let eval_list = eval(env, &args[1])?;
379 let list_val = force_eval(env, eval_list)?;
380
381 if let Value::Stream(_) = &list_val {
382 return Ok(Value::Stream(alloc::rc::Rc::new(core::cell::RefCell::new(
383 super::super::eval::StreamCore::Filter {
384 source: Box::new(list_val.clone()),
385 func: Box::new(func_val),
386 env: Box::new(env.clone()),
387 },
388 ))));
389 }
390
391 let items = if let Value::List(items) = list_val {
392 items
393 } else {
394 return Err(String::from(
395 "filter second argument must be a list or stream",
396 ));
397 };
398
399 let mut result = Vec::new();
400 match func_val {
401 Value::Lambda(arg_names, body_ast, closure_env) => {
402 if arg_names.len() != 1 {
403 return Err(String::from("filter function must take exactly 1 argument"));
404 }
405 for item in items {
406 let mut local_env = Env::with_parent(*closure_env.clone());
407 local_env.set(arg_names[0].clone(), item.clone());
408 let evaluated = eval(&mut local_env, &body_ast)?;
409 let res = force_eval(&mut local_env, evaluated)?;
410 if super::super::eval::is_truthy(&res)? {
411 result.push(item);
412 }
413 }
414 }
415 _ => {
416 return Err(String::from(
417 "filter first argument must be a lambda function",
418 ))
419 }
420 }
421 Ok(Value::List(result))
422}
423
424pub(crate) fn builtin_range(env: &mut Env, args: &[AST]) -> Result<Value, String> {
425 if args.is_empty() || args.len() > 3 {
426 return Err(String::from("range requires 1 to 3 arguments"));
427 }
428 let eval_0 = eval(env, &args[0])?;
429 let start_val = force_eval(env, eval_0)?;
430 let start = if let Value::Num(n) = start_val {
431 n
432 } else {
433 return Err(String::from("range arguments must be numbers"));
434 };
435
436 let mut end = None;
437 if args.len() >= 2 {
438 let eval_1 = eval(env, &args[1])?;
439 let end_val = force_eval(env, eval_1)?;
440 if let Value::Num(n) = end_val {
441 end = Some(n);
442 } else {
443 return Err(String::from("range arguments must be numbers"));
444 }
445 }
446
447 let mut step = BigInt::one();
448 if args.len() == 3 {
449 let eval_2 = eval(env, &args[2])?;
450 let step_val = force_eval(env, eval_2)?;
451 if let Value::Num(n) = step_val {
452 step = n;
453 } else {
454 return Err(String::from("range arguments must be numbers"));
455 }
456 }
457
458 Ok(Value::Stream(alloc::rc::Rc::new(core::cell::RefCell::new(
459 super::super::eval::StreamCore::Range {
460 current: start,
461 end,
462 step,
463 },
464 ))))
465}
466
467pub(crate) fn builtin_take(env: &mut Env, args: &[AST]) -> Result<Value, String> {
468 if args.len() != 2 {
469 return Err(String::from("take requires exactly 2 arguments"));
470 }
471 let eval_0 = eval(env, &args[0])?;
472 let count_val = force_eval(env, eval_0)?;
473 let count = if let Value::Num(n) = count_val {
474 n.to_i64().unwrap_or(0).max(0) as usize
475 } else {
476 return Err(String::from("take count must be a number"));
477 };
478
479 let eval_1 = eval(env, &args[1])?;
480 let stream_val = force_eval(env, eval_1)?;
481
482 match stream_val {
483 Value::Stream(rc) => {
484 let mut result = Vec::new();
485 for _ in 0..count {
486 let next_opt = rc.borrow_mut().next()?;
487 if let Some(val) = next_opt {
488 result.push(val);
489 } else {
490 break;
491 }
492 }
493 Ok(Value::List(result))
494 }
495 Value::List(l) => {
496 let n = count.min(l.len());
497 Ok(Value::List(l[..n].to_vec()))
498 }
499 _ => Err(String::from(
500 "take second argument must be a stream or list",
501 )),
502 }
503}
504
505pub(crate) fn builtin_to_list(env: &mut Env, args: &[AST]) -> Result<Value, String> {
506 if args.len() != 1 {
507 return Err(String::from("to-list requires exactly 1 argument"));
508 }
509 let eval_0 = eval(env, &args[0])?;
510 let stream_val = force_eval(env, eval_0)?;
511
512 match stream_val {
513 Value::Stream(rc) => {
514 let mut result = Vec::new();
515 loop {
516 let next_opt = rc.borrow_mut().next()?;
517 if let Some(val) = next_opt {
518 result.push(val);
519 } else {
520 break;
521 }
522 }
523 Ok(Value::List(result))
524 }
525 Value::List(_) => Ok(stream_val),
526 _ => Err(String::from("to-list argument must be a stream or list")),
527 }
528}
529
530pub(crate) fn builtin_fold(env: &mut Env, args: &[AST]) -> Result<Value, String> {
531 if args.len() != 3 {
532 return Err(String::from(
533 "fold requires exactly 3 arguments: fold(fn, init, list)",
534 ));
535 }
536 let eval_func = eval(env, &args[0])?;
537 let func_val = force_eval(env, eval_func)?;
538 let eval_acc = eval(env, &args[1])?;
539 let mut acc = force_eval(env, eval_acc)?;
540 let eval_list = eval(env, &args[2])?;
541 let list_val = force_eval(env, eval_list)?;
542
543 let items = if let Value::List(items) = list_val {
544 items
545 } else {
546 return Err(String::from("fold third argument must be a list"));
547 };
548
549 match func_val {
550 Value::Lambda(arg_names, body_ast, closure_env) => {
551 if arg_names.len() != 2 {
552 return Err(String::from(
553 "fold function must take exactly 2 arguments (acc, item)",
554 ));
555 }
556 for item in items {
557 let mut local_env = Env::with_parent(*closure_env.clone());
558 local_env.set(arg_names[0].clone(), acc);
559 local_env.set(arg_names[1].clone(), item);
560 let eval_body = eval(&mut local_env, &body_ast)?;
561 acc = force_eval(&mut local_env, eval_body)?;
562 }
563 }
564 _ => {
565 return Err(String::from(
566 "fold first argument must be a lambda function",
567 ))
568 }
569 }
570 Ok(acc)
571}
572
573pub(crate) fn builtin_print(env: &mut Env, args: &[AST]) -> Result<Value, String> {
574 let mut output = String::new();
575 for (i, arg) in args.iter().enumerate() {
576 let val = eval(env, arg)?;
577 let val = force_eval(env, val)?;
578 if i > 0 {
579 output.push(' ');
580 }
581 output.push_str(&val.to_string());
582 }
583 Ok(Value::Str(output))
584}
585
586pub(crate) fn builtin_complex(env: &mut Env, args: &[AST]) -> Result<Value, String> {
587 let vals = eval_args(env, args)?;
588 if vals.len() != 2 {
589 return Err(String::from("complex requires exactly 2 arguments"));
590 }
591
592 let real = match &vals[0] {
593 Value::Num(n) => Ratio::from_bigint(n.clone()),
594 Value::Ratio(r) => r.clone(),
595 _ => return Err(String::from("complex first argument must be a number")),
596 };
597
598 let imag = match &vals[1] {
599 Value::Num(n) => Ratio::from_bigint(n.clone()),
600 Value::Ratio(r) => r.clone(),
601 _ => return Err(String::from("complex second argument must be a number")),
602 };
603
604 Ok(Value::Complex(Complex::new(real, imag)))
605}
606
607pub(crate) fn builtin_get(env: &mut Env, args: &[AST]) -> Result<Value, String> {
608 let vals = eval_args(env, args)?;
609 if vals.len() != 2 {
610 return Err(String::from("get requires exactly 2 arguments (dict, key)"));
611 }
612
613 if let Value::Dict(dict) = &vals[0] {
614 let key_str = match &vals[1] {
615 Value::Str(s) => s.clone(),
616 Value::Symbol(s) => s.clone(),
617 _ => return Err(String::from("get key must be a string or symbol")),
618 };
619
620 if let Some(val) = dict.get(&key_str) {
621 Ok(val.clone())
622 } else {
623 Ok(Value::Nil)
624 }
625 } else {
626 Err(String::from("get first argument must be a dict"))
627 }
628}
629
630pub(crate) fn builtin_set(env: &mut Env, args: &[AST]) -> Result<Value, String> {
631 if args.len() == 2 {
632 if let AST::Symbol(sym) = &args[0] {
634 let val = eval(env, &args[1])?;
635 let forced = force_eval(env, val)?;
636 env.set(sym.clone(), forced.clone());
637 Ok(forced)
638 } else {
639 Err(String::from(
640 "First argument to set/func must be a symbol when binding",
641 ))
642 }
643 } else if args.len() == 3 {
644 let vals = eval_args(env, args)?;
646 if let Value::Dict(dict) = &vals[0] {
647 let key_str = match &vals[1] {
648 Value::Str(s) => s.clone(),
649 Value::Symbol(s) => s.clone(),
650 _ => return Err(String::from("set key must be a string or symbol")),
651 };
652 let mut new_dict = dict.clone();
653 new_dict.insert(key_str, vals[2].clone());
654 Ok(Value::Dict(new_dict))
655 } else {
656 Err(String::from(
657 "set first argument must be a dict when updating",
658 ))
659 }
660 } else {
661 Err(String::from("set/func requires exactly 2 or 3 arguments"))
662 }
663}
664
665pub(crate) fn builtin_length(env: &mut Env, args: &[AST]) -> Result<Value, String> {
666 let vals = eval_args(env, args)?;
667 if vals.len() != 1 {
668 return Err(String::from("length requires exactly 1 argument"));
669 }
670 match &vals[0] {
671 Value::List(l) => Ok(Value::Num(BigInt::from_i64(l.len() as i64))),
672 Value::Str(s) => Ok(Value::Num(BigInt::from_i64(s.chars().count() as i64))),
673 Value::Dict(d) => Ok(Value::Num(BigInt::from_i64(d.len() as i64))),
674 _ => Err(String::from("length requires a list, string, or dict")),
675 }
676}
677
678pub(crate) fn builtin_type(env: &mut Env, args: &[AST]) -> Result<Value, String> {
679 let vals = eval_args(env, args)?;
680 if vals.len() != 1 {
681 return Err(String::from("type requires exactly 1 argument"));
682 }
683 let type_str = match &vals[0] {
684 Value::Num(_) => "num",
685 Value::Ratio(_) => "ratio",
686 Value::Complex(_) => "complex",
687 Value::Str(_) => "str",
688 Value::Bool(_) => "bool",
689 Value::List(_) => "list",
690 Value::Dict(_) => "dict",
691 Value::Symbol(_) => "symbol",
692 Value::Nil => "nil",
693 Value::Stream(_) => "stream",
694 Value::BuiltinFunc(_) | Value::Lambda(_, _, _) => "func",
695 Value::Thunk(_, _) => "thunk",
696 };
697 Ok(Value::Str(String::from(type_str)))
698}
699
700pub(crate) fn builtin_odd(env: &mut Env, args: &[AST]) -> Result<Value, String> {
701 let vals = eval_args(env, args)?;
702 if vals.len() != 1 {
703 return Err(String::from("odd requires exactly 1 argument"));
704 }
705 let is_odd = match &vals[0] {
706 Value::Num(n) => {
707 if n.digits.is_empty() {
708 false
709 } else {
710 n.digits[0] % 2 != 0
711 }
712 }
713 Value::Ratio(r) if r.den == BigInt::one() => {
714 if r.num.digits.is_empty() {
715 false
716 } else {
717 r.num.digits[0] % 2 != 0
718 }
719 }
720 _ => return Err(String::from("odd requires an integer")),
721 };
722 Ok(Value::Bool(is_odd))
723}
724
725pub(crate) fn builtin_even(env: &mut Env, args: &[AST]) -> Result<Value, String> {
726 let vals = eval_args(env, args)?;
727 if vals.len() != 1 {
728 return Err(String::from("even requires exactly 1 argument"));
729 }
730 let is_even = match &vals[0] {
731 Value::Num(n) => {
732 if n.digits.is_empty() {
733 true
734 } else {
735 n.digits[0] % 2 == 0
736 }
737 }
738 Value::Ratio(r) if r.den == BigInt::one() => {
739 if r.num.digits.is_empty() {
740 true
741 } else {
742 r.num.digits[0] % 2 == 0
743 }
744 }
745 _ => return Err(String::from("even requires an integer")),
746 };
747 Ok(Value::Bool(is_even))
748}
749
750