/** 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_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 constexpr void format_specs_handler::on_width(int w){ specs.width = w; } template constexpr void format_specs_handler::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 constexpr void format_specs_handler::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 template constexpr void format_specs_handler::on_dynamic_width(It first, It last){ on_dynamic_width(fmt_ctx.arg_index(first, last)); } template constexpr void format_specs_handler::on_precision(int p){ specs.precision = p; } template constexpr void format_specs_handler::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 constexpr void format_specs_handler::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 template constexpr void format_specs_handler::on_dynamic_precision(It first, It last){ on_dynamic_precision(fmt_ctx.arg_index(first, last)); } template constexpr void format_specs_handler::on_locale(void){ specs.locale = true; } template constexpr void format_specs_handler::on_type_option(int o){ specs.type = o; } template constexpr void format_specs_handler::on_align(int al, int val){ specs.align = static_cast(al); specs.align_char = val; } template constexpr void format_specs_handler::on_sign(int s){ specs.sign = s; } template constexpr void format_specs_handler::on_alt_form(void){ specs.alt_form = true; } template constexpr void format_specs_handler::on_zero_fill(void){ specs.zero_fill = true; } /////////////////////////////dynamic_format_specs_handler////////////////////////////// template constexpr dynamic_format_specs_handler::dynamic_format_specs_handler(ParseCtx& pc, dynamic_format_specs& s): parse_ctx(pc), specs(s){} template constexpr void dynamic_format_specs_handler::on_width(int w){ specs.width = w; } template constexpr void dynamic_format_specs_handler::on_dynamic_width(void){ specs.dyn_width.index = parse_ctx.next_arg_id(); } template constexpr void dynamic_format_specs_handler::on_dynamic_width(int index){ parse_ctx.check_arg_id(index); specs.dyn_width.index = index; } template template constexpr void dynamic_format_specs_handler::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 constexpr void dynamic_format_specs_handler::on_precision(int p){ specs.precision = p; } template constexpr void dynamic_format_specs_handler::on_dynamic_precision(void){ specs.dyn_precision.index = parse_ctx.next_arg_id(); } template constexpr void dynamic_format_specs_handler::on_dynamic_precision(int index){ parse_ctx.check_arg_id(index); specs.dyn_precision.index = index; } template template constexpr void dynamic_format_specs_handler::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 constexpr void dynamic_format_specs_handler::on_locale(void){ specs.locale = true; } template constexpr void dynamic_format_specs_handler::on_type_option(int o){ specs.type = o; } template constexpr void dynamic_format_specs_handler::on_align(int al, int val){ specs.align = static_cast(al); specs.align_char = val; } template constexpr void dynamic_format_specs_handler::on_sign(int s){ specs.sign = s; } template constexpr void dynamic_format_specs_handler::on_alt_form(void){ specs.alt_form = true; } template constexpr void dynamic_format_specs_handler::on_zero_fill(void){ specs.zero_fill = true; } /////////////////////////////cx_format_specs_handler////////////////////////////// template constexpr void cx_format_specs_handler::on_dynamic_width(void){ const auto index = this->parse_ctx.next_arg_id(); if(!is_dynamic_integer(index)){ REXY_THROW_FORMAT_ERROR("Invalid type given as dynamic width"); } } template constexpr void cx_format_specs_handler::on_dynamic_width(int index){ this->parse_ctx.check_arg_id(index); if(!is_dynamic_integer(index)){ REXY_THROW_FORMAT_ERROR("Invalid type given as dynamic width"); } } template template constexpr void cx_format_specs_handler::on_dynamic_width(It first, It last){ on_dynamic_width(find_static_named_arg_id(first, last)); } template constexpr void cx_format_specs_handler::on_dynamic_precision(void){ const auto index = this->parse_ctx.next_arg_id(); if(!is_dynamic_integer(index)){ REXY_THROW_FORMAT_ERROR("Invalid type given as dynamic precision"); } } template constexpr void cx_format_specs_handler::on_dynamic_precision(int index){ this->parse_ctx.check_arg_id(index); if(!is_dynamic_integer(index)){ REXY_THROW_FORMAT_ERROR("Invalid type given as dynamic precision"); } } template template constexpr void cx_format_specs_handler::on_dynamic_precision(It first, It last){ on_dynamic_precision(find_static_named_arg_id(first, last)); } /////////////////////////////format_specs_checker////////////////////////////// template constexpr format_specs_checker::format_specs_checker(const Handler& h, storage_type type): Handler(h), arg_type(type){} template constexpr void format_specs_checker::on_precision(int p){ check_precision(); Handler::on_precision(p); } template constexpr void format_specs_checker::on_dynamic_precision(void){ check_precision(); Handler::on_dynamic_precision(); } template constexpr void format_specs_checker::on_dynamic_precision(int index){ check_precision(); Handler::on_dynamic_precision(index); } template template constexpr void format_specs_checker::on_dynamic_precision(It first, It last){ check_precision(); Handler::on_dynamic_precision(first, last); } template constexpr void format_specs_checker::on_locale(void){ check_arithmetic_type(); Handler::on_locale(); } template constexpr void format_specs_checker::on_sign(int s){ requires_arithmetic_presentation = true; check_arithmetic_type(); Handler::on_sign(s); } template constexpr void format_specs_checker::on_alt_form(void){ requires_arithmetic_presentation = true; check_arithmetic_type(); Handler::on_alt_form(); } template constexpr void format_specs_checker::on_zero_fill(void){ requires_arithmetic_presentation = true; check_arithmetic_type(); Handler::on_zero_fill(); } template constexpr void format_specs_checker::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 constexpr void format_specs_checker::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 constexpr void format_specs_checker::check_arithmetic_type(void){ if(!is_integral_storage_type(arg_type)){ REXY_THROW_FORMAT_ERROR("Formatting argument requires an arithmetic type"); } } } #endif