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//! ### Selective Standard Library Loading (Recommended for DSL)
44//!
45//! For better performance, load only the stdlib modules you need:
46//!
47//! ```
48//! use aether::Aether;
49//!
50//! // Load only string and array utilities
51//! let mut engine = Aether::new()
52//!     .with_stdlib_string_utils()
53//!     .unwrap()
54//!     .with_stdlib_array_utils()
55//!     .unwrap();
56//!
57//! // Or load data structures
58//! let mut engine2 = Aether::new()
59//!     .with_stdlib_set()
60//!     .unwrap()
61//!     .with_stdlib_queue()
62//!     .unwrap()
63//!     .with_stdlib_stack()
64//!     .unwrap();
65//!
66//! // Available modules:
67//! // - with_stdlib_string_utils()
68//! // - with_stdlib_array_utils()
69//! // - with_stdlib_validation()
70//! // - with_stdlib_datetime()
71//! // - with_stdlib_testing()
72//! // - with_stdlib_set()
73//! // - with_stdlib_queue()
74//! // - with_stdlib_stack()
75//! // - with_stdlib_heap()
76//! // - with_stdlib_sorting()
77//! // - with_stdlib_json()
78//! // - with_stdlib_csv()
79//! // - with_stdlib_functional()
80//! // - with_stdlib_cli_utils()
81//! // - with_stdlib_text_template()
82//! // - with_stdlib_regex_utils()
83//! ```
84//!
85//! ## As a Standalone Language (Command-Line Tool)
86//!
87//! The `aether` command-line tool automatically enables all IO permissions,
88//! allowing scripts to freely use file and network operations:
89//!
90//! ```bash
91//! # All IO operations work in CLI mode
92//! aether script.aether
93//! ```
94
95pub mod ast;
96pub mod builtins;
97pub mod cache;
98pub mod environment;
99pub mod evaluator;
100pub mod lexer;
101pub mod optimizer;
102pub mod parser;
103pub mod stdlib;
104pub mod token;
105pub mod value;
106
107// FFI and language bindings
108pub mod ffi;
109
110#[cfg(target_arch = "wasm32")]
111pub mod wasm;
112
113// Re-export commonly used types
114pub use ast::{Expr, Program, Stmt};
115pub use builtins::{BuiltInRegistry, IOPermissions};
116pub use cache::{ASTCache, CacheStats};
117pub use environment::Environment;
118pub use evaluator::{EvalResult, Evaluator, RuntimeError};
119pub use lexer::Lexer;
120pub use optimizer::Optimizer;
121pub use parser::{ParseError, Parser};
122pub use token::Token;
123pub use value::Value;
124
125/// Main Aether engine struct
126pub struct Aether {
127    evaluator: Evaluator,
128    cache: ASTCache,
129    optimizer: Optimizer,
130}
131
132impl Aether {
133    /// Create a new Aether engine instance
134    ///
135    /// **For DSL embedding**: IO operations are disabled by default for security.
136    /// Use `with_permissions()` or `with_all_permissions()` to enable IO.
137    ///
138    /// **For CLI usage**: The command-line tool uses `with_all_permissions()` by default.
139    pub fn new() -> Self {
140        Self::with_permissions(IOPermissions::default())
141    }
142
143    /// Create a new Aether engine with custom IO permissions
144    pub fn with_permissions(permissions: IOPermissions) -> Self {
145        Aether {
146            evaluator: Evaluator::with_permissions(permissions),
147            cache: ASTCache::new(),
148            optimizer: Optimizer::new(),
149        }
150    }
151
152    /// Create a new Aether engine with all IO permissions enabled
153    pub fn with_all_permissions() -> Self {
154        Self::with_permissions(IOPermissions::allow_all())
155    }
156
157    /// Create a new Aether engine with standard library preloaded
158    ///
159    /// This creates an engine with all permissions and automatically loads
160    /// all standard library modules (string_utils, array_utils, validation, datetime, testing).
161    pub fn with_stdlib() -> Result<Self, String> {
162        let mut engine = Self::with_all_permissions();
163        stdlib::preload_stdlib(&mut engine)?;
164        Ok(engine)
165    }
166
167    /// Load a specific standard library module
168    ///
169    /// Available modules: "string_utils", "array_utils", "validation", "datetime", "testing"
170    pub fn load_stdlib_module(&mut self, module_name: &str) -> Result<(), String> {
171        if let Some(code) = stdlib::get_module(module_name) {
172            self.eval(code)?;
173            Ok(())
174        } else {
175            Err(format!("Unknown stdlib module: {}", module_name))
176        }
177    }
178
179    /// Load all standard library modules
180    pub fn load_all_stdlib(&mut self) -> Result<(), String> {
181        stdlib::preload_stdlib(self)
182    }
183
184    // ============================================================
185    // Chainable stdlib module loading methods
186    // ============================================================
187
188    /// Load string utilities module (chainable)
189    pub fn with_stdlib_string_utils(mut self) -> Result<Self, String> {
190        if let Some(code) = stdlib::get_module("string_utils") {
191            self.eval(code)?;
192        }
193        Ok(self)
194    }
195
196    /// Load array utilities module (chainable)
197    pub fn with_stdlib_array_utils(mut self) -> Result<Self, String> {
198        if let Some(code) = stdlib::get_module("array_utils") {
199            self.eval(code)?;
200        }
201        Ok(self)
202    }
203
204    /// Load validation module (chainable)
205    pub fn with_stdlib_validation(mut self) -> Result<Self, String> {
206        if let Some(code) = stdlib::get_module("validation") {
207            self.eval(code)?;
208        }
209        Ok(self)
210    }
211
212    /// Load datetime module (chainable)
213    pub fn with_stdlib_datetime(mut self) -> Result<Self, String> {
214        if let Some(code) = stdlib::get_module("datetime") {
215            self.eval(code)?;
216        }
217        Ok(self)
218    }
219
220    /// Load testing framework module (chainable)
221    pub fn with_stdlib_testing(mut self) -> Result<Self, String> {
222        if let Some(code) = stdlib::get_module("testing") {
223            self.eval(code)?;
224        }
225        Ok(self)
226    }
227
228    /// Load set data structure module (chainable)
229    pub fn with_stdlib_set(mut self) -> Result<Self, String> {
230        if let Some(code) = stdlib::get_module("set") {
231            self.eval(code)?;
232        }
233        Ok(self)
234    }
235
236    /// Load queue data structure module (chainable)
237    pub fn with_stdlib_queue(mut self) -> Result<Self, String> {
238        if let Some(code) = stdlib::get_module("queue") {
239            self.eval(code)?;
240        }
241        Ok(self)
242    }
243
244    /// Load stack data structure module (chainable)
245    pub fn with_stdlib_stack(mut self) -> Result<Self, String> {
246        if let Some(code) = stdlib::get_module("stack") {
247            self.eval(code)?;
248        }
249        Ok(self)
250    }
251
252    /// Load heap data structure module (chainable)
253    pub fn with_stdlib_heap(mut self) -> Result<Self, String> {
254        if let Some(code) = stdlib::get_module("heap") {
255            self.eval(code)?;
256        }
257        Ok(self)
258    }
259
260    /// Load sorting algorithms module (chainable)
261    pub fn with_stdlib_sorting(mut self) -> Result<Self, String> {
262        if let Some(code) = stdlib::get_module("sorting") {
263            self.eval(code)?;
264        }
265        Ok(self)
266    }
267
268    /// Load JSON processing module (chainable)
269    pub fn with_stdlib_json(mut self) -> Result<Self, String> {
270        if let Some(code) = stdlib::get_module("json") {
271            self.eval(code)?;
272        }
273        Ok(self)
274    }
275
276    /// Load CSV processing module (chainable)
277    pub fn with_stdlib_csv(mut self) -> Result<Self, String> {
278        if let Some(code) = stdlib::get_module("csv") {
279            self.eval(code)?;
280        }
281        Ok(self)
282    }
283
284    /// Load functional programming utilities module (chainable)
285    pub fn with_stdlib_functional(mut self) -> Result<Self, String> {
286        if let Some(code) = stdlib::get_module("functional") {
287            self.eval(code)?;
288        }
289        Ok(self)
290    }
291
292    /// Load CLI utilities module (chainable)
293    pub fn with_stdlib_cli_utils(mut self) -> Result<Self, String> {
294        if let Some(code) = stdlib::get_module("cli_utils") {
295            self.eval(code)?;
296        }
297        Ok(self)
298    }
299
300    /// Load text template engine module (chainable)
301    pub fn with_stdlib_text_template(mut self) -> Result<Self, String> {
302        if let Some(code) = stdlib::get_module("text_template") {
303            self.eval(code)?;
304        }
305        Ok(self)
306    }
307
308    /// Load regex utilities module (chainable)
309    pub fn with_stdlib_regex_utils(mut self) -> Result<Self, String> {
310        if let Some(code) = stdlib::get_module("regex_utils") {
311            self.eval(code)?;
312        }
313        Ok(self)
314    }
315
316    /// Evaluate Aether code and return the result
317    pub fn eval(&mut self, code: &str) -> Result<Value, String> {
318        // 尝试从缓存获取AST
319        let program = if let Some(cached_program) = self.cache.get(code) {
320            cached_program
321        } else {
322            // Parse the code
323            let mut parser = Parser::new(code);
324            let program = parser
325                .parse_program()
326                .map_err(|e| format!("Parse error: {}", e))?;
327
328            // 优化AST
329            let optimized = self.optimizer.optimize_program(&program);
330
331            // 将优化后的结果存入缓存
332            self.cache.insert(code, optimized.clone());
333            optimized
334        };
335
336        // Evaluate the program
337        self.evaluator
338            .eval_program(&program)
339            .map_err(|e| format!("Runtime error: {}", e))
340    }
341
342    /// 获取缓存统计信息
343    pub fn cache_stats(&self) -> CacheStats {
344        self.cache.stats()
345    }
346
347    /// 清空缓存
348    pub fn clear_cache(&mut self) {
349        self.cache.clear();
350    }
351
352    /// 设置优化选项
353    pub fn set_optimization(
354        &mut self,
355        constant_folding: bool,
356        dead_code: bool,
357        tail_recursion: bool,
358    ) {
359        self.optimizer.constant_folding = constant_folding;
360        self.optimizer.dead_code_elimination = dead_code;
361        self.optimizer.tail_recursion = tail_recursion;
362    }
363}
364
365impl Default for Aether {
366    fn default() -> Self {
367        Self::new()
368    }
369}
370
371#[cfg(test)]
372mod tests {
373    use super::*;
374
375    #[test]
376    fn test_aether_creation() {
377        let _engine = Aether::new();
378    }
379
380    #[test]
381    fn test_cache_usage() {
382        let mut engine = Aether::new();
383        let code = "Set X 10\nX";
384
385        // 第一次执行会解析
386        let result1 = engine.eval(code).unwrap();
387        assert_eq!(result1, Value::Number(10.0));
388
389        // 第二次执行应该使用缓存
390        let result2 = engine.eval(code).unwrap();
391        assert_eq!(result2, Value::Number(10.0));
392
393        // 检查缓存统计
394        let stats = engine.cache_stats();
395        assert_eq!(stats.hits, 1);
396        assert_eq!(stats.misses, 1);
397    }
398}