From e7f29450cae5dba14d2d577760382f6b0eab6db0 Mon Sep 17 00:00:00 2001 From: Ethiraric Date: Tue, 26 Dec 2023 19:11:17 +0100 Subject: [PATCH] Fix empty keys in implicit mappings. --- saphyr/src/scanner.rs | 43 +++++++++++++++++++++++++++++++++ saphyr/tests/yaml-test-suite.rs | 2 -- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/saphyr/src/scanner.rs b/saphyr/src/scanner.rs index 07c76b5..75cc1ab 100644 --- a/saphyr/src/scanner.rs +++ b/saphyr/src/scanner.rs @@ -251,6 +251,15 @@ pub struct Scanner { token_available: bool, /// Whether all characters encountered since the last newline were whitespace. leading_whitespace: bool, + /// Whether we started a flow mapping. + /// + /// This is used to detect implicit flow mapping starts such as: + /// ```yaml + /// [ : foo ] # { null: "foo" } + /// ``` + flow_mapping_started: bool, + /// Whether we currently are in an implicit flow mapping. + implicit_flow_mapping: bool, } impl> Iterator for Scanner { @@ -365,6 +374,8 @@ impl> Scanner { tokens_parsed: 0, token_available: false, leading_whitespace: true, + flow_mapping_started: false, + implicit_flow_mapping: false, } } @@ -1217,6 +1228,10 @@ impl> Scanner { let start_mark = self.mark; self.skip(); + if tok == TokenType::FlowMappingStart { + self.flow_mapping_started = true; + } + self.tokens.push_back(Token(start_mark, tok)); Ok(()) } @@ -1227,6 +1242,8 @@ impl> Scanner { self.disallow_simple_key(); + self.end_implicit_mapping(self.mark); + let start_mark = self.mark; self.skip(); @@ -1239,6 +1256,8 @@ impl> Scanner { self.remove_simple_key()?; self.allow_simple_key(); + self.end_implicit_mapping(self.mark); + let start_mark = self.mark; self.skip(); @@ -1899,6 +1918,9 @@ impl> Scanner { TokenType::BlockMappingStart, start_mark, ); + } else { + // The parser, upon receiving a `Key`, will insert a `MappingStart` event. + self.flow_mapping_started = true; } self.remove_simple_key()?; @@ -1925,6 +1947,7 @@ impl> Scanner { fn fetch_value(&mut self) -> ScanResult { let sk = self.simple_keys.last().unwrap().clone(); let start_mark = self.mark; + self.implicit_flow_mapping = self.flow_level > 0 && !self.flow_mapping_started; // Skip over ':'. self.skip(); @@ -1943,6 +1966,12 @@ impl> Scanner { let tok = Token(sk.mark, TokenType::Key); let tokens_parsed = self.tokens_parsed; self.insert_token(sk.token_number - tokens_parsed, tok); + if self.implicit_flow_mapping { + self.insert_token( + sk.token_number - tokens_parsed, + Token(self.mark, TokenType::FlowMappingStart), + ); + } // Add the BLOCK-MAPPING-START token if needed. self.roll_indent( @@ -1956,6 +1985,10 @@ impl> Scanner { self.simple_keys.last_mut().unwrap().possible = false; self.disallow_simple_key(); } else { + if self.implicit_flow_mapping { + self.tokens + .push_back(Token(self.mark, TokenType::FlowMappingStart)); + } // The ':' indicator follows a complex key. if self.flow_level == 0 { if !self.simple_key_allowed { @@ -2096,6 +2129,16 @@ impl> Scanner { fn is_within_block(&self) -> bool { !self.indents.is_empty() } + + /// If an implicit mapping had started, end it. + fn end_implicit_mapping(&mut self, mark: Marker) { + if self.implicit_flow_mapping { + self.implicit_flow_mapping = false; + self.flow_mapping_started = false; + self.tokens + .push_back(Token(mark, TokenType::FlowMappingEnd)); + } + } } /// Behavior to adopt regarding treating tabs as whitespace. diff --git a/saphyr/tests/yaml-test-suite.rs b/saphyr/tests/yaml-test-suite.rs index 49a0445..0396fb4 100644 --- a/saphyr/tests/yaml-test-suite.rs +++ b/saphyr/tests/yaml-test-suite.rs @@ -297,8 +297,6 @@ fn expected_events(expected_tree: &str) -> Vec { #[rustfmt::skip] static EXPECTED_FAILURES: &[&str] = &[ - // Empty key in flow mappings - "CFD4", // Document with no nodes and document end "HWV9", "QT73",