<!-- title: Claude Code编译器实战：五天造一门语言 -->

# Claude Code编译器实战：五天从零造一门编程语言

> **学习目标**：跟随Anthropic官方"16 Agent建C编译器"和Guy Powell"五天建编译器"两大标杆案例的方法论，用Claude Code从零构建一门完整编程语言——涵盖词法分析、语法解析、AST构建、字节码编译和栈式虚拟机。读完本文，你将掌握用AI编程工具系统化开发编译器的全流程。
>
> **适用人群**：有基本编程经验、想理解编译器原理但不打算啃800页教材的开发者；已会用Claude Code、想挑战复杂系统级项目的进阶用户。

---

## 一、为什么用Claude Code写编译器？

2026年2月，Anthropic安全研究团队的Nicholas Carlini做了一件事：启动16个并行Claude Code Agent，让它们从零写一个Rust实现的C编译器。两周、近2000次会话、约20000美元API费用后，诞生了一个10万行代码的编译器——能编译自身、通过99%的GCC测试用例、后端支持x86-64/ARM64/RISC-V三大架构[^1]。

独立开发者Guy Powell则走了另一条路：他用Claude AI做"联合架构师"，5天完成了一门语言+编译器+虚拟机。他的核心洞见是——**AI不替代你理解编译原理，而是把"从理解到可运行代码"的Gap从数月压缩到数天**[^2]。

这两条路径的共同启示：

```mermaid
flowchart TD
    A[编译器开发传统路径] -->|数月手写| B[Lexer → Parser → AST]
    B --> C[Semantic Analysis]
    C --> D[Code Generation]
    D --> E[VM / Native Output]

    F[Claude Code加速路径] -->|1-5天迭代| G[描述语言规范]
    G --> H[逐层生成+测试]
    H --> I[集成调试优化]

    style F fill:#e8f5e9
    style G fill:#e8f5e9
    style H fill:#e8f5e9
    style I fill:#e8f5e9
```

关键不是"AI帮你写编译器"，而是——**你设计架构，Claude Code负责把每一层从规范变成可运行的代码和测试**。

---

## 二、前置条件

| 条件 | 说明 |
|------|------|
| Claude Code | 已安装并配置好API Key（`claude --version` ≥ 1.0） |
| TypeScript 5.x | 本教程选择TS实现，兼顾可读性和类型安全 |
| Node.js 20+ | 运行时环境 |
| 基本编译原理知识 | 知道Lexer/Parser/AST是什么即可，细节由教程展开 |
| 测试框架 | Vitest（Claude Code可自动配置） |

```bash
# 创建项目
mkdir minilang && cd minilang
npm init -y
npm install -D vitest typescript @types/node
npx tsc --init

# 用Claude Code初始化项目结构
claude "帮我设计一个叫MiniLang的编程语言项目结构，包含src/lexer、src/parser、src/ast、src/compiler、src/vm五个模块，以及对应的tests目录"
```

---

## 三、编译器架构全景

我们要构建的语言"MiniLang"特性清单：

- 静态类型（number, string, boolean）
- 变量声明与赋值
- 算术/比较/逻辑运算
- if-else条件分支
- while循环
- 函数定义与调用
- 闭包支持

编译器采用经典三段式架构：

```mermaid
flowchart LR
    subgraph Frontend["前端 (Frontend)"]
        SRC[源代码] --> LEX[Lexer<br/>词法分析]
        LEX --> |Token流| PAR[Parser<br/>语法分析]
        PAR --> |AST| SEM[Semantic<br/>语义检查]
    end

    subgraph Middle["中间层 (Middle)"]
        SEM --> |带类型AST| COMP[Compiler<br/>字节码编译]
        COMP --> |字节码| OPT[Optimizer<br/>常量折叠等]
    end

    subgraph Backend["后端 (Backend)"]
        OPT --> VM[VM<br/>栈式虚拟机]
        VM --> OUT[执行结果]
    end

    style Frontend fill:#e3f2fd
    style Middle fill:#fff3e0
    style Backend fill:#e8f5e9
```

### 项目目录结构

```
minilang/
├── src/
│   ├── lexer/
│   │   ├── tokenizer.ts      # 词法分析器
│   │   └── token.ts           # Token类型定义
│   ├── parser/
│   │   ├── parser.ts          # 递归下降解析器
│   │   └── expression.ts      # 表达式解析（Pratt解析）
│   ├── ast/
│   │   ├── nodes.ts           # AST节点定义
│   │   └── visitor.ts         # 访问者模式接口
│   ├── compiler/
│   │   ├── compiler.ts        # AST→字节码编译器
│   │   └── opcode.ts          # 操作码定义
│   ├── vm/
│   │   ├── vm.ts              # 栈式虚拟机
│   │   ├── frame.ts           # 调用栈帧
│   │   └── object.ts          # 运行时对象
│   └── repl.ts                # 交互式REPL
├── tests/
│   ├── lexer.test.ts
│   ├── parser.test.ts
│   ├── compiler.test.ts
│   └── vm.test.ts
├── package.json
└── tsconfig.json
```

---

## 四、第一层：词法分析器（Lexer）

### 4.1 设计Token体系

第一步是定义语言的词法单元。把这段需求直接交给Claude Code：

```
请设计MiniLang的完整Token类型体系，要求：
1. 支持以下Token类型：Number, String, Identifier, 关键字(let, fn, if, else, while, return, true, false)
2. 运算符：+, -, *, /, %, ==, !=, <, >, <=, >=, &&, ||, !, =
3. 分隔符：(, ), {, }, [, ], ;, ,, :
4. 每个Token包含type, literal(原始值), line, column
5. 用TypeScript enum + class实现
6. 包含完整的单元测试
```

**src/lexer/token.ts** — Claude Code生成的核心结构：

```typescript
export enum TokenType {
  // Literals
  Number, String, Identifier,
  // Keywords
  Let, Fn, If, Else, While, Return, True, False,
  // Operators
  Plus, Minus, Star, Slash, Percent,
  Equal, EqualEqual, Bang, BangEqual,
  Less, LessEqual, Greater, GreaterEqual,
  And, Or,
  // Delimiters
  LParen, RParen, LBrace, RBrace,
  LBracket, RBracket, Semicolon, Comma, Colon,
  // Special
  Eof,
}

export class Token {
  constructor(
    public type: TokenType,
    public literal: string,
    public line: number,
    public column: number,
  ) {}
}

// 关键字映射表
const KEYWORDS: Record<string, TokenType> = {
  let:      TokenType.Let,
  fn:       TokenType.Fn,
  if:       TokenType.If,
  else:     TokenType.Else,
  while:    TokenType.While,
  return:   TokenType.Return,
  true:     TokenType.True,
  false:    TokenType.False,
};

export function lookupKeyword(ident: string): TokenType | null {
  return KEYWORDS[ident] ?? null;
}
```

### 4.2 实现Tokenizer

接下来让Claude Code生成词法分析器：

```
请实现src/lexer/tokenizer.ts，要求：
1. 逐字符扫描源码，生成Token数组
2. 正确处理多字符运算符（==, !=, <=, >=, &&, ||）
3. 跳过空白和注释（//单行注释）
4. 字符串字面量用双引号，支持转义字符\n, \t, \", \\
5. 数字支持整数和浮点数
6. 错误报告包含行列号
7. 先写测试用例再写实现
```

**src/lexer/tokenizer.ts** — 核心实现：

