xref: /llvm-project/flang/lib/Parser/openmp-parsers.cpp (revision 15ab7be2e049bc0f4ea6744ca037395686a923bc)
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 #include "llvm/ADT/ArrayRef.h"
20 #include "llvm/ADT/STLExtras.h"
21 #include "llvm/ADT/StringRef.h"
22 #include "llvm/Frontend/OpenMP/OMP.h"
23 
24 // OpenMP Directives and Clauses
25 namespace Fortran::parser {
26 
27 constexpr auto startOmpLine = skipStuffBeforeStatement >> "!$OMP "_sptok;
28 constexpr auto endOmpLine = space >> endOfLine;
29 
30 /// Parse OpenMP directive name (this includes compound directives).
31 struct OmpDirectiveNameParser {
32   using resultType = llvm::omp::Directive;
33   using Token = TokenStringMatch<false, false>;
34 
35   std::optional<resultType> Parse(ParseState &state) const {
36     for (const NameWithId &nid : directives()) {
37       if (attempt(Token(nid.first.data())).Parse(state)) {
38         return nid.second;
39       }
40     }
41     return std::nullopt;
42   }
43 
44 private:
45   using NameWithId = std::pair<std::string, llvm::omp::Directive>;
46 
47   llvm::iterator_range<const NameWithId *> directives() const;
48   void initTokens(NameWithId *) const;
49 };
50 
51 llvm::iterator_range<const OmpDirectiveNameParser::NameWithId *>
52 OmpDirectiveNameParser::directives() const {
53   static NameWithId table[llvm::omp::Directive_enumSize];
54   [[maybe_unused]] static bool init = (initTokens(table), true);
55   return llvm::make_range(std::cbegin(table), std::cend(table));
56 }
57 
58 void OmpDirectiveNameParser::initTokens(NameWithId *table) const {
59   for (size_t i{0}, e{llvm::omp::Directive_enumSize}; i != e; ++i) {
60     auto id{static_cast<llvm::omp::Directive>(i)};
61     llvm::StringRef name{llvm::omp::getOpenMPDirectiveName(id)};
62     table[i] = std::make_pair(name.str(), id);
63   }
64   // Sort the table with respect to the directive name length in a descending
65   // order. This is to make sure that longer names are tried first, before
66   // any potential prefix (e.g. "target update" before "target").
67   std::sort(table, table + llvm::omp::Directive_enumSize,
68       [](auto &a, auto &b) { return a.first.size() > b.first.size(); });
69 }
70 
71 template <typename Clause, typename Separator> struct ModifierList {
72   constexpr ModifierList(Separator sep) : sep_(sep) {}
73   constexpr ModifierList(const ModifierList &) = default;
74   constexpr ModifierList(ModifierList &&) = default;
75 
76   using resultType = std::list<typename Clause::Modifier>;
77 
78   std::optional<resultType> Parse(ParseState &state) const {
79     auto listp{nonemptySeparated(Parser<typename Clause::Modifier>{}, sep_)};
80     if (auto result{attempt(listp).Parse(state)}) {
81       if (!attempt(":"_tok).Parse(state)) {
82         return std::nullopt;
83       }
84       return std::move(result);
85     }
86     return resultType{};
87   }
88 
89 private:
90   const Separator sep_;
91 };
92 
93 // Use a function to create ModifierList because functions allow "partial"
94 // template argument deduction: "modifierList<Clause>(sep)" would be legal,
95 // while "ModifierList<Clause>(sep)" would complain about a missing template
96 // argument "Separator".
97 template <typename Clause, typename Separator>
98 constexpr ModifierList<Clause, Separator> modifierList(Separator sep) {
99   return ModifierList<Clause, Separator>(sep);
100 }
101 
102 // Parse the input as any modifier from ClauseTy, but only succeed if
103 // the result was the SpecificTy. It requires that SpecificTy is one
104 // of the alternatives in ClauseTy::Modifier.
105 // The reason to have this is that ClauseTy::Modifier has "source",
106 // while specific modifiers don't. This class allows to parse a specific
107 // modifier together with obtaining its location.
108 template <typename SpecificTy, typename ClauseTy>
109 struct SpecificModifierParser {
110   using resultType = typename ClauseTy::Modifier;
111   std::optional<resultType> Parse(ParseState &state) const {
112     if (auto result{attempt(Parser<resultType>{}).Parse(state)}) {
113       if (std::holds_alternative<SpecificTy>(result->u)) {
114         return result;
115       }
116     }
117     return std::nullopt;
118   }
119 };
120 
121 // OpenMP Clauses
122 
123 // [5.0] 2.1.6 iterator-specifier -> type-declaration-stmt = subscript-triple |
124 //                                   identifier = subscript-triple
125 // [5.0:47:17-18] In an iterator-specifier, if the iterator-type is not
126 // specified then the type of that iterator is default integer.
127 // [5.0:49:14] The iterator-type must be an integer type.
128 static std::list<EntityDecl> makeEntityList(std::list<ObjectName> &&names) {
129   std::list<EntityDecl> entities;
130 
131   for (auto iter = names.begin(), end = names.end(); iter != end; ++iter) {
132     EntityDecl entityDecl(
133         /*ObjectName=*/std::move(*iter), std::optional<ArraySpec>{},
134         std::optional<CoarraySpec>{}, std::optional<CharLength>{},
135         std::optional<Initialization>{});
136     entities.push_back(std::move(entityDecl));
137   }
138   return entities;
139 }
140 
141 static TypeDeclarationStmt makeIterSpecDecl(
142     DeclarationTypeSpec &&spec, std::list<ObjectName> &&names) {
143   return TypeDeclarationStmt(
144       std::move(spec), std::list<AttrSpec>{}, makeEntityList(std::move(names)));
145 }
146 
147 static TypeDeclarationStmt makeIterSpecDecl(std::list<ObjectName> &&names) {
148   // Assume INTEGER without kind selector.
149   DeclarationTypeSpec typeSpec(
150       IntrinsicTypeSpec{IntegerTypeSpec{std::nullopt}});
151 
152   return TypeDeclarationStmt(std::move(typeSpec), std::list<AttrSpec>{},
153       makeEntityList(std::move(names)));
154 }
155 
156 TYPE_PARSER(sourced(construct<OmpDirectiveSpecification>(
157     OmpDirectiveNameParser{}, maybe(indirect(Parser<OmpClauseList>{})))))
158 
159 // --- Parsers for context traits -------------------------------------
160 
161 static std::string nameToString(Name &&name) { return name.ToString(); }
162 
163 TYPE_PARSER(sourced(construct<OmpTraitPropertyName>( //
164     construct<OmpTraitPropertyName>(space >> charLiteralConstantWithoutKind) ||
165     construct<OmpTraitPropertyName>(
166         applyFunction(nameToString, Parser<Name>{})))))
167 
168 TYPE_PARSER(sourced(construct<OmpTraitScore>( //
169     "SCORE"_id >> parenthesized(scalarIntExpr))))
170 
171 TYPE_PARSER(sourced(construct<OmpTraitPropertyExtension::Complex>(
172     Parser<OmpTraitPropertyName>{},
173     parenthesized(nonemptySeparated(
174         indirect(Parser<OmpTraitPropertyExtension>{}), ",")))))
175 
176 TYPE_PARSER(sourced(construct<OmpTraitPropertyExtension>(
177     construct<OmpTraitPropertyExtension>(
178         Parser<OmpTraitPropertyExtension::Complex>{}) ||
179     construct<OmpTraitPropertyExtension>(Parser<OmpTraitPropertyName>{}) ||
180     construct<OmpTraitPropertyExtension>(scalarExpr))))
181 
182 TYPE_PARSER(construct<OmpTraitSelectorName::Value>(
183     "ARCH"_id >> pure(OmpTraitSelectorName::Value::Arch) ||
184     "ATOMIC_DEFAULT_MEM_ORDER"_id >>
185         pure(OmpTraitSelectorName::Value::Atomic_Default_Mem_Order) ||
186     "CONDITION"_id >> pure(OmpTraitSelectorName::Value::Condition) ||
187     "DEVICE_NUM"_id >> pure(OmpTraitSelectorName::Value::Device_Num) ||
188     "EXTENSION"_id >> pure(OmpTraitSelectorName::Value::Extension) ||
189     "ISA"_id >> pure(OmpTraitSelectorName::Value::Isa) ||
190     "KIND"_id >> pure(OmpTraitSelectorName::Value::Kind) ||
191     "REQUIRES"_id >> pure(OmpTraitSelectorName::Value::Requires) ||
192     "SIMD"_id >> pure(OmpTraitSelectorName::Value::Simd) ||
193     "UID"_id >> pure(OmpTraitSelectorName::Value::Uid) ||
194     "VENDOR"_id >> pure(OmpTraitSelectorName::Value::Vendor)))
195 
196 TYPE_PARSER(sourced(construct<OmpTraitSelectorName>(
197     // Parse predefined names first (because of SIMD).
198     construct<OmpTraitSelectorName>(Parser<OmpTraitSelectorName::Value>{}) ||
199     construct<OmpTraitSelectorName>(OmpDirectiveNameParser{}) ||
200     // identifier-or-string for extensions
201     construct<OmpTraitSelectorName>(
202         applyFunction(nameToString, Parser<Name>{})) ||
203     construct<OmpTraitSelectorName>(space >> charLiteralConstantWithoutKind))))
204 
205 // Parser for OmpTraitSelector::Properties
206 template <typename... PropParser>
207 static constexpr auto propertyListParser(PropParser... pp) {
208   // Parse the property list "(score(expr): item1...)" in three steps:
209   // 1. Parse the "("
210   // 2. Parse the optional "score(expr):"
211   // 3. Parse the "item1, ...)", together with the ")".
212   // The reason for including the ")" in the 3rd step is to force parsing
213   // the entire list in each of the alternative property parsers. Otherwise,
214   // the name parser could stop after "foo" in "(foo, bar(1))", without
215   // allowing the next parser to give the list a try.
216   auto listOf{[](auto parser) { //
217     return nonemptySeparated(parser, ",");
218   }};
219 
220   using P = OmpTraitProperty;
221   return maybe("(" >> //
222       construct<OmpTraitSelector::Properties>(
223           maybe(Parser<OmpTraitScore>{} / ":"),
224           (attempt(listOf(sourced(construct<P>(pp))) / ")") || ...)));
225 }
226 
227 // Parser for OmpTraitSelector
228 struct TraitSelectorParser {
229   using resultType = OmpTraitSelector;
230 
231   constexpr TraitSelectorParser(Parser<OmpTraitSelectorName> p) : np(p) {}
232 
233   std::optional<resultType> Parse(ParseState &state) const {
234     auto name{attempt(np).Parse(state)};
235     if (!name.has_value()) {
236       return std::nullopt;
237     }
238 
239     // Default fallback parser for lists that cannot be parser using the
240     // primary property parser.
241     auto extParser{Parser<OmpTraitPropertyExtension>{}};
242 
243     if (auto *v{std::get_if<OmpTraitSelectorName::Value>(&name->u)}) {
244       // (*) The comments below show the sections of the OpenMP spec that
245       // describe given trait. The cases marked with a (*) are those where
246       // the spec doesn't assign any list-type to these traits, but for
247       // convenience they can be treated as if they were.
248       switch (*v) {
249       // name-list properties
250       case OmpTraitSelectorName::Value::Arch: // [6.0:319:18]
251       case OmpTraitSelectorName::Value::Extension: // [6.0:319:30]
252       case OmpTraitSelectorName::Value::Isa: // [6.0:319:15]
253       case OmpTraitSelectorName::Value::Kind: // [6.0:319:10]
254       case OmpTraitSelectorName::Value::Uid: // [6.0:319:23](*)
255       case OmpTraitSelectorName::Value::Vendor: { // [6.0:319:27]
256         auto pp{propertyListParser(Parser<OmpTraitPropertyName>{}, extParser)};
257         return OmpTraitSelector(std::move(*name), std::move(*pp.Parse(state)));
258       }
259       // clause-list
260       case OmpTraitSelectorName::Value::Atomic_Default_Mem_Order:
261         // [6.0:321:26-29](*)
262       case OmpTraitSelectorName::Value::Requires: // [6.0:319:33]
263       case OmpTraitSelectorName::Value::Simd: { // [6.0:318:31]
264         auto pp{propertyListParser(indirect(Parser<OmpClause>{}), extParser)};
265         return OmpTraitSelector(std::move(*name), std::move(*pp.Parse(state)));
266       }
267       // expr-list
268       case OmpTraitSelectorName::Value::Condition: // [6.0:321:33](*)
269       case OmpTraitSelectorName::Value::Device_Num: { // [6.0:321:23-24](*)
270         auto pp{propertyListParser(scalarExpr, extParser)};
271         return OmpTraitSelector(std::move(*name), std::move(*pp.Parse(state)));
272       }
273       } // switch
274     } else {
275       // The other alternatives are `llvm::omp::Directive`, and `std::string`.
276       // The former doesn't take any properties[1], the latter is a name of an
277       // extension[2].
278       // [1] [6.0:319:1-2]
279       // [2] [6.0:319:36-37]
280       auto pp{propertyListParser(extParser)};
281       return OmpTraitSelector(std::move(*name), std::move(*pp.Parse(state)));
282     }
283 
284     llvm_unreachable("Unhandled trait name?");
285   }
286 
287 private:
288   const Parser<OmpTraitSelectorName> np;
289 };
290 
291 TYPE_PARSER(sourced(construct<OmpTraitSelector>(
292     sourced(TraitSelectorParser(Parser<OmpTraitSelectorName>{})))))
293 
294 TYPE_PARSER(construct<OmpTraitSetSelectorName::Value>(
295     "CONSTRUCT"_id >> pure(OmpTraitSetSelectorName::Value::Construct) ||
296     "DEVICE"_id >> pure(OmpTraitSetSelectorName::Value::Device) ||
297     "IMPLEMENTATION"_id >>
298         pure(OmpTraitSetSelectorName::Value::Implementation) ||
299     "TARGET_DEVICE"_id >> pure(OmpTraitSetSelectorName::Value::Target_Device) ||
300     "USER"_id >> pure(OmpTraitSetSelectorName::Value::User)))
301 
302 TYPE_PARSER(sourced(construct<OmpTraitSetSelectorName>(
303     Parser<OmpTraitSetSelectorName::Value>{})))
304 
305 TYPE_PARSER(sourced(construct<OmpTraitSetSelector>( //
306     Parser<OmpTraitSetSelectorName>{},
307     "=" >> braced(nonemptySeparated(Parser<OmpTraitSelector>{}, ",")))))
308 
309 TYPE_PARSER(sourced(construct<OmpContextSelectorSpecification>(
310     nonemptySeparated(Parser<OmpTraitSetSelector>{}, ","))))
311 
312 // Parser<OmpContextSelector> == Parser<traits::OmpContextSelectorSpecification>
313 
314 // --- Parsers for clause modifiers -----------------------------------
315 
316 TYPE_PARSER(construct<OmpAlignment>(scalarIntExpr))
317 
318 TYPE_PARSER(construct<OmpAlignModifier>( //
319     "ALIGN" >> parenthesized(scalarIntExpr)))
320 
321 TYPE_PARSER(construct<OmpAllocatorComplexModifier>(
322     "ALLOCATOR" >> parenthesized(scalarIntExpr)))
323 
324 TYPE_PARSER(construct<OmpAllocatorSimpleModifier>(scalarIntExpr))
325 
326 TYPE_PARSER(construct<OmpChunkModifier>( //
327     "SIMD" >> pure(OmpChunkModifier::Value::Simd)))
328 
329 TYPE_PARSER(construct<OmpDependenceType>(
330     "SINK" >> pure(OmpDependenceType::Value::Sink) ||
331     "SOURCE" >> pure(OmpDependenceType::Value::Source)))
332 
333 TYPE_PARSER(construct<OmpDeviceModifier>(
334     "ANCESTOR" >> pure(OmpDeviceModifier::Value::Ancestor) ||
335     "DEVICE_NUM" >> pure(OmpDeviceModifier::Value::Device_Num)))
336 
337 TYPE_PARSER(construct<OmpExpectation>( //
338     "PRESENT" >> pure(OmpExpectation::Value::Present)))
339 
340 TYPE_PARSER(construct<OmpIteratorSpecifier>(
341     // Using Parser<TypeDeclarationStmt> or Parser<EntityDecl> has the problem
342     // that they will attempt to treat what follows the '=' as initialization.
343     // There are several issues with that,
344     // 1. integer :: i = 0:10 will be parsed as "integer :: i = 0", followed
345     // by triplet ":10".
346     // 2. integer :: j = i:10 will be flagged as an error because the
347     // initializer 'i' must be constant (in declarations). In an iterator
348     // specifier the 'j' is not an initializer and can be a variable.
349     (applyFunction<TypeDeclarationStmt>(makeIterSpecDecl,
350          Parser<DeclarationTypeSpec>{} / maybe("::"_tok),
351          nonemptyList(Parser<ObjectName>{}) / "="_tok) ||
352         applyFunction<TypeDeclarationStmt>(
353             makeIterSpecDecl, nonemptyList(Parser<ObjectName>{}) / "="_tok)),
354     subscriptTriplet))
355 
356 // [5.0] 2.1.6 iterator -> iterator-specifier-list
357 TYPE_PARSER(construct<OmpIterator>( //
358     "ITERATOR" >>
359     parenthesized(nonemptyList(sourced(Parser<OmpIteratorSpecifier>{})))))
360 
361 TYPE_PARSER(construct<OmpLastprivateModifier>(
362     "CONDITIONAL" >> pure(OmpLastprivateModifier::Value::Conditional)))
363 
364 // 2.15.3.7 LINEAR (linear-list: linear-step)
365 //          linear-list -> list | modifier(list)
366 //          linear-modifier -> REF | VAL | UVAL
367 TYPE_PARSER(construct<OmpLinearModifier>( //
368     "REF" >> pure(OmpLinearModifier::Value::Ref) ||
369     "VAL" >> pure(OmpLinearModifier::Value::Val) ||
370     "UVAL" >> pure(OmpLinearModifier::Value::Uval)))
371 
372 TYPE_PARSER(construct<OmpMapper>( //
373     "MAPPER"_tok >> parenthesized(Parser<ObjectName>{})))
374 
375 // map-type -> ALLOC | DELETE | FROM | RELEASE | TO | TOFROM
376 TYPE_PARSER(construct<OmpMapType>( //
377     "ALLOC" >> pure(OmpMapType::Value::Alloc) ||
378     "DELETE" >> pure(OmpMapType::Value::Delete) ||
379     "FROM" >> pure(OmpMapType::Value::From) ||
380     "RELEASE" >> pure(OmpMapType::Value::Release) ||
381     "TO"_id >> pure(OmpMapType::Value::To) ||
382     "TOFROM" >> pure(OmpMapType::Value::Tofrom)))
383 
384 // map-type-modifier -> ALWAYS | CLOSE | OMPX_HOLD | PRESENT
385 TYPE_PARSER(construct<OmpMapTypeModifier>(
386     "ALWAYS" >> pure(OmpMapTypeModifier::Value::Always) ||
387     "CLOSE" >> pure(OmpMapTypeModifier::Value::Close) ||
388     "OMPX_HOLD" >> pure(OmpMapTypeModifier::Value::Ompx_Hold) ||
389     "PRESENT" >> pure(OmpMapTypeModifier::Value::Present)))
390 
391 // 2.15.3.6 REDUCTION (reduction-identifier: variable-name-list)
392 TYPE_PARSER(construct<OmpReductionIdentifier>(Parser<DefinedOperator>{}) ||
393     construct<OmpReductionIdentifier>(Parser<ProcedureDesignator>{}))
394 
395 TYPE_PARSER(construct<OmpOrderModifier>(
396     "REPRODUCIBLE" >> pure(OmpOrderModifier::Value::Reproducible) ||
397     "UNCONSTRAINED" >> pure(OmpOrderModifier::Value::Unconstrained)))
398 
399 TYPE_PARSER(construct<OmpOrderingModifier>(
400     "MONOTONIC" >> pure(OmpOrderingModifier::Value::Monotonic) ||
401     "NONMONOTONIC" >> pure(OmpOrderingModifier::Value::Nonmonotonic) ||
402     "SIMD" >> pure(OmpOrderingModifier::Value::Simd)))
403 
404 TYPE_PARSER(construct<OmpPrescriptiveness>(
405     "STRICT" >> pure(OmpPrescriptiveness::Value::Strict)))
406 
407 TYPE_PARSER(construct<OmpReductionModifier>(
408     "INSCAN" >> pure(OmpReductionModifier::Value::Inscan) ||
409     "TASK" >> pure(OmpReductionModifier::Value::Task) ||
410     "DEFAULT" >> pure(OmpReductionModifier::Value::Default)))
411 
412 TYPE_PARSER(construct<OmpStepComplexModifier>( //
413     "STEP" >> parenthesized(scalarIntExpr)))
414 
415 TYPE_PARSER(construct<OmpStepSimpleModifier>(scalarIntExpr))
416 
417 TYPE_PARSER(construct<OmpTaskDependenceType>(
418     "DEPOBJ" >> pure(OmpTaskDependenceType::Value::Depobj) ||
419     "IN"_id >> pure(OmpTaskDependenceType::Value::In) ||
420     "INOUT"_id >> pure(OmpTaskDependenceType::Value::Inout) ||
421     "INOUTSET"_id >> pure(OmpTaskDependenceType::Value::Inoutset) ||
422     "MUTEXINOUTSET" >> pure(OmpTaskDependenceType::Value::Mutexinoutset) ||
423     "OUT" >> pure(OmpTaskDependenceType::Value::Out)))
424 
425 TYPE_PARSER(construct<OmpVariableCategory>(
426     "AGGREGATE" >> pure(OmpVariableCategory::Value::Aggregate) ||
427     "ALL"_id >> pure(OmpVariableCategory::Value::All) ||
428     "ALLOCATABLE" >> pure(OmpVariableCategory::Value::Allocatable) ||
429     "POINTER" >> pure(OmpVariableCategory::Value::Pointer) ||
430     "SCALAR" >> pure(OmpVariableCategory::Value::Scalar)))
431 
432 // This could be auto-generated.
433 TYPE_PARSER(
434     sourced(construct<OmpAffinityClause::Modifier>(Parser<OmpIterator>{})))
435 
436 TYPE_PARSER(
437     sourced(construct<OmpAlignedClause::Modifier>(Parser<OmpAlignment>{})))
438 
439 TYPE_PARSER(sourced(construct<OmpAllocateClause::Modifier>(sourced(
440     construct<OmpAllocateClause::Modifier>(Parser<OmpAlignModifier>{}) ||
441     construct<OmpAllocateClause::Modifier>(
442         Parser<OmpAllocatorComplexModifier>{}) ||
443     construct<OmpAllocateClause::Modifier>(
444         Parser<OmpAllocatorSimpleModifier>{})))))
445 
446 TYPE_PARSER(sourced(
447     construct<OmpDefaultmapClause::Modifier>(Parser<OmpVariableCategory>{})))
448 
449 TYPE_PARSER(sourced(construct<OmpDependClause::TaskDep::Modifier>(sourced(
450     construct<OmpDependClause::TaskDep::Modifier>(Parser<OmpIterator>{}) ||
451     construct<OmpDependClause::TaskDep::Modifier>(
452         Parser<OmpTaskDependenceType>{})))))
453 
454 TYPE_PARSER(
455     sourced(construct<OmpDeviceClause::Modifier>(Parser<OmpDeviceModifier>{})))
456 
457 TYPE_PARSER(sourced(construct<OmpFromClause::Modifier>(
458     sourced(construct<OmpFromClause::Modifier>(Parser<OmpExpectation>{}) ||
459         construct<OmpFromClause::Modifier>(Parser<OmpMapper>{}) ||
460         construct<OmpFromClause::Modifier>(Parser<OmpIterator>{})))))
461 
462 TYPE_PARSER(sourced(
463     construct<OmpGrainsizeClause::Modifier>(Parser<OmpPrescriptiveness>{})))
464 
465 TYPE_PARSER(sourced(construct<OmpIfClause::Modifier>(OmpDirectiveNameParser{})))
466 
467 TYPE_PARSER(sourced(construct<OmpInReductionClause::Modifier>(
468     Parser<OmpReductionIdentifier>{})))
469 
470 TYPE_PARSER(sourced(construct<OmpLastprivateClause::Modifier>(
471     Parser<OmpLastprivateModifier>{})))
472 
473 TYPE_PARSER(sourced(
474     construct<OmpLinearClause::Modifier>(Parser<OmpLinearModifier>{}) ||
475     construct<OmpLinearClause::Modifier>(Parser<OmpStepComplexModifier>{}) ||
476     construct<OmpLinearClause::Modifier>(Parser<OmpStepSimpleModifier>{})))
477 
478 TYPE_PARSER(sourced(construct<OmpMapClause::Modifier>(
479     sourced(construct<OmpMapClause::Modifier>(Parser<OmpMapTypeModifier>{}) ||
480         construct<OmpMapClause::Modifier>(Parser<OmpMapper>{}) ||
481         construct<OmpMapClause::Modifier>(Parser<OmpIterator>{}) ||
482         construct<OmpMapClause::Modifier>(Parser<OmpMapType>{})))))
483 
484 TYPE_PARSER(
485     sourced(construct<OmpOrderClause::Modifier>(Parser<OmpOrderModifier>{})))
486 
487 TYPE_PARSER(sourced(
488     construct<OmpNumTasksClause::Modifier>(Parser<OmpPrescriptiveness>{})))
489 
490 TYPE_PARSER(sourced(construct<OmpReductionClause::Modifier>(sourced(
491     construct<OmpReductionClause::Modifier>(Parser<OmpReductionModifier>{}) ||
492     construct<OmpReductionClause::Modifier>(
493         Parser<OmpReductionIdentifier>{})))))
494 
495 TYPE_PARSER(sourced(construct<OmpScheduleClause::Modifier>(sourced(
496     construct<OmpScheduleClause::Modifier>(Parser<OmpChunkModifier>{}) ||
497     construct<OmpScheduleClause::Modifier>(Parser<OmpOrderingModifier>{})))))
498 
499 TYPE_PARSER(sourced(construct<OmpTaskReductionClause::Modifier>(
500     Parser<OmpReductionIdentifier>{})))
501 
502 TYPE_PARSER(sourced(construct<OmpToClause::Modifier>(
503     sourced(construct<OmpToClause::Modifier>(Parser<OmpExpectation>{}) ||
504         construct<OmpToClause::Modifier>(Parser<OmpMapper>{}) ||
505         construct<OmpToClause::Modifier>(Parser<OmpIterator>{})))))
506 
507 TYPE_PARSER(sourced(construct<OmpWhenClause::Modifier>( //
508     Parser<OmpContextSelector>{})))
509 
510 // --- Parsers for clauses --------------------------------------------
511 
512 /// `MOBClause` is a clause that has a
513 ///   std::tuple<Modifiers, OmpObjectList, bool>.
514 /// Helper function to create a typical modifiers-objects clause, where the
515 /// commas separating individual modifiers are optional, and the clause
516 /// contains a bool member to indicate whether it was fully comma-separated
517 /// or not.
518 template <bool CommaSeparated, typename MOBClause>
519 static inline MOBClause makeMobClause(
520     std::list<typename MOBClause::Modifier> &&mods, OmpObjectList &&objs) {
521   if (!mods.empty()) {
522     return MOBClause{std::move(mods), std::move(objs), CommaSeparated};
523   } else {
524     using ListTy = std::list<typename MOBClause::Modifier>;
525     return MOBClause{std::optional<ListTy>{}, std::move(objs), CommaSeparated};
526   }
527 }
528 
529 // [5.0] 2.10.1 affinity([aff-modifier:] locator-list)
530 //              aff-modifier: interator-modifier
531 TYPE_PARSER(construct<OmpAffinityClause>(
532     maybe(nonemptyList(Parser<OmpAffinityClause::Modifier>{}) / ":"),
533     Parser<OmpObjectList>{}))
534 
535 // 2.15.3.1 DEFAULT (PRIVATE | FIRSTPRIVATE | SHARED | NONE)
536 TYPE_PARSER(construct<OmpDefaultClause::DataSharingAttribute>(
537     "PRIVATE" >> pure(OmpDefaultClause::DataSharingAttribute::Private) ||
538     "FIRSTPRIVATE" >>
539         pure(OmpDefaultClause::DataSharingAttribute::Firstprivate) ||
540     "SHARED" >> pure(OmpDefaultClause::DataSharingAttribute::Shared) ||
541     "NONE" >> pure(OmpDefaultClause::DataSharingAttribute::None)))
542 
543 TYPE_PARSER(construct<OmpDefaultClause>(
544     construct<OmpDefaultClause>(
545         Parser<OmpDefaultClause::DataSharingAttribute>{}) ||
546     construct<OmpDefaultClause>(Parser<OmpDirectiveSpecification>{})))
547 
548 // 2.5 PROC_BIND (MASTER | CLOSE | PRIMARY | SPREAD)
549 TYPE_PARSER(construct<OmpProcBindClause>(
550     "CLOSE" >> pure(OmpProcBindClause::AffinityPolicy::Close) ||
551     "MASTER" >> pure(OmpProcBindClause::AffinityPolicy::Master) ||
552     "PRIMARY" >> pure(OmpProcBindClause::AffinityPolicy::Primary) ||
553     "SPREAD" >> pure(OmpProcBindClause::AffinityPolicy::Spread)))
554 
555 TYPE_PARSER(construct<OmpMapClause>(
556     applyFunction<OmpMapClause>(makeMobClause<true>,
557         modifierList<OmpMapClause>(","_tok), Parser<OmpObjectList>{}) ||
558     applyFunction<OmpMapClause>(makeMobClause<false>,
559         modifierList<OmpMapClause>(maybe(","_tok)), Parser<OmpObjectList>{})))
560 
561 // [OpenMP 5.0]
562 // 2.19.7.2 defaultmap(implicit-behavior[:variable-category])
563 //  implicit-behavior -> ALLOC | TO | FROM | TOFROM | FIRSRTPRIVATE | NONE |
564 //  DEFAULT
565 //  variable-category -> ALL | SCALAR | AGGREGATE | ALLOCATABLE | POINTER
566 TYPE_PARSER(construct<OmpDefaultmapClause>(
567     construct<OmpDefaultmapClause::ImplicitBehavior>(
568         "ALLOC" >> pure(OmpDefaultmapClause::ImplicitBehavior::Alloc) ||
569         "TO"_id >> pure(OmpDefaultmapClause::ImplicitBehavior::To) ||
570         "FROM" >> pure(OmpDefaultmapClause::ImplicitBehavior::From) ||
571         "TOFROM" >> pure(OmpDefaultmapClause::ImplicitBehavior::Tofrom) ||
572         "FIRSTPRIVATE" >>
573             pure(OmpDefaultmapClause::ImplicitBehavior::Firstprivate) ||
574         "NONE" >> pure(OmpDefaultmapClause::ImplicitBehavior::None) ||
575         "DEFAULT" >> pure(OmpDefaultmapClause::ImplicitBehavior::Default)),
576     maybe(":" >> nonemptyList(Parser<OmpDefaultmapClause::Modifier>{}))))
577 
578 TYPE_PARSER(construct<OmpScheduleClause::Kind>(
579     "STATIC" >> pure(OmpScheduleClause::Kind::Static) ||
580     "DYNAMIC" >> pure(OmpScheduleClause::Kind::Dynamic) ||
581     "GUIDED" >> pure(OmpScheduleClause::Kind::Guided) ||
582     "AUTO" >> pure(OmpScheduleClause::Kind::Auto) ||
583     "RUNTIME" >> pure(OmpScheduleClause::Kind::Runtime)))
584 
585 TYPE_PARSER(construct<OmpScheduleClause>(
586     maybe(nonemptyList(Parser<OmpScheduleClause::Modifier>{}) / ":"),
587     Parser<OmpScheduleClause::Kind>{}, maybe("," >> scalarIntExpr)))
588 
589 // device([ device-modifier :] scalar-integer-expression)
590 TYPE_PARSER(construct<OmpDeviceClause>(
591     maybe(nonemptyList(Parser<OmpDeviceClause::Modifier>{}) / ":"),
592     scalarIntExpr))
593 
594 // device_type(any | host | nohost)
595 TYPE_PARSER(construct<OmpDeviceTypeClause>(
596     "ANY" >> pure(OmpDeviceTypeClause::DeviceTypeDescription::Any) ||
597     "HOST" >> pure(OmpDeviceTypeClause::DeviceTypeDescription::Host) ||
598     "NOHOST" >> pure(OmpDeviceTypeClause::DeviceTypeDescription::Nohost)))
599 
600 // 2.12 IF (directive-name-modifier: scalar-logical-expr)
601 TYPE_PARSER(construct<OmpIfClause>(
602     maybe(nonemptyList(Parser<OmpIfClause::Modifier>{}) / ":"),
603     scalarLogicalExpr))
604 
605 TYPE_PARSER(construct<OmpReductionClause>(
606     maybe(nonemptyList(Parser<OmpReductionClause::Modifier>{}) / ":"),
607     Parser<OmpObjectList>{}))
608 
609 // OMP 5.0 2.19.5.6 IN_REDUCTION (reduction-identifier: variable-name-list)
610 TYPE_PARSER(construct<OmpInReductionClause>(
611     maybe(nonemptyList(Parser<OmpInReductionClause::Modifier>{}) / ":"),
612     Parser<OmpObjectList>{}))
613 
614 TYPE_PARSER(construct<OmpTaskReductionClause>(
615     maybe(nonemptyList(Parser<OmpTaskReductionClause::Modifier>{}) / ":"),
616     Parser<OmpObjectList>{}))
617 
618 // OMP 5.0 2.11.4 allocate-clause -> ALLOCATE ([allocator:] variable-name-list)
619 // OMP 5.2 2.13.4 allocate-clause -> ALLOCATE ([allocate-modifier
620 //                                   [, allocate-modifier] :]
621 //                                   variable-name-list)
622 //                allocate-modifier -> allocator | align
623 TYPE_PARSER(construct<OmpAllocateClause>(
624     maybe(nonemptyList(Parser<OmpAllocateClause::Modifier>{}) / ":"),
625     Parser<OmpObjectList>{}))
626 
627 // iteration-offset -> +/- non-negative-constant-expr
628 TYPE_PARSER(construct<OmpIterationOffset>(
629     Parser<DefinedOperator>{}, scalarIntConstantExpr))
630 
631 // iteration -> iteration-variable [+/- nonnegative-scalar-integer-constant]
632 TYPE_PARSER(construct<OmpIteration>(name, maybe(Parser<OmpIterationOffset>{})))
633 
634 TYPE_PARSER(construct<OmpIterationVector>(nonemptyList(Parser<OmpIteration>{})))
635 
636 TYPE_PARSER(construct<OmpDoacross>(
637     construct<OmpDoacross>(construct<OmpDoacross::Sink>(
638         "SINK"_tok >> ":"_tok >> Parser<OmpIterationVector>{})) ||
639     construct<OmpDoacross>(construct<OmpDoacross::Source>("SOURCE"_tok))))
640 
641 TYPE_CONTEXT_PARSER("Omp Depend clause"_en_US,
642     construct<OmpDependClause>(
643         // Try to parse OmpDoacross first, because TaskDep will succeed on
644         // "sink: xxx", interpreting it to not have any modifiers, and "sink"
645         // being an OmpObject. Parsing of the TaskDep variant will stop right
646         // after the "sink", leaving the ": xxx" unvisited.
647         construct<OmpDependClause>(Parser<OmpDoacross>{}) ||
648         // Parse TaskDep after Doacross.
649         construct<OmpDependClause>(construct<OmpDependClause::TaskDep>(
650             maybe(nonemptyList(Parser<OmpDependClause::TaskDep::Modifier>{}) /
651                 ": "),
652             Parser<OmpObjectList>{}))))
653 
654 TYPE_CONTEXT_PARSER("Omp Doacross clause"_en_US,
655     construct<OmpDoacrossClause>(Parser<OmpDoacross>{}))
656 
657 TYPE_PARSER(construct<OmpFromClause>(
658     applyFunction<OmpFromClause>(makeMobClause<true>,
659         modifierList<OmpFromClause>(","_tok), Parser<OmpObjectList>{}) ||
660     applyFunction<OmpFromClause>(makeMobClause<false>,
661         modifierList<OmpFromClause>(maybe(","_tok)), Parser<OmpObjectList>{})))
662 
663 TYPE_PARSER(construct<OmpToClause>(
664     applyFunction<OmpToClause>(makeMobClause<true>,
665         modifierList<OmpToClause>(","_tok), Parser<OmpObjectList>{}) ||
666     applyFunction<OmpToClause>(makeMobClause<false>,
667         modifierList<OmpToClause>(maybe(","_tok)), Parser<OmpObjectList>{})))
668 
669 OmpLinearClause makeLinearFromOldSyntax(OmpLinearClause::Modifier &&lm,
670     OmpObjectList &&objs, std::optional<OmpLinearClause::Modifier> &&ssm) {
671   std::list<OmpLinearClause::Modifier> mods;
672   mods.emplace_back(std::move(lm));
673   if (ssm) {
674     mods.emplace_back(std::move(*ssm));
675   }
676   return OmpLinearClause{std::move(objs),
677       mods.empty() ? decltype(mods){} : std::move(mods),
678       /*PostModified=*/false};
679 }
680 
681 TYPE_PARSER(
682     // Parse the "modifier(x)" first, because syntacticaly it will match
683     // an array element (i.e. a list item).
684     // LINEAR(linear-modifier(list) [: step-simple-modifier])
685     construct<OmpLinearClause>( //
686         applyFunction<OmpLinearClause>(makeLinearFromOldSyntax,
687             SpecificModifierParser<OmpLinearModifier, OmpLinearClause>{},
688             parenthesized(Parser<OmpObjectList>{}),
689             maybe(":"_tok >> SpecificModifierParser<OmpStepSimpleModifier,
690                                  OmpLinearClause>{}))) ||
691     // LINEAR(list [: modifiers])
692     construct<OmpLinearClause>( //
693         Parser<OmpObjectList>{},
694         maybe(":"_tok >> nonemptyList(Parser<OmpLinearClause::Modifier>{})),
695         /*PostModified=*/pure(true)))
696 
697 // OpenMPv5.2 12.5.2 detach-clause -> DETACH (event-handle)
698 TYPE_PARSER(construct<OmpDetachClause>(Parser<OmpObject>{}))
699 
700 // 2.8.1 ALIGNED (list: alignment)
701 TYPE_PARSER(construct<OmpAlignedClause>(Parser<OmpObjectList>{},
702     maybe(":" >> nonemptyList(Parser<OmpAlignedClause::Modifier>{}))))
703 
704 TYPE_PARSER(construct<OmpUpdateClause>(
705     construct<OmpUpdateClause>(Parser<OmpDependenceType>{}) ||
706     construct<OmpUpdateClause>(Parser<OmpTaskDependenceType>{})))
707 
708 TYPE_PARSER(construct<OmpOrderClause>(
709     maybe(nonemptyList(Parser<OmpOrderClause::Modifier>{}) / ":"),
710     "CONCURRENT" >> pure(OmpOrderClause::Ordering::Concurrent)))
711 
712 TYPE_PARSER(construct<OmpMatchClause>(
713     Parser<traits::OmpContextSelectorSpecification>{}))
714 
715 TYPE_PARSER(construct<OmpOtherwiseClause>(
716     maybe(sourced(Parser<OmpDirectiveSpecification>{}))))
717 
718 TYPE_PARSER(construct<OmpWhenClause>(
719     maybe(nonemptyList(Parser<OmpWhenClause::Modifier>{}) / ":"),
720     maybe(sourced(Parser<OmpDirectiveSpecification>{}))))
721 
722 // OMP 5.2 12.6.1 grainsize([ prescriptiveness :] scalar-integer-expression)
723 TYPE_PARSER(construct<OmpGrainsizeClause>(
724     maybe(nonemptyList(Parser<OmpGrainsizeClause::Modifier>{}) / ":"),
725     scalarIntExpr))
726 
727 // OMP 5.2 12.6.2 num_tasks([ prescriptiveness :] scalar-integer-expression)
728 TYPE_PARSER(construct<OmpNumTasksClause>(
729     maybe(nonemptyList(Parser<OmpNumTasksClause::Modifier>{}) / ":"),
730     scalarIntExpr))
731 
732 TYPE_PARSER(
733     construct<OmpObject>(designator) || construct<OmpObject>("/" >> name / "/"))
734 
735 // OMP 5.0 2.19.4.5 LASTPRIVATE ([lastprivate-modifier :] list)
736 TYPE_PARSER(construct<OmpLastprivateClause>(
737     maybe(nonemptyList(Parser<OmpLastprivateClause::Modifier>{}) / ":"),
738     Parser<OmpObjectList>{}))
739 
740 // OMP 5.2 11.7.1 BIND ( PARALLEL | TEAMS | THREAD )
741 TYPE_PARSER(construct<OmpBindClause>(
742     "PARALLEL" >> pure(OmpBindClause::Binding::Parallel) ||
743     "TEAMS" >> pure(OmpBindClause::Binding::Teams) ||
744     "THREAD" >> pure(OmpBindClause::Binding::Thread)))
745 
746 TYPE_PARSER(construct<OmpAlignClause>(scalarIntExpr))
747 
748 TYPE_PARSER(construct<OmpAtClause>(
749     "EXECUTION" >> pure(OmpAtClause::ActionTime::Execution) ||
750     "COMPILATION" >> pure(OmpAtClause::ActionTime::Compilation)))
751 
752 TYPE_PARSER(construct<OmpSeverityClause>(
753     "FATAL" >> pure(OmpSeverityClause::Severity::Fatal) ||
754     "WARNING" >> pure(OmpSeverityClause::Severity::Warning)))
755 
756 TYPE_PARSER(construct<OmpMessageClause>(expr))
757 
758 TYPE_PARSER(
759     "ACQUIRE" >> construct<OmpClause>(construct<OmpClause::Acquire>()) ||
760     "ACQ_REL" >> construct<OmpClause>(construct<OmpClause::AcqRel>()) ||
761     "AFFINITY" >> construct<OmpClause>(construct<OmpClause::Affinity>(
762                       parenthesized(Parser<OmpAffinityClause>{}))) ||
763     "ALIGN" >> construct<OmpClause>(construct<OmpClause::Align>(
764                    parenthesized(Parser<OmpAlignClause>{}))) ||
765     "ALIGNED" >> construct<OmpClause>(construct<OmpClause::Aligned>(
766                      parenthesized(Parser<OmpAlignedClause>{}))) ||
767     "ALLOCATE" >> construct<OmpClause>(construct<OmpClause::Allocate>(
768                       parenthesized(Parser<OmpAllocateClause>{}))) ||
769     "ALLOCATOR" >> construct<OmpClause>(construct<OmpClause::Allocator>(
770                        parenthesized(scalarIntExpr))) ||
771     "AT" >> construct<OmpClause>(construct<OmpClause::At>(
772                 parenthesized(Parser<OmpAtClause>{}))) ||
773     "ATOMIC_DEFAULT_MEM_ORDER" >>
774         construct<OmpClause>(construct<OmpClause::AtomicDefaultMemOrder>(
775             parenthesized(Parser<OmpAtomicDefaultMemOrderClause>{}))) ||
776     "BIND" >> construct<OmpClause>(construct<OmpClause::Bind>(
777                   parenthesized(Parser<OmpBindClause>{}))) ||
778     "COLLAPSE" >> construct<OmpClause>(construct<OmpClause::Collapse>(
779                       parenthesized(scalarIntConstantExpr))) ||
780     "COPYIN" >> construct<OmpClause>(construct<OmpClause::Copyin>(
781                     parenthesized(Parser<OmpObjectList>{}))) ||
782     "COPYPRIVATE" >> construct<OmpClause>(construct<OmpClause::Copyprivate>(
783                          (parenthesized(Parser<OmpObjectList>{})))) ||
784     "DEFAULT"_id >> construct<OmpClause>(construct<OmpClause::Default>(
785                         parenthesized(Parser<OmpDefaultClause>{}))) ||
786     "DEFAULTMAP" >> construct<OmpClause>(construct<OmpClause::Defaultmap>(
787                         parenthesized(Parser<OmpDefaultmapClause>{}))) ||
788     "DEPEND" >> construct<OmpClause>(construct<OmpClause::Depend>(
789                     parenthesized(Parser<OmpDependClause>{}))) ||
790     "DESTROY" >>
791         construct<OmpClause>(construct<OmpClause::Destroy>(maybe(parenthesized(
792             construct<OmpDestroyClause>(Parser<OmpObject>{}))))) ||
793     "DEVICE" >> construct<OmpClause>(construct<OmpClause::Device>(
794                     parenthesized(Parser<OmpDeviceClause>{}))) ||
795     "DEVICE_TYPE" >> construct<OmpClause>(construct<OmpClause::DeviceType>(
796                          parenthesized(Parser<OmpDeviceTypeClause>{}))) ||
797     "DIST_SCHEDULE" >>
798         construct<OmpClause>(construct<OmpClause::DistSchedule>(
799             parenthesized("STATIC" >> maybe("," >> scalarIntExpr)))) ||
800     "DOACROSS" >>
801         construct<OmpClause>(parenthesized(Parser<OmpDoacrossClause>{})) ||
802     "DYNAMIC_ALLOCATORS" >>
803         construct<OmpClause>(construct<OmpClause::DynamicAllocators>()) ||
804     "ENTER" >> construct<OmpClause>(construct<OmpClause::Enter>(
805                    parenthesized(Parser<OmpObjectList>{}))) ||
806     "EXCLUSIVE" >> construct<OmpClause>(construct<OmpClause::Exclusive>(
807                        parenthesized(Parser<OmpObjectList>{}))) ||
808     "FILTER" >> construct<OmpClause>(construct<OmpClause::Filter>(
809                     parenthesized(scalarIntExpr))) ||
810     "FINAL" >> construct<OmpClause>(construct<OmpClause::Final>(
811                    parenthesized(scalarLogicalExpr))) ||
812     "FIRSTPRIVATE" >> construct<OmpClause>(construct<OmpClause::Firstprivate>(
813                           parenthesized(Parser<OmpObjectList>{}))) ||
814     "FROM" >> construct<OmpClause>(construct<OmpClause::From>(
815                   parenthesized(Parser<OmpFromClause>{}))) ||
816     "FULL" >> construct<OmpClause>(construct<OmpClause::Full>()) ||
817     "GRAINSIZE" >> construct<OmpClause>(construct<OmpClause::Grainsize>(
818                        parenthesized(Parser<OmpGrainsizeClause>{}))) ||
819     "HAS_DEVICE_ADDR" >>
820         construct<OmpClause>(construct<OmpClause::HasDeviceAddr>(
821             parenthesized(Parser<OmpObjectList>{}))) ||
822     "HINT" >> construct<OmpClause>(
823                   construct<OmpClause::Hint>(parenthesized(constantExpr))) ||
824     "IF" >> construct<OmpClause>(construct<OmpClause::If>(
825                 parenthesized(Parser<OmpIfClause>{}))) ||
826     "INBRANCH" >> construct<OmpClause>(construct<OmpClause::Inbranch>()) ||
827     "INCLUSIVE" >> construct<OmpClause>(construct<OmpClause::Inclusive>(
828                        parenthesized(Parser<OmpObjectList>{}))) ||
829     "IS_DEVICE_PTR" >> construct<OmpClause>(construct<OmpClause::IsDevicePtr>(
830                            parenthesized(Parser<OmpObjectList>{}))) ||
831     "LASTPRIVATE" >> construct<OmpClause>(construct<OmpClause::Lastprivate>(
832                          parenthesized(Parser<OmpLastprivateClause>{}))) ||
833     "LINEAR" >> construct<OmpClause>(construct<OmpClause::Linear>(
834                     parenthesized(Parser<OmpLinearClause>{}))) ||
835     "LINK" >> construct<OmpClause>(construct<OmpClause::Link>(
836                   parenthesized(Parser<OmpObjectList>{}))) ||
837     "MAP" >> construct<OmpClause>(construct<OmpClause::Map>(
838                  parenthesized(Parser<OmpMapClause>{}))) ||
839     "MATCH" >> construct<OmpClause>(construct<OmpClause::Match>(
840                    parenthesized(Parser<OmpMatchClause>{}))) ||
841     "MERGEABLE" >> construct<OmpClause>(construct<OmpClause::Mergeable>()) ||
842     "MESSAGE" >> construct<OmpClause>(construct<OmpClause::Message>(
843                      parenthesized(Parser<OmpMessageClause>{}))) ||
844     "NOCONTEXT" >> construct<OmpClause>(construct<OmpClause::Nocontext>(
845                        parenthesized(scalarLogicalExpr))) ||
846     "NOGROUP" >> construct<OmpClause>(construct<OmpClause::Nogroup>()) ||
847     "NONTEMPORAL" >> construct<OmpClause>(construct<OmpClause::Nontemporal>(
848                          parenthesized(nonemptyList(name)))) ||
849     "NOTINBRANCH" >>
850         construct<OmpClause>(construct<OmpClause::Notinbranch>()) ||
851     "NOVARIANTS" >> construct<OmpClause>(construct<OmpClause::Novariants>(
852                         parenthesized(scalarLogicalExpr))) ||
853     "NOWAIT" >> construct<OmpClause>(construct<OmpClause::Nowait>()) ||
854     "NUM_TASKS" >> construct<OmpClause>(construct<OmpClause::NumTasks>(
855                        parenthesized(Parser<OmpNumTasksClause>{}))) ||
856     "NUM_TEAMS" >> construct<OmpClause>(construct<OmpClause::NumTeams>(
857                        parenthesized(scalarIntExpr))) ||
858     "NUM_THREADS" >> construct<OmpClause>(construct<OmpClause::NumThreads>(
859                          parenthesized(scalarIntExpr))) ||
860     "OMPX_BARE" >> construct<OmpClause>(construct<OmpClause::OmpxBare>()) ||
861     "ORDER" >> construct<OmpClause>(construct<OmpClause::Order>(
862                    parenthesized(Parser<OmpOrderClause>{}))) ||
863     "ORDERED" >> construct<OmpClause>(construct<OmpClause::Ordered>(
864                      maybe(parenthesized(scalarIntConstantExpr)))) ||
865     "OTHERWISE" >> construct<OmpClause>(construct<OmpClause::Otherwise>(
866                        maybe(parenthesized(Parser<OmpOtherwiseClause>{})))) ||
867     "PARTIAL" >> construct<OmpClause>(construct<OmpClause::Partial>(
868                      maybe(parenthesized(scalarIntConstantExpr)))) ||
869     "PRIORITY" >> construct<OmpClause>(construct<OmpClause::Priority>(
870                       parenthesized(scalarIntExpr))) ||
871     "PRIVATE" >> construct<OmpClause>(construct<OmpClause::Private>(
872                      parenthesized(Parser<OmpObjectList>{}))) ||
873     "PROC_BIND" >> construct<OmpClause>(construct<OmpClause::ProcBind>(
874                        parenthesized(Parser<OmpProcBindClause>{}))) ||
875     "REDUCTION"_id >> construct<OmpClause>(construct<OmpClause::Reduction>(
876                           parenthesized(Parser<OmpReductionClause>{}))) ||
877     "IN_REDUCTION" >> construct<OmpClause>(construct<OmpClause::InReduction>(
878                           parenthesized(Parser<OmpInReductionClause>{}))) ||
879     "DETACH" >> construct<OmpClause>(construct<OmpClause::Detach>(
880                     parenthesized(Parser<OmpDetachClause>{}))) ||
881     "TASK_REDUCTION" >>
882         construct<OmpClause>(construct<OmpClause::TaskReduction>(
883             parenthesized(Parser<OmpTaskReductionClause>{}))) ||
884     "RELAXED" >> construct<OmpClause>(construct<OmpClause::Relaxed>()) ||
885     "RELEASE" >> construct<OmpClause>(construct<OmpClause::Release>()) ||
886     "REVERSE_OFFLOAD" >>
887         construct<OmpClause>(construct<OmpClause::ReverseOffload>()) ||
888     "SAFELEN" >> construct<OmpClause>(construct<OmpClause::Safelen>(
889                      parenthesized(scalarIntConstantExpr))) ||
890     "SCHEDULE" >> construct<OmpClause>(construct<OmpClause::Schedule>(
891                       parenthesized(Parser<OmpScheduleClause>{}))) ||
892     "SEQ_CST" >> construct<OmpClause>(construct<OmpClause::SeqCst>()) ||
893     "SEVERITY" >> construct<OmpClause>(construct<OmpClause::Severity>(
894                       parenthesized(Parser<OmpSeverityClause>{}))) ||
895     "SHARED" >> construct<OmpClause>(construct<OmpClause::Shared>(
896                     parenthesized(Parser<OmpObjectList>{}))) ||
897     "SIMD"_id >> construct<OmpClause>(construct<OmpClause::Simd>()) ||
898     "SIMDLEN" >> construct<OmpClause>(construct<OmpClause::Simdlen>(
899                      parenthesized(scalarIntConstantExpr))) ||
900     "SIZES" >> construct<OmpClause>(construct<OmpClause::Sizes>(
901                    parenthesized(nonemptyList(scalarIntExpr)))) ||
902     "PERMUTATION" >> construct<OmpClause>(construct<OmpClause::Permutation>(
903                          parenthesized(nonemptyList(scalarIntExpr)))) ||
904     "THREADS" >> construct<OmpClause>(construct<OmpClause::Threads>()) ||
905     "THREAD_LIMIT" >> construct<OmpClause>(construct<OmpClause::ThreadLimit>(
906                           parenthesized(scalarIntExpr))) ||
907     "TO" >> construct<OmpClause>(construct<OmpClause::To>(
908                 parenthesized(Parser<OmpToClause>{}))) ||
909     "USE_DEVICE_PTR" >> construct<OmpClause>(construct<OmpClause::UseDevicePtr>(
910                             parenthesized(Parser<OmpObjectList>{}))) ||
911     "USE_DEVICE_ADDR" >>
912         construct<OmpClause>(construct<OmpClause::UseDeviceAddr>(
913             parenthesized(Parser<OmpObjectList>{}))) ||
914     "UNIFIED_ADDRESS" >>
915         construct<OmpClause>(construct<OmpClause::UnifiedAddress>()) ||
916     "UNIFIED_SHARED_MEMORY" >>
917         construct<OmpClause>(construct<OmpClause::UnifiedSharedMemory>()) ||
918     "UNIFORM" >> construct<OmpClause>(construct<OmpClause::Uniform>(
919                      parenthesized(nonemptyList(name)))) ||
920     "UNTIED" >> construct<OmpClause>(construct<OmpClause::Untied>()) ||
921     "UPDATE" >> construct<OmpClause>(construct<OmpClause::Update>(
922                     parenthesized(Parser<OmpUpdateClause>{}))) ||
923     "WHEN" >> construct<OmpClause>(construct<OmpClause::When>(
924                   parenthesized(Parser<OmpWhenClause>{}))))
925 
926 // [Clause, [Clause], ...]
927 TYPE_PARSER(sourced(construct<OmpClauseList>(
928     many(maybe(","_tok) >> sourced(Parser<OmpClause>{})))))
929 
930 // 2.1 (variable | /common-block/ | array-sections)
931 TYPE_PARSER(construct<OmpObjectList>(nonemptyList(Parser<OmpObject>{})))
932 
933 TYPE_PARSER(sourced(construct<OmpErrorDirective>(
934     verbatim("ERROR"_tok), Parser<OmpClauseList>{})))
935 
936 TYPE_PARSER(sourced(construct<OmpNothingDirective>("NOTHING" >> ok)))
937 
938 TYPE_PARSER(sourced(construct<OpenMPUtilityConstruct>(
939     sourced(construct<OpenMPUtilityConstruct>(
940         sourced(Parser<OmpErrorDirective>{}))) ||
941     sourced(construct<OpenMPUtilityConstruct>(
942         sourced(Parser<OmpNothingDirective>{}))))))
943 
944 TYPE_PARSER(sourced(construct<OmpMetadirectiveDirective>(
945     "METADIRECTIVE" >> Parser<OmpClauseList>{})))
946 
947 // Omp directives enclosing do loop
948 TYPE_PARSER(sourced(construct<OmpLoopDirective>(first(
949     "DISTRIBUTE PARALLEL DO SIMD" >>
950         pure(llvm::omp::Directive::OMPD_distribute_parallel_do_simd),
951     "DISTRIBUTE PARALLEL DO" >>
952         pure(llvm::omp::Directive::OMPD_distribute_parallel_do),
953     "DISTRIBUTE SIMD" >> pure(llvm::omp::Directive::OMPD_distribute_simd),
954     "DISTRIBUTE" >> pure(llvm::omp::Directive::OMPD_distribute),
955     "DO SIMD" >> pure(llvm::omp::Directive::OMPD_do_simd),
956     "DO" >> pure(llvm::omp::Directive::OMPD_do),
957     "LOOP" >> pure(llvm::omp::Directive::OMPD_loop),
958     "MASKED TASKLOOP SIMD" >>
959         pure(llvm::omp::Directive::OMPD_masked_taskloop_simd),
960     "MASKED TASKLOOP" >> pure(llvm::omp::Directive::OMPD_masked_taskloop),
961     "MASTER TASKLOOP SIMD" >>
962         pure(llvm::omp::Directive::OMPD_master_taskloop_simd),
963     "MASTER TASKLOOP" >> pure(llvm::omp::Directive::OMPD_master_taskloop),
964     "PARALLEL DO SIMD" >> pure(llvm::omp::Directive::OMPD_parallel_do_simd),
965     "PARALLEL DO" >> pure(llvm::omp::Directive::OMPD_parallel_do),
966     "PARALLEL MASKED TASKLOOP SIMD" >>
967         pure(llvm::omp::Directive::OMPD_parallel_masked_taskloop_simd),
968     "PARALLEL MASKED TASKLOOP" >>
969         pure(llvm::omp::Directive::OMPD_parallel_masked_taskloop),
970     "PARALLEL MASTER TASKLOOP SIMD" >>
971         pure(llvm::omp::Directive::OMPD_parallel_master_taskloop_simd),
972     "PARALLEL MASTER TASKLOOP" >>
973         pure(llvm::omp::Directive::OMPD_parallel_master_taskloop),
974     "SIMD" >> pure(llvm::omp::Directive::OMPD_simd),
975     "TARGET LOOP" >> pure(llvm::omp::Directive::OMPD_target_loop),
976     "TARGET PARALLEL DO SIMD" >>
977         pure(llvm::omp::Directive::OMPD_target_parallel_do_simd),
978     "TARGET PARALLEL DO" >> pure(llvm::omp::Directive::OMPD_target_parallel_do),
979     "TARGET PARALLEL LOOP" >>
980         pure(llvm::omp::Directive::OMPD_target_parallel_loop),
981     "TARGET SIMD" >> pure(llvm::omp::Directive::OMPD_target_simd),
982     "TARGET TEAMS DISTRIBUTE PARALLEL DO SIMD" >>
983         pure(llvm::omp::Directive::
984                 OMPD_target_teams_distribute_parallel_do_simd),
985     "TARGET TEAMS DISTRIBUTE PARALLEL DO" >>
986         pure(llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do),
987     "TARGET TEAMS DISTRIBUTE SIMD" >>
988         pure(llvm::omp::Directive::OMPD_target_teams_distribute_simd),
989     "TARGET TEAMS DISTRIBUTE" >>
990         pure(llvm::omp::Directive::OMPD_target_teams_distribute),
991     "TARGET TEAMS LOOP" >> pure(llvm::omp::Directive::OMPD_target_teams_loop),
992     "TASKLOOP SIMD" >> pure(llvm::omp::Directive::OMPD_taskloop_simd),
993     "TASKLOOP" >> pure(llvm::omp::Directive::OMPD_taskloop),
994     "TEAMS DISTRIBUTE PARALLEL DO SIMD" >>
995         pure(llvm::omp::Directive::OMPD_teams_distribute_parallel_do_simd),
996     "TEAMS DISTRIBUTE PARALLEL DO" >>
997         pure(llvm::omp::Directive::OMPD_teams_distribute_parallel_do),
998     "TEAMS DISTRIBUTE SIMD" >>
999         pure(llvm::omp::Directive::OMPD_teams_distribute_simd),
1000     "TEAMS DISTRIBUTE" >> pure(llvm::omp::Directive::OMPD_teams_distribute),
1001     "TEAMS LOOP" >> pure(llvm::omp::Directive::OMPD_teams_loop),
1002     "TILE" >> pure(llvm::omp::Directive::OMPD_tile),
1003     "UNROLL" >> pure(llvm::omp::Directive::OMPD_unroll)))))
1004 
1005 TYPE_PARSER(sourced(construct<OmpBeginLoopDirective>(
1006     sourced(Parser<OmpLoopDirective>{}), Parser<OmpClauseList>{})))
1007 
1008 // 2.14.1 construct-type-clause -> PARALLEL | SECTIONS | DO | TASKGROUP
1009 TYPE_PARSER(sourced(construct<OmpCancelType>(
1010     first("PARALLEL" >> pure(OmpCancelType::Type::Parallel),
1011         "SECTIONS" >> pure(OmpCancelType::Type::Sections),
1012         "DO" >> pure(OmpCancelType::Type::Do),
1013         "TASKGROUP" >> pure(OmpCancelType::Type::Taskgroup)))))
1014 
1015 // 2.14.2 Cancellation Point construct
1016 TYPE_PARSER(sourced(construct<OpenMPCancellationPointConstruct>(
1017     verbatim("CANCELLATION POINT"_tok), Parser<OmpCancelType>{})))
1018 
1019 // 2.14.1 Cancel construct
1020 TYPE_PARSER(sourced(construct<OpenMPCancelConstruct>(verbatim("CANCEL"_tok),
1021     Parser<OmpCancelType>{}, maybe("IF" >> parenthesized(scalarLogicalExpr)))))
1022 
1023 TYPE_PARSER(sourced(construct<OmpFailClause>(
1024     parenthesized(indirect(Parser<OmpMemoryOrderClause>{})))))
1025 
1026 // 2.17.7 Atomic construct/2.17.8 Flush construct [OpenMP 5.0]
1027 //        memory-order-clause ->
1028 //                               seq_cst
1029 //                               acq_rel
1030 //                               release
1031 //                               acquire
1032 //                               relaxed
1033 TYPE_PARSER(sourced(construct<OmpMemoryOrderClause>(
1034     sourced("SEQ_CST" >> construct<OmpClause>(construct<OmpClause::SeqCst>()) ||
1035         "ACQ_REL" >> construct<OmpClause>(construct<OmpClause::AcqRel>()) ||
1036         "RELEASE" >> construct<OmpClause>(construct<OmpClause::Release>()) ||
1037         "ACQUIRE" >> construct<OmpClause>(construct<OmpClause::Acquire>()) ||
1038         "RELAXED" >> construct<OmpClause>(construct<OmpClause::Relaxed>())))))
1039 
1040 // 2.4 Requires construct [OpenMP 5.0]
1041 //        atomic-default-mem-order-clause ->
1042 //                               seq_cst
1043 //                               acq_rel
1044 //                               relaxed
1045 TYPE_PARSER(construct<OmpAtomicDefaultMemOrderClause>(
1046     "SEQ_CST" >> pure(common::OmpAtomicDefaultMemOrderType::SeqCst) ||
1047     "ACQ_REL" >> pure(common::OmpAtomicDefaultMemOrderType::AcqRel) ||
1048     "RELAXED" >> pure(common::OmpAtomicDefaultMemOrderType::Relaxed)))
1049 
1050 // 2.17.7 Atomic construct
1051 //        atomic-clause -> memory-order-clause | HINT(hint-expression)
1052 TYPE_PARSER(sourced(construct<OmpAtomicClause>(
1053     construct<OmpAtomicClause>(Parser<OmpMemoryOrderClause>{}) ||
1054     construct<OmpAtomicClause>("FAIL" >> Parser<OmpFailClause>{}) ||
1055     construct<OmpAtomicClause>("HINT" >>
1056         sourced(construct<OmpClause>(
1057             construct<OmpClause::Hint>(parenthesized(constantExpr))))))))
1058 
1059 // atomic-clause-list -> [atomic-clause, [atomic-clause], ...]
1060 TYPE_PARSER(sourced(construct<OmpAtomicClauseList>(
1061     many(maybe(","_tok) >> sourced(Parser<OmpAtomicClause>{})))))
1062 
1063 TYPE_PARSER(sourced(construct<OpenMPDepobjConstruct>(verbatim("DEPOBJ"_tok),
1064     parenthesized(Parser<OmpObject>{}), sourced(Parser<OmpClause>{}))))
1065 
1066 TYPE_PARSER(sourced(construct<OpenMPFlushConstruct>(verbatim("FLUSH"_tok),
1067     many(maybe(","_tok) >> sourced(Parser<OmpMemoryOrderClause>{})),
1068     maybe(parenthesized(Parser<OmpObjectList>{})))))
1069 
1070 // Simple Standalone Directives
1071 TYPE_PARSER(sourced(construct<OmpSimpleStandaloneDirective>(first(
1072     "BARRIER" >> pure(llvm::omp::Directive::OMPD_barrier),
1073     "ORDERED" >> pure(llvm::omp::Directive::OMPD_ordered),
1074     "SCAN" >> pure(llvm::omp::Directive::OMPD_scan),
1075     "TARGET ENTER DATA" >> pure(llvm::omp::Directive::OMPD_target_enter_data),
1076     "TARGET EXIT DATA" >> pure(llvm::omp::Directive::OMPD_target_exit_data),
1077     "TARGET UPDATE" >> pure(llvm::omp::Directive::OMPD_target_update),
1078     "TASKWAIT" >> pure(llvm::omp::Directive::OMPD_taskwait),
1079     "TASKYIELD" >> pure(llvm::omp::Directive::OMPD_taskyield)))))
1080 
1081 TYPE_PARSER(sourced(construct<OpenMPSimpleStandaloneConstruct>(
1082     Parser<OmpSimpleStandaloneDirective>{}, Parser<OmpClauseList>{})))
1083 
1084 // Standalone Constructs
1085 TYPE_PARSER(
1086     sourced(construct<OpenMPStandaloneConstruct>(
1087                 Parser<OpenMPSimpleStandaloneConstruct>{}) ||
1088         construct<OpenMPStandaloneConstruct>(Parser<OpenMPFlushConstruct>{}) ||
1089         construct<OpenMPStandaloneConstruct>(Parser<OpenMPCancelConstruct>{}) ||
1090         construct<OpenMPStandaloneConstruct>(
1091             Parser<OpenMPCancellationPointConstruct>{}) ||
1092         construct<OpenMPStandaloneConstruct>(
1093             Parser<OmpMetadirectiveDirective>{}) ||
1094         construct<OpenMPStandaloneConstruct>(Parser<OpenMPDepobjConstruct>{})) /
1095     endOfLine)
1096 
1097 // Directives enclosing structured-block
1098 TYPE_PARSER(construct<OmpBlockDirective>(first(
1099     "MASKED" >> pure(llvm::omp::Directive::OMPD_masked),
1100     "MASTER" >> pure(llvm::omp::Directive::OMPD_master),
1101     "ORDERED" >> pure(llvm::omp::Directive::OMPD_ordered),
1102     "PARALLEL MASKED" >> pure(llvm::omp::Directive::OMPD_parallel_masked),
1103     "PARALLEL MASTER" >> pure(llvm::omp::Directive::OMPD_parallel_master),
1104     "PARALLEL WORKSHARE" >> pure(llvm::omp::Directive::OMPD_parallel_workshare),
1105     "PARALLEL" >> pure(llvm::omp::Directive::OMPD_parallel),
1106     "SCOPE" >> pure(llvm::omp::Directive::OMPD_scope),
1107     "SINGLE" >> pure(llvm::omp::Directive::OMPD_single),
1108     "TARGET DATA" >> pure(llvm::omp::Directive::OMPD_target_data),
1109     "TARGET PARALLEL" >> pure(llvm::omp::Directive::OMPD_target_parallel),
1110     "TARGET TEAMS" >> pure(llvm::omp::Directive::OMPD_target_teams),
1111     "TARGET" >> pure(llvm::omp::Directive::OMPD_target),
1112     "TASK"_id >> pure(llvm::omp::Directive::OMPD_task),
1113     "TASKGROUP" >> pure(llvm::omp::Directive::OMPD_taskgroup),
1114     "TEAMS" >> pure(llvm::omp::Directive::OMPD_teams),
1115     "WORKSHARE" >> pure(llvm::omp::Directive::OMPD_workshare))))
1116 
1117 TYPE_PARSER(sourced(construct<OmpBeginBlockDirective>(
1118     sourced(Parser<OmpBlockDirective>{}), Parser<OmpClauseList>{})))
1119 
1120 TYPE_PARSER(construct<OmpReductionInitializerClause>(
1121     "INITIALIZER" >> parenthesized("OMP_PRIV =" >> expr)))
1122 
1123 // 2.16 Declare Reduction Construct
1124 TYPE_PARSER(sourced(construct<OpenMPDeclareReductionConstruct>(
1125     verbatim("DECLARE REDUCTION"_tok),
1126     "(" >> Parser<OmpReductionIdentifier>{} / ":",
1127     nonemptyList(Parser<DeclarationTypeSpec>{}) / ":",
1128     Parser<OmpReductionCombiner>{} / ")",
1129     maybe(Parser<OmpReductionInitializerClause>{}))))
1130 
1131 // declare-target with list
1132 TYPE_PARSER(sourced(construct<OmpDeclareTargetWithList>(
1133     parenthesized(Parser<OmpObjectList>{}))))
1134 
1135 // declare-target with clause
1136 TYPE_PARSER(
1137     sourced(construct<OmpDeclareTargetWithClause>(Parser<OmpClauseList>{})))
1138 
1139 // declare-target-specifier
1140 TYPE_PARSER(
1141     construct<OmpDeclareTargetSpecifier>(Parser<OmpDeclareTargetWithList>{}) ||
1142     construct<OmpDeclareTargetSpecifier>(Parser<OmpDeclareTargetWithClause>{}))
1143 
1144 // 2.10.6 Declare Target Construct
1145 TYPE_PARSER(sourced(construct<OpenMPDeclareTargetConstruct>(
1146     verbatim("DECLARE TARGET"_tok), Parser<OmpDeclareTargetSpecifier>{})))
1147 
1148 // declare-mapper-specifier
1149 TYPE_PARSER(construct<OmpDeclareMapperSpecifier>(
1150     maybe(name / ":" / !":"_tok), typeSpec / "::", name))
1151 
1152 // OpenMP 5.2: 5.8.8 Declare Mapper Construct
1153 TYPE_PARSER(sourced(construct<OpenMPDeclareMapperConstruct>(
1154     verbatim("DECLARE MAPPER"_tok),
1155     "(" >> Parser<OmpDeclareMapperSpecifier>{} / ")", Parser<OmpClauseList>{})))
1156 
1157 TYPE_PARSER(construct<OmpReductionCombiner>(Parser<AssignmentStmt>{}) ||
1158     construct<OmpReductionCombiner>(
1159         construct<OmpReductionCombiner::FunctionCombiner>(
1160             construct<Call>(Parser<ProcedureDesignator>{},
1161                 parenthesized(optionalList(actualArgSpec))))))
1162 
1163 // 2.17.7 atomic -> ATOMIC [clause [,]] atomic-clause [[,] clause] |
1164 //                  ATOMIC [clause]
1165 //       clause -> memory-order-clause | HINT(hint-expression)
1166 //       memory-order-clause -> SEQ_CST | ACQ_REL | RELEASE | ACQUIRE | RELAXED
1167 //       atomic-clause -> READ | WRITE | UPDATE | CAPTURE
1168 
1169 // OMP END ATOMIC
1170 TYPE_PARSER(construct<OmpEndAtomic>(startOmpLine >> "END ATOMIC"_tok))
1171 
1172 // OMP ATOMIC [MEMORY-ORDER-CLAUSE-LIST] READ [MEMORY-ORDER-CLAUSE-LIST]
1173 TYPE_PARSER("ATOMIC" >>
1174     sourced(construct<OmpAtomicRead>(
1175         Parser<OmpAtomicClauseList>{} / maybe(","_tok), verbatim("READ"_tok),
1176         Parser<OmpAtomicClauseList>{} / endOmpLine, statement(assignmentStmt),
1177         maybe(Parser<OmpEndAtomic>{} / endOmpLine))))
1178 
1179 // OMP ATOMIC [MEMORY-ORDER-CLAUSE-LIST] CAPTURE [MEMORY-ORDER-CLAUSE-LIST]
1180 TYPE_PARSER("ATOMIC" >>
1181     sourced(construct<OmpAtomicCapture>(
1182         Parser<OmpAtomicClauseList>{} / maybe(","_tok), verbatim("CAPTURE"_tok),
1183         Parser<OmpAtomicClauseList>{} / endOmpLine, statement(assignmentStmt),
1184         statement(assignmentStmt), Parser<OmpEndAtomic>{} / endOmpLine)))
1185 
1186 TYPE_PARSER(construct<OmpAtomicCompareIfStmt>(indirect(Parser<IfStmt>{})) ||
1187     construct<OmpAtomicCompareIfStmt>(indirect(Parser<IfConstruct>{})))
1188 
1189 // OMP ATOMIC [MEMORY-ORDER-CLAUSE-LIST] COMPARE [MEMORY-ORDER-CLAUSE-LIST]
1190 TYPE_PARSER("ATOMIC" >>
1191     sourced(construct<OmpAtomicCompare>(
1192         Parser<OmpAtomicClauseList>{} / maybe(","_tok), verbatim("COMPARE"_tok),
1193         Parser<OmpAtomicClauseList>{} / endOmpLine,
1194         Parser<OmpAtomicCompareIfStmt>{},
1195         maybe(Parser<OmpEndAtomic>{} / endOmpLine))))
1196 
1197 // OMP ATOMIC [MEMORY-ORDER-CLAUSE-LIST] UPDATE [MEMORY-ORDER-CLAUSE-LIST]
1198 TYPE_PARSER("ATOMIC" >>
1199     sourced(construct<OmpAtomicUpdate>(
1200         Parser<OmpAtomicClauseList>{} / maybe(","_tok), verbatim("UPDATE"_tok),
1201         Parser<OmpAtomicClauseList>{} / endOmpLine, statement(assignmentStmt),
1202         maybe(Parser<OmpEndAtomic>{} / endOmpLine))))
1203 
1204 // OMP ATOMIC [atomic-clause-list]
1205 TYPE_PARSER(sourced(construct<OmpAtomic>(verbatim("ATOMIC"_tok),
1206     Parser<OmpAtomicClauseList>{} / endOmpLine, statement(assignmentStmt),
1207     maybe(Parser<OmpEndAtomic>{} / endOmpLine))))
1208 
1209 // OMP ATOMIC [MEMORY-ORDER-CLAUSE-LIST] WRITE [MEMORY-ORDER-CLAUSE-LIST]
1210 TYPE_PARSER("ATOMIC" >>
1211     sourced(construct<OmpAtomicWrite>(
1212         Parser<OmpAtomicClauseList>{} / maybe(","_tok), verbatim("WRITE"_tok),
1213         Parser<OmpAtomicClauseList>{} / endOmpLine, statement(assignmentStmt),
1214         maybe(Parser<OmpEndAtomic>{} / endOmpLine))))
1215 
1216 // Atomic Construct
1217 TYPE_PARSER(construct<OpenMPAtomicConstruct>(Parser<OmpAtomicRead>{}) ||
1218     construct<OpenMPAtomicConstruct>(Parser<OmpAtomicCapture>{}) ||
1219     construct<OpenMPAtomicConstruct>(Parser<OmpAtomicCompare>{}) ||
1220     construct<OpenMPAtomicConstruct>(Parser<OmpAtomicWrite>{}) ||
1221     construct<OpenMPAtomicConstruct>(Parser<OmpAtomicUpdate>{}) ||
1222     construct<OpenMPAtomicConstruct>(Parser<OmpAtomic>{}))
1223 
1224 // 2.13.2 OMP CRITICAL
1225 TYPE_PARSER(startOmpLine >>
1226     sourced(construct<OmpEndCriticalDirective>(
1227         verbatim("END CRITICAL"_tok), maybe(parenthesized(name)))) /
1228         endOmpLine)
1229 TYPE_PARSER(sourced(construct<OmpCriticalDirective>(verbatim("CRITICAL"_tok),
1230                 maybe(parenthesized(name)), Parser<OmpClauseList>{})) /
1231     endOmpLine)
1232 
1233 TYPE_PARSER(construct<OpenMPCriticalConstruct>(
1234     Parser<OmpCriticalDirective>{}, block, Parser<OmpEndCriticalDirective>{}))
1235 
1236 TYPE_PARSER(sourced(construct<OmpDispatchDirective>(
1237     verbatim("DISPATCH"_tok), Parser<OmpClauseList>{})))
1238 
1239 TYPE_PARSER(
1240     construct<OmpEndDispatchDirective>(startOmpLine >> "END DISPATCH"_tok))
1241 
1242 TYPE_PARSER(sourced(construct<OpenMPDispatchConstruct>(
1243     Parser<OmpDispatchDirective>{} / endOmpLine, block,
1244     maybe(Parser<OmpEndDispatchDirective>{} / endOmpLine))))
1245 
1246 // 2.11.3 Executable Allocate directive
1247 TYPE_PARSER(
1248     sourced(construct<OpenMPExecutableAllocate>(verbatim("ALLOCATE"_tok),
1249         maybe(parenthesized(Parser<OmpObjectList>{})), Parser<OmpClauseList>{},
1250         maybe(nonemptyList(Parser<OpenMPDeclarativeAllocate>{})) / endOmpLine,
1251         statement(allocateStmt))))
1252 
1253 // 6.7 Allocators construct [OpenMP 5.2]
1254 //     allocators-construct -> ALLOCATORS [allocate-clause [,]]
1255 //                                allocate-stmt
1256 //                             [omp-end-allocators-construct]
1257 TYPE_PARSER(sourced(construct<OpenMPAllocatorsConstruct>(
1258     verbatim("ALLOCATORS"_tok), Parser<OmpClauseList>{} / endOmpLine,
1259     statement(allocateStmt), maybe(Parser<OmpEndAllocators>{} / endOmpLine))))
1260 
1261 TYPE_PARSER(construct<OmpEndAllocators>(startOmpLine >> "END ALLOCATORS"_tok))
1262 
1263 // 2.8.2 Declare Simd construct
1264 TYPE_PARSER(
1265     sourced(construct<OpenMPDeclareSimdConstruct>(verbatim("DECLARE SIMD"_tok),
1266         maybe(parenthesized(name)), Parser<OmpClauseList>{})))
1267 
1268 // 2.4 Requires construct
1269 TYPE_PARSER(sourced(construct<OpenMPRequiresConstruct>(
1270     verbatim("REQUIRES"_tok), Parser<OmpClauseList>{})))
1271 
1272 // 2.15.2 Threadprivate directive
1273 TYPE_PARSER(sourced(construct<OpenMPThreadprivate>(
1274     verbatim("THREADPRIVATE"_tok), parenthesized(Parser<OmpObjectList>{}))))
1275 
1276 // 2.11.3 Declarative Allocate directive
1277 TYPE_PARSER(
1278     sourced(construct<OpenMPDeclarativeAllocate>(verbatim("ALLOCATE"_tok),
1279         parenthesized(Parser<OmpObjectList>{}), Parser<OmpClauseList>{})) /
1280     lookAhead(endOmpLine / !statement(allocateStmt)))
1281 
1282 // Declarative constructs
1283 TYPE_PARSER(startOmpLine >>
1284     withMessage("expected OpenMP construct"_err_en_US,
1285         sourced(construct<OpenMPDeclarativeConstruct>(
1286                     Parser<OpenMPDeclareReductionConstruct>{}) ||
1287             construct<OpenMPDeclarativeConstruct>(
1288                 Parser<OpenMPDeclareMapperConstruct>{}) ||
1289             construct<OpenMPDeclarativeConstruct>(
1290                 Parser<OpenMPDeclareSimdConstruct>{}) ||
1291             construct<OpenMPDeclarativeConstruct>(
1292                 Parser<OpenMPDeclareTargetConstruct>{}) ||
1293             construct<OpenMPDeclarativeConstruct>(
1294                 Parser<OpenMPDeclarativeAllocate>{}) ||
1295             construct<OpenMPDeclarativeConstruct>(
1296                 Parser<OpenMPRequiresConstruct>{}) ||
1297             construct<OpenMPDeclarativeConstruct>(
1298                 Parser<OpenMPThreadprivate>{}) ||
1299             construct<OpenMPDeclarativeConstruct>(
1300                 Parser<OpenMPUtilityConstruct>{})) /
1301             endOmpLine))
1302 
1303 // Block Construct
1304 TYPE_PARSER(construct<OpenMPBlockConstruct>(
1305     Parser<OmpBeginBlockDirective>{} / endOmpLine, block,
1306     Parser<OmpEndBlockDirective>{} / endOmpLine))
1307 
1308 // OMP SECTIONS Directive
1309 TYPE_PARSER(construct<OmpSectionsDirective>(first(
1310     "SECTIONS" >> pure(llvm::omp::Directive::OMPD_sections),
1311     "PARALLEL SECTIONS" >> pure(llvm::omp::Directive::OMPD_parallel_sections))))
1312 
1313 // OMP BEGIN and END SECTIONS Directive
1314 TYPE_PARSER(sourced(construct<OmpBeginSectionsDirective>(
1315     sourced(Parser<OmpSectionsDirective>{}), Parser<OmpClauseList>{})))
1316 TYPE_PARSER(
1317     startOmpLine >> sourced(construct<OmpEndSectionsDirective>(
1318                         sourced("END"_tok >> Parser<OmpSectionsDirective>{}),
1319                         Parser<OmpClauseList>{})))
1320 
1321 // OMP SECTION-BLOCK
1322 
1323 TYPE_PARSER(construct<OpenMPSectionConstruct>(block))
1324 
1325 TYPE_PARSER(maybe(startOmpLine >> "SECTION"_tok / endOmpLine) >>
1326     construct<OmpSectionBlocks>(nonemptySeparated(
1327         construct<OpenMPConstruct>(sourced(Parser<OpenMPSectionConstruct>{})),
1328         startOmpLine >> "SECTION"_tok / endOmpLine)))
1329 
1330 // OMP SECTIONS (OpenMP 5.0 - 2.8.1), PARALLEL SECTIONS (OpenMP 5.0 - 2.13.3)
1331 TYPE_PARSER(construct<OpenMPSectionsConstruct>(
1332     Parser<OmpBeginSectionsDirective>{} / endOmpLine,
1333     Parser<OmpSectionBlocks>{}, Parser<OmpEndSectionsDirective>{} / endOmpLine))
1334 
1335 TYPE_CONTEXT_PARSER("OpenMP construct"_en_US,
1336     startOmpLine >>
1337         withMessage("expected OpenMP construct"_err_en_US,
1338             first(construct<OpenMPConstruct>(Parser<OpenMPSectionsConstruct>{}),
1339                 construct<OpenMPConstruct>(Parser<OpenMPLoopConstruct>{}),
1340                 construct<OpenMPConstruct>(Parser<OpenMPBlockConstruct>{}),
1341                 // OpenMPBlockConstruct is attempted before
1342                 // OpenMPStandaloneConstruct to resolve !$OMP ORDERED
1343                 construct<OpenMPConstruct>(Parser<OpenMPStandaloneConstruct>{}),
1344                 construct<OpenMPConstruct>(Parser<OpenMPAtomicConstruct>{}),
1345                 construct<OpenMPConstruct>(Parser<OpenMPUtilityConstruct>{}),
1346                 construct<OpenMPConstruct>(Parser<OpenMPDispatchConstruct>{}),
1347                 construct<OpenMPConstruct>(Parser<OpenMPExecutableAllocate>{}),
1348                 construct<OpenMPConstruct>(Parser<OpenMPAllocatorsConstruct>{}),
1349                 construct<OpenMPConstruct>(Parser<OpenMPDeclarativeAllocate>{}),
1350                 construct<OpenMPConstruct>(Parser<OpenMPCriticalConstruct>{}))))
1351 
1352 // END OMP Block directives
1353 TYPE_PARSER(
1354     startOmpLine >> sourced(construct<OmpEndBlockDirective>(
1355                         sourced("END"_tok >> Parser<OmpBlockDirective>{}),
1356                         Parser<OmpClauseList>{})))
1357 
1358 // END OMP Loop directives
1359 TYPE_PARSER(
1360     startOmpLine >> sourced(construct<OmpEndLoopDirective>(
1361                         sourced("END"_tok >> Parser<OmpLoopDirective>{}),
1362                         Parser<OmpClauseList>{})))
1363 
1364 TYPE_PARSER(construct<OpenMPLoopConstruct>(
1365     Parser<OmpBeginLoopDirective>{} / endOmpLine))
1366 } // namespace Fortran::parser
1367