// Copyright (C) 2024 T. Zachary Laine // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_PARSER_PARSER_HPP #define BOOST_PARSER_PARSER_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace parser { /** A placeholder type used to represent the absence of information, value, etc., inside semantic actions. For instance, calling `_locals(ctx)` in a semantic action associated with a parser that has no locals will yield a `none`. */ struct none; #if defined(BOOST_PARSER_NO_RUNTIME_ASSERTIONS) struct none {}; #else struct none { none() = default; // Constructible from, assignable from, and implicitly convertible to, // anything. template none(T const &) { fail(); } template none & operator=(T const &) { fail(); return *this; } template operator T() const { fail(); return T{}; } // unary operators none operator+() const { fail(); return none{}; } none operator-() const { fail(); return none{}; } none operator*() const { fail(); return none{}; } none operator~() const { fail(); return none{}; } none operator&() const { fail(); return none{}; } none operator!() const { fail(); return none{}; } none operator++() { fail(); return none{}; } none & operator++(int) { fail(); return *this; } none operator--() { fail(); return none{}; } none operator--(int) { fail(); return *this; } // binary operators template none operator<<(T const &) const { fail(); return none{}; } template none operator>>(T const &) const { fail(); return none{}; } template none operator*(T const &) const { fail(); return none{}; } template none operator/(T const &) const { fail(); return none{}; } template none operator%(T const &) const { fail(); return none{}; } template none operator+(T const &) const { fail(); return none{}; } template none operator-(T const &) const { fail(); return none{}; } template none operator<(T const &) const { fail(); return none{}; } template none operator>(T const &) const { fail(); return none{}; } template none operator<=(T const &) const { fail(); return none{}; } template none operator>=(T const &) const { fail(); return none{}; } template none operator==(T const &) const { fail(); return none{}; } template none operator!=(T const &) const { fail(); return none{}; } template none operator||(T const &) const { fail(); return none{}; } template none operator&&(T const &) const { fail(); return none{}; } template none operator&(T const &) const { fail(); return none{}; } template none operator|(T const &) const { fail(); return none{}; } template none operator^(T const &) const { fail(); return none{}; } template none operator,(T const &) const { fail(); return none{}; } template none operator->*(T const &) const { fail(); return none{}; } template none operator<<=(T const &) { fail(); return none{}; } template none operator>>=(T const &) { fail(); return none{}; } template none operator*=(T const &) { fail(); return none{}; } template none operator/=(T const &) { fail(); return none{}; } template none operator%=(T const &) { fail(); return none{}; } template none operator+=(T const &) { fail(); return none{}; } template none operator-=(T const &) { fail(); return none{}; } template none operator&=(T const &) { fail(); return none{}; } template none operator|=(T const &) { fail(); return none{}; } template none operator^=(T const &) { fail(); return none{}; } template none operator[](T const &) const { fail(); return none{}; } // n-ary operators template none operator()(Args const &...) const { fail(); return none{}; } void fail() const { // If you're seeing this, you've probably gotten a `none` out of // the parse context, and are trying to use it because you think // it's something else. For instance, if your parser produces an // int attribute, the semantic ation `[](auto & ctx) { _attr(ctx) // = 0; }` may be fine. If you attach that same semantic action // to `eps`, you end up here, because `eps` has no attribute, and // so `_attr(ctx)` produces a `none`. BOOST_PARSER_DEBUG_ASSERT(false); } }; #endif namespace detail { // Follows boost/mpl/print.hpp. #if defined(_MSC_VER) #pragma warning(push, 3) #pragma warning(disable : 4307) #endif #if defined(__EDG_VERSION__) namespace print_aux { template struct dependent_unsigned { static const unsigned int value = 1; }; } #endif template struct identity_t { using type = T; }; template struct print_t : identity_t { #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wc++11-extensions" const int x_ = 1 / (sizeof(T) - sizeof(T)); #pragma clang diagnostic pop #elif defined(_MSC_VER) enum { n = sizeof(T) + -1 }; #elif defined(__MWERKS__) void f(int); #else enum { n = #if defined(__EDG_VERSION__) print_aux::dependent_unsigned::value > -1 #else sizeof(T) > -1 #endif }; #endif }; #if defined(_MSC_VER) #pragma warning(pop) #endif template using print_type = typename print_t::type; struct null_parser {}; template struct attribute_impl; // Utility types. struct nope { template constexpr nope & operator=(T const &) { return *this; } operator std::nullopt_t() const noexcept { return std::nullopt; } template constexpr bool operator()(Context const &) const noexcept { return true; } constexpr nope operator*() const noexcept { return nope{}; } friend constexpr bool operator==(nope, nope) { return true; } friend constexpr bool operator!=(nope, nope) { return false; } template friend constexpr bool operator==(T, nope) { return false; } template friend constexpr bool operator!=(T, nope) { return false; } }; inline nope const global_nope; template using parser_interface_tag_expr = typename T::parser_interface_derivation_tag; template constexpr bool derived_from_parser_interface_v = is_detected_v; template using nope_or_pointer_t = std::conditional_t< std::is_same_v, nope>, nope, std::conditional_t>; template< bool DoTrace, bool UseCallbacks, typename I, typename S, typename ErrorHandler, typename GlobalState = nope const, typename Callbacks = nope, typename Attr = nope, typename Val = nope, typename RuleTag = void, typename RuleLocals = nope, typename RuleParams = nope, typename Where = nope> struct parse_context { parse_context() = default; parse_context(parse_context const &) = default; parse_context & operator=(parse_context const &) = default; using rule_tag = RuleTag; static constexpr bool do_trace = DoTrace; static constexpr bool use_callbacks = UseCallbacks; I first_; S last_; bool * pass_ = nullptr; int * trace_indent_ = nullptr; symbol_table_tries_t * symbol_table_tries_ = nullptr; pending_symbol_table_operations_t * pending_symbol_table_operations_ = nullptr; ErrorHandler const * error_handler_ = nullptr; nope_or_pointer_t globals_{}; nope_or_pointer_t callbacks_{}; nope_or_pointer_t attr_{}; nope_or_pointer_t val_{}; nope_or_pointer_t locals_{}; nope_or_pointer_t params_{}; nope_or_pointer_t where_{}; int no_case_depth_ = 0; // These exist in order to provide an address, if requested, for // either kind of symbol table struct. The nonstatic member // pointers for these will be null if this context was created // inside of detail::skip(), but nothing prevents the user from // trying to use a symbol_parser anyway. So, we have these. static std::optional empty_symbol_table_tries_; static std::optional empty_pending_symbol_table_operations_; symbol_table_tries_t & get_symbol_table_tries() const { if (symbol_table_tries_) return *symbol_table_tries_; if (!empty_symbol_table_tries_) empty_symbol_table_tries_ = symbol_table_tries_t(); return *empty_symbol_table_tries_; } pending_symbol_table_operations_t & get_pending_symbol_table_operations() const { if (pending_symbol_table_operations_) return *pending_symbol_table_operations_; if (!empty_pending_symbol_table_operations_) { empty_pending_symbol_table_operations_ = pending_symbol_table_operations_t(); } return *empty_pending_symbol_table_operations_; } template static auto nope_or_address(T & x) { if constexpr (std::is_same_v, nope>) return nope{}; else return std::addressof(x); } template static auto other_or_address(T other, U & x) { if constexpr (std::is_same_v, nope>) return other; else return std::addressof(x); } parse_context( std::bool_constant, std::bool_constant, I & first, S last, bool & success, int & indent, ErrorHandler const & error_handler, GlobalState & globals, symbol_table_tries_t & symbol_table_tries, pending_symbol_table_operations_t & pending_symbol_table_operations) : first_(first), last_(last), pass_(std::addressof(success)), trace_indent_(std::addressof(indent)), symbol_table_tries_(std::addressof(symbol_table_tries)), pending_symbol_table_operations_( std::addressof(pending_symbol_table_operations)), error_handler_(std::addressof(error_handler)), globals_(nope_or_address(globals)) {} parse_context( std::bool_constant, std::bool_constant, I & first, S last, bool & success, int & indent, ErrorHandler const & error_handler, GlobalState & globals) : first_(first), last_(last), pass_(std::addressof(success)), trace_indent_(std::addressof(indent)), error_handler_(std::addressof(error_handler)), globals_(nope_or_address(globals)) {} // With callbacks. parse_context( std::bool_constant, std::bool_constant, I & first, S last, bool & success, int & indent, ErrorHandler const & error_handler, Callbacks const & callbacks, GlobalState & globals, symbol_table_tries_t & symbol_table_tries, pending_symbol_table_operations_t & pending_symbol_table_operations) : first_(first), last_(last), pass_(std::addressof(success)), trace_indent_(std::addressof(indent)), symbol_table_tries_(std::addressof(symbol_table_tries)), pending_symbol_table_operations_( std::addressof(pending_symbol_table_operations)), error_handler_(std::addressof(error_handler)), globals_(nope_or_address(globals)), callbacks_(std::addressof(callbacks)) {} // For making rule contexts. template< typename OldVal, typename OldRuleTag, typename OldRuleLocals, typename OldRuleParams, typename NewVal, typename NewRuleTag, typename NewRuleLocals, typename NewRuleParams> parse_context( parse_context< DoTrace, UseCallbacks, I, S, ErrorHandler, GlobalState, Callbacks, Attr, OldVal, OldRuleTag, OldRuleLocals, OldRuleParams> const & other, NewRuleTag * tag_ptr, NewVal & value, NewRuleLocals & locals, NewRuleParams const & params) : first_(other.first_), last_(other.last_), pass_(other.pass_), trace_indent_(other.trace_indent_), symbol_table_tries_(other.symbol_table_tries_), pending_symbol_table_operations_( other.pending_symbol_table_operations_), error_handler_(other.error_handler_), globals_(other.globals_), callbacks_(other.callbacks_), attr_(other.attr_), no_case_depth_(other.no_case_depth_) { if constexpr ( std::is_same_v && !std::is_same_v) { val_ = other.val_; locals_ = other.locals_; params_ = other.params_; } else { val_ = other_or_address(other.val_, value); locals_ = other_or_address(other.locals_, locals); params_ = other_or_address(other.params_, params); } } // For making action contexts. template parse_context( parse_context< DoTrace, UseCallbacks, I, S, ErrorHandler, GlobalState, Callbacks, OldAttr, Val, RuleTag, RuleLocals, RuleParams, OldWhere> const & other, Attr & attr, Where const & where) : first_(other.first_), last_(other.last_), pass_(other.pass_), trace_indent_(other.trace_indent_), symbol_table_tries_(other.symbol_table_tries_), pending_symbol_table_operations_( other.pending_symbol_table_operations_), error_handler_(other.error_handler_), globals_(other.globals_), callbacks_(other.callbacks_), attr_(nope_or_address(attr)), val_(other.val_), locals_(other.locals_), params_(other.params_), where_(nope_or_address(where)), no_case_depth_(other.no_case_depth_) {} }; template< bool DoTrace, bool UseCallbacks, typename I, typename S, typename ErrorHandler, typename GlobalState, typename Callbacks, typename Attr, typename Val, typename RuleTag, typename RuleLocals, typename RuleParams, typename Where> std::optional parse_context< DoTrace, UseCallbacks, I, S, ErrorHandler, GlobalState, Callbacks, Attr, Val, RuleTag, RuleLocals, RuleParams, Where>::empty_symbol_table_tries_; template< bool DoTrace, bool UseCallbacks, typename I, typename S, typename ErrorHandler, typename GlobalState, typename Callbacks, typename Attr, typename Val, typename RuleTag, typename RuleLocals, typename RuleParams, typename Where> std::optional parse_context< DoTrace, UseCallbacks, I, S, ErrorHandler, GlobalState, Callbacks, Attr, Val, RuleTag, RuleLocals, RuleParams, Where>::empty_pending_symbol_table_operations_; template< bool DoTrace, bool UseCallbacks, typename I, typename S, typename ErrorHandler, typename GlobalState, typename Callbacks, typename Val, typename RuleTag, typename RuleLocals, typename RuleParams, typename Attr, typename Where, typename OldAttr> auto make_action_context( parse_context< DoTrace, UseCallbacks, I, S, ErrorHandler, GlobalState, Callbacks, OldAttr, Val, RuleTag, RuleLocals, RuleParams> const & context, Attr & attr, Where const & where) { using result_type = parse_context< DoTrace, UseCallbacks, I, S, ErrorHandler, GlobalState, Callbacks, Attr, Val, RuleTag, RuleLocals, RuleParams, Where>; return result_type(context, attr, where); } template< bool DoTrace, bool UseCallbacks, typename I, typename S, typename ErrorHandler, typename GlobalState, typename Callbacks, typename Attr, typename Val, typename RuleTag, typename RuleLocals, typename RuleParams, typename NewVal, typename NewRuleTag, typename NewRuleLocals, typename NewRuleParams> auto make_rule_context( parse_context< DoTrace, UseCallbacks, I, S, ErrorHandler, GlobalState, Callbacks, Attr, Val, RuleTag, RuleLocals, RuleParams> const & context, NewRuleTag * tag_ptr, NewVal & value, NewRuleLocals & locals, NewRuleParams const & params) { using result_type = parse_context< DoTrace, UseCallbacks, I, S, ErrorHandler, GlobalState, Callbacks, Attr, std::conditional_t, Val, NewVal>, NewRuleTag, std::conditional_t< std::is_same_v, RuleLocals, NewRuleLocals>, std::conditional_t< std::is_same_v, RuleParams, NewRuleParams>>; return result_type(context, tag_ptr, value, locals, params); } template< bool DoTrace, bool UseCallbacks, typename Iter, typename Sentinel, typename ErrorHandler> auto make_context( Iter first, Sentinel last, bool & success, int & indent, ErrorHandler const & error_handler, nope const & n, symbol_table_tries_t & symbol_table_tries, pending_symbol_table_operations_t & pending_symbol_table_operations) noexcept { return parse_context( std::bool_constant{}, std::bool_constant{}, first, last, success, indent, error_handler, n, symbol_table_tries, pending_symbol_table_operations); } template< bool DoTrace, bool UseCallbacks, typename Iter, typename Sentinel, typename ErrorHandler> auto make_context( Iter first, Sentinel last, bool & success, int & indent, ErrorHandler const & error_handler, nope const & n, nope const &, nope const &) noexcept { return parse_context( std::bool_constant{}, std::bool_constant{}, first, last, success, indent, error_handler, n); } template< bool DoTrace, bool UseCallbacks, typename Iter, typename Sentinel, typename ErrorHandler, typename GlobalState> auto make_context( Iter first, Sentinel last, bool & success, int & indent, ErrorHandler const & error_handler, GlobalState & globals, symbol_table_tries_t & symbol_table_tries, pending_symbol_table_operations_t & pending_symbol_table_operations) noexcept { return parse_context( std::bool_constant{}, std::bool_constant{}, first, last, success, indent, error_handler, globals, symbol_table_tries, pending_symbol_table_operations); } template< bool DoTrace, bool UseCallbacks, typename Iter, typename Sentinel, typename ErrorHandler, typename Callbacks> auto make_context( Iter first, Sentinel last, bool & success, int & indent, ErrorHandler const & error_handler, Callbacks const & callbacks, nope & n, symbol_table_tries_t & symbol_table_tries, pending_symbol_table_operations_t & pending_symbol_table_operations) noexcept { return parse_context( std::bool_constant{}, std::bool_constant{}, first, last, success, indent, error_handler, callbacks, n, symbol_table_tries, pending_symbol_table_operations); } template< bool DoTrace, bool UseCallbacks, typename Iter, typename Sentinel, typename ErrorHandler, typename Callbacks, typename GlobalState> auto make_context( Iter first, Sentinel last, bool & success, int & indent, ErrorHandler const & error_handler, Callbacks const & callbacks, GlobalState & globals, symbol_table_tries_t & symbol_table_tries, pending_symbol_table_operations_t & pending_symbol_table_operations) noexcept { return parse_context( std::bool_constant{}, std::bool_constant{}, first, last, success, indent, error_handler, callbacks, globals, symbol_table_tries, pending_symbol_table_operations); } template struct param_t { template decltype(auto) operator()(Context const & context) const { return parser::get(parser::_params(context), llong{}); } }; template using callable = decltype(std::declval()(std::declval()...)); template< typename Context, typename T, bool Callable = is_detected_v> struct resolve_impl { static auto call(Context const &, T const & x) { return x; } }; template struct resolve_impl { static auto call(Context const & context, T const & x) { return x(context); } }; template auto resolve(Context const & context, T const & x) { return resolve_impl::call(context, x); } template auto resolve(Context const &, nope n) { return n; } template auto resolve_rule_params(Context const & context, ParamsTuple const & params) { return detail::hl::transform(params, [&](auto const & x) { return detail::resolve(context, x); }); } template nope resolve_rule_params(Context const & context, nope) { return {}; } template LocalsType make_locals_impl(Context const & context, std::true_type) { return LocalsType(context); } template LocalsType make_locals_impl(Context const & context, std::false_type) { return LocalsType(); } template LocalsType make_locals(Context const & context) { return detail::make_locals_impl( context, typename std::is_convertible:: type{}); } template decltype(auto) _indent(Context const & context) { return *context.trace_indent_; } template decltype(auto) _callbacks(Context const & context) { return *context.callbacks_; } // Type traits. template using remove_cv_ref_t = typename std::remove_cv< typename std::remove_reference::type>::type; template using comparison = decltype(std::declval() == std::declval()); template constexpr bool is_equality_comparable_with_v = is_detected_v; template struct is_nope : std::false_type {}; template<> struct is_nope : std::true_type {}; template constexpr bool is_nope_v = is_nope>::value; template struct is_eps_p : std::false_type {}; template struct is_eps_p> : std::true_type {}; template struct is_unconditional_eps : std::false_type {}; template<> struct is_unconditional_eps> : std::true_type {}; template constexpr bool is_unconditional_eps_v = is_unconditional_eps>::value; template struct is_zero_plus_p : std::false_type {}; template struct is_zero_plus_p> : std::true_type {}; template struct is_or_p : std::false_type {}; template struct is_or_p> : std::true_type {}; template struct is_perm_p : std::false_type {}; template struct is_perm_p> : std::true_type {}; template struct is_seq_p : std::false_type {}; template struct is_seq_p> : std::true_type {}; template struct is_one_plus_p : std::false_type {}; template struct is_one_plus_p> : std::true_type {}; template struct is_utf8_view : std::false_type {}; template struct is_utf8_view> : std::true_type {}; template using optional_type = remove_cv_ref_t())>; template constexpr bool is_invocable_v = is_detected_v; template using has_begin = decltype(*detail::text::detail::begin(std::declval())); template using has_end = decltype(detail::text::detail::end(std::declval())); template constexpr bool is_range = is_detected_v && is_detected_v; template using has_push_back = decltype(std::declval().push_back(*std::declval().begin())); #if BOOST_PARSER_USE_CONCEPTS template using iterator_t = std::ranges::iterator_t; template using sentinel_t = std::ranges::sentinel_t; template using iter_value_t = std::iter_value_t; template using iter_reference_t = std::iter_reference_t; template using range_value_t = std::ranges::range_value_t; template using range_reference_t = std::ranges::range_reference_t; template using range_rvalue_reference_t = std::ranges::range_rvalue_reference_t; template constexpr bool is_parsable_code_unit_v = code_unit; #else template using iterator_t = decltype(detail::text::detail::begin(std::declval())); template using sentinel_t = decltype(detail::text::detail::end(std::declval())); template using iter_value_t = typename std::iterator_traits::value_type; template using iter_reference_t = decltype(*std::declval()); template using iter_rvalue_reference_t = decltype(std::move(*std::declval())); template using range_value_t = iter_value_t>; template using range_reference_t = iter_reference_t>; template using range_rvalue_reference_t = iter_rvalue_reference_t>; template using has_insert = decltype(std::declval().insert( std::declval().begin(), *std::declval().begin())); template using has_range_insert = decltype(std::declval().insert( std::declval().begin(), std::declval().begin(), std::declval().end())); template constexpr bool is_container_v = is_detected_v; template constexpr bool container_and_value_type = is_container_v && (std::is_same_v, U> || (std::is_same_v && std::is_same_v)); template constexpr bool is_parsable_code_unit_impl = std::is_same_v || std::is_same_v || #if defined(__cpp_char8_t) std::is_same_v || #endif std::is_same_v || std::is_same_v; template constexpr bool is_parsable_code_unit_v = is_parsable_code_unit_impl>; template constexpr bool is_parsable_iter_v = is_parsable_code_unit_v< remove_cv_ref_t>>; template constexpr bool is_parsable_range_v = is_parsable_code_unit_v< remove_cv_ref_t>> && is_detected_v; template constexpr bool is_parsable_pointer_v = std::is_pointer_v> && is_parsable_code_unit_v< std::remove_pointer_t>>; template constexpr bool is_parsable_range_like_v = is_parsable_range_v || is_parsable_pointer_v; } template constexpr bool container = detail::is_container_v; namespace detail { #endif template> constexpr bool is_code_unit_pointer_v = false; template constexpr bool is_code_unit_pointer_v = is_parsable_code_unit_v>; template constexpr bool is_range_like = is_range || is_code_unit_pointer_v; template constexpr bool is_char8_iter_v = #if defined(__cpp_char8_t) std::is_same_v, char8_t> #else false #endif ; // Metafunctions. template struct to_hana_tuple_or_type_impl; template struct to_hana_tuple_or_type_impl> { using type = std::optional>; }; template struct to_hana_tuple_or_type_impl> { using type = std::variant; }; template struct to_hana_tuple_or_type_impl> { // The reason this is not two separate specializations, one // for tuple and on for tuple>, is because // MSVC. using type = std::conditional_t, T, std::optional>; }; template struct to_hana_tuple_or_type_impl> { using type = T; }; template<> struct to_hana_tuple_or_type_impl> { using type = nope; }; template<> struct to_hana_tuple_or_type_impl> { using type = nope; }; template struct to_hana_tuple_or_type; template struct to_hana_tuple_or_type> { // This has to be done in two steps like this because MSVC. using type = typename to_hana_tuple_or_type_impl:: type; }; template using to_hana_tuple_or_type_t = typename to_hana_tuple_or_type::type; template auto make_sequence_of() { if constexpr ( std::is_same_v || std::is_same_v) { return std::string{}; } else if constexpr (std::is_same_v) { return nope{}; } else { return std::vector{}; } } template constexpr bool is_char_type_v = std::is_same_v || std::is_same_v; template struct optional_of_impl { using type = std::optional; }; template struct optional_of_impl> { using type = std::optional; }; template<> struct optional_of_impl { using type = nope; }; template using optional_of = typename optional_of_impl::type; template struct unwrapped_optional { using type = T; }; template struct unwrapped_optional> { using type = T; }; template using unwrapped_optional_t = typename unwrapped_optional::type; // Etc. template struct wrapper { using type = T; constexpr bool operator==(wrapper) const { return true; } }; struct wrap { template constexpr auto operator()(T type) const { return wrapper{}; } }; struct unwrap { template constexpr auto operator()(T wrapped_type) const { return typename T::type{}; } }; template void insert(Container & c, T && x) { if constexpr (is_detected_v) { c.push_back((T &&) x); } else { c.insert((T &&) x); } } template void insert(Container & c, I first, I last) { std::for_each(first, last, [&](auto && x) { using type = decltype(x); insert(c, (type &&) x); }); } template constexpr bool needs_transcoding_to_utf8 = (std::is_same_v>, char> #if defined(__cpp_char8_t) || std::is_same_v>, char8_t> #endif ) && (std::is_same_v, char32_t> #if !defined(_MSC_VER) || std::is_same_v, wchar_t> #endif ); template void append(Container & c, T && x, bool gen_attrs) { if (!gen_attrs) return; if constexpr (needs_transcoding_to_utf8) { char32_t cps[1] = {(char32_t)x}; auto const r = cps | text::as_utf8; c.insert(c.end(), r.begin(), r.end()); } else { detail::insert(c, (T &&)x); } } template void append(std::optional & c, T && x, bool gen_attrs) { if (!gen_attrs) return; if (!c) c = Container(); return detail::append(*c, (T &&) x, gen_attrs); } template void append(Container & c, nope &&, bool) {} template void append(nope &, T &&, bool) {} inline void append(nope &, nope &&, bool) {} template void append(Container & c, Iter first, Sentinel last, bool gen_attrs) { if (!gen_attrs) return; if constexpr (needs_transcoding_to_utf8< Container, iter_value_t>) { auto const r = BOOST_PARSER_SUBRANGE(first, last) | text::as_utf8; c.insert(c.end(), r.begin(), r.end()); } else { detail::insert(c, first, last); } } template void append( std::optional & c, Iter first, Sentinel last, bool gen_attrs) { if (!gen_attrs) return; if (!c) c = Container(); return detail::append(*c, first, last, gen_attrs); } template void append(nope &, Iter first, Sentinel last, bool gen_attrs) {} constexpr inline flags default_flags() { return flags( uint32_t(flags::gen_attrs) | uint32_t(flags::use_skip)); } constexpr inline flags enable_skip(flags f) { return flags(uint32_t(f) | uint32_t(flags::use_skip)); } constexpr inline flags disable_skip(flags f) { return flags(uint32_t(f) & ~uint32_t(flags::use_skip)); } constexpr inline flags enable_attrs(flags f) { return flags(uint32_t(f) | uint32_t(flags::gen_attrs)); } constexpr inline flags disable_attrs(flags f) { return flags(uint32_t(f) & ~uint32_t(flags::gen_attrs)); } constexpr inline flags enable_trace(flags f) { return flags(uint32_t(f) | uint32_t(flags::trace)); } constexpr inline flags disable_trace(flags f) { return flags(uint32_t(f) & ~uint32_t(flags::trace)); } constexpr inline flags set_in_apply_parser(flags f) { return flags(uint32_t(f) | uint32_t(flags::in_apply_parser)); } constexpr inline bool gen_attrs(flags f) { return (uint32_t(f) & uint32_t(flags::gen_attrs)) == uint32_t(flags::gen_attrs); } constexpr inline bool use_skip(flags f) { return (uint32_t(f) & uint32_t(flags::use_skip)) == uint32_t(flags::use_skip); } constexpr inline bool in_apply_parser(flags f) { return (uint32_t(f) & uint32_t(flags::in_apply_parser)) == uint32_t(flags::in_apply_parser); } struct skip_skipper { template< typename Iter, typename Sentinel, typename Context, typename SkipParser> nope operator()( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, flags flags, bool & success) const noexcept { return {}; } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void operator()( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, flags flags, bool & success, Attribute & retval) const {} }; template void skip(Iter & first, Sentinel last, null_parser const & skip_, flags f) {} template void skip(Iter & first, Sentinel last, SkipParser const & skip_, flags f) { if (!detail::use_skip(f)) return; bool success = true; int indent = 0; rethrow_error_handler eh; nope const n; auto const context = detail::make_context( first, last, success, indent, eh, n, n, n); while (success) { skip_( first, last, context, skip_skipper{}, detail::disable_trace(detail::disable_skip(f)), success); } } enum : int64_t { unbounded = -1 }; template std::optional make_parse_result(T & x, bool success) { std::optional retval; if (success) retval = std::move(x); return retval; } inline bool make_parse_result(nope &, bool success) { return success; } inline bool make_parse_result(none &, bool success) { return success; } template struct char_pair { LoType lo_; HiType hi_; }; using case_fold_array_t = std::array; template struct no_case_iter : stl_interfaces::proxy_iterator_interface< no_case_iter, std::forward_iterator_tag, char32_t> { no_case_iter() : it_(), last_(), idx_(0), last_idx_() {} no_case_iter(I it, S last) : it_(it), last_(last), idx_(0), last_idx_(0) { fold(); } char32_t operator*() const { return folded_[idx_]; } no_case_iter & operator++() { ++idx_; if (last_idx_ <= idx_) { ++it_; fold(); } return *this; } I base() const { return it_; } friend bool operator==(no_case_iter lhs, S rhs) noexcept { return lhs.it_ == rhs; } friend bool operator==(no_case_iter lhs, no_case_iter rhs) noexcept { return lhs.it_ == rhs.it_ && lhs.idx_ == rhs.idx_; } using base_type = stl_interfaces::proxy_iterator_interface< no_case_iter, std::forward_iterator_tag, char32_t>; using base_type::operator++; private: void fold() { idx_ = 0; if (it_ == last_) { folded_[0] = 0; last_idx_ = 1; return; } auto const folded_last = detail::case_fold(*it_, folded_.begin()); last_idx_ = int(folded_last - folded_.begin()); } case_fold_array_t folded_; I it_; [[no_unique_address]] S last_; int idx_; int last_idx_; }; template struct case_fold_view { using iterator = no_case_iter, detail::sentinel_t>; case_fold_view(V base) : base_(std::move(base)) {} iterator begin() const { return iterator( text::detail::begin(base_), text::detail::end(base_)); } auto end() const { return text::detail::end(base_); } private: V base_; }; enum class symbol_table_op { insert, erase, clear }; template struct symbol_table_operation { std::string key_; std::optional value_; symbol_table_op kind_; }; template void apply_symbol_table_operations( std::vector> & initial_elements, std::vector> & pending_operations) { auto lower_bound = [&initial_elements](std::string const & str) { return std::lower_bound( initial_elements.begin(), initial_elements.end(), str, [](auto const & a, auto b) { return a.first < b; }); }; for (auto & op : pending_operations) { if (op.kind_ == symbol_table_op::insert) { auto it = lower_bound(op.key_); if (it == initial_elements.end() || it->first != op.key_) { initial_elements.insert( it, std::pair( std::move(op.key_), std::move(*op.value_))); } else { it->second = std::move(*op.value_); } } else if (op.kind_ == symbol_table_op::erase) { auto it = lower_bound(op.key_); if (it != initial_elements.end() && it->first == op.key_) initial_elements.erase(it); } else { initial_elements.clear(); } } pending_operations.clear(); } template auto get_trie( Context const & context, symbol_parser const & sym_parser) { using trie_t = text::trie_map, T>; using result_type = std::pair; symbol_table_tries_t & symbol_table_tries = context.get_symbol_table_tries(); auto & [any, has_case_folded] = symbol_table_tries[(void *)&sym_parser.ref()]; bool const needs_case_folded = context.no_case_depth_; if (!any.has_value()) { any = trie_t{}; has_case_folded = false; trie_t & trie = *std::any_cast(&any); for (auto const & e : sym_parser.initial_elements()) { trie.insert(e.first | text::as_utf32, e.second); if (needs_case_folded) { trie.insert( case_fold_view(e.first | text::as_utf32), e.second); has_case_folded = true; } } return result_type(trie, has_case_folded); } else { trie_t & trie = *std::any_cast(&any); if (needs_case_folded && !has_case_folded) { trie_t new_trie = trie; for (auto && [key, value] : trie) { new_trie.insert( case_fold_view(key | text::as_utf32), value); } std::swap(new_trie, trie); } return result_type(trie, has_case_folded); } } template decltype(auto) get_pending_symtab_ops( Context const & context, symbol_parser const & sym_parser) { void const * ptr = static_cast(&sym_parser); auto & entry = (context.get_pending_symbol_table_operations())[ptr]; std::vector> * retval = nullptr; if (entry.visit_) { retval = std::any_cast< std::vector>>( &entry.ops_); } else { entry.ops_ = std::vector>{}; retval = std::any_cast< std::vector>>( &entry.ops_); entry.visit_ = [&sym_parser, ops_ptr = retval] { detail::apply_symbol_table_operations( sym_parser.initial_elements_, *ops_ptr); }; } return *retval; } template<> struct char_subranges { static constexpr char_subrange ranges[] = { {U'0', U'9'}, {U'A', U'F'}, {U'a', U'f'}, {U'\uff10', U'\uff19'}, {U'\uff21', U'\uff26'}, {U'\uff41', U'\uff46'}}; }; template<> struct char_subranges { static constexpr char_subrange ranges[] = { {U'\u0000', U'\u001f'}, {U'\u007f', U'\u009f'}}; }; template struct char_range { template bool contains(T c_, Context const & context) const { if constexpr (SortedUTF32) { return std::binary_search(chars_.begin(), chars_.end(), c_); } if (context.no_case_depth_) { case_fold_array_t folded; auto folded_last = detail::case_fold(c_, folded.begin()); if constexpr (std::is_same_v) { auto const cps = chars_ | text::as_utf32; auto chars_first = no_case_iter(cps.begin(), cps.end()); auto chars_last = cps.end(); auto result = text::search( chars_first, chars_last, folded.begin(), folded_last); return !result.empty(); } else { auto chars_first = no_case_iter(chars_.begin(), chars_.end()); auto chars_last = chars_.end(); auto result = text::search( chars_first, chars_last, folded.begin(), folded_last); return !result.empty(); } } else { if constexpr (std::is_same_v) { auto const cps = chars_ | text::as_utf32; return text::find(cps.begin(), cps.end(), c_) != cps.end(); } else { using element_type = remove_cv_ref_t; element_type const c = c_; return text::find(chars_.begin(), chars_.end(), c) != chars_.end(); } } } BOOST_PARSER_SUBRANGE chars_; }; template constexpr auto make_char_range(Iter first, Sentinel last) { return char_range{ BOOST_PARSER_SUBRANGE{first, last}}; } template constexpr auto make_char_range(R && r) noexcept { if constexpr (std::is_pointer_v>) { return detail::make_char_range( r, text::null_sentinel); } else { return detail::make_char_range( text::detail::begin(r), text::detail::end(r)); } } template auto no_case_aware_compare(Context const & context) { return [no_case = context.no_case_depth_](char32_t a, char32_t b) { if (no_case) { case_fold_array_t folded_a = {0, 0, 0}; detail::case_fold(a, folded_a.begin()); case_fold_array_t folded_b = {0, 0, 0}; detail::case_fold(b, folded_b.begin()); return Equal ? folded_a == folded_b : folded_a < folded_b; } else { return Equal ? a == b : a < b; } }; } template constexpr bool both_character_types = is_parsable_code_unit_v && is_parsable_code_unit_v; template using eq_comparable = decltype(std::declval() == std::declval()); template< typename Context, typename CharType, typename Expected, bool BothCharacters = both_character_types> struct unequal_impl { static bool call(Context const & context, CharType c, Expected const & expected) { auto resolved = detail::resolve(context, expected); if constexpr (is_detected_v< eq_comparable, CharType, decltype(resolved)>) { auto const compare = detail::no_case_aware_compare(context); return !compare(c, resolved); } else { return !resolved.contains(c, context); } } }; template struct unequal_impl { static bool call(Context const & context, CharType c, Expected expected) { return !detail::no_case_aware_compare(context)( c, expected); } }; template bool unequal(Context const & context, CharType c, Expected expected) { return unequal_impl::call( context, c, expected); } template< typename Context, typename CharType, typename LoType, typename HiType> bool unequal( Context const & context, CharType c, char_pair const & expected) { auto const less = detail::no_case_aware_compare(context); { auto lo = detail::resolve(context, expected.lo_); if (less(c, lo)) return true; } { auto hi = detail::resolve(context, expected.hi_); if (less(hi, c)) return true; } return false; } template bool unequal(Context const &, CharType, nope) { return false; } template using insertable = decltype(std::declval().insert( std::declval().end(), std::declval())); template auto make_from_tuple_impl(Tuple && tup, std::integer_sequence) -> decltype(T(parser::get((Tuple &&)tup, llong{})...)) { return T(parser::get((Tuple &&)tup, llong{})...); } template auto make_from_tuple(tuple && tup) -> decltype(detail::make_from_tuple_impl( std::move(tup), std::make_integer_sequence>>())) { return detail::make_from_tuple_impl( std::move(tup), std::make_integer_sequence>>()); } template using constructible_from_tuple_expr = decltype(detail::make_from_tuple(std::declval())); template{}> constexpr bool is_constructible_from_tuple_v = false; template constexpr bool is_constructible_from_tuple_v = is_detected_v; template constexpr void move_back_impl(Container & c, U && x) { using just_t = range_value_t; using just_u = remove_cv_ref_t; if constexpr (needs_transcoding_to_utf8) { char32_t cps[1] = {(char32_t)x}; auto const r = cps | text::as_utf8; c.insert(c.end(), r.begin(), r.end()); } else if constexpr (std::is_convertible_v) { detail::insert(c, (U &&)x); } else if constexpr ( !is_tuple::value && is_tuple::value && std::is_aggregate_v && !is_detected_v && is_struct_assignable_v) { auto int_seq = std::make_integer_sequence>(); detail::insert( c, detail::tuple_to_aggregate((U &&)x, int_seq)); } else if constexpr ( is_tuple::value && !is_tuple::value && std::is_aggregate_v && !is_detected_v && is_tuple_assignable_v) { just_t t; auto tie = detail::tie_aggregate(x); detail::aggregate_to_tuple( t, tie, std::make_integer_sequence>()); detail::insert(c, std::move(t)); } else if constexpr (is_constructible_from_tuple_v< just_t, just_u>) { detail::insert(c, detail::make_from_tuple((U &&)x)); } else { static_assert( sizeof(U) && false, "Could not insert value into container, by: just inserting " "it; doing tuple -> aggregate or aggregate -> tuple " "conversions; or tuple -> class type construction."); } } template constexpr void move_back(Container & c, T && x, bool gen_attrs) { if (!gen_attrs) return; detail::move_back_impl(c, (T &&)x); } template constexpr void move_back(Container & c, Container & x, bool gen_attrs) { if (!gen_attrs) return; c.insert(c.end(), x.begin(), x.end()); } template constexpr void move_back(Container & c, std::optional && x, bool gen_attrs) { if (!gen_attrs || !x) return; c.insert(c.end(), x->begin(), x->end()); } template constexpr void move_back(Container & c, std::optional & x, bool gen_attrs) { if (!gen_attrs || !x) return; detail::move_back_impl(c, std::move(*x)); } template< typename Container, typename T, typename Enable = std::enable_if_t>> constexpr void move_back(Container & c, std::optional && x, bool gen_attrs) { if (!gen_attrs || !x) return; detail::move_back_impl(c, std::move(*x)); } constexpr void move_back(nope, nope, bool gen_attrs) {} template constexpr void move_back(Container & c, nope, bool gen_attrs) {} template using move_assignable_expr = decltype(std::declval() = std::declval()); template constexpr bool is_move_assignable_v = is_detected_v; template constexpr void assign(T & t, U && u) { using just_t = remove_cv_ref_t; using just_u = remove_cv_ref_t; if constexpr (is_move_assignable_v) { static_assert( (!std::is_same_v || !std::is_arithmetic_v), "std::string is assignable from a char. Due to implicit " "conversions among arithmetic types, any arithmetic type " "(like int or double) is assignable to std::string. This " "is almost certainly not what you meant to write, so " "Boost.Parser disallows it. If you want to do this, write " "a semantic action and do it explicitly."); t = (U &&)u; } else if constexpr ( !is_tuple::value && is_tuple::value && std::is_aggregate_v && !std::is_convertible_v && is_struct_assignable_v) { auto int_seq = std::make_integer_sequence>(); t = detail::tuple_to_aggregate((U &&)u, int_seq); } else if constexpr ( is_tuple::value && !is_tuple::value && std::is_aggregate_v && !std::is_convertible_v && is_tuple_assignable_v) { auto tie = detail::tie_aggregate(u); detail::aggregate_to_tuple( t, tie, std::make_integer_sequence>()); } else if constexpr (is_constructible_from_tuple_v< just_t, just_u>) { t = detail::make_from_tuple((U &&)u); } else { static_assert( sizeof(T) && false, "Could not assign value, by: just assigning it; doing tuple " "-> aggregate or aggregate -> tuple conversions; or tuple " "-> class type construction."); } } template constexpr void assign(T &, nope) {} template constexpr void assign_copy(T & t, U const & u) { t = u; } template constexpr void assign_copy(T &, nope) {} template< typename Parser, typename Iter, typename Sentinel, typename Context, typename SkipParser, typename... T> void apply_parser( Parser const & parser, Iter & first, Sentinel last, Context const & context, SkipParser const & skip, flags flags, bool & success, std::optional> & retval) { using attr_t = decltype(parser.call( first, last, context, skip, flags, success)); if constexpr (std::is_same< attr_t, std::optional>>{}) { parser.call(first, last, context, skip, flags, success, retval); } else if constexpr (is_nope_v) { parser.call(first, last, context, skip, flags, success); } else { auto attr = parser.call(first, last, context, skip, flags, success); if (success) detail::assign(retval, std::variant(std::move(attr))); } } template< typename Parser, typename Iter, typename Sentinel, typename Context, typename SkipParser, typename... T> void apply_parser( Parser const & parser, Iter & first, Sentinel last, Context const & context, SkipParser const & skip, flags flags, bool & success, std::variant & retval) { auto attr = parser.call(first, last, context, skip, flags, success); if (success) detail::assign(retval, std::move(attr)); } template< typename Parser, typename Iter, typename Sentinel, typename Context, typename SkipParser, typename T> void apply_parser( Parser const & parser, Iter & first, Sentinel last, Context const & context, SkipParser const & skip, flags flags, bool & success, std::optional & retval) { auto attr = parser.call(first, last, context, skip, flags, success); if (success) detail::assign(retval, std::move(attr)); } template< typename Parser, typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void apply_parser( Parser const & parser, Iter & first, Sentinel last, Context const & context, SkipParser const & skip, flags flags, bool & success, Attribute & retval) { parser.call(first, last, context, skip, flags, success, retval); } // API implementations template auto has_attribute(Iter first, Sentinel last, Parser parser); template struct scoped_base_assign { scoped_base_assign(BaseIter & base, Iter & it) : base_(base), it_(it) {} ~scoped_base_assign() { base_ = it_.base(); } BaseIter & base_; Iter & it_; }; template using has_parser_data_member_expr = decltype(std::declval().parser_); template constexpr bool has_parser_data_member_v = is_detected_v; template using has_parsers_data_member_expr = decltype(std::declval().parsers_); template constexpr bool has_parsers_data_member_v = is_detected_v; struct scoped_apply_pending_symbol_table_operations { scoped_apply_pending_symbol_table_operations( pending_symbol_table_operations_t & pending_ops) : pending_ops_(pending_ops) {} ~scoped_apply_pending_symbol_table_operations() { for (auto & [_, entry] : pending_ops_) { entry.visit_(); } } pending_symbol_table_operations_t & pending_ops_; }; template< bool Debug, typename Iter, typename Sentinel, typename Parser, typename Attr, typename ErrorHandler> bool parse_impl( Iter & first, Sentinel last, Parser const & parser, ErrorHandler const & error_handler, Attr & attr) { auto const initial_first = first; bool success = true; int trace_indent = 0; detail::symbol_table_tries_t symbol_table_tries; pending_symbol_table_operations_t pending_symbol_table_operations; scoped_apply_pending_symbol_table_operations apply_pending( pending_symbol_table_operations); auto context = detail::make_context( first, last, success, trace_indent, error_handler, parser.globals_, symbol_table_tries, pending_symbol_table_operations); auto const flags = Debug ? detail::enable_trace(detail::flags::gen_attrs) : detail::flags::gen_attrs; try { parser( first, last, context, detail::null_parser{}, flags, success, attr); if (Debug) detail::final_trace(context, flags, attr); return success; } catch (parse_error const & e) { if (error_handler(initial_first, last, e) == error_handler_result::rethrow) { throw; } return false; } } template< bool Debug, typename Iter, typename Sentinel, typename Parser, typename ErrorHandler> auto parse_impl( Iter & first, Sentinel last, Parser const & parser, ErrorHandler const & error_handler) { auto const initial_first = first; bool success = true; int trace_indent = 0; detail::symbol_table_tries_t symbol_table_tries; pending_symbol_table_operations_t pending_symbol_table_operations; scoped_apply_pending_symbol_table_operations apply_pending( pending_symbol_table_operations); auto context = detail::make_context( first, last, success, trace_indent, error_handler, parser.globals_, symbol_table_tries, pending_symbol_table_operations); auto const flags = Debug ? detail::enable_trace(detail::flags::gen_attrs) : detail::flags::gen_attrs; using attr_t = typename detail::attribute_impl< BOOST_PARSER_SUBRANGE, Sentinel>, Parser>::type; try { attr_t attr_ = parser( first, last, context, detail::null_parser{}, flags, success); if (Debug) detail::final_trace(context, flags, nope{}); return detail::make_parse_result(attr_, success); } catch (parse_error const & e) { if (error_handler(initial_first, last, e) == error_handler_result::rethrow) { throw; } attr_t attr_{}; return detail::make_parse_result(attr_, false); } } template< bool Debug, typename Iter, typename Sentinel, typename Parser, typename ErrorHandler, typename Callbacks> bool callback_parse_impl( Iter & first, Sentinel last, Parser const & parser, ErrorHandler const & error_handler, Callbacks const & callbacks) { auto const initial_first = first; bool success = true; int trace_indent = 0; detail::symbol_table_tries_t symbol_table_tries; pending_symbol_table_operations_t pending_symbol_table_operations; scoped_apply_pending_symbol_table_operations apply_pending( pending_symbol_table_operations); auto context = detail::make_context( first, last, success, trace_indent, error_handler, callbacks, parser.globals_, symbol_table_tries, pending_symbol_table_operations); auto const flags = Debug ? detail::enable_trace(detail::flags::gen_attrs) : detail::flags::gen_attrs; try { parser( first, last, context, detail::null_parser{}, flags, success); if (Debug) detail::final_trace(context, flags, nope{}); return success; } catch (parse_error const & e) { if (error_handler(initial_first, last, e) == error_handler_result::rethrow) { throw; } return false; } } template< bool Debug, typename Iter, typename Sentinel, typename Parser, typename SkipParser, typename Attr, typename ErrorHandler> bool skip_parse_impl( Iter & first, Sentinel last, Parser const & parser, SkipParser const & skip, ErrorHandler const & error_handler, Attr & attr) { auto const initial_first = first; bool success = true; int trace_indent = 0; detail::symbol_table_tries_t symbol_table_tries; pending_symbol_table_operations_t pending_symbol_table_operations; scoped_apply_pending_symbol_table_operations apply_pending( pending_symbol_table_operations); auto context = detail::make_context( first, last, success, trace_indent, error_handler, parser.globals_, symbol_table_tries, pending_symbol_table_operations); auto const flags = Debug ? detail::enable_trace(detail::default_flags()) : detail::default_flags(); detail::skip(first, last, skip, flags); try { parser(first, last, context, skip, flags, success, attr); detail::skip(first, last, skip, flags); if (Debug) detail::final_trace(context, flags, attr); return success; } catch (parse_error const & e) { if (error_handler(initial_first, last, e) == error_handler_result::rethrow) { throw; } return false; } } template< bool Debug, typename Iter, typename Sentinel, typename Parser, typename SkipParser, typename ErrorHandler> auto skip_parse_impl( Iter & first, Sentinel last, Parser const & parser, SkipParser const & skip, ErrorHandler const & error_handler) { auto const initial_first = first; bool success = true; int trace_indent = 0; detail::symbol_table_tries_t symbol_table_tries; pending_symbol_table_operations_t pending_symbol_table_operations; scoped_apply_pending_symbol_table_operations apply_pending( pending_symbol_table_operations); auto context = detail::make_context( first, last, success, trace_indent, error_handler, parser.globals_, symbol_table_tries, pending_symbol_table_operations); auto const flags = Debug ? detail::enable_trace(detail::default_flags()) : detail::default_flags(); detail::skip(first, last, skip, flags); using attr_t = typename detail::attribute_impl< BOOST_PARSER_SUBRANGE, Sentinel>, Parser, SkipParser>::type; try { attr_t attr_ = parser(first, last, context, skip, flags, success); detail::skip(first, last, skip, flags); if (Debug) detail::final_trace(context, flags, nope{}); return detail::make_parse_result(attr_, success); } catch (parse_error const & e) { if (error_handler(initial_first, last, e) == error_handler_result::rethrow) { throw; } attr_t attr_{}; return detail::make_parse_result(attr_, false); } } template< bool Debug, typename Iter, typename Sentinel, typename Parser, typename SkipParser, typename ErrorHandler, typename Callbacks> bool callback_skip_parse_impl( Iter & first, Sentinel last, Parser const & parser, SkipParser const & skip, ErrorHandler const & error_handler, Callbacks const & callbacks) { auto const initial_first = first; bool success = true; int trace_indent = 0; detail::symbol_table_tries_t symbol_table_tries; pending_symbol_table_operations_t pending_symbol_table_operations; scoped_apply_pending_symbol_table_operations apply_pending( pending_symbol_table_operations); auto context = detail::make_context( first, last, success, trace_indent, error_handler, callbacks, parser.globals_, symbol_table_tries, pending_symbol_table_operations); auto const flags = Debug ? detail::enable_trace(detail::default_flags()) : detail::default_flags(); detail::skip(first, last, skip, flags); try { parser(first, last, context, skip, flags, success); detail::skip(first, last, skip, flags); if (Debug) detail::final_trace(context, flags, nope{}); return success; } catch (parse_error const & e) { if (error_handler(initial_first, last, e) == error_handler_result::rethrow) { throw; } return false; } } template constexpr auto make_input_subrange(R && r) noexcept { using r_t = remove_cv_ref_t; if constexpr (std::is_pointer_v) { using value_type = iter_value_t; if constexpr (std::is_same_v) { return BOOST_PARSER_SUBRANGE(r, text::null_sentinel); } else { return r | text::as_utf32; } } else { using value_type = range_value_t; if constexpr (text::detail::is_bounded_array_v) { if constexpr (std::is_same_v) { auto first = detail::text::detail::begin(r); auto last = detail::text::detail::end(r); if (first != last && !*std::prev(last)) --last; return BOOST_PARSER_SUBRANGE(first, last); } else { return r | text::as_utf32; } } else { if constexpr ( std::is_same_v && !is_utf8_view::value) { return BOOST_PARSER_SUBRANGE( detail::text::detail::begin(r), detail::text::detail::end(r)); } else { return r | text::as_utf32; } } } } template constexpr auto make_view_begin(R & r) noexcept { if constexpr (std::is_pointer_v>) { return r; } else { return detail::text::detail::begin(r); } } template constexpr auto make_view_end(R & r) noexcept { if constexpr (std::is_pointer_v>) { return text::null_sentinel; } else { return detail::text::detail::end(r); } } template< typename Iter1, typename Sentinel1, typename Iter2, typename Sentinel2, typename Pred> std::pair mismatch( Iter1 first1, Sentinel1 last1, Iter2 first2, Sentinel2 last2, Pred pred) { std::pair retval{first1, first2}; while (retval.first != last1 && retval.second != last2 && pred(*retval.first, *retval.second)) { ++retval.first; ++retval.second; } return retval; } template< typename Iter1, typename Sentinel1, typename Iter2, typename Sentinel2> std::pair no_case_aware_string_mismatch( Iter1 first1, Sentinel1 last1, Iter2 first2, Sentinel2 last2, bool no_case) { if (no_case) { auto it1 = no_case_iter(first1, last1); auto it2 = no_case_iter(first2, last2); auto const mismatch = detail::mismatch( it1, last1, it2, last2, std::equal_to{}); return std::pair{ mismatch.first.base(), mismatch.second.base()}; } else { return detail::mismatch( first1, last1, first2, last2, std::equal_to{}); } } template T if_full_parse( I initial_first, I & first, S last, ErrorHandler const & error_handler, T retval) { if (first != last) { if (retval && error_handler( initial_first, last, parse_error(first, "end of input")) == error_handler_result::rethrow) { throw; } if constexpr (std::is_same_v) retval = false; else retval = std::nullopt; } return std::move(retval); } // The notion of comaptibility is that, given a parser with the // Attribute Tuple, we can parse into Struct instead. template constexpr auto is_struct_compatible(); struct element_compatibility { template constexpr auto operator()(T result, U x) const { using struct_elem = remove_cv_ref_t{}))>; using tuple_elem = remove_cv_ref_t{}))>; if constexpr (!T::value) { return std::false_type{}; } else if constexpr ( is_optional_v && is_optional_v) { using struct_opt_type = optional_type; using tuple_opt_type = optional_type; using retval_t = decltype((*this)( result, detail::hl::make_tuple( std::declval(), std::declval()))); return retval_t{}; } else if constexpr (std::is_convertible_v< tuple_elem &&, struct_elem>) { return std::true_type{}; } else if constexpr ( container && container) { return detail::is_struct_compatible< range_value_t, range_value_t>(); } else { return std::bool_constant()>{}; } } }; template constexpr auto is_struct_compatible() { if constexpr ( !std::is_aggregate_v || struct_arity_v != tuple_size_) { return std::false_type{}; } else { using result_t = decltype(detail::hl::fold_left( detail::hl::zip( detail::tie_aggregate(std::declval()), std::declval()), std::true_type{}, element_compatibility{})); return result_t{}; } } template constexpr bool is_struct_compatible_v = detail::is_struct_compatible(); template constexpr auto parser_attr_or_container_value_type() { if constexpr (is_nope_v) { return nope{}; } else if constexpr (is_optional_v) { return ParserAttr{}; } else { using value_type = range_value_t; return std::conditional_t< std::is_convertible_v, ParserAttr, value_type>{}; } } template using parser_attr_or_container_value_type_v = decltype(parser_attr_or_container_value_type< ParserAttr, GivenContainerAttr>()); template constexpr auto tuple_or_struct_size(T && x) { if constexpr (is_tuple>{}) { return hl::size(x); } else { return llong>>{}; } } template struct attr_reset { attr_reset(T & x) : x_(std::addressof(x)) {} attr_reset(attr_reset const &) = delete; attr_reset(attr_reset &&) = delete; attr_reset & operator=(attr_reset const &) = delete; attr_reset & operator=(attr_reset &&) = delete; ~attr_reset() { if (x_) *x_ = T(); } bool operator=(bool b) { if (b) x_ = nullptr; return b; } private: T * x_; }; } #ifndef BOOST_PARSER_DOXYGEN // This constraint is only here to allow the alternate-call semantic // action metaprogramming logic to function on MSVC. template auto _val(Context const & context) -> std::conditional_t< detail::is_nope_v, none, decltype(*context.val_)> { if constexpr (detail::is_nope_v) return none{}; else return *context.val_; } template decltype(auto) _attr(Context const & context) { if constexpr (detail::is_nope_v) return none{}; else return *context.attr_; } template decltype(auto) _where(Context const & context) { return *context.where_; } template decltype(auto) _begin(Context const & context) { return context.first_; } template decltype(auto) _end(Context const & context) { return context.last_; } template decltype(auto) _pass(Context const & context) { return *context.pass_; } template decltype(auto) _locals(Context const & context) { if constexpr (detail::is_nope_v) return none{}; else return *context.locals_; } template decltype(auto) _params(Context const & context) { if constexpr (detail::is_nope_v) return none{}; else return *context.params_; } template decltype(auto) _globals(Context const & context) { if constexpr (detail::is_nope_v) return none{}; else return *context.globals_; } template decltype(auto) _no_case(Context const & context) { return context.no_case_depth_; } template decltype(auto) _error_handler(Context const & context) { return *context.error_handler_; } #if BOOST_PARSER_USE_CONCEPTS template #else template #endif void _report_error(Context const & context, std::string_view message, I location) { return context.error_handler_->diagnose( diagnostic_kind::error, message, context, location); } template void _report_error(Context const & context, std::string_view message) { return context.error_handler_->diagnose( diagnostic_kind::error, message, context); } #if BOOST_PARSER_USE_CONCEPTS template #else template #endif void _report_warning( Context const & context, std::string_view message, I location) { return context.error_handler_->diagnose( diagnostic_kind::warning, message, context, location); } template void _report_warning(Context const & context, std::string_view message) { return context.error_handler_->diagnose( diagnostic_kind::warning, message, context); } #endif /** An invocable that returns the `I`th parameter to the bottommost rule. This is useful for forwarding parameters to sub-rules. */ template inline constexpr detail::param_t _p = {}; // Second order parsers. /** A very large sentinel value used to represent pseudo-infinity. */ int64_t const Inf = detail::unbounded; #ifndef BOOST_PARSER_DOXYGEN template< typename Parser, typename DelimiterParser, typename MinType, typename MaxType> struct repeat_parser { constexpr repeat_parser( Parser parser, MinType _min, MaxType _max, DelimiterParser delimiter_parser = DelimiterParser{}) : parser_(parser), delimiter_parser_(delimiter_parser), min_(_min), max_(_max) {} template< typename Iter, typename Sentinel, typename Context, typename SkipParser> auto call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) const { using attr_t = decltype(parser_.call( first, last, context, skip, flags, success)); auto retval = detail::make_sequence_of(); call(first, last, context, skip, flags, success, retval); return retval; } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute & retval) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, detail::in_apply_parser(flags) ? detail::disable_trace(flags) : flags, retval); if constexpr (detail::is_optional_v) { detail::optional_type attr; detail::apply_parser( *this, first, last, context, skip, detail::set_in_apply_parser(flags), success, attr); if (success) retval = std::move(attr); } else { // Otherwise, Attribute must be a container or a nope. using attr_t = detail::parser_attr_or_container_value_type_v< decltype(parser_.call( first, last, context, skip, flags, success)), Attribute>; int64_t count = 0; for (int64_t end = detail::resolve(context, min_); count != end; ++count) { detail::skip(first, last, skip, flags); attr_t attr{}; parser_.call( first, last, context, skip, flags, success, attr); if (!success) { detail::assign(retval, Attribute()); return; } detail::move_back( retval, std::move(attr), detail::gen_attrs(flags)); } int64_t const end = detail::resolve(context, max_); // It looks like you've created a repeated epsilon parser, by // writing "*eps", "+eps", "repeat(2, Inf)[eps]", or similar. BOOST_PARSER_DEBUG_ASSERT( !detail::is_unconditional_eps{} || end < Inf); for (; count != end; ++count) { auto const prev_first = first; // This is only ever used in delimited_parser, which // always has a min=1; we therefore know we're after a // previous element when this executes. if constexpr (!detail::is_nope_v) { detail::skip(first, last, skip, flags); delimiter_parser_.call( first, last, context, skip, detail::disable_attrs(flags), success); if (!success) { success = true; first = prev_first; break; } } detail::skip(first, last, skip, flags); attr_t attr{}; parser_.call( first, last, context, skip, flags, success, attr); if (!success) { success = true; first = prev_first; break; } detail::move_back( retval, std::move(attr), detail::gen_attrs(flags)); } } } Parser parser_; DelimiterParser delimiter_parser_; MinType min_; MaxType max_; }; #endif template struct zero_plus_parser : repeat_parser { constexpr zero_plus_parser(Parser parser) : repeat_parser(parser, 0, Inf) {} }; template struct one_plus_parser : repeat_parser { constexpr one_plus_parser(Parser parser) : repeat_parser(parser, 1, Inf) {} }; template struct delimited_seq_parser : repeat_parser { constexpr delimited_seq_parser( Parser parser, DelimiterParser delimiter_parser) : repeat_parser( parser, 1, Inf, delimiter_parser) {} }; //[ opt_parser_beginning template struct opt_parser { //] //[ opt_parser_attr_call template< typename Iter, typename Sentinel, typename Context, typename SkipParser> auto call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) const { using attr_t = decltype(parser_.call( first, last, context, skip, flags, success)); detail::optional_of retval; call(first, last, context, skip, flags, success, retval); return retval; } //] //[ opt_parser_out_param_call template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute & retval) const { //[ opt_parser_trace [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, retval); //] //[ opt_parser_skip detail::skip(first, last, skip, flags); //] //[ opt_parser_no_gen_attr_path if (!detail::gen_attrs(flags)) { parser_.call(first, last, context, skip, flags, success); success = true; return; } //] //[ opt_parser_gen_attr_path parser_.call(first, last, context, skip, flags, success, retval); if (!success) retval = Attribute(); success = true; //] } //] //[ opt_parser_end Parser parser_; }; //] template struct or_parser { constexpr or_parser(ParserTuple parsers) : parsers_(parsers) {} #ifndef BOOST_PARSER_DOXYGEN template< typename Iter, typename Sentinel, typename Context, typename SkipParser> struct use_parser_t { template auto operator()(Parser const & parser) const { detail::skip(first_, last_, skip_, flags_); success_ = true; // In case someone earlier already failed... return parser.call( first_, last_, context_, skip_, flags_, success_); } template void operator()(Parser const & parser, Attribute & retval) const { detail::skip(first_, last_, skip_, flags_); success_ = true; // In case someone earlier already failed... detail::apply_parser( parser, first_, last_, context_, skip_, flags_, success_, retval); } Iter & first_; Sentinel last_; Context const & context_; SkipParser const & skip_; detail::flags flags_; bool & success_; }; #endif template< typename Iter, typename Sentinel, typename Context, typename SkipParser> auto call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) const { use_parser_t const use_parser{ first, last, context, skip, flags, success}; // A result type for each of the parsers in parsers_. using all_types = decltype(detail::hl::transform(parsers_, use_parser)); // Same as above, wrapped in detail::wrapper. using all_types_wrapped = decltype(detail::hl::transform(all_types{}, detail::wrap{})); // Returns a tuple<> containing two things: 1) A tuple of only the // unique wrapped types from above, without nopes; this may be // empty. 2) std::true_type or std::false_type indicating whether // nopes were found; if so, the final result is an optional. auto append_unique = [](auto result, auto x) { using x_type = typename decltype(x)::type; if constexpr (detail::is_nope_v) { return detail::hl::make_pair( detail::hl::first(result), std::true_type{}); } else if constexpr (detail::hl::contains( detail::hl::first(result), x)) { return result; } else { return detail::hl::make_pair( detail::hl::append(detail::hl::first(result), x), detail::hl::second(result)); } }; using wrapped_unique_types = decltype(detail::hl::fold_left( all_types_wrapped{}, detail::hl::make_pair(tuple<>{}, std::false_type{}), append_unique)); // Same as above, with the tuple types unwrapped. using unwrapped_types = decltype(detail::hl::make_pair( detail::hl::transform( detail::hl::first(wrapped_unique_types{}), detail::unwrap{}), detail::hl::second(wrapped_unique_types{}))); // Types above converted to a "variant", which may actually be a // non-variant type T if that is the only unique non-nope type, or a // nope if unwrapped_types is empty. using result_t = detail::to_hana_tuple_or_type_t; result_t retval{}; call(first, last, context, skip, flags, success, retval); return retval; } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute & retval) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, retval); use_parser_t const use_parser{ first, last, context, skip, flags, success}; bool done = false; auto try_parser = [prev_first = first, &use_parser, &success, flags, &retval, &done](auto const & parser) { if (done) return; if (detail::gen_attrs(flags)) use_parser(parser, retval); else use_parser(parser); if (success) done = true; else use_parser.first_ = prev_first; }; detail::hl::for_each(parsers_, try_parser); // TODO: -> fold-expr if (!done) success = false; } #ifndef BOOST_PARSER_DOXYGEN template constexpr auto prepend(parser_interface parser) const noexcept; template constexpr auto append(parser_interface parser) const noexcept; #endif ParserTuple parsers_; }; template struct perm_parser { constexpr perm_parser(ParserTuple parsers) : parsers_(parsers) {} constexpr perm_parser( ParserTuple parsers, DelimiterParser delimiter_parser) : parsers_(parsers), delimiter_parser_(delimiter_parser) {} #ifndef BOOST_PARSER_DOXYGEN template< typename Iter, typename Sentinel, typename Context, typename SkipParser> struct use_parser_t { template auto operator()(Parser const & parser) const { detail::skip(first_, last_, skip_, flags_); success_ = true; // In case someone earlier already failed... return parser.call( first_, last_, context_, skip_, flags_, success_); } template void operator()(Parser const & parser, Attribute & retval) const { detail::skip(first_, last_, skip_, flags_); success_ = true; // In case someone earlier already failed... detail::apply_parser( parser, first_, last_, context_, skip_, flags_, success_, retval); } Iter & first_; Sentinel last_; Context const & context_; SkipParser const & skip_; detail::flags flags_; bool & success_; }; #endif template< typename Iter, typename Sentinel, typename Context, typename SkipParser> auto call( Iter & first_, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) const { Iter first = first_; use_parser_t const use_parser{ first, last, context, skip, flags, success}; using result_t = decltype(detail::hl::transform(parsers_, use_parser)); result_t retval{}; [[maybe_unused]] auto _ = detail::scoped_trace( *this, first_, last, context, flags, retval); call_impl( first, last, context, skip, flags, success, retval, std::make_integer_sequence< int, detail::tuple_size_>{}); if (success) first_ = first; return retval; } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void call( Iter & first_, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute & retval) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first_, last, context, flags, retval); Iter first = first_; use_parser_t const use_parser{ first, last, context, skip, flags, success}; using result_t = decltype(detail::hl::transform(parsers_, use_parser)); constexpr auto indices = std:: make_integer_sequence>{}; if constexpr (detail::is_optional_v) { typename Attribute::value_type attr; call(first, last, context, skip, flags, success, attr); if (success) detail::assign(retval, std::move(attr)); } else if constexpr (detail::is_tuple{}) { call_impl( first, last, context, skip, flags, success, retval, indices); if (!success) detail::assign(retval, Attribute()); } else if constexpr ( detail::is_struct_compatible_v || detail::is_constructible_from_tuple_v) { result_t temp_retval{}; call_impl( first, last, context, skip, flags, success, temp_retval, indices); if (success && detail::gen_attrs(flags)) { if constexpr (detail::is_struct_compatible_v< Attribute, result_t>) { detail::assign(retval, temp_retval); } else { detail::assign( retval, detail::make_from_tuple( std::move(temp_retval))); } } } else { #if 0 // TODO Seems incompatible with this parser. // call_impl requires a tuple, so we must wrap this scalar. tuple temp_retval{}; call_impl( first, last, context, skip, flags, success, temp_retval, indices); if (success && detail::gen_attrs(flags)) { detail::assign( retval, std::move(detail::hl::front(temp_retval))); } #else static_assert( std::is_same_v && false, "It looks like you passed an attribute to this permutation " "parser that is not capable of taking the number, or the " "types of values compatible with the ones it produces."); #endif } if (success) first_ = first; } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename... Ts, int... Is> void call_impl( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, tuple & retval, std::integer_sequence) const { std::array used_parsers = {{}}; // Use "parser" to fill in attribute "x", unless "parser" has // previously been used. auto parse_into = [&](int i, auto const & parser, auto & x) { if (used_parsers[i]) return false; detail::skip(first, last, skip, flags); parser.call(first, last, context, skip, flags, success, x); if (success) { used_parsers[i] = true; return true; } success = true; return false; }; // Use one of the previously-unused parsers to parse one // alternative. bool first_iteration = true; auto parsed_one = [&](auto) { if constexpr (!detail::is_nope_v) { if (!first_iteration) { detail::skip(first, last, skip, flags); bool local_success = true; delimiter_parser_.call( first, last, context, skip, flags, local_success); if (!local_success) return false; } first_iteration = false; } return ( parse_into( Is, parser::get(parsers_, llong{}), parser::get(retval, llong{})) || ...); }; success = (parsed_one(Is) && ...); if (!success) retval = tuple{}; } #ifndef BOOST_PARSER_DOXYGEN template constexpr auto prepend(parser_interface parser) const noexcept; template constexpr auto append(parser_interface parser) const noexcept; #endif ParserTuple parsers_; DelimiterParser delimiter_parser_; }; namespace detail { template constexpr auto make_default_combining_impl(std::integer_sequence) { return hl::make_tuple(((void)I, llong{})...); } template class Tuple, typename... Args> constexpr auto make_default_combining(Tuple) { return detail::make_default_combining_impl<0>( std::make_integer_sequence()); } template using default_combining_t = decltype(detail::make_default_combining( std::declval())); struct merge_t {}; struct separate_t {}; template< typename Iter, typename Sentinel, typename Context, typename SkipParser> struct dummy_use_parser_t { dummy_use_parser_t( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) : first_(first), last_(last), context_(context), skip_(skip), flags_(flags), success_(success) {} template auto operator()(Parser const & parser) const { return parser.call( first_, last_, context_, skip_, flags_, success_); } Iter & first_; Sentinel last_; Context const & context_; SkipParser const & skip_; detail::flags flags_; bool & success_; }; template constexpr void static_assert_merge_attributes(tuple parsers); // Combining groups are: 0, which is default merge behavior, as in // seq_parser::combine; -1, which is don't merge with anything, ever; // and N>0, which is merge with other members of group N. template constexpr auto make_combining(tuple parsers) { if constexpr (std::is_same_v) { detail::static_assert_merge_attributes(parsers); return detail::make_default_combining_impl<1>( std::make_integer_sequence()); } else if constexpr (std::is_same_v) { return detail::make_default_combining_impl<-1>( std::make_integer_sequence()); } else { return CombiningGroups{}; } } template using combining_t = decltype(detail::make_combining( std::declval())); struct max_ { template constexpr auto operator()(T x, U y) const { if constexpr (T::value < U::value) return y; else return x; } }; template struct adjust_combining_groups { template constexpr auto operator()(T result, U x) const { if constexpr (U::value <= 0) return hl::append(result, x); else return hl::append(result, llong{}); } }; template constexpr auto make_combined_combining(Tuple1 lhs, Tuple2 rhs) { auto max_group_idx = detail::hl::fold_left(lhs, llong<0>{}, max_{}); auto rhs_adjusted = detail::hl::fold_left( rhs, tuple<>{}, adjust_combining_groups{}); return hl::concat(lhs, rhs_adjusted); } template using combined_combining_t = decltype(detail::make_combined_combining( std::declval(), std::declval())); enum class merge_kind { second_pass_detect, singleton, merged, group }; template struct merge_kind_t { static constexpr merge_kind kind = Kind; }; template static constexpr auto merge_wrap = merge_kind_t{}; } #ifndef BOOST_PARSER_DOXYGEN template< typename ParserTuple, typename BacktrackingTuple, typename CombiningGroups> struct seq_parser { using backtracking = BacktrackingTuple; using combining_groups = CombiningGroups; constexpr seq_parser(ParserTuple parsers) : parsers_(parsers) {} static constexpr auto true_ = std::true_type{}; static constexpr auto false_ = std::false_type{}; struct combine { template auto operator()( T result_merging_indices_and_prev_group, U x_and_group) const { using namespace literals; using detail::merge_wrap; using detail::merge_kind; auto x = parser::get(x_and_group, 0_c); auto group = parser::get(x_and_group, 1_c); auto result = parser::get(result_merging_indices_and_prev_group, 0_c); using result_back_type = typename std::decay_t::type; using unwrapped_optional_result_back_type = detail::unwrapped_optional_t; auto merging = parser::get(result_merging_indices_and_prev_group, 1_c); auto indices = parser::get(result_merging_indices_and_prev_group, 2_c); auto prev_group = parser::get(result_merging_indices_and_prev_group, 3_c); using x_type = typename decltype(x)::type; using unwrapped_optional_x_type = detail::unwrapped_optional_t; if constexpr (detail::is_nope_v) { if constexpr ( !detail::is_nope_v && 0 < decltype(group)::value && decltype(group)::value != decltype(prev_group)::value) { // T >> merge[nope >> ...] -> nope // This is a very special case. If we see a nope at // the begining of a group, and there's a non-nope // before it, we put the nope in place in the result // tuple temporarily, knowing that a non-nope will // come along later in the group to replace it. return detail::hl::make_tuple( detail::hl::append(result, x), detail::hl::append( merging, merge_wrap), detail::hl::append( indices, detail::hl::size(result)), group); } else { // T >> nope -> T return detail::hl::make_tuple( result, detail::hl::append( merging, merge_wrap), detail::hl::append( indices, detail::hl::size_minus_one(result)), prev_group); } } else if constexpr (detail::is_nope_v) { // tuple >> T -> tuple constexpr auto merge = 0 < decltype(group)::value ? merge_kind::group : (decltype(group)::value == -1 ? merge_kind::singleton : merge_kind::second_pass_detect); return detail::hl::make_tuple( detail::hl::append(detail::hl::drop_back(result), x), detail::hl::append(merging, merge_wrap), detail::hl::append( indices, detail::hl::size_minus_one(result)), group); } else if constexpr (0 < decltype(group)::value) { if constexpr ( decltype(prev_group)::value == decltype(group)::value) { return detail::hl::make_tuple( result, detail::hl::append( merging, merge_wrap), detail::hl::append( indices, detail::hl::size_minus_one(result)), group); } else { return detail::hl::make_tuple( detail::hl::append(result, x), detail::hl::append( merging, merge_wrap), detail::hl::append( indices, detail::hl::size(result)), group); } } else if constexpr ( decltype(group)::value == -1 || decltype(group)::value != decltype(prev_group)::value) { constexpr auto merge = decltype(group)::value == -1 ? merge_kind::singleton : merge_kind::second_pass_detect; return detail::hl::make_tuple( detail::hl::append(result, x), detail::hl::append(merging, merge_wrap), detail::hl::append(indices, detail::hl::size(result)), group); } else if constexpr ( detail::is_char_type_v && (detail::is_char_type_v || detail::is_char_type_v)) { // CHAR >> CHAR -> string return detail::hl::make_tuple( detail::hl::append( detail::hl::drop_back(result), detail::wrapper{}), detail::hl::append( detail::hl::append( detail::hl::drop_front(merging), merge_wrap), merge_wrap), detail::hl::append( indices, detail::hl::size_minus_one(result)), group); } else if constexpr ( detail:: container_and_value_type || detail::container_and_value_type< result_back_type, unwrapped_optional_x_type>) { // C >> T -> C // C >> optional -> C return detail::hl::make_tuple( result, detail::hl::append( merging, merge_wrap), detail::hl::append( indices, detail::hl::size_minus_one(result)), group); } else if constexpr ( detail:: container_and_value_type || detail::container_and_value_type< x_type, unwrapped_optional_result_back_type>) { // T >> C -> C // optional >> C -> C return detail::hl::make_tuple( detail::hl::append(detail::hl::drop_back(result), x), detail::hl::append( detail::hl::append( detail::hl::drop_front(merging), merge_wrap), merge_wrap), detail::hl::append( indices, detail::hl::size_minus_one(result)), group); } else { // tuple >> T -> tuple return detail::hl::make_tuple( detail::hl::append(result, x), detail::hl::append( merging, merge_wrap), detail::hl::append(indices, detail::hl::size(result)), group); } } }; struct find_merged { template auto operator()( T final_types_and_result, U x_index_and_prev_merged) const { using namespace literals; using detail::merge_wrap; using detail::merge_kind; auto final_types = parser::get(final_types_and_result, 0_c); auto result = parser::get(final_types_and_result, 1_c); auto x_type_wrapper = parser::get(x_index_and_prev_merged, 0_c); auto index = parser::get(x_index_and_prev_merged, 1_c); auto prev_merged = parser::get(x_index_and_prev_merged, 2_c); auto type_at_index_wrapper = parser::get(final_types, index); using x_type = typename decltype(x_type_wrapper)::type; using type_at_index = typename decltype(type_at_index_wrapper)::type; if constexpr ( decltype(prev_merged)::kind == merge_kind::second_pass_detect) { if constexpr ( !std::is_same_v && container) { return detail::hl::make_tuple( final_types, detail::hl::append( result, merge_wrap)); } else { return detail::hl::make_tuple( final_types, detail::hl::append( result, merge_wrap)); } } else { return detail::hl::make_tuple( final_types, detail::hl::append(result, prev_merged)); } } }; template static constexpr auto merging_from_group(integral_constant) { using detail::merge_wrap; using detail::merge_kind; if constexpr (0 < I) return merge_wrap; else if constexpr (I == -1) return merge_wrap; else return merge_wrap; } // Returns the tuple of values produced by this parser, and the // indices into that tuple that each parser should use in turn. The // case where the tuple only has one element is handled elsewhere. template< typename Iter, typename Sentinel, typename Context, typename SkipParser> auto make_temp_result( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) const { using namespace literals; detail:: dummy_use_parser_t const dummy_use_parser( first, last, context, skip, flags, success); // A result type for each of the parsers in parsers_. using all_types = decltype(detail::hl::transform(parsers_, dummy_use_parser)); // Same as above, wrapped in detail::wrapper. using all_types_wrapped = decltype(detail::hl::transform(all_types{}, detail::wrap{})); using combining_groups = detail::combining_t; constexpr auto first_group = detail::hl::front(combining_groups{}); // Generate a tuple of outputs; the index that each parser should // use to write into its output; and whether the attribute for // each parser was merged into an adjacent container-attribute. constexpr auto combine_start = detail::hl::make_tuple( detail::hl::make_tuple(detail::hl::front(all_types_wrapped{})), detail::hl::make_tuple(merging_from_group(first_group)), tuple>{}, first_group); using combined_types = decltype(detail::hl::fold_left( detail::hl::zip( detail::hl::drop_front(all_types_wrapped{}), detail::hl::drop_front(combining_groups{})), combine_start, combine{})); // Unwrap the result tuple's types. constexpr auto result_type_wrapped = parser::get(combined_types{}, 0_c); using result_type = decltype(detail::hl::transform( result_type_wrapped, detail::unwrap{})); using indices = decltype(parser::get(combined_types{}, 2_c)); using first_pass_merged = decltype(parser::get(combined_types{}, 1_c)); constexpr auto find_merged_start = detail::hl::make_tuple(result_type_wrapped, tuple<>{}); using merged = decltype(detail::hl::fold_left( detail::hl::zip( all_types_wrapped{}, indices{}, first_pass_merged{}), find_merged_start, find_merged{})); return detail::hl::make_tuple( result_type{}, indices{}, parser::get(merged{}, 1_c)); } template< typename Iter, typename Sentinel, typename Context, typename SkipParser> auto call( Iter & first_, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) const { Iter first = first_; auto temp_result = make_temp_result(first, last, context, skip, flags, success); std::decay_t{}))> retval{}; [[maybe_unused]] auto _ = detail::scoped_trace( *this, first_, last, context, detail::in_apply_parser(flags) ? detail::disable_trace(flags) : flags, retval); std::decay_t{}))> indices; std::decay_t{}))> merged; call_impl( first, last, context, skip, flags, success, retval, indices, merged); if (success) first_ = first; // A 1-tuple is converted to a scalar. if constexpr (detail::hl::size(retval) == llong<1>{}) { using namespace literals; return parser::get(retval, 0_c); } else { return retval; } } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void call( Iter & first_, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute & retval) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first_, last, context, detail::in_apply_parser(flags) ? detail::disable_trace(flags) : flags, retval); Iter first = first_; auto temp_result = make_temp_result(first, last, context, skip, flags, success); using temp_result_attr_t = std::decay_t{}))>; std::decay_t{}))> indices; std::decay_t{}))> merged; auto max_ = [](auto result, auto x) { if constexpr (decltype(result)::value < decltype(x)::value) { return x; } else { return result; } }; using max_index_t = decltype(detail::hl::fold_left(indices, llong<0>{}, max_)); if constexpr (detail::is_optional_v) { typename Attribute::value_type attr; call(first, last, context, skip, flags, success, attr); if (success) detail::assign(retval, std::move(attr)); } else if constexpr ( detail::is_tuple{} || detail::is_struct_compatible_v) { call_impl( first, last, context, skip, flags, success, retval, indices, merged); if (!success) detail::assign(retval, Attribute()); } else if constexpr ( 0 < max_index_t::value && detail::is_constructible_from_tuple_v< Attribute, temp_result_attr_t>) { temp_result_attr_t temp_retval{}; call_impl( first, last, context, skip, flags, success, temp_retval, indices, merged); if (success && detail::gen_attrs(flags)) { detail::assign( retval, detail::make_from_tuple( std::move(temp_retval))); } } else { // call_impl requires a tuple, so we must wrap this scalar. tuple temp_retval{}; call_impl( first, last, context, skip, flags, success, temp_retval, indices, merged); if (success && detail::gen_attrs(flags)) { detail::assign( retval, std::move(detail::hl::front(temp_retval))); } } if (success) first_ = first; } // Invokes each parser, placing the resulting values (if any) into // retval, using the index mapping in indices. The case of a tuple // containing only a single value is handled elsewhere. template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute, typename Indices, typename Merged> void call_impl( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute & retval, Indices const & indices, Merged const & merged) const { using detail::merge_wrap; using detail::merge_kind; static_assert( detail::is_tuple{} || std::is_aggregate_v, ""); auto use_parser = [&first, last, &context, &skip, flags_ = flags, &success, &retval](auto const & parser_index_merged_and_backtrack) { if (!success) // Someone earlier already failed... return; auto flags = flags_; using namespace literals; detail::skip(first, last, skip, flags); auto const & parser = parser::get(parser_index_merged_and_backtrack, 0_c); auto merge_kind_t_ = parser::get(parser_index_merged_and_backtrack, 2_c); constexpr bool was_merged_into_adjacent_container = decltype(merge_kind_t_)::kind == merge_kind::merged; constexpr bool is_in_a_group = decltype(merge_kind_t_)::kind == merge_kind::group; bool const can_backtrack = parser::get(parser_index_merged_and_backtrack, 3_c); if (!detail::gen_attrs(flags)) { parser.call(first, last, context, skip, flags, success); if (!success && !can_backtrack) { std::stringstream oss; detail::print_parser(context, parser, oss); throw parse_error(first, oss.str()); } return; } auto const tuple_idx = parser::get(parser_index_merged_and_backtrack, 1_c); auto const tuple_size = detail::tuple_or_struct_size(retval); static_assert( decltype(tuple_idx)::value < decltype(tuple_size)::value, "Looks like you're trying to write some attribute into an " "out-of-bounds position in a tuple/struct. In other " "words, the attribute you're parsing into does not match " "the default attribute used by this parser. This may be " "because you passed an out-param to parse() at the top " "level that is not compatible with the attribute type " "generated by the parser you passed to parse()."); if constexpr (!(decltype(tuple_idx)::value < decltype(tuple_size)::value)) { [[maybe_unused]] detail::print_type _; } auto & out = parser::get(retval, tuple_idx); using attr_t = decltype(parser.call( first, last, context, skip, flags, success)); constexpr bool out_container = container>; constexpr bool attr_container = container; if constexpr ( (out_container == attr_container && !was_merged_into_adjacent_container && !detail::is_nope_v) || is_in_a_group) { parser.call( first, last, context, skip, flags, success, out); if (!success) { if (!can_backtrack) { std::stringstream oss; detail::print_parser(context, parser, oss); throw parse_error(first, oss.str()); } out = std::decay_t(); return; } } else { attr_t x = parser.call(first, last, context, skip, flags, success); if (!success) { if (!can_backtrack) { std::stringstream oss; detail::print_parser(context, parser, oss); throw parse_error(first, oss.str()); } return; } using just_x = attr_t; using just_out = detail::remove_cv_ref_t; if constexpr (detail::is_nope_v) { // nothing to do } if constexpr ( (!out_container || !std::is_same_v) && std::is_assignable_v && (!std::is_same_v || !std::is_integral_v)) { detail::assign(out, std::move(x)); } else { detail::move_back( out, std::move(x), detail::gen_attrs(flags)); } } }; auto const parsers_and_indices = detail::hl::zip(parsers_, indices, merged, backtracking{}); detail::hl::for_each(parsers_and_indices, use_parser); } template constexpr auto prepend(parser_interface parser) const noexcept; template constexpr auto append(parser_interface parser) const noexcept; ParserTuple parsers_; }; #endif namespace detail { template using action_direct_call_expr = decltype(std::declval()(std::declval())); template using action_apply_call_expr = decltype(hl::apply( std::declval(), std::declval())); template using action_assignable_to_val_direct_expr = decltype(_val(std::declval()) = std::declval()(std::declval())); template using action_assignable_to_val_apply_expr = decltype(_val(std::declval()) = hl::apply(std::declval(), std::declval())); template constexpr auto action_assignable_to_val_direct() { if constexpr (is_nope_v().val_)>) { return false; } else if constexpr (!is_detected_v< action_direct_call_expr, Action, Attribute>) { return false; } else if constexpr (std::is_same_v< action_direct_call_expr, void>) { return false; } else { return is_detected_v< action_assignable_to_val_direct_expr, Action, Attribute, Context>; } } template constexpr auto action_assignable_to_val_apply() { if constexpr (is_nope_v().val_)>) { return false; } else if constexpr (!is_tuple>{}) { return false; } else if constexpr (tuple_size_> < 2) { return false; } else if constexpr (!is_detected_v< action_apply_call_expr, Action, Attribute>) { return false; } else if constexpr (std::is_same_v< action_apply_call_expr, void>) { return false; } else { return is_detected_v< action_assignable_to_val_apply_expr, Action, Attribute, Context>; } } template constexpr bool in_recursion = std::is_same_v && !std::is_same_v; } #ifndef BOOST_PARSER_DOXYGEN template struct action_parser { template< typename Iter, typename Sentinel, typename Context, typename SkipParser> detail::nope call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) const { detail::nope retval; call(first, last, context, skip, flags, success, retval); return retval; } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute & retval) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, retval); auto const initial_first = first; auto attr = parser_.call( first, last, context, skip, detail::enable_attrs(flags), success); if (!success) return; if constexpr (detail::action_assignable_to_val_apply< decltype(action_) &, decltype(attr), decltype(context)>()) { _val(context) = detail::hl::apply(action_, std::move(attr)); } else { BOOST_PARSER_SUBRANGE const where(initial_first, first); auto const action_context = detail::make_action_context(context, attr, where); if constexpr (detail::action_assignable_to_val_direct< decltype(action_) &, decltype(action_context) &, decltype(action_context) &>()) { _val(action_context) = action_(action_context); } else if constexpr (std::is_same_v< decltype(action_(action_context)), void>) { action_(action_context); } else { // If you see an error here, it's because you are using an // invocable for a semantic action that returns a non-void // type Ret, but values of type Ret is not assignable to // _val(ctx). To fix this, only use this invocable within // a rule whose attribute type is assignable from Ret, or // remove the non-void return statement(s) from your // invocable. [[maybe_unused]] none n = action_(action_context); } } } Parser parser_; Action action_; }; template struct transform_parser { template< typename Iter, typename Sentinel, typename Context, typename SkipParser> auto call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, detail::global_nope); auto attr = parser_.call(first, last, context, skip, flags, success); if (success && detail::gen_attrs(flags)) return f_(std::move(attr)); else return decltype(f_(std::move(attr))){}; } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute & retval) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, retval); auto attr = parser_.call(first, last, context, skip, flags, success); if (success && detail::gen_attrs(flags)) detail::assign(retval, f_(std::move(attr))); } Parser parser_; F f_; }; template struct omit_parser { template< typename Iter, typename Sentinel, typename Context, typename SkipParser> detail::nope call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, detail::global_nope); parser_.call( first, last, context, skip, detail::disable_attrs(flags), success); return {}; } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute & retval) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, retval); parser_.call( first, last, context, skip, detail::disable_attrs(flags), success); } Parser parser_; }; template struct raw_parser { template< typename Iter, typename Sentinel, typename Context, typename SkipParser> BOOST_PARSER_SUBRANGE call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) const { BOOST_PARSER_SUBRANGE retval; call(first, last, context, skip, flags, success, retval); return retval; } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute & retval) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, retval); auto const initial_first = first; parser_.call( first, last, context, skip, detail::disable_attrs(flags), success); if (success && detail::gen_attrs(flags)) detail::assign( retval, BOOST_PARSER_SUBRANGE(initial_first, first)); } Parser parser_; }; #if BOOST_PARSER_USE_CONCEPTS template struct string_view_parser { template< typename Iter, typename Sentinel, typename Context, typename SkipParser> auto call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) const { auto r = parser::detail::text::unpack_iterator_and_sentinel(first, last); static_assert( std::contiguous_iterator, "string_view_parser and the string_view[] directive that uses " "it requires that the underlying char sequence being parsed be " "a contiguous range. If you're seeing this static_assert, you " "have not met this contract."); using char_type = detail::remove_cv_ref_t; std::basic_string_view retval; call(first, last, context, skip, flags, success, retval); return retval; } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute & retval) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, retval); auto const initial_first = first; parser_.call( first, last, context, skip, detail::disable_attrs(flags), success); if (!success || !detail::gen_attrs(flags)) return; auto r = parser::detail::text::unpack_iterator_and_sentinel( initial_first, first); static_assert( std::contiguous_iterator, "string_view_parser and the string_view[] directive that uses " "it requires that the underlying char sequence being parsed be " "a contiguous range. If you're seeing this static_assert, you " "have not met this contract."); using char_type = detail::remove_cv_ref_t; if (initial_first == first) { detail::assign(retval, std::basic_string_view{}); } else { detail::assign( retval, std::basic_string_view{ &*r.first, std::size_t(r.last - r.first)}); } } Parser parser_; }; #endif template struct lexeme_parser { template< typename Iter, typename Sentinel, typename Context, typename SkipParser> auto call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) const { using attr_t = decltype(parser_.call( first, last, context, skip, flags, success)); attr_t retval{}; call(first, last, context, skip, flags, success, retval); return retval; } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute & retval) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, retval); parser_.call( first, last, context, skip, detail::disable_skip(flags), success, retval); } Parser parser_; }; template struct no_case_parser { template< typename Iter, typename Sentinel, typename Context, typename SkipParser> auto call( Iter & first, Sentinel last, Context const & context_, SkipParser const & skip, detail::flags flags, bool & success) const { auto context = context_; ++context.no_case_depth_; using attr_t = decltype(parser_.call( first, last, context, skip, flags, success)); attr_t retval{}; call(first, last, context, skip, flags, success, retval); return retval; } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void call( Iter & first, Sentinel last, Context const & context_, SkipParser const & skip, detail::flags flags, bool & success, Attribute & retval) const { auto context = context_; ++context.no_case_depth_; [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, retval); parser_.call(first, last, context, skip, flags, success, retval); } Parser parser_; }; template struct skip_parser { template< typename Iter, typename Sentinel, typename Context, typename SkipParser_> auto call( Iter & first, Sentinel last, Context const & context, SkipParser_ const & skip, detail::flags flags, bool & success) const { using attr_t = decltype(parser_.call( first, last, context, skip, flags, success)); attr_t retval{}; call(first, last, context, skip, flags, success, retval); return retval; } template< typename Iter, typename Sentinel, typename Context, typename SkipParser_, typename Attribute> void call( Iter & first, Sentinel last, Context const & context, SkipParser_ const & skip, detail::flags flags, bool & success, Attribute & retval) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, retval); if constexpr (detail::is_nope_v) { parser_.call( first, last, context, skip, detail::enable_skip(flags), success, retval); } else { parser_.call( first, last, context, skip_parser_, detail::enable_skip(flags), success, retval); } } Parser parser_; SkipParser skip_parser_; }; template struct expect_parser { template< typename Iter, typename Sentinel, typename Context, typename SkipParser> detail::nope call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) const { detail::nope retval; call(first, last, context, skip, flags, success, retval); return retval; } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute & retval) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, retval); auto first_copy = first; parser_.call( first_copy, last, context, skip, detail::disable_attrs(flags), success); if (FailOnMatch) success = !success; } Parser parser_; }; template struct symbol_parser { symbol_parser() : copied_from_(nullptr) {} explicit symbol_parser(std::string_view diagnostic_text) : copied_from_(nullptr), diagnostic_text_(diagnostic_text) {} symbol_parser(symbol_parser const & other) : initial_elements_(other.initial_elements_), copied_from_(other.copied_from_ ? other.copied_from_ : &other), diagnostic_text_(other.diagnostic_text_) {} symbol_parser(symbol_parser && other) : initial_elements_(std::move(other.initial_elements_)), copied_from_(other.copied_from_), diagnostic_text_(other.diagnostic_text_) {} /** Inserts an entry consisting of a UTF-8 string `str` to match, and an associated attribute `x`, to `*this`. The entry is added for use in all subsequent top-level parses. Subsequent lookups during the current top-level parse will not necessarily match `str`. */ template void insert_for_next_parse( Context const & context, std::string_view str, T x) { auto & pending_ops = detail::get_pending_symtab_ops(context, ref()); pending_ops.push_back(detail::symbol_table_operation{ std::string(str), std::move(x), detail::symbol_table_op::insert}); } /** Erases the entry whose UTF-8 match string is `str`, from `*this`. The entry will no longer be available for use in all subsequent top-level parses. `str` will not be removed from the symbols matched in the current top-level parse. */ template void erase_for_next_parse(Context const & context, std::string_view str) { auto & pending_ops = detail::get_pending_symtab_ops(context, ref()); pending_ops.push_back(detail::symbol_table_operation{ std::string(str), std::nullopt, detail::symbol_table_op::erase}); } /** Erases all the entries from the copy of the symbol table inside the parse context `context`. */ template void clear_for_next_parse(Context const & context) { auto & pending_ops = detail::get_pending_symtab_ops(context, ref()); pending_ops.push_back(detail::symbol_table_operation{ {}, std::nullopt, detail::symbol_table_op::clear}); } /** Uses UTF-8 string `str` to look up an attribute in the table during parsing, returning it as an optional reference. The lookup is done on the copy of the symbol table inside the parse context `context`. */ template parser::detail::text::optional_ref find(Context const & context, std::string_view str) const { auto [trie, has_case_folded] = detail::get_trie(context, ref()); if (context.no_case_depth_) { return trie[detail::case_fold_view( str | detail::text::as_utf32)]; } else { return trie[str | detail::text::as_utf32]; } } /** Inserts an entry consisting of a UTF-8 string `str` to match, and an associtated attribute `x`, to the copy of the symbol table inside the parse context `context`. */ template void insert(Context const & context, std::string_view str, T && x) const { auto [trie, has_case_folded] = detail::get_trie(context, ref()); if (context.no_case_depth_) { trie.insert( detail::case_fold_view(str | detail::text::as_utf32), std::move(x)); } else { trie.insert(str | detail::text::as_utf32, std::move(x)); } } /** Erases the entry whose UTF-8 match string is `str` from the copy of the symbol table inside the parse context `context`. */ template void erase(Context const & context, std::string_view str) const { auto [trie, has_case_folded] = detail::get_trie(context, ref()); if (context.no_case_depth_) { trie.erase( detail::case_fold_view(str | detail::text::as_utf32)); } else { trie.erase(str | detail::text::as_utf32); } } /** Erases the entry whose UTF-8 match string is `str` from the copy of the symbol table inside the parse context `context`. */ template void clear(Context const & context) const { auto [trie, _] = detail::get_trie(context, ref()); trie.clear(); } template< typename Iter, typename Sentinel, typename Context, typename SkipParser> T call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) const { T retval{}; call(first, last, context, skip, flags, success, retval); return retval; } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute & retval) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, retval); auto [trie, _0] = detail::get_trie(context, ref()); auto const lookup = context.no_case_depth_ ? trie.longest_match(detail::case_fold_view( BOOST_PARSER_SUBRANGE(first, last))) : trie.longest_match(first, last); if (lookup.match) { std::advance(first, lookup.size); detail::assign(retval, T{*trie[lookup]}); } else { success = false; } } mutable std::vector> initial_elements_; symbol_parser const * copied_from_; symbol_parser const & ref() const noexcept { if (copied_from_) return *copied_from_; return *this; } std::vector> & initial_elements() const noexcept { return ref().initial_elements_; } std::string_view diagnostic_text_; }; template< bool CanUseCallbacks, typename TagType, typename Attribute, typename LocalState, typename ParamsTuple> struct rule_parser { using tag_type = TagType; using attr_type = Attribute; using locals_type = LocalState; template< typename Iter, typename Sentinel, typename Context, typename SkipParser> std::conditional_t< detail::in_recursion, detail::nope, attr_type> call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) const { constexpr bool in_recursion = detail::in_recursion; if constexpr (in_recursion) flags = detail::disable_attrs(flags); attr_type retval{}; locals_type locals = detail::make_locals(context); auto params = detail::resolve_rule_params(context, params_); tag_type * const tag_ptr = nullptr; auto const rule_context = detail::make_rule_context( context, tag_ptr, retval, locals, params); [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, rule_context, flags, retval); bool dont_assign = false; if constexpr (in_recursion) { // We have to use this out-arg overload for iterations >= 1 in // recursive rules, since every iteration past the first is // defined to return nope. parse_rule( tag_ptr, first, last, rule_context, skip, flags, success, dont_assign, retval); } else { auto attr = parse_rule( tag_ptr, first, last, rule_context, skip, flags, success, dont_assign); if (success && !dont_assign) { if constexpr (!detail::is_nope_v) detail::assign(retval, attr); } } if constexpr ( CanUseCallbacks && Context::use_callbacks && !in_recursion) { if (!success) return attr_type{}; auto const & callbacks = _callbacks(context); if constexpr (detail::is_nope_v) { static_assert( detail::is_invocable_v, "For rules without attributes, Callbacks must be a " "struct with overloads of the form void(tag_type). If " "you're seeing an error here, you probably have not " "met this contract."); callbacks(tag_type{}); } else { static_assert( detail::is_invocable_v< decltype(callbacks), tag_type, decltype(std::move(retval))>, "For rules with attributes, Callbacks must be a struct " "with overloads of the form void(tag_type, attr_type). " "If you're seeing an error here, you probably have not " "met this contract."); callbacks(tag_type{}, std::move(retval)); } return attr_type{}; } else { if (!success && !in_recursion) detail::assign(retval, attr_type()); if constexpr (in_recursion) return detail::nope{}; else return retval; } } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute_> void call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute_ & retval) const { if constexpr (CanUseCallbacks && Context::use_callbacks) { call(first, last, context, skip, flags, success); } else { locals_type locals = detail::make_locals(context); auto params = detail::resolve_rule_params(context, params_); tag_type * const tag_ptr = nullptr; auto const rule_context = detail::make_rule_context( context, tag_ptr, retval, locals, params); [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, rule_context, flags, retval); bool dont_assign = false; parse_rule( tag_ptr, first, last, rule_context, skip, flags, success, dont_assign, retval); if (!success || dont_assign) retval = Attribute_(); } } std::string_view diagnostic_text_; ParamsTuple params_; }; #endif namespace detail { template using base_member_function_template_expr = decltype(std::declval().template base<2>()); template constexpr bool has_base_member_function_template_v = is_detected_v; template using has_digits1_member_function_template_expr = decltype(std::declval().template digits<1>()); template constexpr bool has_digits1_member_function_template_v = is_detected_v; template using has_digits2_member_function_template_expr = decltype(std::declval().template digits<1, 2>()); template constexpr bool has_digits2_member_function_template_v = is_detected_v; } // Parser interface. template struct parser_interface { using parser_type = Parser; using global_state_type = GlobalState; using error_handler_type = ErrorHandler; constexpr parser_interface() : parser_() {} constexpr parser_interface(parser_type p) : parser_(std::move(p)) {} constexpr parser_interface( parser_type p, global_state_type gs, error_handler_type eh) : parser_(p), globals_(gs), error_handler_(eh) {} /** Returns a `parser_interface` containing a parser equivalent to an `expect_parser` containing `parser_`, with `FailOnMatch == true`. */ constexpr auto operator!() const noexcept { return parser::parser_interface{ expect_parser{parser_}}; } /** Returns a `parser_interface` containing a parser equivalent to an `expect_parser` containing `parser_`, with `FailOnMatch == false`. */ constexpr auto operator&() const noexcept { return parser::parser_interface{ expect_parser{parser_}}; } /** Returns a `parser_interface` containing a parser equivalent to a `zero_plus_parser` containing `parser_`. */ constexpr auto operator*() const noexcept { if constexpr (detail::is_zero_plus_p{}) { return *this; } else if constexpr (detail::is_one_plus_p{}) { using inner_parser = decltype(parser_type::parser_); return parser::parser_interface{ zero_plus_parser(parser_.parser_)}; } else { return parser::parser_interface{ zero_plus_parser(parser_)}; } } /** Returns a `parser_interface` containing a parser equivalent to a `one_plus_parser` containing `parser_`. */ constexpr auto operator+() const noexcept { if constexpr (detail::is_zero_plus_p{}) { using inner_parser = decltype(parser_type::parser_); return parser::parser_interface{ zero_plus_parser(parser_.parser_)}; } else if constexpr (detail::is_one_plus_p{}) { return *this; } else { return parser::parser_interface{ one_plus_parser(parser_)}; } } /** Returns a `parser_interface` containing a parser equivalent to a `opt_parser` containing `parser_`. */ constexpr auto operator-() const noexcept { return parser::parser_interface{opt_parser{parser_}}; } /** Returns a `parser_interface` containing a parser equivalent to a `seq_parser` containing `parser_` followed by `rhs.parser_`. */ template constexpr auto operator>>(parser_interface rhs) const noexcept { if constexpr (detail::is_seq_p{}) { return parser_.template append(rhs); } else if constexpr (detail::is_seq_p{}) { return rhs.parser_.template prepend(*this); } else { using parser_t = seq_parser< tuple, tuple, tuple, llong<0>>>; return parser::parser_interface{parser_t{ tuple{parser_, rhs.parser_}}}; } } /** Returns a `parser_interface` containing a parser equivalent to a `seq_parser` containing `parser_` followed by `lit(rhs)`. */ constexpr auto operator>>(char rhs) const noexcept; /** Returns a `parser_interface` containing a parser equivalent to a `seq_parser` containing `parser_` followed by `lit(rhs)`. */ constexpr auto operator>>(char32_t rhs) const noexcept; /** Returns a `parser_interface` containing a parser equivalent to a `seq_parser` containing `parser_` followed by `lit(rhs)`. */ #if BOOST_PARSER_USE_CONCEPTS template #else template< typename R, typename Enable = std::enable_if_t>> #endif constexpr auto operator>>(R && r) const noexcept; /** Returns a `parser_interface` containing a parser equivalent to a `seq_parser` containing `parser_` followed by `rhs.parser_`. No back-tracking is allowed after `parser_` succeeds; if `rhs.parser_` fails after `parser_` succeeds, the top-level parse fails. */ template constexpr auto operator>(parser_interface rhs) const noexcept { if constexpr (detail::is_seq_p{}) { return parser_.template append(rhs); } else if constexpr (detail::is_seq_p{}) { return rhs.parser_.template prepend(*this); } else { using parser_t = seq_parser< tuple, tuple, tuple, llong<0>>>; return parser::parser_interface{parser_t{ tuple{parser_, rhs.parser_}}}; } } /** Returns a `parser_interface` containing a parser equivalent to a `seq_parser` containing `parser_` followed by `lit(rhs)`. No back-tracking is allowed after `parser_` succeeds; if `lit(rhs)` fails after `parser_` succeeds, the top-level parse fails. */ constexpr auto operator>(char rhs) const noexcept; /** Returns a `parser_interface` containing a parser equivalent to a `seq_parser` containing `parser_` followed by `lit(rhs)`. No back-tracking is allowed after `parser_` succeeds; if `lit(rhs)` fails after `parser_` succeeds, the top-level parse fails. */ constexpr auto operator>(char32_t rhs) const noexcept; /** Returns a `parser_interface` containing a parser equivalent to a `seq_parser` containing `parser_` followed by `lit(rhs)`. No back-tracking is allowed after `parser_` succeeds; if `lit(rhs)` fails after `parser_` succeeds, the top-level parse fails. */ #if BOOST_PARSER_USE_CONCEPTS template #else template< typename R, typename Enable = std::enable_if_t>> #endif constexpr auto operator>(R && r) const noexcept; /** Returns a `parser_interface` containing a parser equivalent to an `or_parser` containing `parser_` followed by `rhs.parser_`. */ template constexpr auto operator|(parser_interface rhs) const noexcept { if constexpr (detail::is_or_p{}) { return parser_.append(rhs); } else if constexpr (detail::is_or_p{}) { return rhs.parser_.prepend(*this); } else { // If you're seeing this as a compile- or run-time failure, // you've tried to put an eps parser at the beginning of an // alternative-parser, such as "eps | int_". This is not what // you meant. Since eps always matches any input, "eps | // int_" is just an awkward spelling for "eps". To fix this // this, put the eps as the last alternative, so the other // alternatives get a chance. Possibly, you may have meant to // add a condition to the eps, like "eps(condition) | int_", // which also is meaningful, and so is allowed. BOOST_PARSER_ASSERT( !detail::is_unconditional_eps{}); return parser::parser_interface{ or_parser>{ tuple{parser_, rhs.parser_}}}; } } /** Returns a `parser_interface` containing a parser equivalent to a `perm_parser` containing `parser_` followed by `rhs.parser_`. It is an error to use `eps` (conditional or not) with this operator. */ template constexpr auto operator||(parser_interface rhs) const noexcept { // If you're seeing this as a compile- or run-time failure, you've // tried to put an eps parser in a permutation-parser, such as // "eps || int_". BOOST_PARSER_ASSERT(!detail::is_eps_p{}); BOOST_PARSER_ASSERT(!detail::is_eps_p{}); if constexpr (detail::is_perm_p{}) { return parser_.append(rhs); } else if constexpr (detail::is_perm_p{}) { return rhs.parser_.prepend(*this); } else { return parser::parser_interface{ perm_parser, detail::nope>{ tuple{parser_, rhs.parser_}}}; } } /** Returns a `parser_interface` containing a parser equivalent to an `or_parser` containing `parser_` followed by `lit(rhs)`. */ constexpr auto operator|(char rhs) const noexcept; /** Returns a `parser_interface` containing a parser equivalent to an `or_parser` containing `parser_` followed by `lit(rhs)`. */ constexpr auto operator|(char32_t rhs) const noexcept; /** Returns a `parser_interface` containing a parser equivalent to an `or_parser` containing `parser_` followed by `lit(rhs)`. */ #if BOOST_PARSER_USE_CONCEPTS template #else template< typename R, typename Enable = std::enable_if_t>> #endif constexpr auto operator|(R && r) const noexcept; /** Returns a `parser_interface` containing a parser equivalent to `!rhs >> *this`. */ template constexpr auto operator-(parser_interface rhs) const noexcept { return !rhs >> *this; } /** Returns a `parser_interface` containing a parser equivalent to `!lit(rhs) >> *this`. */ constexpr auto operator-(char rhs) const noexcept; /** Returns a `parser_interface` containing a parser equivalent to `!lit(rhs) >> *this`. */ constexpr auto operator-(char32_t rhs) const noexcept; /** Returns a `parser_interface` containing a parser equivalent to `!lit(rhs) >> *this`. */ #if BOOST_PARSER_USE_CONCEPTS template #else template< typename R, typename Enable = std::enable_if_t>> #endif constexpr auto operator-(R && r) const noexcept; /** Returns a `parser_interface` containing a parser equivalent to an `delimited_seq_parser` containing `parser_` and `rhs.parser_`. */ template constexpr auto operator%(parser_interface rhs) const noexcept { return parser::parser_interface{ delimited_seq_parser( parser_, rhs.parser_)}; } /** Returns a `parser_interface` containing a parser equivalent to an `delimited_seq_parser` containing `parser_` and `lit(rhs)`. */ constexpr auto operator%(char rhs) const noexcept; /** Returns a `parser_interface` containing a parser equivalent to an `delimited_seq_parser` containing `parser_` and `lit(rhs)`. */ constexpr auto operator%(char32_t rhs) const noexcept; /** Returns a `parser_interface` containing a parser equivalent to an `delimited_seq_parser` containing `parser_` and `lit(rhs)`. */ #if BOOST_PARSER_USE_CONCEPTS template #else template< typename R, typename Enable = std::enable_if_t>> #endif constexpr auto operator%(R && r) const noexcept; /** Returns a `parser_interface` containing a parser equivalent to an `action_parser` containing `parser_`, with semantic action `action`. */ template constexpr auto operator[](Action action) const { using action_parser_t = action_parser; return parser::parser_interface{action_parser_t{parser_, action}}; } /** Returns `parser_((Arg &&)arg, (Args &&)args...)`. This is useful for those parsers that have `operator()` overloads, e.g. `char_('x')`. By convention, parsers' `operator()`s return `parser_interface`s. This function does not participate in overload resolution unless `parser_((Arg &&)arg, (Args &&)args...)` is well-formed. */ template constexpr auto operator()(Arg && arg, Args &&... args) const noexcept -> decltype(std::declval()( (Arg &&) arg, (Args &&) args...)) { return parser_((Arg &&) arg, (Args &&) args...); } #ifndef BOOST_PARSER_DOXYGEN /** Applies `parser_`, returning the parsed attribute, if any, unless the attribute is reported via callback. */ template< typename Iter, typename Sentinel, typename Context, typename SkipParserType> auto operator()( Iter & first, Sentinel last, Context const & context, SkipParserType const & skip, detail::flags flags, bool & success) const { return parser_.call(first, last, context, skip, flags, success); } /** Applies `parser_`, assinging the parsed attribute, if any, to `attr`, unless the attribute is reported via callback. */ template< typename Iter, typename Sentinel, typename Context, typename SkipParserType, typename Attribute> void operator()( Iter & first, Sentinel last, Context const & context, SkipParserType const & skip, detail::flags flags, bool & success, Attribute & attr) const { parser_.call(first, last, context, skip, flags, success, attr); } /** Returns a new `parser_interface` constructed from `parser_.base()`. Note that this only works for integral numeric parsers like `int_` and `uint_`. */ template constexpr auto base() const noexcept { if constexpr (detail::has_base_member_function_template_v< parser_type>) { return parser::parser_interface{ parser_.template base()}; } else { static_assert( detail::has_base_member_function_template_v, "Only certain parsers have a .base<>() member function. " "This is not one of them."); } } /** Returns a new `parser_interface` constructed from `parser_.digits()`. Note that this only works for integral numeric parsers like `int_` and `uint_`. */ template constexpr auto digits() const noexcept { if constexpr (detail::has_digits1_member_function_template_v< parser_type>) { return parser::parser_interface{ parser_.template digits()}; } else { static_assert( detail::has_digits1_member_function_template_v, "Only certain parsers have a .base<>() member function. " "This is not one of them."); } } /** Returns a new `parser_interface` constructed from `parser_.digits()`. Note that this only works for integral numeric parsers like `int_` and `uint_`. */ template constexpr auto digits() const noexcept { if constexpr (detail::has_digits2_member_function_template_v< parser_type>) { return parser::parser_interface{ parser_.template digits()}; } else { static_assert( detail::has_digits2_member_function_template_v, "Only certain parsers have a .base<>() member function. " "This is not one of them."); } } parser_type parser_; global_state_type globals_; error_handler_type error_handler_; #endif using parser_interface_derivation_tag = int; }; /** Returns a `parser_interface` with the same parser and error handler, with `globals` added. The result of passing any non-top-level parser for the `parser` argument is undefined. */ template auto with_globals( parser_interface const & parser, GlobalState & globals) { return parser_interface{ parser.parser_, globals, parser.error_handler_}; } /** Returns a `parser_interface` with the same parser and globals, with `error_handler` added. The result of passing any non-top-level parser for the `parser` argument is undefined. */ template auto with_error_handler( parser_interface const & parser, ErrorHandler & error_handler) { return parser_interface{ parser.parser_, parser.globals_, error_handler}; } /** A `symbols` represents the initial state of a symbol table parser that produces attributes of type `T`. The entries in the symbol table can be changed during parsing, but those mutations do not affect the `symbols` object itself; all mutations happen to a copy of the symbol table in the parse context. For table entries that should be used during every parse, add entries via `add()` or `operator()`. For mid-parse mutations, use `insert()` and `erase()`. */ template struct symbols : parser_interface> { symbols() {} symbols(char const * diagnostic_text) : parser_interface>( symbol_parser(diagnostic_text)) {} symbols(std::initializer_list> il) { this->parser_.initial_elements_.resize(il.size()); std::copy(il.begin(), il.end(), this->parser_.initial_elements_.begin()); } symbols( char const * diagnostic_text, std::initializer_list> il) : parser_interface>( symbol_parser(diagnostic_text)) { this->parser_.initial_elements_.resize(il.size()); std::copy(il.begin(), il.end(), this->parser_.initial_elements_.begin()); } /** Inserts an entry consisting of a UTF-8 string `str` to match, and an associated attribute `x`, to `*this`. The entry is added for use in all subsequent top-level parses. Subsequent lookups during the current top-level parse will not necessarily match `str`. */ void insert_for_next_parse(std::string_view str, T x) { this->parser_.initial_elements_.push_back( std::pair(std::string(str), std::move(x))); } /** Erases the entry whose UTF-8 match string is `str`, from `*this`. The entry will no longer be available for use in all subsequent top-level parses. `str` will not be removed from the symbols matched in the current top-level parse. */ void erase_for_next_parse(std::string_view str) { auto it = std::find_if( this->parser_.initial_elements_.begin(), this->parser_.initial_elements_.end(), [str](auto const & x) { return x.first == str; }); this->parser_.initial_elements_.erase(it); } /** Erases all the entries from the copy of the symbol table inside the parse context `context`. */ void clear_for_next_parse() { this->parser_.initial_elements_.clear(); } /** Inserts an entry consisting of a UTF-8 string `str` to match, and an associated attribute `x`, to `*this`. The entry is added for use in all subsequent top-level parses. Subsequent lookups during the current top-level parse will not necessarily match `str`. */ template void insert_for_next_parse( Context const & context, std::string_view str, T x) { this->parser_.insert_for_next_parse(context, str, std::move(x)); } /** Erases the entry whose UTF-8 match string is `str`, from `*this`. The entry will no longer be available for use in all subsequent top-level parses. `str` will not be removed from the symbols matched in the current top-level parse. */ template void erase_for_next_parse(Context const & context, std::string_view str) { this->parser_.erase_for_next_parse(context, str); } /** Erases all the entries from the copy of the symbol table inside the parse context `context`. */ template void clear_for_next_parse(Context const & context) { this->parser_.clear_for_next_parse(context); } /** Uses UTF-8 string `str` to look up an attribute in the table during parsing, returning it as an optional reference. The lookup is done on the copy of the symbol table inside the parse context `context`, not `*this`. */ template parser::detail::text::optional_ref find(Context const & context, std::string_view str) const { return this->parser_.find(context, str); } /** Inserts an entry consisting of a UTF-8 string to match `str`, and an associated attribute `x`, to the copy of the symbol table inside the parse context `context`. */ template void insert(Context const & context, std::string_view str, T x) const { this->parser_.insert(context, str, std::move(x)); } /** Erases the entry whose UTF-8 match string is `str` from the copy of the symbol table inside the parse context `context`. */ template void erase(Context const & context, std::string_view str) const { this->parser_.erase(context, str); } /** Erases all the entries from the copy of the symbol table inside the parse context `context`. */ template void clear(Context const & context) const { this->parser_.clear(context); } }; #ifndef BOOST_PARSER_DOXYGEN template< typename TagType, typename Attribute, typename LocalState, typename ParamsTuple> struct rule : parser_interface< rule_parser> { static_assert( !std::is_same_v, "void is not a valid tag type for a rule."); constexpr rule(char const * diagnostic_text) { this->parser_.diagnostic_text_ = diagnostic_text; } template constexpr auto with(T && x, Ts &&... xs) const { BOOST_PARSER_ASSERT( (detail::is_nope_v && "If you're seeing this, you tried to chain calls on a rule, " "like 'rule.with(foo).with(bar)'. Quit it!'")); using params_tuple_type = decltype(detail::hl::make_tuple( static_cast(x), static_cast(xs)...)); using rule_parser_type = rule_parser< false, TagType, Attribute, LocalState, params_tuple_type>; using result_type = parser_interface; return result_type{rule_parser_type{ this->parser_.diagnostic_text_, detail::hl::make_tuple( static_cast(x), static_cast(xs)...)}}; } }; template< typename TagType, typename Attribute, typename LocalState, typename ParamsTuple> struct callback_rule : parser_interface< rule_parser> { constexpr callback_rule(char const * diagnostic_text) { this->parser_.diagnostic_text_ = diagnostic_text; } template constexpr auto with(T && x, Ts &&... xs) const { BOOST_PARSER_ASSERT( (detail::is_nope_v && "If you're seeing this, you tried to chain calls on a " "callback_rule, like 'rule.with(foo).with(bar)'. Quit it!'")); using params_tuple_type = decltype(detail::hl::make_tuple( static_cast(x), static_cast(xs)...)); using rule_parser_type = rule_parser< true, TagType, Attribute, LocalState, params_tuple_type>; using result_type = parser_interface; return result_type{rule_parser_type{ this->parser_.diagnostic_text_, detail::hl::make_tuple( static_cast(x), static_cast(xs)...)}}; } }; //[ define_rule_definition #define BOOST_PARSER_DEFINE_IMPL(_, rule_name_) \ template< \ typename Iter, \ typename Sentinel, \ typename Context, \ typename SkipParser> \ decltype(rule_name_)::parser_type::attr_type parse_rule( \ decltype(rule_name_)::parser_type::tag_type *, \ Iter & first, \ Sentinel last, \ Context const & context, \ SkipParser const & skip, \ boost::parser::detail::flags flags, \ bool & success, \ bool & dont_assign) \ { \ auto const & parser = BOOST_PARSER_PP_CAT(rule_name_, _def); \ using attr_t = \ decltype(parser(first, last, context, skip, flags, success)); \ using attr_type = decltype(rule_name_)::parser_type::attr_type; \ if constexpr (boost::parser::detail::is_nope_v) { \ dont_assign = true; \ parser(first, last, context, skip, flags, success); \ return {}; \ } else if constexpr (std::is_same_v) { \ return parser(first, last, context, skip, flags, success); \ } else if constexpr (std::is_constructible_v) { \ return attr_type( \ parser(first, last, context, skip, flags, success)); \ } else { \ attr_type attr{}; \ parser(first, last, context, skip, flags, success, attr); \ return attr; \ } \ } \ \ template< \ typename Iter, \ typename Sentinel, \ typename Context, \ typename SkipParser, \ typename Attribute> \ void parse_rule( \ decltype(rule_name_)::parser_type::tag_type *, \ Iter & first, \ Sentinel last, \ Context const & context, \ SkipParser const & skip, \ boost::parser::detail::flags flags, \ bool & success, \ bool & /*dont_assign*/, \ Attribute & retval) \ { \ auto const & parser = BOOST_PARSER_PP_CAT(rule_name_, _def); \ using attr_t = \ decltype(parser(first, last, context, skip, flags, success)); \ if constexpr (boost::parser::detail::is_nope_v) { \ parser(first, last, context, skip, flags, success); \ } else { \ parser(first, last, context, skip, flags, success, retval); \ } \ } //] #endif /** For each given token `t`, defines a pair of `parse_rule()` overloads, used internally within Boost.Parser. Each such pair implements the parsing behavior rule `t`, using the parser `t_def`. This implementation is in the form of a pair of function templates. You should therefore write this macro only at namespace scope. */ #define BOOST_PARSER_DEFINE_RULES(...) \ BOOST_PARSER_PP_FOR_EACH(BOOST_PARSER_DEFINE_IMPL, _, __VA_ARGS__) #ifndef BOOST_PARSER_DOXYGEN template template constexpr auto or_parser::prepend( parser_interface parser) const noexcept { // If you're seeing this as a compile- or run-time failure, you've // tried to put an eps parser at the beginning of an // alternative-parser, such as "eps | (int_ | double_)". This is not // what you meant. Since eps always matches any input, "eps | (int_ | // double_)" is just an awkward spelling for "eps". To fix this this, // put the eps as the last alternative, so the other alternatives get // a chance. Possibly, you may have meant to add a condition to the // eps, like "eps(condition) | (int_ | double_)", which also is // meaningful, and so is allowed. BOOST_PARSER_ASSERT(!detail::is_unconditional_eps{}); return parser_interface{ or_parser{ detail::hl::prepend(parsers_, parser.parser_)}}; } template template constexpr auto or_parser::append( parser_interface parser) const noexcept { // If you're seeing this as a compile- or run-time failure, you've // tried to put an eps parser in the middle of an alternative-parser, // such as "int_ | eps | double_". This is not what you meant. Since // eps always matches any input, "int_ | eps | double_" is just an // awkward spelling for "int_ | eps". To fix this this, put the eps // as the last alternative, so the other alternatives get a chance. // Possibly, you may have meant to add a condition to the eps, like // "int_ | eps(condition) | double_", which also is meaningful, and so // is allowed. BOOST_PARSER_ASSERT(!detail::is_unconditional_eps_v); if constexpr (detail::is_or_p{}) { return parser_interface{or_parser{ detail::hl::concat(parsers_, parser.parser_.parsers_)}}; } else { return parser_interface{or_parser{ detail::hl::append(parsers_, parser.parser_)}}; } } template template constexpr auto perm_parser::prepend( parser_interface parser) const noexcept { // If you're seeing this as a compile- or run-time failure, you've // tried to put an eps parser in a permutation-parser, such as "eps || // int_". BOOST_PARSER_ASSERT(!detail::is_eps_p{}); return parser_interface{perm_parser< decltype(detail::hl::prepend(parsers_, parser.parser_)), detail::nope>{detail::hl::prepend(parsers_, parser.parser_)}}; } template template constexpr auto perm_parser::append( parser_interface parser) const noexcept { // If you're seeing this as a compile- or run-time failure, you've // tried to put an eps parser in a permutation-parser, such as "int_ // || eps". BOOST_PARSER_ASSERT(!detail::is_eps_p{}); if constexpr (detail::is_perm_p{}) { return parser_interface{perm_parser< decltype(detail::hl::concat(parsers_, parser.parser_.parsers_)), detail::nope>{ detail::hl::concat(parsers_, parser.parser_.parsers_)}}; } else { return parser_interface{perm_parser< decltype(detail::hl::append(parsers_, parser.parser_)), detail::nope>{detail::hl::append(parsers_, parser.parser_)}}; } } template< typename ParserTuple, typename BacktrackingTuple, typename CombiningGroups> template constexpr auto seq_parser::prepend( parser_interface parser) const noexcept { using combining_groups = detail::combining_t; using final_combining_groups = decltype(detail::hl::prepend(combining_groups{}, llong<0>{})); using backtracking = decltype(detail::hl::prepend( detail::hl::prepend( detail::hl::drop_front(BacktrackingTuple{}), std::bool_constant{}), std::true_type{})); using parser_t = seq_parser< decltype(detail::hl::prepend(parsers_, parser.parser_)), backtracking, final_combining_groups>; return parser_interface{ parser_t{detail::hl::prepend(parsers_, parser.parser_)}}; } template< typename ParserTuple, typename BacktrackingTuple, typename CombiningGroups> template constexpr auto seq_parser::append( parser_interface parser) const noexcept { using combining_groups = detail::combining_t; if constexpr (detail::is_seq_p{}) { using parser_combining_groups = detail::combining_t< decltype(parser.parser_.parsers_), typename Parser::combining_groups>; using final_combining_groups = detail:: combined_combining_t; using rhs_backtracking = decltype(detail::hl::prepend( detail::hl::drop_front(typename Parser::backtracking{}), std::bool_constant{})); using backtracking = decltype(detail::hl::concat( BacktrackingTuple{}, rhs_backtracking{})); using parser_t = seq_parser< decltype(detail::hl::concat(parsers_, parser.parser_.parsers_)), backtracking, final_combining_groups>; return parser_interface{parser_t{ detail::hl::concat(parsers_, parser.parser_.parsers_)}}; } else { using final_combining_groups = decltype(detail::hl::append(combining_groups{}, llong<0>{})); using backtracking = decltype(detail::hl::append( BacktrackingTuple{}, std::bool_constant{})); using parser_t = seq_parser< decltype(detail::hl::append(parsers_, parser.parser_)), backtracking, final_combining_groups>; return parser_interface{ parser_t{detail::hl::append(parsers_, parser.parser_)}}; } } #endif // Directives. /** Represents an unparameterized higher-order parser (e.g. `omit_parser`) as a directive (e.g. `omit[other_parser]`). */ template class Parser> struct directive { template constexpr auto operator[](parser_interface rhs) const noexcept { return parser_interface{Parser{rhs.parser_}}; } }; /** The `omit` directive, whose `operator[]` returns a `parser_interface>` from a given parser of type `parser_interface

