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}