granit-parser
Advanced tools
| { | ||
| "git": { | ||
| "sha1": "9359b0a65b0968c6c69a77138d4d007068f871f9" | ||
| "sha1": "db5bf9f1b85fc1de1e32e08ddf296872c1403ee8" | ||
| }, | ||
| "path_in_vcs": "" | ||
| } |
@@ -89,3 +89,3 @@ name: CI | ||
| - name: Upload to Codecov | ||
| uses: codecov/codecov-action@v5 | ||
| uses: codecov/codecov-action@v7 | ||
| with: | ||
@@ -92,0 +92,0 @@ token: ${{ secrets.CODECOV_TOKEN }} |
@@ -23,6 +23,6 @@ name: Fuzz | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 30 | ||
| timeout-minutes: 360 | ||
| env: | ||
| CARGO_TERM_COLOR: always | ||
| FUZZ_SECONDS: ${{ github.event_name == 'schedule' && '1200' || '20' }} | ||
| FUZZ_SECONDS: 1200 | ||
| steps: | ||
@@ -29,0 +29,0 @@ - name: Checkout |
+1
-1
@@ -115,3 +115,3 @@ # This file is automatically @generated by Cargo. | ||
| name = "granit-parser" | ||
| version = "0.0.5" | ||
| version = "0.0.6" | ||
| dependencies = [ | ||
@@ -118,0 +118,0 @@ "arraydeque", |
+1
-1
@@ -16,3 +16,3 @@ # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO | ||
| name = "granit-parser" | ||
| version = "0.0.5" | ||
| version = "0.0.6" | ||
| authors = [ | ||
@@ -19,0 +19,0 @@ "Ethiraric <ethiraric@gmail.com>", |
+7
-0
| # Changelog | ||
| ## v0.0.6 | ||
| - Added `Tag::core_suffix` and made core-tag helpers match the resolved YAML 1.2.2 | ||
| Core Schema tags (`null`, `bool`, `int`, `float`, `map`, `seq`, and `str`) | ||
| instead of checking only the resolved handle. | ||
| - Added `Span::indent` hints for non-empty block scalar content while leaving | ||
| whitespace-only block scalar spans unannotated. | ||
| ## v0.0.5 | ||
@@ -3,0 +10,0 @@ - Performance improvements on comment parsing |
+38
-32
@@ -47,2 +47,3 @@ # granit-parser | ||
| %TAG !example! tag:example.com,2000: | ||
| %TAG !core! tag:yaml.org,2002:i | ||
| --- | ||
@@ -53,2 +54,3 @@ items: !shopping | ||
| - !!str bread | ||
| - !core!nt 1 | ||
| locations: # Example with composite keys | ||
@@ -72,3 +74,4 @@ [47.3769, 8.5417]: local | ||
| println!( | ||
| "scalar tag: {tag} core-str={} tag_start(line,col,byte)={tag_start:?} for {value:?}", | ||
| "scalar tag: {tag} core-suffix={:?} core-str={} tag_start(line,col,byte)={tag_start:?} for {value:?}", | ||
| tag.core_suffix(), | ||
| tag.is_yaml_core_schema_tag("str") | ||
@@ -99,33 +102,35 @@ ); | ||
| StreamStart bytes=Some(0..0) source=Some("") | ||
| DocumentStart(true) bytes=Some(38..41) source=Some("---") | ||
| MappingStart(Block, 0, None) bytes=Some(42..42) source=Some("") | ||
| Scalar("items", Plain, 0, None) bytes=Some(42..47) source=Some("items") | ||
| node tag: !shopping custom=true tag_start(line,col,byte)=Some((4, 7, Some(49))) | ||
| SequenceStart(Block, 0, Some(Tag { handle: "!", suffix: "shopping", original_handle: "!" })) bytes=Some(61..61) source=Some("") | ||
| Scalar("milk", Plain, 0, None) bytes=Some(63..67) source=Some("milk") | ||
| scalar tag: tag:example.com,2000:sliced core-str=false tag_start(line,col,byte)=Some((6, 4, Some(72))) for "bread" | ||
| Scalar("bread", Plain, 0, Some(Tag { handle: "tag:example.com,2000:", suffix: "sliced", original_handle: "!example!" })) bytes=Some(88..93) source=Some("bread") | ||
| scalar tag: tag:yaml.org,2002:str core-str=true tag_start(line,col,byte)=Some((7, 4, Some(98))) for "bread" | ||
| Scalar("bread", Plain, 0, Some(Tag { handle: "tag:yaml.org,2002:", suffix: "str", original_handle: "!!" })) bytes=Some(104..109) source=Some("bread") | ||
| SequenceEnd bytes=Some(110..110) source=Some("") | ||
| Scalar("locations", Plain, 0, None) bytes=Some(110..119) source=Some("locations") | ||
| Comment(" Example with composite keys", Right) bytes=Some(121..150) source=Some("# Example with composite keys") | ||
| MappingStart(Block, 0, None) bytes=Some(153..153) source=Some("") | ||
| SequenceStart(Flow, 0, None) bytes=Some(153..154) source=Some("[") | ||
| Scalar("47.3769", Plain, 0, None) bytes=Some(154..161) source=Some("47.3769") | ||
| Scalar("8.5417", Plain, 0, None) bytes=Some(163..169) source=Some("8.5417") | ||
| SequenceEnd bytes=Some(169..170) source=Some("]") | ||
| Scalar("local", Plain, 0, None) bytes=Some(172..177) source=Some("local") | ||
| SequenceStart(Flow, 0, None) bytes=Some(180..181) source=Some("[") | ||
| Scalar("40.7128", Plain, 0, None) bytes=Some(181..188) source=Some("40.7128") | ||
| Scalar("-74.0060", Plain, 0, None) bytes=Some(190..198) source=Some("-74.0060") | ||
| SequenceEnd bytes=Some(198..199) source=Some("]") | ||
| Scalar("remote", Plain, 0, None) bytes=Some(201..207) source=Some("remote") | ||
| Comment(" JSON-style \\uXXXX surrogate pairs:", Above) bytes=Some(209..245) source=Some("# JSON-style \\uXXXX surrogate pairs:") | ||
| MappingEnd bytes=Some(246..246) source=Some("") | ||
| Scalar("music", Plain, 0, None) bytes=Some(246..251) source=Some("music") | ||
| Scalar("𝄞🎵🎶", DoubleQuoted, 0, None) bytes=Some(253..291) source=Some("\"\\uD834\\uDD1E\\uD83C\\uDFB5\\uD83C\\uDFB6\"") | ||
| MappingEnd bytes=Some(292..292) source=Some("") | ||
| DocumentEnd bytes=Some(292..292) source=Some("") | ||
| StreamEnd bytes=Some(292..292) source=Some("") | ||
| DocumentStart(true) bytes=Some(70..73) source=Some("---") | ||
| MappingStart(Block, 0, None) bytes=Some(74..74) source=Some("") | ||
| Scalar("items", Plain, 0, None) bytes=Some(74..79) source=Some("items") | ||
| node tag: !shopping custom=true tag_start(line,col,byte)=Some((5, 7, Some(81))) | ||
| SequenceStart(Block, 0, Some(Tag { handle: "!", suffix: "shopping", original_handle: "!" })) bytes=Some(93..93) source=Some("") | ||
| Scalar("milk", Plain, 0, None) bytes=Some(95..99) source=Some("milk") | ||
| scalar tag: tag:example.com,2000:sliced core-suffix=None core-str=false tag_start(line,col,byte)=Some((7, 4, Some(104))) for "bread" | ||
| Scalar("bread", Plain, 0, Some(Tag { handle: "tag:example.com,2000:", suffix: "sliced", original_handle: "!example!" })) bytes=Some(120..125) source=Some("bread") | ||
| scalar tag: tag:yaml.org,2002:str core-suffix=Some("str") core-str=true tag_start(line,col,byte)=Some((8, 4, Some(130))) for "bread" | ||
| Scalar("bread", Plain, 0, Some(Tag { handle: "tag:yaml.org,2002:", suffix: "str", original_handle: "!!" })) bytes=Some(136..141) source=Some("bread") | ||
| scalar tag: tag:yaml.org,2002:int core-suffix=Some("int") core-str=false tag_start(line,col,byte)=Some((9, 4, Some(146))) for "1" | ||
| Scalar("1", Plain, 0, Some(Tag { handle: "tag:yaml.org,2002:i", suffix: "nt", original_handle: "!core!" })) bytes=Some(155..156) source=Some("1") | ||
| SequenceEnd bytes=Some(157..157) source=Some("") | ||
| Scalar("locations", Plain, 0, None) bytes=Some(157..166) source=Some("locations") | ||
| Comment(" Example with composite keys", Right) bytes=Some(168..197) source=Some("# Example with composite keys") | ||
| MappingStart(Block, 0, None) bytes=Some(200..200) source=Some("") | ||
| SequenceStart(Flow, 0, None) bytes=Some(200..201) source=Some("[") | ||
| Scalar("47.3769", Plain, 0, None) bytes=Some(201..208) source=Some("47.3769") | ||
| Scalar("8.5417", Plain, 0, None) bytes=Some(210..216) source=Some("8.5417") | ||
| SequenceEnd bytes=Some(216..217) source=Some("]") | ||
| Scalar("local", Plain, 0, None) bytes=Some(219..224) source=Some("local") | ||
| SequenceStart(Flow, 0, None) bytes=Some(227..228) source=Some("[") | ||
| Scalar("40.7128", Plain, 0, None) bytes=Some(228..235) source=Some("40.7128") | ||
| Scalar("-74.0060", Plain, 0, None) bytes=Some(237..245) source=Some("-74.0060") | ||
| SequenceEnd bytes=Some(245..246) source=Some("]") | ||
| Scalar("remote", Plain, 0, None) bytes=Some(248..254) source=Some("remote") | ||
| Comment(" JSON-style \\uXXXX surrogate pairs:", Above) bytes=Some(256..292) source=Some("# JSON-style \\uXXXX surrogate pairs:") | ||
| MappingEnd bytes=Some(293..293) source=Some("") | ||
| Scalar("music", Plain, 0, None) bytes=Some(293..298) source=Some("music") | ||
| Scalar("𝄞🎵🎶", DoubleQuoted, 0, None) bytes=Some(300..338) source=Some("\"\\uD834\\uDD1E\\uD83C\\uDFB5\\uD83C\\uDFB6\"") | ||
| MappingEnd bytes=Some(339..339) source=Some("") | ||
| DocumentEnd bytes=Some(339..339) source=Some("") | ||
| StreamEnd bytes=Some(339..339) source=Some("") | ||
| ``` | ||
@@ -254,2 +259,3 @@ | ||
| - `Tag::original` | ||
| - `Tag::core_suffix` | ||
| - `Tag::is_custom` | ||
@@ -256,0 +262,0 @@ - `Tag::is_yaml_core_schema_tag` |
+30
-1
@@ -1,2 +0,2 @@ | ||
| use granit_parser::{Event, Parser}; | ||
| use granit_parser::{Event, Parser, ScalarStyle}; | ||
@@ -10,2 +10,14 @@ fn scalar_value<'a>(ev: &'a Event<'_>) -> Option<&'a str> { | ||
| fn block_scalar_indents(yaml: &str) -> Vec<(String, Option<usize>)> { | ||
| Parser::new_from_str(yaml) | ||
| .map(|event| event.expect("valid yaml")) | ||
| .filter_map(|(event, span)| match event { | ||
| Event::Scalar(value, ScalarStyle::Literal | ScalarStyle::Folded, ..) => { | ||
| Some((value.into_owned(), span.indent)) | ||
| } | ||
| _ => None, | ||
| }) | ||
| .collect() | ||
| } | ||
| #[test] | ||
@@ -84,1 +96,18 @@ fn indentation_is_reported_for_block_mapping_keys_only() { | ||
| } | ||
| #[test] | ||
| fn indentation_is_reported_for_block_scalar_content() { | ||
| let yaml = "key: |\n body\n"; | ||
| assert_eq!( | ||
| block_scalar_indents(yaml), | ||
| vec![("body\n".to_string(), Some(2))] | ||
| ); | ||
| } | ||
| #[test] | ||
| fn indentation_is_not_reported_for_whitespace_only_block_scalar_content() { | ||
| let yaml = "key: |+\n \n"; | ||
| assert_eq!(block_scalar_indents(yaml), vec![("\n".to_string(), None)]); | ||
| } |
+75
-0
@@ -465,2 +465,47 @@ use granit_parser::{ | ||
| #[test] | ||
| fn test_granit_issue14_colon_in_alias_name_is_not_value_indicator() { | ||
| // YAML 1.2.2: | ||
| // | ||
| // ns-anchor-char ::= ns-char - c-flow-indicator | ||
| // | ||
| // ':' is not a c-flow-indicator, so in `*foo: bar` the alias name is `foo:`. | ||
| // This must not be parsed as alias `foo` used as a mapping key. | ||
| // | ||
| // The first line intentionally defines `&foo`. A buggy parser that stops the | ||
| // alias at ':' will therefore parse the second line as a valid mapping entry. | ||
| // A fixed parser consumes `foo:` as the alias name and then rejects the stray | ||
| // `bar` node. | ||
| let input = "foo: &foo value\n*foo: bar\n"; | ||
| assert!( | ||
| run_parser(input).is_err(), | ||
| "`*foo: bar` must be rejected: ':' is part of the alias name `foo:`, \ | ||
| not a mapping value indicator after alias `foo`" | ||
| ); | ||
| } | ||
| #[test] | ||
| fn test_granit_issue14_space_before_colon_keeps_alias_key_mapping_valid() { | ||
| // With an explicit separation space before ':', the alias name is `foo`. | ||
| // This is the valid alias-key mapping form. | ||
| let input = "foo: &foo value\n*foo : bar\n"; | ||
| assert_eq!( | ||
| run_parser(input).unwrap(), | ||
| [ | ||
| Event::StreamStart, | ||
| Event::DocumentStart(false), | ||
| map(Block), | ||
| Event::Scalar("foo".into(), ScalarStyle::Plain, 0, None), | ||
| Event::Scalar("value".into(), ScalarStyle::Plain, 1, None), | ||
| Event::Alias(1), | ||
| Event::Scalar("bar".into(), ScalarStyle::Plain, 0, None), | ||
| Event::MappingEnd, | ||
| Event::DocumentEnd, | ||
| Event::StreamEnd, | ||
| ] | ||
| ); | ||
| } | ||
| #[test] | ||
| fn test_issue13() { | ||
@@ -627,1 +672,31 @@ // The following input creates an infinite loop. | ||
| } | ||
| #[test] | ||
| fn test_granit_issue14_anchor_and_alias_names_may_end_with_colon() { | ||
| // YAML 1.2.2: | ||
| // | ||
| // ns-anchor-char ::= ns-char - c-flow-indicator | ||
| // ns-anchor-name ::= ns-anchor-char+ | ||
| // | ||
| // `:` is not a flow indicator. Therefore both `&foo:` and `*foo:` | ||
| // use the anchor/alias name `foo:`. | ||
| // | ||
| // A buggy scanner that stops anchor/alias names before `:` will tokenize | ||
| // this as `&foo` / `*foo` followed by a mapping value indicator, producing | ||
| // either a parse error or the wrong event stream. | ||
| let input = "- &foo: value\n- *foo:\n"; | ||
| assert_eq!( | ||
| run_parser(input).unwrap(), | ||
| [ | ||
| Event::StreamStart, | ||
| Event::DocumentStart(false), | ||
| seq(Block), | ||
| Event::Scalar("value".into(), ScalarStyle::Plain, 1, None), | ||
| Event::Alias(1), | ||
| Event::SequenceEnd, | ||
| Event::DocumentEnd, | ||
| Event::StreamEnd, | ||
| ] | ||
| ); | ||
| } |
+2
-1
@@ -39,3 +39,4 @@ use granit_parser::{Parser, ScanError}; | ||
| lines.push(format!( | ||
| "scalar tag: {tag} core-str={} tag_start(line,col,byte)={tag_start:?} for {value:?}", | ||
| "scalar tag: {tag} core-suffix={:?} core-str={} tag_start(line,col,byte)={tag_start:?} for {value:?}", | ||
| tag.core_suffix(), | ||
| tag.is_yaml_core_schema_tag("str") | ||
@@ -42,0 +43,0 @@ )); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display