import { compileSync, type VmScript } from '@mirascript/mirascript'; import { LRUCache } from 'lru-cache'; import type { Evaluator } from './main.js'; /** 一般表达式 */ export interface EvalExpressionCache { /** * 源码 */ source: string; /** * 求值函数 */ func: VmScript; /** * 编译错误 */ error: undefined; } /** 编译错误表达式 */ export interface ErrorExpressionCache { /** * 源码 */ source: string; /** * 求值函数 */ func: undefined; /** * 编译错误 */ error: Error; } /** * 表达式缓存 */ export type ExpressionCache = ErrorExpressionCache | EvalExpressionCache; /** * 编译表达式 */ function compile(expression: string, template: boolean): ExpressionCache { const compiled = { source: expression, } as ExpressionCache; try { const script = compileSync(expression, { input_mode: template ? 'Template' : 'Script' }); compiled.func = script; } catch (ex) { compiled.error = ex as Error; } return compiled; } const kTemplateCache = Symbol('cloudpss.expression.templateCache'); const kExpressionCache = Symbol('cloudpss.expression.expressionCache'); /** 获取编译缓存 */ function getCache(evaluator: Evaluator | null, template: boolean): LRUCache | null { if (evaluator == null) return null; const key = template ? kTemplateCache : kExpressionCache; if (!(key in evaluator)) { const size = template ? evaluator.options.templateCacheSize : evaluator.options.expressionCacheSize; Object.defineProperty(evaluator, key, { value: new LRUCache({ max: size! || 50 }), }); } return (evaluator as Evaluator & Record>)[key]; } /** * 解析表达式 */ export function parse( evaluator: Evaluator | null, expression: string | VmScript, throws: boolean, template: boolean, ): ExpressionCache { if (typeof expression == 'function') { return { source: expression.source, func: expression, error: undefined }; } if (typeof expression != 'string') { throw new TypeError(`${String(expression)} is not a valid expression`); } const cache = getCache(evaluator, template); const cached = cache?.get(expression); if (cached != null) return cached; const complied = compile(expression, template); if (complied.error) { if (throws) throw complied.error; } else { cache?.set(expression, complied); } return complied; }