solidb 1.2.2

A lightweight, high-performance structured database server written in Rust.
//! Primary expression parsing for SDBQL.
//!
//! Handles:
//! - Literals: integers, floats, strings, booleans, null
//! - Variables and identifiers
//! - Function calls
//! - Bind variables (@name)
//! - Parenthesized expressions and subqueries
//! - Postfix operations: field access (.), optional chaining (?.), array indexing ([])

use crate::error::{DbError, DbResult};
use crate::sdbql::ast::Expression;
use crate::sdbql::lexer::Token;
use crate::sdbql::parser::Parser;
use serde_json::Value;

impl Parser {
    /// Parse postfix expression: field access, optional chaining, array indexing
    pub(super) fn parse_postfix_expression(&mut self) -> DbResult<Expression> {
        let mut expr = self.parse_primary_expression()?;

        loop {
            match self.current_token() {
                Token::Dot => {
                    expr = self.parse_field_access(expr)?;
                }
                Token::QuestionDot => {
                    expr = self.parse_optional_field_access(expr)?;
                }
                Token::LeftBracket => {
                    expr = self.parse_bracket_access(expr)?;
                }
                _ => break,
            }
        }

        Ok(expr)
    }

    /// Parse field access: expr.field
    fn parse_field_access(&mut self, base: Expression) -> DbResult<Expression> {
        self.advance(); // consume '.'

        if let Some(field_name) = self.get_field_name() {
            self.advance();
            Ok(Expression::FieldAccess(Box::new(base), field_name))
        } else {
            Err(DbError::ParseError(format!(
                "Expected field name after '.', found {:?}. \
                 If the field name conflicts with a reserved word, \
                 use bracket notation: doc[\"field\"].",
                self.current_token()
            )))
        }
    }

    /// Parse optional field access: expr?.field
    fn parse_optional_field_access(&mut self, base: Expression) -> DbResult<Expression> {
        self.advance(); // consume '?.'

        if let Some(field_name) = self.get_field_name() {
            self.advance();
            Ok(Expression::OptionalFieldAccess(Box::new(base), field_name))
        } else {
            Err(DbError::ParseError(format!(
                "Expected field name after '?.', found {:?}. \
                 If the field name conflicts with a reserved word, \
                 use bracket notation: doc[\"field\"].",
                self.current_token()
            )))
        }
    }

    /// Parse bracket access: expr[index] or expr[*]
    fn parse_bracket_access(&mut self, base: Expression) -> DbResult<Expression> {
        self.advance(); // consume '['

        // Check for [*] array spread syntax
        if matches!(self.current_token(), Token::Star) {
            return self.parse_array_spread_access(base);
        }

        let index_expr = self.with_in_allowed(|p| p.parse_expression())?;
        self.expect(Token::RightBracket)?;

        // Determine access type based on index expression
        Ok(match &index_expr {
            Expression::Literal(Value::Number(_)) => {
                // Numeric index: array access
                Expression::ArrayAccess(Box::new(base), Box::new(index_expr))
            }
            Expression::Literal(Value::String(s)) => {
                // String literal: static field access
                Expression::FieldAccess(Box::new(base), s.clone())
            }
            _ => {
                // Dynamic field access: doc[@field], doc[someVar], etc.
                Expression::DynamicFieldAccess(Box::new(base), Box::new(index_expr))
            }
        })
    }

