oxigdal_algorithms/dsl/mod.rs
1//! Advanced Raster Algebra Domain-Specific Language (DSL)
2//!
3//! This module provides a comprehensive DSL for raster algebra operations with:
4//!
5//! - **Full expression grammar** with variables, functions, and control flow
6//! - **Type inference** for type-safe operations
7//! - **Optimization passes** including constant folding and algebraic simplifications
8//! - **Built-in function library** with 40+ mathematical, statistical, and spatial functions
9//! - **Compile-time macros** for zero-cost abstractions
10//! - **Runtime parser** for dynamic expression evaluation
11//!
12//! # Quick Start
13//!
14//! ## Simple Expression
15//!
16//! ```
17//! use oxigdal_algorithms::dsl::RasterDsl;
18//! use oxigdal_core::buffer::RasterBuffer;
19//! use oxigdal_core::types::RasterDataType;
20//!
21//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
22//! // Create sample bands
23//! let nir = RasterBuffer::zeros(100, 100, RasterDataType::Float32);
24//! let red = RasterBuffer::zeros(100, 100, RasterDataType::Float32);
25//!
26//! // Calculate NDVI
27//! let dsl = RasterDsl::new();
28//! let result = dsl.execute("(B1 - B2) / (B1 + B2)", &[nir, red])?;
29//! # Ok(())
30//! # }
31//! ```
32//!
33//! ## Complex Program
34//!
35//! ```
36//! use oxigdal_algorithms::dsl::RasterDsl;
37//!
38//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
39//! let program = r#"
40//! let ndvi = (B8 - B4) / (B8 + B4);
41//! let evi = 2.5 * ((B8 - B4) / (B8 + 6*B4 - 7.5*B2 + 1));
42//!
43//! if ndvi > 0.6 && evi > 0.5 then
44//! 1.0
45//! else if ndvi > 0.3 then
46//! 0.5
47//! else
48//! 0.0
49//! "#;
50//!
51//! let dsl = RasterDsl::new();
52//! // let result = dsl.execute(program, bands)?;
53//! # Ok(())
54//! # }
55//! ```
56//!
57//! ## Built-in Functions
58//!
59//! The DSL includes a rich set of built-in functions:
60//!
61//! ### Mathematical
62//! - `sqrt`, `abs`, `floor`, `ceil`, `round`
63//! - `log`, `log10`, `log2`, `exp`
64//! - `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `atan2`
65//! - `pow`, `hypot`
66//!
67//! ### Statistical
68//! - `mean`, `median`, `mode`, `stddev`, `variance`
69//! - `sum`, `product`, `min`, `max`
70//! - `percentile`
71//!
72//! ### Spatial Filters
73//! - `gaussian(raster, sigma)` - Gaussian blur
74//! - `median_filter(raster, radius)` - Median filter
75//!
76//! ### Logical
77//! - `and`, `or`, `not`, `xor`
78//!
79//! ### Comparison
80//! - `eq`, `ne`, `lt`, `le`, `gt`, `ge`
81//!
82//! ### Utility
83//! - `clamp(value, min, max)` - Clamp to range
84//! - `select(condition, then, else)` - Conditional selection
85//!
86//! # Optimization
87//!
88//! The DSL includes several optimization levels:
89//!
90//! ```
91//! use oxigdal_algorithms::dsl::{RasterDsl, OptLevel};
92//!
93//! let mut dsl = RasterDsl::new();
94//! dsl.set_opt_level(OptLevel::Aggressive);
95//! ```
96//!
97//! - `None` - No optimization
98//! - `Basic` - Constant folding only
99//! - `Standard` - Basic + algebraic simplifications (default)
100//! - `Aggressive` - Standard + common subexpression elimination
101
102pub mod ast;
103pub mod compiler;
104pub mod functions;
105pub mod optimizer;
106pub mod parser;
107pub mod variables;
108
109#[cfg(feature = "dsl")]
110pub mod macro_support;
111
112use crate::error::Result;
113use oxigdal_core::buffer::RasterBuffer;
114
115pub use ast::{BinaryOp, Expr, Program, Statement, Type, UnaryOp};
116pub use compiler::CompiledProgram;
117pub use functions::FunctionRegistry;
118pub use optimizer::{OptLevel, Optimizer};
119pub use parser::{parse_expression, parse_program};
120pub use variables::{BandContext, Environment, Value};
121
122/// Main DSL interface
123pub struct RasterDsl {
124 optimizer: Optimizer,
125 func_registry: FunctionRegistry,
126}
127
128impl Default for RasterDsl {
129 fn default() -> Self {
130 Self::new()
131 }
132}
133
134impl RasterDsl {
135 /// Creates a new DSL instance with default settings
136 pub fn new() -> Self {
137 Self {
138 optimizer: Optimizer::new(OptLevel::Standard),
139 func_registry: FunctionRegistry::new(),
140 }
141 }
142
143 /// Sets the optimization level
144 pub fn set_opt_level(&mut self, level: OptLevel) {
145 self.optimizer = Optimizer::new(level);
146 }
147
148 /// Gets the current optimization level
149 pub fn opt_level(&self) -> OptLevel {
150 OptLevel::Standard // Return default, could be stored in optimizer
151 }
152
153 /// Parses and executes a DSL expression
154 ///
155 /// # Arguments
156 ///
157 /// * `expression` - The DSL expression or program to execute
158 /// * `bands` - Input raster bands (B1, B2, etc.)
159 ///
160 /// # Examples
161 ///
162 /// ```
163 /// use oxigdal_algorithms::dsl::RasterDsl;
164 /// use oxigdal_core::buffer::RasterBuffer;
165 /// use oxigdal_core::types::RasterDataType;
166 ///
167 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
168 /// let band1 = RasterBuffer::zeros(10, 10, RasterDataType::Float32);
169 /// let band2 = RasterBuffer::zeros(10, 10, RasterDataType::Float32);
170 ///
171 /// let dsl = RasterDsl::new();
172 /// let result = dsl.execute("B1 + B2", &[band1, band2])?;
173 /// # Ok(())
174 /// # }
175 /// ```
176 pub fn execute(&self, expression: &str, bands: &[RasterBuffer]) -> Result<RasterBuffer> {
177 // Try parsing as expression first
178 if let Ok(expr) = parse_expression(expression) {
179 let optimized = self.optimizer.optimize_expr(expr);
180 let program = Program {
181 statements: vec![Statement::Expr(Box::new(optimized))],
182 };
183 let compiled = CompiledProgram::new(program);
184 return compiled.execute(bands);
185 }
186
187 // Try parsing as program
188 let program = parse_program(expression)?;
189 let optimized = self.optimizer.optimize_program(program);
190 let compiled = CompiledProgram::new(optimized);
191 compiled.execute(bands)
192 }
193
194 /// Compiles a DSL expression to a reusable compiled program
195 pub fn compile(&self, expression: &str) -> Result<CompiledProgram> {
196 // Try parsing as expression first
197 if let Ok(expr) = parse_expression(expression) {
198 let optimized = self.optimizer.optimize_expr(expr);
199 let program = Program {
200 statements: vec![Statement::Expr(Box::new(optimized))],
201 };
202 return Ok(CompiledProgram::new(program));
203 }
204
205 // Try parsing as program
206 let program = parse_program(expression)?;
207 let optimized = self.optimizer.optimize_program(program);
208 Ok(CompiledProgram::new(optimized))
209 }
210
211 /// Gets the function registry
212 pub fn functions(&self) -> &FunctionRegistry {
213 &self.func_registry
214 }
215
216 /// Lists all available function names
217 pub fn list_functions(&self) -> Vec<&'static str> {
218 self.func_registry.function_names()
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 use super::*;
225 use oxigdal_core::types::RasterDataType;
226
227 #[test]
228 fn test_dsl_simple_expression() {
229 let band1 = RasterBuffer::zeros(10, 10, RasterDataType::Float32);
230 let band2 = RasterBuffer::zeros(10, 10, RasterDataType::Float32);
231
232 let dsl = RasterDsl::new();
233 let result = dsl.execute("B1 + B2", &[band1, band2]);
234 assert!(result.is_ok());
235 }
236
237 #[test]
238 fn test_dsl_ndvi() {
239 let nir = RasterBuffer::zeros(10, 10, RasterDataType::Float32);
240 let red = RasterBuffer::zeros(10, 10, RasterDataType::Float32);
241
242 let dsl = RasterDsl::new();
243 let result = dsl.execute("(B1 - B2) / (B1 + B2)", &[nir, red]);
244 assert!(result.is_ok());
245 }
246
247 #[test]
248 fn test_dsl_program() {
249 let bands = vec![
250 RasterBuffer::zeros(10, 10, RasterDataType::Float32),
251 RasterBuffer::zeros(10, 10, RasterDataType::Float32),
252 ];
253
254 let program = r#"
255 let ndvi = (B1 - B2) / (B1 + B2);
256 ndvi;
257 "#;
258
259 let dsl = RasterDsl::new();
260 let result = dsl.execute(program, &bands);
261 assert!(result.is_ok());
262 }
263
264 #[test]
265 fn test_dsl_optimization() {
266 let band = RasterBuffer::zeros(10, 10, RasterDataType::Float32);
267
268 let mut dsl = RasterDsl::new();
269 dsl.set_opt_level(OptLevel::Aggressive);
270
271 let result = dsl.execute("B1 + 0", &[band]);
272 assert!(result.is_ok());
273 }
274
275 #[test]
276 fn test_dsl_compile() {
277 let dsl = RasterDsl::new();
278 let compiled = dsl.compile("(B1 - B2) / (B1 + B2)");
279 assert!(compiled.is_ok());
280 }
281
282 #[test]
283 fn test_function_list() {
284 let dsl = RasterDsl::new();
285 let functions = dsl.list_functions();
286 assert!(!functions.is_empty());
287 assert!(functions.contains(&"sqrt"));
288 assert!(functions.contains(&"sin"));
289 }
290}