https://github.com/corsis/XParsec/blob/master/XParsec.fs#L99 看不懂。 看到 Sigma 我还以为是表达 Sum type (safe union) 的呢,真是无语╯﹏╰
不过多向输入的解析 ParserKt 就收下作 NonlinearFeed 了,虽然我不知道有什么用,但反正 state 和 BaseJoinBy 这种极端情况的 API 都有了,多加一个也正好能让 alsoDo 有用点(不过 Feed 为避免用 Character 泛型引用造成不必要的开销,看来只能把 Char 当成 Short int 用作索引了)
或许能
不过能这样的确有点价值:
说起来新 ParserKt 也支持左递归:
val digit = Convert(elementIn('0'..'9'), -'0')
val digits = Piped.leftRec<Int> {
Decide(Seq(it, digit) { first*10+second }, digit)
}
不过多向输入的解析 ParserKt 就收下作 NonlinearFeed 了,虽然我不知道有什么用,但反正 state 和 BaseJoinBy 这种极端情况的 API 都有了,多加一个也正好能让 alsoDo 有用点(不过 Feed 为避免用 Character 泛型引用造成不必要的开销,看来只能把 Char 当成 Short int 用作索引了)
或许能
Repeat(asList(), XE.attr("font")).direct(XMLFeed.child) 会很酷(迫真)不过能这样的确有点价值:
val defs = Seq(XE.tag("key").direct(X.next), Repeat(asList(), XE.tag("def"))).direct(X.child)
val root = Decide(defs, XE.anyElem)
说起来新 ParserKt 也支持左递归:
let digit = oneOf "0123456789" --> fun d -> int(d) - int('0')
let digits = production "digits" digits.rule
<- digits + digit --> fun (a, b) -> a*10+b
|- digit
val digit = Convert(elementIn('0'..'9'), -'0')
val digits = Piped.leftRec<Int> {
Decide(Seq(it, digit) { first*10+second }, digit)
}
GitHub
corsis/XParsec
extensible, type-and-source-polymorphic, non-linear applicative parser combinator library for F# 3.0 and 4.0 - corsis/XParsec
https://github.com/ppolesiuk/Parsor/blob/master/Parsor/Expresion.fs
这个太可怕了,深藏不露啊,算符链竟然支持 前缀/后缀 的优先级…… 新版 PKT 的设计也只是在使用 OpChain.Lazy(atom,op) 的情况下才能用前缀优先级实现 !a[0]+1 这种结合性区分
嗯…… 看了下这个优先级结合的确可以推广到 suffix 上(咱用了 Scala 的名词,叫 postfix ,毕竟和字符串的不是同一种东西 in pre post 也能体现出主次)
比如 (a+1)! 和 (a+(1!) )$ 是可以分出区别
但这一切的前提是,优先级必须支持传递性而不止在 +*-/ 上 zipWithNext ,只参考相邻两项
如果用 Lazy 版的 Lua 算法解析,就可以实现前后缀优先级,不过我觉得这不是一个重要特性(废话)(而且如果这是哪门语言的重要特性,那语言已经废了,不懂对数学表达法去其糟粕)
这个太可怕了,深藏不露啊,算符链竟然支持 前缀/后缀 的优先级…… 新版 PKT 的设计也只是在使用 OpChain.Lazy(atom,op) 的情况下才能用前缀优先级实现 !a[0]+1 这种结合性区分
嗯…… 看了下这个优先级结合的确可以推广到 suffix 上(咱用了 Scala 的名词,叫 postfix ,毕竟和字符串的不是同一种东西 in pre post 也能体现出主次)
比如 (a+1)! 和 (a+(1!) )$ 是可以分出区别
但这一切的前提是,优先级必须支持传递性而不止在 +*-/ 上 zipWithNext ,只参考相邻两项
如果用 Lazy 版的 Lua 算法解析,就可以实现前后缀优先级,不过我觉得这不是一个重要特性(废话)(而且如果这是哪门语言的重要特性,那语言已经废了,不懂对数学表达法去其糟粕)
GitHub
ppolesiuk/Parsor
Another parser combinator library for F#. Contribute to ppolesiuk/Parsor development by creating an account on GitHub.
http://yaccconstructor.github.io/FsGll/
说起来, partial parsing 也是一个相当有用的功能( REPL 补全什么的都用得到,不过在 ParserKt 里也可以用 alsoDo 之类的存储到顶层表达式)
咱表面上没有支持 partial parsing (而且是咱也是自顶向下的递归主动读取解析法,不是自底向上的被动状态转移)
其实是完全做得到的,只需要把解析结果加个 Unknown 节点再上 calm(default_value) 即可,就像很多人以为一些文法一定要 lookahead, 但利用一些蹩脚的写法就会发现能不能预读/回读,完全是在验证框架的组合性能力而已,不是绝对的。
说起来, partial parsing 也是一个相当有用的功能( REPL 补全什么的都用得到,不过在 ParserKt 里也可以用 alsoDo 之类的存储到顶层表达式)
咱表面上没有支持 partial parsing (而且是咱也是自顶向下的递归主动读取解析法,不是自底向上的被动状态转移)
其实是完全做得到的,只需要把解析结果加个 Unknown 节点再上 calm(default_value) 即可,就像很多人以为一些文法一定要 lookahead, 但利用一些蹩脚的写法就会发现能不能预读/回读,完全是在验证框架的组合性能力而已,不是绝对的。
yaccconstructor.github.io
FsGll
http://www.quanttec.com/fparsec/tutorial.html#parsing-json
不知道评测结果怎样,我觉得反正是好,因为不需要判 a-z A-Z 0-9 了(三者都是不同区间)
不过 toInt 有 radix 参数,应该说就不需要 Repeat(asInt(), p) 这种了…… 很鸡肋(迫真,还是需要的,因为 String.toInt 要先存好 String ,浪费内存了)
hex2int c = (int c &&& 15) + (int c >>> 6)*9 这个有点意思🤔def hi(c): return (c&0xF + c>>(8-2)) *9不知道评测结果怎样,我觉得反正是好,因为不需要判 a-z A-Z 0-9 了(三者都是不同区间)
不过 toInt 有 radix 参数,应该说就不需要 Repeat(asInt(), p) 这种了…… 很鸡肋(迫真,还是需要的,因为 String.toInt 要先存好 String ,浪费内存了)
Forwarded from Solidot
Windows XP 泄露的源代码能编译可工作的操作系统
Windows XP 和 Windows Server 2003 的源代码最近被人泄露,源代码被确认是真实的,有 YouTube 主播将源代码编译成可工作的操作系统。消息来源称,源代码是真实的但并不完整。下载源代码的 NTDEV 成功编译了 Windows XP 代码和 Windows Server 2003 代码。NTDEV 表示源代码确实不完整,缺少了 winlogon.exe 和许多驱动。Windows Server 2003 泄露的代码要比 XP 更完整,但和 XP 一样,也缺少 Winlogon。Media
https://www.solidot.org/story?sid=65712
Windows XP 和 Windows Server 2003 的源代码最近被人泄露,源代码被确认是真实的,有 YouTube 主播将源代码编译成可工作的操作系统。消息来源称,源代码是真实的但并不完整。下载源代码的 NTDEV 成功编译了 Windows XP 代码和 Windows Server 2003 代码。NTDEV 表示源代码确实不完整,缺少了 winlogon.exe 和许多驱动。Windows Server 2003 泄露的代码要比 XP 更完整,但和 XP 一样,也缺少 Winlogon。Media
https://www.solidot.org/story?sid=65712
duangsuse::Echo
https://github.com/corsis/XParsec/blob/master/XParsec.fs#L99 看不懂。 看到 Sigma 我还以为是表达 Sum type (safe union) 的呢,真是无语╯﹏╰ 不过多向输入的解析 ParserKt 就收下作 NonlinearFeed 了,虽然我不知道有什么用,但反正 state 和 BaseJoinBy 这种极端情况的 API 都有了,多加一个也正好能让 alsoDo 有用点(不过 Feed 为避免用 Character 泛型引用造成不必要的开销,看来只能把…
嗯…… 谈到 XML 解析突然想到 #Java 的 SAX(Simple API for XML) 和 XMLPull
React 用的也好像 E4X(ECMAScript for XML) 啊
我常区分函数的『主动』与『被动』,比如说计算就是主动,事件和入口就是被动;其实工程界把 push 称为主动, pull 称为被动(当然和 git pull 不同😂),这也是一个比较 English 化的词
XMLPull 被称为最快的 XML 解析器,其实准确来讲它只是一种数据封装思路——不直接新分配并提供数据对象,而是把尽可能裸值化的它提供给 onXXX 函数(比如你只是找 <a href> 就没必要构造整个 Document 对象),这样就能大幅减轻 GC 压力(C++ 也就没必要用 shared_ptr 了,因为根本不存在对象引用,只有虚表的覆写函数)
这个思路其实也怪好的,而且它也能兼容(利用数据栈)构造如 XMLNode 的递归结构,从而作为可扩展的底层来提供。
之前用过 #Ruby 的 XML 解析器 Nokogiri ,那叫一个烂…… 它不是 XMLPull 模式却有“高性能”的觉悟, Node.attributes 都是 Array 非 Hash... 貌似连 CSS selector 也没有只支持 XPath,实在是太草了
说起来, ParserKt 也包含这种思路。
虽然 Pattern<T> = (Feed,ShowReq?) -> T 是明确要『主动』构造 T (这样看起来它的性能不如 ANTLR, GLL, LALR 这类状态栈算法的『被动』 action)
一方面, ParserKt 的可组合性允许创建不依赖 Pair/Triple/Tuple 的 Seq 而可以复用余下所有定义,是能够通过切换少数调用来支持 pull 模式的 event listener 的
另一方面,pull 一定比 push 快因为使用了“高级算法” 其实是常见的误区,而且对DSL解析器而言构造AST数据是主要目的,实际上有 JS 解析器框架里,JSON 跑分结果最好的反而是手写递归下降法,且 Lua 初版是利用 YaCC 生成解析器,后来反以手工重写。
虽然这种方法有 push 的一些固有限制——阻塞、不能统一处理错误消息/部分结果,但它的高代码质量却能带来意想不到的优化,而这种方法支持的文法并不比 pull 少。
有时候选择代码质量、高抽象度、简化的底层接口并不意味着牺牲性能,因为在其上下建立的实现技巧完全可能追回那点小小的开销。
比如,Fold 模式能支持 asInt(16) 这类已经是最低开销的数值读取,而无需“action”将其“rewrite”成 String.toInt ,从不需要用左递归蹩脚的语法来“优化”,而对于参数列表之类也无出其二, asList() 甚至还能 asMap() ,避免创建多余的结构去保留临时 String/List<Pair> ,直接从提出值到构造大结果,何尝不是一种 XMLPull 式思路
尽管用“万能的”左递归优化可以实现 Fold 和 OpChain 所解决的问题,但没人用它——实际上有 DSL 设计需求的人还不少,但有多少人会把 digits 定义成 (digits digit)|digit 的形式?大家看到左递归链的优先级解析就糊涂,甚至不知道为什么加减在前乘除在后
想像一下,如果一个人为了优化 ES5 的解析器而把所有能 Fold 的循环重写其 action,变成左递归定义,看起来会如何?
所以唯一的解决方案就是简化表示法本身,分清主次,用适度特例化的 API 取代“万能” “广义”的“好好先生”们。
React 用的也好像 E4X(ECMAScript for XML) 啊
我常区分函数的『主动』与『被动』,比如说计算就是主动,事件和入口就是被动;其实工程界把 push 称为主动, pull 称为被动(当然和 git pull 不同😂),这也是一个比较 English 化的词
XMLPull 被称为最快的 XML 解析器,其实准确来讲它只是一种数据封装思路——不直接新分配并提供数据对象,而是把尽可能裸值化的它提供给 onXXX 函数(比如你只是找 <a href> 就没必要构造整个 Document 对象),这样就能大幅减轻 GC 压力(C++ 也就没必要用 shared_ptr 了,因为根本不存在对象引用,只有虚表的覆写函数)
这个思路其实也怪好的,而且它也能兼容(利用数据栈)构造如 XMLNode 的递归结构,从而作为可扩展的底层来提供。
之前用过 #Ruby 的 XML 解析器 Nokogiri ,那叫一个烂…… 它不是 XMLPull 模式却有“高性能”的觉悟, Node.attributes 都是 Array 非 Hash... 貌似连 CSS selector 也没有只支持 XPath,实在是太草了
说起来, ParserKt 也包含这种思路。
虽然 Pattern<T> = (Feed,ShowReq?) -> T 是明确要『主动』构造 T (这样看起来它的性能不如 ANTLR, GLL, LALR 这类状态栈算法的『被动』 action)
一方面, ParserKt 的可组合性允许创建不依赖 Pair/Triple/Tuple 的 Seq 而可以复用余下所有定义,是能够通过切换少数调用来支持 pull 模式的 event listener 的
另一方面,pull 一定比 push 快因为使用了“高级算法” 其实是常见的误区,而且对DSL解析器而言构造AST数据是主要目的,实际上有 JS 解析器框架里,JSON 跑分结果最好的反而是手写递归下降法,且 Lua 初版是利用 YaCC 生成解析器,后来反以手工重写。
虽然这种方法有 push 的一些固有限制——阻塞、不能统一处理错误消息/部分结果,但它的高代码质量却能带来意想不到的优化,而这种方法支持的文法并不比 pull 少。
有时候选择代码质量、高抽象度、简化的底层接口并不意味着牺牲性能,因为在其上下建立的实现技巧完全可能追回那点小小的开销。
比如,Fold 模式能支持 asInt(16) 这类已经是最低开销的数值读取,而无需“action”将其“rewrite”成 String.toInt ,从不需要用左递归蹩脚的语法来“优化”,而对于参数列表之类也无出其二, asList() 甚至还能 asMap() ,避免创建多余的结构去保留临时 String/List<Pair> ,直接从提出值到构造大结果,何尝不是一种 XMLPull 式思路
尽管用“万能的”左递归优化可以实现 Fold 和 OpChain 所解决的问题,但没人用它——实际上有 DSL 设计需求的人还不少,但有多少人会把 digits 定义成 (digits digit)|digit 的形式?大家看到左递归链的优先级解析就糊涂,甚至不知道为什么加减在前乘除在后
想像一下,如果一个人为了优化 ES5 的解析器而把所有能 Fold 的循环重写其 action,变成左递归定义,看起来会如何?
所以唯一的解决方案就是简化表示法本身,分清主次,用适度特例化的 API 取代“万能” “广义”的“好好先生”们。
GitHub
GitHub - benjamin-hodgson/Pidgin: A lightweight and fast parsing library for C#.
A lightweight and fast parsing library for C#. Contribute to benjamin-hodgson/Pidgin development by creating an account on GitHub.
duangsuse::Echo
唉,现在看这个 Ruiko.kt (原版是一 F# 函数式大佬的) 还是搞不懂它拿 MutList 实现 lr 是什么个原理,看这架势大概要理解编辑撤销的 stack 指针和为何用 Set 吧。(估计理解了以后又会失望,索性不费这时间) 不过现在的我可一点也不想顶礼膜拜,因为俺明白了俺就是个农民,农民靠拿出切实可用、啥人都看得懂的玩意过活,不是靠智商和“设计模式”,唉。 俺设计的 ParserKt, 起名字都不往函数式、逻辑数学上靠, 是 Seq,Decide,Repeat(asXXX(), p),…
对于感兴趣文本解析领域,和解析组合子之金科绿玉的观众可以去
https://github.com/orangeduck/mpc
看看,一个 C 的半生成器式解析组合子。它提供的基元组合子命名可以说完美列举了本门『教师爷』定下的名词。🧐
不过值得一提的是虽然是 C ,这个库的 many (repeat) 组合子也采用了 Fold 模型(不过是
突然感觉有些函数式者有时在自相矛盾,明明一定要用 F# 写,却还要其中最“高性能”的框架…… 不是自虐么
https://github.com/orangeduck/mpc
看看,一个 C 的半生成器式解析组合子。它提供的基元组合子命名可以说完美列举了本门『教师爷』定下的名词。🧐
不过值得一提的是虽然是 C ,这个库的 many (repeat) 组合子也采用了 Fold 模型(不过是
(*)(int no, **xs) 鹅)来归纳,真是工程界英雄所见略同啊😓,反正我没见函数式框架有过(不少函数式的连他们祖师爷的 unparse 操作都虚无,反而爱强调自己在努力优化性能诶)突然感觉有些函数式者有时在自相矛盾,明明一定要用 F# 写,却还要其中最“高性能”的框架…… 不是自虐么
GitHub
GitHub - orangeduck/mpc: A Parser Combinator library for C
A Parser Combinator library for C. Contribute to orangeduck/mpc development by creating an account on GitHub.
duangsuse::Echo
对于感兴趣文本解析领域,和解析组合子之金科绿玉的观众可以去 https://github.com/orangeduck/mpc 看看,一个 C 的半生成器式解析组合子。它提供的基元组合子命名可以说完美列举了本门『教师爷』定下的名词。🧐 不过值得一提的是虽然是 C ,这个库的 many (repeat) 组合子也采用了 Fold 模型(不过是 (*)(int no, **xs) 鹅)来归纳,真是工程界英雄所见略同啊😓,反正我没见函数式框架有过(不少函数式的连他们祖师爷的 unparse 操作都虚无,反而爱强调自己在努力优化性能诶)…
唉,这位的文风也很清晰,而且他居然会为附加功能的缺失说 Sorry !
看着看着就莫名觉得很感慨😳, ParserKt 所谓之「不制造问题」是有多深刻啊……
几乎所有的,哪怕函数式解析器框架都支持 Backtracing, ParserKt 刻意只让 peek(1) ,即便现在也醒目地和新 peek(n) 划清界地(这也是 "Decide" 这个名称的由来,因为它只能判1字符😂),却可以让用户清晰地利用 Piped.concat 解决 Name|Name '('{Expr}[',']')' 的歧义消除(而不是先读个 Name(Args) ,然后发现没有调用的括号😱, backtrace 折返回去再读一遍 Name ,分配多余的 String)。
我之前都想不到,仅仅一个 Backtrace 就能引出多少问题:除了 buffer, position 栈维护至少占 80 行,竟然还有 FSParserc 专门新建 "memorize parser"(cache bktrace) 来优化(等于记忆 Input String 和其position),要不然就得合并混淆两种不同的语法,而 PKT 竟然如此轻松就靠定义式规避了?!😳
多少框架不支持左递归,多少框架对用户写出文法的顺序和范围自动做处理避免冲突、Bison 把左递归和右递归区别特化,新手根本不懂其中区别,但对其源代码,那支持左递归的部分又有谁看得懂?
PKT 支持了 Piped.leftRec ,竟然只是靠一个 LeftRec.lastResult 对象和其创建函数的“it:Pattern 给出上一次的结果;默认为 notParsed” 直至未解析返回 lastResult ,逻辑这么简单自然以至于我怀疑是错的……
几乎所有函数式框架都用 then/and 甚至半懂不懂的“数学”中缀来定义顺序解析,部分连 or/otherwise/alt/<|> 都作为中缀,为此不惜用链表甚至在构造时自动铺平数据对象(当然,用 Monad 而该这么做的除外),可 PKT 却刻意避免在主要结构上用 infix 记法,只允许普通到不能再普通的 vararg 调用,不考虑这些问题,却带来了编写和阅读时的便利性。
大家都争相效仿“正统函数式parserc”的命名:zeroOrOne(被PKT替换的原 optional), many(被废弃的 Repeat.Many), many1, manyTill(被废弃的原 Until), skipMany, skipMany1, sepBy, sepBy1, sepEndBy, sepEndBy1; skipWhile, take, takeWhile, any, char, not, string, asciiCI(被 TriePattern 替换的 stringNocase), oneOf, noneOf ,为原/否命题(oneOf/anyOf & noneOf)和可空重复起“不同”的 English 化名字, PKT 却仅保留 item/elementIn/satisfy 和其 not. 形式,只有 anyChar/noChar 一对特例。
懂得最小化设计的函数式解析器框架,为优化性能也过不了“缓存 backtrace 结果”这一关,它们陷入思维定势,没有意识到主要是 alt stringP 使用了这一特性,不知道可以换用 Trie 树从根本上解决这一问题。
而它们的子解析器结果 "Reply" "Result<I,T,E>" 用起来冗余贼多 , ParserKt 却拿 Kotlin 的 Nullability 操作和 Exception 替换了臃肿的 union 结构,比起“正统” runParser 又懂得用 TopPattern 以完整的函数式内联+面向对象形式提供设计法;性能与可读性,二者可得而兼。
并不是一开始我就懂要这么设计,也并不是我从 Haskell 里抄来了这个设计,都得感谢 Kotlin ——它对 JVM null 问题前无古人的灵活处理真可谓化腐朽为神奇,而这个库也绝对不负 "Kt" 的名字。
更进一步的框架,有的开始支持内部处理空格跳过,甚至独立出 Token 流
可 PKT 早已步过了泛型“词条”对象流的阶段,认识到旧时最初创建 tokenizer/parser 区分的原因,也就是关键点——跳过空格注释而保留必要空白(如文本量里的),为此只建立了 SkipWhite.Lexer/Feed 嵌套流,只添加一个非阻塞解析的状态机子组件,在保持高效数据流同时实现这个目标
看看实在是过于草生,高层建模让程序和数据直接对应,允许程序和程序自由组合竟然能构造出这样的效果?
还有,真是很难想象,函数式也有为实用性妥协的时候……
从阅读代码到实现逻辑、性能优化, Parser 涉及多少细节,可能陷入多少舍本逐末的片面追求, PKT 靠着否定一切、重构一切的莽撞,以及对定义式、低内存分配、傻瓜也能读懂的贪婪,硬生生一个坑也没掉进去,不愧是函数式交过程式生的杂种。
比起导致问题,再解决问题,
直接让用户改正不好的使用习惯,让问题直接不存在…… 还有,让答案也变得更简单。
我想,这就是我在编程上一直在追求的东西吧。 #Kotlin #statement #learn
看着看着就莫名觉得很感慨😳, ParserKt 所谓之「不制造问题」是有多深刻啊……
几乎所有的,哪怕函数式解析器框架都支持 Backtracing, ParserKt 刻意只让 peek(1) ,即便现在也醒目地和新 peek(n) 划清界地(这也是 "Decide" 这个名称的由来,因为它只能判1字符😂),却可以让用户清晰地利用 Piped.concat 解决 Name|Name '('{Expr}[',']')' 的歧义消除(而不是先读个 Name(Args) ,然后发现没有调用的括号😱, backtrace 折返回去再读一遍 Name ,分配多余的 String)。
我之前都想不到,仅仅一个 Backtrace 就能引出多少问题:除了 buffer, position 栈维护至少占 80 行,竟然还有 FSParserc 专门新建 "memorize parser"(cache bktrace) 来优化(等于记忆 Input String 和其position),要不然就得合并混淆两种不同的语法,而 PKT 竟然如此轻松就靠定义式规避了?!😳
多少框架不支持左递归,多少框架对用户写出文法的顺序和范围自动做处理避免冲突、Bison 把左递归和右递归区别特化,新手根本不懂其中区别,但对其源代码,那支持左递归的部分又有谁看得懂?
PKT 支持了 Piped.leftRec ,竟然只是靠一个 LeftRec.lastResult 对象和其创建函数的“it:Pattern 给出上一次的结果;默认为 notParsed” 直至未解析返回 lastResult ,逻辑这么简单自然以至于我怀疑是错的……
几乎所有函数式框架都用 then/and 甚至半懂不懂的“数学”中缀来定义顺序解析,部分连 or/otherwise/alt/<|> 都作为中缀,为此不惜用链表甚至在构造时自动铺平数据对象(当然,用 Monad 而该这么做的除外),可 PKT 却刻意避免在主要结构上用 infix 记法,只允许普通到不能再普通的 vararg 调用,不考虑这些问题,却带来了编写和阅读时的便利性。
大家都争相效仿“正统函数式parserc”的命名:zeroOrOne(被PKT替换的原 optional), many(被废弃的 Repeat.Many), many1, manyTill(被废弃的原 Until), skipMany, skipMany1, sepBy, sepBy1, sepEndBy, sepEndBy1; skipWhile, take, takeWhile, any, char, not, string, asciiCI(被 TriePattern 替换的 stringNocase), oneOf, noneOf ,为原/否命题(oneOf/anyOf & noneOf)和可空重复起“不同”的 English 化名字, PKT 却仅保留 item/elementIn/satisfy 和其 not. 形式,只有 anyChar/noChar 一对特例。
懂得最小化设计的函数式解析器框架,为优化性能也过不了“缓存 backtrace 结果”这一关,它们陷入思维定势,没有意识到主要是 alt stringP 使用了这一特性,不知道可以换用 Trie 树从根本上解决这一问题。
而它们的子解析器结果 "Reply" "Result<I,T,E>" 用起来冗余贼多 , ParserKt 却拿 Kotlin 的 Nullability 操作和 Exception 替换了臃肿的 union 结构,比起“正统” runParser 又懂得用 TopPattern 以完整的函数式内联+面向对象形式提供设计法;性能与可读性,二者可得而兼。
并不是一开始我就懂要这么设计,也并不是我从 Haskell 里抄来了这个设计,都得感谢 Kotlin ——它对 JVM null 问题前无古人的灵活处理真可谓化腐朽为神奇,而这个库也绝对不负 "Kt" 的名字。
更进一步的框架,有的开始支持内部处理空格跳过,甚至独立出 Token 流
可 PKT 早已步过了泛型“词条”对象流的阶段,认识到旧时最初创建 tokenizer/parser 区分的原因,也就是关键点——跳过空格注释而保留必要空白(如文本量里的),为此只建立了 SkipWhite.Lexer/Feed 嵌套流,只添加一个非阻塞解析的状态机子组件,在保持高效数据流同时实现这个目标
看看实在是过于草生,高层建模让程序和数据直接对应,允许程序和程序自由组合竟然能构造出这样的效果?
还有,真是很难想象,函数式也有为实用性妥协的时候……
从阅读代码到实现逻辑、性能优化, Parser 涉及多少细节,可能陷入多少舍本逐末的片面追求, PKT 靠着否定一切、重构一切的莽撞,以及对定义式、低内存分配、傻瓜也能读懂的贪婪,硬生生一个坑也没掉进去,
比起导致问题,再解决问题,
直接让用户改正不好的使用习惯,让问题直接不存在…… 还有,让答案也变得更简单。
我想,这就是我在编程上一直在追求的东西吧。 #Kotlin #statement #learn
Wikipedia
Backtracking
class of algorithm
https://github.com/search?p=2&q=parser+combinator&type=Repositories
这么多人/组织写过,可是我真的蛮想让 ParserKt 变成最强解析器框架的😒
旧版当然没做到,下一次我要搞个大新闻
反正现在打包好的的 ParserKt 易读性和对实际问题的接近已经秒杀前 2 页的框架了(Kotlin better-parse 在第三页,就凭它的 old-school tokenizer style 和 and/or/skip 表现力, 下版秒杀
毕竟这个项目的最终目标是实现社会主义现代化和中华民族伟大复兴 "Data representation is never a problem" #Kotlin
这么多人/组织写过,可是我真的蛮想让 ParserKt 变成
旧版当然没做到,下一次我要搞个大新闻
反正现在打包好的的 ParserKt 易读性和对实际问题的接近已经秒杀前 2 页的框架了(Kotlin better-parse 在第三页,就凭它的 old-school tokenizer style 和 and/or/skip 表现力, 下版秒杀
毕竟这个项目的最终目标是
GitHub
GitHub is where people build software. More than 50 million people use GitHub to discover, fork, and contribute to over 100 million projects.
https://github.com/inhabitedtype/angstrom/blob/master/examples/rFC2616.ml#L51 说句实在话, lift 在“函数式编程”(其实应该叫“数学式编程”,因为它从不考虑领域外的易懂性😂)里是个被用滥了的模糊词汇,本来我还打算起 TypeCast/NumberLift 这种名字,现在想想为何不用 NumberWiden 这种更明确的名字?看来即便有长度对仗,不该起的名字还是要规避。
duangsuse::Echo
https://github.com/inhabitedtype/angstrom/blob/master/examples/rFC2616.ml#L51 说句实在话, lift 在“函数式编程”(其实应该叫“数学式编程”,因为它从不考虑领域外的易懂性😂)里是个被用滥了的模糊词汇,本来我还打算起 TypeCast/NumberLift 这种名字,现在想想为何不用 NumberWiden 这种更明确的名字?看来即便有长度对仗,不该起的名字还是要规避。
说起来,编译器的 NumWiden 完全可以写在 TypeCast 里的,毕竟基本上就取优先级、统一类型两个操作(如果是 kotlin.Number 上,就多俩操作:化为类型、(关于类型的)应用计算,毕竟 Number 是不能+-*/ 😂
想来我当时重写 @mivik233 的 kamet, 也重用了他的数值提升 when (dst) (type) 写法,只是加了 foldSign 代码至少可以看,实在是思维定式,本来写个 general 的 signess 判定再 maxBy { it.byteSize } 就好的。
想来我当时重写 @mivik233 的 kamet, 也重用了他的数值提升 when (dst) (type) 写法,只是加了 foldSign 代码至少可以看,实在是思维定式,本来写个 general 的 signess 判定再 maxBy { it.byteSize } 就好的。