Skip to main content

clojure_reader/
parse.rs

1//! An EDN syntax parser in Rust.
2#![expect(clippy::inline_always)]
3
4use alloc::boxed::Box;
5use alloc::collections::{BTreeMap, BTreeSet};
6use alloc::vec::Vec;
7use core::mem::replace;
8use core::primitive::str;
9
10use crate::edn::Edn;
11use crate::error::{Code, Error};
12
13#[cfg(feature = "arbitrary-nums")]
14use bigdecimal::BigDecimal;
15#[cfg(feature = "arbitrary-nums")]
16use num_bigint::BigInt;
17#[cfg(feature = "floats")]
18use ordered_float::OrderedFloat;
19
20/// Possible kinds of an EDN node
21///
22/// **NOTE:** The vector of items in [`NodeKind::Set`] may contain duplicate items.
23/// **NOTE:** The vector of entries in [`NodeKind::Map`] may contain duplicate keys.
24#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
25#[non_exhaustive]
26pub enum NodeKind<'e> {
27  Vector(
28    Vec<Node<'e>>,
29    /* Any trailing discards inside vector, e.g. `[foo bar #_baz #_qux]` */ Vec<Discard<'e>>,
30  ),
31  Set(
32    Vec<Node<'e>>,
33    /* Any trailing discards inside set, e.g. `#{foo bar #_baz #_qux}` */ Vec<Discard<'e>>,
34  ),
35  Map(
36    Vec<(Node<'e>, Node<'e>)>,
37    /* Any trailing discards inside map, e.g. `{:foo bar #_baz #_qux}` */ Vec<Discard<'e>>,
38  ),
39  List(
40    Vec<Node<'e>>,
41    /* Any trailing discards inside list, e.g. `(foo bar #_baz #_qux)` */ Vec<Discard<'e>>,
42  ),
43  Key(&'e str),
44  Symbol(&'e str),
45  Str(&'e str),
46  Int(i64),
47  Tagged(&'e str, /* Span of the tag string */ Span, Box<Node<'e>>),
48  #[cfg(feature = "floats")]
49  Double(OrderedFloat<f64>),
50  Rational((i64, i64)),
51  #[cfg(feature = "arbitrary-nums")]
52  BigInt(BigInt),
53  #[cfg(feature = "arbitrary-nums")]
54  BigDec(BigDecimal),
55  Char(char),
56  Bool(bool),
57  #[default]
58  Nil,
59}
60
61/// A **discarded** form containing the node that was discarded
62#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
63pub struct Discard<'e>(pub Node<'e>, pub Span);
64
65/// Concrete EDN syntax tree.
66///
67/// Parse one with [`parse`], then convert it to an [`Edn`] with [`Edn::try_from`].
68#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
69pub struct Node<'e> {
70  pub kind: NodeKind<'e>,
71  pub span: Span,
72  pub leading_discards: Vec<Discard<'e>>,
73}
74
75impl<'e> Node<'e> {
76  /// Construct a `Node` with the given kind and span and no leading discards.
77  pub const fn no_discards(kind: NodeKind<'e>, span: Span) -> Self {
78    Self { kind, span, leading_discards: Vec::new() }
79  }
80
81  #[inline]
82  pub const fn span(&self) -> Span {
83    self.span
84  }
85}
86
87/// Parse a single `Node` from a [`SourceReader`], consuming that form.
88///
89/// # Examples
90///
91/// ```
92/// #[cfg(feature = "unstable")]
93/// {
94///   use clojure_reader::parse::{Node, NodeKind::*, SourceReader, parse};
95///
96///   let source = r#"
97/// (->> txs
98///   (keep :refund-amt)
99///   (reduce +))
100///   ; total refund amount
101/// "#;
102///   let mut reader = SourceReader::new(source);
103///   let Node { kind: node, .. } = parse(&mut reader).expect("failed to parse");
104///
105///   let List(nodes, _) = node else { panic!("unexpected") };
106///
107///   // Destruct main list
108///   let nodes: Vec<_> = nodes.into_iter().map(|n| n.kind).collect();
109///   let [Symbol("->>"), Symbol("txs"), List(keep, _), List(reduce, _)] = nodes.as_slice() else {
110///     panic!("unexpected");
111///   };
112///
113///   // Destruct the list calling `keep`
114///   let keep: Vec<_> = keep.into_iter().map(|n| &n.kind).collect();
115///   let [Symbol("keep"), Key("refund-amt")] = keep.as_slice() else {
116///     panic!("unexpected");
117///   };
118///
119///   // Destruct the list calling `reduce`
120///   let reduce: Vec<_> = reduce.into_iter().map(|n| &n.kind).collect();
121///   let [Symbol("reduce"), Symbol("+")] = reduce.as_slice() else {
122///     panic!("unexpected");
123///   };
124///
125///   assert_eq!(reader.remaining(), "\n  ; total refund amount\n");
126/// }
127/// ```
128///
129/// # Errors
130///
131/// See [`crate::error::Error`].
132#[cfg_attr(not(feature = "unstable"), expect(dead_code))]
133pub fn parse<'r, 'e: 'r>(reader: &'r mut SourceReader<'e>) -> Result<Node<'e>, Error> {
134  let start_pos = reader.read_pos;
135  let builder = NodeBuilder;
136  let parsed = {
137    let mut walker = Walker::new(reader);
138    parse_internal(&mut walker, &builder)?
139  };
140  Ok(parsed.unwrap_or_else(|| builder.nil(reader.span_from(start_pos))))
141}
142
143/// Parse the first EDN form from a string and return it with the unread remainder.
144///
145/// # Errors
146///
147/// See [`crate::error::Error`].
148pub fn parse_as_edn(edn: &str) -> Result<(Edn<'_>, &str), Error> {
149  let mut source_reader = SourceReader::new(edn);
150  let start_pos = source_reader.read_pos;
151  let builder = EdnBuilder;
152  let parsed = {
153    let mut walker = Walker::new(&mut source_reader);
154    parse_internal(&mut walker, &builder)?
155  };
156  let parsed = parsed.unwrap_or_else(|| builder.nil(source_reader.span_from(start_pos)));
157  Ok((parsed, source_reader.remaining()))
158}
159
160#[cfg_attr(not(feature = "unstable"), expect(clippy::redundant_pub_crate))]
161pub(crate) fn parse_optional_edn(edn: &str) -> Result<(Option<Edn<'_>>, &str), Error> {
162  let mut source_reader = SourceReader::new(edn);
163  let parsed = {
164    let mut walker = Walker::new(&mut source_reader);
165    parse_internal(&mut walker, &EdnBuilder)?
166  };
167  Ok((parsed, source_reader.remaining()))
168}
169
170const DELIMITERS: [char; 8] = [',', ']', '}', ')', ';', '(', '[', '{'];
171
172fn is_token_boundary(c: char) -> bool {
173  c.is_whitespace() || DELIMITERS.contains(&c) || c == '"'
174}
175
176#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
177pub struct Position {
178  pub line: usize,
179  pub column: usize,
180  pub ptr: usize,
181}
182
183impl Default for Position {
184  fn default() -> Self {
185    Self { line: 1, column: 1, ptr: 0 }
186  }
187}
188
189#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
190pub struct Span(pub Position, pub Position);
191
192impl Span {
193  /// Whether the span is empty
194  pub const fn is_empty(&self) -> bool {
195    self.0.ptr == self.1.ptr
196  }
197}
198
199/// A string-slice reader that records how much of the slice has been read
200#[derive(Debug)]
201pub struct SourceReader<'s> {
202  slice: &'s str,
203  // Position till where this string has been read
204  read_pos: Position,
205}
206
207impl<'e> SourceReader<'e> {
208  pub fn new(source: &'e str) -> Self {
209    Self { slice: source, read_pos: Position::default() }
210  }
211
212  /// Span from some previously marked position to the current position of the reader
213  #[inline(always)]
214  pub const fn span_from(&self, marker: Position) -> Span {
215    Span(marker, self.read_pos)
216  }
217
218  /// The portion of the source-string remaining to be read
219  ///
220  /// ```
221  /// #[cfg(feature = "unstable")]
222  /// {
223  ///   use clojure_reader::parse::{SourceReader, parse};
224  ///
225  ///   let mut s = SourceReader::new("() []");
226  ///   let _ = parse(&mut s).expect("failed to parse");
227  ///   assert_eq!(s.remaining(), " []");
228  /// }
229  /// ```
230  pub fn remaining(&self) -> &'e str {
231    &self.slice[self.read_pos.ptr..]
232  }
233
234  /// Finishes the source-reader, returning:
235  /// 1. the current reader position, and
236  /// 2. the original source-string.
237  ///
238  /// ```
239  /// #[cfg(feature = "unstable")]
240  /// {
241  ///   use clojure_reader::parse::{Position, SourceReader, parse};
242  ///
243  ///   let mut s = SourceReader::new("() []");
244  ///   let _ = parse(&mut s).expect("failed to parse");
245  ///
246  ///   let (pos, slice) = s.finish();
247  ///   assert_eq!(
248  ///       pos,
249  ///       Position {
250  ///           line: 1,
251  ///           column: 3,
252  ///           ptr: 2
253  ///       }
254  ///   );
255  ///   assert_eq!(slice, "() []");
256  ///   assert_eq!(&slice[pos.ptr..], " []");
257  /// }
258  /// ```
259  #[cfg_attr(not(feature = "unstable"), expect(dead_code))]
260  pub const fn finish(self) -> (Position, &'e str) {
261    (self.read_pos, self.slice)
262  }
263
264  // Slurps until whitespace or delimiter, returning the slice.
265  #[inline(always)]
266  fn slurp_literal(&mut self) -> &'e str {
267    let token = self.slice[self.read_pos.ptr..]
268      .split(is_token_boundary)
269      .next()
270      .expect("Expected at least an empty slice");
271
272    self.read_pos.ptr += token.len();
273    self.read_pos.column += token.chars().count();
274    token
275  }
276
277  // Slurps a char. Special handling for chars that happen to be delimiters
278  #[inline(always)]
279  fn slurp_char(&mut self) -> &'e str {
280    let starting_ptr = self.read_pos.ptr;
281
282    let mut ptr = 0;
283    while let Some(c) = self.peek_next() {
284      // first is always \\, second is always a char we want.
285      // Handles edge cases of having a valid "\\[" but also "\\c[lolthisisvalidedn"
286      if ptr > 1 && (c.is_whitespace() || DELIMITERS.contains(&c)) {
287        break;
288      }
289
290      let _ = self.nibble_next();
291      ptr += c.len_utf8();
292    }
293    &self.slice[starting_ptr..starting_ptr + ptr]
294  }
295
296  #[inline(always)]
297  fn slurp_str(&mut self) -> Result<&'e str, Error> {
298    let _ = self.nibble_next(); // Consume the leading '"' char
299    let starting_ptr = self.read_pos.ptr;
300    let mut escape = false;
301    loop {
302      if let Some(c) = self.nibble_next() {
303        if escape {
304          match c {
305            't' | 'r' | 'n' | '\\' | '"' => (),
306            _ => {
307              return Err(Error::from_position(Code::InvalidEscape, self.read_pos));
308            }
309          }
310          escape = false;
311        } else if c == '"' {
312          return Ok(&self.slice[starting_ptr..self.read_pos.ptr - 1]);
313        } else {
314          escape = c == '\\';
315        }
316      } else {
317        return Err(Error::from_position(Code::UnexpectedEOF, self.read_pos));
318      }
319    }
320  }
321
322  #[inline(always)]
323  fn slurp_tag(&mut self) -> Result<&'e str, Error> {
324    let starting_ptr = self.read_pos.ptr;
325
326    loop {
327      if let Some(c) = self.peek_next() {
328        if is_token_boundary(c) {
329          return Ok(&self.slice[starting_ptr..self.read_pos.ptr]);
330        }
331        let _ = self.nibble_next();
332      } else {
333        return Err(Error::from_position(Code::UnexpectedEOF, self.read_pos));
334      }
335    }
336  }
337
338  // Nibbles away until the next new line
339  #[inline(always)]
340  fn nibble_newline(&mut self) {
341    while let Some(c) = self.peek_next() {
342      if matches!(c, '\n' | '\r') {
343        break;
344      }
345      let _ = self.nibble_next();
346    }
347    self.nibble_whitespace();
348  }
349
350  // Nibbles away until the start of the next form
351  #[inline(always)]
352  fn nibble_whitespace(&mut self) {
353    while let Some(n) = self.peek_next() {
354      if n == ',' || n.is_whitespace() {
355        let _ = self.nibble_next();
356        continue;
357      }
358      break;
359    }
360  }
361
362  // Consumes next
363  #[inline(always)]
364  fn nibble_next(&mut self) -> Option<char> {
365    let char = self.slice[self.read_pos.ptr..].chars().next();
366    if let Some(c) = char {
367      self.read_pos.ptr += c.len_utf8();
368      if c == '\n' {
369        self.read_pos.line += 1;
370        self.read_pos.column = 1;
371      } else {
372        self.read_pos.column += 1;
373      }
374    }
375    char
376  }
377
378  // Peek into the next char
379  #[inline(always)]
380  fn peek_next(&self) -> Option<char> {
381    self.slice[self.read_pos.ptr..].chars().next()
382  }
383}
384
385struct Parsed<I> {
386  item: I,
387  span: Span,
388}
389
390impl<I> Parsed<I> {
391  const fn new(item: I, span: Span) -> Self {
392    Self { item, span }
393  }
394}
395
396struct Walker<'e, 'r, B: InternalParser<'e>> {
397  reader: &'r mut SourceReader<'e>,
398  stack: Vec<ParseContext<'e, B>>,
399}
400
401impl<'e, 'r, B: InternalParser<'e>> Walker<'e, 'r, B> {
402  fn new(reader: &'r mut SourceReader<'e>) -> Self {
403    Self {
404      reader,
405      stack: alloc::vec![ParseContext { kind: ContextKind::Top, discards: Vec::new() }],
406    }
407  }
408
409  #[inline(always)]
410  const fn pos(&self) -> Position {
411    self.reader.read_pos
412  }
413
414  /// Span from some previously marked position to the current position of the walker's reader
415  #[inline(always)]
416  const fn span_from(&self, marker: Position) -> Span {
417    Span(marker, self.reader.read_pos)
418  }
419
420  #[inline(always)]
421  fn push_context(&mut self, ctx: ParseContext<'e, B>) {
422    self.stack.push(ctx);
423  }
424
425  #[inline(always)]
426  fn pop_context(&mut self) -> Option<ParseContext<'e, B>> {
427    self.stack.pop()
428  }
429
430  #[inline(always)]
431  const fn stack_len(&self) -> usize {
432    self.stack.len()
433  }
434
435  const fn make_error(&self, code: Code) -> Error {
436    Error::from_position(code, self.pos())
437  }
438
439  fn last_context_discards(&mut self) -> Option<&mut Vec<B::Discard>> {
440    match self.stack.last_mut() {
441      Some(ParseContext { discards, .. }) => Some(discards),
442      None => None,
443    }
444  }
445}
446
447#[derive(Debug, Clone, Copy)]
448enum OpenDelimiter {
449  Vector,
450  List,
451  Map,
452  Hash,
453}
454
455// `Position`, wherever present, contains the start position of that context
456enum ContextKind<'e, B: InternalParser<'e>> {
457  Top,
458  Vector(B::VectorContext, Position),
459  List(B::ListContext, Position),
460  Map(B::MapContext, Position),
461  Set(B::SetContext, Position),
462  Tag(&'e str, /* Span of the tag string */ Span, Position),
463  Discard(Position),
464}
465
466struct ParseContext<'e, B: InternalParser<'e>> {
467  kind: ContextKind<'e, B>,
468  discards: Vec<B::Discard>,
469}
470
471impl<'e, B: InternalParser<'e>> ParseContext<'e, B> {
472  const fn no_discards(kind: ContextKind<'e, B>) -> Self {
473    Self { kind, discards: Vec::new() }
474  }
475}
476
477#[derive(Debug, Clone)]
478#[cfg_attr(not(feature = "arbitrary-nums"), derive(Copy))]
479enum Atom<'e> {
480  Key(&'e str),
481  Symbol(&'e str),
482  Str(&'e str),
483  Int(i64),
484  #[cfg(feature = "floats")]
485  Double(OrderedFloat<f64>),
486  Rational((i64, i64)),
487  #[cfg(feature = "arbitrary-nums")]
488  BigInt(BigInt),
489  #[cfg(feature = "arbitrary-nums")]
490  BigDec(BigDecimal),
491  Char(char),
492  Bool(bool),
493  Nil,
494}
495
496trait InternalParser<'e> {
497  type Item;
498  type Discard;
499  type VectorContext;
500  type ListContext;
501  type MapContext;
502  type SetContext;
503
504  fn atom(&self, atom: Atom<'e>, span: Span) -> Self::Item;
505
506  fn with_leading_discards(
507    &self,
508    item: Self::Item,
509    leading_discards: Vec<Self::Discard>,
510  ) -> Self::Item;
511
512  fn new_vector_context(&self) -> Self::VectorContext;
513
514  fn new_list_context(&self) -> Self::ListContext;
515
516  fn new_map_context(&self) -> Self::MapContext;
517
518  fn new_set_context(&self) -> Self::SetContext;
519
520  fn add_to_vector(
521    &self,
522    ctx: &mut Self::VectorContext,
523    parsed: Parsed<Self::Item>,
524    leading_discards: Vec<Self::Discard>,
525  ) -> Result<(), Error>;
526
527  fn add_to_list(
528    &self,
529    ctx: &mut Self::ListContext,
530    parsed: Parsed<Self::Item>,
531    leading_discards: Vec<Self::Discard>,
532  ) -> Result<(), Error>;
533
534  fn add_to_map(
535    &self,
536    ctx: &mut Self::MapContext,
537    parsed: Parsed<Self::Item>,
538    leading_discards: Vec<Self::Discard>,
539  ) -> Result<(), Error>;
540
541  fn add_to_set(
542    &self,
543    ctx: &mut Self::SetContext,
544    parsed: Parsed<Self::Item>,
545    leading_discards: Vec<Self::Discard>,
546  ) -> Result<(), Error>;
547
548  fn finish_vector(
549    &self,
550    ctx: Self::VectorContext,
551    trailing_discards: Vec<Self::Discard>,
552    span: Span,
553  ) -> Result<Parsed<Self::Item>, Error>;
554
555  fn finish_set(
556    &self,
557    ctx: Self::SetContext,
558    trailing_discards: Vec<Self::Discard>,
559    validate: bool,
560    span: Span,
561  ) -> Result<Parsed<Self::Item>, Error>;
562
563  fn finish_map(
564    &self,
565    ctx: Self::MapContext,
566    trailing_discards: Vec<Self::Discard>,
567    validate: bool,
568    close_pos: Position,
569    span: Span,
570  ) -> Result<Parsed<Self::Item>, Error>;
571
572  fn finish_list(
573    &self,
574    ctx: Self::ListContext,
575    trailing_discards: Vec<Self::Discard>,
576    span: Span,
577  ) -> Result<Parsed<Self::Item>, Error>;
578
579  fn tag(
580    &self,
581    tag: &'e str,
582    tag_span: Span,
583    value: Parsed<Self::Item>,
584    leading_discards: Vec<Self::Discard>,
585    span: Span,
586  ) -> Result<Parsed<Self::Item>, Error>;
587
588  fn discard(
589    &self,
590    value: Parsed<Self::Item>,
591    leading_discards: Vec<Self::Discard>,
592    discard_span: Span,
593  ) -> Self::Discard;
594
595  fn nil(&self, span: Span) -> Self::Item {
596    self.atom(Atom::Nil, span)
597  }
598}
599
600struct EdnBuilder;
601
602impl<'e> InternalParser<'e> for EdnBuilder {
603  type Item = Edn<'e>;
604  type Discard = ();
605  type VectorContext = Vec<Edn<'e>>;
606  type ListContext = Vec<Edn<'e>>;
607  type MapContext = (Vec<(Parsed<Edn<'e>>, Parsed<Edn<'e>>)>, Option<Parsed<Edn<'e>>>);
608  type SetContext = Vec<Parsed<Edn<'e>>>;
609
610  fn atom(&self, atom: Atom<'e>, span: Span) -> Self::Item {
611    let _ = span;
612    match atom {
613      Atom::Key(key) => Edn::Key(key),
614      Atom::Symbol(symbol) => Edn::Symbol(symbol),
615      Atom::Str(str) => Edn::Str(str),
616      Atom::Int(int) => Edn::Int(int),
617      #[cfg(feature = "floats")]
618      Atom::Double(double) => Edn::Double(double),
619      Atom::Rational(rational) => Edn::Rational(rational),
620      #[cfg(feature = "arbitrary-nums")]
621      Atom::BigInt(big_int) => Edn::BigInt(big_int),
622      #[cfg(feature = "arbitrary-nums")]
623      Atom::BigDec(big_dec) => Edn::BigDec(big_dec),
624      Atom::Char(ch) => Edn::Char(ch),
625      Atom::Bool(bool) => Edn::Bool(bool),
626      Atom::Nil => Edn::Nil,
627    }
628  }
629
630  fn with_leading_discards(
631    &self,
632    item: Self::Item,
633    _leading_discards: Vec<Self::Discard>,
634  ) -> Self::Item {
635    item
636  }
637
638  fn new_vector_context(&self) -> Self::VectorContext {
639    Vec::new()
640  }
641
642  fn new_list_context(&self) -> Self::ListContext {
643    Vec::new()
644  }
645
646  fn new_map_context(&self) -> Self::MapContext {
647    (Vec::new(), None)
648  }
649
650  fn new_set_context(&self) -> Self::SetContext {
651    Vec::new()
652  }
653
654  fn add_to_vector(
655    &self,
656    ctx: &mut Self::VectorContext,
657    parsed: Parsed<Self::Item>,
658    _leading_discards: Vec<Self::Discard>,
659  ) -> Result<(), Error> {
660    ctx.push(parsed.item);
661    Ok(())
662  }
663
664  fn add_to_list(
665    &self,
666    ctx: &mut Self::ListContext,
667    parsed: Parsed<Self::Item>,
668    _leading_discards: Vec<Self::Discard>,
669  ) -> Result<(), Error> {
670    ctx.push(parsed.item);
671    Ok(())
672  }
673
674  fn add_to_map(
675    &self,
676    ctx: &mut Self::MapContext,
677    parsed: Parsed<Self::Item>,
678    _leading_discards: Vec<Self::Discard>,
679  ) -> Result<(), Error> {
680    let (entries, pending) = ctx;
681    if let Some(key) = pending.take() {
682      entries.push((key, parsed));
683    } else {
684      *pending = Some(parsed);
685    }
686    Ok(())
687  }
688
689  fn add_to_set(
690    &self,
691    ctx: &mut Self::SetContext,
692    parsed: Parsed<Self::Item>,
693    _leading_discards: Vec<Self::Discard>,
694  ) -> Result<(), Error> {
695    ctx.push(parsed);
696    Ok(())
697  }
698
699  fn finish_vector(
700    &self,
701    ctx: Self::VectorContext,
702    _trailing_discards: Vec<Self::Discard>,
703    span: Span,
704  ) -> Result<Parsed<Self::Item>, Error> {
705    Ok(Parsed::new(Edn::Vector(ctx), span))
706  }
707
708  fn finish_set(
709    &self,
710    ctx: Self::SetContext,
711    _trailing_discards: Vec<Self::Discard>,
712    validate: bool,
713    span: Span,
714  ) -> Result<Parsed<Self::Item>, Error> {
715    let mut set = BTreeSet::new();
716    for item in ctx {
717      if !set.insert(item.item) && validate {
718        return Err(Error::from_position(Code::SetDuplicateKey, item.span.1));
719      }
720    }
721    Ok(Parsed::new(Edn::Set(set), span))
722  }
723
724  fn finish_map(
725    &self,
726    ctx: Self::MapContext,
727    _trailing_discards: Vec<Self::Discard>,
728    validate: bool,
729    close_pos: Position,
730    span: Span,
731  ) -> Result<Parsed<Self::Item>, Error> {
732    if ctx.1.is_some() {
733      return Err(Error::from_position(Code::UnexpectedEOF, close_pos));
734    }
735    let mut map = BTreeMap::new();
736    for (key, value) in ctx.0 {
737      if map.insert(key.item, value.item).is_some() && validate {
738        return Err(Error::from_position(Code::HashMapDuplicateKey, value.span.1));
739      }
740    }
741    Ok(Parsed::new(Edn::Map(map), span))
742  }
743
744  fn finish_list(
745    &self,
746    ctx: Self::ListContext,
747    _trailing_discards: Vec<Self::Discard>,
748    span: Span,
749  ) -> Result<Parsed<Self::Item>, Error> {
750    Ok(Parsed::new(Edn::List(ctx), span))
751  }
752
753  fn tag(
754    &self,
755    tag: &'e str,
756    tag_span: Span,
757    value: Parsed<Self::Item>,
758    _leading_discards: Vec<Self::Discard>,
759    span: Span,
760  ) -> Result<Parsed<Self::Item>, Error> {
761    crate::edn::validate_tag(tag, tag_span)?;
762    if tag.starts_with(':') && !matches!(&value.item, Edn::Map(_)) {
763      return Err(Error::from_position(Code::InvalidTag, tag_span.0));
764    }
765    Ok(Parsed::new(Edn::Tagged(tag, Box::new(value.item)), span))
766  }
767
768  fn discard(
769    &self,
770    _value: Parsed<Self::Item>,
771    _leading_discards: Vec<Self::Discard>,
772    _discard_span: Span,
773  ) -> Self::Discard {
774  }
775}
776
777struct NodeBuilder;
778
779impl<'e> InternalParser<'e> for NodeBuilder {
780  type Item = Node<'e>;
781  type Discard = Discard<'e>;
782  type VectorContext = Vec<Node<'e>>;
783  type ListContext = Vec<Node<'e>>;
784  type MapContext = (Vec<(Node<'e>, Node<'e>)>, Option<Node<'e>>);
785  type SetContext = Vec<Node<'e>>;
786
787  fn atom(&self, atom: Atom<'e>, span: Span) -> Self::Item {
788    let kind = match atom {
789      Atom::Key(key) => NodeKind::Key(key),
790      Atom::Symbol(symbol) => NodeKind::Symbol(symbol),
791      Atom::Str(str) => NodeKind::Str(str),
792      Atom::Int(int) => NodeKind::Int(int),
793      #[cfg(feature = "floats")]
794      Atom::Double(double) => NodeKind::Double(double),
795      Atom::Rational(rational) => NodeKind::Rational(rational),
796      #[cfg(feature = "arbitrary-nums")]
797      Atom::BigInt(big_int) => NodeKind::BigInt(big_int),
798      #[cfg(feature = "arbitrary-nums")]
799      Atom::BigDec(big_dec) => NodeKind::BigDec(big_dec),
800      Atom::Char(ch) => NodeKind::Char(ch),
801      Atom::Bool(bool) => NodeKind::Bool(bool),
802      Atom::Nil => NodeKind::Nil,
803    };
804
805    Node::no_discards(kind, span)
806  }
807
808  fn with_leading_discards(
809    &self,
810    mut item: Self::Item,
811    leading_discards: Vec<Self::Discard>,
812  ) -> Self::Item {
813    item.leading_discards = leading_discards;
814    item
815  }
816
817  fn new_vector_context(&self) -> Self::VectorContext {
818    Vec::new()
819  }
820
821  fn new_list_context(&self) -> Self::ListContext {
822    Vec::new()
823  }
824
825  fn new_map_context(&self) -> Self::MapContext {
826    (Vec::new(), None)
827  }
828
829  fn new_set_context(&self) -> Self::SetContext {
830    Vec::new()
831  }
832
833  fn add_to_vector(
834    &self,
835    ctx: &mut Self::VectorContext,
836    parsed: Parsed<Self::Item>,
837    leading_discards: Vec<Self::Discard>,
838  ) -> Result<(), Error> {
839    ctx.push(self.with_leading_discards(parsed.item, leading_discards));
840    Ok(())
841  }
842
843  fn add_to_list(
844    &self,
845    ctx: &mut Self::ListContext,
846    parsed: Parsed<Self::Item>,
847    leading_discards: Vec<Self::Discard>,
848  ) -> Result<(), Error> {
849    ctx.push(self.with_leading_discards(parsed.item, leading_discards));
850    Ok(())
851  }
852
853  fn add_to_map(
854    &self,
855    ctx: &mut Self::MapContext,
856    parsed: Parsed<Self::Item>,
857    leading_discards: Vec<Self::Discard>,
858  ) -> Result<(), Error> {
859    let parsed = self.with_leading_discards(parsed.item, leading_discards);
860    if let Some(key) = ctx.1.take() {
861      ctx.0.push((key, parsed));
862    } else {
863      ctx.1 = Some(parsed);
864    }
865    Ok(())
866  }
867
868  fn add_to_set(
869    &self,
870    ctx: &mut Self::SetContext,
871    parsed: Parsed<Self::Item>,
872    leading_discards: Vec<Self::Discard>,
873  ) -> Result<(), Error> {
874    ctx.push(self.with_leading_discards(parsed.item, leading_discards));
875    Ok(())
876  }
877
878  fn finish_vector(
879    &self,
880    ctx: Self::VectorContext,
881    trailing_discards: Vec<Self::Discard>,
882    span: Span,
883  ) -> Result<Parsed<Self::Item>, Error> {
884    Ok(Parsed::new(Node::no_discards(NodeKind::Vector(ctx, trailing_discards), span), span))
885  }
886
887  fn finish_set(
888    &self,
889    ctx: Self::SetContext,
890    trailing_discards: Vec<Self::Discard>,
891    _validate: bool,
892    span: Span,
893  ) -> Result<Parsed<Self::Item>, Error> {
894    Ok(Parsed::new(Node::no_discards(NodeKind::Set(ctx, trailing_discards), span), span))
895  }
896
897  fn finish_map(
898    &self,
899    ctx: Self::MapContext,
900    trailing_discards: Vec<Self::Discard>,
901    _validate: bool,
902    close_pos: Position,
903    span: Span,
904  ) -> Result<Parsed<Self::Item>, Error> {
905    if ctx.1.is_some() {
906      return Err(Error::from_position(Code::UnexpectedEOF, close_pos));
907    }
908    Ok(Parsed::new(Node::no_discards(NodeKind::Map(ctx.0, trailing_discards), span), span))
909  }
910
911  fn finish_list(
912    &self,
913    ctx: Self::ListContext,
914    trailing_discards: Vec<Self::Discard>,
915    span: Span,
916  ) -> Result<Parsed<Self::Item>, Error> {
917    Ok(Parsed::new(Node::no_discards(NodeKind::List(ctx, trailing_discards), span), span))
918  }
919
920  fn tag(
921    &self,
922    tag: &'e str,
923    tag_span: Span,
924    value: Parsed<Self::Item>,
925    leading_discards: Vec<Self::Discard>,
926    span: Span,
927  ) -> Result<Parsed<Self::Item>, Error> {
928    let value = self.with_leading_discards(value.item, leading_discards);
929    Ok(Parsed::new(Node::no_discards(NodeKind::Tagged(tag, tag_span, Box::new(value)), span), span))
930  }
931
932  fn discard(
933    &self,
934    value: Parsed<Self::Item>,
935    leading_discards: Vec<Self::Discard>,
936    discard_span: Span,
937  ) -> Self::Discard {
938    Discard(self.with_leading_discards(value.item, leading_discards), discard_span)
939  }
940}
941
942#[expect(clippy::mem_replace_with_default)]
943const fn take_discards<D>(discards: &mut Vec<D>) -> Vec<D> {
944  replace(discards, Vec::new())
945}
946
947#[inline]
948fn add_to_context<'e, B: InternalParser<'e>>(
949  context: &mut Option<&mut ParseContext<'e, B>>,
950  builder: &B,
951  parsed: Parsed<B::Item>,
952) -> Result<(), Error> {
953  match context.as_mut() {
954    Some(ParseContext { kind: ContextKind::Vector(ctx, _), discards }) => {
955      builder.add_to_vector(ctx, parsed, take_discards(discards))?;
956    }
957    Some(ParseContext { kind: ContextKind::List(ctx, _), discards }) => {
958      builder.add_to_list(ctx, parsed, take_discards(discards))?;
959    }
960    Some(ParseContext { kind: ContextKind::Map(ctx, _), discards }) => {
961      builder.add_to_map(ctx, parsed, take_discards(discards))?;
962    }
963    Some(ParseContext { kind: ContextKind::Set(ctx, _), discards }) => {
964      builder.add_to_set(ctx, parsed, take_discards(discards))?;
965    }
966    _ => {}
967  }
968  Ok(())
969}
970
971#[inline]
972fn handle_open_delimiter<'e, B: InternalParser<'e>>(
973  walker: &mut Walker<'e, '_, B>,
974  builder: &B,
975  delim: OpenDelimiter,
976) -> Result<(), Error> {
977  let pos_start = walker.pos();
978  match delim {
979    OpenDelimiter::Vector => {
980      let _ = walker.reader.nibble_next();
981      walker.push_context(ParseContext::no_discards(ContextKind::Vector(
982        builder.new_vector_context(),
983        pos_start,
984      )));
985    }
986    OpenDelimiter::List => {
987      let _ = walker.reader.nibble_next();
988      walker.push_context(ParseContext::no_discards(ContextKind::List(
989        builder.new_list_context(),
990        pos_start,
991      )));
992    }
993    OpenDelimiter::Map => {
994      let _ = walker.reader.nibble_next();
995      walker.push_context(ParseContext::no_discards(ContextKind::Map(
996        builder.new_map_context(),
997        pos_start,
998      )));
999    }
1000    OpenDelimiter::Hash => {
1001      let _ = walker.reader.nibble_next();
1002      match walker.reader.peek_next() {
1003        Some('{') => {
1004          let _ = walker.reader.nibble_next();
1005          walker.push_context(ParseContext::no_discards(ContextKind::Set(
1006            builder.new_set_context(),
1007            pos_start,
1008          )));
1009        }
1010        Some('_') => {
1011          let _ = walker.reader.nibble_next();
1012          walker.push_context(ParseContext::no_discards(ContextKind::Discard(pos_start)));
1013        }
1014        Some(c) if !c.is_whitespace() && !DELIMITERS.contains(&c) => {
1015          let tag_pos_start = walker.pos();
1016          let tag = walker.reader.slurp_tag()?;
1017          let tag_span = walker.span_from(tag_pos_start);
1018          if tag.is_empty() {
1019            return Err(walker.make_error(Code::InvalidTag));
1020          }
1021
1022          walker.reader.nibble_whitespace();
1023          walker
1024            .push_context(ParseContext::no_discards(ContextKind::Tag(tag, tag_span, pos_start)));
1025        }
1026        Some(_) => return Err(walker.make_error(Code::InvalidTag)),
1027        None => return Err(walker.make_error(Code::UnexpectedEOF)),
1028      }
1029    }
1030  }
1031  Ok(())
1032}
1033
1034fn wrap_pending_tags<'e, 'r, B: InternalParser<'e>>(
1035  walker: &mut Walker<'e, 'r, B>,
1036  builder: &B,
1037  mut parsed: Parsed<B::Item>,
1038) -> Result<Parsed<B::Item>, Error> {
1039  while matches!(walker.stack.last(), Some(ParseContext { kind: ContextKind::Tag(..), .. })) {
1040    let ParseContext { kind: ContextKind::Tag(tag, tag_span, pos_start), discards } =
1041      walker.pop_context().expect("tag context should exist")
1042    else {
1043      unreachable!("tag context should be on top of the stack");
1044    };
1045
1046    parsed = builder.tag(tag, tag_span, parsed, discards, walker.span_from(pos_start))?;
1047  }
1048
1049  Ok(parsed)
1050}
1051
1052fn under_discard<'e, B: InternalParser<'e>>(walker: &Walker<'e, '_, B>) -> bool {
1053  walker.stack.iter().any(|ctx| matches!(ctx.kind, ContextKind::Discard(..)))
1054}
1055
1056fn complete_value<'e, 'r, B: InternalParser<'e>>(
1057  walker: &mut Walker<'e, 'r, B>,
1058  builder: &B,
1059  parsed: Parsed<B::Item>,
1060) -> Result<Option<B::Item>, Error> {
1061  let parsed = wrap_pending_tags(walker, builder, parsed)?;
1062
1063  if walker.stack_len() == 1 {
1064    let leading_discards =
1065      take_discards(walker.last_context_discards().expect("Top should be there"));
1066    return Ok(Some(builder.with_leading_discards(parsed.item, leading_discards)));
1067  }
1068
1069  if let Some(ParseContext { kind: ContextKind::Discard(pos_start), discards }) =
1070    walker.stack.last_mut()
1071  {
1072    let pos_start = *pos_start;
1073    let end_pos = parsed.span.1;
1074    let discarded = builder.discard(parsed, take_discards(discards), Span(pos_start, end_pos));
1075    walker.pop_context();
1076    walker.stack.last_mut().expect("Top should be there").discards.push(discarded);
1077    return Ok(None);
1078  }
1079
1080  add_to_context(&mut walker.stack.last_mut(), builder, parsed)?;
1081  Ok(None)
1082}
1083
1084#[inline]
1085fn handle_close_delimiter<'e, B: InternalParser<'e>>(
1086  walker: &mut Walker<'e, '_, B>,
1087  builder: &B,
1088  delimiter: char,
1089) -> Result<Option<B::Item>, Error> {
1090  if walker.stack_len() <= 1 {
1091    return Err(walker.make_error(Code::UnmatchedDelimiter(delimiter)));
1092  }
1093
1094  let expected = match walker.stack.last().expect("Len > 1 is never empty") {
1095    ParseContext { kind: ContextKind::Vector(..), .. } => ']',
1096    ParseContext { kind: ContextKind::List(..), .. } => ')',
1097    ParseContext { kind: ContextKind::Map(..) | ContextKind::Set(..), .. } => '}',
1098    _ => {
1099      return Err(walker.make_error(Code::UnmatchedDelimiter(delimiter)));
1100    }
1101  };
1102
1103  if delimiter != expected {
1104    return Err(walker.make_error(Code::UnmatchedDelimiter(delimiter)));
1105  }
1106
1107  let parsed = match walker.pop_context() {
1108    Some(ParseContext { kind: ContextKind::Vector(ctx, pos_start), discards }) => {
1109      let _ = walker.reader.nibble_next();
1110      builder.finish_vector(ctx, discards, walker.span_from(pos_start))?
1111    }
1112    Some(ParseContext { kind: ContextKind::List(ctx, pos_start), discards }) => {
1113      let _ = walker.reader.nibble_next();
1114      builder.finish_list(ctx, discards, walker.span_from(pos_start))?
1115    }
1116    Some(ParseContext { kind: ContextKind::Map(ctx, pos_start), discards }) => {
1117      let validate = !under_discard(walker);
1118      let close_pos = walker.pos();
1119      let _ = walker.reader.nibble_next();
1120      builder.finish_map(ctx, discards, validate, close_pos, walker.span_from(pos_start))?
1121    }
1122    Some(ParseContext { kind: ContextKind::Set(ctx, pos_start), discards }) => {
1123      let validate = !under_discard(walker);
1124      let _ = walker.reader.nibble_next();
1125      builder.finish_set(ctx, discards, validate, walker.span_from(pos_start))?
1126    }
1127    _ => {
1128      return Err(walker.make_error(Code::UnmatchedDelimiter(delimiter)));
1129    }
1130  };
1131
1132  complete_value(walker, builder, parsed)
1133}
1134
1135fn parse_internal<'e, B: InternalParser<'e>>(
1136  walker: &mut Walker<'e, '_, B>,
1137  builder: &B,
1138) -> Result<Option<B::Item>, Error> {
1139  let mut result = None;
1140  loop {
1141    walker.reader.nibble_whitespace();
1142    match walker.reader.peek_next() {
1143      Some(';') => walker.reader.nibble_newline(),
1144      Some('[') => handle_open_delimiter(walker, builder, OpenDelimiter::Vector)?,
1145      Some('(') => handle_open_delimiter(walker, builder, OpenDelimiter::List)?,
1146      Some('{') => handle_open_delimiter(walker, builder, OpenDelimiter::Map)?,
1147      Some('#') => handle_open_delimiter(walker, builder, OpenDelimiter::Hash)?,
1148      Some(d) if matches!(d, ']' | ')' | '}') => {
1149        if let Some(parsed) = handle_close_delimiter(walker, builder, d)? {
1150          result = Some(parsed);
1151          break;
1152        }
1153      }
1154      Some(c) => {
1155        let pos_start = walker.reader.read_pos;
1156        let atom = match c {
1157          '\\' => parse_char(walker.reader.slurp_char()).map(Atom::Char),
1158          '"' => Ok(Atom::Str(walker.reader.slurp_str()?)),
1159          _ => edn_literal(walker.reader.slurp_literal()),
1160        }
1161        .map_err(|code| Error::from_position(code, pos_start))?;
1162        let span = walker.reader.span_from(pos_start);
1163        let parsed = Parsed::new(builder.atom(atom, span), span);
1164
1165        if let Some(parsed) = complete_value(walker, builder, parsed)? {
1166          result = Some(parsed);
1167          break;
1168        }
1169      }
1170      None => {
1171        if walker.stack_len() > 1 {
1172          return Err(walker.make_error(Code::UnexpectedEOF));
1173        }
1174        break;
1175      }
1176    }
1177  }
1178  Ok(result)
1179}
1180
1181#[inline]
1182fn edn_literal(literal: &str) -> Result<Atom<'_>, Code> {
1183  fn numeric(s: &str) -> bool {
1184    let (first, second) = {
1185      let mut s = s.chars();
1186      (s.next(), s.next())
1187    };
1188
1189    let first = first.expect("Empty str is previously caught as nil");
1190    if first.is_numeric() {
1191      return true;
1192    }
1193
1194    if (first == '-' || first == '+')
1195      && let Some(s) = second
1196      && s.is_numeric()
1197    {
1198      return true;
1199    }
1200
1201    false
1202  }
1203
1204  Ok(match literal {
1205    "nil" => Atom::Nil,
1206    "true" => Atom::Bool(true),
1207    "false" => Atom::Bool(false),
1208    k if k.starts_with(':') => {
1209      if k.len() <= 1 {
1210        return Err(Code::InvalidKeyword);
1211      }
1212      Atom::Key(&k[1..])
1213    }
1214    n if numeric(n) => parse_number(n)?,
1215    _ => Atom::Symbol(literal),
1216  })
1217}
1218
1219#[inline]
1220fn parse_char(lit: &str) -> Result<char, Code> {
1221  let lit = &lit[1..]; // ignore the leading '\\'
1222  match lit {
1223    "newline" => Ok('\n'),
1224    "return" => Ok('\r'),
1225    "tab" => Ok('\t'),
1226    "space" => Ok(' '),
1227    c if c.chars().count() == 1 => Ok(c.chars().next().expect("c must be one character")),
1228    _ => Err(Code::InvalidChar),
1229  }
1230}
1231
1232#[inline]
1233fn signed_int_from_slice(slice: &str, radix: u8, polarity: i8) -> Option<i64> {
1234  let magnitude = u64::from_str_radix(slice, radix.into()).ok()?;
1235  if polarity < 0 {
1236    if magnitude == (i64::MAX as u64) + 1 {
1237      return Some(i64::MIN);
1238    }
1239    i64::try_from(magnitude).ok().map(|n| -n)
1240  } else {
1241    i64::try_from(magnitude).ok()
1242  }
1243}
1244
1245#[inline]
1246fn parse_number(lit: &str) -> Result<Atom<'_>, Code> {
1247  let mut chars = lit.chars().peekable();
1248  let (number, radix, polarity) = {
1249    let mut num_ptr_start = 0;
1250    let polarity = chars.peek().map_or(1i8, |c| {
1251      if *c == '-' {
1252        num_ptr_start += 1;
1253        -1i8
1254      } else if *c == '+' {
1255        // The EDN spec allows for a redundant '+' symbol, we just ignore it.
1256        num_ptr_start += 1;
1257        1i8
1258      } else {
1259        1i8
1260      }
1261    });
1262
1263    let mut number = &lit[num_ptr_start..];
1264
1265    if number.get(..2).is_some_and(|prefix| prefix.eq_ignore_ascii_case("0x")) {
1266      number = &number[2..];
1267      (number, 16, polarity)
1268    } else if let Some(index) = number.find(['r', 'R']) {
1269      let radix = (number[0..index]).parse::<u8>();
1270
1271      match radix {
1272        Ok(r) => {
1273          // from_str_radix panics if radix is not in the range from 2 to 36
1274          if !(2..=36).contains(&r) {
1275            return Err(Code::InvalidRadix(Some(r)));
1276          }
1277
1278          number = &number[(index + 1)..];
1279          (number, r, polarity)
1280        }
1281        Err(_) => {
1282          return Err(Code::InvalidRadix(None));
1283        }
1284      }
1285    } else {
1286      (number, 10, polarity)
1287    }
1288  };
1289
1290  if let Some(n) = signed_int_from_slice(number, radix, polarity) {
1291    return Ok(Atom::Int(n));
1292  }
1293  if radix == 10
1294    && let Some(index) = number.find('/')
1295  {
1296    let (num, den) = number.split_at(index);
1297    let num = num.parse::<i64>();
1298    let den = &den[1..];
1299    let den_value = den.parse::<i64>();
1300
1301    if let (Ok(n), Ok(d)) = (num, den_value)
1302      && d > 0
1303      && !den.starts_with(['+', '-'])
1304    {
1305      return Ok(Atom::Rational((n * i64::from(polarity), d)));
1306    }
1307  }
1308
1309  #[cfg(feature = "arbitrary-nums")]
1310  if let Some(n) = big_int_from_slice(number, radix, polarity) {
1311    return Ok(Atom::BigInt(n));
1312  }
1313  #[cfg(feature = "floats")]
1314  if radix == 10
1315    && number.contains(['.', 'e', 'E'])
1316    && let Ok(n) = number.parse::<f64>()
1317  {
1318    return Ok(Atom::Double((n * f64::from(polarity)).into()));
1319  }
1320  #[cfg(feature = "arbitrary-nums")]
1321  if let Some(n) = big_dec_from_slice(number, radix, polarity) {
1322    return Ok(Atom::BigDec(n));
1323  }
1324
1325  Err(Code::InvalidNumber)
1326}
1327
1328#[inline]
1329#[cfg(feature = "arbitrary-nums")]
1330fn big_int_from_slice(slice: &str, radix: u8, polarity: i8) -> Option<num_bigint::BigInt> {
1331  // strip ending N, if it exists
1332  let slice = slice.strip_suffix('N').map_or(slice, |slice| slice);
1333  let num = num_bigint::BigInt::parse_bytes(slice.as_bytes(), radix.into())?;
1334  Some(num * polarity)
1335}
1336
1337#[inline]
1338#[cfg(feature = "arbitrary-nums")]
1339fn big_dec_from_slice(slice: &str, radix: u8, polarity: i8) -> Option<bigdecimal::BigDecimal> {
1340  // strip ending M, if it exists
1341  let slice = slice.strip_suffix('M').map_or(slice, |slice| slice);
1342  let num = bigdecimal::BigDecimal::parse_bytes(slice.as_bytes(), radix.into())?;
1343  Some(num * polarity)
1344}