/**
* @file
* This file is about resolve.
*/
package yaml4cj.yaml
let longTagPrefix = "tag:yaml.org,2002:"
var resolveInited = false
let resolveTable = Array<UInt8>(256, repeat: 0)
let resolveMap = HashMap<String, ResolveMapItem>()
open class ResolveMapItem {
let value: JsonValue
let tag: String
protected init(value: JsonValue, tag: String) {
this.value = value
this.tag = tag
}
}
class ResolveMapListItem <: ResolveMapItem {
let list: Array<String>
init(value: JsonValue, tag: String, list: Array<String>) {
super(value, tag)
this.list = list
}
}
let yamlStyleFloat = Regex("^[-+]?(\\.[0-9]+|[0-9]+(\\.[0-9]*)?)([eE][-+]?[0-9]+)?$")
// 初始化
func initResolve() {
if (resolveInited) {
return
}
resolveTable[0x2B] = 0x53 // + => S
resolveTable[0x2D] = 0x53 // - => S
for (v in [0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39]) { // 数字 => D
resolveTable[v] = 0x44
}
for (v in [0x79, 0x59, 0x6E, 0x4E, 0x74, 0x54, 0x66, 0x46, 0x6F, 0x4F, 0x7E]) { // Map => M
resolveTable[v] = 0x4D
}
resolveTable[0x2E] = 0x2E // . => .
let resolveMapList = [
ResolveMapListItem(JsonBool(true), YAML_BOOL_TAG, ["y", "Y", "yes", "Yes", "YES"]),
ResolveMapListItem(JsonBool(true), YAML_BOOL_TAG, ["true", "True", "TRUE"]),
ResolveMapListItem(JsonBool(true), YAML_BOOL_TAG, ["on", "On", "ON"]),
ResolveMapListItem(JsonBool(false), YAML_BOOL_TAG, ["n", "N", "no", "No", "NO"]),
ResolveMapListItem(JsonBool(false), YAML_BOOL_TAG, ["false", "False", "FALSE"]),
ResolveMapListItem(JsonBool(false), YAML_BOOL_TAG, ["off", "Off", "OFF"]),
ResolveMapListItem(JsonNull(), YAML_NULL_TAG, ["", "~", "null", "Null", "NULL"]),
ResolveMapListItem(JsonFloat(Float64.Inf), YAML_FLOAT_TAG, [".inf", ".Inf", ".INF"]),
ResolveMapListItem(JsonFloat(Float64.Inf), YAML_FLOAT_TAG, ["+.inf", "+.Inf", "+.INF"]),
ResolveMapListItem(JsonFloat(-Float64.Inf), YAML_FLOAT_TAG, ["-.inf", "-.Inf", "-.INF"]),
ResolveMapListItem(JsonString("<<"), YAML_MERGE_TAG, ["<<"])
]
for (item in resolveMapList) {
for (s in item.list) {
resolveMap[s] = ResolveMapItem(item.value, item.tag)
}
}
}
/* func shortTag(tag: String): String {
if (tag.startsWith(longTagPrefix)) {
return "!!${tag[longTagPrefix.size..tag.size]}"
}
return tag
}*/
func resolvableTag(tag: String): Bool {
if (tag == "" || tag == YAML_STR_TAG || tag == YAML_BOOL_TAG || tag == YAML_INT_TAG || tag == YAML_FLOAT_TAG ||
tag == YAML_NULL_TAG || tag == YAML_TIMESTAMP_TAG) {
true
} else {
false
}
}
func resolve(tag: String, input: String): (String, JsonValue) {
if (!resolvableTag(tag)) {
return (tag, JsonString(input))
}
let hint: Rune = if (input != "") {
Rune(UInt32(resolveTable[Int64(input.toArray()[0])]))
} else {
r'N'
}
if (hint != Rune(0) && tag != YAML_STR_TAG && tag != YAML_BINARY_TAG) {
if (let Some(item) <- resolveMap.get(input)) {
return (item.tag, item.value)
}
match (hint) {
case r'M' => ()
case r'.' =>
if (let Some(f) <- Float64.tryParse(input)) {
return (YAML_FLOAT_TAG, JsonFloat(f))
}
case r'D' | r'S' =>
if (tag == "" || tag == YAML_TIMESTAMP_TAG) {
let (t, f) = parseTimestamp(input)
match (t) {
case Some(ti) => return (YAML_TIMESTAMP_TAG, JsonString(ti.format(f)))
case _ => ()
}
}
let plain = input.replace("_", "")
if (let Some(i) <- Int64.tryParse(plain)) {
if (isOverflow("${Int64.Max}", plain)) {
return (YAML_STR_TAG, JsonString(plain))
}
return (YAML_INT_TAG, JsonInt(i))
}
if (let Some(i) <- UInt64.tryParse(plain)) {
if (isOverflow("${UInt64.Max}", plain)) {
return (YAML_STR_TAG, JsonString(plain))
}
try {
return (YAML_INT_TAG, JsonInt(Int64(i)))
} catch (e: OverflowException) {
return (YAML_STR_TAG, JsonString(plain))
}
}
if (yamlStyleFloat.matches(input)) {
if (let Some(f) <- Float64.tryParse(input)) {
return (YAML_FLOAT_TAG, JsonFloat(f))
}
}
if (input.startsWith("0b")) {
if (let Some(i) <- Int64.tryParse(plain[2..plain.size])) {
if (isOverflow("${Int64.Max}", plain[2..plain.size])) {
return (YAML_STR_TAG, JsonString(plain))
}
return (YAML_INT_TAG, JsonInt(i))
}
} else if (input.startsWith("-0b")) {
if (let Some(i) <- Int64.tryParse("-${plain[2..plain.size]}")) {
if (isOverflow("${Int64.Max}", "-${plain[2..plain.size]}")) {
return (YAML_STR_TAG, JsonString(plain))
}
return (YAML_INT_TAG, JsonInt(i))
}
}
case _ => throw Exception("resolveTable item not yet handled: ${hint} (with ${input})")
}
}
(YAML_STR_TAG, JsonString(input))
}
func isOverflow(maxValue: String, targetValue: String): Bool {
if (maxValue.size < targetValue.size) {
true
} else if (maxValue.size == targetValue.size) {
let mA = maxValue.toArray()
let tA = targetValue.toArray()
for (i in 0..mA.size) {
if (mA[i] < tA[i]) {
return true
}
}
false
} else {
false
}
}
let allowedTimestampFormats = [
"dd MMM yyyy HH:mm:ss z",
"MMM dd HH:mm:ss zzz yyyy",
"yyyy-MM-dd'T'HH:mm:ss'Z'",
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",
"yyyy-MM-dd'T'HH:mm:ssZ",
"yyyy-MM-dd'T'HH:mm:ss.SSSZ",
"yyyy年MM月dd日 HH时mm分ss秒",
"yyyy-MM-dd HH:mm:ss.SSS",
"yyyy/MM/dd HH:mm:ss",
"yyyy.MM.dd HH:mm:ss",
"yyyy-MM-dd HH:mm",
"yyyyMMddHHmmssSSS",
"yyyyMMddHHmmss",
"yyyyMMdd",
"HH时mm分ss秒",
"yyyy-MM-dd",
"yyyy/MM/dd",
"yyyy.MM.dd",
"HH:mm:ss"
]
func parseTimestamp(s: String): (?DateTime, String) {
for (t in allowedTimestampFormats) {
try {
let n = DateTime.parse(s, t)
return (n, t)
} catch (e: Exception) {}
}
(None, "")
}