1use crate::{
3 core::{
4 cons::Cons,
5 env::{Env, sym},
6 error::{Type, TypeError},
7 gc::{Context, Rt, Rto},
8 object::{
9 Function, Gc, HashTable, IntoObject, LispHashTable, LispString, LispVec, List,
10 ListType, NIL, Object, ObjectType, OptionalFlag, Symbol, WithLifetime,
11 },
12 },
13 data::aref,
14 library::filevercmp::filevercmp,
15 rooted_iter,
16};
17use anyhow::{Result, anyhow, bail, ensure};
18use base64::Engine;
19use fallible_iterator::FallibleIterator;
20use fallible_streaming_iterator::FallibleStreamingIterator;
21use rune_core::macros::{call, list, rebind, root};
22use rune_macros::{defun, elprop};
23
24#[defun]
25fn identity(arg: Object) -> Object {
26 arg
27}
28
29pub(crate) fn slice_into_list<'ob>(
30 slice: &[Object<'ob>],
31 tail: Option<Object<'ob>>,
32 cx: &'ob Context,
33) -> Object<'ob> {
34 let from_end = slice.iter().rev();
35 from_end.fold(tail.into(), |acc, obj| Cons::new(*obj, acc, cx).into())
36}
37
38pub(crate) fn build_list<'ob, E>(
39 mut iter: impl Iterator<Item = Result<Object<'ob>, E>>,
40 cx: &'ob Context,
41) -> Result<Object<'ob>, E> {
42 let Some(first) = iter.next() else { return Ok(NIL) };
43 let head = Cons::new1(first?, cx);
44 let mut prev = head;
45 for elem in iter {
46 let new = Cons::new1(elem?, cx);
47 prev.set_cdr(new.into()).unwrap();
48 prev = new;
49 }
50 Ok(head.into())
51}
52
53#[defun]
54pub(crate) fn eq(obj1: Object, obj2: Object) -> bool {
55 obj1.ptr_eq(obj2)
56}
57
58#[defun]
59pub(crate) fn equal<'ob>(obj1: Object<'ob>, obj2: Object<'ob>) -> bool {
60 obj1 == obj2
61}
62
63#[defun]
64pub(crate) fn eql<'ob>(obj1: Object<'ob>, obj2: Object<'ob>) -> bool {
65 match (obj1.untag(), obj2.untag()) {
66 (ObjectType::Float(f1), ObjectType::Float(f2)) => f1.to_bits() == f2.to_bits(),
67 _ => obj1.ptr_eq(obj2),
68 }
69}
70
71#[defun]
72fn equal_including_properties<'ob>(o1: Object<'ob>, o2: Object<'ob>) -> bool {
73 equal(o1, o2)
75}
76
77#[defun]
78pub fn plist_get<'ob>(plist: Object<'ob>, prop: Object<'ob>) -> Result<Object<'ob>> {
79 let Ok(plist) = Gc::<ListType>::try_from(plist) else { return Ok(NIL) };
80 let mut iter = plist.elements();
82 while let Some(cur_prop) = iter.next() {
83 let Some(value) = iter.next() else { return Ok(NIL) };
84 if eq(cur_prop?, prop) {
85 return Ok(value?);
86 }
87 }
88 Ok(NIL)
89}
90
91#[defun]
92fn plist_member<'ob>(
93 plist: Object<'ob>,
94 prop: Object<'ob>,
95 predicate: Option<Object>,
96) -> Result<Object<'ob>> {
97 ensure!(predicate.is_none(), "plist-member predicate support not implemented");
98 let plist: List = plist.try_into()?;
99 for (idx, value) in plist.conses().enumerate() {
100 if idx % 2 != 0 {
101 continue;
102 }
103 let value = value?;
104 if eq(value.car(), prop) {
105 return Ok(value.into());
106 }
107 }
108 Ok(NIL)
109}
110
111#[defun]
112pub(crate) fn prin1_to_string(object: Object, _noescape: Option<Object>) -> String {
113 format!("{object}")
114}
115
116#[defun]
117fn string_to_multibyte(string: &LispString) -> &LispString {
118 string
120}
121
122#[defun]
123fn string_search(needle: &str, haystack: &str, start_pos: Option<usize>) -> Option<usize> {
124 let start = start_pos.unwrap_or(0);
125 haystack[start..].find(needle).map(|x| x + start)
126}
127
128#[defun]
129pub(crate) fn mapcar<'ob>(
130 function: &Rto<Function>,
131 sequence: &Rto<Object>,
132 env: &mut Rt<Env>,
133 cx: &'ob mut Context,
134) -> Result<Object<'ob>> {
135 let sequence = sequence.bind(cx);
136 match sequence.untag() {
137 ObjectType::NIL => Ok(NIL),
138 ObjectType::Cons(cons) => {
139 rooted_iter!(iter, cons, cx);
140 root!(outputs, new(Vec), cx);
141 while let Some(obj) = iter.next()? {
142 let output = call!(function, obj; env, cx)?;
143 outputs.push(output);
144 }
145 Ok(slice_into_list(Rt::bind_slice(outputs, cx), None, cx))
147 }
148 ObjectType::ByteFn(fun) => {
149 let len = fun.len();
150 root!(fun, cx);
151 root!(outputs, new(Vec), cx);
152 for i in 0..len {
153 let val = fun.bind(cx).index(i, cx).unwrap();
154 let output = call!(function, val; env, cx)?;
155 outputs.push(output);
156 }
157 Ok(slice_into_list(Rt::bind_slice(outputs, cx), None, cx))
159 }
160 _ => Err(TypeError::new(Type::Sequence, sequence).into()),
161 }
162}
163
164#[defun]
165pub(crate) fn mapc<'ob>(
166 function: &Rto<Function>,
167 sequence: &Rto<List>,
168 env: &mut Rt<Env>,
169 cx: &'ob mut Context,
170) -> Result<Object<'ob>> {
171 match sequence.untag(cx) {
172 ListType::Nil => Ok(NIL),
173 ListType::Cons(cons) => {
174 rooted_iter!(elements, cons, cx);
175 while let Some(elem) = elements.next()? {
176 call!(function, elem; env, cx)?;
177 }
178 Ok(sequence.bind(cx).into())
179 }
180 }
181}
182
183#[defun]
184pub(crate) fn mapcan<'ob>(
185 function: &Rto<Function>,
186 sequence: &Rto<Object>,
187 env: &mut Rt<Env>,
188 cx: &'ob mut Context,
189) -> Result<Object<'ob>> {
190 let mapped = mapcar(function, sequence, env, cx)?;
191 let mut lists = Vec::new();
192 for list in mapped.as_list()? {
193 lists.push(list?.try_into()?);
194 }
195 nconc(&lists)
196}
197
198#[defun]
199pub(crate) fn mapconcat(
200 function: &Rto<Function>,
201 sequence: &Rto<Object>,
202 seperator: Option<&Rto<Gc<&LispString>>>,
203 env: &mut Rt<Env>,
204 cx: &mut Context,
205) -> Result<String> {
206 let mapped = rebind!(mapcar(function, sequence, env, cx)?);
207 let sep = match seperator {
208 Some(sep) => sep.bind(cx).untag(),
209 _ => "",
210 };
211 let mut string = String::new();
212 let mut first = true;
213 for element in mapped.as_list()? {
214 if first {
215 first = false;
216 } else {
217 string.push_str(sep);
218 }
219 let element: &str = element?.try_into()?;
220 string.push_str(element);
221 }
222 Ok(string)
223}
224
225#[defun]
226pub(crate) fn nreverse(seq: List) -> Result<Object> {
227 let mut prev = NIL;
228 for tail in seq.conses() {
229 let tail = tail?;
230 tail.set_cdr(prev)?;
231 prev = tail.into();
232 }
233 Ok(prev)
234}
235
236#[defun]
237pub(crate) fn reverse<'ob>(seq: List, cx: &'ob Context) -> Result<Object<'ob>> {
238 let mut tail = NIL;
239 for elem in seq {
240 tail = Cons::new(elem?, tail, cx).into();
241 }
242 Ok(tail)
243}
244
245#[defun]
246pub(crate) fn nconc<'ob>(lists: &[List<'ob>]) -> Result<Object<'ob>> {
247 let mut tail: Option<&Cons> = None;
248 for list in lists {
249 if let Some(cons) = tail {
250 cons.set_cdr((*list).into())?;
251 }
252 if let Some(last) = list.conses().last() {
253 tail = Some(last?);
254 }
255 }
256
257 Ok(match lists.iter().find(|&&x| x != ListType::empty()) {
258 Some(x) => (*x).into(),
259 None => NIL,
260 })
261}
262
263fn join<'ob>(list: &mut Vec<Object<'ob>>, seq: List<'ob>) -> Result<()> {
264 if let Some(cons) = seq.cons() {
265 for elt in cons {
266 list.push(elt?);
267 }
268 }
269 Ok(())
270}
271
272#[defun]
273fn take<'ob>(n: i64, list: List<'ob>, cx: &'ob Context) -> Result<Object<'ob>> {
274 let Ok(n) = usize::try_from(n) else { return Ok(NIL) };
275 Ok(build_list(list.elements().take(n), cx)?)
276}
277
278#[defun]
279pub(crate) fn append<'ob>(
280 append: Object<'ob>,
281 sequences: &[Object<'ob>],
282 cx: &'ob Context,
283) -> Result<Object<'ob>> {
284 let mut list = Vec::new();
285 match append.untag() {
286 ObjectType::String(string) => {
287 for ch in string.chars() {
288 list.push((ch as i64).into());
289 }
290 }
291 ObjectType::ByteString(string) => {
292 for ch in string.iter() {
293 list.push((*ch as i64).into());
294 }
295 }
296 _ => join(&mut list, append.try_into()?)?,
297 }
298 for seq in sequences {
299 join(&mut list, (*seq).try_into()?)?;
300 }
301 Ok(slice_into_list(&list, None, cx))
303}
304
305#[defun]
306pub(crate) fn assq<'ob>(key: Object<'ob>, alist: List<'ob>) -> Result<Object<'ob>> {
307 for elem in alist {
308 if let Some(cons) = elem?.cons()
309 && eq(key, cons.car())
310 {
311 return Ok(cons.into());
312 }
313 }
314 Ok(NIL)
315}
316
317#[defun]
318fn rassq<'ob>(key: Object<'ob>, alist: List<'ob>) -> Result<Object<'ob>> {
319 for elem in alist {
320 if let Some(cons) = elem?.cons()
321 && eq(key, cons.cdr())
322 {
323 return Ok(cons.into());
324 }
325 }
326 Ok(NIL)
327}
328
329#[defun]
330pub(crate) fn assoc<'ob>(
331 key: &Rto<Object<'ob>>,
332 alist: &Rto<List<'ob>>,
333 testfn: Option<&Rto<Object>>,
334 cx: &'ob mut Context,
335 env: &mut Rt<Env>,
336) -> Result<Object<'ob>> {
337 match testfn {
338 Some(x) => {
339 let func: Function = x.bind(cx).try_into()?;
340 root!(func, cx);
341 rooted_iter!(iter, alist, cx);
342 while let Some(elem) = iter.next()? {
343 if let Some(cons) = elem.bind(cx).cons() {
344 let val = cons.car();
345 root!(cons, cx);
346 let result = call!(func, key, val; env, cx)?;
347 if result != NIL {
348 return Ok(cons.bind(cx).into());
349 }
350 }
351 }
352 }
353 None => {
354 let alist = alist.bind(cx);
355 let key = key.bind(cx);
356 for elem in alist {
357 if let Some(cons) = elem?.cons()
358 && equal(key, cons.car())
359 {
360 return Ok(cons.into());
361 }
362 }
363 }
364 };
365 Ok(NIL)
366}
367
368type EqFunc = for<'ob> fn(Object<'ob>, Object<'ob>) -> bool;
369
370#[defun]
371fn copy_alist<'ob>(alist: List<'ob>, cx: &'ob Context) -> Result<Object<'ob>> {
372 match alist.untag() {
373 ListType::Nil => Ok(NIL),
374 ListType::Cons(cons) => {
375 let first = copy_alist_elem(cons.car(), cx);
376 let head = Cons::new1(first, cx);
377 let mut tail = head;
378
379 for elem in cons.cdr().as_list()? {
380 let elem = copy_alist_elem(elem?, cx);
381 let copy = Cons::new1(elem, cx);
382 tail.set_cdr(copy.into()).unwrap();
383 tail = copy;
384 }
385 Ok(head.into())
386 }
387 }
388}
389
390fn copy_alist_elem<'ob>(elem: Object<'ob>, cx: &'ob Context) -> Object<'ob> {
391 match elem.untag() {
392 ObjectType::Cons(cons) => Cons::new(cons.car(), cons.cdr(), cx).into(),
393 _ => elem,
394 }
395}
396
397fn delete_from_list<'ob>(elt: Object<'ob>, list: List<'ob>, eq_fn: EqFunc) -> Result<Object<'ob>> {
398 let mut head = list.into();
399 let mut prev: Option<&'ob Cons> = None;
400 for tail in list.conses() {
401 let tail = tail?;
402 if eq_fn(tail.car(), elt) {
403 if let Some(prev_tail) = &mut prev {
404 prev_tail.set_cdr(tail.cdr())?;
405 } else {
406 head = tail.cdr();
407 }
408 } else {
409 prev = Some(tail);
410 }
411 }
412 Ok(head)
413}
414
415#[defun]
416pub(crate) fn delete<'ob>(elt: Object<'ob>, list: List<'ob>) -> Result<Object<'ob>> {
417 delete_from_list(elt, list, equal)
418}
419
420#[defun]
421pub(crate) fn delq<'ob>(elt: Object<'ob>, list: List<'ob>) -> Result<Object<'ob>> {
422 delete_from_list(elt, list, eq)
423}
424
425fn member_of_list<'ob>(elt: Object<'ob>, list: List<'ob>, eq_fn: EqFunc) -> Result<Object<'ob>> {
426 let val = list.conses().fallible().find(|x| Ok(eq_fn(x.car(), elt)))?;
427 match val {
428 Some(elem) => Ok(elem.into()),
429 None => Ok(NIL),
430 }
431}
432
433#[defun]
434pub(crate) fn memq<'ob>(elt: Object<'ob>, list: List<'ob>) -> Result<Object<'ob>> {
435 member_of_list(elt, list, eq)
436}
437
438#[defun]
439pub(crate) fn memql<'ob>(elt: Object<'ob>, list: List<'ob>) -> Result<Object<'ob>> {
440 member_of_list(elt, list, eql)
441}
442
443#[defun]
444pub(crate) fn member<'ob>(elt: Object<'ob>, list: List<'ob>) -> Result<Object<'ob>> {
445 member_of_list(elt, list, equal)
446}
447
448#[defun]
450fn sort<'ob>(
451 seq: &Rto<List>,
452 predicate: &Rto<Function>,
453 env: &mut Rt<Env>,
454 cx: &'ob mut Context,
455) -> Result<Object<'ob>> {
456 let vec: Vec<_> = seq.bind(cx).elements().fallible().collect()?;
457 if vec.len() <= 1 {
458 return Ok(seq.bind(cx).into());
459 }
460 root!(vec, cx);
461 let mut err = None;
462 vec.sort_by(|a, b| {
464 use std::cmp::Ordering;
465 if err.is_some() {
466 return Ordering::Equal;
469 }
470 let result = call!(predicate, a, b; env, cx);
471 match result {
472 Ok(x) if x == NIL => Ordering::Greater,
473 Ok(_) => Ordering::Less,
474 Err(e) => {
475 err = Some(e.into());
476 Ordering::Equal
477 }
478 }
479 });
480 match err {
481 Some(e) => Err(e),
482 None => Ok(slice_into_list(Rt::bind_slice(vec, cx), None, cx)),
483 }
484}
485
486#[defun]
487pub(crate) fn defvaralias<'ob>(
488 new_alias: Symbol<'ob>,
489 _base_variable: Symbol,
490 _docstring: Option<&str>,
491) -> Symbol<'ob> {
492 new_alias
494}
495
496#[defun]
497pub(crate) fn featurep(_feature: Symbol, _subfeature: Option<Symbol>) {}
499
500#[defun]
501pub(crate) fn require<'ob>(
502 feature: &Rto<Gc<Symbol>>,
503 filename: Option<&Rto<Gc<&LispString>>>,
504 noerror: OptionalFlag,
505 env: &mut Rt<Env>,
506 cx: &'ob mut Context,
507) -> Result<Symbol<'ob>> {
508 let feat = unsafe { feature.untag(cx).with_lifetime() };
510 if crate::data::FEATURES.lock().unwrap().contains(&feat) {
511 return Ok(feature.untag(cx));
512 }
513 let file = match filename {
514 Some(file) => file.untag(cx),
515 None => feature.untag(cx).get().name(),
516 };
517 let file = file.into_obj(cx);
518 root!(file, cx);
519 match crate::lread::load(file, noerror, None, cx, env) {
520 Ok(_) => Ok(feature.untag(cx)),
521 Err(e) => Err(e),
522 }
523}
524
525#[defun]
526pub(crate) fn concat(sequences: &[Object]) -> Result<String> {
527 let mut concat = String::new();
528 for elt in sequences {
529 match elt.untag() {
530 ObjectType::String(string) => concat += string,
531 ObjectType::NIL => continue,
532 _ => bail!("Currently only concatenating strings are supported"),
533 }
534 }
535 Ok(concat)
536}
537
538#[defun]
539pub(crate) fn vconcat<'ob>(sequences: &[Object], cx: &'ob Context) -> Result<Gc<&'ob LispVec>> {
540 let mut concated: Vec<Object> = Vec::new();
541 for elt in sequences {
542 match elt.untag() {
543 ObjectType::String(string) => {
545 for chr in string.chars() {
546 concated.push((chr as i64).into());
547 }
548 }
549 ObjectType::Cons(cons) => {
550 for x in cons {
551 concated.push(x?);
552 }
553 }
554 ObjectType::Vec(vec) => {
555 for x in vec.iter() {
556 concated.push(x.get());
557 }
558 }
559 ObjectType::NIL => {}
560 obj => bail!(TypeError::new(Type::Sequence, obj)),
561 }
562 }
563 Ok(concated.into_obj(cx))
564}
565
566#[defun]
567pub(crate) fn length(sequence: Object) -> Result<usize> {
568 let size = match sequence.untag() {
569 ObjectType::Cons(x) => x.elements().len()?,
570 ObjectType::Vec(x) => x.len(),
571 ObjectType::String(x) => x.len(),
572 ObjectType::ByteString(x) => x.len(),
573 ObjectType::ByteFn(x) => x.len(),
574 ObjectType::NIL => 0,
575 obj => bail!(TypeError::new(Type::Sequence, obj)),
576 };
577 Ok(size)
578}
579
580#[defun]
581pub(crate) fn safe_length(sequence: Object) -> usize {
582 match sequence.untag() {
583 ObjectType::Cons(cons) => cons.elements().take_while(|x| x.is_ok()).count(),
584 _ => 0,
585 }
586}
587
588#[defun]
589pub(crate) fn proper_list_p(object: Object) -> Option<usize> {
590 match object.untag() {
591 ObjectType::Cons(x) => x.elements().len().ok(),
592 _ => None,
593 }
594}
595
596#[defun]
597pub(crate) fn nth(n: usize, list: List) -> Result<Object> {
598 Ok(list.elements().fallible().nth(n)?.unwrap_or_default())
599}
600
601#[defun]
602pub(crate) fn nthcdr(n: usize, list: List) -> Result<Object> {
603 let ListType::Cons(mut cons) = list.untag() else { return Ok(NIL) };
604 let mut tail = list.as_obj_copy();
605 for _ in 0..n {
606 tail = cons.cdr();
607 if let Some(next) = tail.cons() {
608 cons = next;
609 } else {
610 break;
611 }
612 }
613 Ok(tail)
614}
615
616#[defun]
617pub(crate) fn elt<'ob>(sequence: Object<'ob>, n: usize, cx: &'ob Context) -> Result<Object<'ob>> {
618 match sequence.untag() {
619 ObjectType::Cons(x) => nth(n, x.into()),
620 ObjectType::NIL => Ok(NIL),
621 ObjectType::Vec(x) => aref(x.into(), n, cx),
622 ObjectType::Record(x) => aref(x.into(), n, cx),
623 ObjectType::String(x) => aref(x.into(), n, cx),
624 ObjectType::ByteFn(x) => aref(x.into(), n, cx),
625 other => Err(TypeError::new(Type::Sequence, other).into()),
626 }
627}
628
629#[defun]
630pub(crate) fn string_equal<'ob>(s1: Object<'ob>, s2: Object<'ob>) -> Result<bool> {
631 let s1 = match s1.untag() {
632 ObjectType::String(x) => x.as_bytes(),
633 ObjectType::ByteString(x) => x.inner(),
634 ObjectType::Symbol(x) => x.get().as_bytes(),
635 _ => bail!(TypeError::new(Type::String, s1)),
636 };
637 let s2 = match s2.untag() {
638 ObjectType::String(x) => x.as_bytes(),
639 ObjectType::ByteString(x) => x.inner(),
640 ObjectType::Symbol(x) => (x.get()).as_bytes(),
641 _ => bail!(TypeError::new(Type::String, s2)),
642 };
643
644 Ok(s1 == s2)
645}
646
647#[defun]
648pub(crate) fn compare_strings<'ob>(
649 string1: &str,
650 start1: Object<'ob>,
651 end1: Object<'ob>,
652 string2: &str,
653 start2: Object<'ob>,
654 end2: Object<'ob>,
655 ignore_case: OptionalFlag,
656) -> Result<Object<'ob>> {
657 let start1 = match start1.untag() {
658 ObjectType::Int(x) => x,
659 ObjectType::NIL => 0,
660 _ => bail!(TypeError::new(Type::Int, start1)),
661 };
662 let end1 = match end1.untag() {
663 ObjectType::Int(x) => x,
664 ObjectType::NIL => string1.chars().count() as i64,
665 _ => bail!(TypeError::new(Type::Int, end1)),
666 };
667
668 if end1 < start1 {
669 bail!("Args out of range: {string1}, {start1}, {end1}");
670 }
671
672 let s1 = string1.chars().skip(start1 as usize).take((end1 - start1) as usize);
673
674 let start2 = match start2.untag() {
675 ObjectType::Int(x) => x,
676 ObjectType::NIL => 0,
677 _ => bail!(TypeError::new(Type::Int, start2)),
678 };
679 let end2 = match end2.untag() {
680 ObjectType::Int(x) => x,
681 ObjectType::NIL => string2.chars().count() as i64,
682 _ => bail!(TypeError::new(Type::Int, end2)),
683 };
684
685 if end2 < start2 {
686 bail!("Args out of range: {string2}, {start2}, {end2}");
687 }
688 let s2 = string2.chars().skip(start2 as usize).take((end2 - start2) as usize);
690
691 let mut leading = 1;
692 for (c1, c2) in s1.zip(s2) {
693 let (c1, c2) = if ignore_case.is_some() {
694 (c1.to_uppercase().next().unwrap(), c2.to_uppercase().next().unwrap())
696 } else {
697 (c1, c2)
698 };
699
700 match c1.cmp(&c2) {
701 std::cmp::Ordering::Less => return Ok((-leading).into()),
702 std::cmp::Ordering::Greater => return Ok(leading.into()),
703 std::cmp::Ordering::Equal => {}
704 }
705 leading += 1;
706 }
707
708 Ok(true.into())
709}
710
711#[defun]
712pub(crate) fn string_distance(string1: &str, string2: &str, bytecompare: OptionalFlag) -> i64 {
713 if bytecompare.is_none() {
714 levenshtein_distance(string1.chars(), string2.chars())
715 } else {
716 levenshtein_distance(string1.as_bytes().iter(), string2.as_bytes().iter())
717 }
718}
719
720#[inline]
721pub(crate) fn levenshtein_distance<T: PartialEq, I: Iterator<Item = T>>(s1: I, s2: I) -> i64 {
722 use std::cmp::min;
723 let s = s1.collect::<Vec<_>>();
725 let t = s2.collect::<Vec<_>>();
726 let mut v0 = vec![0; t.len() + 1];
727 let mut v1 = vec![0; t.len() + 1];
728
729 for (i, v0i) in v0.iter_mut().enumerate() {
731 *v0i = i as i64;
732 }
733
734 for (i, si) in s.iter().enumerate() {
736 v1[0] = i as i64 + 1;
739
740 for (j, tj) in t.iter().enumerate() {
742 let deletion_cost = v0[j + 1] + 1;
743 let insertion_cost = v1[j] + 1;
744 let substitution_cost = v0[j] + if si == tj { 0 } else { 1 };
745 v1[j + 1] = min(deletion_cost, min(insertion_cost, substitution_cost));
746 }
747
748 std::mem::swap(&mut v0, &mut v1);
750 }
751 v0[t.len()]
753}
754
755#[defun]
756pub(crate) fn string_bytes(string: &str) -> usize {
757 string.len()
758}
759
760#[derive(Debug, Clone, Copy)]
761pub(crate) enum StringOrChar<'ob> {
762 String(&'ob str),
763 Char(u64),
764}
765
766impl<'ob> TryFrom<Object<'ob>> for StringOrChar<'ob> {
767 type Error = TypeError;
768
769 fn try_from(obj: Object<'ob>) -> Result<Self, Self::Error> {
770 match obj.untag() {
771 ObjectType::String(s) => Ok(Self::String(s)),
772 ObjectType::Int(c) if c >= 0 => Ok(Self::Char(c as u64)),
773 _ => Err(TypeError::new(Type::StringOrChar, obj)),
774 }
775 }
776}
777
778impl<'ob> From<&'ob str> for StringOrChar<'ob> {
779 fn from(s: &'ob str) -> Self {
780 Self::String(s)
781 }
782}
783
784impl From<char> for StringOrChar<'_> {
785 fn from(s: char) -> Self {
786 Self::Char(s as u64)
787 }
788}
789
790#[derive(Debug, Clone, Copy)]
791pub(crate) struct StringOrSymbol<'ob>(&'ob str);
792
793impl<'ob> TryFrom<Object<'ob>> for StringOrSymbol<'ob> {
794 type Error = TypeError;
795
796 fn try_from(obj: Object<'ob>) -> Result<Self, Self::Error> {
797 match obj.untag() {
798 ObjectType::String(x) => Ok(Self(x)),
799 ObjectType::Symbol(x) => Ok(Self(x.get().name())),
800 _ => Err(TypeError::new(Type::String, obj)),
801 }
802 }
803}
804
805#[defun]
806pub(crate) fn string_lessp<'ob>(
807 string1: StringOrSymbol<'ob>,
808 string2: StringOrSymbol<'ob>,
809) -> Result<bool> {
810 for (c1, c2) in string1.0.chars().zip(string2.0.chars()) {
811 if c1 != c2 {
812 return Ok(c1 < c2);
813 }
814 }
815 Ok(string1.0.len() < string2.0.len())
816}
817
818#[defun]
819pub(crate) fn string_version_lessp<'ob>(
820 string1: StringOrSymbol<'ob>,
821 string2: StringOrSymbol<'ob>,
822) -> Result<bool> {
823 Ok(filevercmp(string1.0.as_bytes(), string2.0.as_bytes()) == std::cmp::Ordering::Less)
824}
825
826#[defun]
827pub(crate) fn clear_string(string: &LispString) -> Result<Object<'_>> {
828 string.clear();
829 Ok(NIL)
830}
831
832defsym!(KW_TEST);
837defsym!(KW_DOCUMENTATION);
838
839#[defun]
840pub(crate) fn make_hash_table<'ob>(
841 keyword_args: &[Object<'ob>],
842 cx: &'ob Context,
843) -> Result<Object<'ob>> {
844 let kw_test_pos = keyword_args.iter().step_by(2).position(|&x| x == sym::KW_TEST);
845 if let Some(i) = kw_test_pos {
846 let Some(val) = keyword_args.get((i * 2) + 1) else {
847 bail!("Missing keyword value for :test")
848 };
849 if *val != sym::EQ && *val != sym::EQUAL && *val != sym::EQL {
850 bail!("only `eq' and `equal' keywords support for make-hash-table :test. Found {val}");
852 }
853 }
854 let map = HashTable::with_hasher(std::hash::BuildHasherDefault::default());
856 Ok(cx.add(map))
857}
858
859#[defun]
860pub(crate) fn hash_table_p(obj: Object) -> bool {
861 matches!(obj.untag(), ObjectType::HashTable(_))
862}
863
864#[defun]
865pub(crate) fn gethash<'ob>(
866 key: Object<'ob>,
867 table: &'ob LispHashTable,
868 dflt: Option<Object<'ob>>,
869) -> Option<Object<'ob>> {
870 match table.get(key) {
871 Some(x) => Some(x),
872 None => dflt,
873 }
874}
875
876#[defun]
877pub(crate) fn puthash<'ob>(
878 key: Object<'ob>,
879 value: Object<'ob>,
880 table: &'ob LispHashTable,
881) -> Object<'ob> {
882 table.insert(key, value);
883 value
884}
885
886#[defun]
887fn remhash(key: Object, table: &LispHashTable) -> Result<()> {
888 let Some(idx) = table.get_index_of(key) else { return Ok(()) };
889 let iter_idx = table.get_iter_index();
893 if idx < iter_idx {
894 table.set_iter_index(iter_idx - 1);
895 }
896 table.shift_remove(key);
898 Ok(())
899}
900
901#[defun]
902fn maphash(
903 function: &Rto<Function>,
904 table: &Rto<Gc<&LispHashTable>>,
905 env: &mut Rt<Env>,
906 cx: &mut Context,
907) -> Result<bool> {
908 let loop_idx = |table: &LispHashTable| {
909 let end = table.len();
910 let idx = table.get_iter_index();
911 table.set_iter_index(idx + 1);
912 if idx >= end { None } else { Some(idx) }
913 };
914
915 loop {
916 let (key, val) = {
917 let table = table.untag(cx);
918 let Some(idx) = loop_idx(table) else { break };
919 table.get_index(idx).unwrap()
920 };
921 let result = call!(function, key, val; env, cx);
922 if let Err(e) = result {
923 table.untag(cx).set_iter_index(0);
924 return Err(e.into());
925 }
926 }
927 table.untag(cx).set_iter_index(0);
928 Ok(false)
929}
930
931#[defun]
932fn copy_sequence<'ob>(arg: Object<'ob>, cx: &'ob Context) -> Result<Object<'ob>> {
933 match arg.untag() {
934 ObjectType::Vec(x) => Ok(cx.add(x.to_vec())),
935 ObjectType::Cons(x) => {
936 let mut elements = Vec::new();
938 let mut tail = None;
939 for cons in x.conses() {
940 let cons = cons?;
941 elements.push(cons.car());
942 if !matches!(cons.cdr().untag(), ObjectType::Cons(_)) {
943 tail = Some(cons.cdr());
944 }
945 }
946 Ok(slice_into_list(&elements, tail, cx))
947 }
948 ObjectType::String(x) => Ok(cx.add(x.to_owned())),
949 ObjectType::NIL => Ok(NIL),
950 _ => Err(TypeError::new(Type::Sequence, arg).into()),
951 }
952}
953
954#[defun]
955fn substring(string: &str, from: Option<usize>, to: Option<usize>) -> Result<String> {
956 if from.unwrap_or_default() > string.len() || to.unwrap_or_default() > string.len() {
957 bail!("substring args out of range for {string} : {from:?} {to:?}");
958 }
959 let new_string = match (from, to) {
960 (None, None) => string,
961 (None, Some(t)) => &string[..t],
962 (Some(f), None) => &string[f..],
963 (Some(f), Some(t)) => {
964 let range = if f > t { t..f } else { f..t };
965 &string[range]
966 }
967 };
968 Ok(new_string.to_owned())
969}
970
971defsym!(MD5);
972defsym!(SHA1);
973defsym!(SHA224);
974defsym!(SHA256);
975defsym!(SHA384);
976defsym!(SHA512);
977
978#[defun]
979fn secure_hash_algorithms<'ob>(cx: &'ob Context) -> Object<'ob> {
980 list![sym::MD5, sym::SHA1, sym::SHA224, sym::SHA256, sym::SHA384, sym::SHA512; cx]
985}
986
987#[defun]
988fn enable_debug() -> bool {
989 crate::debug::enable_debug();
990 true
991}
992
993#[defun]
994fn debug_enabled() -> bool {
995 crate::debug::debug_enabled()
996}
997
998#[defun]
999fn disable_debug() -> bool {
1000 crate::debug::disable_debug();
1001 false
1002}
1003
1004#[defun]
1009#[elprop("[\x00-\x7F]*", _)]
1010fn base64_encode_string(string: &str, line_break: OptionalFlag) -> Result<String> {
1011 if string.is_ascii() {
1012 Ok(base64_encode(string, line_break.is_some(), true, false))
1013 } else {
1014 Err(anyhow!("Multibyte character in data for base64 encoding"))
1015 }
1016}
1017
1018#[defun]
1024#[elprop("[\x00-\x7F]*", _)]
1025fn base64url_encode_string(string: &str, no_pad: OptionalFlag) -> Result<String> {
1026 if string.is_ascii() {
1027 Ok(base64_encode(string, false, no_pad.is_none(), true))
1028 } else {
1029 Err(anyhow!("Multibyte character in data for base64 encoding"))
1030 }
1031}
1032
1033fn base64_encode(string: &str, _line_break: bool, pad: bool, base64url: bool) -> String {
1034 let config = base64::engine::GeneralPurposeConfig::new().with_encode_padding(pad);
1035 let alphabets = if base64url { base64::alphabet::URL_SAFE } else { base64::alphabet::STANDARD };
1036 let engine = base64::engine::GeneralPurpose::new(&alphabets, config);
1037 engine.encode(string)
1038}
1039
1040#[cfg(test)]
1041mod test {
1042 use crate::{fns::levenshtein_distance, interpreter::assert_lisp};
1043
1044 #[test]
1045 fn test_base64_encode_string() {
1046 assert_lisp("(base64-encode-string \"hello\")", "\"aGVsbG8=\"");
1047 assert_lisp(
1048 "(base64-encode-string \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum\")",
1049 "\"TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdCwgc2VkIGRvIGVpdXNtb2QgdGVtcG9yIGluY2lkaWR1bnQgdXQgbGFib3JlIGV0IGRvbG9yZSBtYWduYSBhbGlxdWEuIFV0IGVuaW0gYWQgbWluaW0gdmVuaWFtLCBxdWlzIG5vc3RydWQgZXhlcmNpdGF0aW9uIHVsbGFtY28gbGFib3JpcyBuaXNpIHV0IGFsaXF1aXAgZXggZWEgY29tbW9kbyBjb25zZXF1YXQuIER1aXMgYXV0ZSBpcnVyZSBkb2xvciBpbiByZXByZWhlbmRlcml0IGluIHZvbHVwdGF0ZSB2ZWxpdCBlc3NlIGNpbGx1bSBkb2xvcmUgZXUgZnVnaWF0IG51bGxhIHBhcmlhdHVyLiBFeGNlcHRldXIgc2ludCBvY2NhZWNhdCBjdXBpZGF0YXQgbm9uIHByb2lkZW50LCBzdW50IGluIGN1bHBhIHF1aSBvZmZpY2lhIGRlc2VydW50IG1vbGxpdCBhbmltIGlkIGVzdCBsYWJvcnVt\"",
1050 );
1051 }
1053
1054 #[test]
1055 fn test_take() {
1056 assert_lisp("(take 2 '(1 2 3 4))", "(1 2)");
1057 }
1058
1059 #[test]
1060 fn test_delq() {
1061 assert_lisp("(delq 1 '(1 2 3 1 4 1))", "(2 3 4)");
1062 assert_lisp("(delq t '(t t t))", "nil");
1063 }
1064
1065 #[test]
1066 fn test_nthcdr() {
1067 assert_lisp("(nthcdr 0 nil)", "nil");
1068 assert_lisp("(nthcdr 3 nil)", "nil");
1069 assert_lisp("(nthcdr 0 '(1 2 3))", "(1 2 3)");
1070 assert_lisp("(nthcdr 1 '(1 2 3))", "(2 3)");
1071 assert_lisp("(nthcdr 2 '(1 2 3))", "(3)");
1072 assert_lisp("(nthcdr 3 '(1 2 3))", "nil");
1073 assert_lisp("(nthcdr 1 '(1 . 2))", "2");
1074 assert_lisp("(nthcdr 2 '(1 2 . 3))", "3");
1075 }
1076
1077 #[test]
1078 fn test_reverse() {
1079 assert_lisp("(nreverse nil)", "nil");
1080 assert_lisp("(nreverse '(1))", "(1)");
1081 assert_lisp("(nreverse '(1 2))", "(2 1)");
1082 assert_lisp("(nreverse '(1 2 3))", "(3 2 1)");
1083 assert_lisp("(nreverse '(1 2 3 4))", "(4 3 2 1)");
1084 }
1085
1086 #[test]
1087 fn test_nconc() {
1088 assert_lisp("(nconc nil)", "nil");
1089 assert_lisp("(nconc '(1 2))", "(1 2)");
1090 assert_lisp("(nconc '(1 2) '(3 4))", "(1 2 3 4)");
1091 assert_lisp("(nconc '(1 2) '(3 4) '(5 6))", "(1 2 3 4 5 6)");
1092 assert_lisp("(nconc nil '(1 2))", "(1 2)");
1093 assert_lisp("(nconc '(1 2) nil)", "(1 2)");
1094 }
1095
1096 #[test]
1097 fn test_append() {
1098 assert_lisp("(append \"hello\")", "(104 101 108 108 111)");
1099 }
1100
1101 #[test]
1102 fn test_assq() {
1103 assert_lisp("(assq 5 '((1 . 2) (3 . 4) (5 . 6)))", "(5 . 6)");
1104 assert_lisp("(assq 6 '((1 . 2) (3 . 4) (5 . 6)))", "nil");
1105 }
1106
1107 #[test]
1108 fn test_string_equal() {
1109 assert_lisp("(string-equal \"hello\" \"hello\")", "t");
1110 assert_lisp("(string-equal \"hello\" \"world\")", "nil");
1111 }
1112
1113 #[test]
1114 fn test_compare_strings() {
1115 assert_lisp("(compare-strings \"hello\" 0 6 \"hello\" 0 6)", "t");
1116 assert_lisp("(compare-strings \"hello\" 0 6 \"world\" 0 6)", "-1");
1117 assert_lisp("(compare-strings \"hello\" 0 6 \"HELLO\" 0 6 t)", "t");
1118 }
1119
1120 #[test]
1121 fn test_string_distance() {
1122 assert_lisp("(string-distance \"hello\" \"hello\")", "0");
1123 assert_lisp("(string-distance \"hello\" \"jello\")", "1");
1124 assert_lisp("(string-distance \"hello\" \"world\")", "4");
1125 }
1126
1127 #[test]
1128 fn test_levenstein_distance() {
1129 assert_eq!(4, levenshtein_distance("hello".chars(), "world".chars()));
1130 assert_eq!(3, levenshtein_distance("kitten".chars(), "sitting".chars()));
1131 assert_eq!(2, levenshtein_distance("book".chars(), "back".chars()));
1132 assert_eq!(5, levenshtein_distance("table".chars(), "dinner".chars()));
1133 assert_eq!(2, levenshtein_distance("person".chars(), "pardon".chars()));
1134 assert_eq!(1, levenshtein_distance("person".chars(), "persons".chars()));
1135 assert_eq!(4, levenshtein_distance("book".chars(), "".chars()));
1136 assert_eq!(4, levenshtein_distance("".chars(), "book".chars()));
1137 assert_eq!(0, levenshtein_distance("".chars(), "".chars()));
1138 assert_eq!(2, levenshtein_distance("Späße".chars(), "Spaß".chars()));
1139 assert_eq!(5, levenshtein_distance("さようなら".chars(), "こんにちは".chars()));
1140 assert_eq!(1, levenshtein_distance("さようなら".chars(), "さようなう".chars()));
1141 assert_eq!(4, levenshtein_distance("こんにちは".chars(), "こんにちは abc".chars()));
1142 assert_eq!(1, levenshtein_distance("༆༃ʘ".chars(), "༆˥ʘ".chars()));
1143 }
1144
1145 #[test]
1146 fn test_string_lessp() {
1147 assert_lisp("(string-lessp \"less\" \"less\")", "nil");
1150 assert_lisp("(string-lessp \"les\" \"less\")", "t");
1152 assert_lisp("(string-lessp \"less\" \"les\")", "nil");
1153 assert_lisp("(string-lessp \"abc\" \"bcd\")", "t");
1155 assert_lisp("(string-lessp \"abx\" \"abcd\")", "nil");
1156
1157 assert_lisp("(string-lessp 'less 'less)", "nil");
1160 assert_lisp("(string-lessp 'les 'less)", "t");
1162 assert_lisp("(string-lessp 'less 'les)", "nil");
1163 assert_lisp("(string-lessp 'abc 'bcd)", "t");
1165
1166 assert_lisp("(string-lessp 'less \"less\")", "nil");
1168 assert_lisp("(string-lessp 'les \"less\")", "t");
1169 }
1170
1171 #[test]
1172 #[cfg(not(miri))] fn test_string_version_lessp() {
1174 assert_lisp("(string-version-lessp \"less\" \"less\")", "nil");
1177 assert_lisp("(string-version-lessp \"les\" \"less\")", "t");
1179 assert_lisp("(string-version-lessp \"less\" \"les\")", "nil");
1180 assert_lisp("(string-version-lessp \"abc\" \"bcd\")", "t");
1182 assert_lisp("(string-version-lessp \"less1\" \"less1\")", "nil");
1184 assert_lisp("(string-version-lessp \"100a\" \"100b\")", "t");
1185 assert_lisp("(string-version-lessp \"1less\" \"less1\")", "t");
1187 assert_lisp("(string-version-lessp \"less12\" \"less1\")", "nil");
1189 assert_lisp("(string-version-lessp \"less1\" \"less12\")", "t");
1191 assert_lisp("(string-version-lessp \"133less1\" \"less12\")", "t");
1193 assert_lisp("(string-version-lessp \"112a\" \"512a\")", "t");
1195 assert_lisp("(string-version-lessp \"101a\" \"100b\")", "nil");
1197 assert_lisp("(string-version-lessp \"10\" \"0100\")", "t");
1199 assert_lisp("(string-version-lessp \"10a100\" \"0100a\")", "t");
1201 assert_lisp("(string-version-lessp \"a100\" \"0100a\")", "nil");
1202 assert_lisp("(string-version-lessp \"01\" \"1\")", "nil");
1203 assert_lisp("(string-version-lessp \"a1a10aa\" \"a1a0100\")", "t");
1204 assert_lisp("(string-version-lessp \"a1b2\" \"a1a3\")", "nil");
1205 assert_lisp("(string-version-lessp \".\" \".\")", "nil");
1206 assert_lisp("(string-version-lessp \"..\" \"..\")", "nil");
1207 assert_lisp("(string-version-lessp \"..\" \".a\")", "t");
1208 assert_lisp("(string-version-lessp \"101a\" \"100b\")", "nil");
1209 assert_lisp("(string-version-lessp \"10\" \"0100\")", "t");
1210 assert_lisp("(string-version-lessp \"001\" \"01\")", "nil");
1211 assert_lisp("(string-version-lessp \"a1a10\" \"a1a0100\")", "t");
1212 assert_lisp("(string-version-lessp \"A\" \":\")", "t");
1213 assert_lisp("(string-version-lessp \"\" \"\")", "nil");
1214 assert_lisp("(string-version-lessp \"\" \".\")", "t");
1215 assert_lisp("(string-version-lessp \".\" \".\")", "nil");
1216 assert_lisp("(string-version-lessp \".\" \"~\")", "t");
1217 assert_lisp("(string-version-lessp \"~\" \"a\")", "t");
1218
1219 assert_lisp("(string-version-lessp 'less 'less)", "nil");
1222 assert_lisp("(string-version-lessp 'les 'less)", "t");
1224 assert_lisp("(string-version-lessp 'less 'les)", "nil");
1225 assert_lisp("(string-version-lessp 'abc 'bcd)", "t");
1227 assert_lisp("(string-version-lessp 'less1 'less1)", "nil");
1229 assert_lisp("(string-version-lessp '100a '100b)", "t");
1230 assert_lisp("(string-version-lessp '1less 'less1)", "t");
1232 assert_lisp("(string-version-lessp 'less12 'less1)", "nil");
1234 assert_lisp("(string-version-lessp 'less1 'less12)", "t");
1236 assert_lisp("(string-version-lessp '133less1 'less12)", "t");
1238 assert_lisp("(string-version-lessp '101a '100b)", "nil");
1240 assert_lisp("(string-version-lessp '10a100 '0100a)", "t");
1243 assert_lisp("(string-version-lessp 'a100 '0100a)", "nil");
1244
1245 assert_lisp("(string-version-lessp 'less \"less\")", "nil");
1247 assert_lisp("(string-version-lessp 'less1 \"less10\")", "t");
1248 }
1249
1250 #[test]
1251 fn test_base64url_encode_string() {
1252 assert_lisp("(base64url-encode-string \"hello\")", "\"aGVsbG8=\"");
1253 assert_lisp("(base64url-encode-string \"hello\" nil)", "\"aGVsbG8=\"");
1254 assert_lisp("(base64url-encode-string \"hello\" t)", "\"aGVsbG8\"");
1255 assert_lisp("(base64url-encode-string \"hello\" 0)", "\"aGVsbG8\"");
1256 }
1257
1258 #[test]
1259 #[cfg(miri)]
1260 fn test_maphash() {
1261 assert_lisp(
1264 "(let ((h (make-hash-table))) (puthash 1 6 h) (puthash 2 8 h) (puthash 3 10 h) (maphash 'eq h))",
1265 "nil",
1266 );
1267 }
1268
1269 #[test]
1270 fn test_legnth() {
1271 assert_lisp("(length nil)", "0");
1272 assert_lisp("(length '(1 2 3))", "3");
1273 assert_lisp("(length \"hello\")", "5");
1274 assert_lisp("(length [1 2 3])", "3");
1275 assert_lisp("(safe-length '(1 . 2))", "1");
1276 assert_lisp("(safe-length '(1 2 3 . 4))", "3");
1277 assert_lisp("(safe-length 'foo)", "0");
1278 }
1279
1280 #[test]
1281 fn test_sort() {
1282 assert_lisp("(sort nil '<)", "nil");
1283 assert_lisp("(sort '(1) '<)", "(1)");
1284 assert_lisp("(sort '(2 1) '<)", "(1 2)");
1285 assert_lisp("(sort '(1 2 3) '<)", "(1 2 3)");
1286 assert_lisp("(sort '(3 2 1) '<)", "(1 2 3)");
1287 assert_lisp("(sort '(3 1 2) '<)", "(1 2 3)");
1288 assert_lisp("(sort '(1 2 3 4 5) '>)", "(5 4 3 2 1)");
1289 assert_lisp(
1290 "(sort '((1 . 1) (1 . 2) (1 . 3)) 'car-less-than-car)",
1291 "((1 . 1) (1 . 2) (1 . 3))",
1292 );
1293 assert_lisp("(condition-case nil (sort '(3 2 1) 'length) (error 7))", "7");
1294 }
1295
1296 #[test]
1297 fn test_copy_alist() {
1298 assert_lisp("(copy-alist '((1 . 2) (3 . 4) (5 . 6)))", "((1 . 2) (3 . 4) (5 . 6))");
1299 }
1300
1301 #[test]
1302 fn test_clear_string() {
1303 assert_lisp(
1304 "(let ((str \"test string\")) (clear-string str) str)",
1305 "\"\0\0\0\0\0\0\0\0\0\0\0\"",
1306 );
1307 assert_lisp("(let ((str \"\")) (clear-string str) str)", "\"\"");
1308 }
1309}