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