Skip to content
Merged
70 changes: 70 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

This is an Effect-based TypeScript implementation of the Monkey programming language interpreter from "Writing An Interpreter In Go", with extensions for symbolic differentiation. The project includes:

- A complete lexer, parser, and evaluator for the Monkey language
- Symbolic differentiation capabilities supporting constant, sum, power, product, quotient, and chain rules
- A web-based REPL interface built with React and Vite
- Trigonometric derivatives implementation

## Development Commands

### Core Development
- `pnpm test` - Run tests with Bun
- `pnpm vitest` - Run tests with Vitest
- `pnpm coverage` - Generate test coverage reports
- `pnpm check` - TypeScript type checking
- `pnpm lint` - Run ESLint on codebase
- `pnpm lint-fix` - Auto-fix lint issues

### Application
- `pnpm repl` - Start the CLI REPL interface
- `pnpm dev` - Start Vite development server for web interface
- `pnpm build` - Build the project

### Testing
- Run single test file: `bunx vitest src/tests/vitest/eval/eval.test.ts`
- Run specific test pattern: `bunx vitest --grep "pattern"`

## Architecture

### Core Services (Effect-based)
The interpreter follows a traditional lexer → parser → evaluator pipeline, implemented using Effect-TS:

- **Lexer** (`src/services/lexer/`) - Tokenizes input strings using state management services
- **Parser** (`src/services/parser/`) - Builds AST from tokens with precedence-based parsing
- **Evaluator** (`src/services/evaluator/`) - Interprets AST nodes and manages runtime environment

### Schema System
The codebase uses a comprehensive schema system in `src/schemas/`:
- `nodes/` - AST node definitions (expressions, statements, program)
- `objs/` - Runtime object representations
- `token/` - Token type definitions
- Built-in function schemas and differentiation schemas

### Key Architectural Patterns
- **Effect Services**: Core functionality implemented as Effect services with dependency injection
- **Schema Validation**: Extensive use of Effect Schema for type-safe parsing and validation
- **Functional Programming**: Immutable data structures and functional composition throughout
- **Error Handling**: Effect-based error handling with typed error channels

### Differentiation Engine
Located in `src/services/diff/`, implements symbolic differentiation:
- `obj.ts` - Core differentiation logic for different object types
- `helper.ts` - Utility functions for differentiation operations
- Supports polynomial objects and various mathematical operations

### Testing Structure
- Unit tests in `src/tests/vitest/` organized by functionality
- Parser tests focus on AST construction correctness
- Evaluator tests verify runtime behavior and differentiation accuracy
- Helper utilities in test directories for common test patterns

## Entry Points
- CLI REPL: `src/services/cli.ts`
- Web interface: `src/vite/main.tsx`
- Core interpreter: `src/services/evaluator/index.ts`
1,038 changes: 726 additions & 312 deletions bun.lock

Large diffs are not rendered by default.

