356 lines
10 KiB
C++

/**
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 <http://www.gnu.org/licenses/>.
*/
#ifndef REXY_DETAIL_FORMAT_PARSE_TPP
#define REXY_DETAIL_FORMAT_PARSE_TPP
#include "parse.hpp"
#include "format_error.hpp"
#include "../../string_view.hpp"
#include <cstddef> //size_t
#include <type_traits> //is_integral, remove_cvref, add_lvalue_reference, remove_reference
#include <algorithm> //min
namespace rexy::fmt::detail::parse{
template<class Char>
constexpr auto find_first_of_it(Char v,
typename basic_string_view<Char>::const_iterator start_it,
typename basic_string_view<Char>::const_iterator last_it)
{
const basic_string_view<Char> 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<class Char>
constexpr auto find_first_of_it(const basic_string_view<Char>& str, Char v){
return find_first_of_it(v, str.begin(), str.end());
}
template<class It>
constexpr bool check_duplicate_char(It start, It end){
if(start == end){
return false;
}
const auto next = start + 1;
return *start == *next;
}
template<class Char>
constexpr bool is_a_number(Char c){
return (c >= '0' && c <= '9');
}
template<class Char>
constexpr bool is_valid_name_char(Char c){
return (c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
(c == '_');
}
template<class It>
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<class T>
constexpr long long dynamic_integer_retriever::operator()(T&& t){
if constexpr(std::is_integral_v<std::remove_cvref_t<T>>){
return static_cast<long long>(t);
}else{
REXY_THROW_FORMAT_ERROR("Invalid dynamic specifier");
return static_cast<long long>(0);
}
}
template<class Specs>
constexpr decltype(auto) dynamic_width_adapter<Specs>::operator()(void){
return specs.on_dynamic_width();
}
template<class Specs>
constexpr decltype(auto) dynamic_width_adapter<Specs>::operator()(int i){
return specs.on_dynamic_width(i);
}
template<class Specs>
template<class It>
constexpr decltype(auto) dynamic_width_adapter<Specs>::operator()(It start, It last){
return specs.on_dynamic_width(start, last);
}
template<class Specs>
constexpr decltype(auto) dynamic_precision_adapter<Specs>::operator()(void){
return specs.on_dynamic_precision();
}
template<class Specs>
constexpr decltype(auto) dynamic_precision_adapter<Specs>::operator()(int i){
return specs.on_dynamic_precision(i);
}
template<class Specs>
template<class It>
constexpr decltype(auto) dynamic_precision_adapter<Specs>::operator()(It start, It last){
return specs.on_dynamic_precision(start, last);
}
template<class Handler>
constexpr decltype(auto) dynamic_index_adapter<Handler>::operator()(void){
return (id = handler.on_arg_id());
}
template<class Handler>
constexpr decltype(auto) dynamic_index_adapter<Handler>::operator()(int i){
id = i;
return handler.on_arg_id(i);
}
template<class Handler>
template<class It>
constexpr decltype(auto) dynamic_index_adapter<Handler>::operator()(It start, It last){
id = handler.on_arg_id(start, last);
return id;
}
template<class It, class FormatSpecs>
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<class It, class FormatSpecs>
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<class It, class FormatSpecs>
constexpr It perform_standard_alt_form_option(It start, It last, FormatSpecs&& specs){
if(*start == '#'){
specs.on_alt_form();
++start;
}
return start;
}
template<class It, class FormatSpecs>
constexpr It perform_standard_zero_fill_option(It start, It last, FormatSpecs&& specs){
if(*start == '0'){
specs.on_zero_fill();
++start;
}
return start;
}
template<class It, class Adapter>
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;
REXY_THROW_FORMAT_ERROR("Invalid index spec");
}
for(auto it = start+1;it != last;++it){
if(!is_a_number(*it)){
func(to_integer(start, it));
return it;
}
}
return last;
}
template<class It, class Adapter>
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<class It, class FormatSpecs>
constexpr It perform_standard_width_option(It start, It last, FormatSpecs&& specs){
using specs_t = std::add_lvalue_reference_t<std::remove_reference_t<FormatSpecs>>;
if(*start == '{'){
return perform_standard_nested_replacement_field(start+1, last, dynamic_width_adapter<specs_t>{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<class It, class FormatSpecs>
constexpr It perform_standard_precision_option(It start, It last, FormatSpecs&& specs){
using specs_t = std::add_lvalue_reference_t<std::remove_reference_t<FormatSpecs>>;
if(*start != '.'){
return start;
}
++start;
if(*start == '{'){
return perform_standard_nested_replacement_field(start+1, last, dynamic_precision_adapter<specs_t>{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<class It, class FormatSpecs>
constexpr It perform_standard_locale_option(It start, It last, FormatSpecs&& specs){
if(*start == 'L'){
specs.on_locale();
return ++start;
}
return start;
}
template<class It, class FormatSpecs>
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<class It, class FormatSpecs>
constexpr It perform_standard_parse(It start, It last, FormatSpecs&& specs){
using specs_t = std::add_lvalue_reference_t<std::remove_reference_t<FormatSpecs>>;
using std_fmt_fn = It(*)(It,It,specs_t);
constexpr std_fmt_fn fns[] = {
perform_standard_fill_align_option<It,specs_t>,
perform_standard_sign_option<It,specs_t>,
perform_standard_alt_form_option<It,specs_t>,
perform_standard_zero_fill_option<It,specs_t>,
perform_standard_width_option<It,specs_t>,
perform_standard_precision_option<It,specs_t>,
perform_standard_locale_option<It,specs_t>,
perform_standard_type_option<It,specs_t>
};
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<class Handler, class It>
constexpr It perform_format_index_spec(Handler&& handler, It start, It last, std::size_t& id){
dynamic_index_adapter<Handler> 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<class Handler, class It>
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<class Handler, class Char>
constexpr void perform_parse(Handler&& handler, basic_string_view<Char> 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