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 /// Drain the in-memory TRACE buffer.
422 ///
423 /// This is designed for DSL-safe debugging: scripts call `TRACE(...)` to record
424 /// values, and the host application reads them out-of-band via this method.
425 pub fn take_trace(&mut self) -> Vec<String> {
426 self.evaluator.take_trace()
427 }
428
429 /// Clear the TRACE buffer without returning it.
430 pub fn clear_trace(&mut self) {
431 self.evaluator.clear_trace();
432 }
433
434 /// Evaluate Aether code asynchronously (requires "async" feature)
435 ///
436 /// This is a convenience wrapper around `eval()` that runs in a background task.
437 /// Useful for integrating Aether into async Rust applications.
438 ///
439 /// # Example
440 ///
441 /// ```no_run
442 /// use aether::Aether;
443 ///
444 /// #[tokio::main]
445 /// async fn main() {
446 /// let mut engine = Aether::new();
447 /// let result = engine.eval_async("Set X 10\n(X + 20)").await.unwrap();
448 /// println!("Result: {}", result);
449 /// }
450 /// ```
451 #[cfg(feature = "async")]
452 pub async fn eval_async(&mut self, code: &str) -> Result<Value, String> {
453 // 由于 Aether 内部使用 Rc (非 Send),我们在当前线程执行
454 // 但通过 tokio::task::yield_now() 让出执行权,避免阻塞事件循环
455 tokio::task::yield_now().await;
456 self.eval(code)
457 }
458
459 /// 获取缓存统计信息
460 pub fn cache_stats(&self) -> CacheStats {
461 self.cache.stats()
462 }
463
464 /// 清空缓存
465 pub fn clear_cache(&mut self) {
466 self.cache.clear();
467 }
468
469 /// 设置优化选项
470 pub fn set_optimization(
471 &mut self,
472 constant_folding: bool,
473 dead_code: bool,
474 tail_recursion: bool,
475 ) {
476 self.optimizer.constant_folding = constant_folding;
477 self.optimizer.dead_code_elimination = dead_code;
478 self.optimizer.tail_recursion = tail_recursion;
479 }
480}
481
482impl Default for Aether {
483 fn default() -> Self {
484 Self::new()
485 }
486}
487
488#[cfg(test)]
489mod tests {
490 use super::*;
491
492 #[test]
493 fn test_aether_creation() {
494 let _engine = Aether::new();
495 }
496
497 #[test]
498 fn test_cache_usage() {
499 let mut engine = Aether::new();
500 let code = "Set X 10\nX";
501
502 // 第一次执行会解析
503 let result1 = engine.eval(code).unwrap();
504 assert_eq!(result1, Value::Number(10.0));
505
506 // 第二次执行应该使用缓存
507 let result2 = engine.eval(code).unwrap();
508 assert_eq!(result2, Value::Number(10.0));
509
510 // 检查缓存统计
511 let stats = engine.cache_stats();
512 assert_eq!(stats.hits, 1);
513 assert_eq!(stats.misses, 1);
514 }
515}