expression parsing, sorted list, tree, other shit; bump to 0.10

This commit is contained in:
alexander.nutz
2024-03-29 16:27:00 +01:00
parent d8c4c5a90d
commit b8bd109182
21 changed files with 748 additions and 50 deletions

View File

@@ -4,7 +4,6 @@ import blitz.collections.inBounds
import blitz.str.ColoredChar
import blitz.str.MutMultiColoredMultiLineString
import blitz.str.MutMultiLineString
import blitz.str.MutString
import blitz.term.AnsiiMode
import blitz.term.Terminal
@@ -143,7 +142,7 @@ object Errors {
byCol.filter { it.isLongDesc }.forEach {
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 ++
}
@@ -153,41 +152,4 @@ object Errors {
Terminal.errln("================================================================================", config.styles[worst]!! + Terminal.STYLES.BOLD)
}
}
}
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)
}

View File

@@ -7,12 +7,4 @@ fun ByteBatchSequence.stringify(batch: Int = 64): Sequence<String> {
iter.nextBytes(batch).decodeToString()
else null
}
}
fun Sequence<String>.flatten(): String {
val out = StringBuilder()
forEach {
out.append(it)
}
return out.toString()
}

View File

@@ -0,0 +1,4 @@
package blitz.collections
fun <T, C: Comparable<C>> SortedArrayList(sorter: (T) -> C) =
SortedList(ArrayList(), sorter)

View File

@@ -7,4 +7,47 @@ fun <T, R> Iterator<T>.mapModifier(fn: (T) -> R): Iterator<R> =
override fun hasNext(): Boolean =
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
}

View File

@@ -0,0 +1,7 @@
package blitz.collections
fun <T> Collection<T>.nullIfEmpty(): Collection<T>? =
ifEmpty { null }
fun String.nullIfEmpty(): String? =
ifEmpty { null }

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

View File

@@ -4,6 +4,7 @@ interface IndexableSequence<T>: Sequence<T> {
operator fun get(index: Int): T
}
// TODO: rename to map
fun <T> IndexableSequence<T>.modifier(mod: (Sequence<T>) -> Sequence<T>) =
object : IndexableSequence<T> {
val other = mod(this@modifier)

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

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

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

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

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

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

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

View File

@@ -0,0 +1,7 @@
package blitz.parse
data class Operator(
val symbol: Char,
val precedence: Int = 0,
val leftAssociative: Boolean = true,
)

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

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

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

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