| //===-- 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<resultType> 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 <cstring> |
| #include <functional> |
| #include <list> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <tuple> |
| #include <type_traits> |
| #include <utility> |
| |
| namespace Fortran::parser { |
| |
| // fail<A>("..."_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 <typename A> class FailParser { |
| public: |
| using resultType = A; |
| constexpr FailParser(const FailParser &) = default; |
| constexpr explicit FailParser(MessageFixedText t) : text_{t} {} |
| std::optional<A> Parse(ParseState &state) const { |
| state.Say(text_); |
| return std::nullopt; |
| } |
| |
| private: |
| const MessageFixedText text_; |
| }; |
| |
| template <typename A = Success> inline constexpr auto fail(MessageFixedText t) { |
| return FailParser<A>{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<A>() is essentially pure(A{}); it returns a default-constructed A{}, |
| // and works even when A is not copy-constructible. |
| template <typename A> class PureParser { |
| public: |
| using resultType = A; |
| constexpr PureParser(const PureParser &) = default; |
| constexpr explicit PureParser(A &&x) : value_(std::move(x)) {} |
| std::optional<A> Parse(ParseState &) const { return value_; } |
| |
| private: |
| const A value_; |
| }; |
| |
| template <typename A> inline constexpr auto pure(A x) { |
| return PureParser<A>(std::move(x)); |
| } |
| |
| template <typename A> class PureDefaultParser { |
| public: |
| using resultType = A; |
| constexpr PureDefaultParser(const PureDefaultParser &) = default; |
| constexpr PureDefaultParser() {} |
| std::optional<A> Parse(ParseState &) const { return std::make_optional<A>(); } |
| }; |
| |
| template <typename A> inline constexpr auto pure() { |
| return PureDefaultParser<A>(); |
| } |
| |
| // 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 <typename A> class BacktrackingParser { |
| public: |
| using resultType = typename A::resultType; |
| constexpr BacktrackingParser(const BacktrackingParser &) = default; |
| constexpr BacktrackingParser(const A &parser) : parser_{parser} {} |
| std::optional<resultType> Parse(ParseState &state) const { |
| Messages messages{std::move(state.messages())}; |
| ParseState backtrack{state}; |
| std::optional<resultType> 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 <typename A> inline constexpr auto attempt(const A &parser) { |
| return BacktrackingParser<A>{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 <typename PA> class NegatedParser { |
| public: |
| using resultType = Success; |
| constexpr NegatedParser(const NegatedParser &) = default; |
| constexpr NegatedParser(PA p) : parser_{p} {} |
| std::optional<Success> 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 <typename PA, typename = typename PA::resultType> |
| constexpr auto operator!(PA p) { |
| return NegatedParser<PA>(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 <typename PA> class LookAheadParser { |
| public: |
| using resultType = Success; |
| constexpr LookAheadParser(const LookAheadParser &) = default; |
| constexpr LookAheadParser(PA p) : parser_{p} {} |
| std::optional<Success> 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 <typename PA> inline constexpr auto lookAhead(PA p) { |
| return LookAheadParser<PA>{p}; |
| } |
| |
| // If a is a parser, inContext("..."_en_US, a) runs it in a nested message |
| // context. |
| template <typename PA> 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<resultType> Parse(ParseState &state) const { |
| state.PushContext(text_); |
| std::optional<resultType> result{parser_.Parse(state)}; |
| state.PopContext(); |
| return result; |
| } |
| |
| private: |
| const MessageFixedText text_; |
| const PA parser_; |
| }; |
| |
| template <typename PA> |
| 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 <typename PA> 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<resultType> Parse(ParseState &state) const { |
| if (state.deferMessages()) { // fast path |
| std::optional<resultType> result{parser_.Parse(state)}; |
| if (!result) { |
| state.set_anyDeferredMessages(); |
| } |
| return result; |
| } |
| Messages messages{std::move(state.messages())}; |
| bool hadAnyTokenMatched{state.anyTokenMatched()}; |
| state.set_anyTokenMatched(false); |
| std::optional<resultType> result{parser_.Parse(state)}; |
| bool emitMessage{false}; |
| if (result) { |
| messages.Annex(std::move(state.messages())); |
| if (hadAnyTokenMatched) { |
| state.set_anyTokenMatched(); |
| } |
| } else if (state.anyTokenMatched()) { |
| emitMessage = state.messages().empty(); |
| messages.Annex(std::move(state.messages())); |
| } else { |
| emitMessage = true; |
| if (hadAnyTokenMatched) { |
| state.set_anyTokenMatched(); |
| } |
| } |
| state.messages() = std::move(messages); |
| if (emitMessage) { |
| state.Say(text_); |
| } |
| return result; |
| } |
| |
| private: |
| const MessageFixedText text_; |
| const PA parser_; |
| }; |
| |
| template <typename PA> |
| 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 <typename PA, typename PB> 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<resultType> Parse(ParseState &state) const { |
| if (pa_.Parse(state)) { |
| return pb2_.Parse(state); |
| } else { |
| return std::nullopt; |
| } |
| } |
| |
| private: |
| const PA pa_; |
| const PB pb2_; |
| }; |
| |
| template <typename PA, typename PB> |
| inline constexpr auto operator>>(PA pa, PB pb) { |
| return SequenceParser<PA, PB>{pa, pb}; |
| } |
| |
| template <typename PA, typename PB> 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<resultType> Parse(ParseState &state) const { |
| if (std::optional<resultType> ax{pa_.Parse(state)}) { |
| if (pb_.Parse(state)) { |
| return ax; |
| } |
| } |
| return std::nullopt; |
| } |
| |
| private: |
| const PA pa_; |
| const PB pb_; |
| }; |
| |
| template <typename PA, typename PB> |
| inline constexpr auto operator/(PA pa, PB pb) { |
| return FollowParser<PA, PB>{pa, pb}; |
| } |
| |
| template <typename PA, typename... Ps> class AlternativesParser { |
| public: |
| using resultType = typename PA::resultType; |
| constexpr AlternativesParser(PA pa, Ps... ps) : ps_{pa, ps...} {} |
| constexpr AlternativesParser(const AlternativesParser &) = default; |
| std::optional<resultType> Parse(ParseState &state) const { |
| Messages messages{std::move(state.messages())}; |
| ParseState backtrack{state}; |
| std::optional<resultType> 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 <int J> |
| void ParseRest(std::optional<resultType> &result, ParseState &state, |
| ParseState &backtrack) const { |
| ParseState prevState{std::move(state)}; |
| state = backtrack; |
| result = std::get<J>(ps_).Parse(state); |
| if (!result) { |
| state.CombineFailedParses(std::move(prevState)); |
| if constexpr (J < sizeof...(Ps)) { |
| ParseRest<J + 1>(result, state, backtrack); |
| } |
| } |
| } |
| |
| const std::tuple<PA, Ps...> ps_; |
| }; |
| |
| template <typename... Ps> inline constexpr auto first(Ps... ps) { |
| return AlternativesParser<Ps...>{ps...}; |
| } |
| |
| template <typename PA, typename PB> |
| inline constexpr auto operator||(PA pa, PB pb) { |
| return AlternativesParser<PA, PB>{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 <typename PA, typename PB> class RecoveryParser { |
| public: |
| using resultType = typename PA::resultType; |
| static_assert(std::is_same_v<resultType, typename PB::resultType>); |
| constexpr RecoveryParser(const RecoveryParser &) = default; |
| constexpr RecoveryParser(PA pa, PB pb) : pa_{pa}, pb_{pb} {} |
| std::optional<resultType> 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<resultType> 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<resultType> 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<resultType> bx{pb_.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 pb_; |
| }; |
| |
| template <typename PA, typename PB> |
| inline constexpr auto recovery(PA pa, PB pb) { |
| return RecoveryParser<PA, PB>{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 <typename PA> class ManyParser { |
| using paType = typename PA::resultType; |
| |
| public: |
| using resultType = std::list<paType>; |
| constexpr ManyParser(const ManyParser &) = default; |
| constexpr ManyParser(PA parser) : parser_{parser} {} |
| std::optional<resultType> Parse(ParseState &state) const { |
| resultType result; |
| auto at{state.GetLocation()}; |
| while (std::optional<paType> 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<PA> parser_; |
| }; |
| |
| template <typename PA> inline constexpr auto many(PA parser) { |
| return ManyParser<PA>{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 <typename PA> class SomeParser { |
| using paType = typename PA::resultType; |
| |
| public: |
| using resultType = std::list<paType>; |
| constexpr SomeParser(const SomeParser &) = default; |
| constexpr SomeParser(PA parser) : parser_{parser} {} |
| std::optional<resultType> Parse(ParseState &state) const { |
| auto start{state.GetLocation()}; |
| if (std::optional<paType> 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 <typename PA> inline constexpr auto some(PA parser) { |
| return SomeParser<PA>{parser}; |
| } |
| |
| // If x is a parser, skipMany(x) is equivalent to many(x) but with no result. |
| template <typename PA> class SkipManyParser { |
| public: |
| using resultType = Success; |
| constexpr SkipManyParser(const SkipManyParser &) = default; |
| constexpr SkipManyParser(PA parser) : parser_{parser} {} |
| std::optional<Success> Parse(ParseState &state) const { |
| for (auto at{state.GetLocation()}; |
| parser_.Parse(state) && state.GetLocation() > at; |
| at = state.GetLocation()) { |
| } |
| return Success{}; |
| } |
| |
| private: |
| const BacktrackingParser<PA> parser_; |
| }; |
| |
| template <typename PA> inline constexpr auto skipMany(PA parser) { |
| return SkipManyParser<PA>{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 <typename PA> class SkipManyFastParser { |
| public: |
| using resultType = Success; |
| constexpr SkipManyFastParser(const SkipManyFastParser &) = default; |
| constexpr SkipManyFastParser(PA parser) : parser_{parser} {} |
| std::optional<Success> Parse(ParseState &state) const { |
| while (parser_.Parse(state)) { |
| } |
| return Success{}; |
| } |
| |
| private: |
| const PA parser_; |
| }; |
| |
| template <typename PA> inline constexpr auto skipManyFast(PA parser) { |
| return SkipManyFastParser<PA>{parser}; |
| } |
| |
| // If x is a parser returning some type A, then maybe(x) returns a |
| // parser that returns std::optional<A>, always succeeding. |
| template <typename PA> class MaybeParser { |
| using paType = typename PA::resultType; |
| |
| public: |
| using resultType = std::optional<paType>; |
| constexpr MaybeParser(const MaybeParser &) = default; |
| constexpr MaybeParser(PA parser) : parser_{parser} {} |
| std::optional<resultType> Parse(ParseState &state) const { |
| if (resultType result{parser_.Parse(state)}) { |
| // permit optional<optional<...>> |
| return {std::move(result)}; |
| } |
| return resultType{}; |
| } |
| |
| private: |
| const BacktrackingParser<PA> parser_; |
| }; |
| |
| template <typename PA> inline constexpr auto maybe(PA parser) { |
| return MaybeParser<PA>{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 <typename PA> class DefaultedParser { |
| public: |
| using resultType = typename PA::resultType; |
| constexpr DefaultedParser(const DefaultedParser &) = default; |
| constexpr DefaultedParser(PA p) : parser_{p} {} |
| std::optional<resultType> Parse(ParseState &state) const { |
| std::optional<std::optional<resultType>> ax{maybe(parser_).Parse(state)}; |
| if (ax.value()) { // maybe() always succeeds |
| return std::move(*ax); |
| } |
| return resultType{}; |
| } |
| |
| private: |
| const BacktrackingParser<PA> parser_; |
| }; |
| |
| template <typename PA> inline constexpr auto defaulted(PA p) { |
| return DefaultedParser<PA>(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<std::optional<>...>. |
| template <typename... PARSER> |
| using ApplyArgs = std::tuple<std::optional<typename PARSER::resultType>...>; |
| |
| template <typename... PARSER, std::size_t... J> |
| inline bool ApplyHelperArgs(const std::tuple<PARSER...> &parsers, |
| ApplyArgs<PARSER...> &args, ParseState &state, std::index_sequence<J...>) { |
| return (... && |
| (std::get<J>(args) = std::get<J>(parsers).Parse(state), |
| std::get<J>(args).has_value())); |
| } |
| |
| // Applies a function to the arguments collected by ApplyHelperArgs. |
| template <typename RESULT, typename... PARSER> |
| using ApplicableFunctionPointer = RESULT (*)(typename PARSER::resultType &&...); |
| template <typename RESULT, typename... PARSER> |
| using ApplicableFunctionObject = |
| const std::function<RESULT(typename PARSER::resultType &&...)> &; |
| |
| template <template <typename...> class FUNCTION, typename RESULT, |
| typename... PARSER, std::size_t... J> |
| inline RESULT ApplyHelperFunction(FUNCTION<RESULT, PARSER...> f, |
| ApplyArgs<PARSER...> &&args, std::index_sequence<J...>) { |
| return f(std::move(*std::get<J>(args))...); |
| } |
| |
| template <template <typename...> class FUNCTION, typename RESULT, |
| typename... PARSER> |
| class ApplyFunction { |
| using funcType = FUNCTION<RESULT, PARSER...>; |
| |
| public: |
| using resultType = RESULT; |
| constexpr ApplyFunction(const ApplyFunction &) = default; |
| constexpr ApplyFunction(funcType f, PARSER... p) |
| : function_{f}, parsers_{p...} {} |
| std::optional<resultType> Parse(ParseState &state) const { |
| ApplyArgs<PARSER...> results; |
| using Sequence = std::index_sequence_for<PARSER...>; |
| if (ApplyHelperArgs(parsers_, results, state, Sequence{})) { |
| return ApplyHelperFunction<FUNCTION, RESULT, PARSER...>( |
| function_, std::move(results), Sequence{}); |
| } else { |
| return std::nullopt; |
| } |
| } |
| |
| private: |
| const funcType function_; |
| const std::tuple<PARSER...> parsers_; |
| }; |
| |
| template <typename RESULT, typename... PARSER> |
| inline constexpr auto applyFunction( |
| ApplicableFunctionPointer<RESULT, PARSER...> f, const PARSER &...parser) { |
| return ApplyFunction<ApplicableFunctionPointer, RESULT, PARSER...>{ |
| f, parser...}; |
| } |
| |
| template <typename RESULT, typename... PARSER> |
| inline /* not constexpr */ auto applyLambda( |
| ApplicableFunctionObject<RESULT, PARSER...> f, const PARSER &...parser) { |
| return ApplyFunction<ApplicableFunctionObject, RESULT, PARSER...>{ |
| f, parser...}; |
| } |
| |
| // Member function application |
| template <typename OBJPARSER, typename... PARSER> class AMFPHelper { |
| using resultType = typename OBJPARSER::resultType; |
| |
| public: |
| using type = void (resultType::*)(typename PARSER::resultType &&...); |
| }; |
| template <typename OBJPARSER, typename... PARSER> |
| using ApplicableMemberFunctionPointer = |
| typename AMFPHelper<OBJPARSER, PARSER...>::type; |
| |
| template <typename OBJPARSER, typename... PARSER, std::size_t... J> |
| inline auto ApplyHelperMember( |
| ApplicableMemberFunctionPointer<OBJPARSER, PARSER...> mfp, |
| ApplyArgs<OBJPARSER, PARSER...> &&args, std::index_sequence<J...>) -> |
| typename OBJPARSER::resultType { |
| ((*std::get<0>(args)).*mfp)(std::move(*std::get<J + 1>(args))...); |
| return std::get<0>(std::move(args)); |
| } |
| |
| template <typename OBJPARSER, typename... PARSER> class ApplyMemberFunction { |
| using funcType = ApplicableMemberFunctionPointer<OBJPARSER, PARSER...>; |
| |
| public: |
| using resultType = typename OBJPARSER::resultType; |
| constexpr ApplyMemberFunction(const ApplyMemberFunction &) = default; |
| constexpr ApplyMemberFunction(funcType f, OBJPARSER o, PARSER... p) |
| : function_{f}, parsers_{o, p...} {} |
| std::optional<resultType> Parse(ParseState &state) const { |
| ApplyArgs<OBJPARSER, PARSER...> results; |
| using Sequence1 = std::index_sequence_for<OBJPARSER, PARSER...>; |
| using Sequence2 = std::index_sequence_for<PARSER...>; |
| if (ApplyHelperArgs(parsers_, results, state, Sequence1{})) { |
| return ApplyHelperMember<OBJPARSER, PARSER...>( |
| function_, std::move(results), Sequence2{}); |
| } else { |
| return std::nullopt; |
| } |
| } |
| |
| private: |
| const funcType function_; |
| const std::tuple<OBJPARSER, PARSER...> parsers_; |
| }; |
| |
| template <typename OBJPARSER, typename... PARSER> |
| inline constexpr auto applyMem( |
| ApplicableMemberFunctionPointer<OBJPARSER, PARSER...> mfp, |
| const OBJPARSER &objParser, PARSER... parser) { |
| return ApplyMemberFunction<OBJPARSER, PARSER...>{mfp, objParser, parser...}; |
| } |
| |
| // As is done with function application via applyFunction() above, class |
| // instance construction can also be based upon the results of successful |
| // parses. For some type T and zero or more parsers a, b, &c., the call |
| // construct<T>(a, b, ...) returns a parser that succeeds if all of |
| // its argument parsers do so in succession, and whose result is an |
| // instance of T constructed upon the values they returned. |
| // With a single argument that is a parser with no usable value, |
| // construct<T>(p) invokes T's default nullary constructor (T(){}). |
| // (This means that "construct<T>(Foo >> Bar >> ok)" is functionally |
| // equivalent to "Foo >> Bar >> construct<T>()", but I'd like to hold open |
| // the opportunity to make construct<> capture source provenance all of the |
| // time, and the first form will then lead to better error positioning.) |
| |
| template <typename RESULT, typename... PARSER, std::size_t... J> |
| inline RESULT ApplyHelperConstructor( |
| ApplyArgs<PARSER...> &&args, std::index_sequence<J...>) { |
| return RESULT{std::move(*std::get<J>(args))...}; |
| } |
| |
| template <typename RESULT, typename... PARSER> class ApplyConstructor { |
| public: |
| using resultType = RESULT; |
| constexpr ApplyConstructor(const ApplyConstructor &) = default; |
| constexpr explicit ApplyConstructor(PARSER... p) : parsers_{p...} {} |
| std::optional<resultType> Parse(ParseState &state) const { |
| if constexpr (sizeof...(PARSER) == 0) { |
| return RESULT{}; |
| } else { |
| if constexpr (sizeof...(PARSER) == 1) { |
| return ParseOne(state); |
| } else { |
| ApplyArgs<PARSER...> results; |
| using Sequence = std::index_sequence_for<PARSER...>; |
| if (ApplyHelperArgs(parsers_, results, state, Sequence{})) { |
| return ApplyHelperConstructor<RESULT, PARSER...>( |
| std::move(results), Sequence{}); |
| } |
| } |
| return std::nullopt; |
| } |
| } |
| |
| private: |
| std::optional<resultType> ParseOne(ParseState &state) const { |
| if constexpr (std::is_same_v<Success, typename PARSER::resultType...>) { |
| if (std::get<0>(parsers_).Parse(state)) { |
| return RESULT{}; |
| } |
| } else if (auto arg{std::get<0>(parsers_).Parse(state)}) { |
| return RESULT{std::move(*arg)}; |
| } |
| return std::nullopt; |
| } |
| |
| const std::tuple<PARSER...> parsers_; |
| }; |
| |
| template <typename RESULT, typename... PARSER> |
| inline constexpr auto construct(PARSER... p) { |
| return ApplyConstructor<RESULT, PARSER...>{p...}; |
| } |
| |
| // For a parser p, indirect(p) returns a parser that builds an indirect |
| // reference to p's return type. |
| template <typename PA> inline constexpr auto indirect(PA p) { |
| return construct<common::Indirection<typename PA::resultType>>(p); |
| } |
| |
| // If a and b are parsers, then nonemptySeparated(a, b) returns a parser |
| // that succeeds if a does. If a succeeds, it then applies many(b >> a). |
| // The result is the list of the values returned from all of the applications |
| // of a. |
| template <typename T> |
| common::IfNoLvalue<std::list<T>, T> prepend(T &&head, std::list<T> &&rest) { |
| rest.push_front(std::move(head)); |
| return std::move(rest); |
| } |
| |
| template <typename PA, typename PB> class NonemptySeparated { |
| private: |
| using paType = typename PA::resultType; |
| |
| public: |
| using resultType = std::list<paType>; |
| constexpr NonemptySeparated(const NonemptySeparated &) = default; |
| constexpr NonemptySeparated(PA p, PB sep) : parser_{p}, separator_{sep} {} |
| std::optional<resultType> Parse(ParseState &state) const { |
| return applyFunction<std::list<paType>>( |
| prepend<paType>, parser_, many(separator_ >> parser_)) |
| .Parse(state); |
| } |
| |
| private: |
| const PA parser_; |
| const PB separator_; |
| }; |
| |
| template <typename PA, typename PB> |
| inline constexpr auto nonemptySeparated(PA p, PB sep) { |
| return NonemptySeparated<PA, PB>{p, sep}; |
| } |
| |
| // ok is a parser that always succeeds. It is useful when a parser |
| // must discard its result in order to be compatible in type with other |
| // parsers in an alternative, e.g. "x >> ok || y >> ok" is type-safe even |
| // when x and y have distinct result types. |
| struct OkParser { |
| using resultType = Success; |
| constexpr OkParser() {} |
| static constexpr std::optional<Success> Parse(ParseState &) { |
| return Success{}; |
| } |
| }; |
| constexpr OkParser ok; |
| |
| // A variant of recovery() above for convenience. |
| template <typename PA, typename PB> |
| inline constexpr auto localRecovery(MessageFixedText msg, PA pa, PB pb) { |
| return recovery(withMessage(msg, pa), pb >> pure<typename PA::resultType>()); |
| } |
| |
| // nextCh is a parser that succeeds if the parsing state is not |
| // at the end of its input, returning the next character location and |
| // advancing the parse when it does so. |
| struct NextCh { |
| using resultType = const char *; |
| constexpr NextCh() {} |
| std::optional<const char *> Parse(ParseState &state) const { |
| if (std::optional<const char *> result{state.GetNextChar()}) { |
| return result; |
| } |
| state.Say("end of file"_err_en_US); |
| return std::nullopt; |
| } |
| }; |
| |
| constexpr NextCh nextCh; |
| |
| // If a is a parser for some nonstandard language feature LF, extension<LF>(a) |
| // is a parser that optionally enabled, sets a strict conformance violation |
| // flag, and may emit a warning message, if those are enabled. |
| template <LanguageFeature LF, typename PA> class NonstandardParser { |
| public: |
| using resultType = typename PA::resultType; |
| constexpr NonstandardParser(const NonstandardParser &) = default; |
| constexpr NonstandardParser(PA parser, MessageFixedText msg) |
| : parser_{parser}, message_{msg} {} |
| constexpr NonstandardParser(PA parser) : parser_{parser} {} |
| std::optional<resultType> Parse(ParseState &state) const { |
| if (UserState * ustate{state.userState()}) { |
| if (!ustate->features().IsEnabled(LF)) { |
| return std::nullopt; |
| } |
| } |
| auto at{state.GetLocation()}; |
| auto result{parser_.Parse(state)}; |
| if (result && !message_.empty()) { |
| state.Nonstandard( |
| CharBlock{at, std::max(state.GetLocation(), at + 1)}, LF, message_); |
| } |
| return result; |
| } |
| |
| private: |
| const PA parser_; |
| const MessageFixedText message_; |
| }; |
| |
| template <LanguageFeature LF, typename PA> |
| inline constexpr auto extension(MessageFixedText feature, PA parser) { |
| return NonstandardParser<LF, PA>(parser, feature); |
| } |
| |
| template <LanguageFeature LF, typename PA> |
| inline constexpr auto extension(PA parser) { |
| return NonstandardParser<LF, PA>(parser); |
| } |
| |
| // If a is a parser for some deprecated or deleted language feature LF, |
| // deprecated<LF>(a) is a parser that is optionally enabled, sets a strict |
| // conformance violation flag, and may emit a warning message, if enabled. |
| template <LanguageFeature LF, typename PA> class DeprecatedParser { |
| public: |
| using resultType = typename PA::resultType; |
| constexpr DeprecatedParser(const DeprecatedParser &) = default; |
| constexpr DeprecatedParser(PA parser) : parser_{parser} {} |
| std::optional<resultType> Parse(ParseState &state) const { |
| if (UserState * ustate{state.userState()}) { |
| if (!ustate->features().IsEnabled(LF)) { |
| return std::nullopt; |
| } |
| } |
| auto at{state.GetLocation()}; |
| auto result{parser_.Parse(state)}; |
| if (result) { |
| state.Nonstandard(CharBlock{at, state.GetLocation()}, LF, |
| "deprecated usage"_port_en_US); |
| } |
| return result; |
| } |
| |
| private: |
| const PA parser_; |
| }; |
| |
| template <LanguageFeature LF, typename PA> |
| inline constexpr auto deprecated(PA parser) { |
| return DeprecatedParser<LF, PA>(parser); |
| } |
| |
| // Parsing objects with "source" members. |
| template <typename PA> class SourcedParser { |
| public: |
| using resultType = typename PA::resultType; |
| constexpr SourcedParser(const SourcedParser &) = default; |
| constexpr SourcedParser(PA parser) : parser_{parser} {} |
| std::optional<resultType> Parse(ParseState &state) const { |
| const char *start{state.GetLocation()}; |
| auto result{parser_.Parse(state)}; |
| if (result) { |
| const char *end{state.GetLocation()}; |
| for (; start < end && start[0] == ' '; ++start) { |
| } |
| for (; start < end && end[-1] == ' '; --end) { |
| } |
| result->source = CharBlock{start, end}; |
| } |
| return result; |
| } |
| |
| private: |
| const PA parser_; |
| }; |
| |
| template <typename PA> inline constexpr auto sourced(PA parser) { |
| return SourcedParser<PA>{parser}; |
| } |
| } // namespace Fortran::parser |
| #endif // FORTRAN_PARSER_BASIC_PARSERS_H_ |