1use super::ast::*;
4
5#[derive(Debug, Clone)]
7pub enum QueryError {
8 ParseError { position: usize, message: String },
10 UnknownElement(String),
12 UnknownAttribute(String),
14 UnknownPseudo(String),
16 InvalidCombinator(String),
18 TypeMismatch { expected: String, got: String },
20 EmptyQuery,
22}
23
24impl std::fmt::Display for QueryError {
25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 match self {
27 QueryError::ParseError { position, message } => {
28 write!(f, "Parse error at position {}: {}", position, message)
29 }
30 QueryError::UnknownElement(e) => write!(f, "Unknown element type: {}", e),
31 QueryError::UnknownAttribute(a) => write!(f, "Unknown attribute: {}", a),
32 QueryError::UnknownPseudo(p) => write!(f, "Unknown pseudo-selector: :{}", p),
33 QueryError::InvalidCombinator(c) => write!(f, "Invalid combinator: {}", c),
34 QueryError::TypeMismatch { expected, got } => {
35 write!(f, "Type mismatch: expected {}, got {}", expected, got)
36 }
37 QueryError::EmptyQuery => write!(f, "Empty query"),
38 }
39 }
40}
41
42impl std::error::Error for QueryError {}
43
44#[derive(Debug, Clone, PartialEq)]
46enum Token {
47 Ident(String),
49 String(String),
50 Number(i64),
51 Float(f64),
52
53 Hash, Dot, Comma, Colon, DoubleColon, Star, Greater, DoubleGreater, Plus, Tilde, LBracket, RBracket, LParen, RParen, Equals, NotEquals, TildeEquals, CaretEquals, DollarEquals, StarEquals, GreaterEquals, LessEquals, Less, Eof,
80}
81
82pub struct QueryParser {
84 }
86
87impl QueryParser {
88 pub fn new() -> Self {
90 Self {}
91 }
92
93 pub fn parse(&self, input: &str) -> Result<Selector, QueryError> {
95 let input = input.trim();
96 if input.is_empty() {
97 return Err(QueryError::EmptyQuery);
98 }
99
100 let tokens = self.tokenize(input)?;
101 let mut parser = SelectorParser::new(tokens);
102 parser.parse_selector_list()
103 }
104
105 fn tokenize(&self, input: &str) -> Result<Vec<Token>, QueryError> {
107 let mut tokens = Vec::new();
108 let mut chars = input.chars().peekable();
109 let mut pos = 0;
110
111 while let Some(&c) = chars.peek() {
112 match c {
113 ' ' | '\t' | '\n' | '\r' => {
115 chars.next();
116 pos += 1;
117 }
118
119 '#' => {
121 chars.next();
122 pos += 1;
123 tokens.push(Token::Hash);
124 }
125 '.' => {
126 chars.next();
127 pos += 1;
128 tokens.push(Token::Dot);
129 }
130 ',' => {
131 chars.next();
132 pos += 1;
133 tokens.push(Token::Comma);
134 }
135 '[' => {
136 chars.next();
137 pos += 1;
138 tokens.push(Token::LBracket);
139 }
140 ']' => {
141 chars.next();
142 pos += 1;
143 tokens.push(Token::RBracket);
144 }
145 '(' => {
146 chars.next();
147 pos += 1;
148 tokens.push(Token::LParen);
149 }
150 ')' => {
151 chars.next();
152 pos += 1;
153 tokens.push(Token::RParen);
154 }
155 '+' => {
156 chars.next();
157 pos += 1;
158 tokens.push(Token::Plus);
159 }
160
161 '*' => {
163 chars.next();
164 pos += 1;
165 if chars.peek() == Some(&'=') {
166 chars.next();
167 pos += 1;
168 tokens.push(Token::StarEquals);
169 } else {
170 tokens.push(Token::Star);
171 }
172 }
173
174 ':' => {
176 chars.next();
177 pos += 1;
178 if chars.peek() == Some(&':') {
179 chars.next();
180 pos += 1;
181 tokens.push(Token::DoubleColon);
182 } else {
183 tokens.push(Token::Colon);
184 }
185 }
186
187 '>' => {
189 chars.next();
190 pos += 1;
191 if chars.peek() == Some(&'>') {
192 chars.next();
193 pos += 1;
194 tokens.push(Token::DoubleGreater);
195 } else if chars.peek() == Some(&'=') {
196 chars.next();
197 pos += 1;
198 tokens.push(Token::GreaterEquals);
199 } else {
200 tokens.push(Token::Greater);
201 }
202 }
203
204 '<' => {
206 chars.next();
207 pos += 1;
208 if chars.peek() == Some(&'=') {
209 chars.next();
210 pos += 1;
211 tokens.push(Token::LessEquals);
212 } else {
213 tokens.push(Token::Less);
214 }
215 }
216
217 '~' => {
219 chars.next();
220 pos += 1;
221 if chars.peek() == Some(&'=') {
222 chars.next();
223 pos += 1;
224 tokens.push(Token::TildeEquals);
225 } else {
226 tokens.push(Token::Tilde);
227 }
228 }
229
230 '^' => {
232 chars.next();
233 pos += 1;
234 if chars.peek() == Some(&'=') {
235 chars.next();
236 pos += 1;
237 tokens.push(Token::CaretEquals);
238 } else {
239 return Err(QueryError::ParseError {
240 position: pos,
241 message: "Expected '=' after '^'".to_string(),
242 });
243 }
244 }
245
246 '$' => {
248 chars.next();
249 pos += 1;
250 if chars.peek() == Some(&'=') {
251 chars.next();
252 pos += 1;
253 tokens.push(Token::DollarEquals);
254 } else {
255 return Err(QueryError::ParseError {
256 position: pos,
257 message: "Expected '=' after '$'".to_string(),
258 });
259 }
260 }
261
262 '!' => {
264 chars.next();
265 pos += 1;
266 if chars.peek() == Some(&'=') {
267 chars.next();
268 pos += 1;
269 tokens.push(Token::NotEquals);
270 } else {
271 return Err(QueryError::ParseError {
272 position: pos,
273 message: "Expected '=' after '!'".to_string(),
274 });
275 }
276 }
277
278 '=' => {
279 chars.next();
280 pos += 1;
281 tokens.push(Token::Equals);
282 }
283
284 '"' | '\'' => {
286 let quote = c;
287 chars.next();
288 pos += 1;
289 let mut s = String::new();
290 loop {
291 match chars.peek() {
292 Some(&c) if c == quote => {
293 chars.next();
294 pos += 1;
295 break;
296 }
297 Some(&'\\') => {
298 chars.next();
299 pos += 1;
300 if let Some(&escaped) = chars.peek() {
301 chars.next();
302 pos += 1;
303 s.push(escaped);
304 }
305 }
306 Some(&c) => {
307 s.push(c);
308 chars.next();
309 pos += 1;
310 }
311 None => {
312 return Err(QueryError::ParseError {
313 position: pos,
314 message: "Unterminated string".to_string(),
315 });
316 }
317 }
318 }
319 tokens.push(Token::String(s));
320 }
321
322 '0'..='9' => {
324 let mut num_str = String::new();
325 let mut has_dot = false;
326 while let Some(&c) = chars.peek() {
327 if c.is_ascii_digit() {
328 num_str.push(c);
329 chars.next();
330 pos += 1;
331 } else if c == '.' && !has_dot {
332 has_dot = true;
333 num_str.push(c);
334 chars.next();
335 pos += 1;
336 } else {
337 break;
338 }
339 }
340 if has_dot {
341 tokens.push(Token::Float(num_str.parse().unwrap_or(0.0)));
342 } else {
343 tokens.push(Token::Number(num_str.parse().unwrap_or(0)));
344 }
345 }
346
347 '-' => {
349 chars.next();
350 pos += 1;
351 if chars.peek().is_some_and(|c| c.is_ascii_digit()) {
352 let mut num_str = String::from("-");
353 let mut has_dot = false;
354 while let Some(&c) = chars.peek() {
355 if c.is_ascii_digit() {
356 num_str.push(c);
357 chars.next();
358 pos += 1;
359 } else if c == '.' && !has_dot {
360 has_dot = true;
361 num_str.push(c);
362 chars.next();
363 pos += 1;
364 } else {
365 break;
366 }
367 }
368 if has_dot {
369 tokens.push(Token::Float(num_str.parse().unwrap_or(0.0)));
370 } else {
371 tokens.push(Token::Number(num_str.parse().unwrap_or(0)));
372 }
373 } else {
374 let mut ident = String::from("-");
376 while let Some(&c) = chars.peek() {
377 if c.is_alphanumeric() || c == '_' || c == '-' {
378 ident.push(c);
379 chars.next();
380 pos += 1;
381 } else {
382 break;
383 }
384 }
385 tokens.push(Token::Ident(ident));
386 }
387 }
388
389 _ if c.is_alphabetic() || c == '_' => {
391 let mut ident = String::new();
392 while let Some(&c) = chars.peek() {
393 if c.is_alphanumeric() || c == '_' || c == '-' {
394 ident.push(c);
395 chars.next();
396 pos += 1;
397 } else {
398 break;
399 }
400 }
401 tokens.push(Token::Ident(ident));
402 }
403
404 _ => {
405 return Err(QueryError::ParseError {
406 position: pos,
407 message: format!("Unexpected character: '{}'", c),
408 });
409 }
410 }
411 }
412
413 tokens.push(Token::Eof);
414 Ok(tokens)
415 }
416}
417
418impl Default for QueryParser {
419 fn default() -> Self {
420 Self::new()
421 }
422}
423
424struct SelectorParser {
426 tokens: Vec<Token>,
427 pos: usize,
428}
429
430impl SelectorParser {
431 fn new(tokens: Vec<Token>) -> Self {
432 Self { tokens, pos: 0 }
433 }
434
435 fn current(&self) -> &Token {
436 self.tokens.get(self.pos).unwrap_or(&Token::Eof)
437 }
438
439 fn _peek_next(&self) -> &Token {
440 self.tokens.get(self.pos + 1).unwrap_or(&Token::Eof)
441 }
442
443 fn advance(&mut self) -> Token {
444 let tok = self.current().clone();
445 if self.pos < self.tokens.len() {
446 self.pos += 1;
447 }
448 tok
449 }
450
451 fn expect(&mut self, expected: &Token) -> Result<(), QueryError> {
452 if self.current() == expected {
453 self.advance();
454 Ok(())
455 } else {
456 Err(QueryError::ParseError {
457 position: self.pos,
458 message: format!("Expected {:?}, got {:?}", expected, self.current()),
459 })
460 }
461 }
462
463 fn parse_selector_list(&mut self) -> Result<Selector, QueryError> {
465 let mut selectors = vec![self.parse_combinator_chain()?];
466
467 while self.current() == &Token::Comma {
468 self.advance();
469 selectors.push(self.parse_combinator_chain()?);
470 }
471
472 if selectors.len() == 1 {
473 Ok(selectors.pop().unwrap())
474 } else {
475 Ok(Selector::Union(selectors))
476 }
477 }
478
479 fn parse_combinator_chain(&mut self) -> Result<Selector, QueryError> {
481 let mut left = self.parse_compound_selector()?;
482
483 loop {
484 let combinator = match self.current() {
485 Token::Greater => {
486 self.advance();
487 CombinatorType::Child
488 }
489 Token::DoubleGreater => {
490 self.advance();
491 CombinatorType::Connected
492 }
493 Token::Tilde => {
494 self.advance();
495 CombinatorType::Sibling
496 }
497 Token::Plus => {
498 self.advance();
499 CombinatorType::Adjacent
500 }
501 Token::DoubleColon => {
502 self.advance();
503 CombinatorType::OnNet
504 }
505 Token::Ident(_) | Token::Hash | Token::Star | Token::LBracket | Token::Colon => {
508 CombinatorType::Descendant
509 }
510 _ => break,
511 };
512
513 let right = self.parse_compound_selector()?;
514 left = Selector::Combinator {
515 left: Box::new(left),
516 combinator,
517 right: Box::new(right),
518 };
519 }
520
521 Ok(left)
522 }
523
524 fn parse_compound_selector(&mut self) -> Result<Selector, QueryError> {
536 let mut parts = Vec::new();
537 let mut has_primary = false; loop {
540 match self.current() {
541 Token::Ident(name) if !has_primary => {
542 let name = name.clone();
543 self.advance();
544 if let Some(elem_type) = ElementType::try_parse(&name) {
545 parts.push(Selector::Element(elem_type));
546 has_primary = true;
547 } else {
548 return Err(QueryError::UnknownElement(name));
549 }
550 }
551 Token::Hash if !has_primary => {
552 self.advance();
553 match self.current() {
554 Token::Ident(id) => {
555 let id = id.clone();
556 self.advance();
557 parts.push(Selector::Id(id));
558 has_primary = true;
559 }
560 Token::Number(n) => {
561 let id = n.to_string();
563 self.advance();
564 parts.push(Selector::Id(id));
565 has_primary = true;
566 }
567 _ => {
568 return Err(QueryError::ParseError {
569 position: self.pos,
570 message: "Expected identifier after #".to_string(),
571 });
572 }
573 }
574 }
575 Token::Star if !has_primary => {
576 self.advance();
577 parts.push(Selector::Universal);
578 has_primary = true;
579 }
580 Token::LBracket => {
581 parts.push(self.parse_attribute_selector()?);
582 }
583 Token::Colon => {
584 parts.push(self.parse_pseudo_selector()?);
585 }
586 _ => break,
587 }
588 }
589
590 if parts.is_empty() {
591 Err(QueryError::ParseError {
592 position: self.pos,
593 message: format!("Expected selector, got {:?}", self.current()),
594 })
595 } else if parts.len() == 1 {
596 Ok(parts.pop().unwrap())
597 } else {
598 Ok(Selector::Compound(parts))
599 }
600 }
601
602 fn parse_attribute_selector(&mut self) -> Result<Selector, QueryError> {
604 self.expect(&Token::LBracket)?;
605
606 let name = match self.current() {
607 Token::Ident(name) => {
608 let n = name.clone().to_lowercase();
609 self.advance();
610 n
611 }
612 _ => {
613 return Err(QueryError::ParseError {
614 position: self.pos,
615 message: "Expected attribute name".to_string(),
616 });
617 }
618 };
619
620 let (op, value) = match self.current() {
622 Token::RBracket => (AttributeOp::Exists, None),
623 Token::Equals => {
624 self.advance();
625 (AttributeOp::Equals, Some(self.parse_value()?))
626 }
627 Token::NotEquals => {
628 self.advance();
629 (AttributeOp::NotEquals, Some(self.parse_value()?))
630 }
631 Token::TildeEquals => {
632 self.advance();
633 (AttributeOp::WordMatch, Some(self.parse_value()?))
634 }
635 Token::CaretEquals => {
636 self.advance();
637 (AttributeOp::StartsWith, Some(self.parse_value()?))
638 }
639 Token::DollarEquals => {
640 self.advance();
641 (AttributeOp::EndsWith, Some(self.parse_value()?))
642 }
643 Token::StarEquals => {
644 self.advance();
645 (AttributeOp::Contains, Some(self.parse_value()?))
646 }
647 Token::Greater => {
648 self.advance();
649 (AttributeOp::GreaterThan, Some(self.parse_value()?))
650 }
651 Token::Less => {
652 self.advance();
653 (AttributeOp::LessThan, Some(self.parse_value()?))
654 }
655 Token::GreaterEquals => {
656 self.advance();
657 (AttributeOp::GreaterOrEqual, Some(self.parse_value()?))
658 }
659 Token::LessEquals => {
660 self.advance();
661 (AttributeOp::LessOrEqual, Some(self.parse_value()?))
662 }
663 _ => {
664 return Err(QueryError::ParseError {
665 position: self.pos,
666 message: format!("Expected operator or ], got {:?}", self.current()),
667 });
668 }
669 };
670
671 let case_insensitive = if let Token::Ident(flag) = self.current() {
673 if flag.to_lowercase() == "i" {
674 self.advance();
675 true
676 } else {
677 false
678 }
679 } else {
680 false
681 };
682
683 self.expect(&Token::RBracket)?;
684
685 Ok(Selector::Attribute(AttributeSelector {
686 name,
687 op,
688 value,
689 case_insensitive,
690 }))
691 }
692
693 fn parse_pseudo_selector(&mut self) -> Result<Selector, QueryError> {
695 self.expect(&Token::Colon)?;
696
697 let name = match self.current() {
698 Token::Ident(name) => {
699 let n = name.clone().to_lowercase();
700 self.advance();
701 n
702 }
703 _ => {
704 return Err(QueryError::ParseError {
705 position: self.pos,
706 message: "Expected pseudo-selector name".to_string(),
707 });
708 }
709 };
710
711 if name == "not" {
713 self.expect(&Token::LParen)?;
714 let inner = self.parse_selector_list()?;
715 self.expect(&Token::RParen)?;
716 return Ok(Selector::Not(Box::new(inner)));
717 }
718
719 if name == "has" {
720 self.expect(&Token::LParen)?;
721 let inner = self.parse_selector_list()?;
722 self.expect(&Token::RParen)?;
723 return Ok(Selector::Has(Box::new(inner)));
724 }
725
726 let arg = if self.current() == &Token::LParen {
728 self.advance();
729 let arg = self.parse_value()?;
730 self.expect(&Token::RParen)?;
731 Some(arg)
732 } else {
733 None
734 };
735
736 if let Some(pseudo) = PseudoSelector::try_parse(&name, arg.as_deref()) {
737 Ok(Selector::Pseudo(pseudo))
738 } else {
739 Err(QueryError::UnknownPseudo(name))
740 }
741 }
742
743 fn parse_value(&mut self) -> Result<String, QueryError> {
745 match self.current().clone() {
746 Token::String(s) => {
747 self.advance();
748 Ok(s)
749 }
750 Token::Number(n) => {
751 self.advance();
752 Ok(n.to_string())
753 }
754 Token::Float(f) => {
755 self.advance();
756 Ok(f.to_string())
757 }
758 Token::Ident(s) => {
759 self.advance();
760 Ok(s)
761 }
762 _ => Err(QueryError::ParseError {
763 position: self.pos,
764 message: format!("Expected value, got {:?}", self.current()),
765 }),
766 }
767 }
768}
769
770#[cfg(test)]
771mod tests {
772 use super::*;
773
774 #[test]
775 fn test_parse_element() {
776 let parser = QueryParser::new();
777 let sel = parser.parse("component").unwrap();
778 assert_eq!(sel, Selector::Element(ElementType::Component));
779 }
780
781 #[test]
782 fn test_parse_id() {
783 let parser = QueryParser::new();
784 let sel = parser.parse("#U1").unwrap();
785 assert_eq!(sel, Selector::Id("U1".to_string()));
786 }
787
788 #[test]
789 fn test_parse_attribute() {
790 let parser = QueryParser::new();
791 let sel = parser.parse("[part=LM7805]").unwrap();
792 match sel {
793 Selector::Attribute(attr) => {
794 assert_eq!(attr.name, "part");
795 assert_eq!(attr.op, AttributeOp::Equals);
796 assert_eq!(attr.value, Some("LM7805".to_string()));
797 }
798 _ => panic!("Expected attribute selector"),
799 }
800 }
801
802 #[test]
803 fn test_parse_attribute_contains() {
804 let parser = QueryParser::new();
805 let sel = parser.parse("[part*=7805]").unwrap();
806 match sel {
807 Selector::Attribute(attr) => {
808 assert_eq!(attr.name, "part");
809 assert_eq!(attr.op, AttributeOp::Contains);
810 assert_eq!(attr.value, Some("7805".to_string()));
811 }
812 _ => panic!("Expected attribute selector"),
813 }
814 }
815
816 #[test]
817 fn test_parse_pseudo() {
818 let parser = QueryParser::new();
819 let sel = parser.parse(":connected").unwrap();
820 assert_eq!(sel, Selector::Pseudo(PseudoSelector::Connected));
821 }
822
823 #[test]
824 fn test_parse_pseudo_with_arg() {
825 let parser = QueryParser::new();
826 let sel = parser.parse(":limit(10)").unwrap();
827 assert_eq!(sel, Selector::Pseudo(PseudoSelector::Limit(10)));
828 }
829
830 #[test]
831 fn test_parse_compound() {
832 let parser = QueryParser::new();
833 let sel = parser.parse("pin[type=input]").unwrap();
834 match sel {
835 Selector::Compound(parts) => {
836 assert_eq!(parts.len(), 2);
837 assert_eq!(parts[0], Selector::Element(ElementType::Pin));
838 }
839 _ => panic!("Expected compound selector"),
840 }
841 }
842
843 #[test]
844 fn test_parse_child_combinator() {
845 let parser = QueryParser::new();
846 let sel = parser.parse("#U1 > pin").unwrap();
847 match sel {
848 Selector::Combinator {
849 left,
850 combinator,
851 right,
852 } => {
853 assert_eq!(*left, Selector::Id("U1".to_string()));
854 assert_eq!(combinator, CombinatorType::Child);
855 assert_eq!(*right, Selector::Element(ElementType::Pin));
856 }
857 _ => panic!("Expected combinator selector"),
858 }
859 }
860
861 #[test]
862 fn test_parse_descendant_combinator() {
863 let parser = QueryParser::new();
864 let sel = parser.parse("#U1 pin").unwrap();
865 match sel {
866 Selector::Combinator { combinator, .. } => {
867 assert_eq!(combinator, CombinatorType::Descendant);
868 }
869 _ => panic!("Expected combinator selector"),
870 }
871 }
872
873 #[test]
874 fn test_parse_union() {
875 let parser = QueryParser::new();
876 let sel = parser.parse("component, port").unwrap();
877 match sel {
878 Selector::Union(selectors) => {
879 assert_eq!(selectors.len(), 2);
880 }
881 _ => panic!("Expected union selector"),
882 }
883 }
884
885 #[test]
886 fn test_parse_not() {
887 let parser = QueryParser::new();
888 let sel = parser.parse(":not(pin)").unwrap();
889 match sel {
890 Selector::Not(inner) => {
891 assert_eq!(*inner, Selector::Element(ElementType::Pin));
892 }
893 _ => panic!("Expected not selector"),
894 }
895 }
896
897 #[test]
898 fn test_parse_complex() {
899 let parser = QueryParser::new();
900 let sel = parser
902 .parse("component[part*=7805] > pin[type=input]")
903 .unwrap();
904 match sel {
905 Selector::Combinator {
906 left,
907 combinator,
908 right,
909 } => {
910 assert_eq!(combinator, CombinatorType::Child);
911 match *left {
912 Selector::Compound(ref parts) => {
913 assert_eq!(parts.len(), 2);
914 }
915 _ => panic!("Expected compound on left"),
916 }
917 match *right {
918 Selector::Compound(ref parts) => {
919 assert_eq!(parts.len(), 2);
920 }
921 _ => panic!("Expected compound on right"),
922 }
923 }
924 _ => panic!("Expected combinator"),
925 }
926 }
927
928 #[test]
929 fn test_parse_on_net() {
930 let parser = QueryParser::new();
931 let sel = parser.parse("#VCC :: pin").unwrap();
932 match sel {
933 Selector::Combinator { combinator, .. } => {
934 assert_eq!(combinator, CombinatorType::OnNet);
935 }
936 _ => panic!("Expected on-net combinator"),
937 }
938 }
939
940 #[test]
941 fn test_parse_string_value() {
942 let parser = QueryParser::new();
943 let sel = parser.parse("[name=\"CLK IN\"]").unwrap();
944 match sel {
945 Selector::Attribute(attr) => {
946 assert_eq!(attr.value, Some("CLK IN".to_string()));
947 }
948 _ => panic!("Expected attribute selector"),
949 }
950 }
951}