PureYAML/Sources/Parsing/LibYAML.Scanner.Anchor.swift
Mihaela Mihaljevic 18101b8ddc 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.
2026-06-24 18:11:58 +02:00

46 lines
1.8 KiB
Swift

extension PureYAML.Parsing.LibYAML.Scanner {
private typealias Classify = PureYAML.Parsing.LibYAML.CharacterClass
/// 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(), isAnchorCharacter(character) {
name.unicodeScalars.append(character)
reader.skip()
}
let endMark = reader.mark
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",
mark: startMark,
)
}
return PureYAML.Parsing.LibYAML.Token(
type: isAnchor ? .anchor(name: name) : .alias(name: name),
startMark: startMark,
endMark: endMark,
)
}
/// `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 ",", "[", "]", "{", "}":
return false
default:
return true
}
}
}