```typescript
import { Token, TokenType, lookupKeyword } from './token';

export class Lexer {
  private position = 0;
  private line = 1;
  private column = 1;
  private tokens: Token[] = [];

  constructor(private source: string) {}

  tokenize(): Token[] {
    while (!this.isAtEnd()) {
      this.skipWhitespace();
      if (this.isAtEnd()) break;
      this.scanToken();
    }
    this.tokens.push(new Token(TokenType.Eof, '', this.line, this.column));
    return this.tokens;
  }

  private scanToken(): void {
    const char = this.advance();

    switch (char) {
      // 单字符Token
      case '(': this.addToken(TokenType.LParen); break;
      case ')': this.addToken(TokenType.RParen); break;
      case '{': this.addToken(TokenType.LBrace); break;
      case '}': this.addToken(TokenType.RBrace); break;
      case '[': this.addToken(TokenType.LBracket); break;
      case ']': this.addToken(TokenType.RBracket); break;
      case ';': this.addToken(TokenType.Semicolon); break;
      case ',': this.addToken(TokenType.Comma); break;
      case ':': this.addToken(TokenType.Colon); break;
      case '+': this.addToken(TokenType.Plus); break;
      case '-': this.addToken(TokenType.Minus); break;
      case '*': this.addToken(TokenType.Star); break;
      case '/':
        if (this.match('/')) {
          // 单行注释
          while (this.peek() !== '\n' && !this.isAtEnd()) this.advance();
        } else {
          this.addToken(TokenType.Slash);
        }
        break;
      case '%': this.addToken(TokenType.Percent); break;
      // 双字符Token
      case '=':
        this.addToken(this.match('=') ? TokenType.EqualEqual : TokenType.Equal);
        break;
      case '!':
        this.addToken(this.match('=') ? TokenType.BangEqual : TokenType.Bang);
        break;
      case '<':
        this.addToken(this.match('=') ? TokenType.LessEqual : TokenType.Less);
        break;
      case '>':
        this.addToken(this.match('=') ? TokenType.GreaterEqual : TokenType.Greater);
        break;
      case '&':
        if (this.match('&')) this.addToken(TokenType.And);
        else throw this.error('&', '&&');
        break;
      case '|':
        if (this.match('|')) this.addToken(TokenType.Or);
        else throw this.error('|', '||');
        break;
      // 字符串
      case '"': this.readString(); break;
      default:
        if (this.isDigit(char)) {
          this.readNumber();
        } else if (this.isAlpha(char)) {
          this.readIdentifier();
        } else {
          throw new Error(
            `Unexpected character '${char}' at ${this.line}:${this.column}`
          );
        }
    }
  }

  private readNumber(): void {
    const start = this.position - 1;
    while (this.isDigit(this.peek())) this.advance();
    // 浮点数
    if (this.peek() === '.' && this.isDigit(this.peekNext())) {
      this.advance(); // 消费 '.'
      while (this.isDigit(this.peek())) this.advance();
    }
    const literal = this.source.slice(start, this.position);
    this.tokens.push(new Token(TokenType.Number, literal, this.line, this.column));
  }

  private readString(): void {
    const start = this.position;
    while (this.peek() !== '"' && !this.isAtEnd()) {
      if (this.peek() === '\n') this.line++;
      if (this.peek() === '\\') this.advance(); // 跳过转义
      this.advance();
    }
    if (this.isAtEnd()) throw new Error('Unterminated string');
    this.advance(); // 消费结尾 "
    const literal = this.source.slice(start, this.position - 1);
    this.tokens.push(
      new Token(TokenType.String, literal.replace(/\\n/g, '\n')
        .replace(/\\t/g, '\t').replace(/\\"/g, '"').replace(/\\\\/g, '\\'),
        this.line, this.column)
    );
  }

  private readIdentifier(): void {
    const start = this.position - 1;
    while (this.isAlphaNumeric(this.peek())) this.advance();
    const literal = this.source.slice(start, this.position);
    const type = lookupKeyword(literal) ?? TokenType.Identifier;
    this.tokens.push(new Token(type, literal, this.line, this.column));
  }

  // 辅助方法省略...
  private advance(): string { this.column++; return this.source[this.position++]; }
  private peek(): string { return this.source[this.position] ?? '\0'; }
  private peekNext(): string { return this.source[this.position + 1] ?? '\0'; }
  private match(expected: string): boolean {
    if (this.source[this.position] !== expected) return false;
    this.position++; this.column++; return true;
  }
  private isAtEnd(): boolean { return this.position >= this.source.length; }
  private addToken(type: TokenType): void {
    this.tokens.push(new Token(type, this.source[this.position - 1], this.line, this.column));
  }
  private isDigit(c: string): boolean { return c >= '0' && c <= '9'; }
  private isAlpha(c: string): boolean { return /[a-zA-Z_]/.test(c); }
  private isAlphaNumeric(c: string): boolean { return this.isAlpha(c) || this.isDigit(c); }
  private skipWhitespace(): void {
    while (/\s/.test(this.peek())) {
      if (this.peek() === '\n') { this.line++; this.column = 0; }
      this.advance();
    }
  }
  private error(char: string, expected: string): Error {
    return new Error(`Expected '${expected}' at ${this.line}:${this.column}`);
  }
}
```

### 4.3 测试驱动开发

让Claude Code为Lexer生成测试——这是AI辅助编译器开发的核心优势。你给出规范，它同时生成测试和实现：

```typescript
// tests/lexer.test.ts
import { describe, it, expect } from 'vitest';
import { Lexer } from '../src/lexer/tokenizer';
import { TokenType } from '../src/lexer/token';

describe('Lexer', () => {
  it('tokenizes number literals', () => {
    const tokens = new Lexer('42 3.14').tokenize();
    expect(tokens[0].type).toBe(TokenType.Number);
    expect(tokens[0].literal).toBe('42');
    expect(tokens[1].type).toBe(TokenType.Number);
    expect(tokens[1].literal).toBe('3.14');
  });

  it('tokenizes string literals with escapes', () => {
    const tokens = new Lexer('"hello\\nworld"').tokenize();
    expect(tokens[0].type).toBe(TokenType.String);
    expect(tokens[0].literal).toBe('hello\nworld');
  });

  it('recognizes all keywords', () => {
    const src = 'let fn if else while return true false';
    const tokens = new Lexer(src).tokenize();
    const types = tokens.map(t => t.type);
    expect(types).toContain(TokenType.Let);
    expect(types).toContain(TokenType.Fn);
    expect(types).toContain(TokenType.If);
    expect(types).toContain(TokenType.Else);
    expect(types).toContain(TokenType.While);
    expect(types).toContain(TokenType.Return);
    expect(types).toContain(TokenType.True);
    expect(types).toContain(TokenType.False);
  });

  it('handles two-character operators', () => {
    const tokens = new Lexer('== != <= >= && ||').tokenize();
    expect(tokens[0].type).toBe(TokenType.EqualEqual);
    expect(tokens[1].type).toBe(TokenType.BangEqual);
    expect(tokens[2].type).toBe(TokenType.LessEqual);
    expect(tokens[3].type).toBe(TokenType.GreaterEqual);
    expect(tokens[4].type).toBe(TokenType.And);
    expect(tokens[5].type).toBe(TokenType.Or);
  });

  it('skips comments', () => {
    const tokens = new Lexer('let x = 1 // comment\nlet y = 2').tokenize();
    const lets = tokens.filter(t => t.type === TokenType.Let);
    expect(lets).toHaveLength(2);
  });

  it('reports errors with position', () => {
    expect(() => new Lexer('let x = @').tokenize()).toThrow(/Unexpected character/);
  });
});
```

**Claude Code工作流提示**：使用 `claude --test` 让它在每次修改后自动运行测试。遇到失败时，直接把错误输出粘贴给它，让它修复。

---

