libyaml makes every '?' in flow context a mapping-key indicator, so it
parses '{ ?foo: bar }' as a key 'foo' (dropping the '?') and '[?x]' as an
empty single-pair mapping. Per ns-plain-first a '?' followed by a
flow-safe character (anything but whitespace, end of input, or a flow
indicator) begins a plain scalar.
Gate the '?' key-indicator dispatch on isBlankZ or, in flow context, a
flow indicator, so '?foo' and '?x' stay plain-scalar content. Hoist the
flow-indicator predicate into CharacterClass and reuse it in the plain
scalar scanner. Surpasses libyaml on 652Z and HM87/01. Port score
290 -> 292.
104 lines
3.9 KiB
Swift
104 lines
3.9 KiB
Swift
extension PureYAML.Parsing.LibYAML {
|
|
/// Port of libyaml's character-class macros (`IS_*` in `yaml_private.h`).
|
|
/// libyaml inspects UTF-8 bytes; this operates on `Unicode.Scalar`, where the
|
|
/// multi-byte line breaks NEL (`U+0085`), LS (`U+2028`), and PS (`U+2029`) are
|
|
/// single scalars. End of input is `nil`, which is libyaml's `\0` sentinel, so
|
|
/// `isZ(nil)` is true and every other predicate is false for `nil`.
|
|
enum CharacterClass {
|
|
/// `IS_Z`: the end of the input (libyaml's `\0`).
|
|
static func isZ(_ scalar: Unicode.Scalar?) -> Bool {
|
|
scalar == nil
|
|
}
|
|
|
|
/// `IS_SPACE`.
|
|
static func isSpace(_ scalar: Unicode.Scalar?) -> Bool {
|
|
scalar == " "
|
|
}
|
|
|
|
/// `IS_TAB`.
|
|
static func isTab(_ scalar: Unicode.Scalar?) -> Bool {
|
|
scalar == "\t"
|
|
}
|
|
|
|
/// `IS_BLANK`: space or tab.
|
|
static func isBlank(_ scalar: Unicode.Scalar?) -> Bool {
|
|
isSpace(scalar) || isTab(scalar)
|
|
}
|
|
|
|
/// `IS_BREAK`: CR, LF, NEL, LS, or PS.
|
|
static func isBreak(_ scalar: Unicode.Scalar?) -> Bool {
|
|
switch scalar {
|
|
case "\r", "\n", "\u{85}", "\u{2028}", "\u{2029}": true
|
|
default: false
|
|
}
|
|
}
|
|
|
|
/// `IS_BREAKZ`: a line break or the end of input.
|
|
static func isBreakZ(_ scalar: Unicode.Scalar?) -> Bool {
|
|
isBreak(scalar) || isZ(scalar)
|
|
}
|
|
|
|
/// `IS_SPACEZ`: a space, a line break, or the end of input.
|
|
static func isSpaceZ(_ scalar: Unicode.Scalar?) -> Bool {
|
|
isSpace(scalar) || isBreakZ(scalar)
|
|
}
|
|
|
|
/// `IS_BLANKZ`: blank (space or tab), a line break, or the end of input.
|
|
static func isBlankZ(_ scalar: Unicode.Scalar?) -> Bool {
|
|
isBlank(scalar) || isBreakZ(scalar)
|
|
}
|
|
|
|
/// A flow collection indicator (`,`, `[`, `]`, `{`, `}`); the YAML
|
|
/// `c-flow-indicator` set excluded from `ns-plain-safe` in flow context.
|
|
static func isFlowIndicator(_ scalar: Unicode.Scalar?) -> Bool {
|
|
switch scalar {
|
|
case ",", "[", "]", "{", "}": true
|
|
default: false
|
|
}
|
|
}
|
|
|
|
/// `IS_DIGIT`.
|
|
static func isDigit(_ scalar: Unicode.Scalar?) -> Bool {
|
|
guard let scalar else { return false }
|
|
return scalar >= "0" && scalar <= "9"
|
|
}
|
|
|
|
/// `IS_ALPHA`: a digit, ASCII letter, `_`, or `-` (the directive, anchor,
|
|
/// and tag-handle name characters).
|
|
static func isAlpha(_ scalar: Unicode.Scalar?) -> Bool {
|
|
guard let scalar else { return false }
|
|
return (scalar >= "0" && scalar <= "9")
|
|
|| (scalar >= "A" && scalar <= "Z")
|
|
|| (scalar >= "a" && scalar <= "z")
|
|
|| scalar == "_"
|
|
|| scalar == "-"
|
|
}
|
|
|
|
/// `IS_HEX`: a hexadecimal digit.
|
|
static func isHex(_ scalar: Unicode.Scalar?) -> Bool {
|
|
guard let scalar else { return false }
|
|
return (scalar >= "0" && scalar <= "9")
|
|
|| (scalar >= "A" && scalar <= "F")
|
|
|| (scalar >= "a" && scalar <= "f")
|
|
}
|
|
|
|
/// `IS_ASCII`.
|
|
static func isASCII(_ scalar: Unicode.Scalar?) -> Bool {
|
|
guard let scalar else { return false }
|
|
return scalar.value <= 0x7F
|
|
}
|
|
|
|
/// `IS_PRINTABLE`: the YAML `c-printable` set.
|
|
static func isPrintable(_ scalar: Unicode.Scalar?) -> Bool {
|
|
guard let scalar else { return false }
|
|
let value = scalar.value
|
|
return value == 0x09 || value == 0x0A || value == 0x0D
|
|
|| (value >= 0x20 && value <= 0x7E)
|
|
|| value == 0x85
|
|
|| (value >= 0xA0 && value <= 0xD7FF)
|
|
|| (value >= 0xE000 && value <= 0xFFFD)
|
|
|| (value >= 0x10000 && value <= 0x10FFFF)
|
|
}
|
|
}
|
|
}
|