11cb0ef41Sopenharmony_ci// Perform a depth-first walk of a tree, ONLY doing the descent (visit)
21cb0ef41Sopenharmony_ci//
31cb0ef41Sopenharmony_ci// This uses a stack rather than recursion, so that it can handle deeply
41cb0ef41Sopenharmony_ci// nested trees without call stack overflows.  (My kingdom for proper TCO!)
51cb0ef41Sopenharmony_ci//
61cb0ef41Sopenharmony_ci// This is only used for cases where leave() is not specified.
71cb0ef41Sopenharmony_ci//
81cb0ef41Sopenharmony_ci// a
91cb0ef41Sopenharmony_ci// +-- b
101cb0ef41Sopenharmony_ci// |   +-- 1
111cb0ef41Sopenharmony_ci// |   +-- 2
121cb0ef41Sopenharmony_ci// +-- c
131cb0ef41Sopenharmony_ci//     +-- 3
141cb0ef41Sopenharmony_ci//     +-- 4
151cb0ef41Sopenharmony_ci//
161cb0ef41Sopenharmony_ci// Expect:
171cb0ef41Sopenharmony_ci// visit a
181cb0ef41Sopenharmony_ci// visit b
191cb0ef41Sopenharmony_ci// visit 1
201cb0ef41Sopenharmony_ci// visit 2
211cb0ef41Sopenharmony_ci// visit c
221cb0ef41Sopenharmony_ci// visit 3
231cb0ef41Sopenharmony_ci// visit 4
241cb0ef41Sopenharmony_ci//
251cb0ef41Sopenharmony_ci// stack.push(tree)
261cb0ef41Sopenharmony_ci// while stack not empty
271cb0ef41Sopenharmony_ci//   pop T from stack
281cb0ef41Sopenharmony_ci//   VISIT(T)
291cb0ef41Sopenharmony_ci//   get children C of T
301cb0ef41Sopenharmony_ci//   push each C onto stack
311cb0ef41Sopenharmony_ci
321cb0ef41Sopenharmony_ciconst depth = ({
331cb0ef41Sopenharmony_ci  visit,
341cb0ef41Sopenharmony_ci  filter,
351cb0ef41Sopenharmony_ci  getChildren,
361cb0ef41Sopenharmony_ci  tree,
371cb0ef41Sopenharmony_ci}) => {
381cb0ef41Sopenharmony_ci  const stack = []
391cb0ef41Sopenharmony_ci  const seen = new Map()
401cb0ef41Sopenharmony_ci
411cb0ef41Sopenharmony_ci  const next = () => {
421cb0ef41Sopenharmony_ci    while (stack.length) {
431cb0ef41Sopenharmony_ci      const node = stack.pop()
441cb0ef41Sopenharmony_ci      const res = visitNode(node)
451cb0ef41Sopenharmony_ci      if (isPromise(res)) {
461cb0ef41Sopenharmony_ci        return res.then(() => next())
471cb0ef41Sopenharmony_ci      }
481cb0ef41Sopenharmony_ci    }
491cb0ef41Sopenharmony_ci    return seen.get(tree)
501cb0ef41Sopenharmony_ci  }
511cb0ef41Sopenharmony_ci
521cb0ef41Sopenharmony_ci  const visitNode = (visitTree) => {
531cb0ef41Sopenharmony_ci    if (seen.has(visitTree)) {
541cb0ef41Sopenharmony_ci      return seen.get(visitTree)
551cb0ef41Sopenharmony_ci    }
561cb0ef41Sopenharmony_ci
571cb0ef41Sopenharmony_ci    seen.set(visitTree, null)
581cb0ef41Sopenharmony_ci    const res = visit ? visit(visitTree) : visitTree
591cb0ef41Sopenharmony_ci    if (isPromise(res)) {
601cb0ef41Sopenharmony_ci      const fullResult = res.then(resThen => {
611cb0ef41Sopenharmony_ci        seen.set(visitTree, resThen)
621cb0ef41Sopenharmony_ci        return kidNodes(visitTree)
631cb0ef41Sopenharmony_ci      })
641cb0ef41Sopenharmony_ci      seen.set(visitTree, fullResult)
651cb0ef41Sopenharmony_ci      return fullResult
661cb0ef41Sopenharmony_ci    } else {
671cb0ef41Sopenharmony_ci      seen.set(visitTree, res)
681cb0ef41Sopenharmony_ci      return kidNodes(visitTree)
691cb0ef41Sopenharmony_ci    }
701cb0ef41Sopenharmony_ci  }
711cb0ef41Sopenharmony_ci
721cb0ef41Sopenharmony_ci  const kidNodes = (kidTree) => {
731cb0ef41Sopenharmony_ci    const kids = getChildren(kidTree, seen.get(kidTree))
741cb0ef41Sopenharmony_ci    return isPromise(kids) ? kids.then(processKids) : processKids(kids)
751cb0ef41Sopenharmony_ci  }
761cb0ef41Sopenharmony_ci
771cb0ef41Sopenharmony_ci  const processKids = (kids) => {
781cb0ef41Sopenharmony_ci    kids = (kids || []).filter(filter)
791cb0ef41Sopenharmony_ci    stack.push(...kids)
801cb0ef41Sopenharmony_ci  }
811cb0ef41Sopenharmony_ci
821cb0ef41Sopenharmony_ci  stack.push(tree)
831cb0ef41Sopenharmony_ci  return next()
841cb0ef41Sopenharmony_ci}
851cb0ef41Sopenharmony_ci
861cb0ef41Sopenharmony_ciconst isPromise = p => p && typeof p.then === 'function'
871cb0ef41Sopenharmony_ci
881cb0ef41Sopenharmony_cimodule.exports = depth
89