`. */ inline constexpr directive omit; /** The `raw` directive, whose `operator[]` returns a `parser_interface>` from a given parser of type `parser_interface

`. */ inline constexpr directive raw; #if defined(BOOST_PARSER_DOXYGEN) || BOOST_PARSER_USE_CONCEPTS /** The `string_view` directive, whose `operator[]` returns a `parser_interface>` from a given parser of type `parser_interface

`. This is only available in C++20 and later. */ inline constexpr directive string_view; #endif /** The `lexeme` directive, whose `operator[]` returns a `parser_interface>` from a given parser of type `parser_interface

`. */ inline constexpr directive lexeme; /** The `no_case` directive, whose `operator[]` returns a `parser_interface>` from a given parser of type `parser_interface

`. */ inline constexpr directive no_case; /** Represents a `repeat_parser` as a directive (e.g. `repeat[other_parser]`). */ template struct repeat_directive { template constexpr auto operator[](parser_interface rhs) const noexcept { using repeat_parser_type = repeat_parser; return parser_interface{ repeat_parser_type{rhs.parser_, min_, max_}}; } MinType min_; MaxType max_; }; /** Returns a `repeat_directive` that repeats exactly `n` times, and whose `operator[]` returns a `parser_interface>` from a given parser of type `parser_interface

