Skip to main content

sqry_core/query/
parser_new.rs

1//! Parser for the query language
2//!
3//! This module implements a recursive descent parser that builds an Abstract Syntax Tree (AST)
4//! from a stream of tokens. The parser respects operator precedence: NOT > AND > OR.
5
6use crate::config::buffers::{max_predicates, max_query_length};
7use crate::query::error::ParseError;
8use crate::query::lexer::{Token, TokenType};
9use crate::query::types::{
10    Condition, Expr, Field, JoinEdgeKind, JoinExpr, Operator, PipelineQuery, PipelineStage, Query,
11    RegexFlags, RegexValue, Span, Value,
12};
13
14/// Parser for query language
15pub struct Parser {
16    /// Token stream
17    tokens: Vec<Token>,
18    /// Current position in token stream
19    position: usize,
20}
21
22impl Parser {
23    /// Create a new parser from a token stream
24    #[must_use]
25    pub fn new(tokens: Vec<Token>) -> Self {
26        Self {
27            tokens,
28            position: 0,
29        }
30    }
31
32    /// Parse a query string into a Query AST
33    ///
34    /// This is a convenience method that lexes and parses in one call.
35    /// Enforces `SQRY_MAX_QUERY_LENGTH` and `SQRY_MAX_PREDICATES` limits.
36    ///
37    /// # Errors
38    ///
39    /// Returns [`QueryError`](crate::query::error::QueryError) when lexing or parsing fails.
40    pub fn parse_query(input: &str) -> Result<Query, crate::query::error::QueryError> {
41        use crate::query::lexer::with_lexer;
42
43        let trimmed = input.trim();
44        if trimmed.is_empty() {
45            return Err(ParseError::EmptyQuery.into());
46        }
47
48        let max_len = max_query_length();
49        let input_len = input.len();
50        if input_len > max_len {
51            return Err(ParseError::InvalidSyntax {
52                message: format!(
53                    "Query too long: {input_len} bytes exceeds {max_len} byte limit. \
54                     Adjust SQRY_MAX_QUERY_LENGTH environment variable if needed."
55                ),
56                span: Span::new(0, 0),
57            }
58            .into());
59        }
60
61        let tokens = with_lexer(input, |batch| Ok(batch.into_vec()))?;
62
63        let mut parser = Parser::new(tokens);
64        let query = parser.parse()?;
65
66        let max_preds = max_predicates();
67        let predicate_count = count_conditions(&query.root);
68        if predicate_count > max_preds {
69            return Err(ParseError::InvalidSyntax {
70                message: format!(
71                    "Too many predicates: {predicate_count} exceeds {max_preds} limit. \
72                     Adjust SQRY_MAX_PREDICATES environment variable if needed."
73                ),
74                span: Span::new(0, 0),
75            }
76            .into());
77        }
78
79        Ok(query)
80    }
81
82    /// Parse a query string into a `PipelineQuery` if it contains pipe operators,
83    /// or return `None` if no pipes are present (use `parse_query` instead).
84    ///
85    /// # Errors
86    ///
87    /// Returns [`QueryError`](crate::query::error::QueryError) when lexing or parsing fails.
88    pub fn parse_pipeline_query(
89        input: &str,
90    ) -> Result<Option<PipelineQuery>, crate::query::error::QueryError> {
91        use crate::query::lexer::with_lexer;
92
93        let trimmed = input.trim();
94        if trimmed.is_empty() {
95            return Err(ParseError::EmptyQuery.into());
96        }
97
98        // Quick check: no pipe → no pipeline
99        if !trimmed.contains('|') {
100            return Ok(None);
101        }
102
103        let tokens = with_lexer(input, |batch| Ok(batch.into_vec()))?;
104
105        let mut parser = Parser::new(tokens);
106        let pipeline = parser.parse_pipeline()?;
107        Ok(pipeline)
108    }
109
110    /// Parse the token stream into a Query AST
111    ///
112    /// # Errors
113    ///
114    /// Returns [`ParseError`] when the token stream is invalid.
115    pub fn parse(&mut self) -> Result<Query, ParseError> {
116        if self.is_at_end() {
117            return Err(ParseError::EmptyQuery);
118        }
119
120        let start_span = self.peek().span.clone();
121        let root = self.parse_join()?;
122
123        // Ensure we've consumed all tokens except EOF and Pipe
124        if !self.is_at_end() && !matches!(self.peek().token_type, TokenType::Pipe) {
125            let token = self.peek();
126            return Err(ParseError::UnexpectedToken {
127                token: token.clone(),
128                expected: "end of query".to_string(),
129            });
130        }
131
132        let end_span = self.tokens[self.position - 1].span.clone();
133        let span = start_span.merge(&end_span);
134
135        Ok(Query { root, span })
136    }
137
138    /// Parse a pipeline query: `base_query | stage1 | stage2 ...`
139    ///
140    /// Returns `None` if no pipe operators are found.
141    fn parse_pipeline(&mut self) -> Result<Option<PipelineQuery>, ParseError> {
142        if self.is_at_end() {
143            return Err(ParseError::EmptyQuery);
144        }
145
146        let start_span = self.peek().span.clone();
147        let query = self.parse()?;
148
149        // Check for pipe
150        if !self.match_token(&TokenType::Pipe) {
151            return Ok(None);
152        }
153
154        let mut stages = Vec::new();
155        stages.push(self.parse_pipeline_stage()?);
156
157        while self.match_token(&TokenType::Pipe) {
158            stages.push(self.parse_pipeline_stage()?);
159        }
160
161        if !self.is_at_end() {
162            let token = self.peek();
163            return Err(ParseError::UnexpectedToken {
164                token: token.clone(),
165                expected: "end of query or '|'".to_string(),
166            });
167        }
168
169        let end_span = self.tokens[self.position - 1].span.clone();
170        let span = start_span.merge(&end_span);
171
172        Ok(Some(PipelineQuery {
173            query,
174            stages,
175            span,
176        }))
177    }
178
179    /// Parse a single pipeline stage: `count`, `group_by <field>`, `top <N> <field>`, `stats`
180    fn parse_pipeline_stage(&mut self) -> Result<PipelineStage, ParseError> {
181        let token = self.advance().clone();
182        match &token.token_type {
183            TokenType::Word(w) | TokenType::Identifier(w) => match w.to_lowercase().as_str() {
184                "count" => Ok(PipelineStage::Count),
185                "stats" => Ok(PipelineStage::Stats),
186                "group_by" => {
187                    let field_token = self.advance().clone();
188                    let field_name = match &field_token.token_type {
189                        TokenType::Word(f) | TokenType::Identifier(f) => f.clone(),
190                        _ => {
191                            return Err(ParseError::InvalidSyntax {
192                                message: "Expected field name after 'group_by'".to_string(),
193                                span: field_token.span,
194                            });
195                        }
196                    };
197                    Ok(PipelineStage::GroupBy {
198                        field: Field::new(field_name),
199                    })
200                }
201                "top" => {
202                    let n_token = self.advance().clone();
203                    let n = match &n_token.token_type {
204                        TokenType::NumberLiteral(n) => {
205                            usize::try_from(*n).map_err(|_| ParseError::InvalidSyntax {
206                                message: format!("Invalid count for 'top': {n}"),
207                                span: n_token.span.clone(),
208                            })?
209                        }
210                        _ => {
211                            return Err(ParseError::InvalidSyntax {
212                                message: "Expected number after 'top'".to_string(),
213                                span: n_token.span,
214                            });
215                        }
216                    };
217                    let field_token = self.advance().clone();
218                    let field_name = match &field_token.token_type {
219                        TokenType::Word(f) | TokenType::Identifier(f) => f.clone(),
220                        _ => {
221                            return Err(ParseError::InvalidSyntax {
222                                message: "Expected field name after 'top <N>'".to_string(),
223                                span: field_token.span,
224                            });
225                        }
226                    };
227                    Ok(PipelineStage::Top {
228                        n,
229                        field: Field::new(field_name),
230                    })
231                }
232                other => Err(ParseError::InvalidSyntax {
233                    message: format!(
234                        "Unknown pipeline stage: '{other}'. Expected: count, group_by, top, stats"
235                    ),
236                    span: token.span,
237                }),
238            },
239            _ => Err(ParseError::InvalidSyntax {
240                message: "Expected pipeline stage name after '|'".to_string(),
241                span: token.span,
242            }),
243        }
244    }
245
246    /// Parse join expression (lowest precedence, above OR)
247    /// `join_expr` ::= `or_expr` (CALLS | IMPORTS | INHERITS | IMPLEMENTS) `or_expr`
248    fn parse_join(&mut self) -> Result<Expr, ParseError> {
249        let left = self.parse_or()?;
250
251        // Check for join keyword (case-insensitive word matching)
252        let join_edge = match &self.peek().token_type {
253            TokenType::Word(w) => match w.to_uppercase().as_str() {
254                "CALLS" => Some(JoinEdgeKind::Calls),
255                "IMPORTS" => Some(JoinEdgeKind::Imports),
256                "INHERITS" => Some(JoinEdgeKind::Inherits),
257                "IMPLEMENTS" => Some(JoinEdgeKind::Implements),
258                _ => None,
259            },
260            _ => None,
261        };
262
263        if let Some(edge) = join_edge {
264            let join_token = self.advance().clone();
265            let right = self.parse_or()?;
266
267            let span = if let Some(left_span) = expr_span(&left) {
268                left_span.merge(&join_token.span)
269            } else {
270                join_token.span
271            };
272
273            Ok(Expr::Join(JoinExpr {
274                left: Box::new(left),
275                edge,
276                right: Box::new(right),
277                span,
278            }))
279        } else {
280            Ok(left)
281        }
282    }
283
284    /// Parse OR expression (lowest precedence)
285    /// `or_expr` ::= `and_expr` (OR `and_expr`)*
286    fn parse_or(&mut self) -> Result<Expr, ParseError> {
287        let mut operands = vec![self.parse_and()?];
288
289        while self.match_token(&TokenType::Or) {
290            operands.push(self.parse_and()?);
291        }
292
293        if operands.len() == 1 {
294            operands
295                .into_iter()
296                .next()
297                .ok_or_else(|| ParseError::InvalidSyntax {
298                    message: "Internal parser error: OR expression has no operands (this should never happen)".to_string(),
299                    // Span is placeholder since this error is impossible and has no meaningful location
300                    span: Span::new(0, 0),
301                })
302        } else {
303            Ok(Expr::Or(operands))
304        }
305    }
306
307    /// Parse AND expression (medium precedence)
308    /// `and_expr` ::= `not_expr` (AND `not_expr`)*
309    fn parse_and(&mut self) -> Result<Expr, ParseError> {
310        let mut operands = vec![self.parse_not()?];
311
312        while self.match_token(&TokenType::And) {
313            operands.push(self.parse_not()?);
314        }
315
316        if operands.len() == 1 {
317            operands
318                .into_iter()
319                .next()
320                .ok_or_else(|| ParseError::InvalidSyntax {
321                    message: "Internal parser error: AND expression has no operands (this should never happen)".to_string(),
322                    // Span is placeholder since this error is impossible and has no meaningful location
323                    span: Span::new(0, 0),
324                })
325        } else {
326            Ok(Expr::And(operands))
327        }
328    }
329
330    /// Parse NOT expression (highest precedence)
331    /// `not_expr` ::= NOT `not_expr` | primary
332    fn parse_not(&mut self) -> Result<Expr, ParseError> {
333        if self.match_token(&TokenType::Not) {
334            let operand = self.parse_not()?; // Right-associative
335            Ok(Expr::Not(Box::new(operand)))
336        } else {
337            self.parse_primary()
338        }
339    }
340
341    /// Parse primary: condition or grouped expression
342    /// primary ::= LPAREN `or_expr` RPAREN | condition
343    fn parse_primary(&mut self) -> Result<Expr, ParseError> {
344        // Check for parentheses
345        if self.match_token(&TokenType::LParen) {
346            let lparen_span = self.tokens[self.position - 1].span.clone();
347            let expr = self.parse_or()?;
348
349            if !self.match_token(&TokenType::RParen) {
350                return Err(ParseError::UnmatchedParen {
351                    open_span: lparen_span,
352                    eof: self.is_at_end(),
353                });
354            }
355
356            return Ok(expr);
357        }
358
359        // Shorthand: bare word/string/regex/variable defaults to name predicate
360        let token_type = self.peek().token_type.clone();
361        match token_type {
362            TokenType::Word(word) => {
363                let token = self.advance().clone();
364                Ok(Expr::Condition(Condition {
365                    field: Field::new("name"),
366                    operator: Operator::Regex,
367                    value: Value::Regex(RegexValue {
368                        pattern: word,
369                        flags: RegexFlags::default(),
370                    }),
371                    span: token.span,
372                }))
373            }
374            TokenType::StringLiteral(value) => {
375                let token = self.advance().clone();
376                Ok(Expr::Condition(Condition {
377                    field: Field::new("name"),
378                    operator: Operator::Equal,
379                    value: Value::String(value),
380                    span: token.span,
381                }))
382            }
383            TokenType::RegexLiteral { pattern, flags } => {
384                let token = self.advance().clone();
385                Ok(Expr::Condition(Condition {
386                    field: Field::new("name"),
387                    operator: Operator::Regex,
388                    value: Value::Regex(RegexValue { pattern, flags }),
389                    span: token.span,
390                }))
391            }
392            TokenType::Variable(name) => {
393                let token = self.advance().clone();
394                Ok(Expr::Condition(Condition {
395                    field: Field::new("name"),
396                    operator: Operator::Equal,
397                    value: Value::Variable(name),
398                    span: token.span,
399                }))
400            }
401            _ => self.parse_condition(),
402        }
403    }
404
405    /// Parse condition: field operator value
406    /// condition ::= identifier operator value
407    ///
408    /// For relation fields (callers, callees, imports, exports, impl, references),
409    /// a `(` after `:` introduces a subquery value.
410    fn parse_condition(&mut self) -> Result<Expr, ParseError> {
411        // Parse field name
412        let field_token = self.advance().clone();
413        let field = match &field_token.token_type {
414            TokenType::Identifier(name) => Field::new(name.clone()),
415            _ => {
416                return Err(ParseError::ExpectedIdentifier { token: field_token });
417            }
418        };
419
420        // Parse operator
421        let operator_token = self.advance().clone();
422        let mut operator = match &operator_token.token_type {
423            TokenType::Colon => Operator::Equal,
424            TokenType::RegexOp => Operator::Regex,
425            TokenType::Greater => Operator::Greater,
426            TokenType::Less => Operator::Less,
427            TokenType::GreaterEq => Operator::GreaterEq,
428            TokenType::LessEq => Operator::LessEq,
429            _ => {
430                return Err(ParseError::ExpectedOperator {
431                    token: operator_token,
432                });
433            }
434        };
435
436        // Check for subquery: relation_field:(inner_expression)
437        let is_relation = crate::query::types::is_relation_field(field.as_str());
438        if is_relation
439            && operator == Operator::Equal
440            && matches!(self.peek().token_type, TokenType::LParen)
441        {
442            let lparen_span = self.peek().span.clone();
443            self.advance(); // consume '('
444            let inner_expr = self.parse_or()?;
445            if !self.match_token(&TokenType::RParen) {
446                return Err(ParseError::UnmatchedParen {
447                    open_span: lparen_span,
448                    eof: self.is_at_end(),
449                });
450            }
451
452            let end_span = self.tokens[self.position - 1].span.clone();
453            let span = field_token.span.merge(&end_span);
454
455            return Ok(Expr::Condition(Condition {
456                field,
457                operator,
458                value: Value::Subquery(Box::new(inner_expr)),
459                span,
460            }));
461        }
462
463        // Parse value
464        let value_token = self.advance().clone();
465        let mut value = match &value_token.token_type {
466            TokenType::StringLiteral(s) | TokenType::Word(s) => Value::String(s.clone()),
467            TokenType::RegexLiteral { pattern, flags } => Value::Regex(RegexValue {
468                pattern: pattern.clone(),
469                flags: flags.clone(),
470            }),
471            TokenType::NumberLiteral(n) => Value::Number(*n),
472            TokenType::BooleanLiteral(b) => Value::Boolean(*b),
473            TokenType::Variable(name) => Value::Variable(name.clone()),
474            _ => {
475                return Err(ParseError::ExpectedValue { token: value_token });
476            }
477        };
478
479        if operator == Operator::Regex {
480            if let Value::String(pattern) = &value {
481                value = Value::Regex(RegexValue {
482                    pattern: pattern.clone(),
483                    flags: RegexFlags::default(),
484                });
485            }
486        } else if operator == Operator::Equal && matches!(value, Value::Regex(_)) {
487            operator = Operator::Regex;
488        }
489
490        let span = field_token.span.merge(&value_token.span);
491
492        Ok(Expr::Condition(Condition {
493            field,
494            operator,
495            value,
496            span,
497        }))
498    }
499
500    /// Check if current token matches the given type and consume it
501    fn match_token(&mut self, token_type: &TokenType) -> bool {
502        if self.check(token_type) {
503            self.advance();
504            true
505        } else {
506            false
507        }
508    }
509
510    /// Check if current token matches the given type without consuming
511    fn check(&self, token_type: &TokenType) -> bool {
512        if self.is_at_end() {
513            return false;
514        }
515
516        std::mem::discriminant(&self.peek().token_type) == std::mem::discriminant(token_type)
517    }
518
519    /// Advance to the next token and return the previous one
520    fn advance(&mut self) -> &Token {
521        if !self.is_at_end() {
522            self.position += 1;
523        }
524        &self.tokens[self.position - 1]
525    }
526
527    /// Check if we're at the end of the token stream
528    fn is_at_end(&self) -> bool {
529        matches!(self.peek().token_type, TokenType::Eof)
530    }
531
532    /// Peek at the current token without consuming
533    fn peek(&self) -> &Token {
534        &self.tokens[self.position]
535    }
536}
537
538fn count_conditions(expr: &Expr) -> usize {
539    match expr {
540        Expr::And(operands) | Expr::Or(operands) => operands.iter().map(count_conditions).sum(),
541        Expr::Not(operand) => count_conditions(operand),
542        Expr::Condition(cond) => {
543            // Count subquery conditions recursively
544            if let Value::Subquery(inner) = &cond.value {
545                1 + count_conditions(inner)
546            } else {
547                1
548            }
549        }
550        Expr::Join(join) => count_conditions(&join.left) + count_conditions(&join.right),
551    }
552}
553
554/// Extract span from an expression (best-effort for error reporting).
555fn expr_span(expr: &Expr) -> Option<Span> {
556    match expr {
557        Expr::Condition(c) => Some(c.span.clone()),
558        Expr::Join(j) => Some(j.span.clone()),
559        Expr::And(ops) | Expr::Or(ops) => {
560            let first = ops.first().and_then(expr_span)?;
561            let last = ops.last().and_then(expr_span)?;
562            Some(first.merge(&last))
563        }
564        Expr::Not(inner) => expr_span(inner),
565    }
566}
567
568#[cfg(test)]
569mod tests {
570    use super::*;
571    use crate::query::lexer::with_lexer;
572    use crate::query::types::RegexFlags;
573
574    fn parse(input: &str) -> Result<Query, ParseError> {
575        let tokens = with_lexer(input, |batch| Ok(batch.into_vec())).unwrap();
576        let mut parser = Parser::new(tokens);
577        parser.parse()
578    }
579
580    #[test]
581    fn parse_query_reuses_thread_local_pool() {
582        crate::query::lexer::configure_pool_for_tests(
583            4,
584            crate::query::lexer::ShrinkPolicy::default(),
585        );
586
587        let (before_stash, before_in_flight, _) = crate::query::lexer::pool_stats_for_tests();
588        assert_eq!(before_stash, 0);
589        assert_eq!(before_in_flight, 0);
590
591        parse("kind:function").unwrap();
592        parse("name:test").unwrap();
593
594        let (after_stash, after_in_flight, max_size) = crate::query::lexer::pool_stats_for_tests();
595        assert!(max_size >= 1);
596        assert!(after_stash >= 1);
597        assert_eq!(after_in_flight, 0);
598
599        crate::query::lexer::reset_pool_to_default_for_tests();
600    }
601
602    #[test]
603    fn test_parse_simple_condition() {
604        let query = parse("kind:function").unwrap();
605
606        match query.root {
607            Expr::Condition(cond) => {
608                assert_eq!(cond.field.as_str(), "kind");
609                assert_eq!(cond.operator, Operator::Equal);
610                assert!(matches!(cond.value, Value::String(ref s) if s == "function"));
611            }
612            _ => panic!("Expected Condition"),
613        }
614    }
615
616    #[test]
617    fn test_parse_and() {
618        let query = parse("kind:function AND async:true").unwrap();
619
620        match query.root {
621            Expr::And(operands) => {
622                assert_eq!(operands.len(), 2);
623
624                match &operands[0] {
625                    Expr::Condition(cond) => {
626                        assert_eq!(cond.field.as_str(), "kind");
627                    }
628                    _ => panic!("Expected Condition"),
629                }
630
631                match &operands[1] {
632                    Expr::Condition(cond) => {
633                        assert_eq!(cond.field.as_str(), "async");
634                    }
635                    _ => panic!("Expected Condition"),
636                }
637            }
638            _ => panic!("Expected And"),
639        }
640    }
641
642    #[test]
643    fn test_parse_or() {
644        let query = parse("kind:function OR kind:class").unwrap();
645
646        match query.root {
647            Expr::Or(operands) => {
648                assert_eq!(operands.len(), 2);
649            }
650            _ => panic!("Expected Or"),
651        }
652    }
653
654    #[test]
655    fn test_parse_not() {
656        let query = parse("NOT kind:function").unwrap();
657
658        match query.root {
659            Expr::Not(operand) => match *operand {
660                Expr::Condition(cond) => {
661                    assert_eq!(cond.field.as_str(), "kind");
662                }
663                _ => panic!("Expected Condition inside Not"),
664            },
665            _ => panic!("Expected Not"),
666        }
667    }
668
669    #[test]
670    fn test_precedence_or_and() {
671        // A OR B AND C should parse as: Or(A, And(B, C))
672        let query = parse("a:1 OR b:2 AND c:3").unwrap();
673
674        match query.root {
675            Expr::Or(operands) => {
676                assert_eq!(operands.len(), 2);
677
678                // First operand should be a condition
679                assert!(matches!(operands[0], Expr::Condition(_)));
680
681                // Second operand should be an AND
682                match &operands[1] {
683                    Expr::And(and_operands) => {
684                        assert_eq!(and_operands.len(), 2);
685                    }
686                    _ => panic!("Expected And as second operand of Or"),
687                }
688            }
689            _ => panic!("Expected Or"),
690        }
691    }
692
693    #[test]
694    fn test_precedence_not_and() {
695        // NOT A AND B should parse as: And(Not(A), B)
696        let query = parse("NOT a:1 AND b:2").unwrap();
697
698        match query.root {
699            Expr::And(operands) => {
700                assert_eq!(operands.len(), 2);
701
702                // First operand should be NOT
703                assert!(matches!(operands[0], Expr::Not(_)));
704
705                // Second operand should be a condition
706                assert!(matches!(operands[1], Expr::Condition(_)));
707            }
708            _ => panic!("Expected And"),
709        }
710    }
711
712    #[test]
713    fn test_parentheses_simple() {
714        let query = parse("(kind:function)").unwrap();
715
716        match query.root {
717            Expr::Condition(cond) => {
718                assert_eq!(cond.field.as_str(), "kind");
719            }
720            _ => panic!("Expected Condition"),
721        }
722    }
723
724    #[test]
725    fn test_parentheses_override_precedence() {
726        // (A OR B) AND C should parse as: And(Or(A, B), C)
727        let query = parse("(a:1 OR b:2) AND c:3").unwrap();
728
729        match query.root {
730            Expr::And(operands) => {
731                assert_eq!(operands.len(), 2);
732
733                // First operand should be OR
734                match &operands[0] {
735                    Expr::Or(or_operands) => {
736                        assert_eq!(or_operands.len(), 2);
737                    }
738                    _ => panic!("Expected Or as first operand"),
739                }
740
741                // Second operand should be a condition
742                assert!(matches!(operands[1], Expr::Condition(_)));
743            }
744            _ => panic!("Expected And"),
745        }
746    }
747
748    #[test]
749    fn test_nested_parentheses() {
750        let query = parse("((a:1))").unwrap();
751
752        match query.root {
753            Expr::Condition(cond) => {
754                assert_eq!(cond.field.as_str(), "a");
755            }
756            _ => panic!("Expected Condition"),
757        }
758    }
759
760    #[test]
761    fn test_complex_query() {
762        let query =
763            parse("(kind:function OR kind:method) AND async:true AND NOT name~=/test/").unwrap();
764
765        match query.root {
766            Expr::And(operands) => {
767                assert_eq!(operands.len(), 3);
768
769                // First: (kind:function OR kind:method)
770                assert!(matches!(operands[0], Expr::Or(_)));
771
772                // Second: async:true
773                assert!(matches!(operands[1], Expr::Condition(_)));
774
775                // Third: NOT name~=/test/
776                assert!(matches!(operands[2], Expr::Not(_)));
777            }
778            _ => panic!("Expected And"),
779        }
780    }
781
782    #[test]
783    fn test_chained_and() {
784        let query = parse("a:1 AND b:2 AND c:3 AND d:4").unwrap();
785
786        match query.root {
787            Expr::And(operands) => {
788                assert_eq!(operands.len(), 4);
789            }
790            _ => panic!("Expected And"),
791        }
792    }
793
794    #[test]
795    fn test_chained_or() {
796        let query = parse("a:1 OR b:2 OR c:3 OR d:4").unwrap();
797
798        match query.root {
799            Expr::Or(operands) => {
800                assert_eq!(operands.len(), 4);
801            }
802            _ => panic!("Expected Or"),
803        }
804    }
805
806    #[test]
807    fn test_double_not() {
808        let query = parse("NOT NOT kind:function").unwrap();
809
810        match query.root {
811            Expr::Not(operand) => match *operand {
812                Expr::Not(inner) => match *inner {
813                    Expr::Condition(_) => {}
814                    _ => panic!("Expected Condition"),
815                },
816                _ => panic!("Expected Not"),
817            },
818            _ => panic!("Expected Not"),
819        }
820    }
821
822    #[test]
823    fn test_triple_not() {
824        let query = parse("NOT NOT NOT kind:function").unwrap();
825
826        match query.root {
827            Expr::Not(operand1) => match *operand1 {
828                Expr::Not(operand2) => match *operand2 {
829                    Expr::Not(operand3) => match *operand3 {
830                        Expr::Condition(_) => {}
831                        _ => panic!("Expected Condition"),
832                    },
833                    _ => panic!("Expected Not"),
834                },
835                _ => panic!("Expected Not"),
836            },
837            _ => panic!("Expected Not"),
838        }
839    }
840
841    #[test]
842    fn test_operators_all() {
843        // Test all operators
844        let _ = parse("a:b").unwrap(); // Equal
845        let _ = parse("a~=/b/").unwrap(); // Regex
846        let _ = parse("a>1").unwrap(); // Greater
847        let _ = parse("a<1").unwrap(); // Less
848        let _ = parse("a>=1").unwrap(); // GreaterEq
849        let _ = parse("a<=1").unwrap(); // LessEq
850    }
851
852    #[test]
853    fn test_value_types() {
854        // String literal
855        let query = parse(r#"name:"hello world""#).unwrap();
856        match query.root {
857            Expr::Condition(cond) => {
858                assert!(matches!(cond.value, Value::String(ref s) if s == "hello world"));
859            }
860            _ => panic!("Expected Condition"),
861        }
862
863        // Number
864        let query = parse("lines:42").unwrap();
865        match query.root {
866            Expr::Condition(cond) => {
867                assert!(matches!(cond.value, Value::Number(42)));
868            }
869            _ => panic!("Expected Condition"),
870        }
871
872        // Boolean
873        let query = parse("async:true").unwrap();
874        match query.root {
875            Expr::Condition(cond) => {
876                assert!(matches!(cond.value, Value::Boolean(true)));
877            }
878            _ => panic!("Expected Condition"),
879        }
880
881        // Regex
882        let query = parse("name~=/^test_/i").unwrap();
883        match query.root {
884            Expr::Condition(cond) => match &cond.value {
885                Value::Regex(regex) => {
886                    assert_eq!(regex.pattern, "^test_");
887                    assert!(regex.flags.case_insensitive);
888                }
889                _ => panic!("Expected Regex value"),
890            },
891            _ => panic!("Expected Condition"),
892        }
893    }
894
895    #[test]
896    fn test_plain_word_defaults_to_name_regex() {
897        let query = parse("Error").unwrap();
898
899        match query.root {
900            Expr::Condition(cond) => {
901                assert_eq!(cond.field.as_str(), "name");
902                assert_eq!(cond.operator, Operator::Regex);
903                match cond.value {
904                    Value::Regex(regex) => {
905                        assert_eq!(regex.pattern, "Error");
906                        assert_eq!(regex.flags, RegexFlags::default());
907                    }
908                    _ => panic!("Expected regex value"),
909                }
910            }
911            _ => panic!("Expected Condition"),
912        }
913    }
914
915    #[test]
916    fn test_plain_string_literal_defaults_to_name_equal() {
917        let query = parse(r#""Error""#).unwrap();
918
919        match query.root {
920            Expr::Condition(cond) => {
921                assert_eq!(cond.field.as_str(), "name");
922                assert_eq!(cond.operator, Operator::Equal);
923                match cond.value {
924                    Value::String(value) => assert_eq!(value, "Error"),
925                    _ => panic!("Expected string value"),
926                }
927            }
928            _ => panic!("Expected Condition"),
929        }
930    }
931
932    #[test]
933    fn test_error_empty_query() {
934        let result = parse("");
935        assert!(matches!(result, Err(ParseError::EmptyQuery)));
936    }
937
938    #[test]
939    fn test_error_unmatched_lparen() {
940        let result = parse("(kind:function");
941        assert!(matches!(result, Err(ParseError::UnmatchedParen { .. })));
942    }
943
944    #[test]
945    fn test_error_unmatched_rparen() {
946        let result = parse("kind:function)");
947        assert!(matches!(result, Err(ParseError::UnexpectedToken { .. })));
948    }
949
950    #[test]
951    fn test_error_missing_value() {
952        let result = parse("kind:");
953        assert!(matches!(result, Err(ParseError::ExpectedValue { .. })));
954    }
955
956    #[test]
957    fn test_error_incomplete_and() {
958        let result = parse("kind:function AND");
959        assert!(matches!(result, Err(ParseError::ExpectedIdentifier { .. })));
960    }
961
962    #[test]
963    fn test_error_incomplete_or() {
964        let result = parse("kind:function OR");
965        assert!(matches!(result, Err(ParseError::ExpectedIdentifier { .. })));
966    }
967
968    #[test]
969    fn test_error_incomplete_not() {
970        let result = parse("NOT");
971        assert!(matches!(result, Err(ParseError::ExpectedIdentifier { .. })));
972    }
973
974    #[test]
975    fn test_error_missing_operator() {
976        // Legacy syntax without an explicit operator should still fail,
977        // leaving the trailing token unconsumed and triggering an error.
978        let result = parse("kind function");
979        assert!(matches!(result, Err(ParseError::UnexpectedToken { .. })));
980    }
981
982    #[test]
983    fn test_precedence_complex_1() {
984        // A OR B AND C OR D should parse as: Or(A, And(B, C), D)
985        let query = parse("a:1 OR b:2 AND c:3 OR d:4").unwrap();
986
987        match query.root {
988            Expr::Or(operands) => {
989                assert_eq!(operands.len(), 3);
990
991                // First operand: condition
992                assert!(matches!(operands[0], Expr::Condition(_)));
993
994                // Second operand: AND
995                assert!(matches!(operands[1], Expr::And(_)));
996
997                // Third operand: condition
998                assert!(matches!(operands[2], Expr::Condition(_)));
999            }
1000            _ => panic!("Expected Or"),
1001        }
1002    }
1003
1004    #[test]
1005    fn test_precedence_complex_2() {
1006        // NOT A OR B AND C should parse as: Or(Not(A), And(B, C))
1007        let query = parse("NOT a:1 OR b:2 AND c:3").unwrap();
1008
1009        match query.root {
1010            Expr::Or(operands) => {
1011                assert_eq!(operands.len(), 2);
1012
1013                // First operand: NOT
1014                assert!(matches!(operands[0], Expr::Not(_)));
1015
1016                // Second operand: AND
1017                assert!(matches!(operands[1], Expr::And(_)));
1018            }
1019            _ => panic!("Expected Or"),
1020        }
1021    }
1022
1023    #[test]
1024    fn test_multiple_groups() {
1025        // (A OR B) AND (C OR D)
1026        let query = parse("(a:1 OR b:2) AND (c:3 OR d:4)").unwrap();
1027
1028        match query.root {
1029            Expr::And(operands) => {
1030                assert_eq!(operands.len(), 2);
1031
1032                // Both operands should be OR
1033                assert!(matches!(operands[0], Expr::Or(_)));
1034                assert!(matches!(operands[1], Expr::Or(_)));
1035            }
1036            _ => panic!("Expected And"),
1037        }
1038    }
1039
1040    #[test]
1041    fn test_not_with_parentheses() {
1042        // NOT (A OR B)
1043        let query = parse("NOT (a:1 OR b:2)").unwrap();
1044
1045        match query.root {
1046            Expr::Not(operand) => match *operand {
1047                Expr::Or(or_operands) => {
1048                    assert_eq!(or_operands.len(), 2);
1049                }
1050                _ => panic!("Expected Or inside Not"),
1051            },
1052            _ => panic!("Expected Not"),
1053        }
1054    }
1055
1056    #[test]
1057    fn test_deeply_nested() {
1058        // ((A AND B) OR (C AND D)) AND E
1059        let query = parse("((a:1 AND b:2) OR (c:3 AND d:4)) AND e:5").unwrap();
1060
1061        match query.root {
1062            Expr::And(operands) => {
1063                assert_eq!(operands.len(), 2);
1064
1065                // First operand should be OR with AND children
1066                match &operands[0] {
1067                    Expr::Or(or_operands) => {
1068                        assert_eq!(or_operands.len(), 2);
1069                        assert!(matches!(or_operands[0], Expr::And(_)));
1070                        assert!(matches!(or_operands[1], Expr::And(_)));
1071                    }
1072                    _ => panic!("Expected Or"),
1073                }
1074
1075                // Second operand should be condition
1076                assert!(matches!(operands[1], Expr::Condition(_)));
1077            }
1078            _ => panic!("Expected And"),
1079        }
1080    }
1081
1082    // P2-34 Phase 2: Tests for scope.* fields
1083
1084    #[test]
1085    fn test_parse_scope_type() {
1086        let query = parse("scope.type:class").unwrap();
1087
1088        match query.root {
1089            Expr::Condition(cond) => {
1090                assert_eq!(cond.field.as_str(), "scope.type");
1091                assert_eq!(cond.operator, Operator::Equal);
1092                assert!(matches!(cond.value, Value::String(ref s) if s == "class"));
1093            }
1094            _ => panic!("Expected Condition"),
1095        }
1096    }
1097
1098    #[test]
1099    fn test_parse_scope_name() {
1100        let query = parse("scope.name:UserService").unwrap();
1101
1102        match query.root {
1103            Expr::Condition(cond) => {
1104                assert_eq!(cond.field.as_str(), "scope.name");
1105                assert_eq!(cond.operator, Operator::Equal);
1106                assert!(matches!(cond.value, Value::String(ref s) if s == "UserService"));
1107            }
1108            _ => panic!("Expected Condition"),
1109        }
1110    }
1111
1112    #[test]
1113    fn test_parse_scope_parent() {
1114        let query = parse("scope.parent:Database").unwrap();
1115
1116        match query.root {
1117            Expr::Condition(cond) => {
1118                assert_eq!(cond.field.as_str(), "scope.parent");
1119                assert_eq!(cond.operator, Operator::Equal);
1120                assert!(matches!(cond.value, Value::String(ref s) if s == "Database"));
1121            }
1122            _ => panic!("Expected Condition"),
1123        }
1124    }
1125
1126    #[test]
1127    fn test_parse_scope_ancestor() {
1128        let query = parse("scope.ancestor:UserModule").unwrap();
1129
1130        match query.root {
1131            Expr::Condition(cond) => {
1132                assert_eq!(cond.field.as_str(), "scope.ancestor");
1133                assert_eq!(cond.operator, Operator::Equal);
1134                assert!(matches!(cond.value, Value::String(ref s) if s == "UserModule"));
1135            }
1136            _ => panic!("Expected Condition"),
1137        }
1138    }
1139
1140    #[test]
1141    fn test_parse_scope_type_with_regex() {
1142        let query = parse("scope.type~=/class|function/").unwrap();
1143
1144        match query.root {
1145            Expr::Condition(cond) => {
1146                assert_eq!(cond.field.as_str(), "scope.type");
1147                assert_eq!(cond.operator, Operator::Regex);
1148                assert!(matches!(cond.value, Value::Regex(_)));
1149            }
1150            _ => panic!("Expected Condition"),
1151        }
1152    }
1153
1154    #[test]
1155    fn test_parse_scope_and_kind() {
1156        let query = parse("scope.type:class AND name:connect").unwrap();
1157
1158        match query.root {
1159            Expr::And(operands) => {
1160                assert_eq!(operands.len(), 2);
1161
1162                match &operands[0] {
1163                    Expr::Condition(cond) => {
1164                        assert_eq!(cond.field.as_str(), "scope.type");
1165                    }
1166                    _ => panic!("Expected Condition"),
1167                }
1168
1169                match &operands[1] {
1170                    Expr::Condition(cond) => {
1171                        assert_eq!(cond.field.as_str(), "name");
1172                    }
1173                    _ => panic!("Expected Condition"),
1174                }
1175            }
1176            _ => panic!("Expected And"),
1177        }
1178    }
1179
1180    #[test]
1181    fn test_parse_scope_with_and_composition() {
1182        // name:connect AND scope.ancestor:UserModule
1183        let query = parse("name:connect AND scope.ancestor:UserModule").unwrap();
1184
1185        match query.root {
1186            Expr::And(operands) => {
1187                assert_eq!(operands.len(), 2);
1188
1189                match &operands[0] {
1190                    Expr::Condition(cond) => {
1191                        assert_eq!(cond.field.as_str(), "name");
1192                        assert!(matches!(cond.value, Value::String(ref s) if s == "connect"));
1193                    }
1194                    _ => panic!("Expected Condition"),
1195                }
1196
1197                match &operands[1] {
1198                    Expr::Condition(cond) => {
1199                        assert_eq!(cond.field.as_str(), "scope.ancestor");
1200                        assert!(matches!(cond.value, Value::String(ref s) if s == "UserModule"));
1201                    }
1202                    _ => panic!("Expected Condition"),
1203                }
1204            }
1205            _ => panic!("Expected And"),
1206        }
1207    }
1208
1209    #[test]
1210    fn test_parse_not_scope_type() {
1211        // NOT scope.type:test
1212        let query = parse("NOT scope.type:test").unwrap();
1213
1214        match query.root {
1215            Expr::Not(operand) => match *operand {
1216                Expr::Condition(cond) => {
1217                    assert_eq!(cond.field.as_str(), "scope.type");
1218                    assert!(matches!(cond.value, Value::String(ref s) if s == "test"));
1219                }
1220                _ => panic!("Expected Condition inside Not"),
1221            },
1222            _ => panic!("Expected Not"),
1223        }
1224    }
1225
1226    #[test]
1227    fn test_parse_complex_scope_composition() {
1228        // scope.type:class AND (name:connect OR name:disconnect)
1229        let query = parse("scope.type:class AND (name:connect OR name:disconnect)").unwrap();
1230
1231        match query.root {
1232            Expr::And(operands) => {
1233                assert_eq!(operands.len(), 2);
1234
1235                // First operand should be scope.type condition
1236                match &operands[0] {
1237                    Expr::Condition(cond) => {
1238                        assert_eq!(cond.field.as_str(), "scope.type");
1239                    }
1240                    _ => panic!("Expected Condition for scope.type"),
1241                }
1242
1243                // Second operand should be OR with name conditions
1244                match &operands[1] {
1245                    Expr::Or(or_operands) => {
1246                        assert_eq!(or_operands.len(), 2);
1247                        match &or_operands[0] {
1248                            Expr::Condition(cond) => {
1249                                assert_eq!(cond.field.as_str(), "name");
1250                                assert!(
1251                                    matches!(cond.value, Value::String(ref s) if s == "connect")
1252                                );
1253                            }
1254                            _ => panic!("Expected name:connect"),
1255                        }
1256                        match &or_operands[1] {
1257                            Expr::Condition(cond) => {
1258                                assert_eq!(cond.field.as_str(), "name");
1259                                assert!(
1260                                    matches!(cond.value, Value::String(ref s) if s == "disconnect")
1261                                );
1262                            }
1263                            _ => panic!("Expected name:disconnect"),
1264                        }
1265                    }
1266                    _ => panic!("Expected Or for name conditions"),
1267                }
1268            }
1269            _ => panic!("Expected And"),
1270        }
1271    }
1272
1273    #[test]
1274    fn test_parse_multiple_scope_filters() {
1275        // scope.type:function AND scope.parent:Database AND scope.ancestor:UserModule
1276        let query =
1277            parse("scope.type:function AND scope.parent:Database AND scope.ancestor:UserModule")
1278                .unwrap();
1279
1280        match query.root {
1281            Expr::And(operands) => {
1282                assert_eq!(operands.len(), 3);
1283
1284                match &operands[0] {
1285                    Expr::Condition(cond) => {
1286                        assert_eq!(cond.field.as_str(), "scope.type");
1287                    }
1288                    _ => panic!("Expected scope.type condition"),
1289                }
1290
1291                match &operands[1] {
1292                    Expr::Condition(cond) => {
1293                        assert_eq!(cond.field.as_str(), "scope.parent");
1294                    }
1295                    _ => panic!("Expected scope.parent condition"),
1296                }
1297
1298                match &operands[2] {
1299                    Expr::Condition(cond) => {
1300                        assert_eq!(cond.field.as_str(), "scope.ancestor");
1301                    }
1302                    _ => panic!("Expected scope.ancestor condition"),
1303                }
1304            }
1305            _ => panic!("Expected And"),
1306        }
1307    }
1308
1309    // Tests for invariant validation (Phase 0 Fix #3)
1310    // Verify that single-operand expressions are NOT wrapped in Or/And
1311
1312    #[test]
1313    fn test_single_operand_or_not_wrapped() {
1314        // Single condition with no OR operators should NOT be wrapped in Or
1315        let query = parse("kind:function").unwrap();
1316
1317        match query.root {
1318            Expr::Condition(_) => {
1319                // Expected: bare Condition, not Or([Condition])
1320            }
1321            Expr::Or(_) => panic!("Single operand should NOT be wrapped in Or expression"),
1322            _ => panic!("Expected Condition expression"),
1323        }
1324    }
1325
1326    #[test]
1327    fn test_multiple_operands_or_wrapped() {
1328        // Multiple conditions with OR should create Or expression
1329        let query = parse("kind:function OR kind:class").unwrap();
1330
1331        match query.root {
1332            Expr::Or(operands) => {
1333                assert_eq!(
1334                    operands.len(),
1335                    2,
1336                    "OR expression should have exactly 2 operands"
1337                );
1338            }
1339            _ => panic!("Multiple operands with OR should create Or expression"),
1340        }
1341    }
1342
1343    #[test]
1344    fn test_single_operand_and_not_wrapped() {
1345        // Single condition with no AND operators should NOT be wrapped in And
1346        let query = parse("name:test").unwrap();
1347
1348        match query.root {
1349            Expr::Condition(_) => {
1350                // Expected: bare Condition, not And([Condition])
1351            }
1352            Expr::And(_) => panic!("Single operand should NOT be wrapped in And expression"),
1353            _ => panic!("Expected Condition expression"),
1354        }
1355    }
1356
1357    #[test]
1358    fn test_multiple_operands_and_wrapped() {
1359        // Multiple conditions with AND should create And expression
1360        let query = parse("kind:function AND async:true").unwrap();
1361
1362        match query.root {
1363            Expr::And(operands) => {
1364                assert_eq!(
1365                    operands.len(),
1366                    2,
1367                    "AND expression should have exactly 2 operands"
1368                );
1369            }
1370            _ => panic!("Multiple operands with AND should create And expression"),
1371        }
1372    }
1373
1374    // D3 Advanced Query Feature tests
1375
1376    #[test]
1377    fn test_parse_variable_value() {
1378        let query = parse("kind:$type").unwrap();
1379        match query.root {
1380            Expr::Condition(cond) => {
1381                assert_eq!(cond.field.as_str(), "kind");
1382                assert_eq!(cond.operator, Operator::Equal);
1383                assert!(matches!(cond.value, Value::Variable(ref n) if n == "type"));
1384            }
1385            _ => panic!("Expected Condition"),
1386        }
1387    }
1388
1389    #[test]
1390    fn test_parse_subquery() {
1391        let query = parse("callers:(kind:function)").unwrap();
1392        match query.root {
1393            Expr::Condition(cond) => {
1394                assert_eq!(cond.field.as_str(), "callers");
1395                assert_eq!(cond.operator, Operator::Equal);
1396                match cond.value {
1397                    Value::Subquery(inner) => match *inner {
1398                        Expr::Condition(inner_cond) => {
1399                            assert_eq!(inner_cond.field.as_str(), "kind");
1400                            assert!(
1401                                matches!(inner_cond.value, Value::String(ref s) if s == "function")
1402                            );
1403                        }
1404                        _ => panic!("Expected inner Condition"),
1405                    },
1406                    _ => panic!("Expected Subquery value"),
1407                }
1408            }
1409            _ => panic!("Expected Condition"),
1410        }
1411    }
1412
1413    #[test]
1414    fn test_parse_non_relation_field_not_subquery() {
1415        // `(kind:function)` is grouping, not a subquery, because there is no relation field
1416        let query = parse("(kind:function)").unwrap();
1417        match query.root {
1418            Expr::Condition(cond) => {
1419                assert_eq!(cond.field.as_str(), "kind");
1420                // Value should be a String, not a Subquery
1421                assert!(matches!(cond.value, Value::String(_)));
1422            }
1423            _ => panic!("Expected Condition (grouping), not a subquery"),
1424        }
1425    }
1426
1427    #[test]
1428    fn test_parse_join_calls() {
1429        let query = parse("(kind:function) CALLS (kind:function)").unwrap();
1430        match query.root {
1431            Expr::Join(join) => {
1432                assert_eq!(join.edge, JoinEdgeKind::Calls);
1433                match *join.left {
1434                    Expr::Condition(ref cond) => assert_eq!(cond.field.as_str(), "kind"),
1435                    _ => panic!("Expected left Condition"),
1436                }
1437                match *join.right {
1438                    Expr::Condition(ref cond) => assert_eq!(cond.field.as_str(), "kind"),
1439                    _ => panic!("Expected right Condition"),
1440                }
1441            }
1442            _ => panic!("Expected Join expression"),
1443        }
1444    }
1445
1446    #[test]
1447    fn test_parse_join_imports() {
1448        let query = parse("(kind:function) IMPORTS (kind:module)").unwrap();
1449        match query.root {
1450            Expr::Join(join) => {
1451                assert_eq!(join.edge, JoinEdgeKind::Imports);
1452                match *join.right {
1453                    Expr::Condition(ref cond) => {
1454                        assert!(matches!(cond.value, Value::String(ref s) if s == "module"));
1455                    }
1456                    _ => panic!("Expected right Condition"),
1457                }
1458            }
1459            _ => panic!("Expected Join expression"),
1460        }
1461    }
1462
1463    #[test]
1464    fn test_parse_pipeline_count() {
1465        let pipeline = Parser::parse_pipeline_query("kind:function | count")
1466            .unwrap()
1467            .expect("Expected Some(PipelineQuery)");
1468        assert_eq!(pipeline.stages.len(), 1);
1469        assert!(matches!(pipeline.stages[0], PipelineStage::Count));
1470    }
1471
1472    #[test]
1473    fn test_parse_pipeline_group_by() {
1474        let pipeline = Parser::parse_pipeline_query("kind:function | group_by lang")
1475            .unwrap()
1476            .expect("Expected Some(PipelineQuery)");
1477        assert_eq!(pipeline.stages.len(), 1);
1478        match &pipeline.stages[0] {
1479            PipelineStage::GroupBy { field } => assert_eq!(field.as_str(), "lang"),
1480            other => panic!("Expected GroupBy, got {other:?}"),
1481        }
1482    }
1483
1484    #[test]
1485    fn test_parse_pipeline_top() {
1486        let pipeline = Parser::parse_pipeline_query("kind:function | top 10 lang")
1487            .unwrap()
1488            .expect("Expected Some(PipelineQuery)");
1489        assert_eq!(pipeline.stages.len(), 1);
1490        match &pipeline.stages[0] {
1491            PipelineStage::Top { n, field } => {
1492                assert_eq!(*n, 10);
1493                assert_eq!(field.as_str(), "lang");
1494            }
1495            other => panic!("Expected Top, got {other:?}"),
1496        }
1497    }
1498
1499    #[test]
1500    fn test_parse_pipeline_stats() {
1501        let pipeline = Parser::parse_pipeline_query("kind:function | stats")
1502            .unwrap()
1503            .expect("Expected Some(PipelineQuery)");
1504        assert_eq!(pipeline.stages.len(), 1);
1505        assert!(matches!(pipeline.stages[0], PipelineStage::Stats));
1506    }
1507}