390 lines
13 KiB
C++
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, std::size_t(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, std::size_t(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 && chosen != presentation::char_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
|