// Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef FORTRAN_PARSER_GRAMMAR_H_ #define FORTRAN_PARSER_GRAMMAR_H_ // Top-level grammar specification for Fortran. These parsers drive // the tokenization parsers in cooked-tokens.h to consume characters, // recognize the productions of Fortran, and to construct a parse tree. // See ParserCombinators.md for documentation on the parser combinator // library used here to implement an LL recursive descent recognizer. #include "basic-parsers.h" #include "characters.h" #include "debug-parser.h" #include "parse-tree.h" #include "stmt-parser.h" #include "token-parsers.h" #include "type-parsers.h" #include "user-state.h" #include #include #include #include #include #include #include #include namespace Fortran::parser { // The productions that follow are derived from the draft Fortran 2018 // standard, with some necessary modifications to remove left recursion // and some generalization in order to defer cases where parses depend // on the definitions of symbols. The "Rxxx" numbers that appear in // comments refer to these numbered requirements in the Fortran standard. // R507 declaration-construct -> // specification-construct | data-stmt | format-stmt | // entry-stmt | stmt-function-stmt // N.B. These parsers incorporate recognition of some other statements that // may have been misplaced in the sequence of statements that are acceptable // as a specification part in order to improve error recovery. // Also note that many instances of specification-part in the standard grammar // are in contexts that impose constraints on the kinds of statements that // are allowed, and so we have a variant production for declaration-construct // that implements those constraints. constexpr auto execPartLookAhead{ first(actionStmt >> ok, openmpEndLoopDirective >> ok, openmpConstruct >> ok, "ASSOCIATE ("_tok, "BLOCK"_tok, "SELECT"_tok, "CHANGE TEAM"_sptok, "CRITICAL"_tok, "DO"_tok, "IF ("_tok, "WHERE ("_tok, "FORALL ("_tok)}; constexpr auto declErrorRecovery{ stmtErrorRecoveryStart >> !execPartLookAhead >> stmtErrorRecovery}; constexpr auto misplacedSpecificationStmt{Parser{} >> fail("misplaced USE statement"_err_en_US) || Parser{} >> fail( "IMPORT statements must follow any USE statements and precede all other declarations"_err_en_US) || Parser{} >> fail( "IMPLICIT statements must follow USE and IMPORT and precede all other declarations"_err_en_US)}; TYPE_PARSER(recovery( withMessage("expected declaration construct"_err_en_US, CONTEXT_PARSER("declaration construct"_en_US, first(construct(specificationConstruct), construct(statement(indirect(dataStmt))), construct( statement(indirect(formatStmt))), construct(statement(indirect(entryStmt))), construct( statement(indirect(Parser{}))), misplacedSpecificationStmt))), construct(declErrorRecovery))) // R507 variant of declaration-construct for use in limitedSpecificationPart. constexpr auto invalidDeclarationStmt{formatStmt >> fail( "FORMAT statements are not permitted in this specification part"_err_en_US) || entryStmt >> fail( "ENTRY statements are not permitted in this specification part"_err_en_US)}; constexpr auto limitedDeclarationConstruct{recovery( withMessage("expected declaration construct"_err_en_US, inContext("declaration construct"_en_US, first(construct(specificationConstruct), construct(statement(indirect(dataStmt))), misplacedSpecificationStmt, invalidDeclarationStmt))), construct( stmtErrorRecoveryStart >> stmtErrorRecovery))}; // R508 specification-construct -> // derived-type-def | enum-def | generic-stmt | interface-block | // parameter-stmt | procedure-declaration-stmt | // other-specification-stmt | type-declaration-stmt TYPE_CONTEXT_PARSER("specification construct"_en_US, first(construct(indirect(Parser{})), construct(indirect(Parser{})), construct( statement(indirect(Parser{}))), construct(indirect(interfaceBlock)), construct(statement(indirect(parameterStmt))), construct( statement(indirect(oldParameterStmt))), construct( statement(indirect(Parser{}))), construct( statement(Parser{})), construct( statement(indirect(typeDeclarationStmt))), construct(indirect(Parser{})), construct(indirect(openmpDeclarativeConstruct)), construct(indirect(compilerDirective)))) // R513 other-specification-stmt -> // access-stmt | allocatable-stmt | asynchronous-stmt | bind-stmt | // codimension-stmt | contiguous-stmt | dimension-stmt | external-stmt | // intent-stmt | intrinsic-stmt | namelist-stmt | optional-stmt | // pointer-stmt | protected-stmt | save-stmt | target-stmt | // volatile-stmt | value-stmt | common-stmt | equivalence-stmt TYPE_PARSER(first( construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})))) // R604 constant -> literal-constant | named-constant // Used only via R607 int-constant and R845 data-stmt-constant. // The look-ahead check prevents occlusion of constant-subobject in // data-stmt-constant. TYPE_PARSER(construct(literalConstant) || construct(namedConstant / !"%"_tok / !"("_tok)) // R608 intrinsic-operator -> // power-op | mult-op | add-op | concat-op | rel-op | // not-op | and-op | or-op | equiv-op // R610 extended-intrinsic-op -> intrinsic-operator // These parsers must be ordered carefully to avoid misrecognition. constexpr auto namedIntrinsicOperator{ ".LT." >> pure(DefinedOperator::IntrinsicOperator::LT) || ".LE." >> pure(DefinedOperator::IntrinsicOperator::LE) || ".EQ." >> pure(DefinedOperator::IntrinsicOperator::EQ) || ".NE." >> pure(DefinedOperator::IntrinsicOperator::NE) || ".GE." >> pure(DefinedOperator::IntrinsicOperator::GE) || ".GT." >> pure(DefinedOperator::IntrinsicOperator::GT) || ".NOT." >> pure(DefinedOperator::IntrinsicOperator::NOT) || ".AND." >> pure(DefinedOperator::IntrinsicOperator::AND) || ".OR." >> pure(DefinedOperator::IntrinsicOperator::OR) || ".EQV." >> pure(DefinedOperator::IntrinsicOperator::EQV) || ".NEQV." >> pure(DefinedOperator::IntrinsicOperator::NEQV) || extension( ".XOR." >> pure(DefinedOperator::IntrinsicOperator::XOR)) || extension( ".N." >> pure(DefinedOperator::IntrinsicOperator::NOT) || ".A." >> pure(DefinedOperator::IntrinsicOperator::AND) || ".O." >> pure(DefinedOperator::IntrinsicOperator::OR) || ".X." >> pure(DefinedOperator::IntrinsicOperator::XOR))}; constexpr auto intrinsicOperator{ "**" >> pure(DefinedOperator::IntrinsicOperator::Power) || "*" >> pure(DefinedOperator::IntrinsicOperator::Multiply) || "//" >> pure(DefinedOperator::IntrinsicOperator::Concat) || "/=" >> pure(DefinedOperator::IntrinsicOperator::NE) || "/" >> pure(DefinedOperator::IntrinsicOperator::Divide) || "+" >> pure(DefinedOperator::IntrinsicOperator::Add) || "-" >> pure(DefinedOperator::IntrinsicOperator::Subtract) || "<=" >> pure(DefinedOperator::IntrinsicOperator::LE) || extension( "<>" >> pure(DefinedOperator::IntrinsicOperator::NE)) || "<" >> pure(DefinedOperator::IntrinsicOperator::LT) || "==" >> pure(DefinedOperator::IntrinsicOperator::EQ) || ">=" >> pure(DefinedOperator::IntrinsicOperator::GE) || ">" >> pure(DefinedOperator::IntrinsicOperator::GT) || namedIntrinsicOperator}; // R609 defined-operator -> // defined-unary-op | defined-binary-op | extended-intrinsic-op TYPE_PARSER(construct(intrinsicOperator) || construct(definedOpName)) // R401 xzy-list -> xzy [, xzy]... template inline constexpr auto nonemptyList(const PA &p) { return nonemptySeparated(p, ","_tok); // p-list } template inline constexpr auto optionalList(const PA &p) { return defaulted(nonemptySeparated(p, ","_tok)); // [p-list] } // R402 xzy-name -> name // R403 scalar-xyz -> xyz // Also define constant-xyz, int-xyz, default-char-xyz. template inline constexpr auto scalar(const PA &p) { return construct>(p); // scalar-p } template inline constexpr auto constant(const PA &p) { return construct>(p); // constant-p } template inline constexpr auto integer(const PA &p) { return construct>(p); // int-p } template inline constexpr auto logical(const PA &p) { return construct>(p); // logical-p } template inline constexpr auto defaultChar(const PA &p) { return construct>(p); // default-char-p } // R1024 logical-expr -> expr constexpr auto logicalExpr{logical(indirect(expr))}; constexpr auto scalarLogicalExpr{scalar(logicalExpr)}; // R1025 default-char-expr -> expr constexpr auto defaultCharExpr{defaultChar(indirect(expr))}; constexpr auto scalarDefaultCharExpr{scalar(defaultCharExpr)}; // R1026 int-expr -> expr constexpr auto intExpr{integer(indirect(expr))}; constexpr auto scalarIntExpr{scalar(intExpr)}; // R1029 constant-expr -> expr constexpr auto constantExpr{constant(indirect(expr))}; // R1030 default-char-constant-expr -> default-char-expr constexpr auto scalarDefaultCharConstantExpr{scalar(defaultChar(constantExpr))}; // R1031 int-constant-expr -> int-expr constexpr auto intConstantExpr{integer(constantExpr)}; constexpr auto scalarIntConstantExpr{scalar(intConstantExpr)}; // R501 program -> program-unit [program-unit]... // This is the top-level production for the Fortran language. // F'2018 6.3.1 defines a program unit as a sequence of one or more lines, // implying that a line can't be part of two distinct program units. // Consequently, a program unit END statement should be the last statement // on its line. We parse those END statements via unterminatedStatement() // and then skip over the end of the line here. TYPE_PARSER(construct(some(StartNewSubprogram{} >> Parser{} / skipMany(";"_tok) / space / recovery(endOfLine, SkipPast<'\n'>{}))) / skipStuffBeforeStatement) // R502 program-unit -> // main-program | external-subprogram | module | submodule | block-data // R503 external-subprogram -> function-subprogram | subroutine-subprogram TYPE_PARSER(construct(indirect(functionSubprogram)) || construct(indirect(subroutineSubprogram)) || construct(indirect(Parser{})) || construct(indirect(Parser{})) || construct(indirect(Parser{})) || construct(indirect(Parser{}))) // R504 specification-part -> // [use-stmt]... [import-stmt]... [implicit-part] // [declaration-construct]... TYPE_CONTEXT_PARSER("specification part"_en_US, construct(many(openmpDeclarativeConstruct), many(unambiguousStatement(indirect(Parser{}))), many(unambiguousStatement(indirect(Parser{}))), implicitPart, many(declarationConstruct))) // R504 variant for many contexts (modules, submodules, BLOCK DATA subprograms, // and interfaces) which have constraints on their specification parts that // preclude FORMAT, ENTRY, and statement functions, and benefit from // specialized error recovery in the event of a spurious executable // statement. constexpr auto limitedSpecificationPart{inContext("specification part"_en_US, construct(many(openmpDeclarativeConstruct), many(unambiguousStatement(indirect(Parser{}))), many(unambiguousStatement(indirect(Parser{}))), implicitPart, many(limitedDeclarationConstruct)))}; // R505 implicit-part -> [implicit-part-stmt]... implicit-stmt // TODO: Can overshoot; any trailing PARAMETER, FORMAT, & ENTRY // statements after the last IMPLICIT should be transferred to the // list of declaration-constructs. TYPE_CONTEXT_PARSER("implicit part"_en_US, construct(many(Parser{}))) // R506 implicit-part-stmt -> // implicit-stmt | parameter-stmt | format-stmt | entry-stmt TYPE_PARSER(first( construct(statement(indirect(Parser{}))), construct(statement(indirect(parameterStmt))), construct(statement(indirect(oldParameterStmt))), construct(statement(indirect(formatStmt))), construct(statement(indirect(entryStmt))))) // R512 internal-subprogram -> function-subprogram | subroutine-subprogram // Internal subprograms are not program units, so their END statements // can be followed by ';' and another statement on the same line. TYPE_CONTEXT_PARSER("internal subprogram"_en_US, (construct(indirect(functionSubprogram)) || construct(indirect(subroutineSubprogram))) / forceEndOfStmt) // R511 internal-subprogram-part -> contains-stmt [internal-subprogram]... TYPE_CONTEXT_PARSER("internal subprogram part"_en_US, construct(statement(containsStmt), many(StartNewSubprogram{} >> Parser{}))) // R515 action-stmt -> // allocate-stmt | assignment-stmt | backspace-stmt | call-stmt | // close-stmt | continue-stmt | cycle-stmt | deallocate-stmt | // endfile-stmt | error-stop-stmt | event-post-stmt | event-wait-stmt | // exit-stmt | fail-image-stmt | flush-stmt | form-team-stmt | // goto-stmt | if-stmt | inquire-stmt | lock-stmt | nullify-stmt | // open-stmt | pointer-assignment-stmt | print-stmt | read-stmt | // return-stmt | rewind-stmt | stop-stmt | sync-all-stmt | // sync-images-stmt | sync-memory-stmt | sync-team-stmt | unlock-stmt | // wait-stmt | where-stmt | write-stmt | computed-goto-stmt | forall-stmt // R1159 continue-stmt -> CONTINUE // R1163 fail-image-stmt -> FAIL IMAGE TYPE_PARSER(first(construct(indirect(Parser{})), construct(indirect(assignmentStmt)), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(construct("CONTINUE"_tok)), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(construct("FAIL IMAGE"_sptok)), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(pointerAssignmentStmt)), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), // & error-stop-stmt construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(whereStmt)), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(forallStmt)), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})))) // Fortran allows the statement with the corresponding label at the end of // a do-construct that begins with an old-style label-do-stmt to be a // new-style END DO statement; e.g., DO 10 I=1,N; ...; 10 END DO. Usually, // END DO statements appear only at the ends of do-constructs that begin // with a nonlabel-do-stmt, so care must be taken to recognize this case and // essentially treat them like CONTINUE statements. // R514 executable-construct -> // action-stmt | associate-construct | block-construct | // case-construct | change-team-construct | critical-construct | // do-construct | if-construct | select-rank-construct | // select-type-construct | where-construct | forall-construct constexpr auto executableConstruct{ first(construct(CapturedLabelDoStmt{}), construct(EndDoStmtForCapturedLabelDoStmt{}), construct(indirect(Parser{})), // Attempt DO statements before assignment statements for better // error messages in cases like "DO10I=1,(error)". construct(statement(actionStmt)), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(Parser{})), construct(indirect(whereConstruct)), construct(indirect(forallConstruct)), construct(indirect(openmpEndLoopDirective)), construct(indirect(openmpConstruct)), construct(indirect(compilerDirective)))}; // R510 execution-part-construct -> // executable-construct | format-stmt | entry-stmt | data-stmt // Extension (PGI/Intel): also accept NAMELIST in execution part constexpr auto obsoleteExecutionPartConstruct{recovery(ignoredStatementPrefix >> fail( "obsolete legacy extension is not supported"_err_en_US), construct( statement("REDIMENSION" >> name >> parenthesized(nonemptyList(Parser{})) >> ok) >> construct()))}; TYPE_PARSER(recovery( withMessage("expected execution part construct"_err_en_US, CONTEXT_PARSER("execution part construct"_en_US, first(construct(executableConstruct), construct( statement(indirect(formatStmt))), construct( statement(indirect(entryStmt))), construct( statement(indirect(dataStmt))), extension( construct( statement(indirect(Parser{}))) || obsoleteExecutionPartConstruct)))), construct(executionPartErrorRecovery))) // R509 execution-part -> executable-construct [execution-part-construct]... TYPE_CONTEXT_PARSER("execution part"_en_US, construct(many(executionPartConstruct))) // R605 literal-constant -> // int-literal-constant | real-literal-constant | // complex-literal-constant | logical-literal-constant | // char-literal-constant | boz-literal-constant TYPE_PARSER( first(construct(Parser{}), construct(realLiteralConstant), construct(intLiteralConstant), construct(Parser{}), construct(Parser{}), construct(charLiteralConstant), construct(Parser{}))) // R606 named-constant -> name TYPE_PARSER(construct(name)) // R701 type-param-value -> scalar-int-expr | * | : constexpr auto star{construct("*"_tok)}; TYPE_PARSER(construct(scalarIntExpr) || construct(star) || construct(construct(":"_tok))) // R702 type-spec -> intrinsic-type-spec | derived-type-spec // N.B. This type-spec production is one of two instances in the Fortran // grammar where intrinsic types and bare derived type names can clash; // the other is below in R703 declaration-type-spec. Look-ahead is required // to disambiguate the cases where a derived type name begins with the name // of an intrinsic type, e.g., REALITY. TYPE_CONTEXT_PARSER("type spec"_en_US, construct(intrinsicTypeSpec / lookAhead("::"_tok || ")"_tok)) || construct(derivedTypeSpec)) // R703 declaration-type-spec -> // intrinsic-type-spec | TYPE ( intrinsic-type-spec ) | // TYPE ( derived-type-spec ) | CLASS ( derived-type-spec ) | // CLASS ( * ) | TYPE ( * ) // N.B. It is critical to distribute "parenthesized()" over the alternatives // for TYPE (...), rather than putting the alternatives within it, which // would fail on "TYPE(real_derived)" with a misrecognition of "real" as an // intrinsic-type-spec. TYPE_CONTEXT_PARSER("declaration type spec"_en_US, construct(intrinsicTypeSpec) || "TYPE" >> (parenthesized(construct(intrinsicTypeSpec)) || parenthesized(construct( construct(derivedTypeSpec))) || construct( "( * )" >> construct())) || "CLASS" >> parenthesized(construct( construct( derivedTypeSpec)) || construct("*" >> construct())) || extension( construct( construct( "RECORD /" >> name / "/")))) // R704 intrinsic-type-spec -> // integer-type-spec | REAL [kind-selector] | DOUBLE PRECISION | // COMPLEX [kind-selector] | CHARACTER [char-selector] | // LOGICAL [kind-selector] // Extensions: DOUBLE COMPLEX, NCHARACTER, BYTE TYPE_CONTEXT_PARSER("intrinsic type spec"_en_US, first(construct(integerTypeSpec), construct( construct("REAL" >> maybe(kindSelector))), construct("DOUBLE PRECISION" >> construct()), construct(construct( "COMPLEX" >> maybe(kindSelector))), construct(construct( "CHARACTER" >> maybe(Parser{}))), construct(construct( "LOGICAL" >> maybe(kindSelector))), construct("DOUBLE COMPLEX" >> extension( construct())), construct(extension( construct( "NCHARACTER" >> maybe(Parser{})))), extension( construct(construct( "BYTE" >> construct>(pure(1))))))) // R705 integer-type-spec -> INTEGER [kind-selector] TYPE_PARSER(construct("INTEGER" >> maybe(kindSelector))) // R706 kind-selector -> ( [KIND =] scalar-int-constant-expr ) // Legacy extension: kind-selector -> * digit-string TYPE_PARSER(construct( parenthesized(maybe("KIND ="_tok) >> scalarIntConstantExpr)) || extension(construct( construct("*" >> digitString / spaceCheck)))) // R707 signed-int-literal-constant -> [sign] int-literal-constant TYPE_PARSER(sourced(construct( SignedIntLiteralConstantWithoutKind{}, maybe(underscore >> kindParam)))) // R708 int-literal-constant -> digit-string [_ kind-param] // The negated look-ahead for a trailing underscore prevents misrecognition // when the digit string is a numeric kind parameter of a character literal. TYPE_PARSER(construct( space >> digitString, maybe(underscore >> kindParam) / !underscore)) // R709 kind-param -> digit-string | scalar-int-constant-name TYPE_PARSER(construct(digitString) || construct(scalar(integer(constant(name))))) // R712 sign -> + | - // N.B. A sign constitutes a whole token, so a space is allowed in free form // after the sign and before a real-literal-constant or // complex-literal-constant. A sign is not a unary operator in these contexts. constexpr auto sign{ "+"_tok >> pure(Sign::Positive) || "-"_tok >> pure(Sign::Negative)}; // R713 signed-real-literal-constant -> [sign] real-literal-constant constexpr auto signedRealLiteralConstant{ construct(maybe(sign), realLiteralConstant)}; // R714 real-literal-constant -> // significand [exponent-letter exponent] [_ kind-param] | // digit-string exponent-letter exponent [_ kind-param] // R715 significand -> digit-string . [digit-string] | . digit-string // R716 exponent-letter -> E | D // Extension: Q // R717 exponent -> signed-digit-string constexpr auto exponentPart{ ("ed"_ch || extension("q"_ch)) >> SignedDigitString{}}; TYPE_CONTEXT_PARSER("REAL literal constant"_en_US, space >> construct( sourced( (skipDigitString >> "."_ch >> !(some(letter) >> "."_ch /* don't misinterpret 1.AND. */) >> maybe(skipDigitString) >> maybe(exponentPart) >> ok || "."_ch >> skipDigitString >> maybe(exponentPart) >> ok || skipDigitString >> exponentPart >> ok) >> construct()), maybe(underscore >> kindParam))) // R718 complex-literal-constant -> ( real-part , imag-part ) TYPE_CONTEXT_PARSER("COMPLEX literal constant"_en_US, parenthesized(construct( Parser{} / ",", Parser{}))) // PGI/Intel extension: signed complex literal constant TYPE_PARSER(construct( sign, Parser{})) // R719 real-part -> // signed-int-literal-constant | signed-real-literal-constant | // named-constant // R720 imag-part -> // signed-int-literal-constant | signed-real-literal-constant | // named-constant TYPE_PARSER(construct(signedRealLiteralConstant) || construct(signedIntLiteralConstant) || construct(namedConstant)) // R721 char-selector -> // length-selector | // ( LEN = type-param-value , KIND = scalar-int-constant-expr ) | // ( type-param-value , [KIND =] scalar-int-constant-expr ) | // ( KIND = scalar-int-constant-expr [, LEN = type-param-value] ) TYPE_PARSER(construct(Parser{}) || parenthesized(construct( "LEN =" >> typeParamValue, ", KIND =" >> scalarIntConstantExpr)) || parenthesized(construct( typeParamValue / ",", maybe("KIND ="_tok) >> scalarIntConstantExpr)) || parenthesized(construct( "KIND =" >> scalarIntConstantExpr, maybe(", LEN =" >> typeParamValue)))) // R722 length-selector -> ( [LEN =] type-param-value ) | * char-length [,] // N.B. The trailing [,] in the production is permitted by the Standard // only in the context of a type-declaration-stmt, but even with that // limitation, it would seem to be unnecessary and buggy to consume the comma // here. TYPE_PARSER(construct( parenthesized(maybe("LEN ="_tok) >> typeParamValue)) || construct("*" >> charLength /* / maybe(","_tok) */)) // R723 char-length -> ( type-param-value ) | digit-string TYPE_PARSER(construct(parenthesized(typeParamValue)) || construct(space >> digitString / spaceCheck)) // R724 char-literal-constant -> // [kind-param _] ' [rep-char]... ' | // [kind-param _] " [rep-char]... " // "rep-char" is any non-control character. Doubled interior quotes are // combined. Backslash escapes can be enabled. // PGI extension: nc'...' is Kanji. // N.B. charLiteralConstantWithoutKind does not skip preceding space. // N.B. the parsing of "name" takes care to not consume the '_'. constexpr auto charLiteralConstantWithoutKind{ "'"_ch >> CharLiteral<'\''>{} || "\""_ch >> CharLiteral<'"'>{}}; TYPE_CONTEXT_PARSER("CHARACTER literal constant"_en_US, construct( kindParam / underscore, charLiteralConstantWithoutKind) || construct(construct>(), space >> charLiteralConstantWithoutKind) || construct( construct>( construct(construct("NC"_tok))), charLiteralConstantWithoutKind)) // deprecated: Hollerith literals constexpr auto rawHollerithLiteral{ deprecated(HollerithLiteral{})}; TYPE_CONTEXT_PARSER( "Hollerith"_en_US, construct(rawHollerithLiteral)) // R725 logical-literal-constant -> // .TRUE. [_ kind-param] | .FALSE. [_ kind-param] // Also accept .T. and .F. as extensions. TYPE_PARSER( construct( (".TRUE."_tok || extension(".T."_tok)) >> pure(true), maybe(underscore >> kindParam)) || construct( (".FALSE."_tok || extension(".F."_tok)) >> pure(false), maybe(underscore >> kindParam))) // R726 derived-type-def -> // derived-type-stmt [type-param-def-stmt]... // [private-or-sequence]... [component-part] // [type-bound-procedure-part] end-type-stmt // R735 component-part -> [component-def-stmt]... TYPE_CONTEXT_PARSER("derived type definition"_en_US, construct(statement(Parser{}), many(unambiguousStatement(Parser{})), many(statement(Parser{})), many(inContext("component"_en_US, unambiguousStatement(Parser{}))), maybe(Parser{}), statement(Parser{}))) // R727 derived-type-stmt -> // TYPE [[, type-attr-spec-list] ::] type-name [( // type-param-name-list )] TYPE_CONTEXT_PARSER("TYPE statement"_en_US, construct( "TYPE" >> optionalListBeforeColons(Parser{}), name, defaulted(parenthesized(nonemptyList(name))))) // R728 type-attr-spec -> // ABSTRACT | access-spec | BIND(C) | EXTENDS ( parent-type-name ) TYPE_PARSER(construct(construct("ABSTRACT"_tok)) || construct(construct("BIND ( C )"_tok)) || construct( construct("EXTENDS" >> parenthesized(name))) || construct(accessSpec)) // R729 private-or-sequence -> private-components-stmt | sequence-stmt TYPE_PARSER(construct(Parser{}) || construct(Parser{})) // R730 end-type-stmt -> END TYPE [type-name] TYPE_PARSER(construct( recovery("END TYPE" >> maybe(name), endStmtErrorRecovery))) // R731 sequence-stmt -> SEQUENCE TYPE_PARSER(construct("SEQUENCE"_tok)) // R732 type-param-def-stmt -> // integer-type-spec , type-param-attr-spec :: type-param-decl-list // R734 type-param-attr-spec -> KIND | LEN TYPE_PARSER(construct(integerTypeSpec / ",", "KIND" >> pure(TypeParamDefStmt::KindOrLen::Kind) || "LEN" >> pure(TypeParamDefStmt::KindOrLen::Len), "::" >> nonemptyList(Parser{}))) // R733 type-param-decl -> type-param-name [= scalar-int-constant-expr] TYPE_PARSER(construct(name, maybe("=" >> scalarIntConstantExpr))) // R736 component-def-stmt -> data-component-def-stmt | // proc-component-def-stmt // Accidental extension not enabled here: PGI accepts type-param-def-stmt in // component-part of derived-type-def. TYPE_PARSER(recovery( withMessage("expected component definition"_err_en_US, first(construct(Parser{}), construct(Parser{}))), construct(stmtErrorRecovery))) // R737 data-component-def-stmt -> // declaration-type-spec [[, component-attr-spec-list] ::] // component-decl-list TYPE_PARSER(construct(declarationTypeSpec, optionalListBeforeColons(Parser{}), nonemptyList(Parser{}))) // R738 component-attr-spec -> // access-spec | ALLOCATABLE | // CODIMENSION lbracket coarray-spec rbracket | // CONTIGUOUS | DIMENSION ( component-array-spec ) | POINTER constexpr auto allocatable{construct("ALLOCATABLE"_tok)}; constexpr auto contiguous{construct("CONTIGUOUS"_tok)}; constexpr auto pointer{construct("POINTER"_tok)}; TYPE_PARSER(construct(accessSpec) || construct(allocatable) || construct("CODIMENSION" >> coarraySpec) || construct(contiguous) || construct("DIMENSION" >> Parser{}) || construct(pointer)) // R739 component-decl -> // component-name [( component-array-spec )] // [lbracket coarray-spec rbracket] [* char-length] // [component-initialization] TYPE_CONTEXT_PARSER("component declaration"_en_US, construct(name, maybe(Parser{}), maybe(coarraySpec), maybe("*" >> charLength), maybe(initialization))) // R740 component-array-spec -> // explicit-shape-spec-list | deferred-shape-spec-list // N.B. Parenthesized here rather than around references to this production. TYPE_PARSER(construct( parenthesized(nonemptyList(explicitShapeSpec))) || construct(parenthesized(deferredShapeSpecList))) // R741 proc-component-def-stmt -> // PROCEDURE ( [proc-interface] ) , proc-component-attr-spec-list // :: proc-decl-list TYPE_CONTEXT_PARSER("PROCEDURE component definition statement"_en_US, construct( "PROCEDURE" >> parenthesized(maybe(procInterface)), "," >> nonemptyList(Parser{}) / "::", nonemptyList(procDecl))) // R742 proc-component-attr-spec -> // access-spec | NOPASS | PASS [(arg-name)] | POINTER constexpr auto noPass{construct("NOPASS"_tok)}; constexpr auto pass{construct("PASS" >> maybe(parenthesized(name)))}; TYPE_PARSER(construct(accessSpec) || construct(noPass) || construct(pass) || construct(pointer)) // R744 initial-data-target -> designator constexpr auto initialDataTarget{indirect(designator)}; // R743 component-initialization -> // = constant-expr | => null-init | => initial-data-target // R805 initialization -> // = constant-expr | => null-init | => initial-data-target // Universal extension: initialization -> / data-stmt-value-list / TYPE_PARSER(construct("=>" >> nullInit) || construct("=>" >> initialDataTarget) || construct("=" >> constantExpr) || extension(construct( "/" >> nonemptyList(indirect(Parser{})) / "/"))) // R745 private-components-stmt -> PRIVATE // R747 binding-private-stmt -> PRIVATE TYPE_PARSER(construct("PRIVATE"_tok)) // R746 type-bound-procedure-part -> // contains-stmt [binding-private-stmt] [type-bound-proc-binding]... TYPE_CONTEXT_PARSER("type bound procedure part"_en_US, construct(statement(containsStmt), maybe(statement(Parser{})), many(inContext("type bound procedure"_en_US, statement(Parser{}))))) // R748 type-bound-proc-binding -> // type-bound-procedure-stmt | type-bound-generic-stmt | // final-procedure-stmt TYPE_PARSER(recovery( withMessage("expected type bound procedure binding"_err_en_US, first(construct(Parser{}), construct(Parser{}), construct(Parser{}))), construct(stmtErrorRecovery))) // R749 type-bound-procedure-stmt -> // PROCEDURE [[, bind-attr-list] ::] type-bound-proc-decl-list | // PROCEDURE ( interface-name ) , bind-attr-list :: binding-name-list TYPE_CONTEXT_PARSER("type bound PROCEDURE statement"_en_US, "PROCEDURE" >> (construct( construct( parenthesized(name) / ",", nonemptyList(Parser{}) / "::", nonemptyList(name))) || construct( construct( optionalListBeforeColons(Parser{}), nonemptyList(Parser{}))))) // R750 type-bound-proc-decl -> binding-name [=> procedure-name] TYPE_PARSER(construct(name, maybe("=>" >> name))) // R751 type-bound-generic-stmt -> // GENERIC [, access-spec] :: generic-spec => binding-name-list TYPE_CONTEXT_PARSER("type bound GENERIC statement"_en_US, construct("GENERIC" >> maybe("," >> accessSpec), "::" >> indirect(genericSpec), "=>" >> nonemptyList(name))) // R752 bind-attr -> // access-spec | DEFERRED | NON_OVERRIDABLE | NOPASS | PASS [(arg-name)] TYPE_PARSER(construct(accessSpec) || construct(construct("DEFERRED"_tok)) || construct( construct("NON_OVERRIDABLE"_tok)) || construct(noPass) || construct(pass)) // R753 final-procedure-stmt -> FINAL [::] final-subroutine-name-list TYPE_CONTEXT_PARSER("FINAL statement"_en_US, construct( "FINAL" >> maybe("::"_tok) >> nonemptyList(name))) // R754 derived-type-spec -> type-name [(type-param-spec-list)] TYPE_PARSER(construct( name, defaulted(parenthesized(nonemptyList(Parser{}))))) // R755 type-param-spec -> [keyword =] type-param-value TYPE_PARSER(construct(maybe(keyword / "="), typeParamValue)) // R756 structure-constructor -> derived-type-spec ( [component-spec-list] ) TYPE_PARSER((construct(derivedTypeSpec, parenthesized(optionalList(Parser{}))) || // This alternative corrects misrecognition of the // component-spec-list as the type-param-spec-list in // derived-type-spec. construct( construct( name, construct>()), parenthesized(optionalList(Parser{})))) / !"("_tok) // R757 component-spec -> [keyword =] component-data-source TYPE_PARSER(construct( maybe(keyword / "="), Parser{})) // R758 component-data-source -> expr | data-target | proc-target TYPE_PARSER(construct(indirect(expr))) // R759 enum-def -> // enum-def-stmt enumerator-def-stmt [enumerator-def-stmt]... // end-enum-stmt TYPE_CONTEXT_PARSER("enum definition"_en_US, construct(statement(Parser{}), some(unambiguousStatement(Parser{})), statement(Parser{}))) // R760 enum-def-stmt -> ENUM, BIND(C) TYPE_PARSER(construct("ENUM , BIND ( C )"_tok)) // R761 enumerator-def-stmt -> ENUMERATOR [::] enumerator-list TYPE_CONTEXT_PARSER("ENUMERATOR statement"_en_US, construct( "ENUMERATOR" >> maybe("::"_tok) >> nonemptyList(Parser{}))) // R762 enumerator -> named-constant [= scalar-int-constant-expr] TYPE_PARSER( construct(namedConstant, maybe("=" >> scalarIntConstantExpr))) // R763 end-enum-stmt -> END ENUM TYPE_PARSER(recovery("END ENUM"_tok, "END" >> SkipPast<'\n'>{}) >> construct()) // R764 boz-literal-constant -> binary-constant | octal-constant | hex-constant // R765 binary-constant -> B ' digit [digit]... ' | B " digit [digit]... " // R766 octal-constant -> O ' digit [digit]... ' | O " digit [digit]... " // R767 hex-constant -> // Z ' hex-digit [hex-digit]... ' | Z " hex-digit [hex-digit]... " // extension: X accepted for Z // extension: BOZX suffix accepted TYPE_PARSER(construct(BOZLiteral{})) // R1124 do-variable -> scalar-int-variable-name constexpr auto doVariable{scalar(integer(name))}; template inline constexpr auto loopBounds(const PA &p) { return construct>( doVariable / "=", p / ",", p, maybe("," >> p)); } // R769 array-constructor -> (/ ac-spec /) | lbracket ac-spec rbracket TYPE_CONTEXT_PARSER("array constructor"_en_US, construct( "(/" >> Parser{} / "/)" || bracketed(Parser{}))) // R770 ac-spec -> type-spec :: | [type-spec ::] ac-value-list TYPE_PARSER(construct( maybe(typeSpec / "::"), nonemptyList(Parser{})) || construct(typeSpec / "::")) // R773 ac-value -> expr | ac-implied-do TYPE_PARSER( // PGI/Intel extension: accept triplets in array constructors extension( construct(construct(scalarIntExpr, ":" >> scalarIntExpr, maybe(":" >> scalarIntExpr)))) || construct(indirect(expr)) || construct(indirect(Parser{}))) // R774 ac-implied-do -> ( ac-value-list , ac-implied-do-control ) TYPE_PARSER(parenthesized( construct(nonemptyList(Parser{} / lookAhead(","_tok)), "," >> Parser{}))) // R775 ac-implied-do-control -> // [integer-type-spec ::] ac-do-variable = scalar-int-expr , // scalar-int-expr [, scalar-int-expr] // R776 ac-do-variable -> do-variable TYPE_PARSER(construct( maybe(integerTypeSpec / "::"), loopBounds(scalarIntExpr))) // R801 type-declaration-stmt -> // declaration-type-spec [[, attr-spec]... ::] entity-decl-list TYPE_PARSER(construct(declarationTypeSpec, optionalListBeforeColons(Parser{}), nonemptyList(entityDecl)) || // PGI-only extension: don't require the colons // N.B.: The standard requires the colons if the entity // declarations contain initializers. extension(construct( declarationTypeSpec, defaulted("," >> nonemptyList(Parser{})), "," >> nonemptyList(entityDecl)))) // R802 attr-spec -> // access-spec | ALLOCATABLE | ASYNCHRONOUS | // CODIMENSION lbracket coarray-spec rbracket | CONTIGUOUS | // DIMENSION ( array-spec ) | EXTERNAL | INTENT ( intent-spec ) | // INTRINSIC | language-binding-spec | OPTIONAL | PARAMETER | POINTER | // PROTECTED | SAVE | TARGET | VALUE | VOLATILE constexpr auto optional{construct("OPTIONAL"_tok)}; constexpr auto protectedAttr{construct("PROTECTED"_tok)}; constexpr auto save{construct("SAVE"_tok)}; TYPE_PARSER(construct(accessSpec) || construct(allocatable) || construct(construct("ASYNCHRONOUS"_tok)) || construct("CODIMENSION" >> coarraySpec) || construct(contiguous) || construct("DIMENSION" >> arraySpec) || construct(construct("EXTERNAL"_tok)) || construct("INTENT" >> parenthesized(intentSpec)) || construct(construct("INTRINSIC"_tok)) || construct(languageBindingSpec) || construct(optional) || construct(construct("PARAMETER"_tok)) || construct(pointer) || construct(protectedAttr) || construct(save) || construct(construct("TARGET"_tok)) || construct(construct("VALUE"_tok)) || construct(construct("VOLATILE"_tok))) // R804 object-name -> name constexpr auto objectName{name}; // R803 entity-decl -> // object-name [( array-spec )] [lbracket coarray-spec rbracket] // [* char-length] [initialization] | // function-name [* char-length] TYPE_PARSER(construct(objectName, maybe(arraySpec), maybe(coarraySpec), maybe("*" >> charLength), maybe(initialization))) // R806 null-init -> function-reference // TODO: confirm in semantics that NULL still intrinsic in this scope TYPE_PARSER(construct("NULL ( )"_tok) / !"("_tok) // R807 access-spec -> PUBLIC | PRIVATE TYPE_PARSER(construct("PUBLIC" >> pure(AccessSpec::Kind::Public)) || construct("PRIVATE" >> pure(AccessSpec::Kind::Private))) // R808 language-binding-spec -> // BIND ( C [, NAME = scalar-default-char-constant-expr] ) // R1528 proc-language-binding-spec -> language-binding-spec TYPE_PARSER(construct( "BIND ( C" >> maybe(", NAME =" >> scalarDefaultCharConstantExpr) / ")")) // R809 coarray-spec -> deferred-coshape-spec-list | explicit-coshape-spec // N.B. Bracketed here rather than around references, for consistency with // array-spec. TYPE_PARSER( construct(bracketed(Parser{})) || construct(bracketed(Parser{}))) // R810 deferred-coshape-spec -> : // deferred-coshape-spec-list - just a list of colons inline int listLength(std::list &&xs) { return xs.size(); } TYPE_PARSER(construct( applyFunction(listLength, nonemptyList(":"_tok)))) // R811 explicit-coshape-spec -> // [[lower-cobound :] upper-cobound ,]... [lower-cobound :] * // R812 lower-cobound -> specification-expr // R813 upper-cobound -> specification-expr TYPE_PARSER(construct( many(explicitShapeSpec / ","), maybe(specificationExpr / ":") / "*")) // R815 array-spec -> // explicit-shape-spec-list | assumed-shape-spec-list | // deferred-shape-spec-list | assumed-size-spec | implied-shape-spec | // implied-shape-or-assumed-size-spec | assumed-rank-spec // N.B. Parenthesized here rather than around references to avoid // a need for forced look-ahead. TYPE_PARSER( construct(parenthesized(nonemptyList(explicitShapeSpec))) || construct( parenthesized(nonemptyList(Parser{}))) || construct(parenthesized(deferredShapeSpecList)) || construct(parenthesized(Parser{})) || construct(parenthesized(Parser{})) || construct(parenthesized(Parser{}))) // R816 explicit-shape-spec -> [lower-bound :] upper-bound // R817 lower-bound -> specification-expr // R818 upper-bound -> specification-expr TYPE_PARSER(construct( maybe(specificationExpr / ":"), specificationExpr)) // R819 assumed-shape-spec -> [lower-bound] : TYPE_PARSER(construct(maybe(specificationExpr) / ":")) // R820 deferred-shape-spec -> : // deferred-shape-spec-list - just a list of colons TYPE_PARSER(construct( applyFunction(listLength, nonemptyList(":"_tok)))) // R821 assumed-implied-spec -> [lower-bound :] * TYPE_PARSER(construct(maybe(specificationExpr / ":") / "*")) // R822 assumed-size-spec -> explicit-shape-spec-list , assumed-implied-spec TYPE_PARSER(construct( nonemptyList(explicitShapeSpec) / ",", assumedImpliedSpec)) // R823 implied-shape-or-assumed-size-spec -> assumed-implied-spec // R824 implied-shape-spec -> assumed-implied-spec , assumed-implied-spec-list // I.e., when the assumed-implied-spec-list has a single item, it constitutes an // implied-shape-or-assumed-size-spec; otherwise, an implied-shape-spec. TYPE_PARSER(construct(nonemptyList(assumedImpliedSpec))) // R825 assumed-rank-spec -> .. TYPE_PARSER(construct(".."_tok)) // R826 intent-spec -> IN | OUT | INOUT TYPE_PARSER(construct("IN OUT" >> pure(IntentSpec::Intent::InOut) || "IN" >> pure(IntentSpec::Intent::In) || "OUT" >> pure(IntentSpec::Intent::Out))) // R827 access-stmt -> access-spec [[::] access-id-list] TYPE_PARSER(construct( accessSpec, defaulted(maybe("::"_tok) >> nonemptyList(Parser{})))) // R828 access-id -> access-name | generic-spec TYPE_PARSER(construct(indirect(genericSpec)) || construct(name)) // initially ambiguous with genericSpec // R829 allocatable-stmt -> ALLOCATABLE [::] allocatable-decl-list TYPE_PARSER(construct( "ALLOCATABLE" >> maybe("::"_tok) >> nonemptyList(Parser{}))) // R830 allocatable-decl -> // object-name [( array-spec )] [lbracket coarray-spec rbracket] // R860 target-decl -> // object-name [( array-spec )] [lbracket coarray-spec rbracket] TYPE_PARSER( construct(objectName, maybe(arraySpec), maybe(coarraySpec))) // R831 asynchronous-stmt -> ASYNCHRONOUS [::] object-name-list TYPE_PARSER(construct( "ASYNCHRONOUS" >> maybe("::"_tok) >> nonemptyList(objectName))) // R832 bind-stmt -> language-binding-spec [::] bind-entity-list TYPE_PARSER(construct( languageBindingSpec / maybe("::"_tok), nonemptyList(Parser{}))) // R833 bind-entity -> entity-name | / common-block-name / TYPE_PARSER(construct(pure(BindEntity::Kind::Object), name) || construct("/" >> pure(BindEntity::Kind::Common), name / "/")) // R834 codimension-stmt -> CODIMENSION [::] codimension-decl-list TYPE_PARSER(construct("CODIMENSION" >> maybe("::"_tok) >> nonemptyList(Parser{}))) // R835 codimension-decl -> coarray-name lbracket coarray-spec rbracket TYPE_PARSER(construct(name, coarraySpec)) // R836 contiguous-stmt -> CONTIGUOUS [::] object-name-list TYPE_PARSER(construct( "CONTIGUOUS" >> maybe("::"_tok) >> nonemptyList(objectName))) // R837 data-stmt -> DATA data-stmt-set [[,] data-stmt-set]... TYPE_CONTEXT_PARSER("DATA statement"_en_US, construct( "DATA" >> nonemptySeparated(Parser{}, maybe(","_tok)))) // R838 data-stmt-set -> data-stmt-object-list / data-stmt-value-list / TYPE_PARSER(construct(nonemptyList(Parser{}), "/"_tok >> nonemptyList(Parser{}) / "/")) // R839 data-stmt-object -> variable | data-implied-do TYPE_PARSER(construct(indirect(variable)) || construct(dataImpliedDo)) // R840 data-implied-do -> // ( data-i-do-object-list , [integer-type-spec ::] data-i-do-variable // = scalar-int-constant-expr , scalar-int-constant-expr // [, scalar-int-constant-expr] ) // R842 data-i-do-variable -> do-variable TYPE_PARSER(parenthesized(construct( nonemptyList(Parser{} / lookAhead(","_tok)) / ",", maybe(integerTypeSpec / "::"), loopBounds(scalarIntConstantExpr)))) // R841 data-i-do-object -> // array-element | scalar-structure-component | data-implied-do TYPE_PARSER(construct(scalar(indirect(designator))) || construct(indirect(dataImpliedDo))) // R843 data-stmt-value -> [data-stmt-repeat *] data-stmt-constant TYPE_PARSER(construct( maybe(Parser{} / "*"), Parser{})) // R847 constant-subobject -> designator // R846 int-constant-subobject -> constant-subobject constexpr auto constantSubobject{constant(indirect(designator))}; // R844 data-stmt-repeat -> scalar-int-constant | scalar-int-constant-subobject // R607 int-constant -> constant // Factored into: // constant -> literal-constant -> int-literal-constant and // constant -> named-constant TYPE_PARSER(construct(intLiteralConstant) || construct(scalar(integer(constantSubobject))) || construct(scalar(integer(namedConstant)))) // R845 data-stmt-constant -> // scalar-constant | scalar-constant-subobject | // signed-int-literal-constant | signed-real-literal-constant | // null-init | initial-data-target | structure-constructor // TODO: Some structure constructors can be misrecognized as array // references into constant subobjects. TYPE_PARSER(first(construct(scalar(Parser{})), construct(nullInit), construct(Parser{}), construct(scalar(constantSubobject)), construct(signedRealLiteralConstant), construct(signedIntLiteralConstant), extension( construct(Parser{})), construct(initialDataTarget))) // R848 dimension-stmt -> // DIMENSION [::] array-name ( array-spec ) // [, array-name ( array-spec )]... TYPE_CONTEXT_PARSER("DIMENSION statement"_en_US, construct("DIMENSION" >> maybe("::"_tok) >> nonemptyList(construct(name, arraySpec)))) // R849 intent-stmt -> INTENT ( intent-spec ) [::] dummy-arg-name-list TYPE_CONTEXT_PARSER("INTENT statement"_en_US, construct( "INTENT" >> parenthesized(intentSpec) / maybe("::"_tok), nonemptyList(name))) // R850 optional-stmt -> OPTIONAL [::] dummy-arg-name-list TYPE_PARSER(construct( "OPTIONAL" >> maybe("::"_tok) >> nonemptyList(name))) // R851 parameter-stmt -> PARAMETER ( named-constant-def-list ) // Legacy extension: omitted parentheses, no implicit typing from names TYPE_CONTEXT_PARSER("PARAMETER statement"_en_US, construct( "PARAMETER" >> parenthesized(nonemptyList(Parser{})))) TYPE_CONTEXT_PARSER("old style PARAMETER statement"_en_US, extension(construct( "PARAMETER" >> nonemptyList(Parser{})))) // R852 named-constant-def -> named-constant = constant-expr TYPE_PARSER(construct(namedConstant, "=" >> constantExpr)) // R853 pointer-stmt -> POINTER [::] pointer-decl-list TYPE_PARSER(construct( "POINTER" >> maybe("::"_tok) >> nonemptyList(Parser{}))) // R854 pointer-decl -> // object-name [( deferred-shape-spec-list )] | proc-entity-name TYPE_PARSER( construct(name, maybe(parenthesized(deferredShapeSpecList)))) // R855 protected-stmt -> PROTECTED [::] entity-name-list TYPE_PARSER(construct( "PROTECTED" >> maybe("::"_tok) >> nonemptyList(name))) // R856 save-stmt -> SAVE [[::] saved-entity-list] TYPE_PARSER(construct("SAVE" >> defaulted(maybe("::"_tok) >> nonemptyList(Parser{})))) // R857 saved-entity -> object-name | proc-pointer-name | / common-block-name / // R858 proc-pointer-name -> name // TODO: Distinguish Kind::ProcPointer and Kind::Object TYPE_PARSER(construct(pure(SavedEntity::Kind::Object), name) || construct("/" >> pure(SavedEntity::Kind::Common), name / "/")) // R859 target-stmt -> TARGET [::] target-decl-list TYPE_PARSER(construct( "TARGET" >> maybe("::"_tok) >> nonemptyList(Parser{}))) // R861 value-stmt -> VALUE [::] dummy-arg-name-list TYPE_PARSER( construct("VALUE" >> maybe("::"_tok) >> nonemptyList(name))) // R862 volatile-stmt -> VOLATILE [::] object-name-list TYPE_PARSER(construct( "VOLATILE" >> maybe("::"_tok) >> nonemptyList(objectName))) // R866 implicit-name-spec -> EXTERNAL | TYPE constexpr auto implicitNameSpec{ "EXTERNAL" >> pure(ImplicitStmt::ImplicitNoneNameSpec::External) || "TYPE" >> pure(ImplicitStmt::ImplicitNoneNameSpec::Type)}; // R863 implicit-stmt -> // IMPLICIT implicit-spec-list | // IMPLICIT NONE [( [implicit-name-spec-list] )] TYPE_CONTEXT_PARSER("IMPLICIT statement"_en_US, construct( "IMPLICIT" >> nonemptyList(Parser{})) || construct("IMPLICIT NONE"_sptok >> defaulted(parenthesized(optionalList(implicitNameSpec))))) // R864 implicit-spec -> declaration-type-spec ( letter-spec-list ) // The variant form of declarationTypeSpec is meant to avoid misrecognition // of a letter-spec as a simple parenthesized expression for kind or character // length, e.g., PARAMETER(I=5,N=1); IMPLICIT REAL(I-N)(O-Z) vs. // IMPLICIT REAL(I-N). The variant form needs to attempt to reparse only // types with optional parenthesized kind/length expressions, so derived // type specs, DOUBLE PRECISION, and DOUBLE COMPLEX need not be considered. constexpr auto noKindSelector{construct>()}; constexpr auto implicitSpecDeclarationTypeSpecRetry{ construct(first( construct( construct("INTEGER" >> noKindSelector)), construct( construct("REAL" >> noKindSelector)), construct( construct("COMPLEX" >> noKindSelector)), construct(construct( "CHARACTER" >> construct>())), construct(construct( "LOGICAL" >> noKindSelector))))}; TYPE_PARSER(construct(declarationTypeSpec, parenthesized(nonemptyList(Parser{}))) || construct(implicitSpecDeclarationTypeSpecRetry, parenthesized(nonemptyList(Parser{})))) // R865 letter-spec -> letter [- letter] TYPE_PARSER(space >> (construct(letter, maybe("-" >> letter)) || construct(otherIdChar, construct>()))) // R867 import-stmt -> // IMPORT [[::] import-name-list] | // IMPORT , ONLY : import-name-list | IMPORT , NONE | IMPORT , ALL TYPE_CONTEXT_PARSER("IMPORT statement"_en_US, construct("IMPORT , ONLY :" >> pure(common::ImportKind::Only), nonemptyList(name)) || construct( "IMPORT , NONE" >> pure(common::ImportKind::None)) || construct( "IMPORT , ALL" >> pure(common::ImportKind::All)) || construct( "IMPORT" >> maybe("::"_tok) >> optionalList(name))) // R868 namelist-stmt -> // NAMELIST / namelist-group-name / namelist-group-object-list // [[,] / namelist-group-name / namelist-group-object-list]... // R869 namelist-group-object -> variable-name TYPE_PARSER(construct("NAMELIST" >> nonemptySeparated( construct("/" >> name / "/", nonemptyList(name)), maybe(","_tok)))) // R870 equivalence-stmt -> EQUIVALENCE equivalence-set-list // R871 equivalence-set -> ( equivalence-object , equivalence-object-list ) TYPE_PARSER(construct("EQUIVALENCE" >> nonemptyList(parenthesized(nonemptyList(Parser{}))))) // R872 equivalence-object -> variable-name | array-element | substring TYPE_PARSER(construct(indirect(designator))) // R873 common-stmt -> // COMMON [/ [common-block-name] /] common-block-object-list // [[,] / [common-block-name] / common-block-object-list]... TYPE_PARSER( construct("COMMON" >> defaulted("/" >> maybe(name) / "/"), nonemptyList(Parser{}), many(maybe(","_tok) >> construct("/" >> maybe(name) / "/", nonemptyList(Parser{}))))) // R874 common-block-object -> variable-name [( array-spec )] TYPE_PARSER(construct(name, maybe(arraySpec))) // R901 designator -> object-name | array-element | array-section | // coindexed-named-object | complex-part-designator | // structure-component | substring // The Standard's productions for designator and its alternatives are // ambiguous without recourse to a symbol table. Many of the alternatives // for designator (viz., array-element, coindexed-named-object, // and structure-component) are all syntactically just data-ref. // What designator boils down to is this: // It starts with either a name or a character literal. // If it starts with a character literal, it must be a substring. // If it starts with a name, it's a sequence of %-separated parts; // each part is a name, maybe a (section-subscript-list), and // maybe an [image-selector]. // If it's a substring, it ends with (substring-range). TYPE_CONTEXT_PARSER("designator"_en_US, construct(substring) || construct(dataRef)) constexpr auto percentOrDot{"%"_tok || // legacy VAX extension for RECORD field access extension( "."_tok / lookAhead(OldStructureComponentName{}))}; // R902 variable -> designator | function-reference // This production appears to be left-recursive in the grammar via // function-reference -> procedure-designator -> proc-component-ref -> // scalar-variable // and would be so if we were to allow functions to be called via procedure // pointer components within derived type results of other function references // (a reasonable extension, esp. in the case of procedure pointer components // that are NOPASS). However, Fortran constrains the use of a variable in a // proc-component-ref to be a data-ref without coindices (C1027). // Some array element references will be misrecognized as function references. constexpr auto noMoreAddressing{!"("_tok >> !"["_tok >> !percentOrDot}; TYPE_CONTEXT_PARSER("variable"_en_US, construct(indirect(functionReference / noMoreAddressing)) || construct(indirect(designator))) // R904 logical-variable -> variable // Appears only as part of scalar-logical-variable. constexpr auto scalarLogicalVariable{scalar(logical(variable))}; // R905 char-variable -> variable constexpr auto charVariable{construct(variable)}; // R906 default-char-variable -> variable // Appears only as part of scalar-default-char-variable. constexpr auto scalarDefaultCharVariable{scalar(defaultChar(variable))}; // R907 int-variable -> variable // Appears only as part of scalar-int-variable. constexpr auto scalarIntVariable{scalar(integer(variable))}; // R908 substring -> parent-string ( substring-range ) // R909 parent-string -> // scalar-variable-name | array-element | coindexed-named-object | // scalar-structure-component | scalar-char-literal-constant | // scalar-named-constant TYPE_PARSER( construct(dataRef, parenthesized(Parser{}))) TYPE_PARSER(construct( charLiteralConstant, parenthesized(Parser{}))) // R910 substring-range -> [scalar-int-expr] : [scalar-int-expr] TYPE_PARSER(construct( maybe(scalarIntExpr), ":" >> maybe(scalarIntExpr))) // R911 data-ref -> part-ref [% part-ref]... // R914 coindexed-named-object -> data-ref // R917 array-element -> data-ref TYPE_PARSER( construct(nonemptySeparated(Parser{}, percentOrDot))) // R912 part-ref -> part-name [( section-subscript-list )] [image-selector] TYPE_PARSER(construct(name, defaulted( parenthesized(nonemptyList(Parser{})) / !"=>"_tok), maybe(Parser{}))) // R913 structure-component -> data-ref TYPE_PARSER(construct( construct(some(Parser{} / percentOrDot)), name)) // R915 complex-part-designator -> designator % RE | designator % IM // %RE and %IM are initially recognized as structure components. constexpr auto complexPartDesignator{construct(dataRef)}; // R916 type-param-inquiry -> designator % type-param-name // Type parameter inquiries are initially recognized as structure components. TYPE_PARSER(construct(structureComponent)) // R918 array-section -> // data-ref [( substring-range )] | complex-part-designator constexpr auto arraySection{construct(designator)}; // R919 subscript -> scalar-int-expr constexpr auto subscript{scalarIntExpr}; // R923 vector-subscript -> int-expr constexpr auto vectorSubscript{intExpr}; // R920 section-subscript -> subscript | subscript-triplet | vector-subscript // N.B. The distinction that needs to be made between "subscript" and // "vector-subscript" is deferred to semantic analysis. TYPE_PARSER(construct(Parser{}) || construct(vectorSubscript) || construct(subscript)) // R921 subscript-triplet -> [subscript] : [subscript] [: stride] TYPE_PARSER(construct( maybe(subscript), ":" >> maybe(subscript), maybe(":" >> subscript))) // R925 cosubscript -> scalar-int-expr constexpr auto cosubscript{scalarIntExpr}; // R924 image-selector -> // lbracket cosubscript-list [, image-selector-spec-list] rbracket TYPE_CONTEXT_PARSER("image selector"_en_US, construct("[" >> nonemptyList(cosubscript / !"="_tok), defaulted("," >> nonemptyList(Parser{})) / "]")) // R1115 team-variable -> scalar-variable constexpr auto teamVariable{scalar(indirect(variable))}; // R926 image-selector-spec -> // STAT = stat-variable | TEAM = team-variable | // TEAM_NUMBER = scalar-int-expr TYPE_PARSER(construct(construct( "STAT =" >> scalar(integer(indirect(variable))))) || construct( construct("TEAM =" >> teamVariable)) || construct(construct( "TEAM_NUMBER =" >> scalarIntExpr))) // R927 allocate-stmt -> // ALLOCATE ( [type-spec ::] allocation-list [, alloc-opt-list] ) TYPE_CONTEXT_PARSER("ALLOCATE statement"_en_US, construct("ALLOCATE (" >> maybe(typeSpec / "::"), nonemptyList(Parser{}), defaulted("," >> nonemptyList(Parser{})) / ")")) // R928 alloc-opt -> // ERRMSG = errmsg-variable | MOLD = source-expr | // SOURCE = source-expr | STAT = stat-variable // R931 source-expr -> expr TYPE_PARSER(construct( construct("MOLD =" >> indirect(expr))) || construct( construct("SOURCE =" >> indirect(expr))) || construct(statOrErrmsg)) // R929 stat-variable -> scalar-int-variable TYPE_PARSER(construct(scalar(integer(variable)))) // R930 errmsg-variable -> scalar-default-char-variable // R1207 iomsg-variable -> scalar-default-char-variable constexpr auto msgVariable{construct(scalarDefaultCharVariable)}; // R932 allocation -> // allocate-object [( allocate-shape-spec-list )] // [lbracket allocate-coarray-spec rbracket] // TODO: allocate-shape-spec-list might be misrecognized as // the final list of subscripts in allocate-object. TYPE_PARSER(construct(Parser{}, defaulted(parenthesized(nonemptyList(Parser{}))), maybe(bracketed(Parser{})))) // R933 allocate-object -> variable-name | structure-component TYPE_PARSER(construct(structureComponent) || construct(name / !"="_tok)) // R935 lower-bound-expr -> scalar-int-expr // R936 upper-bound-expr -> scalar-int-expr constexpr auto boundExpr{scalarIntExpr}; // R934 allocate-shape-spec -> [lower-bound-expr :] upper-bound-expr // R938 allocate-coshape-spec -> [lower-bound-expr :] upper-bound-expr TYPE_PARSER(construct(maybe(boundExpr / ":"), boundExpr)) // R937 allocate-coarray-spec -> // [allocate-coshape-spec-list ,] [lower-bound-expr :] * TYPE_PARSER(construct( defaulted(nonemptyList(Parser{}) / ","), maybe(boundExpr / ":") / "*")) // R939 nullify-stmt -> NULLIFY ( pointer-object-list ) TYPE_CONTEXT_PARSER("NULLIFY statement"_en_US, "NULLIFY" >> parenthesized(construct( nonemptyList(Parser{})))) // R940 pointer-object -> // variable-name | structure-component | proc-pointer-name TYPE_PARSER(construct(structureComponent) || construct(name)) // R941 deallocate-stmt -> // DEALLOCATE ( allocate-object-list [, dealloc-opt-list] ) TYPE_CONTEXT_PARSER("DEALLOCATE statement"_en_US, construct( "DEALLOCATE (" >> nonemptyList(Parser{}), defaulted("," >> nonemptyList(statOrErrmsg)) / ")")) // R942 dealloc-opt -> STAT = stat-variable | ERRMSG = errmsg-variable // R1165 sync-stat -> STAT = stat-variable | ERRMSG = errmsg-variable TYPE_PARSER(construct("STAT =" >> statVariable) || construct("ERRMSG =" >> msgVariable)) // R1001 primary -> // literal-constant | designator | array-constructor | // structure-constructor | function-reference | type-param-inquiry | // type-param-name | ( expr ) constexpr auto primary{instrumented("primary"_en_US, first(construct(indirect(Parser{})), construct(literalConstant), construct(construct(parenthesized(expr))), construct(indirect(functionReference) / !"("_tok), construct(designator / !"("_tok), construct(Parser{}), construct(Parser{}), construct(indirect(Parser{})), // occulted // PGI/XLF extension: COMPLEX constructor (x,y) extension( construct(parenthesized( construct(expr, "," >> expr)))), extension(construct("%LOC" >> parenthesized(construct(indirect(variable)))))))}; // R1002 level-1-expr -> [defined-unary-op] primary // TODO: Reasonable extension: permit multiple defined-unary-ops constexpr auto level1Expr{first( construct(construct(definedOpName, primary)), primary, extension( construct(construct("+" >> primary))), extension( construct(construct("-" >> primary))))}; // R1004 mult-operand -> level-1-expr [power-op mult-operand] // R1007 power-op -> ** // Exponentiation (**) is Fortran's only right-associative binary operation. constexpr struct MultOperand { using resultType = Expr; constexpr MultOperand() {} static inline std::optional Parse(ParseState &); } multOperand; inline std::optional MultOperand::Parse(ParseState &state) { std::optional result{level1Expr.Parse(state)}; if (result) { static constexpr auto op{attempt("**"_tok)}; if (op.Parse(state)) { std::function power{[&result](Expr &&right) { return Expr{Expr::Power(std::move(result).value(), std::move(right))}; }}; return applyLambda(power, multOperand).Parse(state); // right-recursive } } return result; } // R1005 add-operand -> [add-operand mult-op] mult-operand // R1008 mult-op -> * | / // The left recursion in the grammar is implemented iteratively. constexpr struct AddOperand { using resultType = Expr; constexpr AddOperand() {} static inline std::optional Parse(ParseState &state) { std::optional result{multOperand.Parse(state)}; if (result) { std::function multiply{[&result](Expr &&right) { return Expr{ Expr::Multiply(std::move(result).value(), std::move(right))}; }}, divide{[&result](Expr &&right) { return Expr{ Expr::Divide(std::move(result).value(), std::move(right))}; }}; auto more{"*" >> applyLambda(multiply, multOperand) || "/" >> applyLambda(divide, multOperand)}; while (std::optional next{attempt(more).Parse(state)}) { result = std::move(next); } } return result; } } addOperand; // R1006 level-2-expr -> [[level-2-expr] add-op] add-operand // R1009 add-op -> + | - // These are left-recursive productions, implemented iteratively. // Note that standard Fortran admits a unary + or - to appear only here, // by means of a missing first operand; e.g., 2*-3 is valid in C but not // standard Fortran. We accept unary + and - to appear before any primary // as an extension. constexpr struct Level2Expr { using resultType = Expr; constexpr Level2Expr() {} static inline std::optional Parse(ParseState &state) { static constexpr auto unary{ construct(construct("+" >> addOperand)) || construct(construct("-" >> addOperand)) || addOperand}; std::optional result{unary.Parse(state)}; if (result) { std::function add{[&result](Expr &&right) { return Expr{Expr::Add(std::move(result).value(), std::move(right))}; }}, subtract{[&result](Expr &&right) { return Expr{ Expr::Subtract(std::move(result).value(), std::move(right))}; }}; auto more{"+" >> applyLambda(add, addOperand) || "-" >> applyLambda(subtract, addOperand)}; while (std::optional next{attempt(more).Parse(state)}) { result = std::move(next); } } return result; } } level2Expr; // R1010 level-3-expr -> [level-3-expr concat-op] level-2-expr // R1011 concat-op -> // // Concatenation (//) is left-associative for parsing performance, although // one would never notice if it were right-associated. constexpr struct Level3Expr { using resultType = Expr; constexpr Level3Expr() {} static inline std::optional Parse(ParseState &state) { std::optional result{level2Expr.Parse(state)}; if (result) { std::function concat{[&result](Expr &&right) { return Expr{Expr::Concat(std::move(result).value(), std::move(right))}; }}; auto more{"//" >> applyLambda(concat, level2Expr)}; while (std::optional next{attempt(more).Parse(state)}) { result = std::move(next); } } return result; } } level3Expr; // R1012 level-4-expr -> [level-3-expr rel-op] level-3-expr // R1013 rel-op -> // .EQ. | .NE. | .LT. | .LE. | .GT. | .GE. | // == | /= | < | <= | > | >= @ | <> // N.B. relations are not recursive (i.e., LOGICAL is not ordered) constexpr struct Level4Expr { using resultType = Expr; constexpr Level4Expr() {} static inline std::optional Parse(ParseState &state) { std::optional result{level3Expr.Parse(state)}; if (result) { std::function lt{[&result](Expr &&right) { return Expr{Expr::LT(std::move(result).value(), std::move(right))}; }}, le{[&result](Expr &&right) { return Expr{Expr::LE(std::move(result).value(), std::move(right))}; }}, eq{[&result](Expr &&right) { return Expr{Expr::EQ(std::move(result).value(), std::move(right))}; }}, ne{[&result](Expr &&right) { return Expr{Expr::NE(std::move(result).value(), std::move(right))}; }}, ge{[&result](Expr &&right) { return Expr{Expr::GE(std::move(result).value(), std::move(right))}; }}, gt{[&result](Expr &&right) { return Expr{Expr::GT(std::move(result).value(), std::move(right))}; }}; auto more{(".LT."_tok || "<"_tok) >> applyLambda(lt, level3Expr) || (".LE."_tok || "<="_tok) >> applyLambda(le, level3Expr) || (".EQ."_tok || "=="_tok) >> applyLambda(eq, level3Expr) || (".NE."_tok || "/="_tok || extension( "<>"_tok /* PGI/Cray extension; Cray also has .LG. */)) >> applyLambda(ne, level3Expr) || (".GE."_tok || ">="_tok) >> applyLambda(ge, level3Expr) || (".GT."_tok || ">"_tok) >> applyLambda(gt, level3Expr)}; if (std::optional next{attempt(more).Parse(state)}) { return next; } } return result; } } level4Expr; // R1014 and-operand -> [not-op] level-4-expr // R1018 not-op -> .NOT. // N.B. Fortran's .NOT. binds less tightly than its comparison operators do. // PGI/Intel extension: accept multiple .NOT. operators constexpr struct AndOperand { using resultType = Expr; constexpr AndOperand() {} static inline std::optional Parse(ParseState &); } andOperand; inline std::optional AndOperand::Parse(ParseState &state) { static constexpr auto op{attempt(".NOT."_tok)}; int complements{0}; while (op.Parse(state)) { ++complements; } std::optional result{level4Expr.Parse(state)}; if (result.has_value()) { while (complements-- > 0) { result = Expr{Expr::NOT{std::move(*result)}}; } } return result; } // R1015 or-operand -> [or-operand and-op] and-operand // R1019 and-op -> .AND. // .AND. is left-associative constexpr struct OrOperand { using resultType = Expr; constexpr OrOperand() {} static inline std::optional Parse(ParseState &state) { std::optional result{andOperand.Parse(state)}; if (result) { std::function logicalAnd{[&result](Expr &&right) { return Expr{Expr::AND(std::move(result).value(), std::move(right))}; }}; auto more{".AND." >> applyLambda(logicalAnd, andOperand)}; while (std::optional next{attempt(more).Parse(state)}) { result = std::move(next); } } return result; } } orOperand; // R1016 equiv-operand -> [equiv-operand or-op] or-operand // R1020 or-op -> .OR. // .OR. is left-associative constexpr struct EquivOperand { using resultType = Expr; constexpr EquivOperand() {} static inline std::optional Parse(ParseState &state) { std::optional result{orOperand.Parse(state)}; if (result) { std::function logicalOr{[&result](Expr &&right) { return Expr{Expr::OR(std::move(result).value(), std::move(right))}; }}; auto more{".OR." >> applyLambda(logicalOr, orOperand)}; while (std::optional next{attempt(more).Parse(state)}) { result = std::move(next); } } return result; } } equivOperand; // R1017 level-5-expr -> [level-5-expr equiv-op] equiv-operand // R1021 equiv-op -> .EQV. | .NEQV. // Logical equivalence is left-associative. // Extension: .XOR. as synonym for .NEQV. constexpr struct Level5Expr { using resultType = Expr; constexpr Level5Expr() {} static inline std::optional Parse(ParseState &state) { std::optional result{equivOperand.Parse(state)}; if (result) { std::function eqv{[&result](Expr &&right) { return Expr{Expr::EQV(std::move(result).value(), std::move(right))}; }}, neqv{[&result](Expr &&right) { return Expr{ Expr::NEQV(std::move(result).value(), std::move(right))}; }}, logicalXor{[&result](Expr &&right) { return Expr{Expr::XOR(std::move(result).value(), std::move(right))}; }}; auto more{".EQV." >> applyLambda(eqv, equivOperand) || ".NEQV." >> applyLambda(neqv, equivOperand) || extension( ".XOR." >> applyLambda(logicalXor, equivOperand))}; while (std::optional next{attempt(more).Parse(state)}) { result = std::move(next); } } return result; } } level5Expr; // R1022 expr -> [expr defined-binary-op] level-5-expr // Defined binary operators associate leftwards. template<> inline std::optional Parser::Parse(ParseState &state) { std::optional result{level5Expr.Parse(state)}; if (result) { std::function defBinOp{ [&result](DefinedOpName &&op, Expr &&right) { return Expr{Expr::DefinedBinary( std::move(op), std::move(result).value(), std::move(right))}; }}; auto more{applyLambda(defBinOp, definedOpName, level5Expr)}; while (std::optional next{attempt(more).Parse(state)}) { result = std::move(next); } } return result; } // R1028 specification-expr -> scalar-int-expr TYPE_PARSER(construct(scalarIntExpr)) // R1032 assignment-stmt -> variable = expr TYPE_CONTEXT_PARSER("assignment statement"_en_US, construct(variable / "=", expr)) // R1033 pointer-assignment-stmt -> // data-pointer-object [( bounds-spec-list )] => data-target | // data-pointer-object ( bounds-remapping-list ) => data-target | // proc-pointer-object => proc-target // R1034 data-pointer-object -> // variable-name | scalar-variable % data-pointer-component-name // C1022 a scalar-variable shall be a data-ref // C1024 a data-pointer-object shall not be a coindexed object // R1038 proc-pointer-object -> proc-pointer-name | proc-component-ref // // A distinction can't be made at the time of the initial parse between // data-pointer-object and proc-pointer-object, or between data-target // and proc-target. TYPE_CONTEXT_PARSER("pointer assignment statement"_en_US, construct(dataRef, parenthesized(nonemptyList(Parser{})), "=>" >> expr) || construct(dataRef, defaulted(parenthesized(nonemptyList(Parser{}))), "=>" >> expr)) // R1035 bounds-spec -> lower-bound-expr : TYPE_PARSER(construct(boundExpr / ":")) // R1036 bounds-remapping -> lower-bound-expr : upper-bound-expr TYPE_PARSER(construct(boundExpr / ":", boundExpr)) // R1039 proc-component-ref -> scalar-variable % procedure-component-name // C1027 the scalar-variable must be a data-ref without coindices. TYPE_PARSER(construct(structureComponent)) // R1041 where-stmt -> WHERE ( mask-expr ) where-assignment-stmt // R1045 where-assignment-stmt -> assignment-stmt // R1046 mask-expr -> logical-expr TYPE_CONTEXT_PARSER("WHERE statement"_en_US, construct("WHERE" >> parenthesized(logicalExpr), assignmentStmt)) // R1042 where-construct -> // where-construct-stmt [where-body-construct]... // [masked-elsewhere-stmt [where-body-construct]...]... // [elsewhere-stmt [where-body-construct]...] end-where-stmt TYPE_CONTEXT_PARSER("WHERE construct"_en_US, construct(statement(Parser{}), many(whereBodyConstruct), many(construct( statement(Parser{}), many(whereBodyConstruct))), maybe(construct( statement(Parser{}), many(whereBodyConstruct))), statement(Parser{}))) // R1043 where-construct-stmt -> [where-construct-name :] WHERE ( mask-expr ) TYPE_CONTEXT_PARSER("WHERE construct statement"_en_US, construct( maybe(name / ":"), "WHERE" >> parenthesized(logicalExpr))) // R1044 where-body-construct -> // where-assignment-stmt | where-stmt | where-construct TYPE_PARSER(construct(statement(assignmentStmt)) || construct(statement(whereStmt)) || construct(indirect(whereConstruct))) // R1047 masked-elsewhere-stmt -> // ELSEWHERE ( mask-expr ) [where-construct-name] TYPE_CONTEXT_PARSER("masked ELSEWHERE statement"_en_US, construct( "ELSE WHERE" >> parenthesized(logicalExpr), maybe(name))) // R1048 elsewhere-stmt -> ELSEWHERE [where-construct-name] TYPE_CONTEXT_PARSER("ELSEWHERE statement"_en_US, construct("ELSE WHERE" >> maybe(name))) // R1049 end-where-stmt -> ENDWHERE [where-construct-name] TYPE_CONTEXT_PARSER("END WHERE statement"_en_US, construct( recovery("END WHERE" >> maybe(name), endStmtErrorRecovery))) // R1050 forall-construct -> // forall-construct-stmt [forall-body-construct]... end-forall-stmt TYPE_CONTEXT_PARSER("FORALL construct"_en_US, construct(statement(Parser{}), many(Parser{}), statement(Parser{}))) // R1051 forall-construct-stmt -> // [forall-construct-name :] FORALL concurrent-header TYPE_CONTEXT_PARSER("FORALL construct statement"_en_US, construct( maybe(name / ":"), "FORALL" >> indirect(concurrentHeader))) // R1052 forall-body-construct -> // forall-assignment-stmt | where-stmt | where-construct | // forall-construct | forall-stmt TYPE_PARSER(construct(statement(forallAssignmentStmt)) || construct(statement(whereStmt)) || construct(whereConstruct) || construct(indirect(forallConstruct)) || construct(statement(forallStmt))) // R1053 forall-assignment-stmt -> assignment-stmt | pointer-assignment-stmt TYPE_PARSER(construct(assignmentStmt) || construct(pointerAssignmentStmt)) // R1054 end-forall-stmt -> END FORALL [forall-construct-name] TYPE_CONTEXT_PARSER("END FORALL statement"_en_US, construct( recovery("END FORALL" >> maybe(name), endStmtErrorRecovery))) // R1055 forall-stmt -> FORALL concurrent-header forall-assignment-stmt TYPE_CONTEXT_PARSER("FORALL statement"_en_US, construct( "FORALL" >> indirect(concurrentHeader), forallAssignmentStmt)) // R1101 block -> [execution-part-construct]... constexpr auto block{many(executionPartConstruct)}; // R1102 associate-construct -> associate-stmt block end-associate-stmt TYPE_CONTEXT_PARSER("ASSOCIATE construct"_en_US, construct(statement(Parser{}), block, statement(Parser{}))) // R1103 associate-stmt -> // [associate-construct-name :] ASSOCIATE ( association-list ) TYPE_CONTEXT_PARSER("ASSOCIATE statement"_en_US, construct(maybe(name / ":"), "ASSOCIATE" >> parenthesized(nonemptyList(Parser{})))) // R1104 association -> associate-name => selector TYPE_PARSER(construct(name, "=>" >> selector)) // R1105 selector -> expr | variable TYPE_PARSER(construct(variable) / lookAhead(","_tok || ")"_tok) || construct(expr)) // R1106 end-associate-stmt -> END ASSOCIATE [associate-construct-name] TYPE_PARSER(construct( recovery("END ASSOCIATE" >> maybe(name), endStmtErrorRecovery))) // R1107 block-construct -> // block-stmt [block-specification-part] block end-block-stmt TYPE_CONTEXT_PARSER("BLOCK construct"_en_US, construct(statement(Parser{}), Parser{}, // can be empty block, statement(Parser{}))) // R1108 block-stmt -> [block-construct-name :] BLOCK TYPE_PARSER(construct(maybe(name / ":") / "BLOCK")) // R1109 block-specification-part -> // [use-stmt]... [import-stmt]... [implicit-part] // [[declaration-construct]... specification-construct] // C1107 prohibits COMMON, EQUIVALENCE, INTENT, NAMELIST, OPTIONAL, VALUE, // and statement function definitions. C1108 prohibits SAVE /common/. // C1570 indirectly prohibits ENTRY. These constraints are best enforced later. // The odd grammar rule above would have the effect of forcing any // trailing FORMAT and DATA statements after the last specification-construct // to be recognized as part of the block-construct's block part rather than // its block-specification-part, a distinction without any apparent difference. TYPE_PARSER(construct(specificationPart)) // R1110 end-block-stmt -> END BLOCK [block-construct-name] TYPE_PARSER(construct( recovery("END BLOCK" >> maybe(name), endStmtErrorRecovery))) // R1111 change-team-construct -> change-team-stmt block end-change-team-stmt TYPE_CONTEXT_PARSER("CHANGE TEAM construct"_en_US, construct(statement(Parser{}), block, statement(Parser{}))) // R1112 change-team-stmt -> // [team-construct-name :] CHANGE TEAM // ( team-variable [, coarray-association-list] [, sync-stat-list] ) TYPE_CONTEXT_PARSER("CHANGE TEAM statement"_en_US, construct(maybe(name / ":"), "CHANGE TEAM"_sptok >> "("_tok >> teamVariable, defaulted("," >> nonemptyList(Parser{})), defaulted("," >> nonemptyList(statOrErrmsg))) / ")") // R1113 coarray-association -> codimension-decl => selector TYPE_PARSER( construct(Parser{}, "=>" >> selector)) // R1114 end-change-team-stmt -> // END TEAM [( [sync-stat-list] )] [team-construct-name] TYPE_CONTEXT_PARSER("END TEAM statement"_en_US, construct( "END TEAM" >> defaulted(parenthesized(optionalList(statOrErrmsg))), maybe(name))) // R1117 critical-stmt -> // [critical-construct-name :] CRITICAL [( [sync-stat-list] )] TYPE_CONTEXT_PARSER("CRITICAL statement"_en_US, construct(maybe(name / ":"), "CRITICAL" >> defaulted(parenthesized(optionalList(statOrErrmsg))))) // R1116 critical-construct -> critical-stmt block end-critical-stmt TYPE_CONTEXT_PARSER("CRITICAL construct"_en_US, construct(statement(Parser{}), block, statement(Parser{}))) // R1118 end-critical-stmt -> END CRITICAL [critical-construct-name] TYPE_PARSER(construct( recovery("END CRITICAL" >> maybe(name), endStmtErrorRecovery))) // R1119 do-construct -> do-stmt block end-do // R1120 do-stmt -> nonlabel-do-stmt | label-do-stmt TYPE_CONTEXT_PARSER("DO construct"_en_US, construct( statement(Parser{}) / EnterNonlabelDoConstruct{}, block, statement(Parser{}) / LeaveDoConstruct{})) // R1125 concurrent-header -> // ( [integer-type-spec ::] concurrent-control-list // [, scalar-mask-expr] ) TYPE_PARSER(parenthesized(construct( maybe(integerTypeSpec / "::"), nonemptyList(Parser{}), maybe("," >> scalarLogicalExpr)))) // R1126 concurrent-control -> // index-name = concurrent-limit : concurrent-limit [: concurrent-step] // R1127 concurrent-limit -> scalar-int-expr // R1128 concurrent-step -> scalar-int-expr TYPE_PARSER(construct(name / "=", scalarIntExpr / ":", scalarIntExpr, maybe(":" >> scalarIntExpr))) // R1130 locality-spec -> // LOCAL ( variable-name-list ) | LOCAL INIT ( variable-name-list ) | // SHARED ( variable-name-list ) | DEFAULT ( NONE ) TYPE_PARSER(construct(construct( "LOCAL" >> parenthesized(nonemptyList(name)))) || construct(construct( "LOCAL INIT"_sptok >> parenthesized(nonemptyList(name)))) || construct(construct( "SHARED" >> parenthesized(nonemptyList(name)))) || construct( construct("DEFAULT ( NONE )"_tok))) // R1123 loop-control -> // [,] do-variable = scalar-int-expr , scalar-int-expr // [, scalar-int-expr] | // [,] WHILE ( scalar-logical-expr ) | // [,] CONCURRENT concurrent-header concurrent-locality // R1129 concurrent-locality -> [locality-spec]... TYPE_CONTEXT_PARSER("loop control"_en_US, maybe(","_tok) >> (construct(loopBounds(scalarIntExpr)) || construct( "WHILE" >> parenthesized(scalarLogicalExpr)) || construct(construct( "CONCURRENT" >> concurrentHeader, many(Parser{}))))) // R1121 label-do-stmt -> [do-construct-name :] DO label [loop-control] TYPE_CONTEXT_PARSER("label DO statement"_en_US, construct( maybe(name / ":"), "DO" >> label, maybe(loopControl))) // R1122 nonlabel-do-stmt -> [do-construct-name :] DO [loop-control] TYPE_CONTEXT_PARSER("nonlabel DO statement"_en_US, construct(maybe(name / ":"), "DO" >> maybe(loopControl))) // R1132 end-do-stmt -> END DO [do-construct-name] TYPE_CONTEXT_PARSER("END DO statement"_en_US, construct( recovery("END DO" >> maybe(name), endStmtErrorRecovery))) // R1133 cycle-stmt -> CYCLE [do-construct-name] TYPE_CONTEXT_PARSER( "CYCLE statement"_en_US, construct("CYCLE" >> maybe(name))) // R1134 if-construct -> // if-then-stmt block [else-if-stmt block]... // [else-stmt block] end-if-stmt // R1135 if-then-stmt -> [if-construct-name :] IF ( scalar-logical-expr ) // THEN R1136 else-if-stmt -> // ELSE IF ( scalar-logical-expr ) THEN [if-construct-name] // R1137 else-stmt -> ELSE [if-construct-name] // R1138 end-if-stmt -> END IF [if-construct-name] TYPE_CONTEXT_PARSER("IF construct"_en_US, construct( statement(construct(maybe(name / ":"), "IF" >> parenthesized(scalarLogicalExpr) / "THEN")), block, many(construct( unambiguousStatement(construct( "ELSE IF" >> parenthesized(scalarLogicalExpr), "THEN" >> maybe(name))), block)), maybe(construct( statement(construct("ELSE" >> maybe(name))), block)), statement(construct( recovery("END IF" >> maybe(name), endStmtErrorRecovery))))) // R1139 if-stmt -> IF ( scalar-logical-expr ) action-stmt TYPE_CONTEXT_PARSER("IF statement"_en_US, construct("IF" >> parenthesized(scalarLogicalExpr), actionStmt)) // R1140 case-construct -> // select-case-stmt [case-stmt block]... end-select-stmt TYPE_CONTEXT_PARSER("SELECT CASE construct"_en_US, construct(statement(Parser{}), many(construct( unambiguousStatement(Parser{}), block)), statement(endSelectStmt))) // R1141 select-case-stmt -> [case-construct-name :] SELECT CASE ( case-expr // ) R1144 case-expr -> scalar-expr TYPE_CONTEXT_PARSER("SELECT CASE statement"_en_US, construct( maybe(name / ":"), "SELECT CASE" >> parenthesized(scalar(expr)))) // R1142 case-stmt -> CASE case-selector [case-construct-name] TYPE_CONTEXT_PARSER("CASE statement"_en_US, construct("CASE" >> Parser{}, maybe(name))) // R1143 end-select-stmt -> END SELECT [case-construct-name] // R1151 end-select-rank-stmt -> END SELECT [select-construct-name] // R1155 end-select-type-stmt -> END SELECT [select-construct-name] TYPE_PARSER(construct( recovery("END SELECT" >> maybe(name), endStmtErrorRecovery))) // R1145 case-selector -> ( case-value-range-list ) | DEFAULT constexpr auto defaultKeyword{construct("DEFAULT"_tok)}; TYPE_PARSER(parenthesized(construct( nonemptyList(Parser{}))) || construct(defaultKeyword)) // R1147 case-value -> scalar-constant-expr constexpr auto caseValue{scalar(constantExpr)}; // R1146 case-value-range -> // case-value | case-value : | : case-value | case-value : case-value TYPE_PARSER(construct(construct( construct>(caseValue), ":" >> maybe(caseValue))) || construct( construct(construct>(), ":" >> construct>(caseValue))) || construct(caseValue)) // R1148 select-rank-construct -> // select-rank-stmt [select-rank-case-stmt block]... // end-select-rank-stmt TYPE_CONTEXT_PARSER("SELECT RANK construct"_en_US, construct(statement(Parser{}), many(construct( unambiguousStatement(Parser{}), block)), statement(endSelectStmt))) // R1149 select-rank-stmt -> // [select-construct-name :] SELECT RANK // ( [associate-name =>] selector ) TYPE_CONTEXT_PARSER("SELECT RANK statement"_en_US, construct(maybe(name / ":"), "SELECT RANK"_sptok >> "("_tok >> maybe(name / "=>"), selector / ")")) // R1150 select-rank-case-stmt -> // RANK ( scalar-int-constant-expr ) [select-construct-name] | // RANK ( * ) [select-construct-name] | // RANK DEFAULT [select-construct-name] TYPE_CONTEXT_PARSER("RANK case statement"_en_US, "RANK" >> (construct( parenthesized(construct( scalarIntConstantExpr) || construct(star)) || construct(defaultKeyword), maybe(name)))) // R1152 select-type-construct -> // select-type-stmt [type-guard-stmt block]... end-select-type-stmt TYPE_CONTEXT_PARSER("SELECT TYPE construct"_en_US, construct(statement(Parser{}), many(construct( unambiguousStatement(Parser{}), block)), statement(endSelectStmt))) // R1153 select-type-stmt -> // [select-construct-name :] SELECT TYPE // ( [associate-name =>] selector ) TYPE_CONTEXT_PARSER("SELECT TYPE statement"_en_US, construct(maybe(name / ":"), "SELECT TYPE (" >> maybe(name / "=>"), selector / ")")) // R1154 type-guard-stmt -> // TYPE IS ( type-spec ) [select-construct-name] | // CLASS IS ( derived-type-spec ) [select-construct-name] | // CLASS DEFAULT [select-construct-name] TYPE_CONTEXT_PARSER("type guard statement"_en_US, construct("TYPE IS"_sptok >> parenthesized(construct(typeSpec)) || "CLASS IS"_sptok >> parenthesized(construct( derivedTypeSpec)) || construct("CLASS" >> defaultKeyword), maybe(name))) // R1156 exit-stmt -> EXIT [construct-name] TYPE_CONTEXT_PARSER( "EXIT statement"_en_US, construct("EXIT" >> maybe(name))) // R1157 goto-stmt -> GO TO label TYPE_CONTEXT_PARSER( "GOTO statement"_en_US, construct("GO TO" >> label)) // R1158 computed-goto-stmt -> GO TO ( label-list ) [,] scalar-int-expr TYPE_CONTEXT_PARSER("computed GOTO statement"_en_US, construct("GO TO" >> parenthesized(nonemptyList(label)), maybe(","_tok) >> scalarIntExpr)) // R1160 stop-stmt -> STOP [stop-code] [, QUIET = scalar-logical-expr] // R1161 error-stop-stmt -> // ERROR STOP [stop-code] [, QUIET = scalar-logical-expr] TYPE_CONTEXT_PARSER("STOP statement"_en_US, construct("STOP" >> pure(StopStmt::Kind::Stop) || "ERROR STOP"_sptok >> pure(StopStmt::Kind::ErrorStop), maybe(Parser{}), maybe(", QUIET =" >> scalarLogicalExpr))) // R1162 stop-code -> scalar-default-char-expr | scalar-int-expr TYPE_PARSER(construct(scalarDefaultCharExpr) || construct(scalarIntExpr)) // R1164 sync-all-stmt -> SYNC ALL [( [sync-stat-list] )] TYPE_CONTEXT_PARSER("SYNC ALL statement"_en_US, construct("SYNC ALL"_sptok >> defaulted(parenthesized(optionalList(statOrErrmsg))))) // R1166 sync-images-stmt -> SYNC IMAGES ( image-set [, sync-stat-list] ) // R1167 image-set -> int-expr | * TYPE_CONTEXT_PARSER("SYNC IMAGES statement"_en_US, "SYNC IMAGES"_sptok >> parenthesized(construct( construct(intExpr) || construct(star), defaulted("," >> nonemptyList(statOrErrmsg))))) // R1168 sync-memory-stmt -> SYNC MEMORY [( [sync-stat-list] )] TYPE_CONTEXT_PARSER("SYNC MEMORY statement"_en_US, construct("SYNC MEMORY"_sptok >> defaulted(parenthesized(optionalList(statOrErrmsg))))) // R1169 sync-team-stmt -> SYNC TEAM ( team-variable [, sync-stat-list] ) TYPE_CONTEXT_PARSER("SYNC TEAM statement"_en_US, construct("SYNC TEAM"_sptok >> "("_tok >> teamVariable, defaulted("," >> nonemptyList(statOrErrmsg)) / ")")) // R1170 event-post-stmt -> EVENT POST ( event-variable [, sync-stat-list] ) // R1171 event-variable -> scalar-variable TYPE_CONTEXT_PARSER("EVENT POST statement"_en_US, construct("EVENT POST"_sptok >> "("_tok >> scalar(variable), defaulted("," >> nonemptyList(statOrErrmsg)) / ")")) // R1172 event-wait-stmt -> // EVENT WAIT ( event-variable [, event-wait-spec-list] ) TYPE_CONTEXT_PARSER("EVENT WAIT statement"_en_US, construct("EVENT WAIT"_sptok >> "("_tok >> scalar(variable), defaulted("," >> nonemptyList(Parser{})) / ")")) // R1174 until-spec -> UNTIL_COUNT = scalar-int-expr constexpr auto untilSpec{"UNTIL_COUNT =" >> scalarIntExpr}; // R1173 event-wait-spec -> until-spec | sync-stat TYPE_PARSER(construct(untilSpec) || construct(statOrErrmsg)) // R1175 form-team-stmt -> // FORM TEAM ( team-number , team-variable [, form-team-spec-list] ) // R1176 team-number -> scalar-int-expr TYPE_CONTEXT_PARSER("FORM TEAM statement"_en_US, construct("FORM TEAM"_sptok >> "("_tok >> scalarIntExpr, "," >> teamVariable, defaulted("," >> nonemptyList(Parser{})) / ")")) // R1177 form-team-spec -> NEW_INDEX = scalar-int-expr | sync-stat TYPE_PARSER( construct("NEW_INDEX =" >> scalarIntExpr) || construct(statOrErrmsg)) // R1181 lock-variable -> scalar-variable constexpr auto lockVariable{scalar(variable)}; // R1178 lock-stmt -> LOCK ( lock-variable [, lock-stat-list] ) TYPE_CONTEXT_PARSER("LOCK statement"_en_US, construct("LOCK (" >> lockVariable, defaulted("," >> nonemptyList(Parser{})) / ")")) // R1179 lock-stat -> ACQUIRED_LOCK = scalar-logical-variable | sync-stat TYPE_PARSER( construct("ACQUIRED_LOCK =" >> scalarLogicalVariable) || construct(statOrErrmsg)) // R1180 unlock-stmt -> UNLOCK ( lock-variable [, sync-stat-list] ) TYPE_CONTEXT_PARSER("UNLOCK statement"_en_US, construct("UNLOCK (" >> lockVariable, defaulted("," >> nonemptyList(statOrErrmsg)) / ")")) // R1201 io-unit -> file-unit-number | * | internal-file-variable // R1203 internal-file-variable -> char-variable TYPE_PARSER(construct(fileUnitNumber) || construct(star) || construct(charVariable / !"="_tok)) // R1202 file-unit-number -> scalar-int-expr TYPE_PARSER(construct(scalarIntExpr / !"="_tok)) // R1204 open-stmt -> OPEN ( connect-spec-list ) TYPE_CONTEXT_PARSER("OPEN statement"_en_US, construct("OPEN (" >> nonemptyList(Parser{}) / ")")) // R1206 file-name-expr -> scalar-default-char-expr constexpr auto fileNameExpr{scalarDefaultCharExpr}; // R1205 connect-spec -> // [UNIT =] file-unit-number | ACCESS = scalar-default-char-expr | // ACTION = scalar-default-char-expr | // ASYNCHRONOUS = scalar-default-char-expr | // BLANK = scalar-default-char-expr | // DECIMAL = scalar-default-char-expr | // DELIM = scalar-default-char-expr | // ENCODING = scalar-default-char-expr | ERR = label | // FILE = file-name-expr | FORM = scalar-default-char-expr | // IOMSG = iomsg-variable | IOSTAT = scalar-int-variable | // NEWUNIT = scalar-int-variable | PAD = scalar-default-char-expr | // POSITION = scalar-default-char-expr | RECL = scalar-int-expr | // ROUND = scalar-default-char-expr | SIGN = scalar-default-char-expr | // STATUS = scalar-default-char-expr constexpr auto statusExpr{construct(scalarDefaultCharExpr)}; constexpr auto errLabel{construct(label)}; TYPE_PARSER(first(construct(maybe("UNIT ="_tok) >> fileUnitNumber), construct(construct( "ACCESS =" >> pure(ConnectSpec::CharExpr::Kind::Access), scalarDefaultCharExpr)), construct(construct( "ACTION =" >> pure(ConnectSpec::CharExpr::Kind::Action), scalarDefaultCharExpr)), construct(construct( "ASYNCHRONOUS =" >> pure(ConnectSpec::CharExpr::Kind::Asynchronous), scalarDefaultCharExpr)), construct(construct( "BLANK =" >> pure(ConnectSpec::CharExpr::Kind::Blank), scalarDefaultCharExpr)), construct(construct( "DECIMAL =" >> pure(ConnectSpec::CharExpr::Kind::Decimal), scalarDefaultCharExpr)), construct(construct( "DELIM =" >> pure(ConnectSpec::CharExpr::Kind::Delim), scalarDefaultCharExpr)), construct(construct( "ENCODING =" >> pure(ConnectSpec::CharExpr::Kind::Encoding), scalarDefaultCharExpr)), construct("ERR =" >> errLabel), construct("FILE =" >> fileNameExpr), extension( construct("NAME =" >> fileNameExpr)), construct(construct( "FORM =" >> pure(ConnectSpec::CharExpr::Kind::Form), scalarDefaultCharExpr)), construct("IOMSG =" >> msgVariable), construct("IOSTAT =" >> statVariable), construct(construct( "NEWUNIT =" >> scalar(integer(variable)))), construct(construct( "PAD =" >> pure(ConnectSpec::CharExpr::Kind::Pad), scalarDefaultCharExpr)), construct(construct( "POSITION =" >> pure(ConnectSpec::CharExpr::Kind::Position), scalarDefaultCharExpr)), construct( construct("RECL =" >> scalarIntExpr)), construct(construct( "ROUND =" >> pure(ConnectSpec::CharExpr::Kind::Round), scalarDefaultCharExpr)), construct(construct( "SIGN =" >> pure(ConnectSpec::CharExpr::Kind::Sign), scalarDefaultCharExpr)), construct("STATUS =" >> statusExpr), extension( construct(construct( "CONVERT =" >> pure(ConnectSpec::CharExpr::Kind::Convert), scalarDefaultCharExpr))), extension( construct(construct( "DISPOSE =" >> pure(ConnectSpec::CharExpr::Kind::Dispose), scalarDefaultCharExpr))))) // R1209 close-spec -> // [UNIT =] file-unit-number | IOSTAT = scalar-int-variable | // IOMSG = iomsg-variable | ERR = label | // STATUS = scalar-default-char-expr constexpr auto closeSpec{first( construct(maybe("UNIT ="_tok) >> fileUnitNumber), construct("IOSTAT =" >> statVariable), construct("IOMSG =" >> msgVariable), construct("ERR =" >> errLabel), construct("STATUS =" >> statusExpr))}; // R1208 close-stmt -> CLOSE ( close-spec-list ) TYPE_CONTEXT_PARSER("CLOSE statement"_en_US, construct("CLOSE" >> parenthesized(nonemptyList(closeSpec)))) // R1210 read-stmt -> // READ ( io-control-spec-list ) [input-item-list] | // READ format [, input-item-list] constexpr auto inputItemList{ extension( some("," >> inputItem)) || // legacy extension: leading comma optionalList(inputItem)}; TYPE_CONTEXT_PARSER("READ statement"_en_US, construct("READ (" >> construct>(maybe("UNIT ="_tok) >> ioUnit), "," >> construct>(format), defaulted("," >> nonemptyList(ioControlSpec)) / ")", inputItemList) || construct( "READ (" >> construct>(ioUnit), construct>(), defaulted("," >> nonemptyList(ioControlSpec)) / ")", inputItemList) || construct("READ" >> construct>(), construct>(), parenthesized(nonemptyList(ioControlSpec)), inputItemList) || construct("READ" >> construct>(), construct>(format), construct>(), many("," >> inputItem))) // R1214 id-variable -> scalar-int-variable constexpr auto idVariable{construct(scalarIntVariable)}; // R1213 io-control-spec -> // [UNIT =] io-unit | [FMT =] format | [NML =] namelist-group-name | // ADVANCE = scalar-default-char-expr | // ASYNCHRONOUS = scalar-default-char-constant-expr | // BLANK = scalar-default-char-expr | // DECIMAL = scalar-default-char-expr | // DELIM = scalar-default-char-expr | END = label | EOR = label | // ERR = label | ID = id-variable | IOMSG = iomsg-variable | // IOSTAT = scalar-int-variable | PAD = scalar-default-char-expr | // POS = scalar-int-expr | REC = scalar-int-expr | // ROUND = scalar-default-char-expr | SIGN = scalar-default-char-expr | // SIZE = scalar-int-variable constexpr auto endLabel{construct(label)}; constexpr auto eorLabel{construct(label)}; TYPE_PARSER(first(construct("UNIT =" >> ioUnit), construct("FMT =" >> format), construct("NML =" >> name), construct( "ADVANCE =" >> construct( pure(IoControlSpec::CharExpr::Kind::Advance), scalarDefaultCharExpr)), construct(construct( "ASYNCHRONOUS =" >> scalarDefaultCharConstantExpr)), construct("BLANK =" >> construct( pure(IoControlSpec::CharExpr::Kind::Blank), scalarDefaultCharExpr)), construct( "DECIMAL =" >> construct( pure(IoControlSpec::CharExpr::Kind::Decimal), scalarDefaultCharExpr)), construct("DELIM =" >> construct( pure(IoControlSpec::CharExpr::Kind::Delim), scalarDefaultCharExpr)), construct("END =" >> endLabel), construct("EOR =" >> eorLabel), construct("ERR =" >> errLabel), construct("ID =" >> idVariable), construct("IOMSG = " >> msgVariable), construct("IOSTAT = " >> statVariable), construct("PAD =" >> construct( pure(IoControlSpec::CharExpr::Kind::Pad), scalarDefaultCharExpr)), construct( "POS =" >> construct(scalarIntExpr)), construct( "REC =" >> construct(scalarIntExpr)), construct("ROUND =" >> construct( pure(IoControlSpec::CharExpr::Kind::Round), scalarDefaultCharExpr)), construct("SIGN =" >> construct( pure(IoControlSpec::CharExpr::Kind::Sign), scalarDefaultCharExpr)), construct( "SIZE =" >> construct(scalarIntVariable)))) // R1211 write-stmt -> WRITE ( io-control-spec-list ) [output-item-list] constexpr auto outputItemList{ extension( some("," >> outputItem)) || // legacy: allow leading comma optionalList(outputItem)}; TYPE_CONTEXT_PARSER("WRITE statement"_en_US, construct("WRITE (" >> construct>(maybe("UNIT ="_tok) >> ioUnit), "," >> construct>(format), defaulted("," >> nonemptyList(ioControlSpec)) / ")", outputItemList) || construct( "WRITE (" >> construct>(ioUnit), construct>(), defaulted("," >> nonemptyList(ioControlSpec)) / ")", outputItemList) || construct("WRITE" >> construct>(), construct>(), parenthesized(nonemptyList(ioControlSpec)), outputItemList)) // R1212 print-stmt PRINT format [, output-item-list] TYPE_CONTEXT_PARSER("PRINT statement"_en_US, construct( "PRINT" >> format, defaulted("," >> nonemptyList(outputItem)))) // R1215 format -> default-char-expr | label | * TYPE_PARSER(construct(defaultCharExpr / !"="_tok) || construct(label) || construct(star)) // R1216 input-item -> variable | io-implied-do TYPE_PARSER(construct(variable) || construct(indirect(inputImpliedDo))) // R1217 output-item -> expr | io-implied-do TYPE_PARSER(construct(expr) || construct(indirect(outputImpliedDo))) // R1220 io-implied-do-control -> // do-variable = scalar-int-expr , scalar-int-expr [, scalar-int-expr] constexpr auto ioImpliedDoControl{loopBounds(scalarIntExpr)}; // R1218 io-implied-do -> ( io-implied-do-object-list , io-implied-do-control ) // R1219 io-implied-do-object -> input-item | output-item TYPE_CONTEXT_PARSER("input implied DO"_en_US, parenthesized( construct(nonemptyList(inputItem / lookAhead(","_tok)), "," >> ioImpliedDoControl))) TYPE_CONTEXT_PARSER("output implied DO"_en_US, parenthesized(construct( nonemptyList(outputItem / lookAhead(","_tok)), "," >> ioImpliedDoControl))) // R1222 wait-stmt -> WAIT ( wait-spec-list ) TYPE_CONTEXT_PARSER("WAIT statement"_en_US, "WAIT" >> parenthesized(construct(nonemptyList(Parser{})))) // R1223 wait-spec -> // [UNIT =] file-unit-number | END = label | EOR = label | ERR = label | // ID = scalar-int-expr | IOMSG = iomsg-variable | // IOSTAT = scalar-int-variable constexpr auto idExpr{construct(scalarIntExpr)}; TYPE_PARSER(first(construct(maybe("UNIT ="_tok) >> fileUnitNumber), construct("END =" >> endLabel), construct("EOR =" >> eorLabel), construct("ERR =" >> errLabel), construct("ID =" >> idExpr), construct("IOMSG =" >> msgVariable), construct("IOSTAT =" >> statVariable))) template std::list singletonList(A &&x) { std::list result; result.push_front(std::move(x)); return result; } constexpr auto bareUnitNumberAsList{ applyFunction(singletonList, construct(fileUnitNumber))}; constexpr auto positionOrFlushSpecList{ parenthesized(nonemptyList(positionOrFlushSpec)) || bareUnitNumberAsList}; // R1224 backspace-stmt -> // BACKSPACE file-unit-number | BACKSPACE ( position-spec-list ) TYPE_CONTEXT_PARSER("BACKSPACE statement"_en_US, construct("BACKSPACE" >> positionOrFlushSpecList)) // R1225 endfile-stmt -> // ENDFILE file-unit-number | ENDFILE ( position-spec-list ) TYPE_CONTEXT_PARSER("ENDFILE statement"_en_US, construct("ENDFILE" >> positionOrFlushSpecList)) // R1226 rewind-stmt -> REWIND file-unit-number | REWIND ( position-spec-list ) TYPE_CONTEXT_PARSER("REWIND statement"_en_US, construct("REWIND" >> positionOrFlushSpecList)) // R1227 position-spec -> // [UNIT =] file-unit-number | IOMSG = iomsg-variable | // IOSTAT = scalar-int-variable | ERR = label // R1229 flush-spec -> // [UNIT =] file-unit-number | IOSTAT = scalar-int-variable | // IOMSG = iomsg-variable | ERR = label TYPE_PARSER( construct(maybe("UNIT ="_tok) >> fileUnitNumber) || construct("IOMSG =" >> msgVariable) || construct("IOSTAT =" >> statVariable) || construct("ERR =" >> errLabel)) // R1228 flush-stmt -> FLUSH file-unit-number | FLUSH ( flush-spec-list ) TYPE_CONTEXT_PARSER("FLUSH statement"_en_US, construct("FLUSH" >> positionOrFlushSpecList)) // R1231 inquire-spec -> // [UNIT =] file-unit-number | FILE = file-name-expr | // ACCESS = scalar-default-char-variable | // ACTION = scalar-default-char-variable | // ASYNCHRONOUS = scalar-default-char-variable | // BLANK = scalar-default-char-variable | // DECIMAL = scalar-default-char-variable | // DELIM = scalar-default-char-variable | // ENCODING = scalar-default-char-variable | // ERR = label | EXIST = scalar-logical-variable | // FORM = scalar-default-char-variable | // FORMATTED = scalar-default-char-variable | // ID = scalar-int-expr | IOMSG = iomsg-variable | // IOSTAT = scalar-int-variable | // NAME = scalar-default-char-variable | // NAMED = scalar-logical-variable | // NEXTREC = scalar-int-variable | NUMBER = scalar-int-variable | // OPENED = scalar-logical-variable | // PAD = scalar-default-char-variable | // PENDING = scalar-logical-variable | POS = scalar-int-variable | // POSITION = scalar-default-char-variable | // READ = scalar-default-char-variable | // READWRITE = scalar-default-char-variable | // RECL = scalar-int-variable | ROUND = scalar-default-char-variable | // SEQUENTIAL = scalar-default-char-variable | // SIGN = scalar-default-char-variable | // SIZE = scalar-int-variable | // STREAM = scalar-default-char-variable | // STATUS = scalar-default-char-variable | // WRITE = scalar-default-char-variable TYPE_PARSER(first(construct(maybe("UNIT ="_tok) >> fileUnitNumber), construct("FILE =" >> fileNameExpr), construct( "ACCESS =" >> construct( pure(InquireSpec::CharVar::Kind::Access), scalarDefaultCharVariable)), construct( "ACTION =" >> construct( pure(InquireSpec::CharVar::Kind::Action), scalarDefaultCharVariable)), construct( "ASYNCHRONOUS =" >> construct( pure(InquireSpec::CharVar::Kind::Asynchronous), scalarDefaultCharVariable)), construct("BLANK =" >> construct(pure(InquireSpec::CharVar::Kind::Blank), scalarDefaultCharVariable)), construct( "DECIMAL =" >> construct( pure(InquireSpec::CharVar::Kind::Decimal), scalarDefaultCharVariable)), construct("DELIM =" >> construct(pure(InquireSpec::CharVar::Kind::Delim), scalarDefaultCharVariable)), construct( "DIRECT =" >> construct( pure(InquireSpec::CharVar::Kind::Direct), scalarDefaultCharVariable)), construct( "ENCODING =" >> construct( pure(InquireSpec::CharVar::Kind::Encoding), scalarDefaultCharVariable)), construct("ERR =" >> errLabel), construct("EXIST =" >> construct( pure(InquireSpec::LogVar::Kind::Exist), scalarLogicalVariable)), construct("FORM =" >> construct( pure(InquireSpec::CharVar::Kind::Form), scalarDefaultCharVariable)), construct( "FORMATTED =" >> construct( pure(InquireSpec::CharVar::Kind::Formatted), scalarDefaultCharVariable)), construct("ID =" >> idExpr), construct("IOMSG =" >> construct(pure(InquireSpec::CharVar::Kind::Iomsg), scalarDefaultCharVariable)), construct("IOSTAT =" >> construct(pure(InquireSpec::IntVar::Kind::Iostat), scalar(integer(variable)))), construct("NAME =" >> construct( pure(InquireSpec::CharVar::Kind::Name), scalarDefaultCharVariable)), construct("NAMED =" >> construct( pure(InquireSpec::LogVar::Kind::Named), scalarLogicalVariable)), construct("NEXTREC =" >> construct(pure(InquireSpec::IntVar::Kind::Nextrec), scalar(integer(variable)))), construct("NUMBER =" >> construct(pure(InquireSpec::IntVar::Kind::Number), scalar(integer(variable)))), construct("OPENED =" >> construct( pure(InquireSpec::LogVar::Kind::Opened), scalarLogicalVariable)), construct("PAD =" >> construct( pure(InquireSpec::CharVar::Kind::Pad), scalarDefaultCharVariable)), construct("PENDING =" >> construct( pure(InquireSpec::LogVar::Kind::Pending), scalarLogicalVariable)), construct("POS =" >> construct( pure(InquireSpec::IntVar::Kind::Pos), scalar(integer(variable)))), construct( "POSITION =" >> construct( pure(InquireSpec::CharVar::Kind::Position), scalarDefaultCharVariable)), construct("READ =" >> construct( pure(InquireSpec::CharVar::Kind::Read), scalarDefaultCharVariable)), construct( "READWRITE =" >> construct( pure(InquireSpec::CharVar::Kind::Readwrite), scalarDefaultCharVariable)), construct("RECL =" >> construct( pure(InquireSpec::IntVar::Kind::Recl), scalar(integer(variable)))), construct("ROUND =" >> construct(pure(InquireSpec::CharVar::Kind::Round), scalarDefaultCharVariable)), construct( "SEQUENTIAL =" >> construct( pure(InquireSpec::CharVar::Kind::Sequential), scalarDefaultCharVariable)), construct("SIGN =" >> construct( pure(InquireSpec::CharVar::Kind::Sign), scalarDefaultCharVariable)), construct("SIZE =" >> construct( pure(InquireSpec::IntVar::Kind::Size), scalar(integer(variable)))), construct( "STREAM =" >> construct( pure(InquireSpec::CharVar::Kind::Stream), scalarDefaultCharVariable)), construct( "STATUS =" >> construct( pure(InquireSpec::CharVar::Kind::Status), scalarDefaultCharVariable)), construct( "UNFORMATTED =" >> construct( pure(InquireSpec::CharVar::Kind::Unformatted), scalarDefaultCharVariable)), construct("WRITE =" >> construct(pure(InquireSpec::CharVar::Kind::Write), scalarDefaultCharVariable)))) // R1230 inquire-stmt -> // INQUIRE ( inquire-spec-list ) | // INQUIRE ( IOLENGTH = scalar-int-variable ) output-item-list TYPE_CONTEXT_PARSER("INQUIRE statement"_en_US, "INQUIRE" >> (construct( parenthesized(nonemptyList(Parser{}))) || construct(construct( parenthesized("IOLENGTH =" >> scalar(integer(variable))), nonemptyList(outputItem))))) // R1301 format-stmt -> FORMAT format-specification // 13.2.1 allows spaces to appear "at any point" within a format specification // without effect, except of course within a character string edit descriptor. TYPE_CONTEXT_PARSER("FORMAT statement"_en_US, construct("FORMAT" >> Parser{})) // R1321 char-string-edit-desc // N.B. C1313 disallows any kind parameter on the character literal. constexpr auto charStringEditDesc{ space >> (charLiteralConstantWithoutKind || rawHollerithLiteral)}; // R1303 format-items -> format-item [[,] format-item]... constexpr auto formatItems{ nonemptySeparated(space >> Parser{}, maybe(","_tok))}; // R1306 r -> digit-string constexpr DigitStringIgnoreSpaces repeat; // R1304 format-item -> // [r] data-edit-desc | control-edit-desc | char-string-edit-desc | // [r] ( format-items ) TYPE_PARSER(construct( maybe(repeat), Parser{}) || construct( maybe(repeat), Parser{}) || construct(Parser{}) || construct(charStringEditDesc) || construct(maybe(repeat), parenthesized(formatItems))) // R1302 format-specification -> // ( [format-items] ) | ( [format-items ,] unlimited-format-item ) // R1305 unlimited-format-item -> * ( format-items ) // minor extension: the comma is optional before the unlimited-format-item TYPE_PARSER(parenthesized(construct( defaulted(formatItems / maybe(","_tok)), "*" >> parenthesized(formatItems)) || construct(defaulted(formatItems)))) // R1308 w -> digit-string // R1309 m -> digit-string // R1310 d -> digit-string // R1311 e -> digit-string constexpr auto width{repeat}; constexpr auto mandatoryWidth{construct>(width)}; constexpr auto digits{repeat}; constexpr auto noInt{construct>()}; constexpr auto mandatoryDigits{construct>("." >> width)}; // R1307 data-edit-desc -> // I w [. m] | B w [. m] | O w [. m] | Z w [. m] | F w . d | // E w . d [E e] | EN w . d [E e] | ES w . d [E e] | EX w . d [E e] | // G w [. d [E e]] | L w | A [w] | D w . d | // DT [char-literal-constant] [( v-list )] // (part 1 of 2) TYPE_PARSER(construct( "I" >> pure(format::IntrinsicTypeDataEditDesc::Kind::I) || "B" >> pure(format::IntrinsicTypeDataEditDesc::Kind::B) || "O" >> pure(format::IntrinsicTypeDataEditDesc::Kind::O) || "Z" >> pure(format::IntrinsicTypeDataEditDesc::Kind::Z), mandatoryWidth, maybe("." >> digits), noInt) || construct( "F" >> pure(format::IntrinsicTypeDataEditDesc::Kind::F) || "D" >> pure(format::IntrinsicTypeDataEditDesc::Kind::D), mandatoryWidth, mandatoryDigits, noInt) || construct( "E" >> ("N" >> pure(format::IntrinsicTypeDataEditDesc::Kind::EN) || "S" >> pure(format::IntrinsicTypeDataEditDesc::Kind::ES) || "X" >> pure(format::IntrinsicTypeDataEditDesc::Kind::EX) || pure(format::IntrinsicTypeDataEditDesc::Kind::E)), mandatoryWidth, mandatoryDigits, maybe("E" >> digits)) || construct( "G" >> pure(format::IntrinsicTypeDataEditDesc::Kind::G), mandatoryWidth, mandatoryDigits, maybe("E" >> digits)) || construct( "G" >> pure(format::IntrinsicTypeDataEditDesc::Kind::G) || "L" >> pure(format::IntrinsicTypeDataEditDesc::Kind::L), mandatoryWidth, noInt, noInt) || construct( "A" >> pure(format::IntrinsicTypeDataEditDesc::Kind::A), maybe(width), noInt, noInt) || // PGI/Intel extension: omitting width (and all else that follows) extension( construct( "I" >> pure(format::IntrinsicTypeDataEditDesc::Kind::I) || ("B"_tok / !letter /* don't occlude BN & BZ */) >> pure(format::IntrinsicTypeDataEditDesc::Kind::B) || "O" >> pure(format::IntrinsicTypeDataEditDesc::Kind::O) || "Z" >> pure(format::IntrinsicTypeDataEditDesc::Kind::Z) || "F" >> pure(format::IntrinsicTypeDataEditDesc::Kind::F) || ("D"_tok / !letter /* don't occlude DT, DC, & DP */) >> pure(format::IntrinsicTypeDataEditDesc::Kind::D) || "E" >> ("N" >> pure(format::IntrinsicTypeDataEditDesc::Kind::EN) || "S" >> pure(format::IntrinsicTypeDataEditDesc::Kind::ES) || "X" >> pure(format::IntrinsicTypeDataEditDesc::Kind::EX) || pure(format::IntrinsicTypeDataEditDesc::Kind::E)) || "G" >> pure(format::IntrinsicTypeDataEditDesc::Kind::G) || "L" >> pure(format::IntrinsicTypeDataEditDesc::Kind::L), noInt, noInt, noInt))) // R1307 data-edit-desc (part 2 of 2) // R1312 v -> [sign] digit-string constexpr SignedDigitStringIgnoreSpaces scaleFactor; TYPE_PARSER(construct( "D" >> "T"_tok >> defaulted(charLiteralConstantWithoutKind), defaulted(parenthesized(nonemptyList(scaleFactor))))) // R1314 k -> [sign] digit-string constexpr PositiveDigitStringIgnoreSpaces count; // R1313 control-edit-desc -> // position-edit-desc | [r] / | : | sign-edit-desc | k P | // blank-interp-edit-desc | round-edit-desc | decimal-edit-desc // R1315 position-edit-desc -> T n | TL n | TR n | n X // R1316 n -> digit-string // R1317 sign-edit-desc -> SS | SP | S // R1318 blank-interp-edit-desc -> BN | BZ // R1319 round-edit-desc -> RU | RD | RZ | RN | RC | RP // R1320 decimal-edit-desc -> DC | DP TYPE_PARSER(construct( "T" >> ("L" >> pure(format::ControlEditDesc::Kind::TL) || "R" >> pure(format::ControlEditDesc::Kind::TR) || pure(format::ControlEditDesc::Kind::T)), count) || construct(count, "X" >> pure(format::ControlEditDesc::Kind::X) || "/" >> pure(format::ControlEditDesc::Kind::Slash)) || construct( "X" >> pure(format::ControlEditDesc::Kind::X) || "/" >> pure(format::ControlEditDesc::Kind::Slash)) || construct( scaleFactor, "P" >> pure(format::ControlEditDesc::Kind::P)) || construct( ":" >> pure(format::ControlEditDesc::Kind::Colon)) || "S" >> ("S" >> construct( pure(format::ControlEditDesc::Kind::SS)) || "P" >> construct( pure(format::ControlEditDesc::Kind::SP)) || construct( pure(format::ControlEditDesc::Kind::S))) || "B" >> ("N" >> construct( pure(format::ControlEditDesc::Kind::BN)) || "Z" >> construct( pure(format::ControlEditDesc::Kind::BZ))) || "R" >> ("U" >> construct( pure(format::ControlEditDesc::Kind::RU)) || "D" >> construct( pure(format::ControlEditDesc::Kind::RD)) || "Z" >> construct( pure(format::ControlEditDesc::Kind::RZ)) || "N" >> construct( pure(format::ControlEditDesc::Kind::RN)) || "C" >> construct( pure(format::ControlEditDesc::Kind::RC)) || "P" >> construct( pure(format::ControlEditDesc::Kind::RP))) || "D" >> ("C" >> construct( pure(format::ControlEditDesc::Kind::DC)) || "P" >> construct( pure(format::ControlEditDesc::Kind::DP)))) // R1401 main-program -> // [program-stmt] [specification-part] [execution-part] // [internal-subprogram-part] end-program-stmt TYPE_CONTEXT_PARSER("main program"_en_US, construct(maybe(statement(Parser{})), specificationPart, executionPart, maybe(internalSubprogramPart), unterminatedStatement(Parser{}))) // R1402 program-stmt -> PROGRAM program-name // PGI allows empty parentheses after the name. TYPE_CONTEXT_PARSER("PROGRAM statement"_en_US, construct("PROGRAM" >> name / maybe(extension( parenthesized(ok))))) // R1403 end-program-stmt -> END [PROGRAM [program-name]] TYPE_CONTEXT_PARSER("END PROGRAM statement"_en_US, construct(recovery( "END PROGRAM" >> maybe(name) || bareEnd, progUnitEndStmtErrorRecovery))) // R1404 module -> // module-stmt [specification-part] [module-subprogram-part] // end-module-stmt TYPE_CONTEXT_PARSER("module"_en_US, construct(statement(Parser{}), limitedSpecificationPart, maybe(Parser{}), unterminatedStatement(Parser{}))) // R1405 module-stmt -> MODULE module-name TYPE_CONTEXT_PARSER( "MODULE statement"_en_US, construct("MODULE" >> name)) // R1406 end-module-stmt -> END [MODULE [module-name]] TYPE_CONTEXT_PARSER("END MODULE statement"_en_US, construct(recovery( "END MODULE" >> maybe(name) || bareEnd, progUnitEndStmtErrorRecovery))) // R1407 module-subprogram-part -> contains-stmt [module-subprogram]... TYPE_CONTEXT_PARSER("module subprogram part"_en_US, construct(statement(containsStmt), many(StartNewSubprogram{} >> Parser{}))) // R1408 module-subprogram -> // function-subprogram | subroutine-subprogram | // separate-module-subprogram TYPE_PARSER(construct(indirect(functionSubprogram)) || construct(indirect(subroutineSubprogram)) || construct(indirect(Parser{}))) // R1410 module-nature -> INTRINSIC | NON_INTRINSIC constexpr auto moduleNature{ "INTRINSIC" >> pure(UseStmt::ModuleNature::Intrinsic) || "NON_INTRINSIC" >> pure(UseStmt::ModuleNature::Non_Intrinsic)}; // R1409 use-stmt -> // USE [[, module-nature] ::] module-name [, rename-list] | // USE [[, module-nature] ::] module-name , ONLY : [only-list] TYPE_PARSER(construct("USE" >> optionalBeforeColons(moduleNature), name, ", ONLY :" >> optionalList(Parser{})) || construct("USE" >> optionalBeforeColons(moduleNature), name, defaulted("," >> nonemptyList(Parser{})))) // R1411 rename -> // local-name => use-name | // OPERATOR ( local-defined-operator ) => // OPERATOR ( use-defined-operator ) TYPE_PARSER(construct("OPERATOR (" >> construct( definedOpName / ") => OPERATOR (", definedOpName / ")")) || construct(construct(name, "=>" >> name))) // R1412 only -> generic-spec | only-use-name | rename // R1413 only-use-name -> use-name TYPE_PARSER(construct(Parser{}) || construct(indirect(genericSpec)) || construct(name)) // TODO: ambiguous, accepted by genericSpec // R1416 submodule -> // submodule-stmt [specification-part] [module-subprogram-part] // end-submodule-stmt TYPE_CONTEXT_PARSER("submodule"_en_US, construct(statement(Parser{}), limitedSpecificationPart, maybe(Parser{}), unterminatedStatement(Parser{}))) // R1417 submodule-stmt -> SUBMODULE ( parent-identifier ) submodule-name TYPE_CONTEXT_PARSER("SUBMODULE statement"_en_US, construct( "SUBMODULE" >> parenthesized(Parser{}), name)) // R1418 parent-identifier -> ancestor-module-name [: parent-submodule-name] TYPE_PARSER(construct(name, maybe(":" >> name))) // R1419 end-submodule-stmt -> END [SUBMODULE [submodule-name]] TYPE_CONTEXT_PARSER("END SUBMODULE statement"_en_US, construct( recovery("END SUBMODULE" >> maybe(name) || bareEnd, progUnitEndStmtErrorRecovery))) // R1420 block-data -> block-data-stmt [specification-part] end-block-data-stmt TYPE_CONTEXT_PARSER("BLOCK DATA subprogram"_en_US, construct(statement(Parser{}), limitedSpecificationPart, unterminatedStatement(Parser{}))) // R1421 block-data-stmt -> BLOCK DATA [block-data-name] TYPE_CONTEXT_PARSER("BLOCK DATA statement"_en_US, construct("BLOCK DATA" >> maybe(name))) // R1422 end-block-data-stmt -> END [BLOCK DATA [block-data-name]] TYPE_CONTEXT_PARSER("END BLOCK DATA statement"_en_US, construct( recovery("END BLOCK DATA" >> maybe(name) || bareEnd, progUnitEndStmtErrorRecovery))) // R1501 interface-block -> // interface-stmt [interface-specification]... end-interface-stmt TYPE_PARSER(construct(statement(Parser{}), many(Parser{}), statement(Parser{}))) // R1502 interface-specification -> interface-body | procedure-stmt TYPE_PARSER(construct(Parser{}) || construct(statement(Parser{}))) // R1503 interface-stmt -> INTERFACE [generic-spec] | ABSTRACT INTERFACE TYPE_PARSER(construct("INTERFACE" >> maybe(genericSpec)) || construct(construct("ABSTRACT INTERFACE"_sptok))) // R1504 end-interface-stmt -> END INTERFACE [generic-spec] TYPE_PARSER(construct("END INTERFACE" >> maybe(genericSpec))) // R1505 interface-body -> // function-stmt [specification-part] end-function-stmt | // subroutine-stmt [specification-part] end-subroutine-stmt TYPE_CONTEXT_PARSER("interface body"_en_US, construct( construct(statement(functionStmt), indirect(limitedSpecificationPart), statement(endFunctionStmt))) || construct(construct( statement(subroutineStmt), indirect(limitedSpecificationPart), statement(endSubroutineStmt)))) // R1507 specific-procedure -> procedure-name constexpr auto specificProcedure{name}; // R1506 procedure-stmt -> [MODULE] PROCEDURE [::] specific-procedure-list TYPE_PARSER(construct("MODULE PROCEDURE"_sptok >> pure(ProcedureStmt::Kind::ModuleProcedure), maybe("::"_tok) >> nonemptyList(specificProcedure)) || construct( "PROCEDURE" >> pure(ProcedureStmt::Kind::Procedure), maybe("::"_tok) >> nonemptyList(specificProcedure))) // R1508 generic-spec -> // generic-name | OPERATOR ( defined-operator ) | // ASSIGNMENT ( = ) | defined-io-generic-spec // R1509 defined-io-generic-spec -> // READ ( FORMATTED ) | READ ( UNFORMATTED ) | // WRITE ( FORMATTED ) | WRITE ( UNFORMATTED ) TYPE_PARSER(first(construct( "OPERATOR" >> parenthesized(Parser{})), construct( construct("ASSIGNMENT ( = )"_tok)), construct( construct("READ ( FORMATTED )"_tok)), construct( construct("READ ( UNFORMATTED )"_tok)), construct( construct("WRITE ( FORMATTED )"_tok)), construct( construct("WRITE ( UNFORMATTED )"_tok)), construct(name))) // R1510 generic-stmt -> // GENERIC [, access-spec] :: generic-spec => specific-procedure-list TYPE_PARSER(construct("GENERIC" >> maybe("," >> accessSpec), "::" >> genericSpec, "=>" >> nonemptyList(specificProcedure))) // R1511 external-stmt -> EXTERNAL [::] external-name-list TYPE_PARSER("EXTERNAL" >> maybe("::"_tok) >> construct(nonemptyList(name))) // R1512 procedure-declaration-stmt -> // PROCEDURE ( [proc-interface] ) [[, proc-attr-spec]... ::] // proc-decl-list TYPE_PARSER("PROCEDURE" >> construct(parenthesized(maybe(procInterface)), optionalListBeforeColons(Parser{}), nonemptyList(procDecl))) // R1513 proc-interface -> interface-name | declaration-type-spec // R1516 interface-name -> name TYPE_PARSER(construct(declarationTypeSpec) || construct(name)) // R1514 proc-attr-spec -> // access-spec | proc-language-binding-spec | INTENT ( intent-spec ) | // OPTIONAL | POINTER | PROTECTED | SAVE TYPE_PARSER(construct(accessSpec) || construct(languageBindingSpec) || construct("INTENT" >> parenthesized(intentSpec)) || construct(optional) || construct(pointer) || construct(protectedAttr) || construct(save)) // R1515 proc-decl -> procedure-entity-name [=> proc-pointer-init] TYPE_PARSER(construct(name, maybe("=>" >> Parser{}))) // R1517 proc-pointer-init -> null-init | initial-proc-target // R1518 initial-proc-target -> procedure-name TYPE_PARSER( construct(nullInit) || construct(name)) // R1519 intrinsic-stmt -> INTRINSIC [::] intrinsic-procedure-name-list TYPE_PARSER("INTRINSIC" >> maybe("::"_tok) >> construct(nonemptyList(name))) // R1520 function-reference -> procedure-designator ( [actual-arg-spec-list] ) TYPE_CONTEXT_PARSER("function reference"_en_US, construct(construct(Parser{}, parenthesized(optionalList(actualArgSpec)))) / !"["_tok) // R1521 call-stmt -> CALL procedure-designator [( [actual-arg-spec-list] )] TYPE_PARSER( construct(construct("CALL" >> Parser{}, defaulted(parenthesized(optionalList(actualArgSpec)))))) // R1522 procedure-designator -> // procedure-name | proc-component-ref | data-ref % binding-name TYPE_PARSER(construct(Parser{}) || construct(name)) // R1523 actual-arg-spec -> [keyword =] actual-arg TYPE_PARSER(construct( maybe(keyword / "=" / !"="_ch), Parser{})) // R1524 actual-arg -> // expr | variable | procedure-name | proc-component-ref | // alt-return-spec // N.B. the "procedure-name" and "proc-component-ref" alternatives can't // yet be distinguished from "variable". TYPE_PARSER(construct(variable) / lookAhead(","_tok || ")"_tok) || construct(expr) || construct(Parser{}) || extension(construct( construct("%REF" >> parenthesized(variable)))) || extension(construct( construct("%VAL" >> parenthesized(expr))))) // R1525 alt-return-spec -> * label TYPE_PARSER(construct(star >> label)) // R1527 prefix-spec -> // declaration-type-spec | ELEMENTAL | IMPURE | MODULE | // NON_RECURSIVE | PURE | RECURSIVE TYPE_PARSER(first(construct(declarationTypeSpec), construct(construct("ELEMENTAL"_tok)), construct(construct("IMPURE"_tok)), construct(construct("MODULE"_tok)), construct( construct("NON_RECURSIVE"_tok)), construct(construct("PURE"_tok)), construct(construct("RECURSIVE"_tok)))) // R1529 function-subprogram -> // function-stmt [specification-part] [execution-part] // [internal-subprogram-part] end-function-stmt TYPE_CONTEXT_PARSER("FUNCTION subprogram"_en_US, construct(statement(functionStmt), specificationPart, executionPart, maybe(internalSubprogramPart), unterminatedStatement(endFunctionStmt))) // R1530 function-stmt -> // [prefix] FUNCTION function-name ( [dummy-arg-name-list] ) [suffix] // R1526 prefix -> prefix-spec [prefix-spec]... // R1531 dummy-arg-name -> name TYPE_CONTEXT_PARSER("FUNCTION statement"_en_US, construct(many(prefixSpec), "FUNCTION" >> name, parenthesized(optionalList(name)), maybe(suffix)) || extension( construct( // PGI & Intel accept "FUNCTION F" many(prefixSpec), "FUNCTION" >> name, construct>(), construct>()))) // R1532 suffix -> // proc-language-binding-spec [RESULT ( result-name )] | // RESULT ( result-name ) [proc-language-binding-spec] TYPE_PARSER(construct( languageBindingSpec, maybe("RESULT" >> parenthesized(name))) || construct( "RESULT" >> parenthesized(name), maybe(languageBindingSpec))) // R1533 end-function-stmt -> END [FUNCTION [function-name]] TYPE_PARSER(construct(recovery( "END FUNCTION" >> maybe(name) || bareEnd, progUnitEndStmtErrorRecovery))) // R1534 subroutine-subprogram -> // subroutine-stmt [specification-part] [execution-part] // [internal-subprogram-part] end-subroutine-stmt TYPE_CONTEXT_PARSER("SUBROUTINE subprogram"_en_US, construct(statement(subroutineStmt), specificationPart, executionPart, maybe(internalSubprogramPart), unterminatedStatement(endSubroutineStmt))) // R1535 subroutine-stmt -> // [prefix] SUBROUTINE subroutine-name [( [dummy-arg-list] ) // [proc-language-binding-spec]] TYPE_PARSER( construct(many(prefixSpec), "SUBROUTINE" >> name, parenthesized(optionalList(dummyArg)), maybe(languageBindingSpec)) || construct(many(prefixSpec), "SUBROUTINE" >> name, defaulted(cut >> many(dummyArg)), defaulted(cut >> maybe(languageBindingSpec)))) // R1536 dummy-arg -> dummy-arg-name | * TYPE_PARSER(construct(name) || construct(star)) // R1537 end-subroutine-stmt -> END [SUBROUTINE [subroutine-name]] TYPE_PARSER(construct(recovery( "END SUBROUTINE" >> maybe(name) || bareEnd, progUnitEndStmtErrorRecovery))) // R1538 separate-module-subprogram -> // mp-subprogram-stmt [specification-part] [execution-part] // [internal-subprogram-part] end-mp-subprogram-stmt TYPE_CONTEXT_PARSER("separate module subprogram"_en_US, construct(statement(Parser{}), specificationPart, executionPart, maybe(internalSubprogramPart), statement(Parser{}))) // R1539 mp-subprogram-stmt -> MODULE PROCEDURE procedure-name TYPE_CONTEXT_PARSER("MODULE PROCEDURE statement"_en_US, construct("MODULE PROCEDURE"_sptok >> name)) // R1540 end-mp-subprogram-stmt -> END [PROCEDURE [procedure-name]] TYPE_CONTEXT_PARSER("END PROCEDURE statement"_en_US, construct( recovery("END PROCEDURE" >> maybe(name) || bareEnd, progUnitEndStmtErrorRecovery))) // R1541 entry-stmt -> ENTRY entry-name [( [dummy-arg-list] ) [suffix]] TYPE_PARSER( "ENTRY" >> (construct(name, parenthesized(optionalList(dummyArg)), maybe(suffix)) || construct(name, construct>(), construct>()))) // R1542 return-stmt -> RETURN [scalar-int-expr] TYPE_CONTEXT_PARSER("RETURN statement"_en_US, construct("RETURN" >> maybe(scalarIntExpr))) // R1543 contains-stmt -> CONTAINS TYPE_PARSER(construct("CONTAINS"_tok)) // R1544 stmt-function-stmt -> // function-name ( [dummy-arg-name-list] ) = scalar-expr TYPE_CONTEXT_PARSER("statement function definition"_en_US, construct( name, parenthesized(optionalList(name)), "=" >> scalar(expr))) // Directives, extensions, and deprecated statements // !DIR$ IVDEP // !DIR$ IGNORE_TKR [ [(tkr...)] name ]... constexpr auto beginDirective{skipStuffBeforeStatement >> "!"_ch}; constexpr auto endDirective{space >> endOfLine}; constexpr auto ivdep{construct("DIR$ IVDEP"_tok)}; constexpr auto ignore_tkr{ "DIR$ IGNORE_TKR" >> optionalList(construct( defaulted(parenthesized(some("tkr"_ch))), name))}; TYPE_PARSER(beginDirective >> sourced(construct(ivdep) || construct(ignore_tkr)) / endDirective) TYPE_PARSER(extension( construct("POINTER (" >> objectName / ",", objectName, maybe(Parser{}) / ")"))) TYPE_PARSER(construct("STRUCTURE /" >> name / "/", pure(true), optionalList(entityDecl)) || construct( "STRUCTURE" >> name, pure(false), defaulted(cut >> many(entityDecl)))) TYPE_PARSER(construct(statement(StructureComponents{})) || construct(indirect(Parser{})) || construct(indirect(Parser{}))) TYPE_CONTEXT_PARSER("STRUCTURE definition"_en_US, extension(construct( statement(Parser{}), many(Parser{}), statement( construct("END STRUCTURE"_tok))))) TYPE_CONTEXT_PARSER("UNION definition"_en_US, construct(statement(construct("UNION"_tok)), many(Parser{}), statement(construct("END UNION"_tok)))) TYPE_CONTEXT_PARSER("MAP definition"_en_US, construct(statement(construct("MAP"_tok)), many(Parser{}), statement(construct("END MAP"_tok)))) TYPE_CONTEXT_PARSER("arithmetic IF statement"_en_US, deprecated(construct( "IF" >> parenthesized(expr), label / ",", label / ",", label))) TYPE_CONTEXT_PARSER("ASSIGN statement"_en_US, deprecated( construct("ASSIGN" >> label, "TO" >> name))) TYPE_CONTEXT_PARSER("assigned GOTO statement"_en_US, deprecated( construct("GO TO" >> name, defaulted(maybe(","_tok) >> parenthesized(nonemptyList(label)))))) TYPE_CONTEXT_PARSER("PAUSE statement"_en_US, deprecated( construct("PAUSE" >> maybe(Parser{})))) // These requirement productions are defined by the Fortran standard but never // used directly by the grammar: // R620 delimiter -> ( | ) | / | [ | ] | (/ | /) // R1027 numeric-expr -> expr // R1031 int-constant-expr -> int-expr // R1221 dtv-type-spec -> TYPE ( derived-type-spec ) | // CLASS ( derived-type-spec ) // // These requirement productions are defined and used, but need not be // defined independently here in this file: // R771 lbracket -> [ // R772 rbracket -> ] // // Further note that: // R607 int-constant -> constant // is used only once via R844 scalar-int-constant // R904 logical-variable -> variable // is used only via scalar-logical-variable // R906 default-char-variable -> variable // is used only via scalar-default-char-variable // R907 int-variable -> variable // is used only via scalar-int-variable // R1030 default-char-constant-expr -> default-char-expr // is only used via scalar-default-char-constant-expr } // namespace Fortran::parser #endif // FORTRAN_PARSER_GRAMMAR_H_