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 stripped = strip_status_prefix(input);
60 if stripped.is_empty() {
61 return Ok(None);
62 }
63
64 let tokens = tokenize(stripped)?;
65 if tokens.is_empty() {
66 return Ok(None);
67 }
68
69 let mut pos = 0;
70 let expr = parse_expression(&tokens, &mut pos, ub_definitions)?;
71
72 Ok(expr)
73 }
74
75 pub fn parse_raw(input: &str) -> Result<Option<ConditionExpr>, ParseError> {
79 let input = input.trim();
80 if input.is_empty() {
81 return Ok(None);
82 }
83
84 let tokens = tokenize(input)?;
85 if tokens.is_empty() {
86 return Ok(None);
87 }
88
89 let mut pos = 0;
90 let expr = parse_expression(&tokens, &mut pos, &HashMap::new())?;
91
92 Ok(expr)
93 }
94}
95
96fn parse_expression(
98 tokens: &[SpannedToken],
99 pos: &mut usize,
100 ub_definitions: &HashMap<String, ConditionExpr>,
101) -> Result<Option<ConditionExpr>, ParseError> {
102 parse_xor(tokens, pos, ub_definitions)
103}
104
105fn parse_xor(
107 tokens: &[SpannedToken],
108 pos: &mut usize,
109 ub_definitions: &HashMap<String, ConditionExpr>,
110) -> Result<Option<ConditionExpr>, ParseError> {
111 let mut left = match parse_or(tokens, pos, ub_definitions)? {
112 Some(expr) => expr,
113 None => return Ok(None),
114 };
115
116 while *pos < tokens.len() && tokens[*pos].token == Token::Xor {
117 *pos += 1; let right = match parse_or(tokens, pos, ub_definitions)? {
119 Some(expr) => expr,
120 None => return Ok(Some(left)),
121 };
122 left = ConditionExpr::Xor(Box::new(left), Box::new(right));
123 }
124
125 Ok(Some(left))
126}
127
128fn parse_or(
130 tokens: &[SpannedToken],
131 pos: &mut usize,
132 ub_definitions: &HashMap<String, ConditionExpr>,
133) -> Result<Option<ConditionExpr>, ParseError> {
134 let mut left = match parse_and(tokens, pos, ub_definitions)? {
135 Some(expr) => expr,
136 None => return Ok(None),
137 };
138
139 while *pos < tokens.len() && tokens[*pos].token == Token::Or {
140 *pos += 1; let right = match parse_and(tokens, pos, ub_definitions)? {
142 Some(expr) => expr,
143 None => return Ok(Some(left)),
144 };
145 left = match left {
147 ConditionExpr::Or(mut exprs) => {
148 exprs.push(right);
149 ConditionExpr::Or(exprs)
150 }
151 _ => ConditionExpr::Or(vec![left, right]),
152 };
153 }
154
155 Ok(Some(left))
156}
157
158fn parse_and(
161 tokens: &[SpannedToken],
162 pos: &mut usize,
163 ub_definitions: &HashMap<String, ConditionExpr>,
164) -> Result<Option<ConditionExpr>, ParseError> {
165 let mut left = match parse_not(tokens, pos, ub_definitions)? {
166 Some(expr) => expr,
167 None => return Ok(None),
168 };
169
170 while *pos < tokens.len() {
171 if tokens[*pos].token == Token::And {
172 *pos += 1; let right = match parse_not(tokens, pos, ub_definitions)? {
174 Some(expr) => expr,
175 None => return Ok(Some(left)),
176 };
177 left = flatten_and(left, right);
178 } else if matches!(
179 tokens[*pos].token,
180 Token::ConditionId(_) | Token::LeftParen | Token::Not
181 ) {
182 let right = match parse_not(tokens, pos, ub_definitions)? {
184 Some(expr) => expr,
185 None => return Ok(Some(left)),
186 };
187 left = flatten_and(left, right);
188 } else {
189 break;
190 }
191 }
192
193 Ok(Some(left))
194}
195
196fn flatten_and(left: ConditionExpr, right: ConditionExpr) -> ConditionExpr {
198 match left {
199 ConditionExpr::And(mut exprs) => {
200 exprs.push(right);
201 ConditionExpr::And(exprs)
202 }
203 _ => ConditionExpr::And(vec![left, right]),
204 }
205}
206
207fn parse_not(
209 tokens: &[SpannedToken],
210 pos: &mut usize,
211 ub_definitions: &HashMap<String, ConditionExpr>,
212) -> Result<Option<ConditionExpr>, ParseError> {
213 if *pos < tokens.len() && tokens[*pos].token == Token::Not {
214 *pos += 1; let inner = match parse_not(tokens, pos, ub_definitions)? {
216 Some(expr) => expr,
217 None => {
218 return Err(ParseError::UnexpectedToken {
219 position: if *pos < tokens.len() {
220 tokens[*pos].position
221 } else {
222 0
223 },
224 expected: "expression after NOT".to_string(),
225 found: "end of input".to_string(),
226 });
227 }
228 };
229 return Ok(Some(ConditionExpr::Not(Box::new(inner))));
230 }
231 parse_primary(tokens, pos, ub_definitions)
232}
233
234fn parse_primary(
239 tokens: &[SpannedToken],
240 pos: &mut usize,
241 ub_definitions: &HashMap<String, ConditionExpr>,
242) -> Result<Option<ConditionExpr>, ParseError> {
243 if *pos >= tokens.len() {
244 return Ok(None);
245 }
246
247 match &tokens[*pos].token {
248 Token::ConditionId(id) => {
249 *pos += 1;
250 if let Some(ub_expr) = ub_definitions.get(id.as_str()) {
252 return Ok(Some(ub_expr.clone()));
253 }
254 Ok(Some(parse_condition_id(id)))
255 }
256 Token::LeftParen => {
257 *pos += 1; let expr = parse_expression(tokens, pos, ub_definitions)?;
259 if *pos < tokens.len() && tokens[*pos].token == Token::RightParen {
261 *pos += 1;
262 }
263 Ok(expr)
264 }
265 _ => Ok(None),
266 }
267}
268
269fn parse_condition_id(id: &str) -> ConditionExpr {
276 if let Ok(num) = id.parse::<u32>() {
278 return ConditionExpr::Ref(num);
279 }
280
281 if let Some(p_pos) = id.find('P') {
283 let num_part = &id[..p_pos];
284 let range_part = &id[p_pos + 1..];
285 if let Ok(pkg_id) = num_part.parse::<u32>() {
286 let (min, max) = parse_package_range(range_part);
287 return ConditionExpr::Package {
288 id: pkg_id,
289 min,
290 max,
291 };
292 }
293 }
294
295 let numeric_part: String = id.chars().take_while(|c| c.is_ascii_digit()).collect();
297 if let Ok(num) = numeric_part.parse::<u32>() {
298 ConditionExpr::Ref(num)
299 } else {
300 ConditionExpr::Ref(0)
301 }
302}
303
304fn parse_package_range(range: &str) -> (u32, u32) {
306 if range.is_empty() {
307 return (0, u32::MAX);
308 }
309 if let Some((min_str, max_str)) = range.split_once("..") {
310 let min = min_str.parse::<u32>().unwrap_or(0);
311 let max = max_str.parse::<u32>().unwrap_or(u32::MAX);
312 (min, max)
313 } else {
314 let n = range.parse::<u32>().unwrap_or(0);
315 (n, n)
316 }
317}
318
319#[cfg(test)]
320mod tests {
321 use super::*;
322 use pretty_assertions::assert_eq;
323
324 #[test]
327 fn test_parse_single_condition() {
328 let result = ConditionParser::parse("[931]").unwrap().unwrap();
329 assert_eq!(result, ConditionExpr::Ref(931));
330 }
331
332 #[test]
333 fn test_parse_with_muss_prefix() {
334 let result = ConditionParser::parse("Muss [494]").unwrap().unwrap();
335 assert_eq!(result, ConditionExpr::Ref(494));
336 }
337
338 #[test]
339 fn test_parse_with_soll_prefix() {
340 let result = ConditionParser::parse("Soll [494]").unwrap().unwrap();
341 assert_eq!(result, ConditionExpr::Ref(494));
342 }
343
344 #[test]
345 fn test_parse_with_kann_prefix() {
346 let result = ConditionParser::parse("Kann [182]").unwrap().unwrap();
347 assert_eq!(result, ConditionExpr::Ref(182));
348 }
349
350 #[test]
351 fn test_parse_with_x_prefix() {
352 let result = ConditionParser::parse("X [567]").unwrap().unwrap();
353 assert_eq!(result, ConditionExpr::Ref(567));
354 }
355
356 #[test]
359 fn test_parse_simple_and() {
360 let result = ConditionParser::parse("[182] ∧ [152]").unwrap().unwrap();
361 assert_eq!(
362 result,
363 ConditionExpr::And(vec![ConditionExpr::Ref(182), ConditionExpr::Ref(152)])
364 );
365 }
366
367 #[test]
368 fn test_parse_simple_or() {
369 let result = ConditionParser::parse("[1] ∨ [2]").unwrap().unwrap();
370 assert_eq!(
371 result,
372 ConditionExpr::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)])
373 );
374 }
375
376 #[test]
377 fn test_parse_simple_xor() {
378 let result = ConditionParser::parse("[1] ⊻ [2]").unwrap().unwrap();
379 assert_eq!(
380 result,
381 ConditionExpr::Xor(
382 Box::new(ConditionExpr::Ref(1)),
383 Box::new(ConditionExpr::Ref(2)),
384 )
385 );
386 }
387
388 #[test]
391 fn test_parse_three_way_and() {
392 let result = ConditionParser::parse("[1] ∧ [2] ∧ [3]").unwrap().unwrap();
393 assert_eq!(
394 result,
395 ConditionExpr::And(vec![
396 ConditionExpr::Ref(1),
397 ConditionExpr::Ref(2),
398 ConditionExpr::Ref(3),
399 ])
400 );
401 }
402
403 #[test]
404 fn test_parse_three_way_and_with_prefix() {
405 let result = ConditionParser::parse("Kann [182] ∧ [6] ∧ [570]")
406 .unwrap()
407 .unwrap();
408 assert_eq!(
409 result,
410 ConditionExpr::And(vec![
411 ConditionExpr::Ref(182),
412 ConditionExpr::Ref(6),
413 ConditionExpr::Ref(570),
414 ])
415 );
416 assert_eq!(result.condition_ids(), [6, 182, 570].into());
417 }
418
419 #[test]
420 fn test_parse_multiple_xor() {
421 let result = ConditionParser::parse("[1] ⊻ [2] ⊻ [3] ⊻ [4]")
422 .unwrap()
423 .unwrap();
424 assert_eq!(result.condition_ids(), [1, 2, 3, 4].into());
425 }
426
427 #[test]
430 fn test_parse_parenthesized_expression() {
431 let result = ConditionParser::parse("([1] ∨ [2]) ∧ [3]")
432 .unwrap()
433 .unwrap();
434 assert_eq!(
435 result,
436 ConditionExpr::And(vec![
437 ConditionExpr::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]),
438 ConditionExpr::Ref(3),
439 ])
440 );
441 }
442
443 #[test]
444 fn test_parse_nested_parentheses() {
445 let result = ConditionParser::parse("(([1] ∧ [2]) ∨ ([3] ∧ [4])) ∧ [5]")
447 .unwrap()
448 .unwrap();
449 assert_eq!(result.condition_ids(), [1, 2, 3, 4, 5].into());
450 match &result {
452 ConditionExpr::And(exprs) => {
453 assert_eq!(exprs.len(), 2);
454 assert!(matches!(&exprs[0], ConditionExpr::Or(_)));
455 assert_eq!(exprs[1], ConditionExpr::Ref(5));
456 }
457 other => panic!("Expected And, got {other:?}"),
458 }
459 }
460
461 #[test]
464 fn test_and_has_higher_precedence_than_or() {
465 let result = ConditionParser::parse("[1] ∨ [2] ∧ [3]").unwrap().unwrap();
467 assert_eq!(
468 result,
469 ConditionExpr::Or(vec![
470 ConditionExpr::Ref(1),
471 ConditionExpr::And(vec![ConditionExpr::Ref(2), ConditionExpr::Ref(3)]),
472 ])
473 );
474 }
475
476 #[test]
477 fn test_or_has_higher_precedence_than_xor() {
478 let result = ConditionParser::parse("[1] ⊻ [2] ∨ [3]").unwrap().unwrap();
480 assert_eq!(
481 result,
482 ConditionExpr::Xor(
483 Box::new(ConditionExpr::Ref(1)),
484 Box::new(ConditionExpr::Or(vec![
485 ConditionExpr::Ref(2),
486 ConditionExpr::Ref(3),
487 ])),
488 )
489 );
490 }
491
492 #[test]
495 fn test_adjacent_conditions_implicit_and() {
496 let result = ConditionParser::parse("[1] [2]").unwrap().unwrap();
498 assert_eq!(
499 result,
500 ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)])
501 );
502 }
503
504 #[test]
505 fn test_adjacent_conditions_no_space_implicit_and() {
506 let result = ConditionParser::parse("[939][14]").unwrap().unwrap();
508 assert_eq!(
509 result,
510 ConditionExpr::And(vec![ConditionExpr::Ref(939), ConditionExpr::Ref(14)])
511 );
512 }
513
514 #[test]
517 fn test_parse_not() {
518 let result = ConditionParser::parse("NOT [1]").unwrap().unwrap();
519 assert_eq!(result, ConditionExpr::Not(Box::new(ConditionExpr::Ref(1))));
520 }
521
522 #[test]
523 fn test_parse_not_with_and() {
524 let result = ConditionParser::parse("NOT [1] ∧ [2]").unwrap().unwrap();
526 assert_eq!(
527 result,
528 ConditionExpr::And(vec![
529 ConditionExpr::Not(Box::new(ConditionExpr::Ref(1))),
530 ConditionExpr::Ref(2),
531 ])
532 );
533 }
534
535 #[test]
538 fn test_real_world_orders_expression() {
539 let result = ConditionParser::parse("X (([939] [147]) ∨ ([940] [148])) ∧ [567]")
541 .unwrap()
542 .unwrap();
543 assert_eq!(result.condition_ids(), [147, 148, 567, 939, 940].into());
544 }
545
546 #[test]
547 fn test_real_world_xor_expression() {
548 let result = ConditionParser::parse("Muss ([102] ∧ [2006]) ⊻ ([103] ∧ [2005])")
550 .unwrap()
551 .unwrap();
552 assert!(matches!(result, ConditionExpr::Xor(_, _)));
553 assert_eq!(result.condition_ids(), [102, 103, 2005, 2006].into());
554 }
555
556 #[test]
557 fn test_real_world_complex_nested_with_implicit_and() {
558 let result = ConditionParser::parse("([939][14]) ∨ ([940][15])")
560 .unwrap()
561 .unwrap();
562 assert!(matches!(result, ConditionExpr::Or(_)));
563 assert_eq!(result.condition_ids(), [14, 15, 939, 940].into());
564 }
565
566 #[test]
569 fn test_parse_empty_string() {
570 assert!(ConditionParser::parse("").unwrap().is_none());
571 }
572
573 #[test]
574 fn test_parse_whitespace_only() {
575 assert!(ConditionParser::parse(" \t ").unwrap().is_none());
576 }
577
578 #[test]
579 fn test_parse_bare_muss() {
580 assert!(ConditionParser::parse("Muss").unwrap().is_none());
581 }
582
583 #[test]
584 fn test_parse_bare_x() {
585 assert!(ConditionParser::parse("X").unwrap().is_none());
587 }
588
589 #[test]
590 fn test_parse_unmatched_open_paren_graceful() {
591 let result = ConditionParser::parse("([1] ∧ [2]").unwrap().unwrap();
593 assert_eq!(
594 result,
595 ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)])
596 );
597 }
598
599 #[test]
600 fn test_parse_text_and_operator() {
601 let result = ConditionParser::parse("[1] AND [2]").unwrap().unwrap();
602 assert_eq!(
603 result,
604 ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)])
605 );
606 }
607
608 #[test]
609 fn test_parse_text_or_operator() {
610 let result = ConditionParser::parse("[1] OR [2]").unwrap().unwrap();
611 assert_eq!(
612 result,
613 ConditionExpr::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)])
614 );
615 }
616
617 #[test]
618 fn test_parse_text_xor_operator() {
619 let result = ConditionParser::parse("[1] XOR [2]").unwrap().unwrap();
620 assert_eq!(
621 result,
622 ConditionExpr::Xor(
623 Box::new(ConditionExpr::Ref(1)),
624 Box::new(ConditionExpr::Ref(2)),
625 )
626 );
627 }
628
629 #[test]
630 fn test_parse_mixed_unicode_and_text_operators() {
631 let result = ConditionParser::parse("[1] ∧ [2] OR [3]").unwrap().unwrap();
632 assert_eq!(
633 result,
634 ConditionExpr::Or(vec![
635 ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]),
636 ConditionExpr::Ref(3),
637 ])
638 );
639 }
640
641 #[test]
642 fn test_parse_deeply_nested() {
643 let result = ConditionParser::parse("((([1])))").unwrap().unwrap();
645 assert_eq!(result, ConditionExpr::Ref(1));
646 }
647
648 #[test]
651 fn test_parse_package_condition_0_1() {
652 let result = ConditionParser::parse("[4P0..1]").unwrap().unwrap();
653 assert_eq!(
654 result,
655 ConditionExpr::Package {
656 id: 4,
657 min: 0,
658 max: 1
659 }
660 );
661 }
662
663 #[test]
664 fn test_parse_package_condition_1_5() {
665 let result = ConditionParser::parse("[10P1..5]").unwrap().unwrap();
666 assert_eq!(
667 result,
668 ConditionExpr::Package {
669 id: 10,
670 min: 1,
671 max: 5
672 }
673 );
674 }
675
676 #[test]
677 fn test_parse_package_in_expression() {
678 let result = ConditionParser::parse("X [4P0..1] ⊻ [5P0..1]")
679 .unwrap()
680 .unwrap();
681 assert_eq!(
682 result,
683 ConditionExpr::Xor(
684 Box::new(ConditionExpr::Package {
685 id: 4,
686 min: 0,
687 max: 1
688 }),
689 Box::new(ConditionExpr::Package {
690 id: 5,
691 min: 0,
692 max: 1
693 }),
694 )
695 );
696 }
697
698 #[test]
699 fn test_parse_package_bare_p() {
700 let result = ConditionParser::parse("[1P]").unwrap().unwrap();
701 assert_eq!(
702 result,
703 ConditionExpr::Package {
704 id: 1,
705 min: 0,
706 max: u32::MAX
707 }
708 );
709 }
710
711 #[test]
712 fn test_condition_ids_extraction_full() {
713 let result = ConditionParser::parse("Muss ([102] ∧ [2006]) ⊻ ([103] ∧ [2005])")
714 .unwrap()
715 .unwrap();
716 let ids = result.condition_ids();
717 assert!(ids.contains(&102));
718 assert!(ids.contains(&103));
719 assert!(ids.contains(&2005));
720 assert!(ids.contains(&2006));
721 assert_eq!(ids.len(), 4);
722 }
723
724 #[test]
727 fn test_parse_ub_inline_expansion() {
728 let ub1_expr = ConditionParser::parse("[931] ∧ [932]").unwrap().unwrap();
729 let mut ub_map = HashMap::new();
730 ub_map.insert("UB1".to_string(), ub1_expr.clone());
731
732 let result = ConditionParser::parse_with_ub("X [UB1]", &ub_map)
733 .unwrap()
734 .unwrap();
735 assert_eq!(result, ub1_expr);
736 }
737
738 #[test]
739 fn test_parse_ub_unknown_falls_back() {
740 let ub_map = HashMap::new();
741 let result = ConditionParser::parse_with_ub("[UB99]", &ub_map)
742 .unwrap()
743 .unwrap();
744 assert_eq!(result, ConditionExpr::Ref(0));
745 }
746
747 #[test]
748 fn test_parse_with_ub_empty_map_same_as_parse() {
749 let ub_map = HashMap::new();
750 let result = ConditionParser::parse_with_ub("X [931] ∧ [932]", &ub_map)
752 .unwrap()
753 .unwrap();
754 let expected = ConditionParser::parse("X [931] ∧ [932]").unwrap().unwrap();
755 assert_eq!(result, expected);
756 }
757
758 #[test]
759 fn test_parse_ub_in_complex_expression() {
760 let ub1_expr = ConditionParser::parse("[931] ∧ [932]").unwrap().unwrap();
762 let mut ub_map = HashMap::new();
763 ub_map.insert("UB1".to_string(), ub1_expr);
764
765 let result = ConditionParser::parse_with_ub("X [UB1] ∨ [100]", &ub_map)
766 .unwrap()
767 .unwrap();
768 assert_eq!(
770 result,
771 ConditionExpr::Or(vec![
772 ConditionExpr::And(vec![ConditionExpr::Ref(931), ConditionExpr::Ref(932)]),
773 ConditionExpr::Ref(100),
774 ])
775 );
776 }
777
778 #[test]
779 fn test_parse_ub_multiple_references() {
780 let ub1_expr = ConditionParser::parse("[931]").unwrap().unwrap();
782 let ub2_expr = ConditionParser::parse("[932]").unwrap().unwrap();
783 let mut ub_map = HashMap::new();
784 ub_map.insert("UB1".to_string(), ub1_expr);
785 ub_map.insert("UB2".to_string(), ub2_expr);
786
787 let result = ConditionParser::parse_with_ub("X [UB1] ∧ [UB2]", &ub_map)
788 .unwrap()
789 .unwrap();
790 assert_eq!(
791 result,
792 ConditionExpr::And(vec![ConditionExpr::Ref(931), ConditionExpr::Ref(932)])
793 );
794 }
795}