/tmp/duangsuse.sock
行!很完美
还好,虽然因为本身流只能处理一个字符的失败,所以不能重置大组合的失败... 小的处理的就没问题
现在这个样子... 根本不成体统嘛,稍微难看的组合 就会出错
解析一下,还得考虑自己的调用者,是顺序派的(调用自己后 next 一下)呢,还是选择派的呢(调用完不 next...)
还不如自己去 next 算了... 这样自己 ungetc 一次,和 seq 不 next,自己 next 一次 有什么区别...
解析一下,还得考虑自己的调用者,是顺序派的(调用自己后 next 一下)呢,还是选择派的呢(调用完不 next...)
还不如自己去 next 算了... 这样自己 ungetc 一次,和 seq 不 next,自己 next 一次 有什么区别...
fp.js
30.3 KB
令人头晕的 FP.js... 我不打算继续写测试了,还是拿它立刻 写点解析器出来测试吧 #FP #parser #PLT #JavaScript, 何况 FP.js 提供的解析器,本身就存在很大问题,不是实际应该使用的
/tmp/duangsuse.sock
fp.js
完成的孙(划掉)子(不能添加子解析器的)解析器:
satisfy, charP, elemP, kwP, wsP, ws0P
爸爸解析器
seq, possible(万恶...), lookahead1, someFold, manyFold
辅助
run, pmatch, pfail, parsed, presult, Feeder
satisfy, charP, elemP, kwP, wsP, ws0P
爸爸解析器
seq, possible(万恶...), lookahead1, someFold, manyFold
辅助
run, pmatch, pfail, parsed, presult, Feeder
究极迫真版本(跑
Done. 194 APIs tested, 388 passed / 0 failed in 0.942 secs.
#FP #JavaScript 我真的无力维护了,赶快开发应用跑路#JavaScript #FP 分享一些十分不得了(迫真)的心得:我修复了 dropWhile 函数,然后这里我写了惰性递归的 makeCats 造猫(绝望) combination 函数,无论输入数据有多长、重复多少遍都可以利用惰性求值工作,不会像辣鸡过程式一样卡住(数据窗口太大了)
Forwarded from Deleted Account
递归惰性求值的 combination,你调用
const fp = require('./fp');
let xs = fp.stm.lazyCombination("abcdefghijklmnopqrstuvwxyz")(1926);
fp.stmaux.take(100, a);
都可以,反观非惰性求值版本的,反向的数据窗口大小太大了,根本算不出来。(不过递归也可能栈溢... 只能都惰性求值最好)const fp = require('./fp');
let xs = fp.stm.combination("abcdefghijklmnopqrstuvwxyz")(1926);
fp.stmaux.take(100, a); // 卡住
/tmp/duangsuse.sock
行!很完美
FP.js 里面写的那个 chainRec.... 左递归/右递归解析器不是那个用来解析二元运算的,就是普通构造,而且 parserc 的情况也是不可能只是使用这种组合子去组合....
可以编写表达式链解析器,很麻烦的,而且不支持多字符(两个以上就无法撤销或者说继续 lookahead 了,如果最后表达式链解析到 sep 子结构失败的时候,是要撤销掉字符读取的)的 operator (不过利用 Feed 就很简单了)
那个只是玩具而已,就是解析 日卡卡 成(right) [日, [卡, [卡, []]] 这种东西(Array.prototype.concat, [])是为这种和倒序什么的... 反正就是玩具而已
可以编写表达式链解析器,很麻烦的,而且不支持多字符(两个以上就无法撤销或者说继续 lookahead 了,如果最后表达式链解析到 sep 子结构失败的时候,是要撤销掉字符读取的)的 operator (不过利用 Feed 就很简单了)
那个只是玩具而已,就是解析 日卡卡 成(right) [日, [卡, [卡, []]] 这种东西(Array.prototype.concat, [])是为这种和倒序什么的... 反正就是玩具而已
/tmp/duangsuse.sock
这堆松耦合的函数,通过一个通用的返回值模板,居然真的可以正常工作!
question: 如何使用 FP.js 的 parserc 模块构造一个简单的解析器呢?
FP.js 提供了(至少是对我来说...)很方便的一些结构和函数(比如,dropWhile、char Feeder、seq parserc、lookAhead1、andThen 什么的)
可是作为它的使用者,不会用 也是没有办法的。
下面 教一些简单的 FP.js parserc 简易(迫真... 这个不是很优雅,准确的说我有点受到 Python 之前曾用 LL(1) 的误导)方式
并且,会使用 FP.js 的解析组合子模块,解构 GeekSpec 精简版的代码(
FP.js 提供了(至少是对我来说...)很方便的一些结构和函数(比如,dropWhile、char Feeder、seq parserc、lookAhead1、andThen 什么的)
可是作为它的使用者,不会用 也是没有办法的。
下面 教一些简单的 FP.js parserc 简易(迫真... 这个不是很优雅,准确的说我有点受到 Python 之前曾用 LL(1) 的误导)方式
并且,会使用 FP.js 的解析组合子模块,解构 GeekSpec 精简版的代码(
/tmp/duangsuse.sock
question: 如何使用 FP.js 的 parserc 模块构造一个简单的解析器呢? FP.js 提供了(至少是对我来说...)很方便的一些结构和函数(比如,dropWhile、char Feeder、seq parserc、lookAhead1、andThen 什么的) 可是作为它的使用者,不会用 也是没有办法的。 下面 教一些简单的 FP.js parserc 简易(迫真... 这个不是很优雅,准确的说我有点受到 Python 之前曾用 LL(1) 的误导)方式 并且,会使用 FP.js 的解析组合子模块,解构…
首先,既然要使用 FP.js 的解析组合子模块,首先要知道它暴露了什么接口。
此外:
Feeder 是输入字符/对象流的称呼,它上面可以使用:
lastIsCR / lastIsLF / lastIsCRLF / eof / hasNext 判断方法
duplast / reset1Tho 方法(reset1Tho 是给一些不消耗字符但是父 解析器会默认 next 的解析器做一个额外的副作用:保存一个字符)
loc / desc 方法:用于描述
logs / leave 方法:解析组合子的上下文支持利用栈,如果提供了栈,则 logs 和 leave 方法对应 push 和 pop。否则,什么都不肝(用于区分解析模式是调试与否)
lastItem / nextItem : lookAhead1 迭代器的标准数据视窗
next / nextNext : 移动数据视窗
>
>
FP.js 的 Feeder 对象会自动为你解决 CR / LF / CRLF 的换行符问题,三者中任意一种都能正确处理,不会出现行号不一致的情况。
>
>
—— 解析器们
上面提到的 wsP(), ws0P(), kwP() 什么的,全都是可以复用松耦合的解析器。
绝大部分解析器(可能解析失败的那种),为了提供一个良好的用户界面,都存在着一个
+ ws0P 是不可能失败的解析器(因为它要解析空格的数目,但是也可以是 0 个,就是说不可能解析不成功)
+ wsP 接受 msg 和 fmt,若是感受不到空格,返回
例子:
(tips: 如果项目本身定义的四元素 CR/LF/Tab/Space 不满足你对空格的需求,可使用 setWs 函数改变默认集合,当然我修改后直接 push 缺失的项目也可以)
+ elemP 接受 (iset, m, fmt),其中 fmt 默认为
>
上面的例子不是字符流的(FP.js 的解析组合子有意泛化一些),下面是一个字符流的例子:
>
> pp('不和谐')
(显然这只有一项,不过过会就能够看到松耦合组合子的强大组合能力了)
+ kwP(kwstr, msg, fmt) 是关键字的解析组合子。
默认 fmt 是
>
+ charP (ch, m, fmt)
这个家伙消耗一个字符,返回一个字符。
+ satisfy(p, m, fmt) 这是最后一个弟弟(划掉)解析器了,就是取一个项目,判断其是否符合条件谓词 p,若符合则成功,否则失败。
seq, possible, lookahead1, someFold, manyFold 这是用于组合子解析器的单位satisfy, charP, elemP, kwP, wsP, ws0P 这是用于 seq 和 many / some 解析器的单位此外:
Feeder 是输入字符/对象流的称呼,它上面可以使用:
lastIsCR / lastIsLF / lastIsCRLF / eof / hasNext 判断方法
duplast / reset1Tho 方法(reset1Tho 是给一些不消耗字符但是父 解析器会默认 next 的解析器做一个额外的副作用:保存一个字符)
loc / desc 方法:用于描述
logs / leave 方法:解析组合子的上下文支持利用栈,如果提供了栈,则 logs 和 leave 方法对应 push 和 pop。否则,什么都不肝(用于区分解析模式是调试与否)
lastItem / nextItem : lookAhead1 迭代器的标准数据视窗
next / nextNext : 移动数据视窗
const p = require('./fp').parserc;
比如说:let f = new p.Feeder('Hello, cruel world.\n(but...)');
> f.desc()
'<feed>:1:0 (pos=0@<begin>)'>
f.loc()
'<feed>:1:0' >
f.next()
'H'>
f.loc()
'<feed>:1:1'
>
while (f.next() != '.');
> f.desc()
"<feed>:1:19 (pos=19@`.', next=`\n')" FP.js 的 Feeder 对象会自动为你解决 CR / LF / CRLF 的换行符问题,三者中任意一种都能正确处理,不会出现行号不一致的情况。
>
while (f.next() != '.');
> f.desc()
"<feed>:2:5 (pos=25@`.', next=`.')"FP.js 的 Feeder 本身就是一个流的封装,所以你可以使用所有 FP.js 提供的流操作:
const s = require('./fp').stm;
—>
s.dropWhile(x => x === '.', f.stream);
[ '.', '.' ]>
s.desc()
"<feed>:2:5 (pos=25@<eof>, next=`.')"不过,使用这样的东西会打破 Feeder 的封装,所以正确的方法是引入
fp.fn.bound, 然后把 bound(s, 'next') 传入需要流参数的地方—— 解析器们
上面提到的 wsP(), ws0P(), kwP() 什么的,全都是可以复用松耦合的解析器。
绝大部分解析器(可能解析失败的那种),为了提供一个良好的用户界面,都存在着一个
fmt 函数和 error message 的系统:+ ws0P 是不可能失败的解析器(因为它要解析空格的数目,但是也可以是 0 个,就是说不可能解析不成功)
+ wsP 接受 msg 和 fmt,若是感受不到空格,返回
pfail(fmt(feeder, msg)), fmt 的默认值为返回 msg.例子:
var ps = p.wsP('有空格的话会好很多');
var pp = p.run(ps);
> pp('')Either { inner: '有空格的话会好很多', left: true }
> pp(' ')Either { inner: 1, left: false }
> pp(' ')Either { inner: 3, left: false }
(tips: 如果项目本身定义的四元素 CR/LF/Tab/Space 不满足你对空格的需求,可使用 setWs 函数改变默认集合,当然我修改后直接 push 缺失的项目也可以)
+ elemP 接受 (iset, m, fmt),其中 fmt 默认为
(s.eof()? 'Unexpected EOF. ':'') + 'Expecting one of ['+iset.join(', ')+']' +_sp(m)
例如,>
ps = p.elemP('富强x民主x文明x和谐x自由x平等x公正x法治x爱国x敬业x诚信x友善'.split('x'), '不能是不和谐的项目呢。');
[Function: containsP]>
var pp = p.run(ps);
> pp(['富强'])Either { inner: '富强', left: false }
> pp(['不富强'])Either {
inner:
'Expecting one of [富强, 民主, 文明, 和谐, 自由, 平等, 公正, 法治, 爱国, 敬业, 诚信, 友善] 不能是不和谐的项目呢。',
left: true }
> pp(['艹'])Either {
inner:
'Expecting one of [富强, 民主, 文明, 和谐, 自由, 平等, 公正, 法治, 爱国, 敬业, 诚信, 友善] 不能是不和谐的项目呢。',
left: true }
上面的例子不是字符流的(FP.js 的解析组合子有意泛化一些),下面是一个字符流的例子:
>
ps = p.elemP('江胡习赵王董刘波64'.split(''), '非常抱歉,输入过于和谐呢。'); pp = p.run(ps);
[Function: runParser]> pp('不和谐')
Either {
inner:
'Expecting one of [江, 胡, 习, 赵, 王, 董, 刘, 波, 6, 4] 非常抱歉,输入过于和谐呢。',
left: true }
> pp('江不和谐')
Either { inner: '江', left: false }(显然这只有一项,不过过会就能够看到松耦合组合子的强大组合能力了)
+ kwP(kwstr, msg, fmt) 是关键字的解析组合子。
默认 fmt 是
(s.eof()? 'Unexpected EOF. ':'') + 'Expecting keyword <'+str+'>@'+i+ _sp(m)
失败的时候会用 feeder 和 i 来(失败的匹配位置)调用 fmt 函数>
ps = p.kwP('你们啊,Naive!', 'Too young.'); pp = p.run(ps);
> pp('你们')Left('Unexpected EOF. Expecting keyword <你们啊,Naive!>@2 Too young.')
> pp('你们啊,Naive!')Right('你们啊,Naive!')
很多大一点的组合,会抛出 fmt 返回的异常对象+ charP (ch, m, fmt)
这个家伙消耗一个字符,返回一个字符。
> ps = p.charP('🐸'); pp = p.run(ps);
> pp('蛤')Left('expecting 🐸')
> pp('🐸')Right('🐸')
Too young! + satisfy(p, m, fmt) 这是最后一个弟弟(划掉)解析器了,就是取一个项目,判断其是否符合条件谓词 p,若符合则成功,否则失败。
satisfy 因为 FP.js parserc 已经有很多字符流的弟弟解析器,不能在解析普通字符流时很好地发挥实力,必须在解析诸如命令行参数 argv 的时候,才能派上用场。ps = p.satisfy(x => x.startsWith('--'), '需要一个 -- 开头的参数'); pp = p.run(ps);
> pp(['foo.txt'])Left('需要一个 -- 开头的参数')
> pp(['--f', 'foo.txt'])Right('--f')
—总是觉得曾经在酷安过的一些人变的奇怪了呢... 为什么 我应该以 『您』 来称呼... 为什么我说了几次还是被称为“您”
难道我就真的有这么令人讨厌?!因为我总是炫耀自己的技术,而其实我不懂的东西还有很多,所以我值得被人... 如此特殊的看待?
难道我就真的有这么令人讨厌?!因为我总是炫耀自己的技术,而其实我不懂的东西还有很多,所以我值得被人... 如此特殊的看待?
/tmp/duangsuse.sock
总是觉得曾经在酷安过的一些人变的奇怪了呢... 为什么 我应该以 『您』 来称呼... 为什么我说了几次还是被称为“您” 难道我就真的有这么令人讨厌?!因为我总是炫耀自己的技术,而其实我不懂的东西还有很多,所以我值得被人... 如此特殊的看待?
This media is not supported in your browser
VIEW IN TELEGRAM