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    fn pop_parser_and_propagate_anchor_offset(&mut self) {
409        let popped = self.parsers.pop().unwrap();
410        self.propagate_anchor_offset_from_popped(&popped);
411    }
412
413    fn next_event_impl(&mut self) -> Result<(Event<'input>, Span), ScanError> {
414        loop {
415            let Some(any_parser) = self.parsers.last_mut() else {
416                return Ok((
417                    Event::StreamEnd,
418                    Span::empty(crate::scanner::Marker::new(0, 1, 0)),
419                ));
420            };
421
422            let res = match any_parser {
423                AnyParser::String { parser, .. } => parser.next_event(),
424                AnyParser::Iter { parser, .. } => parser.next_event(),
425                AnyParser::Custom { parser, .. } => parser.next_event(),
426                AnyParser::Replay { parser, .. } => parser.next_event(),
427            };
428
429            match res {
430                Some(Ok((Event::StreamEnd, span))) => {
431                    if self.parsers.len() == 1 {
432                        self.parsers.pop();
433                        return Ok((Event::StreamEnd, span));
434                    }
435                    self.pop_parser_and_propagate_anchor_offset();
436                }
437                None => {
438                    if self.parsers.len() == 1 {
439                        self.parsers.pop();
440                        return Ok((
441                            Event::StreamEnd,
442                            Span::empty(crate::scanner::Marker::new(0, 1, 0)),
443                        ));
444                    }
445                    self.pop_parser_and_propagate_anchor_offset();
446                }
447                Some(Err(e)) => {
448                    let e = self.contextualize_error(e);
449                    self.pop_parser_and_propagate_anchor_offset();
450                    return e.into_result();
451                }
452                Some(Ok((Event::DocumentEnd, span))) => {
453                    if self.parsers.len() == 1 {
454                        return Ok((Event::DocumentEnd, span));
455                    }
456
457                    // Continue the parent parser if it has more documents.
458                    let peek_res = match self.parsers.last_mut().unwrap() {
459                        AnyParser::String { parser, .. } => parser.peek(),
460                        AnyParser::Iter { parser, .. } => parser.peek(),
461                        AnyParser::Custom { parser, .. } => parser.peek(),
462                        AnyParser::Replay { parser, .. } => parser.peek(),
463                    };
464
465                    match peek_res {
466                        Some(Ok((Event::StreamEnd, _))) | None => {
467                            self.pop_parser_and_propagate_anchor_offset();
468                        }
469                        Some(Ok(_)) => {
470                            let error = self.contextualize_error(ScanError::from_kind(
471                                span.start,
472                                ErrorKind::MultipleDocumentsUnsupported,
473                            ));
474                            self.pop_parser_and_propagate_anchor_offset();
475                            return Err(error);
476                        }
477                        Some(Err(e)) => {
478                            let e = self.contextualize_error(e);
479                            self.pop_parser_and_propagate_anchor_offset();
480                            return Err(e);
481                        }
482                    }
483                }
484                Some(Ok(event)) => {
485                    if self.parsers.len() > 1
486                        && matches!(event.0, Event::StreamStart | Event::DocumentStart(..))
487                    {
488                        continue;
489                    }
490                    return Ok(event);
491                }
492            }
493        }
494    }
495}
496
497impl<'input, I, T> Default for ParserStack<'input, I, T>
498where
499    I: Iterator<Item = char>,
500    T: BorrowedInput<'input>,
501{
502    fn default() -> Self {
503        Self::new()
504    }
505}
506
507impl<'input, I, T> ParserTrait<'input> for ParserStack<'input, I, T>
508where
509    I: Iterator<Item = char>,
510    T: BorrowedInput<'input>,
511{
512    fn peek(&mut self) -> Option<Result<&(Event<'input>, Span), ScanError>> {
513        if let Some(ref x) = self.current {
514            Some(Ok(x))
515        } else if let Some(error) = &self.current_error {
516            Some(Err(error.clone()))
517        } else {
518            if self.stream_end_emitted {
519                return None;
520            }
521            match self.next_event_impl() {
522                Ok(token) => {
523                    self.current = Some(token);
524                    Some(Ok(self.current.as_ref().unwrap()))
525                }
526                Err(e) => {
527                    self.current_error = Some(e.clone());
528                    Some(Err(e))
529                }
530            }
531        }
532    }
533
534    fn next_event(&mut self) -> Option<ParseResult<'input>> {
535        if let Some(error) = self.current_error.take() {
536            self.stream_end_emitted = true;
537            return Some(Err(error));
538        }
539
540        if let Some(token) = self.current.take() {
541            if let Event::StreamEnd = token.0 {
542                self.stream_end_emitted = true;
543            }
544            return Some(Ok(token));
545        }
546        if self.stream_end_emitted {
547            return None;
548        }
549        match self.next_event_impl() {
550            Ok(token) => {
551                if let Event::StreamEnd = token.0 {
552                    self.stream_end_emitted = true;
553                }
554                Some(Ok(token))
555            }
556            Err(e) => {
557                self.stream_end_emitted = true;
558                Some(Err(e))
559            }
560        }
561    }
562
563    fn load<R: SpannedEventReceiver<'input>>(
564        &mut self,
565        recv: &mut R,
566        multi: bool,
567    ) -> Result<(), ScanError> {
568        while let Some(res) = self.next_event() {
569            // Fetch the next event from the active stack entry.
570            let (ev, span) = res?;
571
572            // Track whether to stop based on `multi`.
573            let is_doc_end = matches!(ev, Event::DocumentEnd);
574            let is_stream_end = matches!(ev, Event::StreamEnd);
575
576            recv.on_event(ev, span);
577
578            if is_stream_end {
579                break;
580            }
581
582            // Stop after one document when multi-document parsing is disabled.
583            if !multi && is_doc_end {
584                break;
585            }
586        }
587
588        Ok(())
589    }
590}
591
592impl<'input, I, T> Iterator for ParserStack<'input, I, T>
593where
594    I: Iterator<Item = char>,
595    T: BorrowedInput<'input>,
596{
597    type Item = Result<(Event<'input>, Span), ScanError>;
598
599    fn next(&mut self) -> Option<Self::Item> {
600        self.next_event()
601    }
602}
603
604impl<'input, I, T> core::iter::FusedIterator for ParserStack<'input, I, T>
605where
606    I: Iterator<Item = char>,
607    T: BorrowedInput<'input>,
608{
609}