@variojs/core 0.1.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.
Files changed (209) hide show
  1. package/__tests__/README.md +101 -0
  2. package/__tests__/errors.test.ts +139 -0
  3. package/__tests__/expression/cache.test.ts +118 -0
  4. package/__tests__/expression/compiler.test.ts +111 -0
  5. package/__tests__/expression/evaluate.test.ts +95 -0
  6. package/__tests__/expression/parser.test.ts +57 -0
  7. package/__tests__/expression/whitelist.test.ts +59 -0
  8. package/__tests__/performance.test.ts +379 -0
  9. package/__tests__/runtime/create-context.test.ts +78 -0
  10. package/__tests__/runtime/loop-context-pool.test.ts +74 -0
  11. package/__tests__/runtime/path.test.ts +128 -0
  12. package/__tests__/vm/executor-timeout.test.ts +117 -0
  13. package/__tests__/vm/executor.test.ts +173 -0
  14. package/__tests__/vm/handlers/array.test.ts +113 -0
  15. package/__tests__/vm/handlers/call.test.ts +93 -0
  16. package/dist/errors.d.ts +100 -0
  17. package/dist/errors.d.ts.map +1 -0
  18. package/dist/errors.js +132 -0
  19. package/dist/errors.js.map +1 -0
  20. package/dist/expression/cache.d.ts +53 -0
  21. package/dist/expression/cache.d.ts.map +1 -0
  22. package/dist/expression/cache.js +158 -0
  23. package/dist/expression/cache.js.map +1 -0
  24. package/dist/expression/compiler.d.ts +34 -0
  25. package/dist/expression/compiler.d.ts.map +1 -0
  26. package/dist/expression/compiler.js +123 -0
  27. package/dist/expression/compiler.js.map +1 -0
  28. package/dist/expression/dependencies.d.ts +17 -0
  29. package/dist/expression/dependencies.d.ts.map +1 -0
  30. package/dist/expression/dependencies.js +106 -0
  31. package/dist/expression/dependencies.js.map +1 -0
  32. package/dist/expression/evaluate.d.ts +22 -0
  33. package/dist/expression/evaluate.d.ts.map +1 -0
  34. package/dist/expression/evaluate.js +75 -0
  35. package/dist/expression/evaluate.js.map +1 -0
  36. package/dist/expression/evaluator.d.ts +22 -0
  37. package/dist/expression/evaluator.d.ts.map +1 -0
  38. package/dist/expression/evaluator.js +506 -0
  39. package/dist/expression/evaluator.js.map +1 -0
  40. package/dist/expression/index.d.ts +12 -0
  41. package/dist/expression/index.d.ts.map +1 -0
  42. package/dist/expression/index.js +12 -0
  43. package/dist/expression/index.js.map +1 -0
  44. package/dist/expression/parser.d.ts +15 -0
  45. package/dist/expression/parser.d.ts.map +1 -0
  46. package/dist/expression/parser.js +42 -0
  47. package/dist/expression/parser.js.map +1 -0
  48. package/dist/expression/utils.d.ts +46 -0
  49. package/dist/expression/utils.d.ts.map +1 -0
  50. package/dist/expression/utils.js +78 -0
  51. package/dist/expression/utils.js.map +1 -0
  52. package/dist/expression/whitelist.d.ts +24 -0
  53. package/dist/expression/whitelist.d.ts.map +1 -0
  54. package/dist/expression/whitelist.js +198 -0
  55. package/dist/expression/whitelist.js.map +1 -0
  56. package/dist/index.d.ts +19 -0
  57. package/dist/index.d.ts.map +1 -0
  58. package/dist/index.js +21 -0
  59. package/dist/index.js.map +1 -0
  60. package/dist/runtime/context.d.ts +8 -0
  61. package/dist/runtime/context.d.ts.map +1 -0
  62. package/dist/runtime/context.js +7 -0
  63. package/dist/runtime/context.js.map +1 -0
  64. package/dist/runtime/create-context.d.ts +50 -0
  65. package/dist/runtime/create-context.d.ts.map +1 -0
  66. package/dist/runtime/create-context.js +73 -0
  67. package/dist/runtime/create-context.js.map +1 -0
  68. package/dist/runtime/index.d.ts +10 -0
  69. package/dist/runtime/index.d.ts.map +1 -0
  70. package/dist/runtime/index.js +10 -0
  71. package/dist/runtime/index.js.map +1 -0
  72. package/dist/runtime/loop-context-pool.d.ts +58 -0
  73. package/dist/runtime/loop-context-pool.d.ts.map +1 -0
  74. package/dist/runtime/loop-context-pool.js +114 -0
  75. package/dist/runtime/loop-context-pool.js.map +1 -0
  76. package/dist/runtime/path.d.ts +114 -0
  77. package/dist/runtime/path.d.ts.map +1 -0
  78. package/dist/runtime/path.js +302 -0
  79. package/dist/runtime/path.js.map +1 -0
  80. package/dist/runtime/proxy.d.ts +18 -0
  81. package/dist/runtime/proxy.d.ts.map +1 -0
  82. package/dist/runtime/proxy.js +53 -0
  83. package/dist/runtime/proxy.js.map +1 -0
  84. package/dist/runtime/sandbox.d.ts +20 -0
  85. package/dist/runtime/sandbox.d.ts.map +1 -0
  86. package/dist/runtime/sandbox.js +32 -0
  87. package/dist/runtime/sandbox.js.map +1 -0
  88. package/dist/types.d.ts +175 -0
  89. package/dist/types.d.ts.map +1 -0
  90. package/dist/types.js +22 -0
  91. package/dist/types.js.map +1 -0
  92. package/dist/vm/action.d.ts +11 -0
  93. package/dist/vm/action.d.ts.map +1 -0
  94. package/dist/vm/action.js +10 -0
  95. package/dist/vm/action.js.map +1 -0
  96. package/dist/vm/errors.d.ts +5 -0
  97. package/dist/vm/errors.d.ts.map +1 -0
  98. package/dist/vm/errors.js +5 -0
  99. package/dist/vm/errors.js.map +1 -0
  100. package/dist/vm/executor.d.ts +35 -0
  101. package/dist/vm/executor.d.ts.map +1 -0
  102. package/dist/vm/executor.js +137 -0
  103. package/dist/vm/executor.js.map +1 -0
  104. package/dist/vm/handlers/array/pop.d.ts +12 -0
  105. package/dist/vm/handlers/array/pop.d.ts.map +1 -0
  106. package/dist/vm/handlers/array/pop.js +28 -0
  107. package/dist/vm/handlers/array/pop.js.map +1 -0
  108. package/dist/vm/handlers/array/push.d.ts +13 -0
  109. package/dist/vm/handlers/array/push.d.ts.map +1 -0
  110. package/dist/vm/handlers/array/push.js +42 -0
  111. package/dist/vm/handlers/array/push.js.map +1 -0
  112. package/dist/vm/handlers/array/shift.d.ts +12 -0
  113. package/dist/vm/handlers/array/shift.d.ts.map +1 -0
  114. package/dist/vm/handlers/array/shift.js +28 -0
  115. package/dist/vm/handlers/array/shift.js.map +1 -0
  116. package/dist/vm/handlers/array/splice.d.ts +12 -0
  117. package/dist/vm/handlers/array/splice.d.ts.map +1 -0
  118. package/dist/vm/handlers/array/splice.js +59 -0
  119. package/dist/vm/handlers/array/splice.js.map +1 -0
  120. package/dist/vm/handlers/array/unshift.d.ts +13 -0
  121. package/dist/vm/handlers/array/unshift.d.ts.map +1 -0
  122. package/dist/vm/handlers/array/unshift.js +42 -0
  123. package/dist/vm/handlers/array/unshift.js.map +1 -0
  124. package/dist/vm/handlers/array/utils.d.ts +10 -0
  125. package/dist/vm/handlers/array/utils.d.ts.map +1 -0
  126. package/dist/vm/handlers/array/utils.js +33 -0
  127. package/dist/vm/handlers/array/utils.js.map +1 -0
  128. package/dist/vm/handlers/batch.d.ts +12 -0
  129. package/dist/vm/handlers/batch.d.ts.map +1 -0
  130. package/dist/vm/handlers/batch.js +40 -0
  131. package/dist/vm/handlers/batch.js.map +1 -0
  132. package/dist/vm/handlers/call.d.ts +14 -0
  133. package/dist/vm/handlers/call.d.ts.map +1 -0
  134. package/dist/vm/handlers/call.js +65 -0
  135. package/dist/vm/handlers/call.js.map +1 -0
  136. package/dist/vm/handlers/emit.d.ts +12 -0
  137. package/dist/vm/handlers/emit.d.ts.map +1 -0
  138. package/dist/vm/handlers/emit.js +26 -0
  139. package/dist/vm/handlers/emit.js.map +1 -0
  140. package/dist/vm/handlers/if.d.ts +13 -0
  141. package/dist/vm/handlers/if.d.ts.map +1 -0
  142. package/dist/vm/handlers/if.js +35 -0
  143. package/dist/vm/handlers/if.js.map +1 -0
  144. package/dist/vm/handlers/index.d.ts +11 -0
  145. package/dist/vm/handlers/index.d.ts.map +1 -0
  146. package/dist/vm/handlers/index.js +46 -0
  147. package/dist/vm/handlers/index.js.map +1 -0
  148. package/dist/vm/handlers/log.d.ts +12 -0
  149. package/dist/vm/handlers/log.d.ts.map +1 -0
  150. package/dist/vm/handlers/log.js +41 -0
  151. package/dist/vm/handlers/log.js.map +1 -0
  152. package/dist/vm/handlers/loop.d.ts +12 -0
  153. package/dist/vm/handlers/loop.d.ts.map +1 -0
  154. package/dist/vm/handlers/loop.js +71 -0
  155. package/dist/vm/handlers/loop.js.map +1 -0
  156. package/dist/vm/handlers/navigate.d.ts +12 -0
  157. package/dist/vm/handlers/navigate.d.ts.map +1 -0
  158. package/dist/vm/handlers/navigate.js +43 -0
  159. package/dist/vm/handlers/navigate.js.map +1 -0
  160. package/dist/vm/handlers/set.d.ts +15 -0
  161. package/dist/vm/handlers/set.d.ts.map +1 -0
  162. package/dist/vm/handlers/set.js +30 -0
  163. package/dist/vm/handlers/set.js.map +1 -0
  164. package/dist/vm/index.d.ts +8 -0
  165. package/dist/vm/index.d.ts.map +1 -0
  166. package/dist/vm/index.js +7 -0
  167. package/dist/vm/index.js.map +1 -0
  168. package/package.json +34 -0
  169. package/src/errors.ts +194 -0
  170. package/src/expression/README.md +192 -0
  171. package/src/expression/cache.ts +199 -0
  172. package/src/expression/compiler.ts +144 -0
  173. package/src/expression/dependencies.ts +116 -0
  174. package/src/expression/evaluate.ts +95 -0
  175. package/src/expression/evaluator.ts +640 -0
  176. package/src/expression/index.ts +27 -0
  177. package/src/expression/parser.ts +54 -0
  178. package/src/expression/utils.ts +89 -0
  179. package/src/expression/whitelist.ts +224 -0
  180. package/src/globals.d.ts +10 -0
  181. package/src/index.ts +72 -0
  182. package/src/runtime/context.ts +8 -0
  183. package/src/runtime/create-context.ts +133 -0
  184. package/src/runtime/index.ts +28 -0
  185. package/src/runtime/loop-context-pool.ts +134 -0
  186. package/src/runtime/path.ts +372 -0
  187. package/src/runtime/proxy.ts +66 -0
  188. package/src/runtime/sandbox.ts +43 -0
  189. package/src/types.ts +177 -0
  190. package/src/vm/errors.ts +10 -0
  191. package/src/vm/executor.ts +210 -0
  192. package/src/vm/handlers/array/pop.ts +47 -0
  193. package/src/vm/handlers/array/push.ts +68 -0
  194. package/src/vm/handlers/array/shift.ts +47 -0
  195. package/src/vm/handlers/array/splice.ts +78 -0
  196. package/src/vm/handlers/array/unshift.ts +68 -0
  197. package/src/vm/handlers/array/utils.ts +41 -0
  198. package/src/vm/handlers/batch.ts +57 -0
  199. package/src/vm/handlers/call.ts +92 -0
  200. package/src/vm/handlers/emit.ts +39 -0
  201. package/src/vm/handlers/if.ts +48 -0
  202. package/src/vm/handlers/index.ts +52 -0
  203. package/src/vm/handlers/log.ts +54 -0
  204. package/src/vm/handlers/loop.ts +102 -0
  205. package/src/vm/handlers/navigate.ts +64 -0
  206. package/src/vm/handlers/set.ts +43 -0
  207. package/src/vm/index.ts +8 -0
  208. package/tsconfig.json +17 -0
  209. package/vitest.config.ts +30 -0
