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