1use super::ast::{BinaryOp, Expr, Program, Statement, UnaryOp};
7use super::functions::FunctionRegistry;
8use super::variables::{BandContext, Environment, Value};
9use crate::error::{AlgorithmError, Result};
10use oxigdal_core::buffer::RasterBuffer;
11use oxigdal_core::types::RasterDataType;
12
13#[cfg(not(feature = "std"))]
14use alloc::{boxed::Box, string::String, vec::Vec};
15
16pub struct CompiledProgram {
18 program: Program,
19 func_registry: FunctionRegistry,
20}
21
22impl CompiledProgram {
23 pub fn new(program: Program) -> Self {
25 Self {
26 program,
27 func_registry: FunctionRegistry::new(),
28 }
29 }
30
31 pub fn execute(&self, bands: &[RasterBuffer]) -> Result<RasterBuffer> {
33 if bands.is_empty() {
34 return Err(AlgorithmError::EmptyInput {
35 operation: "execute",
36 });
37 }
38
39 let width = bands[0].width();
40 let height = bands[0].height();
41
42 for band in bands.iter().skip(1) {
44 if band.width() != width || band.height() != height {
45 return Err(AlgorithmError::InvalidDimensions {
46 message: "All bands must have same dimensions",
47 actual: band.width() as usize,
48 expected: width as usize,
49 });
50 }
51 }
52
53 let mut env = Environment::new();
54 let band_ctx = BandContext::new(bands);
55 let mut executor = Executor::new(&self.func_registry);
56
57 for stmt in &self.program.statements {
59 executor.execute_statement(stmt, &mut env, &band_ctx)?;
60 }
61
62 if let Some(Statement::Expr(expr)) = self.program.statements.last() {
64 let result = executor.evaluate_expr(expr, &env, &band_ctx)?;
65
66 match result {
67 Value::Raster(r) => Ok(*r),
68 Value::Number(n) => {
69 let mut raster = RasterBuffer::zeros(width, height, RasterDataType::Float32);
70 for y in 0..height {
71 for x in 0..width {
72 raster.set_pixel(x, y, n).map_err(AlgorithmError::Core)?;
73 }
74 }
75 Ok(raster)
76 }
77 Value::Bool(b) => {
78 let val = if b { 1.0 } else { 0.0 };
79 let mut raster = RasterBuffer::zeros(width, height, RasterDataType::Float32);
80 for y in 0..height {
81 for x in 0..width {
82 raster.set_pixel(x, y, val).map_err(AlgorithmError::Core)?;
83 }
84 }
85 Ok(raster)
86 }
87 _ => Err(AlgorithmError::InvalidParameter {
88 parameter: "result",
89 message: "Program must return a raster or scalar".to_string(),
90 }),
91 }
92 } else {
93 Err(AlgorithmError::InvalidParameter {
94 parameter: "program",
95 message: "Program has no expression to evaluate".to_string(),
96 })
97 }
98 }
99
100 pub fn execute_expr(&self, expr: &Expr, bands: &[RasterBuffer]) -> Result<RasterBuffer> {
102 if bands.is_empty() {
103 return Err(AlgorithmError::EmptyInput {
104 operation: "execute_expr",
105 });
106 }
107
108 let width = bands[0].width();
109 let height = bands[0].height();
110
111 let env = Environment::new();
112 let band_ctx = BandContext::new(bands);
113 let mut executor = Executor::new(&self.func_registry);
114
115 let result = executor.evaluate_expr(expr, &env, &band_ctx)?;
116
117 match result {
118 Value::Raster(r) => Ok(*r),
119 Value::Number(n) => {
120 let mut raster = RasterBuffer::zeros(width, height, RasterDataType::Float32);
121 for y in 0..height {
122 for x in 0..width {
123 raster.set_pixel(x, y, n).map_err(AlgorithmError::Core)?;
124 }
125 }
126 Ok(raster)
127 }
128 Value::Bool(b) => {
129 let val = if b { 1.0 } else { 0.0 };
130 let mut raster = RasterBuffer::zeros(width, height, RasterDataType::Float32);
131 for y in 0..height {
132 for x in 0..width {
133 raster.set_pixel(x, y, val).map_err(AlgorithmError::Core)?;
134 }
135 }
136 Ok(raster)
137 }
138 _ => Err(AlgorithmError::InvalidParameter {
139 parameter: "result",
140 message: "Expression must return a raster or scalar".to_string(),
141 }),
142 }
143 }
144}
145
146struct Executor<'a> {
148 func_registry: &'a FunctionRegistry,
149}
150
151impl<'a> Executor<'a> {
152 fn new(func_registry: &'a FunctionRegistry) -> Self {
153 Self { func_registry }
154 }
155
156 fn execute_statement(
157 &mut self,
158 stmt: &Statement,
159 env: &mut Environment,
160 band_ctx: &BandContext,
161 ) -> Result<()> {
162 match stmt {
163 Statement::VariableDecl { name, value } => {
164 let val = self.evaluate_expr(value, env, band_ctx)?;
165 env.define(name.clone(), val);
166 Ok(())
167 }
168 Statement::FunctionDecl { name, params, body } => {
169 let func_val = Value::Function {
170 params: params.clone(),
171 body: body.clone(),
172 env: env.clone(),
173 };
174 env.define(name.clone(), func_val);
175 Ok(())
176 }
177 Statement::Return(_) => Err(AlgorithmError::InvalidParameter {
178 parameter: "return",
179 message: "Return statements not supported in top-level".to_string(),
180 }),
181 Statement::Expr(expr) => {
182 let _ = self.evaluate_expr(expr, env, band_ctx)?;
183 Ok(())
184 }
185 }
186 }
187
188 fn evaluate_expr(
189 &mut self,
190 expr: &Expr,
191 env: &Environment,
192 band_ctx: &BandContext,
193 ) -> Result<Value> {
194 match expr {
195 Expr::Number(n) => Ok(Value::Number(*n)),
196 Expr::Band(b) => {
197 let band = band_ctx.get_band(*b)?;
198 Ok(Value::Raster(Box::new(band.clone())))
199 }
200 Expr::Variable(name) => env.lookup(name).cloned(),
201 Expr::Binary {
202 left, op, right, ..
203 } => self.evaluate_binary(left, *op, right, env, band_ctx),
204 Expr::Unary {
205 op, expr: inner, ..
206 } => self.evaluate_unary(*op, inner, env, band_ctx),
207 Expr::Call { name, args, .. } => self.evaluate_call(name, args, env, band_ctx),
208 Expr::Conditional {
209 condition,
210 then_expr,
211 else_expr,
212 ..
213 } => self.evaluate_conditional(condition, then_expr, else_expr, env, band_ctx),
214 Expr::Block {
215 statements, result, ..
216 } => self.evaluate_block(statements, result.as_deref(), env, band_ctx),
217 Expr::ForLoop {
218 var,
219 start,
220 end,
221 body,
222 ..
223 } => self.evaluate_for_loop(var, start, end, body, env, band_ctx),
224 }
225 }
226
227 fn evaluate_binary(
228 &mut self,
229 left: &Expr,
230 op: BinaryOp,
231 right: &Expr,
232 env: &Environment,
233 band_ctx: &BandContext,
234 ) -> Result<Value> {
235 let left_val = self.evaluate_expr(left, env, band_ctx)?;
236 let right_val = self.evaluate_expr(right, env, band_ctx)?;
237
238 match (left_val, right_val) {
239 (Value::Number(l), Value::Number(r)) => {
240 let result = match op {
241 BinaryOp::Add => l + r,
242 BinaryOp::Subtract => l - r,
243 BinaryOp::Multiply => l * r,
244 BinaryOp::Divide => {
245 if r.abs() < f64::EPSILON {
246 f64::NAN
247 } else {
248 l / r
249 }
250 }
251 BinaryOp::Modulo => l % r,
252 BinaryOp::Power => l.powf(r),
253 BinaryOp::Equal => return Ok(Value::Bool((l - r).abs() < f64::EPSILON)),
254 BinaryOp::NotEqual => return Ok(Value::Bool((l - r).abs() >= f64::EPSILON)),
255 BinaryOp::Less => return Ok(Value::Bool(l < r)),
256 BinaryOp::LessEqual => return Ok(Value::Bool(l <= r)),
257 BinaryOp::Greater => return Ok(Value::Bool(l > r)),
258 BinaryOp::GreaterEqual => return Ok(Value::Bool(l >= r)),
259 BinaryOp::And | BinaryOp::Or => {
260 return Err(AlgorithmError::InvalidParameter {
261 parameter: "operator",
262 message: "Logical operators require boolean operands".to_string(),
263 });
264 }
265 };
266 Ok(Value::Number(result))
267 }
268 (Value::Bool(l), Value::Bool(r)) => {
269 let result = match op {
270 BinaryOp::And => l && r,
271 BinaryOp::Or => l || r,
272 BinaryOp::Equal => l == r,
273 BinaryOp::NotEqual => l != r,
274 _ => {
275 return Err(AlgorithmError::InvalidParameter {
276 parameter: "operator",
277 message: format!("Operator {:?} not supported for booleans", op),
278 });
279 }
280 };
281 Ok(Value::Bool(result))
282 }
283 (Value::Raster(l), Value::Raster(r)) => self.evaluate_raster_binary(&l, op, &r),
284 (Value::Raster(l), Value::Number(r)) => {
285 self.evaluate_raster_scalar_binary(&l, op, r, false)
286 }
287 (Value::Number(l), Value::Raster(r)) => {
288 self.evaluate_raster_scalar_binary(&r, op, l, true)
289 }
290 _ => Err(AlgorithmError::InvalidParameter {
291 parameter: "operands",
292 message: "Incompatible operand types".to_string(),
293 }),
294 }
295 }
296
297 fn evaluate_raster_binary(
298 &self,
299 left: &RasterBuffer,
300 op: BinaryOp,
301 right: &RasterBuffer,
302 ) -> Result<Value> {
303 if left.width() != right.width() || left.height() != right.height() {
304 return Err(AlgorithmError::InvalidDimensions {
305 message: "Rasters must have same dimensions",
306 actual: right.width() as usize,
307 expected: left.width() as usize,
308 });
309 }
310
311 let mut result = RasterBuffer::zeros(left.width(), left.height(), left.data_type());
312
313 for y in 0..left.height() {
314 for x in 0..left.width() {
315 let l = left.get_pixel(x, y).map_err(AlgorithmError::Core)?;
316 let r = right.get_pixel(x, y).map_err(AlgorithmError::Core)?;
317
318 let val = match op {
319 BinaryOp::Add => l + r,
320 BinaryOp::Subtract => l - r,
321 BinaryOp::Multiply => l * r,
322 BinaryOp::Divide => {
323 if r.abs() < f64::EPSILON {
324 f64::NAN
325 } else {
326 l / r
327 }
328 }
329 BinaryOp::Modulo => l % r,
330 BinaryOp::Power => l.powf(r),
331 BinaryOp::Equal => {
332 if (l - r).abs() < f64::EPSILON {
333 1.0
334 } else {
335 0.0
336 }
337 }
338 BinaryOp::NotEqual => {
339 if (l - r).abs() >= f64::EPSILON {
340 1.0
341 } else {
342 0.0
343 }
344 }
345 BinaryOp::Less => {
346 if l < r {
347 1.0
348 } else {
349 0.0
350 }
351 }
352 BinaryOp::LessEqual => {
353 if l <= r {
354 1.0
355 } else {
356 0.0
357 }
358 }
359 BinaryOp::Greater => {
360 if l > r {
361 1.0
362 } else {
363 0.0
364 }
365 }
366 BinaryOp::GreaterEqual => {
367 if l >= r {
368 1.0
369 } else {
370 0.0
371 }
372 }
373 BinaryOp::And => {
374 let l_bool = l.abs() > f64::EPSILON;
376 let r_bool = r.abs() > f64::EPSILON;
377 if l_bool && r_bool { 1.0 } else { 0.0 }
378 }
379 BinaryOp::Or => {
380 let l_bool = l.abs() > f64::EPSILON;
382 let r_bool = r.abs() > f64::EPSILON;
383 if l_bool || r_bool { 1.0 } else { 0.0 }
384 }
385 };
386
387 result.set_pixel(x, y, val).map_err(AlgorithmError::Core)?;
388 }
389 }
390
391 Ok(Value::Raster(Box::new(result)))
392 }
393
394 fn evaluate_raster_scalar_binary(
395 &self,
396 raster: &RasterBuffer,
397 op: BinaryOp,
398 scalar: f64,
399 scalar_left: bool,
400 ) -> Result<Value> {
401 let mut result = RasterBuffer::zeros(raster.width(), raster.height(), raster.data_type());
402
403 for y in 0..raster.height() {
404 for x in 0..raster.width() {
405 let r = raster.get_pixel(x, y).map_err(AlgorithmError::Core)?;
406
407 let val = if scalar_left {
408 match op {
409 BinaryOp::Add => scalar + r,
410 BinaryOp::Subtract => scalar - r,
411 BinaryOp::Multiply => scalar * r,
412 BinaryOp::Divide => {
413 if r.abs() < f64::EPSILON {
414 f64::NAN
415 } else {
416 scalar / r
417 }
418 }
419 BinaryOp::Modulo => scalar % r,
420 BinaryOp::Power => scalar.powf(r),
421 BinaryOp::Equal => {
422 if (scalar - r).abs() < f64::EPSILON {
423 1.0
424 } else {
425 0.0
426 }
427 }
428 BinaryOp::NotEqual => {
429 if (scalar - r).abs() >= f64::EPSILON {
430 1.0
431 } else {
432 0.0
433 }
434 }
435 BinaryOp::Less => {
436 if scalar < r {
437 1.0
438 } else {
439 0.0
440 }
441 }
442 BinaryOp::LessEqual => {
443 if scalar <= r {
444 1.0
445 } else {
446 0.0
447 }
448 }
449 BinaryOp::Greater => {
450 if scalar > r {
451 1.0
452 } else {
453 0.0
454 }
455 }
456 BinaryOp::GreaterEqual => {
457 if scalar >= r {
458 1.0
459 } else {
460 0.0
461 }
462 }
463 BinaryOp::And | BinaryOp::Or => {
464 return Err(AlgorithmError::InvalidParameter {
465 parameter: "operator",
466 message: "Logical operators require boolean operands".to_string(),
467 });
468 }
469 }
470 } else {
471 match op {
472 BinaryOp::Add => r + scalar,
473 BinaryOp::Subtract => r - scalar,
474 BinaryOp::Multiply => r * scalar,
475 BinaryOp::Divide => {
476 if scalar.abs() < f64::EPSILON {
477 f64::NAN
478 } else {
479 r / scalar
480 }
481 }
482 BinaryOp::Modulo => r % scalar,
483 BinaryOp::Power => r.powf(scalar),
484 BinaryOp::Equal => {
485 if (r - scalar).abs() < f64::EPSILON {
486 1.0
487 } else {
488 0.0
489 }
490 }
491 BinaryOp::NotEqual => {
492 if (r - scalar).abs() >= f64::EPSILON {
493 1.0
494 } else {
495 0.0
496 }
497 }
498 BinaryOp::Less => {
499 if r < scalar {
500 1.0
501 } else {
502 0.0
503 }
504 }
505 BinaryOp::LessEqual => {
506 if r <= scalar {
507 1.0
508 } else {
509 0.0
510 }
511 }
512 BinaryOp::Greater => {
513 if r > scalar {
514 1.0
515 } else {
516 0.0
517 }
518 }
519 BinaryOp::GreaterEqual => {
520 if r >= scalar {
521 1.0
522 } else {
523 0.0
524 }
525 }
526 BinaryOp::And | BinaryOp::Or => {
527 return Err(AlgorithmError::InvalidParameter {
528 parameter: "operator",
529 message: "Logical operators require boolean operands".to_string(),
530 });
531 }
532 }
533 };
534
535 result.set_pixel(x, y, val).map_err(AlgorithmError::Core)?;
536 }
537 }
538
539 Ok(Value::Raster(Box::new(result)))
540 }
541
542 fn evaluate_unary(
543 &mut self,
544 op: UnaryOp,
545 expr: &Expr,
546 env: &Environment,
547 band_ctx: &BandContext,
548 ) -> Result<Value> {
549 let val = self.evaluate_expr(expr, env, band_ctx)?;
550
551 match val {
552 Value::Number(n) => {
553 let result = match op {
554 UnaryOp::Negate => -n,
555 UnaryOp::Plus => n,
556 UnaryOp::Not => {
557 return Err(AlgorithmError::InvalidParameter {
558 parameter: "operator",
559 message: "Not operator requires boolean".to_string(),
560 });
561 }
562 };
563 Ok(Value::Number(result))
564 }
565 Value::Bool(b) => match op {
566 UnaryOp::Not => Ok(Value::Bool(!b)),
567 _ => Err(AlgorithmError::InvalidParameter {
568 parameter: "operator",
569 message: "Operator not supported for booleans".to_string(),
570 }),
571 },
572 Value::Raster(raster) => {
573 let mut result =
574 RasterBuffer::zeros(raster.width(), raster.height(), raster.data_type());
575
576 for y in 0..raster.height() {
577 for x in 0..raster.width() {
578 let val = raster.get_pixel(x, y).map_err(AlgorithmError::Core)?;
579 let new_val = match op {
580 UnaryOp::Negate => -val,
581 UnaryOp::Plus => val,
582 UnaryOp::Not => {
583 return Err(AlgorithmError::InvalidParameter {
584 parameter: "operator",
585 message: "Not operator requires boolean operands".to_string(),
586 });
587 }
588 };
589 result
590 .set_pixel(x, y, new_val)
591 .map_err(AlgorithmError::Core)?;
592 }
593 }
594
595 Ok(Value::Raster(Box::new(result)))
596 }
597 _ => Err(AlgorithmError::InvalidParameter {
598 parameter: "operand",
599 message: "Incompatible operand type for unary operator".to_string(),
600 }),
601 }
602 }
603
604 fn evaluate_call(
605 &mut self,
606 name: &str,
607 args: &[Expr],
608 env: &Environment,
609 band_ctx: &BandContext,
610 ) -> Result<Value> {
611 if let Ok(func_val) = env.lookup(name) {
613 if let Value::Function {
614 params,
615 body,
616 env: func_env,
617 } = func_val
618 {
619 if params.len() != args.len() {
620 return Err(AlgorithmError::InvalidParameter {
621 parameter: "arguments",
622 message: format!("Expected {} arguments, got {}", params.len(), args.len()),
623 });
624 }
625
626 let mut new_env = Environment::with_parent(func_env.clone());
628 for (param, arg) in params.iter().zip(args.iter()) {
629 let arg_val = self.evaluate_expr(arg, env, band_ctx)?;
630 new_env.define(param.clone(), arg_val);
631 }
632
633 return self.evaluate_expr(body, &new_env, band_ctx);
634 }
635 }
636
637 if let Some((func, arity)) = self.func_registry.lookup(name) {
639 if arity > 0 && args.len() != arity {
640 return Err(AlgorithmError::InvalidParameter {
641 parameter: "arguments",
642 message: format!("Expected {arity} arguments, got {}", args.len()),
643 });
644 }
645
646 let arg_vals: Result<Vec<Value>> = args
647 .iter()
648 .map(|arg| self.evaluate_expr(arg, env, band_ctx))
649 .collect();
650
651 func(&arg_vals?)
652 } else {
653 Err(AlgorithmError::InvalidParameter {
654 parameter: "function",
655 message: format!("Unknown function: {name}"),
656 })
657 }
658 }
659
660 fn evaluate_conditional(
661 &mut self,
662 condition: &Expr,
663 then_expr: &Expr,
664 else_expr: &Expr,
665 env: &Environment,
666 band_ctx: &BandContext,
667 ) -> Result<Value> {
668 let cond_val = self.evaluate_expr(condition, env, band_ctx)?;
669
670 match cond_val {
671 Value::Bool(b) => {
672 if b {
673 self.evaluate_expr(then_expr, env, band_ctx)
674 } else {
675 self.evaluate_expr(else_expr, env, band_ctx)
676 }
677 }
678 Value::Number(n) => {
679 if n.abs() > f64::EPSILON {
680 self.evaluate_expr(then_expr, env, band_ctx)
681 } else {
682 self.evaluate_expr(else_expr, env, band_ctx)
683 }
684 }
685 Value::Raster(cond_raster) => {
686 let then_val = self.evaluate_expr(then_expr, env, band_ctx)?;
688 let else_val = self.evaluate_expr(else_expr, env, band_ctx)?;
689
690 let width = cond_raster.width();
691 let height = cond_raster.height();
692 let mut result = RasterBuffer::zeros(width, height, cond_raster.data_type());
693
694 for y in 0..height {
695 for x in 0..width {
696 let cond = cond_raster.get_pixel(x, y).map_err(AlgorithmError::Core)?;
697 let is_true = cond.abs() > f64::EPSILON;
698
699 let val = if is_true {
700 match &then_val {
701 Value::Raster(r) => {
702 r.get_pixel(x, y).map_err(AlgorithmError::Core)?
703 }
704 Value::Number(n) => *n,
705 Value::Bool(b) => {
706 if *b {
707 1.0
708 } else {
709 0.0
710 }
711 }
712 _ => {
713 return Err(AlgorithmError::InvalidParameter {
714 parameter: "then_expr",
715 message: "Then expression must be raster or scalar"
716 .to_string(),
717 });
718 }
719 }
720 } else {
721 match &else_val {
722 Value::Raster(r) => {
723 r.get_pixel(x, y).map_err(AlgorithmError::Core)?
724 }
725 Value::Number(n) => *n,
726 Value::Bool(b) => {
727 if *b {
728 1.0
729 } else {
730 0.0
731 }
732 }
733 _ => {
734 return Err(AlgorithmError::InvalidParameter {
735 parameter: "else_expr",
736 message: "Else expression must be raster or scalar"
737 .to_string(),
738 });
739 }
740 }
741 };
742
743 result.set_pixel(x, y, val).map_err(AlgorithmError::Core)?;
744 }
745 }
746
747 Ok(Value::Raster(Box::new(result)))
748 }
749 _ => Err(AlgorithmError::InvalidParameter {
750 parameter: "condition",
751 message: "Condition must be boolean, number, or raster".to_string(),
752 }),
753 }
754 }
755
756 fn evaluate_for_loop(
757 &mut self,
758 var: &str,
759 start: &Expr,
760 end: &Expr,
761 body: &Expr,
762 env: &Environment,
763 band_ctx: &BandContext,
764 ) -> Result<Value> {
765 let start_val = self.evaluate_expr(start, env, band_ctx)?.as_number()?;
766 let end_val = self.evaluate_expr(end, env, band_ctx)?.as_number()?;
767
768 const MAX_ITERATIONS: i64 = 1_000_000;
770 let start_i = start_val.floor() as i64;
771 let end_i = end_val.floor() as i64;
772 let iterations = (end_i - start_i).max(0);
773
774 if iterations > MAX_ITERATIONS {
775 return Err(AlgorithmError::InvalidParameter {
776 parameter: "for",
777 message: format!(
778 "For loop would execute {iterations} iterations (max {MAX_ITERATIONS})"
779 ),
780 });
781 }
782
783 let mut last_value = Value::Number(0.0);
785 let mut loop_env = Environment::with_parent(env.clone());
786
787 for i in start_i..end_i {
788 loop_env.define(var.to_string(), Value::Number(i as f64));
789 last_value = self.evaluate_expr(body, &loop_env, band_ctx)?;
790 }
791
792 Ok(last_value)
793 }
794
795 fn evaluate_block(
796 &mut self,
797 statements: &[Statement],
798 result: Option<&Expr>,
799 env: &Environment,
800 band_ctx: &BandContext,
801 ) -> Result<Value> {
802 let mut block_env = Environment::with_parent(env.clone());
803
804 for stmt in statements {
805 self.execute_statement(stmt, &mut block_env, band_ctx)?;
806 }
807
808 if let Some(expr) = result {
809 self.evaluate_expr(expr, &block_env, band_ctx)
810 } else {
811 Ok(Value::Number(0.0))
812 }
813 }
814}
815
816#[cfg(test)]
817mod tests {
818 use super::*;
819 use crate::dsl::parser::parse_expression;
820 use oxigdal_core::types::RasterDataType;
821
822 #[test]
823 fn test_compile_number() {
824 let expr = parse_expression("42").expect("Should parse");
825 let program = Program {
826 statements: vec![Statement::Expr(Box::new(expr))],
827 };
828 let compiled = CompiledProgram::new(program);
829
830 let bands = vec![RasterBuffer::zeros(10, 10, RasterDataType::Float32)];
831 let result = compiled.execute(&bands);
832 assert!(result.is_ok());
833 }
834
835 #[test]
836 fn test_compile_band() {
837 let expr = parse_expression("B1").expect("Should parse");
838 let program = Program {
839 statements: vec![Statement::Expr(Box::new(expr))],
840 };
841 let compiled = CompiledProgram::new(program);
842
843 let bands = vec![RasterBuffer::zeros(10, 10, RasterDataType::Float32)];
844 let result = compiled.execute(&bands);
845 assert!(result.is_ok());
846 }
847
848 #[test]
849 fn test_compile_arithmetic() {
850 let expr = parse_expression("B1 + B2").expect("Should parse");
851 let program = Program {
852 statements: vec![Statement::Expr(Box::new(expr))],
853 };
854 let compiled = CompiledProgram::new(program);
855
856 let bands = vec![
857 RasterBuffer::zeros(10, 10, RasterDataType::Float32),
858 RasterBuffer::zeros(10, 10, RasterDataType::Float32),
859 ];
860 let result = compiled.execute(&bands);
861 assert!(result.is_ok());
862 }
863}