alopex_sql/parser/
expr.rs

1use crate::ast::{BinaryOp, Expr, ExprKind, Literal, Spanned, UnaryOp};
2use crate::error::{ParserError, Result};
3use crate::tokenizer::keyword::Keyword;
4use crate::tokenizer::token::{Token, Word};
5
6use super::Parser;
7use super::precedence::{PREC_UNKNOWN, Precedence};
8
9impl<'a> Parser<'a> {
10    pub(crate) fn parse_subexpr(&mut self, precedence: u8) -> Result<Expr> {
11        let _guard = self.recursion.try_decrease()?;
12
13        let mut expr = self.parse_prefix()?;
14
15        loop {
16            let next_prec = self.next_precedence();
17            if precedence >= next_prec || next_prec == PREC_UNKNOWN {
18                break;
19            }
20            expr = self.parse_infix(expr, next_prec)?;
21        }
22
23        Ok(expr)
24    }
25
26    fn parse_prefix(&mut self) -> Result<Expr> {
27        if let Some(res) = self.dialect.parse_prefix(self) {
28            return res;
29        }
30
31        let token = self.peek().clone();
32        match &token.token {
33            Token::Number(n) => {
34                self.advance();
35                Ok(Expr::new(
36                    ExprKind::Literal(Literal::Number(n.clone())),
37                    token.span,
38                ))
39            }
40            Token::SingleQuotedString(s) => {
41                self.advance();
42                Ok(Expr::new(
43                    ExprKind::Literal(Literal::String(s.clone())),
44                    token.span,
45                ))
46            }
47            Token::Word(Word { keyword, value, .. }) => match keyword {
48                Keyword::TRUE => {
49                    self.advance();
50                    Ok(Expr::new(
51                        ExprKind::Literal(Literal::Boolean(true)),
52                        token.span,
53                    ))
54                }
55                Keyword::FALSE => {
56                    self.advance();
57                    Ok(Expr::new(
58                        ExprKind::Literal(Literal::Boolean(false)),
59                        token.span,
60                    ))
61                }
62                Keyword::NULL => {
63                    self.advance();
64                    Ok(Expr::new(ExprKind::Literal(Literal::Null), token.span))
65                }
66                Keyword::NOT => {
67                    self.advance();
68                    let operand =
69                        self.parse_subexpr(self.dialect.prec_value(Precedence::UnaryNot))?;
70                    let span = token.span.union(&operand.span());
71                    Ok(Expr::new(
72                        ExprKind::UnaryOp {
73                            op: UnaryOp::Not,
74                            operand: Box::new(operand),
75                        },
76                        span,
77                    ))
78                }
79                Keyword::NoKeyword => {
80                    // Identifier or function call
81                    self.advance();
82                    let ident_span = token.span;
83                    if let Token::LParen = self.peek().token {
84                        self.advance(); // consume '('
85                        let mut args = Vec::new();
86                        if !matches!(self.peek().token, Token::RParen) {
87                            loop {
88                                args.push(self.parse_subexpr(PREC_UNKNOWN)?);
89                                if matches!(self.peek().token, Token::Comma) {
90                                    self.advance();
91                                    continue;
92                                }
93                                break;
94                            }
95                        }
96                        self.expect_token("')'", |t| matches!(t, Token::RParen))?;
97                        let end_span = self.prev().map(|t| t.span).unwrap_or(ident_span);
98                        Ok(Expr::new(
99                            ExprKind::FunctionCall {
100                                name: value.clone(),
101                                args,
102                            },
103                            ident_span.union(&end_span),
104                        ))
105                    } else if matches!(self.peek().token, Token::Period) {
106                        self.advance(); // consume '.'
107                        let second = self.expect_token("identifier", |t| {
108                            matches!(
109                                t,
110                                Token::Word(Word {
111                                    keyword: Keyword::NoKeyword,
112                                    ..
113                                })
114                            )
115                        })?;
116                        let end_span = second.span;
117                        let col = match second.token {
118                            Token::Word(Word { value, .. }) => value,
119                            _ => unreachable!(),
120                        };
121                        Ok(Expr::new(
122                            ExprKind::ColumnRef {
123                                table: Some(value.clone()),
124                                column: col,
125                            },
126                            ident_span.union(&end_span),
127                        ))
128                    } else {
129                        Ok(Expr::new(
130                            ExprKind::ColumnRef {
131                                table: None,
132                                column: value.clone(),
133                            },
134                            ident_span,
135                        ))
136                    }
137                }
138                _ => Err(ParserError::UnexpectedToken {
139                    line: token.span.start.line,
140                    column: token.span.start.column,
141                    expected: "identifier or literal".into(),
142                    found: format!("{:?}", token.token),
143                }),
144            },
145            Token::LParen => {
146                self.advance();
147                let expr = self.parse_subexpr(PREC_UNKNOWN)?;
148                self.expect_token("')'", |t| matches!(t, Token::RParen))?;
149                let end_span = self.prev().map(|t| t.span).unwrap_or(expr.span());
150                Ok(Expr::new(expr.kind.clone(), expr.span().union(&end_span)))
151            }
152            Token::Minus => {
153                self.advance();
154                let operand = self.parse_subexpr(self.dialect.prec_value(Precedence::UnaryNot))?;
155                let span = token.span.union(&operand.span());
156                Ok(Expr::new(
157                    ExprKind::UnaryOp {
158                        op: UnaryOp::Minus,
159                        operand: Box::new(operand),
160                    },
161                    span,
162                ))
163            }
164            _ => Err(ParserError::UnexpectedToken {
165                line: token.span.start.line,
166                column: token.span.start.column,
167                expected: "expression".into(),
168                found: format!("{:?}", token.token),
169            }),
170        }
171    }
172
173    pub(crate) fn parse_vector_literal(&mut self) -> Result<Expr> {
174        let start = self.peek().span;
175        self.advance(); // consume '['
176
177        let mut values = Vec::new();
178        if matches!(self.peek().token, Token::RBracket) {
179            return Err(ParserError::InvalidVector {
180                line: start.start.line,
181                column: start.start.column,
182            });
183        }
184        loop {
185            let sign = if matches!(self.peek().token, Token::Minus) {
186                self.advance();
187                -1.0
188            } else {
189                1.0
190            };
191
192            let tok = self.expect_token("number", |t| matches!(t, Token::Number(_)))?;
193            let num = match tok.token {
194                Token::Number(n) => n.parse::<f64>().map_err(|_| ParserError::InvalidVector {
195                    line: tok.span.start.line,
196                    column: tok.span.start.column,
197                })?,
198                _ => unreachable!(),
199            } * sign;
200            values.push(num);
201            if matches!(self.peek().token, Token::Comma) {
202                self.advance();
203                continue;
204            }
205            break;
206        }
207        let end = self
208            .expect_token("']'", |t| matches!(t, Token::RBracket))?
209            .span;
210
211        Ok(Expr::new(
212            ExprKind::VectorLiteral(values),
213            start.union(&end),
214        ))
215    }
216
217    fn parse_infix(&mut self, left: Expr, precedence: u8) -> Result<Expr> {
218        let op_token = self.peek().clone();
219        match &op_token.token {
220            Token::Plus
221            | Token::Minus
222            | Token::Mul
223            | Token::Div
224            | Token::Mod
225            | Token::StringConcat => {
226                self.advance();
227                let op = match op_token.token {
228                    Token::Plus => BinaryOp::Add,
229                    Token::Minus => BinaryOp::Sub,
230                    Token::Mul => BinaryOp::Mul,
231                    Token::Div => BinaryOp::Div,
232                    Token::Mod => BinaryOp::Mod,
233                    Token::StringConcat => BinaryOp::StringConcat,
234                    _ => unreachable!(),
235                };
236                let right = self.parse_subexpr(precedence)?;
237                let span = left.span().union(&right.span());
238                Ok(Expr::new(
239                    ExprKind::BinaryOp {
240                        left: Box::new(left),
241                        op,
242                        right: Box::new(right),
243                    },
244                    span,
245                ))
246            }
247            Token::Eq | Token::Neq | Token::Lt | Token::Gt | Token::LtEq | Token::GtEq => {
248                self.advance();
249                let op = match op_token.token {
250                    Token::Eq => BinaryOp::Eq,
251                    Token::Neq => BinaryOp::Neq,
252                    Token::Lt => BinaryOp::Lt,
253                    Token::Gt => BinaryOp::Gt,
254                    Token::LtEq => BinaryOp::LtEq,
255                    Token::GtEq => BinaryOp::GtEq,
256                    _ => unreachable!(),
257                };
258                let right = self.parse_subexpr(precedence)?;
259                let span = left.span().union(&right.span());
260                Ok(Expr::new(
261                    ExprKind::BinaryOp {
262                        left: Box::new(left),
263                        op,
264                        right: Box::new(right),
265                    },
266                    span,
267                ))
268            }
269            Token::Word(Word { keyword, .. }) => match keyword {
270                Keyword::AND | Keyword::OR => {
271                    self.advance();
272                    let op = if *keyword == Keyword::AND {
273                        BinaryOp::And
274                    } else {
275                        BinaryOp::Or
276                    };
277                    let right = self.parse_subexpr(precedence)?;
278                    let span = left.span().union(&right.span());
279                    Ok(Expr::new(
280                        ExprKind::BinaryOp {
281                            left: Box::new(left),
282                            op,
283                            right: Box::new(right),
284                        },
285                        span,
286                    ))
287                }
288                Keyword::BETWEEN => {
289                    self.advance();
290                    let low = self.parse_subexpr(self.dialect.prec_value(Precedence::Between))?;
291                    self.expect_keyword("AND", Keyword::AND)?;
292                    let high = self.parse_subexpr(self.dialect.prec_value(Precedence::Between))?;
293                    let span = left.span().union(&high.span());
294                    Ok(Expr::new(
295                        ExprKind::Between {
296                            expr: Box::new(left),
297                            low: Box::new(low),
298                            high: Box::new(high),
299                            negated: false,
300                        },
301                        span,
302                    ))
303                }
304                Keyword::LIKE => {
305                    self.advance();
306                    let pattern = self.parse_subexpr(self.dialect.prec_value(Precedence::Like))?;
307                    let escape = if self.consume_keyword(Keyword::ESCAPE) {
308                        Some(Box::new(
309                            self.parse_subexpr(self.dialect.prec_value(Precedence::Like))?,
310                        ))
311                    } else {
312                        None
313                    };
314                    let span = left.span().union(&pattern.span());
315                    let span = if let Some(ref esc) = escape {
316                        span.union(&esc.span())
317                    } else {
318                        span
319                    };
320                    Ok(Expr::new(
321                        ExprKind::Like {
322                            expr: Box::new(left),
323                            pattern: Box::new(pattern),
324                            escape,
325                            negated: false,
326                        },
327                        span,
328                    ))
329                }
330                Keyword::IN => {
331                    self.advance();
332                    self.expect_token("'('", |t| matches!(t, Token::LParen))?;
333                    let mut list = Vec::new();
334                    if !matches!(self.peek().token, Token::RParen) {
335                        loop {
336                            list.push(self.parse_subexpr(PREC_UNKNOWN)?);
337                            if matches!(self.peek().token, Token::Comma) {
338                                self.advance();
339                                continue;
340                            }
341                            break;
342                        }
343                    }
344                    let end_span = self
345                        .expect_token("')'", |t| matches!(t, Token::RParen))?
346                        .span;
347                    let span = left.span().union(&end_span);
348                    Ok(Expr::new(
349                        ExprKind::InList {
350                            expr: Box::new(left),
351                            list,
352                            negated: false,
353                        },
354                        span,
355                    ))
356                }
357                Keyword::IS => {
358                    self.advance();
359                    let negated = self.consume_keyword(Keyword::NOT);
360                    self.expect_keyword("NULL", Keyword::NULL)?;
361                    let span = left.span().union(&self.prev().unwrap().span);
362                    Ok(Expr::new(
363                        ExprKind::IsNull {
364                            expr: Box::new(left),
365                            negated,
366                        },
367                        span,
368                    ))
369                }
370                Keyword::NOT => {
371                    // NOT BETWEEN / NOT LIKE / NOT IN
372                    if let Some(next_kw) = self.peek_keyword_ahead(1) {
373                        match next_kw {
374                            Keyword::BETWEEN => {
375                                self.advance(); // NOT
376                                self.advance(); // BETWEEN
377                                let low = self
378                                    .parse_subexpr(self.dialect.prec_value(Precedence::Between))?;
379                                self.expect_keyword("AND", Keyword::AND)?;
380                                let high = self
381                                    .parse_subexpr(self.dialect.prec_value(Precedence::Between))?;
382                                let span = left.span().union(&high.span());
383                                return Ok(Expr::new(
384                                    ExprKind::Between {
385                                        expr: Box::new(left),
386                                        low: Box::new(low),
387                                        high: Box::new(high),
388                                        negated: true,
389                                    },
390                                    span,
391                                ));
392                            }
393                            Keyword::LIKE => {
394                                self.advance(); // NOT
395                                self.advance(); // LIKE
396                                let pattern =
397                                    self.parse_subexpr(self.dialect.prec_value(Precedence::Like))?;
398                                let escape = if self.consume_keyword(Keyword::ESCAPE) {
399                                    Some(Box::new(self.parse_subexpr(
400                                        self.dialect.prec_value(Precedence::Like),
401                                    )?))
402                                } else {
403                                    None
404                                };
405                                let span = left.span().union(&pattern.span());
406                                let span = if let Some(ref esc) = escape {
407                                    span.union(&esc.span())
408                                } else {
409                                    span
410                                };
411                                return Ok(Expr::new(
412                                    ExprKind::Like {
413                                        expr: Box::new(left),
414                                        pattern: Box::new(pattern),
415                                        escape,
416                                        negated: true,
417                                    },
418                                    span,
419                                ));
420                            }
421                            Keyword::IN => {
422                                self.advance(); // NOT
423                                self.advance(); // IN
424                                self.expect_token("'('", |t| matches!(t, Token::LParen))?;
425                                let mut list = Vec::new();
426                                if !matches!(self.peek().token, Token::RParen) {
427                                    loop {
428                                        list.push(self.parse_subexpr(PREC_UNKNOWN)?);
429                                        if matches!(self.peek().token, Token::Comma) {
430                                            self.advance();
431                                            continue;
432                                        }
433                                        break;
434                                    }
435                                }
436                                let end_span = self
437                                    .expect_token("')'", |t| matches!(t, Token::RParen))?
438                                    .span;
439                                let span = left.span().union(&end_span);
440                                return Ok(Expr::new(
441                                    ExprKind::InList {
442                                        expr: Box::new(left),
443                                        list,
444                                        negated: true,
445                                    },
446                                    span,
447                                ));
448                            }
449                            _ => {}
450                        }
451                    }
452                    Err(ParserError::UnexpectedToken {
453                        line: op_token.span.start.line,
454                        column: op_token.span.start.column,
455                        expected: "BETWEEN, LIKE, IN".into(),
456                        found: "NOT".into(),
457                    })
458                }
459                _ => Err(ParserError::UnexpectedToken {
460                    line: op_token.span.start.line,
461                    column: op_token.span.start.column,
462                    expected: "operator".into(),
463                    found: format!("{:?}", op_token.token),
464                }),
465            },
466            _ => Err(ParserError::UnexpectedToken {
467                line: op_token.span.start.line,
468                column: op_token.span.start.column,
469                expected: "operator".into(),
470                found: format!("{:?}", op_token.token),
471            }),
472        }
473    }
474}