mirror of
				https://github.com/flynx/diff.js.git
				synced 2025-10-30 19:40:10 +00:00 
			
		
		
		
	added stop(..) to walk(..) + examples...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
		
							parent
							
								
									3aa6577eb9
								
							
						
					
					
						commit
						61b57bca2a
					
				
							
								
								
									
										187
									
								
								diff.js
									
									
									
									
									
								
							
							
						
						
									
										187
									
								
								diff.js
									
									
									
									
									
								
							| @ -263,17 +263,23 @@ var proxy = function(path, func){ | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 | ||||||
|  | 
 | ||||||
| //
 | //
 | ||||||
| //	Construct a reusable walker function...
 | //	Construct a reusable walker function...
 | ||||||
| //	walk(get(..))
 | //	walk(get(..))
 | ||||||
| //		-> func(start, ...nodes)
 | //		-> func(state, ...nodes)
 | ||||||
|  | //
 | ||||||
|  | //	Construct a reusable walker function with set initial state...
 | ||||||
|  | //	walk(get(..), state)
 | ||||||
|  | //		-> func(...nodes)
 | ||||||
| //
 | //
 | ||||||
| //	Walk the nodes...
 | //	Walk the nodes...
 | ||||||
| //	walk(get(..), start, ...nodes)
 | //	walk(get(..), state, ...nodes)
 | ||||||
| //		-> res
 | //		-> res
 | ||||||
| //
 | //
 | ||||||
| // The get(..) that walk(..) expects has the following signature:
 | // The get(..) that walk(..) expects has the following signature:
 | ||||||
| //		get(res, node, next(..), down(..))
 | //		get(res, node, next(..), down(..), stop(..))
 | ||||||
| //			-> res
 | //			-> res
 | ||||||
| //
 | //
 | ||||||
| //		Queue next nodes to walk...
 | //		Queue next nodes to walk...
 | ||||||
| @ -286,41 +292,90 @@ var proxy = function(path, func){ | |||||||
| //				state.
 | //				state.
 | ||||||
| //
 | //
 | ||||||
| //		Walk the next set of nodes...
 | //		Walk the next set of nodes...
 | ||||||
| //		down(start, ...nodes)
 | //		down(state, ...nodes)
 | ||||||
| //			-> res
 | //			-> res
 | ||||||
| //			NOTE: this will process all the nodes and then return the 
 | //			NOTE: this will process all the nodes and then return the 
 | ||||||
| //				result.
 | //				result.
 | ||||||
| //
 | //
 | ||||||
|  | //		Stop the walk...
 | ||||||
|  | //		stop()
 | ||||||
|  | //		stop(result)
 | ||||||
|  | //			-> undefined
 | ||||||
|  | //			NOTE: this will break current function execution in a similar 
 | ||||||
|  | //				effect to 'return', i.e. no code in function will be 
 | ||||||
|  | //				executed after stop(..) call...
 | ||||||
|  | //			NOTE: this is done by throwing a special error, this error 
 | ||||||
|  | //				should either not be caught or must be re-thrown, as 
 | ||||||
|  | //				shadowing this exception may have unexpected results...
 | ||||||
|  | //
 | ||||||
| //
 | //
 | ||||||
| // Example:
 | // Example:
 | ||||||
| //		// sum all the values of a nested array...
 | //		// sum all the values of a nested array...
 | ||||||
| //		var sum = walk(function(res, node, next){
 | //		var sum = walk(function(res, node, next){
 | ||||||
| //			// go into arrays...
 | //			return node instanceof Array ?
 | ||||||
| //			if(node instanceof Array){
 | //				// compensate for that next(..) returns undefined...
 | ||||||
| //				next(...node) 
 | //				next(...node) 
 | ||||||
| //
 | //					|| res
 | ||||||
| //			// sum the values...
 | //				: res + node }, 0)
 | ||||||
| //			} else {
 |  | ||||||
| //				console.log(node)
 |  | ||||||
| //				res += node
 |  | ||||||
| //			}
 |  | ||||||
| //			return res
 |  | ||||||
| //		})
 |  | ||||||
| //
 | //
 | ||||||
| //		// depth first walker...
 | //		// depth first walker...
 | ||||||
| //		var sumd = walk(function(res, node, next, down){
 | //		var sumd = walk(function(res, node, next, down, stop){
 | ||||||
| //			if(node instanceof Array){
 | //			return node instanceof Array ?
 | ||||||
| //				res = down(res, ...node)
 | //				down(res, ...node)
 | ||||||
|  | //				: res + node }, 0)
 | ||||||
| //
 | //
 | ||||||
| //			} else {
 | //		sum([1, [2], 3, [[4, 5]]]) // -> 15 ...walks the nodes: 1, 3, 2, 4, 5
 | ||||||
| //				console.log(node)
 | //		sumd([1, [2], 3, [[4, 5]]]) // -> 15 ...walks the nodes: 1, 2, 3, 4, 5
 | ||||||
| //				res += node
 | //
 | ||||||
| //			}
 | //
 | ||||||
| //			return res
 | // Example:
 | ||||||
| //		})
 | // 		// XXX res/path threading seem unnatural here...
 | ||||||
|  | // 		var __firstZero = walk(function(res, node, next, down, stop){
 | ||||||
|  | // 			var k = node[0]
 | ||||||
|  | // 			var v = node[1]
 | ||||||
|  | // 			var path = res[0]
 | ||||||
|  | // 			return v === 0 ?
 | ||||||
|  | // 					// NOTE: we .slice(1) here to remove the initial null
 | ||||||
|  | // 					//		we added in firstZero(..)...
 | ||||||
|  | // 					stop([ path.slice(1).concat([k]) ])
 | ||||||
|  | // 				: v instanceof Object?
 | ||||||
|  | // 					down(
 | ||||||
|  | // 						[path.concat([k]), null], 
 | ||||||
|  | // 						...Object.entries(v))
 | ||||||
|  | // 				: res }, [[], null])
 | ||||||
|  | // 		var firstZero = function(value){ 
 | ||||||
|  | // 			// NOTE: we are wrapping the input here to make it the same 
 | ||||||
|  | // 			//		format as that of Object.entries(..) items...
 | ||||||
|  | // 			return __firstZero([null, value]).pop() }
 | ||||||
|  | //
 | ||||||
|  | //
 | ||||||
|  | // 		// same as the above but illustrates a different strategy, a bit
 | ||||||
|  | // 		// cleaner but creates a walker every time it's called...
 | ||||||
|  | // 		var firstZero = function(value){
 | ||||||
|  | // 			return walk(
 | ||||||
|  | // 				function(res, node, next, down, stop){
 | ||||||
|  | // 			        var k = node[0]
 | ||||||
|  | // 					var v = node[1]
 | ||||||
|  | // 					var path = res[0]
 | ||||||
|  | // 					return v === 0 ?
 | ||||||
|  | // 							// NOTE: we .slice(1) here to remove the initial null
 | ||||||
|  | // 							//		we added in firstZero(..)...
 | ||||||
|  | // 							stop([ path.slice(1).concat([k]) ])
 | ||||||
|  | // 						: v instanceof Object?
 | ||||||
|  | // 							down(
 | ||||||
|  | // 								[path.concat([k]), null], 
 | ||||||
|  | // 								...Object.entries(v))
 | ||||||
|  | // 						: res }, 
 | ||||||
|  | // 				[[], null], 
 | ||||||
|  | // 				// wrap the input to make it compatible with Object.entries(..) 
 | ||||||
|  | // 				// items...
 | ||||||
|  | // 				[null, value])
 | ||||||
|  | // 					// separate the result from path...
 | ||||||
|  | // 					.pop() }
 | ||||||
|  | //
 | ||||||
|  | //
 | ||||||
|  | // 		firstZero([10, 5, [{x: 1, y: 0}, 4]]) // -> ['2', '0', 'y']
 | ||||||
| //
 | //
 | ||||||
| //		sum([1, [2], 3, [[4, 5]]]) // -> 15 and log 1, 3, 2, 4, 5
 |  | ||||||
| //		sumd([1, [2], 3, [[4, 5]]]) // -> 15 and log 1, 2, 3, 4, 5
 |  | ||||||
| //
 | //
 | ||||||
| //
 | //
 | ||||||
| // NOTE: a walker is returned iff walk(..) is passed a single argument.
 | // NOTE: a walker is returned iff walk(..) is passed a single argument.
 | ||||||
| @ -329,60 +384,92 @@ var proxy = function(path, func){ | |||||||
| // 		walk(get)(start, ...nodes)
 | // 		walk(get)(start, ...nodes)
 | ||||||
| // NOTE: walk goes breadth first...
 | // NOTE: walk goes breadth first...
 | ||||||
| //
 | //
 | ||||||
| var walk = function(get, state, ...args){ | // XXX might be a good idea to move this into it's own module...
 | ||||||
| 	var _step = function(args, res){ | // 		generic-walk might be a good name...
 | ||||||
|  | var walk = function(get, state, ...nodes){ | ||||||
|  | 	// this is used to break out of the recursion...
 | ||||||
|  | 	// NOTE: this can leak out but we only care about it's identity thus
 | ||||||
|  | 	// 		no damage can be done...
 | ||||||
|  | 	var WalkStopException | ||||||
|  | 	var err_res | ||||||
|  | 
 | ||||||
|  | 	var _step = function(nodes, res){ | ||||||
| 		// construct a comfortable env for the user and handle the 
 | 		// construct a comfortable env for the user and handle the 
 | ||||||
| 		// results...
 | 		// results...
 | ||||||
| 		var _get = function(node){ | 		var _get = function(node){ | ||||||
| 			var next = [] | 			var next = [] | ||||||
| 			res = get(res, node,  | 			res = get( | ||||||
|  | 				res,  | ||||||
|  | 				node,  | ||||||
| 				// breadth first step...
 | 				// breadth first step...
 | ||||||
| 				// 	next(...nodes)
 | 				// 	next(...nodes) -> undefined
 | ||||||
| 				// 		-> undefined
 |  | ||||||
| 				// NOTE: this is different to down(..) in that this will 
 |  | ||||||
| 				// 		return immediately and no result is known at time 
 |  | ||||||
| 				// 		of call thus undefined is returned...
 |  | ||||||
| 				// NOTE: this is different to down(..) in that the user 
 |  | ||||||
| 				// 		indirectly gives this the state by returning it
 |  | ||||||
| 				// 		from the last get(..) called per level.
 |  | ||||||
| 				function(...nodes){ next = nodes }, | 				function(...nodes){ next = nodes }, | ||||||
| 				// depth first step...
 | 				// depth first step...
 | ||||||
| 				// 	down(state, ..nodes)
 | 				// 	down(state, ..nodes) -> res
 | ||||||
| 				// 		-> res
 |  | ||||||
| 				// NOTE: this is different to next as the user has control
 |  | ||||||
| 				// 		over what value to start with and will get the 
 |  | ||||||
| 				// 		result they can handle...
 |  | ||||||
| 				function(res, ...nodes){  | 				function(res, ...nodes){  | ||||||
| 					return _step(nodes, res) }) | 					return _step(nodes, res) }, | ||||||
|  | 				// stop...
 | ||||||
|  | 				// 	stop()
 | ||||||
|  | 				// 	stop(value)
 | ||||||
|  | 				//
 | ||||||
|  | 				// NOTE: 'throw' is used to stop all handling, including 
 | ||||||
|  | 				// 		the rest of the current level...
 | ||||||
|  | 				function(r){ | ||||||
|  | 					err_res = r | ||||||
|  | 					WalkStopException = new Error('WALK_STOP_EVENT') | ||||||
|  | 					throw WalkStopException  | ||||||
|  | 				}) | ||||||
| 			return next | 			return next | ||||||
| 		} | 		} | ||||||
| 		return args.length == 0 ? | 
 | ||||||
|  | 		return nodes.length == 0 ? | ||||||
| 			// no new nodes to walk...
 | 			// no new nodes to walk...
 | ||||||
| 			res | 			res | ||||||
| 			// do the next level...
 | 			// do the next level...
 | ||||||
| 			// NOTE: see note below... ( ;) )
 | 			// NOTE: see note below... ( ;) )
 | ||||||
| 			: _step(args | 			: _step(nodes | ||||||
| 				.map(_get) | 				.map(_get) | ||||||
| 				.reduce(function(next, e){ | 				.reduce(function(next, e){ | ||||||
| 					return e instanceof Array ?  | 					return e instanceof Array ?  | ||||||
| 						next.concat(e)  | 						next.concat(e)  | ||||||
| 						: next.push(e) }, []), res) }  | 						: next.push(e) }, []), res)  | ||||||
|  | 	}  | ||||||
|  | 	// call _step(..) and handle WalkStopException...
 | ||||||
|  | 	var _walk = function(nodes, res){ | ||||||
|  | 		try{ | ||||||
|  | 			return _step(nodes, res) | ||||||
| 
 | 
 | ||||||
| 	return arguments.length == 1 ? | 		} catch(e){ | ||||||
|  | 			// handle the abort condition...
 | ||||||
|  | 			if(e === WalkStopException){ | ||||||
|  | 				return err_res | ||||||
|  | 			} | ||||||
|  | 			// something broke...
 | ||||||
|  | 			throw e | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return ( | ||||||
| 		// return a reusable walker...
 | 		// return a reusable walker...
 | ||||||
|  | 		arguments.length == 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:
 | ||||||
| 		// 			step(res, ...args.map(_get).reduce(...))
 | 			// 			step(res, ...nodes.map(_get).reduce(...))
 | ||||||
| 			// 		res (if immutable) will first get passed by value and 
 | 			// 		res (if immutable) will first get passed by value and 
 | ||||||
| 			// 		then will get re-assigned in _get(..), thus step(..) will 
 | 			// 		then will get re-assigned in _get(..), thus step(..) will 
 | ||||||
| 			// 		not see the updated version...
 | 			// 		not see the updated version...
 | ||||||
| 			// 		This approach simply pushes the pass-by-value of res till 
 | 			// 		This approach simply pushes the pass-by-value of res till 
 | ||||||
| 			// 		after it gets updated by _get(..).
 | 			// 		after it gets updated by _get(..).
 | ||||||
| 		function(res, ...args){ | 			function(res, ...nodes){ | ||||||
| 			return _step(args, res) } | 				return _walk(nodes, res) } | ||||||
|  | 		// reusable walker with a curried initial state... 
 | ||||||
|  | 		// NOTE: better keep this immutable or clone this in get(..)
 | ||||||
|  | 		: arguments.length == 2 ? | ||||||
|  | 			function(...nodes){ | ||||||
|  | 				return _walk(nodes, state) } | ||||||
| 		// walk...
 | 		// walk...
 | ||||||
| 		: _step(args, res) | 		: _walk(nodes, state)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user