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