diff --git a/flang/documentation/ParserCombinators.md b/flang/documentation/ParserCombinators.md index c56ddb70b469..1c797425f3b5 100644 --- a/flang/documentation/ParserCombinators.md +++ b/flang/documentation/ParserCombinators.md @@ -78,6 +78,9 @@ They are `constexpr`, so they should be viewed as type-safe macros. * `deprecated(p)` parses p if strict standard compliance is disabled, with a warning if deprecated usage warnings are enabled. * `inContext(..., p)` runs p within an error message context. +* `recovery(p, q)` is equivalent to `p || q`, except that error messages + generated from the first parser are retained, and a flag is set in + the ParseState to remember that error recovery was necessary. Note that ``` diff --git a/flang/lib/parser/grammar.h b/flang/lib/parser/grammar.h index a058c801fa76..a0b45fcfa08b 100644 --- a/flang/lib/parser/grammar.h +++ b/flang/lib/parser/grammar.h @@ -193,6 +193,20 @@ template inline constexpr auto statement(const PA &p) { return unterminatedStatement(p) / endOfStmt; } +// Error recovery within statements: skip to the end of the line, +// but not over an END or CONTAINS statement. +constexpr auto skipToEndOfLine = SkipTo<'\n'>{} >> construct{}; +constexpr auto stmtErrorRecovery = + !"END"_tok >> !"CONTAINS"_tok >> skipToEndOfLine; + +// Error recovery across statements: skip the line, unless it looks +// like it might end the containing construct. +constexpr auto errorRecoveryStart = skipMany("\n"_tok) >> maybe(label); +constexpr auto skipBadLine = SkipPast<'\n'>{} >> construct{}; +constexpr auto executionPartErrorRecovery = errorRecoveryStart >> !"END"_tok >> + !"ELSE"_tok >> !"CONTAINS"_tok >> !"CASE"_tok >> !"TYPE IS"_tok >> + !"CLASS"_tok >> !"RANK"_tok >> skipBadLine; + // R507 declaration-construct -> // specification-construct | data-stmt | format-stmt | // entry-stmt | stmt-function-stmt @@ -536,11 +550,6 @@ constexpr auto executableConstruct = construct{}(indirect(whereConstruct)) || construct{}(indirect(forallConstruct)); -constexpr auto executionPartErrorRecovery = skipMany("\n"_tok) >> - maybe(label) >> !"END"_tok >> !"ELSE"_tok >> !"CONTAINS"_tok >> - !"CASE"_tok >> !"TYPE IS"_tok >> !"CLASS"_tok >> - !"RANK"_tok >> SkipPast<'\n'>{} >> construct{}; - // R510 execution-part-construct -> // executable-construct | format-stmt | entry-stmt | data-stmt // Extension (PGI/Intel): also accept NAMELIST in execution part @@ -903,14 +912,13 @@ TYPE_PARSER( construct{}(name, maybe("=" >> scalarIntConstantExpr))) // R736 component-def-stmt -> data-component-def-stmt | -// proc-component-def-stmt -TYPE_PARSER(construct{}(Parser{}) || - construct{}(Parser{}) - // Accidental extension: PGI accepts type-param-def-stmt in - // component-part of derived-type-def. Not enabled here. - // || - // extension(construct{}(Parser{}) -) +// 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(construct{}(Parser{}) || + construct{}(Parser{}), + construct{}(stmtErrorRecovery))) // R737 data-component-def-stmt -> // declaration-type-spec [[, component-attr-spec-list] ::] @@ -996,10 +1004,11 @@ TYPE_CONTEXT_PARSER("type bound procedure part"_en_US, // R748 type-bound-proc-binding -> // type-bound-procedure-stmt | type-bound-generic-stmt | // final-procedure-stmt -TYPE_PARSER( +TYPE_PARSER(recovery( construct{}(Parser{}) || - construct{}(Parser{}) || - construct{}(Parser{})) + construct{}(Parser{}) || + construct{}(Parser{}), + construct{}(stmtErrorRecovery))) // R749 type-bound-procedure-stmt -> // PROCEDURE [[, bind-attr-list] ::] type-bound-proc-decl-list | @@ -3384,8 +3393,8 @@ TYPE_PARSER("PROCEDURE" >> // R1513 proc-interface -> interface-name | declaration-type-spec // R1516 interface-name -> name -TYPE_PARSER(construct{}(name) || - construct{}(declarationTypeSpec)) +TYPE_PARSER(construct{}(declarationTypeSpec) || + construct{}(name)) // R1514 proc-attr-spec -> // access-spec | proc-language-binding-spec | INTENT ( intent-spec ) | diff --git a/flang/lib/parser/parse-state.h b/flang/lib/parser/parse-state.h index 38ceb35d4eac..a6a1e408ff87 100644 --- a/flang/lib/parser/parse-state.h +++ b/flang/lib/parser/parse-state.h @@ -154,6 +154,13 @@ public: return {ch}; } + std::optional PeekAtNextChar() { + if (p_ >= limit_) { + return {}; + } + return {*p_}; + } + private: // Text remaining to be parsed const CookedSource &cooked_; diff --git a/flang/lib/parser/parse-tree.h b/flang/lib/parser/parse-tree.h index cd4eb95fb00f..49b774e2523e 100644 --- a/flang/lib/parser/parse-tree.h +++ b/flang/lib/parser/parse-tree.h @@ -973,7 +973,7 @@ struct ProcComponentDefStmt { // R736 component-def-stmt -> data-component-def-stmt | proc-component-def-stmt struct ComponentDefStmt { UNION_CLASS_BOILERPLATE(ComponentDefStmt); - std::variant u; @@ -1036,7 +1036,8 @@ WRAPPER_CLASS(FinalProcedureStmt, std::list); // final-procedure-stmt struct TypeBoundProcBinding { UNION_CLASS_BOILERPLATE(TypeBoundProcBinding); - std::variant + std::variant u; }; diff --git a/flang/lib/parser/token-parsers.h b/flang/lib/parser/token-parsers.h index d12e7a5f14d4..21842c860ea4 100644 --- a/flang/lib/parser/token-parsers.h +++ b/flang/lib/parser/token-parsers.h @@ -404,6 +404,21 @@ template struct SkipPast { } }; +template struct SkipTo { + using resultType = Success; + constexpr SkipTo() {} + constexpr SkipTo(const SkipTo &) {} + static std::optional Parse(ParseState *state) { + while (std::optional ch{state->PeekAtNextChar()}) { + if (*ch == goal) { + return {Success{}}; + } + state->GetNextChar(); + } + return {}; + } +}; + // A common idiom in the Fortran grammar is an optional item (usually // a nonempty comma-separated list) that, if present, must follow a comma // and precede a doubled colon. When the item is absent, the comma must diff --git a/flang/tools/f18/test-type.cc b/flang/tools/f18/test-type.cc index fb6b63b7f356..39c889507525 100644 --- a/flang/tools/f18/test-type.cc +++ b/flang/tools/f18/test-type.cc @@ -236,6 +236,7 @@ static void visitDerivedTypeDef(const DerivedTypeDef &dtd) { [&](const ProcComponentDefStmt &x) { TODO("ProcComponentDefStmt"); }, + [&](const ErrorRecovery &) {}, }, cds.statement.u); }