`. */ template constexpr repeat_directive repeat(T n) noexcept { return repeat_directive{n, n}; } /** Returns a `repeat_directive` that repeats between `min_` and `max_` times, inclusive, and whose `operator[]` returns a `parser_interface>` from a given parser of type `parser_interface

`. */ template constexpr repeat_directive repeat(MinType min_, MaxType max_) noexcept { return repeat_directive{min_, max_}; } /** A directive that represents a `perm_parser`, where the items parsed are delimited by `DelimiterParser` (e.g. `delimiter(delimter_parser)[some_perm_parser]`). This directive only applies to `perm_parser`s. */ template struct delimiter_directive { template constexpr auto operator[]( parser_interface> rhs) const noexcept { using parser_type = perm_parser; return parser_interface{ parser_type{rhs.parser_.parsers_, delimiter_parser_}}; } DelimiterParser delimiter_parser_; }; /** Returns a `delimiter_directive` whose `operator[]` returns a `perm_parser`, where the items parsed are delimited by `delimiter_parser`. */ template constexpr delimiter_directive delimiter(parser_interface delimiter_parser) noexcept { return delimiter_directive{delimiter_parser.parser_}; } /** Represents a skip parser as a directive. When used without a skip parser, e.g. `skip[parser_in_which_to_do_skipping]`, the skipper for the entire parse is used. When given another parser, e.g. `skip(skip_parser)[parser_in_which_to_do_skipping]`, that other parser is used as the skipper within the directive. */ template struct skip_directive { template constexpr auto operator[](parser_interface rhs) const noexcept { return parser_interface{ skip_parser{rhs.parser_, skip_parser_}}; } /** Returns a `skip_directive` with `skip_parser` as its skipper. */ template constexpr auto operator()(parser_interface skip_parser) const noexcept { BOOST_PARSER_ASSERT( (detail::is_nope_v && "If you're seeing this, you tried to chain calls on skip, " "like 'skip(foo)(bar)'. Quit it!'")); return skip_directive>{skip_parser}; } SkipParser skip_parser_; }; /** The `skip_directive`, whose `operator[]` returns a `parser_interface>` from a given parser of type `parser_interface

