Skip to content

Instantly share code, notes, and snippets.

@HerringtonDarkholme
Created April 13, 2025 02:42
Show Gist options
  • Save HerringtonDarkholme/42e4ada80f59274f91bf54b362c9df41 to your computer and use it in GitHub Desktop.
Save HerringtonDarkholme/42e4ada80f59274f91bf54b362c9df41 to your computer and use it in GitHub Desktop.
use async/await instead of enter/leave
// a generic node for example
interface GNode { text: string; kind: string; next(): GNode | null; children(): GNode[]; }
// Process one node during traversal
type Process<T> = (n: GNode, t: Traversal) => Promise<T> | T
// continue the traversal to process children/siblings
interface Next { <T>(f: Process<T>): Promise<T> | T }
// traversal context
interface Traversal {
// past nodes will be ready in Array
ancestors: GNode[]; prevSiblings: GNode[]
// future nodes access needs function
descendants: Next
nextAll: Next // all next siblings
dfs: Next // all nodes in dfs order
}
// example usage
async function collectImportUsage(node: GNode, { dfs, descendants }: Traversal) {
if (node.kind !== 'import') { return } // not import statement, skip
// find the imported identifier name
const importee = await descendants(n => n.kind === 'identifier' && n.text)
let count = 0 // local variable to count the importee usage
// collect info using closure, no extra book-keeping
await dfs(n => { count += n.kind === 'identifier' && n.text === importee ? 1 : 0 })
return count
}
declare function traverse<T>(node: GNode, f: Process<T>, init: T): Promise<T>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment