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    ///
309    /// `and_expr` ::= `not_expr` ((AND | /* implicit */) `not_expr`)*
310    ///
311    /// Implicit AND fires when the next token is not `OR`, `)`, `|`, EOF, or a
312    /// join keyword (`CALLS`/`IMPORTS`/`INHERITS`/`IMPLEMENTS`).  The `NOT`
313    /// token is intentionally *not* in the stop set — `kind:function NOT name:test`
314    /// produces `And([kind=function, Not(name=test)])`.
315    fn parse_and(&mut self) -> Result<Expr, ParseError> {
316        let mut operands = vec![self.parse_not()?];
317
318        loop {
319            if self.match_token(&TokenType::And) {
320                // Explicit AND — consume the keyword and parse the next operand.
321                operands.push(self.parse_not()?);
322            } else if !self.is_at_end()
323                && !matches!(
324                    self.peek().token_type,
325                    TokenType::Or | TokenType::RParen | TokenType::Pipe
326                )
327                && !self.is_join_keyword()
328            {
329                // Implicit AND: the next token starts a new predicate.
330                operands.push(self.parse_not()?);
331            } else {
332                break;
333            }
334        }
335
336        if operands.len() == 1 {
337            operands
338                .into_iter()
339                .next()
340                .ok_or_else(|| ParseError::InvalidSyntax {
341                    message: "Internal parser error: AND expression has no operands (this should never happen)".to_string(),
342                    // Span is placeholder since this error is impossible and has no meaningful location
343                    span: Span::new(0, 0),
344                })
345        } else {
346            Ok(Expr::And(operands))
347        }
348    }
349
350    /// Return `true` when the current token is a join-expression keyword
351    /// (`CALLS`, `IMPORTS`, `INHERITS`, or `IMPLEMENTS`).
352    ///
353    /// These keywords are tokenized as [`TokenType::Word`] rather than dedicated
354    /// token variants, so the implicit-AND loop must check for them explicitly to
355    /// avoid consuming a join operator as if it were an ordinary predicate.
356    fn is_join_keyword(&self) -> bool {
357        matches!(
358            &self.peek().token_type,
359            TokenType::Word(w)
360                if w.eq_ignore_ascii_case("CALLS")
361                    || w.eq_ignore_ascii_case("IMPORTS")
362                    || w.eq_ignore_ascii_case("INHERITS")
363                    || w.eq_ignore_ascii_case("IMPLEMENTS")
364        )
365    }
366
367    /// Parse NOT expression (highest precedence)
368    /// `not_expr` ::= NOT `not_expr` | primary
369    fn parse_not(&mut self) -> Result<Expr, ParseError> {
370        if self.match_token(&TokenType::Not) {
371            let operand = self.parse_not()?; // Right-associative
372            Ok(Expr::Not(Box::new(operand)))
373        } else {
374            self.parse_primary()
375        }
376    }
377
378    /// Parse primary: condition or grouped expression
379    /// primary ::= LPAREN `or_expr` RPAREN | condition
380    fn parse_primary(&mut self) -> Result<Expr, ParseError> {
381        // Check for parentheses
382        if self.match_token(&TokenType::LParen) {
383            let lparen_span = self.tokens[self.position - 1].span.clone();
384            let expr = self.parse_or()?;
385
386            if !self.match_token(&TokenType::RParen) {
387                return Err(ParseError::UnmatchedParen {
388                    open_span: lparen_span,
389                    eof: self.is_at_end(),
390                });
391            }
392
393            return Ok(expr);
394        }
395
396        // Shorthand: bare word/string/regex/variable defaults to name predicate
397        let token_type = self.peek().token_type.clone();
398        match token_type {
399            TokenType::Word(word) => {
400                let token = self.advance().clone();
401                Ok(Expr::Condition(Condition {
402                    field: Field::new("name"),
403                    operator: Operator::Regex,
404                    value: Value::Regex(RegexValue {
405                        pattern: word,
406                        flags: RegexFlags::default(),
407                    }),
408                    span: token.span,
409                }))
410            }
411            TokenType::StringLiteral(value) => {
412                let token = self.advance().clone();
413                Ok(Expr::Condition(Condition {
414                    field: Field::new("name"),
415                    operator: Operator::Equal,
416                    value: Value::String(value),
417                    span: token.span,
418                }))
419            }
420            TokenType::RegexLiteral { pattern, flags } => {
421                let token = self.advance().clone();
422                Ok(Expr::Condition(Condition {
423                    field: Field::new("name"),
424                    operator: Operator::Regex,
425                    value: Value::Regex(RegexValue { pattern, flags }),
426                    span: token.span,
427                }))
428            }
429            TokenType::Variable(name) => {
430                let token = self.advance().clone();
431                Ok(Expr::Condition(Condition {
432                    field: Field::new("name"),
433                    operator: Operator::Equal,
434                    value: Value::Variable(name),
435                    span: token.span,
436                }))
437            }
438            _ => self.parse_condition(),
439        }
440    }
441
442    /// Parse condition: field operator value
443    /// condition ::= identifier operator value
444    ///
445    /// For relation fields (callers, callees, imports, exports, impl, references),
446    /// a `(` after `:` introduces a subquery value.
447    fn parse_condition(&mut self) -> Result<Expr, ParseError> {
448        // Parse field name
449        let field_token = self.advance().clone();
450        let field = match &field_token.token_type {
451            TokenType::Identifier(name) => Field::new(name.clone()),
452            _ => {
453                return Err(ParseError::ExpectedIdentifier { token: field_token });
454            }
455        };
456
457        // Parse operator
458        let operator_token = self.advance().clone();
459        let mut operator = match &operator_token.token_type {
460            TokenType::Colon => Operator::Equal,
461            TokenType::RegexOp => Operator::Regex,
462            TokenType::Greater => Operator::Greater,
463            TokenType::Less => Operator::Less,
464            TokenType::GreaterEq => Operator::GreaterEq,
465            TokenType::LessEq => Operator::LessEq,
466            _ => {
467                return Err(ParseError::ExpectedOperator {
468                    token: operator_token,
469                });
470            }
471        };
472
473        // Check for subquery: relation_field:(inner_expression)
474        let is_relation = crate::query::types::is_relation_field(field.as_str());
475        if is_relation
476            && operator == Operator::Equal
477            && matches!(self.peek().token_type, TokenType::LParen)
478        {
479            let lparen_span = self.peek().span.clone();
480            self.advance(); // consume '('
481            let inner_expr = self.parse_or()?;
482            if !self.match_token(&TokenType::RParen) {
483                return Err(ParseError::UnmatchedParen {
484                    open_span: lparen_span,
485                    eof: self.is_at_end(),
486                });
487            }
488
489            let end_span = self.tokens[self.position - 1].span.clone();
490            let span = field_token.span.merge(&end_span);
491
492            return Ok(Expr::Condition(Condition {
493                field,
494                operator,
495                value: Value::Subquery(Box::new(inner_expr)),
496                span,
497            }));
498        }
499
500        // Parse value
501        let value_token = self.advance().clone();
502        let mut value = match &value_token.token_type {
503            TokenType::StringLiteral(s) | TokenType::Word(s) => Value::String(s.clone()),
504            TokenType::RegexLiteral { pattern, flags } => Value::Regex(RegexValue {
505                pattern: pattern.clone(),
506                flags: flags.clone(),
507            }),
508            TokenType::NumberLiteral(n) => Value::Number(*n),
509            TokenType::BooleanLiteral(b) => Value::Boolean(*b),
510            TokenType::Variable(name) => Value::Variable(name.clone()),
511            _ => {
512                return Err(ParseError::ExpectedValue { token: value_token });
513            }
514        };
515
516        if operator == Operator::Regex {
517            if let Value::String(pattern) = &value {
518                value = Value::Regex(RegexValue {
519                    pattern: pattern.clone(),
520                    flags: RegexFlags::default(),
521                });
522            }
523        } else if operator == Operator::Equal && matches!(value, Value::Regex(_)) {
524            operator = Operator::Regex;
525        }
526
527        let span = field_token.span.merge(&value_token.span);
528
529        Ok(Expr::Condition(Condition {
530            field,
531            operator,
532            value,
533            span,
534        }))
535    }
536
537    /// Check if current token matches the given type and consume it
538    fn match_token(&mut self, token_type: &TokenType) -> bool {
539        if self.check(token_type) {
540            self.advance();
541            true
542        } else {
543            false
544        }
545    }
546
547    /// Check if current token matches the given type without consuming
548    fn check(&self, token_type: &TokenType) -> bool {
549        if self.is_at_end() {
550            return false;
551        }
552
553        std::mem::discriminant(&self.peek().token_type) == std::mem::discriminant(token_type)
554    }
555
556    /// Advance to the next token and return the previous one
557    fn advance(&mut self) -> &Token {
558        if !self.is_at_end() {
559            self.position += 1;
560        }
561        &self.tokens[self.position - 1]
562    }
563
564    /// Check if we're at the end of the token stream
565    fn is_at_end(&self) -> bool {
566        matches!(self.peek().token_type, TokenType::Eof)
567    }
568
569    /// Peek at the current token without consuming
570    fn peek(&self) -> &Token {
571        &self.tokens[self.position]
572    }
573}
574
575fn count_conditions(expr: &Expr) -> usize {
576    match expr {
577        Expr::And(operands) | Expr::Or(operands) => operands.iter().map(count_conditions).sum(),
578        Expr::Not(operand) => count_conditions(operand),
579        Expr::Condition(cond) => {
580            // Count subquery conditions recursively
581            if let Value::Subquery(inner) = &cond.value {
582                1 + count_conditions(inner)
583            } else {
584                1
585            }
586        }
587        Expr::Join(join) => count_conditions(&join.left) + count_conditions(&join.right),
588    }
589}
590
591/// Extract span from an expression (best-effort for error reporting).
592fn expr_span(expr: &Expr) -> Option<Span> {
593    match expr {
594        Expr::Condition(c) => Some(c.span.clone()),
595        Expr::Join(j) => Some(j.span.clone()),
596        Expr::And(ops) | Expr::Or(ops) => {
597            let first = ops.first().and_then(expr_span)?;
598            let last = ops.last().and_then(expr_span)?;
599            Some(first.merge(&last))
600        }
601        Expr::Not(inner) => expr_span(inner),
602    }
603}
604
605#[cfg(test)]
606mod tests {
607    use super::*;
608    use crate::query::lexer::with_lexer;
609    use crate::query::types::RegexFlags;
610
611    fn parse(input: &str) -> Result<Query, ParseError> {
612        let tokens = with_lexer(input, |batch| Ok(batch.into_vec())).unwrap();
613        let mut parser = Parser::new(tokens);
614        parser.parse()
615    }
616
617    #[test]
618    fn parse_query_reuses_thread_local_pool() {
619        crate::query::lexer::configure_pool_for_tests(
620            4,
621            crate::query::lexer::ShrinkPolicy::default(),
622        );
623
624        let (before_stash, before_in_flight, _) = crate::query::lexer::pool_stats_for_tests();
625        assert_eq!(before_stash, 0);
626        assert_eq!(before_in_flight, 0);
627
628        parse("kind:function").unwrap();
629        parse("name:test").unwrap();
630
631        let (after_stash, after_in_flight, max_size) = crate::query::lexer::pool_stats_for_tests();
632        assert!(max_size >= 1);
633        assert!(after_stash >= 1);
634        assert_eq!(after_in_flight, 0);
635
636        crate::query::lexer::reset_pool_to_default_for_tests();
637    }
638
639    #[test]
640    fn test_parse_simple_condition() {
641        let query = parse("kind:function").unwrap();
642
643        match query.root {
644            Expr::Condition(cond) => {
645                assert_eq!(cond.field.as_str(), "kind");
646                assert_eq!(cond.operator, Operator::Equal);
647                assert!(matches!(cond.value, Value::String(ref s) if s == "function"));
648            }
649            _ => panic!("Expected Condition"),
650        }
651    }
652
653    #[test]
654    fn test_parse_and() {
655        let query = parse("kind:function AND async:true").unwrap();
656
657        match query.root {
658            Expr::And(operands) => {
659                assert_eq!(operands.len(), 2);
660
661                match &operands[0] {
662                    Expr::Condition(cond) => {
663                        assert_eq!(cond.field.as_str(), "kind");
664                    }
665                    _ => panic!("Expected Condition"),
666                }
667
668                match &operands[1] {
669                    Expr::Condition(cond) => {
670                        assert_eq!(cond.field.as_str(), "async");
671                    }
672                    _ => panic!("Expected Condition"),
673                }
674            }
675            _ => panic!("Expected And"),
676        }
677    }
678
679    #[test]
680    fn test_parse_or() {
681        let query = parse("kind:function OR kind:class").unwrap();
682
683        match query.root {
684            Expr::Or(operands) => {
685                assert_eq!(operands.len(), 2);
686            }
687            _ => panic!("Expected Or"),
688        }
689    }
690
691    #[test]
692    fn test_parse_not() {
693        let query = parse("NOT kind:function").unwrap();
694
695        match query.root {
696            Expr::Not(operand) => match *operand {
697                Expr::Condition(cond) => {
698                    assert_eq!(cond.field.as_str(), "kind");
699                }
700                _ => panic!("Expected Condition inside Not"),
701            },
702            _ => panic!("Expected Not"),
703        }
704    }
705
706    #[test]
707    fn test_precedence_or_and() {
708        // A OR B AND C should parse as: Or(A, And(B, C))
709        let query = parse("a:1 OR b:2 AND c:3").unwrap();
710
711        match query.root {
712            Expr::Or(operands) => {
713                assert_eq!(operands.len(), 2);
714
715                // First operand should be a condition
716                assert!(matches!(operands[0], Expr::Condition(_)));
717
718                // Second operand should be an AND
719                match &operands[1] {
720                    Expr::And(and_operands) => {
721                        assert_eq!(and_operands.len(), 2);
722                    }
723                    _ => panic!("Expected And as second operand of Or"),
724                }
725            }
726            _ => panic!("Expected Or"),
727        }
728    }
729
730    #[test]
731    fn test_precedence_not_and() {
732        // NOT A AND B should parse as: And(Not(A), B)
733        let query = parse("NOT a:1 AND b:2").unwrap();
734
735        match query.root {
736            Expr::And(operands) => {
737                assert_eq!(operands.len(), 2);
738
739                // First operand should be NOT
740                assert!(matches!(operands[0], Expr::Not(_)));
741
742                // Second operand should be a condition
743                assert!(matches!(operands[1], Expr::Condition(_)));
744            }
745            _ => panic!("Expected And"),
746        }
747    }
748
749    #[test]
750    fn test_parentheses_simple() {
751        let query = parse("(kind:function)").unwrap();
752
753        match query.root {
754            Expr::Condition(cond) => {
755                assert_eq!(cond.field.as_str(), "kind");
756            }
757            _ => panic!("Expected Condition"),
758        }
759    }
760
761    #[test]
762    fn test_parentheses_override_precedence() {
763        // (A OR B) AND C should parse as: And(Or(A, B), C)
764        let query = parse("(a:1 OR b:2) AND c:3").unwrap();
765
766        match query.root {
767            Expr::And(operands) => {
768                assert_eq!(operands.len(), 2);
769
770                // First operand should be OR
771                match &operands[0] {
772                    Expr::Or(or_operands) => {
773                        assert_eq!(or_operands.len(), 2);
774                    }
775                    _ => panic!("Expected Or as first operand"),
776                }
777
778                // Second operand should be a condition
779                assert!(matches!(operands[1], Expr::Condition(_)));
780            }
781            _ => panic!("Expected And"),
782        }
783    }
784
785    #[test]
786    fn test_nested_parentheses() {
787        let query = parse("((a:1))").unwrap();
788
789        match query.root {
790            Expr::Condition(cond) => {
791                assert_eq!(cond.field.as_str(), "a");
792            }
793            _ => panic!("Expected Condition"),
794        }
795    }
796
797    #[test]
798    fn test_complex_query() {
799        let query =
800            parse("(kind:function OR kind:method) AND async:true AND NOT name~=/test/").unwrap();
801
802        match query.root {
803            Expr::And(operands) => {
804                assert_eq!(operands.len(), 3);
805
806                // First: (kind:function OR kind:method)
807                assert!(matches!(operands[0], Expr::Or(_)));
808
809                // Second: async:true
810                assert!(matches!(operands[1], Expr::Condition(_)));
811
812                // Third: NOT name~=/test/
813                assert!(matches!(operands[2], Expr::Not(_)));
814            }
815            _ => panic!("Expected And"),
816        }
817    }
818
819    #[test]
820    fn test_chained_and() {
821        let query = parse("a:1 AND b:2 AND c:3 AND d:4").unwrap();
822
823        match query.root {
824            Expr::And(operands) => {
825                assert_eq!(operands.len(), 4);
826            }
827            _ => panic!("Expected And"),
828        }
829    }
830
831    #[test]
832    fn test_chained_or() {
833        let query = parse("a:1 OR b:2 OR c:3 OR d:4").unwrap();
834
835        match query.root {
836            Expr::Or(operands) => {
837                assert_eq!(operands.len(), 4);
838            }
839            _ => panic!("Expected Or"),
840        }
841    }
842
843    #[test]
844    fn test_double_not() {
845        let query = parse("NOT NOT kind:function").unwrap();
846
847        match query.root {
848            Expr::Not(operand) => match *operand {
849                Expr::Not(inner) => match *inner {
850                    Expr::Condition(_) => {}
851                    _ => panic!("Expected Condition"),
852                },
853                _ => panic!("Expected Not"),
854            },
855            _ => panic!("Expected Not"),
856        }
857    }
858
859    #[test]
860    fn test_triple_not() {
861        let query = parse("NOT NOT NOT kind:function").unwrap();
862
863        match query.root {
864            Expr::Not(operand1) => match *operand1 {
865                Expr::Not(operand2) => match *operand2 {
866                    Expr::Not(operand3) => match *operand3 {
867                        Expr::Condition(_) => {}
868                        _ => panic!("Expected Condition"),
869                    },
870                    _ => panic!("Expected Not"),
871                },
872                _ => panic!("Expected Not"),
873            },
874            _ => panic!("Expected Not"),
875        }
876    }
877
878    #[test]
879    fn test_operators_all() {
880        // Test all operators
881        let _ = parse("a:b").unwrap(); // Equal
882        let _ = parse("a~=/b/").unwrap(); // Regex
883        let _ = parse("a>1").unwrap(); // Greater
884        let _ = parse("a<1").unwrap(); // Less
885        let _ = parse("a>=1").unwrap(); // GreaterEq
886        let _ = parse("a<=1").unwrap(); // LessEq
887    }
888
889    #[test]
890    fn test_value_types() {
891        // String literal
892        let query = parse(r#"name:"hello world""#).unwrap();
893        match query.root {
894            Expr::Condition(cond) => {
895                assert!(matches!(cond.value, Value::String(ref s) if s == "hello world"));
896            }
897            _ => panic!("Expected Condition"),
898        }
899
900        // Number
901        let query = parse("lines:42").unwrap();
902        match query.root {
903            Expr::Condition(cond) => {
904                assert!(matches!(cond.value, Value::Number(42)));
905            }
906            _ => panic!("Expected Condition"),
907        }
908
909        // Boolean
910        let query = parse("async:true").unwrap();
911        match query.root {
912            Expr::Condition(cond) => {
913                assert!(matches!(cond.value, Value::Boolean(true)));
914            }
915            _ => panic!("Expected Condition"),
916        }
917
918        // Regex
919        let query = parse("name~=/^test_/i").unwrap();
920        match query.root {
921            Expr::Condition(cond) => match &cond.value {
922                Value::Regex(regex) => {
923                    assert_eq!(regex.pattern, "^test_");
924                    assert!(regex.flags.case_insensitive);
925                }
926                _ => panic!("Expected Regex value"),
927            },
928            _ => panic!("Expected Condition"),
929        }
930    }
931
932    #[test]
933    fn test_plain_word_defaults_to_name_regex() {
934        let query = parse("Error").unwrap();
935
936        match query.root {
937            Expr::Condition(cond) => {
938                assert_eq!(cond.field.as_str(), "name");
939                assert_eq!(cond.operator, Operator::Regex);
940                match cond.value {
941                    Value::Regex(regex) => {
942                        assert_eq!(regex.pattern, "Error");
943                        assert_eq!(regex.flags, RegexFlags::default());
944                    }
945                    _ => panic!("Expected regex value"),
946                }
947            }
948            _ => panic!("Expected Condition"),
949        }
950    }
951
952    #[test]
953    fn test_plain_string_literal_defaults_to_name_equal() {
954        let query = parse(r#""Error""#).unwrap();
955
956        match query.root {
957            Expr::Condition(cond) => {
958                assert_eq!(cond.field.as_str(), "name");
959                assert_eq!(cond.operator, Operator::Equal);
960                match cond.value {
961                    Value::String(value) => assert_eq!(value, "Error"),
962                    _ => panic!("Expected string value"),
963                }
964            }
965            _ => panic!("Expected Condition"),
966        }
967    }
968
969    #[test]
970    fn test_error_empty_query() {
971        let result = parse("");
972        assert!(matches!(result, Err(ParseError::EmptyQuery)));
973    }
974
975    #[test]
976    fn test_error_unmatched_lparen() {
977        let result = parse("(kind:function");
978        assert!(matches!(result, Err(ParseError::UnmatchedParen { .. })));
979    }
980
981    #[test]
982    fn test_error_unmatched_rparen() {
983        let result = parse("kind:function)");
984        assert!(matches!(result, Err(ParseError::UnexpectedToken { .. })));
985    }
986
987    #[test]
988    fn test_error_missing_value() {
989        let result = parse("kind:");
990        assert!(matches!(result, Err(ParseError::ExpectedValue { .. })));
991    }
992
993    #[test]
994    fn test_error_incomplete_and() {
995        let result = parse("kind:function AND");
996        assert!(matches!(result, Err(ParseError::ExpectedIdentifier { .. })));
997    }
998
999    #[test]
1000    fn test_error_incomplete_or() {
1001        let result = parse("kind:function OR");
1002        assert!(matches!(result, Err(ParseError::ExpectedIdentifier { .. })));
1003    }
1004
1005    #[test]
1006    fn test_error_incomplete_not() {
1007        let result = parse("NOT");
1008        assert!(matches!(result, Err(ParseError::ExpectedIdentifier { .. })));
1009    }
1010
1011    #[test]
1012    fn test_bare_word_implicit_and_two_words() {
1013        // Two bare words now succeed via implicit AND.
1014        // `kind function` → And([name~=/kind/, name~=/function/])
1015        let query = parse("kind function").unwrap();
1016        match query.root {
1017            Expr::And(operands) => {
1018                assert_eq!(operands.len(), 2);
1019                match &operands[0] {
1020                    Expr::Condition(cond) => {
1021                        assert_eq!(cond.field.as_str(), "name");
1022                        assert_eq!(cond.operator, Operator::Regex);
1023                        assert!(
1024                            matches!(&cond.value, Value::Regex(r) if r.pattern == "kind"),
1025                            "Expected regex pattern 'kind', got {:?}",
1026                            cond.value
1027                        );
1028                    }
1029                    other => panic!("Expected Condition for first bare word, got {other:?}"),
1030                }
1031                match &operands[1] {
1032                    Expr::Condition(cond) => {
1033                        assert_eq!(cond.field.as_str(), "name");
1034                        assert_eq!(cond.operator, Operator::Regex);
1035                        assert!(
1036                            matches!(&cond.value, Value::Regex(r) if r.pattern == "function"),
1037                            "Expected regex pattern 'function', got {:?}",
1038                            cond.value
1039                        );
1040                    }
1041                    other => panic!("Expected Condition for second bare word, got {other:?}"),
1042                }
1043            }
1044            other => panic!("Expected And from two bare words, got {other:?}"),
1045        }
1046    }
1047
1048    #[test]
1049    fn test_precedence_complex_1() {
1050        // A OR B AND C OR D should parse as: Or(A, And(B, C), D)
1051        let query = parse("a:1 OR b:2 AND c:3 OR d:4").unwrap();
1052
1053        match query.root {
1054            Expr::Or(operands) => {
1055                assert_eq!(operands.len(), 3);
1056
1057                // First operand: condition
1058                assert!(matches!(operands[0], Expr::Condition(_)));
1059
1060                // Second operand: AND
1061                assert!(matches!(operands[1], Expr::And(_)));
1062
1063                // Third operand: condition
1064                assert!(matches!(operands[2], Expr::Condition(_)));
1065            }
1066            _ => panic!("Expected Or"),
1067        }
1068    }
1069
1070    #[test]
1071    fn test_precedence_complex_2() {
1072        // NOT A OR B AND C should parse as: Or(Not(A), And(B, C))
1073        let query = parse("NOT a:1 OR b:2 AND c:3").unwrap();
1074
1075        match query.root {
1076            Expr::Or(operands) => {
1077                assert_eq!(operands.len(), 2);
1078
1079                // First operand: NOT
1080                assert!(matches!(operands[0], Expr::Not(_)));
1081
1082                // Second operand: AND
1083                assert!(matches!(operands[1], Expr::And(_)));
1084            }
1085            _ => panic!("Expected Or"),
1086        }
1087    }
1088
1089    #[test]
1090    fn test_multiple_groups() {
1091        // (A OR B) AND (C OR D)
1092        let query = parse("(a:1 OR b:2) AND (c:3 OR d:4)").unwrap();
1093
1094        match query.root {
1095            Expr::And(operands) => {
1096                assert_eq!(operands.len(), 2);
1097
1098                // Both operands should be OR
1099                assert!(matches!(operands[0], Expr::Or(_)));
1100                assert!(matches!(operands[1], Expr::Or(_)));
1101            }
1102            _ => panic!("Expected And"),
1103        }
1104    }
1105
1106    #[test]
1107    fn test_not_with_parentheses() {
1108        // NOT (A OR B)
1109        let query = parse("NOT (a:1 OR b:2)").unwrap();
1110
1111        match query.root {
1112            Expr::Not(operand) => match *operand {
1113                Expr::Or(or_operands) => {
1114                    assert_eq!(or_operands.len(), 2);
1115                }
1116                _ => panic!("Expected Or inside Not"),
1117            },
1118            _ => panic!("Expected Not"),
1119        }
1120    }
1121
1122    #[test]
1123    fn test_deeply_nested() {
1124        // ((A AND B) OR (C AND D)) AND E
1125        let query = parse("((a:1 AND b:2) OR (c:3 AND d:4)) AND e:5").unwrap();
1126
1127        match query.root {
1128            Expr::And(operands) => {
1129                assert_eq!(operands.len(), 2);
1130
1131                // First operand should be OR with AND children
1132                match &operands[0] {
1133                    Expr::Or(or_operands) => {
1134                        assert_eq!(or_operands.len(), 2);
1135                        assert!(matches!(or_operands[0], Expr::And(_)));
1136                        assert!(matches!(or_operands[1], Expr::And(_)));
1137                    }
1138                    _ => panic!("Expected Or"),
1139                }
1140
1141                // Second operand should be condition
1142                assert!(matches!(operands[1], Expr::Condition(_)));
1143            }
1144            _ => panic!("Expected And"),
1145        }
1146    }
1147
1148    // P2-34 Phase 2: Tests for scope.* fields
1149
1150    #[test]
1151    fn test_parse_scope_type() {
1152        let query = parse("scope.type:class").unwrap();
1153
1154        match query.root {
1155            Expr::Condition(cond) => {
1156                assert_eq!(cond.field.as_str(), "scope.type");
1157                assert_eq!(cond.operator, Operator::Equal);
1158                assert!(matches!(cond.value, Value::String(ref s) if s == "class"));
1159            }
1160            _ => panic!("Expected Condition"),
1161        }
1162    }
1163
1164    #[test]
1165    fn test_parse_scope_name() {
1166        let query = parse("scope.name:UserService").unwrap();
1167
1168        match query.root {
1169            Expr::Condition(cond) => {
1170                assert_eq!(cond.field.as_str(), "scope.name");
1171                assert_eq!(cond.operator, Operator::Equal);
1172                assert!(matches!(cond.value, Value::String(ref s) if s == "UserService"));
1173            }
1174            _ => panic!("Expected Condition"),
1175        }
1176    }
1177
1178    #[test]
1179    fn test_parse_scope_parent() {
1180        let query = parse("scope.parent:Database").unwrap();
1181
1182        match query.root {
1183            Expr::Condition(cond) => {
1184                assert_eq!(cond.field.as_str(), "scope.parent");
1185                assert_eq!(cond.operator, Operator::Equal);
1186                assert!(matches!(cond.value, Value::String(ref s) if s == "Database"));
1187            }
1188            _ => panic!("Expected Condition"),
1189        }
1190    }
1191
1192    #[test]
1193    fn test_parse_scope_ancestor() {
1194        let query = parse("scope.ancestor:UserModule").unwrap();
1195
1196        match query.root {
1197            Expr::Condition(cond) => {
1198                assert_eq!(cond.field.as_str(), "scope.ancestor");
1199                assert_eq!(cond.operator, Operator::Equal);
1200                assert!(matches!(cond.value, Value::String(ref s) if s == "UserModule"));
1201            }
1202            _ => panic!("Expected Condition"),
1203        }
1204    }
1205
1206    #[test]
1207    fn test_parse_scope_type_with_regex() {
1208        let query = parse("scope.type~=/class|function/").unwrap();
1209
1210        match query.root {
1211            Expr::Condition(cond) => {
1212                assert_eq!(cond.field.as_str(), "scope.type");
1213                assert_eq!(cond.operator, Operator::Regex);
1214                assert!(matches!(cond.value, Value::Regex(_)));
1215            }
1216            _ => panic!("Expected Condition"),
1217        }
1218    }
1219
1220    #[test]
1221    fn test_parse_scope_and_kind() {
1222        let query = parse("scope.type:class AND name:connect").unwrap();
1223
1224        match query.root {
1225            Expr::And(operands) => {
1226                assert_eq!(operands.len(), 2);
1227
1228                match &operands[0] {
1229                    Expr::Condition(cond) => {
1230                        assert_eq!(cond.field.as_str(), "scope.type");
1231                    }
1232                    _ => panic!("Expected Condition"),
1233                }
1234
1235                match &operands[1] {
1236                    Expr::Condition(cond) => {
1237                        assert_eq!(cond.field.as_str(), "name");
1238                    }
1239                    _ => panic!("Expected Condition"),
1240                }
1241            }
1242            _ => panic!("Expected And"),
1243        }
1244    }
1245
1246    #[test]
1247    fn test_parse_scope_with_and_composition() {
1248        // name:connect AND scope.ancestor:UserModule
1249        let query = parse("name:connect AND scope.ancestor:UserModule").unwrap();
1250
1251        match query.root {
1252            Expr::And(operands) => {
1253                assert_eq!(operands.len(), 2);
1254
1255                match &operands[0] {
1256                    Expr::Condition(cond) => {
1257                        assert_eq!(cond.field.as_str(), "name");
1258                        assert!(matches!(cond.value, Value::String(ref s) if s == "connect"));
1259                    }
1260                    _ => panic!("Expected Condition"),
1261                }
1262
1263                match &operands[1] {
1264                    Expr::Condition(cond) => {
1265                        assert_eq!(cond.field.as_str(), "scope.ancestor");
1266                        assert!(matches!(cond.value, Value::String(ref s) if s == "UserModule"));
1267                    }
1268                    _ => panic!("Expected Condition"),
1269                }
1270            }
1271            _ => panic!("Expected And"),
1272        }
1273    }
1274
1275    #[test]
1276    fn test_parse_not_scope_type() {
1277        // NOT scope.type:test
1278        let query = parse("NOT scope.type:test").unwrap();
1279
1280        match query.root {
1281            Expr::Not(operand) => match *operand {
1282                Expr::Condition(cond) => {
1283                    assert_eq!(cond.field.as_str(), "scope.type");
1284                    assert!(matches!(cond.value, Value::String(ref s) if s == "test"));
1285                }
1286                _ => panic!("Expected Condition inside Not"),
1287            },
1288            _ => panic!("Expected Not"),
1289        }
1290    }
1291
1292    #[test]
1293    fn test_parse_complex_scope_composition() {
1294        // scope.type:class AND (name:connect OR name:disconnect)
1295        let query = parse("scope.type:class AND (name:connect OR name:disconnect)").unwrap();
1296
1297        match query.root {
1298            Expr::And(operands) => {
1299                assert_eq!(operands.len(), 2);
1300
1301                // First operand should be scope.type condition
1302                match &operands[0] {
1303                    Expr::Condition(cond) => {
1304                        assert_eq!(cond.field.as_str(), "scope.type");
1305                    }
1306                    _ => panic!("Expected Condition for scope.type"),
1307                }
1308
1309                // Second operand should be OR with name conditions
1310                match &operands[1] {
1311                    Expr::Or(or_operands) => {
1312                        assert_eq!(or_operands.len(), 2);
1313                        match &or_operands[0] {
1314                            Expr::Condition(cond) => {
1315                                assert_eq!(cond.field.as_str(), "name");
1316                                assert!(
1317                                    matches!(cond.value, Value::String(ref s) if s == "connect")
1318                                );
1319                            }
1320                            _ => panic!("Expected name:connect"),
1321                        }
1322                        match &or_operands[1] {
1323                            Expr::Condition(cond) => {
1324                                assert_eq!(cond.field.as_str(), "name");
1325                                assert!(
1326                                    matches!(cond.value, Value::String(ref s) if s == "disconnect")
1327                                );
1328                            }
1329                            _ => panic!("Expected name:disconnect"),
1330                        }
1331                    }
1332                    _ => panic!("Expected Or for name conditions"),
1333                }
1334            }
1335            _ => panic!("Expected And"),
1336        }
1337    }
1338
1339    #[test]
1340    fn test_parse_multiple_scope_filters() {
1341        // scope.type:function AND scope.parent:Database AND scope.ancestor:UserModule
1342        let query =
1343            parse("scope.type:function AND scope.parent:Database AND scope.ancestor:UserModule")
1344                .unwrap();
1345
1346        match query.root {
1347            Expr::And(operands) => {
1348                assert_eq!(operands.len(), 3);
1349
1350                match &operands[0] {
1351                    Expr::Condition(cond) => {
1352                        assert_eq!(cond.field.as_str(), "scope.type");
1353                    }
1354                    _ => panic!("Expected scope.type condition"),
1355                }
1356
1357                match &operands[1] {
1358                    Expr::Condition(cond) => {
1359                        assert_eq!(cond.field.as_str(), "scope.parent");
1360                    }
1361                    _ => panic!("Expected scope.parent condition"),
1362                }
1363
1364                match &operands[2] {
1365                    Expr::Condition(cond) => {
1366                        assert_eq!(cond.field.as_str(), "scope.ancestor");
1367                    }
1368                    _ => panic!("Expected scope.ancestor condition"),
1369                }
1370            }
1371            _ => panic!("Expected And"),
1372        }
1373    }
1374
1375    // Tests for invariant validation (Phase 0 Fix #3)
1376    // Verify that single-operand expressions are NOT wrapped in Or/And
1377
1378    #[test]
1379    fn test_single_operand_or_not_wrapped() {
1380        // Single condition with no OR operators should NOT be wrapped in Or
1381        let query = parse("kind:function").unwrap();
1382
1383        match query.root {
1384            Expr::Condition(_) => {
1385                // Expected: bare Condition, not Or([Condition])
1386            }
1387            Expr::Or(_) => panic!("Single operand should NOT be wrapped in Or expression"),
1388            _ => panic!("Expected Condition expression"),
1389        }
1390    }
1391
1392    #[test]
1393    fn test_multiple_operands_or_wrapped() {
1394        // Multiple conditions with OR should create Or expression
1395        let query = parse("kind:function OR kind:class").unwrap();
1396
1397        match query.root {
1398            Expr::Or(operands) => {
1399                assert_eq!(
1400                    operands.len(),
1401                    2,
1402                    "OR expression should have exactly 2 operands"
1403                );
1404            }
1405            _ => panic!("Multiple operands with OR should create Or expression"),
1406        }
1407    }
1408
1409    #[test]
1410    fn test_single_operand_and_not_wrapped() {
1411        // Single condition with no AND operators should NOT be wrapped in And
1412        let query = parse("name:test").unwrap();
1413
1414        match query.root {
1415            Expr::Condition(_) => {
1416                // Expected: bare Condition, not And([Condition])
1417            }
1418            Expr::And(_) => panic!("Single operand should NOT be wrapped in And expression"),
1419            _ => panic!("Expected Condition expression"),
1420        }
1421    }
1422
1423    #[test]
1424    fn test_multiple_operands_and_wrapped() {
1425        // Multiple conditions with AND should create And expression
1426        let query = parse("kind:function AND async:true").unwrap();
1427
1428        match query.root {
1429            Expr::And(operands) => {
1430                assert_eq!(
1431                    operands.len(),
1432                    2,
1433                    "AND expression should have exactly 2 operands"
1434                );
1435            }
1436            _ => panic!("Multiple operands with AND should create And expression"),
1437        }
1438    }
1439
1440    // D3 Advanced Query Feature tests
1441
1442    #[test]
1443    fn test_parse_variable_value() {
1444        let query = parse("kind:$type").unwrap();
1445        match query.root {
1446            Expr::Condition(cond) => {
1447                assert_eq!(cond.field.as_str(), "kind");
1448                assert_eq!(cond.operator, Operator::Equal);
1449                assert!(matches!(cond.value, Value::Variable(ref n) if n == "type"));
1450            }
1451            _ => panic!("Expected Condition"),
1452        }
1453    }
1454
1455    #[test]
1456    fn test_parse_subquery() {
1457        let query = parse("callers:(kind:function)").unwrap();
1458        match query.root {
1459            Expr::Condition(cond) => {
1460                assert_eq!(cond.field.as_str(), "callers");
1461                assert_eq!(cond.operator, Operator::Equal);
1462                match cond.value {
1463                    Value::Subquery(inner) => match *inner {
1464                        Expr::Condition(inner_cond) => {
1465                            assert_eq!(inner_cond.field.as_str(), "kind");
1466                            assert!(
1467                                matches!(inner_cond.value, Value::String(ref s) if s == "function")
1468                            );
1469                        }
1470                        _ => panic!("Expected inner Condition"),
1471                    },
1472                    _ => panic!("Expected Subquery value"),
1473                }
1474            }
1475            _ => panic!("Expected Condition"),
1476        }
1477    }
1478
1479    #[test]
1480    fn test_parse_non_relation_field_not_subquery() {
1481        // `(kind:function)` is grouping, not a subquery, because there is no relation field
1482        let query = parse("(kind:function)").unwrap();
1483        match query.root {
1484            Expr::Condition(cond) => {
1485                assert_eq!(cond.field.as_str(), "kind");
1486                // Value should be a String, not a Subquery
1487                assert!(matches!(cond.value, Value::String(_)));
1488            }
1489            _ => panic!("Expected Condition (grouping), not a subquery"),
1490        }
1491    }
1492
1493    #[test]
1494    fn test_parse_join_calls() {
1495        let query = parse("(kind:function) CALLS (kind:function)").unwrap();
1496        match query.root {
1497            Expr::Join(join) => {
1498                assert_eq!(join.edge, JoinEdgeKind::Calls);
1499                match *join.left {
1500                    Expr::Condition(ref cond) => assert_eq!(cond.field.as_str(), "kind"),
1501                    _ => panic!("Expected left Condition"),
1502                }
1503                match *join.right {
1504                    Expr::Condition(ref cond) => assert_eq!(cond.field.as_str(), "kind"),
1505                    _ => panic!("Expected right Condition"),
1506                }
1507            }
1508            _ => panic!("Expected Join expression"),
1509        }
1510    }
1511
1512    #[test]
1513    fn test_parse_join_imports() {
1514        let query = parse("(kind:function) IMPORTS (kind:module)").unwrap();
1515        match query.root {
1516            Expr::Join(join) => {
1517                assert_eq!(join.edge, JoinEdgeKind::Imports);
1518                match *join.right {
1519                    Expr::Condition(ref cond) => {
1520                        assert!(matches!(cond.value, Value::String(ref s) if s == "module"));
1521                    }
1522                    _ => panic!("Expected right Condition"),
1523                }
1524            }
1525            _ => panic!("Expected Join expression"),
1526        }
1527    }
1528
1529    #[test]
1530    fn test_parse_pipeline_count() {
1531        let pipeline = Parser::parse_pipeline_query("kind:function | count")
1532            .unwrap()
1533            .expect("Expected Some(PipelineQuery)");
1534        assert_eq!(pipeline.stages.len(), 1);
1535        assert!(matches!(pipeline.stages[0], PipelineStage::Count));
1536    }
1537
1538    #[test]
1539    fn test_parse_pipeline_group_by() {
1540        let pipeline = Parser::parse_pipeline_query("kind:function | group_by lang")
1541            .unwrap()
1542            .expect("Expected Some(PipelineQuery)");
1543        assert_eq!(pipeline.stages.len(), 1);
1544        match &pipeline.stages[0] {
1545            PipelineStage::GroupBy { field } => assert_eq!(field.as_str(), "lang"),
1546            other => panic!("Expected GroupBy, got {other:?}"),
1547        }
1548    }
1549
1550    #[test]
1551    fn test_parse_pipeline_top() {
1552        let pipeline = Parser::parse_pipeline_query("kind:function | top 10 lang")
1553            .unwrap()
1554            .expect("Expected Some(PipelineQuery)");
1555        assert_eq!(pipeline.stages.len(), 1);
1556        match &pipeline.stages[0] {
1557            PipelineStage::Top { n, field } => {
1558                assert_eq!(*n, 10);
1559                assert_eq!(field.as_str(), "lang");
1560            }
1561            other => panic!("Expected Top, got {other:?}"),
1562        }
1563    }
1564
1565    #[test]
1566    fn test_parse_pipeline_stats() {
1567        let pipeline = Parser::parse_pipeline_query("kind:function | stats")
1568            .unwrap()
1569            .expect("Expected Some(PipelineQuery)");
1570        assert_eq!(pipeline.stages.len(), 1);
1571        assert!(matches!(pipeline.stages[0], PipelineStage::Stats));
1572    }
1573
1574    // ── PARSE_1: implicit AND tests ───────────────────────────────────────────
1575
1576    #[test]
1577    fn test_implicit_and_three_conditions() {
1578        // `kind:function name~=/smb2_/ lang:c` → And([kind=function, name~=/smb2_/, lang=c])
1579        let query = parse("kind:function name~=/smb2_/ lang:c").unwrap();
1580        match query.root {
1581            Expr::And(ref operands) => assert_eq!(operands.len(), 3),
1582            other => panic!("Expected And with 3 operands, got {other:?}"),
1583        }
1584    }
1585
1586    #[test]
1587    fn test_implicit_and_bare_word() {
1588        // `kind:function smb2_open` → And([kind=function, name~=/smb2_open/])
1589        let query = parse("kind:function smb2_open").unwrap();
1590        match query.root {
1591            Expr::And(ref operands) => {
1592                assert_eq!(operands.len(), 2);
1593                match &operands[0] {
1594                    Expr::Condition(c) => assert_eq!(c.field.as_str(), "kind"),
1595                    other => panic!("Expected kind condition, got {other:?}"),
1596                }
1597                match &operands[1] {
1598                    Expr::Condition(c) => {
1599                        assert_eq!(c.field.as_str(), "name");
1600                        assert_eq!(c.operator, Operator::Regex);
1601                        assert!(
1602                            matches!(&c.value, Value::Regex(r) if r.pattern == "smb2_open"),
1603                            "Expected bare word promoted to name regex"
1604                        );
1605                    }
1606                    other => panic!("Expected bare-word condition, got {other:?}"),
1607                }
1608            }
1609            other => panic!("Expected And, got {other:?}"),
1610        }
1611    }
1612
1613    #[test]
1614    fn test_implicit_and_or_precedence() {
1615        // `kind:function OR lang:c name:main`
1616        // Precedence: AND binds tighter than OR → Or([kind=function, And([lang=c, name=main])])
1617        let query = parse("kind:function OR lang:c name:main").unwrap();
1618        match query.root {
1619            Expr::Or(ref operands) => {
1620                assert_eq!(operands.len(), 2, "Expected 2 OR operands");
1621                match &operands[1] {
1622                    Expr::And(and_ops) => assert_eq!(and_ops.len(), 2),
1623                    other => panic!("Expected And on right side of OR, got {other:?}"),
1624                }
1625            }
1626            other => panic!("Expected Or expression, got {other:?}"),
1627        }
1628    }
1629
1630    #[test]
1631    fn test_implicit_and_before_not() {
1632        // `kind:function NOT name:test` → And([kind=function, Not(name=test)])
1633        // NOT is intentionally NOT in the implicit-AND stop set.
1634        let query = parse("kind:function NOT name:test").unwrap();
1635        match query.root {
1636            Expr::And(ref operands) => {
1637                assert_eq!(operands.len(), 2);
1638                assert!(
1639                    matches!(&operands[1], Expr::Not(_)),
1640                    "Expected Not as second operand"
1641                );
1642            }
1643            other => panic!("Expected And expression, got {other:?}"),
1644        }
1645    }
1646
1647    #[test]
1648    fn test_implicit_and_before_paren_group() {
1649        // `lang:rust (kind:function OR kind:method)` → And([lang=rust, Or([kind=fn, kind=method])])
1650        let query = parse("lang:rust (kind:function OR kind:method)").unwrap();
1651        match query.root {
1652            Expr::And(ref operands) => {
1653                assert_eq!(operands.len(), 2);
1654                assert!(
1655                    matches!(&operands[1], Expr::Or(_)),
1656                    "Expected Or as second operand"
1657                );
1658            }
1659            other => panic!("Expected And expression, got {other:?}"),
1660        }
1661    }
1662
1663    #[test]
1664    fn test_implicit_and_stops_at_rparen() {
1665        // `(a:1 b:2) OR c:3` — the implicit AND inside parens stops at `)`.
1666        let query = parse("(kind:function lang:c) OR kind:method").unwrap();
1667        match query.root {
1668            Expr::Or(ref operands) => {
1669                assert_eq!(operands.len(), 2);
1670                match &operands[0] {
1671                    Expr::And(and_ops) => assert_eq!(and_ops.len(), 2),
1672                    other => panic!("Expected And inside parens, got {other:?}"),
1673                }
1674            }
1675            other => panic!("Expected Or expression, got {other:?}"),
1676        }
1677    }
1678
1679    #[test]
1680    fn test_implicit_and_stops_at_pipe() {
1681        // `kind:function lang:c | count` — implicit AND stops at `|`.
1682        let pipeline = Parser::parse_pipeline_query("kind:function lang:c | count")
1683            .unwrap()
1684            .expect("Expected Some(PipelineQuery)");
1685        // Filter should be And([kind=function, lang=c]).
1686        match &pipeline.query.root {
1687            Expr::And(ops) => assert_eq!(ops.len(), 2),
1688            other => panic!("Expected And filter, got {other:?}"),
1689        }
1690        assert_eq!(pipeline.stages.len(), 1);
1691        assert!(matches!(pipeline.stages[0], PipelineStage::Count));
1692    }
1693
1694    #[test]
1695    fn test_implicit_and_within_pipe_stage() {
1696        // Two conditions before the pipe are combined as implicit AND in the filter.
1697        let pipeline = Parser::parse_pipeline_query("kind:function lang:c | count")
1698            .unwrap()
1699            .expect("Expected Some(PipelineQuery)");
1700        assert!(matches!(pipeline.stages[0], PipelineStage::Count));
1701        match &pipeline.query.root {
1702            Expr::And(ops) => assert_eq!(ops.len(), 2),
1703            other => panic!("Expected And, got {other:?}"),
1704        }
1705        // Also verify a standalone multi-predicate query inside a subquery parses correctly.
1706        let inner = parse("kind:method lang:rust").unwrap();
1707        assert!(matches!(inner.root, Expr::And(_)));
1708    }
1709
1710    #[test]
1711    fn test_implicit_and_after_relation_subquery() {
1712        // `callers:(kind:function name:main) lang:rust`
1713        // → And([JoinExpr(callers, And([...])), lang=rust])
1714        let query = parse("callers:(kind:function name:main) lang:rust").unwrap();
1715        match query.root {
1716            Expr::And(ref ops) => {
1717                assert_eq!(ops.len(), 2, "Expected And with 2 operands");
1718            }
1719            other => panic!("Expected And, got {other:?}"),
1720        }
1721    }
1722
1723    #[test]
1724    fn test_error_and_and() {
1725        // `kind:function AND AND name:test` — double AND is a syntax error.
1726        let result = parse("kind:function AND AND name:test");
1727        assert!(
1728            result.is_err(),
1729            "Expected parse error for consecutive AND keywords"
1730        );
1731    }
1732
1733    #[test]
1734    fn test_error_trailing_and() {
1735        // `kind:function AND` — trailing AND with no right operand is an error.
1736        let result = parse("kind:function AND");
1737        assert!(
1738            result.is_err(),
1739            "Expected parse error for trailing AND keyword"
1740        );
1741    }
1742
1743    #[test]
1744    fn test_error_colon_colon() {
1745        // `::` — double colon has no valid interpretation.
1746        let result = parse(": :");
1747        assert!(result.is_err(), "Expected parse error for ':: '");
1748    }
1749
1750    #[test]
1751    fn test_implicit_and_regex_bare_token() {
1752        // `/test_/` as a bare token should promote to name~=/test_/
1753        let query = parse("kind:function /test_/").unwrap();
1754        match query.root {
1755            Expr::And(ref ops) => {
1756                assert_eq!(ops.len(), 2);
1757                match &ops[1] {
1758                    Expr::Condition(c) => {
1759                        assert_eq!(c.field.as_str(), "name");
1760                        assert_eq!(c.operator, Operator::Regex);
1761                    }
1762                    other => panic!("Expected Condition for regex bare token, got {other:?}"),
1763                }
1764            }
1765            other => panic!("Expected And, got {other:?}"),
1766        }
1767    }
1768
1769    #[test]
1770    fn test_implicit_and_string_literal_bare_token() {
1771        // `"my_func"` as a bare token should promote to name="my_func"
1772        let query = parse(r#"kind:function "my_func""#).unwrap();
1773        match query.root {
1774            Expr::And(ref ops) => {
1775                assert_eq!(ops.len(), 2);
1776                match &ops[1] {
1777                    Expr::Condition(c) => {
1778                        assert_eq!(c.field.as_str(), "name");
1779                    }
1780                    other => panic!("Expected Condition for string literal, got {other:?}"),
1781                }
1782            }
1783            other => panic!("Expected And, got {other:?}"),
1784        }
1785    }
1786
1787    #[test]
1788    fn test_implicit_and_multiple_bare_words() {
1789        // `foo bar baz` → And([name~=/foo/, name~=/bar/, name~=/baz/])
1790        let query = parse("foo bar baz").unwrap();
1791        match query.root {
1792            Expr::And(ref ops) => assert_eq!(ops.len(), 3),
1793            other => panic!("Expected And with 3 operands, got {other:?}"),
1794        }
1795    }
1796
1797    #[test]
1798    fn test_implicit_and_does_not_affect_explicit_or() {
1799        // Regression: `kind:function OR kind:method` must remain a flat Or, not And.
1800        let query = parse("kind:function OR kind:method").unwrap();
1801        assert!(
1802            matches!(query.root, Expr::Or(_)),
1803            "Expected Or, implicit AND must not absorb OR"
1804        );
1805    }
1806
1807    #[test]
1808    fn test_implicit_and_same_ast_as_explicit_and() {
1809        // `kind:function lang:rust` and `kind:function AND lang:rust` must yield
1810        // structurally equivalent ASTs (same fields, operators, and values).
1811        let implicit = parse("kind:function lang:rust").unwrap();
1812        let explicit = parse("kind:function AND lang:rust").unwrap();
1813
1814        // Helper: assert a condition has the expected field and string value.
1815        fn assert_string_cond(expr: &Expr, expected_field: &str, expected_value: &str) {
1816            match expr {
1817                Expr::Condition(c) => {
1818                    assert_eq!(
1819                        c.field.as_str(),
1820                        expected_field,
1821                        "Expected field '{expected_field}'"
1822                    );
1823                    assert_eq!(c.operator, Operator::Equal, "Expected Equals operator");
1824                    assert!(
1825                        matches!(&c.value, Value::String(s) if s == expected_value),
1826                        "Expected value '{expected_value}', got {:?}",
1827                        c.value
1828                    );
1829                }
1830                other => panic!("Expected Condition, got {other:?}"),
1831            }
1832        }
1833
1834        match (&implicit.root, &explicit.root) {
1835            (Expr::And(imp_ops), Expr::And(exp_ops)) => {
1836                assert_eq!(imp_ops.len(), 2, "Expected 2 implicit AND operands");
1837                assert_eq!(exp_ops.len(), 2, "Expected 2 explicit AND operands");
1838                // First operand: kind:function
1839                assert_string_cond(&imp_ops[0], "kind", "function");
1840                assert_string_cond(&exp_ops[0], "kind", "function");
1841                // Second operand: lang:rust
1842                assert_string_cond(&imp_ops[1], "lang", "rust");
1843                assert_string_cond(&exp_ops[1], "lang", "rust");
1844            }
1845            _ => panic!("Both queries must parse to And with 2 operands"),
1846        }
1847    }
1848
1849    #[test]
1850    fn test_implicit_and_with_leading_not() {
1851        // `NOT kind:function lang:rust` → And([Not(kind=function), lang=rust])
1852        // NOT has highest precedence, so it only negates the immediately following predicate.
1853        let query = parse("NOT kind:function lang:rust").unwrap();
1854        match query.root {
1855            Expr::And(ref ops) => {
1856                assert_eq!(ops.len(), 2);
1857                assert!(
1858                    matches!(&ops[0], Expr::Not(_)),
1859                    "First operand should be Not"
1860                );
1861            }
1862            other => panic!("Expected And, got {other:?}"),
1863        }
1864    }
1865
1866    #[test]
1867    fn test_join_keyword_stops_implicit_and() {
1868        // `(kind:function) CALLS (kind:function)` — CALLS must be treated as a join
1869        // operator, not an implicit-AND operand.
1870        let query = parse("(kind:function) CALLS (kind:function)").unwrap();
1871        // The result must be a Join with edge=Calls, not an And that consumed CALLS
1872        // as a bare-word name~=/CALLS/ condition.
1873        match query.root {
1874            Expr::Join(join) => {
1875                assert_eq!(
1876                    join.edge,
1877                    JoinEdgeKind::Calls,
1878                    "Expected JoinEdgeKind::Calls"
1879                );
1880                match *join.left {
1881                    Expr::Condition(ref c) => assert_eq!(c.field.as_str(), "kind"),
1882                    other => panic!("Expected kind Condition on left, got {other:?}"),
1883                }
1884                match *join.right {
1885                    Expr::Condition(ref c) => assert_eq!(c.field.as_str(), "kind"),
1886                    other => panic!("Expected kind Condition on right, got {other:?}"),
1887                }
1888            }
1889            other => panic!("Expected Join expression, got {other:?}"),
1890        }
1891    }
1892}