expression parsing, sorted list, tree, other shit; bump to 0.10
This commit is contained in:
52
README.md
52
README.md
@@ -12,7 +12,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("me.alex_s168:blitz:0.9")
|
implementation("me.alex_s168:blitz:0.10")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -138,5 +138,55 @@ Errors.print(config, errors)
|
|||||||
Output:
|
Output:
|
||||||
|
|
||||||

|

|
||||||
|
### Split with nesting
|
||||||
|
```kotlin
|
||||||
|
val inp = "arr[int], long, long, arr[double], int"
|
||||||
|
val split = inp.splitWithNesting(',', nestUp = '[', nestDown = ']')
|
||||||
|
println(split)
|
||||||
|
```
|
||||||
|
### Expression parsing
|
||||||
|
```kotlin
|
||||||
|
val input = "sin(max(2, 3) / (3 * pi))".iterator()
|
||||||
|
val tok = input.tokenize(
|
||||||
|
ignore = listOf(' ', '\n'),
|
||||||
|
operators = listOf(
|
||||||
|
Operator('+', 2, true),
|
||||||
|
Operator('-', 2, true),
|
||||||
|
|
||||||
|
Operator('*', 3, true),
|
||||||
|
Operator('/', 3, true),
|
||||||
|
Operator('%', 3, true),
|
||||||
|
|
||||||
|
Operator('~', 10, false),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val rpn = tok.shuntingYard().collect()
|
||||||
|
if (input.hasNext())
|
||||||
|
error("Unexpected token(s): ${input.collectToString()}")
|
||||||
|
println(rpn)
|
||||||
|
// outputs:
|
||||||
|
// [
|
||||||
|
// Token(type=NUMBER, value=2, op=null),
|
||||||
|
// Token(type=NUMBER, value=3, op=null),
|
||||||
|
// Token(type=IDENT, value=max, op=null),
|
||||||
|
// Token(type=NUMBER, value=3, op=null),
|
||||||
|
// Token(type=IDENT, value=pi, op=null),
|
||||||
|
// Token(type=OPERATOR, value=null, op=Operator(symbol=*, precedence=3, leftAssociative=true)),
|
||||||
|
// Token(type=OPERATOR, value=null, op=Operator(symbol=/, precedence=3, leftAssociative=true)),
|
||||||
|
// Token(type=IDENT, value=sin, op=null)
|
||||||
|
// ]
|
||||||
|
```
|
||||||
|
### SortedList
|
||||||
|
```kotlin
|
||||||
|
val list = SortedArrayList<Int> { it }
|
||||||
|
list.add(1)
|
||||||
|
list.add(9)
|
||||||
|
list.add(5)
|
||||||
|
println(list.contents)
|
||||||
|
// outputs: [1, 5, 9]
|
||||||
|
```
|
||||||
### Either
|
### Either
|
||||||
No example yet
|
No example yet
|
||||||
|
|
||||||
|
### Tree
|
||||||
|
No example yet
|
@@ -5,7 +5,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = "me.alex_s168"
|
group = "me.alex_s168"
|
||||||
version = "0.9"
|
version = "0.10"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
@@ -4,7 +4,6 @@ import blitz.collections.inBounds
|
|||||||
import blitz.str.ColoredChar
|
import blitz.str.ColoredChar
|
||||||
import blitz.str.MutMultiColoredMultiLineString
|
import blitz.str.MutMultiColoredMultiLineString
|
||||||
import blitz.str.MutMultiLineString
|
import blitz.str.MutMultiLineString
|
||||||
import blitz.str.MutString
|
|
||||||
import blitz.term.AnsiiMode
|
import blitz.term.AnsiiMode
|
||||||
import blitz.term.Terminal
|
import blitz.term.Terminal
|
||||||
|
|
||||||
@@ -143,7 +142,7 @@ object Errors {
|
|||||||
|
|
||||||
byCol.filter { it.isLongDesc }.forEach {
|
byCol.filter { it.isLongDesc }.forEach {
|
||||||
val msgLines = MutMultiLineString.from(it.message, fill = ' ')
|
val msgLines = MutMultiLineString.from(it.message, fill = ' ')
|
||||||
msg.set(row, nextCol, msgLines, Terminal.COLORS.WHITE.brighter.fg)
|
msg[row, nextCol, msgLines] = Terminal.COLORS.WHITE.brighter.fg
|
||||||
row ++
|
row ++
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,40 +153,3 @@ object Errors {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun main() {
|
|
||||||
val source = Errors.Source("main.kt", MutMultiLineString.from("""
|
|
||||||
fn main() {
|
|
||||||
return 1 + 0
|
|
||||||
}
|
|
||||||
""".trimIndent(), ' '))
|
|
||||||
|
|
||||||
val errors = listOf(
|
|
||||||
Errors.Error(
|
|
||||||
"cannot return integer from function with return type void",
|
|
||||||
Errors.Error.Level.ERROR,
|
|
||||||
Errors.Location(source, 1, 11, 5)
|
|
||||||
),
|
|
||||||
Errors.Error(
|
|
||||||
"return is deprecated. use yeet instead",
|
|
||||||
Errors.Error.Level.WARN,
|
|
||||||
Errors.Location(source, 1, 4, 6)
|
|
||||||
),
|
|
||||||
Errors.Error(
|
|
||||||
"useless addition",
|
|
||||||
Errors.Error.Level.INFO,
|
|
||||||
Errors.Location(source, 1, 13, 3),
|
|
||||||
isHint = true
|
|
||||||
),
|
|
||||||
Errors.Error(
|
|
||||||
"Visit https://www.example.com/doc/yeet for more information",
|
|
||||||
Errors.Error.Level.INFO,
|
|
||||||
Errors.Location(source, 1, 0, 0),
|
|
||||||
isLongDesc = true
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
val config = Errors.PrintConfig()
|
|
||||||
|
|
||||||
Errors.print(config, errors)
|
|
||||||
}
|
|
@@ -8,11 +8,3 @@ fun ByteBatchSequence.stringify(batch: Int = 64): Sequence<String> {
|
|||||||
else null
|
else null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Sequence<String>.flatten(): String {
|
|
||||||
val out = StringBuilder()
|
|
||||||
forEach {
|
|
||||||
out.append(it)
|
|
||||||
}
|
|
||||||
return out.toString()
|
|
||||||
}
|
|
4
src/main/kotlin/blitz/collections/Constructors.kt
Normal file
4
src/main/kotlin/blitz/collections/Constructors.kt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
package blitz.collections
|
||||||
|
|
||||||
|
fun <T, C: Comparable<C>> SortedArrayList(sorter: (T) -> C) =
|
||||||
|
SortedList(ArrayList(), sorter)
|
@@ -8,3 +8,46 @@ fun <T, R> Iterator<T>.mapModifier(fn: (T) -> R): Iterator<R> =
|
|||||||
override fun hasNext(): Boolean =
|
override fun hasNext(): Boolean =
|
||||||
this@mapModifier.hasNext()
|
this@mapModifier.hasNext()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <T> generateIterator(fn: () -> T?): Iterator<T> =
|
||||||
|
object: Iterator<T> {
|
||||||
|
var calc = false
|
||||||
|
var nx: T? = null
|
||||||
|
|
||||||
|
override fun hasNext(): Boolean {
|
||||||
|
if (!calc) {
|
||||||
|
nx = fn()
|
||||||
|
calc = true
|
||||||
|
}
|
||||||
|
return nx != null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun next(): T {
|
||||||
|
if (!hasNext())
|
||||||
|
throw Exception("iterator end")
|
||||||
|
calc = false
|
||||||
|
return nx!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can't explain this function. Look at the source of [blitz.parse.tokenize] as an example
|
||||||
|
*/
|
||||||
|
fun <T, R> Iterator<T>.funnyMap(fn: (UnGettableIterator<T>) -> R?): Iterator<R> {
|
||||||
|
val iter = asUnGettable()
|
||||||
|
return generateIterator { fn(iter) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> Iterator<T>.collect(to: MutableList<T> = mutableListOf()): MutableList<T> {
|
||||||
|
forEachRemaining {
|
||||||
|
to.add(it)
|
||||||
|
}
|
||||||
|
return to
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> Iterator<T>.collect(to: Vec<T>): Vec<T> {
|
||||||
|
forEachRemaining {
|
||||||
|
to.pushBack(it)
|
||||||
|
}
|
||||||
|
return to
|
||||||
|
}
|
7
src/main/kotlin/blitz/collections/MapEmpty.kt
Normal file
7
src/main/kotlin/blitz/collections/MapEmpty.kt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package blitz.collections
|
||||||
|
|
||||||
|
fun <T> Collection<T>.nullIfEmpty(): Collection<T>? =
|
||||||
|
ifEmpty { null }
|
||||||
|
|
||||||
|
fun String.nullIfEmpty(): String? =
|
||||||
|
ifEmpty { null }
|
53
src/main/kotlin/blitz/collections/Search.kt
Normal file
53
src/main/kotlin/blitz/collections/Search.kt
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package blitz.collections
|
||||||
|
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
|
object Search {
|
||||||
|
/**
|
||||||
|
* Find a element by comparing.
|
||||||
|
* Only works on sorted lists
|
||||||
|
*/
|
||||||
|
fun <T, C: Comparable<C>> findElementComparing(
|
||||||
|
list: List<T>,
|
||||||
|
element: T,
|
||||||
|
comparer: (T) -> C,
|
||||||
|
): Int {
|
||||||
|
var rev = max(0, list.size / 2 - 1)
|
||||||
|
var prev = 0
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
// outside of array:
|
||||||
|
if (rev < 0)
|
||||||
|
break
|
||||||
|
|
||||||
|
// outside of array:
|
||||||
|
if (rev >= list.size)
|
||||||
|
break
|
||||||
|
|
||||||
|
val cmp = comparer(element)
|
||||||
|
.compareTo(comparer(list[rev]))
|
||||||
|
|
||||||
|
// found:
|
||||||
|
if (cmp == 0)
|
||||||
|
return rev
|
||||||
|
|
||||||
|
// in between of two existing values:
|
||||||
|
if (cmp > 0 && prev < 0)
|
||||||
|
break
|
||||||
|
|
||||||
|
// in between of two existing values:
|
||||||
|
if (cmp < 0 && prev > 0)
|
||||||
|
break
|
||||||
|
|
||||||
|
if (cmp < 0)
|
||||||
|
rev --
|
||||||
|
|
||||||
|
else if (cmp > 0)
|
||||||
|
rev ++
|
||||||
|
|
||||||
|
prev = cmp
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
}
|
@@ -4,6 +4,7 @@ interface IndexableSequence<T>: Sequence<T> {
|
|||||||
operator fun get(index: Int): T
|
operator fun get(index: Int): T
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: rename to map
|
||||||
fun <T> IndexableSequence<T>.modifier(mod: (Sequence<T>) -> Sequence<T>) =
|
fun <T> IndexableSequence<T>.modifier(mod: (Sequence<T>) -> Sequence<T>) =
|
||||||
object : IndexableSequence<T> {
|
object : IndexableSequence<T> {
|
||||||
val other = mod(this@modifier)
|
val other = mod(this@modifier)
|
||||||
|
128
src/main/kotlin/blitz/collections/SortedList.kt
Normal file
128
src/main/kotlin/blitz/collections/SortedList.kt
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
package blitz.collections
|
||||||
|
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mutable list that uses a different mutable list as underlying data structure.
|
||||||
|
* The list guarantees all elements to stay sorted unless manually inserted.
|
||||||
|
*/
|
||||||
|
class SortedList<T, C: Comparable<C>>(
|
||||||
|
private val al: MutableList<T>,
|
||||||
|
private val sorter: (T) -> C
|
||||||
|
): MutableList<T>, RandomAccess {
|
||||||
|
override val size: Int =
|
||||||
|
al.size
|
||||||
|
|
||||||
|
override fun clear() =
|
||||||
|
al.clear()
|
||||||
|
|
||||||
|
override fun addAll(elements: Collection<T>): Boolean {
|
||||||
|
elements.forEach(::add)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addAll(index: Int, elements: Collection<T>): Boolean =
|
||||||
|
al.addAll(index, elements)
|
||||||
|
|
||||||
|
override fun add(index: Int, element: T) =
|
||||||
|
al.add(index, element)
|
||||||
|
|
||||||
|
override fun add(element: T): Boolean {
|
||||||
|
var rev = max(0, al.size / 2 - 1)
|
||||||
|
var prev = 0
|
||||||
|
while (true) {
|
||||||
|
if (rev < 0) {
|
||||||
|
al.add(0, element)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (rev >= al.size) {
|
||||||
|
al.add(element)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
val cmp = sorter(element)
|
||||||
|
.compareTo(sorter(al[rev]))
|
||||||
|
if (cmp == 0) {
|
||||||
|
al.add(rev, element)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (cmp > 0 && prev < 0) {
|
||||||
|
al.add(rev - 1, element)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (cmp < 0 && prev > 0) {
|
||||||
|
al.add(rev, element)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (cmp < 0)
|
||||||
|
rev --
|
||||||
|
else if (cmp > 0)
|
||||||
|
rev ++
|
||||||
|
prev = cmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun get(index: Int): T =
|
||||||
|
al[index]
|
||||||
|
|
||||||
|
override fun isEmpty(): Boolean =
|
||||||
|
al.isEmpty()
|
||||||
|
|
||||||
|
override fun iterator(): MutableIterator<T> =
|
||||||
|
al.iterator()
|
||||||
|
|
||||||
|
override fun listIterator(): MutableListIterator<T> =
|
||||||
|
al.listIterator()
|
||||||
|
|
||||||
|
override fun listIterator(index: Int): MutableListIterator<T> =
|
||||||
|
al.listIterator(index)
|
||||||
|
|
||||||
|
override fun removeAt(index: Int): T =
|
||||||
|
al.removeAt(index)
|
||||||
|
|
||||||
|
override fun subList(fromIndex: Int, toIndex: Int): MutableList<T> =
|
||||||
|
al.subList(fromIndex, toIndex)
|
||||||
|
|
||||||
|
override fun set(index: Int, element: T): T =
|
||||||
|
al.set(index, element)
|
||||||
|
|
||||||
|
override fun retainAll(elements: Collection<T>): Boolean =
|
||||||
|
al.retainAll(elements)
|
||||||
|
|
||||||
|
override fun removeAll(elements: Collection<T>): Boolean {
|
||||||
|
var rem = false
|
||||||
|
for (x in elements) {
|
||||||
|
if (remove(x))
|
||||||
|
rem = true
|
||||||
|
}
|
||||||
|
return rem
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove(element: T): Boolean {
|
||||||
|
val i = Search.findElementComparing(al, element, sorter)
|
||||||
|
if (i == -1)
|
||||||
|
return false
|
||||||
|
removeAt(i)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun lastIndexOf(element: T): Int =
|
||||||
|
al.lastIndexOf(element)
|
||||||
|
|
||||||
|
override fun indexOf(element: T): Int =
|
||||||
|
al.indexOf(element)
|
||||||
|
|
||||||
|
override fun containsAll(elements: Collection<T>): Boolean =
|
||||||
|
elements.all { contains(it) }
|
||||||
|
|
||||||
|
override fun contains(element: T): Boolean =
|
||||||
|
Search.findElementComparing(al, element, sorter) != -1
|
||||||
|
|
||||||
|
override fun toString(): String =
|
||||||
|
al.toString()
|
||||||
|
|
||||||
|
inline fun sorted(): SortedList<T, C> =
|
||||||
|
this
|
||||||
|
|
||||||
|
inline fun sortedDescending(): List<T> =
|
||||||
|
this.reversed()
|
||||||
|
}
|
79
src/main/kotlin/blitz/collections/SynchronizedList.kt
Normal file
79
src/main/kotlin/blitz/collections/SynchronizedList.kt
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package blitz.collections
|
||||||
|
|
||||||
|
import blitz.async.Lock
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps any MutableList.
|
||||||
|
* Concurrent by synchronizing.
|
||||||
|
*/
|
||||||
|
class SynchronizedList<T, L: MutableList<T>>(
|
||||||
|
private val innerList: L
|
||||||
|
): MutableList<T>, RandomAccess {
|
||||||
|
private val innerLock = Lock()
|
||||||
|
|
||||||
|
fun <R> access(block: (L) -> R): R =
|
||||||
|
innerLock.use { block(innerList) }
|
||||||
|
|
||||||
|
override val size: Int
|
||||||
|
get() = innerList.size
|
||||||
|
|
||||||
|
override fun clear() =
|
||||||
|
access { it.clear() }
|
||||||
|
|
||||||
|
override fun addAll(elements: Collection<T>): Boolean =
|
||||||
|
access { it.addAll(elements) }
|
||||||
|
|
||||||
|
override fun addAll(index: Int, elements: Collection<T>): Boolean =
|
||||||
|
access { it.addAll(index, elements) }
|
||||||
|
|
||||||
|
override fun add(index: Int, element: T) =
|
||||||
|
access { it.add(index, element) }
|
||||||
|
|
||||||
|
override fun add(element: T): Boolean =
|
||||||
|
access { it.add(element) }
|
||||||
|
|
||||||
|
override fun get(index: Int): T =
|
||||||
|
access { it.get(index) }
|
||||||
|
|
||||||
|
override fun isEmpty(): Boolean =
|
||||||
|
access { it.isEmpty() }
|
||||||
|
|
||||||
|
override fun iterator(): MutableIterator<T> =
|
||||||
|
access { it.iterator() }
|
||||||
|
|
||||||
|
override fun listIterator(): MutableListIterator<T> =
|
||||||
|
access { it.listIterator() }
|
||||||
|
|
||||||
|
override fun listIterator(index: Int): MutableListIterator<T> =
|
||||||
|
access { it.listIterator(index) }
|
||||||
|
|
||||||
|
override fun removeAt(index: Int): T =
|
||||||
|
access { it.removeAt(index) }
|
||||||
|
|
||||||
|
override fun subList(fromIndex: Int, toIndex: Int): MutableList<T> =
|
||||||
|
access { it.subList(fromIndex, toIndex) }
|
||||||
|
|
||||||
|
override fun set(index: Int, element: T): T =
|
||||||
|
access { it.set(index, element) }
|
||||||
|
|
||||||
|
override fun retainAll(elements: Collection<T>): Boolean =
|
||||||
|
access { it.retainAll(elements) }
|
||||||
|
|
||||||
|
override fun removeAll(elements: Collection<T>): Boolean =
|
||||||
|
access { it.removeAll(elements) }
|
||||||
|
|
||||||
|
override fun remove(element: T): Boolean =
|
||||||
|
access { it.remove(element) }
|
||||||
|
|
||||||
|
override fun lastIndexOf(element: T): Int =
|
||||||
|
access { it.lastIndexOf(element) }
|
||||||
|
|
||||||
|
override fun indexOf(element: T): Int =
|
||||||
|
access { it.indexOf(element) }
|
||||||
|
|
||||||
|
override fun containsAll(elements: Collection<T>): Boolean =
|
||||||
|
access { it.containsAll(elements) }
|
||||||
|
|
||||||
|
override fun contains(element: T): Boolean =
|
||||||
|
access { it.contains(element) }
|
||||||
|
}
|
84
src/main/kotlin/blitz/collections/Tree.kt
Normal file
84
src/main/kotlin/blitz/collections/Tree.kt
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
package blitz.collections
|
||||||
|
|
||||||
|
class Tree<T> {
|
||||||
|
var root: Node<T>? = null
|
||||||
|
|
||||||
|
operator fun plusAssign(value: T) {
|
||||||
|
val node = Node<T>()
|
||||||
|
node.value = value
|
||||||
|
root?.children?.add(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun traverse(traverser: TreeTraverser<T>) =
|
||||||
|
root?.traverse(traverser)
|
||||||
|
|
||||||
|
fun traverse(process: (Node<T>) -> Boolean) =
|
||||||
|
root?.traverse(process)
|
||||||
|
|
||||||
|
fun updateParents() {
|
||||||
|
traverse { node ->
|
||||||
|
node.children.forEach { it.parent = node }
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return root.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Node<T>(
|
||||||
|
var value: T? = null,
|
||||||
|
var children: MutableList<Node<T>> = mutableListOf()
|
||||||
|
) {
|
||||||
|
var parent: Node<T>? = null
|
||||||
|
|
||||||
|
fun traverse(traverser: TreeTraverser<T>) =
|
||||||
|
traverser.traverse(this)
|
||||||
|
|
||||||
|
fun traverse(process: (Node<T>) -> Boolean) {
|
||||||
|
val trav = object : TreeTraverser<T> {
|
||||||
|
override fun process(node: Node<T>): Boolean {
|
||||||
|
return process(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
traverse(trav)
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun plusAssign(value: T) {
|
||||||
|
val node = Node<T>()
|
||||||
|
node.value = value
|
||||||
|
children.add(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toLines(): List<Pair<Int, String>> {
|
||||||
|
val lines = mutableListOf<Pair<Int, String>>()
|
||||||
|
lines += 0 to value.toString()
|
||||||
|
children.forEach { child ->
|
||||||
|
child.toLines().forEach { (indent, line) ->
|
||||||
|
lines += indent + 1 to line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
val lines = toLines()
|
||||||
|
val sb = StringBuilder()
|
||||||
|
lines.forEach { (indent, line) ->
|
||||||
|
sb.append(" ".repeat(indent * 2))
|
||||||
|
sb.append(line)
|
||||||
|
sb.append("\n")
|
||||||
|
}
|
||||||
|
return sb.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TreeTraverser<T> {
|
||||||
|
fun process(node: Node<T>): Boolean /* true if continue, false if stop */
|
||||||
|
|
||||||
|
fun traverse(tree: Node<T>) {
|
||||||
|
if (!process(tree)) return
|
||||||
|
tree.children.forEach { traverse(it) }
|
||||||
|
}
|
||||||
|
}
|
27
src/main/kotlin/blitz/collections/Ungettable.kt
Normal file
27
src/main/kotlin/blitz/collections/Ungettable.kt
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package blitz.collections
|
||||||
|
|
||||||
|
interface UnGettableIterator<T>: Iterator<T> {
|
||||||
|
/**
|
||||||
|
* Undo the previous next() operation. Only possible once per next()!
|
||||||
|
*/
|
||||||
|
fun unGet()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> Iterator<T>.asUnGettable(): UnGettableIterator<T> =
|
||||||
|
object: UnGettableIterator<T> {
|
||||||
|
private var prev: T? = null
|
||||||
|
private var unget = false
|
||||||
|
|
||||||
|
override fun unGet() {
|
||||||
|
if (prev == null)
|
||||||
|
throw Exception("Cannot unGet because there was no previous")
|
||||||
|
unget = true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hasNext(): Boolean =
|
||||||
|
unget || this@asUnGettable.hasNext()
|
||||||
|
|
||||||
|
override fun next(): T =
|
||||||
|
if (unget) prev!!.also { unget = false }
|
||||||
|
else this@asUnGettable.next().also { prev = it }
|
||||||
|
}
|
20
src/main/kotlin/blitz/ice/Cooled.kt
Normal file
20
src/main/kotlin/blitz/ice/Cooled.kt
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package blitz.ice
|
||||||
|
|
||||||
|
class Cooled<T>(private val of: T): Freezable {
|
||||||
|
private var frozen = false
|
||||||
|
|
||||||
|
override fun freeze() {
|
||||||
|
frozen = true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isFrozen(): Boolean {
|
||||||
|
return frozen
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getOrNull(): T? =
|
||||||
|
if (isFrozen()) null else of
|
||||||
|
|
||||||
|
fun <R> use(block: (T) -> R): R? =
|
||||||
|
if (isFrozen()) null
|
||||||
|
else block(of)
|
||||||
|
}
|
10
src/main/kotlin/blitz/ice/Freezable.kt
Normal file
10
src/main/kotlin/blitz/ice/Freezable.kt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package blitz.ice
|
||||||
|
|
||||||
|
interface Freezable {
|
||||||
|
fun freeze()
|
||||||
|
fun isFrozen(): Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <R> Freezable.map(block: (Freezable) -> R): R? =
|
||||||
|
if (isFrozen()) null
|
||||||
|
else block(this)
|
36
src/main/kotlin/blitz/logic/Bools.kt
Normal file
36
src/main/kotlin/blitz/logic/Bools.kt
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package blitz.logic
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a block if the boolean is true.
|
||||||
|
*/
|
||||||
|
fun Boolean.then(block: () -> Unit): Boolean {
|
||||||
|
if (this) {
|
||||||
|
block()
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a block if the boolean is false.
|
||||||
|
*/
|
||||||
|
fun Boolean.otherwise(block: () -> Unit): Boolean {
|
||||||
|
if (!this) {
|
||||||
|
block()
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a block if the boolean is true, otherwise execute another block.
|
||||||
|
*/
|
||||||
|
fun Boolean.then(block: () -> Unit, otherwise: () -> Unit): Boolean {
|
||||||
|
if (this) {
|
||||||
|
block()
|
||||||
|
} else {
|
||||||
|
otherwise()
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
7
src/main/kotlin/blitz/parse/Operator.kt
Normal file
7
src/main/kotlin/blitz/parse/Operator.kt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package blitz.parse
|
||||||
|
|
||||||
|
data class Operator(
|
||||||
|
val symbol: Char,
|
||||||
|
val precedence: Int = 0,
|
||||||
|
val leftAssociative: Boolean = true,
|
||||||
|
)
|
62
src/main/kotlin/blitz/parse/ShuntingYard.kt
Normal file
62
src/main/kotlin/blitz/parse/ShuntingYard.kt
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package blitz.parse
|
||||||
|
|
||||||
|
fun Iterator<Token>.shuntingYard(): Iterator<Token> {
|
||||||
|
val iter = this
|
||||||
|
val opStack = mutableListOf<Token>()
|
||||||
|
return sequence {
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
val tk0 = iter.next()
|
||||||
|
when (tk0.type) {
|
||||||
|
Token.Type.NUMBER -> yield(tk0)
|
||||||
|
Token.Type.IDENT -> opStack.add(tk0)
|
||||||
|
Token.Type.OPERATOR -> {
|
||||||
|
while (true) {
|
||||||
|
if (opStack.isEmpty())
|
||||||
|
break
|
||||||
|
val oo = opStack.last()
|
||||||
|
if (oo.type == Token.Type.PAREN_OPEN)
|
||||||
|
break
|
||||||
|
if (!((oo.op!!.precedence > tk0.op!!.precedence) || (tk0.op.precedence == oo.op.precedence && tk0.op.leftAssociative)))
|
||||||
|
break
|
||||||
|
opStack.removeLast()
|
||||||
|
yield(oo)
|
||||||
|
}
|
||||||
|
opStack.add(tk0)
|
||||||
|
}
|
||||||
|
Token.Type.COMMA -> {
|
||||||
|
while (true) {
|
||||||
|
if (opStack.isEmpty())
|
||||||
|
break
|
||||||
|
val oo = opStack.last()
|
||||||
|
if (oo.type == Token.Type.PAREN_OPEN)
|
||||||
|
break
|
||||||
|
opStack.removeLast()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Token.Type.PAREN_OPEN -> opStack.add(tk0)
|
||||||
|
Token.Type.PAREN_CLOSE -> {
|
||||||
|
while (true) {
|
||||||
|
if (opStack.isEmpty())
|
||||||
|
throw Exception("Unexpected closing parenthesis!")
|
||||||
|
val oo = opStack.last()
|
||||||
|
if (oo.type == Token.Type.PAREN_OPEN)
|
||||||
|
break
|
||||||
|
opStack.removeLast()
|
||||||
|
yield(oo)
|
||||||
|
}
|
||||||
|
if (opStack.removeLastOrNull()?.type != Token.Type.PAREN_OPEN)
|
||||||
|
throw Exception("Unexpected closing parenthesis!")
|
||||||
|
if (opStack.lastOrNull()?.type == Token.Type.IDENT)
|
||||||
|
yield(opStack.removeLast())
|
||||||
|
}
|
||||||
|
Token.Type.SEPARATOR -> continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (opStack.isNotEmpty()) {
|
||||||
|
val oo = opStack.removeLast()
|
||||||
|
if (oo.type in listOf(Token.Type.PAREN_OPEN, Token.Type.PAREN_CLOSE))
|
||||||
|
throw Exception("Mismatched parenthesis")
|
||||||
|
yield(oo)
|
||||||
|
}
|
||||||
|
}.iterator()
|
||||||
|
}
|
17
src/main/kotlin/blitz/parse/Token.kt
Normal file
17
src/main/kotlin/blitz/parse/Token.kt
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package blitz.parse
|
||||||
|
|
||||||
|
data class Token(
|
||||||
|
val type: Type,
|
||||||
|
val value: String? = null,
|
||||||
|
val op: Operator? = null,
|
||||||
|
) {
|
||||||
|
enum class Type {
|
||||||
|
IDENT,
|
||||||
|
NUMBER,
|
||||||
|
PAREN_OPEN,
|
||||||
|
PAREN_CLOSE,
|
||||||
|
OPERATOR,
|
||||||
|
SEPARATOR,
|
||||||
|
COMMA,
|
||||||
|
}
|
||||||
|
}
|
91
src/main/kotlin/blitz/parse/Tokenize.kt
Normal file
91
src/main/kotlin/blitz/parse/Tokenize.kt
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
package blitz.parse
|
||||||
|
|
||||||
|
import blitz.collections.funnyMap
|
||||||
|
|
||||||
|
fun Iterator<Char>.tokenize(
|
||||||
|
operators: Collection<Operator> = emptyList(),
|
||||||
|
ignore: Collection<Char> = emptyList(),
|
||||||
|
): Iterator<Token> =
|
||||||
|
funnyMap {
|
||||||
|
val builder = StringBuilder()
|
||||||
|
var ident = false
|
||||||
|
var num = false
|
||||||
|
var paren: Char? = null
|
||||||
|
var op: Operator? = null
|
||||||
|
var sep = false
|
||||||
|
var comma = false
|
||||||
|
while (it.hasNext()) {
|
||||||
|
when (val c = it.next()) {
|
||||||
|
in 'a'..'z',
|
||||||
|
in 'A'..'Z',
|
||||||
|
'_' -> {
|
||||||
|
if (num) {
|
||||||
|
it.unGet()
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
builder.append(c)
|
||||||
|
ident = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in '0'..'9' -> {
|
||||||
|
builder.append(c)
|
||||||
|
if (!ident)
|
||||||
|
num = true
|
||||||
|
}
|
||||||
|
'.' -> {
|
||||||
|
if (num)
|
||||||
|
builder.append(it)
|
||||||
|
else {
|
||||||
|
it.unGet()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'(',
|
||||||
|
')' -> {
|
||||||
|
if (!ident && !num)
|
||||||
|
paren = c
|
||||||
|
else
|
||||||
|
it.unGet()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
in ignore -> {
|
||||||
|
if (!ident && !num)
|
||||||
|
sep = true
|
||||||
|
else
|
||||||
|
it.unGet()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
',' -> {
|
||||||
|
if (!ident && !num)
|
||||||
|
comma = true
|
||||||
|
else
|
||||||
|
it.unGet()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val oo = operators.firstOrNull { o -> o.symbol == c }
|
||||||
|
if (oo != null)
|
||||||
|
op = oo
|
||||||
|
else
|
||||||
|
it.unGet()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ident)
|
||||||
|
Token(Token.Type.IDENT, builder.toString())
|
||||||
|
else if (num)
|
||||||
|
Token(Token.Type.NUMBER, builder.toString())
|
||||||
|
else if (paren == '(')
|
||||||
|
Token(Token.Type.PAREN_OPEN)
|
||||||
|
else if (paren == ')')
|
||||||
|
Token(Token.Type.PAREN_CLOSE)
|
||||||
|
else if (op != null)
|
||||||
|
Token(Token.Type.OPERATOR, op = op)
|
||||||
|
else if (sep)
|
||||||
|
Token(Token.Type.SEPARATOR)
|
||||||
|
else if (comma)
|
||||||
|
Token(Token.Type.COMMA)
|
||||||
|
else
|
||||||
|
null
|
||||||
|
}
|
25
src/main/kotlin/blitz/str/Flatten.kt
Normal file
25
src/main/kotlin/blitz/str/Flatten.kt
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package blitz.str
|
||||||
|
|
||||||
|
fun Sequence<String>.flattenToString(): String {
|
||||||
|
val out = StringBuilder()
|
||||||
|
forEach {
|
||||||
|
out.append(it)
|
||||||
|
}
|
||||||
|
return out.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Iterable<String>.flattenToString(): String {
|
||||||
|
val out = StringBuilder()
|
||||||
|
forEach {
|
||||||
|
out.append(it)
|
||||||
|
}
|
||||||
|
return out.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Iterator<Char>.collectToString(): String {
|
||||||
|
val out = StringBuilder()
|
||||||
|
forEachRemaining {
|
||||||
|
out.append(it)
|
||||||
|
}
|
||||||
|
return out.toString()
|
||||||
|
}
|
Reference in New Issue
Block a user