increase parser perf by a lot
This commit is contained in:
@@ -18,7 +18,6 @@ dependencies {
|
|||||||
|
|
||||||
// https://mvnrepository.com/artifact/org.json/json
|
// https://mvnrepository.com/artifact/org.json/json
|
||||||
implementation("org.json:json:20240303")
|
implementation("org.json:json:20240303")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.test {
|
tasks.test {
|
||||||
|
@@ -1,9 +1,16 @@
|
|||||||
package blitz
|
package blitz
|
||||||
|
|
||||||
class Either<A: Any, B: Any>(
|
class Either<A: Any, B: Any> private constructor(
|
||||||
val a: A?,
|
aIn: A?, bIn: B?
|
||||||
val b: B?
|
|
||||||
) {
|
) {
|
||||||
|
/** DO NOT SET MANUALLY!!! */
|
||||||
|
@JvmField
|
||||||
|
var a: A? = aIn
|
||||||
|
|
||||||
|
/** DO NOT SET MANUALLY!!! */
|
||||||
|
@JvmField
|
||||||
|
var b: B? = bIn
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean =
|
override fun equals(other: Any?): Boolean =
|
||||||
other is Either<*, *> && other.a == a && other.b == b
|
other is Either<*, *> && other.a == a && other.b == b
|
||||||
|
|
||||||
@@ -13,14 +20,8 @@ class Either<A: Any, B: Any>(
|
|||||||
fun assertB(): B =
|
fun assertB(): B =
|
||||||
(b ?: throw Exception("Value of Either is not of type B!"))
|
(b ?: throw Exception("Value of Either is not of type B!"))
|
||||||
|
|
||||||
val isA: Boolean =
|
|
||||||
a != null
|
|
||||||
|
|
||||||
val isB: Boolean =
|
|
||||||
b != null
|
|
||||||
|
|
||||||
override fun toString(): String =
|
override fun toString(): String =
|
||||||
if (isA) "Either<A>(${a!!})"
|
if (isA()) "Either<A>(${a!!})"
|
||||||
else "Either<B>(${b!!})"
|
else "Either<B>(${b!!})"
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
@@ -30,14 +31,20 @@ class Either<A: Any, B: Any>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
inline fun <A: Any, B: Any> ofA(a: A): Either<A, B> =
|
fun <A: Any, B: Any> unsafeCreate(a: A?, b: B?, pool: StupidObjPool<Either<*,*>>? = null): Either<A, B> =
|
||||||
Either(a, null)
|
Either(a, b)
|
||||||
|
|
||||||
inline fun <A: Any, B: Any> ofB(b: B): Either<A, B> =
|
inline fun <A: Any, B: Any> ofA(a: A, pool: StupidObjPool<Either<*,*>>? = null): Either<A, B> =
|
||||||
Either(null, b)
|
unsafeCreate(a, null, pool)
|
||||||
|
|
||||||
|
inline fun <A: Any, B: Any> ofB(b: B, pool: StupidObjPool<Either<*,*>>? = null): Either<A, B> =
|
||||||
|
unsafeCreate(null, b, pool)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fun <A: Any, B: Any> Either<A, B>.isA() = a != null
|
||||||
|
inline fun <A: Any, B: Any> Either<A, B>.isB() = b != null
|
||||||
|
|
||||||
inline fun <A: Any, B: Any> Either<A, B>.getAOr(prov: Provider<A>): A =
|
inline fun <A: Any, B: Any> Either<A, B>.getAOr(prov: Provider<A>): A =
|
||||||
a ?: prov()
|
a ?: prov()
|
||||||
|
|
||||||
@@ -45,35 +52,35 @@ inline fun <A: Any, B: Any> Either<A, B>.getBOr(prov: Provider<B>): B =
|
|||||||
b ?: prov()
|
b ?: prov()
|
||||||
|
|
||||||
inline fun <A: Any, B: Any, R> Either<A, B>.then(af: (A) -> R, bf: (B) -> R): R =
|
inline fun <A: Any, B: Any, R> Either<A, B>.then(af: (A) -> R, bf: (B) -> R): R =
|
||||||
if (isA) af(a!!) else bf(b!!)
|
if (isA()) af(a!!) else bf(b!!)
|
||||||
|
|
||||||
inline fun <A: Any, B: Any, RA: Any> Either<A, B>.mapA(transform: (A) -> RA): Either<RA, B> =
|
inline fun <A: Any, B: Any, RA: Any> Either<A, B>.mapA(transform: (A) -> RA): Either<RA, B> =
|
||||||
Either(a?.let(transform), b)
|
Either.unsafeCreate(a?.let(transform), b)
|
||||||
|
|
||||||
inline fun <A: Any, B: Any> Either<A, B>.flatMapA(transform: (A) -> Either<A, B>): Either<A, B> =
|
inline fun <A: Any, B: Any> Either<A, B>.flatMapA(transform: (A) -> Either<A, B>): Either<A, B> =
|
||||||
if (a != null) {
|
if (a != null) {
|
||||||
transform(a)
|
transform(a!!)
|
||||||
} else this
|
} else this
|
||||||
|
|
||||||
inline fun <A: Any, B: Any> Either<A, B>.flatMapB(transform: (B) -> Either<A, B>): Either<A, B> =
|
inline fun <A: Any, B: Any> Either<A, B>.flatMapB(transform: (B) -> Either<A, B>): Either<A, B> =
|
||||||
if (b != null) {
|
if (b != null) {
|
||||||
transform(b)
|
transform(b!!)
|
||||||
} else this
|
} else this
|
||||||
|
|
||||||
@JvmName("flatMapA_changeType")
|
@JvmName("flatMapA_changeType")
|
||||||
inline fun <A: Any, B: Any, RA: Any> Either<A, B>.flatMapA(transform: (A) -> Either<RA, B>): Either<RA, B> =
|
inline fun <A: Any, B: Any, RA: Any> Either<A, B>.flatMapA(transform: (A) -> Either<RA, B>): Either<RA, B> =
|
||||||
if (a != null) {
|
if (a != null) {
|
||||||
transform(a)
|
transform(a!!)
|
||||||
} else Either.ofB(b!!)
|
} else Either.ofB(b!!)
|
||||||
|
|
||||||
@JvmName("flatMapB_changeType")
|
@JvmName("flatMapB_changeType")
|
||||||
inline fun <A: Any, B: Any, RB: Any> Either<A, B>.flatMapB(transform: (B) -> Either<A, RB>): Either<A, RB> =
|
inline fun <A: Any, B: Any, RB: Any> Either<A, B>.flatMapB(transform: (B) -> Either<A, RB>): Either<A, RB> =
|
||||||
if (b != null) {
|
if (b != null) {
|
||||||
transform(b)
|
transform(b!!)
|
||||||
} else Either.ofA(a!!)
|
} else Either.ofA(a!!)
|
||||||
|
|
||||||
inline fun <A: Any, B: Any, RB: Any> Either<A, B>.mapB(transform: (B) -> RB): Either<A, RB> =
|
inline fun <A: Any, B: Any, RB: Any> Either<A, B>.mapB(transform: (B) -> RB): Either<A, RB> =
|
||||||
Either(a, b?.let(transform))
|
Either.unsafeCreate(a, b?.let(transform))
|
||||||
|
|
||||||
fun <A, B, R: Any> Either<A, B>.flatten(): R where A: R, B: R =
|
fun <A, B, R: Any> Either<A, B>.flatten(): R where A: R, B: R =
|
||||||
a ?: assertB()
|
a ?: assertB()
|
||||||
|
45
src/main/kotlin/blitz/StupidObjPool.kt
Normal file
45
src/main/kotlin/blitz/StupidObjPool.kt
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package blitz
|
||||||
|
|
||||||
|
import blitz.collections.RefVec
|
||||||
|
import kotlin.contracts.ExperimentalContracts
|
||||||
|
import kotlin.contracts.InvocationKind
|
||||||
|
import kotlin.contracts.contract
|
||||||
|
|
||||||
|
/**
|
||||||
|
* all created objects are stored in this pool, and when you call [StupidObjPool.markClear], all objects are marked as free and can be re-used
|
||||||
|
* this is useful for when you need a ton of objects during a process and the process runs multiple times (caches the objects between executions)
|
||||||
|
*/
|
||||||
|
class StupidObjPool<T>(initialCap: Int) {
|
||||||
|
@JvmField val _objs = RefVec<T>(initialCap)
|
||||||
|
|
||||||
|
@JvmField var _nextFreeId = 0
|
||||||
|
|
||||||
|
/** only one of constructor or initializer is called */
|
||||||
|
@OptIn(ExperimentalContracts::class)
|
||||||
|
inline fun get(constructor: () -> T, initializer: (T) -> Unit): T {
|
||||||
|
contract {
|
||||||
|
callsInPlace(constructor, InvocationKind.AT_MOST_ONCE)
|
||||||
|
callsInPlace(initializer, InvocationKind.AT_MOST_ONCE)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_nextFreeId < _objs.size) {
|
||||||
|
val o = _objs[_nextFreeId++]
|
||||||
|
initializer(o)
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
val o = constructor()
|
||||||
|
_objs.pushBack(o)
|
||||||
|
_nextFreeId ++
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearPool() {
|
||||||
|
_objs.clear()
|
||||||
|
_nextFreeId = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fun markClear() {
|
||||||
|
_nextFreeId = 0
|
||||||
|
}
|
||||||
|
}
|
@@ -1,75 +1,73 @@
|
|||||||
package blitz.collections
|
package blitz.collections
|
||||||
|
|
||||||
import kotlin.system.measureTimeMillis
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
class RefVec<T>(private val initCap: Int = 0): Vec<T> {
|
class RefVec<T>(private val initCap: Int = 0): Vec<T> {
|
||||||
override var size = 0
|
override var size = 0
|
||||||
private var cap = initCap
|
@JvmField var _cap = initCap
|
||||||
private var array: Array<Any?>? = if (initCap > 0) arrayOfNulls(initCap) else null
|
@JvmField var _array: Array<Any?>? = if (initCap > 0) arrayOfNulls(initCap) else null
|
||||||
|
|
||||||
override fun clear() {
|
override fun clear() {
|
||||||
size = 0
|
size = 0
|
||||||
if (array == null) return
|
if (_array == null) return
|
||||||
if (array!!.size <= initCap) {
|
if (_array!!.size <= initCap) {
|
||||||
cap = array!!.size
|
_cap = _array!!.size
|
||||||
} else {
|
} else {
|
||||||
cap = 0
|
_cap = 0
|
||||||
array = null
|
_array = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun copyAsArray(): Array<Any?> =
|
inline fun copyAsArray(): Array<Any?> =
|
||||||
array?.copyOfRange(0, size) ?: emptyArray()
|
_array?.copyOfRange(0, size) ?: emptyArray()
|
||||||
|
|
||||||
fun copyIntoArray(arr: Array<Any?>, destOff: Int = 0, startOff: Int = 0) =
|
inline fun copyIntoArray(arr: Array<Any?>, destOff: Int = 0, startOff: Int = 0) =
|
||||||
array?.copyInto(arr, destOff, startOff, size)
|
_array?.copyInto(arr, destOff, startOff, size)
|
||||||
|
|
||||||
override fun copy(): RefVec<T> =
|
override inline fun copy(): RefVec<T> =
|
||||||
RefVec<T>(size).also {
|
RefVec<T>(size).also {
|
||||||
it.array?.let { copyIntoArray(it) }
|
it._array?.let { copyIntoArray(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun reserve(amount: Int) {
|
override fun reserve(amount: Int) {
|
||||||
if (amount > 0 && cap - size >= amount)
|
if (amount > 0 && _cap - size >= amount)
|
||||||
return
|
return
|
||||||
if (array == null) {
|
if (_array == null) {
|
||||||
cap = size + amount
|
_cap = size + amount
|
||||||
array = arrayOfNulls(cap)
|
_array = arrayOfNulls(_cap)
|
||||||
} else {
|
} else {
|
||||||
array = array!!.copyOf(size + amount)
|
_array = _array!!.copyOf(size + amount)
|
||||||
cap = size + amount
|
_cap = size + amount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun reserve(need: Int, totalIfRealloc: Int) {
|
override fun reserve(need: Int, totalIfRealloc: Int) {
|
||||||
if (need > 0 && cap - size >= need)
|
if (need > 0 && _cap - size >= need)
|
||||||
return
|
return
|
||||||
if (array == null) {
|
if (_array == null) {
|
||||||
cap = size + totalIfRealloc
|
_cap = size + totalIfRealloc
|
||||||
array = arrayOfNulls(cap)
|
_array = arrayOfNulls(_cap)
|
||||||
} else {
|
} else {
|
||||||
array = array!!.copyOf(size + totalIfRealloc)
|
_array = _array!!.copyOf(size + totalIfRealloc)
|
||||||
cap = size + totalIfRealloc
|
_cap = size + totalIfRealloc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun popBack(): T =
|
override fun popBack(): T =
|
||||||
array!![size - 1].also {
|
_array!![size - 1].also {
|
||||||
reserve(-1)
|
reserve(-1)
|
||||||
size --
|
size --
|
||||||
} as T
|
} as T
|
||||||
|
|
||||||
override fun get(index: Int): T =
|
override inline fun get(index: Int): T =
|
||||||
array!![index] as T
|
(_array as Array<Any?>)[index] as T
|
||||||
|
|
||||||
override fun flip() {
|
override fun flip() {
|
||||||
array = array?.reversedArray()
|
_array = _array?.reversedArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun pushBack(elem: T) {
|
override fun pushBack(elem: T) {
|
||||||
reserve(1, 8)
|
reserve(1, 8)
|
||||||
array!![size] = elem
|
this[size] = elem
|
||||||
size ++
|
size ++
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +78,7 @@ class RefVec<T>(private val initCap: Int = 0): Vec<T> {
|
|||||||
override fun next(): T {
|
override fun next(): T {
|
||||||
if (!hasNext())
|
if (!hasNext())
|
||||||
throw NoSuchElementException()
|
throw NoSuchElementException()
|
||||||
return array!![index++] as T
|
return _array!![index++] as T
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,13 +86,13 @@ class RefVec<T>(private val initCap: Int = 0): Vec<T> {
|
|||||||
joinToString(prefix = "[", postfix = "]") { it.toString() }
|
joinToString(prefix = "[", postfix = "]") { it.toString() }
|
||||||
|
|
||||||
override fun set(index: Int, value: T) {
|
override fun set(index: Int, value: T) {
|
||||||
array!![index] = value
|
(_array as Array<Any?>)[index] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun <T> from(data: Array<T>) =
|
fun <T> from(data: Array<T>) =
|
||||||
RefVec<T>(data.size).also {
|
RefVec<T>(data.size).also {
|
||||||
it.array?.let { data.copyInto(it) }
|
it._array?.let { data.copyInto(it) }
|
||||||
it.size += data.size
|
it.size += data.size
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,9 +101,9 @@ class RefVec<T>(private val initCap: Int = 0): Vec<T> {
|
|||||||
data.forEach(bv::pushBack)
|
data.forEach(bv::pushBack)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> of(vararg elements: T): RefVec<T> =
|
inline fun <T> of(vararg elements: T): RefVec<T> =
|
||||||
RefVec<T>(elements.size).also {
|
RefVec<T>(elements.size shl 2).also {
|
||||||
it.array?.let { elements.copyInto(it) }
|
it._array?.let { elements.copyInto(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,37 +1,38 @@
|
|||||||
package blitz.parse
|
package blitz.parse
|
||||||
|
|
||||||
|
import blitz.collections.RefVec
|
||||||
import blitz.parse.comb2.*
|
import blitz.parse.comb2.*
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
import kotlin.system.measureNanoTime
|
import kotlin.system.measureNanoTime
|
||||||
|
|
||||||
object JSON {
|
object JSON {
|
||||||
|
|
||||||
|
val jsonBool: Parser<Char, Element> = choose {
|
||||||
|
it(mapValue(seq("true".toList())) { Element.newBool(true) })
|
||||||
|
it(mapValue(seq("false".toList())) { Element.newBool(false) })
|
||||||
|
}
|
||||||
|
|
||||||
|
val jsonNull: Parser<Char, Element> =
|
||||||
|
mapValue(seq("null".toList())) { Element.newNull() }
|
||||||
|
|
||||||
|
val jsonNum: Parser<Char, Element> =
|
||||||
|
mapValue(floatLit, Element::newNum)
|
||||||
|
|
||||||
|
val jsonString: Parser<Char, Element> =
|
||||||
|
mapValue(stringLit, Element::newStr)
|
||||||
|
|
||||||
val jsonElement = futureRec { jsonElement: Parser<Char, Element> ->
|
val jsonElement = futureRec { jsonElement: Parser<Char, Element> ->
|
||||||
|
|
||||||
val jsonNum: Parser<Char, Element> =
|
|
||||||
mapValue(floatLit(), ::Number)
|
|
||||||
|
|
||||||
val jsonString: Parser<Char, Element> =
|
|
||||||
mapValue(stringLit(), ::Str)
|
|
||||||
|
|
||||||
val jsonArray: Parser<Char, Element> =
|
val jsonArray: Parser<Char, Element> =
|
||||||
thenIgnore(
|
thenIgnore(
|
||||||
thenIgnore(
|
thenIgnore(
|
||||||
thenOverwrite(just('['),
|
thenOverwrite(just('['),
|
||||||
mapValue(delimitedBy(jsonElement, just(',')))
|
mapValue(delimitedBy(jsonElement, just(',')), Element::newArr)),
|
||||||
{ Array(it.toList())}),
|
whitespaces),
|
||||||
whitespaces()),
|
|
||||||
just(']')
|
just(']')
|
||||||
)
|
)
|
||||||
|
|
||||||
val jsonBool: Parser<Char, Element> = choose(
|
|
||||||
mapValue(seq("true".toList())) { Bool(true) },
|
|
||||||
mapValue(seq("false".toList())) { Bool(false) },
|
|
||||||
)
|
|
||||||
|
|
||||||
val jsonNull: Parser<Char, Element> =
|
|
||||||
mapValue(seq("null".toList())) { Nul() }
|
|
||||||
|
|
||||||
val jsonObj: Parser<Char, Element> =
|
val jsonObj: Parser<Char, Element> =
|
||||||
mapValue(thenIgnore(thenIgnore(thenOverwrite(
|
mapValue(thenIgnore(thenIgnore(thenOverwrite(
|
||||||
just('{'),
|
just('{'),
|
||||||
@@ -40,74 +41,99 @@ object JSON {
|
|||||||
thenIgnore(
|
thenIgnore(
|
||||||
thenIgnore(
|
thenIgnore(
|
||||||
thenOverwrite(
|
thenOverwrite(
|
||||||
whitespaces(),
|
whitespaces,
|
||||||
stringLit()),
|
stringLit),
|
||||||
whitespaces()),
|
whitespaces),
|
||||||
just(':')),
|
just(':')),
|
||||||
jsonElement),
|
jsonElement),
|
||||||
just(','))),
|
just(','))),
|
||||||
whitespaces()),
|
whitespaces),
|
||||||
just('}'))) { Obj(it.toMap()) }
|
just('}'))) { Element.newObj(it.toMap()) }
|
||||||
|
|
||||||
thenIgnore(thenOverwrite(
|
thenIgnore(thenOverwrite(
|
||||||
whitespaces(),
|
whitespaces,
|
||||||
choose(
|
choose {
|
||||||
jsonArray,
|
it(jsonArray)
|
||||||
jsonNum,
|
it(jsonNum)
|
||||||
jsonString,
|
it(jsonString)
|
||||||
jsonObj,
|
it(jsonObj)
|
||||||
jsonBool,
|
it(jsonBool)
|
||||||
jsonNull
|
it(jsonNull)
|
||||||
)),
|
}),
|
||||||
whitespaces())
|
whitespaces)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Element {
|
class Element(
|
||||||
val arr get() = (this as Array).value
|
@JvmField val kind: Int,
|
||||||
val num get() = (this as Number).value
|
@JvmField val _boxed: Any? = null,
|
||||||
val str get() = (this as Str).value
|
@JvmField val _num: Double = 0.0,
|
||||||
val obj get() = (this as Obj).value
|
@JvmField val _bool: Boolean = false,
|
||||||
val bool get() = (this as Bool).value
|
) {
|
||||||
|
companion object {
|
||||||
|
const val NUM = 0
|
||||||
|
const val BOOL = 1
|
||||||
|
const val NULL = 2
|
||||||
|
const val ARR = 3
|
||||||
|
const val STR = 4
|
||||||
|
const val OBJ = 5
|
||||||
|
|
||||||
fun isArr() = this is Array
|
inline fun newNum(v: Double): Element =
|
||||||
fun isNum() = this is Number
|
Element(NUM, _num = v)
|
||||||
fun isStr() = this is Str
|
inline fun newBool(v: Boolean): Element =
|
||||||
fun isObj() = this is Obj
|
Element(BOOL, _bool = v)
|
||||||
fun isBool() = this is Bool
|
inline fun newNull(): Element =
|
||||||
fun isNul() = this is Nul
|
Element(NULL)
|
||||||
|
inline fun newArr(v: RefVec<Element>): Element =
|
||||||
|
Element(ARR, _boxed = v)
|
||||||
|
inline fun newStr(v: String): Element =
|
||||||
|
Element(STR, _boxed = v)
|
||||||
|
inline fun newObj(v: Map<String, Element>): Element =
|
||||||
|
Element(OBJ, _boxed = v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun Element.uncheckedAsNum(): Double =
|
||||||
|
_num
|
||||||
|
inline fun Element.uncheckedAsBool(): Boolean =
|
||||||
|
_bool
|
||||||
|
inline fun Element.uncheckedAsArr(): RefVec<Element> =
|
||||||
|
_boxed as RefVec<Element>
|
||||||
|
inline fun Element.uncheckedAsStr(): String =
|
||||||
|
_boxed as String
|
||||||
|
inline fun Element.uncheckedAsObj(): Map<String, Element> =
|
||||||
|
_boxed as Map<String, Element>
|
||||||
|
|
||||||
|
inline fun Element.asNum(): Double {
|
||||||
|
require(kind == Element.NUM) { "Element is not a Number" }
|
||||||
|
return _num
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Array(val value: List<Element>): Element {
|
inline fun Element.asBool(): Boolean {
|
||||||
override fun toString(): String =
|
require(kind == Element.BOOL) { "Element is not a Boolean" }
|
||||||
value.joinToString(separator = ", ", prefix = "[", postfix = "]")
|
return _bool
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Number(val value: Double): Element {
|
inline fun Element.asArr(): RefVec<Element> {
|
||||||
override fun toString(): String =
|
require(kind == Element.ARR) { "Element is not an Array" }
|
||||||
value.toString()
|
return _boxed as RefVec<Element>
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Str(val value: String): Element {
|
inline fun Element.asStr(): String {
|
||||||
override fun toString(): String =
|
require(kind == Element.STR) { "Element is not a String" }
|
||||||
"\"$value\""
|
return _boxed as String
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Obj(val value: Map<String, Element>): Element {
|
inline fun Element.asObj(): Map<String, Element> {
|
||||||
override fun toString(): String =
|
require(kind == Element.OBJ) { "Element is not an Object" }
|
||||||
value.map { (k, v) -> "\"$k\": $v" }.joinToString(separator = ", ", prefix = "{", postfix = "}")
|
return _boxed as Map<String, Element>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun parse(string: String): ParseResult<Element> {
|
||||||
data class Bool(val value: Boolean): Element {
|
val ctx = ParseCtx(string.toList(), 0)
|
||||||
override fun toString(): String =
|
val v = jsonElement(ctx)
|
||||||
value.toString()
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
class Nul: Element
|
|
||||||
|
|
||||||
fun parse(string: String): ParseResult<Element> =
|
|
||||||
jsonElement(ParseCtx(string.toList(), 0))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
|
@@ -2,12 +2,11 @@ package blitz.parse.comb2
|
|||||||
|
|
||||||
import blitz.*
|
import blitz.*
|
||||||
import blitz.collections.RefVec
|
import blitz.collections.RefVec
|
||||||
import blitz.collections.contents
|
|
||||||
import blitz.str.charsToString
|
import blitz.str.charsToString
|
||||||
|
|
||||||
data class ParseCtx<I>(
|
data class ParseCtx<I>(
|
||||||
val input: List<I>,
|
@JvmField val input: List<I>,
|
||||||
var idx: Int
|
@JvmField var idx: Int
|
||||||
) {
|
) {
|
||||||
fun loadFrom(old: ParseCtx<I>) {
|
fun loadFrom(old: ParseCtx<I>) {
|
||||||
idx = old.idx
|
idx = old.idx
|
||||||
@@ -15,25 +14,39 @@ data class ParseCtx<I>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
data class ParseError(
|
data class ParseError(
|
||||||
val loc: Int,
|
@JvmField val loc: Int, /** can be -1 */
|
||||||
val message: String?,
|
@JvmField val message: String?,
|
||||||
)
|
)
|
||||||
|
|
||||||
typealias ParseResult<O> = Either<O, RefVec<ParseError>>
|
typealias ParseResult<O> = Either<O, ParseError>
|
||||||
typealias Parser<I, O> = (ParseCtx<I>) -> ParseResult<O>
|
typealias Parser<I, O> = (ParseCtx<I>) -> ParseResult<O>
|
||||||
|
|
||||||
inline fun <I, M: Any, O: Any> mapValue(crossinline self: Parser<I, M>, crossinline fn: (M) -> O): Parser<I, O> =
|
inline fun <I, M: Any, O: Any> mapValue(crossinline self: Parser<I, M>, crossinline fn: (M) -> O): Parser<I, O> =
|
||||||
{ self(it).mapA { fn(it) } }
|
{
|
||||||
|
val r = self(it) as Either<Any, ParseError>
|
||||||
|
r.a?.let {
|
||||||
|
r.a = fn(it as M)
|
||||||
|
}
|
||||||
|
r as Either<O, ParseError>
|
||||||
|
}
|
||||||
|
|
||||||
inline fun <I, O: Any> mapErrors(crossinline self: Parser<I, O>, crossinline fn: (RefVec<ParseError>) -> RefVec<ParseError>): Parser<I, O> =
|
inline fun <I, O: Any> mapErrors(crossinline self: Parser<I, O>, crossinline fn: (ParseError) -> ParseError): Parser<I, O> =
|
||||||
{ self(it).mapB { fn(it) } }
|
{
|
||||||
|
val r = self(it)
|
||||||
|
r.b?.let { r.b = fn(it) }
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
inline fun <I, M: Any, O: Any> then(crossinline self: Parser<I, M>, crossinline other: Parser<I, O>): Parser<I, Pair<M, O>> =
|
inline fun <I, M: Any, O: Any> then(crossinline self: Parser<I, M>, crossinline other: Parser<I, O>): Parser<I, Pair<M, O>> =
|
||||||
{ ctx ->
|
{ ctx ->
|
||||||
self(ctx).flatMapA<_,_,Pair<M,O>> { first ->
|
val r0 = self(ctx) as ParseResult<Any>
|
||||||
other.invoke(ctx)
|
r0.a?.let { first ->
|
||||||
.mapA { first to it }
|
val r1 = other(ctx)
|
||||||
}
|
r1.a?.let { second ->
|
||||||
|
(r1 as ParseResult<Any>).a = Pair(first, second)
|
||||||
|
(r1 as ParseResult<Pair<M, O>>)
|
||||||
|
} ?: (r1 as ParseResult<Pair<M, O>>)
|
||||||
|
} ?: (r0 as ParseResult<Pair<M, O>>)
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <I, M: Any, O: Any> thenOverwrite(crossinline self: Parser<I, M>, crossinline other: Parser<I, O>): Parser<I, O> =
|
inline fun <I, M: Any, O: Any> thenOverwrite(crossinline self: Parser<I, M>, crossinline other: Parser<I, O>): Parser<I, O> =
|
||||||
@@ -57,42 +70,59 @@ inline fun <I, O: Any> orElseVal(crossinline self: Parser<I, O>, value: O): Pars
|
|||||||
inline fun <I, O, R: Any> orElse(crossinline self: Parser<I, O>, crossinline other: Parser<I, R>): Parser<I, R> where O: R =
|
inline fun <I, O, R: Any> orElse(crossinline self: Parser<I, O>, crossinline other: Parser<I, R>): Parser<I, R> where O: R =
|
||||||
{
|
{
|
||||||
val old = it.idx
|
val old = it.idx
|
||||||
self(it).mapB { err ->
|
self(it).mapB { _ ->
|
||||||
it.idx = old
|
it.idx = old
|
||||||
other.invoke(it)
|
other.invoke(it)
|
||||||
.mapB { err.pushBack(it); err }
|
|
||||||
}.partiallyFlattenB()
|
}.partiallyFlattenB()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Use the other choose that takes a function whenever possible because of perf */
|
||||||
fun <I, O: Any> choose(possible: Iterable<Parser<I, O>>): Parser<I, O> =
|
fun <I, O: Any> choose(possible: Iterable<Parser<I, O>>): Parser<I, O> =
|
||||||
{ ctx ->
|
{ ctx ->
|
||||||
val errors = RefVec<ParseError>(possible.count())
|
|
||||||
var res: O? = null
|
var res: O? = null
|
||||||
for (p in possible) {
|
for (p in possible) {
|
||||||
val old = ctx.idx
|
val old = ctx.idx
|
||||||
val t = p.invoke(ctx)
|
val t = p.invoke(ctx)
|
||||||
if (t.isA) {
|
if (t.isA()) {
|
||||||
res = t.a!!
|
res = t.a!!
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
ctx.idx = old
|
ctx.idx = old
|
||||||
errors.pushBack(t.b!!)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res?.let { Either.ofA(it) }
|
res?.let { Either.ofA(it) }
|
||||||
?: Either.ofB(errors)
|
?: Either.ofB(ParseError(ctx.idx, "none of the possible parsers match"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <I, O: Any> choose(vararg possible: Parser<I, O>): Parser<I, O> =
|
inline fun <I, O: Any> choose(crossinline fn: (run: (Parser<I, O>) -> Unit) -> Unit): Parser<I, O> =
|
||||||
|
{ ctx ->
|
||||||
|
var res: O? = null
|
||||||
|
fn { p ->
|
||||||
|
if (res == null) {
|
||||||
|
val old = ctx.idx
|
||||||
|
val t = p.invoke(ctx)
|
||||||
|
if (t.isA()) {
|
||||||
|
res = t.a!!
|
||||||
|
} else {
|
||||||
|
ctx.idx = old
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res?.let { Either.ofA(it) }
|
||||||
|
?: Either.ofB(ParseError(ctx.idx, "none of the possible parsers match"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Use the other choose that takes a function whenever possible because of perf */
|
||||||
|
inline fun <I, O: Any> choose(vararg possible: Parser<I, O>): Parser<I, O> =
|
||||||
choose(possible.toList())
|
choose(possible.toList())
|
||||||
|
|
||||||
inline fun <I, O: Any> repeated(crossinline what: Parser<I, O>): Parser<I, RefVec<O>> =
|
inline fun <I, O: Any> repeated(crossinline what: Parser<I, O>): Parser<I, RefVec<O>> =
|
||||||
{ ctx ->
|
{ ctx ->
|
||||||
val out = RefVec<O>(0)
|
val out = RefVec<O>(16)
|
||||||
while (true) {
|
while (true) {
|
||||||
val old = ctx.idx
|
val old = ctx.idx
|
||||||
val t = what(ctx)
|
val t = what(ctx)
|
||||||
if (t.isA) {
|
if (t.isA()) {
|
||||||
out.pushBack(t.a!!)
|
out.pushBack(t.a!!)
|
||||||
} else {
|
} else {
|
||||||
ctx.idx = old
|
ctx.idx = old
|
||||||
@@ -107,7 +137,7 @@ inline fun <I, O: Any> repeatedNoSave(crossinline what: Parser<I, O>): Parser<I,
|
|||||||
while (true) {
|
while (true) {
|
||||||
val old = ctx.idx
|
val old = ctx.idx
|
||||||
val t = what(ctx)
|
val t = what(ctx)
|
||||||
if (t.isB) {
|
if (t.isB()) {
|
||||||
ctx.idx = old
|
ctx.idx = old
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -118,7 +148,7 @@ inline fun <I, O: Any> repeatedNoSave(crossinline what: Parser<I, O>): Parser<I,
|
|||||||
inline fun <I, O: Any> verifyValue(crossinline self: Parser<I, O>, crossinline verif: (O) -> String?): Parser<I, O> =
|
inline fun <I, O: Any> verifyValue(crossinline self: Parser<I, O>, crossinline verif: (O) -> String?): Parser<I, O> =
|
||||||
{ ctx ->
|
{ ctx ->
|
||||||
self(ctx).flatMapA<_,_,_> {
|
self(ctx).flatMapA<_,_,_> {
|
||||||
verif(it)?.let { Either.ofB(RefVec.of(ParseError(ctx.idx, it))) }
|
verif(it)?.let { Either.ofB(ParseError(ctx.idx, it)) }
|
||||||
?: Either.ofA(it)
|
?: Either.ofA(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -126,7 +156,7 @@ inline fun <I, O: Any> verifyValue(crossinline self: Parser<I, O>, crossinline v
|
|||||||
inline fun <I, O: Any> verifyValueWithSpan(crossinline self: Parser<I, Pair<IntRange, O>>, crossinline fn: (O) -> String?): Parser<I, O> =
|
inline fun <I, O: Any> verifyValueWithSpan(crossinline self: Parser<I, Pair<IntRange, O>>, crossinline fn: (O) -> String?): Parser<I, O> =
|
||||||
{ ctx ->
|
{ ctx ->
|
||||||
self(ctx).flatMapA<_,_,_> { (span, v) ->
|
self(ctx).flatMapA<_,_,_> { (span, v) ->
|
||||||
fn(v)?.let { Either.ofB(RefVec.of(ParseError(span.first, it))) }
|
fn(v)?.let { Either.ofB(ParseError(span.first, it)) }
|
||||||
?: Either.ofA(v)
|
?: Either.ofA(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,7 +167,7 @@ inline fun <I, O: Any> location(crossinline fn: (Int) -> O): Parser<I, O> =
|
|||||||
inline fun <I> location(): Parser<I, Int> =
|
inline fun <I> location(): Parser<I, Int> =
|
||||||
location { it }
|
location { it }
|
||||||
|
|
||||||
inline fun <I, O: Any> withSpan(crossinline p: Parser<I, O>): Parser<I, Pair<IntRange, O>> =
|
fun <I, O: Any> withSpan(p: Parser<I, O>): Parser<I, Pair<IntRange, O>> =
|
||||||
mapValue(then(then(location(), p), location())) { (beginAndV, end) ->
|
mapValue(then(then(location(), p), location())) { (beginAndV, end) ->
|
||||||
(beginAndV.first..end) to beginAndV.second
|
(beginAndV.first..end) to beginAndV.second
|
||||||
}
|
}
|
||||||
@@ -148,17 +178,17 @@ inline fun <I, O: Any> value(value: O): Parser<I, O> =
|
|||||||
fun <I, O: Any> chain(parsers: List<Parser<I, O>>): Parser<I, RefVec<O>> =
|
fun <I, O: Any> chain(parsers: List<Parser<I, O>>): Parser<I, RefVec<O>> =
|
||||||
{ ctx ->
|
{ ctx ->
|
||||||
val results = RefVec<O>(parsers.size)
|
val results = RefVec<O>(parsers.size)
|
||||||
val errs = RefVec<ParseError>(0)
|
var errs: ParseError? = null
|
||||||
for (p in parsers) {
|
for (p in parsers) {
|
||||||
val r = p.invoke(ctx)
|
val r = p.invoke(ctx)
|
||||||
if (r.isA) {
|
if (r.isA()) {
|
||||||
results.pushBack(r.a!!)
|
results.pushBack(r.a!!)
|
||||||
} else {
|
} else {
|
||||||
errs.pushBack(r.b!!)
|
errs = r.b!!
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (errs.size != 0) Either.ofB(errs)
|
if (errs != null) Either.ofB(errs)
|
||||||
else Either.ofA(results)
|
else Either.ofA(results)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,19 +198,34 @@ inline fun <I: Any> seq(want: List<I>): Parser<I, RefVec<I>> =
|
|||||||
inline fun <I: Any> filter(msg: String, crossinline filter: (I) -> Boolean): Parser<I, I> =
|
inline fun <I: Any> filter(msg: String, crossinline filter: (I) -> Boolean): Parser<I, I> =
|
||||||
{ ctx ->
|
{ ctx ->
|
||||||
if (ctx.idx >= ctx.input.size) {
|
if (ctx.idx >= ctx.input.size) {
|
||||||
Either.ofB(RefVec.of(ParseError(ctx.idx, "unexpected end of file")))
|
Either.ofB(ParseError(ctx.idx, "unexpected end of file"))
|
||||||
} else {
|
} else {
|
||||||
val i = ctx.input[ctx.idx++]
|
val i = ctx.input[ctx.idx++]
|
||||||
if (filter(i)) Either.ofA(i)
|
if (filter(i)) Either.ofA(i)
|
||||||
else Either.ofB(RefVec.of(ParseError(ctx.idx - 1, msg)))
|
else Either.ofB(ParseError(ctx.idx - 1, msg))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <I: Any> just(want: I): Parser<I, I> =
|
private class JustParse<I: Any>(wantIn: I): Parser<I, I> {
|
||||||
filter("expected $want") { it == want }
|
@JvmField val want = wantIn
|
||||||
|
@JvmField val uef: ParseResult<I> = Either.ofB(ParseError(-1, "unexpected end of file"))
|
||||||
|
@JvmField val exdiff: ParseResult<I> = Either.ofB(ParseError(-1, "expected $wantIn"))
|
||||||
|
@JvmField val eitherOfWant: ParseResult<I> = Either.ofA(want)
|
||||||
|
override fun invoke(ctx: ParseCtx<I>): ParseResult<I> {
|
||||||
|
return if (ctx.idx >= ctx.input.size) uef
|
||||||
|
else {
|
||||||
|
val i = ctx.input[ctx.idx++]
|
||||||
|
if (i == want) eitherOfWant
|
||||||
|
else exdiff
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <I: Any> just(wantIn: I): Parser<I, I> =
|
||||||
|
JustParse(wantIn)
|
||||||
|
|
||||||
inline fun <I: Any> oneOf(possible: Iterable<I>): Parser<I, I> =
|
inline fun <I: Any> oneOf(possible: Iterable<I>): Parser<I, I> =
|
||||||
filter("expected one of ${possible.contents}") { it in possible }
|
filter("expected different") { it in possible }
|
||||||
|
|
||||||
inline fun <I, O: Any> future(crossinline prov: Provider<Parser<I, O>>): Parser<I, O> =
|
inline fun <I, O: Any> future(crossinline prov: Provider<Parser<I, O>>): Parser<I, O> =
|
||||||
{ prov()(it) }
|
{ prov()(it) }
|
||||||
@@ -197,9 +242,9 @@ fun <O: Any> regex(pattern: Regex, fn: (groups: MatchGroupCollection) -> O): Par
|
|||||||
pattern.matchAt(ctx.input.charsToString(), ctx.idx)?.let {
|
pattern.matchAt(ctx.input.charsToString(), ctx.idx)?.let {
|
||||||
ctx.idx = it.range.last + 1
|
ctx.idx = it.range.last + 1
|
||||||
Either.ofA(fn(it.groups))
|
Either.ofA(fn(it.groups))
|
||||||
} ?: Either.ofB(RefVec.of(
|
} ?: Either.ofB(
|
||||||
ParseError(ctx.idx, "regular expression \"$pattern\" does not apply")
|
ParseError(ctx.idx, "regular expression \"$pattern\" does not apply")
|
||||||
))
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun regex(pattern: Regex) = regex(pattern) { it[0]!!.value }
|
fun regex(pattern: Regex) = regex(pattern) { it[0]!!.value }
|
||||||
|
@@ -5,30 +5,33 @@ import blitz.str.charsToString
|
|||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
import kotlin.math.sign
|
import kotlin.math.sign
|
||||||
|
|
||||||
fun whitespaces(): Parser<Char, Unit> =
|
private fun isWhitespace(it: Char) =
|
||||||
repeatedNoSave(oneOf("\n\t\r\b ".toList()))
|
it == ' ' || it == '\n' || it == '\t' || it == '\r' || it == '\b'
|
||||||
|
val whitespaces: Parser<Char, Unit> =
|
||||||
|
repeatedNoSave(filter("expected whitespace", ::isWhitespace))
|
||||||
|
|
||||||
fun digit(): Parser<Char, Char> =
|
val digit: Parser<Char, Char> =
|
||||||
oneOf("0123456789".toList())
|
filter("expected digit") { it >= '0' && it <= '9' }
|
||||||
|
|
||||||
fun uintLit(): Parser<Char, RefVec<Char>> =
|
val uintLit: Parser<Char, RefVec<Char>> =
|
||||||
verifyValueWithSpan(withSpan(repeated(digit())))
|
verifyValueWithSpan(withSpan(repeated(digit)))
|
||||||
{ if (it.size == 0) "need digits after sign in num lit" else null }
|
{ if (it.size == 0) "need digits after sign in num lit" else null }
|
||||||
|
|
||||||
fun intLit(): Parser<Char, Int> =
|
val intLit: Parser<Char, Int> =
|
||||||
mapValue(then(choose(mapValue(just('+')) { +1 },
|
mapValue(then(choose<Char,Int> {
|
||||||
mapValue(just('-')) { -1 },
|
it(mapValue(just('+')) { +1 })
|
||||||
value(+1)),
|
it(mapValue(just('-')) { -1 })
|
||||||
uintLit()))
|
it(value(+1))
|
||||||
|
}, uintLit))
|
||||||
{ (sign, v) -> sign * v.charsToString().toInt() }
|
{ (sign, v) -> sign * v.charsToString().toInt() }
|
||||||
|
|
||||||
fun floatLit(): Parser<Char, Double> =
|
val floatLit: Parser<Char, Double> =
|
||||||
mapValue(
|
mapValue(
|
||||||
then(
|
then(
|
||||||
thenIgnore(
|
thenIgnore(
|
||||||
intLit(),
|
intLit,
|
||||||
just('.')),
|
just('.')),
|
||||||
orElseVal(uintLit(), RefVec.of('0'))))
|
orElseVal(uintLit, RefVec.of('0'))))
|
||||||
{ (pre, post) ->
|
{ (pre, post) ->
|
||||||
var p = post.charsToString().toDouble()
|
var p = post.charsToString().toDouble()
|
||||||
while (p.absoluteValue >= 1) {
|
while (p.absoluteValue >= 1) {
|
||||||
@@ -38,22 +41,26 @@ fun floatLit(): Parser<Char, Double> =
|
|||||||
(pre.toDouble().absoluteValue + p) * pre.toDouble().sign
|
(pre.toDouble().absoluteValue + p) * pre.toDouble().sign
|
||||||
}
|
}
|
||||||
|
|
||||||
fun escapeChar(): Parser<Char, Char> =
|
val escapeChar: Parser<Char, Char> =
|
||||||
thenOverwrite(just('\\'),
|
thenOverwrite(just('\\'),
|
||||||
mapErrors(choose(just('"'),
|
mapErrors(choose {
|
||||||
just('\''),
|
it(just('"'))
|
||||||
just('\\'),
|
it(just('\''))
|
||||||
mapValue(just('n')) { '\n' },
|
it(just('\\'))
|
||||||
mapValue(just('r')) { '\r' },
|
it(mapValue(just('n')) { '\n' })
|
||||||
mapValue(just('b')) { '\b' },
|
it(mapValue(just('r')) { '\r' })
|
||||||
mapValue(just('t')) { '\t' }))
|
it(mapValue(just('b')) { '\b' })
|
||||||
{ RefVec.of(ParseError(it[0].loc, "invalid escape sequence")) }
|
it(mapValue(just('t')) { '\t' })
|
||||||
|
})
|
||||||
|
{ ParseError(it.loc, "invalid escape sequence") }
|
||||||
)
|
)
|
||||||
|
|
||||||
fun stringLit(): Parser<Char, String> =
|
val stringLit: Parser<Char, String> =
|
||||||
mapValue(thenIgnore(then(just('"'),
|
mapValue(thenIgnore(then(just('"'),
|
||||||
repeated(choose(escapeChar(),
|
repeated(choose<Char,Char>{
|
||||||
filter("a") { it != '"' }))),
|
it(escapeChar)
|
||||||
|
it(filter("a") { it != '"' })
|
||||||
|
})),
|
||||||
just('"')))
|
just('"')))
|
||||||
{ (_, str) -> str.charsToString() }
|
{ (_, str) -> str.charsToString() }
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user