| 1 | //===-- lib/Parser/stmt-parser.h --------------------------------*- C++ -*-===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #ifndef FORTRAN_PARSER_STMT_PARSER_H_ |
| 10 | #define FORTRAN_PARSER_STMT_PARSER_H_ |
| 11 | |
| 12 | // Basic parsing of statements. |
| 13 | |
| 14 | #include "basic-parsers.h" |
| 15 | #include "token-parsers.h" |
| 16 | |
| 17 | namespace Fortran::parser { |
| 18 | |
| 19 | // statement(p) parses Statement<P> for some statement type P that is the |
| 20 | // result type of the argument parser p, while also handling labels and |
| 21 | // end-of-statement markers. |
| 22 | |
| 23 | // R611 label -> digit [digit]... |
| 24 | constexpr auto label{space >> digitString64 / spaceCheck}; |
| 25 | |
| 26 | template <typename PA> |
| 27 | inline constexpr auto unterminatedStatement(const PA &p) { |
| 28 | return skipStuffBeforeStatement >> |
| 29 | sourced(construct<Statement<typename PA::resultType>>( |
| 30 | maybe(label), space >> p)); |
| 31 | } |
| 32 | |
| 33 | constexpr auto atEndOfStmt{space >> |
| 34 | withMessage("expected end of statement"_err_en_US , lookAhead(";\n"_ch ))}; |
| 35 | constexpr auto checkEndOfKnownStmt{recovery(atEndOfStmt, SkipTo<'\n'>{})}; |
| 36 | |
| 37 | constexpr auto endOfLine{ |
| 38 | "\n"_ch >> ok || fail("expected end of line"_err_en_US )}; |
| 39 | |
| 40 | constexpr auto semicolons{";"_ch >> skipMany(";"_tok ) / space / maybe("\n"_ch )}; |
| 41 | constexpr auto endOfStmt{ |
| 42 | space >> withMessage("expected end of statement"_err_en_US , |
| 43 | semicolons || endOfLine)}; |
| 44 | constexpr auto forceEndOfStmt{recovery(endOfStmt, SkipPast<'\n'>{})}; |
| 45 | |
| 46 | template <typename PA> inline constexpr auto statement(const PA &p) { |
| 47 | return unterminatedStatement(p) / endOfStmt; |
| 48 | } |
| 49 | |
| 50 | // unlabeledStatement() is basically statement() for those few situations |
| 51 | // in Fortran where a statement cannot have a label. |
| 52 | template <typename PA> inline constexpr auto unlabeledStatement(const PA &p) { |
| 53 | return space >> |
| 54 | sourced(construct<UnlabeledStatement<typename PA::resultType>>(p)); |
| 55 | } |
| 56 | |
| 57 | // This unambiguousStatement() variant of statement() provides better error |
| 58 | // recovery for contexts containing statements that might have trailing |
| 59 | // garbage, but it must be used only when no instance of the statement in |
| 60 | // question could also be a legal prefix of some other statement that might |
| 61 | // be valid at that point. It only makes sense to use this within "some()" |
| 62 | // or "many()" so as to not end the list of statements. |
| 63 | template <typename PA> inline constexpr auto unambiguousStatement(const PA &p) { |
| 64 | return unterminatedStatement(p) / forceEndOfStmt; |
| 65 | } |
| 66 | |
| 67 | constexpr auto ignoredStatementPrefix{ |
| 68 | skipStuffBeforeStatement >> maybe(label) >> maybe(name / ":" ) >> space}; |
| 69 | |
| 70 | // Error recovery within a statement() call: skip *to* the end of the line, |
| 71 | // unless at an END or CONTAINS statement. |
| 72 | constexpr auto inStmtErrorRecovery{!"END"_tok >> !"CONTAINS"_tok >> |
| 73 | SkipTo<'\n'>{} >> construct<ErrorRecovery>()}; |
| 74 | |
| 75 | // Error recovery within statement sequences: skip *past* the end of the line, |
| 76 | // but not over an END or CONTAINS statement. |
| 77 | constexpr auto skipStmtErrorRecovery{!"END"_tok >> !"CONTAINS"_tok >> |
| 78 | SkipPast<'\n'>{} >> construct<ErrorRecovery>()}; |
| 79 | |
| 80 | // Error recovery across statements: skip the line, unless it looks |
| 81 | // like it might end the containing construct. |
| 82 | constexpr auto stmtErrorRecoveryStart{ignoredStatementPrefix}; |
| 83 | constexpr auto skipBadLine{SkipPast<'\n'>{} >> construct<ErrorRecovery>()}; |
| 84 | constexpr auto executionPartErrorRecovery{stmtErrorRecoveryStart >> |
| 85 | !"END"_tok >> !"CONTAINS"_tok >> !"ELSE"_tok >> !"CASE"_tok >> |
| 86 | !"TYPE IS"_tok >> !"CLASS"_tok >> !"RANK"_tok >> |
| 87 | !("!$ACC "_sptok >> "END"_tok ) >> |
| 88 | !("!$OMP "_sptok >> ("END"_tok || "SECTION"_id )) >> skipBadLine}; |
| 89 | |
| 90 | // END statement error recovery |
| 91 | constexpr auto missingOptionalName{pure<std::optional<Name>>()}; |
| 92 | constexpr auto noNameEnd{"END" >> missingOptionalName}; |
| 93 | |
| 94 | // For unrecognizable construct END statements. Be sure to not consume |
| 95 | // a program unit's END statement. |
| 96 | constexpr auto progUnitEndStmt{ |
| 97 | "END" >> (lookAhead("\n"_ch ) || "SUBROUTINE"_tok || "FUNCTION"_tok || |
| 98 | "PROCEDURE"_tok || "MODULE"_tok || "SUBMODULE"_tok || |
| 99 | "PROGRAM"_tok || "BLOCK DATA"_tok )}; |
| 100 | constexpr auto constructEndStmtErrorRecovery{ |
| 101 | !progUnitEndStmt >> ("END"_tok >> SkipTo<'\n'>{} || ok)}; |
| 102 | constexpr auto namedConstructEndStmtErrorRecovery{ |
| 103 | constructEndStmtErrorRecovery >> missingOptionalName}; |
| 104 | |
| 105 | constexpr auto progUnitEndStmtErrorRecovery{ |
| 106 | (many(!"END"_tok >> SkipPast<'\n'>{}) >> |
| 107 | ("END"_tok >> SkipTo<'\n'>{} || consumedAllInput)) >> |
| 108 | missingOptionalName}; |
| 109 | |
| 110 | constexpr auto beginDirective{skipStuffBeforeStatement >> "!"_ch }; |
| 111 | constexpr auto endDirective{space >> endOfLine}; |
| 112 | |
| 113 | } // namespace Fortran::parser |
| 114 | #endif // FORTRAN_PARSER_STMT_PARSER_H_ |
| 115 | |