`. */ inline constexpr skip_directive<> skip; /** A directive type that can only be used on sequence parsers, that forces the merge of all the sequence_parser's subparser's attributes into a single attribute. */ struct merge_directive { template< typename ParserTuple, typename BacktrackingTuple, typename CombiningGroups> constexpr auto operator[](parser_interface< seq_parser> rhs) const noexcept { return parser_interface{ seq_parser{ rhs.parser_.parsers_}}; } }; /** The `merge_directive`, whose `operator[]` returns a `parser_interface`, from a given parser of type `parser_interface

`, where `P` is a `seq_parser`. `P2` is the same as `P`, except that its `CombiningGroups` template parameter is replaced with a tag type that causes the subparser's attributes to be merged into a single attribute. */ inline constexpr merge_directive merge; /** A directive type that can only be used on sequence parsers, that prevents each of the sequence_parser's subparser's attributes from merging with any other subparser's attribute. */ struct separate_directive { template< typename ParserTuple, typename BacktrackingTuple, typename CombiningGroups> constexpr auto operator[](parser_interface< seq_parser> rhs) const noexcept { return parser_interface{ seq_parser{ rhs.parser_.parsers_}}; } }; /** The `separate_directive`, whose `operator[]` returns a `parser_interface`, from a given parser of type `parser_interface

