parser combinator, json parser, future, other shit
This commit is contained in:
80
src/main/kotlin/blitz/parse/JSON.kt
Normal file
80
src/main/kotlin/blitz/parse/JSON.kt
Normal file
@@ -0,0 +1,80 @@
|
||||
package blitz.parse
|
||||
|
||||
import blitz.parse.comb.*
|
||||
|
||||
object JSON {
|
||||
lateinit var jsonElement: Parser<Element>
|
||||
val jsonNum = parser {
|
||||
it.map(NumParse.float)?.mapSecond { n ->
|
||||
Number(n)
|
||||
}
|
||||
}
|
||||
val jsonString = parser {
|
||||
it.require("\"")
|
||||
?.untilRequire("\"") { str -> Str(str) }
|
||||
}
|
||||
val jsonArray = parser {
|
||||
it.require("[")
|
||||
?.array(",") { elem ->
|
||||
elem.whitespaces()
|
||||
.map(jsonElement)
|
||||
?.whitespaces()
|
||||
}
|
||||
?.require("]")
|
||||
?.mapSecond { x -> Array(x) }
|
||||
}
|
||||
val jsonObj = parser {
|
||||
it.require("{")
|
||||
?.array(",") { elem ->
|
||||
elem.whitespaces()
|
||||
.map(jsonString)
|
||||
?.mapSecond { it.str }
|
||||
?.whitespaces()
|
||||
?.require(":")
|
||||
?.whitespaces()
|
||||
?.map(jsonElement)
|
||||
?.whitespaces()
|
||||
}
|
||||
?.require("}")
|
||||
?.mapSecond { x -> Obj(x.toMap()) }
|
||||
}
|
||||
|
||||
init {
|
||||
jsonElement = (jsonArray or jsonNum or jsonString or jsonObj).trim()
|
||||
}
|
||||
|
||||
interface Element {
|
||||
val arr get() = (this as Array).value
|
||||
val num get() = (this as Number).value
|
||||
val str get() = (this as Str).value
|
||||
val obj get() = (this as Obj).value
|
||||
|
||||
fun isArr() = this is Array
|
||||
fun isNum() = this is Number
|
||||
fun isStr() = this is Str
|
||||
fun isObj() = this is Obj
|
||||
}
|
||||
|
||||
data class Array(val value: List<Element>): Element {
|
||||
override fun toString(): String =
|
||||
value.joinToString(separator = ", ", prefix = "[", postfix = "]")
|
||||
}
|
||||
|
||||
data class Number(val value: Double): Element {
|
||||
override fun toString(): String =
|
||||
value.toString()
|
||||
}
|
||||
|
||||
data class Str(val value: String): Element {
|
||||
override fun toString(): String =
|
||||
"\"$value\""
|
||||
}
|
||||
|
||||
data class Obj(val value: Map<String, Element>): Element {
|
||||
override fun toString(): String =
|
||||
value.map { (k, v) -> "\"$k\": $v" }.joinToString(separator = ", ", prefix = "{", postfix = "}")
|
||||
}
|
||||
|
||||
fun parse(string: String): Element? =
|
||||
jsonElement(Parsable(string))?.second
|
||||
}
|
43
src/main/kotlin/blitz/parse/NumParse.kt
Normal file
43
src/main/kotlin/blitz/parse/NumParse.kt
Normal file
@@ -0,0 +1,43 @@
|
||||
package blitz.parse
|
||||
|
||||
import blitz.parse.comb.*
|
||||
|
||||
object NumParse {
|
||||
private val intBase = parser { it.require("0b")?.to(2) } or
|
||||
parser { it.require("0x")?.to(16) } or
|
||||
parser { it.require("0o")?.to(8) } or
|
||||
constantParser(10)
|
||||
|
||||
private val sign = parser { it.require("+")?.to(1) } or
|
||||
parser { it.require("-")?.to(-1) } or
|
||||
constantParser(1)
|
||||
|
||||
val int = parser { s ->
|
||||
s.map(sign)?.map(intBase)?.map { str, (sign, base) ->
|
||||
val chars = when (base) {
|
||||
2 -> "01"
|
||||
8 -> "01234567"
|
||||
10 -> "0123456789"
|
||||
16 -> "0123456789abcdefABCDEF"
|
||||
else -> error("wtf")
|
||||
}
|
||||
str.asLongAs(*chars.toCharArray()) {
|
||||
it.toLongOrNull(base)?.times(sign)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val float = parser { s ->
|
||||
s.map(sign)?.map { str, sign ->
|
||||
str.asLongAs('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.') {
|
||||
it.toDoubleOrNull()?.times(sign)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun parseInt(str: String): Long? =
|
||||
NumParse.int(Parsable(str))?.second
|
||||
|
||||
fun parseDouble(str: String): Double? =
|
||||
NumParse.float(Parsable(str))?.second
|
105
src/main/kotlin/blitz/parse/comb/Parser.kt
Normal file
105
src/main/kotlin/blitz/parse/comb/Parser.kt
Normal file
@@ -0,0 +1,105 @@
|
||||
package blitz.parse.comb
|
||||
|
||||
import blitz.str.collectToString
|
||||
import kotlin.math.max
|
||||
|
||||
@JvmInline
|
||||
value class Parsable(
|
||||
val str: String
|
||||
)
|
||||
|
||||
typealias Parser<T> = (Parsable) -> Pair<Parsable, T>?
|
||||
|
||||
fun <T> parser(fn: (Parsable) -> Pair<Parsable, T>?): Parser<T> =
|
||||
fn
|
||||
|
||||
fun <T> Parser<T>.trim(): Parser<T> = parser {
|
||||
it.whitespaces()
|
||||
.map(this@trim)
|
||||
?.whitespaces()
|
||||
}
|
||||
|
||||
fun <T> constantParser(const: T): Parser<T> = { it to const }
|
||||
|
||||
infix fun <T> Parser<T>.or(other: Parser<T>): Parser<T> = {
|
||||
this@or(it) ?: other(it)
|
||||
}
|
||||
|
||||
fun Parsable.spaces(): Parsable {
|
||||
return Parsable(str.trimStart(' '))
|
||||
}
|
||||
|
||||
fun Parsable.whitespaces(): Parsable {
|
||||
return Parsable(str.trimStart())
|
||||
}
|
||||
|
||||
fun Parsable.require(what: String): Parsable? {
|
||||
if (str.startsWith(what))
|
||||
return Parsable(str.substring(what.length))
|
||||
return null
|
||||
}
|
||||
|
||||
fun <T> Parsable.untilRequire(c: String, map: (String) -> T?): Pair<Parsable, T>? {
|
||||
return map(str.substringBefore(c))?.let { Parsable(str.substringAfter(c)) to it }
|
||||
}
|
||||
|
||||
fun <T> Parsable.asLongAs(vararg li: Char, map: (String) -> T?): Pair<Parsable, T>? {
|
||||
val o = mutableListOf<Char>()
|
||||
for (c in str) {
|
||||
if (c in li)
|
||||
o.add(c)
|
||||
else
|
||||
break
|
||||
}
|
||||
val out = str.substring(o.size)
|
||||
return map(o.iterator().collectToString())?.let { Parsable(out) to it }
|
||||
}
|
||||
|
||||
fun <T> Parsable.map(parser: Parser<T>): Pair<Parsable, T>? =
|
||||
parser(this)
|
||||
|
||||
fun <T, R> Pair<Parsable, T>.map(fn: (Parsable, T) -> Pair<Parsable, R>?): Pair<Parsable, R>? =
|
||||
fn(first, second)
|
||||
|
||||
fun <A, B> Pair<Parsable, A>.map(parser: Parser<B>): Pair<Parsable, Pair<A, B>>? =
|
||||
map { parsable, a ->
|
||||
parser(parsable)?.let { r ->
|
||||
r.first to (a to r.second)
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> Pair<Parsable, T>.mapFirst(fn: (Parsable) -> Parsable): Pair<Parsable, T> =
|
||||
fn(first) to second
|
||||
|
||||
fun <T> Pair<Parsable, T>.mapFirstNullable(fn: (Parsable) -> Parsable?): Pair<Parsable, T>? =
|
||||
fn(first)?.let { it to second }
|
||||
|
||||
fun <T, R> Pair<Parsable, T>.mapSecond(fn: (T) -> R): Pair<Parsable, R> =
|
||||
first to fn(second)
|
||||
|
||||
fun <T> Pair<Parsable, T>.spaces(): Pair<Parsable, T> =
|
||||
mapFirst { it.spaces() }
|
||||
|
||||
fun <T> Pair<Parsable, T>.whitespaces(): Pair<Parsable, T> =
|
||||
mapFirst { it.whitespaces() }
|
||||
|
||||
fun <T> Pair<Parsable, T>.require(what: String): Pair<Parsable, T>? =
|
||||
mapFirstNullable { it.require(what) }
|
||||
|
||||
fun <T> Parsable.array(sep: String, map: (Parsable) -> Pair<Parsable, T>?): Pair<Parsable, List<T>> {
|
||||
val out = mutableListOf<T>()
|
||||
|
||||
var curr = str
|
||||
fun step() =
|
||||
map(Parsable(curr))?.also {
|
||||
curr = it.first.str
|
||||
}
|
||||
|
||||
while (true) {
|
||||
val r = step() ?: break
|
||||
out.add(r.second)
|
||||
curr = (Parsable(curr).require(sep) ?: break).str
|
||||
}
|
||||
|
||||
return Parsable(curr) to out
|
||||
}
|
Reference in New Issue
Block a user