1 //===-- lib/Parser/expr-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 // Per-type parsers for expressions.
10 
11 #include "expr-parsers.h"
12 #include "basic-parsers.h"
13 #include "debug-parser.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/characters.h"
19 #include "flang/Parser/parse-tree.h"
20 
21 namespace Fortran::parser {
22 
23 // R764 boz-literal-constant -> binary-constant | octal-constant | hex-constant
24 // R765 binary-constant -> B ' digit [digit]... ' | B " digit [digit]... "
25 // R766 octal-constant -> O ' digit [digit]... ' | O " digit [digit]... "
26 // R767 hex-constant ->
27 //        Z ' hex-digit [hex-digit]... ' | Z " hex-digit [hex-digit]... "
28 // extension: X accepted for Z
29 // extension: BOZX suffix accepted
30 TYPE_PARSER(construct<BOZLiteralConstant>(BOZLiteral{}))
31 
32 // R769 array-constructor -> (/ ac-spec /) | lbracket ac-spec rbracket
33 TYPE_CONTEXT_PARSER("array constructor"_en_US,
34     construct<ArrayConstructor>(
35         "(/" >> Parser<AcSpec>{} / "/)" || bracketed(Parser<AcSpec>{})))
36 
37 // R770 ac-spec -> type-spec :: | [type-spec ::] ac-value-list
38 TYPE_PARSER(construct<AcSpec>(maybe(typeSpec / "::"),
39                 nonemptyList("expected array constructor values"_err_en_US,
40                     Parser<AcValue>{})) ||
41     construct<AcSpec>(typeSpec / "::"))
42 
43 // R773 ac-value -> expr | ac-implied-do
44 TYPE_PARSER(
45     // PGI/Intel extension: accept triplets in array constructors
46     extension<LanguageFeature::TripletInArrayConstructor>(
47         "nonstandard usage: triplet in array constructor"_port_en_US,
48         construct<AcValue>(construct<AcValue::Triplet>(scalarIntExpr,
49             ":" >> scalarIntExpr, maybe(":" >> scalarIntExpr)))) ||
50     construct<AcValue>(indirect(expr)) ||
51     construct<AcValue>(indirect(Parser<AcImpliedDo>{})))
52 
53 // R774 ac-implied-do -> ( ac-value-list , ac-implied-do-control )
54 TYPE_PARSER(parenthesized(
55     construct<AcImpliedDo>(nonemptyList(Parser<AcValue>{} / lookAhead(","_tok)),
56         "," >> Parser<AcImpliedDoControl>{})))
57 
58 // R775 ac-implied-do-control ->
59 //        [integer-type-spec ::] ac-do-variable = scalar-int-expr ,
60 //        scalar-int-expr [, scalar-int-expr]
61 // R776 ac-do-variable -> do-variable
62 TYPE_PARSER(construct<AcImpliedDoControl>(
63     maybe(integerTypeSpec / "::"), loopBounds(scalarIntExpr)))
64 
65 // R1001 primary ->
66 //         literal-constant | designator | array-constructor |
67 //         structure-constructor | function-reference | type-param-inquiry |
68 //         type-param-name | ( expr )
69 // type-param-inquiry is parsed as a structure component, except for
70 // substring%KIND/LEN
71 constexpr auto primary{instrumented("primary"_en_US,
72     first(construct<Expr>(indirect(Parser<CharLiteralConstantSubstring>{})),
73         construct<Expr>(literalConstant),
74         construct<Expr>(construct<Expr::Parentheses>(parenthesized(expr))),
75         construct<Expr>(indirect(functionReference) / !"("_tok / !"%"_tok),
76         construct<Expr>(designator / !"("_tok / !"%"_tok),
77         construct<Expr>(indirect(Parser<SubstringInquiry>{})), // %LEN or %KIND
78         construct<Expr>(Parser<StructureConstructor>{}),
79         construct<Expr>(Parser<ArrayConstructor>{}),
80         // PGI/XLF extension: COMPLEX constructor (x,y)
81         extension<LanguageFeature::ComplexConstructor>(
82             "nonstandard usage: generalized COMPLEX constructor"_port_en_US,
83             construct<Expr>(parenthesized(
84                 construct<Expr::ComplexConstructor>(expr, "," >> expr)))),
85         extension<LanguageFeature::PercentLOC>(
86             "nonstandard usage: %LOC"_port_en_US,
87             construct<Expr>("%LOC" >> parenthesized(construct<Expr::PercentLoc>(
88                                           indirect(variable)))))))};
89 
90 // R1002 level-1-expr -> [defined-unary-op] primary
91 // TODO: Reasonable extension: permit multiple defined-unary-ops
92 constexpr auto level1Expr{sourced(
93     first(primary, // must come before define op to resolve .TRUE._8 ambiguity
94         construct<Expr>(construct<Expr::DefinedUnary>(definedOpName, primary)),
95         extension<LanguageFeature::SignedPrimary>(
96             "nonstandard usage: signed primary"_port_en_US,
97             construct<Expr>(construct<Expr::UnaryPlus>("+" >> primary))),
98         extension<LanguageFeature::SignedPrimary>(
99             "nonstandard usage: signed primary"_port_en_US,
100             construct<Expr>(construct<Expr::Negate>("-" >> primary)))))};
101 
102 // R1004 mult-operand -> level-1-expr [power-op mult-operand]
103 // R1007 power-op -> **
104 // Exponentiation (**) is Fortran's only right-associative binary operation.
105 struct MultOperand {
106   using resultType = Expr;
MultOperandFortran::parser::MultOperand107   constexpr MultOperand() {}
108   static inline std::optional<Expr> Parse(ParseState &);
109 };
110 
111 static constexpr auto multOperand{sourced(MultOperand{})};
112 
Parse(ParseState & state)113 inline std::optional<Expr> MultOperand::Parse(ParseState &state) {
114   std::optional<Expr> result{level1Expr.Parse(state)};
115   if (result) {
116     static constexpr auto op{attempt("**"_tok)};
117     if (op.Parse(state)) {
118       std::function<Expr(Expr &&)> power{[&result](Expr &&right) {
119         return Expr{Expr::Power(std::move(result).value(), std::move(right))};
120       }};
121       return applyLambda(power, multOperand).Parse(state); // right-recursive
122     }
123   }
124   return result;
125 }
126 
127 // R1005 add-operand -> [add-operand mult-op] mult-operand
128 // R1008 mult-op -> * | /
129 // The left recursion in the grammar is implemented iteratively.
130 struct AddOperand {
131   using resultType = Expr;
AddOperandFortran::parser::AddOperand132   constexpr AddOperand() {}
ParseFortran::parser::AddOperand133   static inline std::optional<Expr> Parse(ParseState &state) {
134     std::optional<Expr> result{multOperand.Parse(state)};
135     if (result) {
136       auto source{result->source};
137       std::function<Expr(Expr &&)> multiply{[&result](Expr &&right) {
138         return Expr{
139             Expr::Multiply(std::move(result).value(), std::move(right))};
140       }};
141       std::function<Expr(Expr &&)> divide{[&result](Expr &&right) {
142         return Expr{Expr::Divide(std::move(result).value(), std::move(right))};
143       }};
144       auto more{attempt(sourced("*" >> applyLambda(multiply, multOperand) ||
145           "/" >> applyLambda(divide, multOperand)))};
146       while (std::optional<Expr> next{more.Parse(state)}) {
147         result = std::move(next);
148         result->source.ExtendToCover(source);
149       }
150     }
151     return result;
152   }
153 };
154 constexpr AddOperand addOperand;
155 
156 // R1006 level-2-expr -> [[level-2-expr] add-op] add-operand
157 // R1009 add-op -> + | -
158 // These are left-recursive productions, implemented iteratively.
159 // Note that standard Fortran admits a unary + or - to appear only here,
160 // by means of a missing first operand; e.g., 2*-3 is valid in C but not
161 // standard Fortran.  We accept unary + and - to appear before any primary
162 // as an extension.
163 struct Level2Expr {
164   using resultType = Expr;
Level2ExprFortran::parser::Level2Expr165   constexpr Level2Expr() {}
ParseFortran::parser::Level2Expr166   static inline std::optional<Expr> Parse(ParseState &state) {
167     static constexpr auto unary{
168         sourced(
169             construct<Expr>(construct<Expr::UnaryPlus>("+" >> addOperand)) ||
170             construct<Expr>(construct<Expr::Negate>("-" >> addOperand))) ||
171         addOperand};
172     std::optional<Expr> result{unary.Parse(state)};
173     if (result) {
174       auto source{result->source};
175       std::function<Expr(Expr &&)> add{[&result](Expr &&right) {
176         return Expr{Expr::Add(std::move(result).value(), std::move(right))};
177       }};
178       std::function<Expr(Expr &&)> subtract{[&result](Expr &&right) {
179         return Expr{
180             Expr::Subtract(std::move(result).value(), std::move(right))};
181       }};
182       auto more{attempt(sourced("+" >> applyLambda(add, addOperand) ||
183           "-" >> applyLambda(subtract, addOperand)))};
184       while (std::optional<Expr> next{more.Parse(state)}) {
185         result = std::move(next);
186         result->source.ExtendToCover(source);
187       }
188     }
189     return result;
190   }
191 };
192 constexpr Level2Expr level2Expr;
193 
194 // R1010 level-3-expr -> [level-3-expr concat-op] level-2-expr
195 // R1011 concat-op -> //
196 // Concatenation (//) is left-associative for parsing performance, although
197 // one would never notice if it were right-associated.
198 struct Level3Expr {
199   using resultType = Expr;
Level3ExprFortran::parser::Level3Expr200   constexpr Level3Expr() {}
ParseFortran::parser::Level3Expr201   static inline std::optional<Expr> Parse(ParseState &state) {
202     std::optional<Expr> result{level2Expr.Parse(state)};
203     if (result) {
204       auto source{result->source};
205       std::function<Expr(Expr &&)> concat{[&result](Expr &&right) {
206         return Expr{Expr::Concat(std::move(result).value(), std::move(right))};
207       }};
208       auto more{attempt(sourced("//" >> applyLambda(concat, level2Expr)))};
209       while (std::optional<Expr> next{more.Parse(state)}) {
210         result = std::move(next);
211         result->source.ExtendToCover(source);
212       }
213     }
214     return result;
215   }
216 };
217 constexpr Level3Expr level3Expr;
218 
219 // R1012 level-4-expr -> [level-3-expr rel-op] level-3-expr
220 // R1013 rel-op ->
221 //         .EQ. | .NE. | .LT. | .LE. | .GT. | .GE. |
222 //          == | /= | < | <= | > | >=  @ | <>
223 // N.B. relations are not recursive (i.e., LOGICAL is not ordered)
224 struct Level4Expr {
225   using resultType = Expr;
Level4ExprFortran::parser::Level4Expr226   constexpr Level4Expr() {}
ParseFortran::parser::Level4Expr227   static inline std::optional<Expr> Parse(ParseState &state) {
228     std::optional<Expr> result{level3Expr.Parse(state)};
229     if (result) {
230       auto source{result->source};
231       std::function<Expr(Expr &&)> lt{[&result](Expr &&right) {
232         return Expr{Expr::LT(std::move(result).value(), std::move(right))};
233       }};
234       std::function<Expr(Expr &&)> le{[&result](Expr &&right) {
235         return Expr{Expr::LE(std::move(result).value(), std::move(right))};
236       }};
237       std::function<Expr(Expr &&)> eq{[&result](Expr &&right) {
238         return Expr{Expr::EQ(std::move(result).value(), std::move(right))};
239       }};
240       std::function<Expr(Expr &&)> ne{[&result](Expr &&right) {
241         return Expr{Expr::NE(std::move(result).value(), std::move(right))};
242       }};
243       std::function<Expr(Expr &&)> ge{[&result](Expr &&right) {
244         return Expr{Expr::GE(std::move(result).value(), std::move(right))};
245       }};
246       std::function<Expr(Expr &&)> gt{[&result](Expr &&right) {
247         return Expr{Expr::GT(std::move(result).value(), std::move(right))};
248       }};
249       auto more{attempt(
250           sourced((".LT."_tok || "<"_tok) >> applyLambda(lt, level3Expr) ||
251               (".LE."_tok || "<="_tok) >> applyLambda(le, level3Expr) ||
252               (".EQ."_tok || "=="_tok) >> applyLambda(eq, level3Expr) ||
253               (".NE."_tok || "/="_tok ||
254                   extension<LanguageFeature::AlternativeNE>(
255                       "nonstandard usage: <> for /= or .NE."_port_en_US,
256                       "<>"_tok /* PGI/Cray extension; Cray also has .LG. */)) >>
257                   applyLambda(ne, level3Expr) ||
258               (".GE."_tok || ">="_tok) >> applyLambda(ge, level3Expr) ||
259               (".GT."_tok || ">"_tok) >> applyLambda(gt, level3Expr)))};
260       if (std::optional<Expr> next{more.Parse(state)}) {
261         next->source.ExtendToCover(source);
262         return next;
263       }
264     }
265     return result;
266   }
267 };
268 constexpr Level4Expr level4Expr;
269 
270 // R1014 and-operand -> [not-op] level-4-expr
271 // R1018 not-op -> .NOT.
272 // N.B. Fortran's .NOT. binds less tightly than its comparison operators do.
273 // PGI/Intel extension: accept multiple .NOT. operators
274 struct AndOperand {
275   using resultType = Expr;
AndOperandFortran::parser::AndOperand276   constexpr AndOperand() {}
277   static inline std::optional<Expr> Parse(ParseState &);
278 };
279 constexpr AndOperand andOperand;
280 
281 // Match a logical operator or, optionally, its abbreviation.
logicalOp(const char * op,const char * abbrev)282 inline constexpr auto logicalOp(const char *op, const char *abbrev) {
283   return TokenStringMatch{op} ||
284       extension<LanguageFeature::LogicalAbbreviations>(
285           "nonstandard usage: abbreviated LOGICAL operator"_port_en_US,
286           TokenStringMatch{abbrev});
287 }
288 
Parse(ParseState & state)289 inline std::optional<Expr> AndOperand::Parse(ParseState &state) {
290   static constexpr auto notOp{attempt(logicalOp(".NOT.", ".N.") >> andOperand)};
291   if (std::optional<Expr> negation{notOp.Parse(state)}) {
292     return Expr{Expr::NOT{std::move(*negation)}};
293   } else {
294     return level4Expr.Parse(state);
295   }
296 }
297 
298 // R1015 or-operand -> [or-operand and-op] and-operand
299 // R1019 and-op -> .AND.
300 // .AND. is left-associative
301 struct OrOperand {
302   using resultType = Expr;
OrOperandFortran::parser::OrOperand303   constexpr OrOperand() {}
ParseFortran::parser::OrOperand304   static inline std::optional<Expr> Parse(ParseState &state) {
305     static constexpr auto operand{sourced(andOperand)};
306     std::optional<Expr> result{operand.Parse(state)};
307     if (result) {
308       auto source{result->source};
309       std::function<Expr(Expr &&)> logicalAnd{[&result](Expr &&right) {
310         return Expr{Expr::AND(std::move(result).value(), std::move(right))};
311       }};
312       auto more{attempt(sourced(
313           logicalOp(".AND.", ".A.") >> applyLambda(logicalAnd, andOperand)))};
314       while (std::optional<Expr> next{more.Parse(state)}) {
315         result = std::move(next);
316         result->source.ExtendToCover(source);
317       }
318     }
319     return result;
320   }
321 };
322 constexpr OrOperand orOperand;
323 
324 // R1016 equiv-operand -> [equiv-operand or-op] or-operand
325 // R1020 or-op -> .OR.
326 // .OR. is left-associative
327 struct EquivOperand {
328   using resultType = Expr;
EquivOperandFortran::parser::EquivOperand329   constexpr EquivOperand() {}
ParseFortran::parser::EquivOperand330   static inline std::optional<Expr> Parse(ParseState &state) {
331     std::optional<Expr> result{orOperand.Parse(state)};
332     if (result) {
333       auto source{result->source};
334       std::function<Expr(Expr &&)> logicalOr{[&result](Expr &&right) {
335         return Expr{Expr::OR(std::move(result).value(), std::move(right))};
336       }};
337       auto more{attempt(sourced(
338           logicalOp(".OR.", ".O.") >> applyLambda(logicalOr, orOperand)))};
339       while (std::optional<Expr> next{more.Parse(state)}) {
340         result = std::move(next);
341         result->source.ExtendToCover(source);
342       }
343     }
344     return result;
345   }
346 };
347 constexpr EquivOperand equivOperand;
348 
349 // R1017 level-5-expr -> [level-5-expr equiv-op] equiv-operand
350 // R1021 equiv-op -> .EQV. | .NEQV.
351 // Logical equivalence is left-associative.
352 // Extension: .XOR. as synonym for .NEQV.
353 struct Level5Expr {
354   using resultType = Expr;
Level5ExprFortran::parser::Level5Expr355   constexpr Level5Expr() {}
ParseFortran::parser::Level5Expr356   static inline std::optional<Expr> Parse(ParseState &state) {
357     std::optional<Expr> result{equivOperand.Parse(state)};
358     if (result) {
359       auto source{result->source};
360       std::function<Expr(Expr &&)> eqv{[&result](Expr &&right) {
361         return Expr{Expr::EQV(std::move(result).value(), std::move(right))};
362       }};
363       std::function<Expr(Expr &&)> neqv{[&result](Expr &&right) {
364         return Expr{Expr::NEQV(std::move(result).value(), std::move(right))};
365       }};
366       auto more{attempt(sourced(".EQV." >> applyLambda(eqv, equivOperand) ||
367           (".NEQV."_tok ||
368               extension<LanguageFeature::XOROperator>(
369                   "nonstandard usage: .XOR./.X. spelling of .NEQV."_port_en_US,
370                   logicalOp(".XOR.", ".X."))) >>
371               applyLambda(neqv, equivOperand)))};
372       while (std::optional<Expr> next{more.Parse(state)}) {
373         result = std::move(next);
374         result->source.ExtendToCover(source);
375       }
376     }
377     return result;
378   }
379 };
380 constexpr Level5Expr level5Expr;
381 
382 // R1022 expr -> [expr defined-binary-op] level-5-expr
383 // Defined binary operators associate leftwards.
Parse(ParseState & state)384 template <> std::optional<Expr> Parser<Expr>::Parse(ParseState &state) {
385   std::optional<Expr> result{level5Expr.Parse(state)};
386   if (result) {
387     auto source{result->source};
388     std::function<Expr(DefinedOpName &&, Expr &&)> defBinOp{
389         [&result](DefinedOpName &&op, Expr &&right) {
390           return Expr{Expr::DefinedBinary(
391               std::move(op), std::move(result).value(), std::move(right))};
392         }};
393     auto more{attempt(
394         sourced(applyLambda<Expr>(defBinOp, definedOpName, level5Expr)))};
395     while (std::optional<Expr> next{more.Parse(state)}) {
396       result = std::move(next);
397       result->source.ExtendToCover(source);
398     }
399   }
400   return result;
401 }
402 
403 // R1003 defined-unary-op -> . letter [letter]... .
404 // R1023 defined-binary-op -> . letter [letter]... .
405 // R1414 local-defined-operator -> defined-unary-op | defined-binary-op
406 // R1415 use-defined-operator -> defined-unary-op | defined-binary-op
407 // C1003 A defined operator must be distinct from logical literal constants
408 // and intrinsic operator names; this is handled by attempting their parses
409 // first, and by name resolution on their definitions, for best errors.
410 // N.B. The name of the operator is captured with the dots around it.
411 constexpr auto definedOpNameChar{letter ||
412     extension<LanguageFeature::PunctuationInNames>(
413         "nonstandard usage: non-alphabetic character in defined operator"_port_en_US,
414         "$@"_ch)};
415 TYPE_PARSER(
416     space >> construct<DefinedOpName>(sourced("."_ch >>
417                  some(definedOpNameChar) >> construct<Name>() / "."_ch)))
418 
419 // R1028 specification-expr -> scalar-int-expr
420 TYPE_PARSER(construct<SpecificationExpr>(scalarIntExpr))
421 
422 // R1032 assignment-stmt -> variable = expr
423 TYPE_CONTEXT_PARSER("assignment statement"_en_US,
424     construct<AssignmentStmt>(variable / "=", expr))
425 
426 // R1033 pointer-assignment-stmt ->
427 //         data-pointer-object [( bounds-spec-list )] => data-target |
428 //         data-pointer-object ( bounds-remapping-list ) => data-target |
429 //         proc-pointer-object => proc-target
430 // R1034 data-pointer-object ->
431 //         variable-name | scalar-variable % data-pointer-component-name
432 //   C1022 a scalar-variable shall be a data-ref
433 //   C1024 a data-pointer-object shall not be a coindexed object
434 // R1038 proc-pointer-object -> proc-pointer-name | proc-component-ref
435 //
436 // A distinction can't be made at the time of the initial parse between
437 // data-pointer-object and proc-pointer-object, or between data-target
438 // and proc-target.
439 TYPE_CONTEXT_PARSER("pointer assignment statement"_en_US,
440     construct<PointerAssignmentStmt>(dataRef,
441         parenthesized(nonemptyList(Parser<BoundsRemapping>{})), "=>" >> expr) ||
442         construct<PointerAssignmentStmt>(dataRef,
443             defaulted(parenthesized(nonemptyList(Parser<BoundsSpec>{}))),
444             "=>" >> expr))
445 
446 // R1035 bounds-spec -> lower-bound-expr :
447 TYPE_PARSER(construct<BoundsSpec>(boundExpr / ":"))
448 
449 // R1036 bounds-remapping -> lower-bound-expr : upper-bound-expr
450 TYPE_PARSER(construct<BoundsRemapping>(boundExpr / ":", boundExpr))
451 
452 // R1039 proc-component-ref -> scalar-variable % procedure-component-name
453 //   C1027 the scalar-variable must be a data-ref without coindices.
454 TYPE_PARSER(construct<ProcComponentRef>(structureComponent))
455 
456 // R1041 where-stmt -> WHERE ( mask-expr ) where-assignment-stmt
457 // R1045 where-assignment-stmt -> assignment-stmt
458 // R1046 mask-expr -> logical-expr
459 TYPE_CONTEXT_PARSER("WHERE statement"_en_US,
460     construct<WhereStmt>("WHERE" >> parenthesized(logicalExpr), assignmentStmt))
461 
462 // R1042 where-construct ->
463 //         where-construct-stmt [where-body-construct]...
464 //         [masked-elsewhere-stmt [where-body-construct]...]...
465 //         [elsewhere-stmt [where-body-construct]...] end-where-stmt
466 TYPE_CONTEXT_PARSER("WHERE construct"_en_US,
467     construct<WhereConstruct>(statement(Parser<WhereConstructStmt>{}),
468         many(whereBodyConstruct),
469         many(construct<WhereConstruct::MaskedElsewhere>(
470             statement(Parser<MaskedElsewhereStmt>{}),
471             many(whereBodyConstruct))),
472         maybe(construct<WhereConstruct::Elsewhere>(
473             statement(Parser<ElsewhereStmt>{}), many(whereBodyConstruct))),
474         statement(Parser<EndWhereStmt>{})))
475 
476 // R1043 where-construct-stmt -> [where-construct-name :] WHERE ( mask-expr )
477 TYPE_CONTEXT_PARSER("WHERE construct statement"_en_US,
478     construct<WhereConstructStmt>(
479         maybe(name / ":"), "WHERE" >> parenthesized(logicalExpr)))
480 
481 // R1044 where-body-construct ->
482 //         where-assignment-stmt | where-stmt | where-construct
483 TYPE_PARSER(construct<WhereBodyConstruct>(statement(assignmentStmt)) ||
484     construct<WhereBodyConstruct>(statement(whereStmt)) ||
485     construct<WhereBodyConstruct>(indirect(whereConstruct)))
486 
487 // R1047 masked-elsewhere-stmt ->
488 //         ELSEWHERE ( mask-expr ) [where-construct-name]
489 TYPE_CONTEXT_PARSER("masked ELSEWHERE statement"_en_US,
490     construct<MaskedElsewhereStmt>(
491         "ELSE WHERE" >> parenthesized(logicalExpr), maybe(name)))
492 
493 // R1048 elsewhere-stmt -> ELSEWHERE [where-construct-name]
494 TYPE_CONTEXT_PARSER("ELSEWHERE statement"_en_US,
495     construct<ElsewhereStmt>("ELSE WHERE" >> maybe(name)))
496 
497 // R1049 end-where-stmt -> ENDWHERE [where-construct-name]
498 TYPE_CONTEXT_PARSER("END WHERE statement"_en_US,
499     construct<EndWhereStmt>(
500         recovery("END WHERE" >> maybe(name), endStmtErrorRecovery)))
501 
502 // R1050 forall-construct ->
503 //         forall-construct-stmt [forall-body-construct]... end-forall-stmt
504 TYPE_CONTEXT_PARSER("FORALL construct"_en_US,
505     construct<ForallConstruct>(statement(Parser<ForallConstructStmt>{}),
506         many(Parser<ForallBodyConstruct>{}),
507         statement(Parser<EndForallStmt>{})))
508 
509 // R1051 forall-construct-stmt ->
510 //         [forall-construct-name :] FORALL concurrent-header
511 TYPE_CONTEXT_PARSER("FORALL construct statement"_en_US,
512     construct<ForallConstructStmt>(
513         maybe(name / ":"), "FORALL" >> indirect(concurrentHeader)))
514 
515 // R1052 forall-body-construct ->
516 //         forall-assignment-stmt | where-stmt | where-construct |
517 //         forall-construct | forall-stmt
518 TYPE_PARSER(construct<ForallBodyConstruct>(statement(forallAssignmentStmt)) ||
519     construct<ForallBodyConstruct>(statement(whereStmt)) ||
520     construct<ForallBodyConstruct>(whereConstruct) ||
521     construct<ForallBodyConstruct>(indirect(forallConstruct)) ||
522     construct<ForallBodyConstruct>(statement(forallStmt)))
523 
524 // R1053 forall-assignment-stmt -> assignment-stmt | pointer-assignment-stmt
525 TYPE_PARSER(construct<ForallAssignmentStmt>(assignmentStmt) ||
526     construct<ForallAssignmentStmt>(pointerAssignmentStmt))
527 
528 // R1054 end-forall-stmt -> END FORALL [forall-construct-name]
529 TYPE_CONTEXT_PARSER("END FORALL statement"_en_US,
530     construct<EndForallStmt>(
531         recovery("END FORALL" >> maybe(name), endStmtErrorRecovery)))
532 
533 // R1055 forall-stmt -> FORALL concurrent-header forall-assignment-stmt
534 TYPE_CONTEXT_PARSER("FORALL statement"_en_US,
535     construct<ForallStmt>("FORALL" >> indirect(concurrentHeader),
536         unlabeledStatement(forallAssignmentStmt)))
537 } // namespace Fortran::parser
538