## 五、第二层：抽象语法树（AST）

### 5.1 设计AST节点

AST是编译器的"中央货币"——前后端都围绕它工作。用Claude Code生成类型安全的AST定义：

```
请为MiniLang设计完整的AST节点体系：
1. 使用TypeScript discriminated union，每个节点有kind字段用于类型判断
2. 表达式节点：NumberLiteral, StringLiteral, BooleanLiteral, Identifier, BinaryExpr, UnaryExpr, CallExpr, AssignExpr
3. 语句节点：LetStmt, ExpressionStmt, IfStmt, WhileStmt, FnDeclStmt, ReturnStmt, BlockStmt
4. 所有节点共享一个基础接口 { kind: string; line: number; column: number; }
5. 使用访问者模式（Visitor Pattern）定义遍历接口
```

**src/ast/nodes.ts** — 核心定义：

```typescript
// ============ 基础接口 ============
export interface ASTNode {
  kind: string;
  line: number;
  column: number;
}

// ============ 表达式节点 ============
export interface NumberLiteral extends ASTNode {
  kind: 'NumberLiteral';
  value: number;
}

export interface StringLiteral extends ASTNode {
  kind: 'StringLiteral';
  value: string;
}

export interface BooleanLiteral extends ASTNode {
  kind: 'BooleanLiteral';
  value: boolean;
}

export interface Identifier extends ASTNode {
  kind: 'Identifier';
  name: string;
}

export interface BinaryExpr extends ASTNode {
  kind: 'BinaryExpr';
  operator: string;
  left: Expression;
  right: Expression;
}

export interface UnaryExpr extends ASTNode {
  kind: 'UnaryExpr';
  operator: string;
  operand: Expression;
}

export interface CallExpr extends ASTNode {
  kind: 'CallExpr';
  callee: Expression;
  args: Expression[];
}

export interface AssignExpr extends ASTNode {
  kind: 'AssignExpr';
  target: Identifier;
  value: Expression;
}

export type Expression =
  | NumberLiteral | StringLiteral | BooleanLiteral
  | Identifier | BinaryExpr | UnaryExpr
  | CallExpr | AssignExpr;

// ============ 语句节点 ============
export interface LetStmt extends ASTNode {
  kind: 'LetStmt';
  name: string;
  initializer: Expression;
}

export interface ExpressionStmt extends ASTNode {
  kind: 'ExpressionStmt';
  expression: Expression;
}

export interface IfStmt extends ASTNode {
  kind: 'IfStmt';
  condition: Expression;
  thenBranch: BlockStmt;
  elseBranch?: BlockStmt | IfStmt;
}

export interface WhileStmt extends ASTNode {
  kind: 'WhileStmt';
  condition: Expression;
  body: BlockStmt;
}

export interface FnDeclStmt extends ASTNode {
  kind: 'FnDeclStmt';
  name: string;
  params: string[];
  body: BlockStmt;
}

export interface ReturnStmt extends ASTNode {
  kind: 'ReturnStmt';
  value?: Expression;
}

export interface BlockStmt extends ASTNode {
  kind: 'BlockStmt';
  statements: Statement[];
}

export type Statement =
  | LetStmt | ExpressionStmt | IfStmt
  | WhileStmt | FnDeclStmt | ReturnStmt | BlockStmt;

export type Program = BlockStmt;
```

### 5.2 访问者模式

```typescript
// src/ast/visitor.ts
export interface ASTVisitor<T> {
  visitNumberLiteral(node: NumberLiteral): T;
  visitStringLiteral(node: StringLiteral): T;
  visitBooleanLiteral(node: BooleanLiteral): T;
  visitIdentifier(node: Identifier): T;
  visitBinaryExpr(node: BinaryExpr): T;
  visitUnaryExpr(node: UnaryExpr): T;
  visitCallExpr(node: CallExpr): T;
  visitAssignExpr(node: AssignExpr): T;
  visitLetStmt(node: LetStmt): T;
  visitExpressionStmt(node: ExpressionStmt): T;
  visitIfStmt(node: IfStmt): T;
  visitWhileStmt(node: WhileStmt): T;
  visitFnDeclStmt(node: FnDeclStmt): T;
  visitReturnStmt(node: ReturnStmt): T;
  visitBlockStmt(node: BlockStmt): T;
}

export function accept<T>(node: ASTNode, visitor: ASTVisitor<T>): T {
  switch (node.kind) {
    case 'NumberLiteral':  return visitor.visitNumberLiteral(node as NumberLiteral);
    case 'StringLiteral':  return visitor.visitStringLiteral(node as StringLiteral);
    case 'BooleanLiteral': return visitor.visitBooleanLiteral(node as BooleanLiteral);
    case 'Identifier':     return visitor.visitIdentifier(node as Identifier);
    case 'BinaryExpr':     return visitor.visitBinaryExpr(node as BinaryExpr);
    case 'UnaryExpr':      return visitor.visitUnaryExpr(node as UnaryExpr);
    case 'CallExpr':       return visitor.visitCallExpr(node as CallExpr);
    case 'AssignExpr':     return visitor.visitAssignExpr(node as AssignExpr);
    case 'LetStmt':        return visitor.visitLetStmt(node as LetStmt);
    case 'ExpressionStmt': return visitor.visitExpressionStmt(node as ExpressionStmt);
    case 'IfStmt':         return visitor.visitIfStmt(node as IfStmt);
    case 'WhileStmt':      return visitor.visitWhileStmt(node as WhileStmt);
    case 'FnDeclStmt':     return visitor.visitFnDeclStmt(node as FnDeclStmt);
    case 'ReturnStmt':     return visitor.visitReturnStmt(node as ReturnStmt);
    case 'BlockStmt':      return visitor.visitBlockStmt(node as BlockStmt);
    default: throw new Error(`Unknown node kind: ${node.kind}`);
  }
}
```

---

## 六、第三层：语法解析器（Parser）

这是编译器最复杂的部分。我们采用**Pratt解析器**（运算符优先级解析）处理表达式，递归下降处理语句——Guy Powell在他的5天编译器中用的也是这个策略。

### 6.1 运算符优先级表

| 优先级 | 运算符 | 结合性 |
|--------|--------|--------|
| 1（最低）| `\|\|` | 左结合 |
| 2 | `&&` | 左结合 |
| 3 | `==` `!=` | 左结合 |
| 4 | `<` `>` `<=` `>=` | 左结合 |
| 5 | `+` `-` | 左结合 |
| 6 | `*` `/` `%` | 左结合 |
| 7（最高）| `!` `-`（一元）| 右结合 |

### 6.2 让Claude Code实现Parser

这是最适合展示Claude Code"分而治之"能力的环节。不要一次让它写整个Parser——按模块拆分请求：

**请求1**：先生成表达式解析器
```
请实现src/parser/expression.ts，使用Pratt解析器算法：
1. 定义前缀解析函数（prefix parser）：NumberLiteral, StringLiteral, BooleanLiteral, Identifier, 分组表达式, 一元运算符
2. 定义中缀解析函数（infix parser）：二元运算符（按优先级表）, 函数调用
3. 暴露parseExpression(minPrec: number)函数
4. 包含完善的错误恢复（panic mode synchronization）
```

**请求2**：再生成语句解析器
```
请实现src/parser/parser.ts，使用递归下降：
1. parseProgram() → parseStatement() 分发
2. 每种语句类型对应一个parse函数：parseLet, parseFn, parseIf, parseWhile, parseReturn, parseBlock
3. 语句之间用分号分隔
4. 表达式语句由expression.ts的parseExpression处理
```

**src/parser/expression.ts** — Pratt解析核心：

