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}