-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwalk.es6.js.mustache
127 lines (100 loc) · 6.04 KB
/
walk.es6.js.mustache
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// vim: ai:sts=3:sw=3:filetype=javascript
import Debug from 'debug'
const debug = Debug('giraphe:walk')
import assert from 'power-assert'
import _ from 'lodash'
// Okay, yes, before you flip your hat, this is a JavaScript file compiled through Mustache
// templates. Yes, I already know I'm a bad person and should feel bad. Can we move past that?
//
// The constructor that invokes this through `eval()` passes in some useful contextual modules,
// which we consume and close-over immediately. The actual meat is implemented in the
// function-object that we immediately return to `constructWalkFunction`.
//
// Various bits and pieces of `walk()` are completely omitted from the final function, based on
// analysis of the sorts of arguments it will be dealing with, preformed in the `Walker()`
// constructor.
//
// (Of note, there's also some nasty backflips to satisfy Babel; this entire file has to *transpile*
// as if it were, vaguely, a single block of source-code ... even though, at runtime, a completely
// *different* block of source-code, consisting of this file with chunks removed, is actually
// compiled. For instance, `const` is basically a no-no; and I can't excise parts of statements,
// unless the statements compile both with and without that part.)
export default function(symbols, $){ debug( 'walk() constructed:', $ )
return function walk(current, parent, cachebacks, runbacks, allbacks, visited = new Object){
debug( 'walk():', current )
assert( null != current )
assert( 0 !== allbacks.length )
let KEY;
/* {{#keyer?}} */ KEY = $.keyer.call(current, current) /* {{/keyer?}} */
/* {{^keyer?}} */ KEY = current[$.key] /* {{/keyer?}} */
assert( typeof KEY === 'string' && KEY !== '' )
if (visited[KEY]) return null
visited[KEY] = current
const DISCOVERED = new Object
let aborted = false
, rejected = false
rejected = ! _.every(runbacks, callback => {
const returned = callback.call(current, current, parent, DISCOVERED, visited, allbacks)
// If it returns a boolean, or nothing at all, then it's a ‘filter-back’,
if (false === returned) return false
else if (null == returned || true === returned) return true
// and ‘abort-backs’ are a special case,
else if (symbols.abortIteration === returned) {
aborted = yes
return false }
// else, it's a ‘supply-back’! These return either,
// FIXME: D.R.Y. this ffs
// 1. a single node,
/* {{#predicate?}} */
else if ($.predicate.call(returned, returned)) {
let key;
/* {{#keyer?}} */ key = $.keyer.call(returned, returned) /* {{/keyer?}} */
/* {{^keyer?}} */ key = returned[$.key] /* {{/keyer?}} */
assert( typeof key === 'string' && key !== '' )
DISCOVERED[key] = returned }
/* {{/predicate?}} {{^predicate?}} */
else if (returned instanceof $.class) {
let key;
/* {{#keyer?}} */ key = $.keyer.call(returned, returned) /* {{/keyer?}} */
/* {{^keyer?}} */ key = returned[$.key] /* {{/keyer?}} */
assert( typeof key === 'string' && key !== '' )
DISCOVERED[key] = returned }
/* {{/predicate?}} */
// 2. an `Array` of nodes,
else if (_.isArray(returned)) {
/* {{#predicate?}} */
assert( _.every(returned, node => $.predicate.call(node, node)) )
/* {{/predicate?}} {{^predicate?}} */
assert( _.every(returned, node => node instanceof $.class) )
/* {{/predicate?}} */
const elements = _.reduce(returned, (elements, node) => {
let key;
/* {{#keyer?}} */ key = $.keyer.call(node, node) /* {{/keyer?}} */
/* {{^keyer?}} */ key = node[$.key] /* {{/keyer?}} */
elements[key] = node
return elements
}, new Object)
_.assign(DISCOVERED, elements) }
// 3. or a generic Object, behaving as a map of keys-to-nodes.
else {
/* {{#predicate?}} */
assert( _.every(returned, node => $.predicate.call(node, node)) )
/* {{/predicate?}} {{^predicate?}} */
assert( _.every(returned, node => node instanceof $.class) )
/* {{/predicate?}} */
_.assign(DISCOVERED, returned) }
return true })
if (aborted) return false // If walk() returns `false`, it immediately propagates upwards,
if (rejected) return {} // but if it was rejected, then merely add nothing on this step
// ... else, the current node passed all filters, so we can add any
// discovered nodes (and itself!) to the return value for this step
let can_cache = true
for (key in DISCOVERED) {
const node = DISCOVERED[key]
, result = walk(node, current, cachebacks, runbacks, allbacks, visited)
if (false === result) return false // The only non-Object result is an `abortIteration`
// NYI
if (null == result) can_cache = null // invalidate the cache if *any* child was skipped
_.assign(DISCOVERED, result) }
DISCOVERED[KEY] = current
return DISCOVERED } }