```typescript
import { Token, TokenType } from '../lexer/token';
import type { Expression } from '../ast/nodes';

type PrefixParser = () => Expression;
type InfixParser = (left: Expression) => Expression;

interface ParseRule {
  prefix?: PrefixParser;
  infix?: InfixParser;
  precedence: number;
}

const PREC = {
  None: 0, Or: 1, And: 2, Equality: 3,
  Comparison: 4, Term: 5, Factor: 6, Unary: 7, Call: 8,
};

export class ExpressionParser {
  private current = 0;

  constructor(private tokens: Token[]) {}

  parseExpression(minPrec: number = PREC.None): Expression {
    const prefixRule = this.getRule(this.peek().type).prefix;
    if (!prefixRule) throw this.error(this.peek(), 'Expected expression');

    let left = prefixRule.call(this);

    while (minPrec < this.getRule(this.peek().type).precedence) {
      const infixRule = this.getRule(this.peek().type).infix;
      if (!infixRule) break;
      this.advance();
      left = infixRule.call(this, left);
    }
    return left;
  }

  // 前缀解析：字面量、标识符、一元运算符、分组
  private numberLiteral(): Expression {
    const token = this.previous();
    return { kind: 'NumberLiteral', value: parseFloat(token.literal), line: token.line, column: token.column };
  }

  private stringLiteral(): Expression {
    const token = this.previous();
    return { kind: 'StringLiteral', value: token.literal, line: token.line, column: token.column };
  }

  private booleanLiteral(value: boolean): Expression {
    const token = this.previous();
    return { kind: 'BooleanLiteral', value, line: token.line, column: token.column };
  }

  private identifier(): Expression {
    const token = this.previous();
    return { kind: 'Identifier', name: token.literal, line: token.line, column: token.column };
  }

  private grouping(): Expression {
    const expr = this.parseExpression(PREC.None);
    this.consume(TokenType.RParen, "Expected ')' after expression");
    return expr;
  }

  private unary(): Expression {
    const op = this.previous();
    const operand = this.parseExpression(PREC.Unary);
    return { kind: 'UnaryExpr', operator: op.literal, operand, line: op.line, column: op.column };
  }

  // 中缀解析：二元运算
  private binary(left: Expression): Expression {
    const op = this.previous();
    const rule = this.getRule(op.type);
    const right = this.parseExpression(rule.precedence);
    return { kind: 'BinaryExpr', operator: op.literal, left, right, line: op.line, column: op.column };
  }

  // 中缀解析：函数调用
  private call(callee: Expression): Expression {
    const args: Expression[] = [];
    if (!this.check(TokenType.RParen)) {
      do { args.push(this.parseExpression(PREC.None)); }
      while (this.match(TokenType.Comma));
    }
    const paren = this.consume(TokenType.RParen, "Expected ')' after arguments");
    return { kind: 'CallExpr', callee, args, line: paren.line, column: paren.column };
  }

  // 解析规则表
  private getRule(type: TokenType): ParseRule {
    const rules: Partial<Record<TokenType, ParseRule>> = {
      [TokenType.Number]:  { prefix: this.numberLiteral.bind(this), precedence: PREC.None },
      [TokenType.String]:  { prefix: this.readString.bind(this), precedence: PREC.None },
      [TokenType.True]:    { prefix: () => this.booleanLiteral(true), precedence: PREC.None },
      [TokenType.False]:   { prefix: () => this.booleanLiteral(false), precedence: PREC.None },
      [TokenType.Identifier]: { prefix: this.identifier.bind(this), precedence: PREC.None },
      [TokenType.LParen]:  { prefix: this.grouping.bind(this), infix: this.call.bind(this), precedence: PREC.Call },
      [TokenType.Bang]:    { prefix: this.unary.bind(this), precedence: PREC.None },
      [TokenType.Minus]:   { prefix: this.unary.bind(this), infix: this.binary.bind(this), precedence: PREC.Term },
      [TokenType.Plus]:    { infix: this.binary.bind(this), precedence: PREC.Term },
      [TokenType.Star]:    { infix: this.binary.bind(this), precedence: PREC.Factor },
      [TokenType.Slash]:   { infix: this.binary.bind(this), precedence: PREC.Factor },
      [TokenType.Percent]: { infix: this.binary.bind(this), precedence: PREC.Factor },
      [TokenType.EqualEqual]: { infix: this.binary.bind(this), precedence: PREC.Equality },
      [TokenType.BangEqual]:  { infix: this.binary.bind(this), precedence: PREC.Equality },
      [TokenType.Less]:       { infix: this.binary.bind(this), precedence: PREC.Comparison },
      [TokenType.LessEqual]:  { infix: this.binary.bind(this), precedence: PREC.Comparison },
      [TokenType.Greater]:    { infix: this.binary.bind(this), precedence: PREC.Comparison },
      [TokenType.GreaterEqual]: { infix: this.binary.bind(this), precedence: PREC.Comparison },
      [TokenType.And]:        { infix: this.binary.bind(this), precedence: PREC.And },
      [TokenType.Or]:         { infix: this.binary.bind(this), precedence: PREC.Or },
    };
    return rules[type] ?? { precedence: PREC.None };
  }

  // Token辅助方法
  private advance(): Token { return this.tokens[this.current++]; }
  private previous(): Token { return this.tokens[this.current - 1]; }
  private peek(): Token { return this.tokens[this.current]; }
  private check(type: TokenType): boolean { return this.peek().type === type; }
  private match(type: TokenType): boolean {
    if (this.check(type)) { this.advance(); return true; }
    return false;
  }
  private consume(type: TokenType, msg: string): Token {
    if (this.check(type)) return this.advance();
    throw this.error(this.peek(), msg);
  }
  private error(token: Token, msg: string): Error {
    return new Error(`Parse error at ${token.line}:${token.column} — ${msg}`);
  }
}
```

---

## 七、第四层：字节码编译器

### 7.1 设计操作码（Opcode）

字节码是AST和虚拟机之间的桥梁。参考Lox语言的设计[^3]，我们定义一组栈式操作码：

```typescript
// src/compiler/opcode.ts
export enum OpCode {
  // 常量
  Constant,     // 加载常量到栈顶
  // 算术运算
  Add, Subtract, Multiply, Divide, Modulo,
  // 比较
  Equal, NotEqual, Less, LessEqual, Greater, GreaterEqual,
  // 逻辑
  Not, And, Or,
  // 变量
  GetLocal, SetLocal, GetGlobal, SetGlobal,
  // 控制流
  Jump,         // 无条件跳转
  JumpIfFalse,  // 条件假跳转
  JumpIfTrue,   // 条件真跳转
  Loop,         // 回跳（用于循环）
  // 函数
  Call,         // 调用函数
  Return,       // 返回
  // 栈操作
  Pop,          // 弹出栈顶
  Print,        // 打印栈顶
}

export class Bytecode {
  instructions: number[] = [];     // 操作码 + 操作数
  constants: any[] = [];           // 常量池
  lines: number[] = [];            // 行号映射（调试用）

  emitByte(byte: number, line: number): void {
    this.instructions.push(byte);
    this.lines.push(line);
  }

  emitBytes(...bytes: number[]): void {
    const line = this.lines[this.lines.length - 1] ?? 0;
    for (const b of bytes) this.instructions.push(b);
    for (const _ of bytes) this.lines.push(line);
  }

  emitConstant(value: any, line: number): void {
    const index = this.addConstant(value);
    this.emitByte(OpCode.Constant, line);
    this.emitBytes(index);
  }

  addConstant(value: any): number {
    this.constants.push(value);
    return this.constants.length - 1;
  }
}
```

### 7.2 AST→字节码编译器

