@codehz/json-expr 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +537 -86
- package/dist/index.d.mts +183 -94
- package/dist/index.mjs +870 -320
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,12 +6,16 @@
|
|
|
6
6
|
|
|
7
7
|
## 特性
|
|
8
8
|
|
|
9
|
-
- 🎯 **类型安全** - 使用 TypeScript
|
|
9
|
+
- 🎯 **类型安全** - 使用 TypeScript 泛型进行完整的编译时类型推导
|
|
10
10
|
- 📦 **可序列化** - 编译后的表达式为纯 JSON 格式,易于传输和存储
|
|
11
11
|
- 🔧 **灵活的表达式** - 支持任意 JavaScript 表达式,包括函数调用和对象属性访问
|
|
12
|
-
- ⚡ **高性能** -
|
|
12
|
+
- ⚡ **高性能** - 优化的表达式编译和执行
|
|
13
13
|
- 🧩 **可组合** - 表达式可以相互组合,形成复杂的计算树
|
|
14
|
-
-
|
|
14
|
+
- 🔄 **短路求值** - 支持 `&&`、`||`、`??` 和三元表达式的控制流优化
|
|
15
|
+
- 📝 **内联优化** - 自动内联只被引用一次的子表达式
|
|
16
|
+
- 🔗 **Proxy 变量系统** - 支持链式属性访问和方法调用,如 `config.timeout`、`user.profile.name`
|
|
17
|
+
- 🎛️ **Lambda 表达式** - 类型安全的数组方法支持(map、filter、reduce 等)
|
|
18
|
+
- 🛡️ **错误检测** - 编译时检测未定义变量和类型错误
|
|
15
19
|
|
|
16
20
|
## 快速开始
|
|
17
21
|
|
|
@@ -24,12 +28,11 @@ bun install @codehz/json-expr
|
|
|
24
28
|
### 基本用法
|
|
25
29
|
|
|
26
30
|
```typescript
|
|
27
|
-
import {
|
|
28
|
-
import { variable, expr, compile, evaluate, constant } from "@codehz/json-expr";
|
|
31
|
+
import { variable, expr, compile, evaluate, t, lambda } from "@codehz/json-expr";
|
|
29
32
|
|
|
30
|
-
//
|
|
31
|
-
const x = variable
|
|
32
|
-
const y = variable
|
|
33
|
+
// 定义类型化变量(使用 TypeScript 泛型)
|
|
34
|
+
const x = variable<number>();
|
|
35
|
+
const y = variable<number>();
|
|
33
36
|
|
|
34
37
|
// 构建表达式
|
|
35
38
|
const sum = expr({ x, y })("x + y");
|
|
@@ -43,23 +46,29 @@ const compiled = compile(result, { x, y });
|
|
|
43
46
|
// 执行编译后的表达式
|
|
44
47
|
const value = evaluate(compiled, { x: 2, y: 3 });
|
|
45
48
|
// => 11 (2+3 + 2*3 = 5 + 6 = 11)
|
|
49
|
+
|
|
50
|
+
// 使用模板字符串
|
|
51
|
+
const name = variable<string>();
|
|
52
|
+
const greeting = t`Hello, ${name}!`;
|
|
53
|
+
|
|
54
|
+
// 使用 lambda 表达式
|
|
55
|
+
const numbers = variable<number[]>();
|
|
56
|
+
const doubled = numbers.map(lambda<[number], number>((n) => expr({ n })("n * 2")));
|
|
46
57
|
```
|
|
47
58
|
|
|
48
59
|
## 核心概念
|
|
49
60
|
|
|
50
61
|
### Variable(变量)
|
|
51
62
|
|
|
52
|
-
|
|
63
|
+
变量是表达式中的占位符,使用 TypeScript 泛型定义其类型。
|
|
53
64
|
|
|
54
65
|
```typescript
|
|
55
|
-
const age = variable
|
|
56
|
-
const name = variable
|
|
57
|
-
const config = variable
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
})
|
|
62
|
-
);
|
|
66
|
+
const age = variable<number>();
|
|
67
|
+
const name = variable<string>();
|
|
68
|
+
const config = variable<{
|
|
69
|
+
debug: boolean;
|
|
70
|
+
timeout: number;
|
|
71
|
+
}>();
|
|
63
72
|
```
|
|
64
73
|
|
|
65
74
|
### Expression(表达式)
|
|
@@ -67,8 +76,8 @@ const config = variable(
|
|
|
67
76
|
表达式对变量或其他表达式进行运算,使用字符串形式描述。
|
|
68
77
|
|
|
69
78
|
```typescript
|
|
70
|
-
const x = variable
|
|
71
|
-
const y = variable
|
|
79
|
+
const x = variable<number>();
|
|
80
|
+
const y = variable<number>();
|
|
72
81
|
|
|
73
82
|
// 简单表达式
|
|
74
83
|
const sum = expr({ x, y })("x + y");
|
|
@@ -93,51 +102,23 @@ const compiled = compile(result, { x, y });
|
|
|
93
102
|
|
|
94
103
|
## API 参考
|
|
95
104
|
|
|
96
|
-
### `
|
|
97
|
-
|
|
98
|
-
创建一个编译期常量表达式。这是 `expr({})(JSON.stringify(value))` 的快速路径,用于在表达式中嵌入静态值,避免在运行时传入或在多处重复编写。
|
|
99
|
-
|
|
100
|
-
**参数:**
|
|
101
|
-
|
|
102
|
-
- `value` - 要嵌入的常量值(必须是 JSON 可序列化的:string、number、boolean、null、数组或对象)
|
|
103
|
-
|
|
104
|
-
**返回值:** Expression 对象
|
|
105
|
-
|
|
106
|
-
**示例:**
|
|
107
|
-
|
|
108
|
-
```typescript
|
|
109
|
-
import { constant, expr, variable, compile, evaluate } from "@codehz/json-expr";
|
|
110
|
-
import { z } from "zod";
|
|
111
|
-
|
|
112
|
-
// 创建常量
|
|
113
|
-
const PI = constant(3.14159);
|
|
114
|
-
const config = constant({ maxRetries: 3, timeout: 5000 });
|
|
115
|
-
|
|
116
|
-
// 在表达式中使用常量
|
|
117
|
-
const radius = variable(z.number());
|
|
118
|
-
const area = expr({ PI, radius })("PI * radius * radius");
|
|
119
|
-
|
|
120
|
-
const compiled = compile(area, { radius });
|
|
121
|
-
const result = evaluate(compiled, { radius: 2 });
|
|
122
|
-
// => 12.56636
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
### `variable<T>(schema: T): Variable<T>`
|
|
105
|
+
### `variable<T>(): Variable<T>`
|
|
126
106
|
|
|
127
107
|
创建一个类型化变量。
|
|
128
108
|
|
|
129
|
-
**参数:**
|
|
130
|
-
|
|
131
|
-
- `schema` - Zod schema,定义变量的类型和验证规则
|
|
109
|
+
**参数:** 无(类型通过泛型参数 `T` 指定)
|
|
132
110
|
|
|
133
|
-
**返回值:** Variable
|
|
111
|
+
**返回值:** Variable 对象,支持属性访问和方法调用
|
|
134
112
|
|
|
135
113
|
**示例:**
|
|
136
114
|
|
|
137
115
|
```typescript
|
|
138
|
-
const num = variable
|
|
139
|
-
const str = variable
|
|
140
|
-
const
|
|
116
|
+
const num = variable<number>();
|
|
117
|
+
const str = variable<string>();
|
|
118
|
+
const config = variable<{ timeout: number }>();
|
|
119
|
+
|
|
120
|
+
// 支持链式属性访问
|
|
121
|
+
const timeout = config.timeout; // 自动转换为表达式
|
|
141
122
|
```
|
|
142
123
|
|
|
143
124
|
### `expr<TContext>(context: TContext): (source: string) => Expression<TContext, TResult>`
|
|
@@ -153,21 +134,21 @@ const date = variable(z.date());
|
|
|
153
134
|
**示例:**
|
|
154
135
|
|
|
155
136
|
```typescript
|
|
156
|
-
const x = variable
|
|
157
|
-
const y = variable
|
|
137
|
+
const x = variable<number>();
|
|
138
|
+
const y = variable<number>();
|
|
158
139
|
|
|
159
140
|
const sum = expr({ x, y })("x + y");
|
|
160
141
|
const result = expr({ sum, x })("sum * x");
|
|
161
142
|
```
|
|
162
143
|
|
|
163
|
-
### `compile<TResult>(expression: Expression<any, TResult>, variables: Record<string, Variable<any
|
|
144
|
+
### `compile<TResult>(expression: Expression<any, TResult>, variables: Record<string, Variable<any>> | Variable<any>[], options?: CompileOptions): CompiledData`
|
|
164
145
|
|
|
165
146
|
将表达式树编译为可序列化的 JSON 结构。
|
|
166
147
|
|
|
167
148
|
**参数:**
|
|
168
149
|
|
|
169
150
|
- `expression` - 要编译的表达式
|
|
170
|
-
- `variables` -
|
|
151
|
+
- `variables` - 表达式中使用的所有变量映射或数组
|
|
171
152
|
- `options` - 编译选项(可选)
|
|
172
153
|
- `inline?: boolean` - 是否启用内联优化,将只被引用一次的子表达式内联到使用位置(默认:true)
|
|
173
154
|
- `shortCircuit?: boolean` - 是否启用短路求值,为 &&, ||, ??, 和三元表达式生成控制流节点(默认:true)
|
|
@@ -177,14 +158,14 @@ const result = expr({ sum, x })("sum * x");
|
|
|
177
158
|
**示例:**
|
|
178
159
|
|
|
179
160
|
```typescript
|
|
180
|
-
const x = variable
|
|
181
|
-
const y = variable
|
|
161
|
+
const x = variable<number>();
|
|
162
|
+
const y = variable<number>();
|
|
182
163
|
const sum = expr({ x, y })("x + y");
|
|
183
164
|
const product = expr({ x, y })("x * y");
|
|
184
165
|
const result = expr({ sum, product })("sum + product");
|
|
185
166
|
|
|
186
167
|
const compiled = compile(result, { x, y });
|
|
187
|
-
// [["x", "y"], "
|
|
168
|
+
// [["x", "y"], "$0+$1", "$0*$1", "$2+$3"]
|
|
188
169
|
|
|
189
170
|
// 禁用内联优化
|
|
190
171
|
const noInline = compile(result, { x, y }, { inline: false });
|
|
@@ -209,30 +190,222 @@ const noShortCircuit = compile(result, { x, y }, { shortCircuit: false });
|
|
|
209
190
|
**示例:**
|
|
210
191
|
|
|
211
192
|
```typescript
|
|
193
|
+
const x = variable<number>();
|
|
194
|
+
const y = variable<number>();
|
|
195
|
+
const sum = expr({ x, y })("x + y");
|
|
196
|
+
const product = expr({ x, y })("x * y");
|
|
197
|
+
const result = expr({ sum, product })("sum + product");
|
|
198
|
+
|
|
212
199
|
const compiled = compile(result, { x, y });
|
|
213
200
|
const value = evaluate(compiled, { x: 5, y: 3 });
|
|
214
201
|
// => 23 ((5+3) + (5*3) = 8 + 15 = 23)
|
|
215
202
|
```
|
|
216
203
|
|
|
204
|
+
### `t(strings: TemplateStringsArray, ...values: unknown[]): Proxify<string>`
|
|
205
|
+
|
|
206
|
+
使用标签模板函数创建包含变量的字符串表达式。
|
|
207
|
+
|
|
208
|
+
**参数:**
|
|
209
|
+
|
|
210
|
+
- `strings` - 模板字符串的静态部分
|
|
211
|
+
- `values` - 模板中插值的变量和表达式
|
|
212
|
+
|
|
213
|
+
**返回值:** 字符串类型的 Proxy Expression
|
|
214
|
+
|
|
215
|
+
**示例:**
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
const name = variable<string>();
|
|
219
|
+
const count = variable<number>();
|
|
220
|
+
|
|
221
|
+
const greeting = t`Hello, ${name}!`;
|
|
222
|
+
const message = t`You have ${count} items.`;
|
|
223
|
+
|
|
224
|
+
const compiled = compile(greeting, { name });
|
|
225
|
+
const result = evaluate(compiled, { name: "Alice" });
|
|
226
|
+
// => "Hello, Alice!"
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### `lambda<Args, R>(builder: LambdaBuilder<Args, R>): Lambda<Args, R>`
|
|
230
|
+
|
|
231
|
+
创建类型安全的 lambda 表达式,用于数组方法(map、filter、reduce 等)。
|
|
232
|
+
|
|
233
|
+
**参数:**
|
|
234
|
+
|
|
235
|
+
- `builder` - Lambda 构建函数,接收参数代理,返回函数体表达式
|
|
236
|
+
|
|
237
|
+
**返回值:** Lambda 表达式,可在数组方法中使用
|
|
238
|
+
|
|
239
|
+
**示例:**
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
import { lambda } from "@codehz/json-expr";
|
|
243
|
+
|
|
244
|
+
// 单参数 lambda
|
|
245
|
+
const numbers = variable<number[]>();
|
|
246
|
+
const doubled = numbers.map(lambda<[number], number>((n) => expr({ n })("n * 2")));
|
|
247
|
+
|
|
248
|
+
const compiled = compile(doubled, { numbers });
|
|
249
|
+
const result = evaluate(compiled, { numbers: [1, 2, 3] });
|
|
250
|
+
// => [2, 4, 6]
|
|
251
|
+
|
|
252
|
+
// 多参数 lambda(reduce)
|
|
253
|
+
const sum = numbers.reduce(
|
|
254
|
+
lambda<[number, number], number>((acc, val) => expr({ acc, val })("acc + val")),
|
|
255
|
+
0
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
// 捕获外部变量
|
|
259
|
+
const multiplier = variable<number>();
|
|
260
|
+
const scaled = numbers.map(lambda<[number], number>((n) => expr({ n, multiplier })("n * multiplier")));
|
|
261
|
+
```
|
|
262
|
+
|
|
217
263
|
## 高级用法
|
|
218
264
|
|
|
219
|
-
###
|
|
265
|
+
### Proxy 变量系统
|
|
266
|
+
|
|
267
|
+
`variable()` 创建的变量是 Proxy 对象,支持链式属性访问和方法调用,所有操作都会自动转换为表达式。
|
|
268
|
+
|
|
269
|
+
**属性访问:**
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
const config = variable<{
|
|
273
|
+
timeout: number;
|
|
274
|
+
retries: number;
|
|
275
|
+
database: {
|
|
276
|
+
host: string;
|
|
277
|
+
port: number;
|
|
278
|
+
};
|
|
279
|
+
}>();
|
|
280
|
+
|
|
281
|
+
// 链式属性访问
|
|
282
|
+
const timeout = config.timeout; // 自动转换为表达式
|
|
283
|
+
const dbHost = config.database.host; // 支持嵌套访问
|
|
284
|
+
|
|
285
|
+
const compiled = compile(timeout, { config });
|
|
286
|
+
const result = evaluate(compiled, {
|
|
287
|
+
config: { timeout: 5000, retries: 3, database: { host: "localhost", port: 5432 } },
|
|
288
|
+
});
|
|
289
|
+
// => 5000
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
**方法调用:**
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
const calculator = variable<{
|
|
296
|
+
add(a: number, b: number): number;
|
|
297
|
+
multiply(x: number, y: number): number;
|
|
298
|
+
}>();
|
|
299
|
+
|
|
300
|
+
// 方法调用
|
|
301
|
+
const sum = calculator.add(1, 2);
|
|
302
|
+
const product = calculator.multiply(5, 3);
|
|
303
|
+
|
|
304
|
+
// 链式方法调用
|
|
305
|
+
const builder = variable<{
|
|
306
|
+
setName(name: string): typeof builder;
|
|
307
|
+
build(): { name: string };
|
|
308
|
+
}>();
|
|
309
|
+
const result = builder.setName("test").build();
|
|
310
|
+
|
|
311
|
+
// 编译并执行
|
|
312
|
+
const compiled = compile(sum, { calculator });
|
|
313
|
+
const value = evaluate(compiled, {
|
|
314
|
+
calculator: {
|
|
315
|
+
add: (a, b) => a + b,
|
|
316
|
+
multiply: (x, y) => x * y,
|
|
317
|
+
},
|
|
318
|
+
});
|
|
319
|
+
// => 3
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
**数组方法:**
|
|
323
|
+
|
|
324
|
+
数组变量支持所有标准数组方法,并自动处理类型推导:
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
const numbers = variable<number[]>();
|
|
328
|
+
const users = variable<{ id: number; name: string }[]>();
|
|
329
|
+
|
|
330
|
+
// map
|
|
331
|
+
const doubled = numbers.map((n) => expr({ n })("n * 2"));
|
|
332
|
+
|
|
333
|
+
// filter
|
|
334
|
+
const activeUsers = users.filter((u) => expr({ u })("u.active"));
|
|
220
335
|
|
|
221
|
-
|
|
336
|
+
// reduce
|
|
337
|
+
const sum = numbers.reduce(
|
|
338
|
+
lambda<[number, number], number>((acc, val) => expr({ acc, val })("acc + val")),
|
|
339
|
+
0
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
// find, some, every, sort 等
|
|
343
|
+
const firstMatch = users.find((u) => expr({ u })("u.id === 1"));
|
|
344
|
+
const hasAdmins = users.some((u) => expr({ u })("u.role === 'admin'"));
|
|
345
|
+
const allActive = users.every((u) => expr({ u })("u.active"));
|
|
346
|
+
const sorted = numbers.toSorted(lambda<[number, number], number>((a, b) => expr({ a, b })("a - b")));
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### 内置全局对象
|
|
350
|
+
|
|
351
|
+
表达式中可以直接使用以下内置对象(无需在上下文中定义):
|
|
352
|
+
|
|
353
|
+
- `Math`, `JSON`, `Date`, `RegExp`
|
|
354
|
+
- `Number`, `String`, `Boolean`, `Array`, `Object`
|
|
355
|
+
- `undefined`, `NaN`, `Infinity`
|
|
356
|
+
- `isNaN`, `isFinite`, `parseInt`, `parseFloat`
|
|
222
357
|
|
|
223
358
|
```typescript
|
|
224
|
-
const x = variable
|
|
359
|
+
const x = variable<number>();
|
|
225
360
|
|
|
226
|
-
const sqrtExpr = expr({ x
|
|
227
|
-
const compiled = compile(sqrtExpr, { x
|
|
228
|
-
const result = evaluate(compiled, { x: 16
|
|
361
|
+
const sqrtExpr = expr({ x })("Math.sqrt(x)");
|
|
362
|
+
const compiled = compile(sqrtExpr, { x });
|
|
363
|
+
const result = evaluate(compiled, { x: 16 });
|
|
229
364
|
// => 4
|
|
230
365
|
```
|
|
231
366
|
|
|
367
|
+
### 支持的运算符和语法
|
|
368
|
+
|
|
369
|
+
**算术运算符:**
|
|
370
|
+
|
|
371
|
+
- `+`, `-`, `*`, `/`, `%`, `**` (幂运算)
|
|
372
|
+
|
|
373
|
+
**比较运算符:**
|
|
374
|
+
|
|
375
|
+
- `==`, `===`, `!=`, `!==`, `<`, `>`, `<=`, `>=`
|
|
376
|
+
|
|
377
|
+
**逻辑运算符:**
|
|
378
|
+
|
|
379
|
+
- `&&`, `||`, `!`, `??` (空值合并)
|
|
380
|
+
|
|
381
|
+
**位运算符:**
|
|
382
|
+
|
|
383
|
+
- `&`, `|`, `^`, `~`, `<<`, `>>`, `>>>`
|
|
384
|
+
|
|
385
|
+
**其他运算符:**
|
|
386
|
+
|
|
387
|
+
- `? :` (三元表达式)
|
|
388
|
+
- `in` (属性存在检查)
|
|
389
|
+
- `instanceof` (类型检查)
|
|
390
|
+
- `typeof` (类型检测)
|
|
391
|
+
- `?.` (可选链)
|
|
392
|
+
- `?.()` (可选调用)
|
|
393
|
+
- `?.[]` (可选元素访问)
|
|
394
|
+
|
|
395
|
+
**语法特性:**
|
|
396
|
+
|
|
397
|
+
- 对象字面量:`{ key: value, ... }`
|
|
398
|
+
- 数组字面量:`[element1, element2, ...]`
|
|
399
|
+
- 箭头函数:`(param) => expression`
|
|
400
|
+
- 函数调用:`func(arg1, arg2, ...)`
|
|
401
|
+
- 成员访问:`obj.prop`, `obj["prop"]`, `arr[0]`
|
|
402
|
+
- 模板字面量(通过 `t` 标签函数)
|
|
403
|
+
- 分组括号:`(expression)`
|
|
404
|
+
|
|
232
405
|
### 条件表达式
|
|
233
406
|
|
|
234
407
|
```typescript
|
|
235
|
-
const score = variable
|
|
408
|
+
const score = variable<number>();
|
|
236
409
|
const gradeExpr = expr({ score })("score >= 90 ? 'A' : score >= 80 ? 'B' : score >= 70 ? 'C' : 'F'");
|
|
237
410
|
|
|
238
411
|
const compiled = compile(gradeExpr, { score });
|
|
@@ -243,7 +416,7 @@ const grade = evaluate(compiled, { score: 85 });
|
|
|
243
416
|
### 数组和对象操作
|
|
244
417
|
|
|
245
418
|
```typescript
|
|
246
|
-
const numbers = variable
|
|
419
|
+
const numbers = variable<number[]>();
|
|
247
420
|
|
|
248
421
|
const sumExpr = expr({ numbers })("numbers.reduce((a, b) => a + b, 0)");
|
|
249
422
|
|
|
@@ -255,8 +428,8 @@ const sum = evaluate(compiled, { numbers: [1, 2, 3, 4, 5] });
|
|
|
255
428
|
### 链式表达式组合
|
|
256
429
|
|
|
257
430
|
```typescript
|
|
258
|
-
const a = variable
|
|
259
|
-
const b = variable
|
|
431
|
+
const a = variable<number>();
|
|
432
|
+
const b = variable<number>();
|
|
260
433
|
|
|
261
434
|
const sum = expr({ a, b })("a + b");
|
|
262
435
|
const product = expr({ a, b })("a * b");
|
|
@@ -269,6 +442,75 @@ const result = evaluate(compiled, { a: 2, b: 3 });
|
|
|
269
442
|
// => (2+3) * (2*3) - (2-3) = 5 * 6 - (-1) = 30 + 1 = 31
|
|
270
443
|
```
|
|
271
444
|
|
|
445
|
+
### 短路求值(控制流优化)
|
|
446
|
+
|
|
447
|
+
编译器支持为 `&&`、`||`、`??` 和三元表达式生成短路求值代码,避免不必要的计算:
|
|
448
|
+
|
|
449
|
+
```typescript
|
|
450
|
+
const a = variable<boolean>();
|
|
451
|
+
const b = variable<boolean>();
|
|
452
|
+
|
|
453
|
+
// 逻辑或短路
|
|
454
|
+
const orExpr = expr({ a, b })("a || b");
|
|
455
|
+
const compiled = compile(orExpr, { a, b }, { shortCircuit: true });
|
|
456
|
+
|
|
457
|
+
// 当 a 为 true 时,b 不会被求值
|
|
458
|
+
// 编译数据包含控制流节点:
|
|
459
|
+
// [["a", "b"], ["br", "$0", 1], "$1", ["phi"]]
|
|
460
|
+
|
|
461
|
+
// 空值合并
|
|
462
|
+
const x = variable<number | null>();
|
|
463
|
+
const y = variable<number>();
|
|
464
|
+
const coalesce = expr({ x, y })("x ?? y");
|
|
465
|
+
|
|
466
|
+
// 三元表达式
|
|
467
|
+
const condition = variable<boolean>();
|
|
468
|
+
const result = variable<number>();
|
|
469
|
+
const alternative = variable<number>();
|
|
470
|
+
const ternary = expr({ condition, result, alternative })("condition ? result : alternative");
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
### 自动内联优化
|
|
474
|
+
|
|
475
|
+
编译器自动将只被引用一次的子表达式内联到使用位置,减少中间计算:
|
|
476
|
+
|
|
477
|
+
```typescript
|
|
478
|
+
const x = variable<number>();
|
|
479
|
+
const y = variable<number>();
|
|
480
|
+
|
|
481
|
+
const sum = expr({ x, y })("x + y");
|
|
482
|
+
const product = expr({ x, y })("x * y");
|
|
483
|
+
const result = expr({ sum, product })("sum + product");
|
|
484
|
+
|
|
485
|
+
// 自动内联后,编译结果为:
|
|
486
|
+
// [["x", "y"], "($0+$1)+($0*$1)"]
|
|
487
|
+
// 而不是 [["x", "y"], "$0+$1", "$0*$1", "$2+$3"]
|
|
488
|
+
|
|
489
|
+
const compiled = compile(result, { x, y });
|
|
490
|
+
const value = evaluate(compiled, { x: 2, y: 3 });
|
|
491
|
+
// => 11
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
### 直接编译对象和数组
|
|
495
|
+
|
|
496
|
+
`compile` 函数支持直接编译包含 Proxy 的对象和数组:
|
|
497
|
+
|
|
498
|
+
```typescript
|
|
499
|
+
const x = variable<number>();
|
|
500
|
+
const y = variable<number>();
|
|
501
|
+
const sum = expr({ x, y })("x + y");
|
|
502
|
+
|
|
503
|
+
// 编译对象
|
|
504
|
+
const objCompiled = compile({ result: sum, original: { x, y } }, { x, y });
|
|
505
|
+
const objResult = evaluate(objCompiled, { x: 10, y: 20 });
|
|
506
|
+
// => { result: 30, original: { x: 10, y: 20 } }
|
|
507
|
+
|
|
508
|
+
// 编译数组
|
|
509
|
+
const arrCompiled = compile([x, sum, 100], { x, y });
|
|
510
|
+
const arrResult = evaluate(arrCompiled, { x: 5, y: 3 });
|
|
511
|
+
// => [5, 8, 100]
|
|
512
|
+
```
|
|
513
|
+
|
|
272
514
|
## 序列化和传输
|
|
273
515
|
|
|
274
516
|
编译后的数据可以轻松进行 JSON 序列化,适合网络传输或持久化存储:
|
|
@@ -290,16 +532,200 @@ const deserialized = JSON.parse(json);
|
|
|
290
532
|
const value = evaluate(deserialized, { x: 5, y: 3 });
|
|
291
533
|
```
|
|
292
534
|
|
|
535
|
+
## 编译数据格式
|
|
536
|
+
|
|
537
|
+
### V1 格式(基础表达式)
|
|
538
|
+
|
|
539
|
+
基础格式为 JSON 数组:`[variableNames, ...expressions]`
|
|
540
|
+
|
|
541
|
+
```typescript
|
|
542
|
+
// 输入
|
|
543
|
+
const sum = expr({ x, y })("x + y");
|
|
544
|
+
const compiled = compile(sum, { x, y });
|
|
545
|
+
|
|
546
|
+
// 输出
|
|
547
|
+
// [["x", "y"], "$0+$1"]
|
|
548
|
+
// $0 引用 x,$1 引用 y
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
### V2 格式(控制流节点)
|
|
552
|
+
|
|
553
|
+
启用短路求值时,生成包含控制流节点的格式:
|
|
554
|
+
|
|
555
|
+
```typescript
|
|
556
|
+
// 输入
|
|
557
|
+
const result = expr({ a, b })("a || b");
|
|
558
|
+
const compiled = compile(result, { a, b }, { shortCircuit: true });
|
|
559
|
+
|
|
560
|
+
// 输出
|
|
561
|
+
// [
|
|
562
|
+
// ["a", "b"],
|
|
563
|
+
// ["br", "$0", 1], // 如果 $0 为 truthy,跳过 1 条指令
|
|
564
|
+
// "$1", // 否则求值 $1
|
|
565
|
+
// ["phi"] // 取最近求值结果
|
|
566
|
+
// ]
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
**控制流节点类型:**
|
|
570
|
+
|
|
571
|
+
- `["br", condition, offset]` - 条件跳转,条件为真时跳过 offset 条指令
|
|
572
|
+
- `["jmp", offset]` - 无条件跳转,跳过 offset 条指令
|
|
573
|
+
- `["phi"]` - 取最近求值结果(用于合并分支)
|
|
574
|
+
|
|
575
|
+
## 错误处理
|
|
576
|
+
|
|
577
|
+
### 编译时错误
|
|
578
|
+
|
|
579
|
+
编译器会检测并报告以下错误:
|
|
580
|
+
|
|
581
|
+
```typescript
|
|
582
|
+
const x = variable<number>();
|
|
583
|
+
const y = variable<number>();
|
|
584
|
+
|
|
585
|
+
// 错误:引用未定义的变量
|
|
586
|
+
const invalid = expr({ x, y })("x + y + z");
|
|
587
|
+
compile(invalid, { x, y });
|
|
588
|
+
// => Error: Undefined variable(s): z
|
|
589
|
+
|
|
590
|
+
// 错误:变量名冲突
|
|
591
|
+
const xy = variable<number>();
|
|
592
|
+
const conflict = expr({ xy, x })("xy + x");
|
|
593
|
+
// 正确处理:编译器能区分 xy 和 x
|
|
594
|
+
const compiled = compile(conflict, { xy, x });
|
|
595
|
+
// => [["xy", "x"], "$0+$1"]
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
### 运行时错误
|
|
599
|
+
|
|
600
|
+
求值器会验证输入并报告运行时错误:
|
|
601
|
+
|
|
602
|
+
```typescript
|
|
603
|
+
const x = variable<number>();
|
|
604
|
+
const y = variable<number>();
|
|
605
|
+
|
|
606
|
+
const sum = expr({ x, y })("x + y");
|
|
607
|
+
const compiled = compile(sum, { x, y });
|
|
608
|
+
|
|
609
|
+
// 错误:缺少必需变量
|
|
610
|
+
evaluate(compiled, { x: 2 });
|
|
611
|
+
// => Error: Missing required variable: y
|
|
612
|
+
|
|
613
|
+
// 错误:无效的编译数据
|
|
614
|
+
evaluate([], { x: 1 });
|
|
615
|
+
// => Error: Invalid compiled data: must have at least variable names
|
|
616
|
+
```
|
|
617
|
+
|
|
293
618
|
## 类型安全
|
|
294
619
|
|
|
295
620
|
项目充分利用 TypeScript 的类型系统进行编译时检查和类型推导:
|
|
296
621
|
|
|
297
622
|
```typescript
|
|
298
|
-
const x = variable
|
|
299
|
-
const y = variable
|
|
623
|
+
const x = variable<number>();
|
|
624
|
+
const y = variable<string>();
|
|
300
625
|
|
|
301
|
-
//
|
|
302
|
-
const
|
|
626
|
+
// 类型错误会在编译时捕获
|
|
627
|
+
// const invalid = expr({ x, y })("z + y"); // Error: 'z' not in context
|
|
628
|
+
const valid = expr({ x })("-x"); // 编译器推导为 number
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
## 实际应用示例
|
|
632
|
+
|
|
633
|
+
### 动态表单验证规则
|
|
634
|
+
|
|
635
|
+
```typescript
|
|
636
|
+
const formData = variable<{
|
|
637
|
+
username: string;
|
|
638
|
+
password: string;
|
|
639
|
+
confirmPassword: string;
|
|
640
|
+
age: number;
|
|
641
|
+
}>();
|
|
642
|
+
|
|
643
|
+
// 创建验证规则表达式
|
|
644
|
+
const isUsernameValid = expr({ formData })("formData.username.length >= 3 && formData.username.length <= 20");
|
|
645
|
+
|
|
646
|
+
const isPasswordValid = expr({ formData })("formData.password.length >= 8 && /[A-Z]/.test(formData.password)");
|
|
647
|
+
|
|
648
|
+
const doPasswordsMatch = expr({ formData })("formData.password === formData.confirmPassword");
|
|
649
|
+
|
|
650
|
+
const isAgeValid = expr({ formData })("formData.age >= 18 && formData.age <= 120");
|
|
651
|
+
|
|
652
|
+
const isFormValid = expr({
|
|
653
|
+
isUsernameValid,
|
|
654
|
+
isPasswordValid,
|
|
655
|
+
doPasswordsMatch,
|
|
656
|
+
isAgeValid,
|
|
657
|
+
})("isUsernameValid && isPasswordValid && doPasswordsMatch && isAgeValid");
|
|
658
|
+
|
|
659
|
+
// 编译一次,多次执行
|
|
660
|
+
const compiled = compile(isFormValid, { formData });
|
|
661
|
+
|
|
662
|
+
// 在表单输入时实时验证
|
|
663
|
+
evaluate(compiled, {
|
|
664
|
+
formData: {
|
|
665
|
+
username: "john_doe",
|
|
666
|
+
password: "Secure123",
|
|
667
|
+
confirmPassword: "Secure123",
|
|
668
|
+
age: 25,
|
|
669
|
+
},
|
|
670
|
+
}); // => true
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
### 数据转换管道
|
|
674
|
+
|
|
675
|
+
```typescript
|
|
676
|
+
const rawData = variable<any[]>();
|
|
677
|
+
const config = variable<{
|
|
678
|
+
minValue: number;
|
|
679
|
+
maxValue: number;
|
|
680
|
+
transform: (x: number) => number;
|
|
681
|
+
}>();
|
|
682
|
+
|
|
683
|
+
// 构建数据处理管道
|
|
684
|
+
const filtered = rawData.filter(
|
|
685
|
+
lambda<[any], boolean>((item) =>
|
|
686
|
+
expr({ item, config })("item.value >= config.minValue && item.value <= config.maxValue")
|
|
687
|
+
)
|
|
688
|
+
);
|
|
689
|
+
|
|
690
|
+
const transformed = filtered.map(
|
|
691
|
+
lambda<[any], number>((item) => expr({ item, config })("config.transform(item.value)"))
|
|
692
|
+
);
|
|
693
|
+
|
|
694
|
+
const sorted = transformed.toSorted(lambda<[number, number], number>((a, b) => expr({ a, b })("a - b")));
|
|
695
|
+
|
|
696
|
+
const pipeline = compile(sorted, { rawData, config });
|
|
697
|
+
|
|
698
|
+
// 执行数据处理
|
|
699
|
+
const result = evaluate(pipeline, {
|
|
700
|
+
rawData: [{ value: 10 }, { value: 5 }, { value: 20 }, { value: 15 }],
|
|
701
|
+
config: { minValue: 8, maxValue: 18, transform: (x: number) => x * 2 },
|
|
702
|
+
});
|
|
703
|
+
// => [10, 20, 30] (5 被过滤,10*2=20, 15*2=30, 20 被过滤)
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
### 规则引擎
|
|
707
|
+
|
|
708
|
+
```typescript
|
|
709
|
+
// 定义规则条件
|
|
710
|
+
const user = variable<{
|
|
711
|
+
age: number;
|
|
712
|
+
role: string;
|
|
713
|
+
balance: number;
|
|
714
|
+
}>();
|
|
715
|
+
|
|
716
|
+
const isEligible = expr({ user })(
|
|
717
|
+
"(user.age >= 18 && user.age <= 65) && (user.role === 'premium' || user.balance > 10000)"
|
|
718
|
+
);
|
|
719
|
+
|
|
720
|
+
const discountRate = expr({ user, isEligible })("isEligible ? (user.role === 'premium' ? 0.2 : 0.1) : 0");
|
|
721
|
+
|
|
722
|
+
const rule = compile(discountRate, { user });
|
|
723
|
+
|
|
724
|
+
// 应用规则
|
|
725
|
+
const discount = evaluate(rule, {
|
|
726
|
+
user: { age: 30, role: "premium", balance: 5000 },
|
|
727
|
+
});
|
|
728
|
+
// => 0.2 (20% 折扣)
|
|
303
729
|
```
|
|
304
730
|
|
|
305
731
|
## 性能考虑
|
|
@@ -307,20 +733,45 @@ const sum = expr({ x, y })("x + y");
|
|
|
307
733
|
- **编译时间**:编译过程涉及依赖分析和拓扑排序,通常快速完成
|
|
308
734
|
- **执行时间**:表达式通过 `new Function()` 编译为原生 JavaScript,执行性能接近原生代码
|
|
309
735
|
- **内存占用**:编译数据为纯 JSON,占用空间小,适合在网络上传输
|
|
736
|
+
- **缓存机制**:求值器缓存已编译的函数,重复执行时性能更优
|
|
737
|
+
|
|
738
|
+
### 最佳实践
|
|
739
|
+
|
|
740
|
+
1. **编译一次,多次执行**:对于重复使用的表达式,先编译后多次求值
|
|
741
|
+
|
|
742
|
+
```typescript
|
|
743
|
+
const compiled = compile(expression, variables);
|
|
744
|
+
// 缓存 compiled,多次调用 evaluate
|
|
745
|
+
evaluate(compiled, values1);
|
|
746
|
+
evaluate(compiled, values2);
|
|
747
|
+
```
|
|
748
|
+
|
|
749
|
+
2. **合理使用短路求值**:对于条件表达式,启用短路求值可以避免不必要的计算
|
|
750
|
+
|
|
751
|
+
```typescript
|
|
752
|
+
compile(expression, variables, { shortCircuit: true });
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
3. **利用自动内联**:编译器会自动内联只引用一次的子表达式,无需手动优化
|
|
756
|
+
|
|
757
|
+
4. **优先使用 Proxy 链式调用**:对于对象属性访问,使用 `config.timeout` 比 `expr({ config })("config.timeout")` 更简洁且类型更安全
|
|
310
758
|
|
|
311
759
|
## 项目结构
|
|
312
760
|
|
|
313
761
|
```
|
|
314
762
|
src/
|
|
315
|
-
├── index.ts
|
|
316
|
-
├── variable.ts
|
|
317
|
-
├── expr.ts
|
|
318
|
-
├──
|
|
319
|
-
├──
|
|
320
|
-
├──
|
|
321
|
-
├──
|
|
322
|
-
├──
|
|
323
|
-
|
|
763
|
+
├── index.ts # 导出入口
|
|
764
|
+
├── variable.ts # variable<T>() 函数
|
|
765
|
+
├── expr.ts # expr() 函数
|
|
766
|
+
├── template.ts # t() 标签模板函数
|
|
767
|
+
├── lambda.ts # lambda() 函数(数组方法支持)
|
|
768
|
+
├── compile.ts # 编译器(内联优化、短路求值)
|
|
769
|
+
├── evaluate.ts # 运行时求值
|
|
770
|
+
├── parser.ts # 表达式 AST 解析器
|
|
771
|
+
├── type-parser.ts # TypeScript 类型级表达式解析
|
|
772
|
+
├── proxy-variable.ts # Proxy 变量实现
|
|
773
|
+
├── proxy-metadata.ts # Proxy 元数据管理
|
|
774
|
+
└── types.ts # 类型定义(Variable、Expression、Lambda 等)
|
|
324
775
|
```
|
|
325
776
|
|
|
326
777
|
## 开发
|