refactoring .walk(..)...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2019-05-06 16:57:06 +03:00
parent fa40e6a2cf
commit ad47d8036c

View File

@ -798,7 +798,6 @@ var BaseBrowserPrototype = {
// Walk tree passign each node to func(..) and handle nested browser // Walk tree passign each node to func(..) and handle nested browser
// walking in recursion(..) optionally testing if walkable with walkable(..) // walking in recursion(..) optionally testing if walkable with walkable(..)
// .walk2(func(..), recursion(..)[, options]) // .walk2(func(..), recursion(..)[, options])
// .walk2(func(..), recursion(..), walkable(..)[, options])
// -> list // -> list
// //
// //
@ -812,12 +811,12 @@ var BaseBrowserPrototype = {
// //
// //
// Handle walkable node children (recursively)... // Handle walkable node children (recursively)...
// recursion(children, index, path, func(..), stop(..), walk(), options) // recursion(children, index, path, options, context, func(..), stop(..), walk())
// -> list // -> list
// //
// //
// Prepare arguments for call of name function on nested browser... // Prepare arguments for call of name function on nested browser...
// args(...list) // args(list, index, path, options, context, func(..), stop(..))
// -> list // -> list
// //
// //
@ -826,6 +825,9 @@ var BaseBrowserPrototype = {
// -> bool // -> bool
// //
// //
// For examples see: .text(..), .paths(..) and .map(..)
//
//
// NOTE: if recursion(..) is not given then .walk2(..) is used to // NOTE: if recursion(..) is not given then .walk2(..) is used to
// handle nested children... // handle nested children...
// NOTE: if walkable(..) is not given then we check for .walk2(..) // NOTE: if walkable(..) is not given then we check for .walk2(..)
@ -859,30 +861,29 @@ var BaseBrowserPrototype = {
&& args[0] instanceof Function) ? && args[0] instanceof Function) ?
args.shift() args.shift()
: null : null
var walkable = (args[0] instanceof Function
|| args[0] == null) ?
args.shift()
: null
options = args.shift() || {}
// sanity check... // sanity check...
if(formArgs == null && typeof(recursion) == typeof('str')){ if(formArgs == null && typeof(recursion) == typeof('str')){
throw new Error(`.walk2(func, name, formArgs, ..): ` throw new Error(`.walk2(func, name, formArgs, ..): `
+`expected function as third argument, got: ${formArgs}.`) } +`expected function as third argument, got: ${formArgs}.`) }
var walkable = (!formArgs
&& (args[0] instanceof Function
|| args[0] == null)) ?
args.shift()
: null
options = args.shift() || {}
// recursion-threaded args... // recursion context...
var i = args.shift() || 0 var context = args.shift()
var path = args.shift() || [] var path = context instanceof Array ?
var stopParent = args.shift() || null context
: ((context && context.path) || [])
context = context instanceof Array ?
{path: path}
: (context || {})
context.root = context.root || this
var i = context.index = context.index || 0
// options specifics... // options specifics...
options = !options.root ?
Object.assign({},
this.options || {},
options,
// set call context...
{ root: this })
: options
var iterateNonIterable = options.iterateAll || options.iterateNonIterable var iterateNonIterable = options.iterateAll || options.iterateNonIterable
var iterateCollapsed = options.iterateAll || options.iterateCollapsed var iterateCollapsed = options.iterateAll || options.iterateCollapsed
var skipNested = !options.iterateAll && options.skipNested var skipNested = !options.iterateAll && options.skipNested
@ -923,7 +924,15 @@ var BaseBrowserPrototype = {
: list : list
var useWalk = function(){ var useWalk = function(){
return list.walk2(func, recursion, walkable, options, i, p, stop) } return list.walk2(
func,
recursion,
...(formArgs instanceof Function ?
[formArgs]
: []),
walkable,
options,
context) }
// XXX can we add a simpler default case option where: // XXX can we add a simpler default case option where:
// - we call the target method name (given in recursion as string) // - we call the target method name (given in recursion as string)
@ -937,11 +946,11 @@ var BaseBrowserPrototype = {
next('do', state, ...(reverse ? list.slice().reverse() : list)) next('do', state, ...(reverse ? list.slice().reverse() : list))
// user-defined recursion... // user-defined recursion...
: recursion instanceof Function ? : recursion instanceof Function ?
recursion.call(that, list, i, p, func, stop, useWalk, options) recursion.call(that, list, i, p, options, context, func, stop, useWalk)
// method with arg forming... // method with arg forming...
: formArgs instanceof Function : formArgs instanceof Function
&& list[recursion] ? && list[recursion] ?
list[recursion](...(formArgs(list, i, p, func, stop, options) || [])) list[recursion](...(formArgs(list, i, p, options, context, func, stop) || []))
: useWalk()) : useWalk())
.run(function(){ .run(function(){
// normalize... // normalize...
@ -959,7 +968,8 @@ var BaseBrowserPrototype = {
// prepare context... // prepare context...
var id = node.id || node.value var id = node.id || node.value
path = this.path = this.path || path // XXX this is all over the place -- revise path handling...
path = context.path = this.path = this.path || path
var [inline, p, children] = var [inline, p, children] =
// inline... // inline...
isWalkable(node) ? isWalkable(node) ?
@ -968,7 +978,7 @@ var BaseBrowserPrototype = {
: (!skipNested && isWalkable(node.children)) ? : (!skipNested && isWalkable(node.children)) ?
[false, [false,
// prepare context for nested items... // prepare context for nested items...
this.path = path.concat([id]), context.path = this.path = path.concat([id]),
node.children] node.children]
// leaf... // leaf...
: [false, path.concat([id]), undefined] : [false, path.concat([id]), undefined]
@ -999,10 +1009,10 @@ var BaseBrowserPrototype = {
function(state, mode){ function(state, mode){
// if we stopped, thread the stop up... // if we stopped, thread the stop up...
mode == 'stopped' mode == 'stopped'
&& stopParent instanceof Function && context.stop instanceof Function
&& stopParent(state) && context.stop(state)
// normalize the result... // normalize the result...
return (options.root === that return (context.root === that
&& state instanceof Array) ? && state instanceof Array) ?
state.flat() state.flat()
: state }, : state },
@ -1018,49 +1028,38 @@ var BaseBrowserPrototype = {
// This is mainly here for doc/debug purposes... // This is mainly here for doc/debug purposes...
// //
// XXX rename this?? // XXX rename this??
text: function(options, base){ text: function(options, context){
var that = this var that = this
options = options || {}
options = !options.root ?
Object.assign({},
this.options || {},
options,
// set call context...
{ root: this })
: options
base = base || []
return this return this
.walk2( .walk2(
function(node, i, path){ function(node, i, path){
return node ? return node ?
base path.slice(1)
.concat(path) .map(e => ' ')
.slice(1) .join('') + (node.value || node)
.map(e => ' ')
.join('') + (node.value || node)
: [] }, : [] },
'text', 'text',
function(func, i, path){ function(func, i, path, options, context){
return [options, base.concat(path)] }, return [options, context] },
options) options,
.run(function(){ context)
return options.root === that ? .join('\n') },
this.join('\n') paths: function(options, context){
: this }) },
paths: function(options, base){
base = base || []
return this.walk2( return this.walk2(
function(n, i, p){ function(n, i, p){
return n return n
&& [(options || {}).joinPaths !== false ? && [(options || {}).joinPaths !== false ?
base.concat(p).join('/') p.join('/')
: base.concat(p)] }, : p] },
'paths', 'paths',
function(children, i, path){ function(_, i, path, options, context){
return [options, base.concat(path)] }, // NOTE: for paths and indexes to be consistent between
options) }, // levels we need to thread the context on, here and
// into the base .walk2(..) call below...
return [options, context] },
options,
context) },
// Extended map... // Extended map...
@ -1101,56 +1100,39 @@ var BaseBrowserPrototype = {
// - support for options // - support for options
// //
// //
//
// XXX should we move the defaults to .config??? // XXX should we move the defaults to .config???
// XXX make item access by index lazy...
// - index nested stuff and lengths... (.sublist_length)
// - stop when target reached... (control callback???)
// XXX Q: should we have an option to treat groups as elements??? // XXX Q: should we have an option to treat groups as elements???
map: function(func, options){ map: function(func, options){
var that = this var that = this
// parse args... // parse args...
//
// NOTE: in addition to the user signatures documented above this
// also supports two variants used internally:
// .map(func, path[, options])
// .map(func, i, path[, options])
// these set the "base" path and index passed to func...
var args = [...arguments] var args = [...arguments]
func = (args[0] instanceof Function func = (args[0] instanceof Function
|| args[0] === undefined) ? || args[0] === undefined) ?
args.shift() args.shift()
: undefined : undefined
options = args[args.length-1] || {} options = args.shift() || {}
options = !(typeof(options) == typeof(123)
|| options instanceof Array) ?
(args.pop() || {})
: {}
options = !options.defaultReverse ? options = !options.defaultReverse ?
Object.assign({}, Object.assign({},
options, options,
{ defaultReverse: 'flat' }) { defaultReverse: 'flat' })
: options : options
var context = args.shift()
return this.walk2(
return this.walk( function(elem, i, path){
function(i, path, elem, doNested){
return elem != null ? return elem != null ?
[func === undefined ? [func === undefined ?
elem elem
// XXX should this pass the current or the root // XXX should this pass the current or the root
// container to func??? // container to func???
: func.call(that, elem, i, path, that)] : func.call(that, elem, i, path, that)]
: [] }, : [] },
function(_, i, path, children, options){ 'map',
// NOTE: this needs to call the actual func that the user function(_, i, p, options, context){
// gave us and not the constructed function that we return [func, options, context] },
// pass to .walk(..) above... options,
return children.map(func, i, path, options) }, context) },
...args,
options)
},
// XXX EXPERIMENTAL... // XXX EXPERIMENTAL...
@ -1209,7 +1191,6 @@ var BaseBrowserPrototype = {
// //
// //
// XXX use diff // XXX use diff
// XXX add options.ignoreKeywords ???
// XXX add support for 'next'/'prev', ... keywords... (here or in .get(..)???) // XXX add support for 'next'/'prev', ... keywords... (here or in .get(..)???)
// XXX do we actually need to stop this as soon as we find something, // XXX do we actually need to stop this as soon as we find something,
// i.e. options.firstOnly??? // i.e. options.firstOnly???
@ -1219,18 +1200,23 @@ var BaseBrowserPrototype = {
// parse args... // parse args...
var args = [...arguments] var args = [...arguments]
pattern = args.shift() pattern = args.shift()
pattern = pattern === undefined ?
true
: pattern
func = (args[0] instanceof Function func = (args[0] instanceof Function
|| args[0] === undefined) ? || args[0] === undefined) ?
args.shift() args.shift()
: undefined : undefined
options = args[args.length-1] || {} options = args.shift() || {}
options = !(typeof(options) == typeof(123)
|| options instanceof Array) ?
(args.pop() || {})
: {}
// handle pattern keywords... var context = args.pop()
pattern = pattern == 'first' ?
// pattern -- normalize and pattern keywords...
pattern = options.ignoreKeywords ?
pattern
: (pattern === 'all' || pattern == '*') ?
true
: pattern == 'first' ?
0 0
: pattern == 'last' ? : pattern == 'last' ?
-1 -1
@ -1247,7 +1233,9 @@ var BaseBrowserPrototype = {
} }
// normalize/build the test predicate... // normalize/build the test predicate...
// XXX add diff support...
var test = ( var test = (
// all...
pattern === true ? pattern === true ?
pattern pattern
// predicate... // predicate...
@ -1302,26 +1290,28 @@ var BaseBrowserPrototype = {
// && elem[key] instanceof pattern) // && elem[key] instanceof pattern)
) }, true) } ) ) }, true) } )
return this.walk( return this.walk2(
function(i, path, elem, doNested){ function(elem, i, path, _, stop){
console.log('---', path.join('/'))
// match... // match...
// XXX should this use that??? var res = (elem
if(elem && (test === true || test.call(this, elem, i, path))){ && (test === true
// XXX i is not logical when getting items from the || test.call(this, elem, i, path))) ?
// tail of the list... [ func ?
// ...need to either set it to the negative index func.call(this, elem, i, path)
// or .length - i (expensive?) : elem ]
return func ? : []
[func.call(this, elem, i, path)] return ((options.firstMatch
: [[ || typeof(pattern) == typeof(123))
elem, && res.length > 0) ?
i, // XXX BUG: from nested browsers this does not stop
path, // the current level...
]] stop(res)
} : res },
return [] 'search',
}, function(_, i, p, options, context){
options) return [pattern, func, options, context] },
options, context)
}, },