aether/api/
eval.rs

1use super::Aether;
2use crate::evaluator::ErrorReport;
3use crate::parser::Parser;
4use crate::value::Value;
5
6impl Aether {
7    /// 求值 Aether 代码并返回结果
8    pub fn eval(&mut self, code: &str) -> Result<Value, String> {
9        // 在开始新的顶级求值之前清除任何之前的调用栈帧。
10        self.evaluator.clear_call_stack();
11        self.evaluator.reset_step_counter();
12
13        // 尝试从缓存获取AST
14        let program = if let Some(cached_program) = self.cache.get(code) {
15            cached_program
16        } else {
17            // 解析代码
18            let mut parser = Parser::new(code);
19            let program = parser
20                .parse_program()
21                .map_err(|e| format!("Parse error: {}", e))?;
22
23            // 优化AST
24            let optimized = self.optimizer.optimize_program(&program);
25
26            // 将优化后的结果存入缓存
27            self.cache.insert(code, optimized.clone());
28            optimized
29        };
30
31        // 求值程序
32        self.evaluator
33            .eval_program(&program)
34            .map_err(|e| format!("Runtime error: {}", e))
35    }
36
37    /// 求值 Aether 代码并在失败时返回结构化的错误报告。
38    ///
39    /// 这适用于需要机器可读诊断的集成。
40    pub fn eval_report(&mut self, code: &str) -> Result<Value, ErrorReport> {
41        // 在开始新的顶级求值之前清除任何之前的调用栈帧。
42        self.evaluator.clear_call_stack();
43        self.evaluator.reset_step_counter();
44
45        // 首先尝试 AST 缓存
46        let program = if let Some(cached_program) = self.cache.get(code) {
47            cached_program
48        } else {
49            let mut parser = Parser::new(code);
50            let program = parser
51                .parse_program()
52                .map_err(|e| ErrorReport::parse_error(e.to_string()))?;
53
54            let optimized = self.optimizer.optimize_program(&program);
55            self.cache.insert(code, optimized.clone());
56            optimized
57        };
58
59        self.evaluator
60            .eval_program(&program)
61            .map_err(|e| e.to_error_report())
62    }
63
64    /// 配置用于 `Import/Export` 的模块解析器。
65    ///
66    /// 默认情况下(DSL 嵌入),解析器出于安全考虑被禁用。
67    pub fn set_module_resolver(&mut self, resolver: Box<dyn crate::module_system::ModuleResolver>) {
68        self.evaluator.set_module_resolver(resolver);
69    }
70
71    /// 推送用于解析相对导入的基础目录上下文。
72    ///
73    /// 这通常由基于文件的运行器(CLI)在调用 `eval()` 之前使用。
74    pub fn push_import_base(&mut self, module_id: String, base_dir: Option<std::path::PathBuf>) {
75        self.evaluator.push_import_base(module_id, base_dir);
76    }
77
78    /// 弹出最近的基础目录上下文。
79    pub fn pop_import_base(&mut self) {
80        self.evaluator.pop_import_base();
81    }
82
83    /// 从文件路径求值 Aether 脚本。
84    ///
85    /// 这是一个便利包装器,它:
86    /// - 读取文件
87    /// - 推送导入基础上下文(module_id = 规范路径;base_dir = 父目录)
88    /// - 求值代码
89    /// - 弹出导入基础上下文
90    ///
91    /// 注意:这**不会**启用任何模块解析器。为了 DSL 安全性,除非您明确调用 `set_module_resolver(...)`,否则模块加载保持禁用状态。
92    pub fn eval_file(&mut self, path: impl AsRef<std::path::Path>) -> Result<Value, String> {
93        let path = path.as_ref();
94
95        let code = std::fs::read_to_string(path).map_err(|e| format!("IO error: {}", e))?;
96
97        let canon = path.canonicalize().unwrap_or_else(|_| path.to_path_buf());
98        let base_dir = canon.parent().map(|p| p.to_path_buf());
99
100        self.push_import_base(canon.display().to_string(), base_dir);
101        let res = self.eval(&code);
102        self.pop_import_base();
103        res
104    }
105
106    /// 从文件路径求值 Aether 脚本,在失败时返回结构化的错误报告。
107    pub fn eval_file_report(
108        &mut self,
109        path: impl AsRef<std::path::Path>,
110    ) -> Result<Value, ErrorReport> {
111        let path = path.as_ref();
112
113        let code = std::fs::read_to_string(path)
114            .map_err(|e| ErrorReport::io_error(format!("IO error: {e}")))?;
115
116        let canon = path.canonicalize().unwrap_or_else(|_| path.to_path_buf());
117        let base_dir = canon.parent().map(|p| p.to_path_buf());
118
119        self.push_import_base(canon.display().to_string(), base_dir);
120        let res = self.eval_report(&code);
121        self.pop_import_base();
122        res
123    }
124
125    /// 从宿主应用程序设置全局变量,而不使用 `eval()`。
126    ///
127    /// 当您已经有 Rust 端数据并希望将其作为 `Value` 注入脚本环境时,这很有用。
128    pub fn set_global(&mut self, name: &str, value: Value) {
129        self.evaluator.set_global(name.to_string(), value);
130    }
131
132    /// 重置运行时环境(变量/函数),同时保持内置函数注册。
133    ///
134    /// 注意:这会清除通过 `eval()` 引入的任何内容(包括 stdlib 代码)。
135    pub fn reset_env(&mut self) {
136        self.evaluator.reset_env();
137    }
138
139    /// 在隔离的子作用域内运行闭包。
140    ///
141    /// 在闭包内注入或定义的所有变量/函数将在返回时被丢弃,而外部环境被保留。
142    ///
143    /// 这是为 "DSL 宿主" 场景设计的:注入 Rust 数据 + 加载每请求的
144    /// Aether 函数(例如从 DB)+ 运行脚本,而不跨请求污染。
145    pub fn with_isolated_scope<R>(
146        &mut self,
147        f: impl FnOnce(&mut Aether) -> Result<R, String>,
148    ) -> Result<R, String> {
149        let prev_env = self.evaluator.enter_child_scope();
150        let result = f(self);
151        self.evaluator.restore_env(prev_env);
152        result
153    }
154
155    /// 异步求值 Aether 代码(需要 "async" 特性)
156    ///
157    /// 这是围绕 `eval()` 的便利包装器,在后台任务中运行。
158    /// 用于将 Aether 集成到异步 Rust 应用程序中。
159    #[cfg(feature = "async")]
160    pub async fn eval_async(&mut self, code: &str) -> Result<Value, String> {
161        // 由于 Aether 内部使用 Rc (非 Send),我们在当前线程执行
162        // 但通过 tokio::task::yield_now() 让出执行权,避免阻塞事件循环
163        tokio::task::yield_now().await;
164        self.eval(code)
165    }
166}