CodeBlanks Logo Icon CodeBlanks

Integration

Build the working compiler by combining all parts.

Integration

A type-safe compiler combines AST model, visitor pattern, type guards, and exhaustive eval function. AST nodes are modeled with discriminated union; visitor pattern enables traversal; type guards ease external narrowing; exhaustive eval safely processes all types.

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

function assertNever(x: never): never {
  throw new Error("Unexpected");
}

function evalNode(n: Node): string {
  switch (n.kind) {
    case "number": return String(n.value);
    case "string": return n.value;
    case "add": return evalNode(n.left) + evalNode(n.right);
    default: return assertNever(n);
  }
}

Full compiler

A full compiler takes the AST model, walks it with visitor, provides external narrowing with type guards, and evaluates with exhaustive eval. When a new node is added, all parts must be updated; assertNever guarantees this.

const ast: Node = {
  kind: "add",
  left: { kind: "number", value: 2 },
  right: { kind: "string", value: "x" }
};

console.log(evalNode(ast));  // "2x"

Practice Write a full compiler

Define an assertNever(x: never): never function (with throw new Error("Unexpected")). Define the Node discriminated union type ("number" (value: number), "string" (value: string), "add" (left: Node, right: Node)). Write an evalNode(n: Node): string function using switch (n.kind): for number return String(n.value), for string return n.value, for add return evalNode(n.left) + evalNode(n.right), for default write return assertNever(n);. Define an ast value of type Node: { kind: "add", left: { kind: "number", value: 2 }, right: { kind: "string", value: "x" } }. Print the result via console.log(evalNode(ast)).

Starter code

index.ts
// assertNever fonksiyonunu tanımla (never parametre, throw ile)

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

// evalNode fonksiyonunu switch ile yaz, default'ta assertNever çağır

// ast değerini tanımla ve evalNode ile değerlendirip yazdır

Sample solution

index.ts
function assertNever(x: never): never {
  throw new Error("Unexpected");
}

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

function evalNode(n: Node): string {
  switch (n.kind) {
    case "number":
      return String(n.value);
    case "string":
      return n.value;
    case "add":
      return evalNode(n.left) + evalNode(n.right);
    default:
      return assertNever(n);
  }
}

const ast: Node = {
  kind: "add",
  left: { kind: "number", value: 2 },
  right: { kind: "string", value: "x" }
};

console.log(evalNode(ast));

Expected output

2x

Questions and answers

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

1 Which patterns are used together in a type-safe compiler?

  • Only classes
  • Only functions
  • Only interfaces
  • Discriminated union, visitor, type guard, exhaustive check Correct answer

Explanation: A type-safe compiler uses discriminated union for the AST model, visitor for traversal, type guard for narrowing, and exhaustive check for safe evaluation, all together.

2 add node definition

Question
type Node =
  | { kind: "number"; value: number }
  | { kind: "string"; value: string }
  | { kind: ____; left: Node; right: Node };
Answer
type Node =
  | { kind: "number"; value: number }
  | { kind: "string"; value: string }
  | { kind: "add"; left: Node; right: Node };

Explanation: The add node is defined with its own discriminant value "add"; it carries left and right child nodes.

3 Integration parts

Term Meaning
AST modeli with discriminated union
Visitor deseni walks the tree
Type guard'lar provides external narrowing
Exhaustive eval safely processes all types

All lessons in Advanced