|
2 | 2 |
|
3 | 3 | This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
4 | 4 |
|
5 | | -## Commands |
| 5 | +## Repository layout |
6 | 6 |
|
7 | | -```bash |
8 | | -# Build (ESM, UMD, minified UMD) |
9 | | -npm run build |
| 7 | +This is a **yarn workspace monorepo** with three published packages: |
10 | 8 |
|
11 | | -# Run all tests (builds first) |
12 | | -npm test |
| 9 | +``` |
| 10 | +/ |
| 11 | +├── packages/ |
| 12 | +│ ├── expreszo/ # @pro-fa/expreszo — core expression evaluator |
| 13 | +│ ├── expreszo-datetime/ # @pro-fa/expreszo-datetime — optional Luxon plugin |
| 14 | +│ └── expreszo-mcp-server/ # @pro-fa/expreszo-mcp-server — MCP server CLI |
| 15 | +├── eslint.config.js # shared eslint config |
| 16 | +├── tsconfig.base.json # shared TS compiler options (each package extends this) |
| 17 | +└── package.json # private workspace root |
| 18 | +``` |
13 | 19 |
|
14 | | -# Run tests in watch mode |
15 | | -npm run test:watch |
| 20 | +The core never depends on Luxon or `@modelcontextprotocol/sdk` — those live only in their respective companion packages. |
16 | 21 |
|
17 | | -# Run a single test file |
18 | | -npx vitest run test/core/evaluate.ts |
| 22 | +## Commands |
19 | 23 |
|
20 | | -# Lint |
21 | | -npm run lint |
| 24 | +Run from the **workspace root** to fan out across all three packages: |
22 | 25 |
|
23 | | -# Type check |
24 | | -npm run type-check |
| 26 | +```bash |
| 27 | +yarn install --frozen-lockfile # install all workspaces |
| 28 | +yarn workspaces run lint # eslint all packages |
| 29 | +yarn workspaces run type-check # TS type-check all packages |
| 30 | +yarn workspaces run test # build + vitest in each package |
| 31 | +yarn workspaces run build # build dist/ for each package |
| 32 | +``` |
25 | 33 |
|
26 | | -# Coverage (80% threshold required) |
27 | | -npm run coverage |
| 34 | +Run from a **single package** for tighter loops: |
28 | 35 |
|
29 | | -# Benchmarks |
30 | | -npm run bench |
| 36 | +```bash |
| 37 | +cd packages/expreszo |
| 38 | +yarn test:watch # vitest watch mode |
| 39 | +npx vitest run test/core/evaluate.ts # one file |
| 40 | +yarn coverage # 80% threshold required |
| 41 | +yarn bench # parser benchmarks |
31 | 42 | ``` |
32 | 43 |
|
33 | | -## Architecture |
| 44 | +## Core architecture (`@pro-fa/expreszo`) |
34 | 45 |
|
35 | | -**ExpresZo Typescript** is a safe, extensible expression evaluator — a configurable alternative to JavaScript's `eval()`. It uses a **Pratt parser** and an **immutable AST**: |
| 46 | +Safe, extensible expression evaluator — a configurable alternative to `eval()`. Uses a **Pratt parser** and an **immutable AST**: |
36 | 47 |
|
37 | 48 | ``` |
38 | 49 | Expression string → TokenStream (lexer) → Pratt Parser → AST (immutable) → Evaluator (AST walker) → Result |
39 | 50 | ``` |
40 | 51 |
|
41 | 52 | ### Key classes |
42 | 53 |
|
43 | | -- **`Parser`** (`src/parsing/parser.ts`) — entry point; configurable with custom operators, functions, and variable resolvers; produces `Expression` objects |
44 | | -- **`Expression`** (`src/core/expression.ts`) — compiled expression with methods: `evaluate()`, `simplify()`, `substitute()`, `toString()`, `variables()`, `symbols()` |
45 | | -- **`Evaluator`** (`src/eval/sync-evaluator.ts`, `src/eval/async-evaluator.ts`) — AST walker that evaluates expressions; supports async (Promise) evaluation |
46 | | -- **`TokenStream`** (`src/parsing/token-stream.ts`) — lexer that converts expression strings to tokens |
47 | | -- **`AST Nodes`** (`src/ast/nodes.ts`) — immutable AST node types with a visitor pattern (`src/ast/visitor.ts`) |
| 54 | +- **`Parser`** (`packages/expreszo/src/parsing/parser.ts`) — entry point; configurable with custom operators, functions, and variable resolvers; produces `Expression` objects. Carries the runtime `use(plugin)` method. |
| 55 | +- **`Expression`** (`packages/expreszo/src/core/expression.ts`) — compiled expression with methods: `evaluate()`, `simplify()`, `substitute()`, `toString()`, `variables()`, `symbols()`. |
| 56 | +- **`Evaluator`** (`packages/expreszo/src/eval/sync-evaluator.ts`, `async-evaluator.ts`) — AST walker; supports async (Promise) evaluation. |
| 57 | +- **`TokenStream`** (`packages/expreszo/src/parsing/token-stream.ts`) — lexer. |
| 58 | +- **`AST Nodes`** (`packages/expreszo/src/ast/nodes.ts`) — immutable AST node types with a visitor pattern (`visitor.ts`). |
48 | 59 |
|
49 | | -### Source layout |
| 60 | +### Core source layout |
50 | 61 |
|
51 | 62 | ``` |
52 | | -src/ |
53 | | -├── ast/ # AST node types and visitor pattern |
54 | | -├── core/ # Expression, logical operations |
55 | | -├── eval/ # Sync and async evaluators (AST walkers) |
56 | | -├── parsing/ # Pratt parser, TokenStream, token types, parser-state + utils |
57 | | -├── operators/ # Binary (arithmetic, comparison, logical, utility) and unary operators |
58 | | -├── functions/ # Built-ins split by domain: math/, array/, string/, object/, utility/ |
59 | | -├── api/ # defineParser and tree-shakeable presets |
60 | | -├── registry/ # Built-in function documentation and descriptors |
| 63 | +packages/expreszo/src/ |
| 64 | +├── ast/ # AST node types and visitor pattern |
| 65 | +├── core/ # Expression, logical operations |
| 66 | +├── eval/ # Sync and async evaluators (AST walkers) |
| 67 | +├── parsing/ # Pratt parser, TokenStream, token types, parser-state + utils |
| 68 | +├── operators/ # Binary (arithmetic, comparison, logical, utility) and unary operators |
| 69 | +├── functions/ # Built-ins split by domain: math/, array/, string/, object/, utility/ |
| 70 | +├── api/ # defineParser, presets, Plugin interface |
| 71 | +├── registry/ # FunctionDescriptor / OperatorDescriptor catalog |
61 | 72 | ├── language-service/ # IDE completions, hover, diagnostics |
62 | | -├── mcp-server/ # MCP server for AI assistant integration |
63 | | -├── validation/ # Expression validation |
64 | | -├── types/ # Shared TypeScript types and type guards |
65 | | -├── errors/ # Error context helpers |
66 | | -├── utils/ # Shared utilities |
67 | | -└── entries/ # Subpath export entry points |
| 73 | +├── validation/ # Expression validation |
| 74 | +├── types/ # Shared TypeScript types and type guards |
| 75 | +├── errors/ # Error context helpers |
| 76 | +├── utils/ # Shared utilities |
| 77 | +└── entries/ # Subpath export entry points (./core, ./math, ./string, …) |
| 78 | +``` |
| 79 | + |
| 80 | +## Plugin API |
| 81 | + |
| 82 | +Companion packages expose a `Plugin` (`packages/expreszo/src/api/plugin.ts`): |
| 83 | + |
| 84 | +```ts |
| 85 | +interface Plugin { |
| 86 | + readonly name: string; |
| 87 | + readonly version?: string; |
| 88 | + readonly operators?: readonly OperatorDescriptor[]; |
| 89 | + readonly functions?: readonly FunctionDescriptor[]; |
| 90 | + readonly constants?: Readonly<Record<string, Value>>; |
| 91 | +} |
| 92 | +``` |
| 93 | + |
| 94 | +Consumers wire one in with a single call: |
| 95 | + |
| 96 | +```ts |
| 97 | +import { defineParser, fullParser } from '@pro-fa/expreszo'; |
| 98 | +import { dateTimePlugin } from '@pro-fa/expreszo-datetime'; |
| 99 | + |
| 100 | +const parser = defineParser({ ...fullParser }).use(dateTimePlugin); |
68 | 101 | ``` |
69 | 102 |
|
70 | | -### Build targets |
| 103 | +`Parser.use()` merges plugin entries into `unaryOps` / `binaryOps` / `ternaryOps` / `functions` / `numericConstants`, throwing on name collision unless `{ override: true }` is passed. |
| 104 | + |
| 105 | +## Adding a new built-in function |
| 106 | + |
| 107 | +Inside `packages/expreszo/src/functions/<category>/`: |
| 108 | + |
| 109 | +1. Write the implementation as a plain function in the appropriate domain file. |
| 110 | +2. Export it from `packages/expreszo/src/functions/index.ts`. |
| 111 | +3. Add a `FunctionDescriptor` in `packages/expreszo/src/registry/builtin/functions.ts` (`{ name, impl, category, pure, safe, async, docs? }`). |
| 112 | +4. Wire the descriptor into the matching preset under `packages/expreszo/src/registry/presets/<category>.ts`. |
| 113 | +5. Add tests under `packages/expreszo/test/functions/<category>/`. |
| 114 | + |
| 115 | +For functions that need a heavy dependency (e.g., Luxon), keep them out of core and ship a companion package that exports a `Plugin` (see `packages/expreszo-datetime/` for the canonical example). |
| 116 | + |
| 117 | +## Build targets |
| 118 | + |
| 119 | +Each package's `vite.config.ts` is invoked via `BUILD_TARGET` env var: |
71 | 120 |
|
72 | | -Controlled via the `BUILD_TARGET` env var in `vite.config.ts`: |
73 | | -- `esm` (default) — ES module with `.d.ts` declarations → `dist/index.mjs` |
74 | | -- `umd` — universal bundle → `dist/bundle.js` |
75 | | -- `umd-min` — minified UMD → `dist/bundle.min.js` |
| 121 | +- `esm` (default) — ES module with `.d.ts` declarations → `dist/<entry>.mjs` |
| 122 | +- `umd` — universal bundle (core only) → `dist/bundle.js` |
| 123 | +- `umd-min` — minified UMD (core only) → `dist/bundle.min.js` |
76 | 124 |
|
77 | | -### Code style |
| 125 | +## Code style |
78 | 126 |
|
79 | | -ESLint enforces: semicolons, single quotes, 2-space indentation. TypeScript strict mode is on. `@typescript-eslint/no-explicit-any` is relaxed. |
| 127 | +ESLint at the workspace root (`eslint.config.js`) enforces: semicolons, single quotes, 2-space indentation. TypeScript strict mode is on. `@typescript-eslint/no-explicit-any` is relaxed. |
0 commit comments