**这是整个编译器中Claude Code最能发挥价值的环节**——编译逻辑是纯模式匹配，Claude Code可以零错误地生成。

```
请实现src/compiler/compiler.ts，使用ASTVisitor模式遍历AST并生成字节码：
1. 每种表达式/语句类型对应一个emit方法
2. BinaryExpr：left → right → operator（中缀转后缀）
3. IfStmt：condition → JumpIfFalse → then → Jump → else
4. WhileStmt：Loop标记 → condition → JumpIfFalse → body → Loop回跳
5. FnDeclStmt：编译为独立的Bytecode对象，存入常量池
6. CallStmt：依次编译参数，emit Call + 参数数量
7. 局部变量使用栈槽（stack slot），全局变量使用全局表
```

**src/compiler/compiler.ts** — 编译器核心：

```typescript
import { ASTVisitor, accept } from '../ast/visitor';
import { OpCode, Bytecode } from './opcode';
import type * as AST from '../ast/nodes';

class CompilerScope {
  locals: string[] = [];
  parent: CompilerScope | null = null;

  resolveLocal(name: string): number | null {
    const idx = this.locals.indexOf(name);
    if (idx >= 0) return idx;
    return null;
  }

  defineLocal(name: string): number {
    this.locals.push(name);
    return this.locals.length - 1;
  }
}

export class Compiler implements ASTVisitor<void> {
  private bytecode = new Bytecode();
  private scope = new CompilerScope();
  private globalNames: string[] = [];

  compile(program: AST.Program): Bytecode {
    this.visitBlockStmt(program);
    this.bytecode.emitByte(OpCode.Return, 0);
    return this.bytecode;
  }

  // ============ 表达式编译 ============

  visitNumberLiteral(node: AST.NumberLiteral): void {
    this.bytecode.emitConstant(node.value, node.line);
  }

  visitStringLiteral(node: AST.StringLiteral): void {
    this.bytecode.emitConstant(node.value, node.line);
  }

  visitBooleanLiteral(node: AST.BooleanLiteral): void {
    // 优化：true/false用常量池中预定义的布尔值
    this.bytecode.emitConstant(node.value, node.line);
  }

  visitIdentifier(node: AST.Identifier): void {
    const local = this.scope.resolveLocal(node.name);
    if (local !== null) {
      this.bytecode.emitBytes(OpCode.GetLocal, local);
    } else {
      const global = this.globalNames.indexOf(node.name);
      if (global < 0) throw new Error(`Undefined variable: ${node.name}`);
      this.bytecode.emitBytes(OpCode.GetGlobal, global);
    }
  }

  visitBinaryExpr(node: AST.BinaryExpr): void {
    accept(node.left, this);
    accept(node.right, this);
    const opMap: Record<string, OpCode> = {
      '+': OpCode.Add, '-': OpCode.Subtract,
      '*': OpCode.Multiply, '/': OpCode.Divide, '%': OpCode.Modulo,
      '==': OpCode.Equal, '!=': OpCode.NotEqual,
      '<': OpCode.Less, '<=': OpCode.LessEqual,
      '>': OpCode.Greater, '>=': OpCode.GreaterEqual,
      '&&': OpCode.And, '||': OpCode.Or,
    };
    const opcode = opMap[node.operator];
    if (opcode === undefined) throw new Error(`Unknown operator: ${node.operator}`);
    this.bytecode.emitBytes(opcode);
  }

  visitUnaryExpr(node: AST.UnaryExpr): void {
    accept(node.operand, this);
    if (node.operator === '!') this.bytecode.emitBytes(OpCode.Not);
    else if (node.operator === '-') this.bytecode.emitBytes(OpCode.Subtract);
  }

  visitCallExpr(node: AST.CallExpr): void {
    accept(node.callee, this);
    for (const arg of node.args) accept(arg, this);
    this.bytecode.emitBytes(OpCode.Call, node.args.length);
  }

  visitAssignExpr(node: AST.AssignExpr): void {
    accept(node.value, this);
    const local = this.scope.resolveLocal(node.target.name);
    if (local !== null) {
      this.bytecode.emitBytes(OpCode.SetLocal, local);
    } else {
      const global = this.globalNames.indexOf(node.target.name);
      this.bytecode.emitBytes(OpCode.SetGlobal, global);
    }
  }

  // ============ 语句编译 ============

  visitLetStmt(node: AST.LetStmt): void {
    accept(node.initializer, this);
    if (this.scope.parent === null) {
      // 全局作用域
      this.globalNames.push(node.name);
    } else {
      this.scope.defineLocal(node.name);
    }
  }

  visitIfStmt(node: AST.IfStmt): void {
    accept(node.condition, this);
    const thenJump = this.emitJump(OpCode.JumpIfFalse);
    accept(node.thenBranch, this);
    const elseJump = this.emitJump(OpCode.Jump);
    this.patchJump(thenJump);
    if (node.elseBranch) accept(node.elseBranch, this);
    this.patchJump(elseJump);
  }

  visitWhileStmt(node: AST.WhileStmt): void {
    const loopStart = this.bytecode.instructions.length;
    accept(node.condition, this);
    const exitJump = this.emitJump(OpCode.JumpIfFalse);
    accept(node.body, this);
    this.emitLoop(loopStart);
    this.patchJump(exitJump);
  }

  visitFnDeclStmt(node: AST.FnDeclStmt): void {
    // 保存当前作用域，创建新作用域
    const prevScope = this.scope;
    this.scope = new CompilerScope();
    this.scope.parent = prevScope;
    // 参数作为局部变量
    for (const param of node.params) this.scope.defineLocal(param);
    // 编译函数体
    const fnBytecode = new Bytecode();
    const prevBytecode = this.bytecode;
    this.bytecode = fnBytecode;
    this.visitBlockStmt(node.body);
    this.bytecode.emitByte(OpCode.Return, 0);
    this.bytecode = prevBytecode;
    // 恢复作用域
    this.scope = prevScope;
    // 函数对象存入常量池
    this.bytecode.emitConstant({ name: node.name, params: node.params, bytecode: fnBytecode }, 0);
    if (prevScope.parent === null) {
      this.globalNames.push(node.name);
    }
  }

  visitReturnStmt(node: AST.ReturnStmt): void {
    if (node.value) accept(node.value, this);
    this.bytecode.emitBytes(OpCode.Return);
  }

  visitBlockStmt(node: AST.BlockStmt): void {
    for (const stmt of node.statements) accept(stmt, this);
  }

  visitExpressionStmt(node: AST.ExpressionStmt): void {
    accept(node.expression, this);
    this.bytecode.emitBytes(OpCode.Pop); // 表达式语句丢弃结果
  }

  // ============ 跳转辅助 ============

  private emitJump(opcode: OpCode): number {
    this.bytecode.emitBytes(opcode);
    this.bytecode.emitBytes(0xFF); // 占位
    return this.bytecode.instructions.length - 1;
  }

  private patchJump(offset: number): void {
    this.bytecode.instructions[offset] = this.bytecode.instructions.length;
  }

  private emitLoop(loopStart: number): void {
    this.bytecode.emitBytes(OpCode.Loop);
    this.bytecode.emitBytes(loopStart);
  }
}
```

### 7.3 关键编译模式可视化

