//! Home to the YAML Parser. //! //! The parser takes input from the [`crate::scanner::Scanner`], performs final checks for YAML //! compliance, and emits a stream of tokens that can be used by the [`crate::YamlLoader`] to //! construct the [`crate::Yaml`] object.
usecrate::scanner::{Marker, ScanError, Scanner, TScalarStyle, Token, TokenType}; use std::collections::HashMap;
#[derive(Clone, Copy, PartialEq, Debug, Eq)] enum State { /// We await the start of the stream.
StreamStart,
ImplicitDocumentStart,
DocumentStart,
DocumentContent,
DocumentEnd,
BlockNode, // BlockNodeOrIndentlessSequence, // FlowNode,
BlockSequenceFirstEntry,
BlockSequenceEntry,
IndentlessSequenceEntry,
BlockMappingFirstKey,
BlockMappingKey,
BlockMappingValue,
FlowSequenceFirstEntry,
FlowSequenceEntry,
FlowSequenceEntryMappingKey,
FlowSequenceEntryMappingValue,
FlowSequenceEntryMappingEnd,
FlowMappingFirstKey,
FlowMappingKey,
FlowMappingValue,
FlowMappingEmptyValue,
End,
}
/// An event generated by the YAML parser. /// /// Events are used in the low-level event-based API (push parser). The API entrypoint is the /// [`EventReceiver`] trait. #[derive(Clone, PartialEq, Debug, Eq)] pubenum Event { /// Reserved for internal use.
Nothing, /// Event generated at the very beginning of parsing.
StreamStart, /// Last event that will be generated by the parser. Signals EOF.
StreamEnd, /// The YAML start document directive (`---`).
DocumentStart, /// The YAML end document directive (`...`).
DocumentEnd, /// A YAML Alias.
Alias( /// The anchor ID the alias refers to.
usize,
), /// Value, style, anchor id, tag
Scalar(String, TScalarStyle, usize, Option<Tag>), /// The start of a YAML sequence (array).
SequenceStart( /// The anchor ID of the start of the sequence.
usize, /// An optional tag
Option<Tag>,
), /// The end of a YAML sequence (array).
SequenceEnd, /// The start of a YAML mapping (object, hash).
MappingStart( /// The anchor ID of the start of the mapping.
usize, /// An optional tag
Option<Tag>,
), /// The end of a YAML mapping (object, hash).
MappingEnd,
}
/// A YAML tag. #[derive(Clone, PartialEq, Debug, Eq)] pubstruct Tag { /// Handle of the tag (`!` included). pub handle: String, /// The suffix of the tag. pub suffix: String,
}
/// Create an empty scalar with the given anchor. fn empty_scalar_with_anchor(anchor: usize, tag: Option<Tag>) -> Event {
Event::Scalar(String::new(), TScalarStyle::Plain, anchor, tag)
}
}
/// A YAML parser. #[derive(Debug)] pubstruct Parser<T> {
scanner: Scanner<T>,
states: Vec<State>,
state: State,
token: Option<Token>,
current: Option<(Event, Marker)>,
anchors: HashMap<String, usize>,
anchor_id: usize, /// The tag directives (`%TAG`) the parser has encountered. /// /// Key is the handle, and value is the prefix.
tags: HashMap<String, String>, /// Make tags global across all documents.
keep_tags: bool,
}
/// Trait to be implemented in order to use the low-level parsing API. /// /// The low-level parsing API is event-based (a push parser), calling [`EventReceiver::on_event`] /// for each YAML [`Event`] that occurs. /// The [`EventReceiver`] trait only receives events. In order to receive both events and their /// location in the source, use [`MarkedEventReceiver`]. Note that [`EventReceiver`]s implement /// [`MarkedEventReceiver`] automatically. /// /// # Event hierarchy /// The event stream starts with an [`Event::StreamStart`] event followed by an /// [`Event::DocumentStart`] event. If the YAML document starts with a mapping (an object), an /// [`Event::MappingStart`] event is emitted. If it starts with a sequence (an array), an /// [`Event::SequenceStart`] event is emitted. Otherwise, an [`Event::Scalar`] event is emitted. /// /// In a mapping, key-values are sent as consecutive events. The first event after an /// [`Event::MappingStart`] will be the key, and following its value. If the mapping contains no /// sub-mapping or sub-sequence, then even events (starting from 0) will always be keys and odd /// ones will always be values. The mapping ends when an [`Event::MappingEnd`] event is received. /// /// In a sequence, values are sent consecutively until the [`Event::SequenceEnd`] event. /// /// If a value is a sub-mapping or a sub-sequence, an [`Event::MappingStart`] or /// [`Event::SequenceStart`] event will be sent respectively. Following events until the associated /// [`Event::MappingStart`] or [`Event::SequenceEnd`] (beware of nested mappings or sequences) will /// be part of the value and not another key-value pair or element in the sequence. /// /// For instance, the following yaml: /// ```yaml /// a: b /// c: /// d: e /// f: /// - g /// - h /// ``` /// will emit (indented and commented for lisibility): /// ```text /// StreamStart, DocumentStart, MappingStart, /// Scalar("a", ..), Scalar("b", ..) /// Scalar("c", ..), MappingStart, Scalar("d", ..), Scalar("e", ..), MappingEnd, /// Scalar("f", ..), SequenceStart, Scalar("g", ..), Scalar("h", ..), SequenceEnd, /// MappingEnd, DocumentEnd, StreamEnd /// ``` /// /// # Example /// ``` /// # use yaml_rust2::parser::{Event, EventReceiver, Parser}; /// # /// /// Sink of events. Collects them into an array. /// struct EventSink { /// events: Vec<Event>, /// } /// /// /// Implement `on_event`, pushing into `self.events`. /// impl EventReceiver for EventSink { /// fn on_event(&mut self, ev: Event) { /// self.events.push(ev); /// } /// } /// /// /// Load events from a yaml string. /// fn str_to_events(yaml: &str) -> Vec<Event> { /// let mut sink = EventSink { events: Vec::new() }; /// let mut parser = Parser::new_from_str(yaml); /// // Load events using our sink as the receiver. /// parser.load(&mut sink, true).unwrap(); /// sink.events /// } /// ``` pubtrait EventReceiver { /// Handler called for each YAML event that is emitted by the parser. fn on_event(&mutself, ev: Event);
}
/// Trait to be implemented for using the low-level parsing API. /// /// Functionally similar to [`EventReceiver`], but receives a [`Marker`] as well as the event. pubtrait MarkedEventReceiver { /// Handler called for each event that occurs. fn on_event(&mutself, ev: Event, _mark: Marker);
}
impl<R: EventReceiver> MarkedEventReceiver for R { fn on_event(&mutself, ev: Event, _mark: Marker) { self.on_event(ev);
}
}
/// A convenience alias for a `Result` of a parser event. pubtype ParseResult = Result<(Event, Marker), ScanError>;
impl<'a> Parser<core::str::Chars<'a>> { /// Create a new instance of a parser from a &str. #[must_use] pubfn new_from_str(value: &'a str) -> Self {
Parser::new(value.chars())
}
}
impl<T: Iterator<Item = char>> Parser<T> { /// Create a new instance of a parser from the given input of characters. pubfn new(src: T) -> Parser<T> {
Parser {
scanner: Scanner::new(src),
states: Vec::new(),
state: State::StreamStart,
token: None,
current: None,
/// Whether to keep tags across multiple documents when parsing. /// /// This behavior is non-standard as per the YAML specification but can be encountered in the /// wild. This boolean allows enabling this non-standard extension. This would result in the /// parser accepting input from [test /// QLJ7](https://github.com/yaml/yaml-test-suite/blob/ccfa74e56afb53da960847ff6e6976c0a0825709/src/QLJ7.yaml) /// of the yaml-test-suite: /// /// ```yaml /// %TAG !prefix! tag:example.com,2011: /// --- !prefix!A /// a: b /// --- !prefix!B /// c: d /// --- !prefix!C /// e: f /// ``` /// /// With `keep_tags` set to `false`, the above YAML is rejected. As per the specification, tags /// only apply to the document immediately following them. This would error on `!prefix!B`. /// /// With `keep_tags` set to `true`, the above YAML is accepted by the parser. #[must_use] pubfn keep_tags(mutself, value: bool) -> Self { self.keep_tags = value; self
}
/// Try to load the next event and return it, but do not consuming it from `self`. /// /// Any subsequent call to [`Parser::peek`] will return the same value, until a call to /// [`Iterator::next`] or [`Parser::load`]. /// # Errors /// Returns `ScanError` when loading the next event fails. pubfn peek(&mutself) -> Result<&(Event, Marker), ScanError> { iflet Some(ref x) = self.current {
Ok(x)
} else { self.current = Some(self.next_token()?); self.peek()
}
}
/// Try to load the next event and return it, consuming it from `self`. /// # Errors /// Returns `ScanError` when loading the next event fails. pubfn next_token(&mutself) -> ParseResult { matchself.current.take() {
None => self.parse(),
Some(v) => Ok(v),
}
}
/// Peek at the next token from the scanner. fn peek_token(&mutself) -> Result<&Token, ScanError> { matchself.token {
None => { self.token = Some(self.scan_next_token()?);
Ok(self.token.as_ref().unwrap())
}
Some(ref tok) => Ok(tok),
}
}
/// Extract and return the next token from the scanner. /// /// This function does _not_ make use of `self.token`. fn scan_next_token(&mutself) -> Result<Token, ScanError> { let token = self.scanner.next(); match token {
None => matchself.scanner.get_error() {
None => Err(ScanError::new(self.scanner.mark(), "unexpected eof")),
Some(e) => Err(e),
},
Some(tok) => Ok(tok),
}
}
fn fetch_token(&mutself) -> Token { self.token
.take()
.expect("fetch_token needs to be preceded by peek_token")
}
/// Skip the next token from the scanner. fn skip(&mutself) { self.token = None; //self.peek_token();
} /// Pops the top-most state and make it the current state. fn pop_state(&mutself) { self.state = self.states.pop().unwrap();
} /// Push a new state atop the state stack. fn push_state(&mutself, state: State) { self.states.push(state);
}
/// Load the YAML from the stream in `self`, pushing events into `recv`. /// /// The contents of the stream are parsed and the corresponding events are sent into the /// recveiver. For detailed explanations about how events work, see [`EventReceiver`]. /// /// If `multi` is set to `true`, the parser will allow parsing of multiple YAML documents /// inside the stream. /// /// Note that any [`EventReceiver`] is also a [`MarkedEventReceiver`], so implementing the /// former is enough to call this function. /// # Errors /// Returns `ScanError` when loading fails. pubfn load<R: MarkedEventReceiver>(
&mutself,
recv: &mut R,
multi: bool,
) -> Result<(), ScanError> { if !self.scanner.stream_started() { let (ev, mark) = self.next_token()?; if ev != Event::StreamStart { return Err(ScanError::new(mark, "did not find expected <stream-start>"));
}
recv.on_event(ev, mark);
}
ifself.scanner.stream_ended() { // XXX has parsed?
recv.on_event(Event::StreamEnd, self.scanner.mark()); return Ok(());
} loop { let (ev, mark) = self.next_token()?; if ev == Event::StreamEnd {
recv.on_event(ev, mark); return Ok(());
} // clear anchors before a new document self.anchors.clear(); self.load_document(ev, mark, recv)?; if !multi { break;
}
}
Ok(())
}
fn parser_process_directives(&mutself) -> Result<(), ScanError> { letmut version_directive_received = false; loop { letmut tags = HashMap::new(); matchself.peek_token()? {
Token(mark, TokenType::VersionDirective(_, _)) => { // XXX parsing with warning according to spec //if major != 1 || minor > 2 { // return Err(ScanError::new(tok.0, // "found incompatible YAML document")); //} if version_directive_received { return Err(ScanError::new(*mark, "duplicate version directive"));
}
version_directive_received = true;
}
Token(mark, TokenType::TagDirective(handle, prefix)) => { if tags.contains_key(handle) { return Err(ScanError::new(*mark, "the TAG directive must only be given at most once per handle in the same document"));
}
tags.insert(handle.to_string(), prefix.to_string());
}
_ => break,
} self.tags = tags; self.skip();
}
Ok(())
}
/// Resolve a tag from the handle and the suffix. fn resolve_tag(&self, mark: Marker, handle: &str, suffix: String) -> Result<Tag, ScanError> { if handle == "!!" { // "!!" is a shorthand for "tag:yaml.org,2002:". However, that default can be // overridden. matchself.tags.get("!!") {
Some(prefix) => Ok(Tag {
handle: prefix.to_string(),
suffix,
}),
None => Ok(Tag {
handle: "tag:yaml.org,2002:".to_string(),
suffix,
}),
}
} elseif handle.is_empty() && suffix == "!" { // "!" introduces a local tag. Local tags may have their prefix overridden. matchself.tags.get("") {
Some(prefix) => Ok(Tag {
handle: prefix.to_string(),
suffix,
}),
None => Ok(Tag {
handle: String::new(),
suffix,
}),
}
} else { // Lookup handle in our tag directives. let prefix = self.tags.get(handle); iflet Some(prefix) = prefix {
Ok(Tag {
handle: prefix.to_string(),
suffix,
})
} else { // Otherwise, it may be a local handle. With a local handle, the handle is set to // "!" and the suffix to whatever follows it ("!foo" -> ("!", "foo")). // If the handle is of the form "!foo!", this cannot be a local handle and we need // to error. if handle.len() >= 2 && handle.starts_with('!') && handle.ends_with('!') {
Err(ScanError::new(mark, "the handle wasn't declared"))
} else {
Ok(Tag {
handle: handle.to_string(),
suffix,
})
}
}
}
}
}
#[cfg(test)] mod test { usesuper::{Event, Parser}; usecrate::YamlLoader;
#[test] fn test_peek_eq_parse() { let s = "
a0 bb: val
a1: &x
b1: 4
b2: d
a2: 4
a3: [1, 2, 3]
a4:
- [a1, a2]
- 2
a5: *x "; letmut p = Parser::new_from_str(s); while { let event_peek = p.peek().unwrap().clone(); let event = p.next_token().unwrap();
assert_eq!(event, event_peek);
event.0 != Event::StreamEnd
} {}
}
#[test] fn test_keep_tags_across_multiple_documents() { let text = r#"
%YAML 1.1
%TAG !t! tag:test,2024:
--- !t!1 &1
foo: "bar"
--- !t!2 &2
baz: "qux" "#; letmut parser = Parser::new_from_str(text).keep_tags(true); let result = YamlLoader::load_from_parser(&mut parser);
assert!(result.is_ok()); let docs = result.unwrap();
assert_eq!(docs.len(), 2); let yaml = &docs[0];
assert_eq!(yaml["foo"].as_str(), Some("bar")); let yaml = &docs[1];
assert_eq!(yaml["baz"].as_str(), Some("qux"));
letmut parser = Parser::new_from_str(text).keep_tags(false); let result = YamlLoader::load_from_parser(&mut parser);
assert!(result.is_err());
}
}
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.12Angebot
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.