//===-- lib/Parser/basic-parsers.h ------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef FORTRAN_PARSER_BASIC_PARSERS_H_ #define FORTRAN_PARSER_BASIC_PARSERS_H_ // Let a "parser" be an instance of any class that supports this // type definition and member (or static) function: // // using resultType = ...; // std::optional Parse(ParseState &) const; // // which either returns a value to signify a successful recognition or else // returns {} to signify failure. On failure, the state cannot be assumed // to still be valid, in general -- see below for exceptions. // // This header defines the fundamental parser class templates and helper // template functions. See parser-combinators.txt for documentation. #include "flang/Common/Fortran-features.h" #include "flang/Common/idioms.h" #include "flang/Common/indirection.h" #include "flang/Parser/char-block.h" #include "flang/Parser/message.h" #include "flang/Parser/parse-state.h" #include "flang/Parser/provenance.h" #include "flang/Parser/user-state.h" #include #include #include #include #include #include #include #include #include namespace Fortran::parser { // fail("..."_err_en_US) returns a parser that never succeeds. It reports an // error message at the current position. The result type is unused, // but might have to be specified at the point of call to satisfy // the type checker. The state remains valid. template class FailParser { public: using resultType = A; constexpr FailParser(const FailParser &) = default; constexpr explicit FailParser(MessageFixedText t) : text_{t} {} std::optional Parse(ParseState &state) const { state.Say(text_); return std::nullopt; } private: const MessageFixedText text_; }; template inline constexpr auto fail(MessageFixedText t) { return FailParser{t}; } // pure(x) returns a parser that always succeeds, does not advance the // parse, and returns a captured value x whose type must be copy-constructible. // // pure() is essentially pure(A{}); it returns a default-constructed A{}, // and works even when A is not copy-constructible. template class PureParser { public: using resultType = A; constexpr PureParser(const PureParser &) = default; constexpr explicit PureParser(A &&x) : value_(std::move(x)) {} std::optional Parse(ParseState &) const { return value_; } private: const A value_; }; template inline constexpr auto pure(A x) { return PureParser(std::move(x)); } template class PureDefaultParser { public: using resultType = A; constexpr PureDefaultParser(const PureDefaultParser &) = default; constexpr PureDefaultParser() {} std::optional Parse(ParseState &) const { return std::make_optional(); } }; template inline constexpr auto pure() { return PureDefaultParser(); } // If a is a parser, attempt(a) is the same parser, but on failure // the ParseState is guaranteed to have been restored to its initial value. template class BacktrackingParser { public: using resultType = typename A::resultType; constexpr BacktrackingParser(const BacktrackingParser &) = default; constexpr BacktrackingParser(const A &parser) : parser_{parser} {} std::optional Parse(ParseState &state) const { Messages messages{std::move(state.messages())}; ParseState backtrack{state}; std::optional result{parser_.Parse(state)}; if (result) { state.messages().Annex(std::move(messages)); } else { state = std::move(backtrack); state.messages() = std::move(messages); } return result; } private: const A parser_; }; template inline constexpr auto attempt(const A &parser) { return BacktrackingParser{parser}; } // For any parser x, the parser returned by !x is one that succeeds when // x fails, returning a useless (but present) result. !x fails when x succeeds. template class NegatedParser { public: using resultType = Success; constexpr NegatedParser(const NegatedParser &) = default; constexpr NegatedParser(PA p) : parser_{p} {} std::optional Parse(ParseState &state) const { ParseState forked{state}; forked.set_deferMessages(true); if (parser_.Parse(forked)) { return std::nullopt; } return Success{}; } private: const PA parser_; }; template constexpr auto operator!(PA p) { return NegatedParser(p); } // For any parser x, the parser returned by lookAhead(x) is one that succeeds // or fails if x does, but the state is not modified. template class LookAheadParser { public: using resultType = Success; constexpr LookAheadParser(const LookAheadParser &) = default; constexpr LookAheadParser(PA p) : parser_{p} {} std::optional Parse(ParseState &state) const { ParseState forked{state}; forked.set_deferMessages(true); if (parser_.Parse(forked)) { return Success{}; } return std::nullopt; } private: const PA parser_; }; template inline constexpr auto lookAhead(PA p) { return LookAheadParser{p}; } // If a is a parser, inContext("..."_en_US, a) runs it in a nested message // context. template class MessageContextParser { public: using resultType = typename PA::resultType; constexpr MessageContextParser(const MessageContextParser &) = default; constexpr MessageContextParser(MessageFixedText t, PA p) : text_{t}, parser_{p} {} std::optional Parse(ParseState &state) const { state.PushContext(text_); std::optional result{parser_.Parse(state)}; state.PopContext(); return result; } private: const MessageFixedText text_; const PA parser_; }; template inline constexpr auto inContext(MessageFixedText context, PA parser) { return MessageContextParser{context, parser}; } // If a is a parser, withMessage("..."_en_US, a) runs it unchanged if it // succeeds, and overrides its messages with a specific one if it fails and // has matched no tokens. template class WithMessageParser { public: using resultType = typename PA::resultType; constexpr WithMessageParser(const WithMessageParser &) = default; constexpr WithMessageParser(MessageFixedText t, PA p) : text_{t}, parser_{p} {} std::optional Parse(ParseState &state) const { Messages messages{std::move(state.messages())}; ParseState backtrack{state}; state.set_anyTokenMatched(false); std::optional result{parser_.Parse(state)}; bool emitMessage{false}; if (result) { messages.Annex(std::move(state.messages())); if (backtrack.anyTokenMatched()) { state.set_anyTokenMatched(); } } else if (state.anyTokenMatched()) { emitMessage = state.messages().empty(); messages.Annex(std::move(state.messages())); backtrack.set_anyTokenMatched(); if (state.anyDeferredMessages()) { backtrack.set_anyDeferredMessages(true); } state = std::move(backtrack); } else { emitMessage = true; } state.messages() = std::move(messages); if (emitMessage) { state.Say(text_); } return result; } private: const MessageFixedText text_; const PA parser_; }; template inline constexpr auto withMessage(MessageFixedText msg, PA parser) { return WithMessageParser{msg, parser}; } // If a and b are parsers, then a >> b returns a parser that succeeds when // b succeeds after a does so, but fails when either a or b does. The // result is taken from b. Similarly, a / b also succeeds if both a and b // do so, but the result is that returned by a. template class SequenceParser { public: using resultType = typename PB::resultType; constexpr SequenceParser(const SequenceParser &) = default; constexpr SequenceParser(PA pa, PB pb) : pa_{pa}, pb2_{pb} {} std::optional Parse(ParseState &state) const { if (pa_.Parse(state)) { return pb2_.Parse(state); } else { return std::nullopt; } } private: const PA pa_; const PB pb2_; }; template inline constexpr auto operator>>(PA pa, PB pb) { return SequenceParser{pa, pb}; } template class FollowParser { public: using resultType = typename PA::resultType; constexpr FollowParser(const FollowParser &) = default; constexpr FollowParser(PA pa, PB pb) : pa_{pa}, pb_{pb} {} std::optional Parse(ParseState &state) const { if (std::optional ax{pa_.Parse(state)}) { if (pb_.Parse(state)) { return ax; } } return std::nullopt; } private: const PA pa_; const PB pb_; }; template inline constexpr auto operator/(PA pa, PB pb) { return FollowParser{pa, pb}; } template class AlternativesParser { public: using resultType = typename PA::resultType; constexpr AlternativesParser(PA pa, Ps... ps) : ps_{pa, ps...} {} constexpr AlternativesParser(const AlternativesParser &) = default; std::optional Parse(ParseState &state) const { Messages messages{std::move(state.messages())}; ParseState backtrack{state}; std::optional result{std::get<0>(ps_).Parse(state)}; if constexpr (sizeof...(Ps) > 0) { if (!result) { ParseRest<1>(result, state, backtrack); } } state.messages().Annex(std::move(messages)); return result; } private: template void ParseRest(std::optional &result, ParseState &state, ParseState &backtrack) const { ParseState prevState{std::move(state)}; state = backtrack; result = std::get(ps_).Parse(state); if (!result) { state.CombineFailedParses(std::move(prevState)); if constexpr (J < sizeof...(Ps)) { ParseRest(result, state, backtrack); } } } const std::tuple ps_; }; template inline constexpr auto first(Ps... ps) { return AlternativesParser{ps...}; } template inline constexpr auto operator||(PA pa, PB pb) { return AlternativesParser{pa, pb}; } // If a and b are parsers, then recovery(a,b) returns a parser that succeeds if // a does so, or if a fails and b succeeds. If a succeeds, b is not attempted. // All messages from the first parse are retained. // The two parsers must return values of the same type. template class RecoveryParser { public: using resultType = typename PA::resultType; static_assert(std::is_same_v); constexpr RecoveryParser(const RecoveryParser &) = default; constexpr RecoveryParser(PA pa, PB pb) : pa_{pa}, pb3_{pb} {} std::optional Parse(ParseState &state) const { bool originallyDeferred{state.deferMessages()}; ParseState backtrack{state}; if (!originallyDeferred && state.messages().empty() && !state.anyErrorRecovery()) { // Fast path. There are no messages or recovered errors in the incoming // state. Attempt to parse with messages deferred, expecting that the // parse will succeed silently. state.set_deferMessages(true); if (std::optional ax{pa_.Parse(state)}) { if (!state.anyDeferredMessages() && !state.anyErrorRecovery()) { state.set_deferMessages(false); return ax; } } state = backtrack; } Messages messages{std::move(state.messages())}; if (std::optional ax{pa_.Parse(state)}) { state.messages().Annex(std::move(messages)); return ax; } messages.Annex(std::move(state.messages())); bool hadDeferredMessages{state.anyDeferredMessages()}; bool anyTokenMatched{state.anyTokenMatched()}; state = std::move(backtrack); state.set_deferMessages(true); std::optional bx{pb3_.Parse(state)}; state.messages() = std::move(messages); state.set_deferMessages(originallyDeferred); if (anyTokenMatched) { state.set_anyTokenMatched(); } if (hadDeferredMessages) { state.set_anyDeferredMessages(); } if (bx) { // Error recovery situations must also produce messages. CHECK(state.anyDeferredMessages() || state.messages().AnyFatalError()); state.set_anyErrorRecovery(); } return bx; } private: const PA pa_; const PB pb3_; }; template inline constexpr auto recovery(PA pa, PB pb) { return RecoveryParser{pa, pb}; } // If x is a parser, then many(x) returns a parser that always succeeds // and whose value is a list, possibly empty, of the values returned from // repeated application of x until it fails or does not advance the parse. template class ManyParser { using paType = typename PA::resultType; public: using resultType = std::list; constexpr ManyParser(const ManyParser &) = default; constexpr ManyParser(PA parser) : parser_{parser} {} std::optional Parse(ParseState &state) const { resultType result; auto at{state.GetLocation()}; while (std::optional x{parser_.Parse(state)}) { result.emplace_back(std::move(*x)); if (state.GetLocation() <= at) { break; // no forward progress, don't loop } at = state.GetLocation(); } return {std::move(result)}; } private: const BacktrackingParser parser_; }; template inline constexpr auto many(PA parser) { return ManyParser{parser}; } // If x is a parser, then some(x) returns a parser that succeeds if x does // and whose value is a nonempty list of the values returned from repeated // application of x until it fails or does not advance the parse. In other // words, some(x) is a variant of many(x) that has to succeed at least once. template class SomeParser { using paType = typename PA::resultType; public: using resultType = std::list; constexpr SomeParser(const SomeParser &) = default; constexpr SomeParser(PA parser) : parser_{parser} {} std::optional Parse(ParseState &state) const { auto start{state.GetLocation()}; if (std::optional first{parser_.Parse(state)}) { resultType result; result.emplace_back(std::move(*first)); if (state.GetLocation() > start) { result.splice(result.end(), many(parser_).Parse(state).value()); } return {std::move(result)}; } return std::nullopt; } private: const PA parser_; }; template inline constexpr auto some(PA parser) { return SomeParser{parser}; } // If x is a parser, skipMany(x) is equivalent to many(x) but with no result. template class SkipManyParser { public: using resultType = Success; constexpr SkipManyParser(const SkipManyParser &) = default; constexpr SkipManyParser(PA parser) : parser_{parser} {} std::optional Parse(ParseState &state) const { for (auto at{state.GetLocation()}; parser_.Parse(state) && state.GetLocation() > at; at = state.GetLocation()) { } return Success{}; } private: const BacktrackingParser parser_; }; template inline constexpr auto skipMany(PA parser) { return SkipManyParser{parser}; } // If x is a parser, skipManyFast(x) is equivalent to skipMany(x). // The parser x must always advance on success and never invalidate the // state on failure. template class SkipManyFastParser { public: using resultType = Success; constexpr SkipManyFastParser(const SkipManyFastParser &) = default; constexpr SkipManyFastParser(PA parser) : parser_{parser} {} std::optional Parse(ParseState &state) const { while (parser_.Parse(state)) { } return Success{}; } private: const PA parser_; }; template inline constexpr auto skipManyFast(PA parser) { return SkipManyFastParser{parser}; } // If x is a parser returning some type A, then maybe(x) returns a // parser that returns std::optional, always succeeding. template class MaybeParser { using paType = typename PA::resultType; public: using resultType = std::optional; constexpr MaybeParser(const MaybeParser &) = default; constexpr MaybeParser(PA parser) : parser_{parser} {} std::optional Parse(ParseState &state) const { if (resultType result{parser_.Parse(state)}) { // permit optional> return {std::move(result)}; } return resultType{}; } private: const BacktrackingParser parser_; }; template inline constexpr auto maybe(PA parser) { return MaybeParser{parser}; } // If x is a parser, then defaulted(x) returns a parser that always // succeeds. When x succeeds, its result is that of x; otherwise, its // result is a default-constructed value of x's result type. template class DefaultedParser { public: using resultType = typename PA::resultType; constexpr DefaultedParser(const DefaultedParser &) = default; constexpr DefaultedParser(PA p) : parser_{p} {} std::optional Parse(ParseState &state) const { std::optional> ax{maybe(parser_).Parse(state)}; if (ax.value()) { // maybe() always succeeds return std::move(*ax); } return resultType{}; } private: const BacktrackingParser parser_; }; template inline constexpr auto defaulted(PA p) { return DefaultedParser(p); } // If a is a parser, and f is a function mapping an rvalue of a's result type // to some other type T, then applyFunction(f, a) returns a parser that succeeds // iff a does, and whose result value ax has been passed through the function; // the final result is that returned by the call f(std::move(ax)). // // Function application is generalized to functions with more than one // argument with applyFunction(f, a, b, ...) succeeding if all of the parsers // a, b, &c. do so, and the result is the value of applying f to their // results. // // applyLambda(f, ...) is the same concept extended to std::function<> functors. // It is not constexpr. // // Member function application is supported by applyMem(f, a). If the // parser a succeeds and returns some value ax, the result is that returned // by ax.f(). Additional parser arguments can be specified to supply their // results to the member function call, so applyMem(f, a, b) succeeds if // both a and b do so and returns the result of calling ax.f(std::move(bx)). // Runs a sequence of parsers until one fails or all have succeeded. // Collects their results in a std::tuple...>. template using ApplyArgs = std::tuple...>; template inline bool ApplyHelperArgs(const std::tuple &parsers, ApplyArgs &args, ParseState &state, std::index_sequence) { return (... && (std::get(args) = std::get(parsers).Parse(state), std::get(args).has_value())); } // Applies a function to the arguments collected by ApplyHelperArgs. template using ApplicableFunctionPointer = RESULT (*)(typename PARSER::resultType &&...); template using ApplicableFunctionObject = const std::function &; template