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}