Skip to main content

akar_parser/parser/
expression.rs

1//! Expression parsing — arithmetic, boolean, string, list, map, case, function calls.
2
3use super::Rule;
4use super::dml::{parse_property_kv, parse_query_pairs};
5use crate::ast::*;
6
7/// Parse any expression rule to an AST node.
8pub fn parse_expression(pair: pest::iterators::Pair<Rule>) -> Result<Expression, String> {
9    // Handle compound binary expressions by collecting children
10    let rule = pair.as_rule();
11    let children: Vec<_> = pair.clone().into_inner().collect();
12
13    // Handle CASE expression
14    if rule == Rule::case_expr {
15        return parse_case_expr(pair);
16    }
17
18    // Handle comparison_expr specially — it can have 1, 2, or 3 children depending
19    // on which operator is used (IS NULL, IN, STARTS WITH, =, etc.)
20    if rule == Rule::comparison_expr {
21        if children.len() == 1 {
22            return parse_expression(children[0].clone());
23        }
24        let left = parse_expression(children[0].clone())?;
25        if children.len() == 2 {
26            // Postfix/special operators (IS NULL, IN, STARTS WITH, ...)
27            return parse_comparison_suffix(children[1].clone(), left);
28        }
29        if children.len() == 3 {
30            // Standard comparison: left comparison_op right
31            let op_str = children[1].as_str();
32            let op = match op_str {
33                "=" => BinaryOp::Equal,
34                "<>" => BinaryOp::NotEqual,
35                "<" => BinaryOp::LessThan,
36                ">" => BinaryOp::GreaterThan,
37                "<=" => BinaryOp::LessThanOrEqual,
38                ">=" => BinaryOp::GreaterThanOrEqual,
39                _ => return Err(format!("Unknown comparison_op: {}", op_str)),
40            };
41            let right = parse_expression(children[2].clone())?;
42            return Ok(Expression::BinaryOp(op, Box::new(left), Box::new(right)));
43        }
44        return Err(format!("Unexpected comparison_expr with {} children", children.len()));
45    }
46
47    // Unwrap single-child wrappers (priority/precedence levels)
48    if matches!(
49        rule,
50        Rule::expression
51            | Rule::or_expr
52            | Rule::xor_expr
53            | Rule::and_expr
54            | Rule::not_expr
55            | Rule::additive_expr
56            | Rule::multiplicative_expr
57            | Rule::unary_expr
58    ) {
59        if children.len() == 1 {
60            return parse_expression(children[0].clone());
61        }
62        if children.len() >= 3 {
63            let mut result = parse_expression(children[0].clone())?;
64            let mut i = 1;
65            while i + 1 < children.len() {
66                let op = match children[i].as_str() {
67                    "OR" => BinaryOp::Or,
68                    "XOR" => BinaryOp::Xor,
69                    "AND" => BinaryOp::And,
70                    "+" => BinaryOp::Add,
71                    "-" => BinaryOp::Subtract,
72                    "*" => BinaryOp::Multiply,
73                    "/" => BinaryOp::Divide,
74                    "%" => BinaryOp::Modulo,
75                    _ => return Err(format!("Unknown op: {}", children[i].as_str())),
76                };
77                let right = parse_expression(children[i + 1].clone())?;
78                result = Expression::BinaryOp(op, Box::new(result), Box::new(right));
79                i += 2;
80            }
81            return Ok(result);
82        }
83    }
84
85    // Handle unary NOT
86    if rule == Rule::not_expr && children.len() == 2 {
87        let inner = parse_expression(children[1].clone())?;
88        return Ok(Expression::UnaryOp(UnaryOp::Not, Box::new(inner)));
89    }
90
91    match rule {
92        Rule::primary => {
93            // Handle function calls encoded by grammar as: variable ~ function_args?
94            // e.g. nextval('seq') or COUNT(a)
95            if children.len() == 2
96                && children[0].as_rule() == Rule::variable
97                && children[1].as_rule() == Rule::function_args
98            {
99                let name = children[0].as_str().to_string();
100                let mut args = Vec::new();
101                let args_text = children[1].as_str().replace(" ", "");
102                for c in children[1].clone().into_inner() {
103                    if c.as_rule() == Rule::expression {
104                        args.push(parse_expression(c)?);
105                    }
106                }
107                if args.is_empty() && args_text == "(*)" {
108                    args.push(Expression::Star);
109                }
110                return Ok(Expression::FunctionCall(name, args));
111            }
112            parse_expression(children.into_iter().next().ok_or("Empty primary")?)
113        }
114        Rule::literal => parse_literal(children.into_iter().next().ok_or("Empty literal")?),
115        Rule::string => Ok(Expression::Constant(Constant::String(unescape_string(pair.as_str())))),
116        Rule::integer => {
117            let v: i64 = pair.as_str().parse().map_err(|e| format!("Int: {e}"))?;
118            Ok(Expression::Constant(Constant::Integer(v)))
119        }
120        Rule::float => {
121            let v: f64 = pair.as_str().parse().map_err(|e| format!("Float: {e}"))?;
122            Ok(Expression::Constant(Constant::Float(v)))
123        }
124        Rule::boolean_literal => Ok(Expression::Constant(Constant::Bool(
125            pair.as_str().to_uppercase() == "TRUE",
126        ))),
127        Rule::null_literal => Ok(Expression::Constant(Constant::Null)),
128        Rule::variable => Ok(Expression::Variable(pair.as_str().to_string())),
129        Rule::parameter => {
130            let name = pair.as_str().strip_prefix('$').unwrap_or(pair.as_str()).to_string();
131            Ok(Expression::Parameter(name))
132        }
133        Rule::list_literal => {
134            let items = children
135                .into_iter()
136                .filter(|c| c.as_rule() == Rule::expression)
137                .map(parse_expression)
138                .collect::<Result<Vec<_>, _>>()?;
139            Ok(Expression::List(items))
140        }
141        Rule::map_literal => {
142            let mut entries = Vec::new();
143            for c in children {
144                if c.as_rule() == Rule::property_key_value {
145                    entries.push(parse_property_kv(c)?);
146                }
147            }
148            Ok(Expression::Map(entries))
149        }
150        Rule::postfix_expr => {
151            let mut children = pair.clone().into_inner();
152            let mut result = parse_expression(children.next().ok_or("Empty postfix")?)?;
153            for child in children {
154                if child.as_rule() == Rule::property_access {
155                    let prop = child.into_inner().next().unwrap().as_str().to_string();
156                    result = Expression::PropertyAccess(Box::new(result), prop);
157                }
158            }
159            Ok(result)
160        }
161        Rule::exists_subquery => {
162            // EXISTS { MATCH ... }
163            for c in children {
164                if c.as_rule() == Rule::query_statement {
165                    let query = parse_query_pairs(c)?;
166                    return Ok(Expression::ExistsSubquery(Box::new(query)));
167                }
168            }
169            Err("EXISTS subquery requires a query statement".into())
170        }
171        Rule::list_predicate => {
172            // ANY(x IN list WHERE predicate), ALL/NONE/SINGLE
173            // Parse tree has: variable, expression(list), expression(predicate)
174            // Quantifier is extracted from the matched string prefix.
175            let children: Vec<_> = pair.clone().into_inner().collect();
176            if children.len() < 3 {
177                return Err(format!("Invalid list predicate syntax: {} children", children.len()));
178            }
179            // Extract quantifier from the raw token: the first word of the pair
180            let full_text = pair.as_str();
181            let quantifier_str = full_text.split('(').next().unwrap_or("").to_uppercase();
182            let quantifier = match quantifier_str.as_str() {
183                "ANY" => Quantifier::Any,
184                "ALL" => Quantifier::All,
185                "NONE" => Quantifier::None,
186                "SINGLE" => Quantifier::Single,
187                _ => return Err(format!("Unknown quantifier: {}", quantifier_str)),
188            };
189            let var_name = children[0].as_str().to_string();
190            let list = parse_expression(children[1].clone())?;
191            let predicate = parse_expression(children[2].clone())?;
192            Ok(Expression::ListPredicate {
193                quantifier,
194                list: Box::new(list),
195                var_name,
196                predicate: Box::new(predicate),
197            })
198        }
199        Rule::lambda_expr => {
200            let mut children = pair.clone().into_inner();
201            let var_name = children.next().ok_or("Lambda missing variable")?.as_str().to_string();
202            let body = parse_expression(children.next().ok_or("Lambda missing body")?)?;
203            Ok(Expression::Lambda {
204                var_name,
205                body: Box::new(body),
206            })
207        }
208        Rule::function_args => {
209            // function_call can appear as child of postfix_expr
210            // The parent variable is the function name
211            // We detect this in the postfix chain handling
212            if children.is_empty() {
213                return Ok(Expression::Constant(Constant::Null));
214            }
215            // Extract function name from siblings in parent
216            let name = pair.as_str().split('(').next().unwrap_or("").to_string();
217            let args = children
218                .into_iter()
219                .filter(|c| c.as_rule() == Rule::expression)
220                .map(parse_expression)
221                .collect::<Result<Vec<_>, _>>()?;
222            Ok(Expression::FunctionCall(name, args))
223        }
224        Rule::property_access => Err("property_access should be handled within postfix_expr".into()),
225        _ => {
226            // Try unwrapping single child
227            if let Some(child) = children.into_iter().next() {
228                parse_expression(child)
229            } else {
230                Err(format!("Cannot parse: {:?}", rule))
231            }
232        }
233    }
234}
235
236/// Parse a comparison suffix operator node into an expression given the left-hand side.
237pub fn parse_comparison_suffix(pair: pest::iterators::Pair<Rule>, left: Expression) -> Result<Expression, String> {
238    /// Find the additive_expr child inside an operator rule.
239    fn get_rhs(pair: pest::iterators::Pair<Rule>) -> Result<Expression, String> {
240        let rhs_pair = pair
241            .into_inner()
242            .find(|p| p.as_rule() == Rule::additive_expr)
243            .ok_or_else(|| "Operator missing right-hand expression".to_string())?;
244        parse_expression(rhs_pair)
245    }
246
247    match pair.as_rule() {
248        Rule::is_check_op => {
249            let text = pair.as_str();
250            if text.contains("NOT") {
251                Ok(Expression::UnaryOp(UnaryOp::IsNotNull, Box::new(left)))
252            } else {
253                Ok(Expression::UnaryOp(UnaryOp::IsNull, Box::new(left)))
254            }
255        }
256        Rule::in_op => {
257            let right = get_rhs(pair)?;
258            Ok(Expression::BinaryOp(BinaryOp::In, Box::new(left), Box::new(right)))
259        }
260        Rule::not_in_op => {
261            let right = get_rhs(pair)?;
262            Ok(Expression::BinaryOp(BinaryOp::NotIn, Box::new(left), Box::new(right)))
263        }
264        Rule::starts_with_op => {
265            let right = get_rhs(pair)?;
266            Ok(Expression::BinaryOp(
267                BinaryOp::StartsWith,
268                Box::new(left),
269                Box::new(right),
270            ))
271        }
272        Rule::not_starts_with_op => {
273            let right = get_rhs(pair)?;
274            let inner = Expression::BinaryOp(BinaryOp::StartsWith, Box::new(left), Box::new(right));
275            Ok(Expression::UnaryOp(UnaryOp::Not, Box::new(inner)))
276        }
277        Rule::ends_with_op => {
278            let right = get_rhs(pair)?;
279            Ok(Expression::BinaryOp(
280                BinaryOp::EndsWith,
281                Box::new(left),
282                Box::new(right),
283            ))
284        }
285        Rule::not_ends_with_op => {
286            let right = get_rhs(pair)?;
287            let inner = Expression::BinaryOp(BinaryOp::EndsWith, Box::new(left), Box::new(right));
288            Ok(Expression::UnaryOp(UnaryOp::Not, Box::new(inner)))
289        }
290        Rule::contains_op => {
291            let right = get_rhs(pair)?;
292            Ok(Expression::BinaryOp(
293                BinaryOp::Contains,
294                Box::new(left),
295                Box::new(right),
296            ))
297        }
298        Rule::not_contains_op => {
299            let right = get_rhs(pair)?;
300            let inner = Expression::BinaryOp(BinaryOp::Contains, Box::new(left), Box::new(right));
301            Ok(Expression::UnaryOp(UnaryOp::Not, Box::new(inner)))
302        }
303        Rule::like_op => {
304            let right = get_rhs(pair)?;
305            Ok(Expression::BinaryOp(BinaryOp::Like, Box::new(left), Box::new(right)))
306        }
307        Rule::between_op => {
308            let mut children = pair.into_inner();
309            let lower_pair = children
310                .find(|p| p.as_rule() == Rule::additive_expr)
311                .ok_or_else(|| "BETWEEN missing lower bound expression".to_string())?;
312            let upper_pair = children
313                .find(|p| p.as_rule() == Rule::additive_expr)
314                .ok_or_else(|| "BETWEEN missing upper bound expression".to_string())?;
315            let lower_expr = parse_expression(lower_pair)?;
316            let upper_expr = parse_expression(upper_pair)?;
317            let ge = Expression::BinaryOp(
318                BinaryOp::GreaterThanOrEqual,
319                Box::new(left.clone()),
320                Box::new(lower_expr),
321            );
322            let le = Expression::BinaryOp(BinaryOp::LessThanOrEqual, Box::new(left), Box::new(upper_expr));
323            Ok(Expression::BinaryOp(BinaryOp::And, Box::new(ge), Box::new(le)))
324        }
325        r => Err(format!("Unknown comparison suffix: {:?}", r)),
326    }
327}
328
329/// Parse a `CASE [subject] WHEN ... THEN ... [ELSE ...] END` expression.
330pub fn parse_case_expr(pair: pest::iterators::Pair<Rule>) -> Result<Expression, String> {
331    let mut subject: Option<Expression> = None;
332    let mut alternatives: Vec<CaseAlternative> = Vec::new();
333    let mut else_expr: Option<Expression> = None;
334
335    for child in pair.into_inner() {
336        match child.as_rule() {
337            Rule::case_subject => {
338                // case_subject wraps a single expression (with lookahead to skip WHEN)
339                if let Some(expr_pair) = child.into_inner().next() {
340                    subject = Some(parse_expression(expr_pair)?);
341                }
342            }
343            Rule::case_when => {
344                // case_when contains exactly two expression children: WHEN expr, THEN expr
345                let mut exprs = child
346                    .into_inner()
347                    .filter(|p| p.as_rule() == Rule::expression)
348                    .map(parse_expression)
349                    .collect::<Result<Vec<_>, _>>()?;
350                if exprs.len() < 2 {
351                    return Err("CASE WHEN clause requires both WHEN and THEN expressions".into());
352                }
353                let then = exprs.remove(1);
354                let when = exprs.remove(0);
355                alternatives.push(CaseAlternative { when, then });
356            }
357            Rule::case_else => {
358                if let Some(expr_pair) = child.into_inner().find(|p| p.as_rule() == Rule::expression) {
359                    else_expr = Some(parse_expression(expr_pair)?);
360                }
361            }
362            _ => {} // Skip "CASE", "END" keyword tokens
363        }
364    }
365
366    if alternatives.is_empty() {
367        return Err("CASE expression requires at least one WHEN clause".into());
368    }
369
370    Ok(Expression::Case(CaseExpr {
371        subject: subject.map(Box::new),
372        alternatives,
373        else_expr: else_expr.map(Box::new),
374    }))
375}
376
377/// Parse a literal value.
378pub fn parse_literal(pair: pest::iterators::Pair<Rule>) -> Result<Expression, String> {
379    match pair.as_rule() {
380        Rule::string => Ok(Expression::Constant(Constant::String(unescape_string(pair.as_str())))),
381        Rule::integer => {
382            let s = pair.as_str();
383            Ok(Expression::Constant(Constant::Integer(
384                s.trim().parse().map_err(|e| format!("Int: {e} (for string '{s}')"))?,
385            )))
386        }
387        Rule::float => {
388            let s = pair.as_str();
389            Ok(Expression::Constant(Constant::Float(
390                s.trim().parse().map_err(|e| format!("Float: {e} (for string '{s}')"))?,
391            )))
392        }
393        Rule::boolean_literal => Ok(Expression::Constant(Constant::Bool(
394            pair.as_str().to_uppercase() == "TRUE",
395        ))),
396        Rule::null_literal => Ok(Expression::Constant(Constant::Null)),
397        _ => Err(format!("Unknown literal: {:?}", pair.as_rule())),
398    }
399}
400
401/// Unescape a string literal — handles \n, \t, \r, \\, \", \'.
402pub fn unescape_string(s: &str) -> String {
403    let s = s.trim_matches(|c| c == '"' || c == '\'');
404    let mut r = String::with_capacity(s.len());
405    let mut chars = s.chars();
406    while let Some(c) = chars.next() {
407        if c == '\\' {
408            match chars.next() {
409                Some('n') => r.push('\n'),
410                Some('t') => r.push('\t'),
411                Some('r') => r.push('\r'),
412                Some('\\') => r.push('\\'),
413                Some('"') => r.push('"'),
414                Some('\'') => r.push('\''),
415                Some(o) => {
416                    r.push('\\');
417                    r.push(o);
418                }
419                None => r.push('\\'),
420            }
421        } else {
422            r.push(c);
423        }
424    }
425    r
426}