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 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 }; } } }