1mod clause;
2mod directive;
3pub mod openmp;
4
5pub use clause::{Clause, ClauseKind, ClauseRegistry, ClauseRegistryBuilder, ClauseRule};
6pub use directive::{Directive, DirectiveRegistry, DirectiveRegistryBuilder, DirectiveRule};
7
8use super::lexer::{self, Language};
9use nom::{IResult, Parser as _};
10
11pub struct Parser {
12 clause_registry: ClauseRegistry,
13 directive_registry: DirectiveRegistry,
14 language: Language,
15}
16
17impl Parser {
18 pub fn new(directive_registry: DirectiveRegistry, clause_registry: ClauseRegistry) -> Self {
19 Self {
20 clause_registry,
21 directive_registry,
22 language: Language::default(),
23 }
24 }
25
26 pub fn with_language(mut self, language: Language) -> Self {
27 self.language = language;
28
29 if matches!(language, Language::FortranFree | Language::FortranFixed) {
32 self.directive_registry = self.directive_registry.with_case_insensitive(true);
33 self.clause_registry = self.clause_registry.with_case_insensitive(true);
34 }
35
36 self
37 }
38
39 pub fn parse<'a>(&self, input: &'a str) -> IResult<&'a str, Directive<'a>> {
40 let input = match self.language {
50 Language::C => {
51 let (input, _) = (
52 lexer::lex_pragma,
53 lexer::skip_space1_and_comments,
54 lexer::lex_omp,
55 lexer::skip_space1_and_comments,
56 )
57 .parse(input)?;
58 input
59 }
60 Language::FortranFree => {
61 let (input, _) = (
62 lexer::lex_fortran_free_sentinel,
63 lexer::skip_space1_and_comments,
64 )
65 .parse(input)?;
66 input
67 }
68 Language::FortranFixed => {
69 let (input, _) = (
70 lexer::lex_fortran_fixed_sentinel,
71 lexer::skip_space1_and_comments,
72 )
73 .parse(input)?;
74 input
75 }
76 };
77 self.directive_registry.parse(input, &self.clause_registry)
78 }
79}
80
81impl Default for Parser {
82 fn default() -> Self {
83 openmp::parser()
84 }
85}
86
87pub fn parse_omp_directive(input: &str) -> IResult<&str, Directive<'_>> {
88 let parser = Parser::default();
89 parser.parse(input)
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95 use crate::lexer::{self, Language};
96 use std::borrow::Cow;
97
98 #[test]
99 fn parses_full_pragma_with_default_registries() {
100 let input = "#pragma omp parallel private(a, b) nowait";
101
102 let (rest, directive) = parse_omp_directive(input).expect("parsing should succeed");
103
104 assert_eq!(rest, "");
105 assert_eq!(directive.name, "parallel");
106 assert_eq!(directive.clauses.len(), 2);
107 assert_eq!(directive.clauses[0].name, "private");
108 assert_eq!(
109 directive.clauses[0].kind,
110 ClauseKind::Parenthesized("a, b".into())
111 );
112 assert_eq!(directive.clauses[1].name, "nowait");
113 assert_eq!(directive.clauses[1].kind, ClauseKind::Bare);
114 }
115
116 #[test]
117 fn parser_uses_custom_registries() {
118 fn parse_only_bare<'a>(name: Cow<'a, str>, input: &'a str) -> IResult<&'a str, Clause<'a>> {
119 let (input, _) = nom::character::complete::char('(')(input)?;
120 let (input, value) = lexer::lex_clause(input)?;
121 let (input, _) = nom::character::complete::char(')')(input)?;
122
123 Ok((
124 input,
125 Clause {
126 name,
127 kind: ClauseKind::Parenthesized(value.into()),
128 },
129 ))
130 }
131
132 let clause_registry = ClauseRegistry::builder()
133 .register_custom("device", parse_only_bare)
134 .build();
135
136 fn parse_prefixed<'a>(
137 name: Cow<'a, str>,
138 input: &'a str,
139 clause_registry: &ClauseRegistry,
140 ) -> IResult<&'a str, Directive<'a>> {
141 let (input, _) = (
142 nom::character::complete::multispace1,
143 nom::bytes::complete::tag("use:"),
144 nom::character::complete::multispace1,
145 )
146 .parse(input)?;
147 let (input, clauses) = clause_registry.parse_sequence(input)?;
148
149 Ok((input, Directive { name, clauses }))
150 }
151
152 let directive_registry = DirectiveRegistry::builder()
153 .register_custom("target", parse_prefixed)
154 .build();
155
156 let parser = Parser::new(directive_registry, clause_registry);
157
158 let (rest, directive) = parser
159 .parse("#pragma omp target use: device(gpu)")
160 .expect("parsing should succeed");
161
162 assert_eq!(rest, "");
163 assert_eq!(directive.name, "target");
164 assert_eq!(directive.clauses.len(), 1);
165 assert_eq!(directive.clauses[0].name, "device");
166 assert_eq!(
167 directive.clauses[0].kind,
168 ClauseKind::Parenthesized("gpu".into())
169 );
170 }
171
172 #[test]
173 fn parses_c_multiline_directive_with_backslash() {
174 let input = "#pragma omp parallel for \
175 private(a, \
176 b) \
177 nowait";
178 let parser = Parser::default();
179 let (_, directive) = parser.parse(input).expect("directive should parse");
180
181 assert_eq!(directive.name, "parallel for");
182 assert_eq!(directive.clauses.len(), 2);
183 assert_eq!(directive.clauses[0].name, "private");
184 assert_eq!(
185 directive.clauses[0].kind,
186 ClauseKind::Parenthesized("a, b".into())
187 );
188 assert_eq!(directive.clauses[1].name, "nowait");
189 assert_eq!(directive.clauses[1].kind, ClauseKind::Bare);
190 }
191
192 #[test]
193 fn parses_fortran_free_multiline_directive() {
194 let parser = Parser::default().with_language(Language::FortranFree);
195 let input = "!$omp target teams distribute &\n!$omp parallel do &\n!$omp& private(i, j)";
196
197 let (_, directive) = parser.parse(input).expect("directive should parse");
198
199 assert_eq!(directive.name, "target teams distribute parallel do");
200 assert_eq!(directive.clauses.len(), 1);
201 assert_eq!(directive.clauses[0].name, "private");
202 assert_eq!(
203 directive.clauses[0].kind,
204 ClauseKind::Parenthesized("i, j".into())
205 );
206 }
207
208 #[test]
209 fn parses_fortran_parenthesized_clause_with_continuation() {
210 let parser = Parser::default().with_language(Language::FortranFree);
211 let input = "!$omp parallel do private(i, &\n!$omp& j, &\n!$omp& k)";
212
213 let (_, directive) = parser.parse(input).expect("directive should parse");
214
215 assert_eq!(directive.name, "parallel do");
216 assert_eq!(directive.clauses.len(), 1);
217 assert_eq!(directive.clauses[0].name, "private");
218 assert_eq!(
219 directive.clauses[0].kind,
220 ClauseKind::Parenthesized("i, j, k".into())
221 );
222 }
223
224 #[test]
225 fn parses_fortran_fixed_multiline_directive() {
226 let parser = Parser::default().with_language(Language::FortranFixed);
227 let input = " C$OMP DO &\n !$OMP& SCHEDULE(DYNAMIC) &\n !$OMP PRIVATE(I)";
228
229 let (_, directive) = parser.parse(input).expect("directive should parse");
230
231 assert_eq!(directive.name, "do");
232 assert_eq!(directive.clauses.len(), 2);
233 assert_eq!(directive.clauses[0].name, "schedule");
234 assert_eq!(
235 directive.clauses[0].kind,
236 ClauseKind::Parenthesized("DYNAMIC".into())
237 );
238 assert_eq!(directive.clauses[1].name, "private");
239 assert_eq!(
240 directive.clauses[1].kind,
241 ClauseKind::Parenthesized("I".into())
242 );
243 }
244}