preliminary format pattern complete...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2018-09-02 14:55:38 +03:00
parent c2949d3d35
commit 27a9ec0e47
2 changed files with 117 additions and 32 deletions

35
diff.js
View File

@ -251,6 +251,14 @@ var proxy = function(path, func){
// XXX need to avoid recursion... // XXX need to avoid recursion...
// XXX should we avoid backtracking when pattern matching??? // XXX should we avoid backtracking when pattern matching???
// ...specifically when working with IN and OF... // ...specifically when working with IN and OF...
// XXX diffing a mismatching pattern should yield the exact position
// (sub-pattern/rule) that failed and not just the whole pattern...
// ...usually a pattern chain fails, i.e. the nested failing pattern
// also fails its parent and so on, so it is not a trivial task
// getting the source and probably the whole failed chain...
// ...might be a good idea to build a trace failure pattern and
// store it in .trace in the diff...
//
// //
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
var LogicTypeClassPrototype = { var LogicTypeClassPrototype = {
@ -280,9 +288,9 @@ var LogicTypePrototype = {
|| (obj != null || (obj != null
&& obj.__cmp__ && obj.__cmp__
&& obj.__cmp__(this, cmp, cache)) && obj.__cmp__(this, cmp, cache))
c.set(obj, res) c.set(obj, !!res)
return res return !!res
}, },
} }
@ -456,8 +464,8 @@ module.ARRAY =
var NOT = var NOT =
module.NOT = module.NOT =
object.makeConstructor('NOT', Object.assign(new LogicType(), { object.makeConstructor('NOT', Object.assign(new LogicType(), {
__cmp__: function(obj, cmp){ __cmp__: function(obj, cmp, cache){
return !cmp(this.value, obj) }, return !cmp(this.value, obj, cache) },
__init__: function(value){ __init__: function(value){
this.value = value this.value = value
}, },
@ -468,9 +476,9 @@ object.makeConstructor('NOT', Object.assign(new LogicType(), {
var OR = var OR =
module.OR = module.OR =
object.makeConstructor('OR', Object.assign(new LogicType(), { object.makeConstructor('OR', Object.assign(new LogicType(), {
__cmp__: function(obj, cmp){ __cmp__: function(obj, cmp, cache){
for(var m of this.members){ for(var m of this.members){
if(cmp(m, obj)){ if(cmp(m, obj, cache)){
return true return true
} }
} }
@ -486,9 +494,9 @@ object.makeConstructor('OR', Object.assign(new LogicType(), {
var AND = var AND =
module.AND = module.AND =
object.makeConstructor('AND', Object.assign(new LogicType(), { object.makeConstructor('AND', Object.assign(new LogicType(), {
__cmp__: function(obj, cmp){ __cmp__: function(obj, cmp, cache){
for(var m of this.members){ for(var m of this.members){
if(!cmp(m, obj)){ if(!cmp(m, obj, cache)){
return false return false
} }
} }
@ -510,14 +518,14 @@ var IN =
module.IN = module.IN =
object.makeConstructor('IN', Object.assign(new LogicType(), { object.makeConstructor('IN', Object.assign(new LogicType(), {
// XXX add support for other stuff like sets and maps... // XXX add support for other stuff like sets and maps...
__cmp__: function(obj, cmp){ __cmp__: function(obj, cmp, cache){
var p = this.value var p = this.value
// XXX make this a break-on-match and not a go-through-the-whole-thing // XXX make this a break-on-match and not a go-through-the-whole-thing
return typeof(obj) == typeof({}) return typeof(obj) == typeof({})
&& (p in obj && (p in obj
|| obj.reduce(function(res, e){ || obj.reduce(function(res, e){
return res === false ? return res === false ?
cmp(p, e) cmp(p, e, cache)
: res }), false) }, : res }), false) },
__init__: function(value){ __init__: function(value){
this.value = value this.value = value
@ -538,8 +546,8 @@ object.makeConstructor('IN', Object.assign(new LogicType(), {
var AT = var AT =
module.AT = module.AT =
object.makeConstructor('AT', Object.assign(new LogicType(), { object.makeConstructor('AT', Object.assign(new LogicType(), {
__cmp__: function(obj, cmp){ __cmp__: function(obj, cmp, cache){
if(cmp(obj != null ? obj[this.key] : null, this.value)){ if(cmp(obj != null ? obj[this.key] : null, this.value, cache)){
return true return true
} }
return false return false
@ -1033,6 +1041,7 @@ module.Types = {
// the above. // the above.
// //
// NOTE: array path also supports patterns... // NOTE: array path also supports patterns...
// XXX should this use cmp(..) or this.cmp(..)
filter: function(diff, filter){ filter: function(diff, filter){
// string filter... // string filter...
filter = typeof(filter) == typeof('str') ? filter = typeof(filter) == typeof('str') ?
@ -1169,10 +1178,10 @@ module.Types = {
// pass options and cache down. // pass options and cache down.
// see cache setup below... // see cache setup below...
|| (diff(a, b) == null) } || (diff(a, b) == null) }
// cache... // cache...
//cache = this.__cache = cache || this.__cache || new Map() //cache = this.__cache = cache || this.__cache || new Map()
cache = cache || new Map() cache = cache || new Map()
// cached diff...
var diff = cache.diff = cache.diff || function(a, b){ var diff = cache.diff = cache.diff || function(a, b){
var l2 = cache.get(a) || new Map() 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, cache)

114
format.js
View File

@ -20,9 +20,14 @@ var {
/*********************************************************************/ /*********************************************************************/
// XXX need better mismatch checking -- ideally stating the exact spot
// where we did not match...
// //
// XXX need better mismatch checking -- ideally stating the exact spot
// where we did not match and the path of fails it created...
// XXX idea: would be nice to be able to use patterns to extract values
// from structures (parsing)...
//
//---------------------------------------------------------------------
// Flat diff...
var VALUE = var VALUE =
module.VALUE = OR( module.VALUE = OR(
@ -31,39 +36,108 @@ module.VALUE = OR(
ANY) ANY)
var SIDE_VALUES =
module.SIDE_VALUES = OR(
// A and B...
AND(
AT('A', VALUE),
AT('B', VALUE)),
// only A...
AT('A', VALUE),
// only B...
AT('B', VALUE))
var CHANGE = var CHANGE =
module.CHANGE = AND( module.CHANGE = AND(
AT('path', ARRAY), AT('path', ARRAY),
// XXX optional... // XXX optional...
// ...see DIFF_OBJECT's options for description... // ...see DIFF_OBJECT's options for description...
AT('type', OR(STRING, undefined)), AT('type', OR(STRING, undefined)),
OR( SIDE_VALUES)
// A ans B...
AND(
AT('A', VALUE),
AT('B', VALUE)),
// only A...
AT('A', VALUE),
// only B...
AT('B', VALUE)))
var DIFF_FORMAT_FLAT = var DIFF_FLAT =
module.DIFF_FORMAT_FLAT = ARRAY(CHANGE) module.DIFF_FLAT = OR(
ARRAY(CHANGE),
null)
//---------------------------------------------------------------------
// Tree diff...
var BASIC_CHANGE =
module.BASIC_CHANGE = AND(
AT('type', 'Basic'),
SIDE_VALUES)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
var OBJECT_ITEM =
module.OBJECT_ITEM = OR(
[STRING, DIFF_TREE],
[STRING, STRING, DIFF_TREE])
var OBJECT_CHANGE =
module.OBJECT_CHANGE = AND(
AT('type', 'Object'),
AT('items', ARRAY(OBJECT_ITEM)),
// XXX
AT('item_order', undefined))
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
var ARRAY_ITEM =
module.ARRAY_ITEM = OR(
[ANY, ANY, DIFF_TREE],
[[ANY, NUMBER], [ANY, NUMBER], DIFF_TREE])
var ARRAY_ITEMS =
module.ARRAY_ITEMS = AND(
AT('length', OR(
[NUMBER, NUMBER],
undefined)),
AT('items', ARRAY(
OR(
ARRAY_ITEM,
OBJECT_ITEM))),
// XXX
AT('item_order', undefined))
var ARRAY_CHANGE =
module.ARRAY_CHANGE = AND(
AT('type', 'Array'),
ARRAY_ITEMS)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
var TEXT_CHANGE =
module.TEXT_CHANGE = AND(
AT('type', 'Text'),
ARRAY_ITEMS)
// XXX it makes sense to make this a recursive pattern... // XXX it makes sense to make this a recursive pattern...
// ...need to check if we stop on a recursive pattern... // ...need to check if we stop on a recursive pattern...
var DIFF_FORMAT_TREE = // XXX TEST!!!
module.DIFF_FORMAT_TREE = ANY var DIFF_TREE =
module.DIFF_TREE = OR(
BASIC_CHANGE,
OBJECT_CHANGE,
ARRAY_CHANGE,
TEXT_CHANGE,
null)
//---------------------------------------------------------------------
// Diff...
var DIFF_OBJECT = var DIFF_OBJECT =
module.DIFF_OBJECT = AND( module.DIFF_OBJECT = AND(
// format metadata...
AT('format', diff.FORMAT_NAME), AT('format', diff.FORMAT_NAME),
//AT('version', STRING(/\d+\.\d+\.\d+/)), //AT('version', STRING(/\d+\.\d+\.\d+/)),
AT('version', diff.FORMAT_VERSION), AT('version', diff.FORMAT_VERSION),
// instance metadata...
AT('options', AND( AT('options', AND(
AT('tree_diff', OR(BOOL, null)), AT('tree_diff', OR(BOOL, null)),
AT('keep_none', OR(BOOL, null)), AT('keep_none', OR(BOOL, null)),
@ -74,18 +148,20 @@ module.DIFF_OBJECT = AND(
AT('no_length', OR(BOOL, null)), AT('no_length', OR(BOOL, null)),
AT('cmp', OR(FUNCTION, null)) )), AT('cmp', OR(FUNCTION, null)) )),
AT('placeholders', AND( AT('placeholders', AND(
// XXX would be nice to store these and to use them to test later... // XXX would be nice to store these and to use them to test
// deeper stuff (i.e. VALUE)...
AT('NONE', ANY), AT('NONE', ANY),
AT('EMPTY', ANY))), AT('EMPTY', ANY))),
AT('timestamp', NUMBER), AT('timestamp', NUMBER),
// diff...
OR( OR(
AND( AND(
AT('structure', 'flat'), AT('structure', 'flat'),
AT('diff', DIFF_FORMAT_FLAT)), AT('diff', DIFF_FLAT)),
AND( AND(
AT('structure', 'tree'), AT('structure', 'tree'),
AT('diff', DIFF_FORMAT_TREE))) ) AT('diff', DIFF_TREE))) )