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    /// Stack depths and end spans of nested documents whose trailing comments are being read.
176    pending_document_ends: Vec<(usize, Span)>,
177    current: Option<(Event<'input>, Span)>,
178    current_error: Option<ScanError>,
179    stream_end_emitted: bool,
180    #[allow(clippy::type_complexity)]
181    include_resolver: Option<Box<dyn FnMut(&str) -> Result<Cow<'input, str>, ScanError> + 'input>>,
182}
183
184impl<'input, I, T> ParserStack<'input, I, T>
185where
186    I: Iterator<Item = char>,
187    T: BorrowedInput<'input>,
188{
189    /// Creates a new, empty parser stack.
190    #[must_use]
191    pub fn new() -> Self {
192        Self::with_options(Options::default())
193    }
194
195    /// Creates a new, empty parser stack with the supplied parsing options.
196    ///
197    /// Options are applied to sources parsed internally by [`Self::push_include`]. When comment
198    /// emission is disabled, the stack also suppresses comment events from parsers and replay
199    /// streams supplied by the caller. Caller-supplied parsers retain their own scanning options;
200    /// construct them with comment emission disabled as well to avoid capturing comments before
201    /// the stack filters their events.
202    #[must_use]
203    pub fn with_options(options: Options) -> Self {
204        Self {
205            options,
206            parsers: Vec::new(),
207            pending_document_ends: Vec::new(),
208            current: None,
209            current_error: None,
210            stream_end_emitted: false,
211            include_resolver: None,
212        }
213    }
214
215    /// Set the resolver used by [`Self::push_include`].
216    ///
217    /// The resolver receives the include name and returns the included YAML source text.
218    pub fn set_resolver(
219        &mut self,
220        mut resolver: impl FnMut(&str) -> Result<String, ScanError> + 'input,
221    ) {
222        self.include_resolver = Some(Box::new(move |name| resolver(name).map(Cow::Owned)));
223    }
224
225    /// Set an include resolver whose source text can be borrowed for the stack's input lifetime.
226    ///
227    /// Unlike [`Self::set_resolver`], this path lets scalar, comment, anchor, and tag token text in
228    /// included documents borrow directly from the returned source. The included document is still
229    /// validated eagerly so resolution errors retain the same timing and source-stack context.
230    pub fn set_borrowed_resolver(
231        &mut self,
232        mut resolver: impl FnMut(&str) -> Result<&'input str, ScanError> + 'input,
233    ) {
234        self.include_resolver = Some(Box::new(move |name| resolver(name).map(Cow::Borrowed)));
235    }
236
237    /// Resolve an include by name and push the resulting parser onto the stack.
238    ///
239    /// Comment events from the included content follow the setting passed to
240    /// [`Self::with_options`]. Their spans are local to the included content returned by the
241    /// resolver, matching the existing behavior for all included document events.
242    ///
243    /// # Errors
244    /// Returns `ScanError` if no resolver is configured, include resolution fails, or the
245    /// included content cannot be parsed.
246    pub fn push_include(&mut self, include_str: &str) -> Result<(), ScanError> {
247        let resolved = match &mut self.include_resolver {
248            Some(resolver) => resolver(include_str),
249            None => {
250                return Err(self.contextualize_include_error(
251                    ScanError::from_kind(
252                        crate::scanner::Marker::new(0, 1, 0),
253                        ErrorKind::MissingIncludeResolver,
254                    ),
255                    include_str,
256                ));
257            }
258        };
259        let content = match resolved {
260            Ok(content) => content,
261            Err(error) => return Err(self.contextualize_include_error(error, include_str)),
262        };
263        let inherited_anchor_offset = self.parsers.last().map(AnyParser::anchor_offset);
264
265        let (events, next_anchor_offset) = match content {
266            Cow::Borrowed(content) => {
267                let mut parser = Parser::new_from_str_with_options(content, self.options.clone());
268                if let Some(anchor_offset) = inherited_anchor_offset {
269                    parser.set_anchor_offset(anchor_offset);
270                }
271                let mut events = Vec::new();
272                while let Some(event) = parser.next_event() {
273                    match event {
274                        Ok(event) => events.push(event),
275                        Err(error) => {
276                            return Err(self.contextualize_include_error(error, include_str));
277                        }
278                    }
279                }
280                (events, parser.anchor_offset())
281            }
282            Cow::Owned(content) => {
283                let mut parser =
284                    Parser::new_from_iter_with_options(content.chars(), self.options.clone());
285                if let Some(anchor_offset) = inherited_anchor_offset {
286                    parser.set_anchor_offset(anchor_offset);
287                }
288                let mut events = Vec::new();
289                while let Some(event) = parser.next_event() {
290                    match event {
291                        Ok(event) => events.push(event),
292                        Err(error) => {
293                            return Err(self.contextualize_include_error(error, include_str));
294                        }
295                    }
296                }
297                (events, parser.anchor_offset())
298            }
299        };
300
301        self.push_replay_parser(
302            ReplayParser::new(events, next_anchor_offset),
303            include_str.into(),
304        );
305        Ok(())
306    }
307
308    fn contextualize_include_error(&self, error: ScanError, include_str: &str) -> ScanError {
309        let mut source_stack = self.stack();
310        source_stack.push(include_str.into());
311        error.with_source_stack(source_stack)
312    }
313
314    fn prepare_for_push(&mut self) {
315        if matches!(self.current.as_ref(), Some((Event::StreamEnd, _))) {
316            self.current = None;
317        }
318    }
319
320    /// Push a string parser onto the stack.
321    ///
322    /// The pushed parser inherits the current anchor offset so anchors remain unique across stacked
323    /// sources. `name` is returned by [`Self::stack`] for diagnostics.
324    pub fn push_str_parser(&mut self, mut parser: Parser<'input, StrInput<'input>>, name: String) {
325        self.prepare_for_push();
326        if let Some(parent) = self.parsers.last() {
327            parser.set_anchor_offset(parent.anchor_offset());
328        }
329        self.parsers.push(AnyParser::String { parser, name });
330    }
331
332    /// Push an iterator-backed parser onto the stack.
333    ///
334    /// The pushed parser inherits the current anchor offset so anchors remain unique across stacked
335    /// sources. `name` is returned by [`Self::stack`] for diagnostics.
336    pub fn push_iter_parser(
337        &mut self,
338        mut parser: Parser<'static, BufferedInput<I>>,
339        name: String,
340    ) {
341        self.prepare_for_push();
342        if let Some(parent) = self.parsers.last() {
343            parser.set_anchor_offset(parent.anchor_offset());
344        }
345        self.parsers.push(AnyParser::Iter { parser, name });
346    }
347
348    /// Push a custom-input parser onto the stack.
349    ///
350    /// The pushed parser inherits the current anchor offset so anchors remain unique across stacked
351    /// sources. `name` is returned by [`Self::stack`] for diagnostics.
352    pub fn push_custom_parser(&mut self, mut parser: Parser<'input, T>, name: String) {
353        self.prepare_for_push();
354        if let Some(parent) = self.parsers.last() {
355            parser.set_anchor_offset(parent.anchor_offset());
356        }
357        self.parsers.push(AnyParser::Custom { parser, name });
358    }
359
360    /// Push a replay parser onto the stack.
361    ///
362    /// Replay parsers are used for included content that has already been parsed into events.
363    /// `name` is returned by [`Self::stack`] for diagnostics.
364    pub fn push_replay_parser(&mut self, mut parser: ReplayParser<'input>, name: String) {
365        self.prepare_for_push();
366        if let Some(parent) = self.parsers.last() {
367            let inherited = parent.anchor_offset();
368            parser.set_anchor_offset(parser.anchor_offset().max(inherited));
369        }
370
371        self.parsers.push(AnyParser::Replay { parser, name });
372    }
373
374    /// Push a custom parser and set the first event that should be returned from it.
375    ///
376    /// This is used when the caller has already consumed the parser's first event before deciding
377    /// to place it on the stack.
378    pub fn push_custom_parser_with_current(
379        &mut self,
380        mut parser: Parser<'input, T>,
381        name: String,
382        current: (Event<'input>, Span),
383    ) {
384        self.prepare_for_push();
385        if let Some(parent) = self.parsers.last() {
386            parser.set_anchor_offset(parent.anchor_offset());
387        }
388        self.parsers.push(AnyParser::Custom { parser, name });
389        self.current = if self.options.emit_comments || !matches!(current.0, Event::Comment(..)) {
390            Some(current)
391        } else {
392            None
393        };
394    }
395
396    /// Return the anchor offset that a newly pushed parser should inherit.
397    #[must_use]
398    pub fn current_anchor_offset(&self) -> usize {
399        self.parsers.last().map_or(0, AnyParser::anchor_offset)
400    }
401
402    /// Return the names of the parsers currently in the stack, from bottom to top.
403    #[must_use]
404    pub fn stack(&self) -> Vec<String> {
405        self.parsers
406            .iter()
407            .map(|p| match p {
408                AnyParser::String { name, .. }
409                | AnyParser::Iter { name, .. }
410                | AnyParser::Custom { name, .. }
411                | AnyParser::Replay { name, .. } => name.clone(),
412            })
413            .collect()
414    }
415
416    fn contextualize_error(&self, error: ScanError) -> ScanError {
417        if self.parsers.len() > 1 {
418            error.with_source_stack(self.stack())
419        } else {
420            error
421        }
422    }
423
424    fn propagate_anchor_offset_from_popped(&mut self, popped: &AnyParser<'input, I, T>) {
425        if let Some(parent) = self.parsers.last_mut() {
426            let next_offset = parent.anchor_offset().max(popped.anchor_offset());
427            parent.set_anchor_offset(next_offset);
428        }
429    }
430
431    /// Pop the current parser and propagate its anchor offset to its parent.
432    ///
433    /// # Panics
434    /// Panics if the parser stack is empty.
435    #[track_caller]
436    fn pop_parser_and_propagate_anchor_offset(&mut self) {
437        if self
438            .pending_document_ends
439            .last()
440            .is_some_and(|(depth, _)| *depth == self.parsers.len())
441        {
442            self.pending_document_ends.pop();
443        }
444        let popped = self.parsers.pop().unwrap();
445        self.propagate_anchor_offset_from_popped(&popped);
446    }
447
448    fn next_event_impl(&mut self) -> Result<(Event<'input>, Span), ScanError> {
449        loop {
450            let Some(any_parser) = self.parsers.last_mut() else {
451                return Ok((
452                    Event::StreamEnd,
453                    Span::empty(crate::scanner::Marker::new(0, 1, 0)),
454                ));
455            };
456
457            let res = match any_parser {
458                AnyParser::String { parser, .. } => parser.next_event(),
459                AnyParser::Iter { parser, .. } => parser.next_event(),
460                AnyParser::Custom { parser, .. } => parser.next_event(),
461                AnyParser::Replay { parser, .. } => parser.next_event(),
462            };
463
464            if let Some(&(depth, span)) = self.pending_document_ends.last() {
465                if depth == self.parsers.len()
466                    && matches!(&res, Some(Ok((event, _)))
467                        if !matches!(event, Event::Comment(..) | Event::StreamEnd))
468                {
469                    let error = self.contextualize_error(ScanError::from_kind(
470                        span.start,
471                        ErrorKind::MultipleDocumentsUnsupported,
472                    ));
473                    self.pop_parser_and_propagate_anchor_offset();
474                    return Err(error);
475                }
476            }
477
478            match res {
479                Some(Ok((Event::StreamEnd, span))) => {
480                    if self.parsers.len() == 1 {
481                        self.parsers.pop();
482                        return Ok((Event::StreamEnd, span));
483                    }
484                    self.pop_parser_and_propagate_anchor_offset();
485                }
486                None => {
487                    if self.parsers.len() == 1 {
488                        self.parsers.pop();
489                        return Ok((
490                            Event::StreamEnd,
491                            Span::empty(crate::scanner::Marker::new(0, 1, 0)),
492                        ));
493                    }
494                    self.pop_parser_and_propagate_anchor_offset();
495                }
496                Some(Err(e)) => {
497                    let e = self.contextualize_error(e);
498                    self.pop_parser_and_propagate_anchor_offset();
499                    return e.into_result();
500                }
501                Some(Ok((Event::DocumentEnd, span))) => {
502                    if self.parsers.len() == 1 {
503                        return Ok((Event::DocumentEnd, span));
504                    }
505
506                    // Emit or suppress trailing comments through the normal event path, while
507                    // retaining this source's end span across calls and pushes of other parsers.
508                    self.pending_document_ends.push((self.parsers.len(), span));
509                }
510                Some(Ok(event)) => {
511                    if !self.options.emit_comments && matches!(event.0, Event::Comment(..)) {
512                        continue;
513                    }
514                    if self.parsers.len() > 1
515                        && matches!(event.0, Event::StreamStart | Event::DocumentStart(..))
516                    {
517                        continue;
518                    }
519                    return Ok(event);
520                }
521            }
522        }
523    }
524}
525
526impl<'input, I, T> Default for ParserStack<'input, I, T>
527where
528    I: Iterator<Item = char>,
529    T: BorrowedInput<'input>,
530{
531    fn default() -> Self {
532        Self::new()
533    }
534}
535
536impl<'input, I, T> ParserTrait<'input> for ParserStack<'input, I, T>
537where
538    I: Iterator<Item = char>,
539    T: BorrowedInput<'input>,
540{
541    fn peek(&mut self) -> Option<Result<&(Event<'input>, Span), ScanError>> {
542        if let Some(ref x) = self.current {
543            Some(Ok(x))
544        } else if let Some(error) = &self.current_error {
545            Some(Err(error.clone()))
546        } else {
547            if self.stream_end_emitted {
548                return None;
549            }
550            match self.next_event_impl() {
551                Ok(token) => {
552                    self.current = Some(token);
553                    Some(Ok(self.current.as_ref().unwrap()))
554                }
555                Err(e) => {
556                    self.current_error = Some(e.clone());
557                    Some(Err(e))
558                }
559            }
560        }
561    }
562
563    fn next_event(&mut self) -> Option<ParseResult<'input>> {
564        if let Some(error) = self.current_error.take() {
565            self.stream_end_emitted = true;
566            return Some(Err(error));
567        }
568
569        if let Some(token) = self.current.take() {
570            if let Event::StreamEnd = token.0 {
571                self.stream_end_emitted = true;
572            }
573            return Some(Ok(token));
574        }
575        if self.stream_end_emitted {
576            return None;
577        }
578        match self.next_event_impl() {
579            Ok(token) => {
580                if let Event::StreamEnd = token.0 {
581                    self.stream_end_emitted = true;
582                }
583                Some(Ok(token))
584            }
585            Err(e) => {
586                self.stream_end_emitted = true;
587                Some(Err(e))
588            }
589        }
590    }
591
592    fn load<R: SpannedEventReceiver<'input>>(
593        &mut self,
594        recv: &mut R,
595        multi: bool,
596    ) -> Result<(), ScanError> {
597        while let Some(res) = self.next_event() {
598            // Fetch the next event from the active stack entry.
599            let (ev, span) = res?;
600
601            // Track whether to stop based on `multi`.
602            let is_doc_end = matches!(ev, Event::DocumentEnd);
603            let is_stream_end = matches!(ev, Event::StreamEnd);
604
605            recv.on_event(ev, span);
606
607            if is_stream_end {
608                break;
609            }
610
611            // Stop after one document when multi-document parsing is disabled.
612            if !multi && is_doc_end {
613                break;
614            }
615        }
616
617        Ok(())
618    }
619}
620
621impl<'input, I, T> Iterator for ParserStack<'input, I, T>
622where
623    I: Iterator<Item = char>,
624    T: BorrowedInput<'input>,
625{
626    type Item = Result<(Event<'input>, Span), ScanError>;
627
628    fn next(&mut self) -> Option<Self::Item> {
629        self.next_event()
630    }
631}
632
633impl<'input, I, T> core::iter::FusedIterator for ParserStack<'input, I, T>
634where
635    I: Iterator<Item = char>,
636    T: BorrowedInput<'input>,
637{
638}