96 changes: 48 additions & 48 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,63 +19,63 @@
"build": "vite build"
},
"dependencies": {
"@biomejs/biome": "1.9.4",
"@effect/cli": "0.54.1",
"@effect/opentelemetry": "0.42.7",
"@effect/platform": "0.75.1",
"@effect/platform-bun": "0.55.1",
"@effect/platform-node": "0.71.1",
"@opentelemetry/exporter-trace-otlp-http": "0.57.1",
"@opentelemetry/sdk-metrics": "1.30.1",
"@opentelemetry/sdk-trace-base": "1.30.1",
"@opentelemetry/sdk-trace-node": "1.30.1",
"@opentelemetry/sdk-trace-web": "1.30.1",
"@tailwindcss/vite": "^4.0.6",
"@types/react": "^19.0.9",
"@types/react-dom": "^19.0.3",
"@vitejs/plugin-react-swc": "^3.8.0",
"@biomejs/biome": "2.1.2",
"@effect/cli": "0.68.0",
"@effect/opentelemetry": "0.55.0",
"@effect/platform": "0.89.0",
"@effect/platform-bun": "0.75.0",
"@effect/platform-node": "0.92.0",
"@opentelemetry/exporter-trace-otlp-http": "0.203.0",
"@opentelemetry/sdk-metrics": "2.0.1",
"@opentelemetry/sdk-trace-base": "2.0.1",
"@opentelemetry/sdk-trace-node": "2.0.1",
"@opentelemetry/sdk-trace-web": "2.0.1",
"@tailwindcss/vite": "^4.1.11",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
"@vitejs/plugin-react-swc": "^3.11.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"effect": "3.12.7",
"effect": "3.17.0",
"eval": "0.1.8",
"lucide-react": "^0.475.0",
"nerdamer-prime": "^1.2.4",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"tailwind-merge": "^3.0.1",
"lucide-react": "^0.525.0",
"nerdamer-prime": "^1.2.5",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"tailwind-merge": "^3.3.1",
"tailwindcss-animate": "^1.0.7"
},
"devDependencies": {
"@babel/cli": "7.26.4",
"@babel/core": "7.26.7",
"@babel/plugin-transform-export-namespace-from": "7.25.9",
"@babel/plugin-transform-modules-commonjs": "7.26.3",
"@effect/build-utils": "0.7.8",
"@effect/eslint-plugin": "0.2.0",
"@effect/language-service": "0.2.0",
"@effect/vitest": "0.17.3",
"@eslint/compat": "1.2.5",
"@eslint/eslintrc": "3.2.0",
"@eslint/js": "9.19.0",
"@types/bun": "1.2.0",
"@types/node": "22.10.10",
"@typescript-eslint/eslint-plugin": "8.21.0",
"@typescript-eslint/parser": "8.21.0",
"@vitest/coverage-v8": "3.0.7",
"autoprefixer": "^10.4.20",
"@babel/cli": "7.28.0",
"@babel/core": "7.28.0",
"@babel/plugin-transform-export-namespace-from": "7.27.1",
"@babel/plugin-transform-modules-commonjs": "7.27.1",
"@effect/build-utils": "0.8.9",
"@effect/eslint-plugin": "0.3.2",
"@effect/language-service": "0.28.0",
"@effect/vitest": "0.25.0",
"@eslint/compat": "1.3.1",
"@eslint/eslintrc": "3.3.1",
"@eslint/js": "9.31.0",
"@types/bun": "1.2.19",
"@types/node": "24.0.15",
"@typescript-eslint/eslint-plugin": "8.38.0",
"@typescript-eslint/parser": "8.38.0",
"@vitest/coverage-v8": "3.2.4",
"autoprefixer": "^10.4.21",
"babel-plugin-annotate-pure-calls": "0.5.0",
"eslint": "9.19.0",
"eslint-import-resolver-typescript": "3.7.0",
"eslint-plugin-codegen": "0.29.0",
"eslint-plugin-import": "2.31.0",
"eslint": "9.31.0",
"eslint-import-resolver-typescript": "4.4.4",
"eslint-plugin-codegen": "0.33.0",
"eslint-plugin-import": "2.32.0",
"eslint-plugin-simple-import-sort": "12.1.1",
"eslint-plugin-sort-destructure-keys": "2.0.0",
"postcss": "^8.5.2",
"tailwindcss": "^4.0.6",
"tsx": "4.19.2",
"typescript": "5.7.3",
"vite": "^6.1.0",
"vitest": "3.0.4"
"postcss": "^8.5.6",
"tailwindcss": "^4.1.11",
"tsx": "4.20.3",
"typescript": "5.8.3",
"vite": "^7.0.5",
"vitest": "3.2.4"
},
"effect": {
"generateExports": {
Expand Down
11 changes: 6 additions & 5 deletions src/schemas/objs/call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,21 @@

import { Schema } from "effect";
import type { BuiltInFunc } from "../built-in";
import { Exp } from "../nodes/exps/union";
import type { Exp } from "../nodes/exps/union";
import { BuiltInObj } from "./built-in";
import { FunctionObj, type FunctionObjEncoded } from "./function";
import { Obj, type ObjEncoded } from "./union";

export interface CallObj {
readonly _tag: "CallObj";
readonly fn: FunctionObj | BuiltInObj;
readonly args: readonly Exp[];
readonly args: readonly Obj[];
}

export interface CallObjEncoded {
readonly _tag: "CallObj";
readonly fn: FunctionObjEncoded | BuiltInObj;
readonly args: readonly Exp[];
readonly args: readonly ObjEncoded[];
}

export const CallObj = Schema.TaggedStruct("CallObj", {
Expand All @@ -25,10 +26,10 @@ export const CallObj = Schema.TaggedStruct("CallObj", {
FunctionObjEncoded | BuiltInObj
> => Schema.Union(FunctionObj, BuiltInObj),
),
args: Schema.Array(Schema.suspend((): Schema.Schema<Exp> => Exp)),
args: Schema.Array(Schema.suspend((): Schema.Schema<Obj, ObjEncoded> => Obj)),
});

export const BuiltInCallObj = (fn: BuiltInFunc) => (args: readonly Exp[]) =>
export const BuiltInCallObj = (fn: BuiltInFunc) => (args: readonly Obj[]) =>
CallObj.make({
fn: BuiltInObj.make({ fn }),
args,
Expand Down
Loading