CodeBlanks Logo Icon CodeBlanks

Visitor design

Write the AST visitor with the visitor pattern.

Visitor design

The visitor pattern allows performing a separate operation for each node type while traversing a data structure. An AST visitor contains a visit method for each node type; the traverse function takes a node and calls the appropriate visit method.

interface Visitor {
  visitNumber(value: number): void;
  visitString(value: string): void;
  visitAdd(): void;
}

function traverse(n: Node, v: Visitor): void {
  switch (n.kind) {
    case "number": v.visitNumber(n.value); break;
    case "string": v.visitString(n.value); break;
    case "add": v.visitAdd(); break;
  }
}

Walking with traverse

The traverse function recursively walks the AST. For each node the relevant visit method is called; for nodes with children (e.g. add) the children are also traversed. This processes the whole tree.

function traverse(n: Node, v: Visitor): void {
  switch (n.kind) {
    case "number": v.visitNumber(n.value); break;
    case "string": v.visitString(n.value); break;
    case "add":
      v.visitAdd();
      traverse(n.left, v);
      traverse(n.right, v);
      break;
  }
}

Practice Walk the AST with a visitor

Define the Node discriminated union type ("number" (value: number), "string" (value: string), "add" (left: Node, right: Node)). Define the Visitor interface: with visitNumber(value: number): void, visitString(value: string): void, visitAdd(): void methods. Write a traverse(n: Node, v: Visitor): void function using switch. Create a Visitor implementation: visitNumber does console.log("n:" + value), visitString does console.log("s:" + value), visitAdd does console.log("add"). Call traverse({ kind: "add", left: { kind: "number", value: 1 }, right: { kind: "string", value: "x" } }, visitor).

Starter code

index.ts
// Node discriminated union türünü tanımla (kind: number, string, add)

// Visitor interface tanımla (visitNumber, visitString, visitAdd)

// traverse fonksiyonunu switch ile yaz

// Visitor uygulaması oluştur ve traverse çağır

Sample solution

index.ts
type Node =
  | { kind: "number"; value: number }
  | { kind: "string"; value: string }
  | { kind: "add"; left: Node; right: Node };

interface Visitor {
  visitNumber(value: number): void;
  visitString(value: string): void;
  visitAdd(): void;
}

function traverse(n: Node, v: Visitor): void {
  switch (n.kind) {
    case "number":
      v.visitNumber(n.value);
      break;
    case "string":
      v.visitString(n.value);
      break;
    case "add":
      v.visitAdd();
      traverse(n.left, v);
      traverse(n.right, v);
      break;
  }
}

const visitor: Visitor = {
  visitNumber: (value) => console.log("n:" + value),
  visitString: (value) => console.log("s:" + value),
  visitAdd: () => console.log("add"),
};

traverse({ kind: "add", left: { kind: "number", value: 1 }, right: { kind: "string", value: "x" } }, visitor);

Expected output

add
n:1
s:x

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 What is the core purpose of the visitor pattern?

  • To hide runtime errors
  • To merge node types
  • To perform a separate operation for each node type Correct answer
  • To delete nodes

Explanation: The visitor pattern defines a separate visit method for each node type; the traverse function calls the correct method based on the node type.

2 Visitor interface method

Question
interface Visitor {
  visitNumber(value: number): void;
  visitString(value: string): void;
  visit____(): void;
}
Answer
interface Visitor {
  visitNumber(value: number): void;
  visitString(value: string): void;
  visitAdd(): void;
}

Explanation: The visitor pattern contains a separate visit method for each node type; visitAdd is called for the add node.

3 Visitor design parts

Term Meaning
Visitor interface visit methods
traverse fonksiyonu walks the tree
switch (n.kind) calls correct visit
özyinelemeli traverse processes children

All lessons in Advanced