```mermaid
flowchart TD
    subgraph "if-else 编译"
        IF_COND["条件表达式"] --> |"emit条件字节码"| JUMP_FALSE["JumpIfFalse ?"]
        JUMP_FALSE --> THEN["then分支字节码"]
        THEN --> JUMP_END["Jump ?"]
        JUMP_FALSE --> |"patch跳转地址"| ELSE["else分支字节码"]
        ELSE --> |"patch跳转地址"| END["后续代码"]
        JUMP_END --> |"patch跳转地址"| END
    end

    subgraph "while 编译"
        LOOP_START["LOOP标记位置"] --> WHILE_COND["条件表达式"]
        WHILE_COND --> |"emit条件字节码"| EXIT_JUMP["JumpIfFalse ?"]
        EXIT_JUMP --> BODY["循环体字节码"]
        BODY --> |"emit Loop 回跳"| LOOP_START
        EXIT_JUMP --> |"patch跳转地址"| AFTER["循环后代码"]
    end
```

---

## 八、第五层：栈式虚拟机（VM）

### 8.1 虚拟机架构

```mermaid
flowchart LR
    subgraph VM["栈式虚拟机"]
        STACK["操作栈<br/>(值栈)"]
        CALL_STACK["调用栈<br/>(栈帧链)"]
        GLOBALS["全局变量表"]
        CONSTANTS["常量池"]
    end

    BYTECODE["字节码流"] --> |"fetch-decode-execute"| VM
    VM --> RESULT["执行结果"]

    CALL_STACK --> |"每帧包含"| FRAME["返回地址<br/>局部变量数组<br/>栈基指针"]
```

**src/vm/vm.ts** — 完整虚拟机实现：

```typescript
import { OpCode, Bytecode } from '../compiler/opcode';

interface CallFrame {
  bytecode: Bytecode;
  ip: number;           // 指令指针
  basePointer: number;  // 栈基指针（局部变量起始位置）
  name: string;
}

export class VM {
  private stack: any[] = [];
  private frames: CallFrame[] = [];
  private globals: Map<string, any> = new Map();

  run(bytecode: Bytecode): any {
    const frame: CallFrame = {
      bytecode,
      ip: 0,
      basePointer: 0,
      name: '<main>',
    };
    this.frames.push(frame);

    while (this.frames.length > 0) {
      const current = this.frames[this.frames.length - 1];
      const instruction = this.readByte(current);

      switch (instruction) {
        case OpCode.Constant: {
          const index = this.readByte(current);
          this.push(current.bytecode.constants[index]);
          break;
        }
        // 算术运算
        case OpCode.Add: {
          const b = this.pop(), a = this.pop();
          this.push(a + b);
          break;
        }
        case OpCode.Subtract: { const b = this.pop(), a = this.pop(); this.push(a - b); break; }
        case OpCode.Multiply: { const b = this.pop(), a = this.pop(); this.push(a * b); break; }
        case OpCode.Divide:   { const b = this.pop(), a = this.pop(); this.push(a / b); break; }
        case OpCode.Modulo:   { const b = this.pop(), a = this.pop(); this.push(a % b); break; }

        // 比较运算
        case OpCode.Equal:        { const b = this.pop(), a = this.pop(); this.push(a === b); break; }
        case OpCode.NotEqual:     { const b = this.pop(), a = this.pop(); this.push(a !== b); break; }
        case OpCode.Less:         { const b = this.pop(), a = this.pop(); this.push(a < b); break; }
        case OpCode.LessEqual:    { const b = this.pop(), a = this.pop(); this.push(a <= b); break; }
        case OpCode.Greater:      { const b = this.pop(), a = this.pop(); this.push(a > b); break; }
        case OpCode.GreaterEqual: { const b = this.pop(), a = this.pop(); this.push(a >= b); break; }

        // 逻辑运算
        case OpCode.Not: { this.push(!this.pop()); break; }
        case OpCode.And: { const b = this.pop(), a = this.pop(); this.push(a && b); break; }
        case OpCode.Or:  { const b = this.pop(), a = this.pop(); this.push(a || b); break; }

        // 变量
        case OpCode.GetLocal: {
          const slot = this.readByte(current);
          this.push(this.stack[current.basePointer + slot]);
          break;
        }
        case OpCode.SetLocal: {
          const slot = this.readByte(current);
          this.stack[current.basePointer + slot] = this.peek();
          break;
        }
        case OpCode.GetGlobal: {
          const name = current.bytecode.constants[this.readByte(current)];
          if (!this.globals.has(name)) throw new Error(`Undefined variable: ${name}`);
          this.push(this.globals.get(name));
          break;
        }
        case OpCode.SetGlobal: {
          const name = current.bytecode.constants[this.readByte(current)];
          this.globals.set(name, this.peek());
          break;
        }

        // 控制流
        case OpCode.Jump: {
          const offset = this.readByte(current);
          current.ip = offset;
          break;
        }
        case OpCode.JumpIfFalse: {
          const offset = this.readByte(current);
          if (!this.pop()) current.ip = offset;
          break;
        }
        case OpCode.Loop: {
          const offset = this.readByte(current);
          current.ip = offset;
          break;
        }

        // 函数调用
        case OpCode.Call: {
          const argCount = this.readByte(current);
          const callee = this.stack[this.stack.length - argCount - 1];
          if (typeof callee === 'function') {
            // 内置函数
            const args = this.stack.splice(this.stack.length - argCount, argCount);
            this.pop(); // 弹出callee
            this.push(callee(...args));
          } else if (callee?.bytecode) {
            // 用户定义函数
            const frame: CallFrame = {
              bytecode: callee.bytecode,
              ip: 0,
              basePointer: this.stack.length - argCount,
              name: callee.name,
            };
            this.frames.push(frame);
          }
          break;
        }
        case OpCode.Return: {
          const result = this.pop();
          this.frames.pop();
          if (this.frames.length > 0) {
            this.push(result);
          } else {
            return result;
          }
          break;
        }

        case OpCode.Pop: { this.pop(); break; }
        case OpCode.Print: { console.log(this.pop()); break; }

        default:
          throw new Error(`Unknown opcode: ${instruction}`);
      }
    }
    return this.pop();
  }

  private readByte(frame: CallFrame): number {
    return frame.bytecode.instructions[frame.ip++];
  }
  private push(value: any): void { this.stack.push(value); }
  private pop(): any { return this.stack.pop(); }
  private peek(): any { return this.stack[this.stack.length - 1]; }
}
```

---

## 九、完整端到端实战

### 9.1 端到端流水线

```typescript
// src/index.ts — 完整流水线
import { Lexer } from './lexer/tokenizer';
import { Parser } from './parser/parser';
import { Compiler } from './compiler/compiler';
import { VM } from './vm/vm';

export function run(source: string): any {
  // 1. 词法分析
  const tokens = new Lexer(source).tokenize();

  // 2. 语法分析 → AST
  const ast = new Parser(tokens).parse();

  // 3. 编译 → 字节码
  const compiler = new Compiler();
  const bytecode = compiler.compile(ast);

  // 4. 虚拟机执行
  const vm = new VM();
  return vm.run(bytecode);
}
```

### 9.2 运行MiniLang程序

```typescript
// examples/fibonacci.mini
const source = `
  fn fibonacci(n) {
    if (n <= 1) {
      return n;
    }
    return fibonacci(n - 1) + fibonacci(n - 2);
  }

  let result = fibonacci(10);
  result
`;

console.log(run(source)); // 输出: 55
```

```typescript
// examples/closure.mini — 闭包支持测试
const source = `
  fn makeCounter() {
    let count = 0;
    fn increment() {
      count = count + 1;
      return count;
    }
    return increment;
  }

  let counter = makeCounter();
  counter();  // 1
  counter();  // 2
  counter()   // 3
`;

console.log(run(source)); // 输出: 3
```

### 9.3 REPL交互式环境

