diff --git a/README.md b/README.md index f531dc6..5681416 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ This tells us that we have four *changes*: - different `"name"` - different `"hair"` - in `"skills"` missing `"guitar"` -- in `"skills"` different `"length"` +- in `"skills"` different `"length"` or *different number of skills*. Some words on the format: - `A` and `B` indicate the states of the *change* in the input objects, @@ -386,7 +386,10 @@ XXX General description... ### Logic patterns `ANY` -Matches anything +Matches anything. + +Note that this will also match `undefined`, to match anything but `undefined` use `NOT(undefined)`. +XXX this may still change. `NOT(A)` @@ -464,8 +467,8 @@ This is a shorthand for: `AND(ARRAY(x), ARRAY(y), ..)` Patterns support variables, the namespae/context is persistent per diff / compare call. -`VAR(name, pattern)` `VAR(name)` +`VAR(name, pattern)` A `VAR` is uniquely identified by name. This works in stages: 1. Matches `pattern` until *first successful match*, @@ -476,8 +479,8 @@ If no `pattern` is given `ANY` is assumed. Note that if the *cached* object is not a pattern it will not be matched structurally, i.e. first `===` and then `==` are used instead of `cmp(..)`. -`LIKE(name, pattern)` `LIKE(name)` +`LIKE(name, pattern)` 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. @@ -490,7 +493,7 @@ var P = [VAR('x', ANY), VAR('x'), LIKE('x')] cmp(P, [{}, {}, {}]) // -> false var o = {} -// this cuccessds because o === o and cmp(o, {}) is true... +// this succeeds because o === o and cmp(o, {}) is true... cmp(P, [o, o, {}]) // -> true ``` diff --git a/diff.js b/diff.js index ff06d5d..c7b48b0 100644 --- a/diff.js +++ b/diff.js @@ -296,7 +296,7 @@ var LogicTypePrototype = { cmp: function(obj, cmp, context){ cmp = cmp || function(a, b){ return a === b - || a == b + //|| a == b || (a.__cmp__ && a.__cmp__(b, cmp, context)) || (b.__cmp__ && b.__cmp__(a, cmp, context)) } @@ -316,6 +316,8 @@ var LogicTypePrototype = { || (obj != null && obj.__cmp__ && obj.__cmp__(this, cmp, context)) + + // cache... c.set(obj, !!res) return !!res @@ -501,6 +503,11 @@ 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(), { @@ -535,13 +542,64 @@ object.makeConstructor('AND', Object.assign(new LogicType(), { }, })) +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +var VAR = +module.VAR = +object.makeConstructor('VAR', Object.assign(new LogicType(), { + __cmp__: function(obj, cmp, context){ + var context = context || this.context() + var ns = context.ns = context.ns || {} + var pattern = ns[this.name] = + this.name in ns ? + ns[this.name] + : this.pattern + + if(cmp(pattern, obj)){ + ns[this.name] = obj + return true + } + + return false + }, + __init__: function(name, pattern){ + this.name = name + this.pattern = arguments.length < 2 ? ANY : pattern + }, +})) + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// this is like VAR(..) but will do a structural compare... +var LIKE = +module.LIKE = +object.makeConstructor('LIKE', Object.assign(new VAR(), { + __cmp__: function(obj, cmp, context){ + var context = context || this.context() + + return VAR.prototype.__cmp__.call(this, obj, cmp, context) + || Diff.cmp( + this.name in context.ns ? + context.ns[this.name] + : this.pattern, + obj) }, +})) + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// TEST(func) == L iff func(L) is true. +var TEST = +module.TEST = +object.makeConstructor('TEST', Object.assign(new VAR(), { + __cmp__: function(obj, cmp, context){ + return this.func(obj, cmp, context) }, + __init__: function(func){ + this.func = func + } +})) + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // IN(A) == L iff A in L // // NOTE: since this can do a search using cmp(..) thid will be slow on // large containers... -// -// XXX add support for other containers... var IN = module.IN = object.makeConstructor('IN', Object.assign(new LogicType(), { @@ -549,13 +607,13 @@ object.makeConstructor('IN', Object.assign(new LogicType(), { // XXX should we check inherited stuff??? __cmp__: function(obj, cmp, context){ var p = this.value - return (obj instanceof Array ? obj - : obj instanceof Map || obj instanceof Set ? [...obj.values()] + return (obj instanceof Map || obj instanceof Set ? + [...obj.values()] : []) .concat(Object.values(obj)) .reduce(function(res, e){ return res === false ? - cmp(p, e, context) + !!cmp(p, e, context) : res }, false) }, __init__: function(value){ @@ -606,50 +664,6 @@ object.makeConstructor('OF', Object.assign(new LogicType(), { })) -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -var VAR = -module.VAR = -object.makeConstructor('VAR', Object.assign(new LogicType(), { - __cmp__: function(obj, cmp, context){ - var context = context || this.context() - var ns = context.ns = context.ns || {} - var pattern = ns[this.name] = - this.name in ns ? - ns[this.name] - : this.pattern - - if(cmp(pattern, obj)){ - ns[this.name] = obj - return true - } - - return false - }, - __init__: function(name, pattern){ - this.name = name - this.pattern = arguments.length < 2 ? ANY : pattern - }, -})) - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// this is like VAR(..) but will do a structural compare... -var LIKE = -module.LIKE = -object.makeConstructor('LIKE', Object.assign(new VAR(), { - // XXX reuse... - __cmp__: function(obj, cmp, context){ - var context = context || this.context() - - return VAR.prototype.__cmp__.call(this, obj, cmp, context) - || Diff.cmp( - this.name in context.ns ? - context.ns[this.name] - : this.pattern, - obj) }, -})) - - - //--------------------------------------------------------------------- // Placeholders... // @@ -1234,7 +1248,7 @@ module.Types = { // XXX do we need to differentiate things like: new Number(123) vs. 123??? var bcmp = function(a, b, cmp){ return a === b - || a == b + //|| a == b // basic patters... || a === that.ANY || b === that.ANY diff --git a/format.js b/format.js index 9fd4e2b..d7c3811 100644 --- a/format.js +++ b/format.js @@ -16,7 +16,7 @@ var { BOOL, NUMBER, STRING, ARRAY, FUNCTION, OR, AND, NOT, AT, OF, IN, - VAR, LIKE, + VAR, LIKE, TEST, EMPTY, NONE, } = diff