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