```typescript
// src/repl.ts
import * as readline from 'readline';
import { run } from './index';

const rl = readline.createInterface({ input: process.stdin, output: process.stdout });

console.log('MiniLang REPL v1.0 — Type "exit" to quit');
rl.setPrompt('minilang> ');
rl.prompt();

rl.on('line', (line) => {
  if (line.trim() === 'exit') { rl.close(); return; }
  try {
    const result = run(line);
    if (result !== undefined) console.log('=>', result);
  } catch (e: any) {
    console.error('Error:', e.message);
  }
  rl.prompt();
});
```

### 9.4 完整测试套件

```typescript
// tests/integration.test.ts
import { describe, it, expect } from 'vitest';
import { run } from '../src/index';

describe('MiniLang Integration', () => {
  it('evaluates arithmetic', () => {
    expect(run('2 + 3 * 4')).toBe(14);
    expect(run('(2 + 3) * 4')).toBe(20);
    expect(run('10 % 3')).toBe(1);
  });

  it('handles variables', () => {
    expect(run('let x = 10; let y = 20; x + y')).toBe(30);
  });

  it('handles if-else', () => {
    expect(run('let x = 5; if (x > 3) { 100 } else { 200 }')).toBe(100);
    expect(run('let x = 1; if (x > 3) { 100 } else { 200 }')).toBe(200);
  });

  it('handles while loops', () => {
    const src = `let sum = 0; let i = 1;
      while (i <= 10) { sum = sum + i; i = i + 1 }
      sum`;
    expect(run(src)).toBe(55);
  });

  it('handles recursive functions', () => {
    expect(run('fn fact(n) { if (n <= 1) { return 1 } return n * fact(n-1) } fact(5)')).toBe(120);
  });

  it('handles closures', () => {
    const src = `fn adder(x) { fn(y) { x + y } } let add5 = adder(5); add5(3)`;
    expect(run(src)).toBe(8);
  });

  it('handles string operations', () => {
    expect(run('"hello" + " " + "world"')).toBe('hello world');
  });

  it('handles boolean logic', () => {
    expect(run('true && false')).toBe(false);
    expect(run('true || false')).toBe(true);
    expect(run('!true')).toBe(false);
  });
});
```

---

## 十、Claude Code编译器开发最佳实践

### 10.1 五天开发节奏表

基于Guy Powell的经验和Anthropic的16 Agent实验总结：

| 天数 | 目标 | Claude Code策略 |
|------|------|-----------------|
| Day 1 | Token + Lexer + 测试 | 一次请求生成Token定义和Lexer实现，要求同时输出测试用例 |
| Day 2 | AST + Parser | 先生成AST节点定义（纯数据，零逻辑错误），再用Pratt解析分步生成表达式/语句解析 |
| Day 3 | 字节码 + 编译器 | 用ASTVisitor模式，每种节点一条emit规则，错误率极低 |
| Day 4 | VM + 集成 | 逐操作码实现fetch-decode-execute循环，每个case一个测试 |
| Day 5 | 优化 + REPL + 文档 | 常量折叠、尾调用优化、REPL、使用文档 |

### 10.2 与Claude Code协作的关键Prompt模式

**模式一：规范驱动（Specification-Driven）**

```
请根据以下EBNF语法规范实现MiniLang的Parser：

program     → statement* EOF ;
statement   → letStmt | ifStmt | whileStmt | fnDecl | returnStmt | exprStmt ;
letStmt     → "let" IDENTIFIER "=" expression ";" ;
ifStmt      → "if" "(" expression ")" block ("else" (ifStmt | block))? ;
whileStmt   → "while" "(" expression ")" block ;
fnDecl      → "fn" IDENTIFIER "(" parameters? ")" block ;
returnStmt  → "return" expression? ";" ;
block       → "{" statement* "}" ;
parameters  → IDENTIFIER ("," IDENTIFIER)* ;

expression  → assignment ;
assignment  → (call ".")? IDENTIFIER "=" assignment | logic_or ;
logic_or    → logic_and ("||" logic_and)* ;
logic_and   → equality ("&&" equality)* ;
equality    → comparison (("==" | "!=") comparison)* ;
comparison  → term (("<" | "<=" | ">" | ">=") term)* ;
term        → factor (("+" | "-") factor)* ;
factor      → unary (("*" | "/" | "%") unary)* ;
unary       → ("!" | "-") unary | call ;
call        → primary ("(" arguments? ")")* ;
primary     → NUMBER | STRING | "true" | "false" | IDENTIFIER | "(" expression ")" ;
arguments   → expression ("," expression)* ;
```

**模式二：测试先行（Test-First）**

```
请为MiniLang的VM编写以下测试场景，然后实现使其通过：
1. 基本算术：1+2=3, 3*4=12, 10/3≈3.33
2. 变量存取：let x=5; x+3=8
3. 条件分支：if(true){1}else{2}=1
4. 循环：while(i<10){sum+=i;i++}; sum=45
5. 递归：fib(10)=55
6. 闭包：makeAdder(5)(3)=8
7. 字符串拼接："a"+"b"="ab"
```

**模式三：错误诊断循环（Error Diagnosis Loop）**

```bash
# 在终端中运行
claude --test
# 测试失败时直接粘贴错误输出
claude "运行 npx vitest run 报错：TypeError: Cannot read properties of undefined (reading 'kind') at Parser.parseExpression (src/parser/expression.ts:45:22)。请分析原因并修复。"
```

### 10.3 Anthropic 16 Agent实验的架构启示

Nicholas Carlini的16 Agent C编译器实验[^1]揭示了AI构建编译器的关键架构决策：

```mermaid
flowchart TD
    COORD["协调器<br/>(Orchestrator)"] --> A1["Agent 1<br/>Preprocessor"]
    COORD --> A2["Agent 2<br/>Lexer"]
    COORD --> A3["Agent 3<br/>Parser"]
    COORD --> A4["Agent 4<br/>AST"]
    COORD --> A5["Agent 5<br/>Type System"]
    COORD --> A6["Agent 6<br/>x86-64 Codegen"]
    COORD --> A7["Agent 7<br/>ARM64 Codegen"]
    COORD --> A8["Agent 8<br/>Optimizer"]
    COORD --> A9["Agent 9-16<br/>测试/工具链"]

    A1 --> A2 --> A3 --> A4 --> A5
    A5 --> A6
    A5 --> A7
    A5 --> A8
    A6 --> TEST["集成测试<br/>GCC torture suite"]
    A7 --> TEST
    A8 --> TEST

    style COORD fill:#e8eaf6
    style TEST fill:#fce4ec
```

核心启示：

1. **接口契约先行**：每个Agent开工前，先定义模块间的数据结构（如Token结构、AST节点接口）。Anthropic团队发现，Claude Code生成的接口定义几乎不需要修改——**类型系统是AI和人类协作的最佳契约**。

2. **测试即规范**：16个Agent共享一个不断增长的测试套件。每完成一个模块就跑全套测试，测试成了"活的规范文档"。

3. **渐进式复杂度**：先让编译器通过最简单的测试（`return 0;`），再逐步增加复杂度。不要试图一开始就编译整个GCC torture suite。

---

## 十一、调试技巧与常见陷阱

### 11.1 字节码反汇编器

为VM添加反汇编功能，是调试编译器的必备工具：

