diff --git a/README.md b/README.md index 6715b72..f8f32e6 100644 --- a/README.md +++ b/README.md @@ -486,6 +486,12 @@ This is the same as `VAR(..)` bud does a structural match (i.e. via `cmp(..)`). Note that `VAR(..)` and `LIKE(..)` use the same namespace and can be used interchangeably depending on the type of matching desired. +`CONTEXT(pattern)` +A context constructor, matches if `pattern` matches. + +This is needed in case we need a way to access the pattern API from the root of the pattern when it's actually an object (see example below). + + Examples: ```javascript var P = [VAR('x', ANY), VAR('x'), LIKE('x')] @@ -496,6 +502,12 @@ cmp(P, [{}, {}, {}]) // -> false var o = {} // this succeeds because o === o and cmp(o, {}) is true... cmp(P, [o, o, {}]) // -> true + +// and in case we need for P to explicitly be a pattern: +P = CONTEXT(P) + +// now we can use the pattern API directly from P: +P.cmp([o, o, {}]) ``` ### Miscellaneous patterns diff --git a/diff.js b/diff.js index c7b48b0..70a13df 100644 --- a/diff.js +++ b/diff.js @@ -287,21 +287,31 @@ var LogicTypeClassPrototype = { var LogicTypePrototype = { __context__: null, - context: function(){ - return (this.__context__ = this.__context__ || {}) }, + context: function(context){ + var res = (this.__context__ == null || context != null) ? + Object.create(this) + : this + res.__context__ = res.__context__ || context || {} + return res + }, __cmp__: function(obj, cmp, context){ return false }, // XXX need to track loops... cmp: function(obj, cmp, context){ + // XXX HACK??? + if(arguments.length < 3){ + return Diff.cmp( + cmp instanceof Function ? this : this.context(cmp), + obj) + } + cmp = cmp || function(a, b){ return a === b //|| a == b || (a.__cmp__ && a.__cmp__(b, cmp, context)) || (b.__cmp__ && b.__cmp__(a, cmp, context)) } - - // create a pattern context... - var context = context || this.context() + context = context || this.context().__context__ // cache... var cache = context.cache = context.cache || new Map() @@ -503,11 +513,6 @@ object.makeConstructor('NOT', Object.assign(new LogicType(), { // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Will compare as true if one of the .members compares as true... -// -// XXX BUG: -// cmp(OR(1,[2],3), 2) -// -> true -// ...this is likely due to [2] == 2 -> true var OR = module.OR = object.makeConstructor('OR', Object.assign(new LogicType(), { @@ -542,12 +547,26 @@ object.makeConstructor('AND', Object.assign(new LogicType(), { }, })) +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// XXX BUG: +// CONTEXT([ANY, ANY, ANY]).cmp([1, 2, 3]) +// -> false +var CONTEXT = +module.CONTEXT = +object.makeConstructor('CONTEX', Object.assign(new LogicType(), { + __cmp__: function(obj, cmp, context){ + return cmp(this.pattern, obj) }, + __init__: function(pattern){ + this.pattern = arguments.length == 0 ? ANY : pattern + }, +})) + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - var VAR = module.VAR = object.makeConstructor('VAR', Object.assign(new LogicType(), { __cmp__: function(obj, cmp, context){ - var context = context || this.context() + var context = context || this.context().__context__ var ns = context.ns = context.ns || {} var pattern = ns[this.name] = this.name in ns ? @@ -573,7 +592,7 @@ var LIKE = module.LIKE = object.makeConstructor('LIKE', Object.assign(new VAR(), { __cmp__: function(obj, cmp, context){ - var context = context || this.context() + var context = context || this.context().__context__ return VAR.prototype.__cmp__.call(this, obj, cmp, context) || Diff.cmp( @@ -1239,7 +1258,7 @@ module.Types = { // XXX might be a god idea to mix in default options (different // defaults per mode)... // XXX TEST: the format should survive JSON.parse(JSON.stringify(..))... - diff: function(A, B, options, cache){ + diff: function(A, B, options, context){ var that = this options = options ? Object.create(options) : {} options.as_object = options.as_object || [] @@ -1254,9 +1273,9 @@ module.Types = { || b === that.ANY // logic patterns... || (a instanceof LogicType - && a.cmp(b, cmp, cache)) + && a.cmp(b, cmp, context)) || (b instanceof LogicType - && b.cmp(a, cmp, cache)) } + && b.cmp(a, cmp, context)) } // deep compare... var cmp = options.cmp = options.cmp || function(a, b){ @@ -1267,12 +1286,15 @@ module.Types = { // see cache setup below... || (diff(a, b) == null) } // cache... - //cache = this.__cache = cache || this.__cache || new Map() - cache = cache || new Map() + context = context + || (A instanceof LogicType ? A.context().__context__ + : B instanceof LogicType ? B.context().__context__ + : {}) + cache = context.cache = context.cache || new Map() // cached diff... var diff = cache.diff = cache.diff || function(a, b){ var l2 = cache.get(a) || new Map() - var d = l2.get(b) || that.diff(a, b, options, cache) + var d = l2.get(b) || that.diff(a, b, options, context) cache.set(a, l2.set(b, d)) return d } @@ -1326,8 +1348,8 @@ module.Types = { // are going to throw away anyway... // ...this would be possible with a live .walk(..) that would // report changes as it finds them... - cmp: function(A, B, options){ - return this.diff(A, B, options) == null }, + cmp: function(A, B, options, context){ + return this.diff(A, B, options, context) == null }, // Patch (update) obj via diff... //