Skip to main content

saphyr_parser/
scanner.rs

1//! Home to the YAML Scanner.
2//!
3//! The scanner is the lowest-level parsing utility. It is the lexer / tokenizer, reading input a
4//! character at a time and emitting tokens that can later be interpreted by the [`crate::parser`]
5//! to check for more context and validity.
6//!
7//! Due to the grammar of YAML, the scanner has to have some context and is not error-free.
8
9#![allow(clippy::cast_possible_wrap)]
10#![allow(clippy::cast_sign_loss)]
11
12use alloc::{
13    borrow::{Cow, ToOwned},
14    collections::VecDeque,
15    string::String,
16    vec::Vec,
17};
18use core::char;
19
20use thiserror::Error;
21
22use crate::{
23    char_traits::{
24        as_hex, is_anchor_char, is_blank_or_breakz, is_break, is_breakz, is_flow, is_hex,
25        is_tag_char, is_uri_char, is_yaml_non_break,
26    },
27    input::{Input, SkipTabs},
28};
29
30/// The encoding of the input. Currently, only UTF-8 is supported.
31#[derive(Clone, Copy, PartialEq, Debug, Eq)]
32pub enum TEncoding {
33    /// UTF-8 encoding.
34    Utf8,
35}
36
37/// The style as which the scalar was written in the YAML document.
38#[derive(Clone, Copy, PartialEq, Debug, Eq, Hash, PartialOrd, Ord)]
39pub enum ScalarStyle {
40    /// A YAML plain scalar.
41    Plain,
42    /// A YAML single quoted scalar.
43    SingleQuoted,
44    /// A YAML double quoted scalar.
45    DoubleQuoted,
46
47    /// A YAML literal block (`|` block).
48    ///
49    /// See [8.1.2](https://yaml.org/spec/1.2.2/#812-literal-style).
50    /// In literal blocks, any indented character is content, including white space characters.
51    /// There is no way to escape characters, nor to break a long line.
52    Literal,
53    /// A YAML folded block (`>` block).
54    ///
55    /// See [8.1.3](https://yaml.org/spec/1.2.2/#813-folded-style).
56    /// In folded blocks, any indented character is content, including white space characters.
57    /// There is no way to escape characters. Content is subject to line folding, allowing breaking
58    /// long lines.
59    Folded,
60}
61
62/// A location in a yaml document.
63#[derive(Clone, Copy, PartialEq, Debug, Eq, Default)]
64pub struct Marker {
65    /// The index (in characters) in the input string.
66    index: usize,
67    /// The line (1-indexed).
68    line: usize,
69    /// The column (0-indexed, in characters).
70    col: usize,
71}
72
73impl Marker {
74    /// Create a new [`Marker`] at the given position.
75    #[must_use]
76    pub fn new(index: usize, line: usize, col: usize) -> Marker {
77        Marker { index, line, col }
78    }
79
80    /// Return the index (in characters) of the marker in the source.
81    #[must_use]
82    pub fn index(&self) -> usize {
83        self.index
84    }
85
86    /// Return the line (1-indexed) of the marker in the source.
87    #[must_use]
88    pub fn line(&self) -> usize {
89        self.line
90    }
91
92    /// Return the column (0-indexed, in characters) of the marker in the source.
93    #[must_use]
94    pub fn col(&self) -> usize {
95        self.col
96    }
97}
98
99/// A range of locations in a Yaml document.
100#[derive(Clone, Copy, PartialEq, Debug, Eq, Default)]
101pub struct Span {
102    /// The start (inclusive) of the range.
103    pub start: Marker,
104    /// The end (exclusive) of the range.
105    pub end: Marker,
106}
107
108impl Span {
109    /// Create a new [`Span`] for the given range.
110    #[must_use]
111    pub fn new(start: Marker, end: Marker) -> Span {
112        Span { start, end }
113    }
114
115    /// Create a empty [`Span`] at a given location.
116    ///
117    /// An empty span doesn't contain any characters, but its position may still be meaningful.
118    /// For example, for an indented sequence [`SequenceEnd`] has a location but an empty span.
119    ///
120    /// [`SequenceEnd`]: crate::Event::SequenceEnd
121    #[must_use]
122    pub fn empty(mark: Marker) -> Span {
123        Span {
124            start: mark,
125            end: mark,
126        }
127    }
128
129    /// Return the length of the span (in characters).
130    #[must_use]
131    pub fn len(&self) -> usize {
132        self.end.index - self.start.index
133    }
134
135    /// Return whether the [`Span`] has a length of zero.
136    #[must_use]
137    pub fn is_empty(&self) -> bool {
138        self.len() == 0
139    }
140}
141
142/// An error that occurred while scanning.
143#[derive(Clone, PartialEq, Debug, Eq, Error)]
144#[error("{} at byte {} line {} column {}", .info, .mark.index, .mark.line, .mark.col + 1,)]
145pub struct ScanError {
146    /// The position at which the error happened in the source.
147    mark: Marker,
148    /// Human-readable details about the error.
149    info: String,
150}
151
152impl ScanError {
153    /// Create a new error from a location and an error string.
154    #[must_use]
155    pub fn new(loc: Marker, info: String) -> ScanError {
156        ScanError { mark: loc, info }
157    }
158
159    /// Convenience alias for string slices.
160    #[must_use]
161    pub fn new_str(loc: Marker, info: &str) -> ScanError {
162        ScanError {
163            mark: loc,
164            info: info.to_owned(),
165        }
166    }
167
168    /// Return the marker pointing to the error in the source.
169    #[must_use]
170    pub fn marker(&self) -> &Marker {
171        &self.mark
172    }
173
174    /// Return the information string describing the error that happened.
175    #[must_use]
176    pub fn info(&self) -> &str {
177        self.info.as_ref()
178    }
179}
180
181/// The contents of a scanner token.
182#[derive(Clone, PartialEq, Debug, Eq)]
183pub enum TokenType<'input> {
184    /// The start of the stream. Sent first, before even [`TokenType::DocumentStart`].
185    StreamStart(TEncoding),
186    /// The end of the stream, EOF.
187    StreamEnd,
188    /// A YAML version directive.
189    VersionDirective(
190        /// Major
191        u32,
192        /// Minor
193        u32,
194    ),
195    /// A YAML tag directive (e.g.: `!!str`, `!foo!bar`, ...).
196    TagDirective(
197        /// Handle
198        Cow<'input, str>,
199        /// Prefix
200        Cow<'input, str>,
201    ),
202    /// The start of a YAML document (`---`).
203    DocumentStart,
204    /// The end of a YAML document (`...`).
205    DocumentEnd,
206    /// A BOM at the start of a line.
207    ByteOrderMark,
208    /// The start of a sequence block.
209    ///
210    /// Sequence blocks are arrays starting with a `-`.
211    BlockSequenceStart,
212    /// The start of a sequence mapping.
213    ///
214    /// Sequence mappings are "dictionaries" with "key: value" entries.
215    BlockMappingStart,
216    /// End of the corresponding `BlockSequenceStart` or `BlockMappingStart`.
217    BlockEnd,
218    /// Start of an inline sequence (`[ a, b ]`).
219    FlowSequenceStart,
220    /// End of an inline sequence.
221    FlowSequenceEnd,
222    /// Start of an inline mapping (`{ a: b, c: d }`).
223    FlowMappingStart,
224    /// End of an inline mapping.
225    FlowMappingEnd,
226    /// An entry in a block sequence (c.f.: [`TokenType::BlockSequenceStart`]).
227    BlockEntry,
228    /// An entry in a flow sequence (c.f.: [`TokenType::FlowSequenceStart`]).
229    FlowEntry,
230    /// A key in a mapping.
231    Key,
232    /// A value in a mapping.
233    Value,
234    /// A reference to an anchor.
235    Alias(Cow<'input, str>),
236    /// A YAML anchor (`&`/`*`).
237    Anchor(Cow<'input, str>),
238    /// A YAML tag (starting with bangs `!`).
239    Tag(
240        /// The handle of the tag.
241        String,
242        /// The suffix of the tag.
243        String,
244    ),
245    /// A regular YAML scalar.
246    Scalar(ScalarStyle, Cow<'input, str>),
247    /// A reserved YAML directive.
248    ReservedDirective(
249        /// Name
250        String,
251        /// Parameters
252        Vec<String>,
253    ),
254}
255
256/// A scanner token.
257#[derive(Clone, PartialEq, Debug, Eq)]
258pub struct Token<'input>(pub Span, pub TokenType<'input>);
259
260/// A scalar that was parsed and may correspond to a simple key.
261///
262/// Upon scanning the following yaml:
263/// ```yaml
264/// a: b
265/// ```
266/// We do not know that `a` is a key for a map until we have reached the following `:`. For this
267/// YAML, we would store `a` as a scalar token in the [`Scanner`], but not emit it yet. It would be
268/// kept inside the scanner until more context is fetched and we are able to know whether it is a
269/// plain scalar or a key.
270///
271/// For example, see the following 2 yaml documents:
272/// ```yaml
273/// ---
274/// a: b # Here, `a` is a key.
275/// ...
276/// ---
277/// a # Here, `a` is a plain scalar.
278/// ...
279/// ```
280/// An instance of [`SimpleKey`] is created in the [`Scanner`] when such ambiguity occurs.
281///
282/// In both documents, scanning `a` would lead to the creation of a [`SimpleKey`] with
283/// [`Self::possible`] set to `true`. The token for `a` would be pushed in the [`Scanner`] but not
284/// yet emitted. Instead, more context would be fetched (through [`Scanner::fetch_more_tokens`]).
285///
286/// In the first document, upon reaching the `:`, the [`SimpleKey`] would be inspected and our
287/// scalar `a` since it is a possible key, would be "turned" into a key. This is done by prepending
288/// a [`TokenType::Key`] to our scalar token in the [`Scanner`]. This way, the
289/// [`crate::parser::Parser`] would read the [`TokenType::Key`] token before the
290/// [`TokenType::Scalar`] token.
291///
292/// In the second document however, reaching the EOF would stale the [`SimpleKey`] and no
293/// [`TokenType::Key`] would be emitted by the scanner.
294#[derive(Clone, PartialEq, Debug, Eq)]
295struct SimpleKey {
296    /// Whether the token this [`SimpleKey`] refers to may still be a key.
297    ///
298    /// Sometimes, when we have more context, we notice that what we thought could be a key no
299    /// longer can be. In that case, [`Self::possible`] is set to `false`.
300    ///
301    /// For instance, let us consider the following invalid YAML:
302    /// ```yaml
303    /// key
304    ///   : value
305    /// ```
306    /// Upon reading the `\n` after `key`, the [`SimpleKey`] that was created for `key` is staled
307    /// and [`Self::possible`] set to `false`.
308    possible: bool,
309    /// Whether the token this [`SimpleKey`] refers to is required to be a key.
310    ///
311    /// With more context, we may know for sure that the token must be a key. If the YAML is
312    /// invalid, it may happen that the token be deemed not a key. In such event, an error has to
313    /// be raised. This boolean helps us know when to raise such error.
314    ///
315    /// TODO(ethiraric, 30/12/2023): Example of when this happens.
316    required: bool,
317    /// The index of the token referred to by the [`SimpleKey`].
318    ///
319    /// This is the index in the scanner, which takes into account both the tokens that have been
320    /// emitted and those about to be emitted. See [`Scanner::tokens_parsed`] and
321    /// [`Scanner::tokens`] for more details.
322    token_number: usize,
323    /// The position at which the token the [`SimpleKey`] refers to is.
324    mark: Marker,
325}
326
327impl SimpleKey {
328    /// Create a new [`SimpleKey`] at the given `Marker` and with the given flow level.
329    fn new(mark: Marker) -> SimpleKey {
330        SimpleKey {
331            possible: false,
332            required: false,
333            token_number: 0,
334            mark,
335        }
336    }
337}
338
339/// An indentation level on the stack of indentations.
340#[derive(Clone, Debug, Default)]
341struct Indent {
342    /// The former indentation level.
343    indent: isize,
344    /// Whether, upon closing, this indents generates a `BlockEnd` token.
345    ///
346    /// There are levels of indentation which do not start a block. Examples of this would be:
347    /// ```yaml
348    /// -
349    ///   foo # ok
350    /// -
351    /// bar # ko, bar needs to be indented further than the `-`.
352    /// - [
353    ///  baz, # ok
354    /// quux # ko, quux needs to be indented further than the '-'.
355    /// ] # ko, the closing bracket needs to be indented further than the `-`.
356    /// ```
357    ///
358    /// The indentation level created by the `-` is for a single entry in the sequence. Emitting a
359    /// `BlockEnd` when this indentation block ends would generate one `BlockEnd` per entry in the
360    /// sequence, although we must have exactly one to end the sequence.
361    needs_block_end: bool,
362}
363
364/// The knowledge we have about an implicit mapping.
365///
366/// Implicit mappings occur in flow sequences where the opening `{` for a mapping in a flow
367/// sequence is omitted:
368/// ```yaml
369/// [ a: b, c: d ]
370/// # Equivalent to
371/// [ { a: b }, { c: d } ]
372/// # Equivalent to
373/// - a: b
374/// - c: d
375/// ```
376///
377/// The state must be carefully tracked for each nested flow sequence since we must emit a
378/// [`FlowMappingStart`] event when encountering `a` and `c` in our previous example without a
379/// character hinting us. Similarly, we must emit a [`FlowMappingEnd`] event when we reach the `,`
380/// or the `]`. If the state is not properly tracked, we may omit to emit these events or emit them
381/// out-of-order.
382///
383/// [`FlowMappingStart`]: TokenType::FlowMappingStart
384/// [`FlowMappingEnd`]: TokenType::FlowMappingEnd
385#[derive(Debug, Clone, PartialEq)]
386enum ImplicitMappingState {
387    /// It is possible there is an implicit mapping.
388    ///
389    /// This state is the one when we have just encountered the opening `[`. We need more context
390    /// to know whether an implicit mapping follows.
391    Possible,
392    /// We are inside the implcit mapping.
393    ///
394    /// Note that this state is not set immediately (we need to have encountered the `:` to know).
395    Inside,
396}
397
398/// The YAML scanner.
399///
400/// This corresponds to the low-level interface when reading YAML. The scanner emits token as they
401/// are read (akin to a lexer), but it also holds sufficient context to be able to disambiguate
402/// some of the constructs. It has understanding of indentation and whitespace and is able to
403/// generate error messages for some invalid YAML constructs.
404///
405/// It is however not a full parser and needs [`crate::parser::Parser`] to fully detect invalid
406/// YAML documents.
407#[derive(Debug)]
408#[allow(clippy::struct_excessive_bools)]
409pub struct Scanner<'input, T> {
410    /// The input source.
411    ///
412    /// This must implement [`Input`].
413    input: T,
414    /// The position of the cursor within the reader.
415    mark: Marker,
416    /// Buffer for tokens to be returned.
417    ///
418    /// This buffer can hold some temporary tokens that are not yet ready to be returned. For
419    /// instance, if we just read a scalar, it can be a value or a key if an implicit mapping
420    /// follows. In this case, the token stays in the `VecDeque` but cannot be returned from
421    /// [`Self::next`] until we have more context.
422    tokens: VecDeque<Token<'input>>,
423    /// The last error that happened.
424    error: Option<ScanError>,
425
426    /// Whether we have already emitted the `StreamStart` token.
427    stream_start_produced: bool,
428    /// Whether we have already emitted the `StreamEnd` token.
429    stream_end_produced: bool,
430    /// In some flow contexts, the value of a mapping is allowed to be adjacent to the `:`. When it
431    /// is, the index at which the `:` may be must be stored in `adjacent_value_allowed_at`.
432    adjacent_value_allowed_at: usize,
433    /// Whether a simple key could potentially start at the current position.
434    ///
435    /// Simple keys are the opposite of complex keys which are keys starting with `?`.
436    simple_key_allowed: bool,
437    /// A stack of potential simple keys.
438    ///
439    /// Refer to the documentation of [`SimpleKey`] for a more in-depth explanation of what they
440    /// are.
441    simple_keys: Vec<SimpleKey>,
442    /// The current indentation level.
443    indent: isize,
444    /// List of all block indentation levels we are in (except the current one).
445    indents: Vec<Indent>,
446    /// Level of nesting of flow sequences.
447    flow_level: u8,
448    /// The number of tokens that have been returned from the scanner.
449    ///
450    /// This excludes the tokens from [`Self::tokens`].
451    tokens_parsed: usize,
452    /// Whether a token is ready to be taken from [`Self::tokens`].
453    token_available: bool,
454    /// Whether all characters encountered since the last newline were whitespace.
455    leading_whitespace: bool,
456    /// Whether we started a flow mapping.
457    ///
458    /// This is used to detect implicit flow mapping starts such as:
459    /// ```yaml
460    /// [ : foo ] # { null: "foo" }
461    /// ```
462    flow_mapping_started: bool,
463    /// An array of states, representing whether flow sequences have implicit mappings.
464    ///
465    /// When a flow mapping is possible (when encountering the first `[` or a `,` in a sequence),
466    /// the state is set to [`Possible`].
467    /// When we encounter the `:`, we know we are in an implicit mapping and can set the state to
468    /// [`Inside`].
469    ///
470    /// There is one entry in this [`Vec`] for each nested flow sequence that we are in.
471    /// The entries are created with the opening `]` and popped with the closing `]`.
472    ///
473    /// [`Possible`]: ImplicitMappingState::Possible
474    /// [`Inside`]: ImplicitMappingState::Inside
475    implicit_flow_mapping_states: Vec<ImplicitMappingState>,
476    /// If a plain scalar was terminated by a `#` comment on its line, we set this
477    /// to detect an illegal multiline continuation on the following line.
478    interrupted_plain_by_comment: Option<Marker>,
479    buf_leading_break: String,
480    buf_trailing_breaks: String,
481    buf_whitespaces: String,
482}
483
484impl<T: Input + Clone> Clone for Scanner<'_, T> {
485    fn clone(&self) -> Self {
486        Self {
487            input: self.input.clone(),
488            mark: self.mark,
489            tokens: self.tokens.clone(),
490            error: self.error.clone(),
491            stream_start_produced: self.stream_start_produced,
492            stream_end_produced: self.stream_end_produced,
493            adjacent_value_allowed_at: self.adjacent_value_allowed_at,
494            simple_key_allowed: self.simple_key_allowed,
495            simple_keys: self.simple_keys.clone(),
496            indent: self.indent,
497            indents: self.indents.clone(),
498            flow_level: self.flow_level,
499            tokens_parsed: self.tokens_parsed,
500            token_available: self.token_available,
501            leading_whitespace: self.leading_whitespace,
502            flow_mapping_started: self.flow_mapping_started,
503            implicit_flow_mapping_states: self.implicit_flow_mapping_states.clone(),
504            interrupted_plain_by_comment: self.interrupted_plain_by_comment,
505            buf_leading_break: self.buf_leading_break.clone(),
506            buf_trailing_breaks: self.buf_trailing_breaks.clone(),
507            buf_whitespaces: self.buf_whitespaces.clone(),
508        }
509    }
510}
511
512impl<'input, T: Input> Iterator for Scanner<'input, T> {
513    type Item = Token<'input>;
514
515    fn next(&mut self) -> Option<Self::Item> {
516        if self.error.is_some() {
517            return None;
518        }
519        match self.next_token() {
520            Ok(Some(tok)) => {
521                debug_print!(
522                    "    \x1B[;32m\u{21B3} {:?} \x1B[;36m{:?}\x1B[;m",
523                    tok.1,
524                    tok.0
525                );
526                Some(tok)
527            }
528            Ok(tok) => tok,
529            Err(e) => {
530                self.error = Some(e);
531                None
532            }
533        }
534    }
535}
536
537/// A convenience alias for scanner functions that may fail without returning a value.
538pub type ScanResult = Result<(), ScanError>;
539
540impl<'input, T: Input> Scanner<'input, T> {
541    /// Creates the YAML tokenizer.
542    pub fn new(input: T) -> Self {
543        Scanner {
544            input,
545            mark: Marker::new(0, 1, 0),
546            tokens: VecDeque::new(),
547            error: None,
548
549            stream_start_produced: false,
550            stream_end_produced: false,
551            adjacent_value_allowed_at: 0,
552            simple_key_allowed: true,
553            simple_keys: Vec::new(),
554            indent: -1,
555            indents: Vec::new(),
556            flow_level: 0,
557            tokens_parsed: 0,
558            token_available: false,
559            leading_whitespace: true,
560            flow_mapping_started: false,
561            implicit_flow_mapping_states: vec![],
562            interrupted_plain_by_comment: None,
563
564            buf_leading_break: String::new(),
565            buf_trailing_breaks: String::new(),
566            buf_whitespaces: String::new(),
567        }
568    }
569
570    /// Get a copy of the last error that was encountered, if any.
571    ///
572    /// This does not clear the error state and further calls to [`Self::get_error`] will return (a
573    /// clone of) the same error.
574    #[inline]
575    pub fn get_error(&self) -> Option<ScanError> {
576        self.error.clone()
577    }
578
579    /// Consume the next character. It is assumed the next character is a blank.
580    #[inline]
581    fn skip_blank(&mut self) {
582        self.input.skip();
583
584        self.mark.index += 1;
585        self.mark.col += 1;
586    }
587
588    /// Consume the next character. It is assumed the next character is not a blank.
589    #[inline]
590    fn skip_non_blank(&mut self) {
591        self.input.skip();
592
593        self.mark.index += 1;
594        self.mark.col += 1;
595        self.leading_whitespace = false;
596    }
597
598    /// Consume the next characters. It is assumed none of the next characters are blanks.
599    #[inline]
600    fn skip_n_non_blank(&mut self, count: usize) {
601        self.input.skip_n(count);
602
603        self.mark.index += count;
604        self.mark.col += count;
605        self.leading_whitespace = false;
606    }
607
608    /// Consume the next character. It is assumed the next character is a newline.
609    #[inline]
610    fn skip_nl(&mut self) {
611        self.input.skip();
612
613        self.mark.index += 1;
614        self.mark.col = 0;
615        self.mark.line += 1;
616        self.leading_whitespace = true;
617    }
618
619    /// Consume a linebreak (either CR, LF or CRLF), if any. Do nothing if there's none.
620    #[inline]
621    fn skip_linebreak(&mut self) {
622        if self.input.next_2_are('\r', '\n') {
623            // While technically not a blank, this does not matter as `self.leading_whitespace`
624            // will be reset by `skip_nl`.
625            self.skip_blank();
626            self.skip_nl();
627        } else if self.input.next_is_break() {
628            self.skip_nl();
629        }
630    }
631
632    /// Return whether the [`TokenType::StreamStart`] event has been emitted.
633    #[inline]
634    pub fn stream_started(&self) -> bool {
635        self.stream_start_produced
636    }
637
638    /// Return whether the [`TokenType::StreamEnd`] event has been emitted.
639    #[inline]
640    pub fn stream_ended(&self) -> bool {
641        self.stream_end_produced
642    }
643
644    /// Get the current position in the input stream.
645    #[inline]
646    pub fn mark(&self) -> Marker {
647        self.mark
648    }
649
650    // Read and consume a line break (either `\r`, `\n` or `\r\n`).
651    //
652    // A `\n` is pushed into `s`.
653    //
654    // # Panics (in debug)
655    // If the next characters do not correspond to a line break.
656    #[inline]
657    fn read_break(&mut self, s: &mut String) {
658        self.skip_break();
659        s.push('\n');
660    }
661
662    // Read and consume a line break (either `\r`, `\n` or `\r\n`).
663    //
664    // # Panics (in debug)
665    // If the next characters do not correspond to a line break.
666    #[inline]
667    fn skip_break(&mut self) {
668        let c = self.input.peek();
669        let nc = self.input.peek_nth(1);
670        debug_assert!(is_break(c));
671        if c == '\r' && nc == '\n' {
672            self.skip_blank();
673        }
674        self.skip_nl();
675    }
676
677    /// Insert a token at the given position.
678    fn insert_token(&mut self, pos: usize, tok: Token<'input>) {
679        let old_len = self.tokens.len();
680        assert!(pos <= old_len);
681        self.tokens.insert(pos, tok);
682    }
683
684    fn allow_simple_key(&mut self) {
685        self.simple_key_allowed = true;
686    }
687
688    fn disallow_simple_key(&mut self) {
689        self.simple_key_allowed = false;
690    }
691
692    /// Fetch the next token in the stream.
693    ///
694    /// # Errors
695    /// Returns `ScanError` when the scanner does not find the next expected token.
696    pub fn fetch_next_token(&mut self) -> ScanResult {
697        self.input.lookahead(1);
698
699        if !self.stream_start_produced {
700            self.fetch_stream_start();
701            return Ok(());
702        }
703        self.skip_to_next_token()?;
704
705        debug_print!(
706            "  \x1B[38;5;244m\u{2192} fetch_next_token after whitespace {:?} {:?}\x1B[m",
707            self.mark,
708            self.input.peek()
709        );
710
711        self.stale_simple_keys()?;
712
713        let mark = self.mark;
714        self.unroll_indent(mark.col as isize);
715
716        self.input.lookahead(4);
717
718        if self.input.next_is_z() {
719            if self.input.peek_opt() == Some('\0') {
720                return Err(ScanError::new_str(self.mark, "found unprintable character"));
721            }
722            self.fetch_stream_end()?;
723            return Ok(());
724        }
725
726        // Only the parser knows whether a byte order mark at the start of a line is between
727        // documents. Anywhere else, it's inside one.
728        if self.input.next_char_is('\u{FEFF}') {
729            if self.mark.col == 0 && self.flow_level == 0 {
730                return self.fetch_byte_order_mark();
731            }
732
733            return Err(ScanError::new_str(
734                self.mark,
735                "a byte order mark must not appear inside a document",
736            ));
737        }
738
739        if self.mark.col == 0 {
740            if self.input.next_char_is('%') {
741                return self.fetch_directive();
742            } else if self.input.next_is_document_start() {
743                return self.fetch_document_indicator(TokenType::DocumentStart);
744            } else if self.input.next_is_document_end() {
745                self.fetch_document_indicator(TokenType::DocumentEnd)?;
746                self.skip_ws_to_eol(SkipTabs::Yes)?;
747                if !self.input.next_is_breakz() {
748                    return Err(ScanError::new_str(
749                        self.mark,
750                        "invalid content after document end marker",
751                    ));
752                }
753                return Ok(());
754            }
755        }
756
757        if (self.mark.col as isize) < self.indent {
758            return Err(ScanError::new_str(self.mark, "invalid indentation"));
759        }
760
761        let c = self.input.peek();
762        let nc = self.input.peek_nth(1);
763        match c {
764            '[' => self.fetch_flow_collection_start(TokenType::FlowSequenceStart),
765            '{' => self.fetch_flow_collection_start(TokenType::FlowMappingStart),
766            ']' => self.fetch_flow_collection_end(TokenType::FlowSequenceEnd),
767            '}' => self.fetch_flow_collection_end(TokenType::FlowMappingEnd),
768            ',' => self.fetch_flow_entry(),
769            '-' if is_blank_or_breakz(nc) => self.fetch_block_entry(),
770            '?' if is_blank_or_breakz(nc) => self.fetch_key(),
771            ':' if is_blank_or_breakz(nc) => self.fetch_value(),
772            ':' if self.flow_level > 0
773                && (is_flow(nc) || self.mark.index == self.adjacent_value_allowed_at) =>
774            {
775                self.fetch_flow_value()
776            }
777            // Is it an alias?
778            '*' => self.fetch_anchor(true),
779            // Is it an anchor?
780            '&' => self.fetch_anchor(false),
781            '!' => self.fetch_tag(),
782            // Is it a literal scalar?
783            '|' if self.flow_level == 0 => self.fetch_block_scalar(true),
784            // Is it a folded scalar?
785            '>' if self.flow_level == 0 => self.fetch_block_scalar(false),
786            '\'' => self.fetch_flow_scalar(true),
787            '"' => self.fetch_flow_scalar(false),
788            // plain scalar
789            '-' if !is_blank_or_breakz(nc) => self.fetch_plain_scalar(),
790            ':' | '?' if !is_blank_or_breakz(nc) && self.flow_level == 0 => {
791                self.fetch_plain_scalar()
792            }
793            '%' | '@' | '`' => Err(ScanError::new(
794                self.mark,
795                format!("unexpected character: `{c}'"),
796            )),
797            _ => self.fetch_plain_scalar(),
798        }
799    }
800
801    /// Return the next token in the stream.
802    /// # Errors
803    /// Returns `ScanError` when scanning fails to find an expected next token.
804    pub fn next_token(&mut self) -> Result<Option<Token<'input>>, ScanError> {
805        if self.stream_end_produced {
806            return Ok(None);
807        }
808
809        if !self.token_available {
810            self.fetch_more_tokens()?;
811        }
812        let Some(t) = self.tokens.pop_front() else {
813            return Err(ScanError::new_str(
814                self.mark,
815                "did not find expected next token",
816            ));
817        };
818        self.token_available = false;
819        self.tokens_parsed += 1;
820
821        if let TokenType::StreamEnd = t.1 {
822            self.stream_end_produced = true;
823        }
824        Ok(Some(t))
825    }
826
827    /// Fetch tokens from the token stream.
828    /// # Errors
829    /// Returns `ScanError` when loading fails.
830    pub fn fetch_more_tokens(&mut self) -> ScanResult {
831        let mut need_more;
832        loop {
833            if self.tokens.is_empty() {
834                need_more = true;
835            } else {
836                need_more = false;
837                // Stale potential keys that we know won't be keys.
838                self.stale_simple_keys()?;
839                // If our next token to be emitted may be a key, fetch more context.
840                for sk in &self.simple_keys {
841                    if sk.possible && sk.token_number == self.tokens_parsed {
842                        need_more = true;
843                        break;
844                    }
845                }
846            }
847
848            if !need_more {
849                break;
850            }
851            self.fetch_next_token()?;
852        }
853        self.token_available = true;
854
855        Ok(())
856    }
857
858    /// Mark simple keys that can no longer be keys as such.
859    ///
860    /// This function sets `possible` to `false` to each key that, now we have more context, we
861    /// know will not be keys.
862    ///
863    /// # Errors
864    /// This function returns an error if one of the key we would stale was required to be a key.
865    fn stale_simple_keys(&mut self) -> ScanResult {
866        for sk in &mut self.simple_keys {
867            if sk.possible
868                // If not in a flow construct, simple keys cannot span multiple lines.
869                && self.flow_level == 0
870                    && (sk.mark.line < self.mark.line || sk.mark.index + 1024 < self.mark.index)
871            {
872                if sk.required {
873                    return Err(ScanError::new_str(self.mark, "simple key expect ':'"));
874                }
875                sk.possible = false;
876            }
877        }
878        Ok(())
879    }
880
881    /// Skip over all whitespace (`\t`, ` `, `\n`, `\r`) and comments until the next token.
882    ///
883    /// # Errors
884    /// This function returns an error if a tabulation is encountered where there should not be
885    /// one.
886    fn skip_to_next_token(&mut self) -> ScanResult {
887        loop {
888            match self.input.look_ch() {
889                // Tabs may not be used as indentation.
890                // "Indentation" only exists as long as a block is started, but does not exist
891                // inside of flow-style constructs. Tabs are allowed as part of leading
892                // whitespaces outside of indentation.
893                // If a flow-style construct is in an indented block, its contents must still be
894                // indented. Also, tabs are allowed anywhere in it if it has no content.
895                '\t' if self.is_within_block()
896                    && self.leading_whitespace
897                    && (self.mark.col as isize) < self.indent =>
898                {
899                    self.skip_ws_to_eol(SkipTabs::Yes)?;
900                    // If we have content on that line with a tab, return an error.
901                    if !self.input.next_is_breakz() {
902                        return Err(ScanError::new_str(
903                            self.mark,
904                            "tabs disallowed within this context (block indentation)",
905                        ));
906                    }
907                }
908                '\t' | ' ' => self.skip_blank(),
909                '\n' | '\r' => {
910                    self.input.lookahead(2);
911                    self.skip_linebreak();
912                    if self.flow_level == 0 {
913                        self.allow_simple_key();
914                    }
915                }
916                '#' => {
917                    let comment_length = self.input.skip_while_non_breakz();
918                    self.mark.index += comment_length;
919                    self.mark.col += comment_length;
920                }
921                _ => break,
922            }
923        }
924        // If a plain scalar was interrupted by a comment, and the next line could
925        // continue the scalar in block context, this is invalid.
926        if let Some(err_mark) = self.interrupted_plain_by_comment.take() {
927            // Ensure enough lookahead for the check below (peek and peek_nth) and for
928            // document indicator detection which needs 4 chars.
929            self.input.lookahead(4);
930            // BS4K should only trigger when the continuation would start on the immediate next
931            // line (no intervening empty/comment-only lines). A blank line resets the folding
932            // opportunity and thus should not error.
933            let is_immediate_next_line = self.mark.line == err_mark.line + 1;
934            if self.flow_level == 0
935                && is_immediate_next_line
936                && self.mark.col as isize > self.indent
937                && !self.input.next_is_z()
938                && !self.input.next_is_document_indicator()
939                && self.input.next_can_be_plain_scalar(false)
940            {
941                return Err(ScanError::new_str(
942                    err_mark,
943                    "comment intercepting the multiline text",
944                ));
945            }
946        }
947        Ok(())
948    }
949
950    /// Skip over YAML whitespace (` `, `\n`, `\r`).
951    ///
952    /// # Errors
953    /// This function returns an error if no whitespace was found.
954    fn skip_yaml_whitespace(&mut self) -> ScanResult {
955        let mut need_whitespace = true;
956        loop {
957            match self.input.look_ch() {
958                ' ' => {
959                    self.skip_blank();
960
961                    need_whitespace = false;
962                }
963                '\n' | '\r' => {
964                    self.input.lookahead(2);
965                    self.skip_linebreak();
966                    if self.flow_level == 0 {
967                        self.allow_simple_key();
968                    }
969                    need_whitespace = false;
970                }
971                '#' => {
972                    let comment_length = self.input.skip_while_non_breakz();
973                    self.mark.index += comment_length;
974                    self.mark.col += comment_length;
975                }
976                _ => break,
977            }
978        }
979
980        if need_whitespace {
981            Err(ScanError::new_str(self.mark(), "expected whitespace"))
982        } else {
983            Ok(())
984        }
985    }
986
987    fn skip_ws_to_eol(&mut self, skip_tabs: SkipTabs) -> Result<SkipTabs, ScanError> {
988        let (n_bytes, result) = self.input.skip_ws_to_eol(skip_tabs);
989        self.mark.col += n_bytes;
990        self.mark.index += n_bytes;
991        // Skipping stops at a byte order mark. Unless it starts the line, it's inside a document.
992        if self.mark.col > 0 && self.input.look_ch() == '\u{FEFF}' {
993            return Err(ScanError::new_str(
994                self.mark,
995                "a byte order mark must not appear inside a document",
996            ));
997        }
998
999        result.map_err(|msg| ScanError::new_str(self.mark, msg))
1000    }
1001
1002    fn fetch_stream_start(&mut self) {
1003        let mark = self.mark;
1004        self.indent = -1;
1005        self.stream_start_produced = true;
1006        self.allow_simple_key();
1007        self.tokens.push_back(Token(
1008            Span::empty(mark),
1009            TokenType::StreamStart(TEncoding::Utf8),
1010        ));
1011        self.simple_keys.push(SimpleKey::new(Marker::new(0, 0, 0)));
1012    }
1013
1014    fn fetch_stream_end(&mut self) -> ScanResult {
1015        // force new line
1016        if self.mark.col != 0 {
1017            self.mark.col = 0;
1018            self.mark.line += 1;
1019        }
1020
1021        // If the stream ended, we won't have more context. We can stall all the simple keys we
1022        // had. If one was required, however, that was an error and we must propagate it.
1023        for sk in &mut self.simple_keys {
1024            if sk.required && sk.possible {
1025                return Err(ScanError::new_str(self.mark, "simple key expected"));
1026            }
1027            sk.possible = false;
1028        }
1029
1030        self.unroll_indent(-1);
1031        self.remove_simple_key()?;
1032        self.disallow_simple_key();
1033
1034        self.tokens
1035            .push_back(Token(Span::empty(self.mark), TokenType::StreamEnd));
1036        Ok(())
1037    }
1038
1039    fn fetch_directive(&mut self) -> ScanResult {
1040        self.unroll_indent(-1);
1041        self.remove_simple_key()?;
1042
1043        self.disallow_simple_key();
1044
1045        let tok = self.scan_directive()?;
1046        self.tokens.push_back(tok);
1047
1048        Ok(())
1049    }
1050
1051    fn scan_directive(&mut self) -> Result<Token<'input>, ScanError> {
1052        let start_mark = self.mark;
1053        self.skip_non_blank();
1054
1055        let name = self.scan_directive_name()?;
1056        let tok = match name.as_ref() {
1057            "YAML" => self.scan_version_directive_value(&start_mark)?,
1058            "TAG" => self.scan_tag_directive_value(&start_mark)?,
1059            _ => {
1060                let mut params = Vec::new();
1061                while self.input.next_is_blank() {
1062                    let n_blanks = self.input.skip_while_blank();
1063                    self.mark.index += n_blanks;
1064                    self.mark.col += n_blanks;
1065
1066                    if !is_blank_or_breakz(self.input.peek()) {
1067                        let mut param = String::new();
1068                        let n_chars = self.input.fetch_while_is_yaml_non_space(&mut param);
1069                        self.mark.index += n_chars;
1070                        self.mark.col += n_chars;
1071                        params.push(param);
1072                    }
1073                }
1074
1075                Token(
1076                    Span::new(start_mark, self.mark),
1077                    TokenType::ReservedDirective(name, params),
1078                )
1079            }
1080        };
1081
1082        self.skip_ws_to_eol(SkipTabs::Yes)?;
1083
1084        if self.input.next_is_breakz() {
1085            self.input.lookahead(2);
1086            self.skip_linebreak();
1087            Ok(tok)
1088        } else {
1089            Err(ScanError::new_str(
1090                start_mark,
1091                "while scanning a directive, did not find expected comment or line break",
1092            ))
1093        }
1094    }
1095
1096    fn scan_version_directive_value(&mut self, mark: &Marker) -> Result<Token<'input>, ScanError> {
1097        let n_blanks = self.input.skip_while_blank();
1098        self.mark.index += n_blanks;
1099        self.mark.col += n_blanks;
1100
1101        let major = self.scan_version_directive_number(mark)?;
1102
1103        if self.input.peek() != '.' {
1104            return Err(ScanError::new_str(
1105                *mark,
1106                "while scanning a YAML directive, did not find expected digit or '.' character",
1107            ));
1108        }
1109        self.skip_non_blank();
1110
1111        let minor = self.scan_version_directive_number(mark)?;
1112
1113        Ok(Token(
1114            Span::new(*mark, self.mark),
1115            TokenType::VersionDirective(major, minor),
1116        ))
1117    }
1118
1119    fn scan_directive_name(&mut self) -> Result<String, ScanError> {
1120        let start_mark = self.mark;
1121        let mut string = String::new();
1122
1123        let n_chars = self.input.fetch_while_is_yaml_non_space(&mut string);
1124        self.mark.index += n_chars;
1125        self.mark.col += n_chars;
1126
1127        if string.is_empty() {
1128            return Err(ScanError::new_str(
1129                start_mark,
1130                "while scanning a directive, could not find expected directive name",
1131            ));
1132        }
1133
1134        if !is_blank_or_breakz(self.input.peek()) {
1135            return Err(ScanError::new_str(
1136                start_mark,
1137                "while scanning a directive, found unexpected non-alphabetical character",
1138            ));
1139        }
1140
1141        Ok(string)
1142    }
1143
1144    fn scan_version_directive_number(&mut self, mark: &Marker) -> Result<u32, ScanError> {
1145        let mut val = 0u32;
1146        let mut length = 0usize;
1147        while let Some(digit) = self.input.look_ch().to_digit(10) {
1148            if length + 1 > 9 {
1149                return Err(ScanError::new_str(
1150                    *mark,
1151                    "while scanning a YAML directive, found extremely long version number",
1152                ));
1153            }
1154            length += 1;
1155            val = val * 10 + digit;
1156            self.skip_non_blank();
1157        }
1158
1159        if length == 0 {
1160            return Err(ScanError::new_str(
1161                *mark,
1162                "while scanning a YAML directive, did not find expected version number",
1163            ));
1164        }
1165
1166        Ok(val)
1167    }
1168
1169    fn scan_tag_directive_value(&mut self, mark: &Marker) -> Result<Token<'input>, ScanError> {
1170        let n_blanks = self.input.skip_while_blank();
1171        self.mark.index += n_blanks;
1172        self.mark.col += n_blanks;
1173
1174        let handle = self.scan_tag_handle(true, mark)?;
1175
1176        let n_blanks = self.input.skip_while_blank();
1177        self.mark.index += n_blanks;
1178        self.mark.col += n_blanks;
1179
1180        let prefix = self.scan_tag_prefix(mark)?;
1181
1182        self.input.lookahead(1);
1183
1184        if self.input.next_is_blank_or_breakz() {
1185            Ok(Token(
1186                Span::new(*mark, self.mark),
1187                TokenType::TagDirective(handle.into(), prefix.into()),
1188            ))
1189        } else {
1190            Err(ScanError::new_str(
1191                *mark,
1192                "while scanning TAG, did not find expected whitespace or line break",
1193            ))
1194        }
1195    }
1196
1197    fn fetch_tag(&mut self) -> ScanResult {
1198        self.save_simple_key();
1199        self.disallow_simple_key();
1200
1201        let tok = self.scan_tag()?;
1202        self.tokens.push_back(tok);
1203        Ok(())
1204    }
1205
1206    fn scan_tag(&mut self) -> Result<Token<'input>, ScanError> {
1207        let start_mark = self.mark;
1208        let mut handle = String::new();
1209        let mut suffix;
1210
1211        // Check if the tag is in the canonical form (verbatim).
1212        self.input.lookahead(2);
1213
1214        if self.input.nth_char_is(1, '<') {
1215            suffix = self.scan_verbatim_tag(&start_mark)?;
1216        } else {
1217            // The tag has either the '!suffix' or the '!handle!suffix'
1218            handle = self.scan_tag_handle(false, &start_mark)?;
1219            // Check if it is, indeed, handle.
1220            if handle.len() >= 2 && handle.starts_with('!') && handle.ends_with('!') {
1221                // A tag handle starting with "!!" is a secondary tag handle.
1222                let is_secondary_handle = handle == "!!";
1223                suffix =
1224                    self.scan_tag_shorthand_suffix(false, is_secondary_handle, "", &start_mark)?;
1225            } else {
1226                suffix = self.scan_tag_shorthand_suffix(false, false, &handle, &start_mark)?;
1227                "!".clone_into(&mut handle);
1228                // A special case: the '!' tag.  Set the handle to '' and the
1229                // suffix to '!'.
1230                if suffix.is_empty() {
1231                    handle.clear();
1232                    "!".clone_into(&mut suffix);
1233                }
1234            }
1235        }
1236
1237        if is_blank_or_breakz(self.input.look_ch())
1238            || (self.flow_level > 0 && self.input.next_is_flow())
1239        {
1240            // XXX: ex 7.2, an empty scalar can follow a secondary tag
1241            Ok(Token(
1242                Span::new(start_mark, self.mark),
1243                TokenType::Tag(handle, suffix),
1244            ))
1245        } else {
1246            Err(ScanError::new_str(
1247                start_mark,
1248                "while scanning a tag, did not find expected whitespace or line break",
1249            ))
1250        }
1251    }
1252
1253    fn scan_tag_handle(&mut self, directive: bool, mark: &Marker) -> Result<String, ScanError> {
1254        let mut string = String::new();
1255        if self.input.look_ch() != '!' {
1256            return Err(ScanError::new_str(
1257                *mark,
1258                "while scanning a tag, did not find expected '!'",
1259            ));
1260        }
1261
1262        string.push(self.input.peek());
1263        self.skip_non_blank();
1264
1265        let n_chars = self.input.fetch_while_is_alpha(&mut string);
1266        self.mark.index += n_chars;
1267        self.mark.col += n_chars;
1268
1269        // Check if the trailing character is '!' and copy it.
1270        if self.input.peek() == '!' {
1271            string.push(self.input.peek());
1272            self.skip_non_blank();
1273        } else if directive && string != "!" {
1274            // It's either the '!' tag or not really a tag handle.  If it's a %TAG
1275            // directive, it's an error.  If it's a tag token, it must be a part of
1276            // URI.
1277            return Err(ScanError::new_str(
1278                *mark,
1279                "while parsing a tag directive, did not find expected '!'",
1280            ));
1281        }
1282        Ok(string)
1283    }
1284
1285    /// Scan for a tag prefix (6.8.2.2).
1286    ///
1287    /// There are 2 kinds of tag prefixes:
1288    ///   - Local: Starts with a `!`, contains only URI chars (`!foo`)
1289    ///   - Global: Starts with a tag char, contains then URI chars (`!foo,2000:app/`)
1290    fn scan_tag_prefix(&mut self, start_mark: &Marker) -> Result<String, ScanError> {
1291        let mut string = String::new();
1292
1293        if self.input.look_ch() == '!' {
1294            // If we have a local tag, insert and skip `!`.
1295            string.push(self.input.peek());
1296            self.skip_non_blank();
1297        } else if !is_tag_char(self.input.peek()) {
1298            // Otherwise, check if the first global tag character is valid.
1299            return Err(ScanError::new_str(
1300                *start_mark,
1301                "invalid global tag character",
1302            ));
1303        } else if self.input.peek() == '%' {
1304            // If it is valid and an escape sequence, escape it.
1305            string.push(self.scan_uri_escapes(start_mark)?);
1306        } else {
1307            // Otherwise, push the first character.
1308            string.push(self.input.peek());
1309            self.skip_non_blank();
1310        }
1311
1312        while is_uri_char(self.input.look_ch()) {
1313            if self.input.peek() == '%' {
1314                string.push(self.scan_uri_escapes(start_mark)?);
1315            } else {
1316                string.push(self.input.peek());
1317                self.skip_non_blank();
1318            }
1319        }
1320
1321        Ok(string)
1322    }
1323
1324    /// Scan for a verbatim tag.
1325    ///
1326    /// The prefixing `!<` must _not_ have been skipped.
1327    fn scan_verbatim_tag(&mut self, start_mark: &Marker) -> Result<String, ScanError> {
1328        // Eat `!<`
1329        self.skip_non_blank();
1330        self.skip_non_blank();
1331
1332        let mut string = String::new();
1333        while is_uri_char(self.input.look_ch()) {
1334            if self.input.peek() == '%' {
1335                string.push(self.scan_uri_escapes(start_mark)?);
1336            } else {
1337                string.push(self.input.peek());
1338                self.skip_non_blank();
1339            }
1340        }
1341
1342        if self.input.peek() != '>' {
1343            return Err(ScanError::new_str(
1344                *start_mark,
1345                "while scanning a verbatim tag, did not find the expected '>'",
1346            ));
1347        }
1348        self.skip_non_blank();
1349
1350        Ok(string)
1351    }
1352
1353    fn scan_tag_shorthand_suffix(
1354        &mut self,
1355        _directive: bool,
1356        _is_secondary: bool,
1357        head: &str,
1358        mark: &Marker,
1359    ) -> Result<String, ScanError> {
1360        let mut length = head.len();
1361        let mut string = String::new();
1362
1363        // Copy the head if needed.
1364        // Note that we don't copy the leading '!' character.
1365        if length > 1 {
1366            string.extend(head.chars().skip(1));
1367        }
1368
1369        while is_tag_char(self.input.look_ch()) {
1370            // Check if it is a URI-escape sequence.
1371            if self.input.peek() == '%' {
1372                string.push(self.scan_uri_escapes(mark)?);
1373            } else {
1374                string.push(self.input.peek());
1375                self.skip_non_blank();
1376            }
1377
1378            length += 1;
1379        }
1380
1381        if length == 0 {
1382            return Err(ScanError::new_str(
1383                *mark,
1384                "while parsing a tag, did not find expected tag URI",
1385            ));
1386        }
1387
1388        Ok(string)
1389    }
1390
1391    fn scan_uri_escapes(&mut self, mark: &Marker) -> Result<char, ScanError> {
1392        let mut width = 0usize;
1393        let mut code = 0u32;
1394        loop {
1395            self.input.lookahead(3);
1396
1397            let c = self.input.peek_nth(1);
1398            let nc = self.input.peek_nth(2);
1399
1400            if !(self.input.peek() == '%' && is_hex(c) && is_hex(nc)) {
1401                return Err(ScanError::new_str(
1402                    *mark,
1403                    "while parsing a tag, found an invalid escape sequence",
1404                ));
1405            }
1406
1407            let byte = (as_hex(c) << 4) + as_hex(nc);
1408            if width == 0 {
1409                width = match byte {
1410                    _ if byte & 0x80 == 0x00 => 1,
1411                    _ if byte & 0xE0 == 0xC0 => 2,
1412                    _ if byte & 0xF0 == 0xE0 => 3,
1413                    _ if byte & 0xF8 == 0xF0 => 4,
1414                    _ => {
1415                        return Err(ScanError::new_str(
1416                            *mark,
1417                            "while parsing a tag, found an incorrect leading UTF-8 byte",
1418                        ));
1419                    }
1420                };
1421                code = byte;
1422            } else {
1423                if byte & 0xc0 != 0x80 {
1424                    return Err(ScanError::new_str(
1425                        *mark,
1426                        "while parsing a tag, found an incorrect trailing UTF-8 byte",
1427                    ));
1428                }
1429                code = (code << 8) + byte;
1430            }
1431
1432            self.skip_n_non_blank(3);
1433
1434            width -= 1;
1435            if width == 0 {
1436                break;
1437            }
1438        }
1439
1440        match char::from_u32(code) {
1441            Some(ch) => Ok(ch),
1442            None => Err(ScanError::new_str(
1443                *mark,
1444                "while parsing a tag, found an invalid UTF-8 codepoint",
1445            )),
1446        }
1447    }
1448
1449    fn fetch_anchor(&mut self, alias: bool) -> ScanResult {
1450        self.save_simple_key();
1451        self.disallow_simple_key();
1452
1453        let tok = self.scan_anchor(alias)?;
1454
1455        self.tokens.push_back(tok);
1456
1457        Ok(())
1458    }
1459
1460    fn scan_anchor(&mut self, alias: bool) -> Result<Token<'input>, ScanError> {
1461        let mut string = String::new();
1462        let start_mark = self.mark;
1463
1464        self.skip_non_blank();
1465        while is_anchor_char(self.input.look_ch()) {
1466            string.push(self.input.peek());
1467            self.skip_non_blank();
1468        }
1469
1470        if string.is_empty() {
1471            return Err(ScanError::new_str(
1472                start_mark,
1473                "while scanning an anchor or alias, did not find expected alphabetic or numeric character",
1474            ));
1475        }
1476
1477        let tok = if alias {
1478            TokenType::Alias(string.into())
1479        } else {
1480            TokenType::Anchor(string.into())
1481        };
1482        Ok(Token(Span::new(start_mark, self.mark), tok))
1483    }
1484
1485    fn fetch_flow_collection_start(&mut self, tok: TokenType<'input>) -> ScanResult {
1486        // The indicators '[' and '{' may start a simple key.
1487        self.save_simple_key();
1488
1489        self.roll_one_col_indent();
1490        self.increase_flow_level()?;
1491
1492        self.allow_simple_key();
1493
1494        let start_mark = self.mark;
1495        self.skip_non_blank();
1496
1497        if tok == TokenType::FlowMappingStart {
1498            self.flow_mapping_started = true;
1499        } else {
1500            self.implicit_flow_mapping_states
1501                .push(ImplicitMappingState::Possible);
1502        }
1503
1504        self.skip_ws_to_eol(SkipTabs::Yes)?;
1505
1506        self.tokens
1507            .push_back(Token(Span::new(start_mark, self.mark), tok));
1508        Ok(())
1509    }
1510
1511    fn fetch_flow_collection_end(&mut self, tok: TokenType<'input>) -> ScanResult {
1512        // A closing bracket without a corresponding opening is invalid YAML.
1513        if self.flow_level == 0 {
1514            return Err(ScanError::new_str(self.mark, "misplaced bracket"));
1515        }
1516
1517        self.remove_simple_key()?;
1518        self.decrease_flow_level();
1519
1520        self.disallow_simple_key();
1521
1522        if matches!(tok, TokenType::FlowSequenceEnd) {
1523            self.end_implicit_mapping(self.mark);
1524            // We are out exiting the flow sequence, nesting goes down 1 level.
1525            self.implicit_flow_mapping_states.pop();
1526        }
1527
1528        let start_mark = self.mark;
1529        self.skip_non_blank();
1530        self.skip_ws_to_eol(SkipTabs::Yes)?;
1531
1532        // A flow collection within a flow mapping can be a key. In that case, the value may be
1533        // adjacent to the `:`.
1534        // ```yaml
1535        // - [ {a: b}:value ]
1536        // ```
1537        if self.flow_level > 0 {
1538            self.adjacent_value_allowed_at = self.mark.index;
1539        }
1540
1541        self.tokens
1542            .push_back(Token(Span::new(start_mark, self.mark), tok));
1543        Ok(())
1544    }
1545
1546    /// Push the `FlowEntry` token and skip over the `,`.
1547    fn fetch_flow_entry(&mut self) -> ScanResult {
1548        self.remove_simple_key()?;
1549        self.allow_simple_key();
1550
1551        self.end_implicit_mapping(self.mark);
1552
1553        let start_mark = self.mark;
1554        self.skip_non_blank();
1555        self.skip_ws_to_eol(SkipTabs::Yes)?;
1556
1557        self.tokens.push_back(Token(
1558            Span::new(start_mark, self.mark),
1559            TokenType::FlowEntry,
1560        ));
1561        Ok(())
1562    }
1563
1564    fn increase_flow_level(&mut self) -> ScanResult {
1565        self.simple_keys.push(SimpleKey::new(Marker::new(0, 0, 0)));
1566        self.flow_level = self
1567            .flow_level
1568            .checked_add(1)
1569            .ok_or_else(|| ScanError::new_str(self.mark, "recursion limit exceeded"))?;
1570        Ok(())
1571    }
1572
1573    fn decrease_flow_level(&mut self) {
1574        if self.flow_level > 0 {
1575            self.flow_level -= 1;
1576            self.simple_keys.pop().unwrap();
1577        }
1578    }
1579
1580    /// Push the `Block*` token(s) and skip over the `-`.
1581    ///
1582    /// Add an indentation level and push a `BlockSequenceStart` token if needed, then push a
1583    /// `BlockEntry` token.
1584    /// This function only skips over the `-` and does not fetch the entry value.
1585    fn fetch_block_entry(&mut self) -> ScanResult {
1586        if self.flow_level > 0 {
1587            // - * only allowed in block
1588            return Err(ScanError::new_str(
1589                self.mark,
1590                r#""-" is only valid inside a block"#,
1591            ));
1592        }
1593        // Check if we are allowed to start a new entry.
1594        if !self.simple_key_allowed {
1595            return Err(ScanError::new_str(
1596                self.mark,
1597                "block sequence entries are not allowed in this context",
1598            ));
1599        }
1600
1601        // ???, fixes test G9HC.
1602        if let Some(Token(span, TokenType::Anchor(..) | TokenType::Tag(..))) = self.tokens.back() {
1603            if self.mark.col == 0 && span.start.col == 0 && self.indent > -1 {
1604                return Err(ScanError::new_str(
1605                    span.start,
1606                    "invalid indentation for anchor",
1607                ));
1608            }
1609        }
1610
1611        // Skip over the `-`.
1612        let mark = self.mark;
1613        self.skip_non_blank();
1614
1615        // generate BLOCK-SEQUENCE-START if indented
1616        self.roll_indent(mark.col, None, TokenType::BlockSequenceStart, mark);
1617        let found_tabs = self.skip_ws_to_eol(SkipTabs::Yes)?.found_tabs();
1618        self.input.lookahead(2);
1619        if found_tabs && self.input.next_char_is('-') && is_blank_or_breakz(self.input.peek_nth(1))
1620        {
1621            return Err(ScanError::new_str(
1622                self.mark,
1623                "'-' must be followed by a valid YAML whitespace",
1624            ));
1625        }
1626
1627        self.skip_ws_to_eol(SkipTabs::No)?;
1628        self.input.lookahead(1);
1629        if self.input.next_is_break() || self.input.next_is_flow() {
1630            self.roll_one_col_indent();
1631        }
1632
1633        self.remove_simple_key()?;
1634        self.allow_simple_key();
1635
1636        self.tokens
1637            .push_back(Token(Span::empty(self.mark), TokenType::BlockEntry));
1638
1639        Ok(())
1640    }
1641
1642    fn fetch_byte_order_mark(&mut self) -> ScanResult {
1643        self.unroll_indent(-1);
1644        self.remove_simple_key()?;
1645
1646        // Don't count BOMs as content.
1647        let mark = self.mark;
1648        self.input.skip();
1649        self.mark.index += 1;
1650        self.allow_simple_key();
1651
1652        self.tokens
1653            .push_back(Token(Span::new(mark, self.mark), TokenType::ByteOrderMark));
1654        Ok(())
1655    }
1656
1657    fn fetch_document_indicator(&mut self, t: TokenType<'input>) -> ScanResult {
1658        self.unroll_indent(-1);
1659        self.remove_simple_key()?;
1660        self.disallow_simple_key();
1661
1662        let mark = self.mark;
1663
1664        self.skip_n_non_blank(3);
1665
1666        self.tokens.push_back(Token(Span::new(mark, self.mark), t));
1667        Ok(())
1668    }
1669
1670    fn fetch_block_scalar(&mut self, literal: bool) -> ScanResult {
1671        self.save_simple_key();
1672        self.allow_simple_key();
1673        let tok = self.scan_block_scalar(literal)?;
1674
1675        self.tokens.push_back(tok);
1676        Ok(())
1677    }
1678
1679    #[allow(clippy::too_many_lines)]
1680    fn scan_block_scalar(&mut self, literal: bool) -> Result<Token<'input>, ScanError> {
1681        let start_mark = self.mark;
1682        let mut chomping = Chomping::Clip;
1683        let mut increment: usize = 0;
1684        let mut indent: usize = 0;
1685        let mut trailing_blank: bool;
1686        let mut leading_blank: bool = false;
1687        let style = if literal {
1688            ScalarStyle::Literal
1689        } else {
1690            ScalarStyle::Folded
1691        };
1692
1693        let mut string = String::new();
1694        let mut leading_break = String::new();
1695        let mut trailing_breaks = String::new();
1696
1697        // skip '|' or '>'
1698        self.skip_non_blank();
1699        self.unroll_non_block_indents();
1700
1701        if self.input.look_ch() == '+' || self.input.peek() == '-' {
1702            if self.input.peek() == '+' {
1703                chomping = Chomping::Keep;
1704            } else {
1705                chomping = Chomping::Strip;
1706            }
1707            self.skip_non_blank();
1708            self.input.lookahead(1);
1709            if self.input.next_is_digit() {
1710                if self.input.peek() == '0' {
1711                    return Err(ScanError::new_str(
1712                        start_mark,
1713                        "while scanning a block scalar, found an indentation indicator equal to 0",
1714                    ));
1715                }
1716                increment = (self.input.peek() as usize) - ('0' as usize);
1717                self.skip_non_blank();
1718            }
1719        } else if self.input.next_is_digit() {
1720            if self.input.peek() == '0' {
1721                return Err(ScanError::new_str(
1722                    start_mark,
1723                    "while scanning a block scalar, found an indentation indicator equal to 0",
1724                ));
1725            }
1726
1727            increment = (self.input.peek() as usize) - ('0' as usize);
1728            self.skip_non_blank();
1729            self.input.lookahead(1);
1730            if self.input.peek() == '+' || self.input.peek() == '-' {
1731                if self.input.peek() == '+' {
1732                    chomping = Chomping::Keep;
1733                } else {
1734                    chomping = Chomping::Strip;
1735                }
1736                self.skip_non_blank();
1737            }
1738        }
1739
1740        self.skip_ws_to_eol(SkipTabs::Yes)?;
1741
1742        // Check if we are at the end of the line.
1743        self.input.lookahead(1);
1744        if !self.input.next_is_breakz() {
1745            return Err(ScanError::new_str(
1746                start_mark,
1747                "while scanning a block scalar, did not find expected comment or line break",
1748            ));
1749        }
1750
1751        if self.input.next_is_break() {
1752            self.input.lookahead(2);
1753            self.skip_break();
1754        }
1755
1756        if self.input.look_ch() == '\t' {
1757            return Err(ScanError::new_str(
1758                start_mark,
1759                "a block scalar content cannot start with a tab",
1760            ));
1761        }
1762
1763        if increment > 0 {
1764            indent = if self.indent >= 0 {
1765                (self.indent + increment as isize) as usize
1766            } else {
1767                increment
1768            }
1769        }
1770
1771        // Scan the leading line breaks and determine the indentation level if needed.
1772        if indent == 0 {
1773            self.skip_block_scalar_first_line_indent(&mut indent, &mut trailing_breaks);
1774        } else {
1775            self.skip_block_scalar_indent(indent, &mut trailing_breaks);
1776        }
1777
1778        // We have an end-of-stream with no content, e.g.:
1779        // ```yaml
1780        // - |+
1781        // ```
1782        if self.input.next_is_z() {
1783            let contents = match chomping {
1784                // The line break ending the header line is not content, so with no content lines
1785                // there is nothing to strip or clip.
1786                Chomping::Strip | Chomping::Clip => String::new(),
1787                // We keep lines: only the breaks following the header line remain. An unterminated
1788                // whitespace-only line still counts as a line.
1789                Chomping::Keep
1790                    if trailing_breaks.is_empty()
1791                        && self.mark.line > start_mark.line()
1792                        && self.mark.col > 0 =>
1793                {
1794                    "\n".into()
1795                }
1796                Chomping::Keep => trailing_breaks,
1797            };
1798            return Ok(Token(
1799                Span::new(start_mark, self.mark),
1800                TokenType::Scalar(style, contents.into()),
1801            ));
1802        }
1803
1804        if self.mark.col < indent && (self.mark.col as isize) > self.indent {
1805            return Err(ScanError::new_str(
1806                self.mark,
1807                "wrongly indented line in block scalar",
1808            ));
1809        }
1810
1811        let mut line_buffer = String::with_capacity(100);
1812        let start_mark = self.mark;
1813        while self.mark.col == indent && is_yaml_non_break(self.input.peek()) {
1814            if indent == 0 {
1815                self.input.lookahead(4);
1816                if self.input.next_is_document_end() {
1817                    break;
1818                }
1819            }
1820
1821            // We are at the first content character of a content line.
1822            trailing_blank = self.input.next_is_blank();
1823            if !literal && !leading_break.is_empty() && !leading_blank && !trailing_blank {
1824                string.push_str(&trailing_breaks);
1825                if trailing_breaks.is_empty() {
1826                    string.push(' ');
1827                }
1828            } else {
1829                string.push_str(&leading_break);
1830                string.push_str(&trailing_breaks);
1831            }
1832
1833            leading_break.clear();
1834            trailing_breaks.clear();
1835
1836            leading_blank = self.input.next_is_blank();
1837
1838            self.scan_block_scalar_content_line(&mut string, &mut line_buffer);
1839
1840            // break unless the line ends with a line break, e.g., on EOF
1841            self.input.lookahead(2);
1842            if !self.input.next_is_break() {
1843                break;
1844            }
1845
1846            self.read_break(&mut leading_break);
1847
1848            // Eat the following indentation spaces and line breaks.
1849            self.skip_block_scalar_indent(indent, &mut trailing_breaks);
1850        }
1851
1852        // Chomp the tail.
1853        if chomping != Chomping::Strip {
1854            string.push_str(&leading_break);
1855            // If we had reached an eof but the last character wasn't an end-of-line, check if the
1856            // last line was indented at least as the rest of the scalar, then we need to consider
1857            // there is a newline.
1858            if self.input.next_is_z() && self.mark.col >= indent.max(1) {
1859                string.push('\n');
1860            }
1861        }
1862
1863        if chomping == Chomping::Keep {
1864            string.push_str(&trailing_breaks);
1865        }
1866
1867        Ok(Token(
1868            Span::new(start_mark, self.mark),
1869            TokenType::Scalar(style, string.into()),
1870        ))
1871    }
1872
1873    /// Retrieve the contents of the line, parsing it as a block scalar.
1874    ///
1875    /// The contents will be appended to `string`. `line_buffer` is used as a temporary buffer to
1876    /// store bytes before pushing them to `string` and thus avoiding reallocating more than
1877    /// necessary. `line_buffer` is assumed to be empty upon calling this function. It will be
1878    /// `clear`ed before the end of the function.
1879    ///
1880    /// This function assumed the first character to read is the first content character in the
1881    /// line. This function does not consume the line break character(s) after the line.
1882    fn scan_block_scalar_content_line(&mut self, string: &mut String, line_buffer: &mut String) {
1883        // Start by evaluating characters in the buffer.
1884        while !self.input.buf_is_empty() && is_yaml_non_break(self.input.peek()) {
1885            string.push(self.input.peek());
1886            // We may technically skip non-blank characters. However, the only distinction is
1887            // to determine what is leading whitespace and what is not. Here, we read the
1888            // contents of the line until either eof or a linebreak. We know we will not read
1889            // `self.leading_whitespace` until the end of the line, where it will be reset.
1890            // This allows us to call a slightly less expensive function.
1891            self.skip_blank();
1892        }
1893
1894        // All characters that were in the buffer were consumed. We need to check if more
1895        // follow.
1896        if self.input.buf_is_empty() {
1897            // We will read all consecutive non-breakz characters. We push them into a
1898            // temporary buffer. The main difference with going through `self.buffer` is that
1899            // characters are appended here as their real size (1B for ascii, or up to 4 bytes for
1900            // UTF-8). We can then use the internal `line_buffer` `Vec` to push data into `string`
1901            // (using `String::push_str`).
1902            while let Some(c) = self.input.raw_read_non_breakz_ch() {
1903                line_buffer.push(c);
1904            }
1905
1906            // We need to manually update our position; we haven't called a `skip` function.
1907            let n_chars = line_buffer.chars().count();
1908            self.mark.col += n_chars;
1909            self.mark.index += n_chars;
1910
1911            // We can now append our bytes to our `string`.
1912            string.reserve(line_buffer.len());
1913            string.push_str(line_buffer);
1914            // This clears the _contents_ without touching the _capacity_.
1915            line_buffer.clear();
1916        }
1917    }
1918
1919    /// Skip the block scalar indentation and empty lines.
1920    fn skip_block_scalar_indent(&mut self, indent: usize, breaks: &mut String) {
1921        loop {
1922            // Consume all spaces. Tabs cannot be used as indentation.
1923            if indent < self.input.bufmaxlen() - 2 {
1924                self.input.lookahead(self.input.bufmaxlen());
1925                while self.mark.col < indent && self.input.peek() == ' ' {
1926                    self.skip_blank();
1927                }
1928            } else {
1929                loop {
1930                    self.input.lookahead(self.input.bufmaxlen());
1931                    while !self.input.buf_is_empty()
1932                        && self.mark.col < indent
1933                        && self.input.peek() == ' '
1934                    {
1935                        self.skip_blank();
1936                    }
1937                    // If we reached our indent, we can break. We must also break if we have
1938                    // reached content or EOF; that is, the buffer is not empty and the next
1939                    // character is not a space.
1940                    if self.mark.col == indent
1941                        || (!self.input.buf_is_empty() && self.input.peek() != ' ')
1942                    {
1943                        break;
1944                    }
1945                }
1946                self.input.lookahead(2);
1947            }
1948
1949            // If our current line is empty, skip over the break and continue looping.
1950            if self.input.next_is_break() {
1951                self.read_break(breaks);
1952            } else {
1953                // Otherwise, we have a content line. Return control.
1954                break;
1955            }
1956        }
1957    }
1958
1959    /// Determine the indentation level for a block scalar from the first line of its contents.
1960    ///
1961    /// The function skips over whitespace-only lines and sets `indent` to the the longest
1962    /// whitespace line that was encountered.
1963    fn skip_block_scalar_first_line_indent(&mut self, indent: &mut usize, breaks: &mut String) {
1964        let mut max_indent = 0;
1965        loop {
1966            // Consume all spaces. Tabs cannot be used as indentation.
1967            while self.input.look_ch() == ' ' {
1968                self.skip_blank();
1969            }
1970
1971            if self.mark.col > max_indent {
1972                max_indent = self.mark.col;
1973            }
1974
1975            if self.input.next_is_break() {
1976                // If our current line is empty, skip over the break and continue looping.
1977                self.input.lookahead(2);
1978                self.read_break(breaks);
1979            } else {
1980                // Otherwise, we have a content line. Return control.
1981                break;
1982            }
1983        }
1984
1985        // In case a yaml looks like:
1986        // ```yaml
1987        // |
1988        // foo
1989        // bar
1990        // ```
1991        // We need to set the indent to 0 and not 1. In all other cases, the indent must be at
1992        // least 1. When in the above example, `self.indent` will be set to -1.
1993        *indent = max_indent.max((self.indent + 1) as usize);
1994        if self.indent > 0 {
1995            *indent = (*indent).max(1);
1996        }
1997    }
1998
1999    fn fetch_flow_scalar(&mut self, single: bool) -> ScanResult {
2000        self.save_simple_key();
2001        self.disallow_simple_key();
2002
2003        let tok = self.scan_flow_scalar(single)?;
2004
2005        // From spec: To ensure JSON compatibility, if a key inside a flow mapping is JSON-like,
2006        // YAML allows the following value to be specified adjacent to the “:”.
2007        self.skip_to_next_token()?;
2008        self.adjacent_value_allowed_at = self.mark.index;
2009
2010        self.tokens.push_back(tok);
2011        Ok(())
2012    }
2013
2014    #[allow(clippy::too_many_lines)]
2015    fn scan_flow_scalar(&mut self, single: bool) -> Result<Token<'input>, ScanError> {
2016        let start_mark = self.mark;
2017
2018        let mut string = String::new();
2019        let mut leading_break = String::new();
2020        let mut trailing_breaks = String::new();
2021        let mut whitespaces = String::new();
2022        let mut leading_blanks;
2023
2024        /* Eat the left quote. */
2025        self.skip_non_blank();
2026
2027        loop {
2028            /* Check for a document indicator. */
2029            self.input.lookahead(4);
2030
2031            if self.mark.col == 0 && self.input.next_is_document_indicator() {
2032                return Err(ScanError::new_str(
2033                    start_mark,
2034                    "while scanning a quoted scalar, found unexpected document indicator",
2035                ));
2036            }
2037
2038            if self.input.next_is_z() {
2039                return Err(ScanError::new_str(
2040                    start_mark,
2041                    "while scanning a quoted scalar, found unexpected end of stream",
2042                ));
2043            }
2044
2045            if (self.mark.col as isize) < self.indent {
2046                return Err(ScanError::new_str(
2047                    start_mark,
2048                    "invalid indentation in quoted scalar",
2049                ));
2050            }
2051
2052            leading_blanks = false;
2053            self.consume_flow_scalar_non_whitespace_chars(
2054                single,
2055                &mut string,
2056                &mut leading_blanks,
2057                &start_mark,
2058            )?;
2059
2060            match self.input.look_ch() {
2061                '\'' if single => break,
2062                '"' if !single => break,
2063                _ => {}
2064            }
2065
2066            // Consume blank characters.
2067            while self.input.next_is_blank() || self.input.next_is_break() {
2068                if self.input.next_is_blank() {
2069                    // Consume a space or a tab character.
2070                    if leading_blanks {
2071                        if self.input.peek() == '\t' && (self.mark.col as isize) < self.indent {
2072                            return Err(ScanError::new_str(
2073                                self.mark,
2074                                "tab cannot be used as indentation",
2075                            ));
2076                        }
2077                        self.skip_blank();
2078                    } else {
2079                        whitespaces.push(self.input.peek());
2080                        self.skip_blank();
2081                    }
2082                } else {
2083                    self.input.lookahead(2);
2084                    // Check if it is a first line break.
2085                    if leading_blanks {
2086                        self.read_break(&mut trailing_breaks);
2087                    } else {
2088                        whitespaces.clear();
2089                        self.read_break(&mut leading_break);
2090                        leading_blanks = true;
2091                    }
2092                }
2093                self.input.lookahead(1);
2094            }
2095
2096            // Join the whitespaces or fold line breaks.
2097            if leading_blanks {
2098                if leading_break.is_empty() {
2099                    string.push_str(&leading_break);
2100                    string.push_str(&trailing_breaks);
2101                    trailing_breaks.clear();
2102                    leading_break.clear();
2103                } else {
2104                    if trailing_breaks.is_empty() {
2105                        string.push(' ');
2106                    } else {
2107                        string.push_str(&trailing_breaks);
2108                        trailing_breaks.clear();
2109                    }
2110                    leading_break.clear();
2111                }
2112            } else {
2113                string.push_str(&whitespaces);
2114                whitespaces.clear();
2115            }
2116        } // loop
2117
2118        // Eat the right quote.
2119        self.skip_non_blank();
2120        // Ensure there is no invalid trailing content.
2121        self.skip_ws_to_eol(SkipTabs::Yes)?;
2122        match self.input.peek() {
2123            // These can be encountered in flow sequences or mappings.
2124            ',' | '}' | ']' if self.flow_level > 0 => {}
2125            // An end-of-line / end-of-stream is fine. No trailing content.
2126            c if is_breakz(c) => {}
2127            // ':' can be encountered if our scalar is a key.
2128            // Outside of flow contexts, keys cannot span multiple lines
2129            ':' if self.flow_level == 0 && start_mark.line == self.mark.line => {}
2130            // Inside a flow context, this is allowed.
2131            ':' if self.flow_level > 0 => {}
2132            _ => {
2133                return Err(ScanError::new_str(
2134                    self.mark,
2135                    "invalid trailing content after double-quoted scalar",
2136                ));
2137            }
2138        }
2139
2140        let style = if single {
2141            ScalarStyle::SingleQuoted
2142        } else {
2143            ScalarStyle::DoubleQuoted
2144        };
2145        Ok(Token(
2146            Span::new(start_mark, self.mark),
2147            TokenType::Scalar(style, string.into()),
2148        ))
2149    }
2150
2151    /// Consume successive non-whitespace characters from a flow scalar.
2152    ///
2153    /// This function resolves escape sequences and stops upon encountering a whitespace, the end
2154    /// of the stream or the closing character for the scalar (`'` for single quoted scalars, `"`
2155    /// for double quoted scalars).
2156    ///
2157    /// # Errors
2158    /// Return an error if an invalid escape sequence is found.
2159    fn consume_flow_scalar_non_whitespace_chars(
2160        &mut self,
2161        single: bool,
2162        string: &mut String,
2163        leading_blanks: &mut bool,
2164        start_mark: &Marker,
2165    ) -> Result<(), ScanError> {
2166        self.input.lookahead(2);
2167        while !is_blank_or_breakz(self.input.peek()) {
2168            match self.input.peek() {
2169                // Check for an escaped single quote.
2170                '\'' if self.input.peek_nth(1) == '\'' && single => {
2171                    string.push('\'');
2172                    self.skip_n_non_blank(2);
2173                }
2174                // Check for the right quote.
2175                '\'' if single => break,
2176                '"' if !single => break,
2177                // Check for an escaped line break.
2178                '\\' if !single && is_break(self.input.peek_nth(1)) => {
2179                    self.input.lookahead(3);
2180                    self.skip_non_blank();
2181                    self.skip_linebreak();
2182                    *leading_blanks = true;
2183                    break;
2184                }
2185                // Check for an escape sequence.
2186                '\\' if !single => {
2187                    string.push(self.resolve_flow_scalar_escape_sequence(start_mark)?);
2188                }
2189                c => {
2190                    string.push(c);
2191                    self.skip_non_blank();
2192                }
2193            }
2194            self.input.lookahead(2);
2195        }
2196        Ok(())
2197    }
2198
2199    /// Escape the sequence we encounter in a flow scalar.
2200    ///
2201    /// `self.input.peek()` must point to the `\` starting the escape sequence.
2202    ///
2203    /// # Errors
2204    /// Return an error if an invalid escape sequence is found.
2205    fn resolve_flow_scalar_escape_sequence(
2206        &mut self,
2207        start_mark: &Marker,
2208    ) -> Result<char, ScanError> {
2209        let mut code_length = 0usize;
2210        let mut ret = '\0';
2211
2212        match self.input.peek_nth(1) {
2213            '0' => ret = '\0',
2214            'a' => ret = '\x07',
2215            'b' => ret = '\x08',
2216            't' | '\t' => ret = '\t',
2217            'n' => ret = '\n',
2218            'v' => ret = '\x0b',
2219            'f' => ret = '\x0c',
2220            'r' => ret = '\x0d',
2221            'e' => ret = '\x1b',
2222            ' ' => ret = '\x20',
2223            '"' => ret = '"',
2224            '/' => ret = '/',
2225            '\\' => ret = '\\',
2226            // Unicode next line (#x85)
2227            'N' => ret = char::from_u32(0x85).unwrap(),
2228            // Unicode non-breaking space (#xA0)
2229            '_' => ret = char::from_u32(0xA0).unwrap(),
2230            // Unicode line separator (#x2028)
2231            'L' => ret = char::from_u32(0x2028).unwrap(),
2232            // Unicode paragraph separator (#x2029)
2233            'P' => ret = char::from_u32(0x2029).unwrap(),
2234            'x' => code_length = 2,
2235            'u' => code_length = 4,
2236            'U' => code_length = 8,
2237            _ => {
2238                return Err(ScanError::new_str(
2239                    *start_mark,
2240                    "while parsing a quoted scalar, found unknown escape character",
2241                ));
2242            }
2243        }
2244        self.skip_n_non_blank(2);
2245
2246        // Consume an arbitrary escape code.
2247        if code_length > 0 {
2248            self.input.lookahead(code_length);
2249            let mut value = 0u32;
2250            for i in 0..code_length {
2251                let c = self.input.peek_nth(i);
2252                if !is_hex(c) {
2253                    return Err(ScanError::new_str(
2254                        *start_mark,
2255                        "while parsing a quoted scalar, did not find expected hexadecimal number",
2256                    ));
2257                }
2258                value = (value << 4) + as_hex(c);
2259            }
2260
2261            let Some(ch) = char::from_u32(value) else {
2262                return Err(ScanError::new_str(
2263                    *start_mark,
2264                    "while parsing a quoted scalar, found invalid Unicode character escape code",
2265                ));
2266            };
2267            ret = ch;
2268
2269            self.skip_n_non_blank(code_length);
2270        }
2271        Ok(ret)
2272    }
2273
2274    fn fetch_plain_scalar(&mut self) -> ScanResult {
2275        self.save_simple_key();
2276        self.disallow_simple_key();
2277
2278        let tok = self.scan_plain_scalar()?;
2279
2280        self.tokens.push_back(tok);
2281        Ok(())
2282    }
2283
2284    /// Scan for a plain scalar.
2285    ///
2286    /// Plain scalars are the most readable but restricted style. They may span multiple lines in
2287    /// some contexts.
2288    #[allow(clippy::too_many_lines)]
2289    fn scan_plain_scalar(&mut self) -> Result<Token<'input>, ScanError> {
2290        self.unroll_non_block_indents();
2291        let indent = self.indent + 1;
2292        let start_mark = self.mark;
2293
2294        if self.flow_level > 0 && (start_mark.col as isize) < indent {
2295            return Err(ScanError::new_str(
2296                start_mark,
2297                "invalid indentation in flow construct",
2298            ));
2299        }
2300
2301        let mut string = String::with_capacity(32);
2302        self.buf_whitespaces.clear();
2303        self.buf_leading_break.clear();
2304        self.buf_trailing_breaks.clear();
2305        let mut end_mark = self.mark;
2306
2307        loop {
2308            self.input.lookahead(4);
2309            if (self.mark.col == 0 && self.input.next_is_document_indicator())
2310                || self.input.peek() == '#'
2311            {
2312                // BS4K: If a `#` starts a comment after some separation spaces following content
2313                // of a plain scalar in block context, and there is potential continuation on the
2314                // next line, this is invalid. We cannot decide yet if there will be continuation,
2315                // so record that a comment interrupted a plain scalar.
2316                if self.input.peek() == '#'
2317                    && !string.is_empty()
2318                    && !self.buf_whitespaces.is_empty()
2319                    && self.flow_level == 0
2320                {
2321                    self.interrupted_plain_by_comment = Some(self.mark);
2322                }
2323                break;
2324            }
2325
2326            if self.flow_level > 0 && self.input.peek() == '-' && is_flow(self.input.peek_nth(1)) {
2327                return Err(ScanError::new_str(
2328                    self.mark,
2329                    "plain scalar cannot start with '-' followed by ,[]{}",
2330                ));
2331            }
2332
2333            if !self.input.next_is_blank_or_breakz()
2334                && self.input.next_can_be_plain_scalar(self.flow_level > 0)
2335            {
2336                if self.leading_whitespace {
2337                    if self.buf_leading_break.is_empty() {
2338                        string.push_str(&self.buf_leading_break);
2339                        string.push_str(&self.buf_trailing_breaks);
2340                        self.buf_trailing_breaks.clear();
2341                        self.buf_leading_break.clear();
2342                    } else {
2343                        if self.buf_trailing_breaks.is_empty() {
2344                            string.push(' ');
2345                        } else {
2346                            string.push_str(&self.buf_trailing_breaks);
2347                            self.buf_trailing_breaks.clear();
2348                        }
2349                        self.buf_leading_break.clear();
2350                    }
2351                    self.leading_whitespace = false;
2352                } else if !self.buf_whitespaces.is_empty() {
2353                    string.push_str(&self.buf_whitespaces);
2354                    self.buf_whitespaces.clear();
2355                }
2356
2357                // We can unroll the first iteration of the loop.
2358                string.push(self.input.peek());
2359                self.skip_non_blank();
2360                string.reserve(self.input.bufmaxlen());
2361
2362                // Add content non-blank characters to the scalar.
2363                let mut end = false;
2364                while !end {
2365                    // Fill the buffer once and process all characters in the buffer until the next
2366                    // fetch. Note that `next_can_be_plain_scalar` needs 2 lookahead characters,
2367                    // hence the `for` loop looping `self.input.bufmaxlen() - 1` times.
2368                    self.input.lookahead(self.input.bufmaxlen());
2369                    for _ in 0..self.input.bufmaxlen() - 1 {
2370                        if self.input.next_is_blank_or_breakz()
2371                            || !self.input.next_can_be_plain_scalar(self.flow_level > 0)
2372                        {
2373                            end = true;
2374                            break;
2375                        }
2376                        string.push(self.input.peek());
2377                        self.skip_non_blank();
2378                    }
2379                }
2380                end_mark = self.mark;
2381            }
2382
2383            // We may reach the end of a plain scalar if:
2384            //  - We reach eof
2385            //  - We reach ": "
2386            //  - We find a flow character in a flow context
2387            if !(self.input.next_is_blank() || self.input.next_is_break()) {
2388                break;
2389            }
2390
2391            // Process blank characters.
2392            self.input.lookahead(2);
2393            while self.input.next_is_blank_or_break() {
2394                if self.input.next_is_blank() {
2395                    if !self.leading_whitespace {
2396                        self.buf_whitespaces.push(self.input.peek());
2397                        self.skip_blank();
2398                    } else if (self.mark.col as isize) < indent && self.input.peek() == '\t' {
2399                        // Tabs in an indentation columns are allowed if and only if the line is
2400                        // empty. Skip to the end of the line.
2401                        self.skip_ws_to_eol(SkipTabs::Yes)?;
2402                        if !self.input.next_is_breakz() {
2403                            return Err(ScanError::new_str(
2404                                start_mark,
2405                                "while scanning a plain scalar, found a tab",
2406                            ));
2407                        }
2408                    } else {
2409                        self.skip_blank();
2410                    }
2411                } else {
2412                    // Check if it is a first line break
2413                    if self.leading_whitespace {
2414                        self.skip_break();
2415                        self.buf_trailing_breaks.push('\n');
2416                    } else {
2417                        self.buf_whitespaces.clear();
2418                        self.skip_break();
2419                        self.buf_leading_break.push('\n');
2420                        self.leading_whitespace = true;
2421                    }
2422                }
2423                self.input.lookahead(2);
2424            }
2425
2426            // check indentation level
2427            if self.flow_level == 0 && (self.mark.col as isize) < indent {
2428                break;
2429            }
2430        }
2431
2432        if self.leading_whitespace {
2433            self.allow_simple_key();
2434        }
2435
2436        if string.is_empty() {
2437            // `fetch_plain_scalar` must absolutely consume at least one byte. Otherwise,
2438            // `fetch_next_token` will never stop calling it. An empty plain scalar may happen with
2439            // erroneous inputs such as "{...".
2440            Err(ScanError::new_str(
2441                start_mark,
2442                "unexpected end of plain scalar",
2443            ))
2444        } else {
2445            Ok(Token(
2446                Span::new(start_mark, end_mark),
2447                TokenType::Scalar(ScalarStyle::Plain, string.into()),
2448            ))
2449        }
2450    }
2451
2452    fn fetch_key(&mut self) -> ScanResult {
2453        let start_mark = self.mark;
2454        if self.flow_level == 0 {
2455            // Check if we are allowed to start a new key (not necessarily simple).
2456            if !self.simple_key_allowed {
2457                return Err(ScanError::new_str(
2458                    self.mark,
2459                    "mapping keys are not allowed in this context",
2460                ));
2461            }
2462            self.roll_indent(
2463                start_mark.col,
2464                None,
2465                TokenType::BlockMappingStart,
2466                start_mark,
2467            );
2468        } else {
2469            // The scanner, upon emitting a `Key`, will prepend a `MappingStart` event.
2470            self.flow_mapping_started = true;
2471        }
2472
2473        self.remove_simple_key()?;
2474
2475        if self.flow_level == 0 {
2476            self.allow_simple_key();
2477        } else {
2478            self.disallow_simple_key();
2479        }
2480
2481        self.skip_non_blank();
2482        self.skip_yaml_whitespace()?;
2483        if self.input.peek() == '\t' {
2484            return Err(ScanError::new_str(
2485                self.mark(),
2486                "tabs disallowed in this context",
2487            ));
2488        }
2489        self.tokens
2490            .push_back(Token(Span::new(start_mark, self.mark), TokenType::Key));
2491        Ok(())
2492    }
2493
2494    /// Fetch a value in a mapping inside of a flow collection.
2495    ///
2496    /// This must not be called if [`self.flow_level`] is 0. This ensures the rules surrounding
2497    /// values in flow collections are respected prior to calling [`fetch_value`].
2498    ///
2499    /// [`self.flow_level`]: Self::flow_level
2500    /// [`fetch_value`]: Self::fetch_value
2501    fn fetch_flow_value(&mut self) -> ScanResult {
2502        let nc = self.input.peek_nth(1);
2503
2504        // If we encounter a ':' inside a flow collection and it is not immediately
2505        // followed by a blank or breakz:
2506        //   - We must check whether an adjacent value is allowed
2507        //     `["a":[]]` is valid. If the key is double-quoted, no need for a space. This
2508        //     is needed for JSON compatibility.
2509        //   - If not, we must ensure there is a space after the ':' and before its value.
2510        //     `[a: []]` is valid while `[a:[]]` isn't. `[a:b]` is treated as `["a:b"]`.
2511        //   - But if the value is empty (null), then it's okay.
2512        // The last line is for YAMLs like `[a:]`. The ':' is followed by a ']' (which is a
2513        // flow character), but the ']' is not the value. The value is an invisible empty
2514        // space which is represented as null ('~').
2515        if self.mark.index != self.adjacent_value_allowed_at && (nc == '[' || nc == '{') {
2516            return Err(ScanError::new_str(
2517                self.mark,
2518                "':' may not precede any of `[{` in flow mapping",
2519            ));
2520        }
2521
2522        self.fetch_value()
2523    }
2524
2525    /// Fetch a value from a mapping (after a `:`).
2526    fn fetch_value(&mut self) -> ScanResult {
2527        let sk = self.simple_keys.last().unwrap().clone();
2528        let start_mark = self.mark;
2529        let is_implicit_flow_mapping =
2530            !self.implicit_flow_mapping_states.is_empty() && !self.flow_mapping_started;
2531        if is_implicit_flow_mapping {
2532            *self.implicit_flow_mapping_states.last_mut().unwrap() = ImplicitMappingState::Inside;
2533        }
2534
2535        // Skip over ':'.
2536        self.skip_non_blank();
2537        if self.input.look_ch() == '\t'
2538            && !self.skip_ws_to_eol(SkipTabs::Yes)?.has_valid_yaml_ws()
2539            && (self.input.peek() == '-' || self.input.next_is_alpha())
2540        {
2541            return Err(ScanError::new_str(
2542                self.mark,
2543                "':' must be followed by a valid YAML whitespace",
2544            ));
2545        }
2546
2547        if sk.possible {
2548            // insert simple key
2549            let tok = Token(Span::empty(sk.mark), TokenType::Key);
2550            self.insert_token(sk.token_number - self.tokens_parsed, tok);
2551            if is_implicit_flow_mapping {
2552                if sk.mark.line < start_mark.line {
2553                    return Err(ScanError::new_str(
2554                        start_mark,
2555                        "illegal placement of ':' indicator",
2556                    ));
2557                }
2558                self.insert_token(
2559                    sk.token_number - self.tokens_parsed,
2560                    Token(Span::empty(sk.mark), TokenType::FlowMappingStart),
2561                );
2562            }
2563
2564            // Add the BLOCK-MAPPING-START token if needed.
2565            self.roll_indent(
2566                sk.mark.col,
2567                Some(sk.token_number),
2568                TokenType::BlockMappingStart,
2569                sk.mark,
2570            );
2571            self.roll_one_col_indent();
2572
2573            self.simple_keys.last_mut().unwrap().possible = false;
2574            self.disallow_simple_key();
2575        } else {
2576            if is_implicit_flow_mapping {
2577                self.tokens
2578                    .push_back(Token(Span::empty(start_mark), TokenType::FlowMappingStart));
2579            }
2580            // The ':' indicator follows a complex key.
2581            if self.flow_level == 0 {
2582                if !self.simple_key_allowed {
2583                    return Err(ScanError::new_str(
2584                        start_mark,
2585                        "mapping values are not allowed in this context",
2586                    ));
2587                }
2588
2589                self.roll_indent(
2590                    start_mark.col,
2591                    None,
2592                    TokenType::BlockMappingStart,
2593                    start_mark,
2594                );
2595            }
2596            self.roll_one_col_indent();
2597
2598            if self.flow_level == 0 {
2599                self.allow_simple_key();
2600            } else {
2601                self.disallow_simple_key();
2602            }
2603        }
2604        self.tokens
2605            .push_back(Token(Span::empty(start_mark), TokenType::Value));
2606
2607        Ok(())
2608    }
2609
2610    /// Add an indentation level to the stack with the given block token, if needed.
2611    ///
2612    /// An indentation level is added only if:
2613    ///   - We are not in a flow-style construct (which don't have indentation per-se).
2614    ///   - The current column is further indented than the last indent we have registered.
2615    fn roll_indent(
2616        &mut self,
2617        col: usize,
2618        number: Option<usize>,
2619        tok: TokenType<'input>,
2620        mark: Marker,
2621    ) {
2622        if self.flow_level > 0 {
2623            return;
2624        }
2625
2626        // If the last indent was a non-block indent, remove it.
2627        // This means that we prepared an indent that we thought we wouldn't use, but realized just
2628        // now that it is a block indent.
2629        if self.indent <= col as isize {
2630            if let Some(indent) = self.indents.last() {
2631                if !indent.needs_block_end {
2632                    self.indent = indent.indent;
2633                    self.indents.pop();
2634                }
2635            }
2636        }
2637
2638        if self.indent < col as isize {
2639            self.indents.push(Indent {
2640                indent: self.indent,
2641                needs_block_end: true,
2642            });
2643            self.indent = col as isize;
2644            let tokens_parsed = self.tokens_parsed;
2645            match number {
2646                Some(n) => self.insert_token(n - tokens_parsed, Token(Span::empty(mark), tok)),
2647                None => self.tokens.push_back(Token(Span::empty(mark), tok)),
2648            }
2649        }
2650    }
2651
2652    /// Pop indentation levels from the stack as much as needed.
2653    ///
2654    /// Indentation levels are popped from the stack while they are further indented than `col`.
2655    /// If we are in a flow-style construct (which don't have indentation per-se), this function
2656    /// does nothing.
2657    fn unroll_indent(&mut self, col: isize) {
2658        if self.flow_level > 0 {
2659            return;
2660        }
2661        while self.indent > col {
2662            let indent = self.indents.pop().unwrap();
2663            self.indent = indent.indent;
2664            if indent.needs_block_end {
2665                self.tokens
2666                    .push_back(Token(Span::empty(self.mark), TokenType::BlockEnd));
2667            }
2668        }
2669    }
2670
2671    /// Add an indentation level of 1 column that does not start a block.
2672    ///
2673    /// See the documentation of [`Indent::needs_block_end`] for more details.
2674    /// An indentation is not added if we are inside a flow level or if the last indent is already
2675    /// a non-block indent.
2676    fn roll_one_col_indent(&mut self) {
2677        if self.flow_level == 0 && self.indents.last().is_some_and(|x| x.needs_block_end) {
2678            self.indents.push(Indent {
2679                indent: self.indent,
2680                needs_block_end: false,
2681            });
2682            self.indent += 1;
2683        }
2684    }
2685
2686    /// Unroll all last indents created with [`Self::roll_one_col_indent`].
2687    fn unroll_non_block_indents(&mut self) {
2688        while let Some(indent) = self.indents.last() {
2689            if indent.needs_block_end {
2690                break;
2691            }
2692            self.indent = indent.indent;
2693            self.indents.pop();
2694        }
2695    }
2696
2697    /// Mark the next token to be inserted as a potential simple key.
2698    fn save_simple_key(&mut self) {
2699        if self.simple_key_allowed {
2700            let required = self.flow_level == 0
2701                && self.indent == (self.mark.col as isize)
2702                && self.indents.last().unwrap().needs_block_end;
2703            let mut sk = SimpleKey::new(self.mark);
2704            sk.possible = true;
2705            sk.required = required;
2706            sk.token_number = self.tokens_parsed + self.tokens.len();
2707
2708            self.simple_keys.pop();
2709            self.simple_keys.push(sk);
2710        }
2711    }
2712
2713    fn remove_simple_key(&mut self) -> ScanResult {
2714        let last = self.simple_keys.last_mut().unwrap();
2715        if last.possible && last.required {
2716            return Err(ScanError::new_str(self.mark, "simple key expected"));
2717        }
2718
2719        last.possible = false;
2720        Ok(())
2721    }
2722
2723    /// Return whether the scanner is inside a block but outside of a flow sequence.
2724    fn is_within_block(&self) -> bool {
2725        !self.indents.is_empty()
2726    }
2727
2728    /// If an implicit mapping had started, end it.
2729    ///
2730    /// This function does not pop the state in [`implicit_flow_mapping_states`].
2731    ///
2732    /// [`implicit_flow_mapping_states`]: Self::implicit_flow_mapping_states
2733    fn end_implicit_mapping(&mut self, mark: Marker) {
2734        if let Some(implicit_mapping) = self.implicit_flow_mapping_states.last_mut() {
2735            if *implicit_mapping == ImplicitMappingState::Inside {
2736                self.flow_mapping_started = false;
2737                *implicit_mapping = ImplicitMappingState::Possible;
2738                self.tokens
2739                    .push_back(Token(Span::empty(mark), TokenType::FlowMappingEnd));
2740            }
2741        }
2742    }
2743}
2744
2745/// Chomping, how final line breaks and trailing empty lines are interpreted.
2746///
2747/// See YAML spec 8.1.1.2.
2748#[derive(PartialEq, Eq)]
2749pub enum Chomping {
2750    /// The final line break and any trailing empty lines are excluded.
2751    Strip,
2752    /// The final line break is preserved, but trailing empty lines are excluded.
2753    Clip,
2754    /// The final line break and trailing empty lines are included.
2755    Keep,
2756}
2757
2758#[cfg(test)]
2759mod test {
2760    #[test]
2761    fn test_is_anchor_char() {
2762        use super::is_anchor_char;
2763        assert!(is_anchor_char('x'));
2764    }
2765}