rexylib/include/rexy/detail/format/specs_handler.tpp

390 lines
13 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_SPECS_HANDLER_TPP
#define REXY_DETAIL_FORMAT_SPECS_HANDLER_TPP
#include "specs_handler.hpp"
#include "format_error.hpp"
#include "traits.hpp"
#include "named_args.hpp"
#include "format_args.hpp"
#include "internal_types.hpp"
#include "standard_types.hpp"
#include "basic_types.hpp"
namespace rexy::fmt::detail{
/////////////////////////////format_specs_handler//////////////////////////////
template<class ParseCtx, class FmtCtx>
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_width(int w){
specs.width = w;
}
template<class ParseCtx, class FmtCtx>
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_dynamic_width(void){
const auto index = parse_ctx.next_arg_id();
const auto arg = fmt_ctx.arg(index);
const auto value = visit_format_arg(parse::dynamic_integer_retriever{}, arg);
on_width(value);
}
template<class ParseCtx, class FmtCtx>
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_dynamic_width(int index){
parse_ctx.check_arg_id(index);
const auto arg = fmt_ctx.arg(index);
const auto value = visit_format_arg(parse::dynamic_integer_retriever{}, arg);
on_width(value);
}
template<class ParseCtx, class FmtCtx>
template<class It>
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_dynamic_width(It first, It last){
on_dynamic_width(fmt_ctx.arg_index(first, last));
}
template<class ParseCtx, class FmtCtx>
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_precision(int p){
specs.precision = p;
}
template<class ParseCtx, class FmtCtx>
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_dynamic_precision(void){
const auto index = parse_ctx.next_arg_id();
const auto arg = fmt_ctx.arg(index);
const auto value = visit_format_arg(parse::dynamic_integer_retriever{}, arg);
on_precision(value);
}
template<class ParseCtx, class FmtCtx>
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_dynamic_precision(int index){
parse_ctx.check_arg_id(index);
const auto arg = fmt_ctx.arg(index);
const auto value = visit_format_arg(parse::dynamic_integer_retriever{}, arg);
on_precision(value);
}
template<class ParseCtx, class FmtCtx>
template<class It>
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_dynamic_precision(It first, It last){
on_dynamic_precision(fmt_ctx.arg_index(first, last));
}
template<class ParseCtx, class FmtCtx>
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_locale(void){
specs.locale = true;
}
template<class ParseCtx, class FmtCtx>
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_type_option(int o){
specs.type = o;
}
template<class ParseCtx, class FmtCtx>
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_align(int al, int val){
specs.align = static_cast<alignment>(al);
specs.align_char = val;
}
template<class ParseCtx, class FmtCtx>
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_sign(int s){
specs.sign = s;
}
template<class ParseCtx, class FmtCtx>
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_alt_form(void){
specs.alt_form = true;
}
template<class ParseCtx, class FmtCtx>
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_zero_fill(void){
specs.zero_fill = true;
}
/////////////////////////////dynamic_format_specs_handler//////////////////////////////
template<class ParseCtx>
constexpr dynamic_format_specs_handler<ParseCtx>::dynamic_format_specs_handler(ParseCtx& pc, dynamic_format_specs<typename ParseCtx::char_type>& s):
parse_ctx(pc),
specs(s){}
template<class ParseCtx>
constexpr void dynamic_format_specs_handler<ParseCtx>::on_width(int w){
specs.width = w;
}
template<class ParseCtx>
constexpr void dynamic_format_specs_handler<ParseCtx>::on_dynamic_width(void){
specs.dyn_width.index = parse_ctx.next_arg_id();
}
template<class ParseCtx>
constexpr void dynamic_format_specs_handler<ParseCtx>::on_dynamic_width(int index){
parse_ctx.check_arg_id(index);
specs.dyn_width.index = index;
}
template<class ParseCtx>
template<class It>
constexpr void dynamic_format_specs_handler<ParseCtx>::on_dynamic_width(It first, It last){
parse_ctx.check_arg_id(0); //just to make sure we aren't switching indexing modes
specs.dyn_width.name = {first, last - first};
specs.width_type = dyn_type::string;
}
template<class ParseCtx>
constexpr void dynamic_format_specs_handler<ParseCtx>::on_precision(int p){
specs.precision = p;
}
template<class ParseCtx>
constexpr void dynamic_format_specs_handler<ParseCtx>::on_dynamic_precision(void){
specs.dyn_precision.index = parse_ctx.next_arg_id();
}
template<class ParseCtx>
constexpr void dynamic_format_specs_handler<ParseCtx>::on_dynamic_precision(int index){
parse_ctx.check_arg_id(index);
specs.dyn_precision.index = index;
}
template<class ParseCtx>
template<class It>
constexpr void dynamic_format_specs_handler<ParseCtx>::on_dynamic_precision(It first, It last){
parse_ctx.check_arg_id(0); //just to make sure we aren't switching indexing modes
specs.dyn_precision.name = {first, last - first};
specs.precision_type = dyn_type::string;
}
template<class ParseCtx>
constexpr void dynamic_format_specs_handler<ParseCtx>::on_locale(void){
specs.locale = true;
}
template<class ParseCtx>
constexpr void dynamic_format_specs_handler<ParseCtx>::on_type_option(int o){
specs.type = o;
}
template<class ParseCtx>
constexpr void dynamic_format_specs_handler<ParseCtx>::on_align(int al, int val){
specs.align = static_cast<alignment>(al);
specs.align_char = val;
}
template<class ParseCtx>
constexpr void dynamic_format_specs_handler<ParseCtx>::on_sign(int s){
specs.sign = s;
}
template<class ParseCtx>
constexpr void dynamic_format_specs_handler<ParseCtx>::on_alt_form(void){
specs.alt_form = true;
}
template<class ParseCtx>
constexpr void dynamic_format_specs_handler<ParseCtx>::on_zero_fill(void){
specs.zero_fill = true;
}
/////////////////////////////cx_format_specs_handler//////////////////////////////
template<class ParseCtx, class... Args>
constexpr void cx_format_specs_handler<ParseCtx,Args...>::on_dynamic_width(void){
const auto index = this->parse_ctx.next_arg_id();
if(!is_dynamic_integer<char_type,Args...>(index)){
REXY_THROW_FORMAT_ERROR("Invalid type given as dynamic width");
}
}
template<class ParseCtx, class... Args>
constexpr void cx_format_specs_handler<ParseCtx,Args...>::on_dynamic_width(int index){
this->parse_ctx.check_arg_id(index);
if(!is_dynamic_integer<char_type,Args...>(index)){
REXY_THROW_FORMAT_ERROR("Invalid type given as dynamic width");
}
}
template<class ParseCtx, class... Args>
template<class It>
constexpr void cx_format_specs_handler<ParseCtx,Args...>::on_dynamic_width(It first, It last){
on_dynamic_width(find_static_named_arg_id<It,Args...>(first, last));
}
template<class ParseCtx, class... Args>
constexpr void cx_format_specs_handler<ParseCtx,Args...>::on_dynamic_precision(void){
const auto index = this->parse_ctx.next_arg_id();
if(!is_dynamic_integer<char_type,Args...>(index)){
REXY_THROW_FORMAT_ERROR("Invalid type given as dynamic precision");
}
}
template<class ParseCtx, class... Args>
constexpr void cx_format_specs_handler<ParseCtx,Args...>::on_dynamic_precision(int index){
this->parse_ctx.check_arg_id(index);
if(!is_dynamic_integer<char_type,Args...>(index)){
REXY_THROW_FORMAT_ERROR("Invalid type given as dynamic precision");
}
}
template<class ParseCtx, class... Args>
template<class It>
constexpr void cx_format_specs_handler<ParseCtx,Args...>::on_dynamic_precision(It first, It last){
on_dynamic_precision(find_static_named_arg_id<It,Args...>(first, last));
}
/////////////////////////////format_specs_checker//////////////////////////////
template<class Handler>
constexpr format_specs_checker<Handler>::format_specs_checker(const Handler& h, storage_type type):
Handler(h), arg_type(type){}
template<class Handler>
constexpr void format_specs_checker<Handler>::on_precision(int p){
check_precision();
Handler::on_precision(p);
}
template<class Handler>
constexpr void format_specs_checker<Handler>::on_dynamic_precision(void){
check_precision();
Handler::on_dynamic_precision();
}
template<class Handler>
constexpr void format_specs_checker<Handler>::on_dynamic_precision(int index){
check_precision();
Handler::on_dynamic_precision(index);
}
template<class Handler>
template<class It>
constexpr void format_specs_checker<Handler>::on_dynamic_precision(It first, It last){
check_precision();
Handler::on_dynamic_precision(first, last);
}
template<class Handler>
constexpr void format_specs_checker<Handler>::on_locale(void){
check_arithmetic_type();
Handler::on_locale();
}
template<class Handler>
constexpr void format_specs_checker<Handler>::on_sign(int s){
requires_arithmetic_presentation = true;
check_arithmetic_type();
Handler::on_sign(s);
}
template<class Handler>
constexpr void format_specs_checker<Handler>::on_alt_form(void){
requires_arithmetic_presentation = true;
check_arithmetic_type();
Handler::on_alt_form();
}
template<class Handler>
constexpr void format_specs_checker<Handler>::on_zero_fill(void){
requires_arithmetic_presentation = true;
check_arithmetic_type();
Handler::on_zero_fill();
}
template<class Handler>
constexpr void format_specs_checker<Handler>::on_type_option(int type){
presentation& chosen = this->specs.present;
switch(type){
case 0:
break;
case 'b':
case 'B':
case 'd':
case 'o':
case 'x':
case 'X':
chosen = presentation::int_t;
break;
case 'c':
chosen = presentation::char_t;
break;
case 's':
chosen = presentation::string_t;
break;
case 'a':
case 'A':
case 'e':
case 'E':
case 'f':
case 'F':
case 'g':
case 'G':
chosen = presentation::float_t;
break;
case 'p':
chosen = presentation::ptr_t;
break;
default:
REXY_THROW_FORMAT_ERROR("Invalid type specifier");
};
switch(arg_type){
case storage_type::none_t:
REXY_THROW_FORMAT_ERROR("Invalid argument");
break;
case storage_type::bool_t:
if(chosen == presentation::default_t){
chosen = presentation::string_t;
}
if(chosen != presentation::string_t && chosen != presentation::int_t){
REXY_THROW_FORMAT_ERROR("Invalid type specifier for bool");
}
break;
case storage_type::char_t:
if(chosen == presentation::default_t){
chosen = presentation::char_t;
}
if(chosen != presentation::char_t && chosen != presentation::int_t){
REXY_THROW_FORMAT_ERROR("Invalid type specifier for character");
}
break;
case storage_type::int_t:
case storage_type::uint_t:
case storage_type::long_long_t:
case storage_type::ulong_long_t:
if(chosen == presentation::default_t){
chosen = presentation::int_t;
}
if(chosen != presentation::int_t && chosen != presentation::char_t){
REXY_THROW_FORMAT_ERROR("Invalid type specifier for integral");
}
break;
case storage_type::float_t:
case storage_type::double_t:
case storage_type::long_double_t:
if(chosen == presentation::default_t){
chosen = presentation::float_t;
}
if(chosen != presentation::float_t){
REXY_THROW_FORMAT_ERROR("Invalid type specifier for floating point");
}
break;
case storage_type::char_ptr_t:
case storage_type::string_t:
if(chosen == presentation::default_t){
chosen = presentation::string_t;
}
if(chosen != presentation::string_t){
REXY_THROW_FORMAT_ERROR("Invalid type specifier for string");
}
break;
case storage_type::ptr_t:
if(chosen == presentation::default_t){
chosen = presentation::ptr_t;
}
if(chosen != presentation::ptr_t){
REXY_THROW_FORMAT_ERROR("Invalid type specifier for pointer");
}
break;
case storage_type::custom_t:
break;
};
if(requires_arithmetic_presentation && chosen != presentation::int_t && chosen != presentation::float_t){
REXY_THROW_FORMAT_ERROR("Invalid type specifier with modifier requiring integer presentation");
}
return Handler::on_type_option(type);
}
template<class Handler>
constexpr void format_specs_checker<Handler>::check_precision(void){
if(!(is_floating_storage_type(arg_type) || is_string_storage_type(arg_type))){
REXY_THROW_FORMAT_ERROR("Precision not valid for argument type");
}
}
template<class Handler>
constexpr void format_specs_checker<Handler>::check_arithmetic_type(void){
if(!is_integral_storage_type(arg_type)){
REXY_THROW_FORMAT_ERROR("Formatting argument requires an arithmetic type");
}
}
}
#endif