mirror of
https://github.com/gkd-kit/gkd.git
synced 2024-11-16 11:42:22 +08:00
parent
a6cde2094d
commit
d41fb7f469
|
@ -189,7 +189,6 @@ composeCompiler {
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
implementation(project(mapOf("path" to ":selector")))
|
implementation(project(mapOf("path" to ":selector")))
|
||||||
implementation(project(mapOf("path" to ":json5")))
|
|
||||||
|
|
||||||
implementation(libs.androidx.appcompat)
|
implementation(libs.androidx.appcompat)
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
|
@ -259,4 +258,6 @@ dependencies {
|
||||||
|
|
||||||
implementation(libs.toaster)
|
implementation(libs.toaster)
|
||||||
implementation(libs.permissions)
|
implementation(libs.permissions)
|
||||||
|
|
||||||
|
implementation(libs.json5)
|
||||||
}
|
}
|
|
@ -69,6 +69,7 @@ reorderable = { module = "sh.calvin.reorderable:reorderable", version = "2.3.0"
|
||||||
exp4j = { module = "net.objecthunter:exp4j", version = "0.4.8" }
|
exp4j = { module = "net.objecthunter:exp4j", version = "0.4.8" }
|
||||||
toaster = { module = "com.github.getActivity:Toaster", version = "12.6" }
|
toaster = { module = "com.github.getActivity:Toaster", version = "12.6" }
|
||||||
permissions = { module = "com.github.getActivity:XXPermissions", version = "18.63" }
|
permissions = { module = "com.github.getActivity:XXPermissions", version = "18.63" }
|
||||||
|
json5 = { module = "io.github.lisonge:json5", version = "0.0.2" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android_library = { id = "com.android.library", version.ref = "android" }
|
android_library = { id = "com.android.library", version.ref = "android" }
|
||||||
|
|
1
json5/.gitignore
vendored
1
json5/.gitignore
vendored
|
@ -1 +0,0 @@
|
||||||
/build
|
|
|
@ -1,22 +0,0 @@
|
||||||
plugins {
|
|
||||||
alias(libs.plugins.kotlin.multiplatform)
|
|
||||||
alias(libs.plugins.kotlin.serialization)
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlin {
|
|
||||||
jvm()
|
|
||||||
|
|
||||||
sourceSets {
|
|
||||||
val commonMain by getting {
|
|
||||||
dependencies {
|
|
||||||
api(libs.kotlinx.serialization.json)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val commonTest by getting {
|
|
||||||
dependencies {
|
|
||||||
implementation(libs.kotlin.test)
|
|
||||||
implementation(libs.kotlinx.serialization.json)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
package li.songe.json5
|
|
||||||
|
|
||||||
import kotlinx.serialization.json.JsonArray
|
|
||||||
import kotlinx.serialization.json.JsonElement
|
|
||||||
import kotlinx.serialization.json.JsonObject
|
|
||||||
import kotlinx.serialization.json.JsonPrimitive
|
|
||||||
|
|
||||||
object Json5 {
|
|
||||||
fun parseToJson5Element(string: String): JsonElement {
|
|
||||||
return Json5Decoder(string).read()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun encodeToString(element: JsonElement, indent: Int = 2) = encodeToString(element, indent, 0)
|
|
||||||
|
|
||||||
private fun encodeToString(element: JsonElement, indent: Int = 2, depth: Int = 0): String {
|
|
||||||
val lineSeparator = if (indent == 0) "" else "\n"
|
|
||||||
val keySeparator = if (indent == 0) ":" else ": "
|
|
||||||
val prefixSpaces = if (indent == 0) "" else " ".repeat(indent * (depth + 1))
|
|
||||||
val closingSpaces = if (indent == 0) "" else " ".repeat(indent * depth)
|
|
||||||
|
|
||||||
return when (element) {
|
|
||||||
is JsonPrimitive -> {
|
|
||||||
if (element.isString) {
|
|
||||||
stringifyString(element.content)
|
|
||||||
} else {
|
|
||||||
element.content
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is JsonObject -> {
|
|
||||||
if (element.isEmpty()) {
|
|
||||||
"{}"
|
|
||||||
} else {
|
|
||||||
element.entries.joinToString(",$lineSeparator") { (key, value) ->
|
|
||||||
"${prefixSpaces}${stringifyKey(key)}${keySeparator}${
|
|
||||||
encodeToString(
|
|
||||||
value,
|
|
||||||
indent,
|
|
||||||
depth + 1
|
|
||||||
)
|
|
||||||
}"
|
|
||||||
}.let {
|
|
||||||
"{$lineSeparator$it$lineSeparator$closingSpaces}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is JsonArray -> {
|
|
||||||
if (element.isEmpty()) {
|
|
||||||
"[]"
|
|
||||||
} else {
|
|
||||||
element.joinToString(",$lineSeparator") {
|
|
||||||
"${prefixSpaces}${encodeToString(it, indent, depth + 1)}"
|
|
||||||
}.let {
|
|
||||||
"[$lineSeparator$it$lineSeparator$closingSpaces]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -1,539 +0,0 @@
|
||||||
package li.songe.json5
|
|
||||||
|
|
||||||
import kotlinx.serialization.json.*
|
|
||||||
import java.lang.StringBuilder
|
|
||||||
import kotlin.collections.set
|
|
||||||
import kotlin.let
|
|
||||||
import kotlin.ranges.contains
|
|
||||||
import kotlin.text.endsWith
|
|
||||||
import kotlin.text.getOrNull
|
|
||||||
import kotlin.text.substring
|
|
||||||
import kotlin.text.toDouble
|
|
||||||
import kotlin.text.toInt
|
|
||||||
import kotlin.text.toLong
|
|
||||||
import kotlin.text.trimEnd
|
|
||||||
|
|
||||||
// https://spec.json5.org/
|
|
||||||
internal class Json5Decoder(private val input: CharSequence) {
|
|
||||||
private var i = 0
|
|
||||||
private val char: Char?
|
|
||||||
get() = input.getOrNull(i)
|
|
||||||
private val end: Boolean
|
|
||||||
get() = i >= input.length
|
|
||||||
|
|
||||||
private fun stop(): Nothing {
|
|
||||||
if (end) {
|
|
||||||
error("Unexpected Char: EOF")
|
|
||||||
}
|
|
||||||
error("Unexpected Char: $char at index $i")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun read(): JsonElement {
|
|
||||||
val root = i == 0
|
|
||||||
readUseless()
|
|
||||||
val element = when (char) {
|
|
||||||
'{' -> readObject()
|
|
||||||
'[' -> readArray()
|
|
||||||
'"', '\'' -> JsonPrimitive(readString())
|
|
||||||
in '0'..'9', '-', '+', '.', 'N', 'I' -> JsonPrimitive(readNumber())
|
|
||||||
't' -> { // true
|
|
||||||
i++
|
|
||||||
next('r')
|
|
||||||
next('u')
|
|
||||||
next('e')
|
|
||||||
JsonPrimitive(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
'f' -> { // false
|
|
||||||
i++
|
|
||||||
next('a')
|
|
||||||
next('l')
|
|
||||||
next('s')
|
|
||||||
next('e')
|
|
||||||
JsonPrimitive(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
'n' -> { // null
|
|
||||||
i++
|
|
||||||
next('u')
|
|
||||||
next('l')
|
|
||||||
next('l')
|
|
||||||
JsonNull
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> stop()
|
|
||||||
}
|
|
||||||
if (root) {
|
|
||||||
readUseless()
|
|
||||||
if (!end) {
|
|
||||||
stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return element
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun next(c: Char) {
|
|
||||||
if (c == char) {
|
|
||||||
i++
|
|
||||||
return
|
|
||||||
}
|
|
||||||
stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readObject(): JsonObject {
|
|
||||||
i++
|
|
||||||
readUseless()
|
|
||||||
if (char == '}') {
|
|
||||||
i++
|
|
||||||
return JsonObject(emptyMap())
|
|
||||||
}
|
|
||||||
val map = mutableMapOf<String, JsonElement>()
|
|
||||||
while (true) {
|
|
||||||
readUseless()
|
|
||||||
val key = readObjectKey()
|
|
||||||
readUseless()
|
|
||||||
next(':')
|
|
||||||
readUseless()
|
|
||||||
val value = read()
|
|
||||||
map[key] = value
|
|
||||||
readUseless()
|
|
||||||
if (char == '}') {
|
|
||||||
i++
|
|
||||||
break
|
|
||||||
} else if (char == ',') {
|
|
||||||
i++
|
|
||||||
readUseless()
|
|
||||||
if (char == '}') {
|
|
||||||
i++
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return JsonObject(map)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readObjectKey(): String {
|
|
||||||
val c = char
|
|
||||||
if (c == '\'' || c == '"') {
|
|
||||||
return readString()
|
|
||||||
}
|
|
||||||
val sb = StringBuilder()
|
|
||||||
if (c == '\\') {
|
|
||||||
i++
|
|
||||||
next('u')
|
|
||||||
repeat(4) {
|
|
||||||
if (!isHexDigit(char)) {
|
|
||||||
stop()
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
val n = input.substring(i - 4, i).toInt(16).toChar()
|
|
||||||
if (!isIdStartChar(n)) {
|
|
||||||
stop()
|
|
||||||
}
|
|
||||||
sb.append(n)
|
|
||||||
} else if (!isIdStartChar(c)) {
|
|
||||||
stop()
|
|
||||||
} else {
|
|
||||||
sb.append(c)
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
while (!end) {
|
|
||||||
if (char == '\\') {
|
|
||||||
i++
|
|
||||||
next('u')
|
|
||||||
repeat(4) {
|
|
||||||
if (!isHexDigit(char)) {
|
|
||||||
stop()
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
val n = input.substring(i - 4, i).toInt(16).toChar()
|
|
||||||
if (!isIdContinueChar(n)) {
|
|
||||||
stop()
|
|
||||||
}
|
|
||||||
sb.append(n)
|
|
||||||
} else if (isIdContinueChar(char)) {
|
|
||||||
sb.append(char)
|
|
||||||
i++
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sb.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readArray(): JsonArray {
|
|
||||||
i++
|
|
||||||
readUseless()
|
|
||||||
if (char == ']') {
|
|
||||||
i++
|
|
||||||
return JsonArray(emptyList())
|
|
||||||
}
|
|
||||||
val list = mutableListOf<JsonElement>()
|
|
||||||
while (true) {
|
|
||||||
readUseless()
|
|
||||||
list.add(read())
|
|
||||||
readUseless()
|
|
||||||
if (char == ']') {
|
|
||||||
i++
|
|
||||||
break
|
|
||||||
} else if (char == ',') {
|
|
||||||
i++
|
|
||||||
readUseless()
|
|
||||||
if (char == ']') {
|
|
||||||
i++
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return JsonArray(list)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readString(): String {
|
|
||||||
val wrapChar = char!!
|
|
||||||
i++
|
|
||||||
val sb = StringBuilder()
|
|
||||||
while (true) {
|
|
||||||
when (char) {
|
|
||||||
null -> stop()
|
|
||||||
wrapChar -> {
|
|
||||||
i++
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
'\\' -> {
|
|
||||||
i++
|
|
||||||
when (char) {
|
|
||||||
null -> stop()
|
|
||||||
wrapChar -> {
|
|
||||||
sb.append(wrapChar)
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
'x' -> {
|
|
||||||
i++
|
|
||||||
repeat(2) {
|
|
||||||
if (!isHexDigit(char)) {
|
|
||||||
stop()
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
val hex = input.substring(i - 2, i)
|
|
||||||
sb.append(hex.toInt(16).toChar())
|
|
||||||
}
|
|
||||||
|
|
||||||
'u' -> {
|
|
||||||
i++
|
|
||||||
repeat(4) {
|
|
||||||
if (!isHexDigit(char)) {
|
|
||||||
stop()
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
val hex = input.substring(i - 4, i)
|
|
||||||
sb.append(hex.toInt(16).toChar())
|
|
||||||
}
|
|
||||||
|
|
||||||
'\'' -> {
|
|
||||||
sb.append('\'')
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
'\"' -> {
|
|
||||||
sb.append('\"')
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
'\\' -> {
|
|
||||||
sb.append('\\')
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
'b' -> {
|
|
||||||
sb.append('\b')
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
'f' -> {
|
|
||||||
sb.append('\u000C')
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
'n' -> {
|
|
||||||
sb.append('\n')
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
'r' -> {
|
|
||||||
sb.append('\r')
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
't' -> {
|
|
||||||
sb.append('\t')
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
'v' -> {
|
|
||||||
sb.append('\u000B')
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
'0' -> {
|
|
||||||
sb.append('\u0000')
|
|
||||||
i++
|
|
||||||
if (isDigit(char)) {
|
|
||||||
stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// multiline string
|
|
||||||
'\u000D' -> {// \r
|
|
||||||
i++
|
|
||||||
if (char == '\u000A') {// \n
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// multiline string
|
|
||||||
'\u000A', '\u2028', '\u2029' -> {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
in '1'..'9' -> stop()
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
sb.append(char)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
sb.append(char)
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sb.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readNumber(signal: Boolean = false): Number {
|
|
||||||
return when (char) {
|
|
||||||
'-' -> {
|
|
||||||
if (!signal) {
|
|
||||||
i++
|
|
||||||
val n = readNumber(true)
|
|
||||||
if (n is Double) {
|
|
||||||
return -n
|
|
||||||
}
|
|
||||||
if (n is Long) {
|
|
||||||
return -n
|
|
||||||
}
|
|
||||||
if (n is Int) {
|
|
||||||
return -n
|
|
||||||
}
|
|
||||||
stop()
|
|
||||||
} else {
|
|
||||||
stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
'+' -> {
|
|
||||||
if (!signal) {
|
|
||||||
i++
|
|
||||||
return readNumber(true)
|
|
||||||
} else {
|
|
||||||
stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
'N' -> {// NaN
|
|
||||||
i++
|
|
||||||
next('a')
|
|
||||||
next('N')
|
|
||||||
Double.NaN
|
|
||||||
}
|
|
||||||
|
|
||||||
'I' -> {// Infinity
|
|
||||||
i++
|
|
||||||
next('n')
|
|
||||||
next('f')
|
|
||||||
next('i')
|
|
||||||
next('n')
|
|
||||||
next('i')
|
|
||||||
next('t')
|
|
||||||
next('y')
|
|
||||||
Double.POSITIVE_INFINITY
|
|
||||||
}
|
|
||||||
|
|
||||||
'.' -> {
|
|
||||||
var start = i
|
|
||||||
i++
|
|
||||||
readInteger()
|
|
||||||
val numPart = input.substring(start, i).trimEnd('0').let {
|
|
||||||
if (it == ".") { // .0 -> 0
|
|
||||||
"0"
|
|
||||||
} else {
|
|
||||||
it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (numPart == "0") {
|
|
||||||
0L
|
|
||||||
} else {
|
|
||||||
if (isPowerStartChar(char)) {
|
|
||||||
start = i + 1
|
|
||||||
readNumberPower()
|
|
||||||
val power = input.substring(start, i)
|
|
||||||
(numPart + power).toDouble()
|
|
||||||
} else {
|
|
||||||
input.substring(start, i).toDouble()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
in '0'..'9' -> {
|
|
||||||
var start = i
|
|
||||||
var hasHex = false
|
|
||||||
if (char == '0') { // 0x11
|
|
||||||
i++
|
|
||||||
if (isDigit(char)) {// not allow 00 01
|
|
||||||
stop()
|
|
||||||
} else if (isHexStartChar(char)) {
|
|
||||||
i++
|
|
||||||
hasHex = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hasHex) {
|
|
||||||
if (!isHexDigit(char)) {
|
|
||||||
stop()
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
while (!end && isHexDigit(char)) {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
input.substring(start + 2, i).toLong(16)
|
|
||||||
} else {
|
|
||||||
var hasPoint = false // 1.2
|
|
||||||
while (!end) {
|
|
||||||
if (char == '.') {
|
|
||||||
if (!hasPoint) {
|
|
||||||
hasPoint = true
|
|
||||||
} else {
|
|
||||||
stop()
|
|
||||||
}
|
|
||||||
} else if (!isDigit(char)) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
val hasEndPoint = hasPoint && input[i - 1] == '.' // kotlin not support 1.
|
|
||||||
val numPart = if (hasEndPoint) {
|
|
||||||
hasPoint = false
|
|
||||||
input.substring(start, i - 1) // 1. -> 1
|
|
||||||
} else {
|
|
||||||
if (hasPoint) {
|
|
||||||
input.substring(start, i).trimEnd('0').let { // 1.10 -> 1.1, 1.0 -> 1.
|
|
||||||
if (it.endsWith('.')) { // 1. -> 1
|
|
||||||
hasPoint = false
|
|
||||||
it.substring(0, it.length - 1)
|
|
||||||
} else {
|
|
||||||
it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
input.substring(start, i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isPowerStartChar(char)) {
|
|
||||||
start = i
|
|
||||||
readNumberPower()
|
|
||||||
val power = input.substring(start, i)
|
|
||||||
(numPart + power).toDouble()
|
|
||||||
} else {
|
|
||||||
if (hasPoint) {
|
|
||||||
numPart.toDouble()
|
|
||||||
} else {
|
|
||||||
numPart.run { toLongOrNull() ?: toDouble() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readInteger() {
|
|
||||||
val start = i
|
|
||||||
while (isDigit(char)) {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
if (start == i) {
|
|
||||||
stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readNumberPower() {
|
|
||||||
i++
|
|
||||||
if (char == '-' || char == '+') {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
readInteger()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readUseless() {
|
|
||||||
while (true) {
|
|
||||||
val oldIndex = i
|
|
||||||
readCommentOrWhitespace()
|
|
||||||
if (oldIndex == i) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readCommentOrWhitespace() {
|
|
||||||
when {
|
|
||||||
char == '/' -> {
|
|
||||||
i++
|
|
||||||
when (char) {
|
|
||||||
'/' -> {
|
|
||||||
i++
|
|
||||||
while (!isNewLine(char) && !end) {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
'*' -> {
|
|
||||||
i++
|
|
||||||
while (true) {
|
|
||||||
when (char) {
|
|
||||||
null -> stop()
|
|
||||||
'*' -> {
|
|
||||||
if (input.getOrNull(i + 1) == '/') {
|
|
||||||
i += 2
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isWhiteSpace(char) -> {
|
|
||||||
i++
|
|
||||||
while (isWhiteSpace(char)) {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
package li.songe.json5
|
|
||||||
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import kotlinx.serialization.json.decodeFromJsonElement
|
|
||||||
import kotlinx.serialization.serializer
|
|
||||||
|
|
||||||
inline fun <reified T> Json.encodeToJson5String(value: T): String {
|
|
||||||
return Json5.encodeToString(
|
|
||||||
encodeToJsonElement(serializersModule.serializer(), value),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun <reified T> Json.decodeFromJson5String(value: String): T {
|
|
||||||
return decodeFromJsonElement<T>(Json5.parseToJson5Element(value))
|
|
||||||
}
|
|
|
@ -1,149 +0,0 @@
|
||||||
package li.songe.json5
|
|
||||||
|
|
||||||
import kotlin.text.category
|
|
||||||
|
|
||||||
private val unicodeLetterCategories = hashSetOf(
|
|
||||||
CharCategory.UPPERCASE_LETTER,
|
|
||||||
CharCategory.LOWERCASE_LETTER,
|
|
||||||
CharCategory.TITLECASE_LETTER,
|
|
||||||
CharCategory.MODIFIER_LETTER,
|
|
||||||
CharCategory.OTHER_LETTER,
|
|
||||||
CharCategory.LETTER_NUMBER,
|
|
||||||
)
|
|
||||||
|
|
||||||
private val unicodeIdCategories = hashSetOf(
|
|
||||||
CharCategory.NON_SPACING_MARK,
|
|
||||||
CharCategory.COMBINING_SPACING_MARK,
|
|
||||||
CharCategory.DECIMAL_DIGIT_NUMBER,
|
|
||||||
CharCategory.CONNECTOR_PUNCTUATION,
|
|
||||||
)
|
|
||||||
|
|
||||||
internal fun isIdStartChar(c: Char?): Boolean {
|
|
||||||
c ?: return false
|
|
||||||
return c.category in unicodeLetterCategories || c == '_' || c == '$'
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun isIdContinueChar(c: Char?): Boolean {
|
|
||||||
c ?: return false
|
|
||||||
return isIdStartChar(c) || c.category in unicodeIdCategories || c == '\u200C' || c == '\u200D'
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun isDigit(c: Char?): Boolean {
|
|
||||||
c ?: return false
|
|
||||||
return c in '0'..'9'
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun isHexDigit(c: Char?): Boolean {
|
|
||||||
c ?: return false
|
|
||||||
return (c in '0'..'9') || (c in 'A'..'F') || (c in 'a'..'f')
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun isPowerStartChar(c: Char?): Boolean {
|
|
||||||
c ?: return false
|
|
||||||
return c == 'e' || c == 'E'
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun isHexStartChar(c: Char?): Boolean {
|
|
||||||
c ?: return false
|
|
||||||
return c == 'x' || c == 'X'
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun isWhiteSpace(c: Char?): Boolean {
|
|
||||||
c ?: return false
|
|
||||||
return when (c) {
|
|
||||||
'\u0009' -> true
|
|
||||||
in '\u000A'..'\u000D' -> true
|
|
||||||
'\u0020' -> true
|
|
||||||
'\u00A0' -> true
|
|
||||||
'\u2028' -> true
|
|
||||||
'\u2029' -> true
|
|
||||||
'\uFEFF' -> true
|
|
||||||
|
|
||||||
'\u1680' -> true
|
|
||||||
in '\u2000'..'\u200A' -> true
|
|
||||||
'\u202F' -> true
|
|
||||||
'\u205F' -> true
|
|
||||||
'\u3000' -> true
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun isNewLine(c: Char?): Boolean {
|
|
||||||
c ?: return false
|
|
||||||
return when (c) {
|
|
||||||
'\u000A' -> true
|
|
||||||
'\u000D' -> true
|
|
||||||
'\u2028' -> true
|
|
||||||
'\u2029' -> true
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val escapeReplacements = hashMapOf(
|
|
||||||
'\\' to "\\\\",
|
|
||||||
'\b' to "\\b",
|
|
||||||
'\u000C' to "\\f",
|
|
||||||
'\n' to "\\n",
|
|
||||||
'\r' to "\\r",
|
|
||||||
'\t' to "\\t",
|
|
||||||
'\u000B' to "\\v",
|
|
||||||
'\u0000' to "\\0",
|
|
||||||
'\u2028' to "\\u2028",
|
|
||||||
'\u2029' to "\\u2029",
|
|
||||||
)
|
|
||||||
|
|
||||||
internal fun stringifyString(value: String, singleQuote: Boolean = true): String {
|
|
||||||
// https://github.com/json5/json5/blob/main/lib/stringify.js
|
|
||||||
val wrapChar = if (singleQuote) '\'' else '"'
|
|
||||||
val sb = StringBuilder()
|
|
||||||
sb.append(wrapChar)
|
|
||||||
value.forEachIndexed { i, c ->
|
|
||||||
when {
|
|
||||||
c == wrapChar -> {
|
|
||||||
sb.append("\\$wrapChar")
|
|
||||||
}
|
|
||||||
|
|
||||||
c == '\u0000' -> {
|
|
||||||
if (isDigit(value.getOrNull(i + 1))) {
|
|
||||||
// "\u00002" -> \x002
|
|
||||||
sb.append("\\x00")
|
|
||||||
} else {
|
|
||||||
sb.append("\\0")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c in escapeReplacements.keys -> {
|
|
||||||
sb.append(escapeReplacements[c])
|
|
||||||
}
|
|
||||||
|
|
||||||
c.code in 0..0xf -> {
|
|
||||||
sb.append("\\x0" + c.code.toString(16))
|
|
||||||
}
|
|
||||||
|
|
||||||
c.code in 0..0x1f -> {
|
|
||||||
sb.append("\\x" + c.code.toString(16))
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
sb.append(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sb.append(wrapChar)
|
|
||||||
return sb.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun stringifyKey(key: String, singleQuote: Boolean = true): String {
|
|
||||||
if (key.isEmpty()) {
|
|
||||||
return stringifyString(key, singleQuote)
|
|
||||||
}
|
|
||||||
if (!isIdStartChar(key[0])) {
|
|
||||||
return stringifyString(key, singleQuote)
|
|
||||||
}
|
|
||||||
for (c in key) {
|
|
||||||
if (!isIdContinueChar(c)) {
|
|
||||||
return stringifyString(key, singleQuote)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return key
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
package li.songe.json5
|
|
||||||
|
|
||||||
import kotlin.test.Test
|
|
||||||
|
|
||||||
class Json5Test {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun parse() {
|
|
||||||
// https://github.com/json5/json5/blob/main/test/parse.js
|
|
||||||
val element = Json5.parseToJson5Element("[1,2,3,'\\x002\\n']/*23*///1")
|
|
||||||
println("element: $element")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun format() {
|
|
||||||
val element = Json5.parseToJson5Element("{'a-1':1,b:{c:['d',{f:233}]}}")
|
|
||||||
println("element: $element")
|
|
||||||
val formatted = Json5.encodeToString(element, 2)
|
|
||||||
println("formatted:\n$formatted")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -22,5 +22,3 @@ dependencyResolutionManagement {
|
||||||
maven("https://jitpack.io")
|
maven("https://jitpack.io")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
include(":json5")
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user