```typescript
// src/compiler/disassembler.ts
import { OpCode } from './opcode';

const OP_NAMES: Record<number, string> = {
  [OpCode.Constant]: 'CONSTANT',
  [OpCode.Add]: 'ADD', [OpCode.Subtract]: 'SUB',
  [OpCode.Multiply]: 'MUL', [OpCode.Divide]: 'DIV',
  [OpCode.Equal]: 'EQ', [OpCode.NotEqual]: 'NEQ',
  [OpCode.Jump]: 'JUMP', [OpCode.JumpIfFalse]: 'JUMP_IF_FALSE',
  [OpCode.Loop]: 'LOOP', [OpCode.Call]: 'CALL',
  [OpCode.Return]: 'RETURN', [OpCode.Pop]: 'POP',
  [OpCode.GetLocal]: 'GET_LOCAL', [OpCode.SetLocal]: 'SET_LOCAL',
};

export function disassemble(bytecode: Bytecode): string {
  const lines: string[] = [];
  let ip = 0;
  while (ip < bytecode.instructions.length) {
    const op = bytecode.instructions[ip];
    const name = OP_NAMES[op] ?? `UNKNOWN(${op})`;
    const line = bytecode.lines[ip];
    if ([OpCode.Constant, OpCode.Jump, OpCode.JumpIfFalse, OpCode.Loop,
         OpCode.Call, OpCode.GetLocal, OpCode.SetLocal].includes(op)) {
      const arg = bytecode.instructions[ip + 1];
      const constVal = op === OpCode.Constant ? ` (${JSON.stringify(bytecode.constants[arg])})` : '';
      lines.push(`${ip.toString().padStart(4, '0')}  ${name.padEnd(16)} ${arg}${constVal}  [line ${line}]`);
      ip += 2;
    } else {
      lines.push(`${ip.toString().padStart(4, '0')}  ${name.padEnd(16)} [line ${line}]`);
      ip += 1;
    }
  }
  return lines.join('\n');
}
```

### 11.2 常见编译器Bug与修复

| 症状 | 原因 | 修复方法 |
|------|------|----------|
| `fibonacci(10)` 返回NaN | 函数调用时callee弹出时机不对 | 检查Call操作码中栈指针偏移 |
| while死循环 | Loop回跳地址计算错误 | 反汇编确认loopStart地址 |
| 闭包变量为undefined | 上值(Upvalue)未捕获 | 需要在FnDecl时创建闭包对象 |
| 运算符优先级错误 | Pratt表优先级数值配置错误 | 用 `(2+3)*4` vs `2+3*4` 验证 |
| 变量作用域泄漏 | exitScope时未pop局部变量 | 编译器Block退出时恢复scope |

### 11.3 闭包支持的关键修改

上面的基础VM不完整支持闭包。要正确实现，需要引入**Upvalue机制**。核心思路：

1. 编译期：当函数体引用外层变量时，记录为Upvalue
2. 运行期：闭包对象持有Upvalue引用（指向栈上或已关闭的堆变量）
3. 函数返回后：将栈上Upvalue"关闭"到堆上

这个改造最适合让Claude Code完成——给出Upvalue的算法描述，让它实现编译期捕获和运行期关闭逻辑：

```
请为MiniLang的编译器添加闭包支持：
1. 在CompilerScope中添加upvalues数组，记录每个闭包捕获的外层变量
2. 编译FnDecl时，扫描函数体中的标识符，区分local/upvalue/global
3. 新增OpCode.GetUpvalue和OpCode.SetUpvalue
4. 在VM中，Closure对象持有upvalue指针数组
5. 当栈帧退出时，通过closeUpvalues()将栈上引用移到堆上
6. 添加测试：makeCounter()、adder(x)(y)
```

---

## 十二、性能基准与优化方向

### 12.1 基准测试

```typescript
// benchmarks/perf.test.ts
import { describe, it, expect } from 'vitest';
import { run } from '../src/index';

describe('Performance', () => {
  it('fibonacci(30) under 1s', () => {
    const start = Date.now();
    const result = run(`fn fib(n) { if (n <= 1) { return n } return fib(n-1) + fib(n-2) } fib(30)`);
    const elapsed = Date.now() - start;
    expect(result).toBe(832040);
    expect(elapsed).toBeLessThan(1000);
  });

  it('loop 1M iterations under 500ms', () => {
    const start = Date.now();
    run(`let i = 0; while (i < 1000000) { i = i + 1 }; i`);
    expect(Date.now() - start).toBeLessThan(500);
  });
});
```

### 12.2 优化路线图

```mermaid
flowchart TD
    BASE["基础VM<br/>~10K ops/s"] --> OPT1["优化1: 直接线程化<br/>switch→跳转表<br/>~50K ops/s"]
    OPT1 --> OPT2["优化2: 常量折叠<br/>编译期计算<br/>~减少30%指令"]
    OPT2 --> OPT3["优化3: 尾调用优化<br/>TCO消除递归栈<br/>fib(10000)不溢出"]
    OPT3 --> OPT4["优化4: 内联缓存<br/>Inline Cache<br/>属性访问加速"]
    OPT4 --> FINAL["生产级VM<br/>~200K ops/s"]

    style BASE fill:#ffcdd2
    style FINAL fill:#c8e6c9
```

每一步优化都可以作为一个独立的Claude Code会话：

```
请为MiniLang的VM实现直接线程化（direct threading）优化：
1. 将switch-case替换为函数跳转表
2. 每个操作码对应一个独立的处理函数
3. 使用函数指针数组替代switch
4. 保持原有语义不变，通过性能测试验证提升
```

---

## 十三、延伸阅读

- [Crafting Interpreters](https://craftinginterpreters.com/) — Robert Nystrom的经典编译器教程，MiniLang的设计大量参考了Lox语言
- [Build Your Own Programming Language](https://build-your-own.org/) — 从零构建编程语言的系列教程

## 训练营适用性评估

| 维度 | 评估 |
|------|------|
| 适合训练营类型 | AI编程工具实战营、编译原理实验课、TypeScript进阶训练营 |
| 教学时长 | 5天密集训练营（每天6小时）或 10周线上课程（每周3小时） |
| 学员基础要求 | TypeScript中级，了解基本数据结构 |
| 难度曲线 | Day1-2偏易（Lexer/AST），Day3-4中等（Parser/Compiler），Day5挑战（VM+优化） |
| 推荐分组 | 2-3人一组，每组负责不同的语言特性扩展（如面向对象、模式匹配、协程） |

---

## 参考来源

1. [Building a C compiler with a team of parallel Claudes](https://www.anthropic.com/engineering/building-c-compiler) — Anthropic官方工程博客，16个并行Agent用两周构建10万行Rust C编译器的详细记录
2. [I Built a Compiler in Five Days with Claude AI](https://powellg.substack.com/p/compiler-built-with-claude-ai) — Guy Powell的Substack文章，5天完成语言+编译器+VM的完整案例
3. [Crafting Interpreters](https://craftinginterpreters.com/) — Robert Nystrom的在线书籍，Lox语言实现的完整教程，字节码VM设计的经典参考
4. [Sixteen Claude AI agents working together created a new C compiler](https://arstechnica.com/ai/2026/02/sixteen-claude-ai-agents-working-together-created-a-new-c-compiler/) — Ars Technica对Anthropic 16 Agent实验的深度报道
5. [Claude Code GitHub Repository](https://github.com/anthropics/claude-code) — Claude Code官方仓库，包含最新功能和使用文档

> [!tip] 51学通信知识星球
> **星主：爱卫生**（微信：gprshome201101）——公众号「51学通信」创办者，19 年通信行业运维与讲师经验，著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书（各大电商平台有售）。
> **星球定位**：深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域，主打运维岗位与信令分析。
> **当前规模**：5200 篇主题 / 990 个回答，198 元/年。
> **加入福利**：加入星球后请加爱卫生微信，拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题，另有群内分享。
> **AI 加持**：51学通信知识星球已作为 skill 接入 WorkBuddy，使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例，做总结、找文档和 PPT 等场景全覆盖。

![51学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
