Literate Kotlin 可以教小白了解自己的实现,但写起来很冗长,emmm……
回看那篇
回看那篇
Share
看完这段 Kotlin 代码后我哭了
🐕 duangsuse’s shared files(e.g. productive software projects, documents)
/tmp/duangsuse.sock
Calc.kt
提问!如何编写一个十进制解析器?
我们先来看看上一个版本怎么做:
不知道,好像不得不引入 contextual 解析器…… 其实也不一定需要…… 但是最好要有……
我们先来看看上一个版本怎么做:
class DecimalRead(mzero: Int): Monoid<Int>(mzero, { i -> this*10 + i })
object HexRead: Monoid<Int>(0, { i -> this*16 + i })
private val oneNine = element('1'..'9') then { it-'0' }
private val zero = item('0') const 0
private val digit = or(oneNine, zero)
private fun digitsCtx(zero: Int, optional: Boolean): CParser<Int>
= repeat(DecimalRead(zero), digit, if (optional) MAYBE else SOME)
val digits = digitsCtx(0, optional = false)
val digitsNoLeadingZero: CParser<Int> = or(digit.single(), oneNine contextual { digitsCtx(it, optional = true) })
private val hexDigit: CParser<Int> = or(digit, element('A'..'F') then { it-'A'+10 }, element('a'..'f') then { it-'a'+10 })
那么, 如何不允许前置 '0'?不知道,好像不得不引入 contextual 解析器…… 其实也不一定需要…… 但是最好要有……
class DecimalRead(zero: Int): Monoid<Int>(zero, { this*10 + it })
object Digit: Convert<Char, Char, Int>(RangeElement('0'..'9'), {it-'0'}, {'0'+it})
object Decimal: Repeat<Char, Int, Int>(DecimalRead(0), Digit) {
override fun show(write: Consumer<Char>, item: Int?) { item?.toString()?.forEach(write) }
}>>> val s=CharInput("",InputStreamFeed(System.`in`))
>>> s.takeWhile { it in '0'..'9' || it == '\n' }.toList()
12313
432
res17: kotlin.collections.List<kotlin.Char> = [
, 1, 2, 3, 1, 3,
, 4, 3, 2,
]
修好了 InputStreamFeedParser.kt
17.7 KB
class DecimalRead(zero: Int): Monoid<Int>(zero, { this*10 + it })
object Digit: Convert<Char, Char, Int>(RangeElement('0'..'9'), {it-'0'}, {'0'+it})
object Decimal: Repeat<Char, Int, Int>(DecimalRead(0), Digit) {
override fun show(write: Consumer<Char>, item: Int?) { item?.toString()?.forEach(write) }
}
总之就是可以用,但怎么用呢…… 明天再看 #Kotlin #codekotlinc Parser.kt
kotlinc -cp . Calc.kt
kotlinc -cp .
>>> Kalc.read(CharInput("",SliceFeed(slice("12* 31")) )) 结果又找出 TrieParser 里面一个 bug,那么好一个 pattern。
/tmp/duangsuse.sock
Calc.kt
这次算是告一段落了,我最讨厌完全依赖编译器的检查的那种感觉,每次都战战兢兢的害怕不通过。
Parser.kt 现在有最大的问题就是 Input,不支持我之前说的镇静解析策略的 ErrorHandler,反而去兼容了 MarkReset —— 其实 MarkReset 完全不应该首要兼容的,它只会解决一些非常不容易出现的问题,还影响性能
Input 和 Feed 的层次也容易让人糊涂,Input 的魔法一点都不好玩,只是让人开心那么一会而已,很快实践时就会造成大问题,过度设计。
最重要的是 object: Pat 和 val name = Pat() 这种区别不应该再支持了,再支持势必要出大问题
此外是 SOR 模式不规范,不容易让人记住,我给起了个简化名 "SURD",Seq, Until, Repeat, Decide
然后对 atom patterns 进行规范,只有 deferred, satisfy, item, elem
扩展 pattern 有 TriePattern, InfixPattern, SurroundBy, JoinBy
InfixPattern 对 join operator 的抽象也有很严重的问题,之前是 enum class 都容易声明的,现在变成什么 from/to 啥鬼样子,不男不女的还造成了很严重的相等性判断问题,简直不是人写的
Input 和 Feed 的层次也容易让人糊涂,Input 的魔法一点都不好玩,只是让人开心那么一会而已,很快实践时就会造成大问题,过度设计。
最重要的是 object: Pat 和 val name = Pat() 这种区别不应该再支持了,再支持势必要出大问题
此外是 SOR 模式不规范,不容易让人记住,我给起了个简化名 "SURD",Seq, Until, Repeat, Decide
然后对 atom patterns 进行规范,只有 deferred, satisfy, item, elem
扩展 pattern 有 TriePattern, InfixPattern, SurroundBy, JoinBy
InfixPattern 对 join operator 的抽象也有很严重的问题,之前是 enum class 都容易声明的,现在变成什么 from/to 啥鬼样子,不男不女的还造成了很严重的相等性判断问题,简直不是人写的
/tmp/duangsuse.sock
看完这样的屑B代码,我整个灵魂都升华了,原来个计算器都可以这么麻烦,服气。
interface Feed<out T> {
val peek: T; fun consume(): T
class End: Exception("no more")
}
interface ErrorHandler<IN> {
fun handleError(input: Input<IN>)
}
interface Input<IN>: Feed<IN>, ErrorHandler<IN>
interface SourceLocated {
val sourceLoc: SourceLocation
}
data class SourceLocation(val file: String, var line: Cnt, var column: Cnt, var position: Cnt) {
constructor(file: String): this(file, 1, 1, 0)
val tag get() = "$file:$line:$column"
override fun toString() = "$tag #$position"
}
interface CharInput: Input<Char>, SourceLocated
abstract class StreamFeed<T, STREAM>(private val stream: STREAM): Feed<T> {
protected abstract fun toIterator(stream: STREAM): Iterator<T>
protected var nextOne: T
override fun toString() = "Stream(${nextOne.rawString()}...${stream})"
}
class IteratorFeed<T>(private val iterator: Iterator<T>): StreamFeed<T, Iterator<T>>()
open class InputStreamFeed(private val reader: InputStreamReader): StreamFeed<Char, InputStramReader>() {
constructor(stream_in: InputStream, charset: Charset = StandardCharsets.UTF_8): this(InputStreamReader(stream_in, charset))
open val endTransaction = '\u0004'
}
//Side B
interface Sized {
val size: Cnt
}
val Sized.lastIndex get() = size.dec()
val Sized.indices get() = 0..lastIndex
val Sized.isEmpty get() = size == 0
val Sized.isNotEmpty get() = !isEmpty
interface Slice<out T>: Sized {
operator fun get(index: Idx): T
}
abstract class AnyBy(val obj: Any) {
override fun equals(other: Any?) =
if (other !is AnyBy) obj == other
else obj == other.obj
override fun hashCode() = obj.hashCode()
override fun toString() = obj.toString()
}
fun <T> slice(ary: Array<T>): Slice<T> = object: Slice<T>, AnyBy(ary) {
override val size get() = ary.size
override fun get(index: Idx) = ary[index]
}
fun <T> slice(list: List<T>): Slice<T> = object: Slice<T>, AnyBy(list) {
override val size get() = list.size
override fun get(index: Idx) = list[index]
}
fun slice(str: CharSequence): Slice<Char> = object: Slice<Char>, AnyBy(str) {
override val size get() = str.length
override fun get(index: Idx) = str[index]
}
class SliceFeed<T>(private val slice: Slice<T>): Feed<T>, StackMarkReset<Idx>() {
override var saved = 0
override val peek get() = try { slice[saved] }
catch (_: IndexOutOfBoundsException) { throw Feed.End() }
override fun consume() = try { slice[saved++] }
catch (_: IndexOutOfBoundsException) { throw Feed.End() }
override fun toString() = "$showPeek@$saved...$slice"
private val showPeek get() = runCatching{peek}.getOrNull()?.rawString() ?: "?>${slice.lastIndex}"
}fun <T> showRawString(value: T) = value.rawString()
fun <T> T.rawString() = toString().prefixTranslate(KOTLIN_ESCAPE, "\\").let {
if (it.length == 1) it.surround("'", "'")
else it.surround("\"", "\"")
}
val KOTLIN_ESCAPE = mapOf( // \"\'\t\b\n\r\$\\
'"' to '"', '\'' to '\'',
'\t' to 't', '\b' to 'b',
'\n' to 'n', '\r' to 'r',
'$' to '$', '\\' to '\\'
)
fun String.surround(prefix: String, suffix: String): String = prefix+this+suffix
fun String.prefixTranslate(map: Map<Char, Char>, prefix: String): String = fold (StringBuilder()) { sb, c ->
map[c]?.let { sb.append(prefix).append(it) } ?: sb.append(c) }.toString()
fun impossible(): Nothing = error("impossible")
fun <T> MutableList<T>.removeLast() = removeAt(lastIndex)
fun <T, R> Iterable<T>.mapToSet(transform: (T) -> R) = mapTo(mutableSetOf(), transform)
fun <K, V> Map<K, V>.reversedMap(): Map<V, K> = entries.kvMap { kv -> kv.value to kv.key }
fun <A, B> Iterable<A>.toMap(transform: (A) -> Pair<A, B>): Map<A, B> {
val map: MutableMap<A, B> = mutableMapOf()
for (item in this) {
val (k, v) = transform(item)
map[k] = v
}
return map
}
fun <K, V, K1, V1> Iterable<Map.Entry<K, V>>.kvMap(transform: (Map.Entry<K, V>) -> Pair<K1, V1>): Map<K1, V1> {
val map: MutableMap<K1, V1> = mutableMapOf()
for (kv in this) {
val (k1, v1) = transform(kv)
map[k1] = v1
}
return map
}
fun <T> T.rawString() = toString().prefixTranslate(KOTLIN_ESCAPE, "\\").let {
if (it.length == 1) it.surround("'", "'")
else it.surround("\"", "\"")
}
val KOTLIN_ESCAPE = mapOf( // \"\'\t\b\n\r\$\\
'"' to '"', '\'' to '\'',
'\t' to 't', '\b' to 'b',
'\n' to 'n', '\r' to 'r',
'$' to '$', '\\' to '\\'
)
fun String.surround(prefix: String, suffix: String): String = prefix+this+suffix
fun String.prefixTranslate(map: Map<Char, Char>, prefix: String): String = fold (StringBuilder()) { sb, c ->
map[c]?.let { sb.append(prefix).append(it) } ?: sb.append(c) }.toString()
fun impossible(): Nothing = error("impossible")
fun <T> MutableList<T>.removeLast() = removeAt(lastIndex)
fun <T, R> Iterable<T>.mapToSet(transform: (T) -> R) = mapTo(mutableSetOf(), transform)
fun <K, V> Map<K, V>.reversedMap(): Map<V, K> = entries.kvMap { kv -> kv.value to kv.key }
fun <A, B> Iterable<A>.toMap(transform: (A) -> Pair<A, B>): Map<A, B> {
val map: MutableMap<A, B> = mutableMapOf()
for (item in this) {
val (k, v) = transform(item)
map[k] = v
}
return map
}
fun <K, V, K1, V1> Iterable<Map.Entry<K, V>>.kvMap(transform: (Map.Entry<K, V>) -> Pair<K1, V1>): Map<K1, V1> {
val map: MutableMap<K1, V1> = mutableMapOf()
for (kv in this) {
val (k1, v1) = transform(kv)
map[k1] = v1
}
return map
}
fun <T> Feed<T>.consumeIf(predicate: Predicate<T>): T? =
peek.takeIf(predicate)?.let { runCatching(::consume).getOrNull() }
fun <T> Feed<T>.takeWhile(predicate: Predicate<T>): Sequence<T> = sequence {
while (true) {
val item = consumeIf(predicate)
yield(item ?: break)
}
}
fun <T> Feed<T>.take(n: Cnt): Sequence<T> {
var count = 0; return takeWhile { count++ != n }
}
open class CharInput(feed: Feed<Char>, file: String, protected val eol: Char): Input<Char>(feed), SourceLocated
open class CharInputCRLF(feed: Feed<Char>, file: String, eol: Char): CharInput(feed, file, eol)
peek.takeIf(predicate)?.let { runCatching(::consume).getOrNull() }
fun <T> Feed<T>.takeWhile(predicate: Predicate<T>): Sequence<T> = sequence {
while (true) {
val item = consumeIf(predicate)
yield(item ?: break)
}
}
fun <T> Feed<T>.take(n: Cnt): Sequence<T> {
var count = 0; return takeWhile { count++ != n }
}
open class CharInput(feed: Feed<Char>, file: String, protected val eol: Char): Input<Char>(feed), SourceLocated
open class CharInputCRLF(feed: Feed<Char>, file: String, eol: Char): CharInput(feed, file, eol)
abstract class Tuple<E>(override val size: Cnt): Sized {
protected abstract val items: Array<E>
operator fun get(index: Idx) = items[index]
operator fun set(index: Idx, value: E) { items[index] = value }
fun toArray(): Array<E> = items
protected class Index<T>(private val idx: Idx) {
operator fun getValue(self: Tuple<out T>, _p: KProperty<*>): T = self[idx]
operator fun setValue(self: Tuple<in T>, _p: KProperty<*>, value: T) { self[idx] = value }
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
return if (other !is Tuple<*>) false
else (size == other.size) && items.contentEquals(other.items)
}
override fun hashCode(): Int = items.contentHashCode()
override fun toString(): String = "(${items.joinToString(", ")})"
}
data class Tuple2<A, B>(var first: A, var second: B)
protected abstract val items: Array<E>
operator fun get(index: Idx) = items[index]
operator fun set(index: Idx, value: E) { items[index] = value }
fun toArray(): Array<E> = items
protected class Index<T>(private val idx: Idx) {
operator fun getValue(self: Tuple<out T>, _p: KProperty<*>): T = self[idx]
operator fun setValue(self: Tuple<in T>, _p: KProperty<*>, value: T) { self[idx] = value }
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
return if (other !is Tuple<*>) false
else (size == other.size) && items.contentEquals(other.items)
}
override fun hashCode(): Int = items.contentHashCode()
override fun toString(): String = "(${items.joinToString(", ")})"
}
data class Tuple2<A, B>(var first: A, var second: B)
typealias Producer<T> = () -> T
typealias Consumer<T> = (T) -> Unit
typealias Predicate<T> = (T) -> Boolean
typealias Fold<T, R> = FoldOp<T, *, R>
interface Reducer<in T, out R> {
fun accept(item: T)
fun finish(): R
}
abstract class FoldOp<in A, B, out C> {
protected abstract val initial: B
protected abstract fun join(base: B, item: A): B
protected abstract fun convert(base: B): C
fun reducer() = object: Reducer<A, C> {
private var base: B = initial
override fun accept(item: A) { base = join(base, item) }
override fun finish(): C = convert(base)
}
}
////////
abstract class Effect<A, B, C>: FoldOp<A, B, C>() {
protected abstract fun accept(base: B, item: A)
final override fun join(base: B, item: A): B = base.also { accept(base, item) }
}
abstract class Monoid<T>(mzero: T, private val mplus: T.(T) -> T): FoldOp<T, T, T>() {
override val initial: T = mzero
final override fun join(base: T, item: T): T = base.mplus(item)
final override fun convert(base: T): T = base
}
typealias AsList<T> = Effect<T, MutableList<T>, List<T>>
fun <T> asList() = object: AsList<T>() {
override val initial: MutableList<T> get() = mutableListOf()
override fun accept(base: MutableList<T>, item: T) { base.add(item) }
override fun convert(base: MutableList<T>): List<T> = base
}
fun buildStr() = object: FoldOp<Char, StringBuilder, String>() {
override val initial get() = StringBuilder()
override fun join(base: StringBuilder, item: Char) = base.append(item)
override fun convert(base: StringBuilder) = base.toString()
}
typealias Consumer<T> = (T) -> Unit
typealias Predicate<T> = (T) -> Boolean
typealias Fold<T, R> = FoldOp<T, *, R>
interface Reducer<in T, out R> {
fun accept(item: T)
fun finish(): R
}
abstract class FoldOp<in A, B, out C> {
protected abstract val initial: B
protected abstract fun join(base: B, item: A): B
protected abstract fun convert(base: B): C
fun reducer() = object: Reducer<A, C> {
private var base: B = initial
override fun accept(item: A) { base = join(base, item) }
override fun finish(): C = convert(base)
}
}
////////
abstract class Effect<A, B, C>: FoldOp<A, B, C>() {
protected abstract fun accept(base: B, item: A)
final override fun join(base: B, item: A): B = base.also { accept(base, item) }
}
abstract class Monoid<T>(mzero: T, private val mplus: T.(T) -> T): FoldOp<T, T, T>() {
override val initial: T = mzero
final override fun join(base: T, item: T): T = base.mplus(item)
final override fun convert(base: T): T = base
}
typealias AsList<T> = Effect<T, MutableList<T>, List<T>>
fun <T> asList() = object: AsList<T>() {
override val initial: MutableList<T> get() = mutableListOf()
override fun accept(base: MutableList<T>, item: T) { base.add(item) }
override fun convert(base: MutableList<T>): List<T> = base
}
fun buildStr() = object: FoldOp<Char, StringBuilder, String>() {
override val initial get() = StringBuilder()
override fun join(base: StringBuilder, item: Char) = base.append(item)
override fun convert(base: StringBuilder) = base.toString()
}
interface Pattern<T, R> {
fun read(s: Input<T>): R?
fun show(write: Consumer<T>, item: R?)
}
interface PositivePattern<T, R>: Pattern<T, R> {
override fun read(s: Input<T>): R
}
inline val notParsed: Nothing? get() = null
fun <T, R> Pattern<T, R>.toDefault(defaultValue: R) = object: PositivePattern<T, R> {
override fun read(s: Input<T>): R = this@toDefault.read(s) ?: defaultValue
override fun show(write: Consumer<T>, item: R?) { this@toDefault.show(write, item!!) }
}
fun <T, R> Pattern<T, R>.tryRead(s: Input<T>): R? {
fun read(): R? = try { this.read(s) }
catch (_: Feed.End) { notParsed }
val parsed: R?
s.mark(); parsed = read()
s.run { if (parsed == notParsed) reset() else unmark() }
return parsed
}
/tmp/duangsuse.sock
Parser.kt 现在有最大的问题就是 Input,不支持我之前说的镇静解析策略的 ErrorHandler,反而去兼容了 MarkReset —— 其实 MarkReset 完全不应该首要兼容的,它只会解决一些非常不容易出现的问题,还影响性能 Input 和 Feed 的层次也容易让人糊涂,Input 的魔法一点都不好玩,只是让人开心那么一会而已,很快实践时就会造成大问题,过度设计。 最重要的是 object: Pat 和 val name = Pat() 这种区别不应该再支持了,再支持势必要出大问题…
rt 此外是 SOR 模式不规范,不容易让人记住,我给起了个简化名 "SURD",Seq, Until, Repeat, Decide
然后对 atom patterns 进行规范,只有 deferred, satisfy, item, elem
扩展 pattern 有 TriePattern, InfixPattern, SurroundBy, JoinBy
InfixPattern 对 join operator 的抽象也有很严重的问题,之前是 enum class 都容易声明的,现在变成什么 from/to 啥鬼样子,不男不女的还造成了很严重的相等性判断问题,简直不是人写的
open class Deferred<T, R>(private val pat: Producer<Pattern<T, R>>): Pattern<T, R>
abstract class SatisfyTest<T>: Pattern<T, T> {
abstract fun test(item: T): Boolean
}
open class Satisfy<T>(val predicate: Predicate<T>): SatisfyTest<T>()
class AnyItem<T>: Satisfy<T>({ true })
open class Item<T>(val x: T): SatisfyTest<T>()
open class Element<T>(vararg val xs: T): AbstractSatisfy<T>() {
override fun test(item: T) = item in xs
override fun toString() = "(${xs.joinToString("|", transform = ::showRawString)})"
}
open class RangeElement(val xs: CharRange): AbstractSatisfy<Char>() {
override fun test(item: Char) = item in xs
override fun toString() = "$xs"
}
open class Seq<IN, T, TUP: Tuple<T>>(val allocator: Producer<TUP>, vararg val items: Pattern<IN, T>): Pattern<IN, TUP> {
override fun toString() = items.joinToString(" ").surround("(", ")")
}
abstract class Repeat<IN, T, R>(val fold: Fold<T, R>, val item: Pattern<IN, T>): Pattern<IN, R> {
open val countBound: Predicate<Cnt> = {true}
override fun toString() = "{$item}"
}
abstract class Decide<T, R>(vararg val cases: Pattern<T, R>): Pattern<T, R> {
override fun toString() = cases.joinToString("|").surround("(", ")")
}
open class Convert<T, R, R1>(val item: Pattern<T, R>, val from: (R) -> R1, val to: (R1) -> R): Pattern<T, R1>
override fun toString() = item.toString()
}
open class SurroundBy<T, R>(val lr: Pair<T, T>, val item: Pattern<T, R>): Pattern<T, R>
class TriePattern<K, V>: Trie<K, V>(), Pattern<K, V>
abstract class InfixPattern<IN, ATOM>(val atom: Pattern<IN, ATOM>, val op: Pattern<IN, Join<ATOM>>): Pattern<IN, ATOM> {
abstract fun onError(base: ATOM, op1: Join<ATOM>): Nothing
}
然后对 atom patterns 进行规范,只有 deferred, satisfy, item, elem
扩展 pattern 有 TriePattern, InfixPattern, SurroundBy, JoinBy
InfixPattern 对 join operator 的抽象也有很严重的问题,之前是 enum class 都容易声明的,现在变成什么 from/to 啥鬼样子,不男不女的还造成了很严重的相等性判断问题,简直不是人写的
open class Deferred<T, R>(private val pat: Producer<Pattern<T, R>>): Pattern<T, R>
abstract class SatisfyTest<T>: Pattern<T, T> {
abstract fun test(item: T): Boolean
}
open class Satisfy<T>(val predicate: Predicate<T>): SatisfyTest<T>()
class AnyItem<T>: Satisfy<T>({ true })
open class Item<T>(val x: T): SatisfyTest<T>()
open class Element<T>(vararg val xs: T): AbstractSatisfy<T>() {
override fun test(item: T) = item in xs
override fun toString() = "(${xs.joinToString("|", transform = ::showRawString)})"
}
open class RangeElement(val xs: CharRange): AbstractSatisfy<Char>() {
override fun test(item: Char) = item in xs
override fun toString() = "$xs"
}
open class Seq<IN, T, TUP: Tuple<T>>(val allocator: Producer<TUP>, vararg val items: Pattern<IN, T>): Pattern<IN, TUP> {
override fun toString() = items.joinToString(" ").surround("(", ")")
}
abstract class Repeat<IN, T, R>(val fold: Fold<T, R>, val item: Pattern<IN, T>): Pattern<IN, R> {
open val countBound: Predicate<Cnt> = {true}
override fun toString() = "{$item}"
}
abstract class Decide<T, R>(vararg val cases: Pattern<T, R>): Pattern<T, R> {
override fun toString() = cases.joinToString("|").surround("(", ")")
}
open class Convert<T, R, R1>(val item: Pattern<T, R>, val from: (R) -> R1, val to: (R1) -> R): Pattern<T, R1>
override fun toString() = item.toString()
}
open class SurroundBy<T, R>(val lr: Pair<T, T>, val item: Pattern<T, R>): Pattern<T, R>
class TriePattern<K, V>: Trie<K, V>(), Pattern<K, V>
abstract class InfixPattern<IN, ATOM>(val atom: Pattern<IN, ATOM>, val op: Pattern<IN, Join<ATOM>>): Pattern<IN, ATOM> {
abstract fun onError(base: ATOM, op1: Join<ATOM>): Nothing
}
deferred(pat)
SatisyTest
satisfy
anyItem, item(value)
element(vararg xs)
range(CharRange)
Seq(data, items)
Until(fold, item)
Repeat(fold, item)
Decide(cases)
Convert(item, from, to)
Contextual(head, body)
SurroundBy(lr, item)
JoinBy(seprator, item)
TriePattern()
InfixPattern(atom, op)
SatisyTest
satisfy
anyItem, item(value)
element(vararg xs)
range(CharRange)
Seq(data, items)
Until(fold, item)
Repeat(fold, item)
Decide(cases)
Convert(item, from, to)
Contextual(head, body)
SurroundBy(lr, item)
JoinBy(seprator, item)
TriePattern()
InfixPattern(atom, op)