1use 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
14pub struct Parser {
16 tokens: Vec<Token>,
18 position: usize,
20}
21
22impl Parser {
23 #[must_use]
25 pub fn new(tokens: Vec<Token>) -> Self {
26 Self {
27 tokens,
28 position: 0,
29 }
30 }
31
32 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 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 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 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 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 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 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 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 fn parse_join(&mut self) -> Result<Expr, ParseError> {
249 let left = self.parse_or()?;
250
251 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 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: Span::new(0, 0),
301 })
302 } else {
303 Ok(Expr::Or(operands))
304 }
305 }
306
307 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: Span::new(0, 0),
324 })
325 } else {
326 Ok(Expr::And(operands))
327 }
328 }
329
330 fn parse_not(&mut self) -> Result<Expr, ParseError> {
333 if self.match_token(&TokenType::Not) {
334 let operand = self.parse_not()?; Ok(Expr::Not(Box::new(operand)))
336 } else {
337 self.parse_primary()
338 }
339 }
340
341 fn parse_primary(&mut self) -> Result<Expr, ParseError> {
344 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 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 fn parse_condition(&mut self) -> Result<Expr, ParseError> {
411 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 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 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(); 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 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 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 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 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 fn is_at_end(&self) -> bool {
529 matches!(self.peek().token_type, TokenType::Eof)
530 }
531
532 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 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
554fn 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 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 assert!(matches!(operands[0], Expr::Condition(_)));
680
681 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 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 assert!(matches!(operands[0], Expr::Not(_)));
704
705 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 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 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 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 assert!(matches!(operands[0], Expr::Or(_)));
771
772 assert!(matches!(operands[1], Expr::Condition(_)));
774
775 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 let _ = parse("a:b").unwrap(); let _ = parse("a~=/b/").unwrap(); let _ = parse("a>1").unwrap(); let _ = parse("a<1").unwrap(); let _ = parse("a>=1").unwrap(); let _ = parse("a<=1").unwrap(); }
851
852 #[test]
853 fn test_value_types() {
854 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 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 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 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 let result = parse("kind function");
979 assert!(matches!(result, Err(ParseError::UnexpectedToken { .. })));
980 }
981
982 #[test]
983 fn test_precedence_complex_1() {
984 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 assert!(matches!(operands[0], Expr::Condition(_)));
993
994 assert!(matches!(operands[1], Expr::And(_)));
996
997 assert!(matches!(operands[2], Expr::Condition(_)));
999 }
1000 _ => panic!("Expected Or"),
1001 }
1002 }
1003
1004 #[test]
1005 fn test_precedence_complex_2() {
1006 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 assert!(matches!(operands[0], Expr::Not(_)));
1015
1016 assert!(matches!(operands[1], Expr::And(_)));
1018 }
1019 _ => panic!("Expected Or"),
1020 }
1021 }
1022
1023 #[test]
1024 fn test_multiple_groups() {
1025 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 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 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 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 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 assert!(matches!(operands[1], Expr::Condition(_)));
1077 }
1078 _ => panic!("Expected And"),
1079 }
1080 }
1081
1082 #[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 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 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 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 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 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 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 #[test]
1313 fn test_single_operand_or_not_wrapped() {
1314 let query = parse("kind:function").unwrap();
1316
1317 match query.root {
1318 Expr::Condition(_) => {
1319 }
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 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 let query = parse("name:test").unwrap();
1347
1348 match query.root {
1349 Expr::Condition(_) => {
1350 }
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 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 #[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 let query = parse("(kind:function)").unwrap();
1417 match query.root {
1418 Expr::Condition(cond) => {
1419 assert_eq!(cond.field.as_str(), "kind");
1420 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}