#pragma once #include // IWYU pragma: keep #include #include #include #include #include #include #include #include #include #include #include "detail/InternalConcepts.hpp" #include "detail/StringLiteral.hpp" namespace ovdl::dsl { template constexpr auto callback(Callback... cb) { return lexy::bind(lexy::callback(cb...), lexy::parse_state, lexy::values); } template constexpr auto bind_sink(Sink sink) { return lexy::bind_sink(sink, lexy::parse_state); } template struct _sink_with_state { using return_type = ReturnT; LEXY_EMPTY_MEMBER Sink _sink_cb; template struct _sink_callback { StateType& _state; SinkCallback _sink_cb; using return_type = decltype(LEXY_MOV(_sink_cb).finish()); template constexpr void operator()(Args&&... args) { lexy::_detail::invoke(_sink_cb, _state, LEXY_FWD(args)...); } constexpr return_type finish() && { return LEXY_MOV(_sink_cb).finish(); } }; template constexpr auto operator()(detail::IsStateType auto& state, Args... args) const -> decltype(_sink_cb(state, LEXY_FWD(args)...)) { return _sink_cb(state, LEXY_FWD(args)...); } constexpr auto sink(detail::IsStateType auto& state) const { return _sink_callback, decltype(_sink_cb.sink())> { state, _sink_cb.sink() }; } }; template constexpr auto sink(Sink&& sink) { return bind_sink(_sink_with_state { LEXY_FWD(sink) }); } template constexpr auto collect(Callback callback) { return sink(lexy::collect(callback)); } template constexpr auto collect(Callback callback) { return sink(lexy::collect(callback)); } template constexpr auto construct = callback( [](detail::IsParseState auto& state, ovdl::NodeLocation loc, auto&& arg) { if constexpr (std::same_as, lexy::nullopt>) return state.ast().template create(loc); else return state.ast().template create(loc, DRYAD_FWD(arg)); }, [](detail::IsParseState auto& state, ovdl::NodeLocation loc, auto&&... args) { return state.ast().template create(loc, DRYAD_FWD(args)...); }); template constexpr auto construct_list = callback( [](detail::IsParseState auto& state, const char* begin, ListType&& arg, const char* end) { return state.ast().template create(NodeLocation::make_from(begin, end), DRYAD_FWD(arg)); }, [](detail::IsParseState auto& state, const char* begin, lexy::nullopt, const char* end) { return state.ast().template create(NodeLocation::make_from(begin, end)); }, [](detail::IsParseState auto& state, const char* begin, const char* end) { return state.ast().template create(NodeLocation::make_from(begin, end)); }, [](detail::IsParseState auto& state) { return nullptr; }); template constexpr auto construct_list = callback( [](detail::IsParseState auto& state, const char* begin, ListType&& arg, const char* end) { return state.ast().template create(NodeLocation::make_from(begin, end), DRYAD_FWD(arg)); }, [](detail::IsParseState auto& state, const char* begin, lexy::nullopt, const char* end) { return state.ast().template create(NodeLocation::make_from(begin, end)); }); template struct _crange : lexyd::char_class_base<_crange> { static_assert(LowC >= 0, "LowC cannot be less than 0"); static_assert(HighC - LowC > 0, "LowC must be less than HighC"); static constexpr auto char_class_unicode() { return LowC <= 0x7F && HighC <= 0x7F; } static LEXY_CONSTEVAL auto char_class_name() { return "range"; } static LEXY_CONSTEVAL auto char_class_ascii() { lexy::_detail::ascii_set result; if constexpr (LowC <= 0x7F && HighC <= 0x7F) for (auto c = LowC; c <= HighC; c++) result.insert(c); return result; } static constexpr auto char_class_match_cp([[maybe_unused]] char32_t cp) { if constexpr (LowC <= 0x7F && HighC <= 0x7F) return std::false_type {}; else return LowC <= cp && cp <= HighC; } }; template constexpr auto lit_c_range = _crange {}; template constexpr auto lit_b_range = _crange {}; template constexpr auto position_brackets = lexy::dsl::brackets(lexy::dsl::position(lexy::dsl::lit_c), lexy::dsl::position(lexy::dsl::lit_c)); constexpr auto round_bracketed = position_brackets<'(', ')'>; constexpr auto square_bracketed = position_brackets<'[', ']'>; constexpr auto curly_bracketed = position_brackets<'{', '}'>; constexpr auto angle_bracketed = position_brackets<'<', '>'>; template constexpr auto p = lexy::dsl::position(lexy::dsl::p); template static constexpr auto default_kw_value = dsl::callback( [](detail::IsParseState auto& state, NodeLocation loc) { return state.ast().template create(loc, state.ast().intern(Keyword.data(), Keyword.size())); }); template< auto Identifier, typename RuleValue, ovdl::detail::string_literal Keyword, auto Production, auto Value> struct keyword_rule { struct rule_t { static constexpr auto keyword = ovdl::dsl::keyword(Identifier); static constexpr auto rule = lexy::dsl::position(keyword) >> lexy::dsl::equal_sign; static constexpr auto value = Value; }; static constexpr auto rule = dsl::p >> Production; static constexpr auto value = construct; }; template< auto Identifier, typename RuleValue, ovdl::detail::string_literal Keyword, auto Production, auto Value> struct fkeyword_rule : keyword_rule { using base_type = keyword_rule; struct context_t; struct rule_t : base_type::rule_t { static constexpr auto flag = lexy::dsl::context_flag; struct too_many_error { static constexpr auto name = "expected event " + Keyword + " to only be found once"; }; static constexpr auto must = lexy::dsl::must(rule_t::flag.is_reset()) . // See https://stackoverflow.com/questions/77144003/use-of-template-keyword-before-dependent-template-name // THANKS FOR NOTHING MICROSOFT, CAN'T EVEN GET THE STANDARD RIGHT #if !defined(_MSC_VER) || defined(__clang__) template #endif error; }; static constexpr auto make_flag = rule_t::flag.create(); static constexpr auto rule = dsl::p >> (rule_t::must >> rule_t::flag.set()) >> Production; static constexpr auto value = construct; }; template struct rule_helper { static constexpr auto flags = (Args::make_flag + ...); static constexpr auto p = (lexy::dsl::p | ...); }; template struct _peek : lexyd::branch_base { template struct bp { typename Reader::iterator begin; typename Reader::marker end; constexpr bool try_parse(const void*, Reader reader) { using encoding = typename Reader::encoding; auto parser = [&] { if constexpr (std::same_as || std::same_as) { // We need to match the entire rule. return lexy::token_parser_for { reader }; } else { // We need to match the entire rule. return lexy::token_parser_for { reader }; } }(); begin = reader.position(); auto result = parser.try_parse(reader); end = parser.end; return result; } template constexpr void cancel(Context& context) { context.on(lexyd::_ev::backtracked {}, begin, end.position()); } template LEXY_PARSER_FUNC bool finish(Context& context, Reader& reader, Args&&... args) { context.on(lexyd::_ev::backtracked {}, begin, end.position()); return NextParser::parse(context, reader, LEXY_FWD(args)...); } }; template struct p { template LEXY_PARSER_FUNC static bool parse(Context& context, Reader& reader, Args&&... args) { bp impl {}; if (!impl.try_parse(context.control_block, reader)) { // Report that we've failed. using tag = lexy::_detail::type_or; auto err = lexy::error(impl.begin, impl.end.position()); context.on(lexyd::_ev::error {}, err); // But recover immediately, as we wouldn't have consumed anything either way. } context.on(lexyd::_ev::backtracked {}, impl.begin, impl.end); return NextParser::parse(context, reader, LEXY_FWD(args)...); } }; template static constexpr _peek error = {}; }; template constexpr auto peek(Rule, RuleUtf) { return _peek {}; } }