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

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