/tmp/duangsuse.sock
23 subscribers
303 photos
3 videos
92 files
337 links
从 duangsuse::Echo (@dsuse) 跟进出来的分支,将在作者恢复原帐号访问的时候合并删除。
Download Telegram
其实 ParserKt 的 rebuild 有时候也会成为累赘…… 比如说,在支持 仅仅是一种表达形式的值 的时候…… 几乎毫无意义,还导致了各种 mergeXXX 的乱入

ParserKt 讲究的的确是“无遗漏(完全解析)”、“一遍过(one-pass)”,但随着编写的解析器开始倾向实际工程,完全不损失信息逐渐变得不可能,于是才出现了开始的 Convert (no convert back) 以及现在的 mergeConstantJoin 和 mergeFirst, mergeSecond, flatten, toConstant 这类指定默认值或者转化存储法的修饰方法……

但现在 Decide 这样的解析器,也是没办法再转化了,不得不写 mergeFirst 来合并它的 caseNo…… 即便我根本不打算利用 rebuild,

为了兼容更多更复杂的文法,Contextual、Piped 之类的解析器的使用也开始频繁,没办法完美地解决从不完整的数据 rebuild 的问题,但仍希望更多解决方案能在将来被加入。
/tmp/duangsuse.sock
看了这篇,压死骆驼的最后一根稻草,我觉得那些不能落地的,都不算是做得实事。 中文编程曾经是笑话,我将让它不再是笑话。
有了 ParserKt 的帮助,绝句便可以轻松地实现各种复杂的文法(诸如动态定义中缀操作符、缩进块、中文数字),而且不需要几行代码、与 Kotlin 程序设计语言无缝交接。
又写了个 JSON 解析器出来
/tmp/duangsuse.sock
又写了个 JSON 解析器出来
其实如果说 ParserKt 设计绝句解析器的可能性,已经完成了。
汉字数值读写不存在问题,绝句里实际上不存在需要 lookahead 才能够判断的注释,都是单字符的 🙃

ParserKt 实际上已经能用了,SURDIES, FoldPattern, SingleFeed, CCDP(Convert, Contextual, Deferred, Piped), SJIT(SurroundBy, JoinBy, InfixPattern, TriePattern), NumUnitPattern, LayoutPattern,这些都是经历实际测试的 class 了,它们能够解决对各种序列结构的读取和显示。

现在是时候从 duangsuse 的 Share 那里分家出来了。严谨化 sample、建立独立的文档、JitPack 打包,把 ParserKt 包装出来。

https://github.com/duangsuse-valid-projects/Share/blob/2c35e429d9923eafe6d2aa9c1db07c3df6dc7e9a/Others/kt_misc/pkt_9/Parser.kt
8 天前 ParserKt 的第九次重构开始,从 449 行增长到今天的 1, 425 行

https://github.com/duangsuse-valid-projects/Share/blob/2c35e429d9923eafe6d2aa9c1db07c3df6dc7e9a/Others/kt_misc/pkt_7/Parser.kt
18 天前 ParserKt 的第七次重构起始,这个寒假里最费事的玩意……

希望它能好好的“报效”我为它花的时间,保佑以后都不用写各种奇奇怪怪的解析程序/文法定义了。

ParserKt 是我写出复用性、灵活性、健壮性最好的解析器框架了,但不得不说,如果没有 Kotlin 的类型推导它就是一堆废话,希望大家不要忘了幕后的人们啊。
#PL #Project #Algorithm
#GitHub #Project duangsuse 的 Share 已经有 300 commits 了!
那是不比较代码本身的,然后我们再对比一下两者的定义:

== 加减乘除运算符 ==
val ops = KeywordPattern<InfixOp<Number>>().apply {
listOf("*", "乘").forEach { register(it infixl 0 join fn(Int::times)) }
listOf("/", "除以").forEach { register(it infixl 0 join fn(Int::div)) }
register("除" infixl 0 join flip(fn(Int::div)))
register("分" infixl 0 join { a: Number, b: Number-> (a.toDouble() / b.toDouble()) as Number })
listOf("+", "加") .forEach { register(it infixl 1 join fn(Int::plus)) }
listOf("-", "减") .forEach { register(it infixl 1 join fn(Int::minus)) }
}

ParserKt 费了 7 行定义完了所有出现的运算符,并且包含它们的优先级信息。

const Plus = createToken({
name: "Plus",
pattern: /\+/,
categories: AdditionOperator
}) //...

SAP/chevrotain 单单定义加减乘除长啥样,就用掉 24 行代码
并且,它还把加减法都归类为 AdditionalOperator…… 显然对数学运算符,有没有对称性会比这种优先级上的工作有意义。
const AdditionOperator = createToken({
name: "AdditionOperator",
pattern: Lexer.NA
})

