Skip to content

Commit 8ec68f9

Browse files
committed
Introduce NodePath and head inside NodeStack
1 parent 4799a7f commit 8ec68f9

File tree

4 files changed

+62
-5
lines changed

4 files changed

+62
-5
lines changed

.changeset/tidy-bananas-collect.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@codama/visitors-core': minor
3+
---
4+
5+
Introduces the `NodePath` type
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { Node } from '@codama/nodes';
2+
3+
export type NodePath<TNode extends Node = Node> = readonly [...Node[], TNode];
4+
5+
export function getLastNodeFromPath<TNode extends Node>(path: NodePath<TNode>): TNode {
6+
return path[path.length - 1] as TNode;
7+
}

packages/visitors-core/src/NodeStack.ts

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,35 @@
1-
import { GetNodeFromKind, InstructionNode, isNode, Node, NodeKind, ProgramNode } from '@codama/nodes';
1+
import {
2+
assertIsNode,
3+
GetNodeFromKind,
4+
InstructionNode,
5+
isNode,
6+
Node,
7+
NodeKind,
8+
ProgramNode,
9+
REGISTERED_NODE_KINDS,
10+
} from '@codama/nodes';
11+
12+
import { NodePath } from './NodePath';
213

314
export class NodeStack {
4-
private readonly stack: Node[];
15+
/**
16+
* Contains all the node stacks saved during the traversal.
17+
*
18+
* - The very last stack is the current stack which is being
19+
* used during the traversal.
20+
* - The other stacks can be used to save and restore the
21+
* current stack when jumping to different parts of the tree.
22+
*
23+
* There must at least be one stack in the heap at all times.
24+
*/
25+
private readonly heap: [...Node[][], Node[]];
26+
27+
constructor(...heap: readonly [...(readonly (readonly Node[])[]), readonly Node[]] | readonly []) {
28+
this.heap = heap.length === 0 ? [[]] : ([...heap.map(nodes => [...nodes])] as [...Node[][], Node[]]);
29+
}
530

6-
constructor(stack: Node[] = []) {
7-
this.stack = [...stack];
31+
public get stack(): Node[] {
32+
return this.heap[this.heap.length - 1];
833
}
934

1035
public push(node: Node): void {
@@ -19,6 +44,19 @@ export class NodeStack {
1944
return this.isEmpty() ? undefined : this.stack[this.stack.length - 1];
2045
}
2146

47+
public pushStack(newStack: readonly Node[] = []): void {
48+
this.heap.push([...newStack]);
49+
}
50+
51+
public popStack(): readonly Node[] {
52+
const oldStack = this.heap.pop() as Node[];
53+
if (this.heap.length === 0) {
54+
// TODO: Coded error
55+
throw new Error('The heap of stacks can never be empty.');
56+
}
57+
return [...oldStack] as readonly Node[];
58+
}
59+
2260
public find<TKind extends NodeKind>(kind: TKind | TKind[]): GetNodeFromKind<TKind> | undefined {
2361
for (let index = this.stack.length - 1; index >= 0; index--) {
2462
const node = this.stack[index];
@@ -39,12 +77,18 @@ export class NodeStack {
3977
return [...this.stack];
4078
}
4179

80+
public getPath<TKind extends NodeKind>(kind?: TKind | TKind[]): NodePath<GetNodeFromKind<TKind>> {
81+
const node = this.peek();
82+
assertIsNode(node, kind ?? REGISTERED_NODE_KINDS);
83+
return [...this.stack] as unknown as NodePath<GetNodeFromKind<TKind>>;
84+
}
85+
4286
public isEmpty(): boolean {
4387
return this.stack.length === 0;
4488
}
4589

4690
public clone(): NodeStack {
47-
return new NodeStack(this.stack);
91+
return new NodeStack(...this.heap);
4892
}
4993

5094
public toString(): string {

packages/visitors-core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export * from './interceptVisitor';
1212
export * from './LinkableDictionary';
1313
export * from './mapVisitor';
1414
export * from './mergeVisitor';
15+
export * from './NodePath';
1516
export * from './NodeSelector';
1617
export * from './NodeStack';
1718
export * from './nonNullableIdentityVisitor';

0 commit comments

Comments
 (0)