Skip to main content

oxigdal_algorithms/dsl/
parser.rs

1//! Parser for Raster Algebra DSL
2//!
3//! This module uses Pest to parse DSL text into an AST.
4
5// Parser derive macro generates items that don't have documentation
6#![allow(missing_docs)]
7
8use super::ast::{BinaryOp, Expr, Program, Statement, Type, UnaryOp};
9use crate::error::{AlgorithmError, Result};
10use pest::Parser;
11use pest_derive::Parser;
12
13#[cfg(not(feature = "std"))]
14use alloc::{boxed::Box, string::String, vec::Vec};
15
16#[derive(Parser)]
17#[grammar = "dsl/grammar.pest"]
18struct RasterParser;
19
20/// Parses a DSL program from text
21pub fn parse_program(input: &str) -> Result<Program> {
22    let pairs = RasterParser::parse(Rule::program, input).map_err(|e| {
23        AlgorithmError::InvalidParameter {
24            parameter: "dsl",
25            message: format!("Parse error: {e}"),
26        }
27    })?;
28
29    let mut statements = Vec::new();
30
31    for pair in pairs {
32        match pair.as_rule() {
33            Rule::program => {
34                for inner in pair.into_inner() {
35                    if inner.as_rule() == Rule::statement {
36                        statements.push(parse_statement(inner)?);
37                    }
38                }
39            }
40            Rule::EOI => {}
41            _ => {
42                return Err(AlgorithmError::InvalidParameter {
43                    parameter: "dsl",
44                    message: format!("Unexpected rule: {:?}", pair.as_rule()),
45                });
46            }
47        }
48    }
49
50    Ok(Program { statements })
51}
52
53/// Parses a single expression from text
54pub fn parse_expression(input: &str) -> Result<Expr> {
55    let mut full_input = String::from(input);
56    if !full_input.ends_with(';') {
57        full_input.push(';');
58    }
59
60    let pairs = RasterParser::parse(Rule::program, &full_input).map_err(|e| {
61        AlgorithmError::InvalidParameter {
62            parameter: "dsl",
63            message: format!("Parse error: {e}"),
64        }
65    })?;
66
67    for pair in pairs {
68        if pair.as_rule() == Rule::program {
69            for inner in pair.into_inner() {
70                if inner.as_rule() == Rule::statement {
71                    // Get the inner rule from statement (expr_stmt, variable_decl, etc.)
72                    let stmt_inner = inner.into_inner().next().ok_or_else(|| {
73                        AlgorithmError::InvalidParameter {
74                            parameter: "dsl",
75                            message: "Empty statement".to_string(),
76                        }
77                    })?;
78
79                    // If it's an expr_stmt, extract the expression
80                    if stmt_inner.as_rule() == Rule::expr_stmt {
81                        return parse_expr_stmt(stmt_inner);
82                    }
83                }
84            }
85        }
86    }
87
88    Err(AlgorithmError::InvalidParameter {
89        parameter: "dsl",
90        message: "No expression found".to_string(),
91    })
92}
93
94fn parse_statement(pair: pest::iterators::Pair<Rule>) -> Result<Statement> {
95    let inner = pair
96        .into_inner()
97        .next()
98        .ok_or_else(|| AlgorithmError::InvalidParameter {
99            parameter: "dsl",
100            message: "Empty statement".to_string(),
101        })?;
102
103    match inner.as_rule() {
104        Rule::variable_decl => {
105            let mut parts = inner.into_inner();
106            let name = parts
107                .next()
108                .ok_or_else(|| AlgorithmError::InvalidParameter {
109                    parameter: "dsl",
110                    message: "Missing variable name".to_string(),
111                })?
112                .as_str()
113                .to_string();
114
115            let value = parts
116                .next()
117                .ok_or_else(|| AlgorithmError::InvalidParameter {
118                    parameter: "dsl",
119                    message: "Missing variable value".to_string(),
120                })?;
121
122            Ok(Statement::VariableDecl {
123                name,
124                value: Box::new(parse_expr(value)?),
125            })
126        }
127        Rule::function_decl => {
128            let mut parts = inner.into_inner();
129            let name = parts
130                .next()
131                .ok_or_else(|| AlgorithmError::InvalidParameter {
132                    parameter: "dsl",
133                    message: "Missing function name".to_string(),
134                })?
135                .as_str()
136                .to_string();
137
138            let mut params = Vec::new();
139            let mut body_pair = None;
140
141            for part in parts {
142                match part.as_rule() {
143                    Rule::param_list => {
144                        for param in part.into_inner() {
145                            params.push(param.as_str().to_string());
146                        }
147                    }
148                    Rule::expression => {
149                        body_pair = Some(part);
150                    }
151                    _ => {}
152                }
153            }
154
155            let body = body_pair.ok_or_else(|| AlgorithmError::InvalidParameter {
156                parameter: "dsl",
157                message: "Missing function body".to_string(),
158            })?;
159
160            Ok(Statement::FunctionDecl {
161                name,
162                params,
163                body: Box::new(parse_expr(body)?),
164            })
165        }
166        Rule::return_stmt => {
167            let expr =
168                inner
169                    .into_inner()
170                    .next()
171                    .ok_or_else(|| AlgorithmError::InvalidParameter {
172                        parameter: "dsl",
173                        message: "Missing return expression".to_string(),
174                    })?;
175
176            Ok(Statement::Return(Box::new(parse_expr(expr)?)))
177        }
178        Rule::expr_stmt => parse_expr_stmt(inner).map(|e| Statement::Expr(Box::new(e))),
179        _ => Err(AlgorithmError::InvalidParameter {
180            parameter: "dsl",
181            message: format!("Unexpected statement: {:?}", inner.as_rule()),
182        }),
183    }
184}
185
186fn parse_expr_stmt(pair: pest::iterators::Pair<Rule>) -> Result<Expr> {
187    let expr = pair
188        .into_inner()
189        .next()
190        .ok_or_else(|| AlgorithmError::InvalidParameter {
191            parameter: "dsl",
192            message: "Empty expression statement".to_string(),
193        })?;
194
195    parse_expr(expr)
196}
197
198fn parse_expr(pair: pest::iterators::Pair<Rule>) -> Result<Expr> {
199    match pair.as_rule() {
200        Rule::expression => parse_expr(pair.into_inner().next().ok_or_else(|| {
201            AlgorithmError::InvalidParameter {
202                parameter: "dsl",
203                message: "Empty expression".to_string(),
204            }
205        })?),
206        Rule::logical_or => parse_binary_op(pair, BinaryOp::Or),
207        Rule::logical_and => parse_binary_op(pair, BinaryOp::And),
208        Rule::logical_not => {
209            let mut inner = pair.into_inner();
210            let mut not_count = 0;
211
212            // Count NOT operators
213            while let Some(next) = inner.peek() {
214                if matches!(next.as_rule(), Rule::not_op) {
215                    not_count += 1;
216                    inner.next();
217                } else {
218                    break;
219                }
220            }
221
222            let mut expr =
223                parse_expr(
224                    inner
225                        .next()
226                        .ok_or_else(|| AlgorithmError::InvalidParameter {
227                            parameter: "dsl",
228                            message: "Missing expression after not".to_string(),
229                        })?,
230                )?;
231
232            // Apply NOT operators
233            for _ in 0..not_count {
234                expr = Expr::Unary {
235                    op: UnaryOp::Not,
236                    expr: Box::new(expr),
237                    ty: Type::Unknown,
238                };
239            }
240
241            Ok(expr)
242        }
243        Rule::comparison => parse_comparison(pair),
244        Rule::additive => parse_additive(pair),
245        Rule::multiplicative => parse_multiplicative(pair),
246        Rule::power => parse_power(pair),
247        Rule::unary => parse_unary(pair),
248        Rule::primary => parse_primary(pair),
249        _ => Err(AlgorithmError::InvalidParameter {
250            parameter: "dsl",
251            message: format!("Unexpected expression rule: {:?}", pair.as_rule()),
252        }),
253    }
254}
255
256fn parse_binary_op(pair: pest::iterators::Pair<Rule>, default_op: BinaryOp) -> Result<Expr> {
257    let mut inner = pair.into_inner();
258    let mut left = parse_expr(
259        inner
260            .next()
261            .ok_or_else(|| AlgorithmError::InvalidParameter {
262                parameter: "dsl",
263                message: "Missing left operand".to_string(),
264            })?,
265    )?;
266
267    while let Some(next) = inner.next() {
268        let op = match next.as_rule() {
269            Rule::or_op => BinaryOp::Or,
270            Rule::and_op => BinaryOp::And,
271            _ => {
272                let right = parse_expr(next)?;
273                left = Expr::Binary {
274                    left: Box::new(left),
275                    op: default_op,
276                    right: Box::new(right),
277                    ty: Type::Unknown,
278                };
279                continue;
280            }
281        };
282
283        let right = parse_expr(
284            inner
285                .next()
286                .ok_or_else(|| AlgorithmError::InvalidParameter {
287                    parameter: "dsl",
288                    message: "Missing right operand".to_string(),
289                })?,
290        )?;
291
292        left = Expr::Binary {
293            left: Box::new(left),
294            op,
295            right: Box::new(right),
296            ty: Type::Unknown,
297        };
298    }
299
300    Ok(left)
301}
302
303fn parse_comparison(pair: pest::iterators::Pair<Rule>) -> Result<Expr> {
304    let mut inner = pair.into_inner();
305    let left = parse_expr(
306        inner
307            .next()
308            .ok_or_else(|| AlgorithmError::InvalidParameter {
309                parameter: "dsl",
310                message: "Missing left operand".to_string(),
311            })?,
312    )?;
313
314    if let Some(op_pair) = inner.next() {
315        let op = match op_pair.as_rule() {
316            Rule::eq_op => BinaryOp::Equal,
317            Rule::ne_op => BinaryOp::NotEqual,
318            Rule::lt_op => BinaryOp::Less,
319            Rule::le_op => BinaryOp::LessEqual,
320            Rule::gt_op => BinaryOp::Greater,
321            Rule::ge_op => BinaryOp::GreaterEqual,
322            _ => {
323                return Err(AlgorithmError::InvalidParameter {
324                    parameter: "dsl",
325                    message: format!("Unknown comparison operator: {:?}", op_pair.as_rule()),
326                });
327            }
328        };
329
330        let right = parse_expr(
331            inner
332                .next()
333                .ok_or_else(|| AlgorithmError::InvalidParameter {
334                    parameter: "dsl",
335                    message: "Missing right operand".to_string(),
336                })?,
337        )?;
338
339        Ok(Expr::Binary {
340            left: Box::new(left),
341            op,
342            right: Box::new(right),
343            ty: Type::Unknown,
344        })
345    } else {
346        Ok(left)
347    }
348}
349
350fn parse_additive(pair: pest::iterators::Pair<Rule>) -> Result<Expr> {
351    let mut inner = pair.into_inner();
352    let mut left = parse_expr(
353        inner
354            .next()
355            .ok_or_else(|| AlgorithmError::InvalidParameter {
356                parameter: "dsl",
357                message: "Missing left operand".to_string(),
358            })?,
359    )?;
360
361    while let Some(op_pair) = inner.next() {
362        let op = match op_pair.as_rule() {
363            Rule::add_op => BinaryOp::Add,
364            Rule::sub_op => BinaryOp::Subtract,
365            _ => {
366                let right = parse_expr(op_pair)?;
367                left = Expr::Binary {
368                    left: Box::new(left),
369                    op: BinaryOp::Add,
370                    right: Box::new(right),
371                    ty: Type::Unknown,
372                };
373                continue;
374            }
375        };
376
377        let right = parse_expr(
378            inner
379                .next()
380                .ok_or_else(|| AlgorithmError::InvalidParameter {
381                    parameter: "dsl",
382                    message: "Missing right operand".to_string(),
383                })?,
384        )?;
385
386        left = Expr::Binary {
387            left: Box::new(left),
388            op,
389            right: Box::new(right),
390            ty: Type::Unknown,
391        };
392    }
393
394    Ok(left)
395}
396
397fn parse_multiplicative(pair: pest::iterators::Pair<Rule>) -> Result<Expr> {
398    let mut inner = pair.into_inner();
399    let mut left = parse_expr(
400        inner
401            .next()
402            .ok_or_else(|| AlgorithmError::InvalidParameter {
403                parameter: "dsl",
404                message: "Missing left operand".to_string(),
405            })?,
406    )?;
407
408    while let Some(op_pair) = inner.next() {
409        let op = match op_pair.as_rule() {
410            Rule::mul_op => BinaryOp::Multiply,
411            Rule::div_op => BinaryOp::Divide,
412            Rule::mod_op => BinaryOp::Modulo,
413            _ => {
414                let right = parse_expr(op_pair)?;
415                left = Expr::Binary {
416                    left: Box::new(left),
417                    op: BinaryOp::Multiply,
418                    right: Box::new(right),
419                    ty: Type::Unknown,
420                };
421                continue;
422            }
423        };
424
425        let right = parse_expr(
426            inner
427                .next()
428                .ok_or_else(|| AlgorithmError::InvalidParameter {
429                    parameter: "dsl",
430                    message: "Missing right operand".to_string(),
431                })?,
432        )?;
433
434        left = Expr::Binary {
435            left: Box::new(left),
436            op,
437            right: Box::new(right),
438            ty: Type::Unknown,
439        };
440    }
441
442    Ok(left)
443}
444
445fn parse_power(pair: pest::iterators::Pair<Rule>) -> Result<Expr> {
446    let mut inner = pair.into_inner();
447    let mut left = parse_expr(
448        inner
449            .next()
450            .ok_or_else(|| AlgorithmError::InvalidParameter {
451                parameter: "dsl",
452                message: "Missing left operand".to_string(),
453            })?,
454    )?;
455
456    while let Some(op_pair) = inner.next() {
457        if matches!(op_pair.as_rule(), Rule::pow_op) {
458            let right =
459                parse_expr(
460                    inner
461                        .next()
462                        .ok_or_else(|| AlgorithmError::InvalidParameter {
463                            parameter: "dsl",
464                            message: "Missing right operand".to_string(),
465                        })?,
466                )?;
467
468            left = Expr::Binary {
469                left: Box::new(left),
470                op: BinaryOp::Power,
471                right: Box::new(right),
472                ty: Type::Unknown,
473            };
474        } else {
475            let right = parse_expr(op_pair)?;
476            left = Expr::Binary {
477                left: Box::new(left),
478                op: BinaryOp::Power,
479                right: Box::new(right),
480                ty: Type::Unknown,
481            };
482        }
483    }
484
485    Ok(left)
486}
487
488fn parse_unary(pair: pest::iterators::Pair<Rule>) -> Result<Expr> {
489    let mut inner = pair.into_inner();
490    let first = inner
491        .next()
492        .ok_or_else(|| AlgorithmError::InvalidParameter {
493            parameter: "dsl",
494            message: "Empty unary expression".to_string(),
495        })?;
496
497    match first.as_rule() {
498        Rule::sub_op => {
499            // Next element will be another unary expression
500            let expr = inner
501                .next()
502                .ok_or_else(|| AlgorithmError::InvalidParameter {
503                    parameter: "dsl",
504                    message: "Missing expression after -".to_string(),
505                })?;
506
507            Ok(Expr::Unary {
508                op: UnaryOp::Negate,
509                expr: Box::new(parse_expr(expr)?),
510                ty: Type::Unknown,
511            })
512        }
513        Rule::add_op => {
514            // Next element will be another unary expression
515            let expr = inner
516                .next()
517                .ok_or_else(|| AlgorithmError::InvalidParameter {
518                    parameter: "dsl",
519                    message: "Missing expression after +".to_string(),
520                })?;
521
522            Ok(Expr::Unary {
523                op: UnaryOp::Plus,
524                expr: Box::new(parse_expr(expr)?),
525                ty: Type::Unknown,
526            })
527        }
528        _ => parse_expr(first),
529    }
530}
531
532fn parse_primary(pair: pest::iterators::Pair<Rule>) -> Result<Expr> {
533    let inner = pair
534        .into_inner()
535        .next()
536        .ok_or_else(|| AlgorithmError::InvalidParameter {
537            parameter: "dsl",
538            message: "Empty primary expression".to_string(),
539        })?;
540
541    match inner.as_rule() {
542        Rule::number => {
543            let num =
544                inner
545                    .as_str()
546                    .parse::<f64>()
547                    .map_err(|_| AlgorithmError::InvalidParameter {
548                        parameter: "dsl",
549                        message: format!("Invalid number: {}", inner.as_str()),
550                    })?;
551            Ok(Expr::Number(num))
552        }
553        Rule::band_ref => {
554            let band_str = inner.as_str();
555            let band_num =
556                band_str[1..]
557                    .parse::<usize>()
558                    .map_err(|_| AlgorithmError::InvalidParameter {
559                        parameter: "dsl",
560                        message: format!("Invalid band reference: {band_str}"),
561                    })?;
562            Ok(Expr::Band(band_num))
563        }
564        Rule::function_call => {
565            let mut parts = inner.into_inner();
566            let name = parts
567                .next()
568                .ok_or_else(|| AlgorithmError::InvalidParameter {
569                    parameter: "dsl",
570                    message: "Missing function name".to_string(),
571                })?
572                .as_str()
573                .to_string();
574
575            let mut args = Vec::new();
576            if let Some(arg_list) = parts.next() {
577                for arg in arg_list.into_inner() {
578                    args.push(parse_expr(arg)?);
579                }
580            }
581
582            Ok(Expr::Call {
583                name,
584                args,
585                ty: Type::Unknown,
586            })
587        }
588        Rule::variable_ref => Ok(Expr::Variable(inner.as_str().to_string())),
589        Rule::conditional => {
590            let mut parts = inner.into_inner();
591            let condition =
592                parse_expr(
593                    parts
594                        .next()
595                        .ok_or_else(|| AlgorithmError::InvalidParameter {
596                            parameter: "dsl",
597                            message: "Missing condition".to_string(),
598                        })?,
599                )?;
600
601            let then_expr =
602                parse_expr(
603                    parts
604                        .next()
605                        .ok_or_else(|| AlgorithmError::InvalidParameter {
606                            parameter: "dsl",
607                            message: "Missing then expression".to_string(),
608                        })?,
609                )?;
610
611            let else_expr =
612                parse_expr(
613                    parts
614                        .next()
615                        .ok_or_else(|| AlgorithmError::InvalidParameter {
616                            parameter: "dsl",
617                            message: "Missing else expression".to_string(),
618                        })?,
619                )?;
620
621            Ok(Expr::Conditional {
622                condition: Box::new(condition),
623                then_expr: Box::new(then_expr),
624                else_expr: Box::new(else_expr),
625                ty: Type::Unknown,
626            })
627        }
628        Rule::block => {
629            let mut statements = Vec::new();
630            let mut result = None;
631
632            for part in inner.into_inner() {
633                match part.as_rule() {
634                    Rule::statement => statements.push(parse_statement(part)?),
635                    Rule::expression => result = Some(Box::new(parse_expr(part)?)),
636                    _ => {}
637                }
638            }
639
640            Ok(Expr::Block {
641                statements,
642                result,
643                ty: Type::Unknown,
644            })
645        }
646        Rule::expression => parse_expr(inner),
647        _ => Err(AlgorithmError::InvalidParameter {
648            parameter: "dsl",
649            message: format!("Unexpected primary: {:?}", inner.as_rule()),
650        }),
651    }
652}
653
654#[cfg(test)]
655mod tests {
656    use super::*;
657
658    #[test]
659    fn test_parse_number() {
660        let expr = parse_expression("42.5").expect("Should parse");
661        assert!(matches!(expr, Expr::Number(n) if (n - 42.5).abs() < 1e-10));
662    }
663
664    #[test]
665    fn test_parse_band() {
666        let expr = parse_expression("B1").expect("Should parse");
667        assert!(matches!(expr, Expr::Band(1)));
668    }
669
670    #[test]
671    fn test_parse_arithmetic() {
672        let result = parse_expression("1 + 2 * 3");
673        assert!(result.is_ok());
674    }
675
676    #[test]
677    fn test_parse_ndvi() {
678        let result = parse_expression("(B1 - B2) / (B1 + B2)");
679        assert!(result.is_ok());
680    }
681
682    #[test]
683    fn test_parse_conditional() {
684        let result = parse_expression("if B1 > 0.5 then 1 else 0");
685        if let Err(e) = &result {
686            eprintln!("Parse error: {:?}", e);
687        }
688        assert!(result.is_ok(), "Parse failed: {:?}", result);
689    }
690
691    #[test]
692    fn test_parse_function_call() {
693        let result = parse_expression("sqrt(B1 * B1 + B2 * B2)");
694        assert!(result.is_ok());
695    }
696
697    #[test]
698    fn test_parse_program() {
699        let program = r#"
700            let ndvi = (B8 - B4) / (B8 + B4);
701            let result = if ndvi > 0.5 then 1 else 0;
702        "#;
703        let result = parse_program(program);
704        assert!(result.is_ok());
705    }
706}