`, where `P` is a `seq_parser`. `P2` is the same as `P`, except that its `CombiningGroups` template parameter is replaced with a tag type that prevents each subparser's attribute from merging with any other subparser's attribute. */ inline constexpr separate_directive separate; /** A directive that transforms the attribute generated by a parser. `operator[]` returns a `parser_interface>`. */ template struct transform_directive { template constexpr auto operator[](parser_interface rhs) const noexcept { return parser_interface{ transform_parser{rhs.parser_, f_}}; } F f_; }; /** Returns a `transform_directive` that uses invocable `F` to do its work. */ template constexpr auto transform(F f) { return transform_directive{std::move(f)}; } // First order parsers. #ifndef BOOST_PARSER_DOXYGEN template struct eps_parser { template< typename Iter, typename Sentinel, typename Context, typename SkipParser> detail::nope call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) const noexcept { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, detail::global_nope); BOOST_PARSER_SUBRANGE const where(first, first); auto const predicate_context = detail::make_action_context( context, detail::global_nope, where); // Predicate must be a parse predicate. If you see an error here, // you have not met this contract. See the terminology section of // the online docs if you don't know what that a parse predicate // is. success = pred_(predicate_context); return {}; } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute & retval) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, retval); BOOST_PARSER_SUBRANGE const where(first, first); auto const predicate_context = detail::make_action_context( context, detail::global_nope, where); // Predicate must be a parse predicate. If you see an error here, // you have not met this contract. See the terminology section of // the online docs if you don't know what that a parse predicate // is. success = pred_(predicate_context); } /** Returns a `parser_interface` containing an `eps_parser` that will fail if `pred` evaluates to false. */ template constexpr auto operator()(Predicate2 pred) const noexcept { BOOST_PARSER_ASSERT( (detail::is_nope_v && "If you're seeing this, you tried to chain calls on eps, " "like 'eps(foo)(bar)'. Quit it!'")); return parser_interface{eps_parser{std::move(pred)}}; } Predicate pred_; }; #endif /** The epsilon parser. This matches anything, and consumes no input. If used with an optional predicate, like `eps(pred)`, it matches iff `pred(ctx)` evaluates to true, where `ctx` is the parser context. */ inline constexpr parser_interface> eps; #ifndef BOOST_PARSER_DOXYGEN struct eoi_parser { template< typename Iter, typename Sentinel, typename Context, typename SkipParser> detail::nope call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, detail::global_nope); if (first != last) success = false; return {}; } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute & retval) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, retval); if (first != last) success = false; } }; #endif /** The end-of-input parser. It matches only the end of input. */ inline constexpr parser_interface eoi; #ifndef BOOST_PARSER_DOXYGEN template struct attr_parser { template< typename Iter, typename Sentinel, typename Context, typename SkipParser> auto call( Iter & first, Sentinel last, Context const & context, SkipParser const &, detail::flags flags, bool &) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, detail::global_nope); return detail::resolve(context, attr_); } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute_> void call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute_ & retval) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, retval); if (detail::gen_attrs(flags)) detail::assign_copy(retval, detail::resolve(context, attr_)); } Attribute attr_; }; #endif /** Returns an `attr_parser` which matches anything, and consumes no input, and which produces `a` as its attribute. */ template constexpr auto attr(Attribute a) noexcept { return parser_interface{attr_parser{std::move(a)}}; } #ifndef BOOST_PARSER_DOXYGEN template struct char_parser { constexpr char_parser() {} constexpr char_parser(Expected expected) : expected_(expected) {} template using attribute_type = std::conditional_t< std::is_same_v, std::decay_t, AttributeType>; template< typename Iter, typename Sentinel, typename Context, typename SkipParser> auto call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) const -> attribute_type { attribute_type retval{}; call(first, last, context, skip, flags, success, retval); return retval; } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute & retval) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, retval); if (first == last) { success = false; return; } attribute_type const x = *first; if (detail::unequal(context, x, expected_)) { success = false; return; } detail::assign(retval, x); ++first; } /** Returns a `parser_interface` containing a `char_parser` that matches `x`. \tparam T Constrained by `!parsable_range_like`. */ #if BOOST_PARSER_USE_CONCEPTS template requires(!parsable_range_like) #else template< typename T, typename Enable = std::enable_if_t>> #endif constexpr auto operator()(T x) const noexcept { BOOST_PARSER_ASSERT( (detail::is_nope_v && "If you're seeing this, you tried to chain calls on char_, " "like 'char_('a')('b')'. Quit it!'")); return parser_interface{ char_parser{std::move(x)}}; } /** Returns a `parser_interface` containing a `char_parser` that matches any value in `[lo, hi]`. */ template constexpr auto operator()(LoType lo, HiType hi) const noexcept { BOOST_PARSER_ASSERT( (detail::is_nope_v && "If you're seeing this, you tried to chain calls on char_, " "like 'char_('a', 'b')('c', 'd')'. Quit it!'")); using char_pair_t = detail::char_pair; using char_parser_t = char_parser; return parser_interface( char_parser_t(char_pair_t{std::move(lo), std::move(hi)})); } /** Returns a `parser_interface` containing a `char_parser` that matches one of the values in `r`. If the character being matched during the parse is a `char32_t` value, the elements of `r` are transcoded from their presumed encoding to UTF-32 during the comparison. Otherwise, the character begin matched is directly compared to the elements of `r`. */ #if BOOST_PARSER_USE_CONCEPTS template #else template< typename R, typename Enable = std::enable_if_t>> #endif constexpr auto operator()(R && r) const noexcept { BOOST_PARSER_ASSERT( ((!std::is_rvalue_reference_v || !detail::is_range>) && "It looks like you tried to pass an rvalue range to " "char_(). Don't do that, or you'll end up with dangling " "references.")); BOOST_PARSER_ASSERT( (detail::is_nope_v && "If you're seeing this, you tried to chain calls on char_, " "like 'char_(char-set)(char-set)'. Quit it!'")); auto chars = detail::make_char_range(r); using char_range_t = decltype(chars); using char_parser_t = char_parser; return parser_interface(char_parser_t(chars)); } /** Returns a `parser_interface` containing a `char_parser` that matches one of the values in `r`. `r` must be a sorted, random-access sequence of `char32_t`. The character begin matched is directly compared to the elements of `r`. The match is found via binary search. No case folding is performed. \tparam R Additionally constrained by `std::same_as, char32_t>`. */ #if BOOST_PARSER_USE_CONCEPTS template requires std::same_as, char32_t> #else template< typename R, typename Enable = std::enable_if_t< detail::is_parsable_range_like_v && std::is_same_v, char32_t>>> #endif constexpr auto operator()(sorted_t, R && r) const noexcept { BOOST_PARSER_ASSERT( ((!std::is_rvalue_reference_v || !detail::is_range>) && "It looks like you tried to pass an rvalue range to " "char_(). Don't do that, or you'll end up with dangling " "references.")); BOOST_PARSER_ASSERT( (detail::is_nope_v && "If you're seeing this, you tried to chain calls on char_, " "like 'char_(char-set)(char-set)'. Quit it!'")); auto chars = detail::make_char_range(r); using char_range_t = decltype(chars); using char_parser_t = char_parser; return parser_interface(char_parser_t(chars)); } Expected expected_; }; struct digit_parser { constexpr digit_parser() {} template using attribute_type = std::decay_t; template< typename Iter, typename Sentinel, typename Context, typename SkipParser> auto call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) const -> attribute_type { attribute_type retval{}; call(first, last, context, skip, flags, success, retval); return retval; } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute & retval) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, retval); if (first == last) { success = false; return; } attribute_type const x = *first; char32_t const x_cmp = x; if (x_cmp < U'\x0100' && (x_cmp < U'0' || U'9' < x_cmp)) { success = false; return; } char32_t const * it = std::upper_bound( std::begin(zero_cps) + 1, std::end(zero_cps), x_cmp); if (it == std::begin(zero_cps) || x_cmp < *(it - 1) || *(it - 1) + 9 < x_cmp) { success = false; return; } detail::assign(retval, x); ++first; } // Produced from // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp, using // "[:nt=De:]" for the Input field. static constexpr char32_t zero_cps[] = { U'\u0030', // U+0030 DIGIT ZERO U'\u0660', // U+0660 ARABIC-INDIC DIGIT ZERO U'\u06F0', // U+06F0 EXTENDED ARABIC-INDIC DIGIT ZERO U'\u07C0', // U+07C0 NKO DIGIT ZERO U'\u0966', // U+0966 DEVANAGARI DIGIT ZERO U'\u09E6', // U+09E6 BENGALI DIGIT ZERO U'\u0A66', // U+0A66 GURMUKHI DIGIT ZERO U'\u0AE6', // U+0AE6 GUJARATI DIGIT ZERO U'\u0B66', // U+0B66 ORIYA DIGIT ZERO U'\u0BE6', // U+0BE6 TAMIL DIGIT ZERO U'\u0C66', // U+0C66 TELUGU DIGIT ZERO U'\u0CE6', // U+0CE6 KANNADA DIGIT ZERO U'\u0D66', // U+0D66 MALAYALAM DIGIT ZERO U'\u0DE6', // U+0DE6 SINHALA LITH DIGIT ZERO U'\u0E50', // U+0E50 THAI DIGIT ZERO U'\u0ED0', // U+0ED0 LAO DIGIT ZERO U'\u0F20', // U+0F20 TIBETAN DIGIT ZERO U'\u1040', // U+1040 MYANMAR DIGIT ZERO U'\u1090', // U+1090 MYANMAR SHAN DIGIT ZERO U'\u17E0', // U+17E0 KHMER DIGIT ZERO U'\u1810', // U+1810 MONGOLIAN DIGIT ZERO U'\u1946', // U+1946 LIMBU DIGIT ZERO U'\u19D0', // U+19D0 NEW TAI LUE DIGIT ZERO U'\u1A80', // U+1A80 TAI THAM HORA DIGIT ZERO U'\u1A90', // U+1A90 TAI THAM THAM DIGIT ZERO U'\u1B50', // U+1B50 BALINESE DIGIT ZERO U'\u1BB0', // U+1BB0 SUNDANESE DIGIT ZERO U'\u1C40', // U+1C40 LEPCHA DIGIT ZERO U'\u1C50', // U+1C50 OL CHIKI DIGIT ZERO U'\uA620', // U+A620 VAI DIGIT ZERO U'\uA8D0', // U+A8D0 SAURASHTRA DIGIT ZERO U'\uA900', // U+A900 KAYAH LI DIGIT ZERO U'\uA9D0', // U+A9D0 JAVANESE DIGIT ZERO U'\uA9F0', // U+A9F0 MYANMAR TAI LAING DIGIT ZERO U'\uAA50', // U+AA50 CHAM DIGIT ZERO U'\uABF0', // U+ABF0 MEETEI MAYEK DIGIT ZERO U'\uFF10', // U+FF10 FULLWIDTH DIGIT ZERO U'\U000104A0', // U+104A0 OSMANYA DIGIT ZERO U'\U00010D30', // U+10D30 HANIFI ROHINGYA DIGIT ZERO U'\U00011066', // U+11066 BRAHMI DIGIT ZERO U'\U000110F0', // U+110F0 SORA SOMPENG DIGIT ZERO U'\U00011136', // U+11136 CHAKMA DIGIT ZERO U'\U000111D0', // U+111D0 SHARADA DIGIT ZERO U'\U000112F0', // U+112F0 KHUDAWADI DIGIT ZERO U'\U00011450', // U+11450 NEWA DIGIT ZERO U'\U000114D0', // U+114D0 TIRHUTA DIGIT ZERO U'\U00011650', // U+11650 MODI DIGIT ZERO U'\U000116C0', // U+116C0 TAKRI DIGIT ZERO U'\U00011730', // U+11730 AHOM DIGIT ZERO U'\U000118E0', // U+118E0 WARANG CITI DIGIT ZERO U'\U00011950', // U+11950 DIVES AKURU DIGIT ZERO U'\U00011C50', // U+11C50 BHAIKSUKI DIGIT ZERO U'\U00011D50', // U+11D50 MASARAM GONDI DIGIT ZERO U'\U00011DA0', // U+11DA0 GUNJALA GONDI DIGIT ZERO U'\U00011F50', // U+11F50 KAWI DIGIT ZERO U'\U00016A60', // U+16A60 MRO DIGIT ZERO U'\U00016AC0', // U+16AC0 TANGSA DIGIT ZERO U'\U00016B50', // U+16B50 PAHAWH HMONG DIGIT ZERO U'\U0001D7CE', // U+1D7CE MATHEMATICAL BOLD DIGIT ZERO U'\U0001D7D8', // U+1D7D8 MATHEMATICAL DOUBLE-STRUCK DIGIT ZERO U'\U0001D7E2', // U+1D7E2 MATHEMATICAL SANS-SERIF DIGIT ZERO U'\U0001D7EC', // U+1D7EC MATHEMATICAL SANS-SERIF BOLD DIGIT ZERO U'\U0001D7F6', // U+1D7F6 MATHEMATICAL MONOSPACE DIGIT ZERO U'\U0001E140', // U+1E140 NYIAKENG PUACHUE HMONG DIGIT ZERO U'\U0001E2F0', // U+1E2F0 WANCHO DIGIT ZERO U'\U0001E4F0', // U+1E4F0 NAG MUNDARI DIGIT ZERO U'\U0001E950', // U+1E950 ADLAM DIGIT ZERO U'\U0001FBF0' // U+1FBF0 SEGMENTED DIGIT ZERO }; }; template struct char_set_parser { BOOST_PARSER_ALGO_CONSTEXPR char_set_parser() { auto const & chars = detail::char_set::chars; auto const first = std::begin(chars); auto const last = std::end(chars); auto it = std::upper_bound(first, last, 0x100, [](auto x, auto y){ using common_t = std::common_type_t; return (common_t)x < (common_t)y; }); if (it != last) one_byte_offset_ = int(it - first); } template using attribute_type = std::decay_t; template< typename Iter, typename Sentinel, typename Context, typename SkipParser> auto call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) const -> attribute_type { attribute_type retval{}; call(first, last, context, skip, flags, success, retval); return retval; } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute & retval) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, retval); if (first == last) { success = false; return; } auto const & chars = detail::char_set::chars; attribute_type const x = *first; uint32_t const x_cmp = x; if (x_cmp < U'\x0100') { uint32_t const * it = std::lower_bound( std::begin(chars), std::begin(chars) + one_byte_offset_, x_cmp); if (it != std::end(chars) && *it == x_cmp) { detail::assign(retval, x_cmp); ++first; } else { success = false; } return; } uint32_t const * it = std::lower_bound( std::begin(chars) + one_byte_offset_, std::end(chars), x_cmp); if (it != std::end(chars) && *it == x_cmp) { detail::assign(retval, x_cmp); ++first; return; } success = false; } int one_byte_offset_ = 0; }; template struct char_subrange_parser { constexpr char_subrange_parser() {} template using attribute_type = std::decay_t; template< typename Iter, typename Sentinel, typename Context, typename SkipParser> auto call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) const -> attribute_type { attribute_type retval{}; call(first, last, context, skip, flags, success, retval); return retval; } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute & retval) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, retval); if (first == last) { success = false; return; } attribute_type const x = *first; char32_t const x_cmp = x; success = false; for (auto subrange : detail::char_subranges::ranges) { if (subrange.lo_ <= x_cmp && x_cmp <= subrange.hi_) { success = true; detail::assign(retval, x); ++first; return; } } } }; #endif /** The single-character parser. The produced attribute is the type of the matched code point (`char` or `char32_t`). Used as-is, `char_` matches any code point. `char_` can also be used to create code point parsers that match one or more specific code point values, by calling it with: a single value comparable to a code point; a closed range of code point values `[lo, hi]`, or a set of code point values passed as a range. When calling with a range, only the iterators that bound the range are stored. Make sure the range you pass outlives the use of the resulting parser. Note that a string literal is a range, and that it outlives any parser it is used to construct. */ inline constexpr parser_interface> char_; /** The code point parser. It produces a `char32_t` attribute. Used as-is, `cp` matches any code point. `cp` can also be used to create code point parsers that match one or more specific code point values, by calling it with: a single value comparable to a code point; a closed range of code point values `[lo, hi]`, or a set of code point values passed as a range. When calling with a range, only the iterators that bound the range are stored. Make sure the range you pass outlives the use of the resulting parser. Note that a string literal is a range, and that it outlives any parser it is used to construct. */ inline constexpr parser_interface> cp; /** The code unit parser. It produces a `char` attribute. Used as-is, `cu` matches any code point. `cu` can also can be used to create code point parsers that match one or more specific code point values, by calling it with: a single value comparable to a code point; a closed range of code point values `[lo, hi]`, or a set of code point values passed as a range. When calling with a range, only the iterators that bound the range are stored. Make sure the range you pass outlives the use of the resulting parser. Note that a string literal is a range, and that it outlives any parser it is used to construct. */ inline constexpr parser_interface> cu; /** Returns a literal code point parser that produces no attribute. */ inline constexpr auto lit(char c) noexcept { return omit[char_(c)]; } #if defined(__cpp_char8_t) || defined(BOOST_PARSER_DOXYGEN) /** Returns a literal code point parser that produces no attribute. */ inline constexpr auto lit(char8_t c) noexcept { return omit[char_(c)]; } #endif /** Returns a literal code point parser that produces no attribute. */ inline constexpr auto lit(char32_t c) noexcept { return omit[char_(c)]; } #ifndef BOOST_PARSER_DOXYGEN template struct string_parser { constexpr string_parser() : expected_first_(), expected_last_() {} #if BOOST_PARSER_USE_CONCEPTS template #else template< typename R, typename Enable = std::enable_if_t>> #endif constexpr string_parser(R && r) : expected_first_(detail::make_view_begin(r)), expected_last_(detail::make_view_end(r)) {} template< typename Iter, typename Sentinel, typename Context, typename SkipParser> std::string call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) const { std::string retval; call(first, last, context, skip, flags, success, retval); return retval; } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute & retval) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, retval); if (first == last) { success = false; return; } if constexpr (std::is_same_v< detail::remove_cv_ref_t, char32_t>) { auto const cps = BOOST_PARSER_SUBRANGE(expected_first_, expected_last_) | detail::text::as_utf32; auto const mismatch = detail::no_case_aware_string_mismatch( first, last, cps.begin(), cps.end(), context.no_case_depth_); if (mismatch.second != cps.end()) { success = false; return; } detail::append( retval, first, mismatch.first, detail::gen_attrs(flags)); first = mismatch.first; } else { auto const mismatch = detail::no_case_aware_string_mismatch( first, last, expected_first_, expected_last_, context.no_case_depth_); if (mismatch.second != expected_last_) { success = false; return; } detail::append( retval, first, mismatch.first, detail::gen_attrs(flags)); first = mismatch.first; } } StrIter expected_first_; StrSentinel expected_last_; }; #if BOOST_PARSER_USE_CONCEPTS template #else template #endif string_parser(R r) -> string_parser< decltype(detail::make_view_begin(r)), decltype(detail::make_view_end(r))>; #endif /** Returns a parser that matches `str` that produces the matched string as its attribute. */ #if BOOST_PARSER_USE_CONCEPTS template #else template #endif constexpr auto string(R && str) noexcept { return parser_interface{string_parser(str)}; } template struct quoted_string_parser { constexpr quoted_string_parser() : chs_(), ch_('"') {} #if BOOST_PARSER_USE_CONCEPTS template #else template< typename R, typename Enable = std::enable_if_t>> #endif constexpr quoted_string_parser( R && r, parser_interface char_p = parser_interface{CharParser()}) : chs_((R &&)r), char_p_(char_p), ch_(0) { BOOST_PARSER_DEBUG_ASSERT(r.begin() != r.end()); } #if BOOST_PARSER_USE_CONCEPTS template #else template< typename R, typename Enable = std::enable_if_t>> #endif constexpr quoted_string_parser( R && r, Escapes escapes, parser_interface char_p = parser_interface{CharParser()}) : chs_((R &&)r), escapes_(escapes), char_p_(char_p), ch_(0) { BOOST_PARSER_DEBUG_ASSERT(r.begin() != r.end()); } constexpr quoted_string_parser( char32_t cp, parser_interface char_p = parser_interface{CharParser()}) : chs_(), char_p_(char_p), ch_(cp) {} constexpr quoted_string_parser( char32_t cp, Escapes escapes, parser_interface char_p = parser_interface{CharParser()}) : chs_(), escapes_(escapes), char_p_(char_p), ch_(cp) {} template< typename Iter, typename Sentinel, typename Context, typename SkipParser> std::string call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) const { std::string retval; call(first, last, context, skip, flags, success, retval); return retval; } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute & retval) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, retval); if (first == last) { success = false; return; } auto const prev_first = first; auto append = [&retval, gen_attrs = detail::gen_attrs(flags)](auto & ctx) { detail::move_back(retval, _attr(ctx), gen_attrs); }; auto quote_ch = [&]() { if constexpr (detail::is_nope_v) { detail::remove_cv_ref_t curr = *first; if ((char32_t)curr == ch_) ++first; else success = false; return ch_; } else { detail::remove_cv_ref_t const ch = *first; bool found = false; if constexpr (std:: is_same_v) { auto r = chs_ | detail::text::as_utf32; found = detail::text::find(r.begin(), r.end(), ch) != r.end(); } else { found = detail::text::find( chs_.begin(), chs_.end(), ch) != chs_.end(); } if (found) ++first; else success = false; return ch; } }; auto const ch = quote_ch(); if (!success) return; decltype(ch) const backslash_and_delim[] = {'\\', ch}; auto const back_delim = char_(backslash_and_delim); auto make_parser = [&]() { if constexpr (detail::is_nope_v) { return *((lit('\\') >> back_delim) | (char_p_ - back_delim))[append] > ch; } else { return *((lit('\\') >> back_delim)[append] | (lit('\\') >> parser_interface(escapes_))[append] | (char_p_ - back_delim)[append]) > ch; } }; auto const p = make_parser(); p.parser_.call( first, last, context, skip, detail::disable_skip(flags), success); if (!success) { retval = Attribute(); first = prev_first; } } /** Returns a `parser_interface` containing a `quoted_string_parser` that uses `x` as its quotation marks. */ #if BOOST_PARSER_USE_CONCEPTS template> requires(!parsable_range_like) #else template< typename T, typename Parser = char_parser, typename Enable = std::enable_if_t>> #endif constexpr auto operator()(T x, parser_interface char_p = char_) const noexcept { if constexpr (!detail::is_nope_v) { BOOST_PARSER_ASSERT( (chs_.empty() && ch_ == '"' && "If you're seeing this, you tried to chain calls on " "quoted_string, like 'quoted_string('\"')('\\'')'. Quit " "it!'")); } return parser_interface( quoted_string_parser( std::move(x), char_p)); } /** Returns a `parser_interface` containing a `quoted_string_parser` that accepts any of the values in `r` as its quotation marks. If the input being matched during the parse is a a sequence of `char32_t`, the elements of `r` are transcoded from their presumed encoding to UTF-32 during the comparison. Otherwise, the character begin matched is directly compared to the elements of `r`. */ #if BOOST_PARSER_USE_CONCEPTS template< parsable_range_like R, typename Parser = char_parser> #else template< typename R, typename Parser = char_parser, typename Enable = std::enable_if_t>> #endif constexpr auto operator()( R && r, parser_interface char_p = char_) const noexcept { BOOST_PARSER_ASSERT((( !std::is_rvalue_reference_v || !detail::is_range>)&&"It looks like you tried to pass an rvalue range to " "quoted_string(). Don't do that, or you'll end up " "with dangling references.")); if constexpr (!detail::is_nope_v) { BOOST_PARSER_ASSERT( (chs_.empty() && ch_ == '"' && "If you're seeing this, you tried to chain calls on " "quoted_string, like " "'quoted_string(char-range)(char-range)'. Quit it!'")); } return parser_interface( quoted_string_parser< decltype(BOOST_PARSER_SUBRANGE( detail::make_view_begin(r), detail::make_view_end(r))), detail::nope, Parser>( BOOST_PARSER_SUBRANGE( detail::make_view_begin(r), detail::make_view_end(r)), char_p)); } /** Returns a `parser_interface` containing a `quoted_string_parser` that uses `x` as its quotation marks. `symbols` provides a list of strings that may appear after a backslash to form an escape sequence, and what character(s) each escape sequence represents. Note that `"\\"` and `"\ch"` are always valid escape sequences. */ #if BOOST_PARSER_USE_CONCEPTS template< typename T, typename U, typename Parser = char_parser> requires(!parsable_range_like) #else template< typename T, typename U, typename Parser = char_parser, typename Enable = std::enable_if_t>> #endif auto operator()( T x, symbols const & escapes, parser_interface char_p = char_) const noexcept { if constexpr (!detail::is_nope_v) { BOOST_PARSER_ASSERT( (chs_.empty() && ch_ == '"' && "If you're seeing this, you tried to chain calls on " "quoted_string, like 'quoted_string('\"')('\\'')'. Quit " "it!'")); } auto symbols = symbol_parser(escapes.parser_); auto parser = quoted_string_parser( char32_t(x), symbols, char_p); return parser_interface(parser); } /** Returns a `parser_interface` containing a `quoted_string_parser` that accepts any of the values in `r` as its quotation marks. If the input being matched during the parse is a a sequence of `char32_t`, the elements of `r` are transcoded from their presumed encoding to UTF-32 during the comparison. Otherwise, the character begin matched is directly compared to the elements of `r`. `symbols` provides a list of strings that may appear after a backslash to form an escape sequence, and what character(s) each escape sequence represents. Note that `"\\"` and `"\ch"` are always valid escape sequences. */ #if BOOST_PARSER_USE_CONCEPTS template< parsable_range_like R, typename T, typename Parser = char_parser> #else template< typename R, typename T, typename Parser = char_parser, typename Enable = std::enable_if_t>> #endif auto operator()( R && r, symbols const & escapes, parser_interface char_p = char_) const noexcept { BOOST_PARSER_ASSERT((( !std::is_rvalue_reference_v || !detail::is_range>)&&"It looks like you tried to pass an rvalue range to " "quoted_string(). Don't do that, or you'll end up " "with dangling references.")); if constexpr (!detail::is_nope_v) { BOOST_PARSER_ASSERT( (chs_.empty() && ch_ == '"' && "If you're seeing this, you tried to chain calls on " "quoted_string, like " "'quoted_string(char-range)(char-range)'. Quit it!'")); } auto symbols = symbol_parser(escapes.parser_); auto quotes = BOOST_PARSER_SUBRANGE( detail::make_view_begin(r), detail::make_view_end(r)); auto parser = quoted_string_parser< decltype(quotes), decltype(symbols), Parser>(quotes, symbols, char_p); return parser_interface(parser); } Quotes chs_; Escapes escapes_; parser_interface char_p_; char32_t ch_; }; /** Parses a string delimited by quotation marks. This parser can be used to create parsers that accept one or more specific quotation mark characters. By default, the quotation marks are `'"'`; an alternate quotation mark can be specified by calling this parser with a single character, or a range of characters. If a range is specified, the opening quote must be one of the characters specified, and the closing quote must match the opening quote. Quotation marks may appear within the string if escaped with a backslash, and a pair of backslashes is treated as a single escaped backslash; all other backslashes cause the parse to fail, unless a symbol table is in use. A symbol table can be provided as a second parameter after the single character or range described above. The symbol table is used to recognize escape sequences. Each escape sequence is a backslash followed by a value in the symbol table. When using a symbol table, any backslash that is not followed by another backslash, the opening quote character, or a symbol from the symbol table will cause the parse to fail. Skipping is disabled during parsing of the entire quoted string, including the quotation marks. There is an expectation point before the closing quotation mark. Produces a `std::string` attribute. */ inline constexpr parser_interface> quoted_string; /** Returns a parser that matches `str` that produces no attribute. */ #if BOOST_PARSER_USE_CONCEPTS template #else template #endif constexpr auto lit(R && str) noexcept { return omit[parser::string(str)]; } #ifndef BOOST_PARSER_DOXYGEN template struct ws_parser { constexpr ws_parser() {} static_assert(!NewlinesOnly || !NoNewlines); template< typename Iter, typename Sentinel, typename Context, typename SkipParser> detail::nope call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) const { detail::nope nope; call(first, last, context, skip, flags, success, nope); return {}; } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute & retval) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, retval); if (first == last) { success = false; return; } int const x = *first; if constexpr (NewlinesOnly) { if (x == 0x000a) { // lf ++first; return; } if (x == 0x000d) { // cr ++first; if (first != last && *first == 0x000a) // lf ++first; return; } if (0x000b == x || x == 0x000c || x == 0x0085 || x == 0x2028 || x == 0x2029) { ++first; return; } success = false; } else if constexpr (NoNewlines) { if (x == 0x0020) { // space ++first; return; } if (x == 0x0009) { // tab ++first; return; } if (x == 0x00a0 || x == 0x1680 || (0x2000 <= x && x <= 0x200a) || x == 0x202F || x == 0x205F || x == 0x3000) { ++first; return; } success = false; } else { if (x == 0x0020 || x == 0x000a) { // space, lf ++first; return; } if (x == 0x000d) { // cr ++first; if (first != last && *first == 0x000a) // lf ++first; return; } if (0x0009 <= x && x <= 0x000c) { // tab through cr ++first; return; } if (x == 0x0085 || x == 0x00a0 || x == 0x1680 || (0x2000 <= x && x <= 0x200a) || x == 0x2028 || x == 0x2029 || x == 0x202F || x == 0x205F || x == 0x3000) { ++first; return; } success = false; } } }; #endif /** The end-of-line parser. This matches "\r\n", or any one of the line break code points from the Unicode Line Break Algorithm, described in https://unicode.org/reports/tr14. Produces no attribute. */ inline constexpr parser_interface> eol; /** The whitespace parser. This matches "\r\n", or any one of the Unicode code points with the White_Space property, as defined in https://www.unicode.org/Public/UCD/latest/ucd/PropList.txt. Produces no attribute. */ inline constexpr parser_interface> ws; /** The whitespace parser that does not match end-of-line. This matches any one of the Unicode code points with the White_Space property, as defined in https://www.unicode.org/Public/UCD/latest/ucd/PropList.txt, except for the ones matched by `eol`. Produces no attribute. */ inline constexpr parser_interface> blank; /** The decimal digit parser. Matches the full set of Unicode decimal digits; in other words, all Unicode code points with the "Nd" character property. Note that this covers all Unicode scripts, only a few of which are Latin. */ inline constexpr parser_interface digit; /** The hexadecimal digit parser. Matches the full set of Unicode hexadecimal digits (upper or lower case); in other words, all Unicode code points with the "Hex_Digit" character property. */ inline constexpr parser_interface< char_subrange_parser> hex_digit; /** The control character parser. Matches the all Unicode code points with the "Cc" ("control character") character property. */ inline constexpr parser_interface< char_subrange_parser> control; /** The punctuation character parser. Matches the full set of Unicode punctuation classes (specifically, "Pc", "Pd", "Pe", "Pf", "Pi", "Ps", and "Po"). */ inline BOOST_PARSER_ALGO_CONSTEXPR parser_interface> punct; /** The symbol character parser. Matches the full set of Unicode symbol classes (specifically, "Sc", "Sk", "Sm", and "So"). */ inline BOOST_PARSER_ALGO_CONSTEXPR parser_interface> symb; /** The lower case character parser. Matches the full set of Unicode lower case code points (class "Ll"). */ inline BOOST_PARSER_ALGO_CONSTEXPR parser_interface> lower; /** The lower case character parser. Matches the full set of Unicode upper case code points (class "Lu"). */ inline BOOST_PARSER_ALGO_CONSTEXPR parser_interface> upper; #ifndef BOOST_PARSER_DOXYGEN struct bool_parser { template< typename Iter, typename Sentinel, typename Context, typename SkipParser> bool call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) const { bool retval{}; call(first, last, context, skip, flags, success, retval); return retval; } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute & retval) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, retval); auto compare = [no_case = context.no_case_depth_](char32_t a, char32_t b) { if (no_case && 0x41 <= b && b < 0x5b) b += 0x20; return a == b; }; // The lambda quiets a signed/unsigned mismatch warning when // comparing the chars here to code points. char const t[] = "true"; if (detail::mismatch(t, t + 4, first, last, compare).first == t + 4) { std::advance(first, 4); detail::assign(retval, true); return; } char const f[] = "false"; if (detail::mismatch(f, f + 5, first, last, compare).first == f + 5) { std::advance(first, 5); detail::assign(retval, false); return; } success = false; } }; #endif /** The Boolean parser. Parses "true" and "false", producing attributes `true` and `false`, respectively, and fails on any other input. */ inline constexpr parser_interface bool_; #ifndef BOOST_PARSER_DOXYGEN template< typename T, int Radix, int MinDigits, int MaxDigits, typename Expected> struct uint_parser { static_assert( Radix == 2 || Radix == 8 || Radix == 10 || Radix == 16, "Unsupported radix."); static_assert(1 <= MinDigits); static_assert(MaxDigits == -1 || MinDigits <= MaxDigits); constexpr uint_parser() {} explicit constexpr uint_parser(Expected expected) : expected_(expected) {} template< typename Iter, typename Sentinel, typename Context, typename SkipParser> T call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) const { T retval{}; call(first, last, context, skip, flags, success, retval); return retval; } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute & retval) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, retval); T attr = 0; auto const initial = first; success = detail::numeric::parse_int( first, last, attr); if (first == initial || attr != detail::resolve(context, expected_)) success = false; if (success) detail::assign(retval, attr); } /** Returns a `parser_interface` containing a `uint_parser` that matches the exact value `expected`. */ template constexpr auto operator()(Expected2 expected) const noexcept { BOOST_PARSER_ASSERT( (detail::is_nope_v && "If you're seeing this, you tried to chain calls on this " "parser, like 'uint_(2)(3)'. Quit it!'")); using parser_t = uint_parser; return parser_interface{parser_t{expected}}; } /** Returns a `uint_parser` identical to `*this`, except that it parses digits as base-`Radix2` instead of base-`Radix`. */ template constexpr auto base() const noexcept { return uint_parser{ expected_}; } /** Returns a `uint_parser` identical to `*this`, except that it only accepts numbers exactly `Digits` digits. */ template constexpr auto digits() const noexcept { return uint_parser{expected_}; } /** Returns a `uint_parser` identical to `*this`, except that it only accepts numbers `D` digits long, where `D` is in [`MinDigits2`, MaxDigits2`]. */ template constexpr auto digits() const noexcept { return uint_parser{ expected_}; } Expected expected_; }; #endif /** The binary unsigned integer parser. Produces an `unsigned int` attribute. To parse a particular value `x`, use `bin(x)`. */ inline constexpr parser_interface> bin; /** The octal unsigned integer parser. Produces an `unsigned int` attribute. To parse a particular value `x`, use `oct(x)`. */ inline constexpr parser_interface> oct; /** The hexadecimal unsigned integer parser. Produces an `unsigned int` attribute. To parse a particular value `x`, use `hex(x)`. */ inline constexpr parser_interface> hex; /** The `unsigned short` parser. Produces an `unsigned short` attribute. To parse a particular value `x`, use `ushort_(x)`. */ inline constexpr parser_interface> ushort_; /** The `unsigned int` parser. Produces an `unsigned int` attribute. To parse a particular value `x`, use `uint_(x)`. */ inline constexpr parser_interface> uint_; /** The `unsigned long` parser. Produces an `unsigned long` attribute. To parse a particular value `x`, use `ulong_(x)`. */ inline constexpr parser_interface> ulong_; /** The `unsigned long long` parser. Produces an `unsigned long long` attribute. To parse a particular value `x`, use `ulong_long(x)`. */ inline constexpr parser_interface> ulong_long; #ifndef BOOST_PARSER_DOXYGEN template< typename T, int Radix, int MinDigits, int MaxDigits, typename Expected> struct int_parser { static_assert( Radix == 2 || Radix == 8 || Radix == 10 || Radix == 16, "Unsupported radix."); static_assert(1 <= MinDigits); static_assert(MaxDigits == -1 || MinDigits <= MaxDigits); constexpr int_parser() {} explicit constexpr int_parser(Expected expected) : expected_(expected) {} template< typename Iter, typename Sentinel, typename Context, typename SkipParser> T call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) const { T retval{}; call(first, last, context, skip, flags, success, retval); return retval; } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute & retval) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, retval); T attr = 0; auto const initial = first; success = detail::numeric::parse_int( first, last, attr); if (first == initial || attr != detail::resolve(context, expected_)) success = false; if (success) detail::assign(retval, attr); } /** Returns a `parser_interface` containing an `int_parser` that matches the exact value `expected`. */ template constexpr auto operator()(Expected2 expected) const noexcept { BOOST_PARSER_ASSERT( (detail::is_nope_v && "If you're seeing this, you tried to chain calls on this " "parser, like 'int_(2)(3)'. Quit it!'")); using parser_t = int_parser; return parser_interface{parser_t{expected}}; } /** Returns an `int_parser` identical to `*this`, except that it parses digits as base-`Radix2` instead of base-`Radix`. */ template constexpr auto base() const noexcept { return int_parser{ expected_}; } /** Returns an `int_parser` identical to `*this`, except that it only accepts numbers exactly `Digits` digits. */ template constexpr auto digits() const noexcept { return int_parser{expected_}; } /** Returns an `int_parser` identical to `*this`, except that it only accepts numbers `D` digits long, where `D` is in [`MinDigits2`, MaxDigits2`]. */ template constexpr auto digits() const noexcept { return int_parser{ expected_}; } Expected expected_; }; #endif /** The `short` parser. Produces a `short` attribute. To parse a particular value `x`, use `short_(x)`. */ inline constexpr parser_interface> short_; /** The `int` parser. Produces an `int` attribute. To parse a particular value `x`, use `int_(x)`. */ inline constexpr parser_interface> int_; /** The `long` parser. Produces a `long` attribute. To parse a particular value `x`, use `long_(x)`. */ inline constexpr parser_interface> long_; /** The `long long` parser. Produces a `long long` attribute. To parse a particular value `x`, use `long_long(x)`. */ inline constexpr parser_interface> long_long; #ifndef BOOST_PARSER_DOXYGEN template struct float_parser { constexpr float_parser() {} template< typename Iter, typename Sentinel, typename Context, typename SkipParser> T call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) const { T retval = 0; call(first, last, context, skip, flags, success, retval); return retval; } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute & retval) const { [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, retval); T attr = 0; auto const initial = first; success = detail::numeric::parse_real(first, last, attr); if (first == initial) success = false; if (success) detail::assign(retval, attr); } }; #endif /** The `float` parser. Produces a `float` attribute. */ inline constexpr parser_interface> float_; /** The `double` parser. Produces a `double` attribute. */ inline constexpr parser_interface> double_; /** Represents a sequence parser, the first parser of which is an `epsilon_parser` with predicate, as a directive (e.g. `if_(pred)[p]`). */ template struct if_directive { template constexpr auto operator[](parser_interface rhs) const noexcept { return eps(pred_) >> rhs; } Predicate pred_; }; /** Returns an `if_directive` that fails if the given predicate `pred` is `false`, and otherwise, applies another parser. For instance, in `if_(pred)[p]`, `p` is only applied if `pred` is true. */ template constexpr auto if_(Predicate pred) noexcept { return if_directive{pred}; } namespace detail { template struct switch_parser_equal { template bool operator()(Context & context) const { auto const switch_value = detail::resolve(context, switch_value_); auto const value = detail::resolve(context, value_); return value == switch_value; } SwitchValue switch_value_; Value value_; }; } #ifndef BOOST_PARSER_DOXYGEN template struct switch_parser { switch_parser() {} switch_parser(SwitchValue switch_value) : switch_value_(switch_value) {} switch_parser(SwitchValue switch_value, OrParser or_parser) : switch_value_(switch_value), or_parser_(or_parser) {} template< typename Iter, typename Sentinel, typename Context, typename SkipParser> auto call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success) const { BOOST_PARSER_ASSERT( (!detail::is_nope_v && "It looks like you tried to write switch_(val). You need at " "least one alternative, like: switch_(val)(value_1, " "parser_1)(value_2, parser_2)...")); using attr_t = decltype(or_parser_.call( first, last, context, skip, flags, success)); attr_t attr{}; [[maybe_unused]] auto _ = detail::scoped_trace(*this, first, last, context, flags, attr); attr = or_parser_.call(first, last, context, skip, flags, success); return attr; } template< typename Iter, typename Sentinel, typename Context, typename SkipParser, typename Attribute> void call( Iter & first, Sentinel last, Context const & context, SkipParser const & skip, detail::flags flags, bool & success, Attribute & retval) const { BOOST_PARSER_ASSERT( (!detail::is_nope_v && "It looks like you tried to write switch_(val). You need at " "least one alternative, like: switch_(val)(value_1, " "parser_1)(value_2, parser_2)...")); [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, context, flags, retval); or_parser_.call(first, last, context, skip, flags, success, retval); } /** Returns a `parser_interface` containing a `switch_parser`, with the case `value_`,`rhs` appended to its `or_parser_`. */ template constexpr auto operator()(Value value_, parser_interface rhs) const noexcept { auto const match = detail::switch_parser_equal{ switch_value_, value_}; auto or_parser = make_or_parser(or_parser_, eps(match) >> rhs); using switch_parser_type = switch_parser; return parser_interface{ switch_parser_type{switch_value_, or_parser}}; } #ifndef BOOST_PARSER_DOXYGEN template static constexpr auto make_or_parser(Parser1 parser1, parser_interface parser2) { return (parser_interface{parser1} | parser2).parser_; } template static constexpr auto make_or_parser(detail::nope, parser_interface parser) { return parser.parser_; } #endif SwitchValue switch_value_; OrParser or_parser_; }; #endif /** Returns a `switch`-like parser. The resulting parser uses the given value `x` to select one of the following value/parser pairs, and to apply the selected parser. `x` may be a value to be used directly, or a unary invocable that takes a reference to the parse context, and returns the value to use. You can add more value/parser cases to the returned parser, using its call operator, e.g. `switch_(x)(y1, p1)(y2, p2)`. As with the `x` passed to this function, each `yN` value can be a value or a unary invocable. */ template constexpr auto switch_(T x) noexcept { return switch_parser{x}; } #ifndef BOOST_PARSER_DOXYGEN template constexpr auto parser_interface::operator>>( char rhs) const noexcept { return *this >> parser::lit(rhs); } template constexpr auto parser_interface::operator>>( char32_t rhs) const noexcept { return *this >> parser::lit(rhs); } template #if BOOST_PARSER_USE_CONCEPTS template #else template #endif constexpr auto parser_interface::operator>>( R && r) const noexcept { return *this >> parser::lit(r); } #endif /** Returns a parser equivalent to `lit(c) >> rhs`. */ template constexpr auto operator>>(char c, parser_interface rhs) noexcept { if constexpr (detail::is_seq_p{}) { return rhs.parser_.template prepend(parser::lit(c)); } else { return parser::lit(c) >> rhs; } } /** Returns a parser equivalent to `lit(c) >> rhs`. */ template constexpr auto operator>>(char32_t c, parser_interface rhs) noexcept { if constexpr (detail::is_seq_p{}) { return rhs.parser_.template prepend(parser::lit(c)); } else { return parser::lit(c) >> rhs; } } /** Returns a parser equivalent to `lit(str) >> rhs`. */ #if BOOST_PARSER_USE_CONCEPTS template #else template< typename R, typename Parser, typename Enable = std::enable_if_t>> #endif constexpr auto operator>>(R && r, parser_interface rhs) noexcept { if constexpr (detail::is_seq_p{}) { return rhs.parser_.template prepend(parser::lit(r)); } else { return parser::lit(r) >> rhs; } } #ifndef BOOST_PARSER_DOXYGEN template constexpr auto parser_interface::operator>( char rhs) const noexcept { return *this > parser::lit(rhs); } template constexpr auto parser_interface::operator>( char32_t rhs) const noexcept { return *this > parser::lit(rhs); } template #if BOOST_PARSER_USE_CONCEPTS template #else template #endif constexpr auto parser_interface::operator>( R && r) const noexcept { return *this > parser::lit(r); } #endif /** Returns a parser equivalent to `lit(c) > rhs`. */ template constexpr auto operator>(char c, parser_interface rhs) noexcept { if constexpr (detail::is_seq_p{}) { return rhs.parser_.template prepend(parser::lit(c)); } else { return parser::lit(c) > rhs; } } /** Returns a parser equivalent to `lit(c) > rhs`. */ template constexpr auto operator>(char32_t c, parser_interface rhs) noexcept { if constexpr (detail::is_seq_p{}) { return rhs.parser_.template prepend(parser::lit(c)); } else { return parser::lit(c) > rhs; } } /** Returns a parser equivalent to `lit(str) > rhs`. */ #if BOOST_PARSER_USE_CONCEPTS template #else template< typename R, typename Parser, typename Enable = std::enable_if_t>> #endif constexpr auto operator>(R && r, parser_interface rhs) noexcept { if constexpr (detail::is_seq_p{}) { return rhs.parser_.template prepend(parser::lit(r)); } else { return parser::lit(r) > rhs; } } #ifndef BOOST_PARSER_DOXYGEN template constexpr auto parser_interface::operator|( char rhs) const noexcept { return *this | parser::lit(rhs); } template constexpr auto parser_interface::operator|( char32_t rhs) const noexcept { return *this | parser::lit(rhs); } template #if BOOST_PARSER_USE_CONCEPTS template #else template #endif constexpr auto parser_interface::operator|( R && r) const noexcept { return *this | parser::lit(r); } #endif /** Returns a parser equivalent to `lit(c) | rhs`. */ template constexpr auto operator|(char c, parser_interface rhs) noexcept { if constexpr (detail::is_or_p{}) { return rhs.parser_.prepend(parser::lit(c)); } else { return parser::lit(c) | rhs; } } /** Returns a parser equivalent to `lit(c) | rhs`. */ template constexpr auto operator|(char32_t c, parser_interface rhs) noexcept { if constexpr (detail::is_or_p{}) { return rhs.parser_.prepend(parser::lit(c)); } else { return parser::lit(c) | rhs; } } /** Returns a parser equivalent to `lit(str) | rhs`. */ #if BOOST_PARSER_USE_CONCEPTS template #else template< typename R, typename Parser, typename Enable = std::enable_if_t>> #endif constexpr auto operator|(R && r, parser_interface rhs) noexcept { if constexpr (detail::is_or_p{}) { return rhs.parser_.prepend(parser::lit(r)); } else { return parser::lit(r) | rhs; } } #ifndef BOOST_PARSER_DOXYGEN template constexpr auto parser_interface::operator-( char rhs) const noexcept { return !parser::lit(rhs) >> *this; } template constexpr auto parser_interface::operator-( char32_t rhs) const noexcept { return !parser::lit(rhs) >> *this; } template #if BOOST_PARSER_USE_CONCEPTS template #else template #endif constexpr auto parser_interface::operator-( R && r) const noexcept { return !parser::lit(r) >> *this; } #endif /** Returns a parser equivalent to `!rhs >> lit(c)`. */ template constexpr auto operator-(char c, parser_interface rhs) noexcept { return !rhs >> parser::lit(c); } /** Returns a parser equivalent to `!rhs >> lit(c)`. */ template constexpr auto operator-(char32_t c, parser_interface rhs) noexcept { return !rhs >> parser::lit(c); } /** Returns a parser equivalent to `!rhs >> lit(str)`. */ #if BOOST_PARSER_USE_CONCEPTS template #else template< typename R, typename Parser, typename Enable = std::enable_if_t>> #endif constexpr auto operator-(R && r, parser_interface rhs) noexcept { return !rhs >> parser::lit(r); } #ifndef BOOST_PARSER_DOXYGEN template constexpr auto parser_interface::operator%( char rhs) const noexcept { return *this % parser::lit(rhs); } template constexpr auto parser_interface::operator%( char32_t rhs) const noexcept { return *this % parser::lit(rhs); } template #if BOOST_PARSER_USE_CONCEPTS template #else template #endif constexpr auto parser_interface::operator%( R && r) const noexcept { return *this % parser::lit(r); } #endif /** Returns a parser equivalent to `lit(c) % rhs`. */ template constexpr auto operator%(char c, parser_interface rhs) noexcept { return parser::lit(c) % rhs; } /** Returns a parser equivalent to `lit(c) % rhs`. */ template constexpr auto operator%(char32_t c, parser_interface rhs) noexcept { return parser::lit(c) % rhs; } /** Returns a parser equivalent to `lit(str) % rhs`. */ #if BOOST_PARSER_USE_CONCEPTS template #else template< typename R, typename Parser, typename Enable = std::enable_if_t>> #endif constexpr auto operator%(R && r, parser_interface rhs) noexcept { return parser::lit(r) % rhs; } }} #include namespace boost { namespace parser { /** An enumeration used for parameters to enable and disable trace in the `*parse()` functions. */ enum class trace { off, on }; // Parse API. /** Parses `[first, last)` using `parser`, and returns whether the parse was successful. On success, `attr` will be assigned the value of the attribute produced by `parser`. If `trace_mode == trace::on`, a verbose trace of the parse will be streamed to `std::cout`. \tparam Attr Constrained by `!detail::derived_from_parser_interface_v`. */ #if BOOST_PARSER_USE_CONCEPTS template< parsable_iter I, std::sentinel_for S, typename Parser, typename GlobalState, error_handler ErrorHandler, typename Attr> #else template< typename I, typename S, typename Parser, typename GlobalState, typename ErrorHandler, typename Attr, typename Enable = std::enable_if_t< detail::is_parsable_iter_v && detail::is_equality_comparable_with_v && !detail::derived_from_parser_interface_v< detail::remove_cv_ref_t>>> #endif bool prefix_parse( I & first, S last, parser_interface const & parser, Attr & attr, trace trace_mode = trace::off) #if BOOST_PARSER_USE_CONCEPTS requires( !detail::derived_from_parser_interface_v>) #endif { detail::attr_reset reset(attr); if constexpr (!detail::is_char8_iter_v) { static_assert( decltype(detail::has_attribute(first, last, parser)){}, "If you're seeing this error, you're trying to get parse() to " "fill in attr above, using the attribute generated by parser. " "However, parser does not generate an attribute."); if (trace_mode == trace::on) { return reset = detail::parse_impl( first, last, parser, parser.error_handler_, attr); } else { return reset = detail::parse_impl( first, last, parser, parser.error_handler_, attr); } } else { auto r = BOOST_PARSER_SUBRANGE(first, last) | detail::text::as_utf32; auto f = r.begin(); auto const l = r.end(); auto _ = detail::scoped_base_assign(first, f); static_assert( decltype(detail::has_attribute(f, l, parser)){}, "If you're seeing this error, you're trying to get parse() to " "fill in attr above, using the attribute generated by parser. " "However, parser does not generate an attribute."); if (trace_mode == trace::on) { return reset = detail::parse_impl( f, l, parser, parser.error_handler_, attr); } else { return reset = detail::parse_impl( f, l, parser, parser.error_handler_, attr); } } } /** Parses `r` using `parser`, and returns whether the parse was successful. The entire input range `r` must be consumed for the parse to be considered successful. On success, `attr` will be assigned the value of the attribute produced by `parser`. If `trace_mode == trace::on`, a verbose trace of the parse will be streamed to `std::cout`. \tparam ErrorHandler Constrained by `error_handler, std::ranges::sentinel_t, GlobalState>`, where `subrange_of` is an implementation detail that: creates subranges out of pointers; trims trailing zeros off of bounded arrays (such as string literals); and transcodes to UTF-32 if the input is non-`char`. \tparam Attr Constrained by `!detail::derived_from_parser_interface_v`. */ #if BOOST_PARSER_USE_CONCEPTS template< parsable_range R, typename Parser, typename GlobalState, typename ErrorHandler, typename Attr> #else template< typename R, typename Parser, typename GlobalState, typename ErrorHandler, typename Attr, typename Enable = std::enable_if_t< detail::is_parsable_range_v && !detail::derived_from_parser_interface_v< detail::remove_cv_ref_t>>> #endif bool parse( R const & r, parser_interface const & parser, Attr & attr, trace trace_mode = trace::off) #if BOOST_PARSER_USE_CONCEPTS // clang-format off requires error_handler< ErrorHandler, std::ranges::iterator_t, std::ranges::sentinel_t, GlobalState> && (!detail::derived_from_parser_interface_v>) // clang-format on #endif { detail::attr_reset reset(attr); auto r_ = detail::make_input_subrange(r); auto first = r_.begin(); auto const last = r_.end(); auto const initial_first = first; return reset = detail::if_full_parse( initial_first, first, last, parser.error_handler_, parser::prefix_parse(first, last, parser, attr, trace_mode)); } /** Parses `[first, last)` using `parser`. Returns a `std::optional` containing the attribute produced by `parser` on parse success, and `std::nullopt` on parse failure. If `trace_mode == trace::on`, a verbose trace of the parse will be streamed to `std::cout`. \tparam Attr Constrained by `!detail::derived_from_parser_interface_v`. */ #if BOOST_PARSER_USE_CONCEPTS template< parsable_iter I, std::sentinel_for S, typename Parser, typename GlobalState, error_handler ErrorHandler> #else template< typename I, typename S, typename Parser, typename GlobalState, typename ErrorHandler, typename Enable = std::enable_if_t< detail::is_parsable_iter_v && detail::is_equality_comparable_with_v>> #endif auto prefix_parse( I & first, S last, parser_interface const & parser, trace trace_mode = trace::off) { if constexpr (!detail::is_char8_iter_v) { if (trace_mode == trace::on) { return detail::parse_impl( first, last, parser, parser.error_handler_); } else { return detail::parse_impl( first, last, parser, parser.error_handler_); } } else { auto r = BOOST_PARSER_SUBRANGE(first, last) | detail::text::as_utf32; auto f = r.begin(); auto const l = r.end(); auto _ = detail::scoped_base_assign(first, f); if (trace_mode == trace::on) { return detail::parse_impl( f, l, parser, parser.error_handler_); } else { return detail::parse_impl( f, l, parser, parser.error_handler_); } } } /** Parses `r` using `parser`. Returns a `std::optional` containing the attribute produced by `parser` on parse success, and `std::nullopt` on parse failure. The entire input range `r` must be consumed for the parse to be considered successful. If `trace_mode == trace::on`, a verbose trace of the parse will be streamed to `std::cout`. \tparam ErrorHandler Constrained by `error_handler, std::ranges::sentinel_t, GlobalState>`, where `subrange_of` is an implementation detail that: creates subranges out of pointers; trims trailing zeros off of bounded arrays (such as string literals); and transcodes to UTF-32 if the input is non-`char`. \tparam Attr Constrained by `!detail::derived_from_parser_interface_v`. */ #if BOOST_PARSER_USE_CONCEPTS template< parsable_range R, typename Parser, typename GlobalState, typename ErrorHandler> #else template< typename R, typename Parser, typename GlobalState, typename ErrorHandler, typename Enable = std::enable_if_t>> #endif auto parse( R const & r, parser_interface const & parser, trace trace_mode = trace::off) #if BOOST_PARSER_USE_CONCEPTS // clang-format off requires error_handler< ErrorHandler, std::ranges::iterator_t, std::ranges::sentinel_t, GlobalState> // clang-format on #endif { auto r_ = detail::make_input_subrange(r); auto first = r_.begin(); auto const last = r_.end(); auto const initial_first = first; return detail::if_full_parse( initial_first, first, last, parser.error_handler_, parser::prefix_parse(first, last, parser, trace_mode)); } /** Parses `[first, last)` using `parser`, skipping all input recognized by `skip` between the application of any two parsers, and returns whether the parse was successful. On success, `attr` will be assigned the value of the attribute produced by `parser`. If `trace_mode == trace::on`, a verbose trace of the parse will be streamed to `std::cout`. */ #if BOOST_PARSER_USE_CONCEPTS template< parsable_iter I, std::sentinel_for S, typename Parser, typename GlobalState, error_handler ErrorHandler, typename SkipParser, typename Attr> #else template< typename I, typename S, typename Parser, typename GlobalState, typename ErrorHandler, typename SkipParser, typename Attr, typename Enable = std::enable_if_t< detail::is_parsable_iter_v && detail::is_equality_comparable_with_v>> #endif bool prefix_parse( I & first, S last, parser_interface const & parser, parser_interface const & skip, Attr & attr, trace trace_mode = trace::off) { detail::attr_reset reset(attr); if constexpr (!detail::is_char8_iter_v) { static_assert( decltype(detail::has_attribute(first, last, parser)){}, "If you're seeing this error, you're trying to get parse() to " "fill in attr above, using the attribute generated by parser. " "However, parser does not generate an attribute."); if (trace_mode == trace::on) { return reset = detail::skip_parse_impl( first, last, parser, skip, parser.error_handler_, attr); } else { return reset = detail::skip_parse_impl( first, last, parser, skip, parser.error_handler_, attr); } } else { auto r = BOOST_PARSER_SUBRANGE(first, last) | detail::text::as_utf32; auto f = r.begin(); auto const l = r.end(); auto _ = detail::scoped_base_assign(first, f); static_assert( decltype(detail::has_attribute(f, l, parser)){}, "If you're seeing this error, you're trying to get parse() to " "fill in attr above, using the attribute generated by parser. " "However, parser does not generate an attribute."); if (trace_mode == trace::on) { return reset = detail::skip_parse_impl( f, l, parser, skip, parser.error_handler_, attr); } else { return reset = detail::skip_parse_impl( f, l, parser, skip, parser.error_handler_, attr); } } } /** Parses `r` using `parser`, skipping all input recognized by `skip` between the application of any two parsers, and returns whether the parse was successful. The entire input range `r` must be consumed for the parse to be considered successful. On success, `attr` will be assigned the value of the attribute produced by `parser`. If `trace_mode == trace::on`, a verbose trace of the parse will be streamed to `std::cout`. \tparam ErrorHandler Constrained by `error_handler, std::ranges::sentinel_t, GlobalState>`, where `subrange_of` is an implementation detail that: creates subranges out of pointers; trims trailing zeros off of bounded arrays (such as string literals); and transcodes to UTF-32 if the input is non-`char`. */ #if BOOST_PARSER_USE_CONCEPTS template< parsable_range R, typename Parser, typename GlobalState, typename ErrorHandler, typename SkipParser, typename Attr> #else template< typename R, typename Parser, typename GlobalState, typename ErrorHandler, typename SkipParser, typename Attr, typename Enable = std::enable_if_t>> #endif bool parse( R const & r, parser_interface const & parser, parser_interface const & skip, Attr & attr, trace trace_mode = trace::off) #if BOOST_PARSER_USE_CONCEPTS // clang-format off requires error_handler< ErrorHandler, std::ranges::iterator_t, std::ranges::sentinel_t, GlobalState> // clang-format on #endif { detail::attr_reset reset(attr); auto r_ = detail::make_input_subrange(r); auto first = r_.begin(); auto const last = r_.end(); auto const initial_first = first; return reset = detail::if_full_parse( initial_first, first, last, parser.error_handler_, parser::prefix_parse( first, last, parser, skip, attr, trace_mode)); } /** Parses `[first, last)` using `parser`, skipping all input recognized by `skip` between the application of any two parsers. Returns a `std::optional` containing the attribute produced by `parser` on parse success, and `std::nullopt` on parse failure. If `trace_mode == trace::on`, a verbose trace of the parse will be streamed to `std::cout`. */ #if BOOST_PARSER_USE_CONCEPTS template< parsable_iter I, std::sentinel_for S, typename Parser, typename GlobalState, error_handler ErrorHandler, typename SkipParser> #else template< typename I, typename S, typename Parser, typename GlobalState, typename ErrorHandler, typename SkipParser, typename Enable = std::enable_if_t< detail::is_parsable_iter_v && detail::is_equality_comparable_with_v>> #endif auto prefix_parse( I & first, S last, parser_interface const & parser, parser_interface const & skip, trace trace_mode = trace::off) { if constexpr (!detail::is_char8_iter_v) { if (trace_mode == trace::on) { return detail::skip_parse_impl( first, last, parser, skip, parser.error_handler_); } else { return detail::skip_parse_impl( first, last, parser, skip, parser.error_handler_); } } else { auto r = BOOST_PARSER_SUBRANGE(first, last) | detail::text::as_utf32; auto f = r.begin(); auto const l = r.end(); auto _ = detail::scoped_base_assign(first, f); if (trace_mode == trace::on) { return detail::skip_parse_impl( f, l, parser, skip, parser.error_handler_); } else { return detail::skip_parse_impl( f, l, parser, skip, parser.error_handler_); } } } /** Parses `r` using `parser`, skipping all input recognized by `skip` between the application of any two parsers. Returns a `std::optional` containing the attribute produced by `parser` on parse success, and `std::nullopt` on parse failure. The entire input range `r` must be consumed for the parse to be considered successful. If `trace_mode == trace::on`, a verbose trace of the parse will be streamed to `std::cout`. \tparam ErrorHandler Constrained by `error_handler, std::ranges::sentinel_t, GlobalState>`, where `subrange_of` is an implementation detail that: creates subranges out of pointers; trims trailing zeros off of bounded arrays (such as string literals); and transcodes to UTF-32 if the input is non-`char`. */ #if BOOST_PARSER_USE_CONCEPTS template< parsable_range R, typename Parser, typename GlobalState, typename ErrorHandler, typename SkipParser> #else template< typename R, typename Parser, typename GlobalState, typename ErrorHandler, typename SkipParser, typename Enable = std::enable_if_t>> #endif auto parse( R const & r, parser_interface const & parser, parser_interface const & skip, trace trace_mode = trace::off) #if BOOST_PARSER_USE_CONCEPTS // clang-format off requires error_handler< ErrorHandler, std::ranges::iterator_t, std::ranges::sentinel_t, GlobalState> // clang-format on #endif { auto r_ = detail::make_input_subrange(r); auto first = r_.begin(); auto const last = r_.end(); auto const initial_first = first; return detail::if_full_parse( initial_first, first, last, parser.error_handler_, parser::prefix_parse(first, last, parser, skip, trace_mode)); } /** Parses `[first, last)` using `parser`, and returns whether the parse was successful. When a callback rule `r` is successful during the parse, one of two things happens: 1) if `r` has an attribute, `callbacks(tag, x)` will be called (where `tag` is `decltype(r)::tag_type{}`, and `x` is the attribute produced by `r`); or 2) if `r` has no attribute, `callbacks(tag)` will be called. `Callbacks` is expected to be an invocable with the correct overloads required to support all successful rule parses that might occur. If `trace_mode == trace::on`, a verbose trace of the parse will be streamed to `std::cout`. */ #if BOOST_PARSER_USE_CONCEPTS template< parsable_iter I, std::sentinel_for S, typename Parser, typename GlobalState, error_handler ErrorHandler, typename Callbacks> #else template< typename I, typename S, typename Parser, typename GlobalState, typename ErrorHandler, typename Callbacks, typename Enable = std::enable_if_t< detail::is_parsable_iter_v && detail::is_equality_comparable_with_v>> #endif bool callback_prefix_parse( I & first, S last, parser_interface const & parser, Callbacks const & callbacks, trace trace_mode = trace::off) { if constexpr (!detail::is_char8_iter_v) { if (trace_mode == trace::on) { return detail::callback_parse_impl( first, last, parser, parser.error_handler_, callbacks); } else { return detail::callback_parse_impl( first, last, parser, parser.error_handler_, callbacks); } } else { auto r = BOOST_PARSER_SUBRANGE(first, last) | detail::text::as_utf32; auto f = r.begin(); auto const l = r.end(); auto _ = detail::scoped_base_assign(first, f); if (trace_mode == trace::on) { return detail::callback_parse_impl( f, l, parser, parser.error_handler_, callbacks); } else { return detail::callback_parse_impl( f, l, parser, parser.error_handler_, callbacks); } } } /** Parses `r` using `parser`, and returns whether the parse was successful. The entire input range `r` must be consumed for the parse to be considered successful. When a callback rule `r` is successful during the parse, one of two things happens: 1) if `r` has an attribute, `callbacks(tag, x)` will be called (where `tag` is `decltype(r)::tag_type{}`, and `x` is the attribute produced by `r`); or 2) if `r` has no attribute, `callbacks(tag)` will be called. `Callbacks` is expected to be an invocable with the correct overloads required to support all successful rule parses that might occur. If `trace_mode == trace::on`, a verbose trace of the parse will be streamed to `std::cout`. \tparam ErrorHandler Constrained by `error_handler, std::ranges::sentinel_t, GlobalState>`, where `subrange_of` is an implementation detail that: creates subranges out of pointers; trims trailing zeros off of bounded arrays (such as string literals); and transcodes to UTF-32 if the input is non-`char`. */ #if BOOST_PARSER_USE_CONCEPTS template< parsable_range R, typename Parser, typename GlobalState, typename ErrorHandler, typename Callbacks> #else template< typename R, typename Parser, typename GlobalState, typename ErrorHandler, typename Callbacks, typename Enable = std::enable_if_t>> #endif bool callback_parse( R const & r, parser_interface const & parser, Callbacks const & callbacks, trace trace_mode = trace::off) #if BOOST_PARSER_USE_CONCEPTS // clang-format off requires error_handler< ErrorHandler, std::ranges::iterator_t, std::ranges::sentinel_t, GlobalState> // clang-format on #endif { auto r_ = detail::make_input_subrange(r); auto first = r_.begin(); auto const last = r_.end(); auto const initial_first = first; return detail::if_full_parse( initial_first, first, last, parser.error_handler_, parser::callback_prefix_parse(first, last, parser, callbacks)); } /** Parses `[first, last)` using `parser`, skipping all input recognized by `skip` between the application of any two parsers, and returns whether the parse was successful. When a callback rule `r` is successful during the parse, one of two things happens: 1) if `r` has an attribute, `callbacks(tag, x)` will be called (where `tag` is `decltype(r)::tag_type{}`, and `x` is the attribute produced by `r`); or 2) if `r` has no attribute, `callbacks(tag)` will be called. `Callbacks` is expected to be an invocable with the correct overloads required to support all successful rule parses that might occur. If `trace_mode == trace::on`, a verbose trace of the parse will be streamed to `std::cout`. */ #if BOOST_PARSER_USE_CONCEPTS template< parsable_iter I, std::sentinel_for S, typename Parser, typename GlobalState, error_handler ErrorHandler, typename SkipParser, typename Callbacks> #else template< typename I, typename S, typename Parser, typename GlobalState, typename ErrorHandler, typename SkipParser, typename Callbacks, typename Enable = std::enable_if_t< detail::is_parsable_iter_v && detail::is_equality_comparable_with_v>> #endif bool callback_prefix_parse( I & first, S last, parser_interface const & parser, parser_interface const & skip, Callbacks const & callbacks, trace trace_mode = trace::off) { if constexpr (!detail::is_char8_iter_v) { if (trace_mode == trace::on) { return detail::callback_skip_parse_impl( first, last, parser, skip, parser.error_handler_, callbacks); } else { return detail::callback_skip_parse_impl( first, last, parser, skip, parser.error_handler_, callbacks); } } else { auto r = BOOST_PARSER_SUBRANGE(first, last) | detail::text::as_utf32; auto f = r.begin(); auto const l = r.end(); auto _ = detail::scoped_base_assign(first, f); if (trace_mode == trace::on) { return detail::callback_skip_parse_impl( f, l, parser, skip, parser.error_handler_, callbacks); } else { return detail::callback_skip_parse_impl( f, l, parser, skip, parser.error_handler_, callbacks); } } } /** Parses `r` using `parser`, skipping all input recognized by `skip` between the application of any two parsers, and returns whether the parse was successful. The entire input range `r` must be consumed for the parse to be considered successful. When a callback rule `r` is successful during the parse, one of two things happens: 1) if `r` has an attribute, `callbacks(tag, x)` will be called (where `tag` is `decltype(r)::tag_type{}`, and `x` is the attribute produced by `r`); or 2) if `r` has no attribute, `callbacks(tag)` will be called. `Callbacks` is expected to be an invocable with the correct overloads required to support all successful rule parses that might occur. If `trace_mode == trace::on`, a verbose trace of the parse will be streamed to `std::cout`. \tparam ErrorHandler Constrained by `error_handler, std::ranges::sentinel_t, GlobalState>`, where `subrange_of` is an implementation detail that: creates subranges out of pointers; trims trailing zeros off of bounded arrays (such as string literals); and transcodes to UTF-32 if the input is non-`char`. */ #if BOOST_PARSER_USE_CONCEPTS template< parsable_range R, typename Parser, typename GlobalState, typename ErrorHandler, typename SkipParser, typename Callbacks> #else template< typename R, typename Parser, typename GlobalState, typename ErrorHandler, typename SkipParser, typename Callbacks, typename Enable = std::enable_if_t>> #endif bool callback_parse( R const & r, parser_interface const & parser, parser_interface const & skip, Callbacks const & callbacks, trace trace_mode = trace::off) #if BOOST_PARSER_USE_CONCEPTS // clang-format off requires error_handler< ErrorHandler, std::ranges::iterator_t, std::ranges::sentinel_t, GlobalState> // clang-format on #endif { auto r_ = detail::make_input_subrange(r); auto first = r_.begin(); auto const last = r_.end(); auto const initial_first = first; return detail::if_full_parse( initial_first, first, last, parser.error_handler_, parser::callback_prefix_parse( first, last, parser, skip, callbacks, trace_mode)); } namespace literals { /** Returns a literal parser equivalent to `lit(c)`. */ constexpr auto operator""_l(char c) { return parser::lit(c); } #if defined(__cpp_char8_t) || defined(BOOST_PARSER_DOXYGEN) /** Returns a literal parser equivalent to `lit(c)`. */ constexpr auto operator""_l(char8_t c) { return parser::lit(c); } #endif /** Returns a literal parser equivalent to `lit(c)`. */ constexpr auto operator""_l(char32_t c) { return parser::lit(c); } /** Returns a literal parser equivalent to `lit(str)`. */ constexpr auto operator""_l(char const * str, std::size_t) { return parser::lit(str); } #if defined(__cpp_char8_t) || defined(BOOST_PARSER_DOXYGEN) /** Returns a literal parser equivalent to `lit(str)`. */ constexpr auto operator""_l(char8_t const * str, std::size_t) { return parser::lit(str); } #endif /** Returns a literal parser equivalent to `lit(str)`. */ constexpr auto operator""_l(char32_t const * str, std::size_t) { return parser::lit(str); } /** Returns a character parser equivalent to `char_(c)`. */ constexpr auto operator""_p(char c) { return char_(c); } #if defined(__cpp_char8_t) || defined(BOOST_PARSER_DOXYGEN) /** Returns a character parser equivalent to `char_(c)`. */ constexpr auto operator""_p(char8_t c) { return char_(c); } #endif /** Returns a character parser equivalent to `char_(c)`. */ constexpr auto operator""_p(char32_t c) { return char_(c); } /** Returns a string parser equivalent to `string(str)`. */ constexpr auto operator""_p(char const * str, std::size_t) { return parser::string(str); } #if defined(__cpp_char8_t) || defined(BOOST_PARSER_DOXYGEN) /** Returns a string parser equivalent to `string(str)`. */ constexpr auto operator""_p(char8_t const * str, std::size_t) { return parser::string(str); } #endif /** Returns a string parser equivalent to `string(str)`. */ constexpr auto operator""_p(char32_t const * str, std::size_t) { return parser::string(str); } } namespace detail { template struct attribute_impl { using parser_type = typename Parser::parser_type; using global_state_type = typename Parser::global_state_type; using error_handler_type = typename Parser::error_handler_type; using iterator = detail::iterator_t; using sentinel = detail::sentinel_t; using context = decltype(detail::make_context( std::declval(), std::declval(), std::declval(), std::declval(), std::declval(), std::declval(), std::declval(), std::declval())); using type = decltype(std::declval()( std::declval(), std::declval(), std::declval(), SkipParser{}, detail::flags::gen_attrs, std::declval())); }; template auto has_attribute(Iter first, Sentinel last, Parser parser) { using attr_t = typename attribute_impl< BOOST_PARSER_SUBRANGE, Parser>::type; return std::integral_constant>{}; } template constexpr wrapper attr_wrapped_final; template<> inline constexpr wrapper attr_wrapped_final; } template struct attribute { using initial_type = typename detail::attribute_impl< decltype(detail::make_input_subrange(std::declval())), Parser>::type; using type = typename decltype(detail::attr_wrapped_final)::type; }; namespace detail { template constexpr void static_assert_merge_attributes(tuple parsers) { using context_t = parse_context< false, false, char const *, char const *, default_error_handler>; using skipper_t = parser_interface>; using use_parser_t = dummy_use_parser_t< char const *, char const *, context_t, skipper_t> const; using all_types = decltype(hl::transform(parsers, std::declval())); auto all_types_wrapped = hl::transform(all_types{}, detail::wrap{}); auto first_non_nope = hl::fold_left( all_types_wrapped, wrapper{}, [=](auto result, auto type) { if constexpr (is_nope_v) { return type; } else { return result; } }); using first_t = typename decltype(first_non_nope)::type; static_assert( !is_nope_v, "It looks like you wrote merge[p1 >> p2 >> ... pn], and none " "of the parsers p1, p2, ... pn produces an attribute. Please " "fix."); if constexpr (is_nope_v) { [[maybe_unused]] detail::print_type> tuple_types; [[maybe_unused]] detail::print_type attribute_types; } hl::for_each(all_types_wrapped, [=](auto type) { using t = typename decltype(type)::type; if constexpr (!is_nope_v) { static_assert( std::is_same_v, "If you see an error here, you wrote merge[p1 >> " "p2 >> ... pn] where at least one of the types in " "ATTR(p1), ATTR(p2), ... ATTR(pn) is not the same " "type as one of the others."); if constexpr (!std::is_same_v) { [[maybe_unused]] detail::print_type> tuple_types(parsers); [[maybe_unused]] detail::print_type attribute_types; [[maybe_unused]] detail::print_type first_type; [[maybe_unused]] detail::print_type this_type; } } }); } } }} #endif