fix(parser): match block continuation parsing

This commit is contained in:
Mihaela Mihaljevic 2026-06-05 22:12:12 +02:00
parent 09b7f52cbc
commit 37239fe613
6 changed files with 100 additions and 4 deletions

View File

@ -7,7 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
Nothing yet.
### Fixed
- Treat more-indented plain scalar continuation lines that start with `-` as
text unless they appear at a known sequence-entry indentation. This fixes
OpenAPI-style `allOf` descriptions while preserving nested block sequences
such as `- - 123`.
- Parse explicit scalar keys whose `:` value starts a same-line mapping and
continues with indented mapping siblings, matching large OpenAPI schema keys.
## [0.1.0] - 2026-06-05

View File

@ -138,14 +138,14 @@ extension PureYAML.Parsing.Scanner {
guard !probe.isAtEnd, !isLineBreak(probe.peek()), probe.peek() != "#" else {
return value
}
guard !isBlockEntry(probe) else {
return value
}
let baseIndentation = plainScalarContinuationBaseIndentation(state: state)
guard indentationWidth > baseIndentation else {
return value
}
if isBlockEntry(probe), state.validImplicitIndentation.contains(indentationWidth) {
return value
}
var continuationState = state
continuationState.reader = probe

View File

@ -30,6 +30,13 @@ extension PureYAML.Parsing.TokenEventParser {
if properties == .none, canParsePlainScalarWithContinuation(after: valueIndicator) {
return try parsePlainScalarWithContinuation(properties: properties)
}
if cursor.current?.kind.isMappingKey == true, hasNodeOnSameLine(after: valueIndicator) {
return try parseBlockMapping(
properties: properties,
includeIndentedContinuation: true,
minimumColumn: cursor.current?.mark.column,
)
}
if properties != .none || hasNodeOnSameLine(after: valueIndicator) {
return try parseNode(properties: properties)
}

View File

@ -166,6 +166,39 @@ enum CollectionCompatibilityFixtures {
]))),
])),
),
SuccessCase(
name: "mapping value with indentless sequence of inline mappings",
yaml: """
schema:
allOf:
- $ref: '#/components/schemas/ErrorCategory'
- description: An integer value that specifies the category of a query
failure error. Values include 1 - System, 2 - User, and 3
- Other.
""",
expected: .mapping(.init([
.init(key: "schema", value: .mapping(.init([
.init(key: "allOf", value: .sequence([
.mapping(.init([
.init(
key: "$ref",
value: .string("#/components/schemas/ErrorCategory"),
),
])),
.mapping(.init([
.init(
key: "description",
value: .string(
"An integer value that specifies the category of a query "
+ "failure error. Values include 1 - System, 2 - User, and 3 "
+ "- Other.",
),
),
])),
])),
]))),
])),
),
]
static let parseErrors: [ParseErrorCase] = [

View File

@ -84,6 +84,34 @@ struct ComplexMappingKeyParsingTests {
#expect(value.rootMapping?["[\"a\", \"b\"]"] == nil)
}
@Test("Parses explicit scalar keys with same-line mapping values")
func test_parsesExplicitScalarKeysWithSameLineMappingValues() throws {
let value = try PureYAML.parse("""
? __stringMin14PatternS3Captions
: minLength: 14
pattern: ^((s3://(.*?)\\.(srt|SRT))|(https?://(.*?)\\.(srt|SRT)))$
type: string
next: done
""")
let key = PureYAML.Model.Key.string("__stringMin14PatternS3Captions")
#expect(value == .mapping(.init([
.init(keyNode: key, value: .mapping(.init([
.init(key: "minLength", value: .int(14)),
.init(
key: "pattern",
value: .string("^((s3://(.*?)\\.(srt|SRT))|(https?://(.*?)\\.(srt|SRT)))$"),
),
.init(key: "type", value: .string("string")),
]))),
.init(key: "next", value: .string("done")),
])))
#expect(value.rootMapping?[key]?.rootMapping?["minLength"] == .int(14))
#expect(value.rootMapping?[key]?.rootMapping?["type"] == .string("string"))
#expect(value.rootMapping?["next"] == .string("done"))
#expect(value.rootMapping?["minLength"] == nil)
}
@Test("Expands merged mappings with complex-key local overrides")
func test_expandsMergedMappingsWithComplexKeyLocalOverrides() throws {
let value = try PureYAML.parse("""

View File

@ -43,3 +43,24 @@ Every compatibility test must pin at least one of:
- exact validation issue;
- exact path-aware decoding or encoding error;
- exact required absence check for fields that must not appear.
## Parser Compatibility Lessons
YAML compatibility is parser-state compatibility, not only value-model
compatibility. Indicator characters such as `-`, `?`, and `:` are meaningful
only after indentation, flow depth, and the current parser production are taken
into account.
When fixing scanner or parser behavior:
- preserve tests for the exact production corpus shape that failed, not only a
smaller feature in isolation;
- combine interacting YAML features in one regression fixture when the bug
requires their interaction;
- keep implementation fixes grammar-level and content-independent; never branch
on corpus file names, OpenAPI field names, vendor names, schema prefixes, or
other production-data fingerprints;
- for plain scalar continuation bugs, cover both sides explicitly:
- a more-indented continuation line that starts with an indicator but remains
text;
- a same-context indicator line that must remain a collection item.