@@ -0,0 +1,144 @@
1
+ /**
2
+ * 表达式编译器
3
+ *
4
+ * 功能:
5
+ * - 将简单表达式编译为直接访问函数
6
+ * - 提升简单表达式的执行性能
7
+ * - 复杂表达式回退到解释执行
8
+ */
9
+
10
+ import type * as ESTree from '@babel/types'
11
+ import type { RuntimeContext } from '../types.js'
12
+
13
+ /**
14
+ * 编译后的表达式函数类型
15
+ */
16
+ export type CompiledExpression = (ctx: RuntimeContext) => unknown
17
+
18
+ /**
19
+ * 编译缓存(全局,表达式字符串 → 编译函数)
20
+ */
21
+ const compiledCache = new Map<string, CompiledExpression | null>()
22
+
23
+ /**
24
+ * 提取静态路径(从 MemberExpression)
25
+ *
26
+ * 例如:user.name → "user.name"
27
+ * 例如:items.0.text → "items.0.text"
28
+ */
29
+ function extractStaticPath(node: ESTree.Node): string | null {
30
+ if (node.type === 'Identifier') {
31
+ return node.name
32
+ }
33
+
34
+ if (node.type === 'MemberExpression') {
35
+ const object = extractStaticPath(node.object)
36
+ if (!object) return null
37
+
38
+ if (node.computed) {
39
+ // 计算属性:obj[key],需要检查 key 是否为字面量
40
+ if (node.property.type === 'NumericLiteral') {
41
+ const prop = (node.property as ESTree.NumericLiteral).value
42
+ return `${object}.${String(prop)}`
43
+ }
44
+ if (node.property.type === 'StringLiteral') {
45
+ const prop = (node.property as ESTree.StringLiteral).value
46
+ return `${object}.${String(prop)}`
47
+ }
48
+ // 处理其他字面量类型(已由上面的具体类型处理,这里不需要)
49
+ return null
50
+ } else {
51
+ // 静态属性:obj.prop
52
+ if (node.property.type === 'Identifier') {
53
+ return `${object}.${node.property.name}`
54
+ }
55
+ return null
56
+ }
57
+ }
58
+
59
+ return null
60
+ }
61
+
62
+ /**
63
+ * 编译简单表达式为直接访问函数
64
+ *
65
+ * @param ast AST 节点
66
+ * @returns 编译后的函数,如果无法编译则返回 null
67
+ */
68
+ export function compileSimpleExpression(ast: ESTree.Node): CompiledExpression | null {
69
+ // 字面量:{{ 42 }} → () => 42
70
+ if (ast.type === 'NumericLiteral') {
71
+ const value = (ast as ESTree.NumericLiteral).value
72
+ return () => value
73
+ }
74
+ if (ast.type === 'StringLiteral') {
75
+ const value = (ast as ESTree.StringLiteral).value
76
+ return () => value
77
+ }
78
+ if (ast.type === 'BooleanLiteral') {
79
+ const value = (ast as ESTree.BooleanLiteral).value
80
+ return () => value
81
+ }
82
+ if (ast.type === 'NullLiteral') {
83
+ return () => null
84
+ }
85
+ // 注意:Literal 类型已被更具体的类型(NumericLiteral, StringLiteral等)替代
86
+ // 如果遇到其他字面量类型,回退到解释执行
87
+
88
+ // 标识符:{{ user }} → (ctx) => ctx._get('user')
89
+ // 注意:全局对象名称(window, document等)不应该被编译,应该回退到解释执行以进行安全检查
90
+ if (ast.type === 'Identifier') {
91
+ const name = ast.name
92
+ // 全局对象名称不应该被编译(需要安全检查)
93
+ const globalObjectNames = ['window', 'document', 'global', 'globalThis', 'self']
94
+ if (globalObjectNames.includes(name)) {
95
+ return null // 回退到解释执行
96
+ }
97
+ return (ctx: RuntimeContext) => ctx._get(name)
98
+ }
99
+
100
+ // 静态成员访问:{{ user.name }} → (ctx) => ctx._get('user.name')
101
+ // 注意:如果路径以全局对象名称开头,不应该被编译
102
+ const path = extractStaticPath(ast)
103
+ if (path) {
104
+ // 检查路径是否以全局对象名称开头
105
+ const globalObjectNames = ['window', 'document', 'global', 'globalThis', 'self']
106
+ const firstSegment = path.split('.')[0]
107
+ if (globalObjectNames.includes(firstSegment)) {
108
+ return null // 回退到解释执行以进行安全检查
109
+ }
110
+ return (ctx: RuntimeContext) => ctx._get(path)
111
+ }
112
+
113
+ return null // 复杂表达式,无法编译
114
+ }
115
+
116
+ /**
117
+ * 获取或编译表达式
118
+ *
119
+ * @param expr 表达式字符串
120
+ * @param ast AST 节点(已解析)
121
+ * @returns 编译后的函数,如果无法编译则返回 null
122
+ */
123
+ export function getCompiledExpression(
124
+ expr: string,
125
+ ast: ESTree.Node
126
+ ): CompiledExpression | null {
127
+ // 检查缓存
128
+ if (compiledCache.has(expr)) {
129
+ return compiledCache.get(expr) || null
130
+ }
131
+
132
+ // 尝试编译
133
+ const compiled = compileSimpleExpression(ast)
134
+ compiledCache.set(expr, compiled)
135
+
136
+ return compiled
137
+ }
138
+
139
+ /**
140
+ * 清除编译缓存
141
+ */
142
+ export function clearCompiledCache(): void {
143
+ compiledCache.clear()
144
+ }
@@ -0,0 +1,116 @@
1
+ /**
2
+ * 依赖提取算法
3
+ *
4
+ * 功能:
5
+ * - 从 AST 中提取状态依赖
6
+ * - 支持通配符依赖(items.*)
7
+ * - 保守策略:标记整个对象
8
+ */
9
+
10
+ import type * as ESTree from '@babel/types'
11
+
12
+ /**
13
+ * 从 AST 中提取依赖的状态路径
14
+ *
15
+ * @param ast AST 节点
16
+ * @returns 依赖路径数组(支持通配符)
17
+ */
18
+ export function extractDependencies(ast: ESTree.Node): string[] {
19
+ const dependencies = new Set<string>()
20
+
21
+ function traverse(node: ESTree.Node, path: string = ''): void {
22
+ switch (node.type) {
23
+ case 'Identifier': {
24
+ const id = node as ESTree.Identifier
25
+ // 标识符可能是状态依赖(排除全局函数)
26
+ if (!isGlobalFunction(id.name)) {
27
+ dependencies.add(id.name)
28
+ }
29
+ break
30
+ }
31
+
32
+ case 'MemberExpression': {
33
+ const member = node as ESTree.MemberExpression
34
+ const object = member.object
35
+
36
+ if (object.type === 'Identifier') {
37
+ const basePath = object.name
38
+
39
+ if (member.computed) {
40
+ // 计算属性:items[0] -> 保守策略:标记 items.*
41
+ dependencies.add(`${basePath}.*`)
42
+ traverse(member.property, path)
43
+ } else {
44
+ // 静态属性:user.name
45
+ const prop = (member.property as ESTree.Identifier).name
46
+ dependencies.add(`${basePath}.${prop}`)
47
+ // 同时添加通配符依赖(保守策略)
48
+ dependencies.add(`${basePath}.*`)
49
+ }
50
+ } else {
51
+ // 嵌套成员访问:user.profile.name
52
+ traverse(object, path)
53
+ }
54
+
55
+ break
56
+ }
57
+
58
+ case 'OptionalMemberExpression': {
59
+ const member = node as ESTree.OptionalMemberExpression
60
+ const object = member.object
61
+
62
+ if (object.type === 'Identifier') {
63
+ const basePath = object.name
64
+
65
+ if (member.computed) {
66
+ dependencies.add(`${basePath}.*`)
67
+ traverse(member.property, path)
68
+ } else {
69
+ const prop = (member.property as ESTree.Identifier).name
70
+ dependencies.add(`${basePath}.${prop}`)
71
+ dependencies.add(`${basePath}.*`)
72
+ }
73
+ } else {
74
+ traverse(object, path)
75
+ }
76
+
77
+ break
78
+ }
79
+
80
+ default: {
81
+ // 递归遍历所有子节点
82
+ for (const key in node) {
83
+ const value = (node as unknown as Record<string, unknown>)[key]
84
+
85
+ if (value == null) continue
86
+
87
+ if (Array.isArray(value)) {
88
+ value.forEach(child => {
89
+ if (child && typeof child === 'object' && 'type' in child) {
90
+ traverse(child as ESTree.Node, path)
91
+ }
92
+ })
93
+ } else if (value && typeof value === 'object' && 'type' in value) {
94
+ traverse(value as ESTree.Node, path)
95
+ }
96
+ }
97
+ }
98
+ }
99
+ }
100
+
101
+ traverse(ast)
102
+ return Array.from(dependencies)
103
+ }
104
+
105
+ /**
106
+ * 检查是否为全局函数(不应作为依赖)
107
+ */
108
+ function isGlobalFunction(name: string): boolean {
109
+ const globals = new Set([
110
+ 'String', 'Number', 'Boolean', 'BigInt', 'Symbol',
111
+ 'Array', 'Object', 'Math', 'Date',
112
+ 'console', 'JSON', 'parseInt', 'parseFloat',
113
+ 'isNaN', 'isFinite',
114
+ ])
115
+ return globals.has(name)
116
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * 表达式求值入口函数
3
+ *
4
+ * 整合解析、验证、缓存、求值流程
5
+ */
6
+
7
+ import type { RuntimeContext, ExpressionOptions } from '../types.js'
8
+ import { ExpressionError, ErrorCodes } from '../errors.js'
9
+ import { parseExpression } from './parser.js'
10
+ import { validateAST } from './whitelist.js'
11
+ import { evaluateExpression } from './evaluator.js'
12
+ import { extractDependencies } from './dependencies.js'
13
+ import { getCompiledExpression } from './compiler.js'
14
+ import {
15
+ getCachedExpression,
16
+ setCachedExpression,
17
+ } from './cache.js'
18
+
19
+ /**
20
+ * 求值表达式(完整流程)
21
+ *
22
+ * @param expr 表达式字符串
23
+ * @param ctx 运行时上下文
24
+ * @returns 求值结果(类型无法静态推导,返回 unknown)
25
+ *
26
+ * 注意:表达式求值结果类型无法在编译时确定,因为:
27
+ * 1. 表达式是运行时字符串
28
+ * 2. 状态类型是动态的
29
+ * 3. 表达式可能返回任意类型
30
+ *
31
+ * 如果需要类型安全,应在使用结果时进行类型守卫或类型断言
32
+ */
33
+ export function evaluate(
34
+ expr: string,
35
+ ctx: RuntimeContext,
36
+ options: ExpressionOptions = {}
37
+ ): unknown {
38
+ try {
39
+ // 合并选项:ctx.$exprOptions 优先级低于直接传入的 options
40
+ const mergedOptions = {
41
+ ...ctx.$exprOptions,
42
+ ...options
43
+ }
44
+
45
+ // 1. 检查结果缓存
46
+ const cached = getCachedExpression(expr, ctx)
47
+ if (cached !== null) {
48
+ return cached
49
+ }
50
+
51
+ // 2. 解析为 AST
52
+ const ast = parseExpression(expr)
53
+
54
+ // 3. AST 白名单校验(传递 allowGlobals 和 maxNestingDepth 选项)
55
+ validateAST(ast, {
56
+ allowGlobals: mergedOptions.allowGlobals,
57
+ maxNestingDepth: mergedOptions.maxNestingDepth
58
+ })
59
+
60
+ // 4. 尝试使用编译缓存(简单表达式)
61
+ const compiled = getCompiledExpression(expr, ast)
62
+ if (compiled) {
63
+ const result = compiled(ctx)
64
+ // 提取依赖并缓存结果
65
+ const dependencies = extractDependencies(ast)
66
+ setCachedExpression(expr, result, dependencies, ctx)
67
+ return result
68
+ }
69
+
70
+ // 5. 复杂表达式,使用解释执行
71
+ const result = evaluateExpression(ast, ctx, mergedOptions)
72
+
73
+ // 6. 提取依赖并缓存
74
+ const dependencies = extractDependencies(ast)
75
+ setCachedExpression(expr, result, dependencies, ctx)
76
+
77
+ return result
78
+ } catch (error: unknown) {
79
+ if (error instanceof ExpressionError) {
80
+ throw error
81
+ }
82
+ const errorMessage = error instanceof Error ? error.message : String(error)
83
+ throw new ExpressionError(
84
+ expr,
85
+ `Expression evaluation failed: ${errorMessage}`,
86
+ ErrorCodes.EXPRESSION_EVALUATION_ERROR,
87
+ {
88
+ metadata: {
89
+ originalError: error instanceof Error ? error.name : 'Unknown',
90
+ stack: error instanceof Error ? error.stack?.split('\n').slice(0, 5) : undefined
91
+ }
92
+ }
93
+ )
94
+ }
95
+ }