1use crate::{
2  builtins,
3  builtins::records::find_in_fields,
4  call_stack,
5  call_stack::CallStackList,
6  data::{cirru, edn},
7  primes,
8  primes::{gen_core_id, Calcit, CalcitErr, CalcitItems, CrListWrap},
9  runner,
10  util::number::f64_to_usize,
11};
12
13use cirru_edn::EdnKwd;
14use cirru_parser::{Cirru, CirruWriterOptions};
15
16use std::cmp::Ordering;
17use std::sync::atomic::AtomicUsize;
18use std::sync::{atomic, Arc};
19
20static SYMBOL_INDEX: AtomicUsize = AtomicUsize::new(0);
21static JS_SYMBOL_INDEX: AtomicUsize = AtomicUsize::new(0);
22
23pub fn type_of(xs: &CalcitItems) -> Result<Calcit, CalcitErr> {
24  if xs.len() != 1 {
25    return CalcitErr::err_str(format!("type-of expected 1 argument, got: {:?}", xs));
26  }
27  match &xs[0] {
28    Calcit::Nil => Ok(Calcit::kwd("nil")),
29    Calcit::Bool(..) => Ok(Calcit::kwd("bool")),
31    Calcit::Number(..) => Ok(Calcit::kwd("number")),
32    Calcit::Symbol { .. } => Ok(Calcit::kwd("symbol")),
33    Calcit::Keyword(..) => Ok(Calcit::kwd("keyword")),
34    Calcit::Str(..) => Ok(Calcit::kwd("string")),
35    Calcit::Thunk(..) => Ok(Calcit::kwd("thunk")), Calcit::Ref(..) => Ok(Calcit::kwd("ref")),
37    Calcit::Tuple(..) => Ok(Calcit::kwd("tuple")),
38    Calcit::Buffer(..) => Ok(Calcit::kwd("buffer")),
39    Calcit::Recur(..) => Ok(Calcit::kwd("recur")),
40    Calcit::List(..) => Ok(Calcit::kwd("list")),
41    Calcit::Set(..) => Ok(Calcit::kwd("set")),
42    Calcit::Map(..) => Ok(Calcit::kwd("map")),
43    Calcit::Record(..) => Ok(Calcit::kwd("record")),
44    Calcit::Proc(..) => Ok(Calcit::kwd("fn")), Calcit::Macro { .. } => Ok(Calcit::kwd("macro")),
46    Calcit::Fn { .. } => Ok(Calcit::kwd("fn")),
47    Calcit::Syntax(..) => Ok(Calcit::kwd("synta")),
48  }
49}
50
51pub fn recur(xs: &CalcitItems) -> Result<Calcit, CalcitErr> {
52  Ok(Calcit::Recur(xs.to_owned()))
53}
54
55pub fn format_to_lisp(xs: &CalcitItems) -> Result<Calcit, CalcitErr> {
56  match xs.get(0) {
57    Some(v) => Ok(Calcit::Str(v.lisp_str().into())),
58    None => CalcitErr::err_str("format-to-lisp expected 1 argument"),
59  }
60}
61
62pub fn format_to_cirru(xs: &CalcitItems) -> Result<Calcit, CalcitErr> {
63  match xs.get(0) {
64    Some(v) => cirru_parser::format(&[transform_code_to_cirru(v)], CirruWriterOptions { use_inline: false })
65      .map(|s| Calcit::Str(s.into()))
66      .map_err(CalcitErr::use_str),
67    None => CalcitErr::err_str("format-to-cirru expected 1 argument"),
68  }
69}
70
71fn transform_code_to_cirru(x: &Calcit) -> Cirru {
72  match x {
73    Calcit::List(ys) => {
74      let mut xs: Vec<Cirru> = Vec::with_capacity(ys.len());
75      for y in ys {
76        xs.push(transform_code_to_cirru(y));
77      }
78      Cirru::List(xs)
79    }
80    Calcit::Symbol { sym, .. } => Cirru::Leaf((**sym).into()),
81    Calcit::Syntax(s, _ns) => Cirru::Leaf(s.to_string().into()),
82    Calcit::Proc(s) => Cirru::Leaf((**s).into()),
83    a => Cirru::leaf(format!("{}", a)),
84  }
85}
86
87pub fn gensym(xs: &CalcitItems) -> Result<Calcit, CalcitErr> {
88  let idx = SYMBOL_INDEX.fetch_add(1, atomic::Ordering::SeqCst);
89  let n = idx + 1; let s = if xs.is_empty() {
92    let mut chunk = String::from("G__");
93    chunk.push_str(&n.to_string());
94    chunk
95  } else {
96    match &xs[0] {
97      Calcit::Str(s) | Calcit::Symbol { sym: s, .. } => {
98        let mut chunk = (**s).to_string();
99        chunk.push('_');
100        chunk.push('_');
101        chunk.push_str(&n.to_string());
102        chunk
103      }
104      Calcit::Keyword(s) => {
105        let mut chunk = s.to_string();
106        chunk.push('_');
107        chunk.push('_');
108        chunk.push_str(&n.to_string());
109        chunk
110      }
111      a => return CalcitErr::err_str(format!("gensym expected a string, but got: {}", a)),
112    }
113  };
114  Ok(Calcit::Symbol {
115    sym: s.into(),
116    ns: primes::GEN_NS.to_owned(),
117    at_def: primes::GEN_DEF.to_owned(),
118    resolved: None,
119  })
120}
121
122pub fn reset_gensym_index(_xs: &CalcitItems) -> Result<Calcit, CalcitErr> {
123  let _ = SYMBOL_INDEX.swap(0, atomic::Ordering::SeqCst);
124  Ok(Calcit::Nil)
125}
126
127pub fn force_reset_gensym_index() -> Result<(), String> {
128  let _ = SYMBOL_INDEX.swap(0, atomic::Ordering::SeqCst);
129  Ok(())
130}
131
132pub fn reset_js_gensym_index() {
133  let _ = JS_SYMBOL_INDEX.swap(0, atomic::Ordering::SeqCst);
134}
135
136pub fn js_gensym(name: &str) -> String {
138  let idx = JS_SYMBOL_INDEX.fetch_add(1, atomic::Ordering::SeqCst);
139  let n = idx + 1; let mut chunk = String::from(name);
142  chunk.push_str("_AUTO_");
143  chunk.push_str(&n.to_string());
144  chunk
145}
146
147pub fn generate_id(xs: &CalcitItems) -> Result<Calcit, CalcitErr> {
149  let size = match xs.get(0) {
150    Some(Calcit::Number(n)) => match f64_to_usize(*n) {
151      Ok(size) => Some(size),
152      Err(e) => return CalcitErr::err_str(e),
153    },
154    Some(a) => return CalcitErr::err_str(format!("expected usize, got: {}", a)),
155    None => None, };
157
158  match (size, xs.get(1)) {
159    (None, None) => Ok(Calcit::Str(gen_core_id())),
160    (Some(_n), None) => Ok(Calcit::Str(gen_core_id())),
161    (Some(_n), Some(Calcit::Str(s))) => {
162      let mut charset: Vec<char> = Vec::with_capacity(s.len());
163      for c in s.chars() {
164        charset.push(c);
165      }
166      Ok(Calcit::Str(gen_core_id()))
167    }
168    (a, b) => CalcitErr::err_str(format!("generate-id! expected size or charset, got: {:?} {:?}", a, b)),
169  }
170}
171
172pub fn display_stack(_xs: &CalcitItems, call_stack: &CallStackList) -> Result<Calcit, CalcitErr> {
173  call_stack::show_stack(call_stack);
174  Ok(Calcit::Nil)
175}
176
177pub fn parse_cirru(xs: &CalcitItems) -> Result<Calcit, CalcitErr> {
178  match xs.get(0) {
179    Some(Calcit::Str(s)) => match cirru_parser::parse(s) {
180      Ok(nodes) => Ok(cirru::cirru_to_calcit(&Cirru::List(nodes))),
181      Err(e) => CalcitErr::err_str(format!("parse-cirru failed, {}", e)),
182    },
183    Some(a) => CalcitErr::err_str(format!("parse-cirru expected a string, got: {}", a)),
184    None => CalcitErr::err_str("parse-cirru expected 1 argument"),
185  }
186}
187
188pub fn format_cirru(xs: &CalcitItems) -> Result<Calcit, CalcitErr> {
189  match xs.get(0) {
190    Some(a) => {
191      let options = cirru_parser::CirruWriterOptions { use_inline: false };
192      match cirru::calcit_data_to_cirru(a) {
193        Ok(v) => {
194          if let Cirru::List(ys) = v {
195            Ok(Calcit::Str(cirru_parser::format(&ys, options)?.into()))
196          } else {
197            CalcitErr::err_str(format!("expected vector for Cirru formatting: {}", v))
198          }
199        }
200        Err(e) => CalcitErr::err_str(format!("format-cirru failed, {}", e)),
201      }
202    }
203    None => CalcitErr::err_str("parse-cirru expected 1 argument"),
204  }
205}
206
207pub fn parse_cirru_edn(xs: &CalcitItems) -> Result<Calcit, CalcitErr> {
208  match xs.get(0) {
209    Some(Calcit::Str(s)) => match cirru_edn::parse(s) {
210      Ok(nodes) => Ok(edn::edn_to_calcit(&nodes)),
211      Err(e) => CalcitErr::err_str(format!("parse-cirru-edn failed, {}", e)),
212    },
213    Some(a) => CalcitErr::err_str(format!("parse-cirru-edn expected a string, got: {}", a)),
214    None => CalcitErr::err_str("parse-cirru-edn expected 1 argument"),
215  }
216}
217
218pub fn format_cirru_edn(xs: &CalcitItems) -> Result<Calcit, CalcitErr> {
219  match xs.get(0) {
220    Some(a) => Ok(Calcit::Str(cirru_edn::format(&edn::calcit_to_edn(a)?, true)?.into())),
221    None => CalcitErr::err_str("format-cirru-edn expected 1 argument"),
222  }
223}
224
225pub fn turn_symbol(xs: &CalcitItems) -> Result<Calcit, CalcitErr> {
226  if xs.len() != 1 {
227    return CalcitErr::err_str(format!("turn-symbol expected 1 argument, got: {:?}", xs));
228  }
229  match &xs[0] {
230    Calcit::Str(s) => Ok(Calcit::Symbol {
231      sym: s.to_owned(),
232      ns: primes::GEN_NS.to_owned(),
233      at_def: primes::GEN_DEF.to_owned(),
234      resolved: None,
235    }),
236    Calcit::Keyword(s) => Ok(Calcit::Symbol {
237      sym: s.to_string().into(),
238      ns: primes::GEN_NS.to_owned(),
239      at_def: primes::GEN_DEF.to_owned(),
240      resolved: None,
241    }),
242    Calcit::Symbol { sym, ns, at_def, resolved } => Ok(Calcit::Symbol {
243      sym: sym.to_owned(),
244      ns: ns.to_owned(),
245      at_def: at_def.to_owned(),
246      resolved: resolved.to_owned(),
247    }),
248    a => CalcitErr::err_str(format!("turn-symbol cannot turn this to symbol: {}", a)),
249  }
250}
251
252pub fn turn_keyword(xs: &CalcitItems) -> Result<Calcit, CalcitErr> {
253  if xs.len() != 1 {
254    return CalcitErr::err_str(format!("turn-keyword cannot turn this to keyword: {:?}", xs));
255  }
256  match &xs[0] {
257    Calcit::Str(s) => Ok(Calcit::kwd(s)),
258    Calcit::Keyword(s) => Ok(Calcit::Keyword(s.to_owned())),
259    Calcit::Symbol { sym, .. } => Ok(Calcit::kwd(sym)),
260    a => CalcitErr::err_str(format!("turn-keyword cannot turn this to keyword: {}", a)),
261  }
262}
263
264pub fn new_tuple(xs: &CalcitItems) -> Result<Calcit, CalcitErr> {
265  if xs.len() != 2 {
266    CalcitErr::err_str(format!("tuple expected 2 arguments, got {}", CrListWrap(xs.to_owned())))
267  } else {
268    Ok(Calcit::Tuple(Arc::new(xs[0].to_owned()), Arc::new(xs[1].to_owned())))
269  }
270}
271
272pub fn invoke_method(name: &str, invoke_args: &CalcitItems, call_stack: &CallStackList) -> Result<Calcit, CalcitErr> {
273  if invoke_args.is_empty() {
274    return Err(CalcitErr::use_msg_stack(
275      format!("expected operand for method invoking: {:?}", invoke_args),
276      call_stack,
277    ));
278  }
279  let value = invoke_args[0].to_owned();
280  let s0 = rpds::HashTrieMap::new_sync();
281  let class = match &invoke_args[0] {
282    Calcit::Tuple(a, _b) => (**a).to_owned(),
283    Calcit::List(..) => runner::evaluate_symbol("&core-list-class", &s0, primes::CORE_NS, call_stack)?,
285    Calcit::Map(..) => runner::evaluate_symbol("&core-map-class", &s0, primes::CORE_NS, call_stack)?,
286    Calcit::Number(..) => runner::evaluate_symbol("&core-number-class", &s0, primes::CORE_NS, call_stack)?,
287    Calcit::Str(..) => runner::evaluate_symbol("&core-string-class", &s0, primes::CORE_NS, call_stack)?,
288    Calcit::Set(..) => runner::evaluate_symbol("&core-set-class", &s0, primes::CORE_NS, call_stack)?,
289    Calcit::Record(..) => runner::evaluate_symbol("&core-record-class", &s0, primes::CORE_NS, call_stack)?,
290    Calcit::Nil => runner::evaluate_symbol("&core-nil-class", &s0, primes::CORE_NS, call_stack)?,
291    Calcit::Fn { .. } | Calcit::Proc(..) => runner::evaluate_symbol("&core-fn-class", &s0, primes::CORE_NS, call_stack)?,
292    x => return Err(CalcitErr::use_msg_stack(format!("cannot decide a class from: {:?}", x), call_stack)),
293  };
294  match &class {
295    Calcit::Record(_, fields, values) => {
296      match find_in_fields(fields, &EdnKwd::from(name)) {
297        Some(idx) => {
298          let method_args = invoke_args.drop_left().unshift(value);
299
300          match &values[idx] {
301            Calcit::Fn {
303              def_ns, scope, args, body, ..
304            } => runner::run_fn(&method_args, scope, args, body, def_ns.to_owned(), call_stack),
305            Calcit::Proc(proc) => builtins::handle_proc(proc, &method_args, call_stack),
306            Calcit::Syntax(syn, _ns) => Err(CalcitErr::use_msg_stack(
307              format!("cannot get syntax here since instance is always evaluated, got: {}", syn),
308              call_stack,
309            )),
310            y => Err(CalcitErr::use_msg_stack(
311              format!("expected a function to invoke, got: {}", y),
312              call_stack,
313            )),
314          }
315        }
316        None => {
317          let mut content = String::from("");
318          for k in &**fields {
319            content = format!("{},{}", content, k)
320          }
321          Err(CalcitErr::use_msg_stack(
322            format!("missing field `{}` in {}", name, content),
323            call_stack,
324          ))
325        }
326      }
327    }
328    x => Err(CalcitErr::use_msg_stack(
329      format!("method invoking expected a record as class, got: {}", x),
330      call_stack,
331    )),
332  }
333}
334
335pub fn native_compare(xs: &CalcitItems) -> Result<Calcit, CalcitErr> {
336  if xs.len() != 2 {
337    return CalcitErr::err_str(format!("&compare expected 2 values, got {:?}", xs));
338  }
339  match xs[0].cmp(&xs[1]) {
340    Ordering::Less => Ok(Calcit::Number(-1.0)),
341    Ordering::Greater => Ok(Calcit::Number(1.0)),
342    Ordering::Equal => Ok(Calcit::Number(0.0)),
343  }
344}
345
346pub fn tuple_nth(xs: &CalcitItems) -> Result<Calcit, CalcitErr> {
347  if xs.len() != 2 {
348    return CalcitErr::err_str(format!("&tuple:nth expected 2 argument, got: {}", CrListWrap(xs.to_owned())));
349  }
350  match (&xs[0], &xs[1]) {
351    (Calcit::Tuple(a, b), Calcit::Number(n)) => match f64_to_usize(*n) {
352      Ok(0) => Ok((**a).to_owned()),
353      Ok(1) => Ok((**b).to_owned()),
354      Ok(m) => CalcitErr::err_str(format!("Tuple only got 2 elements, trying to index with {}", m)),
355      Err(e) => CalcitErr::err_str(format!("&tuple:nth expect usize, {}", e)),
356    },
357    (a, b) => CalcitErr::err_str(format!("&tuple:nth expected a tuple and an index, got: {} {}", a, b)),
358  }
359}
360
361pub fn assoc(xs: &CalcitItems) -> Result<Calcit, CalcitErr> {
362  if xs.len() != 3 {
363    return CalcitErr::err_str(format!("tuple:assoc expected 3 arguments, got: {:?}", xs));
364  }
365  match (&xs[0], &xs[1]) {
366    (Calcit::Tuple(a0, a1), Calcit::Number(n)) => match f64_to_usize(*n) {
367      Ok(idx) => {
368        if idx == 0 {
369          Ok(Calcit::Tuple(Arc::new(xs[2].to_owned()), a1.to_owned()))
370        } else if idx == 1 {
371          Ok(Calcit::Tuple(a0.to_owned(), Arc::new(xs[2].to_owned())))
372        } else {
373          CalcitErr::err_str(format!("Tuple only has fields of 0,1 , unknown index: {}", idx))
374        }
375      }
376      Err(e) => CalcitErr::err_str(e),
377    },
378    (a, b, ..) => CalcitErr::err_str(format!("tuple:assoc expected a tuple, got: {} {}", a, b)),
379  }
380}
381
382pub fn no_op() -> Result<Calcit, CalcitErr> {
383  Ok(Calcit::Nil)
384}
385
386pub fn get_os(_xs: &CalcitItems) -> Result<Calcit, CalcitErr> {
387  Ok(Calcit::kwd(std::env::consts::OS))
389}
390
391pub fn async_sleep(xs: &CalcitItems, call_stack: &CallStackList) -> Result<Calcit, CalcitErr> {
392  use std::{thread, time};
393  let sec = if xs.is_empty() {
394    1.0
395  } else if let Calcit::Number(n) = xs[0] {
396    n
397  } else {
398    return Err(CalcitErr::use_msg_stack("expected number", call_stack));
399  };
400
401  runner::track::track_task_add();
402
403  let _handle = thread::spawn(move || {
404    let ten_secs = time::Duration::from_secs(sec.round() as u64);
405    thread::sleep(ten_secs);
407
408    runner::track::track_task_release();
409  });
410
411  Ok(Calcit::Nil)
414}
415
416pub fn format_ternary_tree(xs: &CalcitItems) -> Result<Calcit, CalcitErr> {
417  if xs.len() != 1 {
418    return CalcitErr::err_str(format!("&format-ternary-tree expected 1 argument, got: {:?}", xs));
419  }
420  match &xs[0] {
421    Calcit::List(ys) => Ok(Calcit::Str(ys.format_inline().into())),
422    a => CalcitErr::err_str(format!("&format-ternary-tree expected a list, got: {}", a)),
423  }
424}