1 | //===-- lib/Parser/openmp-parsers.cpp -------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | // Top-level grammar specification for OpenMP. |
10 | // See OpenMP-4.5-grammar.txt for documentation. |
11 | |
12 | #include "basic-parsers.h" |
13 | #include "expr-parsers.h" |
14 | #include "misc-parsers.h" |
15 | #include "stmt-parser.h" |
16 | #include "token-parsers.h" |
17 | #include "type-parser-implementation.h" |
18 | #include "flang/Parser/parse-tree.h" |
19 | |
20 | // OpenMP Directives and Clauses |
21 | namespace Fortran::parser { |
22 | |
23 | constexpr auto startOmpLine = skipStuffBeforeStatement >> "!$OMP "_sptok ; |
24 | constexpr auto endOmpLine = space >> endOfLine; |
25 | |
26 | // OpenMP Clauses |
27 | // 2.15.3.1 DEFAULT (PRIVATE | FIRSTPRIVATE | SHARED | NONE) |
28 | TYPE_PARSER(construct<OmpDefaultClause>( |
29 | "PRIVATE" >> pure(OmpDefaultClause::Type::Private) || |
30 | "FIRSTPRIVATE" >> pure(OmpDefaultClause::Type::Firstprivate) || |
31 | "SHARED" >> pure(OmpDefaultClause::Type::Shared) || |
32 | "NONE" >> pure(OmpDefaultClause::Type::None))) |
33 | |
34 | // 2.5 PROC_BIND (MASTER | CLOSE | SPREAD) |
35 | TYPE_PARSER(construct<OmpProcBindClause>( |
36 | "CLOSE" >> pure(OmpProcBindClause::Type::Close) || |
37 | "MASTER" >> pure(OmpProcBindClause::Type::Master) || |
38 | "SPREAD" >> pure(OmpProcBindClause::Type::Spread))) |
39 | |
40 | // 2.15.5.1 MAP ([ [ALWAYS[,]] map-type : ] variable-name-list) |
41 | // map-type -> TO | FROM | TOFROM | ALLOC | RELEASE | DELETE |
42 | TYPE_PARSER(construct<OmpMapType>( |
43 | maybe("ALWAYS" >> construct<OmpMapType::Always>() / maybe(","_tok )), |
44 | ("TO"_id >> pure(OmpMapType::Type::To) || |
45 | "FROM" >> pure(OmpMapType::Type::From) || |
46 | "TOFROM" >> pure(OmpMapType::Type::Tofrom) || |
47 | "ALLOC" >> pure(OmpMapType::Type::Alloc) || |
48 | "RELEASE" >> pure(OmpMapType::Type::Release) || |
49 | "DELETE" >> pure(OmpMapType::Type::Delete)) / |
50 | ":" )) |
51 | |
52 | TYPE_PARSER(construct<OmpMapClause>( |
53 | maybe(Parser<OmpMapType>{}), Parser<OmpObjectList>{})) |
54 | |
55 | // [OpenMP 5.0] |
56 | // 2.19.7.2 defaultmap(implicit-behavior[:variable-category]) |
57 | // implicit-behavior -> ALLOC | TO | FROM | TOFROM | FIRSRTPRIVATE | NONE | |
58 | // DEFAULT |
59 | // variable-category -> SCALAR | AGGREGATE | ALLOCATABLE | POINTER |
60 | TYPE_PARSER(construct<OmpDefaultmapClause>( |
61 | construct<OmpDefaultmapClause::ImplicitBehavior>( |
62 | "ALLOC" >> pure(OmpDefaultmapClause::ImplicitBehavior::Alloc) || |
63 | "TO"_id >> pure(OmpDefaultmapClause::ImplicitBehavior::To) || |
64 | "FROM" >> pure(OmpDefaultmapClause::ImplicitBehavior::From) || |
65 | "TOFROM" >> pure(OmpDefaultmapClause::ImplicitBehavior::Tofrom) || |
66 | "FIRSTPRIVATE" >> |
67 | pure(OmpDefaultmapClause::ImplicitBehavior::Firstprivate) || |
68 | "NONE" >> pure(OmpDefaultmapClause::ImplicitBehavior::None) || |
69 | "DEFAULT" >> pure(OmpDefaultmapClause::ImplicitBehavior::Default)), |
70 | maybe(":" >> |
71 | construct<OmpDefaultmapClause::VariableCategory>( |
72 | "SCALAR" >> pure(OmpDefaultmapClause::VariableCategory::Scalar) || |
73 | "AGGREGATE" >> |
74 | pure(OmpDefaultmapClause::VariableCategory::Aggregate) || |
75 | "ALLOCATABLE" >> |
76 | pure(OmpDefaultmapClause::VariableCategory::Allocatable) || |
77 | "POINTER" >> |
78 | pure(OmpDefaultmapClause::VariableCategory::Pointer))))) |
79 | |
80 | // 2.7.1 SCHEDULE ([modifier1 [, modifier2]:]kind[, chunk_size]) |
81 | // Modifier -> MONITONIC | NONMONOTONIC | SIMD |
82 | // kind -> STATIC | DYNAMIC | GUIDED | AUTO | RUNTIME |
83 | // chunk_size -> ScalarIntExpr |
84 | TYPE_PARSER(construct<OmpScheduleModifierType>( |
85 | "MONOTONIC" >> pure(OmpScheduleModifierType::ModType::Monotonic) || |
86 | "NONMONOTONIC" >> pure(OmpScheduleModifierType::ModType::Nonmonotonic) || |
87 | "SIMD" >> pure(OmpScheduleModifierType::ModType::Simd))) |
88 | |
89 | TYPE_PARSER(construct<OmpScheduleModifier>(Parser<OmpScheduleModifierType>{}, |
90 | maybe("," >> Parser<OmpScheduleModifierType>{}) / ":" )) |
91 | |
92 | TYPE_PARSER(construct<OmpScheduleClause>(maybe(Parser<OmpScheduleModifier>{}), |
93 | "STATIC" >> pure(OmpScheduleClause::ScheduleType::Static) || |
94 | "DYNAMIC" >> pure(OmpScheduleClause::ScheduleType::Dynamic) || |
95 | "GUIDED" >> pure(OmpScheduleClause::ScheduleType::Guided) || |
96 | "AUTO" >> pure(OmpScheduleClause::ScheduleType::Auto) || |
97 | "RUNTIME" >> pure(OmpScheduleClause::ScheduleType::Runtime), |
98 | maybe("," >> scalarIntExpr))) |
99 | |
100 | // device([ device-modifier :] scalar-integer-expression) |
101 | TYPE_PARSER(construct<OmpDeviceClause>( |
102 | maybe( |
103 | ("ANCESTOR" >> pure(OmpDeviceClause::DeviceModifier::Ancestor) || |
104 | "DEVICE_NUM" >> pure(OmpDeviceClause::DeviceModifier::Device_Num)) / |
105 | ":" ), |
106 | scalarIntExpr)) |
107 | |
108 | // device_type(any | host | nohost) |
109 | TYPE_PARSER(construct<OmpDeviceTypeClause>( |
110 | "ANY" >> pure(OmpDeviceTypeClause::Type::Any) || |
111 | "HOST" >> pure(OmpDeviceTypeClause::Type::Host) || |
112 | "NOHOST" >> pure(OmpDeviceTypeClause::Type::Nohost))) |
113 | |
114 | // 2.12 IF (directive-name-modifier: scalar-logical-expr) |
115 | TYPE_PARSER(construct<OmpIfClause>( |
116 | maybe( |
117 | ("PARALLEL" >> pure(OmpIfClause::DirectiveNameModifier::Parallel) || |
118 | "SIMD" >> pure(OmpIfClause::DirectiveNameModifier::Simd) || |
119 | "TARGET ENTER DATA" >> |
120 | pure(OmpIfClause::DirectiveNameModifier::TargetEnterData) || |
121 | "TARGET EXIT DATA" >> |
122 | pure(OmpIfClause::DirectiveNameModifier::TargetExitData) || |
123 | "TARGET DATA" >> |
124 | pure(OmpIfClause::DirectiveNameModifier::TargetData) || |
125 | "TARGET UPDATE" >> |
126 | pure(OmpIfClause::DirectiveNameModifier::TargetUpdate) || |
127 | "TARGET" >> pure(OmpIfClause::DirectiveNameModifier::Target) || |
128 | "TASK"_id >> pure(OmpIfClause::DirectiveNameModifier::Task) || |
129 | "TASKLOOP" >> pure(OmpIfClause::DirectiveNameModifier::Taskloop) || |
130 | "TEAMS" >> pure(OmpIfClause::DirectiveNameModifier::Teams)) / |
131 | ":" ), |
132 | scalarLogicalExpr)) |
133 | |
134 | // 2.15.3.6 REDUCTION (reduction-identifier: variable-name-list) |
135 | TYPE_PARSER(construct<OmpReductionOperator>(Parser<DefinedOperator>{}) || |
136 | construct<OmpReductionOperator>(Parser<ProcedureDesignator>{})) |
137 | |
138 | TYPE_PARSER(construct<OmpReductionClause>( |
139 | maybe( |
140 | ("INSCAN" >> pure(OmpReductionClause::ReductionModifier::Inscan) || |
141 | "TASK" >> pure(OmpReductionClause::ReductionModifier::Task) || |
142 | "DEFAULT" >> pure(OmpReductionClause::ReductionModifier::Default)) / |
143 | "," ), |
144 | Parser<OmpReductionOperator>{} / ":" , Parser<OmpObjectList>{})) |
145 | |
146 | // OMP 5.0 2.19.5.6 IN_REDUCTION (reduction-identifier: variable-name-list) |
147 | TYPE_PARSER(construct<OmpInReductionClause>( |
148 | Parser<OmpReductionOperator>{} / ":" , Parser<OmpObjectList>{})) |
149 | |
150 | // OMP 5.0 2.11.4 allocate-clause -> ALLOCATE ([allocator:] variable-name-list) |
151 | // OMP 5.2 2.13.4 allocate-clause -> ALLOCATE ([allocate-modifier |
152 | // [, allocate-modifier] :] |
153 | // variable-name-list) |
154 | // allocate-modifier -> allocator | align |
155 | TYPE_PARSER(construct<OmpAllocateClause>( |
156 | maybe( |
157 | first( |
158 | construct<OmpAllocateClause::AllocateModifier>("ALLOCATOR" >> |
159 | construct<OmpAllocateClause::AllocateModifier::ComplexModifier>( |
160 | parenthesized(construct< |
161 | OmpAllocateClause::AllocateModifier::Allocator>( |
162 | scalarIntExpr)) / |
163 | "," , |
164 | "ALIGN" >> parenthesized(construct< |
165 | OmpAllocateClause::AllocateModifier::Align>( |
166 | scalarIntExpr)))), |
167 | construct<OmpAllocateClause::AllocateModifier>("ALLOCATOR" >> |
168 | parenthesized( |
169 | construct<OmpAllocateClause::AllocateModifier::Allocator>( |
170 | scalarIntExpr))), |
171 | construct<OmpAllocateClause::AllocateModifier>("ALIGN" >> |
172 | parenthesized( |
173 | construct<OmpAllocateClause::AllocateModifier::Align>( |
174 | scalarIntExpr))), |
175 | construct<OmpAllocateClause::AllocateModifier>( |
176 | construct<OmpAllocateClause::AllocateModifier::Allocator>( |
177 | scalarIntExpr))) / |
178 | ":" ), |
179 | Parser<OmpObjectList>{})) |
180 | |
181 | // 2.13.9 DEPEND (SOURCE | SINK : vec | (IN | OUT | INOUT) : list |
182 | TYPE_PARSER(construct<OmpDependSinkVecLength>( |
183 | Parser<DefinedOperator>{}, scalarIntConstantExpr)) |
184 | |
185 | TYPE_PARSER( |
186 | construct<OmpDependSinkVec>(name, maybe(Parser<OmpDependSinkVecLength>{}))) |
187 | |
188 | TYPE_PARSER( |
189 | construct<OmpDependenceType>("IN"_id >> pure(OmpDependenceType::Type::In) || |
190 | "INOUT" >> pure(OmpDependenceType::Type::Inout) || |
191 | "OUT" >> pure(OmpDependenceType::Type::Out))) |
192 | |
193 | TYPE_CONTEXT_PARSER("Omp Depend clause"_en_US , |
194 | construct<OmpDependClause>(construct<OmpDependClause::Sink>( |
195 | "SINK :" >> nonemptyList(Parser<OmpDependSinkVec>{}))) || |
196 | construct<OmpDependClause>( |
197 | construct<OmpDependClause::Source>("SOURCE"_tok )) || |
198 | construct<OmpDependClause>(construct<OmpDependClause::InOut>( |
199 | Parser<OmpDependenceType>{}, ":" >> nonemptyList(designator)))) |
200 | |
201 | // 2.15.3.7 LINEAR (linear-list: linear-step) |
202 | // linear-list -> list | modifier(list) |
203 | // linear-modifier -> REF | VAL | UVAL |
204 | TYPE_PARSER( |
205 | construct<OmpLinearModifier>("REF" >> pure(OmpLinearModifier::Type::Ref) || |
206 | "VAL" >> pure(OmpLinearModifier::Type::Val) || |
207 | "UVAL" >> pure(OmpLinearModifier::Type::Uval))) |
208 | |
209 | TYPE_CONTEXT_PARSER("Omp LINEAR clause"_en_US , |
210 | construct<OmpLinearClause>( |
211 | construct<OmpLinearClause>(construct<OmpLinearClause::WithModifier>( |
212 | Parser<OmpLinearModifier>{}, parenthesized(nonemptyList(name)), |
213 | maybe(":" >> scalarIntConstantExpr))) || |
214 | construct<OmpLinearClause>(construct<OmpLinearClause::WithoutModifier>( |
215 | nonemptyList(name), maybe(":" >> scalarIntConstantExpr))))) |
216 | |
217 | // 2.8.1 ALIGNED (list: alignment) |
218 | TYPE_PARSER(construct<OmpAlignedClause>( |
219 | Parser<OmpObjectList>{}, maybe(":" >> scalarIntConstantExpr))) |
220 | |
221 | // 2.9.5 ORDER ([order-modifier :]concurrent) |
222 | TYPE_PARSER(construct<OmpOrderModifier>( |
223 | "REPRODUCIBLE" >> pure(OmpOrderModifier::Kind::Reproducible)) || |
224 | construct<OmpOrderModifier>( |
225 | "UNCONSTRAINED" >> pure(OmpOrderModifier::Kind::Unconstrained))) |
226 | |
227 | TYPE_PARSER(construct<OmpOrderClause>( |
228 | maybe(Parser<OmpOrderModifier>{} / ":" ), |
229 | "CONCURRENT" >> pure(OmpOrderClause::Type::Concurrent))) |
230 | |
231 | TYPE_PARSER( |
232 | construct<OmpObject>(designator) || construct<OmpObject>("/" >> name / "/" )) |
233 | |
234 | TYPE_PARSER( |
235 | "ACQUIRE" >> construct<OmpClause>(construct<OmpClause::Acquire>()) || |
236 | "ACQ_REL" >> construct<OmpClause>(construct<OmpClause::AcqRel>()) || |
237 | "ALIGNED" >> construct<OmpClause>(construct<OmpClause::Aligned>( |
238 | parenthesized(Parser<OmpAlignedClause>{}))) || |
239 | "ALLOCATE" >> construct<OmpClause>(construct<OmpClause::Allocate>( |
240 | parenthesized(Parser<OmpAllocateClause>{}))) || |
241 | "ALLOCATOR" >> construct<OmpClause>(construct<OmpClause::Allocator>( |
242 | parenthesized(scalarIntExpr))) || |
243 | "ATOMIC_DEFAULT_MEM_ORDER" >> |
244 | construct<OmpClause>(construct<OmpClause::AtomicDefaultMemOrder>( |
245 | parenthesized(Parser<OmpAtomicDefaultMemOrderClause>{}))) || |
246 | "COLLAPSE" >> construct<OmpClause>(construct<OmpClause::Collapse>( |
247 | parenthesized(scalarIntConstantExpr))) || |
248 | "COPYIN" >> construct<OmpClause>(construct<OmpClause::Copyin>( |
249 | parenthesized(Parser<OmpObjectList>{}))) || |
250 | "COPYPRIVATE" >> construct<OmpClause>(construct<OmpClause::Copyprivate>( |
251 | (parenthesized(Parser<OmpObjectList>{})))) || |
252 | "DEFAULT"_id >> construct<OmpClause>(construct<OmpClause::Default>( |
253 | parenthesized(Parser<OmpDefaultClause>{}))) || |
254 | "DEFAULTMAP" >> construct<OmpClause>(construct<OmpClause::Defaultmap>( |
255 | parenthesized(Parser<OmpDefaultmapClause>{}))) || |
256 | "DEPEND" >> construct<OmpClause>(construct<OmpClause::Depend>( |
257 | parenthesized(Parser<OmpDependClause>{}))) || |
258 | "DEVICE" >> construct<OmpClause>(construct<OmpClause::Device>( |
259 | parenthesized(Parser<OmpDeviceClause>{}))) || |
260 | "DEVICE_TYPE" >> construct<OmpClause>(construct<OmpClause::DeviceType>( |
261 | parenthesized(Parser<OmpDeviceTypeClause>{}))) || |
262 | "DIST_SCHEDULE" >> |
263 | construct<OmpClause>(construct<OmpClause::DistSchedule>( |
264 | parenthesized("STATIC" >> maybe("," >> scalarIntExpr)))) || |
265 | "DYNAMIC_ALLOCATORS" >> |
266 | construct<OmpClause>(construct<OmpClause::DynamicAllocators>()) || |
267 | "ENTER" >> construct<OmpClause>(construct<OmpClause::Enter>( |
268 | parenthesized(Parser<OmpObjectList>{}))) || |
269 | "FINAL" >> construct<OmpClause>(construct<OmpClause::Final>( |
270 | parenthesized(scalarLogicalExpr))) || |
271 | "FULL" >> construct<OmpClause>(construct<OmpClause::Full>()) || |
272 | "FIRSTPRIVATE" >> construct<OmpClause>(construct<OmpClause::Firstprivate>( |
273 | parenthesized(Parser<OmpObjectList>{}))) || |
274 | "FROM" >> construct<OmpClause>(construct<OmpClause::From>( |
275 | parenthesized(Parser<OmpObjectList>{}))) || |
276 | "GRAINSIZE" >> construct<OmpClause>(construct<OmpClause::Grainsize>( |
277 | parenthesized(scalarIntExpr))) || |
278 | "HAS_DEVICE_ADDR" >> |
279 | construct<OmpClause>(construct<OmpClause::HasDeviceAddr>( |
280 | parenthesized(Parser<OmpObjectList>{}))) || |
281 | "HINT" >> construct<OmpClause>( |
282 | construct<OmpClause::Hint>(parenthesized(constantExpr))) || |
283 | "IF" >> construct<OmpClause>(construct<OmpClause::If>( |
284 | parenthesized(Parser<OmpIfClause>{}))) || |
285 | "INBRANCH" >> construct<OmpClause>(construct<OmpClause::Inbranch>()) || |
286 | "IS_DEVICE_PTR" >> construct<OmpClause>(construct<OmpClause::IsDevicePtr>( |
287 | parenthesized(Parser<OmpObjectList>{}))) || |
288 | "LASTPRIVATE" >> construct<OmpClause>(construct<OmpClause::Lastprivate>( |
289 | parenthesized(Parser<OmpObjectList>{}))) || |
290 | "LINEAR" >> construct<OmpClause>(construct<OmpClause::Linear>( |
291 | parenthesized(Parser<OmpLinearClause>{}))) || |
292 | "LINK" >> construct<OmpClause>(construct<OmpClause::Link>( |
293 | parenthesized(Parser<OmpObjectList>{}))) || |
294 | "MAP" >> construct<OmpClause>(construct<OmpClause::Map>( |
295 | parenthesized(Parser<OmpMapClause>{}))) || |
296 | "MERGEABLE" >> construct<OmpClause>(construct<OmpClause::Mergeable>()) || |
297 | "NOGROUP" >> construct<OmpClause>(construct<OmpClause::Nogroup>()) || |
298 | "NONTEMPORAL" >> construct<OmpClause>(construct<OmpClause::Nontemporal>( |
299 | parenthesized(nonemptyList(name)))) || |
300 | "NOTINBRANCH" >> |
301 | construct<OmpClause>(construct<OmpClause::Notinbranch>()) || |
302 | "NOWAIT" >> construct<OmpClause>(construct<OmpClause::Nowait>()) || |
303 | "NUM_TASKS" >> construct<OmpClause>(construct<OmpClause::NumTasks>( |
304 | parenthesized(scalarIntExpr))) || |
305 | "NUM_TEAMS" >> construct<OmpClause>(construct<OmpClause::NumTeams>( |
306 | parenthesized(scalarIntExpr))) || |
307 | "NUM_THREADS" >> construct<OmpClause>(construct<OmpClause::NumThreads>( |
308 | parenthesized(scalarIntExpr))) || |
309 | "ORDER" >> construct<OmpClause>(construct<OmpClause::Order>( |
310 | parenthesized(Parser<OmpOrderClause>{}))) || |
311 | "ORDERED" >> construct<OmpClause>(construct<OmpClause::Ordered>( |
312 | maybe(parenthesized(scalarIntConstantExpr)))) || |
313 | "PARTIAL" >> construct<OmpClause>(construct<OmpClause::Partial>( |
314 | maybe(parenthesized(scalarIntConstantExpr)))) || |
315 | "PRIORITY" >> construct<OmpClause>(construct<OmpClause::Priority>( |
316 | parenthesized(scalarIntExpr))) || |
317 | "PRIVATE" >> construct<OmpClause>(construct<OmpClause::Private>( |
318 | parenthesized(Parser<OmpObjectList>{}))) || |
319 | "PROC_BIND" >> construct<OmpClause>(construct<OmpClause::ProcBind>( |
320 | parenthesized(Parser<OmpProcBindClause>{}))) || |
321 | "REDUCTION" >> construct<OmpClause>(construct<OmpClause::Reduction>( |
322 | parenthesized(Parser<OmpReductionClause>{}))) || |
323 | "IN_REDUCTION" >> construct<OmpClause>(construct<OmpClause::InReduction>( |
324 | parenthesized(Parser<OmpInReductionClause>{}))) || |
325 | "TASK_REDUCTION" >> |
326 | construct<OmpClause>(construct<OmpClause::TaskReduction>( |
327 | parenthesized(Parser<OmpReductionClause>{}))) || |
328 | "RELAXED" >> construct<OmpClause>(construct<OmpClause::Relaxed>()) || |
329 | "RELEASE" >> construct<OmpClause>(construct<OmpClause::Release>()) || |
330 | "REVERSE_OFFLOAD" >> |
331 | construct<OmpClause>(construct<OmpClause::ReverseOffload>()) || |
332 | "SAFELEN" >> construct<OmpClause>(construct<OmpClause::Safelen>( |
333 | parenthesized(scalarIntConstantExpr))) || |
334 | "SCHEDULE" >> construct<OmpClause>(construct<OmpClause::Schedule>( |
335 | parenthesized(Parser<OmpScheduleClause>{}))) || |
336 | "SEQ_CST" >> construct<OmpClause>(construct<OmpClause::SeqCst>()) || |
337 | "SHARED" >> construct<OmpClause>(construct<OmpClause::Shared>( |
338 | parenthesized(Parser<OmpObjectList>{}))) || |
339 | "SIMD"_id >> construct<OmpClause>(construct<OmpClause::Simd>()) || |
340 | "SIMDLEN" >> construct<OmpClause>(construct<OmpClause::Simdlen>( |
341 | parenthesized(scalarIntConstantExpr))) || |
342 | "SIZES" >> construct<OmpClause>(construct<OmpClause::Sizes>( |
343 | parenthesized(nonemptyList(scalarIntExpr)))) || |
344 | "THREADS" >> construct<OmpClause>(construct<OmpClause::Threads>()) || |
345 | "THREAD_LIMIT" >> construct<OmpClause>(construct<OmpClause::ThreadLimit>( |
346 | parenthesized(scalarIntExpr))) || |
347 | "TO" >> construct<OmpClause>(construct<OmpClause::To>( |
348 | parenthesized(Parser<OmpObjectList>{}))) || |
349 | "USE_DEVICE_PTR" >> construct<OmpClause>(construct<OmpClause::UseDevicePtr>( |
350 | parenthesized(Parser<OmpObjectList>{}))) || |
351 | "USE_DEVICE_ADDR" >> |
352 | construct<OmpClause>(construct<OmpClause::UseDeviceAddr>( |
353 | parenthesized(Parser<OmpObjectList>{}))) || |
354 | "UNIFIED_ADDRESS" >> |
355 | construct<OmpClause>(construct<OmpClause::UnifiedAddress>()) || |
356 | "UNIFIED_SHARED_MEMORY" >> |
357 | construct<OmpClause>(construct<OmpClause::UnifiedSharedMemory>()) || |
358 | "UNIFORM" >> construct<OmpClause>(construct<OmpClause::Uniform>( |
359 | parenthesized(nonemptyList(name)))) || |
360 | "UNTIED" >> construct<OmpClause>(construct<OmpClause::Untied>())) |
361 | |
362 | // [Clause, [Clause], ...] |
363 | TYPE_PARSER(sourced(construct<OmpClauseList>( |
364 | many(maybe(","_tok ) >> sourced(Parser<OmpClause>{}))))) |
365 | |
366 | // 2.1 (variable | /common-block | array-sections) |
367 | TYPE_PARSER(construct<OmpObjectList>(nonemptyList(Parser<OmpObject>{}))) |
368 | |
369 | // Omp directives enclosing do loop |
370 | TYPE_PARSER(sourced(construct<OmpLoopDirective>(first( |
371 | "DISTRIBUTE PARALLEL DO SIMD" >> |
372 | pure(llvm::omp::Directive::OMPD_distribute_parallel_do_simd), |
373 | "DISTRIBUTE PARALLEL DO" >> |
374 | pure(llvm::omp::Directive::OMPD_distribute_parallel_do), |
375 | "DISTRIBUTE SIMD" >> pure(llvm::omp::Directive::OMPD_distribute_simd), |
376 | "DISTRIBUTE" >> pure(llvm::omp::Directive::OMPD_distribute), |
377 | "DO SIMD" >> pure(llvm::omp::Directive::OMPD_do_simd), |
378 | "DO" >> pure(llvm::omp::Directive::OMPD_do), |
379 | "PARALLEL DO SIMD" >> pure(llvm::omp::Directive::OMPD_parallel_do_simd), |
380 | "PARALLEL DO" >> pure(llvm::omp::Directive::OMPD_parallel_do), |
381 | "SIMD" >> pure(llvm::omp::Directive::OMPD_simd), |
382 | "TARGET PARALLEL DO SIMD" >> |
383 | pure(llvm::omp::Directive::OMPD_target_parallel_do_simd), |
384 | "TARGET PARALLEL DO" >> pure(llvm::omp::Directive::OMPD_target_parallel_do), |
385 | "TARGET SIMD" >> pure(llvm::omp::Directive::OMPD_target_simd), |
386 | "TARGET TEAMS DISTRIBUTE PARALLEL DO SIMD" >> |
387 | pure(llvm::omp::Directive:: |
388 | OMPD_target_teams_distribute_parallel_do_simd), |
389 | "TARGET TEAMS DISTRIBUTE PARALLEL DO" >> |
390 | pure(llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do), |
391 | "TARGET TEAMS DISTRIBUTE SIMD" >> |
392 | pure(llvm::omp::Directive::OMPD_target_teams_distribute_simd), |
393 | "TARGET TEAMS DISTRIBUTE" >> |
394 | pure(llvm::omp::Directive::OMPD_target_teams_distribute), |
395 | "TASKLOOP SIMD" >> pure(llvm::omp::Directive::OMPD_taskloop_simd), |
396 | "TASKLOOP" >> pure(llvm::omp::Directive::OMPD_taskloop), |
397 | "TEAMS DISTRIBUTE PARALLEL DO SIMD" >> |
398 | pure(llvm::omp::Directive::OMPD_teams_distribute_parallel_do_simd), |
399 | "TEAMS DISTRIBUTE PARALLEL DO" >> |
400 | pure(llvm::omp::Directive::OMPD_teams_distribute_parallel_do), |
401 | "TEAMS DISTRIBUTE SIMD" >> |
402 | pure(llvm::omp::Directive::OMPD_teams_distribute_simd), |
403 | "TEAMS DISTRIBUTE" >> pure(llvm::omp::Directive::OMPD_teams_distribute), |
404 | "TILE" >> pure(llvm::omp::Directive::OMPD_tile), |
405 | "UNROLL" >> pure(llvm::omp::Directive::OMPD_unroll))))) |
406 | |
407 | TYPE_PARSER(sourced(construct<OmpBeginLoopDirective>( |
408 | sourced(Parser<OmpLoopDirective>{}), Parser<OmpClauseList>{}))) |
409 | |
410 | // 2.14.1 construct-type-clause -> PARALLEL | SECTIONS | DO | TASKGROUP |
411 | TYPE_PARSER(sourced(construct<OmpCancelType>( |
412 | first("PARALLEL" >> pure(OmpCancelType::Type::Parallel), |
413 | "SECTIONS" >> pure(OmpCancelType::Type::Sections), |
414 | "DO" >> pure(OmpCancelType::Type::Do), |
415 | "TASKGROUP" >> pure(OmpCancelType::Type::Taskgroup))))) |
416 | |
417 | // 2.14.2 Cancellation Point construct |
418 | TYPE_PARSER(sourced(construct<OpenMPCancellationPointConstruct>( |
419 | verbatim("CANCELLATION POINT"_tok ), Parser<OmpCancelType>{}))) |
420 | |
421 | // 2.14.1 Cancel construct |
422 | TYPE_PARSER(sourced(construct<OpenMPCancelConstruct>(verbatim("CANCEL"_tok ), |
423 | Parser<OmpCancelType>{}, maybe("IF" >> parenthesized(scalarLogicalExpr))))) |
424 | |
425 | // 2.17.7 Atomic construct/2.17.8 Flush construct [OpenMP 5.0] |
426 | // memory-order-clause -> |
427 | // seq_cst |
428 | // acq_rel |
429 | // release |
430 | // acquire |
431 | // relaxed |
432 | TYPE_PARSER(sourced(construct<OmpMemoryOrderClause>( |
433 | sourced("SEQ_CST" >> construct<OmpClause>(construct<OmpClause::SeqCst>()) || |
434 | "ACQ_REL" >> construct<OmpClause>(construct<OmpClause::AcqRel>()) || |
435 | "RELEASE" >> construct<OmpClause>(construct<OmpClause::Release>()) || |
436 | "ACQUIRE" >> construct<OmpClause>(construct<OmpClause::Acquire>()) || |
437 | "RELAXED" >> construct<OmpClause>(construct<OmpClause::Relaxed>()))))) |
438 | |
439 | // 2.4 Requires construct [OpenMP 5.0] |
440 | // atomic-default-mem-order-clause -> |
441 | // seq_cst |
442 | // acq_rel |
443 | // relaxed |
444 | TYPE_PARSER(construct<OmpAtomicDefaultMemOrderClause>( |
445 | "SEQ_CST" >> pure(common::OmpAtomicDefaultMemOrderType::SeqCst) || |
446 | "ACQ_REL" >> pure(common::OmpAtomicDefaultMemOrderType::AcqRel) || |
447 | "RELAXED" >> pure(common::OmpAtomicDefaultMemOrderType::Relaxed))) |
448 | |
449 | // 2.17.7 Atomic construct |
450 | // atomic-clause -> memory-order-clause | HINT(hint-expression) |
451 | TYPE_PARSER(sourced(construct<OmpAtomicClause>( |
452 | construct<OmpAtomicClause>(Parser<OmpMemoryOrderClause>{}) || |
453 | construct<OmpAtomicClause>("HINT" >> |
454 | sourced(construct<OmpClause>( |
455 | construct<OmpClause::Hint>(parenthesized(constantExpr)))))))) |
456 | |
457 | // atomic-clause-list -> [atomic-clause, [atomic-clause], ...] |
458 | TYPE_PARSER(sourced(construct<OmpAtomicClauseList>( |
459 | many(maybe(","_tok ) >> sourced(Parser<OmpAtomicClause>{}))))) |
460 | |
461 | TYPE_PARSER(sourced(construct<OpenMPFlushConstruct>(verbatim("FLUSH"_tok ), |
462 | many(maybe(","_tok ) >> sourced(Parser<OmpMemoryOrderClause>{})), |
463 | maybe(parenthesized(Parser<OmpObjectList>{}))))) |
464 | |
465 | // Simple Standalone Directives |
466 | TYPE_PARSER(sourced(construct<OmpSimpleStandaloneDirective>(first( |
467 | "BARRIER" >> pure(llvm::omp::Directive::OMPD_barrier), |
468 | "ORDERED" >> pure(llvm::omp::Directive::OMPD_ordered), |
469 | "TARGET ENTER DATA" >> pure(llvm::omp::Directive::OMPD_target_enter_data), |
470 | "TARGET EXIT DATA" >> pure(llvm::omp::Directive::OMPD_target_exit_data), |
471 | "TARGET UPDATE" >> pure(llvm::omp::Directive::OMPD_target_update), |
472 | "TASKWAIT" >> pure(llvm::omp::Directive::OMPD_taskwait), |
473 | "TASKYIELD" >> pure(llvm::omp::Directive::OMPD_taskyield))))) |
474 | |
475 | TYPE_PARSER(sourced(construct<OpenMPSimpleStandaloneConstruct>( |
476 | Parser<OmpSimpleStandaloneDirective>{}, Parser<OmpClauseList>{}))) |
477 | |
478 | // Standalone Constructs |
479 | TYPE_PARSER( |
480 | sourced(construct<OpenMPStandaloneConstruct>( |
481 | Parser<OpenMPSimpleStandaloneConstruct>{}) || |
482 | construct<OpenMPStandaloneConstruct>(Parser<OpenMPFlushConstruct>{}) || |
483 | construct<OpenMPStandaloneConstruct>(Parser<OpenMPCancelConstruct>{}) || |
484 | construct<OpenMPStandaloneConstruct>( |
485 | Parser<OpenMPCancellationPointConstruct>{})) / |
486 | endOfLine) |
487 | |
488 | // Directives enclosing structured-block |
489 | TYPE_PARSER(construct<OmpBlockDirective>(first( |
490 | "MASTER" >> pure(llvm::omp::Directive::OMPD_master), |
491 | "ORDERED" >> pure(llvm::omp::Directive::OMPD_ordered), |
492 | "PARALLEL WORKSHARE" >> pure(llvm::omp::Directive::OMPD_parallel_workshare), |
493 | "PARALLEL" >> pure(llvm::omp::Directive::OMPD_parallel), |
494 | "SINGLE" >> pure(llvm::omp::Directive::OMPD_single), |
495 | "TARGET DATA" >> pure(llvm::omp::Directive::OMPD_target_data), |
496 | "TARGET PARALLEL" >> pure(llvm::omp::Directive::OMPD_target_parallel), |
497 | "TARGET TEAMS" >> pure(llvm::omp::Directive::OMPD_target_teams), |
498 | "TARGET" >> pure(llvm::omp::Directive::OMPD_target), |
499 | "TASK"_id >> pure(llvm::omp::Directive::OMPD_task), |
500 | "TASKGROUP" >> pure(llvm::omp::Directive::OMPD_taskgroup), |
501 | "TEAMS" >> pure(llvm::omp::Directive::OMPD_teams), |
502 | "WORKSHARE" >> pure(llvm::omp::Directive::OMPD_workshare)))) |
503 | |
504 | TYPE_PARSER(sourced(construct<OmpBeginBlockDirective>( |
505 | sourced(Parser<OmpBlockDirective>{}), Parser<OmpClauseList>{}))) |
506 | |
507 | TYPE_PARSER(construct<OmpReductionInitializerClause>( |
508 | "INITIALIZER" >> parenthesized("OMP_PRIV =" >> expr))) |
509 | |
510 | // 2.16 Declare Reduction Construct |
511 | TYPE_PARSER(sourced(construct<OpenMPDeclareReductionConstruct>( |
512 | verbatim("DECLARE REDUCTION"_tok ), |
513 | "(" >> Parser<OmpReductionOperator>{} / ":" , |
514 | nonemptyList(Parser<DeclarationTypeSpec>{}) / ":" , |
515 | Parser<OmpReductionCombiner>{} / ")" , |
516 | maybe(Parser<OmpReductionInitializerClause>{})))) |
517 | |
518 | // declare-target with list |
519 | TYPE_PARSER(sourced(construct<OmpDeclareTargetWithList>( |
520 | parenthesized(Parser<OmpObjectList>{})))) |
521 | |
522 | // declare-target with clause |
523 | TYPE_PARSER( |
524 | sourced(construct<OmpDeclareTargetWithClause>(Parser<OmpClauseList>{}))) |
525 | |
526 | // declare-target-specifier |
527 | TYPE_PARSER( |
528 | construct<OmpDeclareTargetSpecifier>(Parser<OmpDeclareTargetWithList>{}) || |
529 | construct<OmpDeclareTargetSpecifier>(Parser<OmpDeclareTargetWithClause>{})) |
530 | |
531 | // 2.10.6 Declare Target Construct |
532 | TYPE_PARSER(sourced(construct<OpenMPDeclareTargetConstruct>( |
533 | verbatim("DECLARE TARGET"_tok ), Parser<OmpDeclareTargetSpecifier>{}))) |
534 | |
535 | TYPE_PARSER(construct<OmpReductionCombiner>(Parser<AssignmentStmt>{}) || |
536 | construct<OmpReductionCombiner>( |
537 | construct<OmpReductionCombiner::FunctionCombiner>( |
538 | construct<Call>(Parser<ProcedureDesignator>{}, |
539 | parenthesized(optionalList(actualArgSpec)))))) |
540 | |
541 | // 2.17.7 atomic -> ATOMIC [clause [,]] atomic-clause [[,] clause] | |
542 | // ATOMIC [clause] |
543 | // clause -> memory-order-clause | HINT(hint-expression) |
544 | // memory-order-clause -> SEQ_CST | ACQ_REL | RELEASE | ACQUIRE | RELAXED |
545 | // atomic-clause -> READ | WRITE | UPDATE | CAPTURE |
546 | |
547 | // OMP END ATOMIC |
548 | TYPE_PARSER(construct<OmpEndAtomic>(startOmpLine >> "END ATOMIC"_tok )) |
549 | |
550 | // OMP ATOMIC [MEMORY-ORDER-CLAUSE-LIST] READ [MEMORY-ORDER-CLAUSE-LIST] |
551 | TYPE_PARSER("ATOMIC" >> |
552 | construct<OmpAtomicRead>(Parser<OmpAtomicClauseList>{} / maybe(","_tok ), |
553 | verbatim("READ"_tok ), Parser<OmpAtomicClauseList>{} / endOmpLine, |
554 | statement(assignmentStmt), maybe(Parser<OmpEndAtomic>{} / endOmpLine))) |
555 | |
556 | // OMP ATOMIC [MEMORY-ORDER-CLAUSE-LIST] CAPTURE [MEMORY-ORDER-CLAUSE-LIST] |
557 | TYPE_PARSER("ATOMIC" >> |
558 | construct<OmpAtomicCapture>(Parser<OmpAtomicClauseList>{} / maybe(","_tok ), |
559 | verbatim("CAPTURE"_tok ), Parser<OmpAtomicClauseList>{} / endOmpLine, |
560 | statement(assignmentStmt), statement(assignmentStmt), |
561 | Parser<OmpEndAtomic>{} / endOmpLine)) |
562 | |
563 | // OMP ATOMIC [MEMORY-ORDER-CLAUSE-LIST] UPDATE [MEMORY-ORDER-CLAUSE-LIST] |
564 | TYPE_PARSER("ATOMIC" >> |
565 | construct<OmpAtomicUpdate>(Parser<OmpAtomicClauseList>{} / maybe(","_tok ), |
566 | verbatim("UPDATE"_tok ), Parser<OmpAtomicClauseList>{} / endOmpLine, |
567 | statement(assignmentStmt), maybe(Parser<OmpEndAtomic>{} / endOmpLine))) |
568 | |
569 | // OMP ATOMIC [atomic-clause-list] |
570 | TYPE_PARSER(construct<OmpAtomic>(verbatim("ATOMIC"_tok ), |
571 | Parser<OmpAtomicClauseList>{} / endOmpLine, statement(assignmentStmt), |
572 | maybe(Parser<OmpEndAtomic>{} / endOmpLine))) |
573 | |
574 | // OMP ATOMIC [MEMORY-ORDER-CLAUSE-LIST] WRITE [MEMORY-ORDER-CLAUSE-LIST] |
575 | TYPE_PARSER("ATOMIC" >> |
576 | construct<OmpAtomicWrite>(Parser<OmpAtomicClauseList>{} / maybe(","_tok ), |
577 | verbatim("WRITE"_tok ), Parser<OmpAtomicClauseList>{} / endOmpLine, |
578 | statement(assignmentStmt), maybe(Parser<OmpEndAtomic>{} / endOmpLine))) |
579 | |
580 | // Atomic Construct |
581 | TYPE_PARSER(construct<OpenMPAtomicConstruct>(Parser<OmpAtomicRead>{}) || |
582 | construct<OpenMPAtomicConstruct>(Parser<OmpAtomicCapture>{}) || |
583 | construct<OpenMPAtomicConstruct>(Parser<OmpAtomicWrite>{}) || |
584 | construct<OpenMPAtomicConstruct>(Parser<OmpAtomicUpdate>{}) || |
585 | construct<OpenMPAtomicConstruct>(Parser<OmpAtomic>{})) |
586 | |
587 | // 2.13.2 OMP CRITICAL |
588 | TYPE_PARSER(startOmpLine >> |
589 | sourced(construct<OmpEndCriticalDirective>( |
590 | verbatim("END CRITICAL"_tok ), maybe(parenthesized(name)))) / |
591 | endOmpLine) |
592 | TYPE_PARSER(sourced(construct<OmpCriticalDirective>(verbatim("CRITICAL"_tok ), |
593 | maybe(parenthesized(name)), Parser<OmpClauseList>{})) / |
594 | endOmpLine) |
595 | |
596 | TYPE_PARSER(construct<OpenMPCriticalConstruct>( |
597 | Parser<OmpCriticalDirective>{}, block, Parser<OmpEndCriticalDirective>{})) |
598 | |
599 | // 2.11.3 Executable Allocate directive |
600 | TYPE_PARSER( |
601 | sourced(construct<OpenMPExecutableAllocate>(verbatim("ALLOCATE"_tok ), |
602 | maybe(parenthesized(Parser<OmpObjectList>{})), Parser<OmpClauseList>{}, |
603 | maybe(nonemptyList(Parser<OpenMPDeclarativeAllocate>{})) / endOmpLine, |
604 | statement(allocateStmt)))) |
605 | |
606 | // 6.7 Allocators construct [OpenMP 5.2] |
607 | // allocators-construct -> ALLOCATORS [allocate-clause [,]] |
608 | // allocate-stmt |
609 | // [omp-end-allocators-construct] |
610 | TYPE_PARSER(sourced(construct<OpenMPAllocatorsConstruct>( |
611 | verbatim("ALLOCATORS"_tok ), Parser<OmpClauseList>{} / endOmpLine, |
612 | statement(allocateStmt), maybe(Parser<OmpEndAllocators>{} / endOmpLine)))) |
613 | |
614 | TYPE_PARSER(construct<OmpEndAllocators>(startOmpLine >> "END ALLOCATORS"_tok )) |
615 | |
616 | // 2.8.2 Declare Simd construct |
617 | TYPE_PARSER( |
618 | sourced(construct<OpenMPDeclareSimdConstruct>(verbatim("DECLARE SIMD"_tok ), |
619 | maybe(parenthesized(name)), Parser<OmpClauseList>{}))) |
620 | |
621 | // 2.4 Requires construct |
622 | TYPE_PARSER(sourced(construct<OpenMPRequiresConstruct>( |
623 | verbatim("REQUIRES"_tok ), Parser<OmpClauseList>{}))) |
624 | |
625 | // 2.15.2 Threadprivate directive |
626 | TYPE_PARSER(sourced(construct<OpenMPThreadprivate>( |
627 | verbatim("THREADPRIVATE"_tok ), parenthesized(Parser<OmpObjectList>{})))) |
628 | |
629 | // 2.11.3 Declarative Allocate directive |
630 | TYPE_PARSER( |
631 | sourced(construct<OpenMPDeclarativeAllocate>(verbatim("ALLOCATE"_tok ), |
632 | parenthesized(Parser<OmpObjectList>{}), Parser<OmpClauseList>{})) / |
633 | lookAhead(endOmpLine / !statement(allocateStmt))) |
634 | |
635 | // Declarative constructs |
636 | TYPE_PARSER(startOmpLine >> |
637 | sourced(construct<OpenMPDeclarativeConstruct>( |
638 | Parser<OpenMPDeclareReductionConstruct>{}) || |
639 | construct<OpenMPDeclarativeConstruct>( |
640 | Parser<OpenMPDeclareSimdConstruct>{}) || |
641 | construct<OpenMPDeclarativeConstruct>( |
642 | Parser<OpenMPDeclareTargetConstruct>{}) || |
643 | construct<OpenMPDeclarativeConstruct>( |
644 | Parser<OpenMPDeclarativeAllocate>{}) || |
645 | construct<OpenMPDeclarativeConstruct>( |
646 | Parser<OpenMPRequiresConstruct>{}) || |
647 | construct<OpenMPDeclarativeConstruct>(Parser<OpenMPThreadprivate>{})) / |
648 | endOmpLine) |
649 | |
650 | // Block Construct |
651 | TYPE_PARSER(construct<OpenMPBlockConstruct>( |
652 | Parser<OmpBeginBlockDirective>{} / endOmpLine, block, |
653 | Parser<OmpEndBlockDirective>{} / endOmpLine)) |
654 | |
655 | // OMP SECTIONS Directive |
656 | TYPE_PARSER(construct<OmpSectionsDirective>(first( |
657 | "SECTIONS" >> pure(llvm::omp::Directive::OMPD_sections), |
658 | "PARALLEL SECTIONS" >> pure(llvm::omp::Directive::OMPD_parallel_sections)))) |
659 | |
660 | // OMP BEGIN and END SECTIONS Directive |
661 | TYPE_PARSER(sourced(construct<OmpBeginSectionsDirective>( |
662 | sourced(Parser<OmpSectionsDirective>{}), Parser<OmpClauseList>{}))) |
663 | TYPE_PARSER( |
664 | startOmpLine >> sourced(construct<OmpEndSectionsDirective>( |
665 | sourced("END"_tok >> Parser<OmpSectionsDirective>{}), |
666 | Parser<OmpClauseList>{}))) |
667 | |
668 | // OMP SECTION-BLOCK |
669 | |
670 | TYPE_PARSER(construct<OpenMPSectionConstruct>(block)) |
671 | |
672 | TYPE_PARSER(maybe(startOmpLine >> "SECTION"_tok / endOmpLine) >> |
673 | construct<OmpSectionBlocks>(nonemptySeparated( |
674 | construct<OpenMPConstruct>(sourced(Parser<OpenMPSectionConstruct>{})), |
675 | startOmpLine >> "SECTION"_tok / endOmpLine))) |
676 | |
677 | // OMP SECTIONS (OpenMP 5.0 - 2.8.1), PARALLEL SECTIONS (OpenMP 5.0 - 2.13.3) |
678 | TYPE_PARSER(construct<OpenMPSectionsConstruct>( |
679 | Parser<OmpBeginSectionsDirective>{} / endOmpLine, |
680 | Parser<OmpSectionBlocks>{}, Parser<OmpEndSectionsDirective>{} / endOmpLine)) |
681 | |
682 | TYPE_CONTEXT_PARSER("OpenMP construct"_en_US , |
683 | startOmpLine >> |
684 | first(construct<OpenMPConstruct>(Parser<OpenMPSectionsConstruct>{}), |
685 | construct<OpenMPConstruct>(Parser<OpenMPLoopConstruct>{}), |
686 | construct<OpenMPConstruct>(Parser<OpenMPBlockConstruct>{}), |
687 | // OpenMPBlockConstruct is attempted before |
688 | // OpenMPStandaloneConstruct to resolve !$OMP ORDERED |
689 | construct<OpenMPConstruct>(Parser<OpenMPStandaloneConstruct>{}), |
690 | construct<OpenMPConstruct>(Parser<OpenMPAtomicConstruct>{}), |
691 | construct<OpenMPConstruct>(Parser<OpenMPExecutableAllocate>{}), |
692 | construct<OpenMPConstruct>(Parser<OpenMPAllocatorsConstruct>{}), |
693 | construct<OpenMPConstruct>(Parser<OpenMPDeclarativeAllocate>{}), |
694 | construct<OpenMPConstruct>(Parser<OpenMPCriticalConstruct>{}))) |
695 | |
696 | // END OMP Block directives |
697 | TYPE_PARSER( |
698 | startOmpLine >> sourced(construct<OmpEndBlockDirective>( |
699 | sourced("END"_tok >> Parser<OmpBlockDirective>{}), |
700 | Parser<OmpClauseList>{}))) |
701 | |
702 | // END OMP Loop directives |
703 | TYPE_PARSER( |
704 | startOmpLine >> sourced(construct<OmpEndLoopDirective>( |
705 | sourced("END"_tok >> Parser<OmpLoopDirective>{}), |
706 | Parser<OmpClauseList>{}))) |
707 | |
708 | TYPE_PARSER(construct<OpenMPLoopConstruct>( |
709 | Parser<OmpBeginLoopDirective>{} / endOmpLine)) |
710 | } // namespace Fortran::parser |
711 | |