From cdea1507c4ea8f90f5d0b160ccc72b9f4892ab61 Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Fri, 23 May 2014 00:43:32 -0400 Subject: [PATCH] Initial commit of port of minimart fastrouting --- route.js | 984 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ tr.js | 54 +++ 2 files changed, 1038 insertions(+) create mode 100644 route.js create mode 100644 tr.js diff --git a/route.js b/route.js new file mode 100644 index 0000000..3aed4a2 --- /dev/null +++ b/route.js @@ -0,0 +1,984 @@ +/* Routing */ + +function Routing(exports) { + + var __ = "__"; /* wildcard marker */ + + var SOA = "__["; // start of array + var EOA = "__]"; // end of array + + function die(message) { + throw new Error(message); + } + + // The pattern argument defaults to wildcard, __. + function $Capture(pattern) { + this.pattern = (typeof pattern === 'undefined' ? __ : pattern); + } + + // Abbreviation: _$(x) <==> new $Capture(x) + function _$(pattern) { + return new $Capture(pattern); + } + + var SOC = "__{{"; // start of capture + var EOC = "__}}"; // end of capture + + function $Success(value) { + this.value = value; + } + + function $WildcardSequence(matcher) { + this.matcher = matcher; + } + + function $Dict() { + this.length = 0; + this.entries = {}; + } + + $Dict.prototype.get = function (key) { + return this.entries[key] || emptyMatcher; + }; + + $Dict.prototype.set = function (key, val) { + if (!(key in this.entries)) this.length++; + this.entries[key] = val; + }; + + $Dict.prototype.clear = function (key) { + if (key in this.entries) this.length--; + delete this.entries[key]; + }; + + $Dict.prototype.isEmpty = function () { + return this.length === 0; + }; + + $Dict.prototype.copy = function () { + var other = new $Dict(); + other.length = this.length; + for (var key in this.entries) { + if (this.entries.hasOwnProperty(key)) { + other.entries[key] = this.entries[key]; + } + } + return other; + }; + + $Dict.prototype.emptyGuard = function () { + if (this.isEmpty()) return emptyMatcher; + return this; + }; + + $Dict.prototype.has = function (key) { + return key in this.entries; + }; + + $Dict.prototype.smallerOf = function (other) { + return (other.length < this.length) ? other : this; + }; + + $Dict.prototype.largerOf = function (other) { + return (this.length < other.length) ? this : other; + }; + + function is_emptyMatcher(m) { + return (m === emptyMatcher); + } + + /////////////////////////////////////////////////////////////////////////// + // Constructors + + var emptyMatcher = null; + + function rsuccess(v) { + return (v === emptyMatcher) ? emptyMatcher : new $Success(v); + } + + function rseq(e, r) { + if (r === emptyMatcher) return emptyMatcher; + var s = new $Dict(); + s.set(e, r); + return s; + } + + function rwild(r) { + return rseq(__, r); + } + + function rwildseq(r) { + return (r === emptyMatcher) ? emptyMatcher : new $WildcardSequence(r); + } + + /////////////////////////////////////////////////////////////////////////// + + function compilePattern(v, p) { + if (!p) die("compilePattern: missing pattern"); + return walk(p, rseq(EOA, rsuccess(v))); + + function walk(p, acc) { + if (p === __) return rwild(acc); + + if (Array.isArray(p)) { + acc = rseq(EOA, acc); + for (var i = p.length - 1; i >= 0; i--) { + acc = walk(p[i], acc); + } + return rseq(SOA, acc); + } + + return rseq(p, acc); + } + } + + function shallowCopyArray(s) { + return s.slice(); + } + + function rupdate(r, key, k) { + if (is_emptyMatcher(k)) { + if (is_emptyMatcher(r)) return r; + r = r.copy(); + r.clear(key); + return r.emptyGuard(); + } else { + r = (is_emptyMatcher(r) ? new $Dict() : r.copy()); + r.set(key, k); + return r; + } + } + + function rupdateInplace(r, key, k) { + if (is_emptyMatcher(k)) { + r.clear(key); + } else { + r.set(key, k); + } + } + + function is_keyOpen(k) { + return k === SOA; + } + + function is_keyClose(k) { + return k === EOA; + } + + function is_keyNormal(k) { + return !(is_keyOpen(k) || is_keyClose(k)); + } + + /////////////////////////////////////////////////////////////////////////// + // Enough of sets to get by with + + function newSet() { + var s = {}; + for (var i = 0; i < arguments.length; i++) { + s[arguments[i]] = arguments[i]; + } + return s; + } + + function setUnion(s1, s2) { + var s = {}; + setUnionInplace(s, s1); + setUnionInplace(s, s2); + return s; + } + + function setEmpty(s) { + for (var k in s) { + if (s.hasOwnProperty(k)) + return false; + } + return true; + } + + function setSubtract(s1, s2) { + var s = {}; + for (var key in s1) { + if (s1.hasOwnProperty(key) && !s2.hasOwnProperty(key)) { + s[key] = s1[key]; + } + } + return s; + } + + function setUnionInplace(acc, s) { + for (var key in s) { + if (s.hasOwnProperty(key)) { + acc[key] = s[key]; + } + } + } + + /////////////////////////////////////////////////////////////////////////// + + var unionSuccesses = function (v1, v2) { + if (v1 === true) return v2; + if (v2 === true) return v1; + return setUnion(v1, v2); + }; + + var intersectSuccesses = function (v1, v2) { + return v1; + }; + + var erasePathSuccesses = function (v1, v2) { + var r = setSubtract(v1, v2); + if (setEmpty(r)) return null; + return r; + }; + + var matchMatcherSuccesses = function (v1, v2, acc) { + setUnionInplace(acc, v2); + }; + + var projectSuccess = function (v) { + return v; + }; + + /////////////////////////////////////////////////////////////////////////// + + function expandWildseq(r) { + return union(rwild(rwildseq(r)), rseq(EOA, r)); + } + + function union(o1, o2) { + return merge(o1, o2); + + function merge(o1, o2) { + if (is_emptyMatcher(o1)) return o2; + if (is_emptyMatcher(o2)) return o1; + return walk(o1, o2); + } + + function walk(r1, r2) { + if (r1 instanceof $WildcardSequence) { + if (r2 instanceof $WildcardSequence) { + return rwildseq(walk(r1.matcher, r2.matcher)); + } + r1 = expandWildseq(r1.matcher); + } else if (r2 instanceof $WildcardSequence) { + r2 = expandWildseq(r2.matcher); + } + + if (r1 instanceof $Success && r2 instanceof $Success) { + return rsuccess(unionSuccesses(r1.value, r2.value)); + } + + var w = merge(r1.get(__), r2.get(__)); + if (is_emptyMatcher(w)) { + var smaller = r1.smallerOf(r2); + var larger = r1.largerOf(r2); + var target = larger.copy(); + for (var key in smaller.entries) { + var k = merge(smaller.get(key), larger.get(key)); + rupdateInplace(target, key, k); + } + return target.emptyGuard(); + } else { + var target = rwild(w).copy(); + for (var key in r1.entries) { examineKey(r1, key, r2); } + for (var key in r2.entries) { examineKey(r2, key, r1); } + return target; + function examineKey(rA, key, rB) { + if ((key !== __) && !target.has(key)) { + var k = merge(rA.get(key), rB.get(key)); + if (is_keyOpen(key)) { + rupdateInplace(target, key, merge(rwildseq(w), k)); + } else if (is_keyClose(key)) { + if (w instanceof $WildcardSequence) { + rupdateInplace(target, key, merge(w.matcher, k)); + } else { + rupdateInplace(target, key, k); + } + } else { + rupdateInplace(target, key, merge(w, k)); + } + } + } + } + } + } + + function unionN() { + var acc = emptyMatcher; + for (var i = 0; i < arguments.length; i++) { + acc = union(acc, arguments[i]); + } + return acc; + } + + function intersect(o1, o2) { + if (is_emptyMatcher(o1)) return emptyMatcher; + if (is_emptyMatcher(o2)) return emptyMatcher; + return walk(o1, o2); + + function walkFlipped(r2, r1) { return walk(r1, r2); } + + function walk(r1, r2) { + // INVARIANT: r1 is a part of the original o1, and + // likewise for r2. This is so that the first arg to + // intersectSuccesses always comes from r1, and the second + // from r2. + if (is_emptyMatcher(r1)) return emptyMatcher; + if (is_emptyMatcher(r2)) return emptyMatcher; + + if (r1 instanceof $WildcardSequence) { + if (r2 instanceof $WildcardSequence) { + return rwildseq(walk(r1.matcher, r2.matcher)); + } + r1 = expandWildseq(r1.matcher); + } else if (r2 instanceof $WildcardSequence) { + r2 = expandWildseq(r2.matcher); + } + + if (r1 instanceof $Success && r2 instanceof $Success) { + return rsuccess(intersectSuccesses(r1.value, r2.value)); + } + + var w1 = r1.get(__); + var w2 = r2.get(__); + var w = walk(w1, w2); + + var target = new $Dict(); + if (is_emptyMatcher(w1)) { + if (is_emptyMatcher(w2)) { + for (var key in r1.smallerOf(r2).entries) examineKey(key); + } else { + for (var key in r1.entries) examineKey(key); + } + } else { + if (is_emptyMatcher(w2)) { + for (var key in r2.entries) examineKey(key); + } else { + rupdateInplace(target, __, w); + for (var key in r1.entries) examineKey(key); + for (var key in r2.entries) examineKey(key); + } + } + return target.emptyGuard(); + + function examineKey(key) { + if ((key !== __) && !target.has(key)) { + var k1 = r1.get(key); + var k2 = r2.get(key); + if (is_emptyMatcher(k1)) { + if (is_emptyMatcher(k2)) { + rupdateInplace(target, key, emptyMatcher); + } else { + rupdateInplace(target, key, walkWild(walk, w1, key, k2)); + } + } else { + if (is_emptyMatcher(k2)) { + rupdateInplace(target, key, walkWild(walkFlipped, w2, key, k1)); + } else { + rupdateInplace(target, key, walk(k1, k2)); + } + } + } + } + } + + function walkWild(walker, w, key, k) { + if (is_emptyMatcher(w)) return emptyMatcher; + if (is_keyOpen(key)) return walker(rwildseq(w), k); + if (is_keyClose(key)) { + if (w instanceof $WildcardSequence) return walker(w.matcher, k); + return emptyMatcher; + } + return walker(w, k); + } + } + + // Removes r2's mappings from r1. Assumes r2 has previously been + // union'd into r1. The erasePathSuccesses function should return + // null to signal "no remaining success values". + function erasePath(o1, o2) { + return walk(o1, o2); + + function cofinitePattern() { + die("Cofinite pattern required"); + } + + function walk(r1, r2) { + if (is_emptyMatcher(r1)) { + if (is_emptyMatcher(r2)) { + return emptyMatcher; + } else { + cofinitePattern(); + } + } else { + if (is_emptyMatcher(r2)) { + return r1; + } + } + + if (r1 instanceof $WildcardSequence) { + if (r2 instanceof $WildcardSequence) { + return rwildseq(walk(r1.matcher, r2.matcher)); + } + cofinitePattern(); + } else if (r2 instanceof $WildcardSequence) { + r2 = expandWildseq(r2.matcher); + } + + if (r1 instanceof $Success && r2 instanceof $Success) { + return rsuccess(erasePathSuccesses(r1.value, r2.value)); + } + + var w1 = r1.get(__); + var w2 = r2.get(__); + var w = walk(w1, w2); + var target; + + if (is_emptyMatcher(w2)) { + target = r1.copy(); + for (var key in r2.entries) examineKey(key); + } else { + target = rwild(w); + for (var key in r1.entries) examineKey(key); + for (var key in r2.entries) examineKey(key); + } + return target.emptyGuard(); + + function examineKey(key) { + if ((key !== __) && !target.has(key)) { + var k1 = r1.get(key); + var k2 = r2.get(key); + var updatedK; + if (is_emptyMatcher(k1)) { + if (is_emptyMatcher(k2)) { + updatedK = emptyMatcher; + } else { + cofinitePattern(); + } + } else { + if (is_emptyMatcher(k2)) { + updatedK = walkWild(key, k1, w2); + } else { + updatedK = walk(k1, k2); + } + } + // Here we ensure a "minimal" remainder in cases + // where after an erasure, a particular key's + // continuation is the same as the wildcard's + // continuation. TODO: the requal check may be + // expensive. If so, how can it be made cheaper? + rupdateInplace(target, key, (requal(updatedK, w) ? emptyMatcher : updatedK)); + } + } + } + + function walkWild(key, k, w) { + if (is_emptyMatcher(w)) return k; + if (is_keyOpen(key)) return walk(k, rwildseq(w)); + if (is_keyClose(key)) { + if (w instanceof $WildcardSequence) return walk(k, w.matcher); + return k; + } + return walk(k, w); + } + } + + // Returns null on failed match, otherwise the appropriate success + // value contained in the matcher r. + function matchValue(r, v) { + var failureResult = null; + + var vs = [v]; + var stack = [[]]; + + while (!is_emptyMatcher(r)) { + if (r instanceof $WildcardSequence) { + if (stack.length === 0) return failureResult; + vs = stack.pop(); + r = r.matcher; + continue; + } + + if (r instanceof $Success) { + if (vs.length === 0 && stack.length === 0) return r.value; + return failureResult; + } + + if (vs.length === 0) { + if (stack.length === 0) return failureResult; + vs = stack.pop(); + r = r.get(EOA); + continue; + } + + var v = vs.shift(); + + if (typeof v === 'string' && v.substring(0, 2) === '__') { + die("Cannot match special string starting with __"); + } + + if (Array.isArray(v)) { + if (SOA in r.entries) { + r = r.get(SOA); + stack.push(vs); + vs = shallowCopyArray(v); + } else { + r = r.get(__); + } + } else { + if (v in r.entries) { + r = r.get(v); + } else { + r = r.get(__); + } + } + } + + return failureResult; + } + + // TODO: better name for this + function matchMatcher(o1, o2, seed) { + var acc = seed || newSet(); // will be modified in place + walk(o1, o2); + return acc; + + function walkFlipped(r2, r1) { return walk(r1, r2); } + + function walk(r1, r2) { + if (is_emptyMatcher(r1) || is_emptyMatcher(r2)) return; + + if (r1 instanceof $WildcardSequence) { + if (r2 instanceof $WildcardSequence) { + walk(r1.matcher, r2.matcher); + return; + } + r1 = expandWildseq(r1.matcher); + } else if (r2 instanceof $WildcardSequence) { + r2 = expandWildseq(r2.matcher); + } + + if (r1 instanceof $Success && r2 instanceof $Success) { + matchMatcherSuccesses(r1.value, r2.value, acc); + return; + } + + var w1 = r1.get(__); + var w2 = r2.get(__); + walk(w1, w2); + + // Optimize similarly to intersect(). + if (is_emptyMatcher(w1)) { + if (is_emptyMatcher(w2)) { + for (var key in r1.smallerOf(r2).entries) examineKey(key); + } else { + for (var key in r1.entries) examineKey(key); + } + } else { + if (is_emptyMatcher(w2)) { + for (var key in r2.entries) examineKey(key); + } else { + for (var key in r1.entries) examineKey(key); + for (var key in r2.entries) examineKey(key); + } + } + + function examineKey(key) { + if (key !== __) { + var k1 = r1.get(key); + var k2 = r2.get(key); + if (is_emptyMatcher(k1)) { + if (is_emptyMatcher(k2)) { + return; + } else { + walkWild(walk, w1, key, k2); + } + } else { + if (is_emptyMatcher(k2)) { + walkWild(walkFlipped, w2, key, k1); + } else { + walk(k1, k2); + } + } + } + } + } + + function walkWild(walker, w, key, k) { + if (is_emptyMatcher(w)) return; + if (is_keyOpen(key)) { + walker(rwildseq(w), k); + return; + } + if (is_keyClose(key)) { + if (w instanceof $WildcardSequence) walker(w.matcher, k); + return; + } + walker(w, k); + } + } + + function relabel(m, f) { + return walk(m); + + function walk(m) { + if (is_emptyMatcher(m)) return emptyMatcher; + if (m instanceof $WildcardSequence) return rwildseq(walk(m.matcher)); + if (m instanceof $Success) return rsuccess(f(m.value)); + + var target = new $Dict(); + for (var key in m.entries) { + rupdateInplace(target, key, walk(m.get(key))); + } + return target.emptyGuard(); + } + } + + function compileProjection(p) { + var acc = []; + walk(p); + acc.push(EOA); + return acc; + + function walk(p) { + if (p instanceof $Capture) { + acc.push(SOC); + walk(p.pattern); + acc.push(EOC); + return; + } + + if (Array.isArray(p)) { + acc.push(SOA); + for (var i = 0; i < p.length; i++) { + walk(p[i]); + } + acc.push(EOA); + return; + } + + acc.push(p); + } + } + + function projectionToPattern(p) { + return walk(p); + + function walk(p) { + if (p instanceof $Capture) return walk(p.pattern); + + if (Array.isArray(p)) { + var result = []; + for (var i = 0; i < p.length; i++) { + result.push(walk(p[i])); + } + return result; + } + + return p; + } + } + + function project(m, spec) { + return rseq(SOA, walk(false, m, 0)); + + function walk(isCapturing, m, specIndex) { + if (specIndex >= spec.length) { + if (isCapturing) die("Bad specification: unclosed capture"); + if (m instanceof $Success) { + return rseq(EOA, rseq(EOA, rsuccess(projectSuccess(m.value)))); + } else { + return emptyMatcher; + } + } + + if (is_emptyMatcher(m)) return emptyMatcher; + + var item = spec[specIndex]; + var nextIndex = specIndex + 1; + + if (item === EOC) { + if (!isCapturing) die("Bad specification: unepxected EOC"); + return walk(false, m, nextIndex); + } + + if (item === SOC) { + if (isCapturing) die("Bad specification: nested capture"); + return walk(true, m, nextIndex); + } + + if (item === __) { + if (m instanceof $WildcardSequence) { + if (isCapturing) { + return rwild(walk(isCapturing, m, nextIndex)); + } else { + return walk(isCapturing, m, nextIndex); + } + } + + if (m instanceof $Success) { + return emptyMatcher; + } + + var target; + if (isCapturing) { + target = new $Dict(); + rupdateInplace(target, __, walk(isCapturing, m.get(__), nextIndex)); + for (var key in m.entries) { + if (key !== __) { + var mk = m.get(key); + if (is_keyOpen(key)) { + function cont(mk2) { return walk(isCapturing, mk2, nextIndex); } + rupdateInplace(target, key, captureNested(mk, cont)); + } else if (is_keyClose(key)) { + // do nothing + } else { + rupdateInplace(target, key, walk(isCapturing, mk, nextIndex)); + } + } + } + } else { + target = walk(isCapturing, m.get(__), nextIndex); + for (var key in m.entries) { + if (key !== __) { + var mk = m.get(key); + if (is_keyOpen(key)) { + function cont(mk2) { return walk(isCapturing, mk2, nextIndex); } + target = union(target, skipNested(mk, cont)); + } else if (is_keyClose(key)) { + // do nothing + } else { + target = union(target, walk(isCapturing, mk, nextIndex)); + } + } + } + } + return target; + } + + var result; + if (m instanceof $WildcardSequence) { + if (is_keyOpen(item)) { + result = walk(isCapturing, rwildseq(m), nextIndex); + } else if (is_keyClose(item)) { + result = walk(isCapturing, m.matcher, nextIndex); + } else { + result = walk(isCapturing, m, nextIndex); + } + } else if (m instanceof $Success) { + result = emptyMatcher; + } else { + if (is_keyOpen(item)) { + result = walk(isCapturing, rwildseq(m.get(__)), nextIndex); + } else if (is_keyClose(item)) { + result = emptyMatcher; + } else { + result = walk(isCapturing, m.get(__), nextIndex); + } + result = union(result, walk(isCapturing, m.get(item), nextIndex)); + } + if (isCapturing) { + result = rseq(item, result); + } + return result; + } + + function captureNested(m, cont) { + if (m instanceof $WildcardSequence) { + return rwildseq(cont(m.matcher)); + } + + if (is_emptyMatcher(m) || (m instanceof $Success)) { + return emptyMatcher; + } + + var target = new $Dict(); + rupdateInplace(target, __, captureNested(m.get(__), cont)); + for (var key in m.entries) { + if (key !== __) { + var mk = m.get(key); + if (is_keyOpen(key)) { + function cont2(mk2) { return captureNested(mk2, cont); } + rupdateInplace(target, key, captureNested(mk, cont2)); + } else if (is_keyClose(key)) { + rupdateInplace(target, key, cont(mk)); + } else { + rupdateInplace(target, key, captureNested(mk, cont)); + } + } + } + return target.emptyGuard(); + } + + function skipNested(m, cont) { + if (m instanceof $WildcardSequence) { + return cont(m.matcher); + } + + if (is_emptyMatcher(m) || (m instanceof $Success)) { + return emptyMatcher; + } + + var target = skipNested(m.get(__), cont); + for (var key in m.entries) { + if (key !== __) { + var mk = m.get(key); + if (is_keyOpen(key)) { + function cont2(mk2) { return skipNested(mk2, cont); } + target = union(target, skipNested(mk, cont2)); + } else if (is_keyClose(key)) { + target = union(target, cont(mk)); + } else { + target = union(target, skipNested(mk, cont)); + } + } + } + return target; + } + } + + function matcherKeys(m) { + return walk(m, function (v, k) { return [v]; }); + + function walk(m, k) { + if (m instanceof $WildcardSequence) return null; + if (m instanceof $Success) return []; + if (m.has(__)) return null; + var acc = []; + for (var key in m.entries) { + var mk = m.get(key); + var piece; + if (is_keyOpen(key)) { + piece = walkSeq(mk, seqK); + function seqK(vss, vsk) { + var acc = []; + for (var i = 0; i < vss.length; i++) { + var vs = vss[i]; + acc = acc.concat(k(transformSeqs(vs, key), vsk)); + } + return acc; + } + } else if (is_keyClose(key)) { + die("matcherKeys: internal error: unexpected key-close"); + } else { + piece = k(key, mk); + } + if (piece == null) return null; + acc = acc.concat(piece); + } + return acc; + } + + function walkSeq(m, k) { + if (m instanceof $WildcardSequence) return null; + if (m instanceof $Success) return k([], emptyMatcher); // TODO: ?? + if (m.has(__)) return null; + var acc = []; + for (var key in m.entries) { + var mk = m.get(key); + var piece; + if (is_keyClose(key)) { + piece = k([[]], mk); + } else { + piece = walk(rseq(key, mk), outerK); + function outerK(v, vk) { + return walkSeq(vk, innerK); + function innerK(vss, vsk) { + var acc = []; + for (var i = 0; i < vss.length; i++) { + var vs = shallowCopyArray(vss[i]); + vs.unshift(v); + acc.push(vs); + } + return k(acc, vsk); + } + } + } + if (piece == null) return null; + acc = acc.concat(piece); + } + return acc; + } + + function transformSeqs(vs, opener) { + if (opener === SOA) return vs; + die("Internal error: unknown opener " + opener); + } + } + + function prettyMatcher(m, initialIndent) { + var acc = []; + walk(initialIndent || 0, m); + return acc.join(''); + + function walk(i, m) { + if (is_emptyMatcher(m)) { + acc.push("::: no further matches possible"); + return; + } + if (m instanceof $WildcardSequence) { + acc.push("...>"); + walk(i + 4, m.matcher); + return; + } + if (m instanceof $Success) { + acc.push("{" + JSON.stringify(m.value) + "}"); + return; + } + + if (m.length === 0) { + acc.push(" ::: empty hash!"); + return; + } + + var needSep = false; + for (var key in m.entries) { + if (key === '__length') continue; + var k = m.entries[key]; + if (needSep) { + acc.push("\n"); + acc.push(indentStr(i)); + } else { + needSep = true; + } + acc.push(" "); + if (key === __) key = '★'; + if (key === SOA) key = '<'; + if (key === EOA) key = '>'; + acc.push(key); + walk(i + key.length + 1, k); + } + } + + function indentStr(i) { + return new Array(i + 1).join(' '); // eww + } + } + + /////////////////////////////////////////////////////////////////////////// + + exports.__ = __; + exports.newSet = newSet; + exports.$Capture = $Capture; + exports._$ = _$; + exports.is_emptyMatcher = is_emptyMatcher; + exports.emptyMatcher = emptyMatcher; + exports.compilePattern = compilePattern; + exports.union = unionN; + exports.intersect = intersect; + exports.erasePath = erasePath; + exports.matchValue = matchValue; + exports.matchMatcher = matchMatcher; + exports.relabel = relabel; + exports.compileProjection = compileProjection; + exports.projectionToPattern = projectionToPattern; + exports.project = project; + exports.matcherKeys = matcherKeys; + exports.prettyMatcher = prettyMatcher; +} + +if (typeof module !== 'undefined' && module.exports) { + Routing(module.exports); +} else if (window) { + Routing(window); +} diff --git a/tr.js b/tr.js new file mode 100644 index 0000000..cac6aa8 --- /dev/null +++ b/tr.js @@ -0,0 +1,54 @@ +var util = require('util'); +var r = require("./route.js"); + +function dump(x) { + console.log(util.inspect(x, { depth: null })); + return x; +} + +function dumpM(m) { + console.log(r.prettyMatcher(m)); + console.log(); + return m; +} + +mAny = r.compilePattern(r.newSet('mAny'), r.__); +mAAny = r.compilePattern(r.newSet('mAAny'), ['A', r.__]); +dumpM(mAny); +dumpM(mAAny); + +dump("mAny:"); +dump(r.matchValue(mAny, 'hi')); +dump(r.matchValue(mAny, ['A', 'hi'])); +dump(r.matchValue(mAny, ['B', 'hi'])); +dump(r.matchValue(mAny, ['A', [['hi']]])); + +dump("mAAny:"); +dump(r.matchValue(mAAny, 'hi')); +dump(r.matchValue(mAAny, ['A', 'hi'])); +dump(r.matchValue(mAAny, ['B', 'hi'])); +dump(r.matchValue(mAAny, ['A', [['hi']]])); + +console.log("unions"); + +dumpM(r.union(r.compilePattern(r.newSet('A'), [r.__, 'A']), + r.compilePattern(r.newSet('B'), [r.__, 'B']))); + +dumpM(r.union(r.compilePattern(r.newSet('A'), [r.__, 'A']), + r.compilePattern(r.newSet('W'), r.__))); + +console.log("projections"); + +dumpM(r.project(r.union(r.compilePattern(r.newSet('A'), r.__), + r.compilePattern(r.newSet('B'), ['b'])), + r.compileProjection(r._$([[r.__]])))); + +dumpM(r.project(r.union(r.compilePattern(r.newSet('A'), [1, 2]), + r.compilePattern(r.newSet('C'), [1, 3]), + r.compilePattern(r.newSet('B'), [3, 4])), + r.compileProjection([r._$(), r._$()]))); + +dump(r.matcherKeys(r.project(r.union(r.compilePattern(r.newSet('A'), [1, 2]), + r.compilePattern(r.newSet('C'), [1, 3]), + r.compilePattern(r.newSet('B'), [3, 4])), + r.compileProjection([r._$(), r._$()]))));