UNPKG

emk

Version:

ECMAScript Make - a powerful build tool for the JavaScript elite

162 lines (124 loc) 3.23 kB
module.exports = class graph { static invert(h_edges) { let h_invs = {}; for(let si_node in h_edges) { for(let si_dep of h_edges[si_node]) { let as_sups = h_invs[si_dep]; if(!as_sups) as_sups = h_invs[si_dep] = new Set(); as_sups.add(si_node); } if(!h_invs[si_node]) h_invs[si_node] = new Set(); } return h_invs; } constructor() { Object.assign(this, { nodes: {}, outs: {}, invs: null, }); } schedule({cycle:fe_cyclic}) { let h_outs = this.outs; // make inverted graph direction let h_invs = this.invs = this.invs || graph.invert(h_outs); // check for DAG this.sort(this.outs, fe_cyclic); // transitive reduction this.reduce(h_outs); // prep rank hash let h_ranks = {}; // each leaf node for(let si_node in h_outs) { if(!h_outs[si_node].size) { // rank all super nodes this.rank(h_invs, si_node, h_ranks); } } // convert to array let a_ranks = []; for(let [si_node, i_rank] of Object.entries(h_ranks)) { (a_ranks[i_rank] = a_ranks[i_rank] || []).push(si_node); } return a_ranks; } rank(h_edges, si_node, h_ranks, i_rank=0) { // longest path h_ranks[si_node] = Math.max(i_rank, h_ranks[si_node] || 0); // each super dependency for(let si_sup of h_edges[si_node]) { this.rank(h_edges, si_sup, h_ranks, i_rank+1); } } reduce(h_edges) { // each node for(let si_node in h_edges) { // prep a hash for marking completion let h_done = {}; // each dependency; reduce it's span of decendents for(let si_dep of h_edges[si_node]) { this.reduce_pair(h_edges, si_node, si_dep, h_done); } } } reduce_pair(h_edges, si_node, si_dep, h_done) { // already checked this node if(si_node in h_done) return; // each sub dependency for(let si_subdep of h_edges[si_dep]) { // remove transitive edge if one exists h_edges[si_node].delete(si_subdep); // recurse this.reduce_pair(h_edges, si_node, si_subdep, h_done); } // flag as done h_done[si_node] = 1; } // check_dag(fe_cyclic) { // let { // outs: h_outs, // invs: h_invs, // } = this; // // prep marks hash // let h_marks = {}; // for(let s_key in h_outs) h_marks[s_key] = 0; // // each root node // for(let si_node in h_outs) { // if(!h_invs[si_node].size) { // this.visit(h_outs, si_node, h_marks, fe_cyclic); // } // } // } sort(h_edges, fe_cyclic) { let a_sorted = []; // prep marks hash let h_marks = {}; for(let s_key in h_edges) h_marks[s_key] = 0; // each unmarked node for(let si_node in h_edges) { if(!h_marks[si_node]) { // visit this.visit(h_edges, si_node, h_marks, fe_cyclic, a_sorted); } } return a_sorted; } visit(h_edges, si_node, h_marks, fe_cyclic, a_sorted=[]) { // test mark let xc_mark = h_marks[si_node]; // permanent, ok if(2 === xc_mark) return; // cycle detected if(1 === xc_mark) return fe_cyclic(si_node); // mark temporarily h_marks[si_node] = 1; // each dependency for(let si_dep of h_edges[si_node]) { this.visit(h_edges, si_dep, h_marks, fe_cyclic, a_sorted); } // mark permanently h_marks[si_node] = 2; // add to head of list a_sorted.unshift(si_node); } };