diff --git a/crates/fidc-core/src/platform_expr_strategy.rs b/crates/fidc-core/src/platform_expr_strategy.rs index e6d635d..ea4c6d1 100644 --- a/crates/fidc-core/src/platform_expr_strategy.rs +++ b/crates/fidc-core/src/platform_expr_strategy.rs @@ -1,5 +1,6 @@ use std::cell::RefCell; use std::collections::{BTreeMap, BTreeSet, HashMap}; +use std::sync::Arc; use chrono::{Datelike, Duration, NaiveDate, NaiveDateTime, NaiveTime}; use rhai::{AST, Dynamic, Engine, Map, Scope}; @@ -835,6 +836,11 @@ impl<'a> SelectiveExpressionScope<'a> { } } +struct ExpressionEvalPlan { + normalized: String, + identifiers: BTreeSet, +} + pub struct PlatformExprStrategy { config: PlatformExprStrategyConfig, engine: Engine, @@ -857,6 +863,7 @@ pub struct PlatformExprStrategy { /// 命中计数与未命中计数,便于在 unit test 中验证缓存生效;非生产指标。 cache_hits: RefCell, cache_misses: RefCell, + expression_plan_cache: RefCell>>, normalized_prelude: String, prelude_identifier_candidates: BTreeSet, prelude_declared_identifiers: BTreeSet, @@ -1177,6 +1184,7 @@ impl PlatformExprStrategy { compiled_cache: RefCell::new(HashMap::new()), cache_hits: RefCell::new(0), cache_misses: RefCell::new(0), + expression_plan_cache: RefCell::new(HashMap::new()), normalized_prelude, prelude_identifier_candidates, prelude_declared_identifiers, @@ -4453,8 +4461,9 @@ impl PlatformExprStrategy { stock: Option<&StockExpressionState>, position: Option<&PositionExpressionState>, ) -> Result { - let normalized_expr = Self::normalize_expr(expr); - let normalized_identifiers = Self::extract_identifier_candidates(&normalized_expr); + let expression_plan = self.expression_eval_plan(expr); + let normalized_expr = expression_plan.normalized.as_str(); + let normalized_identifiers = &expression_plan.identifiers; let prelude_identifiers = &self.prelude_identifier_candidates; let include_day_factors = normalized_identifiers.contains("day_factors") || normalized_identifiers.contains("day_factor") @@ -4481,27 +4490,30 @@ impl PlatformExprStrategy { let expanded_expr = self.expand_runtime_helpers(ctx, day, stock, &normalized_expr, &mut scope)?; if let Some(item) = stock { - let mut expanded_identifiers = Self::extract_identifier_candidates(&expanded_prelude); - expanded_identifiers.extend(Self::extract_identifier_candidates(&expanded_expr)); - for identifier in expanded_identifiers { + let factor_identifiers = normalized_identifiers.iter().chain( + prelude_identifiers + .iter() + .filter(|identifier| !normalized_identifiers.contains(*identifier)), + ); + for identifier in factor_identifiers { if Self::is_reserved_scope_name(identifier.as_str()) - || self.prelude_declared_identifiers.contains(&identifier) - || (!self.stock_extra_factor_identifiers.contains(&identifier) - && !item.extra_factors.contains_key(&identifier) - && !day.available_factor_names.contains(&identifier) - && !day.available_text_factor_names.contains(&identifier)) + || self.prelude_declared_identifiers.contains(identifier) + || (!self.stock_extra_factor_identifiers.contains(identifier) + && !item.extra_factors.contains_key(identifier) + && !day.available_factor_names.contains(identifier) + && !day.available_text_factor_names.contains(identifier)) { continue; } - if let Some(value) = item.extra_text_factors.get(&identifier) { - scope.push_dynamic(identifier, Dynamic::from(value.clone())); + if let Some(value) = item.extra_text_factors.get(identifier) { + scope.push_dynamic(identifier.clone(), Dynamic::from(value.clone())); } else { let value = item .extra_factors - .get(&identifier) + .get(identifier) .copied() .unwrap_or(f64::NAN); - scope.push_dynamic(identifier, Dynamic::from(value)); + scope.push_dynamic(identifier.clone(), Dynamic::from(value)); } } } @@ -4514,6 +4526,21 @@ impl PlatformExprStrategy { self.eval_with_cache(&mut scope, &script) } + fn expression_eval_plan(&self, expr: &str) -> Arc { + if let Some(plan) = self.expression_plan_cache.borrow().get(expr).cloned() { + return plan; + } + let normalized = Self::normalize_expr(expr); + let plan = Arc::new(ExpressionEvalPlan { + identifiers: Self::extract_identifier_candidates(&normalized), + normalized, + }); + self.expression_plan_cache + .borrow_mut() + .insert(expr.to_string(), Arc::clone(&plan)); + plan + } + fn normalize_expr(expr: &str) -> String { let expr = Self::normalize_runtime_field_aliases(expr.trim()); let expr = Self::normalize_python_numeric_division(&expr);