/tmp/duangsuse.sock
今天晚上,我还会完成文法布局的 Pattern 编写 可以认为,ParserKt 现在对 read-only pattern 和序列 validate 的支持已经比较完备了 对于文法布局的支持应该会像对 infix chain 的一样,不会有问题
不是特别顺利,对级联收尾的理解有偏差
之前的知识是,不能用异常。但现在我也看了 Pattern<IN, Tuple2<List<T>, Int>>,那个方案不可行
ParserKt 是 one-pass 的解析器,绝对没有 MarkReset 的机会,所以对布局解析器来说,
h1
h2
h3
h3
h1
这里面最后一个 h1 的位置,实际上调用栈最顶端的最后一个 h3 负责读取它,但如果读取消耗了,解析来的 h2 就又不知道往哪里关了。
(如果支持 mark/reset 的话,只要预取 indent 判等后决定是否消耗即可,其他情况都是输入序列错误)
所以说每次要收尾所有 > indent 的层次,然后才能实现这个解析器。
这一点也不说特别难,但 ParserKt 里不是很好解决,因为我们的设计是
而且它不仅仅要做到级联,还不能暴露出框架的流结构的这个 one-pass……
我希望不要把它做成一个 abstract class,简单一点,不能让它管理这个递归的 nesting tree 数据结构,要不然至少对用户会很麻烦。
现在好像是只能用异常了,因为它就是一个 Pattern,可以被任何其他 pattern 使用…… 要做到级联收尾必须这么做,而且这么做我们就可以用上 FoldPattern 的 API,更规范一些
如何 show 这又是另外一个问题,不过我现在的意见是,无法支持,因为布局的输出,只能存那么一点信息(没法多存布局的信息进去,顶多可能被扩展嵌套结构,但就不是我们的事了),的确没法弄好,Pattern 的使用是动态的。
之前的知识是,不能用异常。但现在我也看了 Pattern<IN, Tuple2<List<T>, Int>>,那个方案不可行
ParserKt 是 one-pass 的解析器,绝对没有 MarkReset 的机会,所以对布局解析器来说,
h1
h2
h3
h3
h1
这里面最后一个 h1 的位置,实际上调用栈最顶端的最后一个 h3 负责读取它,但如果读取消耗了,解析来的 h2 就又不知道往哪里关了。
(如果支持 mark/reset 的话,只要预取 indent 判等后决定是否消耗即可,其他情况都是输入序列错误)
所以说每次要收尾所有 > indent 的层次,然后才能实现这个解析器。
这一点也不说特别难,但 ParserKt 里不是很好解决,因为我们的设计是
function where 像这个这个 where 是我们放这个 pattern 的地方而且它不仅仅要做到级联,还不能暴露出框架的流结构的这个 one-pass……
我希望不要把它做成一个 abstract class,简单一点,不能让它管理这个递归的 nesting tree 数据结构,要不然至少对用户会很麻烦。
现在好像是只能用异常了,因为它就是一个 Pattern,可以被任何其他 pattern 使用…… 要做到级联收尾必须这么做,而且这么做我们就可以用上 FoldPattern 的 API,更规范一些
如何 show 这又是另外一个问题,不过我现在的意见是,无法支持,因为布局的输出,只能存那么一点信息(没法多存布局的信息进去,顶多可能被扩展嵌套结构,但就不是我们的事了),的确没法弄好,Pattern 的使用是动态的。
/tmp/duangsuse.sock
不是特别顺利,对级联收尾的理解有偏差 之前的知识是,不能用异常。但现在我也看了 Pattern<IN, Tuple2<List<T>, Int>>,那个方案不可行 ParserKt 是 one-pass 的解析器,绝对没有 MarkReset 的机会,所以对布局解析器来说, h1 h2 h3 h3 h1 这里面最后一个 h1 的位置,实际上调用栈最顶端的最后一个 h3 负责读取它,但如果读取消耗了,解析来的 h2 就又不知道往哪里关了。 (如果支持 mark/reset 的话,只要预取…
大意就是,除了异常以外没有办法在两层
除非使用 abstract class 硬性建模何时开启新 pattern,否则这个信息没法通过返回值传递出去,即便能手写起来复杂性也不可接受。
LayoutPattern 注定是递归结构,所以 caller 必须去 handle 它的 closing indent level 返回值,对应用来说那必须手写模板代码,我觉得比起那样还是异常靠谱一些(虽然不可能有泛型,要强制转换了……)
这个和 infix pattern 不一样,它毕竟得是一个 unstable feature……
LayoutPattern.read 之间传递控制权,因为使用处是不确定的除非使用 abstract class 硬性建模何时开启新 pattern,否则这个信息没法通过返回值传递出去,即便能手写起来复杂性也不可接受。
LayoutPattern 注定是递归结构,所以 caller 必须去 handle 它的 closing indent level 返回值,对应用来说那必须手写模板代码,我觉得比起那样还是异常靠谱一些(虽然不可能有泛型,要强制转换了……)
这个和 infix pattern 不一样,它毕竟得是一个 unstable feature……
现在这么实现最主要的问题是得有一个 level 0,不能让异常漏处 Pattern 模型的范畴……
好像不必了,因为每层都是
好像不必了,因为每层都是
if (baseLevel > indent) return reducer.finish() 的嘛
/tmp/duangsuse.sock
大意就是,除了异常以外没有办法在两层 LayoutPattern.read 之间传递控制权,因为使用处是不确定的 除非使用 abstract class 硬性建模何时开启新 pattern,否则这个信息没法通过返回值传递出去,即便能手写起来复杂性也不可接受。 LayoutPattern 注定是递归结构,所以 caller 必须去 handle 它的 closing indent level 返回值,对应用来说那必须手写模板代码,我觉得比起那样还是异常靠谱一些(虽然不可能有泛型,要强制转换了……) 这个和…
最终我决定还是采取内部递归数据结构的方法来解决,因为异常会打断所有没有特别支持 LayoutPattern read 的子 Pattern read,不能把已经解析到的数据收集回来
Forwarded from Deleted Account
比如说解析一个 2D 布局的列表:
具体的架构 很难说明白
但是异常一抛,调用 Layout read 的子解析器的 collect 过程就会被打断,最后 abnormal terminate 什么都不能返回
[] where
abcs
123s
[] where
winnie
donkey 具体的架构 很难说明白
但是异常一抛,调用 Layout read 的子解析器的 collect 过程就会被打断,最后 abnormal terminate 什么都不能返回
Forwarded from Deleted Account
我知道,现在还是 one-pass 的,我决定还是用另外一种不依赖外部递归,直接把 nesting tree 和 visitor 做到框架里支持的另一种方法
Forwarded from Deleted Account
直接在内部做好递归解析的问题,不依赖外部了,然后 layout 的开启就分
item@(function somefn) tail@(where)item tail children 三个来保证,直接在框架里递归解析,然后收集结果也是在框架层做、用户用 Visitor 去翻译到目标 AST
...children
Forwarded from Deleted Account
既然 iseki 都被大佬喷了,那我就在这里直播
首先说说我们的目标是求得 x=1; y=1; z=1,使用的方法是 unification —— 利用相等关系,让两个 Value(Val/Var) 在一个 State 里实际上划上等号
而一个 Var 呢,我们可以认为是一个 "Symbol",它的 equals 实现为全等 (x === y),就可以了。
microKanren 有 six primitives: State, Variable, fresh(introduce), Eq, Either, Both
前三个是最基础的 unification 需求,后三个是关系式编程(relational programming) 里最重要的关系
毕竟是在 Telegram 直接写了,我就不说 Either 和 Both 这种高级操作了(它们能 satisfyIn 的 state 都可能不只一个)
首先我们依赖的需要知道啥东西能够 unify 又怎么去 unify,或者说我们得对 Value 的子类型实现
我们知道相等关系是有对称性(symmetric) 的
y=1; x=y=z 的 unify 过程 #PLT首先说说我们的目标是求得 x=1; y=1; z=1,使用的方法是 unification —— 利用相等关系,让两个 Value(Val/Var) 在一个 State 里实际上划上等号
而一个 Var 呢,我们可以认为是一个 "Symbol",它的 equals 实现为全等 (x === y),就可以了。
microKanren 有 six primitives: State, Variable, fresh(introduce), Eq, Either, Both
前三个是最基础的 unification 需求,后三个是关系式编程(relational programming) 里最重要的关系
毕竟是在 Telegram 直接写了,我就不说 Either 和 Both 这种高级操作了(它们能 satisfyIn 的 state 都可能不只一个)
fun main(vararg args: String) {
val xyz = State()
xyz.intro("x", "y", "z") {
val (x, y, z) = it
y.eq(Val(1))
x.eq(y); y.eq(z)
}
println(xyz) //State{x: 1, y: 1, z: 1}
}
在 Kotlin 里面以 EDSL 的风格 去完成……首先我们依赖的需要知道啥东西能够 unify 又怎么去 unify,或者说我们得对 Value 的子类型实现
unifyIn(state, other) 操作我们知道相等关系是有对称性(symmetric) 的
(x=y, y=x),所以“解构”式的操作只需要有一边定义就 OK 了,然后因为 Kotlin 是强类型的可以加入类型检查。typealias Consumer<T> = (T) -> Unit艹我写不下去了,太长了
typealias MonoTriple<T> = Triple<T, T, T>
typealias MonoPair<T> = Pair<T, T>
typealias StateMap = MutableMap<Value.Var, Any?>
typealias Variable = Value.Var
class State(private val map: StateMap = mutableMapOf()): StateMap by map {
fun intro(x1: String, x2: Sting, op: Consumer<MonoPair<Variable>>) {
val xs = bind(x1, x2)
op(Pair(xs[0], xs[1]))
}
fun intro(x1: String, x2: String, x3: String, op: Consumer<MonoTriple<Variable>>) {
val xs = bind(x1, x2, x3)
op(Triple(xs[0], xs[1], xs[2]))
}
fun bind(vararg names: String): List<Value.Var> {
val variables = names.map(::Variable)
for (variables
return variables
}
override fun toString() = "State$map"
}
interface Unifible<T> {
fun unifyIn(state: State, other: Unifible<T>)
}
sealed class Value(open val value: Any?) {
data class Val(value: Any?): Value(value) {
override fun toString() = "Val($value)"
}
data class Var(val name: String, val binding: State) {
override fun equals(other: Any?) = this === other
override fun hashCode() = super.hashCode()
override fun toString() = "Var($name)"
}
}
Forwarded from Deleted Account
具体怎么实现 unify 以及 unify 的对称性的代码复用留给 iseki 大佬自己研究去吧,我重写我的 ParserKt 去了……