increase parser perf by a lot

This commit is contained in:
alex_s168
2024-09-21 10:27:34 +00:00
parent 8c2325bdd3
commit f18798bb5c
7 changed files with 315 additions and 188 deletions

View File

@@ -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 {

View File

@@ -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()

View 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
}
}

View File

@@ -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) }
} }
} }
} }

View File

@@ -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() {

View File

@@ -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 }

View File

@@ -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() }