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 optimizer;
179pub mod parser;
180pub mod stdlib;
181pub mod token;
182pub mod value;
183
184// FFI and language bindings
185pub mod ffi;
186
187#[cfg(target_arch = "wasm32")]
188pub mod wasm;
189
190pub mod pytranspile;
191
192// Re-export commonly used types
193pub use ast::{Expr, Program, Stmt};
194pub use builtins::{BuiltInRegistry, IOPermissions};
195pub use cache::{ASTCache, CacheStats};
196pub use environment::Environment;
197pub use evaluator::{EvalResult, Evaluator, RuntimeError};
198pub use lexer::Lexer;
199pub use optimizer::Optimizer;
200pub use parser::{ParseError, Parser};
201pub use token::Token;
202pub use value::Value;
203
204/// Main Aether engine struct
205pub struct Aether {
206    evaluator: Evaluator,
207    cache: ASTCache,
208    optimizer: Optimizer,
209}
210
211impl Aether {
212    /// Create a new Aether engine instance
213    ///
214    /// **For DSL embedding**: IO operations are disabled by default for security.
215    /// Use `with_permissions()` or `with_all_permissions()` to enable IO.
216    ///
217    /// **For CLI usage**: The command-line tool uses `with_all_permissions()` by default.
218    pub fn new() -> Self {
219        Self::with_permissions(IOPermissions::default())
220    }
221
222    /// Create a new Aether engine with custom IO permissions
223    pub fn with_permissions(permissions: IOPermissions) -> Self {
224        Aether {
225            evaluator: Evaluator::with_permissions(permissions),
226            cache: ASTCache::new(),
227            optimizer: Optimizer::new(),
228        }
229    }
230
231    /// Create a new Aether engine with all IO permissions enabled
232    pub fn with_all_permissions() -> Self {
233        Self::with_permissions(IOPermissions::allow_all())
234    }
235
236    /// Create a new Aether engine with standard library preloaded
237    ///
238    /// This creates an engine with all permissions and automatically loads
239    /// all standard library modules (string_utils, array_utils, validation, datetime, testing).
240    pub fn with_stdlib() -> Result<Self, String> {
241        let mut engine = Self::with_all_permissions();
242        stdlib::preload_stdlib(&mut engine)?;
243        Ok(engine)
244    }
245
246    /// Load a specific standard library module
247    ///
248    /// Available modules: "string_utils", "array_utils", "validation", "datetime", "testing"
249    pub fn load_stdlib_module(&mut self, module_name: &str) -> Result<(), String> {
250        if let Some(code) = stdlib::get_module(module_name) {
251            self.eval(code)?;
252            Ok(())
253        } else {
254            Err(format!("Unknown stdlib module: {}", module_name))
255        }
256    }
257
258    /// Load all standard library modules
259    pub fn load_all_stdlib(&mut self) -> Result<(), String> {
260        stdlib::preload_stdlib(self)
261    }
262
263    // ============================================================
264    // Chainable stdlib module loading methods
265    // ============================================================
266
267    /// Load string utilities module (chainable)
268    pub fn with_stdlib_string_utils(mut self) -> Result<Self, String> {
269        if let Some(code) = stdlib::get_module("string_utils") {
270            self.eval(code)?;
271        }
272        Ok(self)
273    }
274
275    /// Load array utilities module (chainable)
276    pub fn with_stdlib_array_utils(mut self) -> Result<Self, String> {
277        if let Some(code) = stdlib::get_module("array_utils") {
278            self.eval(code)?;
279        }
280        Ok(self)
281    }
282
283    /// Load validation module (chainable)
284    pub fn with_stdlib_validation(mut self) -> Result<Self, String> {
285        if let Some(code) = stdlib::get_module("validation") {
286            self.eval(code)?;
287        }
288        Ok(self)
289    }
290
291    /// Load datetime module (chainable)
292    pub fn with_stdlib_datetime(mut self) -> Result<Self, String> {
293        if let Some(code) = stdlib::get_module("datetime") {
294            self.eval(code)?;
295        }
296        Ok(self)
297    }
298
299    /// Load testing framework module (chainable)
300    pub fn with_stdlib_testing(mut self) -> Result<Self, String> {
301        if let Some(code) = stdlib::get_module("testing") {
302            self.eval(code)?;
303        }
304        Ok(self)
305    }
306
307    /// Load set data structure module (chainable)
308    pub fn with_stdlib_set(mut self) -> Result<Self, String> {
309        if let Some(code) = stdlib::get_module("set") {
310            self.eval(code)?;
311        }
312        Ok(self)
313    }
314
315    /// Load queue data structure module (chainable)
316    pub fn with_stdlib_queue(mut self) -> Result<Self, String> {
317        if let Some(code) = stdlib::get_module("queue") {
318            self.eval(code)?;
319        }
320        Ok(self)
321    }
322
323    /// Load stack data structure module (chainable)
324    pub fn with_stdlib_stack(mut self) -> Result<Self, String> {
325        if let Some(code) = stdlib::get_module("stack") {
326            self.eval(code)?;
327        }
328        Ok(self)
329    }
330
331    /// Load heap data structure module (chainable)
332    pub fn with_stdlib_heap(mut self) -> Result<Self, String> {
333        if let Some(code) = stdlib::get_module("heap") {
334            self.eval(code)?;
335        }
336        Ok(self)
337    }
338
339    /// Load sorting algorithms module (chainable)
340    pub fn with_stdlib_sorting(mut self) -> Result<Self, String> {
341        if let Some(code) = stdlib::get_module("sorting") {
342            self.eval(code)?;
343        }
344        Ok(self)
345    }
346
347    /// Load JSON processing module (chainable)
348    pub fn with_stdlib_json(mut self) -> Result<Self, String> {
349        if let Some(code) = stdlib::get_module("json") {
350            self.eval(code)?;
351        }
352        Ok(self)
353    }
354
355    /// Load CSV processing module (chainable)
356    pub fn with_stdlib_csv(mut self) -> Result<Self, String> {
357        if let Some(code) = stdlib::get_module("csv") {
358            self.eval(code)?;
359        }
360        Ok(self)
361    }
362
363    /// Load functional programming utilities module (chainable)
364    pub fn with_stdlib_functional(mut self) -> Result<Self, String> {
365        if let Some(code) = stdlib::get_module("functional") {
366            self.eval(code)?;
367        }
368        Ok(self)
369    }
370
371    /// Load CLI utilities module (chainable)
372    pub fn with_stdlib_cli_utils(mut self) -> Result<Self, String> {
373        if let Some(code) = stdlib::get_module("cli_utils") {
374            self.eval(code)?;
375        }
376        Ok(self)
377    }
378
379    /// Load text template engine module (chainable)
380    pub fn with_stdlib_text_template(mut self) -> Result<Self, String> {
381        if let Some(code) = stdlib::get_module("text_template") {
382            self.eval(code)?;
383        }
384        Ok(self)
385    }
386
387    /// Load regex utilities module (chainable)
388    pub fn with_stdlib_regex_utils(mut self) -> Result<Self, String> {
389        if let Some(code) = stdlib::get_module("regex_utils") {
390            self.eval(code)?;
391        }
392        Ok(self)
393    }
394
395    /// Evaluate Aether code and return the result
396    pub fn eval(&mut self, code: &str) -> Result<Value, String> {
397        // 尝试从缓存获取AST
398        let program = if let Some(cached_program) = self.cache.get(code) {
399            cached_program
400        } else {
401            // Parse the code
402            let mut parser = Parser::new(code);
403            let program = parser
404                .parse_program()
405                .map_err(|e| format!("Parse error: {}", e))?;
406
407            // 优化AST
408            let optimized = self.optimizer.optimize_program(&program);
409
410            // 将优化后的结果存入缓存
411            self.cache.insert(code, optimized.clone());
412            optimized
413        };
414
415        // Evaluate the program
416        self.evaluator
417            .eval_program(&program)
418            .map_err(|e| format!("Runtime error: {}", e))
419    }
420
421    /// Evaluate Aether code asynchronously (requires "async" feature)
422    ///
423    /// This is a convenience wrapper around `eval()` that runs in a background task.
424    /// Useful for integrating Aether into async Rust applications.
425    ///
426    /// # Example
427    ///
428    /// ```no_run
429    /// use aether::Aether;
430    ///
431    /// #[tokio::main]
432    /// async fn main() {
433    ///     let mut engine = Aether::new();
434    ///     let result = engine.eval_async("Set X 10\n(X + 20)").await.unwrap();
435    ///     println!("Result: {}", result);
436    /// }
437    /// ```
438    #[cfg(feature = "async")]
439    pub async fn eval_async(&mut self, code: &str) -> Result<Value, String> {
440        // 由于 Aether 内部使用 Rc (非 Send),我们在当前线程执行
441        // 但通过 tokio::task::yield_now() 让出执行权,避免阻塞事件循环
442        tokio::task::yield_now().await;
443        self.eval(code)
444    }
445
446    /// 获取缓存统计信息
447    pub fn cache_stats(&self) -> CacheStats {
448        self.cache.stats()
449    }
450
451    /// 清空缓存
452    pub fn clear_cache(&mut self) {
453        self.cache.clear();
454    }
455
456    /// 设置优化选项
457    pub fn set_optimization(
458        &mut self,
459        constant_folding: bool,
460        dead_code: bool,
461        tail_recursion: bool,
462    ) {
463        self.optimizer.constant_folding = constant_folding;
464        self.optimizer.dead_code_elimination = dead_code;
465        self.optimizer.tail_recursion = tail_recursion;
466    }
467}
468
469impl Default for Aether {
470    fn default() -> Self {
471        Self::new()
472    }
473}
474
475#[cfg(test)]
476mod tests {
477    use super::*;
478
479    #[test]
480    fn test_aether_creation() {
481        let _engine = Aether::new();
482    }
483
484    #[test]
485    fn test_cache_usage() {
486        let mut engine = Aether::new();
487        let code = "Set X 10\nX";
488
489        // 第一次执行会解析
490        let result1 = engine.eval(code).unwrap();
491        assert_eq!(result1, Value::Number(10.0));
492
493        // 第二次执行应该使用缓存
494        let result2 = engine.eval(code).unwrap();
495        assert_eq!(result2, Value::Number(10.0));
496
497        // 检查缓存统计
498        let stats = engine.cache_stats();
499        assert_eq!(stats.hits, 1);
500        assert_eq!(stats.misses, 1);
501    }
502}