    /// Parse array expansion after `[`: `[*]`, `[*].path`, `[**]`, and the
    /// inline forms `[* FILTER cond LIMIT [off,] n RETURN proj]`.
    ///
    /// Plain `[*]` / `[*].path` keep their [`Expression::ArraySpreadAccess`]
    /// node (and its evaluation); anything else becomes an
    /// [`Expression::ArrayInline`].
    fn parse_array_spread_access(&mut self, base: Expression) -> DbResult<Expression> {
        let mut depth = 0usize;
        while matches!(self.current_token(), Token::Star) {
            self.advance();
            depth += 1;
        }

        // Operations come in AQL's order: FILTER, then LIMIT, then RETURN.
        // `CURRENT` names the element inside them.
        let (filter, limit, projection) = self.with_in_allowed(|p| {
            let filter = if matches!(p.current_token(), Token::Filter) {
                p.advance();
                Some(Box::new(p.parse_expression()?))
            } else {
                None
            };
            let limit = if matches!(p.current_token(), Token::Limit) {
                p.advance();
                let first = p.parse_expression()?;
                if matches!(p.current_token(), Token::Comma) {
                    p.advance();
                    let count = p.parse_expression()?;
                    Some((Box::new(first), Box::new(count)))
                } else {
                    Some((
                        Box::new(Expression::Literal(Value::Number(0.into()))),
                        Box::new(first),
                    ))
                }
            } else {
                None
            };
            let projection = if matches!(p.current_token(), Token::Return) {
                p.advance();
                Some(Box::new(p.parse_expression()?))
            } else {
                None
            };
            Ok((filter, limit, projection))
        })?;

        if !matches!(self.current_token(), Token::RightBracket) {
            return Err(DbError::ParseError(format!(
                "Expected ']' to close the array expansion (after [*, only FILTER, LIMIT \
                 and RETURN may follow, in that order), found {:?}",
                self.current_token()
            )));
        }
        self.advance(); // consume ']'

        // Collect subsequent dot-separated field path
        let field_path = if matches!(self.current_token(), Token::Dot) {
            let mut path = String::new();
            while matches!(self.current_token(), Token::Dot) {
                self.advance();
                if let Some(name) = Self::token_to_field_name(self.current_token()) {
                    if !path.is_empty() {
                        path.push('.');
                    }
                    path.push_str(&name);
                    self.advance();
                } else {
                    break;
                }
            }
            if path.is_empty() {
                None
            } else {
                Some(path)
            }
        } else {
            None
        };

        if depth == 1 && filter.is_none() && limit.is_none() && projection.is_none() {
            return Ok(Expression::ArraySpreadAccess(Box::new(base), field_path));
        }

        Ok(Expression::ArrayInline {
            base: Box::new(base),
            depth,
            filter,
            limit,
            projection,
            field_path,
        })
    }

    /// Parse primary expression (highest precedence)
    pub(super) fn parse_primary_expression(&mut self) -> DbResult<Expression> {
        match self.current_token() {
            Token::Identifier(ref name) => {
                // Quantifier: NONE x IN array SATISFIES condition
                // (also accepts the parenthesized form NONE(x IN ... SATISFIES ...))
                if name.eq_ignore_ascii_case("NONE") && self.is_none_quantifier() {
                    let parenthesized = matches!(self.peek_token(1), Token::LeftParen);
                    self.advance(); // consume NONE
                    if parenthesized {
                        self.advance(); // consume (
                    }
                    return self.parse_quantifier_expression_after_keyword("NONE", parenthesized);
                }
                self.parse_identifier_expression(name.clone())
            }
            Token::Any => self.parse_quantifier_expression("ANY"),
            Token::Count => self.parse_keyword_as_function("COUNT"),
            Token::Left => self.parse_keyword_as_function_no_window(
                "LEFT",
                "Unexpected token in expression: Left",
            ),
            Token::Right => self.parse_keyword_as_function_no_window(
                "RIGHT",
                "Unexpected token in expression: Right",
            ),
            Token::Like => self.parse_keyword_as_function_no_window(
                "LIKE",
                "Unexpected token in expression: Like",
            ),
            Token::Replace => self.parse_keyword_as_function_no_window(
                "REPLACE",
                "Unexpected token in expression: Replace",
            ),
            Token::Join => self.parse_keyword_as_function_no_window(
                "JOIN",
                "Unexpected token in expression: Join",
            ),
            Token::Filter => self.parse_keyword_as_function_no_window(
                "FILTER",
                "Unexpected token in expression: Filter",
            ),
            Token::Integer(n) => self.parse_integer(*n),
            Token::Float(f) => self.parse_float(*f),
            Token::String(s) => self.parse_string(s.clone()),
            Token::True => self.parse_boolean(true),
            Token::False => self.parse_boolean(false),
            Token::Null => self.parse_null(),
            Token::BindVar(name) => self.parse_bind_variable(name.clone()),
            Token::LeftBrace => self.parse_object_expression(),
            Token::LeftBracket => self.parse_array_expression(),
            Token::LeftParen => self.parse_parenthesized_expression(),
            Token::For | Token::Let => self.parse_unparenthesized_subquery(),
            Token::Case => self.parse_case_expression(),
            Token::TemplateString(parts) => {
                let parts = parts.clone();
                self.advance();
                self.parse_template_string(parts)
            }
            _ => Err(DbError::ParseError(format!(
                "Unexpected token in expression: {:?}",
                self.current_token()
            ))),
        }
    }

