calcit/
primes.rs

1mod syntax_name;
2
3use core::cmp::Ord;
4use std::cmp::Eq;
5use std::cmp::Ordering;
6use std::cmp::Ordering::*;
7use std::fmt;
8use std::hash::{Hash, Hasher};
9use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
10use std::sync::Arc;
11
12use cirru_edn::EdnKwd;
13use im_ternary_tree::TernaryTreeList;
14
15static ID_GEN: AtomicUsize = AtomicUsize::new(0);
16
17pub use syntax_name::CalcitSyntax;
18
19use crate::call_stack::CallStackList;
20
21#[derive(Debug, Clone, PartialEq)]
22pub enum SymbolResolved {
23  ResolvedLocal,
24  ResolvedRaw, // raw syntax, no target
25  ResolvedDef {
26    ns: Arc<str>,
27    def: Arc<str>,
28    rule: Option<Arc<ImportRule>>,
29  }, // ns, def
30}
31
32/// defRule: ns def
33#[derive(Debug, Clone, PartialEq, Eq)]
34pub enum ImportRule {
35  NsAs(Arc<str>),                 // ns
36  NsReferDef(Arc<str>, Arc<str>), // ns, def
37  NsDefault(Arc<str>),            // ns, js only
38}
39
40// scope
41pub type CalcitScope = rpds::HashTrieMapSync<Arc<str>, Calcit>;
42pub type CalcitItems = TernaryTreeList<Calcit>;
43
44/// special types wraps vector of calcit data for displaying
45#[derive(Debug, Clone, PartialEq, PartialOrd)]
46pub struct CrListWrap(pub TernaryTreeList<Calcit>);
47
48#[derive(Debug, Clone)]
49pub enum Calcit {
50  Nil,
51  Bool(bool),
52  Number(f64),
53  Symbol {
54    sym: Arc<str>,
55    ns: Arc<str>,
56    at_def: Arc<str>,
57    resolved: Option<Arc<SymbolResolved>>,
58  }, // content, ns... so it has meta information
59  Keyword(EdnKwd),
60  Str(Arc<str>),
61  Thunk(Arc<Calcit>, Option<Arc<Calcit>>),
62  /// holding a path to its state
63  Ref(Arc<str>),
64  /// more tagged union type, more like an internal structure
65  Tuple(Arc<Calcit>, Arc<Calcit>),
66  ///  to be used by FFIs
67  Buffer(Vec<u8>),
68  /// not for data, but for recursion
69  Recur(CalcitItems),
70  List(CalcitItems),
71  Set(rpds::HashTrieSetSync<Calcit>),
72  Map(rpds::HashTrieMapSync<Calcit, Calcit>),
73  Record(EdnKwd, Arc<Vec<EdnKwd>>, Arc<Vec<Calcit>>), // usize of keyword id
74  Proc(Arc<str>),
75  Macro {
76    name: Arc<str>,           // name
77    def_ns: Arc<str>,         // ns
78    id: Arc<str>,             // an id
79    args: Arc<Vec<Arc<str>>>, // args
80    body: Arc<CalcitItems>,   // body
81  },
82  Fn {
83    name: Arc<str>,   // name
84    def_ns: Arc<str>, // ns
85    id: Arc<str>,     // an id
86    scope: Arc<CalcitScope>,
87    args: Arc<Vec<Arc<str>>>, // args
88    body: Arc<CalcitItems>,   // body
89  },
90  Syntax(CalcitSyntax, Arc<str>), // name, ns... notice that `ns` is a meta info
91}
92
93impl fmt::Display for Calcit {
94  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95    match self {
96      Calcit::Nil => f.write_str("nil"),
97      Calcit::Bool(v) => f.write_str(&format!("{}", v)),
98      Calcit::Number(n) => f.write_str(&format!("{}", n)),
99      Calcit::Symbol { sym, .. } => f.write_str(&format!("'{}", sym)),
100      Calcit::Keyword(s) => f.write_str(&format!(":{}", s)),
101      Calcit::Str(s) => {
102        if is_simple_str(s) {
103          write!(f, "|{}", s)
104        } else {
105          write!(f, "\"|{}\"", s.escape_default())
106        }
107      } // TODO, escaping choices
108      Calcit::Thunk(code, v) => match v {
109        Some(data) => f.write_str(&format!("(&thunk {} {})", data, code)),
110        None => f.write_str(&format!("(&thunk _ {})", code)),
111      },
112      Calcit::Ref(name) => f.write_str(&format!("(&ref {})", name)),
113      Calcit::Tuple(a, b) => f.write_str(&format!("(:: {} {})", a, b)),
114      Calcit::Buffer(buf) => {
115        f.write_str("(&buffer")?;
116        if buf.len() > 8 {
117          f.write_str(&format!(
118            " {} {} {} {} {} {} {} {} ..+{}",
119            buffer_bit_hex(buf[0]),
120            buffer_bit_hex(buf[1]),
121            buffer_bit_hex(buf[2]),
122            buffer_bit_hex(buf[3]),
123            buffer_bit_hex(buf[4]),
124            buffer_bit_hex(buf[5]),
125            buffer_bit_hex(buf[6]),
126            buffer_bit_hex(buf[7]),
127            buf.len() - 8
128          ))?;
129        } else {
130          for b in buf {
131            f.write_str(" ")?;
132            f.write_str(&buffer_bit_hex(b.to_owned()))?;
133          }
134        }
135        f.write_str(")")
136      }
137      Calcit::Recur(xs) => {
138        f.write_str("(&recur")?;
139        for x in xs {
140          f.write_str(&format!(" {}", x))?;
141        }
142        f.write_str(")")
143      }
144      Calcit::List(xs) => {
145        f.write_str("([]")?;
146        for x in xs {
147          f.write_str(&format!(" {}", x))?;
148        }
149        f.write_str(")")
150      }
151      Calcit::Set(xs) => {
152        f.write_str("(#{}")?;
153        for x in xs {
154          f.write_str(&format!(" {}", x))?;
155        }
156        f.write_str(")")
157      }
158      Calcit::Map(xs) => {
159        f.write_str("({}")?;
160        for (k, v) in xs {
161          f.write_str(&format!(" ({} {})", k, v))?;
162        }
163        f.write_str(")")?;
164        Ok(())
165      }
166      Calcit::Record(name, fields, values) => {
167        f.write_str(&format!("(%{{}} {}", Calcit::Keyword(name.to_owned())))?;
168        for idx in 0..fields.len() {
169          f.write_str(&format!(" ({} {})", Calcit::Keyword(fields[idx].to_owned()), values[idx]))?;
170        }
171        f.write_str(")")
172      }
173      Calcit::Proc(name) => f.write_str(&format!("(&proc {})", name)),
174      Calcit::Macro { name, args, body, .. } => {
175        f.write_str(&format!("(&macro {} (", name))?;
176        let mut need_space = false;
177        for a in &**args {
178          if need_space {
179            f.write_str(" ")?;
180          }
181          f.write_str(&**a)?;
182          need_space = true;
183        }
184        f.write_str(") (")?;
185        need_space = false;
186        for b in &**body {
187          if need_space {
188            f.write_str(" ")?;
189          }
190          f.write_str(&format_to_lisp(b))?;
191          need_space = true;
192        }
193        f.write_str("))")
194      }
195      Calcit::Fn { name, args, body, .. } => {
196        f.write_str(&format!("(&fn {} (", name))?;
197        let mut need_space = false;
198        for a in &**args {
199          if need_space {
200            f.write_str(" ")?;
201          }
202          f.write_str(&**a)?;
203          need_space = true;
204        }
205        f.write_str(") ")?;
206        need_space = false;
207        for b in &**body {
208          if need_space {
209            f.write_str(" ")?;
210          }
211          f.write_str(&format_to_lisp(b))?;
212          need_space = true;
213        }
214        f.write_str(")")
215      }
216      Calcit::Syntax(name, _ns) => f.write_str(&format!("(&syntax {})", name)),
217    }
218  }
219}
220
221fn is_simple_str(tok: &str) -> bool {
222  for c in tok.chars() {
223    if !matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '?' | '!' | '|') {
224      return false;
225    }
226  }
227  true
228}
229
230fn buffer_bit_hex(n: u8) -> String {
231  hex::encode(vec![n])
232}
233
234/// special types wraps vector of calcit data for displaying
235impl fmt::Display for CrListWrap {
236  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237    f.write_str(&format_to_lisp(&Calcit::List(self.0.to_owned()))) // TODO performance
238  }
239}
240
241/// display data into Lisp style for readability
242pub fn format_to_lisp(x: &Calcit) -> String {
243  match x {
244    Calcit::List(ys) => {
245      let mut s = String::from("(");
246      for (idx, y) in ys.into_iter().enumerate() {
247        if idx > 0 {
248          s.push(' ');
249        }
250        s.push_str(&format_to_lisp(y));
251      }
252      s.push(')');
253      s
254    }
255    Calcit::Symbol { sym, .. } => sym.to_string(),
256    Calcit::Syntax(s, _ns) => s.to_string(),
257    Calcit::Proc(s) => s.to_string(),
258    a => format!("{}", a),
259  }
260}
261
262impl Hash for Calcit {
263  fn hash<H>(&self, _state: &mut H)
264  where
265    H: Hasher,
266  {
267    match self {
268      Calcit::Nil => "nil:".hash(_state),
269      Calcit::Bool(v) => {
270        "bool:".hash(_state);
271        v.hash(_state);
272      }
273      Calcit::Number(n) => {
274        "number:".hash(_state);
275        // TODO https://stackoverflow.com/q/39638363/883571
276        (*n as usize).hash(_state)
277      }
278      Calcit::Symbol { sym, .. } => {
279        "symbol:".hash(_state);
280        sym.hash(_state);
281        // probaly no need, also won't be used in hashing
282        // ns.hash(_state);
283      }
284      Calcit::Keyword(s) => {
285        "keyword:".hash(_state);
286        s.hash(_state);
287      }
288      Calcit::Str(s) => {
289        "string:".hash(_state);
290        s.hash(_state);
291      }
292      Calcit::Thunk(v, _) => {
293        "quote:".hash(_state);
294        v.hash(_state);
295      }
296      Calcit::Ref(name) => {
297        "ref:".hash(_state);
298        name.hash(_state);
299      }
300      Calcit::Tuple(a, b) => {
301        "tuple:".hash(_state);
302        a.hash(_state);
303        b.hash(_state);
304      }
305      Calcit::Buffer(buf) => {
306        "buffer:".hash(_state);
307        buf.hash(_state);
308      }
309      Calcit::Recur(v) => {
310        "list:".hash(_state);
311        v.hash(_state);
312      }
313      Calcit::List(v) => {
314        "list:".hash(_state);
315        v.hash(_state);
316      }
317      Calcit::Set(v) => {
318        "set:".hash(_state);
319        // TODO order for set is stable
320        for x in v {
321          x.hash(_state)
322        }
323      }
324      Calcit::Map(v) => {
325        "map:".hash(_state);
326        // TODO order for map is not stable
327        for x in v {
328          x.hash(_state)
329        }
330      }
331      Calcit::Record(name, fields, values) => {
332        "record:".hash(_state);
333        name.hash(_state);
334        fields.hash(_state);
335        values.hash(_state);
336      }
337      Calcit::Proc(name) => {
338        "proc:".hash(_state);
339        name.hash(_state);
340      }
341      Calcit::Macro { id: gen_id, .. } => {
342        "macro:".hash(_state);
343        // name.hash(_state);
344        gen_id.hash(_state);
345      }
346      Calcit::Fn { id: gen_id, .. } => {
347        "fn:".hash(_state);
348        // name.hash(_state);
349        gen_id.hash(_state);
350      }
351      Calcit::Syntax(name, _ns) => {
352        "syntax:".hash(_state);
353        // syntax name can be used as identity
354        name.to_string().hash(_state); // TODO
355      }
356    }
357  }
358}
359
360impl Ord for Calcit {
361  fn cmp(&self, other: &Self) -> Ordering {
362    match (self, other) {
363      (Calcit::Nil, Calcit::Nil) => Equal,
364      (Calcit::Nil, _) => Less,
365      (_, Calcit::Nil) => Greater,
366
367      (Calcit::Bool(a), Calcit::Bool(b)) => a.cmp(b),
368      (Calcit::Bool(_), _) => Less,
369      (_, Calcit::Bool(_)) => Greater,
370
371      (Calcit::Number(a), Calcit::Number(b)) => {
372        if a < b {
373          Less
374        } else if a > b {
375          Greater
376        } else {
377          Equal
378        }
379      }
380      (Calcit::Number(_), _) => Less,
381      (_, Calcit::Number(_)) => Greater,
382
383      (Calcit::Symbol { sym: a, .. }, Calcit::Symbol { sym: b, .. }) => a.cmp(b),
384      (Calcit::Symbol { .. }, _) => Less,
385      (_, Calcit::Symbol { .. }) => Greater,
386
387      (Calcit::Keyword(a), Calcit::Keyword(b)) => a.cmp(b),
388      (Calcit::Keyword(_), _) => Less,
389      (_, Calcit::Keyword(_)) => Greater,
390
391      (Calcit::Str(a), Calcit::Str(b)) => a.cmp(b),
392      (Calcit::Str(_), _) => Less,
393      (_, Calcit::Str(_)) => Greater,
394
395      (Calcit::Thunk(a, _), Calcit::Thunk(b, _)) => a.cmp(b),
396      (Calcit::Thunk(_, _), _) => Less,
397      (_, Calcit::Thunk(_, _)) => Greater,
398
399      (Calcit::Ref(a), Calcit::Ref(b)) => a.cmp(b),
400      (Calcit::Ref(_), _) => Less,
401      (_, Calcit::Ref(_)) => Greater,
402
403      (Calcit::Tuple(a0, b0), Calcit::Tuple(a1, b1)) => match a0.cmp(a1) {
404        Equal => b0.cmp(b1),
405        v => v,
406      },
407      (Calcit::Tuple(_, _), _) => Less,
408      (_, Calcit::Tuple(_, _)) => Greater,
409
410      (Calcit::Buffer(buf1), Calcit::Buffer(buf2)) => buf1.cmp(buf2),
411      (Calcit::Buffer(..), _) => Less,
412      (_, Calcit::Buffer(..)) => Greater,
413
414      (Calcit::Recur(a), Calcit::Recur(b)) => a.cmp(b),
415      (Calcit::Recur(_), _) => Less,
416      (_, Calcit::Recur(_)) => Greater,
417
418      (Calcit::List(a), Calcit::List(b)) => a.cmp(b),
419      (Calcit::List(_), _) => Less,
420      (_, Calcit::List(_)) => Greater,
421
422      (Calcit::Set(a), Calcit::Set(b)) => match a.size().cmp(&b.size()) {
423        Equal => {
424          if a == b {
425            Equal
426          } else {
427            unreachable!("TODO sets are not cmp ed") // TODO
428          }
429        }
430        a => a,
431      },
432      (Calcit::Set(_), _) => Less,
433      (_, Calcit::Set(_)) => Greater,
434
435      (Calcit::Map(a), Calcit::Map(b)) => {
436        unreachable!("TODO maps are not cmp ed {:?} {:?}", a, b)
437        // TODO
438      }
439      (Calcit::Map(_), _) => Less,
440      (_, Calcit::Map(_)) => Greater,
441
442      (Calcit::Record(_name1, _fields1, _values1), Calcit::Record(_name2, _fields2, _values2)) => {
443        unreachable!("TODO records are not cmp ed") // TODO
444      }
445      (Calcit::Record(..), _) => Less,
446      (_, Calcit::Record(..)) => Greater,
447
448      (Calcit::Proc(a), Calcit::Proc(b)) => a.cmp(b),
449      (Calcit::Proc(_), _) => Less,
450      (_, Calcit::Proc(_)) => Greater,
451
452      (Calcit::Macro { id: a, .. }, Calcit::Macro { id: b, .. }) => a.cmp(b),
453      (Calcit::Macro { .. }, _) => Less,
454      (_, Calcit::Macro { .. }) => Greater,
455
456      (Calcit::Fn { id: a, .. }, Calcit::Fn { id: b, .. }) => a.cmp(b), // compared with nanoid
457      (Calcit::Fn { .. }, _) => Less,
458      (_, Calcit::Fn { .. }) => Greater,
459
460      (Calcit::Syntax(a, _), Calcit::Syntax(b, _)) => a.cmp(b),
461    }
462  }
463}
464
465impl PartialOrd for Calcit {
466  fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
467    Some(self.cmp(other))
468  }
469}
470
471impl Eq for Calcit {}
472
473impl PartialEq for Calcit {
474  fn eq(&self, other: &Self) -> bool {
475    match (self, other) {
476      (Calcit::Nil, Calcit::Nil) => true,
477      (Calcit::Bool(a), Calcit::Bool(b)) => a == b,
478      (Calcit::Number(a), Calcit::Number(b)) => a == b,
479      (Calcit::Symbol { sym: a, .. }, Calcit::Symbol { sym: b, .. }) => a == b,
480      (Calcit::Keyword(a), Calcit::Keyword(b)) => a == b,
481      (Calcit::Str(a), Calcit::Str(b)) => a == b,
482      (Calcit::Thunk(a, _), Calcit::Thunk(b, _)) => a == b,
483      (Calcit::Ref(a), Calcit::Ref(b)) => a == b,
484      (Calcit::Tuple(a, b), Calcit::Tuple(c, d)) => a == c && b == d,
485      (Calcit::Buffer(b), Calcit::Buffer(d)) => b == d,
486      (Calcit::List(a), Calcit::List(b)) => a == b,
487      (Calcit::Set(a), Calcit::Set(b)) => a == b,
488      (Calcit::Map(a), Calcit::Map(b)) => a == b,
489      (Calcit::Record(name1, fields1, values1), Calcit::Record(name2, fields2, values2)) => {
490        name1 == name2 && fields1 == fields2 && values1 == values2
491      }
492
493      // functions compared with nanoid
494      (Calcit::Proc(a), Calcit::Proc(b)) => a == b,
495      (Calcit::Macro { id: a, .. }, Calcit::Macro { id: b, .. }) => a == b,
496      (Calcit::Fn { id: a, .. }, Calcit::Fn { id: b, .. }) => a == b,
497      (Calcit::Syntax(a, _), Calcit::Syntax(b, _)) => a == b,
498      (_, _) => false,
499    }
500  }
501}
502
503pub const CORE_NS: &str = "calcit.core";
504pub const BUILTIN_CLASSES_ENTRY: &str = "&init-builtin-classes!";
505pub const GENERATED_NS: &str = "calcit.gen";
506pub const GENERATED_DEF: &str = "gen%";
507
508lazy_static! {
509  pub static ref GEN_NS: Arc<str> = GENERATED_NS.to_string().into();
510  pub static ref GEN_DEF: Arc<str> = GENERATED_DEF.to_string().into();
511  pub static ref GEN_CORE_NS: Arc<str> = CORE_NS.to_string().into();
512  pub static ref GEN_CLASS_ENTRY: Arc<str> = BUILTIN_CLASSES_ENTRY.to_string().into();
513}
514
515impl Calcit {
516  pub fn turn_string(&self) -> String {
517    match self {
518      Calcit::Nil => String::from(""),
519      Calcit::Str(s) => (**s).to_owned(),
520      _ => format!("{}", self),
521    }
522  }
523
524  pub fn lisp_str(&self) -> String {
525    format_to_lisp(self)
526  }
527
528  pub fn new_str<T: Into<String>>(s: T) -> Calcit {
529    Calcit::Str(s.into().into())
530  }
531
532  /// makes sure that keyword is from global dict, not created by fresh
533  pub fn kwd(s: &str) -> Self {
534    Calcit::Keyword(EdnKwd::from(s))
535  }
536}
537
538/// too naive id generator to be safe in WASM
539pub fn gen_core_id() -> Arc<str> {
540  let c = ID_GEN.fetch_add(1, SeqCst);
541  format!("gen_id_{}", c).into()
542}
543
544#[derive(Debug, Clone, PartialEq)]
545pub struct CalcitErr {
546  pub msg: String,
547  pub warnings: Vec<String>,
548  pub stack: rpds::ListSync<crate::call_stack::CalcitStack>,
549}
550
551impl fmt::Display for CalcitErr {
552  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
553    f.write_str(&self.msg)?;
554    if !self.warnings.is_empty() {
555      f.write_str("\n")?;
556      for w in &self.warnings {
557        writeln!(f, "{}", w)?;
558      }
559    }
560    Ok(())
561  }
562}
563
564impl From<String> for CalcitErr {
565  /// hope this does not add extra costs
566  fn from(msg: String) -> Self {
567    CalcitErr {
568      msg,
569      warnings: vec![],
570      stack: rpds::List::new_sync(),
571    }
572  }
573}
574
575impl CalcitErr {
576  pub fn use_str<T: Into<String>>(msg: T) -> Self {
577    CalcitErr {
578      msg: msg.into(),
579      warnings: vec![],
580      stack: rpds::List::new_sync(),
581    }
582  }
583  pub fn err_str<T: Into<String>>(msg: T) -> Result<Calcit, Self> {
584    Err(CalcitErr {
585      msg: msg.into(),
586      warnings: vec![],
587      stack: rpds::List::new_sync(),
588    })
589  }
590  pub fn use_msg_stack<T: Into<String>>(msg: T, stack: &CallStackList) -> Self {
591    CalcitErr {
592      msg: msg.into(),
593      warnings: vec![],
594      stack: stack.to_owned(),
595    }
596  }
597}