feat(parsing): accept full YAML 1.2 anchor and alias names
An anchor or alias name is ns-anchor-char+ (any printable character that is not whitespace, a line break, the BOM, or a flow indicator), so Unicode and punctuation names are valid. libyaml restricts the name to alphanumerics and rejects them. Canonical event conformance: 271 -> 275 of 308 (2SXE, 8XYN, W5VH, Y2GN). 379 tests pass.
This commit is contained in:
parent
df3aa0b5d4
commit
18101b8ddc
@ -1,20 +1,21 @@
|
||||
extension PureYAML.Parsing.LibYAML.Scanner {
|
||||
private typealias Classify = PureYAML.Parsing.LibYAML.CharacterClass
|
||||
|
||||
/// Port of `yaml_parser_scan_anchor`. Scans an anchor (`&name`) or alias
|
||||
/// (`*name`): the name is a run of `IS_ALPHA` characters and must be non-empty
|
||||
/// and followed by a blank, the end of input, or one of the indicators that may
|
||||
/// terminate it.
|
||||
/// Port of `yaml_parser_scan_anchor`, widened to YAML 1.2: an anchor (`&name`)
|
||||
/// or alias (`*name`) name is `ns-anchor-char+`, i.e. any printable character
|
||||
/// that is not whitespace, a line break, or a flow indicator (`,[]{}`). libyaml
|
||||
/// restricts the name to alphanumerics, which rejects Unicode and punctuation
|
||||
/// anchor names that YAML 1.2 allows.
|
||||
mutating func scanAnchor(isAnchor: Bool) throws -> PureYAML.Parsing.LibYAML.Token {
|
||||
let startMark = reader.mark
|
||||
reader.skip()
|
||||
var name = ""
|
||||
while let character = reader.peek(), Classify.isAlpha(character) {
|
||||
while let character = reader.peek(), isAnchorCharacter(character) {
|
||||
name.unicodeScalars.append(character)
|
||||
reader.skip()
|
||||
}
|
||||
let endMark = reader.mark
|
||||
guard !name.isEmpty, Classify.isBlankZ(reader.peek()) || isAnchorTerminator(reader.peek()) else {
|
||||
guard !name.isEmpty else {
|
||||
throw PureYAML.Parsing.LibYAML.ScannerError(
|
||||
context: isAnchor ? "while scanning an anchor" : "while scanning an alias",
|
||||
problem: "did not find expected alphabetic or numeric character",
|
||||
@ -28,10 +29,17 @@ extension PureYAML.Parsing.LibYAML.Scanner {
|
||||
)
|
||||
}
|
||||
|
||||
private func isAnchorTerminator(_ scalar: Unicode.Scalar?) -> Bool {
|
||||
/// `ns-anchor-char`: a printable, non-blank, non-break character that is not a
|
||||
/// flow indicator or the byte-order mark.
|
||||
private func isAnchorCharacter(_ scalar: Unicode.Scalar) -> Bool {
|
||||
guard Classify.isPrintable(scalar), !Classify.isBlankZ(scalar), scalar != "\u{FEFF}" else {
|
||||
return false
|
||||
}
|
||||
switch scalar {
|
||||
case "?", ":", ",", "]", "}", "%", "@", "`": true
|
||||
default: false
|
||||
case ",", "[", "]", "{", "}":
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,14 +21,14 @@ struct CanonicalEventConformanceTests {
|
||||
// itself handles.
|
||||
//
|
||||
// false-rejects (parseEvents throws on valid input, as libyaml does)
|
||||
"2JQS", "2SXE", "4ABK", "58MP", "5T43", "6BCT", "6CA3", "6M2F",
|
||||
"8XYN", "96NN/00", "96NN/01", "A2M4", "CFD4", "DBG4", "DK3J", "DK95/00",
|
||||
"2JQS", "4ABK", "58MP", "5T43", "6BCT", "6CA3", "6M2F",
|
||||
"96NN/00", "96NN/01", "A2M4", "CFD4", "DBG4", "DK3J", "DK95/00",
|
||||
"DK95/04", "FP8R", "FRK4", "HM87/00", "M2N8/00", "M7A3", "NHX8",
|
||||
"NKF9", "Q5MG", "R4YG", "S3PD", "SM9W/01", "UKK6/00", "W4TN",
|
||||
"W5VH", "Y79Y/001", "Y79Y/010",
|
||||
"Y79Y/001", "Y79Y/010",
|
||||
// wrong event stream: the port matches libyaml but both deviate from the
|
||||
// canonical events (libyaml quirks to be surpassed).
|
||||
"652Z", "HM87/01", "JEF9/02", "Y2GN",
|
||||
"652Z", "HM87/01", "JEF9/02",
|
||||
]
|
||||
|
||||
@Test("Every valid case emits canonical events, except the recorded gaps")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user