    /// Detect the NONE quantifier form: `NONE x IN ...` or `NONE(x IN ...)`.
    /// A plain function call like `NONE(arr)` or `NONE(arr, x -> cond)` does
    /// not match (the token after the variable must be IN).
    fn is_none_quantifier(&self) -> bool {
        // NONE x IN ...
        if matches!(self.peek_token(1), Token::Identifier(_))
            && matches!(self.peek_token(2), Token::In)
        {
            return true;
        }
        // NONE(x IN ...)
        matches!(self.peek_token(1), Token::LeftParen)
            && matches!(self.peek_token(2), Token::Identifier(_))
            && matches!(self.peek_token(3), Token::In)
    }

    /// Parse identifier: variable or function call
    fn parse_identifier_expression(&mut self, name: String) -> DbResult<Expression> {
        // Check for lambda: x -> expr
        if matches!(self.peek_token(1), Token::Arrow) {
            return self.parse_lambda_expression();
        }

        self.advance();

        // Check if this is a function call
        if matches!(self.current_token(), Token::LeftParen) {
            self.advance(); // consume '('
            let args = self.parse_function_call_args()?;

            // Check for OVER clause - if present, this is a window function
            if matches!(self.current_token(), Token::Over) {
                return self.parse_window_function(name, args);
            }

            // Function names are case-insensitive; normalising here means
            // every consumer (dispatch, EXPLAIN, the query cache, the
            // aggregation fast paths) sees one spelling.
            Ok(Expression::FunctionCall {
                name: name.to_uppercase(),
                args,
            })
        } else {
            Ok(Expression::Variable(name))
        }
    }

    /// Parse integer literal
    fn parse_integer(&mut self, n: i64) -> DbResult<Expression> {
        self.advance();
        Ok(Expression::Literal(Value::Number(
            serde_json::Number::from(n),
        )))
    }

    /// Parse float literal
    fn parse_float(&mut self, f: f64) -> DbResult<Expression> {
        // `"1e999".parse::<f64>()` is `Ok(inf)`, which JSON cannot hold.
        let Some(n) = serde_json::Number::from_f64(f) else {
            return Err(DbError::ParseError(
                "Number literal is out of range".to_string(),
            ));
        };
        self.advance();
        Ok(Expression::Literal(Value::Number(n)))
    }

    /// Parse string literal
    fn parse_string(&mut self, s: String) -> DbResult<Expression> {
        self.advance();
        Ok(Expression::Literal(Value::String(s)))
    }

    /// Parse boolean literal
    fn parse_boolean(&mut self, value: bool) -> DbResult<Expression> {
        self.advance();
        Ok(Expression::Literal(Value::Bool(value)))
    }

    /// Parse null literal
    fn parse_null(&mut self) -> DbResult<Expression> {
        self.advance();
        Ok(Expression::Literal(Value::Null))
    }

    /// Parse bind variable (@name)
    fn parse_bind_variable(&mut self, name: String) -> DbResult<Expression> {
        self.advance();
        Ok(Expression::BindVariable(name))
    }

    /// Parse parenthesized expression or subquery
    fn parse_parenthesized_expression(&mut self) -> DbResult<Expression> {
        // Check for lambda: (params) -> expr
        if self.is_lambda_params() {
            return self.parse_lambda_expression();
        }

        self.advance(); // consume '('

        self.with_in_allowed(|p| {
            // Check if this is a subquery (starts with FOR or LET)
            if matches!(p.current_token(), Token::For | Token::Let) {
                let subquery = p.parse_query(false)?;
                p.expect(Token::RightParen)?;
                Ok(Expression::Subquery(Box::new(subquery)))
            } else {
                let expr = p.parse_expression()?;
                p.expect(Token::RightParen)?;
                Ok(expr)
            }
        })
    }

    /// Parse unparenthesized subquery (FOR ... or LET ...)
    fn parse_unparenthesized_subquery(&mut self) -> DbResult<Expression> {
        let subquery = self.parse_query(false)?;
        Ok(Expression::Subquery(Box::new(subquery)))
    }
}