syndicate-2017/js/src/trie.js

979 lines
27 KiB
JavaScript

"use strict";
var Immutable = require("immutable");
var Struct = require('./struct.js');
var $Special = require('./special.js');
function die(message) {
throw new Error(message);
}
///////////////////////////////////////////////////////////////////////////
// Trie representations
//
// A Trie is one of
// - emptyTrie
// - an instance of $Success
// - an instance of $Branch
var emptyTrie = new $Special("Mt");
function is_emptyTrie(m) {
return m === emptyTrie;
}
function $Success(value) {
this.value = value;
}
$Success.prototype.equals = function (other) {
if (!(other instanceof $Success)) return false;
return Immutable.is(this.value, other.value);
};
function $Branch(wild, edges, count) {
this.wild = wild || emptyTrie; // Trie
this.edges = edges || Immutable.Map(); // Map from arity to Map from key-like-thing to Trie
// (Sigmas are 0-ary.)
this.count = count || 0;
}
$Branch.prototype.equals = function (other) {
if (!(other instanceof $Branch)) return false;
return (this.count === other.count) && Immutable.is(this.wild, other.wild) && Immutable.is(this.edges, other.edges);
};
///////////////////////////////////////////////////////////////////////////
// Patterns, projections and captures
var __ = Struct.__; /* imported rather than defined here because of cyclic dep in Struct */
var SOA = new $Special("array"); /* key for start-of-array */
function $Embedded(trie, arrayLength) {
this.trie = trie;
this.arrayLength = arrayLength;
}
function embeddedTrie(trie) {
return new $Embedded(trie, null);
}
function embeddedTrieArray(trie, arrayLength) {
return new $Embedded(trie, arrayLength);
}
// The name argument should be a string or null; it defaults to null.
// The pattern argument defaults to wildcard, __.
function $Capture(name, pattern) {
this.name = name || null;
this.pattern = (typeof pattern === 'undefined' ? __ : pattern);
}
// Abbreviation: _$(...) <==> new $Capture(...)
function _$(name, pattern) {
return new $Capture(name, pattern);
}
function isCapture(x) { return x instanceof $Capture || x === _$; }
function captureName(x) { return x instanceof $Capture ? x.name : null; }
function capturePattern(x) { return x instanceof $Capture ? x.pattern : __; }
///////////////////////////////////////////////////////////////////////////
// Constructors
function rsuccess(v) {
return new $Success(v);
}
function rseq(arity, key, r) {
if (is_emptyTrie(r)) return emptyTrie;
return new $Branch(emptyTrie, Immutable.Map.of(arity, Immutable.Map.of(key, r)), 1);
}
function rwild(r) {
if (is_emptyTrie(r)) return emptyTrie;
return new $Branch(r, Immutable.Map(), 0);
}
function rcopybranch(r) {
return new $Branch(r.wild, r.edges, r.count);
}
function prepend_wilds(n, r) {
while (n-- > 0) { r = rwild(r); }
return r;
}
// true iff r1 could have been the output of prepend_wilds(n, r2).
function equal_upto_wilds(n, r1, r2) {
while (true) {
if (n === 0 || is_emptyTrie(r1)) {
return Immutable.is(r1, r2);
}
if (!(r1 instanceof $Branch) || r1.count > 0) {
return false;
}
n = n - 1;
r1 = r1.wild;
}
}
function rupdate_inplace(r, arity, key, k) {
if (equal_upto_wilds(arity, k, r.wild)) {
var m = r.edges.get(arity);
if (!m) return;
if (m.has(key)) r.count--;
m = m.remove(key);
r.edges = m.isEmpty() ? r.edges.remove(arity) : r.edges.set(arity, m);
} else {
var m = r.edges.get(arity) || Immutable.Map();
if (!m.has(key)) r.count++;
r.edges = r.edges.set(arity, m.set(key, k));
}
}
function rlookup(r, arity, key) {
var m = r.edges.get(arity);
m = m && m.get(key);
return m || prepend_wilds(arity, r.wild);
}
///////////////////////////////////////////////////////////////////////////
function collapse(r) {
if ((r instanceof $Branch) && is_emptyTrie(r.wild) && (r.count === 0)) {
return emptyTrie;
} else {
return r;
}
}
// use with extreme care
function newEmptyBranch() {
return new $Branch(emptyTrie, Immutable.Map(), 0);
}
var canonicalExpandedEmpty = newEmptyBranch();
function expand(r) {
if (is_emptyTrie(r)) {
return canonicalExpandedEmpty;
} else {
return r;
}
}
///////////////////////////////////////////////////////////////////////////
function compilePattern(v, p) {
if (!p) die("compilePattern: missing pattern");
return walk(p, rsuccess(v));
function walk(p, acc) {
if (p === __) return rwild(acc);
if (Array.isArray(p)) {
for (var i = p.length - 1; i >= 0; i--) {
acc = walk(p[i], acc);
}
return rseq(p.length, SOA, acc);
}
if (Immutable.List.isList(p)) {
p.reverse().forEach(function (element) {
acc = walk(element, acc);
});
return rseq(p.size, SOA, acc);
}
if (Struct.isStructure(p)) {
for (var i = p.meta.arity - 1; i >= 0; i--) {
acc = walk(p[i], acc);
}
return rseq(p.meta.arity, p.meta, acc);
}
if (p instanceof $Embedded) {
acc = appendTrie(p.trie, function (v) { return acc; });
if (p.arrayLength !== null) {
acc = rseq(p.arrayLength, SOA, acc);
}
return acc;
} else {
return rseq(0, p, acc);
}
}
}
function matchPattern(v, p) {
var captureCount = 0;
var result = {};
try {
walk(v, p);
} catch (e) {
if (e.matchPatternFailed) return null;
throw e;
}
result.length = captureCount;
return result;
function walk(v, p) {
if (p === v) return;
if (p === __) return;
if (Struct.isStructure(p) && Struct.isStructure(v) && (p.meta.equals(v.meta)))
{
for (var i = 0; i < p.meta.arity; i++) {
walk(v[i], p[i]);
}
return;
}
if (Array.isArray(p) && Array.isArray(v) && p.length === v.length) {
for (var i = 0; i < p.length; i++) {
walk(v[i], p[i]);
}
return;
}
if (isCapture(p)) {
var thisCapture = captureCount++;
walk(v, capturePattern(p));
result[captureName(p) || ('$' + thisCapture)] = v;
return;
}
if (p instanceof $Embedded) {
die("$Embedded patterns not supported in matchPattern()");
}
throw {matchPatternFailed: true};
}
}
///////////////////////////////////////////////////////////////////////////
function combine(combineSuccess, leftEmpty, rightEmpty, leftBase, rightBase, r1, r2) {
return walk(r1, r2);
function walk(r1, r2) {
if (is_emptyTrie(r1)) return collapse(leftEmpty(r2));
if (is_emptyTrie(r2)) return collapse(rightEmpty(r1));
if ((r1 instanceof $Success) || (r2 instanceof $Success)) {
return collapse(combineSuccess(r1, r2));
}
if (!(r1 instanceof $Branch) || !(r2 instanceof $Branch)) {
die("Invalid trie given to combine");
}
/* fold-over-keys */
var w = walk(r1.wild, r2.wild);
var acc;
if (!is_emptyTrie(r1.wild) && !is_emptyTrie(r2.wild)) {
acc = rcopybranch(expand(rwild(w)));
var seen = Immutable.Map();
r1.edges.forEach(function (keymap, arity) {
keymap.forEach(function (r1v, key) {
var r2v = rlookup(r2, arity, key);
rupdate_inplace(acc, arity, key, walk(r1v, r2v));
seen = seen.set(arity, (seen.get(arity) || Immutable.Set()).add(key));
});
});
r2.edges.forEach(function (keymap, arity) {
keymap.forEach(function (r2v, key) {
var r1v = rlookup(r1, arity, key);
var s = seen.get(arity);
s = s && s.has(key);
if (!s) rupdate_inplace(acc, arity, key, walk(r1v, r2v));
});
});
} else if (!is_emptyTrie(r1.wild) || (is_emptyTrie(r2.wild) && (r1.count > r2.count))) {
acc = rcopybranch(expand(leftBase(r1)));
acc.wild = w;
r2.edges.forEach(function (keymap, arity) {
keymap.forEach(function (r2v, key) {
var r1v = rlookup(r1, arity, key);
rupdate_inplace(acc, arity, key, walk(r1v, r2v));
});
});
} else {
acc = rcopybranch(expand(rightBase(r2)));
acc.wild = w;
r1.edges.forEach(function (keymap, arity) {
keymap.forEach(function (r1v, key) {
var r2v = rlookup(r2, arity, key);
rupdate_inplace(acc, arity, key, walk(r1v, r2v));
});
});
}
return collapse(acc);
}
}
function asymmetricTrieError(r1, r2) {
die("Asymmetric tries: " + r1 + ", " + r2);
}
///////////////////////////////////////////////////////////////////////////
var unionSuccessesDefault = function (v1, v2) {
return rsuccess(v1.union(v2));
};
function union(o1, o2, unionSuccessesOpt) {
var unionSuccesses = unionSuccessesOpt || unionSuccessesDefault;
return combine(unionCombiner,
function (x) { return x; },
function (x) { return x; },
function (x) { return x; },
function (x) { return x; },
o1,
o2);
function unionCombiner(r1, r2) {
if ((r1 instanceof $Success) && (r2 instanceof $Success))
return unionSuccesses(r1.value, r2.value);
if (is_emptyTrie(r1)) return r2;
if (is_emptyTrie(r2)) return r1;
asymmetricTrieError(r1, r2);
}
}
function unionN() {
var acc = emptyTrie;
for (var i = 0; i < arguments.length; i++) {
acc = union(acc, arguments[i]);
}
return acc;
}
///////////////////////////////////////////////////////////////////////////
var intersectSuccessesDefault = unionSuccessesDefault;
function intersect(o1, o2, intersectSuccessesOpt) {
var intersectSuccesses = intersectSuccessesOpt || intersectSuccessesDefault;
return combine(intersectCombiner,
function (x) { return emptyTrie; },
function (x) { return emptyTrie; },
function (x) { return emptyTrie; },
function (x) { return emptyTrie; },
o1,
o2);
function intersectCombiner(r1, r2) {
if ((r1 instanceof $Success) && (r2 instanceof $Success))
return intersectSuccesses(r1.value, r2.value);
if (is_emptyTrie(r1)) return emptyTrie;
if (is_emptyTrie(r2)) return emptyTrie;
asymmetricTrieError(r1, r2);
}
}
///////////////////////////////////////////////////////////////////////////
var subtractSuccessesDefault = function (v1, v2) {
var r = v1.subtract(v2);
if (r.isEmpty()) return emptyTrie;
return rsuccess(r);
};
function subtract(o1, o2, subtractSuccessesOpt) {
var subtractSuccesses = subtractSuccessesOpt || subtractSuccessesDefault;
return combine(subtractCombiner,
function (x) { return emptyTrie; },
function (x) { return x; },
function (x) { return x; },
function (x) { return emptyTrie; },
o1,
o2);
function subtractCombiner(r1, r2) {
if ((r1 instanceof $Success) && (r2 instanceof $Success))
return subtractSuccesses(r1.value, r2.value);
if (is_emptyTrie(r1)) return emptyTrie;
if (is_emptyTrie(r2)) return r1;
asymmetricTrieError(r1, r2);
}
}
///////////////////////////////////////////////////////////////////////////
// Returns failureResult on failed match, otherwise the appropriate
// success value contained in the trie r. If wildcardUnionOpt
// non-falsy, allows wildcards in "messages" v.
function matchValue(r, v, failureResult, wildcardUnionOpt) {
return walk(Immutable.List.of(v), r);
function walk(vs, r) {
while (!is_emptyTrie(r)) {
if (r instanceof $Success) {
return vs.isEmpty() ? r.value : failureResult;
}
if (vs.isEmpty()) return failureResult;
var v = vs.first();
vs = vs.rest();
if (v === __) {
if (!wildcardUnionOpt) {
die("Cannot match wildcard in value unless wildcardUnionOpt non-falsy");
}
var seed = walk(vs, r.wild);
r.edges.forEach(function (keymap, arity) {
keymap.forEach(function (k, key) {
seed = wildcardUnionOpt(walk(Immutable.Repeat(__, arity).concat(vs), k), seed);
});
});
return seed;
}
if (typeof v === 'string' && v.substring(0, 2) === '__') {
die("Cannot match special string starting with __");
}
if (Array.isArray(v)) {
r = rlookup(r, v.length, SOA);
vs = Immutable.List(v).concat(vs);
} else if (Struct.isStructure(v)) {
r = rlookup(r, v.meta.arity, v.meta);
vs = Immutable.List(v.fields).concat(vs);
} else {
r = rlookup(r, 0, v);
}
}
return failureResult;
}
}
function matchTrie(o1, o2, seed, combiner) {
var acc = typeof seed === 'undefined' ? Immutable.Set() : seed; // variable updated imperatively
walk(o1, o2);
return acc;
function walk(r1, r2) {
if (is_emptyTrie(r1) || is_emptyTrie(r2)) return;
if ((r1 instanceof $Success) && (r2 instanceof $Success)) {
acc = combiner(r1.value, r2.value, acc);
return;
}
if (!(r1 instanceof $Branch) || !(r2 instanceof $Branch)) {
asymmetricTrieError(r1, r2);
}
walk(r1.wild, r2.wild);
function examineKeys(keymap, arity) {
keymap.forEach(function (_val, key) {
walk(rlookup(r1, arity, key), rlookup(r2, arity, key));
});
}
if (is_emptyTrie(r1.wild)) {
if (is_emptyTrie(r2.wild)) {
(r1.count < r2.count ? r1 : r2).edges.forEach(examineKeys);
} else {
r1.edges.forEach(examineKeys);
}
} else {
if (is_emptyTrie(r2.wild)) {
r2.edges.forEach(examineKeys);
} else {
r1.edges.forEach(examineKeys);
r2.edges.forEach(examineKeys);
}
}
}
}
function appendTrie(m, mTailFn) {
return walk(m);
function walk(m) {
if (is_emptyTrie(m)) return emptyTrie;
if (m instanceof $Success) return mTailFn(m.value);
var target = newEmptyBranch();
target.wild = walk(m.wild);
m.edges.forEach(function (keymap, arity) {
keymap.forEach(function (k, key) {
rupdate_inplace(target, arity, key, walk(k));
});
});
return collapse(target);
}
}
// DANGEROUS: prefer subtract() instead.
//
// function triePruneBranch(m, arityKeys) {
// if (arityKeys.isEmpty()) return emptyTrie;
// if (!(m instanceof $Branch)) return m;
// var arityKey = arityKeys.first();
// var rest = arityKeys.shift();
// var arity = arityKey[0];
// var key = arityKey[1];
// m = rcopybranch(m);
// rupdate_inplace(m, arity, key, triePruneBranch(rlookup(m, arity, key), rest));
// return collapse(m);
// }
function trieStep(m, arity, key) {
if (typeof key === 'undefined') {
// Cope with API change which would otherwise silently cause problems
die("trieStep: missing 'key' argument");
}
if (is_emptyTrie(m)) return emptyTrie;
if (m instanceof $Success) return emptyTrie;
return rlookup(m, arity, key);
}
function relabel(m, f) {
return appendTrie(m, function (v) {
var v1 = f(v);
return v1 ? rsuccess(v1) : emptyTrie;
});
}
///////////////////////////////////////////////////////////////////////////
function projectionNames(p) {
var names = [];
walk(p);
return names;
function walk(p) {
if (isCapture(p)) {
names.push(captureName(p));
walk(capturePattern(p));
return;
}
if (Array.isArray(p)) {
for (var i = 0; i < p.length; i++) walk(p[i]);
return;
}
if (Struct.isStructure(p)) {
for (var i = 0; i < p.meta.arity; i++) walk(p[i]);
return;
}
}
}
function projectionArity(p) {
return projectionNames(p).length;
}
function projectionToPattern(p) {
return walk(p);
function walk(p) {
if (isCapture(p)) return walk(capturePattern(p));
if (Array.isArray(p)) {
var result = [];
for (var i = 0; i < p.length; i++) {
result.push(walk(p[i]));
}
return result;
}
if (Struct.isStructure(p)) {
var resultFields = [];
for (var i = 0; i < p.meta.arity; i++) {
resultFields[i] = walk(p[i]);
}
return new Struct.Structure(p.meta, resultFields);
}
return p;
}
}
function project(t, wholeSpec, projectSuccessOpt, combinerOpt) {
return projectMany(t, Immutable.List.of(wholeSpec), projectSuccessOpt, combinerOpt);
}
function projectMany(t, wholeSpecs, projectSuccessOpt, combinerOpt) {
var projectSuccess = projectSuccessOpt || rsuccess;
var combiner = combinerOpt || unionSuccessesDefault;
return walk(false, t, Immutable.List(wholeSpecs), function (t) {
if (t instanceof $Success) {
return projectSuccess(t.value);
} else {
return emptyTrie;
}
});
function walk(isCapturing, t, specs, kont) {
if (specs.isEmpty()) return kont(t);
if (!(t instanceof $Branch)) return emptyTrie;
var spec = specs.first();
var specsRest = specs.rest();
if (isCapture(spec)) {
if (isCapturing) {
die("projectMany: nested capture in projection: " + wholeSpecs);
}
return walk(true, t, Immutable.List.of(capturePattern(spec)), function (intermediate) {
return walk(false, intermediate, specsRest, kont);
});
}
if (spec === __) {
if (isCapturing) {
var target = newEmptyBranch();
target.wild = walk(isCapturing, t.wild, specsRest, kont);
t.edges.forEach(function (keymap, arity) {
var innerSpecs = Immutable.Repeat(__, arity);
keymap.forEach(function (k, key) {
rupdate_inplace(target, arity, key,
walk(isCapturing, k, innerSpecs, function (intermediate) {
return walk(isCapturing, intermediate, specsRest, kont);
}));
});
});
return collapse(target);
} else {
var seed = walk(isCapturing, t.wild, specsRest, kont);
t.edges.forEach(function (keymap, arity) {
var innerSpecs = Immutable.Repeat(__, arity);
keymap.forEach(function (k, key) {
seed = union(seed,
walk(isCapturing, k, innerSpecs, function (intermediate) {
return walk(isCapturing, intermediate, specsRest, kont);
}),
combiner);
});
});
return seed;
}
return target;
}
if (Struct.isStructure(spec)) {
var intermediate = walk(isCapturing,
rlookup(t, spec.meta.arity, spec.meta),
Immutable.List(spec.fields),
function (intermediate) {
return walk(isCapturing, intermediate, specsRest, kont);
});
return isCapturing ? rseq(spec.meta.arity, spec.meta, intermediate) : intermediate;
}
if (Array.isArray(spec)) {
var intermediate = walk(isCapturing,
rlookup(t, spec.length, SOA),
Immutable.List(spec),
function (intermediate) {
return walk(isCapturing, intermediate, specsRest, kont);
});
return isCapturing ? rseq(spec.length, SOA, intermediate) : intermediate;
}
if (spec instanceof $Embedded) {
die("$Embedded patterns not supported in projectMany()");
}
/* It is a normal atom */
var intermediate = walk(isCapturing, rlookup(t, 0, spec), specsRest, kont);
return isCapturing ? rseq(0, spec, intermediate) : intermediate;
}
}
function reconstructSequence(key, items) {
if (key === SOA) {
return items.toArray();
} else {
return key.instantiate(items.toArray());
}
}
function trieKeys(m, takeCount0) {
if (typeof takeCount0 !== 'number') {
// Cope with API change which would otherwise silently cause problems
die("Missing mandatory argument 'takeCount' to Trie.trieKeys");
}
if (is_emptyTrie(m)) return Immutable.Set();
return walk(m, takeCount0, Immutable.List(),
function (items, tail) {
if (is_emptyTrie(tail)) return Immutable.Set();
if (tail instanceof $Success) return Immutable.Set.of(items);
die("Trie contains more than the requested "+takeCount0+" items");
});
function walk(m, takeCount, valsRev, kont) {
if (takeCount === 0) return kont(valsRev.reverse(), m);
if (is_emptyTrie(m)) return Immutable.Set();
if (m instanceof $Success) {
die("Trie contains fewer than the requested "+takeCount0+" items");
}
if (!is_emptyTrie(m.wild)) return false;
var result = Immutable.Set();
m.edges.forEach(function (keymap, arity) {
if (result === false) return false; // break out of iteration
keymap.forEach(function (k, key) {
if (result === false) return false; // break out of iteration
var piece;
if (Struct.isStructureType(key) || key === SOA) {
piece = walk(k, arity, Immutable.List(), function (items, m1) {
var item = reconstructSequence(key, items);
return walk(m1, takeCount - 1, valsRev.unshift(item), kont);
});
} else {
piece = walk(k, takeCount - 1, valsRev.unshift(key), kont);
}
result = (piece === false) ? false : result.union(piece);
});
});
return result;
}
}
function captureToObject(captures, names) {
var d = {};
captures.forEach(function (key, index) {
d[names[index] || ('$' + index)] = key;
});
return d;
}
function trieKeysToObjects(trieKeysResult, names) {
if (trieKeysResult === false) return false;
return trieKeysResult.toList().map(function (e) { return captureToObject(e, names); });
}
function projectObjects(m, projection) {
var names = projectionNames(projection);
return trieKeysToObjects(trieKeys(project(m, projection), names.length), names);
}
// (Projection, CaptureObject) -> Pattern
function instantiateProjection(p, obj) {
var captureCount = 0;
return walk(p);
function walk(p) {
if (isCapture(p)) {
var thisCapture = captureCount++;
var name = captureName(p);
return (name !== null) ? obj[name] : obj['$' + thisCapture];
}
if (Array.isArray(p)) {
var result = [];
for (var i = 0; i < p.length; i++) {
result.push(walk(p[i]));
}
return result;
}
if (Struct.isStructure(p)) {
var resultFields = [];
for (var i = 0; i < p.meta.arity; i++) {
resultFields[i] = walk(p[i]);
}
return new Struct.Structure(p.meta, resultFields);
}
return p;
}
}
function prettyTrie(m, optionsOpt) {
var acc = [];
var options = optionsOpt || {};
walk(options.initialIndent || 0, m);
return acc.join('');
function walk(i, m) {
if (m instanceof $Success) {
var v = m.value;
if ('prettySuccess' in options) {
acc.push(" " + options.prettySuccess(v));
} else {
if (Immutable.Set.isSet(v)) { v = v.toArray(); }
acc.push(" {" + JSON.stringify(v) + "}");
}
return;
}
if (is_emptyTrie(m)) {
acc.push(" ::: nothing");
return;
}
var needSep = false;
if (!is_emptyTrie(m.wild)) {
var key = "★";
needSep = true;
acc.push(" ");
acc.push(key);
walk(i + key.length + 1, m.wild);
}
m.edges
.toOrderedMap()
.sortBy(function (keymap, arity) { return arity })
.forEach(function (keymap, arity) {
keymap
.toOrderedMap()
.sortBy(function (k, key) { return key })
.forEach(function (k, key) {
if (needSep) {
acc.push(options.lineSep || "\n");
acc.push(indentStr(i));
} else {
needSep = true;
}
acc.push(" ");
if (key === SOA) key = '<' + arity + '>';
else if (Struct.isStructureType(key)) key = key.label + '<' + arity + '>';
else if (key instanceof $Special) key = key.name;
else key = JSON.stringify(key);
if (typeof key === 'undefined') key = 'undefined';
acc.push(key);
walk(i + key.length + 1, k);
});
});
}
function indentStr(i) {
return new Array(i + 1).join(' '); // eww
}
}
///////////////////////////////////////////////////////////////////////////
function parenTypeToString(key) {
if (Struct.isStructureType(key)) {
return ':' + key.label;
} else {
return 'L';
}
}
function stringToParenType(arity, key) {
if (key[0] === ':') {
return new Struct.StructureType(key.slice(1), arity);
} else if (key === 'L') {
return SOA;
}
throw new Error("Unsupported JSON trie paren type: "+key);
}
function trieToJSON(t) {
if (is_emptyTrie(t)) { return []; }
if (t instanceof $Success) { return [true]; } // TODO: consider generalizing
// It's a $Branch.
var jParens = [];
var jAtoms = [];
t.edges.forEach(function (keymap, arity) {
keymap.forEach(function (k, key) {
var jk = trieToJSON(k);
if (Struct.isStructureType(key) || key === SOA) {
jParens.push([arity, parenTypeToString(key), jk]);
} else {
jAtoms.push([key, jk]);
}
});
});
return [jParens, trieToJSON(t.wild), jAtoms];
}
function badJSON(j) {
die("Cannot deserialize JSON trie: " + JSON.stringify(j));
}
function trieFromJSON(j) {
return decode(j);
function decode(j) {
if (!Array.isArray(j)) badJSON(j);
switch (j.length) {
case 0: return emptyTrie;
case 1: return rsuccess(true); // TODO: consider generalizing
case 3: {
var result = rcopybranch(expand(rwild(decode(j[1]))));
j[0].forEach(function (entry) {
var arity = entry[0];
if (typeof arity !== 'number') badJSON(j);
var key = stringToParenType(arity, entry[1]);
rupdate_inplace(result, arity, key, decode(entry[2]));
});
j[2].forEach(function (entry) {
var key = entry[0];
rupdate_inplace(result, 0, key, decode(entry[1]));
});
return collapse(result);
}
default: badJSON(j);
}
}
}
///////////////////////////////////////////////////////////////////////////
module.exports.__ = __;
module.exports.SOA = SOA;
module.exports.$Capture = $Capture;
module.exports.$Special = $Special;
module.exports._$ = _$;
module.exports.is_emptyTrie = is_emptyTrie;
module.exports.emptyTrie = emptyTrie;
module.exports.embeddedTrie = embeddedTrie;
module.exports.embeddedTrieArray = embeddedTrieArray;
module.exports.compilePattern = compilePattern;
module.exports.matchPattern = matchPattern;
module.exports._union = union;
module.exports.union = unionN;
module.exports.intersect = intersect;
module.exports.subtract = subtract;
module.exports.matchValue = matchValue;
module.exports.matchTrie = matchTrie;
module.exports.appendTrie = appendTrie;
// module.exports.triePruneBranch = triePruneBranch;
module.exports.trieStep = trieStep;
module.exports.trieSuccess = rsuccess;
module.exports.relabel = relabel;
module.exports.projectionNames = projectionNames;
module.exports.projectionArity = projectionArity;
module.exports.projectionToPattern = projectionToPattern;
module.exports.project = project;
module.exports.projectMany = projectMany;
module.exports.trieKeys = trieKeys;
module.exports.captureToObject = captureToObject;
module.exports.trieKeysToObjects = trieKeysToObjects;
module.exports.projectObjects = projectObjects;
module.exports.instantiateProjection = instantiateProjection;
module.exports.prettyTrie = prettyTrie;
module.exports.trieToJSON = trieToJSON;
module.exports.trieFromJSON = trieFromJSON;
// For testing
module.exports._testing = {
rsuccess: rsuccess,
rseq: rseq,
rwild: rwild
};