1use std::collections::HashMap;
18
19use super::ast::ConditionExpr;
20use super::token::{strip_status_prefix, tokenize, SpannedToken, Token};
21use crate::error::ParseError;
22
23pub struct ConditionParser;
25
26impl ConditionParser {
27 pub fn parse(input: &str) -> Result<Option<ConditionExpr>, ParseError> {
42 Self::parse_with_ub(input, &HashMap::new())
43 }
44
45 pub fn parse_with_ub(
51 input: &str,
52 ub_definitions: &HashMap<String, ConditionExpr>,
53 ) -> Result<Option<ConditionExpr>, ParseError> {
54 let input = input.trim();
55 if input.is_empty() {
56 return Ok(None);
57 }
58
59 let lines: Vec<&str> = input
68 .lines()
69 .map(str::trim)
70 .filter(|l| !l.is_empty())
71 .collect();
72
73 let mut alternatives: Vec<ConditionExpr> = Vec::new();
74 for line in &lines {
75 let stripped = strip_status_prefix(line);
76 if stripped.is_empty() {
77 continue;
78 }
79 let tokens = tokenize(stripped)?;
80 if tokens.is_empty() {
81 continue;
82 }
83 let mut pos = 0;
84 if let Some(expr) = parse_expression(&tokens, &mut pos, ub_definitions)? {
85 alternatives.push(expr);
86 }
87 }
88
89 match alternatives.len() {
90 0 => Ok(None),
91 1 => Ok(Some(alternatives.into_iter().next().unwrap())),
92 _ => Ok(Some(ConditionExpr::Or(alternatives))),
93 }
94 }
95
96 pub fn parse_raw(input: &str) -> Result<Option<ConditionExpr>, ParseError> {
100 let input = input.trim();
101 if input.is_empty() {
102 return Ok(None);
103 }
104
105 let tokens = tokenize(input)?;
106 if tokens.is_empty() {
107 return Ok(None);
108 }
109
110 let mut pos = 0;
111 let expr = parse_expression(&tokens, &mut pos, &HashMap::new())?;
112
113 Ok(expr)
114 }
115}
116
117fn parse_expression(
119 tokens: &[SpannedToken],
120 pos: &mut usize,
121 ub_definitions: &HashMap<String, ConditionExpr>,
122) -> Result<Option<ConditionExpr>, ParseError> {
123 parse_xor(tokens, pos, ub_definitions)
124}
125
126fn parse_xor(
128 tokens: &[SpannedToken],
129 pos: &mut usize,
130 ub_definitions: &HashMap<String, ConditionExpr>,
131) -> Result<Option<ConditionExpr>, ParseError> {
132 let mut left = match parse_or(tokens, pos, ub_definitions)? {
133 Some(expr) => expr,
134 None => return Ok(None),
135 };
136
137 while *pos < tokens.len() && tokens[*pos].token == Token::Xor {
138 *pos += 1; let right = match parse_or(tokens, pos, ub_definitions)? {
140 Some(expr) => expr,
141 None => return Ok(Some(left)),
142 };
143 left = ConditionExpr::Xor(Box::new(left), Box::new(right));
144 }
145
146 Ok(Some(left))
147}
148
149fn parse_or(
151 tokens: &[SpannedToken],
152 pos: &mut usize,
153 ub_definitions: &HashMap<String, ConditionExpr>,
154) -> Result<Option<ConditionExpr>, ParseError> {
155 let mut left = match parse_and(tokens, pos, ub_definitions)? {
156 Some(expr) => expr,
157 None => return Ok(None),
158 };
159
160 while *pos < tokens.len() && tokens[*pos].token == Token::Or {
161 *pos += 1; let right = match parse_and(tokens, pos, ub_definitions)? {
163 Some(expr) => expr,
164 None => return Ok(Some(left)),
165 };
166 left = match left {
168 ConditionExpr::Or(mut exprs) => {
169 exprs.push(right);
170 ConditionExpr::Or(exprs)
171 }
172 _ => ConditionExpr::Or(vec![left, right]),
173 };
174 }
175
176 Ok(Some(left))
177}
178
179fn parse_and(
182 tokens: &[SpannedToken],
183 pos: &mut usize,
184 ub_definitions: &HashMap<String, ConditionExpr>,
185) -> Result<Option<ConditionExpr>, ParseError> {
186 let mut left = match parse_not(tokens, pos, ub_definitions)? {
187 Some(expr) => expr,
188 None => return Ok(None),
189 };
190
191 while *pos < tokens.len() {
192 if tokens[*pos].token == Token::And {
193 *pos += 1; let right = match parse_not(tokens, pos, ub_definitions)? {
195 Some(expr) => expr,
196 None => return Ok(Some(left)),
197 };
198 left = flatten_and(left, right);
199 } else if matches!(
200 tokens[*pos].token,
201 Token::ConditionId(_) | Token::LeftParen | Token::Not
202 ) {
203 let right = match parse_not(tokens, pos, ub_definitions)? {
205 Some(expr) => expr,
206 None => return Ok(Some(left)),
207 };
208 left = flatten_and(left, right);
209 } else {
210 break;
211 }
212 }
213
214 Ok(Some(left))
215}
216
217fn flatten_and(left: ConditionExpr, right: ConditionExpr) -> ConditionExpr {
219 match left {
220 ConditionExpr::And(mut exprs) => {
221 exprs.push(right);
222 ConditionExpr::And(exprs)
223 }
224 _ => ConditionExpr::And(vec![left, right]),
225 }
226}
227
228fn parse_not(
230 tokens: &[SpannedToken],
231 pos: &mut usize,
232 ub_definitions: &HashMap<String, ConditionExpr>,
233) -> Result<Option<ConditionExpr>, ParseError> {
234 if *pos < tokens.len() && tokens[*pos].token == Token::Not {
235 *pos += 1; let inner = match parse_not(tokens, pos, ub_definitions)? {
237 Some(expr) => expr,
238 None => {
239 return Err(ParseError::UnexpectedToken {
240 position: if *pos < tokens.len() {
241 tokens[*pos].position
242 } else {
243 0
244 },
245 expected: "expression after NOT".to_string(),
246 found: "end of input".to_string(),
247 });
248 }
249 };
250 return Ok(Some(ConditionExpr::Not(Box::new(inner))));
251 }
252 parse_primary(tokens, pos, ub_definitions)
253}
254
255fn parse_primary(
260 tokens: &[SpannedToken],
261 pos: &mut usize,
262 ub_definitions: &HashMap<String, ConditionExpr>,
263) -> Result<Option<ConditionExpr>, ParseError> {
264 if *pos >= tokens.len() {
265 return Ok(None);
266 }
267
268 match &tokens[*pos].token {
269 Token::ConditionId(id) => {
270 *pos += 1;
271 if let Some(ub_expr) = ub_definitions.get(id.as_str()) {
273 return Ok(Some(ub_expr.clone()));
274 }
275 Ok(Some(parse_condition_id(id)))
276 }
277 Token::LeftParen => {
278 *pos += 1; let expr = parse_expression(tokens, pos, ub_definitions)?;
280 if *pos < tokens.len() && tokens[*pos].token == Token::RightParen {
282 *pos += 1;
283 }
284 Ok(expr)
285 }
286 _ => Ok(None),
287 }
288}
289
290fn parse_condition_id(id: &str) -> ConditionExpr {
297 if let Ok(num) = id.parse::<u32>() {
299 return ConditionExpr::Ref(num);
300 }
301
302 if let Some(p_pos) = id.find('P') {
304 let num_part = &id[..p_pos];
305 let range_part = &id[p_pos + 1..];
306 if let Ok(pkg_id) = num_part.parse::<u32>() {
307 let (min, max) = parse_package_range(range_part);
308 return ConditionExpr::Package {
309 id: pkg_id,
310 min,
311 max,
312 };
313 }
314 }
315
316 let numeric_part: String = id.chars().take_while(|c| c.is_ascii_digit()).collect();
318 if let Ok(num) = numeric_part.parse::<u32>() {
319 ConditionExpr::Ref(num)
320 } else {
321 ConditionExpr::Ref(0)
322 }
323}
324
325fn parse_package_range(range: &str) -> (u32, u32) {
327 if range.is_empty() {
328 return (0, u32::MAX);
329 }
330 if let Some((min_str, max_str)) = range.split_once("..") {
331 let min = min_str.parse::<u32>().unwrap_or(0);
332 let max = max_str.parse::<u32>().unwrap_or(u32::MAX);
333 (min, max)
334 } else {
335 let n = range.parse::<u32>().unwrap_or(0);
336 (n, n)
337 }
338}
339
340#[cfg(test)]
341mod tests {
342 use super::*;
343 use pretty_assertions::assert_eq;
344
345 #[test]
348 fn test_parse_single_condition() {
349 let result = ConditionParser::parse("[931]").unwrap().unwrap();
350 assert_eq!(result, ConditionExpr::Ref(931));
351 }
352
353 #[test]
354 fn test_parse_with_muss_prefix() {
355 let result = ConditionParser::parse("Muss [494]").unwrap().unwrap();
356 assert_eq!(result, ConditionExpr::Ref(494));
357 }
358
359 #[test]
360 fn test_parse_with_soll_prefix() {
361 let result = ConditionParser::parse("Soll [494]").unwrap().unwrap();
362 assert_eq!(result, ConditionExpr::Ref(494));
363 }
364
365 #[test]
366 fn test_parse_with_kann_prefix() {
367 let result = ConditionParser::parse("Kann [182]").unwrap().unwrap();
368 assert_eq!(result, ConditionExpr::Ref(182));
369 }
370
371 #[test]
372 fn test_parse_with_x_prefix() {
373 let result = ConditionParser::parse("X [567]").unwrap().unwrap();
374 assert_eq!(result, ConditionExpr::Ref(567));
375 }
376
377 #[test]
380 fn test_parse_simple_and() {
381 let result = ConditionParser::parse("[182] ∧ [152]").unwrap().unwrap();
382 assert_eq!(
383 result,
384 ConditionExpr::And(vec![ConditionExpr::Ref(182), ConditionExpr::Ref(152)])
385 );
386 }
387
388 #[test]
389 fn test_parse_simple_or() {
390 let result = ConditionParser::parse("[1] ∨ [2]").unwrap().unwrap();
391 assert_eq!(
392 result,
393 ConditionExpr::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)])
394 );
395 }
396
397 #[test]
398 fn test_parse_simple_xor() {
399 let result = ConditionParser::parse("[1] ⊻ [2]").unwrap().unwrap();
400 assert_eq!(
401 result,
402 ConditionExpr::Xor(
403 Box::new(ConditionExpr::Ref(1)),
404 Box::new(ConditionExpr::Ref(2)),
405 )
406 );
407 }
408
409 #[test]
412 fn test_parse_three_way_and() {
413 let result = ConditionParser::parse("[1] ∧ [2] ∧ [3]").unwrap().unwrap();
414 assert_eq!(
415 result,
416 ConditionExpr::And(vec![
417 ConditionExpr::Ref(1),
418 ConditionExpr::Ref(2),
419 ConditionExpr::Ref(3),
420 ])
421 );
422 }
423
424 #[test]
425 fn test_parse_three_way_and_with_prefix() {
426 let result = ConditionParser::parse("Kann [182] ∧ [6] ∧ [570]")
427 .unwrap()
428 .unwrap();
429 assert_eq!(
430 result,
431 ConditionExpr::And(vec![
432 ConditionExpr::Ref(182),
433 ConditionExpr::Ref(6),
434 ConditionExpr::Ref(570),
435 ])
436 );
437 assert_eq!(result.condition_ids(), [6, 182, 570].into());
438 }
439
440 #[test]
441 fn test_parse_multiple_xor() {
442 let result = ConditionParser::parse("[1] ⊻ [2] ⊻ [3] ⊻ [4]")
443 .unwrap()
444 .unwrap();
445 assert_eq!(result.condition_ids(), [1, 2, 3, 4].into());
446 }
447
448 #[test]
451 fn test_parse_parenthesized_expression() {
452 let result = ConditionParser::parse("([1] ∨ [2]) ∧ [3]")
453 .unwrap()
454 .unwrap();
455 assert_eq!(
456 result,
457 ConditionExpr::And(vec![
458 ConditionExpr::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]),
459 ConditionExpr::Ref(3),
460 ])
461 );
462 }
463
464 #[test]
465 fn test_parse_nested_parentheses() {
466 let result = ConditionParser::parse("(([1] ∧ [2]) ∨ ([3] ∧ [4])) ∧ [5]")
468 .unwrap()
469 .unwrap();
470 assert_eq!(result.condition_ids(), [1, 2, 3, 4, 5].into());
471 match &result {
473 ConditionExpr::And(exprs) => {
474 assert_eq!(exprs.len(), 2);
475 assert!(matches!(&exprs[0], ConditionExpr::Or(_)));
476 assert_eq!(exprs[1], ConditionExpr::Ref(5));
477 }
478 other => panic!("Expected And, got {other:?}"),
479 }
480 }
481
482 #[test]
485 fn test_and_has_higher_precedence_than_or() {
486 let result = ConditionParser::parse("[1] ∨ [2] ∧ [3]").unwrap().unwrap();
488 assert_eq!(
489 result,
490 ConditionExpr::Or(vec![
491 ConditionExpr::Ref(1),
492 ConditionExpr::And(vec![ConditionExpr::Ref(2), ConditionExpr::Ref(3)]),
493 ])
494 );
495 }
496
497 #[test]
498 fn test_or_has_higher_precedence_than_xor() {
499 let result = ConditionParser::parse("[1] ⊻ [2] ∨ [3]").unwrap().unwrap();
501 assert_eq!(
502 result,
503 ConditionExpr::Xor(
504 Box::new(ConditionExpr::Ref(1)),
505 Box::new(ConditionExpr::Or(vec![
506 ConditionExpr::Ref(2),
507 ConditionExpr::Ref(3),
508 ])),
509 )
510 );
511 }
512
513 #[test]
516 fn test_adjacent_conditions_implicit_and() {
517 let result = ConditionParser::parse("[1] [2]").unwrap().unwrap();
519 assert_eq!(
520 result,
521 ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)])
522 );
523 }
524
525 #[test]
526 fn test_adjacent_conditions_no_space_implicit_and() {
527 let result = ConditionParser::parse("[939][14]").unwrap().unwrap();
529 assert_eq!(
530 result,
531 ConditionExpr::And(vec![ConditionExpr::Ref(939), ConditionExpr::Ref(14)])
532 );
533 }
534
535 #[test]
538 fn test_parse_not() {
539 let result = ConditionParser::parse("NOT [1]").unwrap().unwrap();
540 assert_eq!(result, ConditionExpr::Not(Box::new(ConditionExpr::Ref(1))));
541 }
542
543 #[test]
544 fn test_parse_not_with_and() {
545 let result = ConditionParser::parse("NOT [1] ∧ [2]").unwrap().unwrap();
547 assert_eq!(
548 result,
549 ConditionExpr::And(vec![
550 ConditionExpr::Not(Box::new(ConditionExpr::Ref(1))),
551 ConditionExpr::Ref(2),
552 ])
553 );
554 }
555
556 #[test]
559 fn test_real_world_orders_expression() {
560 let result = ConditionParser::parse("X (([939] [147]) ∨ ([940] [148])) ∧ [567]")
562 .unwrap()
563 .unwrap();
564 assert_eq!(result.condition_ids(), [147, 148, 567, 939, 940].into());
565 }
566
567 #[test]
568 fn test_real_world_xor_expression() {
569 let result = ConditionParser::parse("Muss ([102] ∧ [2006]) ⊻ ([103] ∧ [2005])")
571 .unwrap()
572 .unwrap();
573 assert!(matches!(result, ConditionExpr::Xor(_, _)));
574 assert_eq!(result.condition_ids(), [102, 103, 2005, 2006].into());
575 }
576
577 #[test]
578 fn test_real_world_complex_nested_with_implicit_and() {
579 let result = ConditionParser::parse("([939][14]) ∨ ([940][15])")
581 .unwrap()
582 .unwrap();
583 assert!(matches!(result, ConditionExpr::Or(_)));
584 assert_eq!(result.condition_ids(), [14, 15, 939, 940].into());
585 }
586
587 #[test]
590 fn test_parse_empty_string() {
591 assert!(ConditionParser::parse("").unwrap().is_none());
592 }
593
594 #[test]
595 fn test_parse_whitespace_only() {
596 assert!(ConditionParser::parse(" \t ").unwrap().is_none());
597 }
598
599 #[test]
600 fn test_parse_bare_muss() {
601 assert!(ConditionParser::parse("Muss").unwrap().is_none());
602 }
603
604 #[test]
605 fn test_parse_bare_x() {
606 assert!(ConditionParser::parse("X").unwrap().is_none());
608 }
609
610 #[test]
611 fn test_parse_unmatched_open_paren_graceful() {
612 let result = ConditionParser::parse("([1] ∧ [2]").unwrap().unwrap();
614 assert_eq!(
615 result,
616 ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)])
617 );
618 }
619
620 #[test]
621 fn test_parse_text_and_operator() {
622 let result = ConditionParser::parse("[1] AND [2]").unwrap().unwrap();
623 assert_eq!(
624 result,
625 ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)])
626 );
627 }
628
629 #[test]
630 fn test_parse_text_or_operator() {
631 let result = ConditionParser::parse("[1] OR [2]").unwrap().unwrap();
632 assert_eq!(
633 result,
634 ConditionExpr::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)])
635 );
636 }
637
638 #[test]
639 fn test_parse_text_xor_operator() {
640 let result = ConditionParser::parse("[1] XOR [2]").unwrap().unwrap();
641 assert_eq!(
642 result,
643 ConditionExpr::Xor(
644 Box::new(ConditionExpr::Ref(1)),
645 Box::new(ConditionExpr::Ref(2)),
646 )
647 );
648 }
649
650 #[test]
651 fn test_parse_mixed_unicode_and_text_operators() {
652 let result = ConditionParser::parse("[1] ∧ [2] OR [3]").unwrap().unwrap();
653 assert_eq!(
654 result,
655 ConditionExpr::Or(vec![
656 ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]),
657 ConditionExpr::Ref(3),
658 ])
659 );
660 }
661
662 #[test]
663 fn test_parse_deeply_nested() {
664 let result = ConditionParser::parse("((([1])))").unwrap().unwrap();
666 assert_eq!(result, ConditionExpr::Ref(1));
667 }
668
669 #[test]
672 fn test_parse_package_condition_0_1() {
673 let result = ConditionParser::parse("[4P0..1]").unwrap().unwrap();
674 assert_eq!(
675 result,
676 ConditionExpr::Package {
677 id: 4,
678 min: 0,
679 max: 1
680 }
681 );
682 }
683
684 #[test]
685 fn test_parse_package_condition_1_5() {
686 let result = ConditionParser::parse("[10P1..5]").unwrap().unwrap();
687 assert_eq!(
688 result,
689 ConditionExpr::Package {
690 id: 10,
691 min: 1,
692 max: 5
693 }
694 );
695 }
696
697 #[test]
698 fn test_parse_package_in_expression() {
699 let result = ConditionParser::parse("X [4P0..1] ⊻ [5P0..1]")
700 .unwrap()
701 .unwrap();
702 assert_eq!(
703 result,
704 ConditionExpr::Xor(
705 Box::new(ConditionExpr::Package {
706 id: 4,
707 min: 0,
708 max: 1
709 }),
710 Box::new(ConditionExpr::Package {
711 id: 5,
712 min: 0,
713 max: 1
714 }),
715 )
716 );
717 }
718
719 #[test]
720 fn test_parse_package_bare_p() {
721 let result = ConditionParser::parse("[1P]").unwrap().unwrap();
722 assert_eq!(
723 result,
724 ConditionExpr::Package {
725 id: 1,
726 min: 0,
727 max: u32::MAX
728 }
729 );
730 }
731
732 #[test]
733 fn test_condition_ids_extraction_full() {
734 let result = ConditionParser::parse("Muss ([102] ∧ [2006]) ⊻ ([103] ∧ [2005])")
735 .unwrap()
736 .unwrap();
737 let ids = result.condition_ids();
738 assert!(ids.contains(&102));
739 assert!(ids.contains(&103));
740 assert!(ids.contains(&2005));
741 assert!(ids.contains(&2006));
742 assert_eq!(ids.len(), 4);
743 }
744
745 #[test]
748 fn test_parse_ub_inline_expansion() {
749 let ub1_expr = ConditionParser::parse("[931] ∧ [932]").unwrap().unwrap();
750 let mut ub_map = HashMap::new();
751 ub_map.insert("UB1".to_string(), ub1_expr.clone());
752
753 let result = ConditionParser::parse_with_ub("X [UB1]", &ub_map)
754 .unwrap()
755 .unwrap();
756 assert_eq!(result, ub1_expr);
757 }
758
759 #[test]
760 fn test_parse_ub_unknown_falls_back() {
761 let ub_map = HashMap::new();
762 let result = ConditionParser::parse_with_ub("[UB99]", &ub_map)
763 .unwrap()
764 .unwrap();
765 assert_eq!(result, ConditionExpr::Ref(0));
766 }
767
768 #[test]
769 fn test_parse_with_ub_empty_map_same_as_parse() {
770 let ub_map = HashMap::new();
771 let result = ConditionParser::parse_with_ub("X [931] ∧ [932]", &ub_map)
773 .unwrap()
774 .unwrap();
775 let expected = ConditionParser::parse("X [931] ∧ [932]").unwrap().unwrap();
776 assert_eq!(result, expected);
777 }
778
779 #[test]
780 fn test_parse_ub_in_complex_expression() {
781 let ub1_expr = ConditionParser::parse("[931] ∧ [932]").unwrap().unwrap();
783 let mut ub_map = HashMap::new();
784 ub_map.insert("UB1".to_string(), ub1_expr);
785
786 let result = ConditionParser::parse_with_ub("X [UB1] ∨ [100]", &ub_map)
787 .unwrap()
788 .unwrap();
789 assert_eq!(
791 result,
792 ConditionExpr::Or(vec![
793 ConditionExpr::And(vec![ConditionExpr::Ref(931), ConditionExpr::Ref(932)]),
794 ConditionExpr::Ref(100),
795 ])
796 );
797 }
798
799 #[test]
800 fn test_parse_ub_multiple_references() {
801 let ub1_expr = ConditionParser::parse("[931]").unwrap().unwrap();
803 let ub2_expr = ConditionParser::parse("[932]").unwrap().unwrap();
804 let mut ub_map = HashMap::new();
805 ub_map.insert("UB1".to_string(), ub1_expr);
806 ub_map.insert("UB2".to_string(), ub2_expr);
807
808 let result = ConditionParser::parse_with_ub("X [UB1] ∧ [UB2]", &ub_map)
809 .unwrap()
810 .unwrap();
811 assert_eq!(
812 result,
813 ConditionExpr::And(vec![ConditionExpr::Ref(931), ConditionExpr::Ref(932)])
814 );
815 }
816
817 #[test]
818 fn test_parse_multi_line_alternatives_are_or() {
819 let status = "Muss [315] ∧ [707]\nSoll [8] ∧ [301] ∧ [707]";
822 let result = ConditionParser::parse(status).unwrap().unwrap();
823 assert_eq!(
824 result,
825 ConditionExpr::Or(vec![
826 ConditionExpr::And(vec![ConditionExpr::Ref(315), ConditionExpr::Ref(707)]),
827 ConditionExpr::And(vec![
828 ConditionExpr::Ref(8),
829 ConditionExpr::Ref(301),
830 ConditionExpr::Ref(707),
831 ]),
832 ])
833 );
834 }
835
836 #[test]
837 fn test_parse_multi_line_with_crlf() {
838 let result = ConditionParser::parse("Muss [10]\r\nSoll [20]")
840 .unwrap()
841 .unwrap();
842 assert_eq!(
843 result,
844 ConditionExpr::Or(vec![ConditionExpr::Ref(10), ConditionExpr::Ref(20)])
845 );
846 }
847
848 #[test]
849 fn test_parse_single_line_unchanged() {
850 let result = ConditionParser::parse("Muss [315] ∧ [707]")
852 .unwrap()
853 .unwrap();
854 assert_eq!(
855 result,
856 ConditionExpr::And(vec![ConditionExpr::Ref(315), ConditionExpr::Ref(707)])
857 );
858 }
859}