All files / src/compiler/phases visitors.js

95.89% Statements 70/73
100% Branches 11/11
66.66% Functions 4/6
95.89% Lines 70/73

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 742x 2x 2x   2x 2x     2x 2x 2x 2x 2x 2x 2x 2x 2x 15348x 15348x 15348x 15348x 61298x 852048x 852048x 852048x 61298x 15348x 15348x 15348x 15348x 15348x 15348x 616284x 616284x 616284x 616284x 616284x 616284x 616284x 421986x 421986x 421986x 421986x 421986x 1139285x 1139285x 717594x 717594x 717594x 717594x 717594x 717594x 717594x 717594x 332203x 332203x 332203x 717594x 717594x 1139285x 385096x 385096x 1139285x 421986x 421986x 421986x 616284x 616284x 616284x 616284x 15348x 15348x 15348x  
/** @import { Visitors, Context } from 'zimmerframe' */
const overrides = {
	visit() {
		throw new Error('Cannot call visit() during analysis');
	},
	stop() {
		throw new Error('Cannot call stop() during analysis');
	}
};
 
/**
 * @template {{ type: string }} T
 * @template U
 * @param  {...Visitors<T, U>} tasks
 * @returns
 */
export function merge(...tasks) {
	/** @type {Record<string, any[]>} */
	const visitors = {};
 
	for (const task of tasks) {
		for (const key in task) {
			if (!visitors[key]) visitors[key] = [];
			visitors[key].push(task[key]);
		}
	}
 
	/** @type {Visitors<T, U>} */
	// @ts-expect-error
	const combined = {};
 
	for (const key in visitors) {
		const fns = visitors[key];
 
		/**
		 * @param {T} node
		 * @param {Context<T, U>} context
		 */
		function visitor(node, context) {
			/**
			 * @param {number} i
			 * @param {U} state
			 */
			function go(i, state) {
				const fn = fns[i];
				if (!fn) return context.next(state);
 
				let called_next = false;
 
				fn(node, {
					...context,
					...overrides,
					state,
					next(next_state = state) {
						called_next = true;
						go(i + 1, next_state);
					}
				});
 
				if (!called_next) {
					go(i + 1, state);
				}
			}
 
			go(0, context.state);
		}
 
		// @ts-expect-error
		combined[key] = visitor;
	}
 
	return combined;
}