文章灵感与参考来自于 用宏实现的极简协程库 中字符串解析的事例
parser 每消费一个字符时,由上次挂起处继续向后顺序执行,例如解析 GET /root/path HTTP/1.1 时,遇到第二个空格时不会走入第一个空格的后续分支
相比使用状态机进行解析,借助协程可以用更简单更顺序化的逻辑
fun main() {
val str = "GET /root/path HTTP/1.1"
val parser = SuspendParser()
for (char in str) {
parser.resume(char)
}
parser.end()
println(parser.result())
}
class SuspendParser {
private val method = LinkedList<Char>()
private val path = LinkedList<Char>()
private val version = LinkedList<Char>()
private var co: Continuation<Char> = Continuation(EmptyCoroutineContext) {
val block: suspend Char.() -> Unit = ::parse
block.startCoroutine(
receiver = it.getOrThrow(),
completion = Continuation(EmptyCoroutineContext) { println("all end") }
)
}
private suspend fun parse(first: Char) {
var char = first
while (char.isAlpha()) {
method += char
char = next()
}
if (char == ' ') {
char = next()
}
while (char.isAlpha() || char.isSlash()) {
path += char
char = next()
}
if (char == ' ') {
char = next()
}
while (char.isAlpha() || char.isSlash() || char.isNum() || char.isDot()) {
version += char
char = next()
}
}
private suspend fun next(): Char {
return suspendCoroutine { co = it }
}
fun resume(c: Char) {
co.resume(c)
}
fun end() {
co.resume('$') // make co all end by a nothing key word
}
fun result(): String {
val m = method.joinToString(separator = "")
val p = path.joinToString(separator = "")
val v = version.joinToString(separator = "")
return "method = $m path = $p version = $v"
}
}
fun Char.isNum(): Boolean = this in '0'..'9'
fun Char.isSlash(): Boolean = this == '/'
fun Char.isDot(): Boolean = this == '.'
fun Char.isAlpha(): Boolean = isAlphaLow() || isAlphaUp()
fun Char.isAlphaUp(): Boolean = this in 'A'..'Z'
fun Char.isAlphaLow(): Boolean = this in 'a'..'z'