/** This file is a part of rexy's general purpose library Copyright (C) 2022 rexy712 This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef REXY_DETAIL_FORMAT_PARSE_TPP #define REXY_DETAIL_FORMAT_PARSE_TPP #include "parse.hpp" #include "format_error.hpp" #include "../../string_view.hpp" #include //size_t #include //is_integral, remove_cvref, add_lvalue_reference, remove_reference #include //min namespace rexy::fmt::detail::parse{ template constexpr auto find_first_of_it(Char v, typename basic_string_view::const_iterator start_it, typename basic_string_view::const_iterator last_it) { const basic_string_view view{start_it, last_it}; const std::size_t range_size = last_it - start_it; return start_it + std::min(view.find_first_of(v), range_size); } template constexpr auto find_first_of_it(const basic_string_view& str, Char v){ return find_first_of_it(v, str.begin(), str.end()); } template constexpr bool check_duplicate_char(It start, It end){ if(start == end){ return false; } const auto next = start + 1; return *start == *next; } template constexpr bool is_a_number(Char c){ return (c >= '0' && c <= '9'); } template constexpr bool is_valid_name_char(Char c){ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '_'); } template constexpr int to_integer(It start, It end){ int retval = 0; std::size_t mul = 1; for(auto it = end;it != start;--it){ const auto cur = it-1; retval += ((*cur - '0') * mul); mul *= 10; } return retval; } template constexpr long long dynamic_integer_retriever::operator()(T&& t){ if constexpr(std::is_integral_v>){ return static_cast(t); }else{ REXY_THROW_FORMAT_ERROR("Invalid dynamic specifier"); } return {}; } template constexpr decltype(auto) dynamic_width_adapter::operator()(void){ return specs.on_dynamic_width(); } template constexpr decltype(auto) dynamic_width_adapter::operator()(int i){ return specs.on_dynamic_width(i); } template template constexpr decltype(auto) dynamic_width_adapter::operator()(It start, It last){ return specs.on_dynamic_width(start, last); } template constexpr decltype(auto) dynamic_precision_adapter::operator()(void){ return specs.on_dynamic_precision(); } template constexpr decltype(auto) dynamic_precision_adapter::operator()(int i){ return specs.on_dynamic_precision(i); } template template constexpr decltype(auto) dynamic_precision_adapter::operator()(It start, It last){ return specs.on_dynamic_precision(start, last); } template constexpr decltype(auto) dynamic_index_adapter::operator()(void){ return (id = handler.on_arg_id()); } template constexpr decltype(auto) dynamic_index_adapter::operator()(int i){ id = i; return handler.on_arg_id(i); } template template constexpr decltype(auto) dynamic_index_adapter::operator()(It start, It last){ id = handler.on_arg_id(start, last); return id; } template constexpr It perform_standard_fill_align_option(It start, It last, FormatSpecs&& specs){ char fill_val = ' '; It it = start; for(int i = 0;i < 2 && it != last;++i){ if(*it == '>' || *it == '<' || *it == '^'){ specs.on_align(*it, fill_val); return ++it; } fill_val = *it; ++it; } return start; } template constexpr It perform_standard_sign_option(It start, It last, FormatSpecs&& specs){ if(*start == '+' || *start == '-' || *start == ' '){ specs.on_sign(*start); return ++start; } return start; } template constexpr It perform_standard_alt_form_option(It start, It last, FormatSpecs&& specs){ if(*start == '#'){ specs.on_alt_form(); ++start; } return start; } template constexpr It perform_standard_zero_fill_option(It start, It last, FormatSpecs&& specs){ if(*start == '0'){ specs.on_zero_fill(); ++start; } return start; } template constexpr It perform_index_retrieve(It start, It last, Adapter&& func){ if(*start == '0'){ func(0); return start+1; } if(*start == '}' || *start == ':'){ func(); return start; } if(!is_a_number(*start)){ auto it = start; for(;it != last;++it){ if(!is_valid_name_char(*it)){ func(start, it); return it; } } return last; } for(auto it = start+1;it != last;++it){ if(!is_a_number(*it)){ func(to_integer(start, it)); return it; } } return last; } template constexpr It perform_standard_nested_replacement_field(It start, It last, Adapter&& func){ auto it = perform_index_retrieve(start, last, func); if(*it != '}'){ REXY_THROW_FORMAT_ERROR("Dynamic index invalid spec"); } return it+1; } template constexpr It perform_standard_width_option(It start, It last, FormatSpecs&& specs){ using specs_t = std::add_lvalue_reference_t>; if(*start == '{'){ return perform_standard_nested_replacement_field(start+1, last, dynamic_width_adapter{specs}); } if(*start == '0'){ REXY_THROW_FORMAT_ERROR("Field invalid spec"); } It it = start; for(;is_a_number(*it) && it != last;++it){} if(it == start){ return start; } specs.on_width(to_integer(start, it)); return it; } template constexpr It perform_standard_precision_option(It start, It last, FormatSpecs&& specs){ using specs_t = std::add_lvalue_reference_t>; if(*start != '.'){ return start; } ++start; if(*start == '{'){ return perform_standard_nested_replacement_field(start+1, last, dynamic_precision_adapter{specs}); } if(*start == '0'){ REXY_THROW_FORMAT_ERROR("Field invalid spec"); } It it = start; for(;is_a_number(*it) && it != last;++it){} if(it == start){ REXY_THROW_FORMAT_ERROR("Field invalid spec"); } specs.on_precision(to_integer(start, it)); return it; } template constexpr It perform_standard_locale_option(It start, It last, FormatSpecs&& specs){ if(*start == 'L'){ specs.on_locale(); return ++start; } return start; } template constexpr It perform_standard_type_option(It start, It last, FormatSpecs&& specs){ if(*start == '}'){ specs.on_type_option(); return start; } specs.on_type_option(*start); return ++start; } template constexpr It perform_standard_parse(It start, It last, FormatSpecs&& specs){ using specs_t = std::add_lvalue_reference_t>; using std_fmt_fn = It(*)(It,It,specs_t); constexpr std_fmt_fn fns[] = { perform_standard_fill_align_option, perform_standard_sign_option, perform_standard_alt_form_option, perform_standard_zero_fill_option, perform_standard_width_option, perform_standard_precision_option, perform_standard_locale_option, perform_standard_type_option }; constexpr int num_std_fns = sizeof(fns) / sizeof(fns[0]); for(int i = 0;i < num_std_fns;++i){ start = fns[i](start, last, specs); if(start == last){ break; } } if(*start != '}'){ REXY_THROW_FORMAT_ERROR("Missing closing brace for format field"); } return start; } template constexpr It perform_format_index_spec(Handler&& handler, It start, It last, std::size_t& id){ dynamic_index_adapter adapter{handler, id}; const auto it = perform_index_retrieve(start, last, adapter); if(*it != '}' && *it != ':'){ REXY_THROW_FORMAT_ERROR("Invalid index spec"); } return it; } template constexpr It perform_format_field_parse(Handler&& handler, It start, It last){ if(start == last){ REXY_THROW_FORMAT_ERROR("Unmatched brace"); } std::size_t index = 0; start = perform_format_index_spec(handler, start, last, index); if(*start == ':'){ return handler.do_format_spec(start+1, last, index); } handler.do_empty_format(start, last, index); return start; } template constexpr void perform_parse(Handler&& handler, basic_string_view fmt){ using char_type = Char; auto work_it = fmt.begin(); auto open_brace_it = fmt.begin(); auto close_brace_it = fmt.begin(); while(true){ open_brace_it = find_first_of_it(char_type{'{'}, work_it, fmt.end()); while(true){ close_brace_it = find_first_of_it(char_type{'}'}, work_it, open_brace_it); if(close_brace_it == open_brace_it){ //don't go past open brace because we still need to parse it handler.do_raw(work_it, close_brace_it); work_it = close_brace_it; break; } if(check_duplicate_char(close_brace_it, fmt.end())){ ++close_brace_it; handler.do_raw(work_it, close_brace_it); //go past closing brace to not double parse it work_it = close_brace_it + 1; }else{ REXY_THROW_FORMAT_ERROR("Unmatched brace"); } } if(open_brace_it == fmt.end()){ break; } if(check_duplicate_char(open_brace_it, fmt.end())){ ++open_brace_it; handler.do_raw(work_it, open_brace_it); work_it = open_brace_it + 1; continue; } work_it = perform_format_field_parse(handler, open_brace_it+1, fmt.end()) + 1; } return; } } #endif