其实 ParserKt 的 rebuild 有时候也会成为累赘…… 比如说,在支持 仅仅是一种表达形式的值 的时候…… 几乎毫无意义,还导致了各种 mergeXXX 的乱入
ParserKt 讲究的的确是“无遗漏(完全解析)”、“一遍过(one-pass)”,但随着编写的解析器开始倾向实际工程,完全不损失信息逐渐变得不可能,于是才出现了开始的 Convert (no convert back) 以及现在的 mergeConstantJoin 和 mergeFirst, mergeSecond, flatten, toConstant 这类指定默认值或者转化存储法的修饰方法……
但现在 Decide 这样的解析器,也是没办法再转化了,不得不写 mergeFirst 来合并它的 caseNo…… 即便我根本不打算利用 rebuild,
为了兼容更多更复杂的文法,Contextual、Piped 之类的解析器的使用也开始频繁,没办法完美地解决从不完整的数据 rebuild 的问题,但仍希望更多解决方案能在将来被加入。
ParserKt 讲究的的确是“无遗漏(完全解析)”、“一遍过(one-pass)”,但随着编写的解析器开始倾向实际工程,完全不损失信息逐渐变得不可能,于是才出现了开始的 Convert (no convert back) 以及现在的 mergeConstantJoin 和 mergeFirst, mergeSecond, flatten, toConstant 这类指定默认值或者转化存储法的修饰方法……
但现在 Decide 这样的解析器,也是没办法再转化了,不得不写 mergeFirst 来合并它的 caseNo…… 即便我根本不打算利用 rebuild,
为了兼容更多更复杂的文法,Contextual、Piped 之类的解析器的使用也开始频繁,没办法完美地解决从不完整的数据 rebuild 的问题,但仍希望更多解决方案能在将来被加入。
/tmp/duangsuse.sock
看了这篇,压死骆驼的最后一根稻草,我觉得那些不能落地的,都不算是做得实事。 中文编程曾经是笑话,我将让它不再是笑话。
有了 ParserKt 的帮助,绝句便可以轻松地实现各种复杂的文法(诸如动态定义中缀操作符、缩进块、中文数字),而且不需要几行代码、与 Kotlin 程序设计语言无缝交接。
/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
汉字数值读写不存在问题,绝句里实际上不存在需要 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
duangsuse-valid-projects/Share
🐕 duangsuse's shared files(e.g. productive software projects, documents) - duangsuse-valid-projects/Share
/tmp/duangsuse.sock
其实如果说 ParserKt 设计绝句解析器的可能性,已经完成了。 汉字数值读写不存在问题,绝句里实际上不存在需要 lookahead 才能够判断的注释,都是单字符的 🙃 ParserKt 实际上已经能用了,SURDIES, FoldPattern, SingleFeed, CCDP(Convert, Contextual, Deferred, Piped), SJIT(SurroundBy, JoinBy, InfixPattern, TriePattern), NumUnitPattern, Lay…
你们觉得好笑吧,ParserKt 的中文计算器写了 62 行还带 REPL,star 最多的 TypeScript 原生解析器框架(非 parser gen)就多支持了个函数调用,总共写了 300 多行
再加几个 example 上来就抵过 ParserKt 整个项目的代码量了,现在谁是 KISS,谁是『令人不明觉厉、显得很大佬』?
再加几个 example 上来就抵过 ParserKt 整个项目的代码量了,现在谁是 KISS,谁是『令人不明觉厉、显得很大佬』?
GitHub
duangsuse-valid-projects/Share
🐕 duangsuse's shared files(e.g. productive software projects, documents) - duangsuse-valid-projects/Share
那是不比较代码本身的,然后我们再对比一下两者的定义:
== 加减乘除运算符 ==
ParserKt 费了 7 行定义完了所有出现的运算符,并且包含它们的优先级信息。
SAP/chevrotain 单单定义加减乘除长啥样,就用掉 24 行代码
并且,它还把加减法都归类为 AdditionalOperator…… 显然对数学运算符,有没有对称性会比这种优先级上的工作有意义。
再说两个细节,数字读取和括号表达式。
star 很多的框架写了 5 行。
ParserKt 只需写一行就完了,加上错误处理也只用一行。
如果说完全等价的,
== 加减乘除运算符 ==
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 行。GitHub
SAP/chevrotain
Parser Building Toolkit for JavaScript. Contribute to SAP/chevrotain development by creating an account on GitHub.
Forwarded from Deleted Account
虽然我一直有在优化项目的接口易用性,但感觉还是有些“小乌云”……
顺序、直到、重复、选择 (SURD) 这些大结构没有变
单项、含于、符合 (IES) 的最后一个字母我给它加上了 StickyEnd,也算是解决了 Feed 模型的一个不被重视的问题
模式、正模式、可选模式、修饰模式 (PPOP) 我最后削掉了那个 PositivePattern
现在又变成了
顺序、直到、重复、选择 (SURD) 这些大结构没有变
单项、含于、符合 (IES) 的最后一个字母我给它加上了 StickyEnd,也算是解决了 Feed 模型的一个不被重视的问题
模式、正模式、可选模式、修饰模式 (PPOP) 我最后削掉了那个 PositivePattern
现在又变成了
POPCorn (都是助记的) 算是把 ConstantPattern 给合并到了基本模型里,方便 skip whitespaces 的 Pattern 的 rebuild。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, > 草写不出来了
sap.github.io
Tutorial - Lexer | Chevrotain
Parser Building Toolkit for JavaScript
/tmp/duangsuse.sock
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, le…
其实我们 ParserKt 是不兴包含 lexer 的架构的
ParserKt 实在是太 general-purpose 了,以至于都不好专门为 [a-z]\w 那种情况添加函数
ParserKt 实在是太 general-purpose 了,以至于都不好专门为 [a-z]\w 那种情况添加函数
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))Strumenta
Parsing in JavaScript: all the tools and libraries you can use
We present and compare all possible alternatives you can use to parse languages in JavaScript. From libraries to parser generators, we present all options
/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
其实也可以用 iterator input,但着实是没好的 Pattern 去支持即时更新的 iterator