expression parsing, sorted list, tree, other shit; bump to 0.10
This commit is contained in:
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
|
||||
}
|
Reference in New Issue
Block a user