1use super::{
45 lang, ClauseData, ClauseItem, ConversionError, DefaultKind, DependType, DirectiveIR,
46 DirectiveKind, Expression, Identifier, Language, MapType, ParserConfig, ProcBind,
47 ReductionOperator, ScheduleKind, ScheduleModifier, SourceLocation,
48};
49use crate::parser::{Clause, ClauseKind, Directive};
50
51pub fn parse_directive_kind(name: &str) -> Result<DirectiveKind, ConversionError> {
64 let normalized = name.trim().to_lowercase();
66 let normalized = normalized.as_str();
67
68 match normalized {
69 "parallel" => Ok(DirectiveKind::Parallel),
71 "parallel for" => Ok(DirectiveKind::ParallelFor),
72 "parallel for simd" => Ok(DirectiveKind::ParallelForSimd),
73 "parallel sections" => Ok(DirectiveKind::ParallelSections),
74 "parallel workshare" => Ok(DirectiveKind::ParallelWorkshare),
75 "parallel loop" => Ok(DirectiveKind::ParallelLoop),
76 "parallel masked" => Ok(DirectiveKind::ParallelMasked),
77 "parallel master" => Ok(DirectiveKind::ParallelMaster),
78
79 "for" => Ok(DirectiveKind::For),
81 "for simd" => Ok(DirectiveKind::ForSimd),
82 "sections" => Ok(DirectiveKind::Sections),
83 "section" => Ok(DirectiveKind::Section),
84 "single" => Ok(DirectiveKind::Single),
85 "workshare" => Ok(DirectiveKind::Workshare),
86 "loop" => Ok(DirectiveKind::Loop),
87
88 "simd" => Ok(DirectiveKind::Simd),
90 "declare simd" => Ok(DirectiveKind::DeclareSimd),
91
92 "task" => Ok(DirectiveKind::Task),
94 "taskloop" => Ok(DirectiveKind::Taskloop),
95 "taskloop simd" => Ok(DirectiveKind::TaskloopSimd),
96 "taskyield" => Ok(DirectiveKind::Taskyield),
97 "taskwait" => Ok(DirectiveKind::Taskwait),
98 "taskgroup" => Ok(DirectiveKind::Taskgroup),
99
100 "target" => Ok(DirectiveKind::Target),
102 "target data" => Ok(DirectiveKind::TargetData),
103 "target enter data" => Ok(DirectiveKind::TargetEnterData),
104 "target exit data" => Ok(DirectiveKind::TargetExitData),
105 "target update" => Ok(DirectiveKind::TargetUpdate),
106 "target parallel" => Ok(DirectiveKind::TargetParallel),
107 "target parallel for" => Ok(DirectiveKind::TargetParallelFor),
108 "target parallel for simd" => Ok(DirectiveKind::TargetParallelForSimd),
109 "target parallel loop" => Ok(DirectiveKind::TargetParallelLoop),
110 "target simd" => Ok(DirectiveKind::TargetSimd),
111 "target teams" => Ok(DirectiveKind::TargetTeams),
112 "target teams distribute" => Ok(DirectiveKind::TargetTeamsDistribute),
113 "target teams distribute simd" => Ok(DirectiveKind::TargetTeamsDistributeSimd),
114 "target teams distribute parallel for" => {
115 Ok(DirectiveKind::TargetTeamsDistributeParallelFor)
116 }
117 "target teams distribute parallel for simd" => {
118 Ok(DirectiveKind::TargetTeamsDistributeParallelForSimd)
119 }
120 "target teams loop" => Ok(DirectiveKind::TargetTeamsLoop),
121
122 "teams" => Ok(DirectiveKind::Teams),
124 "teams distribute" => Ok(DirectiveKind::TeamsDistribute),
125 "teams distribute simd" => Ok(DirectiveKind::TeamsDistributeSimd),
126 "teams distribute parallel for" => Ok(DirectiveKind::TeamsDistributeParallelFor),
127 "teams distribute parallel for simd" => Ok(DirectiveKind::TeamsDistributeParallelForSimd),
128 "teams loop" => Ok(DirectiveKind::TeamsLoop),
129
130 "barrier" => Ok(DirectiveKind::Barrier),
132 "critical" => Ok(DirectiveKind::Critical),
133 "atomic" => Ok(DirectiveKind::Atomic),
134 "flush" => Ok(DirectiveKind::Flush),
135 "ordered" => Ok(DirectiveKind::Ordered),
136 "master" => Ok(DirectiveKind::Master),
137 "masked" => Ok(DirectiveKind::Masked),
138
139 "declare reduction" => Ok(DirectiveKind::DeclareReduction),
141 "declare mapper" => Ok(DirectiveKind::DeclareMapper),
142 "declare target" => Ok(DirectiveKind::DeclareTarget),
143 "declare variant" => Ok(DirectiveKind::DeclareVariant),
144
145 "distribute" => Ok(DirectiveKind::Distribute),
147 "distribute simd" => Ok(DirectiveKind::DistributeSimd),
148 "distribute parallel for" => Ok(DirectiveKind::DistributeParallelFor),
149 "distribute parallel for simd" => Ok(DirectiveKind::DistributeParallelForSimd),
150
151 "metadirective" => Ok(DirectiveKind::Metadirective),
153
154 "threadprivate" => Ok(DirectiveKind::Threadprivate),
156 "allocate" => Ok(DirectiveKind::Allocate),
157 "requires" => Ok(DirectiveKind::Requires),
158 "scan" => Ok(DirectiveKind::Scan),
159 "depobj" => Ok(DirectiveKind::Depobj),
160 "nothing" => Ok(DirectiveKind::Nothing),
161 "error" => Ok(DirectiveKind::Error),
162
163 _ => Err(ConversionError::UnknownDirective(name.to_string())),
164 }
165}
166
167pub fn parse_identifier_list(
172 content: &str,
173 config: &ParserConfig,
174) -> Result<Vec<ClauseItem>, ConversionError> {
175 lang::parse_clause_item_list(content, config)
176}
177
178pub fn parse_reduction_operator(op_str: &str) -> Result<ReductionOperator, ConversionError> {
191 match op_str {
192 "+" => Ok(ReductionOperator::Add),
193 "-" => Ok(ReductionOperator::Subtract),
194 "*" => Ok(ReductionOperator::Multiply),
195 "&" => Ok(ReductionOperator::BitwiseAnd),
196 "|" => Ok(ReductionOperator::BitwiseOr),
197 "^" => Ok(ReductionOperator::BitwiseXor),
198 "&&" => Ok(ReductionOperator::LogicalAnd),
199 "||" => Ok(ReductionOperator::LogicalOr),
200 "min" => Ok(ReductionOperator::Min),
201 "max" => Ok(ReductionOperator::Max),
202 _ => Err(ConversionError::InvalidClauseSyntax(format!(
203 "Unknown reduction operator: {op_str}"
204 ))),
205 }
206}
207
208pub fn parse_schedule_clause(
221 content: &str,
222 config: &ParserConfig,
223) -> Result<ClauseData, ConversionError> {
224 let (modifiers, rest) = if let Some(colon_pos) = content.find(':') {
226 let (mod_str, kind_str) = content.split_at(colon_pos);
227 let kind_str = kind_str[1..].trim(); let mods: Vec<ScheduleModifier> = mod_str
231 .split(',')
232 .map(|s| s.trim())
233 .filter(|s| !s.is_empty())
234 .map(|s| match s {
235 "monotonic" => Ok(ScheduleModifier::Monotonic),
236 "nonmonotonic" => Ok(ScheduleModifier::Nonmonotonic),
237 "simd" => Ok(ScheduleModifier::Simd),
238 _ => Err(ConversionError::InvalidClauseSyntax(format!(
239 "Unknown schedule modifier: {s}"
240 ))),
241 })
242 .collect::<Result<Vec<_>, _>>()?;
243
244 (mods, kind_str)
245 } else {
246 (vec![], content)
247 };
248
249 let parts: Vec<&str> = rest.split(',').map(|s| s.trim()).collect();
251
252 let kind = match parts.first() {
253 Some(&"static") => ScheduleKind::Static,
254 Some(&"dynamic") => ScheduleKind::Dynamic,
255 Some(&"guided") => ScheduleKind::Guided,
256 Some(&"auto") => ScheduleKind::Auto,
257 Some(&"runtime") => ScheduleKind::Runtime,
258 Some(s) => {
259 return Err(ConversionError::InvalidClauseSyntax(format!(
260 "Unknown schedule kind: {s}"
261 )))
262 }
263 None => {
264 return Err(ConversionError::InvalidClauseSyntax(
265 "schedule clause requires a kind".to_string(),
266 ))
267 }
268 };
269
270 let chunk_size = parts.get(1).map(|s| Expression::new(*s, config));
271
272 Ok(ClauseData::Schedule {
273 kind,
274 modifiers,
275 chunk_size,
276 })
277}
278
279pub fn parse_map_clause(
297 content: &str,
298 config: &ParserConfig,
299) -> Result<ClauseData, ConversionError> {
300 let mut remainder = content.trim();
301 let mut mapper = None;
302
303 if remainder.len() >= 6 && remainder[..6].eq_ignore_ascii_case("mapper") {
305 let after_keyword = remainder[6..].trim_start();
306 if after_keyword.starts_with('(') {
307 let (mapper_body, rest) = extract_parenthesized(after_keyword)?;
309 mapper = Some(Identifier::new(mapper_body.trim()));
310 remainder = rest.trim_start();
311
312 if remainder.starts_with(',') {
314 remainder = remainder[1..].trim_start();
315 }
316 }
317 }
318
319 let (map_type, items_str) =
321 if let Some((type_str, items)) = lang::split_once_top_level(remainder, ':') {
322 let map_type = match type_str.trim().to_ascii_lowercase().as_str() {
323 "" => None,
324 "to" => Some(MapType::To),
325 "from" => Some(MapType::From),
326 "tofrom" => Some(MapType::ToFrom),
327 "alloc" => Some(MapType::Alloc),
328 "release" => Some(MapType::Release),
329 "delete" => Some(MapType::Delete),
330 other => {
331 return Err(ConversionError::InvalidClauseSyntax(format!(
332 "Unknown map type: {other}"
333 )))
334 }
335 };
336 (map_type, items.trim())
337 } else {
338 (None, remainder)
339 };
340
341 let items = parse_identifier_list(items_str, config)?;
342
343 Ok(ClauseData::Map {
344 map_type,
345 mapper,
346 items,
347 })
348}
349
350fn extract_parenthesized(input: &str) -> Result<(&str, &str), ConversionError> {
356 lang::extract_bracket_content(input, '(', ')')
358}
359
360pub fn parse_depend_type(type_str: &str) -> Result<DependType, ConversionError> {
370 match type_str {
371 "in" => Ok(DependType::In),
372 "out" => Ok(DependType::Out),
373 "inout" => Ok(DependType::Inout),
374 "mutexinoutset" => Ok(DependType::Mutexinoutset),
375 "depobj" => Ok(DependType::Depobj),
376 "source" => Ok(DependType::Source),
377 "sink" => Ok(DependType::Sink),
378 _ => Err(ConversionError::InvalidClauseSyntax(format!(
379 "Unknown depend type: {type_str}"
380 ))),
381 }
382}
383
384pub fn parse_linear_clause(
399 content: &str,
400 config: &ParserConfig,
401) -> Result<ClauseData, ConversionError> {
402 if let Some((items_str, step_str)) = lang::rsplit_once_top_level(content, ':') {
404 if items_str.contains('(') {
406 return Err(ConversionError::Unsupported(
407 "linear clause with modifiers not yet supported".to_string(),
408 ));
409 }
410
411 let items = parse_identifier_list(items_str, config)?;
412 let step = Some(Expression::new(step_str.trim(), config));
413
414 Ok(ClauseData::Linear {
415 modifier: None,
416 items,
417 step,
418 })
419 } else {
420 let items = parse_identifier_list(content, config)?;
422 Ok(ClauseData::Linear {
423 modifier: None,
424 items,
425 step: None,
426 })
427 }
428}
429
430pub fn parse_clause_data<'a>(
439 clause: &'a Clause<'a>,
440 config: &ParserConfig,
441) -> Result<ClauseData, ConversionError> {
442 let clause_name = clause.name.as_ref();
443
444 match clause_name {
445 "nowait" | "nogroup" | "untied" | "mergeable" | "seq_cst" | "relaxed" | "release"
447 | "acquire" | "acq_rel" => Ok(ClauseData::Bare(Identifier::new(clause_name))),
448
449 "default" => {
451 if let ClauseKind::Parenthesized(ref content) = clause.kind {
452 let content = content.as_ref();
453 let kind_str = content.trim();
454 let kind = match kind_str {
455 "shared" => DefaultKind::Shared,
456 "none" => DefaultKind::None,
457 "private" => DefaultKind::Private,
458 "firstprivate" => DefaultKind::Firstprivate,
459 _ => {
460 return Err(ConversionError::InvalidClauseSyntax(format!(
461 "Unknown default kind: {kind_str}"
462 )))
463 }
464 };
465 Ok(ClauseData::Default(kind))
466 } else {
467 Err(ConversionError::InvalidClauseSyntax(
468 "default clause requires parenthesized content".to_string(),
469 ))
470 }
471 }
472
473 "private" => {
475 if let ClauseKind::Parenthesized(ref content) = clause.kind {
476 let content = content.as_ref();
477 let items = parse_identifier_list(content, config)?;
478 Ok(ClauseData::Private { items })
479 } else {
480 Ok(ClauseData::Private { items: vec![] })
481 }
482 }
483
484 "firstprivate" => {
486 if let ClauseKind::Parenthesized(ref content) = clause.kind {
487 let content = content.as_ref();
488 let items = parse_identifier_list(content, config)?;
489 Ok(ClauseData::Firstprivate { items })
490 } else {
491 Ok(ClauseData::Firstprivate { items: vec![] })
492 }
493 }
494
495 "shared" => {
497 if let ClauseKind::Parenthesized(ref content) = clause.kind {
498 let content = content.as_ref();
499 let items = parse_identifier_list(content, config)?;
500 Ok(ClauseData::Shared { items })
501 } else {
502 Ok(ClauseData::Shared { items: vec![] })
503 }
504 }
505
506 "num_threads" => {
508 if let ClauseKind::Parenthesized(ref content) = clause.kind {
509 let content = content.as_ref();
510 Ok(ClauseData::NumThreads {
511 num: Expression::new(content.trim(), config),
512 })
513 } else {
514 Err(ConversionError::InvalidClauseSyntax(
515 "num_threads requires expression".to_string(),
516 ))
517 }
518 }
519
520 "if" => {
522 if let ClauseKind::Parenthesized(ref content) = clause.kind {
523 let content = content.as_ref();
524 if let Some((modifier, condition)) = lang::split_once_top_level(content, ':') {
526 Ok(ClauseData::If {
527 directive_name: Some(Identifier::new(modifier.trim())),
528 condition: Expression::new(condition.trim(), config),
529 })
530 } else {
531 Ok(ClauseData::If {
532 directive_name: None,
533 condition: Expression::new(content.trim(), config),
534 })
535 }
536 } else {
537 Err(ConversionError::InvalidClauseSyntax(
538 "if clause requires parenthesized content".to_string(),
539 ))
540 }
541 }
542
543 "collapse" => {
545 if let ClauseKind::Parenthesized(ref content) = clause.kind {
546 let content = content.as_ref();
547 Ok(ClauseData::Collapse {
548 n: Expression::new(content.trim(), config),
549 })
550 } else {
551 Err(ConversionError::InvalidClauseSyntax(
552 "collapse requires expression".to_string(),
553 ))
554 }
555 }
556
557 "ordered" => match clause.kind {
559 ClauseKind::Bare => Ok(ClauseData::Ordered { n: None }),
560 ClauseKind::Parenthesized(ref content) => Ok(ClauseData::Ordered {
561 n: Some(Expression::new(content.as_ref().trim(), config)),
562 }),
563 },
564
565 "reduction" => {
567 if let ClauseKind::Parenthesized(ref content) = clause.kind {
568 let content = content.as_ref();
569 if let Some((op_str, items_str)) = lang::split_once_top_level(content, ':') {
571 let operator = parse_reduction_operator(op_str.trim())?;
573
574 let items = parse_identifier_list(items_str.trim(), config)?;
576
577 Ok(ClauseData::Reduction { operator, items })
578 } else {
579 Err(ConversionError::InvalidClauseSyntax(
580 "reduction clause requires 'operator: list' format".to_string(),
581 ))
582 }
583 } else {
584 Err(ConversionError::InvalidClauseSyntax(
585 "reduction clause requires parenthesized content".to_string(),
586 ))
587 }
588 }
589
590 "schedule" => {
592 if let ClauseKind::Parenthesized(ref content) = clause.kind {
593 let content = content.as_ref();
594 parse_schedule_clause(content, config)
595 } else {
596 Err(ConversionError::InvalidClauseSyntax(
597 "schedule clause requires parenthesized content".to_string(),
598 ))
599 }
600 }
601
602 "map" => {
604 if let ClauseKind::Parenthesized(ref content) = clause.kind {
605 let content = content.as_ref();
606 parse_map_clause(content, config)
607 } else {
608 Err(ConversionError::InvalidClauseSyntax(
609 "map clause requires parenthesized content".to_string(),
610 ))
611 }
612 }
613
614 "depend" => {
616 if let ClauseKind::Parenthesized(ref content) = clause.kind {
617 let content = content.as_ref();
618 if let Some((type_str, items_str)) = lang::split_once_top_level(content, ':') {
620 let depend_type = parse_depend_type(type_str.trim())?;
622
623 let items = parse_identifier_list(items_str.trim(), config)?;
625
626 Ok(ClauseData::Depend { depend_type, items })
627 } else {
628 Err(ConversionError::InvalidClauseSyntax(
629 "depend clause requires 'type: list' format".to_string(),
630 ))
631 }
632 } else {
633 Err(ConversionError::InvalidClauseSyntax(
634 "depend clause requires parenthesized content".to_string(),
635 ))
636 }
637 }
638
639 "linear" => {
641 if let ClauseKind::Parenthesized(ref content) = clause.kind {
642 let content = content.as_ref();
643 parse_linear_clause(content, config)
644 } else {
645 Err(ConversionError::InvalidClauseSyntax(
646 "linear clause requires parenthesized content".to_string(),
647 ))
648 }
649 }
650
651 "proc_bind" => {
653 if let ClauseKind::Parenthesized(ref content) = clause.kind {
654 let content = content.as_ref();
655 let kind_str = content.trim();
656 let proc_bind = match kind_str {
657 "master" => ProcBind::Master,
658 "close" => ProcBind::Close,
659 "spread" => ProcBind::Spread,
660 "primary" => ProcBind::Primary,
661 _ => {
662 return Err(ConversionError::InvalidClauseSyntax(format!(
663 "Unknown proc_bind kind: {kind_str}"
664 )))
665 }
666 };
667 Ok(ClauseData::ProcBind(proc_bind))
668 } else {
669 Err(ConversionError::InvalidClauseSyntax(
670 "proc_bind clause requires parenthesized content".to_string(),
671 ))
672 }
673 }
674
675 _ => Ok(ClauseData::Generic {
677 name: Identifier::new(clause_name),
678 data: match clause.kind {
679 ClauseKind::Bare => None,
680 ClauseKind::Parenthesized(ref content) => Some(content.as_ref().to_string()),
681 },
682 }),
683 }
684}
685
686pub fn convert_directive<'a>(
708 directive: &'a Directive<'a>,
709 location: SourceLocation,
710 language: Language,
711 config: &ParserConfig,
712) -> Result<DirectiveIR, ConversionError> {
713 let directive_name = directive.name.to_string();
717
718 let kind = parse_directive_kind(&directive_name)?;
720
721 let mut clauses = Vec::new();
723 let clause_config = config.for_language(language);
724 for clause in &directive.clauses {
725 let clause_data = parse_clause_data(clause, &clause_config)?;
726 clauses.push(clause_data);
727 }
728
729 Ok(DirectiveIR::new(
730 kind,
731 &directive_name,
732 clauses,
733 location,
734 language,
735 ))
736}
737
738#[cfg(test)]
743mod tests {
744 use super::*;
745
746 #[test]
747 fn test_parse_directive_kind_parallel() {
748 assert_eq!(
749 parse_directive_kind("parallel").unwrap(),
750 DirectiveKind::Parallel
751 );
752 assert_eq!(
753 parse_directive_kind("parallel for").unwrap(),
754 DirectiveKind::ParallelFor
755 );
756 }
757
758 #[test]
759 fn test_parse_directive_kind_case_insensitive() {
760 assert_eq!(
761 parse_directive_kind("PARALLEL").unwrap(),
762 DirectiveKind::Parallel
763 );
764 assert_eq!(
765 parse_directive_kind("Parallel For").unwrap(),
766 DirectiveKind::ParallelFor
767 );
768 }
769
770 #[test]
771 fn test_parse_directive_kind_whitespace() {
772 assert_eq!(
773 parse_directive_kind(" parallel ").unwrap(),
774 DirectiveKind::Parallel
775 );
776 }
777
778 #[test]
779 fn test_parse_directive_kind_unknown() {
780 assert!(parse_directive_kind("unknown_directive").is_err());
781 }
782
783 #[test]
784 fn test_parse_identifier_list_single() {
785 let config = ParserConfig::with_parsing(Language::C);
786 let items = parse_identifier_list("x", &config).unwrap();
787 assert_eq!(items.len(), 1);
788 }
789
790 #[test]
791 fn test_parse_identifier_list_multiple() {
792 let config = ParserConfig::with_parsing(Language::C);
793 let items = parse_identifier_list("x, y, z", &config).unwrap();
794 assert_eq!(items.len(), 3);
795 }
796
797 #[test]
798 fn test_parse_identifier_list_with_spaces() {
799 let config = ParserConfig::with_parsing(Language::C);
800 let items = parse_identifier_list(" x , y , z ", &config).unwrap();
801 assert_eq!(items.len(), 3);
802 }
803
804 #[test]
805 fn test_parse_identifier_list_empty() {
806 let config = ParserConfig::with_parsing(Language::C);
807 let items = parse_identifier_list("", &config).unwrap();
808 assert_eq!(items.len(), 0);
809 }
810
811 #[test]
812 fn test_parse_clause_data_bare() {
813 let clause = Clause {
814 name: "nowait".into(),
815 kind: ClauseKind::Bare,
816 };
817 let config = ParserConfig::default();
818 let data = parse_clause_data(&clause, &config).unwrap();
819 assert!(matches!(data, ClauseData::Bare(_)));
820 assert_eq!(data.to_string(), "nowait");
821 }
822
823 #[test]
824 fn test_parse_clause_data_default_shared() {
825 let clause = Clause {
826 name: "default".into(),
827 kind: ClauseKind::Parenthesized("shared".into()),
828 };
829 let config = ParserConfig::default();
830 let data = parse_clause_data(&clause, &config).unwrap();
831 assert_eq!(data, ClauseData::Default(DefaultKind::Shared));
832 }
833
834 #[test]
835 fn test_parse_clause_data_private() {
836 let clause = Clause {
837 name: "private".into(),
838 kind: ClauseKind::Parenthesized("x, y".into()),
839 };
840 let config = ParserConfig::default();
841 let data = parse_clause_data(&clause, &config).unwrap();
842 if let ClauseData::Private { items } = data {
843 assert_eq!(items.len(), 2);
844 } else {
845 panic!("Expected Private clause");
846 }
847 }
848
849 #[test]
850 fn test_parse_clause_data_num_threads() {
851 let clause = Clause {
852 name: "num_threads".into(),
853 kind: ClauseKind::Parenthesized("4".into()),
854 };
855 let config = ParserConfig::default();
856 let data = parse_clause_data(&clause, &config).unwrap();
857 assert!(matches!(data, ClauseData::NumThreads { .. }));
858 }
859
860 #[test]
861 fn test_parse_clause_data_if_simple() {
862 let clause = Clause {
863 name: "if".into(),
864 kind: ClauseKind::Parenthesized("n > 100".into()),
865 };
866 let config = ParserConfig::default();
867 let data = parse_clause_data(&clause, &config).unwrap();
868 if let ClauseData::If {
869 directive_name,
870 condition,
871 } = data
872 {
873 assert!(directive_name.is_none());
874 assert_eq!(condition.to_string(), "n > 100");
875 } else {
876 panic!("Expected If clause");
877 }
878 }
879
880 #[test]
881 fn test_parse_clause_data_if_with_modifier() {
882 let clause = Clause {
883 name: "if".into(),
884 kind: ClauseKind::Parenthesized("parallel: n > 100".into()),
885 };
886 let config = ParserConfig::default();
887 let data = parse_clause_data(&clause, &config).unwrap();
888 if let ClauseData::If {
889 directive_name,
890 condition,
891 } = data
892 {
893 assert!(directive_name.is_some());
894 assert_eq!(directive_name.unwrap().to_string(), "parallel");
895 assert_eq!(condition.to_string(), "n > 100");
896 } else {
897 panic!("Expected If clause");
898 }
899 }
900
901 #[test]
902 fn test_convert_directive_simple() {
903 let directive = Directive {
904 name: "parallel".into(),
905 clauses: vec![],
906 };
907 let config = ParserConfig::default();
908 let ir =
909 convert_directive(&directive, SourceLocation::start(), Language::C, &config).unwrap();
910 assert_eq!(ir.kind(), DirectiveKind::Parallel);
911 assert_eq!(ir.clauses().len(), 0);
912 }
913
914 #[test]
915 fn test_convert_directive_with_clauses() {
916 let directive = Directive {
917 name: "parallel".into(),
918 clauses: vec![
919 Clause {
920 name: "default".into(),
921 kind: ClauseKind::Parenthesized("shared".into()),
922 },
923 Clause {
924 name: "private".into(),
925 kind: ClauseKind::Parenthesized("x".into()),
926 },
927 ],
928 };
929 let config = ParserConfig::default();
930 let ir =
931 convert_directive(&directive, SourceLocation::start(), Language::C, &config).unwrap();
932 assert_eq!(ir.kind(), DirectiveKind::Parallel);
933 assert_eq!(ir.clauses().len(), 2);
934 }
935
936 #[test]
938 fn test_parse_reduction_operator_arithmetic() {
939 assert_eq!(
940 parse_reduction_operator("+").unwrap(),
941 ReductionOperator::Add
942 );
943 assert_eq!(
944 parse_reduction_operator("-").unwrap(),
945 ReductionOperator::Subtract
946 );
947 assert_eq!(
948 parse_reduction_operator("*").unwrap(),
949 ReductionOperator::Multiply
950 );
951 }
952
953 #[test]
954 fn test_parse_reduction_operator_bitwise() {
955 assert_eq!(
956 parse_reduction_operator("&").unwrap(),
957 ReductionOperator::BitwiseAnd
958 );
959 assert_eq!(
960 parse_reduction_operator("|").unwrap(),
961 ReductionOperator::BitwiseOr
962 );
963 assert_eq!(
964 parse_reduction_operator("^").unwrap(),
965 ReductionOperator::BitwiseXor
966 );
967 }
968
969 #[test]
970 fn test_parse_reduction_operator_logical() {
971 assert_eq!(
972 parse_reduction_operator("&&").unwrap(),
973 ReductionOperator::LogicalAnd
974 );
975 assert_eq!(
976 parse_reduction_operator("||").unwrap(),
977 ReductionOperator::LogicalOr
978 );
979 }
980
981 #[test]
982 fn test_parse_reduction_operator_minmax() {
983 assert_eq!(
984 parse_reduction_operator("min").unwrap(),
985 ReductionOperator::Min
986 );
987 assert_eq!(
988 parse_reduction_operator("max").unwrap(),
989 ReductionOperator::Max
990 );
991 }
992
993 #[test]
994 fn test_parse_reduction_operator_unknown() {
995 assert!(parse_reduction_operator("unknown").is_err());
996 }
997
998 #[test]
1000 fn test_parse_clause_data_reduction() {
1001 let clause = Clause {
1002 name: "reduction".into(),
1003 kind: ClauseKind::Parenthesized("+: sum".into()),
1004 };
1005 let config = ParserConfig::default();
1006 let data = parse_clause_data(&clause, &config).unwrap();
1007 if let ClauseData::Reduction { operator, items } = data {
1008 assert_eq!(operator, ReductionOperator::Add);
1009 assert_eq!(items.len(), 1);
1010 } else {
1011 panic!("Expected Reduction clause");
1012 }
1013 }
1014
1015 #[test]
1016 fn test_parse_clause_data_reduction_multiple_items() {
1017 let clause = Clause {
1018 name: "reduction".into(),
1019 kind: ClauseKind::Parenthesized("*: a, b, c".into()),
1020 };
1021 let config = ParserConfig::default();
1022 let data = parse_clause_data(&clause, &config).unwrap();
1023 if let ClauseData::Reduction { operator, items } = data {
1024 assert_eq!(operator, ReductionOperator::Multiply);
1025 assert_eq!(items.len(), 3);
1026 } else {
1027 panic!("Expected Reduction clause");
1028 }
1029 }
1030
1031 #[test]
1032 fn test_parse_clause_data_reduction_minmax() {
1033 let clause = Clause {
1034 name: "reduction".into(),
1035 kind: ClauseKind::Parenthesized("min: value".into()),
1036 };
1037 let config = ParserConfig::default();
1038 let data = parse_clause_data(&clause, &config).unwrap();
1039 if let ClauseData::Reduction { operator, items } = data {
1040 assert_eq!(operator, ReductionOperator::Min);
1041 assert_eq!(items.len(), 1);
1042 } else {
1043 panic!("Expected Reduction clause");
1044 }
1045 }
1046
1047 #[test]
1049 fn test_parse_schedule_clause_static() {
1050 let config = ParserConfig::with_parsing(Language::C);
1051 let data = parse_schedule_clause("static", &config).unwrap();
1052 if let ClauseData::Schedule {
1053 kind,
1054 modifiers,
1055 chunk_size,
1056 } = data
1057 {
1058 assert_eq!(kind, ScheduleKind::Static);
1059 assert!(modifiers.is_empty());
1060 assert!(chunk_size.is_none());
1061 } else {
1062 panic!("Expected Schedule clause");
1063 }
1064 }
1065
1066 #[test]
1067 fn test_parse_schedule_clause_with_chunk() {
1068 let config = ParserConfig::with_parsing(Language::C);
1069 let data = parse_schedule_clause("dynamic, 10", &config).unwrap();
1070 if let ClauseData::Schedule {
1071 kind,
1072 modifiers,
1073 chunk_size,
1074 } = data
1075 {
1076 assert_eq!(kind, ScheduleKind::Dynamic);
1077 assert!(modifiers.is_empty());
1078 assert!(chunk_size.is_some());
1079 assert_eq!(chunk_size.unwrap().to_string(), "10");
1080 } else {
1081 panic!("Expected Schedule clause");
1082 }
1083 }
1084
1085 #[test]
1086 fn test_parse_schedule_clause_with_modifier() {
1087 let config = ParserConfig::with_parsing(Language::C);
1088 let data = parse_schedule_clause("monotonic: static, 4", &config).unwrap();
1089 if let ClauseData::Schedule {
1090 kind,
1091 modifiers,
1092 chunk_size,
1093 } = data
1094 {
1095 assert_eq!(kind, ScheduleKind::Static);
1096 assert_eq!(modifiers.len(), 1);
1097 assert_eq!(modifiers[0], ScheduleModifier::Monotonic);
1098 assert!(chunk_size.is_some());
1099 } else {
1100 panic!("Expected Schedule clause");
1101 }
1102 }
1103
1104 #[test]
1105 fn test_parse_schedule_clause_with_multiple_modifiers() {
1106 let config = ParserConfig::with_parsing(Language::C);
1107 let data = parse_schedule_clause("monotonic, simd: dynamic", &config).unwrap();
1108 if let ClauseData::Schedule {
1109 kind,
1110 modifiers,
1111 chunk_size,
1112 } = data
1113 {
1114 assert_eq!(kind, ScheduleKind::Dynamic);
1115 assert_eq!(modifiers.len(), 2);
1116 assert!(chunk_size.is_none());
1117 } else {
1118 panic!("Expected Schedule clause");
1119 }
1120 }
1121
1122 #[test]
1124 fn test_parse_map_clause_with_type() {
1125 let config = ParserConfig::with_parsing(Language::C);
1126 let data = parse_map_clause("to: arr", &config).unwrap();
1127 if let ClauseData::Map {
1128 map_type,
1129 mapper,
1130 items,
1131 } = data
1132 {
1133 assert_eq!(map_type, Some(MapType::To));
1134 assert!(mapper.is_none());
1135 assert_eq!(items.len(), 1);
1136 } else {
1137 panic!("Expected Map clause");
1138 }
1139 }
1140
1141 #[test]
1142 fn test_parse_map_clause_tofrom() {
1143 let config = ParserConfig::with_parsing(Language::C);
1144 let data = parse_map_clause("tofrom: x, y, z", &config).unwrap();
1145 if let ClauseData::Map {
1146 map_type,
1147 mapper,
1148 items,
1149 } = data
1150 {
1151 assert_eq!(map_type, Some(MapType::ToFrom));
1152 assert!(mapper.is_none());
1153 assert_eq!(items.len(), 3);
1154 } else {
1155 panic!("Expected Map clause");
1156 }
1157 }
1158
1159 #[test]
1160 fn test_parse_map_clause_without_type() {
1161 let config = ParserConfig::with_parsing(Language::C);
1162 let data = parse_map_clause("var1, var2", &config).unwrap();
1163 if let ClauseData::Map {
1164 map_type,
1165 mapper,
1166 items,
1167 } = data
1168 {
1169 assert!(map_type.is_none());
1170 assert!(mapper.is_none());
1171 assert_eq!(items.len(), 2);
1172 } else {
1173 panic!("Expected Map clause");
1174 }
1175 }
1176
1177 #[test]
1178 fn test_parse_map_clause_with_array_section() {
1179 let config = ParserConfig::with_parsing(Language::C);
1180 let data = parse_map_clause("to: arr[0:N:2]", &config).unwrap();
1181 if let ClauseData::Map { items, .. } = data {
1182 match &items[0] {
1183 ClauseItem::Variable(var) => {
1184 assert_eq!(var.name(), "arr");
1185 assert_eq!(var.array_sections.len(), 1);
1186 let section = &var.array_sections[0];
1187 assert!(section.lower_bound.is_some());
1188 assert!(section.length.is_some());
1189 assert!(section.stride.is_some());
1190 }
1191 other => panic!("Expected variable, got {other:?}"),
1192 }
1193 } else {
1194 panic!("Expected Map clause");
1195 }
1196 }
1197
1198 #[test]
1199 fn test_parse_map_clause_with_mapper() {
1200 let config = ParserConfig::with_parsing(Language::C);
1201 let data = parse_map_clause("mapper(custom), to: arr[0:N]", &config).unwrap();
1202 if let ClauseData::Map {
1203 map_type,
1204 mapper,
1205 items,
1206 } = data
1207 {
1208 assert_eq!(map_type, Some(MapType::To));
1209 assert_eq!(mapper.unwrap().to_string(), "custom");
1210 assert_eq!(items.len(), 1);
1211 assert!(matches!(items[0], ClauseItem::Variable(_)));
1212 } else {
1213 panic!("Expected Map clause with mapper");
1214 }
1215 }
1216
1217 #[test]
1219 fn test_parse_depend_type() {
1220 assert_eq!(parse_depend_type("in").unwrap(), DependType::In);
1221 assert_eq!(parse_depend_type("out").unwrap(), DependType::Out);
1222 assert_eq!(parse_depend_type("inout").unwrap(), DependType::Inout);
1223 }
1224
1225 #[test]
1226 fn test_parse_clause_data_depend() {
1227 let clause = Clause {
1228 name: "depend".into(),
1229 kind: ClauseKind::Parenthesized("in: x, y".into()),
1230 };
1231 let config = ParserConfig::default();
1232 let data = parse_clause_data(&clause, &config).unwrap();
1233 if let ClauseData::Depend { depend_type, items } = data {
1234 assert_eq!(depend_type, DependType::In);
1235 assert_eq!(items.len(), 2);
1236 } else {
1237 panic!("Expected Depend clause");
1238 }
1239 }
1240
1241 #[test]
1243 fn test_parse_linear_clause_simple() {
1244 let config = ParserConfig::with_parsing(Language::C);
1245 let data = parse_linear_clause("x, y", &config).unwrap();
1246 if let ClauseData::Linear {
1247 modifier,
1248 items,
1249 step,
1250 } = data
1251 {
1252 assert!(modifier.is_none());
1253 assert_eq!(items.len(), 2);
1254 assert!(step.is_none());
1255 } else {
1256 panic!("Expected Linear clause");
1257 }
1258 }
1259
1260 #[test]
1261 fn test_parse_linear_clause_with_step() {
1262 let config = ParserConfig::with_parsing(Language::C);
1263 let data = parse_linear_clause("i: 2", &config).unwrap();
1264 if let ClauseData::Linear {
1265 modifier,
1266 items,
1267 step,
1268 } = data
1269 {
1270 assert!(modifier.is_none());
1271 assert_eq!(items.len(), 1);
1272 assert!(step.is_some());
1273 assert_eq!(step.unwrap().to_string(), "2");
1274 } else {
1275 panic!("Expected Linear clause");
1276 }
1277 }
1278
1279 #[test]
1280 fn test_parse_clause_data_fortran_private_variables() {
1281 let clause = Clause {
1282 name: "private".into(),
1283 kind: ClauseKind::Parenthesized("A(1:N), B(:, :)".into()),
1284 };
1285 let config = ParserConfig::with_parsing(Language::Fortran);
1286 let data = parse_clause_data(&clause, &config).unwrap();
1287 if let ClauseData::Private { items } = data {
1288 assert_eq!(items.len(), 2);
1289 match &items[0] {
1290 ClauseItem::Variable(var) => {
1291 assert_eq!(var.name(), "A");
1292 assert_eq!(var.array_sections.len(), 1);
1293 }
1294 other => panic!("expected variable, got {other:?}"),
1295 }
1296 } else {
1297 panic!("Expected Private clause");
1298 }
1299 }
1300
1301 #[test]
1303 fn test_parse_clause_data_proc_bind() {
1304 let clause = Clause {
1305 name: "proc_bind".into(),
1306 kind: ClauseKind::Parenthesized("close".into()),
1307 };
1308 let config = ParserConfig::default();
1309 let data = parse_clause_data(&clause, &config).unwrap();
1310 assert_eq!(data, ClauseData::ProcBind(ProcBind::Close));
1311 }
1312}