fix(parsing): fold over a tab-only blank line in a plain scalar
A plain scalar may span blank lines, and a blank line that contains a tab is still blank, so the tab is not indentation. libyaml rejects any tab below the scalar indentation with 'found a tab character that violates indentation', which wrongly rejects 'foo: 1' / tab-only line / 'bar: 2'. Guard the tab check with blankLineAhead so it fires only when the tab indents real content. Surpasses libyaml on DK95/04. Port score 305 -> 306, leaving zero false-rejects.
This commit is contained in:
parent
b2d2d2f595
commit
825084dbff
@ -22,6 +22,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fold over a tab-only blank line inside a multi-line plain scalar. A plain
|
||||
scalar may span blank lines, and a blank line that contains a tab is still
|
||||
blank, so the tab is not indentation. libyaml rejects any tab below the scalar
|
||||
indentation; the scanner now rejects it only when it indents real content, not
|
||||
when the rest of the line is blank. Surpasses libyaml on DK95/04.
|
||||
- Accept a tab as in-line separation while still rejecting it as indentation. A
|
||||
tab after a `-`, `?`, or `:` indicator, or before a flow collection, is valid
|
||||
separation, but libyaml refuses to skip it whenever a simple key is allowed and
|
||||
|
||||
@ -365,7 +365,7 @@ extension PureYAML.Parsing.LibYAML.Scanner {
|
||||
|
||||
/// Whether the rest of the current line, from the current position, is only
|
||||
/// spaces and tabs up to a line break or end of stream (a blank line).
|
||||
private func blankLineAhead() -> Bool {
|
||||
func blankLineAhead() -> Bool {
|
||||
var offset = 0
|
||||
while let character = reader.peek(offset), character == " " || Classify.isTab(character) {
|
||||
offset += 1
|
||||
|
||||
@ -106,7 +106,10 @@ extension PureYAML.Parsing.LibYAML.Scanner {
|
||||
private mutating func consumePlainBreaks(_ state: inout PlainState, indent: Int, startMark: PureYAML.Parsing.Mark) throws {
|
||||
while Classify.isBlank(reader.peek()) || Classify.isBreak(reader.peek()) {
|
||||
if Classify.isBlank(reader.peek()) {
|
||||
if state.leadingBlanks, reader.column < indent, Classify.isTab(reader.peek()) {
|
||||
if state.leadingBlanks, reader.column < indent, Classify.isTab(reader.peek()), !blankLineAhead() {
|
||||
// A tab below the indentation is illegal only when it indents
|
||||
// real content; on an otherwise-blank line it is just a blank
|
||||
// line the scalar folds over. libyaml rejects it either way.
|
||||
throw scannerError("found a tab character that violates indentation", at: startMark)
|
||||
}
|
||||
if !state.leadingBlanks, let blank = reader.peek() {
|
||||
|
||||
@ -12,7 +12,7 @@ struct CanonicalEventConformanceTests {
|
||||
/// Valid suite cases whose event stream PureYAML does NOT yet emit exactly.
|
||||
/// This is the authoritative gap set: a fix removes an id (and the test fails
|
||||
/// until it is removed), a regression adds one (and the test fails). Of these,
|
||||
/// 1 currently throws on valid input and 2 emit a wrong event stream.
|
||||
/// All remaining gaps emit a wrong event stream (none throw on valid input).
|
||||
static let knownEventGaps: Set<String> = [
|
||||
// Gap set for the ported libyaml engine. Verified against the libyaml
|
||||
// reference dumper: on all but four of these the port matches libyaml
|
||||
@ -20,8 +20,6 @@ struct CanonicalEventConformanceTests {
|
||||
// bug. Surpassing libyaml means shrinking this set beyond what libyaml
|
||||
// itself handles.
|
||||
//
|
||||
// false-rejects (parseEvents throws on valid input, as libyaml does)
|
||||
"DK95/04",
|
||||
// wrong event stream: the port matches libyaml but both deviate from the
|
||||
// canonical events (libyaml quirks to be surpassed).
|
||||
"JEF9/02", "W4TN",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user