Int(_, radix:) accepts a sign, so 0-3 (a CPU range, a port range) was read as the integer -3, and 0x-1, 0b-1 and 0o-1 as negative numbers. After a radix prefix only digits may follow. Octal, hex, binary and signed integers read as before.
100 lines
3.8 KiB
Swift
100 lines
3.8 KiB
Swift
extension PureYAML.Parsing {
|
|
/// How a plain (unquoted) scalar reads: as null, a boolean, an integer, a floating-point number, or else a string.
|
|
///
|
|
/// This is the one definition of that reading. The parser takes the value of every plain scalar from it, and the emitter asks it whether a string can be
|
|
/// written without quotes, since a string that reads as anything else would come back changed. Two lists of the same words kept apart drift apart: the
|
|
/// emitter once wrote the string `yes` plain while the parser read `yes` as a boolean.
|
|
enum PlainScalar {
|
|
/// The value `text` has as a plain scalar. `text` has no surrounding whitespace.
|
|
static func resolve(_ text: String) -> PureYAML.Model.Value {
|
|
switch text {
|
|
case "", "~", "null", "Null", "NULL":
|
|
return .null
|
|
default:
|
|
break
|
|
}
|
|
if let bool = bool(text) {
|
|
return .bool(bool)
|
|
}
|
|
if let int = integer(text), !text.contains(".") {
|
|
return .int(int)
|
|
}
|
|
if text.contains(".") || text.contains("e") || text.contains("E"), let double = double(text) {
|
|
return .double(double)
|
|
}
|
|
return .string(text)
|
|
}
|
|
|
|
/// Whether `text` written plain reads back as the same string.
|
|
static func readsAsString(_ text: String) -> Bool {
|
|
if case .string = resolve(text) {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
static func bool(_ text: String) -> Bool? {
|
|
switch text {
|
|
case "true", "True", "TRUE", "yes", "Yes", "YES", "on", "On", "ON":
|
|
true
|
|
case "false", "False", "FALSE", "no", "No", "NO", "off", "Off", "OFF":
|
|
false
|
|
default:
|
|
nil
|
|
}
|
|
}
|
|
|
|
static func integer(_ text: String) -> Int? {
|
|
let value = removingNumericSeparators(from: text)
|
|
let negative = value.hasPrefix("-")
|
|
let hasSign = negative || value.hasPrefix("+")
|
|
let unsigned = hasSign ? String(value.dropFirst()) : value
|
|
|
|
guard !unsigned.isEmpty else {
|
|
return nil
|
|
}
|
|
guard unsigned != "0" else {
|
|
return 0
|
|
}
|
|
|
|
let radixPrefixes: [(prefix: String, radix: Int)] = [
|
|
("0x", 16),
|
|
("0X", 16),
|
|
("0b", 2),
|
|
("0B", 2),
|
|
("0o", 8),
|
|
("0O", 8),
|
|
("0", 8),
|
|
]
|
|
|
|
for radixPrefix in radixPrefixes where unsigned.hasPrefix(radixPrefix.prefix) {
|
|
let digits = String(unsigned.dropFirst(radixPrefix.prefix.count))
|
|
// `Int(_, radix:)` accepts a sign, so `0-3` would read as minus three: after the prefix only digits may follow
|
|
guard let first = digits.first, first != "-", first != "+", let magnitude = Int(digits, radix: radixPrefix.radix) else {
|
|
return nil
|
|
}
|
|
return negative ? -magnitude : magnitude
|
|
}
|
|
|
|
return Int(value)
|
|
}
|
|
|
|
static func double(_ text: String) -> Double? {
|
|
switch text {
|
|
case ".inf", ".Inf", ".INF", "+.inf", "+.Inf", "+.INF":
|
|
.infinity
|
|
case "-.inf", "-.Inf", "-.INF":
|
|
-.infinity
|
|
case ".nan", ".NaN", ".NAN", "+.nan", "+.NaN", "+.NAN", "-.nan", "-.NaN", "-.NAN":
|
|
.nan
|
|
default:
|
|
Double(removingNumericSeparators(from: text))
|
|
}
|
|
}
|
|
|
|
private static func removingNumericSeparators(from text: String) -> String {
|
|
text.filter { $0 != "_" }
|
|
}
|
|
}
|
|
}
|