mirror of
https://github.com/flynx/walk.js.git
synced 2025-10-29 11:00:13 +00:00
added a walk done handler + updated docs...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
7df2a6b4d9
commit
1f6b2a0f69
86
README.md
86
README.md
@ -3,22 +3,36 @@
|
|||||||
An extensible tree walk(..) framework...
|
An extensible tree walk(..) framework...
|
||||||
|
|
||||||
|
|
||||||
|
- [walk.js](#walkjs)
|
||||||
|
- [Theory and operation](#theory-and-operation)
|
||||||
|
- [Constructing the walker and walking ( walk(..) )](#constructing-the-walker-and-walking--walk)
|
||||||
|
- [Getting and processing nodes ( getter(..) )](#getting-and-processing-nodes--getter)
|
||||||
|
- [Putting it all together](#putting-it-all-together)
|
||||||
|
- [Installation and loading](#installation-and-loading)
|
||||||
|
- [API](#api)
|
||||||
|
- [`getter(..)`](#getter)
|
||||||
|
- [`done(..)` (optional)](#done-optional)
|
||||||
|
- [Examples](#examples)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Theory and operation
|
## Theory and operation
|
||||||
|
|
||||||
This module generalizes structure traverse (*walking*). This is done via a `walk(..)` function that recieves a user-defined `getter(..)` function and returns a *walker*.
|
This module generalizes structure traverse (*walking*). This is done via a `walk(..)` function that recieves a user-defined `getter(..)` function and returns a *walker*.
|
||||||
|
|
||||||
|
|
||||||
### Constructing the walker and walking
|
### Constructing the walker and walking ( walk(..) )
|
||||||
|
|
||||||
`walk(getter)(state, ...nodes) -> state`
|
`walk(getter[, done])(state, ...nodes) -> state`
|
||||||
`walk(getter, state)(...nodes) -> state`
|
`walk(getter[, done], state)(...nodes) -> state`
|
||||||
`walk(getter, state, ...nodes) -> state`
|
`walk(getter[, done], state, ...nodes) -> state`
|
||||||
- Recieves a `getter` function a `state` and a list of `nodes`,
|
- Recieves a `getter` function, an optional `done` function, a `state` and a list of `nodes`,
|
||||||
- Iterates through `nodes` calling the `getter(..)` per node, threading the `state` through each call,
|
- Iterates through `nodes`, calling the `getter(..)` per node and threading the `state` through each call,
|
||||||
|
- If `done(..)` is given, it is called passing the `state` after walking is done, the return value is set as the new `state` value,
|
||||||
- Returns the `state` when there are no more `nodes`.
|
- Returns the `state` when there are no more `nodes`.
|
||||||
|
|
||||||
|
|
||||||
### Getting and processing nodes
|
### Getting and processing nodes ( getter(..) )
|
||||||
|
|
||||||
`getter(state, node, next, stop) -> state`
|
`getter(state, node, next, stop) -> state`
|
||||||
- Recieves `state`, `node` and two control functions: `next` and `stop`,
|
- Recieves `state`, `node` and two control functions: `next` and `stop`,
|
||||||
@ -84,6 +98,7 @@ And for *flat* lists `.reduce(..)` and friends are simpler and more logical. `wa
|
|||||||
See a more usefull search in [examples](#examples)...
|
See a more usefull search in [examples](#examples)...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Installation and loading
|
## Installation and loading
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
@ -97,21 +112,27 @@ var walk = require('generic-walk').walk
|
|||||||
*Note: This module supports both AMD and node's `require(..)`**
|
*Note: This module supports both AMD and node's `require(..)`**
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
`walk(getter(..)) -> walker(state, ...nodes)`
|
`walk(getter(..)) -> walker(state, ...nodes)`
|
||||||
|
`walk(getter(..), done(..)) -> walker(state, ...nodes)`
|
||||||
Construct a reusable walker.
|
Construct a reusable walker.
|
||||||
|
|
||||||
|
|
||||||
`walk(getter(..), state) -> walker(...nodes)`
|
`walk(getter(..), state) -> walker(...nodes)`
|
||||||
|
`walk(getter(..), done(..), state) -> walker(...nodes)`
|
||||||
Construct a reusable walker with fixed initial state.
|
Construct a reusable walker with fixed initial state.
|
||||||
|
|
||||||
|
|
||||||
`walk(getter(..), state, ...nodes) -> result`
|
`walk(getter(..), state, ...nodes) -> result`
|
||||||
|
`walk(getter(..), done(..), state, ...nodes) -> result`
|
||||||
Walk the nodes.
|
Walk the nodes.
|
||||||
|
|
||||||
|
*Note that `state` can not be a function.*
|
||||||
|
|
||||||
### The getter
|
|
||||||
|
### `getter(..)`
|
||||||
|
|
||||||
`getter(state, node, next(..), stop(..)) -> state`
|
`getter(state, node, next(..), stop(..)) -> state`
|
||||||
User provided function, called to process a node.
|
User provided function, called to process a node.
|
||||||
@ -132,6 +153,13 @@ Stop walking and return `state`. The passed `state` is directly returned from th
|
|||||||
*Note that `stop(..)` behaves in a similar manner to `return`, i.e. execution is aborted immidiately.*
|
*Note that `stop(..)` behaves in a similar manner to `return`, i.e. execution is aborted immidiately.*
|
||||||
|
|
||||||
|
|
||||||
|
### `done(..)` (optional)
|
||||||
|
|
||||||
|
`done(state) -> state`
|
||||||
|
User provided function, if given, is called by the *walker* after walking is done (no more nodes to handle). `state` is passed as argument and the return value is returned from the *walker*. This is run in the same context (`this`) as `getter(..)`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
Sum all the values of a nested array (breadth-first)...
|
Sum all the values of a nested array (breadth-first)...
|
||||||
@ -158,31 +186,31 @@ sumd([1, [2], 3, [[4, 5]]]) // -> 15 ...walks the nodes: 1, 2, 3, 4, 5
|
|||||||
To explicitly see the paths the `sum`/`sumd` take we need to modify them a little:
|
To explicitly see the paths the `sum`/`sumd` take we need to modify them a little:
|
||||||
```javascript
|
```javascript
|
||||||
var makeSummer = function(mode){
|
var makeSummer = function(mode){
|
||||||
return walk(function(res, node, next){
|
var walker = walk(
|
||||||
this.log(node)
|
function(res, node, next){
|
||||||
return node instanceof Array ?
|
this.log(node)
|
||||||
next(mode == 'breadth-first' ? 'queue' : 'do', res, ...node)
|
return node instanceof Array ?
|
||||||
: res + node }, 0) }
|
next(mode == 'breadth-first' ? 'queue' : 'do',
|
||||||
|
res,
|
||||||
|
...node)
|
||||||
|
: res + node },
|
||||||
|
// print the path when done...
|
||||||
|
function(state){
|
||||||
|
console.log('-->', this.path)
|
||||||
|
return state
|
||||||
|
},
|
||||||
|
1)
|
||||||
|
// log the nodes...
|
||||||
|
walker.prototype.log = function(node){
|
||||||
|
this.path = node instanceof Array ?
|
||||||
|
this.path
|
||||||
|
: (this.path || []).concat([node]) }
|
||||||
|
return walker
|
||||||
|
}
|
||||||
|
|
||||||
var sum = makeSummer('breadth-first')
|
var sum = makeSummer('breadth-first')
|
||||||
var sumd = makeSummer('depth-first')
|
var sumd = makeSummer('depth-first')
|
||||||
|
|
||||||
// define the path logger...
|
|
||||||
sum.prototype.log =
|
|
||||||
sumd.prototype.log =
|
|
||||||
function(node){
|
|
||||||
this.path = node instanceof Array ?
|
|
||||||
this.path
|
|
||||||
: (this.path || []).concat([node])
|
|
||||||
}
|
|
||||||
// XXX need a more natural way to catch the end of the walk...
|
|
||||||
sum.prototype.onWalkEnd =
|
|
||||||
sumd.prototype.onWalkEnd =
|
|
||||||
function(res){
|
|
||||||
console.log('-->', this.path)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
sum([1, [2], 3, [[4, 5]]]) // -> 15
|
sum([1, [2], 3, [[4, 5]]]) // -> 15
|
||||||
|
|
||||||
sumd([1, [2], 3, [[4, 5]]]) // -> 15
|
sumd([1, [2], 3, [[4, 5]]]) // -> 15
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "generic-walk",
|
"name": "generic-walk",
|
||||||
"version": "1.1.0",
|
"version": "1.3.0",
|
||||||
"description": "An extensible tree walk(..) framework...",
|
"description": "An extensible tree walk(..) framework...",
|
||||||
"main": "walk.js",
|
"main": "walk.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
70
walk.js
70
walk.js
@ -7,38 +7,43 @@
|
|||||||
(function(require){ var module={} // make module AMD/node compatible...
|
(function(require){ var module={} // make module AMD/node compatible...
|
||||||
/*********************************************************************/
|
/*********************************************************************/
|
||||||
|
|
||||||
// XXX might be a good idea to add a way to do something on start/end of
|
// Walk a set of nodes...
|
||||||
// walk...
|
//
|
||||||
// ...we can easily determine if this is the first call to getter(..)
|
// Construct a walker...
|
||||||
// by checking and then setting an attr on the context...
|
// walk(getter(..))
|
||||||
|
// walk(getter(..), done(..))
|
||||||
|
// -> walker(state, ...nodes)
|
||||||
|
// -> state
|
||||||
|
//
|
||||||
|
// Construct a walker with a curried start state...
|
||||||
|
// walk(getter(..), state)
|
||||||
|
// walk(getter(..), done(..), state)
|
||||||
|
// -> walker(...nodes)
|
||||||
|
// -> state
|
||||||
|
//
|
||||||
|
// Walk the set of nodes...
|
||||||
|
// walk(getter(..), state, ...nodes)
|
||||||
|
// walk(getter(..), done(..), state, ...nodes)
|
||||||
|
// -> state
|
||||||
|
//
|
||||||
|
// NOTE: state can not be a function...
|
||||||
|
//
|
||||||
|
//
|
||||||
// XXX this is essentially a version of .reduce(..), I wonder if it is
|
// XXX this is essentially a version of .reduce(..), I wonder if it is
|
||||||
// feasible to do a version of .map(..), i.e. a mechanism to modify/clone
|
// feasible to do a version of .map(..), i.e. a mechanism to modify/clone
|
||||||
// the input...
|
// the input...
|
||||||
// XXX we need a way to handle the walk end event...
|
// XXX EXPERIMENTAL: done(..) handler is not yet final...
|
||||||
// ways this can be done:
|
|
||||||
// - a type argument to getter...
|
|
||||||
// getter(action, ...)
|
|
||||||
// getter('start', state)
|
|
||||||
// getter('node', state, node, ...)
|
|
||||||
// getter('stop', state)
|
|
||||||
// + uniform and adds no new clutter...
|
|
||||||
// - this will make the general case getter more complex
|
|
||||||
// Q: can we implement this or a similar approach in way
|
|
||||||
// that would abstract the user from the actions unless
|
|
||||||
// they want to handle it???
|
|
||||||
// ...currently, they would need to handle the actions
|
|
||||||
// to ignore the non-node actions...
|
|
||||||
// ...one way to go is to simply mark the first/last
|
|
||||||
// calls to getter in some way...
|
|
||||||
// the problem here is that there is no way to tell
|
|
||||||
// if a specific getter is last before the call is made...
|
|
||||||
// - event handlers (see .onWalkEnd) -- REJECTED
|
|
||||||
// - non-uniform...
|
|
||||||
// - a second handler passed to walk(..)
|
|
||||||
// - will mess up the signature that is already complex...
|
|
||||||
var walk =
|
var walk =
|
||||||
module.walk =
|
module.walk =
|
||||||
function(getter, state, ...nodes){
|
function(getter, state, ...nodes){
|
||||||
|
// XXX EXPERIMENTAL...
|
||||||
|
var done
|
||||||
|
// we've got a done handler passed...
|
||||||
|
if(state instanceof Function){
|
||||||
|
done = state
|
||||||
|
state = nodes.shift()
|
||||||
|
}
|
||||||
|
|
||||||
// holds the constructed walker function, used mainly to link its
|
// holds the constructed walker function, used mainly to link its
|
||||||
// .prototype to the context of the getter(..)...
|
// .prototype to the context of the getter(..)...
|
||||||
var func
|
var func
|
||||||
@ -141,11 +146,10 @@ function(getter, state, ...nodes){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// onWalkEnd handler...
|
// call the done handler...
|
||||||
// XXX need a more natural way to do this while providing access
|
// XXX EXPERIMENTAL...
|
||||||
// to the context...
|
res = done ?
|
||||||
res = context.onWalkEnd ?
|
done.call(context, res)
|
||||||
context.onWalkEnd(res)
|
|
||||||
: res
|
: res
|
||||||
|
|
||||||
return res
|
return res
|
||||||
@ -153,7 +157,7 @@ function(getter, state, ...nodes){
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
// reusable walker...
|
// reusable walker...
|
||||||
arguments.length == 1 ?
|
arguments.length == (done ? 2 : 1) ?
|
||||||
// NOTE: this wrapper is here so as to isolate and re-order res
|
// NOTE: this wrapper is here so as to isolate and re-order res
|
||||||
// construction and passing it to the next level...
|
// construction and passing it to the next level...
|
||||||
// this is needed as when we do:
|
// this is needed as when we do:
|
||||||
@ -167,7 +171,7 @@ function(getter, state, ...nodes){
|
|||||||
return _walk(nodes, state) })
|
return _walk(nodes, state) })
|
||||||
// reusable walker with a curried initial state...
|
// reusable walker with a curried initial state...
|
||||||
// NOTE: better keep this immutable or clone this in get(..)
|
// NOTE: better keep this immutable or clone this in get(..)
|
||||||
: arguments.length == 2 ?
|
: arguments.length == (done ? 3 : 2) ?
|
||||||
(func = function(...nodes){
|
(func = function(...nodes){
|
||||||
return _walk(nodes, state) })
|
return _walk(nodes, state) })
|
||||||
// walk...
|
// walk...
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user