aether/
lib.rs

1//! Aether - A lightweight, embeddable domain-specific language
2//!
3//! This crate provides a complete implementation of the Aether language,
4//! including lexer, parser, evaluator, and standard library.
5//!
6//! # Quick Start
7//!
8//! ## As a DSL (Embedded in Your Application)
9//!
10//! When embedding Aether as a DSL, IO operations are **disabled by default** for security:
11//!
12//! ```
13//! use aether::Aether;
14//!
15//! // Default: IO disabled (safe for user scripts)
16//! let mut engine = Aether::new();
17//! let code = r#"
18//!     Set X 10
19//!     Set Y 20
20//!     (X + Y)
21//! "#;
22//!
23//! match engine.eval(code) {
24//!     Ok(result) => println!("Result: {}", result),
25//!     Err(e) => eprintln!("Error: {}", e),
26//! }
27//! ```
28//!
29//! Enable IO only when needed:
30//!
31//! ```
32//! use aether::{Aether, IOPermissions};
33//!
34//! // Enable only filesystem
35//! let mut perms = IOPermissions::default();
36//! perms.filesystem_enabled = true;
37//! let mut engine = Aether::with_permissions(perms);
38//!
39//! // Or enable all IO
40//! let mut engine = Aether::with_all_permissions();
41//! ```
42//!
43//! ## High-Performance Engine Modes (New!)
44//!
45//! For **high-frequency, large-scale DSL execution**, Aether provides three optimized engine modes:
46//!
47//! ### 1. GlobalEngine - Global Singleton (Best for Single-Thread)
48//!
49//! ```rust
50//! use aether::engine::GlobalEngine;
51//!
52//! // Execute with isolated environment (variables cleared each time)
53//! let result = GlobalEngine::eval_isolated("Set X 10\n(X + 20)").unwrap();
54//! println!("Result: {}", result);
55//!
56//! // Benefits:
57//! // - ✅ Maximum performance (engine created only once)
58//! // - ✅ AST cache accumulates (up to 142x speedup!)
59//! // - ✅ Environment isolation (variables cleared between calls)
60//! // - ⚠️ Single-threaded (uses Mutex)
61//! ```
62//!
63//! ### 2. EnginePool - Engine Pool (Best for Multi-Thread)
64//!
65//! ```rust
66//! use aether::engine::EnginePool;
67//! use std::thread;
68//!
69//! // Create pool once (size = 2-4x CPU cores recommended)
70//! let pool = EnginePool::new(8);
71//!
72//! // Use across threads
73//! let handles: Vec<_> = (0..4).map(|i| {
74//!     let pool = pool.clone();
75//!     thread::spawn(move || {
76//!         let mut engine = pool.acquire(); // Auto-acquire
77//!         let code = format!("Set X {}\n(X * 2)", i);
78//!         engine.eval(&code)
79//!     }) // Auto-return on scope exit
80//! }).collect();
81//!
82//! // Benefits:
83//! // - ✅ Multi-thread safe (lock-free queue)
84//! // - ✅ RAII pattern (auto-return to pool)
85//! // - ✅ Environment isolation (cleared on acquire)
86//! // - ✅ AST cache per engine
87//! ```
88//!
89//! ### 3. ScopedEngine - Closure Style (Best for Simplicity)
90//!
91//! ```rust
92//! use aether::engine::ScopedEngine;
93//!
94//! // Closure style (like Py3o)
95//! let result = ScopedEngine::with(|engine| {
96//!     engine.eval("Set X 10")?;
97//!     engine.eval("(X + 20)")
98//! }).unwrap();
99//!
100//! // Or simplified version
101//! let result = ScopedEngine::eval("Set X 10\n(X + 20)").unwrap();
102//!
103//! // Benefits:
104//! // - ✅ Complete isolation (new engine each time)
105//! // - ✅ Clean API (auto lifetime management)
106//! // - ⚠️ Lower performance (no cache reuse)
107//! ```
108//!
109//! ### Mode Comparison
110//!
111//! | Feature | GlobalEngine | EnginePool | ScopedEngine |
112//! |---------|-------------|------------|--------------|
113//! | Performance | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ |
114//! | Multi-thread | ❌ | ✅ | ✅ |
115//! | Isolation | ✅ | ✅ | ✅ |
116//! | AST Cache | ✅ | ✅ | ❌ |
117//! | Use Case | Single-thread high-freq | Multi-thread | Occasional |
118//!
119//! ### Selective Standard Library Loading (Recommended for DSL)
120//!
121//! For better performance, load only the stdlib modules you need:
122//!
123//! ```
124//! use aether::Aether;
125//!
126//! // Load only string and array utilities
127//! let mut engine = Aether::new()
128//!     .with_stdlib_string_utils()
129//!     .unwrap()
130//!     .with_stdlib_array_utils()
131//!     .unwrap();
132//!
133//! // Or load data structures
134//! let mut engine2 = Aether::new()
135//!     .with_stdlib_set()
136//!     .unwrap()
137//!     .with_stdlib_queue()
138//!     .unwrap()
139//!     .with_stdlib_stack()
140//!     .unwrap();
141//!
142//! // Available modules:
143//! // - with_stdlib_string_utils()
144//! // - with_stdlib_array_utils()
145//! // - with_stdlib_validation()
146//! // - with_stdlib_datetime()
147//! // - with_stdlib_testing()
148//! // - with_stdlib_set()
149//! // - with_stdlib_queue()
150//! // - with_stdlib_stack()
151//! // - with_stdlib_heap()
152//! // - with_stdlib_sorting()
153//! // - with_stdlib_json()
154//! // - with_stdlib_csv()
155//! // - with_stdlib_functional()
156//! // - with_stdlib_cli_utils()
157//! // - with_stdlib_text_template()
158//! // - with_stdlib_regex_utils()
159//! ```
160//!
161//! ## As a Standalone Language (Command-Line Tool)
162//!
163//! The `aether` command-line tool automatically enables all IO permissions,
164//! allowing scripts to freely use file and network operations:
165//!
166//! ```bash
167//! # All IO operations work in CLI mode
168//! aether script.aether
169//! ```
170
171pub mod ast;
172pub mod builtins;
173pub mod cache;
174pub mod engine;
175pub mod environment;
176pub mod evaluator;
177pub mod lexer;
178pub mod module_system;
179pub mod optimizer;
180pub mod parser;
181pub mod stdlib;
182pub mod token;
183pub mod value;
184
185// FFI and language bindings
186pub mod ffi;
187
188#[cfg(target_arch = "wasm32")]
189pub mod wasm;
190
191pub mod pytranspile;
192
193// Re-export commonly used types
194pub use ast::{Expr, Program, Stmt};
195pub use builtins::{BuiltInRegistry, IOPermissions};
196pub use cache::{ASTCache, CacheStats};
197pub use environment::Environment;
198pub use evaluator::{EvalResult, Evaluator, RuntimeError};
199pub use lexer::Lexer;
200pub use module_system::{DisabledModuleResolver, FileSystemModuleResolver, ModuleResolver};
201pub use optimizer::Optimizer;
202pub use parser::{ParseError, Parser};
203pub use token::Token;
204pub use value::Value;
205
206/// Main Aether engine struct
207pub struct Aether {
208    evaluator: Evaluator,
209    cache: ASTCache,
210    optimizer: Optimizer,
211}
212
213impl Aether {
214    /// Create a new Aether engine instance
215    ///
216    /// **For DSL embedding**: IO operations are disabled by default for security.
217    /// Use `with_permissions()` or `with_all_permissions()` to enable IO.
218    ///
219    /// **For CLI usage**: The command-line tool uses `with_all_permissions()` by default.
220    pub fn new() -> Self {
221        Self::with_permissions(IOPermissions::default())
222    }
223
224    /// Create a new Aether engine with custom IO permissions
225    pub fn with_permissions(permissions: IOPermissions) -> Self {
226        Aether {
227            evaluator: Evaluator::with_permissions(permissions),
228            cache: ASTCache::new(),
229            optimizer: Optimizer::new(),
230        }
231    }
232
233    /// Create a new Aether engine with all IO permissions enabled
234    pub fn with_all_permissions() -> Self {
235        Self::with_permissions(IOPermissions::allow_all())
236    }
237
238    /// Create a new Aether engine with standard library preloaded
239    ///
240    /// This creates an engine with all permissions and automatically loads
241    /// all standard library modules (string_utils, array_utils, validation, datetime, testing).
242    pub fn with_stdlib() -> Result<Self, String> {
243        let mut engine = Self::with_all_permissions();
244        stdlib::preload_stdlib(&mut engine)?;
245        Ok(engine)
246    }
247
248    /// Load a specific standard library module
249    ///
250    /// Available modules: "string_utils", "array_utils", "validation", "datetime", "testing"
251    pub fn load_stdlib_module(&mut self, module_name: &str) -> Result<(), String> {
252        if let Some(code) = stdlib::get_module(module_name) {
253            self.eval(code)?;
254            Ok(())
255        } else {
256            Err(format!("Unknown stdlib module: {}", module_name))
257        }
258    }
259
260    /// Load all standard library modules
261    pub fn load_all_stdlib(&mut self) -> Result<(), String> {
262        stdlib::preload_stdlib(self)
263    }
264
265    // ============================================================
266    // Chainable stdlib module loading methods
267    // ============================================================
268
269    /// Load string utilities module (chainable)
270    pub fn with_stdlib_string_utils(mut self) -> Result<Self, String> {
271        if let Some(code) = stdlib::get_module("string_utils") {
272            self.eval(code)?;
273        }
274        Ok(self)
275    }
276
277    /// Load array utilities module (chainable)
278    pub fn with_stdlib_array_utils(mut self) -> Result<Self, String> {
279        if let Some(code) = stdlib::get_module("array_utils") {
280            self.eval(code)?;
281        }
282        Ok(self)
283    }
284
285    /// Load validation module (chainable)
286    pub fn with_stdlib_validation(mut self) -> Result<Self, String> {
287        if let Some(code) = stdlib::get_module("validation") {
288            self.eval(code)?;
289        }
290        Ok(self)
291    }
292
293    /// Load datetime module (chainable)
294    pub fn with_stdlib_datetime(mut self) -> Result<Self, String> {
295        if let Some(code) = stdlib::get_module("datetime") {
296            self.eval(code)?;
297        }
298        Ok(self)
299    }
300
301    /// Load testing framework module (chainable)
302    pub fn with_stdlib_testing(mut self) -> Result<Self, String> {
303        if let Some(code) = stdlib::get_module("testing") {
304            self.eval(code)?;
305        }
306        Ok(self)
307    }
308
309    /// Load set data structure module (chainable)
310    pub fn with_stdlib_set(mut self) -> Result<Self, String> {
311        if let Some(code) = stdlib::get_module("set") {
312            self.eval(code)?;
313        }
314        Ok(self)
315    }
316
317    /// Load queue data structure module (chainable)
318    pub fn with_stdlib_queue(mut self) -> Result<Self, String> {
319        if let Some(code) = stdlib::get_module("queue") {
320            self.eval(code)?;
321        }
322        Ok(self)
323    }
324
325    /// Load stack data structure module (chainable)
326    pub fn with_stdlib_stack(mut self) -> Result<Self, String> {
327        if let Some(code) = stdlib::get_module("stack") {
328            self.eval(code)?;
329        }
330        Ok(self)
331    }
332
333    /// Load heap data structure module (chainable)
334    pub fn with_stdlib_heap(mut self) -> Result<Self, String> {
335        if let Some(code) = stdlib::get_module("heap") {
336            self.eval(code)?;
337        }
338        Ok(self)
339    }
340
341    /// Load sorting algorithms module (chainable)
342    pub fn with_stdlib_sorting(mut self) -> Result<Self, String> {
343        if let Some(code) = stdlib::get_module("sorting") {
344            self.eval(code)?;
345        }
346        Ok(self)
347    }
348
349    /// Load JSON processing module (chainable)
350    pub fn with_stdlib_json(mut self) -> Result<Self, String> {
351        if let Some(code) = stdlib::get_module("json") {
352            self.eval(code)?;
353        }
354        Ok(self)
355    }
356
357    /// Load CSV processing module (chainable)
358    pub fn with_stdlib_csv(mut self) -> Result<Self, String> {
359        if let Some(code) = stdlib::get_module("csv") {
360            self.eval(code)?;
361        }
362        Ok(self)
363    }
364
365    /// Load functional programming utilities module (chainable)
366    pub fn with_stdlib_functional(mut self) -> Result<Self, String> {
367        if let Some(code) = stdlib::get_module("functional") {
368            self.eval(code)?;
369        }
370        Ok(self)
371    }
372
373    /// Load CLI utilities module (chainable)
374    pub fn with_stdlib_cli_utils(mut self) -> Result<Self, String> {
375        if let Some(code) = stdlib::get_module("cli_utils") {
376            self.eval(code)?;
377        }
378        Ok(self)
379    }
380
381    /// Load text template engine module (chainable)
382    pub fn with_stdlib_text_template(mut self) -> Result<Self, String> {
383        if let Some(code) = stdlib::get_module("text_template") {
384            self.eval(code)?;
385        }
386        Ok(self)
387    }
388
389    /// Load regex utilities module (chainable)
390    pub fn with_stdlib_regex_utils(mut self) -> Result<Self, String> {
391        if let Some(code) = stdlib::get_module("regex_utils") {
392            self.eval(code)?;
393        }
394        Ok(self)
395    }
396
397    /// Evaluate Aether code and return the result
398    pub fn eval(&mut self, code: &str) -> Result<Value, String> {
399        // 尝试从缓存获取AST
400        let program = if let Some(cached_program) = self.cache.get(code) {
401            cached_program
402        } else {
403            // Parse the code
404            let mut parser = Parser::new(code);
405            let program = parser
406                .parse_program()
407                .map_err(|e| format!("Parse error: {}", e))?;
408
409            // 优化AST
410            let optimized = self.optimizer.optimize_program(&program);
411
412            // 将优化后的结果存入缓存
413            self.cache.insert(code, optimized.clone());
414            optimized
415        };
416
417        // Evaluate the program
418        self.evaluator
419            .eval_program(&program)
420            .map_err(|e| format!("Runtime error: {}", e))
421    }
422
423    /// Drain the in-memory TRACE buffer.
424    ///
425    /// This is designed for DSL-safe debugging: scripts call `TRACE(...)` to record
426    /// values, and the host application reads them out-of-band via this method.
427    pub fn take_trace(&mut self) -> Vec<String> {
428        self.evaluator.take_trace()
429    }
430
431    /// Clear the TRACE buffer without returning it.
432    pub fn clear_trace(&mut self) {
433        self.evaluator.clear_trace();
434    }
435
436    /// Configure the module resolver used for `Import/Export`.
437    ///
438    /// By default (DSL embedding), the resolver is disabled for safety.
439    pub fn set_module_resolver(&mut self, resolver: Box<dyn crate::module_system::ModuleResolver>) {
440        self.evaluator.set_module_resolver(resolver);
441    }
442
443    /// Push a base directory context for resolving relative imports.
444    ///
445    /// This is typically used by a file-based runner (CLI) before calling `eval()`.
446    pub fn push_import_base(&mut self, module_id: String, base_dir: Option<std::path::PathBuf>) {
447        self.evaluator.push_import_base(module_id, base_dir);
448    }
449
450    /// Pop the most recent base directory context.
451    pub fn pop_import_base(&mut self) {
452        self.evaluator.pop_import_base();
453    }
454
455    /// Evaluate an Aether script from a file path.
456    ///
457    /// This is a convenience wrapper that:
458    /// - reads the file
459    /// - pushes an import base context (module_id = canonical path; base_dir = parent dir)
460    /// - evaluates the code
461    /// - pops the import base context
462    ///
463    /// Note: this does **not** enable any module resolver. For DSL safety, module loading
464    /// remains disabled unless you explicitly call `set_module_resolver(...)`.
465    pub fn eval_file(&mut self, path: impl AsRef<std::path::Path>) -> Result<Value, String> {
466        let path = path.as_ref();
467
468        let code = std::fs::read_to_string(path).map_err(|e| format!("IO error: {}", e))?;
469
470        let canon = path.canonicalize().unwrap_or_else(|_| path.to_path_buf());
471        let base_dir = canon.parent().map(|p| p.to_path_buf());
472
473        self.push_import_base(canon.display().to_string(), base_dir);
474        let res = self.eval(&code);
475        self.pop_import_base();
476        res
477    }
478
479    /// Set a global variable from the host application without using `eval()`.
480    ///
481    /// This is useful when you already have Rust-side data and want to inject it
482    /// as `Value` into the script environment.
483    pub fn set_global(&mut self, name: &str, value: Value) {
484        self.evaluator.set_global(name.to_string(), value);
485    }
486
487    /// Reset the runtime environment (variables/functions) while keeping built-ins registered.
488    ///
489    /// Note: this clears anything that was introduced via `eval()` (including stdlib code).
490    pub fn reset_env(&mut self) {
491        self.evaluator.reset_env();
492    }
493
494    /// Run a closure inside an isolated child scope.
495    ///
496    /// All variables/functions you inject or define inside the closure will be dropped
497    /// when it returns, while the outer environment is preserved.
498    ///
499    /// This is designed for the "DSL host" scenario: inject Rust data + load per-request
500    /// Aether functions (e.g. from DB) + run the script, without cross-request pollution.
501    pub fn with_isolated_scope<R>(
502        &mut self,
503        f: impl FnOnce(&mut Aether) -> Result<R, String>,
504    ) -> Result<R, String> {
505        let prev_env = self.evaluator.enter_child_scope();
506        let result = f(self);
507        self.evaluator.restore_env(prev_env);
508        result
509    }
510
511    /// Evaluate Aether code asynchronously (requires "async" feature)
512    ///
513    /// This is a convenience wrapper around `eval()` that runs in a background task.
514    /// Useful for integrating Aether into async Rust applications.
515    ///
516    /// # Example
517    ///
518    /// ```no_run
519    /// use aether::Aether;
520    ///
521    /// #[tokio::main]
522    /// async fn main() {
523    ///     let mut engine = Aether::new();
524    ///     let result = engine.eval_async("Set X 10\n(X + 20)").await.unwrap();
525    ///     println!("Result: {}", result);
526    /// }
527    /// ```
528    #[cfg(feature = "async")]
529    pub async fn eval_async(&mut self, code: &str) -> Result<Value, String> {
530        // 由于 Aether 内部使用 Rc (非 Send),我们在当前线程执行
531        // 但通过 tokio::task::yield_now() 让出执行权,避免阻塞事件循环
532        tokio::task::yield_now().await;
533        self.eval(code)
534    }
535
536    /// 获取缓存统计信息
537    pub fn cache_stats(&self) -> CacheStats {
538        self.cache.stats()
539    }
540
541    /// 清空缓存
542    pub fn clear_cache(&mut self) {
543        self.cache.clear();
544    }
545
546    /// 设置优化选项
547    pub fn set_optimization(
548        &mut self,
549        constant_folding: bool,
550        dead_code: bool,
551        tail_recursion: bool,
552    ) {
553        self.optimizer.constant_folding = constant_folding;
554        self.optimizer.dead_code_elimination = dead_code;
555        self.optimizer.tail_recursion = tail_recursion;
556    }
557}
558
559impl Default for Aether {
560    fn default() -> Self {
561        Self::new()
562    }
563}
564
565#[cfg(test)]
566mod tests {
567    use super::*;
568
569    #[test]
570    fn test_aether_creation() {
571        let _engine = Aether::new();
572    }
573
574    #[test]
575    fn test_cache_usage() {
576        let mut engine = Aether::new();
577        let code = "Set X 10\nX";
578
579        // 第一次执行会解析
580        let result1 = engine.eval(code).unwrap();
581        assert_eq!(result1, Value::Number(10.0));
582
583        // 第二次执行应该使用缓存
584        let result2 = engine.eval(code).unwrap();
585        assert_eq!(result2, Value::Number(10.0));
586
587        // 检查缓存统计
588        let stats = engine.cache_stats();
589        assert_eq!(stats.hits, 1);
590        assert_eq!(stats.misses, 1);
591    }
592}