diff --git a/packages/core/src/skeleton.js b/packages/core/src/skeleton.js index 42f7b4a..673456e 100644 --- a/packages/core/src/skeleton.js +++ b/packages/core/src/skeleton.js @@ -423,6 +423,32 @@ function unpackScoped(a, k) { /////////////////////////////////////////////////////////////////////////// +function match(p, v) { + let captures = Immutable.List(); + + function walk(p, v) { + if (Capture.isClassOf(p)) { + if (!walk(p.get(0), v)) return false; + captures = captures.push(v); + return true; + } + + if (Discard.isClassOf(p)) return true; + + const pcls = classOf(p); + const vcls = classOf(v); + if (!Immutable.is(pcls, vcls)) return false; + + if (pcls === null) return Immutable.is(p, v); + if (typeof pcls === 'number') return p.every((pv, i) => walk(pv, v.get(i))); + return p.fields.every((pv, i) => walk(pv, v.fields.get(i))); + } + + return walk(p, v) ? captures : false; +} + +/////////////////////////////////////////////////////////////////////////// + module.exports.EVENT_ADDED = EVENT_ADDED; module.exports.EVENT_REMOVED = EVENT_REMOVED; module.exports.EVENT_MESSAGE = EVENT_MESSAGE; @@ -430,3 +456,4 @@ module.exports.Index = Index; module.exports.analyzeAssertion = analyzeAssertion; module.exports.instantiateAssertion = instantiateAssertion; +module.exports.match = match; diff --git a/packages/core/test/test-skeleton.js b/packages/core/test/test-skeleton.js index cf7643a..1cfe074 100644 --- a/packages/core/test/test-skeleton.js +++ b/packages/core/test/test-skeleton.js @@ -266,4 +266,71 @@ describe('skeleton', () => { }); }); + describe('matching a single pattern against a value', () => { + it('should accept matching simple records', () => { + expect(Skeleton.match(Immutable.fromJS(A(1, 2)), + Immutable.fromJS(A(1, 2)))) + .to.equal(Immutable.List()); + }); + it('should capture from matching simple records', () => { + expect(Skeleton.match(Immutable.fromJS(A(1, _$)), + Immutable.fromJS(A(1, 2)))) + .to.equal(Immutable.List([2])); + }); + it('should reject mismatching simple records', () => { + expect(Skeleton.match(Immutable.fromJS(A(1, 2)), + Immutable.fromJS(A(1, "hi")))) + .to.equal(false); + }); + it('should accept matching simple lists', () => { + expect(Skeleton.match(Immutable.fromJS([1, 2, 3]), + Immutable.fromJS([1, 2, 3]))) + .to.equal(Immutable.List()); + }); + it('should accept matching nested lists', () => { + expect(Skeleton.match(Immutable.fromJS([1, [2, 4], 3]), + Immutable.fromJS([1, [2, 4], 3]))) + .to.equal(Immutable.List()); + }); + it('should capture matches from simple lists', () => { + expect(Skeleton.match(Immutable.fromJS([1, Capture(2), 3]), + Immutable.fromJS([1, 2, 3]))) + .to.equal(Immutable.List([2])); + }); + it('should capture discards from simple lists', () => { + expect(Skeleton.match(Immutable.fromJS([1, Capture(__), 3]), + Immutable.fromJS([1, 2, 3]))) + .to.equal(Immutable.List([2])); + }); + it('should capture discards from nested lists', () => { + expect(Skeleton.match(Immutable.fromJS([1, Capture(__), 3]), + Immutable.fromJS([1, [2, 4], 3]))) + .to.equal(Immutable.fromJS([[2, 4]])); + }); + it('should capture nested discards from nested lists', () => { + expect(Skeleton.match(Immutable.fromJS([1, Capture([__, 4]), 3]), + Immutable.fromJS([1, [2, 4], 3]))) + .to.equal(Immutable.fromJS([[2, 4]])); + }); + it('should reject nested mismatches from nested lists', () => { + expect(Skeleton.match(Immutable.fromJS([1, Capture([__, 5]), 3]), + Immutable.fromJS([1, [2, 4], 3]))) + .to.equal(false); + }); + it('should reject mismatching captures from simple lists', () => { + expect(Skeleton.match(Immutable.fromJS([1, Capture(9), 3]), + Immutable.fromJS([1, 2, 3]))) + .to.equal(false); + }); + it('should reject simple lists varying in arity', () => { + expect(Skeleton.match(Immutable.fromJS([1, 2, 3, 4]), + Immutable.fromJS([1, 2, 3]))) + .to.equal(false); + }); + it('should reject simple lists varying in order', () => { + expect(Skeleton.match(Immutable.fromJS([1, 3, 2]), + Immutable.fromJS([1, 2, 3]))) + .to.equal(false); + }); + }); });