makeStructureConstructor

This commit is contained in:
Tony Garnock-Jones 2016-03-18 12:32:09 -04:00
parent f675f91719
commit 21fd0f574a
2 changed files with 83 additions and 79 deletions

View File

@ -2,6 +2,20 @@
var Immutable = require("immutable");
function makeStructureConstructor(label, argumentNames) {
var $SyndicateMeta$ = {
label: label,
arguments: argumentNames
};
return function() {
var result = {"$SyndicateMeta$": $SyndicateMeta$};
for (var i = 0; i < argumentNames.length; i++) {
result[argumentNames[i]] = arguments[i];
}
return result;
};
}
function $Special(name) {
this.name = name;
}
@ -111,6 +125,16 @@ function compilePattern(v, p) {
return rseq(SOA, acc);
}
if (p.$SyndicateMeta$) {
var args = p.$SyndicateMeta$.arguments;
acc = rseq(EOA, acc);
for (var i = args.length - 1; i >= 0; i--) {
acc = walk(p[args[i]], acc);
}
acc = rseq(p.$SyndicateMeta$.label, acc);
return rseq(SOA, acc);
}
if (p instanceof $Embedded) {
return appendTrie(p.trie, function (v) { return acc; });
} else {
@ -119,45 +143,6 @@ function compilePattern(v, p) {
}
}
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 (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 rupdate(r, key, k) {
var oldWild = r.get(__, emptyTrie);
if (Immutable.is(k, oldWild)) {
@ -498,6 +483,18 @@ function matchValue(r, v) {
} else {
r = rlookup(r, __);
}
} else if (v.$SyndicateMeta$) {
if (r.has(SOA)) {
r = rlookup(r, SOA);
stack.push(vs);
vs = Immutable.List.of(v.$SyndicateMeta$.label);
var args = v.$SyndicateMeta$.arguments;
for (var i = 0; i < args.length; i++) {
vs = vs.push(v[args[i]]);
}
} else {
r = rlookup(r, __);
}
} else {
if (r.has(v)) {
r = rlookup(r, v);
@ -683,6 +680,17 @@ function compileProjection(/* projection, projection, ... */) {
return;
}
if (p.$SyndicateMeta$) {
acc.push(SOA);
acc.push(p.$SyndicateMeta$.label);
var args = p.$SyndicateMeta$.arguments;
for (var i = 0; i < args.length; i++) {
walk(p[args[i]]);
}
acc.push(EOA);
return;
}
if (p instanceof $Embedded) {
die("Cannot embed trie in projection");
} else {
@ -705,6 +713,15 @@ function projectionToPattern(p) {
return result;
}
if (p.$SyndicateMeta$) {
var result = {"$SyndicateMeta$": p.$SyndicateMeta$};
var args = p.$SyndicateMeta$.arguments;
for (var i = 0; i < args.length; i++) {
result[args[i]] = walk(p[args[i]]);
}
return result;
}
if (p instanceof $Embedded) {
return p.trie;
} else {
@ -1010,12 +1027,12 @@ module.exports.SOA = SOA;
module.exports.EOA = EOA;
module.exports.$Capture = $Capture;
module.exports.$Special = $Special;
module.exports.makeStructureConstructor = makeStructureConstructor;
module.exports._$ = _$;
module.exports.is_emptyTrie = is_emptyTrie;
module.exports.emptyTrie = emptyTrie;
module.exports.embeddedTrie = embeddedTrie;
module.exports.compilePattern = compilePattern;
module.exports.matchPattern = matchPattern;
module.exports._union = union;
module.exports.union = unionN;
module.exports.intersect = intersect;

View File

@ -351,45 +351,6 @@ describe("embedding tries in patterns", function () {
});
});
describe("calls to matchPattern", function () {
it("should yield appropriately-named/-numbered fields", function () {
expect(r.matchPattern([1, 2, 3], [r.__, 2, r._$])).to.eql({'$0': 3, 'length': 1});
expect(r.matchPattern([1, 2, 3], [r.__, 2, r._$("three")])).to.eql({'three': 3, 'length': 1});
expect(r.matchPattern([1, 2, 3], [r._$, 2, r._$("three")]))
.to.eql({'$0': 1, 'three': 3, 'length': 2});
expect(r.matchPattern([1, 2, 3], [r._$("one"), 2, r._$]))
.to.eql({'one': 1, '$1': 3, 'length': 2});
expect(r.matchPattern([1, 2, 3], [r._$("one"), 2, r._$("three")]))
.to.eql({'one': 1, 'three': 3, 'length': 2});
});
it("should fail on value mismatch", function () {
expect(r.matchPattern([1, 2, 3], [r.__, 999, r._$("three")])).to.be(null);
});
it("should fail on array length mismatch", function () {
expect(r.matchPattern([1, 2, 3], [r.__, 2, r._$("three"), 4])).to.be(null);
});
it("matches substructure", function () {
expect(r.matchPattern([1, [2, 999], 3], [r._$("one"), r._$(null, [2, r.__]), r._$("three")]))
.to.eql({ one: 1, '$1': [ 2, 999 ], three: 3, length: 3 });
expect(r.matchPattern([1, [2, 999], 3], [r._$("one"), r._$("two", [2, r.__]), r._$("three")]))
.to.eql({ one: 1, two: [ 2, 999 ], three: 3, length: 3 });
expect(r.matchPattern([1, [999, 2], 3], [r._$("one"), r._$(null, [2, r.__]), r._$("three")]))
.to.be(null);
expect(r.matchPattern([1, [999, 2], 3], [r._$("one"), r._$("two", [2, r.__]), r._$("three")]))
.to.be(null);
});
it("matches nested captures", function () {
expect(r.matchPattern([1, [2, 999], 3], [r._$("one"), r._$(null, [2, r._$]), r._$("three")]))
.to.eql({ one: 1, '$2': 999, '$1': [ 2, 999 ], three: 3, length: 4 });
expect(r.matchPattern([1, [2, 999], 3], [r._$("one"), r._$("two", [2, r._$]), r._$("three")]))
.to.eql({ one: 1, '$2': 999, two: [ 2, 999 ], three: 3, length: 4 });
});
});
describe("Projection with no captures", function () {
it("should yield the empty sequence when there's a match", function () {
var emptySequence = [' >{["A"]}'];
@ -489,3 +450,29 @@ describe('triePruneBranch', function () {
' "z" >{true}']);
});
});
describe('makeStructureConstructor', function () {
it('should produce the right metadata', function () {
var ctor = r.makeStructureConstructor('foo', ['bar', 'zot']);
var inst = ctor(123, 234);
expect(inst.$SyndicateMeta$.label).to.equal('foo');
expect(inst.$SyndicateMeta$.arguments).to.eql(['bar', 'zot']);
});
it('should produce the right instance data', function () {
var ctor = r.makeStructureConstructor('foo', ['bar', 'zot']);
var inst = ctor(123, 234);
expect(inst.bar).to.equal(123);
expect(inst.zot).to.equal(234);
});
it('should work with compilePattern and matchValue', function () {
var sA = Immutable.Set(["A"]);
var ctor = r.makeStructureConstructor('foo', ['bar', 'zot']);
var inst = ctor(123, 234);
var x = r.compilePattern(sA, ctor(123, r.__));
checkPrettyTrie(x, [' < "foo" 123 ★ > >{["A"]}']);
expect(r.matchValue(x, ctor(123, 234))).to.eql(sA);
expect(r.matchValue(x, ctor(234, 123))).to.eql(null);
});
});