至于左递归 additionExpression, multiplicationExpression 就没啥可说了,毕竟属于解析器框架提供接口上的区别,这些定义用掉 18 行。

再说两个细节,数字读取和括号表达式。

$.RULE("parenthesisExpression", () => {
$.CONSUME(LParen)
$.SUBRULE($.expression)
$.CONSUME(RParen)
})

star 很多的框架写了 5 行

val atomParen = SurroundBy(parens.toCharPat(), Deferred{expr})
ParserKt 只需写一行就完了,加上错误处理也只用一行。

const NumberLiteral = createToken({
name: "NumberLiteral",
pattern: /[1-9]\d*/
})
$.RULE("atomicExpression", () => {
$.OR([/**/{ ALT: () => $.CONSUME(NumberLiteral) }, ...
单单定义 NumberLiteral 叫什么名字、长什么样子,就写了 4 行

val digit = digitFor('0'..'9')
val zeroNotation = Decide(
StickyEnd(item('0') or EOF, 0) { if (octal.test(peek)) error("no octal notations"); -1 }
)
val numPart = Contextual<Char, Int, Int>(digit) {
if (it == 0) zeroNotation
else Repeat(asInt(10, it), digit).Many()
}.discardFirst()
带上可以继续扩展/抽提的结构和直接数值读取,8 行
如果说完全等价的,Contextual(digitFor('1'..'9')) { Repeat(asInt(10, it), digitFor('0'..'9')) }.ignoreFirst(),1 行。
一支 爷爷 跳了 我 给 校长 孔雀 舞
Forwarded from Deleted Account
现在 ParserKt 可以在 100 行左右完成 JSON 的 read / show 了 🤔
Forwarded from Deleted Account
虽然我一直有在优化项目的接口易用性,但感觉还是有些“小乌云”……
顺序、直到、重复、选择 (SURD) 这些大结构没有变
单项、含于、符合 (IES) 的最后一个字母我给它加上了 StickyEnd,也算是解决了 Feed 模型的一个不被重视的问题
模式、正模式、可选模式、修饰模式 (PPOP) 我最后削掉了那个 PositivePattern
现在又变成了 POPCorn (都是助记的) 算是把 ConstantPattern 给合并到了基本模型里,方便 skip whitespaces 的 Pattern 的 rebuild。
Forwarded from Deleted Account
突然觉得我可以“汉化”一下,然后投稿给汉语编程,哈哈哈 😂
https://sap.github.io/chevrotain/docs/tutorial/step1_lexing.html#introduction
SELECT column1 FROM table2
SELECT name, age FROM persons WHERE age > 100

val letter = elementIn('a'..'z', 'A'..'Z') or item('_')
// [a-zA-Z]\w*
val name = Seq(::StringTuple, letter.toStringPat(), stringFor(letter or digit))
// 0|[1-9]\d*
val int = Contextual

sealed class SQL {
// SELECT {Name} FROM Name (WHERE Expr)?
class Select(val columns: List<String>, val table: String, val filter: Where?): SQL()
class Where(val expr: Expr)
}

val keyword = KeywordPattern<Pattern<Char, >


草写不出来了
https://tomassetti.me/parsing-in-javascript/#chevrotain

sealed class Token {
data class NamedConst(val name: String): Token()
}

fun KeywordPattern<Token>.mergeConsts(vararg names: String) = names.forEach { this[it] = Token.NamedConst(it) }
val namedConsts = KeywordPattern<Token>().apply {
mergeConsts("true", "false", "null")
}
val namedEscape = mapOf('b' to '\b', 't' to '\t', 'n' to '\n', 'r' to '\r', 'f' to '\u000C', 'v' to '\u000B')
val stringChar = Decide(!elementIn('"', '\n', '\\'), MapPattern(namedEscape) prefix item('\\')).mergeFirst {
if (it in namedEscape.values) 1 else 0 }
val string = SurroundBy(item('"') to item('"').clam {"unterminated string"}, stringFor(stringChar))
/tmp/duangsuse.sock
https://tomassetti.me/parsing-in-javascript/#chevrotain sealed class Token { data class NamedConst(val name: String): Token() } fun KeywordPattern<Token>.mergeConsts(vararg names: String) = names.forEach { this[it] = Token.NamedConst(it) } val namedConsts…
ParserKt 是 scannerless parsing 框架,目前对这样传统的 lexer-parser 结构 支持还不完善……

其实也可以用 iterator input,但着实是没好的 Pattern 去支持即时更新的 iterator
This media is not supported in your browser
VIEW IN TELEGRAM
我去专门独立于 Repeat pattern,写一个这种输入吧。