Skip to main content

granit_parser/
parser_stack.rs

1use crate::{
2    error::{ErrorKind, ScanError},
3    input::{str::StrInput, BorrowedInput, BufferedInput},
4    parser::{Event, ParseResult, Parser, ParserTrait, SpannedEventReceiver},
5    scanner::Span,
6};
7use alloc::{borrow::Cow, boxed::Box, string::String, vec::Vec};
8
9/// A lightweight parser that replays a pre-collected event stream.
10pub struct ReplayParser<'input> {
11    events: alloc::vec::IntoIter<(Event<'input>, Span)>,
12    anchor_offset: usize,
13}
14
15impl<'input> ReplayParser<'input> {
16    /// Create a parser that replays `events` and starts anchor allocation at `anchor_offset`.
17    #[must_use]
18    pub fn new(events: Vec<(Event<'input>, Span)>, anchor_offset: usize) -> Self {
19        Self {
20            events: events.into_iter(),
21            anchor_offset,
22        }
23    }
24
25    /// Return the next anchor ID that should be assigned after replayed events.
26    #[must_use]
27    pub fn anchor_offset(&self) -> usize {
28        self.anchor_offset
29    }
30
31    /// Set the next anchor ID that should be assigned after replayed events.
32    pub fn set_anchor_offset(&mut self, offset: usize) {
33        self.anchor_offset = offset;
34    }
35
36    fn advance_anchor_offset(&mut self, event: &Event<'input>) {
37        let anchor_id = match event {
38            Event::Scalar(_, _, anchor_id, _)
39            | Event::SequenceStart(_, anchor_id, _)
40            | Event::MappingStart(_, anchor_id, _) => *anchor_id,
41            _ => 0,
42        };
43
44        if anchor_id > 0 {
45            self.anchor_offset = self.anchor_offset.max(anchor_id.saturating_add(1));
46        }
47    }
48}
49
50impl<'input> ParserTrait<'input> for ReplayParser<'input> {
51    fn peek(&mut self) -> Option<Result<&(Event<'input>, Span), ScanError>> {
52        self.events.as_slice().first().map(Ok)
53    }
54
55    fn next_event(&mut self) -> Option<ParseResult<'input>> {
56        let event = self.events.next()?;
57        self.advance_anchor_offset(&event.0);
58        Some(Ok(event))
59    }
60
61    fn load<R: SpannedEventReceiver<'input>>(
62        &mut self,
63        recv: &mut R,
64        multi: bool,
65    ) -> Result<(), ScanError> {
66        while let Some(res) = self.next_event() {
67            let (ev, span) = res?;
68            let is_doc_end = matches!(ev, Event::DocumentEnd);
69            let is_stream_end = matches!(ev, Event::StreamEnd);
70            recv.on_event(ev, span);
71            if is_stream_end {
72                break;
73            }
74            if !multi && is_doc_end {
75                break;
76            }
77        }
78        Ok(())
79    }
80}
81
82impl<'input> Iterator for ReplayParser<'input> {
83    type Item = ParseResult<'input>;
84
85    fn next(&mut self) -> Option<Self::Item> {
86        self.next_event()
87    }
88}
89
90impl core::iter::FusedIterator for ReplayParser<'_> {}
91
92/// A wrapper for different types of parsers.
93enum AnyParser<'input, I, T>
94where
95    I: Iterator<Item = char>,
96    T: BorrowedInput<'input>,
97{
98    /// A parser over borrowed string input.
99    String {
100        /// Parser currently producing events for this stack entry.
101        parser: Parser<'input, StrInput<'input>>,
102        /// Human-readable source name returned by [`ParserStack::stack`].
103        name: String,
104    },
105    /// A parser over an iterator of characters.
106    Iter {
107        /// Parser currently producing events for this stack entry.
108        parser: Parser<'static, BufferedInput<I>>,
109        /// Human-readable source name returned by [`ParserStack::stack`].
110        name: String,
111    },
112    /// A parser over a custom input.
113    Custom {
114        /// Parser currently producing events for this stack entry.
115        parser: Parser<'input, T>,
116        /// Human-readable source name returned by [`ParserStack::stack`].
117        name: String,
118    },
119    /// A parser over a replayed event stream.
120    Replay {
121        /// Replay parser currently producing pre-collected events for this stack entry.
122        parser: ReplayParser<'input>,
123        /// Human-readable source name returned by [`ParserStack::stack`].
124        name: String,
125    },
126}
127
128impl<'input, I, T> AnyParser<'input, I, T>
129where
130    I: Iterator<Item = char>,
131    T: BorrowedInput<'input>,
132{
133    fn anchor_offset(&self) -> usize {
134        match self {
135            AnyParser::String { parser, .. } => parser.anchor_offset(),
136            AnyParser::Iter { parser, .. } => parser.anchor_offset(),
137            AnyParser::Custom { parser, .. } => parser.anchor_offset(),
138            AnyParser::Replay { parser, .. } => parser.anchor_offset(),
139        }
140    }
141
142    fn set_anchor_offset(&mut self, offset: usize) {
143        match self {
144            AnyParser::String { parser, .. } => parser.set_anchor_offset(offset),
145            AnyParser::Iter { parser, .. } => parser.set_anchor_offset(offset),
146            AnyParser::Custom { parser, .. } => parser.set_anchor_offset(offset),
147            AnyParser::Replay { parser, .. } => parser.set_anchor_offset(offset),
148        }
149    }
150}
151
152/// A parser implementation that uses a stack for include-style parsing.
153///
154/// Note: `ParserStack` deliberately suppresses nested [`Event::StreamStart`] /
155/// [`Event::DocumentStart`] events when more than one parser is stacked, and the tests assert
156/// outputs where a nested parser starts directly with [`Event::MappingStart`] before the parent
157/// stream/document wrapper appears.
158///
159/// That is exactly what we want for `!include`-style subtree injection.
160///
161/// Included parser events, including [`Event::Comment`] events, are replayed through the same
162/// event stream as parent events. Their [`Span`] values remain local to the included source, just
163/// like every other event span from an included parser. `ParserStack` does not attach file names,
164/// source IDs, or other include provenance to events or spans. Errors do retain the nested source
165/// names through [`ScanError::source_stack`].
166pub struct ParserStack<'input, I = core::iter::Empty<char>, T = StrInput<'input>>
167where
168    I: Iterator<Item = char>,
169    T: BorrowedInput<'input>,
170{
171    parsers: Vec<AnyParser<'input, I, T>>,
172    current: Option<(Event<'input>, Span)>,
173    current_error: Option<ScanError>,
174    stream_end_emitted: bool,
175    #[allow(clippy::type_complexity)]
176    include_resolver: Option<Box<dyn FnMut(&str) -> Result<Cow<'input, str>, ScanError> + 'input>>,
177}
178
179impl<'input, I, T> ParserStack<'input, I, T>
180where
181    I: Iterator<Item = char>,
182    T: BorrowedInput<'input>,
183{
184    /// Creates a new, empty parser stack.
185    #[must_use]
186    pub fn new() -> Self {
187        Self {
188            parsers: Vec::new(),
189            current: None,
190            current_error: None,
191            stream_end_emitted: false,
192            include_resolver: None,
193        }
194    }
195
196    /// Set the resolver used by [`Self::push_include`].
197    ///
198    /// The resolver receives the include name and returns the included YAML source text.
199    pub fn set_resolver(
200        &mut self,
201        mut resolver: impl FnMut(&str) -> Result<String, ScanError> + 'input,
202    ) {
203        self.include_resolver = Some(Box::new(move |name| resolver(name).map(Cow::Owned)));
204    }
205
206    /// Set an include resolver whose source text can be borrowed for the stack's input lifetime.
207    ///
208    /// Unlike [`Self::set_resolver`], this path lets scalar, comment, anchor, and tag token text in
209    /// included documents borrow directly from the returned source. The included document is still
210    /// validated eagerly so resolution errors retain the same timing and source-stack context.
211    pub fn set_borrowed_resolver(
212        &mut self,
213        mut resolver: impl FnMut(&str) -> Result<&'input str, ScanError> + 'input,
214    ) {
215        self.include_resolver = Some(Box::new(move |name| resolver(name).map(Cow::Borrowed)));
216    }
217
218    /// Resolve an include by name and push the resulting parser onto the stack.
219    ///
220    /// Comment events from the included content are preserved. Their spans are local to the
221    /// included content returned by the resolver, matching the existing behavior for all included
222    /// document events.
223    ///
224    /// # Errors
225    /// Returns `ScanError` if no resolver is configured, include resolution fails, or the
226    /// included content cannot be parsed.
227    pub fn push_include(&mut self, include_str: &str) -> Result<(), ScanError> {
228        let resolved = match &mut self.include_resolver {
229            Some(resolver) => resolver(include_str),
230            None => {
231                return Err(self.contextualize_include_error(
232                    ScanError::from_kind(
233                        crate::scanner::Marker::new(0, 1, 0),
234                        ErrorKind::MissingIncludeResolver,
235                    ),
236                    include_str,
237                ));
238            }
239        };
240        let content = match resolved {
241            Ok(content) => content,
242            Err(error) => return Err(self.contextualize_include_error(error, include_str)),
243        };
244        let inherited_anchor_offset = self.parsers.last().map(AnyParser::anchor_offset);
245
246        let (events, next_anchor_offset) = match content {
247            Cow::Borrowed(content) => {
248                let mut parser = Parser::new_from_str(content);
249                if let Some(anchor_offset) = inherited_anchor_offset {
250                    parser.set_anchor_offset(anchor_offset);
251                }
252                let mut events = Vec::new();
253                while let Some(event) = parser.next_event() {
254                    match event {
255                        Ok(event) => events.push(event),
256                        Err(error) => {
257                            return Err(self.contextualize_include_error(error, include_str));
258                        }
259                    }
260                }
261                (events, parser.anchor_offset())
262            }
263            Cow::Owned(content) => {
264                let mut parser =
265                    Parser::new_from_iter(content.chars().collect::<Vec<_>>().into_iter());
266                if let Some(anchor_offset) = inherited_anchor_offset {
267                    parser.set_anchor_offset(anchor_offset);
268                }
269                let mut events = Vec::new();
270                while let Some(event) = parser.next_event() {
271                    match event {
272                        Ok(event) => events.push(event),
273                        Err(error) => {
274                            return Err(self.contextualize_include_error(error, include_str));
275                        }
276                    }
277                }
278                (events, parser.anchor_offset())
279            }
280        };
281
282        self.push_replay_parser(
283            ReplayParser::new(events, next_anchor_offset),
284            include_str.into(),
285        );
286        Ok(())
287    }
288
289    fn contextualize_include_error(&self, error: ScanError, include_str: &str) -> ScanError {
290        let mut source_stack = self.stack();
291        source_stack.push(include_str.into());
292        error.with_source_stack(source_stack)
293    }
294
295    fn prepare_for_push(&mut self) {
296        if matches!(self.current.as_ref(), Some((Event::StreamEnd, _))) {
297            self.current = None;
298        }
299    }
300
301    /// Push a string parser onto the stack.
302    ///
303    /// The pushed parser inherits the current anchor offset so anchors remain unique across stacked
304    /// sources. `name` is returned by [`Self::stack`] for diagnostics.
305    pub fn push_str_parser(&mut self, mut parser: Parser<'input, StrInput<'input>>, name: String) {
306        self.prepare_for_push();
307        if let Some(parent) = self.parsers.last() {
308            parser.set_anchor_offset(parent.anchor_offset());
309        }
310        self.parsers.push(AnyParser::String { parser, name });
311    }
312
313    /// Push an iterator-backed parser onto the stack.
314    ///
315    /// The pushed parser inherits the current anchor offset so anchors remain unique across stacked
316    /// sources. `name` is returned by [`Self::stack`] for diagnostics.
317    pub fn push_iter_parser(
318        &mut self,
319        mut parser: Parser<'static, BufferedInput<I>>,
320        name: String,
321    ) {
322        self.prepare_for_push();
323        if let Some(parent) = self.parsers.last() {
324            parser.set_anchor_offset(parent.anchor_offset());
325        }
326        self.parsers.push(AnyParser::Iter { parser, name });
327    }
328
329    /// Push a custom-input parser onto the stack.
330    ///
331    /// The pushed parser inherits the current anchor offset so anchors remain unique across stacked
332    /// sources. `name` is returned by [`Self::stack`] for diagnostics.
333    pub fn push_custom_parser(&mut self, mut parser: Parser<'input, T>, name: String) {
334        self.prepare_for_push();
335        if let Some(parent) = self.parsers.last() {
336            parser.set_anchor_offset(parent.anchor_offset());
337        }
338        self.parsers.push(AnyParser::Custom { parser, name });
339    }
340
341    /// Push a replay parser onto the stack.
342    ///
343    /// Replay parsers are used for included content that has already been parsed into events.
344    /// `name` is returned by [`Self::stack`] for diagnostics.
345    pub fn push_replay_parser(&mut self, mut parser: ReplayParser<'input>, name: String) {
346        self.prepare_for_push();
347        if let Some(parent) = self.parsers.last() {
348            let inherited = parent.anchor_offset();
349            parser.set_anchor_offset(parser.anchor_offset().max(inherited));
350        }
351
352        self.parsers.push(AnyParser::Replay { parser, name });
353    }
354
355    /// Push a custom parser and set the first event that should be returned from it.
356    ///
357    /// This is used when the caller has already consumed the parser's first event before deciding
358    /// to place it on the stack.
359    pub fn push_custom_parser_with_current(
360        &mut self,
361        mut parser: Parser<'input, T>,
362        name: String,
363        current: (Event<'input>, Span),
364    ) {
365        self.prepare_for_push();
366        if let Some(parent) = self.parsers.last() {
367            parser.set_anchor_offset(parent.anchor_offset());
368        }
369        self.parsers.push(AnyParser::Custom { parser, name });
370        self.current = Some(current);
371    }
372
373    /// Return the anchor offset that a newly pushed parser should inherit.
374    #[must_use]
375    pub fn current_anchor_offset(&self) -> usize {
376        self.parsers.last().map_or(0, AnyParser::anchor_offset)
377    }
378
379    /// Return the names of the parsers currently in the stack, from bottom to top.
380    #[must_use]
381    pub fn stack(&self) -> Vec<String> {
382        self.parsers
383            .iter()
384            .map(|p| match p {
385                AnyParser::String { name, .. }
386                | AnyParser::Iter { name, .. }
387                | AnyParser::Custom { name, .. }
388                | AnyParser::Replay { name, .. } => name.clone(),
389            })
390            .collect()
391    }
392
393    fn contextualize_error(&self, error: ScanError) -> ScanError {
394        if self.parsers.len() > 1 {
395            error.with_source_stack(self.stack())
396        } else {
397            error
398        }
399    }
400
401    fn propagate_anchor_offset_from_popped(&mut self, popped: &AnyParser<'input, I, T>) {
402        if let Some(parent) = self.parsers.last_mut() {
403            let next_offset = parent.anchor_offset().max(popped.anchor_offset());
404            parent.set_anchor_offset(next_offset);
405        }
406    }
407
408    /// Pop the current parser and propagate its anchor offset to its parent.
409    ///
410    /// # Panics
411    /// Panics if the parser stack is empty.
412    #[track_caller]
413    fn pop_parser_and_propagate_anchor_offset(&mut self) {
414        let popped = self.parsers.pop().unwrap();
415        self.propagate_anchor_offset_from_popped(&popped);
416    }
417
418    fn next_event_impl(&mut self) -> Result<(Event<'input>, Span), ScanError> {
419        loop {
420            let Some(any_parser) = self.parsers.last_mut() else {
421                return Ok((
422                    Event::StreamEnd,
423                    Span::empty(crate::scanner::Marker::new(0, 1, 0)),
424                ));
425            };
426
427            let res = match any_parser {
428                AnyParser::String { parser, .. } => parser.next_event(),
429                AnyParser::Iter { parser, .. } => parser.next_event(),
430                AnyParser::Custom { parser, .. } => parser.next_event(),
431                AnyParser::Replay { parser, .. } => parser.next_event(),
432            };
433
434            match res {
435                Some(Ok((Event::StreamEnd, span))) => {
436                    if self.parsers.len() == 1 {
437                        self.parsers.pop();
438                        return Ok((Event::StreamEnd, span));
439                    }
440                    self.pop_parser_and_propagate_anchor_offset();
441                }
442                None => {
443                    if self.parsers.len() == 1 {
444                        self.parsers.pop();
445                        return Ok((
446                            Event::StreamEnd,
447                            Span::empty(crate::scanner::Marker::new(0, 1, 0)),
448                        ));
449                    }
450                    self.pop_parser_and_propagate_anchor_offset();
451                }
452                Some(Err(e)) => {
453                    let e = self.contextualize_error(e);
454                    self.pop_parser_and_propagate_anchor_offset();
455                    return e.into_result();
456                }
457                Some(Ok((Event::DocumentEnd, span))) => {
458                    if self.parsers.len() == 1 {
459                        return Ok((Event::DocumentEnd, span));
460                    }
461
462                    // Continue the parent parser if it has more documents.
463                    let peek_res = match self.parsers.last_mut().unwrap() {
464                        AnyParser::String { parser, .. } => parser.peek(),
465                        AnyParser::Iter { parser, .. } => parser.peek(),
466                        AnyParser::Custom { parser, .. } => parser.peek(),
467                        AnyParser::Replay { parser, .. } => parser.peek(),
468                    };
469
470                    match peek_res {
471                        Some(Ok((Event::StreamEnd, _))) | None => {
472                            self.pop_parser_and_propagate_anchor_offset();
473                        }
474                        Some(Ok(_)) => {
475                            let error = self.contextualize_error(ScanError::from_kind(
476                                span.start,
477                                ErrorKind::MultipleDocumentsUnsupported,
478                            ));
479                            self.pop_parser_and_propagate_anchor_offset();
480                            return Err(error);
481                        }
482                        Some(Err(e)) => {
483                            let e = self.contextualize_error(e);
484                            self.pop_parser_and_propagate_anchor_offset();
485                            return Err(e);
486                        }
487                    }
488                }
489                Some(Ok(event)) => {
490                    if self.parsers.len() > 1
491                        && matches!(event.0, Event::StreamStart | Event::DocumentStart(..))
492                    {
493                        continue;
494                    }
495                    return Ok(event);
496                }
497            }
498        }
499    }
500}
501
502impl<'input, I, T> Default for ParserStack<'input, I, T>
503where
504    I: Iterator<Item = char>,
505    T: BorrowedInput<'input>,
506{
507    fn default() -> Self {
508        Self::new()
509    }
510}
511
512impl<'input, I, T> ParserTrait<'input> for ParserStack<'input, I, T>
513where
514    I: Iterator<Item = char>,
515    T: BorrowedInput<'input>,
516{
517    fn peek(&mut self) -> Option<Result<&(Event<'input>, Span), ScanError>> {
518        if let Some(ref x) = self.current {
519            Some(Ok(x))
520        } else if let Some(error) = &self.current_error {
521            Some(Err(error.clone()))
522        } else {
523            if self.stream_end_emitted {
524                return None;
525            }
526            match self.next_event_impl() {
527                Ok(token) => {
528                    self.current = Some(token);
529                    Some(Ok(self.current.as_ref().unwrap()))
530                }
531                Err(e) => {
532                    self.current_error = Some(e.clone());
533                    Some(Err(e))
534                }
535            }
536        }
537    }
538
539    fn next_event(&mut self) -> Option<ParseResult<'input>> {
540        if let Some(error) = self.current_error.take() {
541            self.stream_end_emitted = true;
542            return Some(Err(error));
543        }
544
545        if let Some(token) = self.current.take() {
546            if let Event::StreamEnd = token.0 {
547                self.stream_end_emitted = true;
548            }
549            return Some(Ok(token));
550        }
551        if self.stream_end_emitted {
552            return None;
553        }
554        match self.next_event_impl() {
555            Ok(token) => {
556                if let Event::StreamEnd = token.0 {
557                    self.stream_end_emitted = true;
558                }
559                Some(Ok(token))
560            }
561            Err(e) => {
562                self.stream_end_emitted = true;
563                Some(Err(e))
564            }
565        }
566    }
567
568    fn load<R: SpannedEventReceiver<'input>>(
569        &mut self,
570        recv: &mut R,
571        multi: bool,
572    ) -> Result<(), ScanError> {
573        while let Some(res) = self.next_event() {
574            // Fetch the next event from the active stack entry.
575            let (ev, span) = res?;
576
577            // Track whether to stop based on `multi`.
578            let is_doc_end = matches!(ev, Event::DocumentEnd);
579            let is_stream_end = matches!(ev, Event::StreamEnd);
580
581            recv.on_event(ev, span);
582
583            if is_stream_end {
584                break;
585            }
586
587            // Stop after one document when multi-document parsing is disabled.
588            if !multi && is_doc_end {
589                break;
590            }
591        }
592
593        Ok(())
594    }
595}
596
597impl<'input, I, T> Iterator for ParserStack<'input, I, T>
598where
599    I: Iterator<Item = char>,
600    T: BorrowedInput<'input>,
601{
602    type Item = Result<(Event<'input>, Span), ScanError>;
603
604    fn next(&mut self) -> Option<Self::Item> {
605        self.next_event()
606    }
607}
608
609impl<'input, I, T> core::iter::FusedIterator for ParserStack<'input, I, T>
610where
611    I: Iterator<Item = char>,
612    T: BorrowedInput<'input>,
613{
614}