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