Forwarded from 《一天世界》博客 (Lawrence Li (自由閪如一))
Exactly correct. This is why they have nice things in Hong Kong.
/tmp/duangsuse.sock
首先,既然要使用 FP.js 的解析组合子模块,首先要知道它暴露了什么接口。 seq, possible, lookahead1, someFold, manyFold 这是用于组合子解析器的单位 satisfy, charP, elemP, kwP, wsP, ws0P 这是用于 seq 和 many / some 解析器的单位 此外: Feeder 是输入字符/对象流的称呼,它上面可以使用: lastIsCR / lastIsLF / lastIsCRLF / eof / hasNext 判断方法…
我们忘记无关内容,开始讲这个很辣鸡但是依然可以用的解析组合子。
昨天我们说了,如何去解析
现在是组合解析器的时候了:以上解析器皆可被用于哥哥(貌似)解析器,来实现顺序解析和循环解析(递归不支持,如果需要的话解析程序得自己写)
由于是基于无法重置字符流、简单函数闭包组合的方式,视窗有限并且无法做到在任何解析器失败时都可以重置(消耗的字符已经也只能消耗了,无法回到从前出现第二次)
所以所有这些哥哥 都很废(因为 FP.js 也只是个弟弟...),哥哥不能再组合自己的兄弟、只能组合不消耗字符 / 最多 lookahead 一个字符的弟弟 也是没有办法的事情呢(要不然就要使用 workaround)
这个哥哥是顺序解析弟弟的。
注意 kwP 的行为是正常的,它本来就是字符流的解析器(它不叫 listP)
它的行为就是阅读每个项目的第一个字符(在字符流里,这肯定是一个字符而已)、和需要的 keyword 比较,如果成功则判断下一项,失败就完了
如果你使用 possible 和 kwP 的组合,务必注意:kwP 实际上只能区别开头的一个字符,如果有成功的情况,万事大吉
如果失败,请 务必 让 possible 和嵌套它的组合也一起失败,不然就是未定义行为(可能发生莫名其妙的解析错误,因为 kwP 做不出能够在失败时重置输入流的担保)。
比如 cat 和 cab 就分不清了(都是 c 开头)但是能够分出 cat 和 dog(一个 c 一个 d)
(题外话,kwP 这个名字应该改成 stringP 的,此外,它的功能也应该被泛化成列表解析,而非字符串解析....
>
比如我们有一门叫做 def 的语言,它的
<def> ws name ws0 <=> ws0 value
假设 value 肯定是
演示方便就不写复杂了
+ possible(ps, msgr)
这个哥哥可以组合很多弟弟的可能性
+ someFold(p, folder, msgr)
这个哥哥顺序解析弟弟、直到解析不了 return f(f(f...(v, x), y), z)
+ manyFold(p, folder)
这个哥哥首先看弟弟能不能被解析,如果不能直接成功 return v,重复解析弟弟、直到解析不了
+ lookahead1(pmap, flmap, msgr)
这个哥哥首先判断 pmap 是个 array 还是个 object
如果是 object,pmap 的 key 是下一个字符的可能性,value 是处理这个可能性的函数
如果是 array,pmap 的 key 是一个谓词函数,lookahead1 以下一个字符尝试找到满足的函数,并且在 flmap 里找到这个函数名字的键,就是它的处理器。
任何不匹配都会导致解析失败
昨天我们说了,如何去解析
satisfy, charP, elemP, kwP, wsP, ws0P 并且提供好看的错误标识现在是组合解析器的时候了:以上解析器皆可被用于哥哥(貌似)解析器,来实现顺序解析和循环解析(递归不支持,如果需要的话解析程序得自己写)
由于是基于无法重置字符流、简单函数闭包组合的方式,视窗有限并且无法做到在任何解析器失败时都可以重置(消耗的字符已经也只能消耗了,无法回到从前出现第二次)
所以所有这些哥哥 都很废(因为 FP.js 也只是个弟弟...),哥哥不能再组合自己的兄弟、只能组合不消耗字符 / 最多 lookahead 一个字符的弟弟 也是没有办法的事情呢(要不然就要使用 workaround)
seq, possible, lookahead1, someFold, manyFold
+ seq(ps, folder, msgr)这个哥哥是顺序解析弟弟的。
ps = p.seq([p.satisfy(x => x.startsWith('--')), p.elemP(['mode', 'quiet', 'version']), p.kwP('ok')]); pp = p.run(ps);
> pp(['--version', 'mode', 'o', 'k'])
Either { inner: [ '--version', 'mode', 'ok' ], left: false }
这里我们组合了 satisfy 和 elemP、kwP注意 kwP 的行为是正常的,它本来就是字符流的解析器(它不叫 listP)
它的行为就是阅读每个项目的第一个字符(在字符流里,这肯定是一个字符而已)、和需要的 keyword 比较,如果成功则判断下一项,失败就完了
如果你使用 possible 和 kwP 的组合,务必注意:kwP 实际上只能区别开头的一个字符,如果有成功的情况,万事大吉
如果失败,请 务必 让 possible 和嵌套它的组合也一起失败,不然就是未定义行为(可能发生莫名其妙的解析错误,因为 kwP 做不出能够在失败时重置输入流的担保)。
比如 cat 和 cab 就分不清了(都是 c 开头)但是能够分出 cat 和 dog(一个 c 一个 d)
(题外话,kwP 这个名字应该改成 stringP 的,此外,它的功能也应该被泛化成列表解析,而非字符串解析....
>
ps = p.seq([p.kwP('你好', '我不好?'), p.kwP('世界', '404 Not Found')]); pp = p.run(ps);
> pp('你')Left('Parser failed @<feed>:1:1 (pos=1@<eof>), sequence item 0: Unexpected EOF. Expecting keyword <你好>@1 我不好?')
> pp('你好')Left('Parser failed @<feed>:1:2 (pos=2@<eof>), sequence item 1: Unexpected EOF. Expecting keyword <世界>@0 404 Not Found')
> pp('你好世界')Right([ '你好', '世界' ])pp('你好世界哈').toString()
>
'R(你好,世界)'seq 使用的 match folder(这个名字有点莫名其妙) 真正的魅力在于可以快速创建 AST
比如我们有一门叫做 def 的语言,它的
Item 规则是这样的:<def> ws name ws0 <=> ws0 value
假设 value 肯定是
"abc" 这种的,则实际上可以这样:function DefItem(_d, w0, name, w1, _eq, w2, val) {
this.wss = [w0, w1, w2];
this.name = name;
this.value = val;
}
> ps = p.seq([p.kwP('def'), p.wsP(), p.someFold(p.elemP(['a','b','c','d'])), p.ws0P(), p.kwP('='), p.ws0P(), p.someFold(p.elemP(['1','2','3'])) ], p.makeNew(DefItem))
> pp = p.run(ps);
我们试一下演示方便就不写复杂了
+ possible(ps, msgr)
这个哥哥可以组合很多弟弟的可能性
+ someFold(p, folder, msgr)
这个哥哥顺序解析弟弟、直到解析不了 return f(f(f...(v, x), y), z)
+ manyFold(p, folder)
这个哥哥首先看弟弟能不能被解析,如果不能直接成功 return v,重复解析弟弟、直到解析不了
+ lookahead1(pmap, flmap, msgr)
这个哥哥首先判断 pmap 是个 array 还是个 object
如果是 object,pmap 的 key 是下一个字符的可能性,value 是处理这个可能性的函数
如果是 array,pmap 的 key 是一个谓词函数,lookahead1 以下一个字符尝试找到满足的函数,并且在 flmap 里找到这个函数名字的键,就是它的处理器。
任何不匹配都会导致解析失败
/tmp/duangsuse.sock
啊... 这个『无论怎么样都要加花括号』『不要使用 i++』原则还真是蛮重要的,我都没有想到可能会被解析成不知道什么样子....
开始我利用了 i++,可是没有想到:
1. 如果某次判定失败,则 for update 块的 i++ 一般来说会运作(但不可能大于 str.length+1,因为那时已经 break (&&短路))
2. 如果第一次判定失败,则 i 为 undefined,失败
如果成功,则 for 后 i 是 str.length+1,问题是,在 for.predicate 里更新变量... 本身就是很差的编程风格
这样就出现了一个问题:
1. 如果最后的条件不符合,i 会被更新,但是条件实际上不符合,导致了
"你_世_" 这样的也能 match....
人间之鉴...
1. 如果某次判定失败,则 for update 块的 i++ 一般来说会运作(但不可能大于 str.length+1,因为那时已经 break (&&短路))
2. 如果第一次判定失败,则 i 为 undefined,失败
如果成功,则 for 后 i 是 str.length+1,问题是,在 for.predicate 里更新变量... 本身就是很差的编程风格
这样就出现了一个问题:
1. 如果最后的条件不符合,i 会被更新,但是条件实际上不符合,导致了
"你_世_" 这样的也能 match....
人间之鉴...
这类模板化的循环,应该以可读性为主... 这点很多嵌入式软件设计的例子很好,都是模板化的判断
我看不起模板化(实际上我在 JavaScript 这样的层次也使用 C/C++ 的模板逻辑风格),可是这却导致了难以察觉和修复的错误的产生...
我看不起模板化(实际上我在 JavaScript 这样的层次也使用 C/C++ 的模板逻辑风格),可是这却导致了难以察觉和修复的错误的产生...
另外的一个 #人间之鉴 :一定要理清思路再下手,尤其是隐式上下文的问题,如果各种加 Workaround,你就会进入无限 REPL 和修代码加 case 狂猜的状态无法自拔,最终会浪费掉大量的时间。 如果成功则最终代码会令人莫名其妙而且往往有比它好十倍的解决方案;如果失败,则你就白白浪费了几个小时的时间...
什么人间之鉴,但的确是有点莫名其妙。这个问题我最终没有解决,
'2' 和 '=' 看起来是一模一样,可我不知道为什么差一个空格两个空格就会有问题
我已经没法去想了,对我来说这真的就是莫名其妙,很对不起,也很对不起我自己... 坐了这么久、盯着屏幕,但是没有任何进展并且模拟的都成功、REPL 全不符合预期,我根本搞不懂,为什么『两边』还不一样,而其他结果全都正确。
'2' 和 '=' 看起来是一模一样,可我不知道为什么差一个空格两个空格就会有问题
我已经没法去想了,对我来说这真的就是莫名其妙,很对不起,也很对不起我自己... 坐了这么久、盯着屏幕,但是没有任何进展并且模拟的都成功、REPL 全不符合预期,我根本搞不懂,为什么『两边』还不一样,而其他结果全都正确。
/tmp/duangsuse.sock
另外的一个 #人间之鉴 :一定要理清思路再下手,尤其是隐式上下文的问题,如果各种加 Workaround,你就会进入无限 REPL 和修代码加 case 狂猜的状态无法自拔,最终会浪费掉大量的时间。 如果成功则最终代码会令人莫名其妙而且往往有比它好十倍的解决方案;如果失败,则你就白白浪费了几个小时的时间...
对不起,真的没办法了,我真的两面 方法都试过了,可是用了很长时间,依然没有办法,而且,我不知道为什么 两个完全相同的解析器还『不一样』,为什么 '=' 和 '2' 还不一样、为什么同样的循环,一会对一会错... 但我真的没法想办法弄正确了,我已经尝试了很久,没办法再继续这样可能没有希望的事情... 或许未来再写就会明白吧...
/tmp/duangsuse.sock
对不起,真的没办法了,我真的两面 方法都试过了,可是用了很长时间,依然没有办法,而且,我不知道为什么 两个完全相同的解析器还『不一样』,为什么 '=' 和 '2' 还不一样、为什么同样的循环,一会对一会错... 但我真的没法想办法弄正确了,我已经尝试了很久,没办法再继续这样可能没有希望的事情... 或许未来再写就会明白吧...
(给自己)提示一下:其实这个问题的『疑点』都是编程错误和 L-R 扫描的结果而已,是对称的,但是循环和空格跳过/非空格保留上存在控制流的问题;本身也和组合子使用的流架构不好有关
以后如果再写,我不会再写错了.... 真的对不起自己啊
目前,使用 workaround.
以后如果再写,我不会再写错了.... 真的对不起自己啊
目前,使用 workaround.
Forwarded from Rachel 碎碎念 (IFTTT)
知乎读到这篇回答的时候觉得终于有一个脑子正常的人说暂停台湾自由行这事儿了,结果读完之后想点赞才发现已经因为“政治敏感”被建议修改了,之前他们说知乎已完的时候我还觉得至少可以当做一个渠道兼听一下,现在才发现已经只准赞美,完全没有信息量了 pic.twitter.com/9I02QyU9sP
— lobatt (@lobatt) August 1, 2019
— lobatt (@lobatt) August 1, 2019
Twitter
lobatt
知乎读到这篇回答的时候觉得终于有一个脑子正常的人说暂停台湾自由行这事儿了,结果读完之后想点赞才发现已经因为“政治敏感”被建议修改了,之前他们说知乎已完的时候我还觉得至少可以当做一个渠道兼听一下,现在才发现已经只准赞美,完全没有信息量了