1use crate::scheme::arena::{Arena, ValueId, ValueData, NIL_ID};
14use std::rc::Rc;
15
16pub type ArenaResult = Result<ValueId, String>;
17
18#[inline]
22pub fn car(arena: &Arena, args: &[ValueId]) -> ArenaResult {
23 if args.len() != 1 {
24 return Err("car requires exactly 1 argument".to_string());
25 }
26
27 match arena.get(args[0]) {
28 ValueData::Pair { car, .. } => Ok(*car),
29 _ => Err(format!("car: not a pair")),
30 }
31}
32
33#[inline]
37pub fn cdr(arena: &Arena, args: &[ValueId]) -> ArenaResult {
38 if args.len() != 1 {
39 return Err("cdr requires exactly 1 argument".to_string());
40 }
41
42 match arena.get(args[0]) {
43 ValueData::Pair { cdr, .. } => Ok(*cdr),
44 _ => Err(format!("cdr: not a pair")),
45 }
46}
47
48#[inline]
52pub fn cons(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
53 if args.len() != 2 {
54 return Err("cons requires exactly 2 arguments".to_string());
55 }
56
57 Ok(arena.cons(args[0], args[1]))
58}
59
60#[inline]
64pub fn null(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
65 if args.len() != 1 {
66 return Err("null? requires exactly 1 argument".to_string());
67 }
68
69 Ok(if args[0] == NIL_ID {
70 crate::scheme::arena::TRUE_ID
71 } else {
72 crate::scheme::arena::FALSE_ID
73 })
74}
75
76pub fn equal(arena: &Arena, args: &[ValueId]) -> ArenaResult {
80 if args.len() != 2 {
81 return Err("equal? requires exactly 2 arguments".to_string());
82 }
83
84 Ok(if equal_impl(arena, args[0], args[1]) {
85 crate::scheme::arena::TRUE_ID
86 } else {
87 crate::scheme::arena::FALSE_ID
88 })
89}
90
91fn equal_impl(arena: &Arena, a: ValueId, b: ValueId) -> bool {
93 if a == b {
95 return true;
96 }
97
98 match (arena.get(a), arena.get(b)) {
99 (ValueData::Pair { car: car1, cdr: cdr1, .. },
101 ValueData::Pair { car: car2, cdr: cdr2, .. }) => {
102 equal_impl(arena, *car1, *car2) && equal_impl(arena, *cdr1, *cdr2)
103 }
104
105 (ValueData::Vector(v1), ValueData::Vector(v2)) => {
107 v1.len() == v2.len() &&
108 v1.iter().zip(v2.iter()).all(|(a, b)| equal_impl(arena, *a, *b))
109 }
110
111 (ValueData::Integer(n1), ValueData::Integer(n2)) => n1 == n2,
113 (ValueData::Real(f1), ValueData::Real(f2)) => f1 == f2,
114 (ValueData::Bool(b1), ValueData::Bool(b2)) => b1 == b2,
115 (ValueData::Char(c1), ValueData::Char(c2)) => c1 == c2,
116 (ValueData::String(s1), ValueData::String(s2)) => s1 == s2,
117 (ValueData::Symbol(s1), ValueData::Symbol(s2)) => s1 == s2,
118
119 _ => false,
120 }
121}
122
123#[inline]
130pub fn cadr(arena: &Arena, args: &[ValueId]) -> ArenaResult {
131 if args.len() != 1 {
132 return Err("cadr requires exactly 1 argument".to_string());
133 }
134 let cdr_result = cdr(arena, args)?;
135 car(arena, &[cdr_result])
136}
137
138#[inline]
141pub fn caddr(arena: &Arena, args: &[ValueId]) -> ArenaResult {
142 if args.len() != 1 {
143 return Err("caddr requires exactly 1 argument".to_string());
144 }
145 let cdr1 = cdr(arena, args)?;
146 let cdr2 = cdr(arena, &[cdr1])?;
147 car(arena, &[cdr2])
148}
149
150#[inline]
153pub fn cadddr(arena: &Arena, args: &[ValueId]) -> ArenaResult {
154 if args.len() != 1 {
155 return Err("cadddr requires exactly 1 argument".to_string());
156 }
157 let cdr1 = cdr(arena, args)?;
158 let cdr2 = cdr(arena, &[cdr1])?;
159 let cdr3 = cdr(arena, &[cdr2])?;
160 car(arena, &[cdr3])
161}
162
163pub fn list(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
166 let mut result = NIL_ID;
167 for &arg in args.iter().rev() {
168 result = arena.cons(arg, result);
169 }
170 Ok(result)
171}
172
173pub fn length(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
176 if args.len() != 1 {
177 return Err("length requires exactly 1 argument".to_string());
178 }
179
180 let mut len = 0i64;
181 let mut current = args[0];
182
183 loop {
184 if current == NIL_ID {
185 return Ok(arena.int(len));
186 }
187
188 match arena.get(current) {
189 ValueData::Pair { cdr, .. } => {
190 len += 1;
191 current = *cdr;
192 }
193 _ => return Err("length: not a proper list".to_string()),
194 }
195 }
196}
197
198pub fn reverse(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
201 if args.len() != 1 {
202 return Err("reverse requires exactly 1 argument".to_string());
203 }
204
205 let mut result = NIL_ID;
206 let mut current = args[0];
207
208 loop {
209 if current == NIL_ID {
210 return Ok(result);
211 }
212
213 let (car_val, cdr_val) = match arena.get(current) {
215 ValueData::Pair { car, cdr, .. } => (*car, *cdr),
216 _ => return Err("reverse: not a proper list".to_string()),
217 };
218
219 result = arena.cons(car_val, result);
220 current = cdr_val;
221 }
222}
223
224pub fn append(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
227 if args.is_empty() {
228 return Ok(NIL_ID);
229 }
230
231 if args.len() == 1 {
232 return Ok(args[0]);
233 }
234
235 let mut result = args[args.len() - 1];
237
238 for i in (0..args.len() - 1).rev() {
239 let mut items = Vec::new();
240 let mut current = args[i];
241
242 loop {
244 if current == NIL_ID {
245 break;
246 }
247
248 match arena.get(current) {
249 ValueData::Pair { car, cdr, .. } => {
250 items.push(*car);
251 current = *cdr;
252 }
253 _ => return Err("append: not a proper list".to_string()),
254 }
255 }
256
257 for &item in items.iter().rev() {
259 result = arena.cons(item, result);
260 }
261 }
262
263 Ok(result)
264}
265
266pub fn list_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
269 if args.len() != 1 {
270 return Err("list? requires exactly 1 argument".to_string());
271 }
272
273 let mut current = args[0];
274 loop {
275 if current == NIL_ID {
276 return Ok(crate::scheme::arena::TRUE_ID);
277 }
278
279 match arena.get(current) {
280 ValueData::Pair { cdr, .. } => {
281 current = *cdr;
282 }
283 _ => return Ok(crate::scheme::arena::FALSE_ID),
284 }
285 }
286}
287
288pub fn list_ref(arena: &Arena, args: &[ValueId]) -> ArenaResult {
291 if args.len() != 2 {
292 return Err("list-ref requires exactly 2 arguments".to_string());
293 }
294
295 let k = match arena.get(args[1]) {
296 ValueData::Integer(n) if *n >= 0 => *n as usize,
297 ValueData::Integer(_) => return Err("list-ref: index must be non-negative".to_string()),
298 _ => return Err("list-ref: index must be an integer".to_string()),
299 };
300
301 let mut current = args[0];
302 for _ in 0..k {
303 match arena.get(current) {
304 ValueData::Pair { cdr, .. } => {
305 current = *cdr;
306 }
307 _ => return Err("list-ref: index out of bounds".to_string()),
308 }
309 }
310
311 match arena.get(current) {
312 ValueData::Pair { car, .. } => Ok(*car),
313 _ => Err("list-ref: index out of bounds".to_string()),
314 }
315}
316
317#[inline]
324pub fn pair_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
325 if args.len() != 1 {
326 return Err("pair? requires exactly 1 argument".to_string());
327 }
328
329 Ok(match arena.get(args[0]) {
330 ValueData::Pair { .. } => crate::scheme::arena::TRUE_ID,
331 _ => crate::scheme::arena::FALSE_ID,
332 })
333}
334
335#[inline]
338pub fn number_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
339 if args.len() != 1 {
340 return Err("number? requires exactly 1 argument".to_string());
341 }
342
343 Ok(match arena.get(args[0]) {
344 ValueData::Integer(_) | ValueData::Real(_) => crate::scheme::arena::TRUE_ID,
345 _ => crate::scheme::arena::FALSE_ID,
346 })
347}
348
349#[inline]
352pub fn integer_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
353 if args.len() != 1 {
354 return Err("integer? requires exactly 1 argument".to_string());
355 }
356
357 Ok(match arena.get(args[0]) {
358 ValueData::Integer(_) => crate::scheme::arena::TRUE_ID,
359 _ => crate::scheme::arena::FALSE_ID,
360 })
361}
362
363#[inline]
366pub fn real_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
367 if args.len() != 1 {
368 return Err("real? requires exactly 1 argument".to_string());
369 }
370
371 Ok(match arena.get(args[0]) {
372 ValueData::Real(_) => crate::scheme::arena::TRUE_ID,
373 _ => crate::scheme::arena::FALSE_ID,
374 })
375}
376
377#[inline]
380pub fn string_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
381 if args.len() != 1 {
382 return Err("string? requires exactly 1 argument".to_string());
383 }
384
385 Ok(match arena.get(args[0]) {
386 ValueData::String(_) => crate::scheme::arena::TRUE_ID,
387 _ => crate::scheme::arena::FALSE_ID,
388 })
389}
390
391#[inline]
394pub fn symbol_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
395 if args.len() != 1 {
396 return Err("symbol? requires exactly 1 argument".to_string());
397 }
398
399 Ok(match arena.get(args[0]) {
400 ValueData::Symbol(_) => crate::scheme::arena::TRUE_ID,
401 _ => crate::scheme::arena::FALSE_ID,
402 })
403}
404
405#[inline]
408pub fn char_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
409 if args.len() != 1 {
410 return Err("char? requires exactly 1 argument".to_string());
411 }
412
413 Ok(match arena.get(args[0]) {
414 ValueData::Char(_) => crate::scheme::arena::TRUE_ID,
415 _ => crate::scheme::arena::FALSE_ID,
416 })
417}
418
419#[inline]
422pub fn boolean_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
423 if args.len() != 1 {
424 return Err("boolean? requires exactly 1 argument".to_string());
425 }
426
427 Ok(match arena.get(args[0]) {
428 ValueData::Bool(_) => crate::scheme::arena::TRUE_ID,
429 _ => crate::scheme::arena::FALSE_ID,
430 })
431}
432
433#[inline]
436pub fn zero_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
437 if args.len() != 1 {
438 return Err("zero? requires exactly 1 argument".to_string());
439 }
440
441 Ok(match arena.get(args[0]) {
442 ValueData::Integer(n) => {
443 if *n == 0 {
444 crate::scheme::arena::TRUE_ID
445 } else {
446 crate::scheme::arena::FALSE_ID
447 }
448 }
449 ValueData::Real(f) => {
450 if *f == 0.0 {
451 crate::scheme::arena::TRUE_ID
452 } else {
453 crate::scheme::arena::FALSE_ID
454 }
455 }
456 _ => return Err("zero?: not a number".to_string()),
457 })
458}
459
460#[inline]
463pub fn positive_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
464 if args.len() != 1 {
465 return Err("positive? requires exactly 1 argument".to_string());
466 }
467
468 Ok(match arena.get(args[0]) {
469 ValueData::Integer(n) => {
470 if *n > 0 {
471 crate::scheme::arena::TRUE_ID
472 } else {
473 crate::scheme::arena::FALSE_ID
474 }
475 }
476 ValueData::Real(f) => {
477 if *f > 0.0 {
478 crate::scheme::arena::TRUE_ID
479 } else {
480 crate::scheme::arena::FALSE_ID
481 }
482 }
483 _ => return Err("positive?: not a number".to_string()),
484 })
485}
486
487#[inline]
490pub fn negative_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
491 if args.len() != 1 {
492 return Err("negative? requires exactly 1 argument".to_string());
493 }
494
495 Ok(match arena.get(args[0]) {
496 ValueData::Integer(n) => {
497 if *n < 0 {
498 crate::scheme::arena::TRUE_ID
499 } else {
500 crate::scheme::arena::FALSE_ID
501 }
502 }
503 ValueData::Real(f) => {
504 if *f < 0.0 {
505 crate::scheme::arena::TRUE_ID
506 } else {
507 crate::scheme::arena::FALSE_ID
508 }
509 }
510 _ => return Err("negative?: not a number".to_string()),
511 })
512}
513
514#[inline]
517pub fn odd_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
518 if args.len() != 1 {
519 return Err("odd? requires exactly 1 argument".to_string());
520 }
521
522 match arena.get(args[0]) {
523 ValueData::Integer(n) => {
524 Ok(if n % 2 != 0 {
525 crate::scheme::arena::TRUE_ID
526 } else {
527 crate::scheme::arena::FALSE_ID
528 })
529 }
530 _ => Err("odd?: not an integer".to_string()),
531 }
532}
533
534#[inline]
537pub fn even_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
538 if args.len() != 1 {
539 return Err("even? requires exactly 1 argument".to_string());
540 }
541
542 match arena.get(args[0]) {
543 ValueData::Integer(n) => {
544 Ok(if n % 2 == 0 {
545 crate::scheme::arena::TRUE_ID
546 } else {
547 crate::scheme::arena::FALSE_ID
548 })
549 }
550 _ => Err("even?: not an integer".to_string()),
551 }
552}
553
554fn to_number(arena: &Arena, id: ValueId) -> Result<f64, String> {
560 match arena.get(id) {
561 ValueData::Integer(n) => Ok(*n as f64),
562 ValueData::Real(f) => Ok(*f),
563 ValueData::Quantity { magnitude, unit } => {
564 Ok(unit.to_inches(*magnitude))
566 }
567 _ => Err("not a number".to_string()),
568 }
569}
570
571fn from_number(arena: &mut Arena, n: f64) -> ValueId {
573 if n.fract() == 0.0 && n.is_finite() && n >= i64::MIN as f64 && n <= i64::MAX as f64 {
574 arena.int(n as i64)
575 } else {
576 arena.real(n)
577 }
578}
579
580pub fn add(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
584 if args.is_empty() {
585 return Ok(arena.int(0));
586 }
587
588 let mut sum = 0.0;
589 let mut all_ints = true;
590 let mut int_sum = 0i64;
591 let mut quantity_magnitude: Option<f64> = None;
592 let mut quantity_unit: Option<crate::scheme::value::Unit> = None;
593
594 for &arg in args {
595 match arena.get(arg) {
596 ValueData::Integer(n) => {
597 if quantity_magnitude.is_none() {
598 int_sum = int_sum.wrapping_add(*n);
599 }
600 sum += *n as f64;
601 }
602 ValueData::Real(f) => {
603 all_ints = false;
604 sum += f;
605 }
606 ValueData::Quantity { magnitude, unit } => {
607 if let Some(_existing_unit) = quantity_unit {
608 quantity_magnitude = Some(quantity_magnitude.unwrap() + magnitude);
610 } else {
611 quantity_magnitude = Some(*magnitude + sum);
613 quantity_unit = Some(*unit);
614 sum = 0.0; }
616 all_ints = false;
617 }
618 _ => return Err("+: not a number".to_string()),
619 }
620 }
621
622 if let (Some(mag), Some(unit)) = (quantity_magnitude, quantity_unit) {
624 Ok(arena.alloc(ValueData::Quantity {
625 magnitude: mag + sum,
626 unit,
627 }))
628 } else {
629 Ok(if all_ints {
630 arena.int(int_sum)
631 } else {
632 arena.real(sum)
633 })
634 }
635}
636
637pub fn subtract(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
640 if args.is_empty() {
641 return Err("-: requires at least 1 argument".to_string());
642 }
643
644 if args.len() == 1 {
645 match arena.get(args[0]) {
647 ValueData::Integer(n) => return Ok(arena.int(-n)),
648 ValueData::Real(f) => return Ok(arena.real(-f)),
649 _ => return Err("-: not a number".to_string()),
650 }
651 }
652
653 let mut result = to_number(arena, args[0])?;
654 let mut all_ints = matches!(arena.get(args[0]), ValueData::Integer(_));
655
656 for &arg in &args[1..] {
657 result -= to_number(arena, arg)?;
658 if !matches!(arena.get(arg), ValueData::Integer(_)) {
659 all_ints = false;
660 }
661 }
662
663 Ok(if all_ints && result.fract() == 0.0 {
664 arena.int(result as i64)
665 } else {
666 arena.real(result)
667 })
668}
669
670pub fn multiply(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
674 if args.is_empty() {
675 return Ok(arena.int(1));
676 }
677
678 let mut product = 1.0;
679 let mut all_ints = true;
680 let mut int_product = 1i64;
681 let mut quantity_magnitude: Option<f64> = None;
682 let mut quantity_unit: Option<crate::scheme::value::Unit> = None;
683
684 for &arg in args {
685 match arena.get(arg) {
686 ValueData::Integer(n) => {
687 int_product = int_product.wrapping_mul(*n);
688 product *= *n as f64;
689 }
690 ValueData::Real(f) => {
691 all_ints = false;
692 product *= f;
693 }
694 ValueData::Quantity { magnitude, unit } => {
695 if quantity_unit.is_some() {
696 return Err("*: cannot multiply two quantities".to_string());
697 }
698 quantity_magnitude = Some(*magnitude);
699 quantity_unit = Some(*unit);
700 all_ints = false;
701 }
702 _ => return Err("*: not a number".to_string()),
703 }
704 }
705
706 if let (Some(mag), Some(unit)) = (quantity_magnitude, quantity_unit) {
708 Ok(arena.alloc(ValueData::Quantity {
709 magnitude: mag * product,
710 unit,
711 }))
712 } else {
713 Ok(if all_ints {
714 arena.int(int_product)
715 } else {
716 arena.real(product)
717 })
718 }
719}
720
721pub fn divide(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
724 if args.is_empty() {
725 return Err("/: requires at least 1 argument".to_string());
726 }
727
728 if args.len() == 1 {
729 let n = to_number(arena, args[0])?;
731 if n == 0.0 {
732 return Err("/: division by zero".to_string());
733 }
734 return Ok(arena.real(1.0 / n));
735 }
736
737 let mut result = to_number(arena, args[0])?;
738
739 for &arg in &args[1..] {
740 let n = to_number(arena, arg)?;
741 if n == 0.0 {
742 return Err("/: division by zero".to_string());
743 }
744 result /= n;
745 }
746
747 Ok(arena.real(result))
749}
750
751pub fn quotient(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
754 if args.len() != 2 {
755 return Err("quotient requires exactly 2 arguments".to_string());
756 }
757
758 match (arena.get(args[0]), arena.get(args[1])) {
759 (ValueData::Integer(n1), ValueData::Integer(n2)) => {
760 if *n2 == 0 {
761 return Err("quotient: division by zero".to_string());
762 }
763 Ok(arena.int(n1 / n2))
764 }
765 _ => Err("quotient: arguments must be integers".to_string()),
766 }
767}
768
769pub fn remainder(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
772 if args.len() != 2 {
773 return Err("remainder requires exactly 2 arguments".to_string());
774 }
775
776 match (arena.get(args[0]), arena.get(args[1])) {
777 (ValueData::Integer(n1), ValueData::Integer(n2)) => {
778 if *n2 == 0 {
779 return Err("remainder: division by zero".to_string());
780 }
781 Ok(arena.int(n1 % n2))
782 }
783 _ => Err("remainder: arguments must be integers".to_string()),
784 }
785}
786
787pub fn modulo(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
790 if args.len() != 2 {
791 return Err("modulo requires exactly 2 arguments".to_string());
792 }
793
794 match (arena.get(args[0]), arena.get(args[1])) {
795 (ValueData::Integer(n1), ValueData::Integer(n2)) => {
796 if *n2 == 0 {
797 return Err("modulo: division by zero".to_string());
798 }
799 Ok(arena.int(n1.rem_euclid(*n2)))
800 }
801 _ => Err("modulo: arguments must be integers".to_string()),
802 }
803}
804
805pub fn num_eq(arena: &Arena, args: &[ValueId]) -> ArenaResult {
808 if args.len() < 2 {
809 return Err("=: requires at least 2 arguments".to_string());
810 }
811
812 let first = to_number(arena, args[0])?;
813 for &arg in &args[1..] {
814 if to_number(arena, arg)? != first {
815 return Ok(crate::scheme::arena::FALSE_ID);
816 }
817 }
818 Ok(crate::scheme::arena::TRUE_ID)
819}
820
821pub fn num_lt(arena: &Arena, args: &[ValueId]) -> ArenaResult {
824 if args.len() < 2 {
825 return Err("<: requires at least 2 arguments".to_string());
826 }
827
828 let mut prev = to_number(arena, args[0])?;
829 for &arg in &args[1..] {
830 let curr = to_number(arena, arg)?;
831 if prev >= curr {
832 return Ok(crate::scheme::arena::FALSE_ID);
833 }
834 prev = curr;
835 }
836 Ok(crate::scheme::arena::TRUE_ID)
837}
838
839pub fn num_gt(arena: &Arena, args: &[ValueId]) -> ArenaResult {
842 if args.len() < 2 {
843 return Err(">: requires at least 2 arguments".to_string());
844 }
845
846 let mut prev = to_number(arena, args[0])?;
847 for &arg in &args[1..] {
848 let curr = to_number(arena, arg)?;
849 if prev <= curr {
850 return Ok(crate::scheme::arena::FALSE_ID);
851 }
852 prev = curr;
853 }
854 Ok(crate::scheme::arena::TRUE_ID)
855}
856
857pub fn num_le(arena: &Arena, args: &[ValueId]) -> ArenaResult {
860 if args.len() < 2 {
861 return Err("<=: requires at least 2 arguments".to_string());
862 }
863
864 let mut prev = to_number(arena, args[0])?;
865 for &arg in &args[1..] {
866 let curr = to_number(arena, arg)?;
867 if prev > curr {
868 return Ok(crate::scheme::arena::FALSE_ID);
869 }
870 prev = curr;
871 }
872 Ok(crate::scheme::arena::TRUE_ID)
873}
874
875pub fn num_ge(arena: &Arena, args: &[ValueId]) -> ArenaResult {
878 if args.len() < 2 {
879 return Err(">=: requires at least 2 arguments".to_string());
880 }
881
882 let mut prev = to_number(arena, args[0])?;
883 for &arg in &args[1..] {
884 let curr = to_number(arena, arg)?;
885 if prev < curr {
886 return Ok(crate::scheme::arena::FALSE_ID);
887 }
888 prev = curr;
889 }
890 Ok(crate::scheme::arena::TRUE_ID)
891}
892
893pub fn abs(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
896 if args.len() != 1 {
897 return Err("abs requires exactly 1 argument".to_string());
898 }
899
900 match arena.get(args[0]) {
901 ValueData::Integer(n) => Ok(arena.int(n.abs())),
902 ValueData::Real(f) => Ok(arena.real(f.abs())),
903 _ => Err("abs: not a number".to_string()),
904 }
905}
906
907pub fn min(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
910 if args.is_empty() {
911 return Err("min requires at least 1 argument".to_string());
912 }
913
914 let mut min_val = to_number(arena, args[0])?;
915 let mut all_ints = matches!(arena.get(args[0]), ValueData::Integer(_));
916
917 for &arg in &args[1..] {
918 let val = to_number(arena, arg)?;
919 if val < min_val {
920 min_val = val;
921 }
922 if !matches!(arena.get(arg), ValueData::Integer(_)) {
923 all_ints = false;
924 }
925 }
926
927 Ok(if all_ints {
928 arena.int(min_val as i64)
929 } else {
930 arena.real(min_val)
931 })
932}
933
934pub fn max(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
937 if args.is_empty() {
938 return Err("max requires at least 1 argument".to_string());
939 }
940
941 let mut max_val = to_number(arena, args[0])?;
942 let mut all_ints = matches!(arena.get(args[0]), ValueData::Integer(_));
943
944 for &arg in &args[1..] {
945 let val = to_number(arena, arg)?;
946 if val > max_val {
947 max_val = val;
948 }
949 if !matches!(arena.get(arg), ValueData::Integer(_)) {
950 all_ints = false;
951 }
952 }
953
954 Ok(if all_ints {
955 arena.int(max_val as i64)
956 } else {
957 arena.real(max_val)
958 })
959}
960
961pub fn floor(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
964 if args.len() != 1 {
965 return Err("floor requires exactly 1 argument".to_string());
966 }
967
968 let n = to_number(arena, args[0])?;
969 Ok(arena.int(n.floor() as i64))
970}
971
972pub fn ceiling(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
975 if args.len() != 1 {
976 return Err("ceiling requires exactly 1 argument".to_string());
977 }
978
979 let n = to_number(arena, args[0])?;
980 Ok(arena.int(n.ceil() as i64))
981}
982
983pub fn truncate(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
986 if args.len() != 1 {
987 return Err("truncate requires exactly 1 argument".to_string());
988 }
989
990 let n = to_number(arena, args[0])?;
991 Ok(arena.int(n.trunc() as i64))
992}
993
994pub fn round(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
997 if args.len() != 1 {
998 return Err("round requires exactly 1 argument".to_string());
999 }
1000
1001 let n = to_number(arena, args[0])?;
1002 Ok(arena.int(n.round() as i64))
1003}
1004
1005pub fn sqrt(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
1008 if args.len() != 1 {
1009 return Err("sqrt requires exactly 1 argument".to_string());
1010 }
1011
1012 let n = to_number(arena, args[0])?;
1013 Ok(arena.real(n.sqrt()))
1014}
1015
1016pub fn sin(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
1019 if args.len() != 1 {
1020 return Err("sin requires exactly 1 argument".to_string());
1021 }
1022
1023 let n = to_number(arena, args[0])?;
1024 Ok(arena.real(n.sin()))
1025}
1026
1027pub fn cos(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
1030 if args.len() != 1 {
1031 return Err("cos requires exactly 1 argument".to_string());
1032 }
1033
1034 let n = to_number(arena, args[0])?;
1035 Ok(arena.real(n.cos()))
1036}
1037
1038pub fn tan(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
1041 if args.len() != 1 {
1042 return Err("tan requires exactly 1 argument".to_string());
1043 }
1044
1045 let n = to_number(arena, args[0])?;
1046 Ok(arena.real(n.tan()))
1047}
1048
1049pub fn asin(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
1052 if args.len() != 1 {
1053 return Err("asin requires exactly 1 argument".to_string());
1054 }
1055
1056 let n = to_number(arena, args[0])?;
1057 Ok(arena.real(n.asin()))
1058}
1059
1060pub fn acos(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
1063 if args.len() != 1 {
1064 return Err("acos requires exactly 1 argument".to_string());
1065 }
1066
1067 let n = to_number(arena, args[0])?;
1068 Ok(arena.real(n.acos()))
1069}
1070
1071pub fn atan(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
1074 if args.len() != 1 {
1075 return Err("atan requires exactly 1 argument".to_string());
1076 }
1077
1078 let n = to_number(arena, args[0])?;
1079 Ok(arena.real(n.atan()))
1080}
1081
1082pub fn exp(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
1085 if args.len() != 1 {
1086 return Err("exp requires exactly 1 argument".to_string());
1087 }
1088
1089 let n = to_number(arena, args[0])?;
1090 Ok(arena.real(n.exp()))
1091}
1092
1093pub fn log(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
1096 if args.len() != 1 {
1097 return Err("log requires exactly 1 argument".to_string());
1098 }
1099
1100 let n = to_number(arena, args[0])?;
1101 Ok(arena.real(n.ln()))
1102}
1103
1104pub fn expt(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
1107 if args.len() != 2 {
1108 return Err("expt requires exactly 2 arguments".to_string());
1109 }
1110
1111 let base = to_number(arena, args[0])?;
1112 let exp = to_number(arena, args[1])?;
1113 Ok(arena.real(base.powf(exp)))
1114}
1115
1116pub fn string_length(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
1123 if args.len() != 1 {
1124 return Err("string-length requires exactly 1 argument".to_string());
1125 }
1126
1127 match arena.get(args[0]) {
1128 ValueData::String(s) => Ok(arena.int(s.chars().count() as i64)),
1131 _ => Err("string-length: not a string".to_string()),
1132 }
1133}
1134
1135pub fn string_ref(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
1138 if args.len() != 2 {
1139 return Err("string-ref requires exactly 2 arguments".to_string());
1140 }
1141
1142 let s = match arena.get(args[0]) {
1143 ValueData::String(s) => s,
1144 _ => return Err("string-ref: first argument must be a string".to_string()),
1145 };
1146
1147 let k = match arena.get(args[1]) {
1148 ValueData::Integer(n) if *n >= 0 => *n as usize,
1149 ValueData::Integer(_) => return Err("string-ref: index must be non-negative".to_string()),
1150 _ => return Err("string-ref: second argument must be an integer".to_string()),
1151 };
1152
1153 match s.chars().nth(k) {
1154 Some(c) => Ok(arena.char(c)),
1155 None => Err("string-ref: index out of bounds".to_string()),
1156 }
1157}
1158
1159pub fn substring(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
1162 if args.len() != 3 {
1163 return Err("substring requires exactly 3 arguments".to_string());
1164 }
1165
1166 let s = get_string_data(arena, args[0], "substring")?;
1168
1169 let start = match arena.get(args[1]) {
1170 ValueData::Integer(n) if *n >= 0 => *n as usize,
1171 ValueData::Integer(_) => return Err("substring: start must be non-negative".to_string()),
1172 _ => return Err("substring: start must be an integer".to_string()),
1173 };
1174
1175 let end = match arena.get(args[2]) {
1176 ValueData::Integer(n) if *n >= 0 => *n as usize,
1177 ValueData::Integer(_) => return Err("substring: end must be non-negative".to_string()),
1178 _ => return Err("substring: end must be an integer".to_string()),
1179 };
1180
1181 let chars: Vec<char> = s.chars().collect();
1182 let len = chars.len();
1183
1184 let clamped_start = start.min(len);
1187 let clamped_end = end.min(len);
1188
1189 if clamped_start > clamped_end {
1190 return Err("substring: start must be <= end".to_string());
1191 }
1192
1193 let result: String = chars[clamped_start..clamped_end].iter().collect();
1194 Ok(arena.string(result))
1195}
1196
1197pub fn string_append(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
1200 let mut result = String::new();
1201
1202 for &arg in args {
1203 match arena.get(arg) {
1204 ValueData::String(s) => result.push_str(s),
1205 ValueData::Unspecified => {}, _ => return Err("string-append: all arguments must be strings".to_string()),
1208 }
1209 }
1210
1211 Ok(arena.string(result))
1212}
1213
1214fn get_string_data<'a>(arena: &'a Arena, id: ValueId, prim_name: &str) -> Result<std::borrow::Cow<'a, str>, String> {
1216 match arena.get(id) {
1217 ValueData::String(s) => Ok(std::borrow::Cow::Borrowed(s.as_str())),
1218 ValueData::Symbol(s) => Ok(std::borrow::Cow::Borrowed(s.as_ref())),
1219 _ => Err(format!("{}: not a string or symbol", prim_name)),
1220 }
1221}
1222
1223pub fn string_eq(arena: &Arena, args: &[ValueId]) -> ArenaResult {
1226 if args.len() < 2 {
1227 return Err("string=?: requires at least 2 arguments".to_string());
1228 }
1229
1230 let first = get_string_data(arena, args[0], "string=?")?;
1231
1232 for &arg in &args[1..] {
1233 let s = get_string_data(arena, arg, "string=?")?;
1234 if s != first {
1235 return Ok(crate::scheme::arena::FALSE_ID);
1236 }
1237 }
1238
1239 Ok(crate::scheme::arena::TRUE_ID)
1240}
1241
1242pub fn string_lt(arena: &Arena, args: &[ValueId]) -> ArenaResult {
1245 if args.len() < 2 {
1246 return Err("string<?: requires at least 2 arguments".to_string());
1247 }
1248
1249 let mut prev = get_string_data(arena, args[0], "string<?")?.into_owned();
1250
1251 for &arg in &args[1..] {
1252 let s = get_string_data(arena, arg, "string<?")?;
1253 if prev.as_str() >= s.as_ref() {
1254 return Ok(crate::scheme::arena::FALSE_ID);
1255 }
1256 prev = s.into_owned();
1257 }
1258
1259 Ok(crate::scheme::arena::TRUE_ID)
1260}
1261
1262pub fn string_gt(arena: &Arena, args: &[ValueId]) -> ArenaResult {
1265 if args.len() < 2 {
1266 return Err("string>?: requires at least 2 arguments".to_string());
1267 }
1268
1269 let mut prev = get_string_data(arena, args[0], "string>?")?.into_owned();
1270
1271 for &arg in &args[1..] {
1272 let s = get_string_data(arena, arg, "string>?")?;
1273 if prev.as_str() <= s.as_ref() {
1274 return Ok(crate::scheme::arena::FALSE_ID);
1275 }
1276 prev = s.into_owned();
1277 }
1278
1279 Ok(crate::scheme::arena::TRUE_ID)
1280}
1281
1282pub fn string_le(arena: &Arena, args: &[ValueId]) -> ArenaResult {
1285 if args.len() < 2 {
1286 return Err("string<=?: requires at least 2 arguments".to_string());
1287 }
1288
1289 let mut prev = get_string_data(arena, args[0], "string<=?")?.into_owned();
1290
1291 for &arg in &args[1..] {
1292 let s = get_string_data(arena, arg, "string<=?")?;
1293 if prev.as_str() > s.as_ref() {
1294 return Ok(crate::scheme::arena::FALSE_ID);
1295 }
1296 prev = s.into_owned();
1297 }
1298
1299 Ok(crate::scheme::arena::TRUE_ID)
1300}
1301
1302pub fn string_ge(arena: &Arena, args: &[ValueId]) -> ArenaResult {
1305 if args.len() < 2 {
1306 return Err("string>=?: requires at least 2 arguments".to_string());
1307 }
1308
1309 let mut prev = get_string_data(arena, args[0], "string>=?")?.into_owned();
1310
1311 for &arg in &args[1..] {
1312 let s = get_string_data(arena, arg, "string>=?")?;
1313 if prev.as_str() < s.as_ref() {
1314 return Ok(crate::scheme::arena::FALSE_ID);
1315 }
1316 prev = s.into_owned();
1317 }
1318
1319 Ok(crate::scheme::arena::TRUE_ID)
1320}
1321
1322pub fn string_ci_eq(arena: &Arena, args: &[ValueId]) -> ArenaResult {
1325 if args.len() < 2 {
1326 return Err("string-ci=?: requires at least 2 arguments".to_string());
1327 }
1328
1329 let first = get_string_data(arena, args[0], "string-ci=?")?.to_lowercase();
1330
1331 for &arg in &args[1..] {
1332 let s = get_string_data(arena, arg, "string-ci=?")?;
1333 if s.to_lowercase() != first {
1334 return Ok(crate::scheme::arena::FALSE_ID);
1335 }
1336 }
1337
1338 Ok(crate::scheme::arena::TRUE_ID)
1339}
1340
1341pub fn string_ci_lt(arena: &Arena, args: &[ValueId]) -> ArenaResult {
1344 if args.len() < 2 {
1345 return Err("string-ci<?: requires at least 2 arguments".to_string());
1346 }
1347
1348 let mut prev = get_string_data(arena, args[0], "string-ci<?")?.to_lowercase();
1349
1350 for &arg in &args[1..] {
1351 let s = get_string_data(arena, arg, "string-ci<?")?;
1352 let curr = s.to_lowercase();
1353 if prev >= curr {
1354 return Ok(crate::scheme::arena::FALSE_ID);
1355 }
1356 prev = curr;
1357 }
1358
1359 Ok(crate::scheme::arena::TRUE_ID)
1360}
1361
1362pub fn string_ci_gt(arena: &Arena, args: &[ValueId]) -> ArenaResult {
1365 if args.len() < 2 {
1366 return Err("string-ci>?: requires at least 2 arguments".to_string());
1367 }
1368
1369 let mut prev = get_string_data(arena, args[0], "string-ci>?")?.to_lowercase();
1370
1371 for &arg in &args[1..] {
1372 let s = get_string_data(arena, arg, "string-ci>?")?;
1373 let curr = s.to_lowercase();
1374 if prev <= curr {
1375 return Ok(crate::scheme::arena::FALSE_ID);
1376 }
1377 prev = curr;
1378 }
1379
1380 Ok(crate::scheme::arena::TRUE_ID)
1381}
1382
1383pub fn string_ci_le(arena: &Arena, args: &[ValueId]) -> ArenaResult {
1386 if args.len() < 2 {
1387 return Err("string-ci<=?: requires at least 2 arguments".to_string());
1388 }
1389
1390 let mut prev = get_string_data(arena, args[0], "string-ci<=?")?.to_lowercase();
1391
1392 for &arg in &args[1..] {
1393 let s = get_string_data(arena, arg, "string-ci<=?")?;
1394 let curr = s.to_lowercase();
1395 if prev > curr {
1396 return Ok(crate::scheme::arena::FALSE_ID);
1397 }
1398 prev = curr;
1399 }
1400
1401 Ok(crate::scheme::arena::TRUE_ID)
1402}
1403
1404pub fn string_ci_ge(arena: &Arena, args: &[ValueId]) -> ArenaResult {
1407 if args.len() < 2 {
1408 return Err("string-ci>=?: requires at least 2 arguments".to_string());
1409 }
1410
1411 let mut prev = get_string_data(arena, args[0], "string-ci>=?")?.to_lowercase();
1412
1413 for &arg in &args[1..] {
1414 let s = get_string_data(arena, arg, "string-ci>=?")?;
1415 let curr = s.to_lowercase();
1416 if prev < curr {
1417 return Ok(crate::scheme::arena::FALSE_ID);
1418 }
1419 prev = curr;
1420 }
1421
1422 Ok(crate::scheme::arena::TRUE_ID)
1423}
1424
1425#[inline]
1432pub fn char_eq(arena: &Arena, args: &[ValueId]) -> ArenaResult {
1433 if args.len() != 2 {
1434 return Err("char=? requires exactly 2 arguments".to_string());
1435 }
1436
1437 let c1 = match arena.get(args[0]) {
1438 ValueData::Char(c) => *c,
1439 _ => return Err("char=?: not a character".to_string()),
1440 };
1441
1442 let c2 = match arena.get(args[1]) {
1443 ValueData::Char(c) => *c,
1444 _ => return Err("char=?: not a character".to_string()),
1445 };
1446
1447 Ok(if c1 == c2 {
1448 crate::scheme::arena::TRUE_ID
1449 } else {
1450 crate::scheme::arena::FALSE_ID
1451 })
1452}
1453
1454#[inline]
1457pub fn char_lt(arena: &Arena, args: &[ValueId]) -> ArenaResult {
1458 if args.len() != 2 {
1459 return Err("char<? requires exactly 2 arguments".to_string());
1460 }
1461
1462 let c1 = match arena.get(args[0]) {
1463 ValueData::Char(c) => *c,
1464 _ => return Err("char<?: not a character".to_string()),
1465 };
1466
1467 let c2 = match arena.get(args[1]) {
1468 ValueData::Char(c) => *c,
1469 _ => return Err("char<?: not a character".to_string()),
1470 };
1471
1472 Ok(if c1 < c2 {
1473 crate::scheme::arena::TRUE_ID
1474 } else {
1475 crate::scheme::arena::FALSE_ID
1476 })
1477}
1478
1479#[inline]
1482pub fn char_gt(arena: &Arena, args: &[ValueId]) -> ArenaResult {
1483 if args.len() != 2 {
1484 return Err("char>? requires exactly 2 arguments".to_string());
1485 }
1486
1487 let c1 = match arena.get(args[0]) {
1488 ValueData::Char(c) => *c,
1489 _ => return Err("char>?: not a character".to_string()),
1490 };
1491
1492 let c2 = match arena.get(args[1]) {
1493 ValueData::Char(c) => *c,
1494 _ => return Err("char>?: not a character".to_string()),
1495 };
1496
1497 Ok(if c1 > c2 {
1498 crate::scheme::arena::TRUE_ID
1499 } else {
1500 crate::scheme::arena::FALSE_ID
1501 })
1502}
1503
1504#[inline]
1507pub fn char_le(arena: &Arena, args: &[ValueId]) -> ArenaResult {
1508 if args.len() != 2 {
1509 return Err("char<=? requires exactly 2 arguments".to_string());
1510 }
1511
1512 let c1 = match arena.get(args[0]) {
1513 ValueData::Char(c) => *c,
1514 _ => return Err("char<=?: not a character".to_string()),
1515 };
1516
1517 let c2 = match arena.get(args[1]) {
1518 ValueData::Char(c) => *c,
1519 _ => return Err("char<=?: not a character".to_string()),
1520 };
1521
1522 Ok(if c1 <= c2 {
1523 crate::scheme::arena::TRUE_ID
1524 } else {
1525 crate::scheme::arena::FALSE_ID
1526 })
1527}
1528
1529#[inline]
1532pub fn char_ge(arena: &Arena, args: &[ValueId]) -> ArenaResult {
1533 if args.len() != 2 {
1534 return Err("char>=? requires exactly 2 arguments".to_string());
1535 }
1536
1537 let c1 = match arena.get(args[0]) {
1538 ValueData::Char(c) => *c,
1539 _ => return Err("char>=?: not a character".to_string()),
1540 };
1541
1542 let c2 = match arena.get(args[1]) {
1543 ValueData::Char(c) => *c,
1544 _ => return Err("char>=?: not a character".to_string()),
1545 };
1546
1547 Ok(if c1 >= c2 {
1548 crate::scheme::arena::TRUE_ID
1549 } else {
1550 crate::scheme::arena::FALSE_ID
1551 })
1552}
1553
1554#[inline]
1557pub fn char_ci_eq(arena: &Arena, args: &[ValueId]) -> ArenaResult {
1558 if args.len() != 2 {
1559 return Err("char-ci=? requires exactly 2 arguments".to_string());
1560 }
1561
1562 let c1 = match arena.get(args[0]) {
1563 ValueData::Char(c) => c.to_ascii_lowercase(),
1564 _ => return Err("char-ci=?: not a character".to_string()),
1565 };
1566
1567 let c2 = match arena.get(args[1]) {
1568 ValueData::Char(c) => c.to_ascii_lowercase(),
1569 _ => return Err("char-ci=?: not a character".to_string()),
1570 };
1571
1572 Ok(if c1 == c2 {
1573 crate::scheme::arena::TRUE_ID
1574 } else {
1575 crate::scheme::arena::FALSE_ID
1576 })
1577}
1578
1579#[inline]
1582pub fn char_ci_lt(arena: &Arena, args: &[ValueId]) -> ArenaResult {
1583 if args.len() != 2 {
1584 return Err("char-ci<? requires exactly 2 arguments".to_string());
1585 }
1586
1587 let c1 = match arena.get(args[0]) {
1588 ValueData::Char(c) => c.to_ascii_lowercase(),
1589 _ => return Err("char-ci<?: not a character".to_string()),
1590 };
1591
1592 let c2 = match arena.get(args[1]) {
1593 ValueData::Char(c) => c.to_ascii_lowercase(),
1594 _ => return Err("char-ci<?: not a character".to_string()),
1595 };
1596
1597 Ok(if c1 < c2 {
1598 crate::scheme::arena::TRUE_ID
1599 } else {
1600 crate::scheme::arena::FALSE_ID
1601 })
1602}
1603
1604#[inline]
1607pub fn char_ci_gt(arena: &Arena, args: &[ValueId]) -> ArenaResult {
1608 if args.len() != 2 {
1609 return Err("char-ci>? requires exactly 2 arguments".to_string());
1610 }
1611
1612 let c1 = match arena.get(args[0]) {
1613 ValueData::Char(c) => c.to_ascii_lowercase(),
1614 _ => return Err("char-ci>?: not a character".to_string()),
1615 };
1616
1617 let c2 = match arena.get(args[1]) {
1618 ValueData::Char(c) => c.to_ascii_lowercase(),
1619 _ => return Err("char-ci>?: not a character".to_string()),
1620 };
1621
1622 Ok(if c1 > c2 {
1623 crate::scheme::arena::TRUE_ID
1624 } else {
1625 crate::scheme::arena::FALSE_ID
1626 })
1627}
1628
1629#[inline]
1632pub fn char_ci_le(arena: &Arena, args: &[ValueId]) -> ArenaResult {
1633 if args.len() != 2 {
1634 return Err("char-ci<=? requires exactly 2 arguments".to_string());
1635 }
1636
1637 let c1 = match arena.get(args[0]) {
1638 ValueData::Char(c) => c.to_ascii_lowercase(),
1639 _ => return Err("char-ci<=?: not a character".to_string()),
1640 };
1641
1642 let c2 = match arena.get(args[1]) {
1643 ValueData::Char(c) => c.to_ascii_lowercase(),
1644 _ => return Err("char-ci<=?: not a character".to_string()),
1645 };
1646
1647 Ok(if c1 <= c2 {
1648 crate::scheme::arena::TRUE_ID
1649 } else {
1650 crate::scheme::arena::FALSE_ID
1651 })
1652}
1653
1654#[inline]
1657pub fn char_ci_ge(arena: &Arena, args: &[ValueId]) -> ArenaResult {
1658 if args.len() != 2 {
1659 return Err("char-ci>=? requires exactly 2 arguments".to_string());
1660 }
1661
1662 let c1 = match arena.get(args[0]) {
1663 ValueData::Char(c) => c.to_ascii_lowercase(),
1664 _ => return Err("char-ci>=?: not a character".to_string()),
1665 };
1666
1667 let c2 = match arena.get(args[1]) {
1668 ValueData::Char(c) => c.to_ascii_lowercase(),
1669 _ => return Err("char-ci>=?: not a character".to_string()),
1670 };
1671
1672 Ok(if c1 >= c2 {
1673 crate::scheme::arena::TRUE_ID
1674 } else {
1675 crate::scheme::arena::FALSE_ID
1676 })
1677}
1678
1679pub fn char_upcase(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
1682 if args.len() != 1 {
1683 return Err("char-upcase requires exactly 1 argument".to_string());
1684 }
1685
1686 match arena.get(args[0]) {
1687 ValueData::Char(c) => Ok(arena.char(c.to_ascii_uppercase())),
1688 _ => Err("char-upcase: not a character".to_string()),
1689 }
1690}
1691
1692pub fn char_downcase(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
1695 if args.len() != 1 {
1696 return Err("char-downcase requires exactly 1 argument".to_string());
1697 }
1698
1699 match arena.get(args[0]) {
1700 ValueData::Char(c) => Ok(arena.char(c.to_ascii_lowercase())),
1701 _ => Err("char-downcase: not a character".to_string()),
1702 }
1703}
1704
1705#[inline]
1708pub fn char_alphabetic_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
1709 if args.len() != 1 {
1710 return Err("char-alphabetic? requires exactly 1 argument".to_string());
1711 }
1712
1713 match arena.get(args[0]) {
1714 ValueData::Char(c) => Ok(if c.is_alphabetic() {
1715 crate::scheme::arena::TRUE_ID
1716 } else {
1717 crate::scheme::arena::FALSE_ID
1718 }),
1719 _ => Err("char-alphabetic?: not a character".to_string()),
1720 }
1721}
1722
1723#[inline]
1726pub fn char_numeric_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
1727 if args.len() != 1 {
1728 return Err("char-numeric? requires exactly 1 argument".to_string());
1729 }
1730
1731 match arena.get(args[0]) {
1732 ValueData::Char(c) => Ok(if c.is_numeric() {
1733 crate::scheme::arena::TRUE_ID
1734 } else {
1735 crate::scheme::arena::FALSE_ID
1736 }),
1737 _ => Err("char-numeric?: not a character".to_string()),
1738 }
1739}
1740
1741#[inline]
1744pub fn char_whitespace_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
1745 if args.len() != 1 {
1746 return Err("char-whitespace? requires exactly 1 argument".to_string());
1747 }
1748
1749 match arena.get(args[0]) {
1750 ValueData::Char(c) => Ok(if c.is_whitespace() {
1751 crate::scheme::arena::TRUE_ID
1752 } else {
1753 crate::scheme::arena::FALSE_ID
1754 }),
1755 _ => Err("char-whitespace?: not a character".to_string()),
1756 }
1757}
1758
1759pub fn char_to_integer(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
1762 if args.len() != 1 {
1763 return Err("char->integer requires exactly 1 argument".to_string());
1764 }
1765
1766 match arena.get(args[0]) {
1767 ValueData::Char(c) => Ok(arena.int(*c as i64)),
1768 _ => Err("char->integer: not a character".to_string()),
1769 }
1770}
1771
1772pub fn integer_to_char(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
1775 if args.len() != 1 {
1776 return Err("integer->char requires exactly 1 argument".to_string());
1777 }
1778
1779 match arena.get(args[0]) {
1780 ValueData::Integer(n) => {
1781 if *n < 0 || *n > 0x10FFFF {
1782 return Err(format!("integer->char: invalid code point: {}", n));
1783 }
1784 match char::from_u32(*n as u32) {
1785 Some(c) => Ok(arena.char(c)),
1786 None => Err(format!("integer->char: invalid Unicode code point: {}", n)),
1787 }
1788 }
1789 _ => Err("integer->char: not an integer".to_string()),
1790 }
1791}
1792
1793pub fn char_property(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
1796 if args.len() != 1 {
1797 return Err("char-property requires exactly 1 argument".to_string());
1798 }
1799
1800 match arena.get(args[0]) {
1801 ValueData::Char(_) => Ok(arena.string("unknown".to_string())),
1802 _ => Err("char-property: not a character".to_string()),
1803 }
1804}
1805
1806pub fn char_script_case(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
1809 if args.len() != 1 {
1810 return Err("char-script-case requires exactly 1 argument".to_string());
1811 }
1812
1813 match arena.get(args[0]) {
1814 ValueData::Char(_) => Ok(arena.symbol(std::rc::Rc::from("unknown"))),
1815 _ => Err("char-script-case: not a character".to_string()),
1816 }
1817}
1818
1819pub fn symbol_to_string(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
1826 if args.len() != 1 {
1827 return Err("symbol->string requires exactly 1 argument".to_string());
1828 }
1829
1830 match arena.get(args[0]) {
1831 ValueData::Symbol(s) => Ok(arena.string(s.to_string())),
1832 _ => Err("symbol->string: not a symbol".to_string()),
1833 }
1834}
1835
1836pub fn string_to_symbol(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
1839 if args.len() != 1 {
1840 return Err("string->symbol requires exactly 1 argument".to_string());
1841 }
1842
1843 match arena.get(args[0]) {
1844 ValueData::String(s) => Ok(arena.symbol(std::rc::Rc::from(s.as_str()))),
1845 _ => Err("string->symbol: not a string".to_string()),
1846 }
1847}
1848
1849#[inline]
1852pub fn keyword_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
1853 if args.len() != 1 {
1854 return Err("keyword? requires exactly 1 argument".to_string());
1855 }
1856
1857 Ok(match arena.get(args[0]) {
1858 ValueData::Keyword(_) => crate::scheme::arena::TRUE_ID,
1859 _ => crate::scheme::arena::FALSE_ID,
1860 })
1861}
1862
1863pub fn keyword_to_string(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
1866 if args.len() != 1 {
1867 return Err("keyword->string requires exactly 1 argument".to_string());
1868 }
1869
1870 match arena.get(args[0]) {
1871 ValueData::Keyword(k) => Ok(arena.string(k.to_string())),
1872 _ => Err("keyword->string: not a keyword".to_string()),
1873 }
1874}
1875
1876pub fn string_to_keyword(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
1879 if args.len() != 1 {
1880 return Err("string->keyword requires exactly 1 argument".to_string());
1881 }
1882
1883 match arena.get(args[0]) {
1884 ValueData::String(s) => Ok(arena.keyword(std::rc::Rc::from(s.as_str()))),
1885 _ => Err("string->keyword: not a string".to_string()),
1886 }
1887}
1888
1889fn eq(arena: &Arena, a: ValueId, b: ValueId) -> bool {
1895 if a == b {
1897 return true;
1898 }
1899
1900 match (arena.get(a), arena.get(b)) {
1903 (ValueData::Integer(n1), ValueData::Integer(n2)) => n1 == n2,
1904 (ValueData::Char(c1), ValueData::Char(c2)) => c1 == c2,
1905 (ValueData::Bool(b1), ValueData::Bool(b2)) => b1 == b2,
1906 (ValueData::Symbol(s1), ValueData::Symbol(s2)) => s1 == s2,
1907 _ => false, }
1909}
1910
1911fn eqv(arena: &Arena, a: ValueId, b: ValueId) -> bool {
1913 if a == b {
1915 return true;
1916 }
1917
1918 match (arena.get(a), arena.get(b)) {
1920 (ValueData::Integer(n1), ValueData::Integer(n2)) => n1 == n2,
1921 (ValueData::Real(f1), ValueData::Real(f2)) => f1 == f2,
1922 (ValueData::Char(c1), ValueData::Char(c2)) => c1 == c2,
1923 (ValueData::Bool(b1), ValueData::Bool(b2)) => b1 == b2,
1924 (ValueData::Symbol(s1), ValueData::Symbol(s2)) => s1 == s2,
1925 _ => false,
1926 }
1927}
1928
1929pub fn memq(arena: &Arena, args: &[ValueId]) -> ArenaResult {
1932 if args.len() != 2 {
1933 return Err("memq requires exactly 2 arguments".to_string());
1934 }
1935
1936 let obj = args[0];
1937 let mut current = args[1];
1938
1939 loop {
1940 if current == crate::scheme::arena::NIL_ID {
1941 return Ok(crate::scheme::arena::FALSE_ID);
1942 }
1943
1944 match arena.get(current) {
1945 ValueData::Pair { car, cdr, .. } => {
1946 if eq(arena, obj, *car) {
1947 return Ok(current);
1948 }
1949 current = *cdr;
1950 }
1951 _ => return Err("memq: not a proper list".to_string()),
1952 }
1953 }
1954}
1955
1956pub fn memv(arena: &Arena, args: &[ValueId]) -> ArenaResult {
1959 if args.len() != 2 {
1960 return Err("memv requires exactly 2 arguments".to_string());
1961 }
1962
1963 let obj = args[0];
1964 let mut current = args[1];
1965
1966 loop {
1967 if current == crate::scheme::arena::NIL_ID {
1968 return Ok(crate::scheme::arena::FALSE_ID);
1969 }
1970
1971 match arena.get(current) {
1972 ValueData::Pair { car, cdr, .. } => {
1973 if eqv(arena, obj, *car) {
1974 return Ok(current);
1975 }
1976 current = *cdr;
1977 }
1978 _ => return Err("memv: not a proper list".to_string()),
1979 }
1980 }
1981}
1982
1983pub fn member(arena: &Arena, args: &[ValueId]) -> ArenaResult {
1986 if args.len() != 2 {
1987 return Err("member requires exactly 2 arguments".to_string());
1988 }
1989
1990 let obj = args[0];
1991 let mut current = args[1];
1992
1993 loop {
1994 if current == crate::scheme::arena::NIL_ID {
1995 return Ok(crate::scheme::arena::FALSE_ID);
1996 }
1997
1998 match arena.get(current) {
1999 ValueData::Pair { car, cdr, .. } => {
2000 if equal_impl(arena, obj, *car) {
2001 return Ok(current);
2002 }
2003 current = *cdr;
2004 }
2005 _ => return Err("member: not a proper list".to_string()),
2006 }
2007 }
2008}
2009
2010pub fn assq(arena: &Arena, args: &[ValueId]) -> ArenaResult {
2013 if args.len() != 2 {
2014 return Err("assq requires exactly 2 arguments".to_string());
2015 }
2016
2017 let obj = args[0];
2018 let mut current = args[1];
2019
2020 loop {
2021 if current == crate::scheme::arena::NIL_ID {
2022 return Ok(crate::scheme::arena::FALSE_ID);
2023 }
2024
2025 match arena.get(current) {
2026 ValueData::Pair { car, cdr, .. } => {
2027 if let ValueData::Pair { car: inner_car, .. } = arena.get(*car) {
2029 if eq(arena, obj, *inner_car) {
2030 return Ok(*car);
2031 }
2032 }
2033 current = *cdr;
2034 }
2035 _ => return Err("assq: not a proper list".to_string()),
2036 }
2037 }
2038}
2039
2040pub fn assv(arena: &Arena, args: &[ValueId]) -> ArenaResult {
2043 if args.len() != 2 {
2044 return Err("assv requires exactly 2 arguments".to_string());
2045 }
2046
2047 let obj = args[0];
2048 let mut current = args[1];
2049
2050 loop {
2051 if current == crate::scheme::arena::NIL_ID {
2052 return Ok(crate::scheme::arena::FALSE_ID);
2053 }
2054
2055 match arena.get(current) {
2056 ValueData::Pair { car, cdr, .. } => {
2057 if let ValueData::Pair { car: inner_car, .. } = arena.get(*car) {
2059 if eqv(arena, obj, *inner_car) {
2060 return Ok(*car);
2061 }
2062 }
2063 current = *cdr;
2064 }
2065 _ => return Err("assv: not a proper list".to_string()),
2066 }
2067 }
2068}
2069
2070pub fn assoc(arena: &Arena, args: &[ValueId]) -> ArenaResult {
2073 if args.len() != 2 {
2074 return Err("assoc requires exactly 2 arguments".to_string());
2075 }
2076
2077 let obj = args[0];
2078 let mut current = args[1];
2079
2080 loop {
2081 if current == crate::scheme::arena::NIL_ID {
2082 return Ok(crate::scheme::arena::FALSE_ID);
2083 }
2084
2085 match arena.get(current) {
2086 ValueData::Pair { car, cdr, .. } => {
2087 if let ValueData::Pair { car: inner_car, .. } = arena.get(*car) {
2089 if equal_impl(arena, obj, *inner_car) {
2090 return Ok(*car);
2091 }
2092 }
2093 current = *cdr;
2094 }
2095 _ => return Err("assoc: not a proper list".to_string()),
2096 }
2097 }
2098}
2099
2100#[inline]
2107pub fn not(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
2108 if args.len() != 1 {
2109 return Err("not requires exactly 1 argument".to_string());
2110 }
2111
2112 let val = args[0];
2113 Ok(if val == crate::scheme::arena::FALSE_ID {
2115 crate::scheme::arena::TRUE_ID
2116 } else {
2117 crate::scheme::arena::FALSE_ID
2118 })
2119}
2120
2121#[inline]
2124pub fn eq_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
2125 if args.len() != 2 {
2126 return Err("eq? requires exactly 2 arguments".to_string());
2127 }
2128
2129 Ok(if eq(arena, args[0], args[1]) {
2130 crate::scheme::arena::TRUE_ID
2131 } else {
2132 crate::scheme::arena::FALSE_ID
2133 })
2134}
2135
2136#[inline]
2139pub fn eqv_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
2140 if args.len() != 2 {
2141 return Err("eqv? requires exactly 2 arguments".to_string());
2142 }
2143
2144 Ok(if eqv(arena, args[0], args[1]) {
2145 crate::scheme::arena::TRUE_ID
2146 } else {
2147 crate::scheme::arena::FALSE_ID
2148 })
2149}
2150
2151#[inline]
2154pub fn caar(arena: &Arena, args: &[ValueId]) -> ArenaResult {
2155 if args.len() != 1 {
2156 return Err("caar requires exactly 1 argument".to_string());
2157 }
2158
2159 let car1 = car(arena, args)?;
2160 car(arena, &[car1])
2161}
2162
2163#[inline]
2166pub fn cdar(arena: &Arena, args: &[ValueId]) -> ArenaResult {
2167 if args.len() != 1 {
2168 return Err("cdar requires exactly 1 argument".to_string());
2169 }
2170
2171 let car_val = car(arena, args)?;
2172 cdr(arena, &[car_val])
2173}
2174
2175#[inline]
2178pub fn cddr(arena: &Arena, args: &[ValueId]) -> ArenaResult {
2179 if args.len() != 1 {
2180 return Err("cddr requires exactly 1 argument".to_string());
2181 }
2182
2183 let cdr1 = cdr(arena, args)?;
2184 cdr(arena, &[cdr1])
2185}
2186
2187#[inline]
2192pub fn caaar(arena: &Arena, args: &[ValueId]) -> ArenaResult {
2193 if args.len() != 1 {
2194 return Err("caaar requires exactly 1 argument".to_string());
2195 }
2196 let car1 = car(arena, args)?;
2197 let car2 = car(arena, &[car1])?;
2198 car(arena, &[car2])
2199}
2200
2201#[inline]
2204pub fn caadr(arena: &Arena, args: &[ValueId]) -> ArenaResult {
2205 if args.len() != 1 {
2206 return Err("caadr requires exactly 1 argument".to_string());
2207 }
2208 let cdr1 = cdr(arena, args)?;
2209 let car2 = car(arena, &[cdr1])?;
2210 car(arena, &[car2])
2211}
2212
2213#[inline]
2216pub fn cadar(arena: &Arena, args: &[ValueId]) -> ArenaResult {
2217 if args.len() != 1 {
2218 return Err("cadar requires exactly 1 argument".to_string());
2219 }
2220 let car1 = car(arena, args)?;
2221 let cdr2 = cdr(arena, &[car1])?;
2222 car(arena, &[cdr2])
2223}
2224
2225#[inline]
2228pub fn cdaar(arena: &Arena, args: &[ValueId]) -> ArenaResult {
2229 if args.len() != 1 {
2230 return Err("cdaar requires exactly 1 argument".to_string());
2231 }
2232 let car1 = car(arena, args)?;
2233 let car2 = car(arena, &[car1])?;
2234 cdr(arena, &[car2])
2235}
2236
2237#[inline]
2240pub fn cdadr(arena: &Arena, args: &[ValueId]) -> ArenaResult {
2241 if args.len() != 1 {
2242 return Err("cdadr requires exactly 1 argument".to_string());
2243 }
2244 let cdr1 = cdr(arena, args)?;
2245 let car2 = car(arena, &[cdr1])?;
2246 cdr(arena, &[car2])
2247}
2248
2249#[inline]
2252pub fn cddar(arena: &Arena, args: &[ValueId]) -> ArenaResult {
2253 if args.len() != 1 {
2254 return Err("cddar requires exactly 1 argument".to_string());
2255 }
2256 let car1 = car(arena, args)?;
2257 let cdr2 = cdr(arena, &[car1])?;
2258 cdr(arena, &[cdr2])
2259}
2260
2261#[inline]
2264pub fn cdddr(arena: &Arena, args: &[ValueId]) -> ArenaResult {
2265 if args.len() != 1 {
2266 return Err("cdddr requires exactly 1 argument".to_string());
2267 }
2268 let cdr1 = cdr(arena, args)?;
2269 let cdr2 = cdr(arena, &[cdr1])?;
2270 cdr(arena, &[cdr2])
2271}
2272
2273#[inline]
2278pub fn vector(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
2279 Ok(arena.vector(args.to_vec()))
2280}
2281
2282#[inline]
2285pub fn make_vector(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
2286 if args.is_empty() || args.len() > 2 {
2287 return Err("make-vector requires 1 or 2 arguments".to_string());
2288 }
2289
2290 match arena.get(args[0]) {
2291 ValueData::Integer(k) if *k >= 0 => {
2292 let size = *k as usize;
2293 let fill = if args.len() == 2 {
2294 args[1]
2295 } else {
2296 crate::scheme::arena::UNSPECIFIED_ID
2297 };
2298 let elements = vec![fill; size];
2299 Ok(arena.vector(elements))
2300 }
2301 ValueData::Integer(_) => Err("make-vector: size must be non-negative".to_string()),
2302 _ => Err("make-vector: first argument must be an integer".to_string()),
2303 }
2304}
2305
2306#[inline]
2309pub fn vector_length(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
2310 if args.len() != 1 {
2311 return Err("vector-length requires exactly 1 argument".to_string());
2312 }
2313
2314 match arena.get(args[0]) {
2315 ValueData::Vector(elements) => Ok(arena.int(elements.len() as i64)),
2316 _ => Err("vector-length: argument must be a vector".to_string()),
2317 }
2318}
2319
2320#[inline]
2323pub fn vector_ref(arena: &Arena, args: &[ValueId]) -> ArenaResult {
2324 if args.len() != 2 {
2325 return Err("vector-ref requires exactly 2 arguments".to_string());
2326 }
2327
2328 let vec_id = args[0];
2329 match arena.get(vec_id) {
2330 ValueData::Vector(elements) => {
2331 match arena.get(args[1]) {
2332 ValueData::Integer(k) if *k >= 0 => {
2333 let index = *k as usize;
2334 if index < elements.len() {
2335 Ok(elements[index])
2336 } else {
2337 Err(format!("vector-ref: index {} out of bounds (length {})", index, elements.len()))
2338 }
2339 }
2340 ValueData::Integer(_) => Err("vector-ref: index must be non-negative".to_string()),
2341 _ => Err("vector-ref: second argument must be an integer".to_string()),
2342 }
2343 }
2344 _ => Err("vector-ref: first argument must be a vector".to_string()),
2345 }
2346}
2347
2348#[inline]
2352pub fn vector_set(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
2353 if args.len() != 3 {
2354 return Err("vector-set! requires exactly 3 arguments".to_string());
2355 }
2356
2357 let vec_id = args[0];
2358 let new_val = args[2];
2359
2360 match arena.get(args[1]) {
2361 ValueData::Integer(k) if *k >= 0 => {
2362 let index = *k as usize;
2363 match arena.get_mut(vec_id) {
2364 ValueData::Vector(ref mut elements) => {
2365 if index < elements.len() {
2366 elements[index] = new_val;
2367 Ok(crate::scheme::arena::UNSPECIFIED_ID)
2368 } else {
2369 Err(format!("vector-set!: index {} out of bounds (length {})", index, elements.len()))
2370 }
2371 }
2372 _ => Err("vector-set!: first argument must be a vector".to_string()),
2373 }
2374 }
2375 ValueData::Integer(_) => Err("vector-set!: index must be non-negative".to_string()),
2376 _ => Err("vector-set!: second argument must be an integer".to_string()),
2377 }
2378}
2379
2380#[inline]
2383pub fn vector_to_list(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
2384 if args.len() != 1 {
2385 return Err("vector->list requires exactly 1 argument".to_string());
2386 }
2387
2388 let elements = match arena.get(args[0]) {
2390 ValueData::Vector(elements) => elements.clone(),
2391 _ => return Err("vector->list: argument must be a vector".to_string()),
2392 };
2393
2394 let mut result = crate::scheme::arena::NIL_ID;
2396 for &elem in elements.iter().rev() {
2397 result = arena.cons(elem, result);
2398 }
2399 Ok(result)
2400}
2401
2402#[inline]
2405pub fn list_to_vector(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
2406 if args.len() != 1 {
2407 return Err("list->vector requires exactly 1 argument".to_string());
2408 }
2409
2410 let mut elements = Vec::new();
2412 let mut current = args[0];
2413
2414 loop {
2415 match arena.get(current) {
2416 ValueData::Nil => break,
2417 ValueData::Pair { car, cdr, .. } => {
2418 elements.push(*car);
2419 current = *cdr;
2420 }
2421 _ => return Err("list->vector: argument must be a proper list".to_string()),
2422 }
2423 }
2424
2425 Ok(arena.vector(elements))
2426}
2427
2428#[inline]
2431pub fn vector_fill(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
2432 if args.len() != 2 {
2433 return Err("vector-fill! requires exactly 2 arguments".to_string());
2434 }
2435
2436 let vec_id = args[0];
2437 let fill = args[1];
2438
2439 match arena.get_mut(vec_id) {
2440 ValueData::Vector(ref mut elements) => {
2441 for elem in elements.iter_mut() {
2442 *elem = fill;
2443 }
2444 Ok(crate::scheme::arena::UNSPECIFIED_ID)
2445 }
2446 _ => Err("vector-fill!: first argument must be a vector".to_string()),
2447 }
2448}
2449
2450#[inline]
2455pub fn vector_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
2456 if args.len() != 1 {
2457 return Err("vector? requires exactly 1 argument".to_string());
2458 }
2459
2460 match arena.get(args[0]) {
2461 ValueData::Vector(_) => Ok(crate::scheme::arena::TRUE_ID),
2462 _ => Ok(crate::scheme::arena::FALSE_ID),
2463 }
2464}
2465
2466#[inline]
2469pub fn procedure_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
2470 if args.len() != 1 {
2471 return Err("procedure? requires exactly 1 argument".to_string());
2472 }
2473
2474 match arena.get(args[0]) {
2475 ValueData::Procedure(_) => Ok(crate::scheme::arena::TRUE_ID),
2476 _ => Ok(crate::scheme::arena::FALSE_ID),
2477 }
2478}
2479
2480#[inline]
2483pub fn set_car(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
2484 if args.len() != 2 {
2485 return Err("set-car! requires exactly 2 arguments".to_string());
2486 }
2487
2488 let pair_id = args[0];
2489 let new_car = args[1];
2490
2491 match arena.get_mut(pair_id) {
2492 ValueData::Pair { car, .. } => {
2493 *car = new_car;
2494 Ok(crate::scheme::arena::UNSPECIFIED_ID)
2495 }
2496 _ => Err("set-car!: first argument must be a pair".to_string()),
2497 }
2498}
2499
2500#[inline]
2503pub fn set_cdr(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
2504 if args.len() != 2 {
2505 return Err("set-cdr! requires exactly 2 arguments".to_string());
2506 }
2507
2508 let pair_id = args[0];
2509 let new_cdr = args[1];
2510
2511 match arena.get_mut(pair_id) {
2512 ValueData::Pair { cdr, .. } => {
2513 *cdr = new_cdr;
2514 Ok(crate::scheme::arena::UNSPECIFIED_ID)
2515 }
2516 _ => Err("set-cdr!: first argument must be a pair".to_string()),
2517 }
2518}
2519
2520#[inline]
2523pub fn list_tail(arena: &Arena, args: &[ValueId]) -> ArenaResult {
2524 if args.len() != 2 {
2525 return Err("list-tail requires exactly 2 arguments".to_string());
2526 }
2527
2528 let k = match arena.get(args[1]) {
2529 ValueData::Integer(n) if *n >= 0 => *n as usize,
2530 ValueData::Integer(_) => return Err("list-tail: index must be non-negative".to_string()),
2531 _ => return Err("list-tail: second argument must be an integer".to_string()),
2532 };
2533
2534 let mut current = args[0];
2535 for _ in 0..k {
2536 match arena.get(current) {
2537 ValueData::Pair { cdr, .. } => {
2538 current = *cdr;
2539 }
2540 ValueData::Nil => return Err("list-tail: list too short".to_string()),
2541 _ => return Err("list-tail: argument must be a list".to_string()),
2542 }
2543 }
2544
2545 Ok(current)
2546}
2547
2548#[inline]
2553pub fn string_upcase(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
2554 if args.len() != 1 {
2555 return Err("string-upcase requires exactly 1 argument".to_string());
2556 }
2557
2558 match arena.get(args[0]) {
2559 ValueData::String(s) => Ok(arena.string(s.to_uppercase())),
2560 _ => Err("string-upcase: argument must be a string".to_string()),
2561 }
2562}
2563
2564#[inline]
2567pub fn string_downcase(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
2568 if args.len() != 1 {
2569 return Err("string-downcase requires exactly 1 argument".to_string());
2570 }
2571
2572 match arena.get(args[0]) {
2573 ValueData::String(s) => Ok(arena.string(s.to_lowercase())),
2574 _ => Err("string-downcase: argument must be a string".to_string()),
2575 }
2576}
2577
2578#[inline]
2581pub fn case_fold_down(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
2582 string_downcase(arena, args)
2583}
2584
2585#[inline]
2589pub fn string_index(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
2590 if args.len() != 2 {
2591 return Err("string-index requires exactly 2 arguments".to_string());
2592 }
2593
2594 let s = match arena.get(args[0]) {
2595 ValueData::String(s) => s.clone(),
2596 _ => return Err("string-index: first argument must be a string".to_string()),
2597 };
2598
2599 let search_str = match arena.get(args[1]) {
2601 ValueData::Char(c) => c.to_string(),
2602 ValueData::String(search) => search.clone(),
2603 _ => return Err("string-index: second argument must be a character or string".to_string()),
2604 };
2605
2606 match s.find(search_str.as_str()) {
2608 Some(idx) => {
2609 let char_idx = s[..idx].chars().count();
2611 Ok(arena.int(char_idx as i64))
2612 }
2613 None => Ok(arena.int(-1)), }
2615}
2616
2617#[inline]
2620pub fn string_to_number(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
2621 if args.len() != 1 {
2622 return Err("string->number requires exactly 1 argument".to_string());
2623 }
2624
2625 match arena.get(args[0]) {
2626 ValueData::String(s) => {
2627 if let Ok(n) = s.parse::<i64>() {
2629 return Ok(arena.int(n));
2630 }
2631 if let Ok(f) = s.parse::<f64>() {
2633 return Ok(arena.real(f));
2634 }
2635 Ok(crate::scheme::arena::FALSE_ID)
2637 }
2638 _ => Err("string->number: argument must be a string".to_string()),
2639 }
2640}
2641
2642#[inline]
2645pub fn number_to_string(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
2646 if args.len() != 1 {
2647 return Err("number->string requires exactly 1 argument".to_string());
2648 }
2649
2650 match arena.get(args[0]) {
2651 ValueData::Integer(n) => Ok(arena.string(n.to_string())),
2652 ValueData::Real(f) => Ok(arena.string(f.to_string())),
2653 _ => Err("number->string: argument must be a number".to_string()),
2654 }
2655}
2656
2657#[inline]
2660pub fn string_to_list(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
2661 if args.len() != 1 {
2662 return Err("string->list requires exactly 1 argument".to_string());
2663 }
2664
2665 let s = match arena.get(args[0]) {
2667 ValueData::String(s) => s.clone(),
2668 _ => return Err("string->list: argument must be a string".to_string()),
2669 };
2670
2671 let mut result = crate::scheme::arena::NIL_ID;
2672 for c in s.chars().rev() {
2673 let char_id = arena.char(c);
2674 result = arena.cons(char_id, result);
2675 }
2676 Ok(result)
2677}
2678
2679#[inline]
2682pub fn list_to_string(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
2683 if args.len() != 1 {
2684 return Err("list->string requires exactly 1 argument".to_string());
2685 }
2686
2687 let mut chars = Vec::new();
2688 let mut current = args[0];
2689
2690 loop {
2691 match arena.get(current) {
2692 ValueData::Nil => break,
2693 ValueData::Pair { car, cdr, .. } => {
2694 match arena.get(*car) {
2695 ValueData::Char(c) => {
2696 chars.push(*c);
2697 current = *cdr;
2698 }
2699 _ => return Err("list->string: list must contain only characters".to_string()),
2700 }
2701 }
2702 _ => return Err("list->string: argument must be a proper list".to_string()),
2703 }
2704 }
2705
2706 let s: String = chars.into_iter().collect();
2707 Ok(arena.string(s))
2708}
2709
2710#[inline]
2715pub fn gcd(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
2716 if args.is_empty() {
2717 return Ok(arena.int(0));
2718 }
2719
2720 fn gcd_two(a: i64, b: i64) -> i64 {
2721 let (mut a, mut b) = (a.abs(), b.abs());
2722 while b != 0 {
2723 let temp = b;
2724 b = a % b;
2725 a = temp;
2726 }
2727 a
2728 }
2729
2730 let mut result = 0i64;
2731 for &arg in args {
2732 match arena.get(arg) {
2733 ValueData::Integer(n) => {
2734 result = gcd_two(result, *n);
2735 }
2736 _ => return Err("gcd: all arguments must be integers".to_string()),
2737 }
2738 }
2739
2740 Ok(arena.int(result))
2741}
2742
2743#[inline]
2746pub fn lcm(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
2747 if args.is_empty() {
2748 return Ok(arena.int(1));
2749 }
2750
2751 fn gcd_two(a: i64, b: i64) -> i64 {
2752 let (mut a, mut b) = (a.abs(), b.abs());
2753 while b != 0 {
2754 let temp = b;
2755 b = a % b;
2756 a = temp;
2757 }
2758 a
2759 }
2760
2761 fn lcm_two(a: i64, b: i64) -> i64 {
2762 if a == 0 || b == 0 {
2763 return 0;
2764 }
2765 (a.abs() / gcd_two(a, b)) * b.abs()
2766 }
2767
2768 let mut result = 1i64;
2769 for &arg in args {
2770 match arena.get(arg) {
2771 ValueData::Integer(n) => {
2772 result = lcm_two(result, *n);
2773 }
2774 _ => return Err("lcm: all arguments must be integers".to_string()),
2775 }
2776 }
2777
2778 Ok(arena.int(result))
2779}
2780
2781#[inline]
2784pub fn exact_to_inexact(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
2785 if args.len() != 1 {
2786 return Err("exact->inexact requires exactly 1 argument".to_string());
2787 }
2788
2789 match arena.get(args[0]) {
2790 ValueData::Integer(n) => Ok(arena.real(*n as f64)),
2791 ValueData::Real(_) => Ok(args[0]), _ => Err("exact->inexact: argument must be a number".to_string()),
2793 }
2794}
2795
2796#[inline]
2799pub fn inexact_to_exact(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
2800 if args.len() != 1 {
2801 return Err("inexact->exact requires exactly 1 argument".to_string());
2802 }
2803
2804 match arena.get(args[0]) {
2805 ValueData::Real(f) => Ok(arena.int(f.round() as i64)),
2806 ValueData::Integer(_) => Ok(args[0]), _ => Err("inexact->exact: argument must be a number".to_string()),
2808 }
2809}
2810
2811#[inline]
2814pub fn make_string(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
2815 if args.is_empty() || args.len() > 2 {
2816 return Err("make-string requires 1 or 2 arguments".to_string());
2817 }
2818
2819 let k = match arena.get(args[0]) {
2820 ValueData::Integer(n) if *n >= 0 => *n as usize,
2821 ValueData::Integer(_) => return Err("make-string: length must be non-negative".to_string()),
2822 _ => return Err("make-string: first argument must be an integer".to_string()),
2823 };
2824
2825 let fill_char = if args.len() == 2 {
2826 match arena.get(args[1]) {
2827 ValueData::Char(c) => *c,
2828 _ => return Err("make-string: second argument must be a character".to_string()),
2829 }
2830 } else {
2831 ' '
2832 };
2833
2834 let s: String = std::iter::repeat(fill_char).take(k).collect();
2835 Ok(arena.string(s))
2836}
2837
2838#[inline]
2841pub fn string(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
2842 let mut chars = Vec::new();
2843
2844 for &arg in args {
2845 match arena.get(arg) {
2846 ValueData::Char(c) => chars.push(*c),
2847 _ => return Err("string: all arguments must be characters".to_string()),
2848 }
2849 }
2850
2851 let s: String = chars.into_iter().collect();
2852 Ok(arena.string(s))
2853}
2854
2855#[inline]
2858pub fn reverse_bang(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
2859 if args.len() != 1 {
2860 return Err("reverse! requires exactly 1 argument".to_string());
2861 }
2862
2863 let mut pairs = Vec::new();
2865 let mut current = args[0];
2866
2867 loop {
2868 match arena.get(current) {
2869 ValueData::Nil => break,
2870 ValueData::Pair { car, cdr, .. } => {
2871 pairs.push((current, *car));
2872 current = *cdr;
2873 }
2874 _ => return Err("reverse!: argument must be a list".to_string()),
2875 }
2876 }
2877
2878 if pairs.is_empty() {
2879 return Ok(crate::scheme::arena::NIL_ID);
2880 }
2881
2882 let mut prev = crate::scheme::arena::NIL_ID;
2884 for &(pair_id, _car_val) in &pairs {
2885 match arena.get_mut(pair_id) {
2886 ValueData::Pair { cdr, .. } => {
2887 *cdr = prev;
2888 prev = pair_id;
2889 }
2890 _ => unreachable!(),
2891 }
2892 }
2893
2894 Ok(prev)
2895}
2896
2897pub fn string_set(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
2904 if args.len() != 3 {
2905 return Err("string-set!: expected 3 arguments".to_string());
2906 }
2907
2908 let s = match arena.get(args[0]) {
2909 ValueData::String(s) => s.clone(),
2910 _ => return Err("string-set!: first argument must be a string".to_string()),
2911 };
2912
2913 let k = match arena.get(args[1]) {
2914 ValueData::Integer(n) => *n as usize,
2915 _ => return Err("string-set!: second argument must be an integer".to_string()),
2916 };
2917
2918 let c = match arena.get(args[2]) {
2919 ValueData::Char(c) => *c,
2920 _ => return Err("string-set!: third argument must be a character".to_string()),
2921 };
2922
2923 if k >= s.len() {
2924 return Err(format!("string-set!: index {} out of bounds for string of length {}", k, s.len()));
2925 }
2926
2927 let mut chars: Vec<char> = s.chars().collect();
2928 chars[k] = c;
2929 let new_string: String = chars.into_iter().collect();
2930
2931 Ok(arena.string(new_string))
2932}
2933
2934pub fn string_copy(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
2936 if args.len() != 1 {
2937 return Err("string-copy: expected 1 argument".to_string());
2938 }
2939
2940 let s = match arena.get(args[0]) {
2941 ValueData::String(s) => s.clone(),
2942 _ => return Err("string-copy: argument must be a string".to_string()),
2943 };
2944
2945 Ok(arena.string(s))
2946}
2947
2948pub fn string_fill(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
2951 if args.len() != 2 {
2952 return Err("string-fill!: expected 2 arguments".to_string());
2953 }
2954
2955 let s = match arena.get(args[0]) {
2956 ValueData::String(s) => s.clone(),
2957 _ => return Err("string-fill!: first argument must be a string".to_string()),
2958 };
2959
2960 let c = match arena.get(args[1]) {
2961 ValueData::Char(c) => *c,
2962 _ => return Err("string-fill!: second argument must be a character".to_string()),
2963 };
2964
2965 let new_string: String = std::iter::repeat(c).take(s.len()).collect();
2966 Ok(arena.string(new_string))
2967}
2968
2969pub fn char_lower_case_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
2971 if args.len() != 1 {
2972 return Err("char-lower-case?: expected 1 argument".to_string());
2973 }
2974
2975 match arena.get(args[0]) {
2976 ValueData::Char(c) => Ok(if c.is_lowercase() { crate::scheme::arena::TRUE_ID } else { crate::scheme::arena::FALSE_ID }),
2977 _ => Err("char-lower-case?: argument must be a character".to_string()),
2978 }
2979}
2980
2981pub fn char_upper_case_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
2983 if args.len() != 1 {
2984 return Err("char-upper-case?: expected 1 argument".to_string());
2985 }
2986
2987 match arena.get(args[0]) {
2988 ValueData::Char(c) => Ok(if c.is_uppercase() { crate::scheme::arena::TRUE_ID } else { crate::scheme::arena::FALSE_ID }),
2989 _ => Err("char-upper-case?: argument must be a character".to_string()),
2990 }
2991}
2992
2993pub fn last(arena: &Arena, args: &[ValueId]) -> ArenaResult {
2999 if args.len() != 1 {
3000 return Err("last: expected 1 argument".to_string());
3001 }
3002
3003 let mut current = args[0];
3004 let mut prev_car = crate::scheme::arena::NIL_ID;
3005
3006 loop {
3007 match arena.get(current) {
3008 ValueData::Nil => {
3009 if prev_car == crate::scheme::arena::NIL_ID {
3010 return Err("last: argument must be a non-empty list".to_string());
3011 }
3012 return Ok(prev_car);
3013 }
3014 ValueData::Pair { car, cdr, .. } => {
3015 prev_car = *car;
3016 current = *cdr;
3017 }
3018 _ => return Err("last: argument must be a proper list".to_string()),
3019 }
3020 }
3021}
3022
3023pub fn last_pair(arena: &Arena, args: &[ValueId]) -> ArenaResult {
3025 if args.len() != 1 {
3026 return Err("last-pair: expected 1 argument".to_string());
3027 }
3028
3029 let mut current = args[0];
3030
3031 if matches!(arena.get(current), ValueData::Nil) {
3033 return Err("last-pair: argument must be a non-empty list".to_string());
3034 }
3035
3036 loop {
3037 match arena.get(current) {
3038 ValueData::Pair { cdr, .. } => {
3039 if matches!(arena.get(*cdr), ValueData::Nil) {
3040 return Ok(current);
3041 }
3042 current = *cdr;
3043 }
3044 _ => return Err("last-pair: argument must be a proper list".to_string()),
3045 }
3046 }
3047}
3048
3049pub fn list_copy(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
3051 if args.len() != 1 {
3052 return Err("list-copy: expected 1 argument".to_string());
3053 }
3054
3055 let mut result = crate::scheme::arena::NIL_ID;
3056 let mut tail = crate::scheme::arena::NIL_ID;
3057 let mut current = args[0];
3058
3059 loop {
3060 let (car_val, cdr_val) = match arena.get(current) {
3062 ValueData::Nil => {
3063 return Ok(result);
3064 }
3065 ValueData::Pair { car, cdr, .. } => (*car, *cdr),
3066 _ => return Err("list-copy: argument must be a proper list".to_string()),
3067 };
3068
3069 let new_pair = arena.cons(car_val, crate::scheme::arena::NIL_ID);
3070 if result == crate::scheme::arena::NIL_ID {
3071 result = new_pair;
3072 tail = new_pair;
3073 } else {
3074 match arena.get_mut(tail) {
3076 ValueData::Pair { cdr: tail_cdr, .. } => {
3077 *tail_cdr = new_pair;
3078 }
3079 _ => unreachable!(),
3080 }
3081 tail = new_pair;
3082 }
3083 current = cdr_val;
3084 }
3085}
3086
3087pub fn append_bang(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
3089 if args.is_empty() {
3090 return Ok(crate::scheme::arena::NIL_ID);
3091 }
3092
3093 if args.len() == 1 {
3094 return Ok(args[0]);
3095 }
3096
3097 let first = args[0];
3099 if matches!(arena.get(first), ValueData::Nil) {
3100 return append_bang(arena, &args[1..]);
3102 }
3103
3104 let mut current = first;
3105 loop {
3106 match arena.get(current) {
3107 ValueData::Pair { cdr, .. } => {
3108 if matches!(arena.get(*cdr), ValueData::Nil) {
3109 let rest = if args.len() > 2 {
3111 append_bang(arena, &args[1..])?
3112 } else {
3113 args[1]
3114 };
3115 match arena.get_mut(current) {
3116 ValueData::Pair { cdr: last_cdr, .. } => {
3117 *last_cdr = rest;
3118 }
3119 _ => unreachable!(),
3120 }
3121 return Ok(first);
3122 }
3123 current = *cdr;
3124 }
3125 _ => return Err("append!: arguments must be lists".to_string()),
3126 }
3127 }
3128}
3129
3130pub fn iota(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
3132 if args.is_empty() || args.len() > 3 {
3133 return Err("iota: expected 1 to 3 arguments".to_string());
3134 }
3135
3136 let count = match arena.get(args[0]) {
3137 ValueData::Integer(n) => *n,
3138 _ => return Err("iota: first argument must be an integer".to_string()),
3139 };
3140
3141 let start = if args.len() > 1 {
3142 match arena.get(args[1]) {
3143 ValueData::Integer(n) => *n,
3144 _ => return Err("iota: second argument must be an integer".to_string()),
3145 }
3146 } else {
3147 0
3148 };
3149
3150 let step = if args.len() > 2 {
3151 match arena.get(args[2]) {
3152 ValueData::Integer(n) => *n,
3153 _ => return Err("iota: third argument must be an integer".to_string()),
3154 }
3155 } else {
3156 1
3157 };
3158
3159 if count < 0 {
3160 return Err("iota: count must be non-negative".to_string());
3161 }
3162
3163 let mut result = crate::scheme::arena::NIL_ID;
3164 for i in (0..count).rev() {
3165 let val = arena.int(start + i * step);
3166 result = arena.cons(val, result);
3167 }
3168
3169 Ok(result)
3170}
3171
3172pub fn take(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
3178 if args.len() != 2 {
3179 return Err("take: expected 2 arguments".to_string());
3180 }
3181
3182 let list = args[0];
3183 let n = match arena.get(args[1]) {
3184 ValueData::Integer(n) => *n,
3185 _ => return Err("take: second argument must be an integer".to_string()),
3186 };
3187
3188 if n < 0 {
3189 return Err("take: count must be non-negative".to_string());
3190 }
3191
3192 let mut result = crate::scheme::arena::NIL_ID;
3193 let mut tail = crate::scheme::arena::NIL_ID;
3194 let mut current = list;
3195 let mut count = 0;
3196
3197 while count < n {
3198 let (car_val, cdr_val) = match arena.get(current) {
3199 ValueData::Nil => {
3200 return Err(format!("take: list has only {} elements, cannot take {}", count, n));
3201 }
3202 ValueData::Pair { car, cdr, .. } => (*car, *cdr),
3203 _ => return Err("take: first argument must be a list".to_string()),
3204 };
3205
3206 let new_pair = arena.cons(car_val, crate::scheme::arena::NIL_ID);
3207 if result == crate::scheme::arena::NIL_ID {
3208 result = new_pair;
3209 tail = new_pair;
3210 } else {
3211 match arena.get_mut(tail) {
3212 ValueData::Pair { cdr: tail_cdr, .. } => {
3213 *tail_cdr = new_pair;
3214 }
3215 _ => unreachable!(),
3216 }
3217 tail = new_pair;
3218 }
3219
3220 current = cdr_val;
3221 count += 1;
3222 }
3223
3224 Ok(result)
3225}
3226
3227pub fn drop(arena: &Arena, args: &[ValueId]) -> ArenaResult {
3229 if args.len() != 2 {
3230 return Err("drop: expected 2 arguments".to_string());
3231 }
3232
3233 let mut list = args[0];
3234 let n = match arena.get(args[1]) {
3235 ValueData::Integer(n) => *n,
3236 _ => return Err("drop: second argument must be an integer".to_string()),
3237 };
3238
3239 if n < 0 {
3240 return Err("drop: count must be non-negative".to_string());
3241 }
3242
3243 for i in 0..n {
3244 match arena.get(list) {
3245 ValueData::Nil => {
3246 return Err(format!("drop: list has only {} elements, cannot drop {}", i, n));
3247 }
3248 ValueData::Pair { cdr, .. } => {
3249 list = *cdr;
3250 }
3251 _ => return Err("drop: first argument must be a list".to_string()),
3252 }
3253 }
3254
3255 Ok(list)
3256}
3257
3258pub fn split_at(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
3261 if args.len() != 2 {
3262 return Err("split-at: expected 2 arguments".to_string());
3263 }
3264
3265 let taken = take(arena, args)?;
3266 let dropped = drop(arena, args)?;
3267
3268 Ok(arena.cons(taken, dropped))
3269}
3270
3271pub fn filter(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
3275 Err("filter: not available in arena mode (requires callback to evaluator)".to_string())
3276}
3277
3278pub fn remove(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
3282 Err("remove: not available in arena mode (requires callback to evaluator)".to_string())
3283}
3284
3285pub fn numerator(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
3291 if args.len() != 1 {
3292 return Err("numerator: expected 1 argument".to_string());
3293 }
3294
3295 match arena.get(args[0]) {
3296 ValueData::Integer(n) => Ok(arena.int(*n)),
3297 ValueData::Real(f) => {
3298 Ok(arena.int(*f as i64))
3300 }
3301 _ => Err("numerator: argument must be a number".to_string()),
3302 }
3303}
3304
3305pub fn denominator(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
3307 if args.len() != 1 {
3308 return Err("denominator: expected 1 argument".to_string());
3309 }
3310
3311 match arena.get(args[0]) {
3312 ValueData::Integer(_) | ValueData::Real(_) => Ok(arena.int(1)),
3313 _ => Err("denominator: argument must be a number".to_string()),
3314 }
3315}
3316
3317pub fn rationalize(arena: &Arena, args: &[ValueId]) -> ArenaResult {
3319 if args.len() != 2 {
3320 return Err("rationalize: expected 2 arguments".to_string());
3321 }
3322
3323 match arena.get(args[0]) {
3325 ValueData::Integer(_) | ValueData::Real(_) => Ok(args[0]),
3326 _ => Err("rationalize: first argument must be a number".to_string()),
3327 }
3328}
3329
3330pub fn angle(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
3332 if args.len() != 1 {
3333 return Err("angle: expected 1 argument".to_string());
3334 }
3335
3336 match arena.get(args[0]) {
3337 ValueData::Integer(n) => {
3338 if *n < 0 {
3339 Ok(arena.real(std::f64::consts::PI))
3340 } else {
3341 Ok(arena.real(0.0))
3342 }
3343 }
3344 ValueData::Real(f) => {
3345 if *f < 0.0 {
3346 Ok(arena.real(std::f64::consts::PI))
3347 } else {
3348 Ok(arena.real(0.0))
3349 }
3350 }
3351 _ => Err("angle: argument must be a number".to_string()),
3352 }
3353}
3354
3355pub fn magnitude(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
3357 if args.len() != 1 {
3358 return Err("magnitude: expected 1 argument".to_string());
3359 }
3360
3361 match arena.get(args[0]) {
3362 ValueData::Integer(n) => Ok(arena.int(n.abs())),
3363 ValueData::Real(f) => Ok(arena.real(f.abs())),
3364 _ => Err("magnitude: argument must be a number".to_string()),
3365 }
3366}
3367
3368pub fn string_to_number_radix(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
3371 if args.is_empty() || args.len() > 2 {
3372 return Err("string->number: expected 1 or 2 arguments".to_string());
3373 }
3374
3375 let s = match arena.get(args[0]) {
3376 ValueData::String(s) => s.clone(),
3377 _ => return Err("string->number: first argument must be a string".to_string()),
3378 };
3379
3380 let radix = if args.len() == 2 {
3381 match arena.get(args[1]) {
3382 ValueData::Integer(r) => *r as u32,
3383 _ => return Err("string->number: radix must be an integer".to_string()),
3384 }
3385 } else {
3386 10
3387 };
3388
3389 if radix < 2 || radix > 36 {
3390 return Err("string->number: radix must be between 2 and 36".to_string());
3391 }
3392
3393 if let Ok(n) = i64::from_str_radix(&s, radix) {
3395 return Ok(arena.int(n));
3396 }
3397
3398 if radix == 10 {
3400 if let Ok(f) = s.parse::<f64>() {
3401 return Ok(arena.real(f));
3402 }
3403 }
3404
3405 Ok(crate::scheme::arena::FALSE_ID)
3407}
3408
3409pub fn number_to_string_radix(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
3415 if args.is_empty() || args.len() > 2 {
3416 return Err("number->string: expected 1 or 2 arguments".to_string());
3417 }
3418
3419 let radix = if args.len() == 2 {
3420 match arena.get(args[1]) {
3421 ValueData::Integer(r) => *r as u32,
3422 _ => return Err("number->string: radix must be an integer".to_string()),
3423 }
3424 } else {
3425 10
3426 };
3427
3428 if radix < 2 || radix > 36 {
3429 return Err("number->string: radix must be between 2 and 36".to_string());
3430 }
3431
3432 match arena.get(args[0]) {
3433 ValueData::Integer(n) => {
3434 if radix == 10 {
3435 Ok(arena.string(n.to_string()))
3436 } else {
3437 let s = if *n < 0 {
3439 format!("-{}", format_radix((-n) as u64, radix))
3440 } else {
3441 format_radix(*n as u64, radix)
3442 };
3443 Ok(arena.string(s))
3444 }
3445 }
3446 ValueData::Real(f) => {
3447 if radix != 10 {
3448 return Err("number->string: non-decimal radix not supported for real numbers".to_string());
3449 }
3450 Ok(arena.string(f.to_string()))
3451 }
3452 _ => Err("number->string: first argument must be a number".to_string()),
3453 }
3454}
3455
3456fn format_radix(mut n: u64, radix: u32) -> String {
3458 if n == 0 {
3459 return "0".to_string();
3460 }
3461
3462 let digits = "0123456789abcdefghijklmnopqrstuvwxyz";
3463 let mut result = String::new();
3464
3465 while n > 0 {
3466 let digit = (n % radix as u64) as usize;
3467 result.insert(0, digits.chars().nth(digit).unwrap());
3468 n /= radix as u64;
3469 }
3470
3471 result
3472}
3473
3474pub fn list_p_improved(arena: &Arena, args: &[ValueId]) -> ArenaResult {
3476 if args.len() != 1 {
3477 return Err("list?: expected 1 argument".to_string());
3478 }
3479
3480 let mut current = args[0];
3481 let mut visited = std::collections::HashSet::new();
3482
3483 loop {
3484 if !visited.insert(current) {
3486 return Ok(crate::scheme::arena::FALSE_ID);
3488 }
3489
3490 match arena.get(current) {
3491 ValueData::Nil => return Ok(crate::scheme::arena::TRUE_ID),
3492 ValueData::Pair { cdr, .. } => {
3493 current = *cdr;
3494 }
3495 _ => return Ok(crate::scheme::arena::FALSE_ID),
3496 }
3497 }
3498}
3499
3500pub fn null_list_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
3502 if args.len() != 1 {
3503 return Err("null-list?: expected 1 argument".to_string());
3504 }
3505
3506 match arena.get(args[0]) {
3507 ValueData::Nil => Ok(crate::scheme::arena::TRUE_ID),
3508 _ => Ok(crate::scheme::arena::FALSE_ID),
3509 }
3510}
3511
3512pub fn improper_list_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
3514 if args.len() != 1 {
3515 return Err("improper-list?: expected 1 argument".to_string());
3516 }
3517
3518 let mut current = args[0];
3519
3520 loop {
3521 match arena.get(current) {
3522 ValueData::Nil => return Ok(crate::scheme::arena::FALSE_ID), ValueData::Pair { cdr, .. } => {
3524 current = *cdr;
3525 }
3526 _ => return Ok(crate::scheme::arena::TRUE_ID), }
3528 }
3529}
3530
3531pub fn circular_list_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
3533 if args.len() != 1 {
3534 return Err("circular-list?: expected 1 argument".to_string());
3535 }
3536
3537 let mut slow = args[0];
3539 let mut fast = args[0];
3540
3541 loop {
3542 match arena.get(slow) {
3544 ValueData::Pair { cdr, .. } => slow = *cdr,
3545 _ => return Ok(crate::scheme::arena::FALSE_ID), }
3547
3548 match arena.get(fast) {
3550 ValueData::Pair { cdr: cdr1, .. } => {
3551 fast = *cdr1;
3552 match arena.get(fast) {
3553 ValueData::Pair { cdr: cdr2, .. } => fast = *cdr2,
3554 _ => return Ok(crate::scheme::arena::FALSE_ID), }
3556 }
3557 _ => return Ok(crate::scheme::arena::FALSE_ID), }
3559
3560 if slow == fast {
3562 return Ok(crate::scheme::arena::TRUE_ID);
3563 }
3564 }
3565}
3566
3567pub fn bitwise_and(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
3573 if args.is_empty() {
3574 return Ok(arena.int(-1)); }
3576
3577 let mut result = -1i64;
3578 for &arg in args {
3579 match arena.get(arg) {
3580 ValueData::Integer(n) => {
3581 result &= n;
3582 }
3583 _ => return Err("bitwise-and: all arguments must be integers".to_string()),
3584 }
3585 }
3586
3587 Ok(arena.int(result))
3588}
3589
3590pub fn bitwise_ior(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
3592 if args.is_empty() {
3593 return Ok(arena.int(0)); }
3595
3596 let mut result = 0i64;
3597 for &arg in args {
3598 match arena.get(arg) {
3599 ValueData::Integer(n) => {
3600 result |= n;
3601 }
3602 _ => return Err("bitwise-ior: all arguments must be integers".to_string()),
3603 }
3604 }
3605
3606 Ok(arena.int(result))
3607}
3608
3609pub fn bitwise_xor(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
3611 if args.is_empty() {
3612 return Ok(arena.int(0)); }
3614
3615 let mut result = 0i64;
3616 for &arg in args {
3617 match arena.get(arg) {
3618 ValueData::Integer(n) => {
3619 result ^= n;
3620 }
3621 _ => return Err("bitwise-xor: all arguments must be integers".to_string()),
3622 }
3623 }
3624
3625 Ok(arena.int(result))
3626}
3627
3628pub fn bitwise_not(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
3630 if args.len() != 1 {
3631 return Err("bitwise-not: expected 1 argument".to_string());
3632 }
3633
3634 match arena.get(args[0]) {
3635 ValueData::Integer(n) => Ok(arena.int(!n)),
3636 _ => Err("bitwise-not: argument must be an integer".to_string()),
3637 }
3638}
3639
3640pub fn arithmetic_shift(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
3642 if args.len() != 2 {
3643 return Err("arithmetic-shift: expected 2 arguments".to_string());
3644 }
3645
3646 let n = match arena.get(args[0]) {
3647 ValueData::Integer(n) => *n,
3648 _ => return Err("arithmetic-shift: first argument must be an integer".to_string()),
3649 };
3650
3651 let shift = match arena.get(args[1]) {
3652 ValueData::Integer(s) => *s,
3653 _ => return Err("arithmetic-shift: second argument must be an integer".to_string()),
3654 };
3655
3656 let result = if shift >= 0 {
3657 if shift >= 64 {
3659 0 } else {
3661 n.wrapping_shl(shift as u32)
3662 }
3663 } else {
3664 if shift <= -64 {
3666 if n < 0 { -1 } else { 0 }
3667 } else {
3668 n >> (-shift)
3669 }
3670 };
3671
3672 Ok(arena.int(result))
3673}
3674
3675pub fn bit_extract(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
3677 if args.len() != 3 {
3678 return Err("bit-extract: expected 3 arguments".to_string());
3679 }
3680
3681 let n = match arena.get(args[0]) {
3682 ValueData::Integer(n) => *n as u64,
3683 _ => return Err("bit-extract: first argument must be an integer".to_string()),
3684 };
3685
3686 let start = match arena.get(args[1]) {
3687 ValueData::Integer(s) => *s as u32,
3688 _ => return Err("bit-extract: second argument must be an integer".to_string()),
3689 };
3690
3691 let end = match arena.get(args[2]) {
3692 ValueData::Integer(e) => *e as u32,
3693 _ => return Err("bit-extract: third argument must be an integer".to_string()),
3694 };
3695
3696 if start > end {
3697 return Err("bit-extract: start must be <= end".to_string());
3698 }
3699
3700 if end > 64 {
3701 return Err("bit-extract: end must be <= 64".to_string());
3702 }
3703
3704 let count = end - start;
3705 let mask = if count >= 64 {
3706 u64::MAX
3707 } else {
3708 (1u64 << count) - 1
3709 };
3710
3711 let result = (n >> start) & mask;
3712 Ok(arena.int(result as i64))
3713}
3714
3715pub fn bitwise_bit_set_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
3717 if args.len() != 2 {
3718 return Err("bitwise-bit-set?: expected 2 arguments".to_string());
3719 }
3720
3721 let n = match arena.get(args[0]) {
3722 ValueData::Integer(n) => *n,
3723 _ => return Err("bitwise-bit-set?: first argument must be an integer".to_string()),
3724 };
3725
3726 let pos = match arena.get(args[1]) {
3727 ValueData::Integer(p) => *p,
3728 _ => return Err("bitwise-bit-set?: second argument must be an integer".to_string()),
3729 };
3730
3731 if pos < 0 || pos >= 64 {
3732 return Err("bitwise-bit-set?: position must be between 0 and 63".to_string());
3733 }
3734
3735 let is_set = (n & (1i64 << pos)) != 0;
3736 Ok(if is_set { crate::scheme::arena::TRUE_ID } else { crate::scheme::arena::FALSE_ID })
3737}
3738
3739pub fn bitwise_bit_count(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
3741 if args.len() != 1 {
3742 return Err("bitwise-bit-count: expected 1 argument".to_string());
3743 }
3744
3745 match arena.get(args[0]) {
3746 ValueData::Integer(n) => {
3747 let count = if *n < 0 {
3748 (*n as u64).count_ones()
3750 } else {
3751 (*n as u64).count_ones()
3752 };
3753 Ok(arena.int(count as i64))
3754 }
3755 _ => Err("bitwise-bit-count: argument must be an integer".to_string()),
3756 }
3757}
3758
3759pub fn display(arena: &Arena, args: &[ValueId]) -> ArenaResult {
3765 if args.len() != 1 {
3766 return Err("display: expected 1 argument".to_string());
3767 }
3768
3769 let _s = value_to_display_string(arena, args[0]);
3773 Ok(crate::scheme::arena::UNSPECIFIED_ID)
3774}
3775
3776fn value_to_display_string(arena: &Arena, id: ValueId) -> String {
3778 match arena.get(id) {
3779 ValueData::Nil => "()".to_string(),
3780 ValueData::Bool(true) => "#t".to_string(),
3781 ValueData::Bool(false) => "#f".to_string(),
3782 ValueData::Integer(n) => n.to_string(),
3783 ValueData::Real(f) => f.to_string(),
3784 ValueData::Char(c) => c.to_string(),
3785 ValueData::String(s) => s.clone(),
3786 ValueData::Symbol(s) => s.to_string(),
3787 ValueData::Keyword(k) => format!("{}:", k),
3788 ValueData::Pair { .. } => {
3789 let mut result = String::from("(");
3791 let mut current = id;
3792 let mut first = true;
3793
3794 loop {
3795 match arena.get(current) {
3796 ValueData::Pair { car, cdr, .. } => {
3797 if !first {
3798 result.push(' ');
3799 }
3800 first = false;
3801 result.push_str(&value_to_display_string(arena, *car));
3802
3803 current = *cdr;
3804 if matches!(arena.get(current), ValueData::Nil) {
3805 break;
3806 } else if !matches!(arena.get(current), ValueData::Pair { .. }) {
3807 result.push_str(" . ");
3808 result.push_str(&value_to_display_string(arena, current));
3809 break;
3810 }
3811 }
3812 _ => break,
3813 }
3814 }
3815 result.push(')');
3816 result
3817 }
3818 ValueData::Vector(elements) => {
3819 let mut result = String::from("#(");
3820 for (i, elem) in elements.iter().enumerate() {
3821 if i > 0 {
3822 result.push(' ');
3823 }
3824 result.push_str(&value_to_display_string(arena, *elem));
3825 }
3826 result.push(')');
3827 result
3828 }
3829 ValueData::Procedure(_) => "#<procedure>".to_string(),
3830 ValueData::Unspecified => "#<unspecified>".to_string(),
3831 ValueData::Error => "#<error>".to_string(),
3832 _ => "#<unknown>".to_string(),
3833 }
3834}
3835
3836pub fn newline(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
3838 if !args.is_empty() {
3839 return Err("newline: expected 0 arguments".to_string());
3840 }
3841
3842 Ok(crate::scheme::arena::UNSPECIFIED_ID)
3845}
3846
3847pub fn write(arena: &Arena, args: &[ValueId]) -> ArenaResult {
3849 if args.len() != 1 {
3850 return Err("write: expected 1 argument".to_string());
3851 }
3852
3853 let _s = value_to_write_string(arena, args[0]);
3855 Ok(crate::scheme::arena::UNSPECIFIED_ID)
3856}
3857
3858fn value_to_write_string(arena: &Arena, id: ValueId) -> String {
3859 match arena.get(id) {
3860 ValueData::String(s) => format!("\"{}\"", s.escape_default()),
3861 ValueData::Char(c) => format!("#\\{}", c),
3862 _ => value_to_display_string(arena, id),
3863 }
3864}
3865
3866pub fn write_char(arena: &Arena, args: &[ValueId]) -> ArenaResult {
3868 if args.len() != 1 {
3869 return Err("write-char: expected 1 argument".to_string());
3870 }
3871
3872 match arena.get(args[0]) {
3873 ValueData::Char(_) => {
3874 Ok(crate::scheme::arena::UNSPECIFIED_ID)
3876 }
3877 _ => Err("write-char: argument must be a character".to_string()),
3878 }
3879}
3880
3881pub fn read_char(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
3883 if !args.is_empty() {
3884 return Err("read-char: expected 0 arguments".to_string());
3885 }
3886
3887 Err("read-char: not available in arena mode (requires I/O)".to_string())
3889}
3890
3891pub fn eof_object_p(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
3893 if args.len() != 1 {
3894 return Err("eof-object?: expected 1 argument".to_string());
3895 }
3896
3897 Ok(crate::scheme::arena::FALSE_ID)
3900}
3901
3902pub fn format_number(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
3908 if args.len() != 2 {
3909 return Err("format-number: expected 2 arguments".to_string());
3910 }
3911
3912 let n = match arena.get(args[0]) {
3913 ValueData::Integer(i) => *i,
3914 ValueData::Real(r) => *r as i64,
3915 _ => return Err("format-number: first argument must be a number".to_string()),
3916 };
3917
3918 let format = match arena.get(args[1]) {
3919 ValueData::String(s) => s.as_str(),
3920 ValueData::Symbol(s) => s.as_ref(),
3921 _ => return Err("format-number: second argument must be a string or symbol".to_string()),
3922 };
3923
3924 let result = match format {
3926 "1" | "decimal" => n.to_string(),
3927 "I" | "roman-upper" => {
3928 let roman_str = match n {
3930 1 => "I",
3931 2 => "II",
3932 3 => "III",
3933 4 => "IV",
3934 5 => "V",
3935 6 => "VI",
3936 7 => "VII",
3937 8 => "VIII",
3938 9 => "IX",
3939 10 => "X",
3940 11 => "XI",
3941 12 => "XII",
3942 13 => "XIII",
3943 14 => "XIV",
3944 15 => "XV",
3945 16 => "XVI",
3946 17 => "XVII",
3947 18 => "XVIII",
3948 19 => "XIX",
3949 20 => "XX",
3950 _ => return Ok(arena.string(n.to_string())),
3951 };
3952 roman_str.to_string()
3953 }
3954 "i" | "roman-lower" => {
3955 let roman_str = match n {
3956 1 => "i",
3957 2 => "ii",
3958 3 => "iii",
3959 4 => "iv",
3960 5 => "v",
3961 6 => "vi",
3962 7 => "vii",
3963 8 => "viii",
3964 9 => "ix",
3965 10 => "x",
3966 11 => "xi",
3967 12 => "xii",
3968 13 => "xiii",
3969 14 => "xiv",
3970 15 => "xv",
3971 16 => "xvi",
3972 17 => "xvii",
3973 18 => "xviii",
3974 19 => "xix",
3975 20 => "xx",
3976 _ => return Ok(arena.string(n.to_string())),
3977 };
3978 roman_str.to_string()
3979 }
3980 "a" | "alpha-lower" => {
3981 if n >= 1 && n <= 26 {
3983 ((b'a' + (n as u8 - 1)) as char).to_string()
3984 } else {
3985 n.to_string()
3986 }
3987 }
3988 "A" | "alpha-upper" => {
3989 if n >= 1 && n <= 26 {
3991 ((b'A' + (n as u8 - 1)) as char).to_string()
3992 } else {
3993 n.to_string()
3994 }
3995 }
3996 _ => n.to_string(), };
3998
3999 Ok(arena.string(result))
4000}
4001
4002pub fn format_number_list(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
4004 if args.is_empty() || args.len() > 2 {
4005 return Err("format-number-list: expected 1 or 2 arguments".to_string());
4006 }
4007
4008 let separator = if args.len() == 2 {
4009 match arena.get(args[1]) {
4010 ValueData::String(s) => s.as_str(),
4011 ValueData::Symbol(s) => s.as_ref(),
4012 _ => ".",
4013 }
4014 } else {
4015 "."
4016 };
4017
4018 let mut numbers = Vec::new();
4019 let mut current = args[0];
4020
4021 loop {
4022 match arena.get(current) {
4023 ValueData::Nil => break,
4024 ValueData::Pair { car, cdr, .. } => {
4025 match arena.get(*car) {
4026 ValueData::Integer(n) => numbers.push(n.to_string()),
4027 ValueData::Real(r) => numbers.push((*r as i64).to_string()),
4028 _ => {
4029 return Err(
4030 "format-number-list: list must contain only numbers".to_string()
4031 )
4032 }
4033 }
4034 current = *cdr;
4035 }
4036 _ => return Err("format-number-list: not a proper list".to_string()),
4037 }
4038 }
4039
4040 Ok(arena.string(numbers.join(separator)))
4041}
4042
4043pub fn empty_sosofo(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
4049 if !args.is_empty() {
4050 return Err("empty-sosofo: expected no arguments".to_string());
4051 }
4052
4053 Ok(arena.alloc(ValueData::Sosofo))
4054}
4055
4056pub fn sosofo_append(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
4060 if args.is_empty() {
4062 return Ok(arena.alloc(ValueData::Sosofo));
4063 }
4064
4065 for &arg in args {
4067 match arena.get(arg) {
4068 ValueData::Sosofo | ValueData::Unspecified | ValueData::Nil => {},
4069 _ => return Err(format!("sosofo-append: not a sosofo")),
4070 }
4071 }
4072
4073 Ok(arena.alloc(ValueData::Sosofo))
4076}
4077
4078#[inline]
4082pub fn if_first_page(_arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
4083 if args.len() != 2 {
4084 return Err("if-first-page: expected 2 arguments (then-sosofo else-sosofo)".to_string());
4085 }
4086
4087 Ok(args[1])
4090}
4091
4092#[inline]
4096pub fn if_front_page(_arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
4097 if args.len() != 2 {
4098 return Err("if-front-page: expected 2 arguments (then-sosofo else-sosofo)".to_string());
4099 }
4100
4101 Ok(args[1])
4104}
4105
4106pub fn current_node(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
4112 if !args.is_empty() {
4113 return Err("current-node: expected no arguments".to_string());
4114 }
4115
4116 match &arena.current_node {
4118 Some(node) => Ok(arena.alloc(ValueData::Node(node.clone()))),
4119 None => Err("current-node: no current node set".to_string()),
4120 }
4121}
4122
4123pub fn gi(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
4129 let node = if args.is_empty() {
4130 match &arena.current_node {
4132 Some(n) => n.clone(),
4133 None => return Err("gi: no current node".to_string()),
4134 }
4135 } else if args.len() == 1 {
4136 match arena.get(args[0]) {
4138 ValueData::Bool(false) => {
4139 return Ok(crate::scheme::arena::FALSE_ID);
4141 }
4142 ValueData::Symbol(_s) => {
4143 return Ok(args[0]);
4145 }
4146 ValueData::Node(n) => n.clone(),
4147 ValueData::NodeList(nl) => {
4148 if nl.is_empty() {
4149 return Ok(crate::scheme::arena::FALSE_ID);
4151 }
4152 let first = nl.first();
4153 let rest = nl.rest();
4154 if rest.is_empty() {
4155 Rc::new(first.unwrap())
4157 } else {
4158 return Err("gi: node-list must be a singleton".to_string());
4160 }
4161 }
4162 other => {
4163 let type_name = match other {
4164 ValueData::Nil => "nil",
4165 ValueData::Bool(_) => "boolean",
4166 ValueData::Integer(_) => "integer",
4167 ValueData::Real(_) => "real",
4168 ValueData::Quantity { .. } => "quantity",
4169 ValueData::Char(_) => "char",
4170 ValueData::String(_) => "string",
4171 ValueData::Keyword(_) => "keyword",
4172 ValueData::Pair { .. } => "pair",
4173 ValueData::Vector(_) => "vector",
4174 ValueData::Procedure(_) => "procedure",
4175 ValueData::Sosofo => "sosofo",
4176 ValueData::Unspecified => "unspecified",
4177 _ => "unknown",
4178 };
4179 return Err(format!("gi: argument must be a node, node-list, #f, or symbol (got {})", type_name));
4180 }
4181 }
4182 } else {
4183 return Err("gi: expected 0 or 1 arguments".to_string());
4184 };
4185
4186 match node.gi() {
4188 Some(name) => Ok(arena.symbol(name.into())),
4189 None => Ok(crate::scheme::arena::FALSE_ID),
4190 }
4191}
4192
4193pub fn data(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
4195 if args.len() > 1 {
4197 return Err("data: expected 0 or 1 arguments".to_string());
4198 }
4199
4200 let node_list = if args.is_empty() {
4201 match &arena.current_node {
4203 Some(n) => {
4204 let singleton = crate::grove::VecNodeList::new(vec![n.clone_node()]);
4205 Box::new(singleton) as Box<dyn crate::grove::NodeList>
4206 }
4207 None => return Err("data: no current node".to_string()),
4208 }
4209 } else {
4210 match arena.get(args[0]) {
4212 ValueData::Node(n) => {
4213 let singleton = crate::grove::VecNodeList::new(vec![n.clone_node()]);
4215 Box::new(singleton) as Box<dyn crate::grove::NodeList>
4216 }
4217 ValueData::NodeList(nl) => {
4218 let vec_nodes: Vec<Box<dyn crate::grove::Node>> = {
4220 let mut result = Vec::new();
4221 let mut current = nl.clone();
4222 loop {
4223 if current.is_empty() {
4224 break;
4225 }
4226 result.push(current.first().unwrap());
4227 current = Rc::new(current.rest());
4228 }
4229 result
4230 };
4231 Box::new(crate::grove::VecNodeList::new(vec_nodes)) as Box<dyn crate::grove::NodeList>
4232 }
4233 _ => return Err("data: argument must be a node or node-list".to_string()),
4234 }
4235 };
4236
4237 let mut result = String::new();
4240 let mut current_list: Box<dyn crate::grove::NodeList> = node_list;
4241 loop {
4242 if current_list.is_empty() {
4243 break;
4244 }
4245 let node = current_list.first().unwrap();
4246 if let Some(text) = node.data() {
4247 result.push_str(&text);
4248 }
4249 current_list = current_list.rest();
4250 }
4251
4252 Ok(arena.string(result))
4254}
4255
4256pub fn id(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
4258 let node = if args.is_empty() {
4259 match &arena.current_node {
4261 Some(n) => n.clone(),
4262 None => return Err("id: no current node".to_string()),
4263 }
4264 } else if args.len() == 1 {
4265 match arena.get(args[0]) {
4267 ValueData::Node(n) => n.clone(),
4268 ValueData::NodeList(nl) => {
4269 if nl.is_empty() {
4270 return Ok(crate::scheme::arena::FALSE_ID);
4272 }
4273 let first = nl.first();
4274 let rest = nl.rest();
4275 if rest.is_empty() {
4276 Rc::new(first.unwrap())
4278 } else {
4279 return Err("id: node-list must be a singleton".to_string());
4281 }
4282 }
4283 _ => return Ok(crate::scheme::arena::FALSE_ID),
4285 }
4286 } else {
4287 return Err("id: expected 0 or 1 arguments".to_string());
4288 };
4289
4290 match node.id() {
4292 Some(id_str) => Ok(arena.string(id_str)),
4293 None => Ok(crate::scheme::arena::FALSE_ID),
4294 }
4295}
4296
4297pub fn children(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
4303 let node = if args.is_empty() {
4304 match &arena.current_node {
4306 Some(n) => n.clone(),
4307 None => return Err("children: no current node".to_string()),
4308 }
4309 } else if args.len() == 1 {
4310 match arena.get(args[0]) {
4313 ValueData::Node(n) => n.clone(),
4314 ValueData::NodeList(nl) => {
4315 if nl.is_empty() {
4317 return Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(crate::grove::VecNodeList::new(vec![]))))));
4319 }
4320 let first = nl.first();
4321 let rest = nl.rest();
4322 if rest.is_empty() {
4323 Rc::new(first.unwrap())
4325 } else {
4326 let mut all_children = Vec::new();
4328 let mut current = nl.as_ref();
4329 while !current.is_empty() {
4330 if let Some(node) = current.first() {
4331 let node_children = node.children();
4332 let mut child_current = Box::new(node_children);
4333 while !child_current.is_empty() {
4334 if let Some(child) = child_current.first() {
4335 all_children.push(child);
4336 }
4337 child_current = Box::new(child_current.rest());
4338 }
4339 }
4340 current = Box::leak(Box::new(current.rest()));
4341 }
4342 let result = crate::grove::VecNodeList::new(all_children);
4343 return Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(result)))));
4344 }
4345 }
4346 _ => return Err("children: argument must be a node or node-list".to_string()),
4347 }
4348 } else {
4349 return Err("children: expected 0 or 1 arguments".to_string());
4350 };
4351
4352 let children = node.children();
4354 Ok(arena.alloc(ValueData::NodeList(Rc::new(children))))
4355}
4356
4357pub fn parent(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
4359 let node = if args.is_empty() {
4360 match &arena.current_node {
4362 Some(n) => n.clone(),
4363 None => return Err("parent: no current node".to_string()),
4364 }
4365 } else if args.len() == 1 {
4366 match arena.get(args[0]) {
4368 ValueData::Node(n) => n.clone(),
4369 ValueData::NodeList(nl) => {
4370 if nl.is_empty() {
4371 return Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(crate::grove::EmptyNodeList)))));
4373 }
4374 let first = nl.first();
4375 let rest = nl.rest();
4376 if rest.is_empty() {
4377 Rc::new(first.unwrap())
4379 } else {
4380 return Err("parent: node-list must be a singleton".to_string());
4382 }
4383 }
4384 _ => return Err("parent: argument must be a node or node-list".to_string()),
4385 }
4386 } else {
4387 return Err("parent: expected 0 or 1 arguments".to_string());
4388 };
4389
4390 match node.parent() {
4392 Some(parent) => {
4393 use crate::grove::VecNodeList;
4394 let nodes = vec![parent.clone_node()];
4395 Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(VecNodeList::new(nodes))))))
4396 }
4397 None => Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(crate::grove::EmptyNodeList))))),
4398 }
4399}
4400
4401pub fn attributes(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
4403 let _node = if args.is_empty() {
4404 match &arena.current_node {
4406 Some(n) => n.clone(),
4407 None => return Err("attributes: no current node".to_string()),
4408 }
4409 } else if args.len() == 1 {
4410 match arena.get(args[0]) {
4412 ValueData::Node(n) => n.clone(),
4413 _ => return Err("attributes: argument must be a node".to_string()),
4414 }
4415 } else {
4416 return Err("attributes: expected 0 or 1 arguments".to_string());
4417 };
4418
4419 Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(crate::grove::EmptyNodeList::new())))))
4422}
4423
4424pub fn node_list_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
4430 if args.len() != 1 {
4431 return Err("node-list?: expected 1 argument".to_string());
4432 }
4433
4434 match arena.get(args[0]) {
4435 ValueData::NodeList(_) => Ok(crate::scheme::arena::TRUE_ID),
4436 _ => Ok(crate::scheme::arena::FALSE_ID),
4437 }
4438}
4439
4440pub fn empty_node_list(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
4442 if !args.is_empty() {
4443 return Err("empty-node-list: expected no arguments".to_string());
4444 }
4445
4446 Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(crate::grove::EmptyNodeList::new())))))
4447}
4448
4449pub fn node_list_empty_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
4451 if args.len() != 1 {
4452 return Err("node-list-empty?: expected 1 argument".to_string());
4453 }
4454
4455 match arena.get(args[0]) {
4457 ValueData::NodeList(nl) => {
4458 if nl.is_empty() {
4459 Ok(crate::scheme::arena::TRUE_ID)
4460 } else {
4461 Ok(crate::scheme::arena::FALSE_ID)
4462 }
4463 }
4464 ValueData::Node(_) => {
4465 Ok(crate::scheme::arena::FALSE_ID)
4467 }
4468 _ => Err("node-list-empty?: argument must be a node or node-list".to_string()),
4469 }
4470}
4471
4472pub fn node_list_length(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
4474 if args.len() != 1 {
4475 return Err("node-list-length: expected 1 argument".to_string());
4476 }
4477
4478 match arena.get(args[0]) {
4479 ValueData::NodeList(nl) => Ok(arena.int(nl.length() as i64)),
4480 _ => Err("node-list-length: argument must be a node-list".to_string()),
4481 }
4482}
4483
4484pub fn node_list_first(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
4486 if args.len() != 1 {
4487 return Err("node-list-first: expected 1 argument".to_string());
4488 }
4489
4490 match arena.get(args[0]) {
4491 ValueData::NodeList(nl) => {
4492 match nl.first() {
4493 Some(node) => Ok(arena.alloc(ValueData::Node(Rc::new(node)))),
4494 None => Ok(crate::scheme::arena::FALSE_ID),
4497 }
4498 }
4499 _ => Err("node-list-first: argument must be a node-list".to_string()),
4500 }
4501}
4502
4503pub fn attribute_string(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
4510 if args.is_empty() || args.len() > 2 {
4511 return Err("attribute-string: expected 1 or 2 arguments".to_string());
4512 }
4513
4514 let attr_name = match arena.get(args[0]) {
4516 ValueData::String(s) => s.clone(),
4517 ValueData::Symbol(s) => s.to_string(),
4518 _ => return Err("attribute-string: first argument must be a string or symbol".to_string()),
4519 };
4520
4521 let node = if args.len() == 2 {
4523 match arena.get(args[1]) {
4524 ValueData::Node(n) => Some(n.clone()),
4525 ValueData::NodeList(nl) => {
4526 if nl.is_empty() {
4527 return Ok(crate::scheme::arena::FALSE_ID);
4529 }
4530 let first = nl.first();
4531 let rest = nl.rest();
4532 if rest.is_empty() {
4533 Some(Rc::new(first.unwrap()))
4535 } else {
4536 return Err("attribute-string: node-list must be a singleton".to_string());
4538 }
4539 }
4540 _ => return Ok(crate::scheme::arena::FALSE_ID),
4543 }
4544 } else {
4545 arena.current_node.clone()
4547 };
4548
4549 let node = match node {
4551 Some(n) => n,
4552 None => return Err("attribute-string: no current node".to_string()),
4553 };
4554
4555 match node.attribute_string(&attr_name) {
4557 Some(value) => Ok(arena.string(value)),
4558 None => Ok(crate::scheme::arena::FALSE_ID),
4559 }
4560}
4561
4562pub fn node_list_rest(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
4568 if args.len() != 1 {
4569 return Err("node-list-rest: expected 1 argument".to_string());
4570 }
4571
4572 match arena.get(args[0]) {
4573 ValueData::NodeList(nl) => {
4574 let rest = nl.rest();
4575 Ok(arena.alloc(ValueData::NodeList(Rc::new(rest))))
4576 }
4577 _ => Err("node-list-rest: argument must be a node-list".to_string()),
4578 }
4579}
4580
4581pub fn node_list_ref(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
4583 if args.len() != 2 {
4584 return Err("node-list-ref: expected 2 arguments".to_string());
4585 }
4586
4587 let nl = match arena.get(args[0]) {
4588 ValueData::NodeList(nl) => nl.clone(),
4589 _ => return Err("node-list-ref: first argument must be a node-list".to_string()),
4590 };
4591
4592 let index = match arena.get(args[1]) {
4593 ValueData::Integer(n) => {
4594 if *n < 0 {
4595 return Err("node-list-ref: index must be non-negative".to_string());
4596 }
4597 *n as usize
4598 }
4599 _ => return Err("node-list-ref: second argument must be an integer".to_string()),
4600 };
4601
4602 let mut current = nl.as_ref();
4604 let mut i = 0;
4605 while i < index {
4606 if current.is_empty() {
4607 return Ok(crate::scheme::arena::FALSE_ID);
4609 }
4610 let rest = current.rest();
4611 current = Box::leak(Box::new(rest)); i += 1;
4613 }
4614
4615 match current.first() {
4616 Some(node) => Ok(arena.alloc(ValueData::Node(Rc::new(node)))),
4617 None => Ok(crate::scheme::arena::FALSE_ID),
4619 }
4620}
4621
4622pub fn node_list_reverse(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
4624 if args.len() != 1 {
4625 return Err("node-list-reverse: expected 1 argument".to_string());
4626 }
4627
4628 match arena.get(args[0]) {
4629 ValueData::NodeList(nl) => {
4630 let mut nodes = Vec::new();
4632 let mut current = nl.as_ref();
4633 while !current.is_empty() {
4634 if let Some(node) = current.first() {
4635 nodes.push(node);
4636 }
4637 current = Box::leak(Box::new(current.rest()));
4638 }
4639
4640 nodes.reverse();
4642
4643 let reversed = crate::grove::VecNodeList::new(nodes);
4645 Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(reversed)))))
4646 }
4647 _ => Err("node-list-reverse: argument must be a node-list".to_string()),
4648 }
4649}
4650
4651pub fn node_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
4657 if args.len() != 1 {
4658 return Err("node?: expected 1 argument".to_string());
4659 }
4660
4661 match arena.get(args[0]) {
4662 ValueData::Node(_) => Ok(crate::scheme::arena::TRUE_ID),
4663 _ => Ok(crate::scheme::arena::FALSE_ID),
4664 }
4665}
4666
4667pub fn sosofo_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
4669 if args.len() != 1 {
4670 return Err("sosofo?: expected 1 argument".to_string());
4671 }
4672
4673 match arena.get(args[0]) {
4674 ValueData::Sosofo => Ok(crate::scheme::arena::TRUE_ID),
4675 _ => Ok(crate::scheme::arena::FALSE_ID),
4676 }
4677}
4678
4679pub fn quantity_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
4681 if args.len() != 1 {
4682 return Err("quantity?: expected 1 argument".to_string());
4683 }
4684
4685 match arena.get(args[0]) {
4686 ValueData::Quantity { .. } => Ok(crate::scheme::arena::TRUE_ID),
4687 _ => Ok(crate::scheme::arena::FALSE_ID),
4688 }
4689}
4690
4691pub fn color_p(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
4697 if args.len() != 1 {
4698 return Err("color?: expected 1 argument".to_string());
4699 }
4700 Ok(crate::scheme::arena::FALSE_ID)
4702}
4703
4704pub fn color(_arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
4706 Ok(crate::scheme::arena::FALSE_ID)
4709}
4710
4711pub fn display_space_p(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
4713 if args.len() != 1 {
4714 return Err("display-space?: expected 1 argument".to_string());
4715 }
4716 Ok(crate::scheme::arena::FALSE_ID)
4718}
4719
4720pub fn inline_space_p(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
4722 if args.len() != 1 {
4723 return Err("inline-space?: expected 1 argument".to_string());
4724 }
4725 Ok(crate::scheme::arena::FALSE_ID)
4727}
4728
4729pub fn quantity_to_number(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
4735 if args.len() != 1 {
4736 return Err("quantity->number: expected 1 argument".to_string());
4737 }
4738
4739 match arena.get(args[0]) {
4740 ValueData::Quantity { magnitude, .. } => Ok(arena.real(*magnitude)),
4741 _ => Err("quantity->number: argument must be a quantity".to_string()),
4742 }
4743}
4744
4745pub fn number_to_quantity(_arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
4747 if args.len() < 2 || args.len() > 3 {
4748 return Err("number->quantity: expected 2 or 3 arguments".to_string());
4749 }
4750
4751 Ok(args[0])
4754}
4755
4756pub fn quantity_convert(_arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
4758 if args.len() != 2 {
4759 return Err("quantity-convert: expected 2 arguments".to_string());
4760 }
4761
4762 Ok(args[0])
4764}
4765
4766pub fn device_length(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
4768 if args.len() != 1 {
4769 return Err("device-length: expected 1 argument".to_string());
4770 }
4771
4772 Ok(arena.int(0))
4774}
4775
4776pub fn label_distance(arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
4778 Ok(arena.int(0))
4780}
4781
4782pub fn ancestor(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
4788 if args.is_empty() || args.len() > 2 {
4789 return Err("ancestor: expected 1 or 2 arguments".to_string());
4790 }
4791
4792 let ancestor_name = match arena.get(args[0]) {
4794 ValueData::String(s) => s.clone(),
4795 ValueData::Symbol(s) => s.to_string(),
4796 _ => return Err("ancestor: first argument must be a string or symbol".to_string()),
4797 };
4798
4799 let node = if args.len() == 2 {
4802 match arena.get(args[1]) {
4803 ValueData::Node(n) => n.clone(),
4804 ValueData::NodeList(nl) => {
4805 if nl.is_empty() {
4806 return Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(crate::grove::EmptyNodeList)))));
4808 }
4809 let first = nl.first();
4810 let rest = nl.rest();
4811 if rest.is_empty() {
4812 Rc::new(first.unwrap())
4814 } else {
4815 return Err("ancestor: node-list must be a singleton".to_string());
4817 }
4818 }
4819 _ => return Err("ancestor: second argument must be a node or singleton node-list".to_string()),
4820 }
4821 } else {
4822 match &arena.current_node {
4823 Some(n) => n.clone(),
4824 None => return Err("ancestor: no current node".to_string()),
4825 }
4826 };
4827
4828 let mut current = node.parent();
4830 while let Some(parent_node) = current {
4831 if let Some(gi) = parent_node.gi() {
4832 if gi == ancestor_name {
4833 use crate::grove::VecNodeList;
4835 let nodes = vec![parent_node.clone_node()];
4836 return Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(VecNodeList::new(nodes))))));
4837 }
4838 }
4839 current = parent_node.parent();
4840 }
4841
4842 Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(crate::grove::EmptyNodeList)))))
4844}
4845
4846pub fn descendants(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
4848 if args.len() > 1 {
4849 return Err("descendants: expected 0 or 1 arguments".to_string());
4850 }
4851
4852 fn collect_descendants(node: &dyn crate::grove::Node, result: &mut Vec<Box<dyn crate::grove::Node>>) {
4854 let children = node.children();
4855 let mut current = children.as_ref();
4856 while !current.is_empty() {
4857 if let Some(child) = current.first() {
4858 collect_descendants(child.as_ref(), result);
4859 result.push(child);
4860 }
4861 current = Box::leak(current.rest());
4862 }
4863 }
4864
4865 if args.is_empty() {
4867 let node = match &arena.current_node {
4869 Some(n) => n.clone(),
4870 None => return Err("descendants: no current node".to_string()),
4871 };
4872 let mut descendants = Vec::new();
4873 collect_descendants(node.as_ref().as_ref(), &mut descendants);
4874 let node_list = crate::grove::VecNodeList::new(descendants);
4875 return Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(node_list)))));
4876 }
4877
4878 match arena.get(args[0]) {
4880 ValueData::NodeList(nl) => {
4881 let mut all_descendants = Vec::new();
4883 let mut current = nl.as_ref().as_ref();
4884 while !current.is_empty() {
4885 if let Some(node) = current.first() {
4886 collect_descendants(node.as_ref(), &mut all_descendants);
4887 }
4888 current = Box::leak(current.rest());
4889 }
4890 let result = crate::grove::VecNodeList::new(all_descendants);
4891 Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(result)))))
4892 }
4893 ValueData::Node(n) => {
4894 let mut descendants = Vec::new();
4896 collect_descendants(n.as_ref().as_ref(), &mut descendants);
4897 let node_list = crate::grove::VecNodeList::new(descendants);
4898 Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(node_list)))))
4899 }
4900 _ => Err("descendants: argument must be a node or node-list".to_string()),
4901 }
4902}
4903
4904pub fn follow(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
4906 if args.len() > 1 {
4907 return Err("follow: expected 0 or 1 arguments".to_string());
4908 }
4909
4910 let _node = if args.is_empty() {
4911 match &arena.current_node {
4912 Some(n) => n.clone(),
4913 None => return Err("follow: no current node".to_string()),
4914 }
4915 } else {
4916 match arena.get(args[0]) {
4917 ValueData::Node(n) => n.clone(),
4918 _ => return Err("follow: argument must be a node".to_string()),
4919 }
4920 };
4921
4922 Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(crate::grove::EmptyNodeList)))))
4925}
4926
4927pub fn preced(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
4929 if args.len() > 1 {
4930 return Err("preced: expected 0 or 1 arguments".to_string());
4931 }
4932
4933 let _node = if args.is_empty() {
4934 match &arena.current_node {
4935 Some(n) => n.clone(),
4936 None => return Err("preced: no current node".to_string()),
4937 }
4938 } else {
4939 match arena.get(args[0]) {
4940 ValueData::Node(n) => n.clone(),
4941 _ => return Err("preced: argument must be a node".to_string()),
4942 }
4943 };
4944
4945 Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(crate::grove::EmptyNodeList)))))
4948}
4949
4950pub fn ipreced(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
4952 if args.len() > 1 {
4953 return Err("ipreced: expected 0 or 1 arguments".to_string());
4954 }
4955
4956 let _node = if args.is_empty() {
4957 match &arena.current_node {
4958 Some(n) => n.clone(),
4959 None => return Err("ipreced: no current node".to_string()),
4960 }
4961 } else {
4962 match arena.get(args[0]) {
4963 ValueData::Node(n) => n.clone(),
4964 _ => return Err("ipreced: argument must be a node".to_string()),
4965 }
4966 };
4967
4968 Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(crate::grove::EmptyNodeList)))))
4971}
4972
4973pub fn node_list_last(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
4979 if args.len() != 1 {
4980 return Err("node-list-last: expected 1 argument".to_string());
4981 }
4982
4983 match arena.get(args[0]) {
4984 ValueData::NodeList(nl) => {
4985 let mut last_node = None;
4986 let mut current = nl.as_ref().as_ref();
4987 while !current.is_empty() {
4988 if let Some(node) = current.first() {
4989 last_node = Some(node);
4990 }
4991 current = Box::leak(current.rest());
4992 }
4993 match last_node {
4994 Some(node) => Ok(arena.alloc(ValueData::Node(Rc::new(node)))),
4995 None => Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(crate::grove::EmptyNodeList))))),
4996 }
4997 }
4998 _ => Err("node-list-last: argument must be a node-list".to_string()),
4999 }
5000}
5001
5002pub fn node_list_union(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
5004 if args.is_empty() {
5005 return Err("node-list-union: expected at least 1 argument".to_string());
5006 }
5007
5008 let mut nodes: Vec<Box<dyn crate::grove::Node>> = Vec::new();
5010
5011 for (i, arg_id) in args.iter().enumerate() {
5012 let nl = match arena.get(*arg_id) {
5013 ValueData::NodeList(nl) => nl.clone(),
5014 _ => return Err(format!("node-list-union: argument {} must be a node-list", i + 1)),
5015 };
5016
5017 let len = nl.length();
5019 for idx in 0..len {
5020 if let Some(node) = nl.get(idx) {
5021 let mut already_present = false;
5023 for existing_node in &nodes {
5024 if node.node_eq(existing_node.as_ref()) {
5025 already_present = true;
5026 break;
5027 }
5028 }
5029 if !already_present {
5030 nodes.push(node);
5031 }
5032 }
5033 }
5034 }
5035
5036 let union = crate::grove::VecNodeList::new(nodes);
5038 Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(union)))))
5039}
5040
5041pub fn node_list_intersection(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
5043 if args.len() != 2 {
5044 return Err("node-list-intersection: expected 2 arguments".to_string());
5045 }
5046
5047 let _nl1 = match arena.get(args[0]) {
5048 ValueData::NodeList(nl) => nl.clone(),
5049 _ => return Err("node-list-intersection: first argument must be a node-list".to_string()),
5050 };
5051
5052 let _nl2 = match arena.get(args[1]) {
5053 ValueData::NodeList(nl) => nl.clone(),
5054 _ => return Err("node-list-intersection: second argument must be a node-list".to_string()),
5055 };
5056
5057 Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(crate::grove::EmptyNodeList)))))
5060}
5061
5062pub fn node_list_difference(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
5064 if args.len() != 2 {
5065 return Err("node-list-difference: expected 2 arguments".to_string());
5066 }
5067
5068 let nl1 = match arena.get(args[0]) {
5069 ValueData::NodeList(nl) => nl.clone(),
5070 _ => return Err("node-list-difference: first argument must be a node-list".to_string()),
5071 };
5072
5073 let _nl2 = match arena.get(args[1]) {
5074 ValueData::NodeList(nl) => nl.clone(),
5075 _ => return Err("node-list-difference: second argument must be a node-list".to_string()),
5076 };
5077
5078 Ok(arena.alloc(ValueData::NodeList(nl1)))
5081}
5082
5083pub fn node_list_remove_duplicates(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
5085 if args.len() != 1 {
5086 return Err("node-list-remove-duplicates: expected 1 argument".to_string());
5087 }
5088
5089 let nl = match arena.get(args[0]) {
5090 ValueData::NodeList(nl) => nl.clone(),
5091 _ => return Err("node-list-remove-duplicates: argument must be a node-list".to_string()),
5092 };
5093
5094 Ok(arena.alloc(ValueData::NodeList(nl)))
5097}
5098
5099pub fn select_elements(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
5106 if args.len() != 2 {
5107 return Err("select-elements: expected 2 arguments".to_string());
5108 }
5109
5110 let nl = match arena.get(args[0]) {
5112 ValueData::NodeList(nl) => nl.clone(),
5113 _ => return Err("select-elements: first argument must be a node-list".to_string()),
5114 };
5115
5116 let element_name = match arena.get(args[1]) {
5118 ValueData::String(s) => s.clone(),
5119 ValueData::Symbol(s) => s.to_string(),
5120 _ => return Err("select-elements: second argument must be a string or symbol".to_string()),
5121 };
5122
5123 let mut selected = Vec::new();
5125 let mut current = nl.as_ref().as_ref();
5126 while !current.is_empty() {
5127 if let Some(node) = current.first() {
5128 if let Some(gi) = node.gi() {
5129 if gi == element_name {
5130 selected.push(node);
5131 }
5132 }
5133 }
5134 current = Box::leak(current.rest());
5135 }
5136
5137 let result = crate::grove::VecNodeList::new(selected);
5138 Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(result)))))
5139}
5140
5141pub fn first_sibling_p(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
5143 if args.len() > 1 {
5144 return Err("first-sibling?: expected 0 or 1 arguments".to_string());
5145 }
5146
5147 let node = if args.is_empty() {
5148 match &arena.current_node {
5149 Some(n) => n.clone(),
5150 None => return Err("first-sibling?: no current node".to_string()),
5151 }
5152 } else {
5153 match arena.get(args[0]) {
5154 ValueData::Node(n) => n.clone(),
5155 _ => return Err("first-sibling?: argument must be a node".to_string()),
5156 }
5157 };
5158
5159 let gi = node.gi();
5162
5163 match node.parent() {
5164 Some(parent) => {
5165 let children = parent.children();
5166 let len = children.length();
5167
5168 for idx in 0..len {
5170 if let Some(sibling) = children.get(idx) {
5171 if sibling.gi() == gi {
5173 if node.node_eq(sibling.as_ref()) {
5175 return Ok(crate::scheme::arena::TRUE_ID);
5177 } else {
5178 return Ok(crate::scheme::arena::FALSE_ID);
5180 }
5181 }
5182 }
5183 }
5184 Ok(crate::scheme::arena::FALSE_ID)
5186 }
5187 None => Ok(crate::scheme::arena::TRUE_ID), }
5189}
5190
5191pub fn last_sibling_p(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
5193 if args.len() > 1 {
5194 return Err("last-sibling?: expected 0 or 1 arguments".to_string());
5195 }
5196
5197 let node = if args.is_empty() {
5198 match &arena.current_node {
5199 Some(n) => n.clone(),
5200 None => return Err("last-sibling?: no current node".to_string()),
5201 }
5202 } else {
5203 match arena.get(args[0]) {
5204 ValueData::Node(n) => n.clone(),
5205 _ => return Err("last-sibling?: argument must be a node".to_string()),
5206 }
5207 };
5208
5209 let gi = node.gi();
5212
5213 match node.parent() {
5214 Some(parent) => {
5215 let children = parent.children();
5216 let len = children.length();
5217
5218 for idx in (0..len).rev() {
5220 if let Some(sibling) = children.get(idx) {
5221 if sibling.gi() == gi {
5223 if node.node_eq(sibling.as_ref()) {
5226 return Ok(crate::scheme::arena::TRUE_ID);
5227 } else {
5228 return Ok(crate::scheme::arena::FALSE_ID);
5229 }
5230 }
5231 }
5232 }
5233 Ok(crate::scheme::arena::FALSE_ID)
5235 }
5236 None => Ok(crate::scheme::arena::TRUE_ID), }
5238}
5239
5240pub fn child_number(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
5242 if args.len() > 1 {
5243 return Err("child-number: expected 0 or 1 arguments".to_string());
5244 }
5245
5246 let node = if args.is_empty() {
5247 match &arena.current_node {
5248 Some(n) => n.clone(),
5249 None => return Err("child-number: no current node".to_string()),
5250 }
5251 } else {
5252 match arena.get(args[0]) {
5253 ValueData::Node(n) => n.clone(),
5254 _ => return Err("child-number: argument must be a node".to_string()),
5255 }
5256 };
5257
5258 let gi = node.gi();
5261
5262 match node.parent() {
5263 Some(parent) => {
5264 let children = parent.children();
5265 let len = children.length();
5266 let mut position = 0;
5267
5268 for idx in 0..len {
5270 if let Some(sibling) = children.get(idx) {
5271 if sibling.gi() == gi {
5273 position += 1;
5274 if node.node_eq(sibling.as_ref()) {
5276 return Ok(arena.int(position as i64));
5277 }
5278 }
5279 }
5280 }
5281 Ok(arena.int(1))
5283 }
5284 None => Ok(arena.int(1)), }
5286}
5287
5288pub fn element_with_id(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
5290 if args.len() != 1 {
5291 return Err("element-with-id: expected 1 argument".to_string());
5292 }
5293
5294 let id = match arena.get(args[0]) {
5295 ValueData::String(s) => s.clone(),
5296 ValueData::Symbol(s) => s.to_string(),
5297 _ => return Err("element-with-id: argument must be a string or symbol".to_string()),
5298 };
5299
5300 match &arena.grove {
5302 Some(grove) => {
5303 match grove.element_with_id(&id) {
5304 Some(node) => {
5306 let singleton = crate::grove::VecNodeList::new(vec![node]);
5307 Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(singleton)))))
5308 }
5309 None => Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(crate::grove::EmptyNodeList))))),
5310 }
5311 }
5312 None => Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(crate::grove::EmptyNodeList))))),
5313 }
5314}
5315
5316pub fn element_number(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
5322 if args.len() > 1 {
5323 return Err("element-number: expected 0 or 1 arguments".to_string());
5324 }
5325
5326 let _node = if args.is_empty() {
5327 match &arena.current_node {
5328 Some(n) => n.clone(),
5329 None => return Err("element-number: no current node".to_string()),
5330 }
5331 } else {
5332 match arena.get(args[0]) {
5333 ValueData::Node(n) => n.clone(),
5334 _ => return Err("element-number: argument must be a node".to_string()),
5335 }
5336 };
5337
5338 Ok(arena.int(1))
5341}
5342
5343pub fn hierarchical_number(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
5345 if args.len() > 1 {
5346 return Err("hierarchical-number: expected 0 or 1 arguments".to_string());
5347 }
5348
5349 let _node = if args.is_empty() {
5350 match &arena.current_node {
5351 Some(n) => n.clone(),
5352 None => return Err("hierarchical-number: no current node".to_string()),
5353 }
5354 } else {
5355 match arena.get(args[0]) {
5356 ValueData::Node(n) => n.clone(),
5357 _ => return Err("hierarchical-number: argument must be a node".to_string()),
5358 }
5359 };
5360
5361 let one = arena.int(1);
5364 Ok(arena.cons(one, crate::scheme::arena::NIL_ID))
5365}
5366
5367pub fn hierarchical_number_recursive(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
5369 if args.len() < 1 || args.len() > 2 {
5370 return Err("hierarchical-number-recursive: expected 1 or 2 arguments".to_string());
5371 }
5372
5373 let _element_names = args[0];
5375
5376 let _node = if args.len() == 2 {
5377 match arena.get(args[1]) {
5378 ValueData::Node(n) => n.clone(),
5379 _ => return Err("hierarchical-number-recursive: second argument must be a node".to_string()),
5380 }
5381 } else {
5382 match &arena.current_node {
5383 Some(n) => n.clone(),
5384 None => return Err("hierarchical-number-recursive: no current node".to_string()),
5385 }
5386 };
5387
5388 let one = arena.int(1);
5390 Ok(arena.cons(one, crate::scheme::arena::NIL_ID))
5391}
5392
5393fn int_to_roman(mut n: i32, uppercase: bool) -> String {
5395 if n <= 0 || n > 3999 {
5396 return n.to_string();
5397 }
5398
5399 let values = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
5400 let symbols_upper = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"];
5401 let symbols_lower = ["m", "cm", "d", "cd", "c", "xc", "l", "xl", "x", "ix", "v", "iv", "i"];
5402
5403 let symbols = if uppercase { &symbols_upper } else { &symbols_lower };
5404
5405 let mut result = String::new();
5406 for (i, &value) in values.iter().enumerate() {
5407 while n >= value {
5408 result.push_str(symbols[i]);
5409 n -= value;
5410 }
5411 }
5412 result
5413}
5414
5415fn int_to_alpha(n: i32, uppercase: bool) -> String {
5416 if n <= 0 {
5417 return n.to_string();
5418 }
5419
5420 let base = if uppercase { b'A' } else { b'a' };
5421 let mut result = String::new();
5422 let mut num = n - 1;
5423
5424 loop {
5425 result.insert(0, (base + (num % 26) as u8) as char);
5426 num /= 26;
5427 if num == 0 {
5428 break;
5429 }
5430 num -= 1;
5431 }
5432
5433 result
5434}
5435
5436pub fn ancestors(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
5442 if args.len() > 1 {
5443 return Err("ancestors: expected 0 or 1 arguments".to_string());
5444 }
5445
5446 let node = if args.is_empty() {
5447 match &arena.current_node {
5448 Some(n) => n.clone(),
5449 None => return Err("ancestors: no current node".to_string()),
5450 }
5451 } else {
5452 match arena.get(args[0]) {
5454 ValueData::Node(n) => n.clone(),
5455 ValueData::NodeList(nl) => {
5456 if nl.is_empty() {
5457 return Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(
5459 crate::grove::VecNodeList::new(Vec::new())
5460 )))));
5461 }
5462 let first = nl.first();
5463 let rest = nl.rest();
5464 if rest.is_empty() {
5465 Rc::new(first.unwrap())
5467 } else {
5468 return Err("ancestors: node-list must be a singleton".to_string());
5470 }
5471 }
5472 _ => return Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(
5474 crate::grove::VecNodeList::new(Vec::new())
5475 ))))),
5476 }
5477 };
5478
5479 let mut ancestors = Vec::new();
5481 let mut current = node.parent();
5482 while let Some(parent_node) = current {
5483 ancestors.push(parent_node.clone_node());
5484 current = parent_node.parent();
5485 }
5486
5487 let node_list = crate::grove::VecNodeList::new(ancestors);
5488 Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(node_list)))))
5489}
5490
5491pub fn document_element(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
5493 if !args.is_empty() {
5494 return Err("document-element: expected no arguments".to_string());
5495 }
5496
5497 match &arena.grove {
5498 Some(grove) => {
5499 let root = grove.root();
5500 Ok(arena.alloc(ValueData::Node(Rc::new(root))))
5501 }
5502 None => Err("document-element: no grove available".to_string()),
5503 }
5504}
5505
5506pub fn have_ancestor_p(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
5508 if args.is_empty() || args.len() > 2 {
5509 return Err("have-ancestor?: expected 1 or 2 arguments".to_string());
5510 }
5511
5512 let ancestor_name = match arena.get(args[0]) {
5513 ValueData::String(s) => s.clone(),
5514 ValueData::Symbol(s) => s.to_string(),
5515 _ => return Err("have-ancestor?: first argument must be a string or symbol".to_string()),
5516 };
5517
5518 let node = if args.len() == 2 {
5519 match arena.get(args[1]) {
5520 ValueData::Node(n) => n.clone(),
5521 _ => return Err("have-ancestor?: second argument must be a node".to_string()),
5522 }
5523 } else {
5524 match &arena.current_node {
5525 Some(n) => n.clone(),
5526 None => return Err("have-ancestor?: no current node".to_string()),
5527 }
5528 };
5529
5530 let mut current = node.parent();
5532 while let Some(parent_node) = current {
5533 if let Some(gi) = parent_node.gi() {
5534 if gi == ancestor_name {
5535 return Ok(crate::scheme::arena::TRUE_ID);
5536 }
5537 }
5538 current = parent_node.parent();
5539 }
5540
5541 Ok(crate::scheme::arena::FALSE_ID)
5542}
5543
5544pub fn match_element_p(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
5546 if args.is_empty() || args.len() > 2 {
5547 return Err("match-element?: expected 1 or 2 arguments".to_string());
5548 }
5549
5550 let element_name = match arena.get(args[0]) {
5551 ValueData::String(s) => s.clone(),
5552 ValueData::Symbol(s) => s.to_string(),
5553 _ => return Err("match-element?: first argument must be a string or symbol".to_string()),
5554 };
5555
5556 let node = if args.len() == 2 {
5557 match arena.get(args[1]) {
5558 ValueData::Node(n) => n.clone(),
5559 _ => return Err("match-element?: second argument must be a node".to_string()),
5560 }
5561 } else {
5562 match &arena.current_node {
5563 Some(n) => n.clone(),
5564 None => return Err("match-element?: no current node".to_string()),
5565 }
5566 };
5567
5568 match node.gi() {
5569 Some(gi) if gi == element_name => Ok(crate::scheme::arena::TRUE_ID),
5570 _ => Ok(crate::scheme::arena::FALSE_ID),
5571 }
5572}
5573
5574pub fn node_list_map(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
5576 if args.len() != 2 {
5577 return Err("node-list-map: expected 2 arguments".to_string());
5578 }
5579
5580 let _func = args[0];
5581 let nl = match arena.get(args[1]) {
5582 ValueData::NodeList(nl) => nl.clone(),
5583 _ => return Err("node-list-map: second argument must be a node-list".to_string()),
5584 };
5585
5586 Ok(arena.alloc(ValueData::NodeList(nl)))
5589}
5590
5591pub fn node_property(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
5597 if args.len() < 1 || args.len() > 2 {
5598 return Err("node-property: expected 1 or 2 arguments".to_string());
5599 }
5600
5601 let _property_name = match arena.get(args[0]) {
5602 ValueData::String(s) => s.clone(),
5603 ValueData::Symbol(s) => s.to_string(),
5604 _ => return Err("node-property: first argument must be a string or symbol".to_string()),
5605 };
5606
5607 let _node = if args.len() == 2 {
5608 match arena.get(args[1]) {
5609 ValueData::Bool(false) => {
5610 return Ok(crate::scheme::arena::FALSE_ID);
5612 }
5613 ValueData::Node(n) => n.clone(),
5614 ValueData::NodeList(nl) => {
5615 if nl.is_empty() {
5617 return Ok(crate::scheme::arena::FALSE_ID);
5618 }
5619 let first = nl.first();
5620 let rest = nl.rest();
5621 if rest.is_empty() {
5622 Rc::new(first.unwrap())
5624 } else {
5625 return Err("node-property: node-list must be a singleton".to_string());
5627 }
5628 }
5629 _ => {
5630 return Err("node-property: second argument must be a node, singleton node-list, or #f".to_string());
5631 }
5632 }
5633 } else {
5634 match &arena.current_node {
5635 Some(n) => n.clone(),
5636 None => return Err("node-property: no current node".to_string()),
5637 }
5638 };
5639
5640 Ok(crate::scheme::arena::FALSE_ID)
5643}
5644
5645pub fn absolute_first_sibling_p(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
5647 if args.len() > 1 {
5648 return Err("absolute-first-sibling?: expected 0 or 1 arguments".to_string());
5649 }
5650
5651 let _node = if args.is_empty() {
5652 match &arena.current_node {
5653 Some(n) => n.clone(),
5654 None => return Err("absolute-first-sibling?: no current node".to_string()),
5655 }
5656 } else {
5657 match arena.get(args[0]) {
5658 ValueData::Node(n) => n.clone(),
5659 _ => return Err("absolute-first-sibling?: argument must be a node".to_string()),
5660 }
5661 };
5662
5663 Ok(crate::scheme::arena::FALSE_ID)
5666}
5667
5668pub fn absolute_last_sibling_p(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
5670 if args.len() > 1 {
5671 return Err("absolute-last-sibling?: expected 0 or 1 arguments".to_string());
5672 }
5673
5674 let _node = if args.is_empty() {
5675 match &arena.current_node {
5676 Some(n) => n.clone(),
5677 None => return Err("absolute-last-sibling?: no current node".to_string()),
5678 }
5679 } else {
5680 match arena.get(args[0]) {
5681 ValueData::Node(n) => n.clone(),
5682 _ => return Err("absolute-last-sibling?: argument must be a node".to_string()),
5683 }
5684 };
5685
5686 Ok(crate::scheme::arena::FALSE_ID)
5689}
5690
5691pub fn node_list_to_list(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
5693 if args.len() != 1 {
5694 return Err("node-list->list: expected 1 argument".to_string());
5695 }
5696
5697 let nl = match arena.get(args[0]) {
5698 ValueData::NodeList(nl) => nl.clone(),
5699 _ => return Err("node-list->list: argument must be a node-list".to_string()),
5700 };
5701
5702 let mut nodes = Vec::new();
5704 let mut current = nl.as_ref().as_ref();
5705 while !current.is_empty() {
5706 if let Some(node) = current.first() {
5707 let node_id = arena.alloc(ValueData::Node(Rc::new(node)));
5708 nodes.push(node_id);
5709 }
5710 current = Box::leak(current.rest());
5711 }
5712
5713 let mut result = crate::scheme::arena::NIL_ID;
5715 for node_id in nodes.iter().rev() {
5716 result = arena.cons(*node_id, result);
5717 }
5718
5719 Ok(result)
5720}
5721
5722pub fn node_list_contains_p(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
5724 if args.len() != 2 {
5725 return Err("node-list-contains?: expected 2 arguments".to_string());
5726 }
5727
5728 let nl = match arena.get(args[0]) {
5729 ValueData::NodeList(nl) => nl.clone(),
5730 _ => return Err("node-list-contains?: first argument must be a node-list".to_string()),
5731 };
5732
5733 let target_node = match arena.get(args[1]) {
5735 ValueData::Node(n) => n.clone(),
5736 ValueData::NodeList(nl) => {
5737 if nl.is_empty() {
5738 return Ok(crate::scheme::arena::FALSE_ID);
5740 }
5741 let first = nl.first();
5742 let rest = nl.rest();
5743 if rest.is_empty() {
5744 Rc::new(first.unwrap())
5746 } else {
5747 return Err("node-list-contains?: second argument (node-list) must be a singleton".to_string());
5749 }
5750 }
5751 _ => return Err("node-list-contains?: second argument must be a node or node-list".to_string()),
5752 };
5753
5754 let mut nodes = Vec::new();
5757 let mut current_nl = nl.clone();
5758 loop {
5759 if current_nl.is_empty() {
5760 break;
5761 }
5762 nodes.push(current_nl.first().unwrap());
5763 current_nl = Rc::new(current_nl.rest());
5764 }
5765
5766 for node in nodes {
5768 if node.as_ref().node_eq(&**target_node) {
5769 return Ok(crate::scheme::arena::TRUE_ID);
5770 }
5771 }
5772
5773 Ok(crate::scheme::arena::FALSE_ID)
5774}
5775
5776pub fn entity_system_id(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
5782 if args.is_empty() {
5783 return Err("entity-system-id: requires at least 1 argument".to_string());
5784 }
5785
5786 let _entity_name = match arena.get(args[0]) {
5788 ValueData::String(s) => s.clone(),
5789 ValueData::Symbol(s) => s.to_string(),
5790 _ => return Err("entity-system-id: argument must be a string or symbol".to_string()),
5791 };
5792
5793 Ok(crate::scheme::arena::FALSE_ID)
5796}
5797
5798pub fn entity_public_id(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
5800 if args.is_empty() {
5801 return Err("entity-public-id: requires at least 1 argument".to_string());
5802 }
5803
5804 let _entity_name = match arena.get(args[0]) {
5806 ValueData::String(s) => s.clone(),
5807 ValueData::Symbol(s) => s.to_string(),
5808 _ => return Err("entity-public-id: argument must be a string or symbol".to_string()),
5809 };
5810
5811 Ok(crate::scheme::arena::FALSE_ID)
5814}
5815
5816pub fn entity_type(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
5818 if args.is_empty() {
5819 return Err("entity-type: requires at least 1 argument".to_string());
5820 }
5821
5822 let _entity_name = match arena.get(args[0]) {
5824 ValueData::String(s) => s.clone(),
5825 ValueData::Symbol(s) => s.to_string(),
5826 _ => return Err("entity-type: argument must be a string or symbol".to_string()),
5827 };
5828
5829 Ok(crate::scheme::arena::FALSE_ID)
5833}
5834
5835pub fn notation_system_id(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
5837 if args.is_empty() {
5838 return Err("notation-system-id: requires at least 1 argument".to_string());
5839 }
5840
5841 let _notation_name = match arena.get(args[0]) {
5843 ValueData::String(s) => s.clone(),
5844 ValueData::Symbol(s) => s.to_string(),
5845 _ => return Err("notation-system-id: argument must be a string or symbol".to_string()),
5846 };
5847
5848 Ok(crate::scheme::arena::FALSE_ID)
5851}
5852
5853pub fn notation_public_id(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
5855 if args.is_empty() {
5856 return Err("notation-public-id: requires at least 1 argument".to_string());
5857 }
5858
5859 let _notation_name = match arena.get(args[0]) {
5861 ValueData::String(s) => s.clone(),
5862 ValueData::Symbol(s) => s.to_string(),
5863 _ => return Err("notation-public-id: argument must be a string or symbol".to_string()),
5864 };
5865
5866 Ok(crate::scheme::arena::FALSE_ID)
5869}
5870
5871pub fn current_language(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
5877 if !args.is_empty() {
5878 return Err("current-language: requires 0 arguments".to_string());
5879 }
5880
5881 Ok(crate::scheme::arena::FALSE_ID)
5885}
5886
5887pub fn current_mode(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
5889 if !args.is_empty() {
5890 return Err("current-mode: requires 0 arguments".to_string());
5891 }
5892
5893 Ok(crate::scheme::arena::FALSE_ID)
5897}
5898
5899pub fn current_node_address(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
5901 if !args.is_empty() {
5902 return Err("current-node-address: requires 0 arguments".to_string());
5903 }
5904
5905 Ok(crate::scheme::arena::FALSE_ID)
5909}
5910
5911pub fn current_node_page_number_sosofo(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
5913 if !args.is_empty() {
5914 return Err("current-node-page-number-sosofo: requires 0 arguments".to_string());
5915 }
5916
5917 Ok(arena.alloc(ValueData::Sosofo))
5921}
5922
5923pub fn debug(arena: &Arena, args: &[ValueId]) -> ArenaResult {
5925 eprint!("[DEBUG]");
5927 for arg_id in args {
5928 match arena.get(*arg_id) {
5929 ValueData::Integer(n) => eprint!(" {}", n),
5930 ValueData::Real(f) => eprint!(" {}", f),
5931 ValueData::String(s) => eprint!(" \"{}\"", s),
5932 ValueData::Symbol(s) => eprint!(" {}", s),
5933 ValueData::Bool(b) => eprint!(" {}", if *b { "#t" } else { "#f" }),
5934 ValueData::Char(c) => eprint!(" #\\{}", c),
5935 ValueData::Nil => eprint!(" ()"),
5936 ValueData::Pair { .. } => eprint!(" <pair>"),
5937 ValueData::Vector(_) => eprint!(" <vector>"),
5938 ValueData::Node(_) => eprint!(" <node>"),
5939 ValueData::NodeList(_) => eprint!(" <node-list>"),
5940 ValueData::Sosofo => eprint!(" <sosofo>"),
5941 ValueData::Unspecified => eprint!(" <unspecified>"),
5942 ValueData::Error => eprint!(" <error>"),
5943 ValueData::Procedure(_) => eprint!(" <procedure>"),
5944 ValueData::Quantity { magnitude, .. } => eprint!(" {}pt", magnitude),
5945 ValueData::Keyword(k) => eprint!(" #:{}", k),
5946 }
5947 }
5948 eprintln!();
5949
5950 Ok(crate::scheme::arena::UNSPECIFIED_ID)
5952}
5953
5954pub fn exact_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
5958 if args.len() != 1 {
5959 return Err(format!("exact?: expected 1 argument, got {}", args.len()));
5960 }
5961
5962 match arena.get(args[0]) {
5963 ValueData::Integer(_) => Ok(crate::scheme::arena::TRUE_ID),
5964 ValueData::Real(_) => Ok(crate::scheme::arena::FALSE_ID),
5965 ValueData::Quantity { .. } => Ok(crate::scheme::arena::FALSE_ID),
5966 _ => Err("exact?: argument must be a number".to_string()),
5967 }
5968}
5969
5970pub fn inexact_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
5972 if args.len() != 1 {
5973 return Err(format!("inexact?: expected 1 argument, got {}", args.len()));
5974 }
5975
5976 match arena.get(args[0]) {
5977 ValueData::Integer(_) => Ok(crate::scheme::arena::FALSE_ID),
5978 ValueData::Real(_) => Ok(crate::scheme::arena::TRUE_ID),
5979 ValueData::Quantity { .. } => Ok(crate::scheme::arena::TRUE_ID),
5980 _ => Err("inexact?: argument must be a number".to_string()),
5981 }
5982}
5983
5984pub fn error(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
5986 let msg = if args.is_empty() {
5987 "error".to_string()
5988 } else {
5989 let mut parts = Vec::new();
5991 for arg in args {
5992 match arena.get(*arg) {
5993 ValueData::String(s) => parts.push(s.clone()),
5994 ValueData::Symbol(s) => parts.push(s.to_string()),
5995 ValueData::Integer(n) => parts.push(n.to_string()),
5996 ValueData::Real(f) => parts.push(f.to_string()),
5997 ValueData::Bool(b) => parts.push(if *b { "#t" } else { "#f" }.to_string()),
5998 ValueData::Char(c) => parts.push(format!("#\\{}", c)),
5999 _ => parts.push("<value>".to_string()),
6000 }
6001 }
6002 parts.join(" ")
6003 };
6004
6005 Err(msg)
6006}
6007
6008pub fn address_p(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
6013 if args.len() != 1 {
6014 return Err(format!("address?: expected 1 argument, got {}", args.len()));
6015 }
6016 Ok(crate::scheme::arena::FALSE_ID)
6018}
6019
6020pub fn address_local_p(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
6022 if args.len() != 1 {
6023 return Err(format!("address-local?: expected 1 argument, got {}", args.len()));
6024 }
6025 Ok(crate::scheme::arena::FALSE_ID)
6027}
6028
6029pub fn address_visited_p(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
6031 if args.len() != 1 {
6032 return Err(format!("address-visited?: expected 1 argument, got {}", args.len()));
6033 }
6034 Ok(crate::scheme::arena::FALSE_ID)
6036}
6037
6038pub fn color_space_p(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
6043 if args.len() != 1 {
6044 return Err(format!("color-space?: expected 1 argument, got {}", args.len()));
6045 }
6046 Ok(crate::scheme::arena::FALSE_ID)
6048}
6049
6050pub fn color_space(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
6052 Ok(crate::scheme::arena::UNSPECIFIED_ID)
6054}
6055
6056pub fn display_space(_arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
6058 Ok(crate::scheme::arena::UNSPECIFIED_ID)
6060}
6061
6062pub fn inline_space(_arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
6064 Ok(crate::scheme::arena::UNSPECIFIED_ID)
6066}
6067
6068pub fn glyph_id_p(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
6073 if args.len() != 1 {
6074 return Err(format!("glyph-id?: expected 1 argument, got {}", args.len()));
6075 }
6076 Ok(crate::scheme::arena::FALSE_ID)
6078}
6079
6080pub fn glyph_id(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
6082 Ok(crate::scheme::arena::UNSPECIFIED_ID)
6084}
6085
6086pub fn glyph_subst_table_p(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
6088 if args.len() != 1 {
6089 return Err(format!("glyph-subst-table?: expected 1 argument, got {}", args.len()));
6090 }
6091 Ok(crate::scheme::arena::FALSE_ID)
6093}
6094
6095pub fn glyph_subst_table(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
6097 Ok(crate::scheme::arena::UNSPECIFIED_ID)
6099}
6100
6101pub fn glyph_subst(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
6103 Ok(crate::scheme::arena::UNSPECIFIED_ID)
6105}
6106
6107pub fn time(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
6112 Ok(crate::scheme::arena::UNSPECIFIED_ID)
6114}
6115
6116pub fn time_to_string(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
6118 if args.len() != 1 {
6119 return Err(format!("time->string: expected 1 argument, got {}", args.len()));
6120 }
6121 Ok(arena.string(String::new()))
6123}
6124
6125pub fn time_le(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
6127 if args.len() != 2 {
6128 return Err(format!("time<=?: expected 2 arguments, got {}", args.len()));
6129 }
6130 Ok(crate::scheme::arena::FALSE_ID)
6132}
6133
6134pub fn time_lt(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
6136 if args.len() != 2 {
6137 return Err(format!("time<?: expected 2 arguments, got {}", args.len()));
6138 }
6139 Ok(crate::scheme::arena::FALSE_ID)
6141}
6142
6143pub fn time_ge(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
6145 if args.len() != 2 {
6146 return Err(format!("time>=?: expected 2 arguments, got {}", args.len()));
6147 }
6148 Ok(crate::scheme::arena::FALSE_ID)
6150}
6151
6152pub fn time_gt(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
6154 if args.len() != 2 {
6155 return Err(format!("time>?: expected 2 arguments, got {}", args.len()));
6156 }
6157 Ok(crate::scheme::arena::FALSE_ID)
6159}
6160
6161pub fn language_p(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
6166 if args.len() != 1 {
6167 return Err(format!("language?: expected 1 argument, got {}", args.len()));
6168 }
6169 Ok(crate::scheme::arena::FALSE_ID)
6171}
6172
6173pub fn language(arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
6177 Ok(arena.symbol("en".into()))
6180}
6181
6182pub fn style_p(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
6184 if args.len() != 1 {
6185 return Err(format!("style?: expected 1 argument, got {}", args.len()));
6186 }
6187 Ok(crate::scheme::arena::FALSE_ID)
6189}
6190
6191pub fn string_equiv_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
6195 if args.len() != 2 {
6196 return Err(format!("string-equiv?: expected 2 arguments, got {}", args.len()));
6197 }
6198
6199 let s1 = match arena.get(args[0]) {
6200 ValueData::String(s) => s.to_lowercase(),
6201 _ => return Err("string-equiv?: first argument must be a string".to_string()),
6202 };
6203
6204 let s2 = match arena.get(args[1]) {
6205 ValueData::String(s) => s.to_lowercase(),
6206 _ => return Err("string-equiv?: second argument must be a string".to_string()),
6207 };
6208
6209 Ok(if s1 == s2 {
6210 crate::scheme::arena::TRUE_ID
6211 } else {
6212 crate::scheme::arena::FALSE_ID
6213 })
6214}
6215
6216pub fn label_length(arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
6218 Ok(arena.int(0))
6220}
6221
6222pub fn external_procedure(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
6225 use crate::scheme::arena::ValueData;
6226 use std::rc::Rc;
6227
6228 if args.len() != 1 {
6229 return Err("external-procedure: requires 1 argument".to_string());
6230 }
6231
6232 let name_str = match arena.get(args[0]) {
6234 ValueData::String(s) => s.clone(),
6235 _ => return Err("external-procedure: argument must be a string".to_string()),
6236 };
6237
6238 let proc_name = if let Some(pos) = name_str.rfind("::") {
6241 &name_str[pos + 2..]
6242 } else {
6243 &name_str
6244 };
6245
6246 let valid = matches!(proc_name,
6248 "if-first-page" | "if-front-page" | "empty-sosofo" | "literal" |
6250 "sosofo-append" | "page-number-sosofo" | "current-node-page-number-sosofo" |
6251 "process-children" | "process-node-list" | "next-match" |
6253 "current-node" | "node-list-first" | "node-list-rest" | "node-list-length" |
6255 "empty-node-list" | "children" | "parent" | "ancestor" | "descendants" |
6256 "follow" | "preced" | "attributes" | "gi" | "id" | "data" |
6257 "attribute-string" | "inherited-attribute-string"
6258 );
6259
6260 if !valid {
6261 return Err(format!("external-procedure: unknown procedure '{}'", proc_name));
6262 }
6263
6264 let symbol_data = ValueData::Symbol(Rc::from(proc_name));
6266 Ok(arena.alloc(symbol_data))
6267}
6268
6269pub fn declaration(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
6274 Ok(crate::scheme::arena::UNSPECIFIED_ID)
6276}
6277
6278pub fn dtd(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
6280 Ok(crate::scheme::arena::UNSPECIFIED_ID)
6282}
6283
6284pub fn epilog(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
6286 Ok(crate::scheme::arena::UNSPECIFIED_ID)
6288}
6289
6290pub fn prolog(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
6292 Ok(crate::scheme::arena::UNSPECIFIED_ID)
6294}
6295
6296pub fn sgml_declaration(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
6298 Ok(crate::scheme::arena::UNSPECIFIED_ID)
6300}
6301
6302pub fn sgml_parse(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
6304 Ok(crate::scheme::arena::UNSPECIFIED_ID)
6306}
6307
6308pub fn entity_address(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
6313 Ok(crate::scheme::arena::UNSPECIFIED_ID)
6315}
6316
6317pub fn entity_generated_system_id(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
6319 if args.len() != 1 {
6320 return Err(format!("entity-generated-system-id: expected 1 argument, got {}", args.len()));
6321 }
6322 Ok(arena.string(String::new()))
6324}
6325
6326pub fn entity_name_normalize(_arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
6328 if args.len() != 1 {
6329 return Err(format!("entity-name-normalize: expected 1 argument, got {}", args.len()));
6330 }
6331 Ok(args[0])
6333}
6334
6335pub fn general_name_normalize(_arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
6339 if args.is_empty() || args.len() > 2 {
6340 return Err(format!("general-name-normalize: expected 1 or 2 arguments, got {}", args.len()));
6341 }
6342 Ok(args[0])
6344}
6345
6346pub fn normalize(_arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
6349 general_name_normalize(_arena, args)
6351}
6352
6353pub fn first_child_gi(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
6357 if args.len() != 1 {
6358 return Err(format!("first-child-gi: expected 1 argument, got {}", args.len()));
6359 }
6360 Ok(arena.string(String::new()))
6362}
6363
6364pub fn tree_root(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
6366 if let Some(ref grove) = arena.grove {
6370 let root = grove.root();
6372 Ok(arena.alloc(ValueData::Node(Rc::new(root))))
6373 } else {
6374 let start_node = if args.is_empty() {
6377 match &arena.current_node {
6379 Some(n) => n.clone(),
6380 None => return Ok(crate::scheme::arena::UNSPECIFIED_ID),
6381 }
6382 } else {
6383 match arena.get(args[0]) {
6385 ValueData::Node(n) => n.clone(),
6386 ValueData::NodeList(nl) => {
6387 if nl.is_empty() {
6388 return Ok(crate::scheme::arena::UNSPECIFIED_ID);
6389 }
6390 let first = nl.first();
6391 let rest = nl.rest();
6392 if rest.is_empty() {
6393 Rc::new(first.unwrap())
6394 } else {
6395 return Err("tree-root: node-list must be a singleton".to_string());
6396 }
6397 }
6398 _ => return Ok(crate::scheme::arena::UNSPECIFIED_ID),
6399 }
6400 };
6401
6402 let mut current = start_node;
6404 loop {
6405 match current.parent() {
6406 Some(parent) => {
6407 current = Rc::new(parent);
6409 }
6410 None => {
6411 break;
6413 }
6414 }
6415 }
6416
6417 Ok(arena.alloc(ValueData::Node(current)))
6418 }
6419}
6420
6421pub fn declare_default_language(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
6423 Ok(crate::scheme::arena::UNSPECIFIED_ID)
6425}
6426
6427pub fn read_entity(arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
6429 Ok(arena.string(String::new()))
6431}
6432
6433pub fn set_visited(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
6435 Ok(crate::scheme::arena::UNSPECIFIED_ID)
6437}
6438
6439pub fn sosofo_contains_node_p(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
6443 if args.len() != 2 {
6444 return Err(format!("sosofo-contains-node?: expected 2 arguments, got {}", args.len()));
6445 }
6446 Ok(crate::scheme::arena::FALSE_ID)
6448}
6449
6450pub fn page_number_sosofo(arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
6452 Ok(arena.alloc(ValueData::Sosofo))
6455}
6456
6457pub fn ifollow(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
6459 Ok(crate::scheme::arena::UNSPECIFIED_ID)
6461}
6462
6463pub fn with_language(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
6465 Ok(crate::scheme::arena::UNSPECIFIED_ID)
6468}
6469
6470pub fn all_element_number(arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
6474 Ok(arena.int(0))
6476}
6477
6478pub fn ancestor_child_number(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
6480 if args.len() != 2 {
6481 return Err(format!("ancestor-child-number: expected 2 arguments, got {}", args.len()));
6482 }
6483 Ok(arena.int(0))
6485}
6486
6487pub fn element_number_list(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
6489 Ok(crate::scheme::arena::NIL_ID)
6491}
6492
6493pub fn inherited_attribute_string(_arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
6497 if args.is_empty() || args.len() > 2 {
6498 return Err(format!("inherited-attribute-string: expected 1-2 arguments, got {}", args.len()));
6499 }
6500 Ok(crate::scheme::arena::FALSE_ID)
6502}
6503
6504pub fn inherited_element_attribute_string(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
6506 if args.len() < 1 || args.len() > 2 {
6507 return Err(format!("inherited-element-attribute-string: expected 1 or 2 arguments, got {}", args.len()));
6508 }
6509 Ok(arena.string(String::new()))
6511}
6512
6513pub fn inherited_start_indent(arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
6515 Ok(arena.int(0))
6517}
6518
6519pub fn inherited_end_indent(arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
6521 Ok(arena.int(0))
6523}
6524
6525pub fn inherited_line_spacing(arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
6527 Ok(arena.int(0))
6529}
6530
6531pub fn inherited_font_family_name(arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
6535 Ok(arena.string(String::new()))
6537}
6538
6539pub fn inherited_font_size(arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
6541 Ok(arena.int(12))
6543}
6544
6545pub fn inherited_font_weight(arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
6547 Ok(arena.string(String::new()))
6549}
6550
6551pub fn inherited_font_posture(arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
6553 Ok(arena.string(String::new()))
6555}
6556
6557pub fn inherited_dbhtml_value(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
6559 if args.len() != 1 {
6560 return Err(format!("inherited-dbhtml-value: expected 1 argument, got {}", args.len()));
6561 }
6562 Ok(arena.string(String::new()))
6564}
6565
6566pub fn inherited_pi_value(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
6568 if args.len() != 1 {
6569 return Err(format!("inherited-pi-value: expected 1 argument, got {}", args.len()));
6570 }
6571 Ok(arena.string(String::new()))
6573}
6574
6575pub fn node_list(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
6581 use crate::grove::VecNodeList;
6582
6583 let mut result_nodes = Vec::new();
6584
6585 for arg in args {
6586 match arena.get(*arg) {
6587 ValueData::Node(node) => {
6588 result_nodes.push(node.as_ref().clone_node());
6590 }
6591 ValueData::NodeList(nl) => {
6592 let len = nl.length();
6594 for i in 0..len {
6595 if let Some(node) = nl.get(i) {
6596 result_nodes.push(node);
6597 }
6598 }
6599 }
6600 _ => {
6601 return Err("node-list: arguments must be nodes or node-lists".to_string());
6602 }
6603 }
6604 }
6605
6606 Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(VecNodeList::new(result_nodes))))))
6607}
6608
6609pub fn node_list_eq_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
6612 use crate::scheme::arena::{FALSE_ID, TRUE_ID};
6613
6614 if args.len() != 2 {
6615 return Err(format!("node-list=?: expected 2 arguments, got {}", args.len()));
6616 }
6617
6618 if args[0] == args[1] {
6620 return Ok(TRUE_ID);
6621 }
6622
6623 let nl1 = match arena.get(args[0]) {
6625 ValueData::Bool(false) => {
6626 vec![]
6628 }
6629 ValueData::Node(n) => {
6630 vec![n.clone()]
6632 }
6633 ValueData::NodeList(nl) => {
6634 let mut nodes = Vec::new();
6636 let mut index = 0;
6637 while let Some(node) = nl.get(index) {
6638 nodes.push(std::rc::Rc::new(node));
6639 index += 1;
6640 }
6641 nodes
6642 }
6643 _ => return Err("node-list=?: first argument must be a node, node-list, or #f".to_string()),
6644 };
6645
6646 let nl2 = match arena.get(args[1]) {
6647 ValueData::Bool(false) => {
6648 vec![]
6650 }
6651 ValueData::Node(n) => {
6652 vec![n.clone()]
6654 }
6655 ValueData::NodeList(nl) => {
6656 let mut nodes = Vec::new();
6658 let mut index = 0;
6659 while let Some(node) = nl.get(index) {
6660 nodes.push(std::rc::Rc::new(node));
6661 index += 1;
6662 }
6663 nodes
6664 }
6665 v => {
6666 let type_name = match v {
6667 ValueData::Nil => "empty list",
6668 ValueData::Bool(_) => "boolean",
6669 ValueData::Integer(_) => "integer",
6670 ValueData::Real(_) => "real",
6671 ValueData::Char(_) => "char",
6672 ValueData::String(_) => "string",
6673 ValueData::Symbol(_) => "symbol",
6674 ValueData::Keyword(_) => "keyword",
6675 ValueData::Pair { .. } => "pair",
6676 ValueData::Vector(_) => "vector",
6677 ValueData::Procedure(_) => "procedure",
6678 ValueData::Quantity { .. } => "quantity",
6679 ValueData::Sosofo => "sosofo",
6680 ValueData::Unspecified => "unspecified",
6681 ValueData::Error => "error",
6682 _ => "unknown",
6683 };
6684 return Err(format!("node-list=?: second argument must be a node, node-list, or #f (got {})", type_name));
6685 }
6686 };
6687
6688 if nl1.len() != nl2.len() {
6690 return Ok(FALSE_ID);
6691 }
6692
6693 for (node1, node2) in nl1.iter().zip(nl2.iter()) {
6698 if !node1.as_ref().as_ref().node_eq(node2.as_ref().as_ref()) {
6701 return Ok(FALSE_ID);
6702 }
6703 }
6704
6705 Ok(TRUE_ID)
6706}
6707
6708pub fn node_list_count(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
6710 if args.len() != 1 {
6711 return Err(format!("node-list-count: expected 1 argument, got {}", args.len()));
6712 }
6713 let unique_nl = node_list_remove_duplicates(arena, args)?;
6715 node_list_length(arena, &[unique_nl])
6716}
6717
6718pub fn node_list_union_map(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
6720 if args.len() != 2 {
6721 return Err(format!("node-list-union-map: expected 2 arguments, got {}", args.len()));
6722 }
6723 Ok(crate::scheme::arena::NIL_ID)
6725}
6726
6727pub fn node_list_symmetrical_difference(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
6729 if args.len() != 2 {
6730 return Err(format!("node-list-symmetrical-difference: expected 2 arguments, got {}", args.len()));
6731 }
6732 Ok(crate::scheme::arena::NIL_ID)
6734}
6735
6736pub fn node_list_address(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
6741 if args.is_empty() || args.len() > 2 {
6742 return Err(format!("node-list-address: expected 1-2 arguments, got {}", args.len()));
6743 }
6744 Ok(crate::scheme::arena::NIL_ID)
6746}
6747
6748pub fn node_list_error(_arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
6750 if args.len() != 1 {
6751 return Err(format!("node-list-error: expected 1 argument, got {}", args.len()));
6752 }
6753 Ok(crate::scheme::arena::NIL_ID)
6755}
6756
6757pub fn node_list_no_order(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
6759 if args.len() != 1 {
6760 return Err(format!("node-list-no-order: expected 1 argument, got {}", args.len()));
6761 }
6762 Ok(crate::scheme::arena::NIL_ID)
6764}
6765
6766pub fn origin_to_subnode_rel_forest_addr(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
6768 if args.len() != 1 {
6769 return Err(format!("origin-to-subnode-rel-forest-addr: expected 1 argument, got {}", args.len()));
6770 }
6771 Ok(crate::scheme::arena::NIL_ID)
6773}
6774
6775pub fn named_node(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
6779 if args.len() != 1 {
6780 return Err(format!("named-node: expected 1 argument, got {}", args.len()));
6781 }
6782 Ok(crate::scheme::arena::FALSE_ID)
6784}
6785
6786pub fn named_node_list_p(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
6788 if args.len() != 1 {
6789 return Err(format!("named-node-list?: expected 1 argument, got {}", args.len()));
6790 }
6791 Ok(crate::scheme::arena::FALSE_ID)
6793}
6794
6795pub fn named_node_list_names(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
6797 if args.len() != 1 {
6798 return Err(format!("named-node-list-names: expected 1 argument, got {}", args.len()));
6799 }
6800 Ok(crate::scheme::arena::NIL_ID)
6802}
6803
6804pub fn select_by_class(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
6808 if args.len() != 1 {
6809 return Err(format!("select-by-class: expected 1 argument, got {}", args.len()));
6810 }
6811 Ok(crate::scheme::arena::NIL_ID)
6813}
6814
6815pub fn select_children(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
6817 Ok(crate::scheme::arena::NIL_ID)
6820}
6821
6822pub fn process_children_trim(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
6826 Ok(crate::scheme::arena::UNSPECIFIED_ID)
6829}
6830
6831pub fn process_element_with_id(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
6833 if args.len() != 1 {
6834 return Err(format!("process-element-with-id: expected 1 argument, got {}", args.len()));
6835 }
6836 Ok(crate::scheme::arena::UNSPECIFIED_ID)
6839}
6840
6841pub fn process_first_descendant(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
6843 if args.len() != 1 {
6844 return Err(format!("process-first-descendant: expected 1 argument, got {}", args.len()));
6845 }
6846 Ok(crate::scheme::arena::UNSPECIFIED_ID)
6849}
6850
6851pub fn process_matching_children(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
6853 if args.len() != 2 {
6854 return Err(format!("process-matching-children: expected 2 arguments, got {}", args.len()));
6855 }
6856 Ok(crate::scheme::arena::UNSPECIFIED_ID)
6859}
6860
6861pub fn next_match(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
6863 Ok(crate::scheme::arena::UNSPECIFIED_ID)
6866}
6867
6868#[cfg(test)]
6869mod tests {
6870 use super::*;
6871
6872 #[test]
6873 fn test_car() {
6874 let mut arena = Arena::new();
6875 let one = arena.int(1);
6876 let two = arena.int(2);
6877 let pair = arena.cons(one, two);
6878
6879 let result = car(&arena, &[pair]).unwrap();
6880 assert_eq!(result, one);
6881 }
6882
6883 #[test]
6884 fn test_cdr() {
6885 let mut arena = Arena::new();
6886 let one = arena.int(1);
6887 let two = arena.int(2);
6888 let pair = arena.cons(one, two);
6889
6890 let result = cdr(&arena, &[pair]).unwrap();
6891 assert_eq!(result, two);
6892 }
6893
6894 #[test]
6895 fn test_cons() {
6896 let mut arena = Arena::new();
6897 let one = arena.int(1);
6898 let two = arena.int(2);
6899
6900 let pair = cons(&mut arena, &[one, two]).unwrap();
6901
6902 match arena.get(pair) {
6903 ValueData::Pair { car, cdr, .. } => {
6904 assert_eq!(*car, one);
6905 assert_eq!(*cdr, two);
6906 }
6907 _ => panic!("Expected pair"),
6908 }
6909 }
6910
6911 #[test]
6912 fn test_null() {
6913 let mut arena = Arena::new();
6914
6915 assert_eq!(
6916 null(&arena, &[NIL_ID]).unwrap(),
6917 crate::scheme::arena::TRUE_ID
6918 );
6919
6920 let one = arena.int(1);
6921 assert_eq!(
6922 null(&arena, &[one]).unwrap(),
6923 crate::scheme::arena::FALSE_ID
6924 );
6925 }
6926
6927 #[test]
6928 fn test_equal() {
6929 let mut arena = Arena::new();
6930
6931 let one1 = arena.int(1);
6933 let one2 = arena.int(1);
6934 assert_eq!(
6935 equal(&arena, &[one1, one2]).unwrap(),
6936 crate::scheme::arena::TRUE_ID
6937 );
6938
6939 let two = arena.int(2);
6941 assert_eq!(
6942 equal(&arena, &[one1, two]).unwrap(),
6943 crate::scheme::arena::FALSE_ID
6944 );
6945
6946 let list1 = arena.cons(one1, NIL_ID);
6948 let list2 = arena.cons(one2, NIL_ID);
6949 assert_eq!(
6950 equal(&arena, &[list1, list2]).unwrap(),
6951 crate::scheme::arena::TRUE_ID
6952 );
6953 }
6954}