862 lines
17 KiB
JavaScript
862 lines
17 KiB
JavaScript
function cons(a, b) {
|
|
return [a, b];
|
|
}
|
|
|
|
function *listIter(l) {
|
|
while (l) {
|
|
yield l[0];
|
|
l = l[1];
|
|
}
|
|
}
|
|
|
|
function keySymVals(obj) {
|
|
return [...Object.getOwnPropertyNames(obj),...Object.getOwnPropertySymbols(obj)]
|
|
.map(e => [e,obj[e]]);
|
|
}
|
|
|
|
class EaterBase {
|
|
result = {}; ownsResult = true;
|
|
|
|
static #bannedSymbols = new WeakSet;
|
|
|
|
static isBannedSymbol(sym) {
|
|
return this.#bannedSymbols.has(sym);
|
|
}
|
|
|
|
static banSymbol(sym) {
|
|
this.#bannedSymbols.add(sym);
|
|
}
|
|
|
|
inherit(...from) {
|
|
from = from.filter(e => e);
|
|
if (!from.length) from = [this];
|
|
if (from.length == 1) {
|
|
if (from[0] == this) return;
|
|
this.result = from[0].result;
|
|
from[0].ownsResult = false;
|
|
this.ownsResult = false;
|
|
} else {
|
|
let iter = from.map(e => ({ result: e.result, eater: e })).values();
|
|
do {
|
|
let val = iter.next();
|
|
if (val.done) break;
|
|
val.value.eater.ownsResult = false;
|
|
this.result = { ...val.value.result };
|
|
this.ownsResult = true;
|
|
} while (((o)=>{
|
|
for (let i in o) return false;
|
|
if (Object.getOwnPropertySymbols(o).length) return false;
|
|
return true;
|
|
})(this.result))
|
|
for (let k of Object.getOwnPropertySymbols(this.result)) {
|
|
if (EaterBase.isBannedSymbol(k))
|
|
delete this.result[k];
|
|
}
|
|
let vals = iter.flatMap(e => (e.eater.ownsResult=false,keySymVals(e.result)))
|
|
.filter(([k,v]) => !EaterBase.isBannedSymbol(k));
|
|
for (let [k, v] of vals) {
|
|
v.type == 'list' ? [...listIter(v.data)]
|
|
.reverse()
|
|
.forEach(e => this.addResult(k, e))
|
|
: this.mapResult(k, () => v.data);
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
addResult(k, v) {
|
|
if (!this.ownsResult) {
|
|
this.result = { ...this.result };
|
|
this.ownsResult = true;
|
|
}
|
|
if ((this.result[k]?.type??'list') != 'list') {
|
|
return false;
|
|
}
|
|
this.result[k] = {
|
|
type: 'list',
|
|
data: cons(v, this.result[k]?.data)
|
|
};
|
|
return true;
|
|
}
|
|
|
|
setResult(k, v) {
|
|
if (!this.ownsResult) {
|
|
this.result = { ...this.result };
|
|
this.ownsResult = true;
|
|
}
|
|
if (this.result[k]) {
|
|
return false;
|
|
}
|
|
this.result[k] = {
|
|
type: 'value',
|
|
data: v
|
|
};
|
|
if (v == null)
|
|
delete this.result[k];
|
|
return true;
|
|
}
|
|
|
|
getResult(k) {
|
|
if (this.result[k]?.type == 'value')
|
|
return this.result[k].data;
|
|
else if (this.result[k]?.type == 'list')
|
|
return listIter(this.result[k].data);
|
|
return null;
|
|
}
|
|
|
|
mapResult(k, f) {
|
|
if (!this.ownsResult) {
|
|
this.result = { ...this.result };
|
|
this.ownsResult = true;
|
|
}
|
|
let v = f(this.getResult(k));
|
|
if (v == null && (k in this.result))
|
|
delete this.result[k]
|
|
else if (v != null)
|
|
this.result[k] = {
|
|
type: 'value',
|
|
data: v
|
|
};
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
class EaterResult extends EaterBase {
|
|
build() {
|
|
return Object.fromEntries(
|
|
Object.entries(this.result)
|
|
.map(([k,v]) => ([k,({
|
|
'value': v => v.data,
|
|
'list': v => [...listIter(v.data)].reverse(),
|
|
})[v.type](v)]))
|
|
)
|
|
}
|
|
}
|
|
|
|
class TransitionController {
|
|
#decider = null;
|
|
next = () => {
|
|
return this.#decider();
|
|
};
|
|
runWith(f, decider) {
|
|
try {
|
|
let prevDecider = this.#decider;
|
|
this.#decider = decider;
|
|
return f();
|
|
} finally {
|
|
this.#decider = prevDecider;
|
|
}
|
|
}
|
|
setDecider(decider) { this.#decider = decider; }
|
|
}
|
|
|
|
export class Eater extends EaterBase {
|
|
constructor() {
|
|
super();
|
|
}
|
|
|
|
#autoInherit(o, par, ...other) {
|
|
par = par ?? [this];
|
|
if (par instanceof Eater || par instanceof EaterResult)
|
|
par = [par];
|
|
o.inherit(...par, ...other, o);
|
|
}
|
|
|
|
rawNext;
|
|
get next() {
|
|
let next = this.rawNext;
|
|
if (typeof next == 'function')
|
|
return (...par) => {
|
|
let o = next.apply(this);
|
|
if (!o)
|
|
o = new EaterResult;
|
|
this.#autoInherit(o, ...par);
|
|
if (o instanceof Eater)
|
|
o.start();
|
|
return o;
|
|
};
|
|
else if (next == null) {
|
|
return (...par) => {
|
|
let o = new EaterResult;
|
|
this.#autoInherit(o, ...par);
|
|
return o;
|
|
}
|
|
}
|
|
return () => next;
|
|
}
|
|
set next(v) {
|
|
if (v == null)
|
|
v = undefined;
|
|
else if (!(
|
|
typeof v == 'function' ||
|
|
v instanceof Eater ||
|
|
v instanceof EaterResult
|
|
)) {
|
|
throw new TypeError('Invalid value for \'Eater.next\'');
|
|
}
|
|
this.rawNext = v;
|
|
}
|
|
|
|
isDone() { return true; }
|
|
peek(c) { return false; }
|
|
eat(c) { return this.peek(c); }
|
|
start() {}
|
|
end(c) {}
|
|
|
|
eof() {
|
|
let eater = this, ok;
|
|
({ ok, eater } = this.endResolve());
|
|
if (ok && !(eater instanceof EaterResult))
|
|
throw new Error('EOF but still Eater');
|
|
return ok
|
|
? eater
|
|
: false;
|
|
}
|
|
|
|
eatResolve(c) {
|
|
let eater = this, ok;
|
|
({ ok, eater } = this.endResolve(c));
|
|
if (!ok)
|
|
return {
|
|
ate: false,
|
|
eater: eater instanceof EaterResult ? eater
|
|
: (e => e instanceof EaterResult ? e : false)(eater.endResolve())
|
|
};
|
|
if (!eater.eat(c))
|
|
throw new Error('peek() reported edibility but eat() failed');
|
|
return { ate: true, eater: eater.resolve() };
|
|
}
|
|
|
|
endResolve(...c) {
|
|
let o = this;
|
|
let e = !!c.length;
|
|
let check = e
|
|
? () => o instanceof Eater && o.peek(...c)
|
|
: () => o instanceof EaterResult;
|
|
let checked = null;
|
|
|
|
while (1) {
|
|
if (checked ? checked.res : check()) {
|
|
return { ok: true, eater: o };
|
|
}
|
|
|
|
checked = null;
|
|
|
|
if (!(o instanceof Eater))
|
|
return { ok: false, eater: o };
|
|
|
|
o.end(...c);
|
|
let old = o;
|
|
o = o.resolve();
|
|
|
|
if ((checked = { res: check() }).res) {
|
|
continue;
|
|
}
|
|
|
|
if (old == o)
|
|
return { ok: false, eater: o };
|
|
}
|
|
}
|
|
|
|
resolve() {
|
|
if (this.isDone()) {
|
|
let v = this.next();
|
|
if (v instanceof Eater)
|
|
v = v.resolve();
|
|
return v;
|
|
}
|
|
return this;
|
|
}
|
|
}
|
|
|
|
class EatGate extends Eater {
|
|
#d = false; #fn;
|
|
constructor(fn) {
|
|
super();
|
|
this.#fn = fn;
|
|
}
|
|
isDone() { return this.#d; }
|
|
peek(c) { return false; }
|
|
eat(c) { return false; }
|
|
end(...c) {
|
|
this.#fn(this);
|
|
this.#d = true;
|
|
}
|
|
}
|
|
|
|
class AbortEater extends Eater {
|
|
isDone() { return false; }
|
|
peek(c) { return false; }
|
|
}
|
|
|
|
class DisjunctionEater extends Eater {
|
|
#u = []; #f = []; #d = false; #ctrl; #count=0;
|
|
#syms = { count: Symbol('DisjunctionEater.count'), };
|
|
constructor(ctrl, ...u) {
|
|
super();
|
|
ctrl.setDecider(() => {
|
|
let o = new EatGate((o) => {
|
|
o.setResult(this.#syms.count, this.#count);
|
|
});
|
|
o.next = this.rawNext;
|
|
return o;
|
|
});
|
|
this.#ctrl = ctrl;
|
|
this.#u = [...u];
|
|
}
|
|
#checkFinish() {
|
|
if (this.#f.length && !this.#u.length) {
|
|
this.#d = true;
|
|
let next = this.#f.pop();
|
|
for (let i in this.#syms) {
|
|
EaterBase.banSymbol(this.#syms[i]);
|
|
}
|
|
this.next = next;
|
|
this.#f = [];
|
|
}
|
|
}
|
|
start() {
|
|
let nextU = [];
|
|
let nextF = [];
|
|
for (let o of this.#u) {
|
|
o.inherit(this);
|
|
o.start();
|
|
o = o.resolve();
|
|
let oi = o.getResult(this.#syms.count);
|
|
if (oi == null)
|
|
nextU.push(o);
|
|
else
|
|
nextF[oi] ??= o;
|
|
}
|
|
this.#u = nextU;
|
|
this.#f = nextF;
|
|
this.#checkFinish();
|
|
}
|
|
end(...c) {
|
|
let nextU = [];
|
|
let nextF = [];
|
|
for (let i in this.#f) {
|
|
let o = this.#f[i];
|
|
if (o instanceof EaterResult) {
|
|
let oi = o.getResult(this.#syms.count);
|
|
nextF[oi] ??= o;
|
|
continue;
|
|
}
|
|
let ok;
|
|
({ ok, eater: o } = o.endResolve(...c));
|
|
let oi = o.getResult(this.#syms.count);
|
|
nextF[oi] ??= o;
|
|
}
|
|
for (let o of this.#u) {
|
|
let ok;
|
|
({ ok, eater: o } = o.endResolve(...c));
|
|
let oi = o.getResult(this.#syms.count);
|
|
if (oi == null)
|
|
nextU.push(o);
|
|
else
|
|
nextF[oi] ??= o;
|
|
}
|
|
this.#u = nextU;
|
|
this.#f = nextF;
|
|
this.#checkFinish();
|
|
}
|
|
peek(c) {
|
|
for (let o of this.#u) {
|
|
if (o.peek(c))
|
|
return true;
|
|
}
|
|
for (let i in this.#f) {
|
|
let o = this.#f[i];
|
|
if (o instanceof EaterResult)
|
|
continue;
|
|
if (o.peek(c))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
isDone() {
|
|
return this.#d;
|
|
}
|
|
eat(c) {
|
|
if (!this.#u.length)
|
|
return false;
|
|
let nextU = [];
|
|
let nextF = [];
|
|
for (let i in this.#f) {
|
|
let eater = this.#f[i];
|
|
if (!(eater instanceof Eater)) {
|
|
continue;
|
|
}
|
|
let ate;
|
|
({ ate, eater } = eater.eatResolve(c));
|
|
let oi = eater.getResult(this.#syms.count);
|
|
if (ate) {
|
|
nextF[oi] ??= eater;
|
|
}
|
|
}
|
|
for (let eater of this.#u) {
|
|
let ate;
|
|
({ ate, eater } = eater.eatResolve(c));
|
|
if (ate) {
|
|
let oi = eater.getResult(this.#syms.count);
|
|
if (oi == null) {
|
|
nextU.push(eater);
|
|
} else {
|
|
nextF[oi] ??= eater;
|
|
}
|
|
}
|
|
}
|
|
this.#u = nextU;
|
|
this.#f = nextF;
|
|
let ok = this.#u.length || this.#f.length
|
|
this.#checkFinish();
|
|
this.#count++;
|
|
return ok;
|
|
}
|
|
}
|
|
|
|
class StringEater extends Eater {
|
|
#s; #i=0; constructor(s) { super(); this.#s = s; }
|
|
isDone() {
|
|
return this.#i >= this.#s.length;
|
|
}
|
|
peek(c) {
|
|
return this.#s[this.#i] == c;
|
|
}
|
|
eat(c) {
|
|
let ok = this.peek(c);
|
|
this.#i++;
|
|
return ok;
|
|
}
|
|
}
|
|
|
|
class RepeatEater extends Eater {
|
|
#min; #max; #spawn; #ctrl; #sym;
|
|
#cur = null; #cont = null; #ctrlSetNext;
|
|
get #i() { return this.getResult(this.#sym.i) ?? 0; }
|
|
constructor(ctrl, spawn, min, max, sym={ i: Symbol('RepeatEater.i'), over: Symbol('RepeatEater.over') }) {
|
|
super();
|
|
this.#ctrl = ctrl;
|
|
let next;
|
|
this.#ctrlSetNext = x => next = x;
|
|
ctrl.setDecider(() => {
|
|
let u = new (class extends Eater {
|
|
start() {
|
|
this.mapResult(sym.i, i => (i??0) + 1);
|
|
this.setResult(sym.over, true);
|
|
let next = this.next(this);
|
|
next.sus = true;
|
|
this.next = next;
|
|
}
|
|
});
|
|
u.next = () => {
|
|
let o = new RepeatEater(ctrl, spawn, min, max, sym);
|
|
o.next = next;
|
|
o.id=Math.random();
|
|
return o;
|
|
}
|
|
return u
|
|
});
|
|
this.#spawn = function(...par) {
|
|
let ns = spawn()
|
|
ns.inherit(...par);
|
|
ns.mapResult(sym.over, () => null);
|
|
ns.start();
|
|
return ns;
|
|
};
|
|
this.#min = min ?? 0;
|
|
this.#max = max ?? Infinity;
|
|
this.#sym = sym;
|
|
}
|
|
#ended = false;
|
|
#check() {
|
|
if (this.#cur?.getResult(this.#sym.over)) {
|
|
this.#ended = true;
|
|
this.next = this.#cur;
|
|
return;
|
|
}
|
|
if (((!!this.#cur) + (!!this.#cont)) == 1) {
|
|
this.#ended = true;
|
|
this.next = this.#cur || this.#cont;
|
|
return;
|
|
}
|
|
}
|
|
start() {
|
|
this.#ctrlSetNext(this.rawNext);
|
|
if (this.#i < this.#max)
|
|
this.#cur = this.#spawn(this);
|
|
if (this.#i >= this.#min) {
|
|
let [over, i] = ['over','i'].map(e => this.getResult(this.#sym[e]));
|
|
this.mapResult(this.#sym.over, () => null);
|
|
this.mapResult(this.#sym.i, () => null);
|
|
this.#cont = this.next();
|
|
this.mapResult(this.#sym.over, () => over);
|
|
this.mapResult(this.#sym.i, () => i);
|
|
}
|
|
this.#check();
|
|
}
|
|
isDone() {
|
|
return this.#ended;
|
|
}
|
|
peek(c) {
|
|
return (
|
|
(this.#cur instanceof Eater && this.#cur.peek(c)) ||
|
|
(this.#cont instanceof Eater && this.#cont.peek(c))
|
|
)
|
|
}
|
|
#forEaters(f,cl) {
|
|
if (this.#cur instanceof Eater) {
|
|
let etc, eater, ok;
|
|
({ eater, ...etc } = f(this.#cur));
|
|
ok = etc.ok ?? etc.ate;
|
|
this.#cur = ok ? eater : null;
|
|
} else if (cl) {
|
|
this.#cur = null;
|
|
}
|
|
if (this.#cont instanceof Eater) {
|
|
let etc, eater, ok;
|
|
({ eater, ...etc } = f(this.#cont));
|
|
ok = etc.ok ?? etc.ate;
|
|
this.#cont = ok ? eater : null;
|
|
} else if (cl) {
|
|
this.#cont = null;
|
|
}
|
|
this.#check();
|
|
}
|
|
eat(c) {
|
|
this.#forEaters(o => o.eatResolve(c), 1)
|
|
return this.#cur || this.#cont
|
|
}
|
|
end(...c) {
|
|
this.#forEaters(o => o.endResolve(...c), c.length);
|
|
}
|
|
}
|
|
|
|
class CaptureEater extends Eater {
|
|
#s; #d; #opts;
|
|
#syms = { eaten: Symbol('CaptureEater.eaten') };
|
|
constructor(s, { key, add, finish, multi, ctrl }) {
|
|
super();
|
|
this.#s = s;
|
|
ctrl.setDecider(() => {
|
|
let o = new EatGate(o => {
|
|
let ok;
|
|
if (this.#opts.multi)
|
|
ok = o.addResult(this.#opts.key, this.#opts.finish())
|
|
else
|
|
ok = o.setResult(this.#opts.key, this.#opts.finish());
|
|
o.setResult(this.#syms.eaten, true);
|
|
if (!ok)
|
|
o.next = () => new AbortEater;
|
|
});
|
|
o.next = this.rawNext;
|
|
return o;
|
|
});
|
|
this.#opts = { key, add, finish, multi, ctrl };
|
|
}
|
|
isDone() { return this.#d; }
|
|
#check() {
|
|
if (this.#s.getResult(this.#syms.eaten) != null) {
|
|
this.#d = true;
|
|
for (let i in this.#syms) {
|
|
EaterBase.banSymbol(this.#syms[i]);
|
|
}
|
|
this.next = this.#s;
|
|
}
|
|
}
|
|
start() {
|
|
this.#s.inherit(this);
|
|
this.#s.start();
|
|
this.#s = this.#s.resolve();
|
|
this.#check();
|
|
}
|
|
end(...c) {
|
|
({ eater: this.#s } = this.#s.endResolve(...c));
|
|
this.#check();
|
|
}
|
|
peek(c) { return (this.#s instanceof Eater) && this.#s.peek(c); }
|
|
eat(c) {
|
|
let ate;
|
|
({ ate, eater: this.#s } = this.#s.eatResolve(c));
|
|
this.#opts.add(c);
|
|
this.#check();
|
|
return ate;
|
|
}
|
|
}
|
|
|
|
class CharsetEater extends Eater {
|
|
#f; #d = false;
|
|
constructor(f) {
|
|
super();
|
|
this.#f = f;
|
|
}
|
|
isDone() { return this.#d; }
|
|
peek(c) { return this.#f(c); }
|
|
eat(c) {
|
|
if (this.peek(c))
|
|
return this.#d = true;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
class ResultEditEater extends Eater {
|
|
#f; constructor(f) { super(); this.#f = f; }
|
|
isDone() { return true; }
|
|
peek(c) { return false; }
|
|
start() {
|
|
let ret = this.#f({
|
|
set: (k, v) => {
|
|
this.mapResult(k, () => v);
|
|
},
|
|
get: (k) => {
|
|
return this.getResult(k);
|
|
},
|
|
add: (k, v) => {
|
|
return this.addResult(k, v);
|
|
}
|
|
});
|
|
this.#f = null;
|
|
if ((!ret) && (ret != null)) {
|
|
this.next = () => new AbortEater;
|
|
}
|
|
}
|
|
}
|
|
|
|
function lastfun(f) {
|
|
while (f.next) {
|
|
f = f.next;
|
|
}
|
|
return f;
|
|
}
|
|
|
|
function _nwrapper() {
|
|
let wf;
|
|
wf = Object.assign((...arg) => {
|
|
let v = wf.inner(...arg);
|
|
v.next = wf.next;
|
|
if (wf.dbg)
|
|
v.dbg = wf.dbg;
|
|
return v;
|
|
}, {
|
|
inner: null,
|
|
next: null,
|
|
prev: null,
|
|
regexoidal: true,
|
|
});
|
|
return wf;
|
|
}
|
|
|
|
function nwrap(f, dbg) {
|
|
let wf = _nwrapper();
|
|
wf.inner = f;
|
|
return wf;
|
|
}
|
|
|
|
function nwrapClone(wf, onLast) {
|
|
let cf = nwrap(wf.inner);
|
|
let fcf = cf;
|
|
cf.prev = wf;
|
|
while (wf) {
|
|
if (wf.next) {
|
|
cf.next = nwrap(wf.next.inner);
|
|
cf.next.prev = cf;
|
|
}
|
|
|
|
wf = wf.next;
|
|
if ((!wf) && onLast) {
|
|
onLast(cf);
|
|
}
|
|
cf = cf.next;
|
|
}
|
|
return fcf;
|
|
}
|
|
|
|
function checkprev(f) {
|
|
if (f.prev) {
|
|
throw new Error('cannot use the same regex element more than once');
|
|
}
|
|
}
|
|
|
|
let r = {
|
|
seq(...elem) {
|
|
elem = elem.map(e => {
|
|
if (typeof e == 'function' && e.regexoidal) {
|
|
return e;
|
|
} else if (e.length != null) {
|
|
return nwrap(() => new StringEater(e));
|
|
} else {
|
|
throw new Error('bad element: '+e);
|
|
}
|
|
});
|
|
if (!elem.length) {
|
|
elem = [nwrap(() => new Eater())];
|
|
}
|
|
let lastfs = elem.map(e => lastfun(e));
|
|
for (let i=0; i<elem.length; i++) {
|
|
let [f, lf] = [elem[i], lastfs[i]];
|
|
checkprev(f);
|
|
if (i < elem.length - 1){
|
|
lf.next = elem[i+1];
|
|
}
|
|
if (i > 0) {
|
|
f.prev = lastfs[i-1];
|
|
}
|
|
}
|
|
return elem[0];
|
|
},
|
|
|
|
any(...elem) {
|
|
elem = elem.map(e => this.seq(e));
|
|
let f = nwrap(() => {
|
|
let ctrl = new TransitionController;
|
|
return new DisjunctionEater(ctrl,
|
|
...(elem
|
|
.map(e => nwrapClone(e, e => e.next = ctrl.next))
|
|
.map(e => e())
|
|
)
|
|
)
|
|
});
|
|
elem.forEach(e => (
|
|
checkprev(e),
|
|
e.prev = f
|
|
));
|
|
return f;
|
|
},
|
|
|
|
rep([min, max], ...elem) {
|
|
let spawn = this.seq(...elem);
|
|
let f = nwrap(() => {
|
|
let ctrl = new TransitionController;
|
|
return new RepeatEater(ctrl,
|
|
nwrapClone(spawn, e => e.next = ctrl.next),
|
|
min, max
|
|
)
|
|
});
|
|
checkprev(spawn);
|
|
spawn.prev = f;
|
|
return f;
|
|
},
|
|
|
|
abort() {
|
|
return nwrap(() => new AbortEater);
|
|
},
|
|
|
|
map(f) {
|
|
return nwrap(() => new ResultEditEater(f));
|
|
},
|
|
|
|
chars(pred) {
|
|
return nwrap(() => new CharsetEater(pred));
|
|
},
|
|
|
|
...((o) => Object.fromEntries(
|
|
Object.entries(o)
|
|
.flatMap(([k,v]) => [
|
|
[k, function(...arg) { return v.call(this, false, ...arg); }],
|
|
[k+'Multi', function(...arg) { return v.call(this, true, ...arg); }],
|
|
])
|
|
))({
|
|
mark(multi, key, value=true) {
|
|
return nwrap(() => {
|
|
let ctrl = new TransitionController;
|
|
return new CaptureEater((e => (e.next = ctrl.next, e))(new Eater), {
|
|
key,
|
|
add: () => {},
|
|
finish: () => value,
|
|
multi,
|
|
ctrl,
|
|
})
|
|
});
|
|
},
|
|
|
|
capture(multi, key, ...elem) {
|
|
let s = this.seq(...elem);
|
|
let f = nwrap(() => {
|
|
let ctrl = new TransitionController;
|
|
let val = [];
|
|
return new CaptureEater(nwrapClone(s, e => e.next = ctrl.next)(), {
|
|
key,
|
|
add: c => val.push(c),
|
|
finish: () => val.join(''),
|
|
multi,
|
|
ctrl,
|
|
})
|
|
});
|
|
checkprev(s);
|
|
s.prev = f;
|
|
return f;
|
|
},
|
|
}),
|
|
|
|
custom(f, ...arg) {
|
|
return nwrap(() => f(...arg));
|
|
},
|
|
};
|
|
|
|
for (let k in r)
|
|
r[k] = r[k].bind(r);
|
|
|
|
export function regexoid(f) {
|
|
let spawn = r.seq(f(r));
|
|
|
|
return () => {
|
|
let o = spawn();
|
|
o.start();
|
|
o = o.resolve();
|
|
|
|
let eofResult = null;
|
|
|
|
return function(chunk) {
|
|
let i = 0;
|
|
if (!chunk) {
|
|
if (eofResult) {
|
|
return eofResult;
|
|
}
|
|
o = ((o instanceof Eater) ? o.eof() : o) || false;
|
|
return {
|
|
done: true,
|
|
match: o ? o.build() : o,
|
|
count: 0,
|
|
}
|
|
} else {
|
|
let ret;
|
|
for (let c of chunk) {
|
|
if (eofResult) {
|
|
eofResult = null;
|
|
break;
|
|
}
|
|
i++;
|
|
if (o instanceof Eater) {
|
|
let ate;
|
|
({ ate, eater: o } = o.eatResolve(c));
|
|
if (!ate) {
|
|
let v = o;
|
|
o = null;
|
|
return {
|
|
done: true,
|
|
match: v ? v.build() : v,
|
|
count: i - 1,
|
|
}
|
|
}
|
|
}
|
|
if (o instanceof EaterResult) {
|
|
ret = eofResult = {
|
|
done: true,
|
|
match: o.build(),
|
|
count: i,
|
|
};
|
|
o = null;
|
|
}
|
|
}
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
return {
|
|
done: false,
|
|
count: i
|
|
};
|
|
}
|
|
}
|
|
}
|