31#ifndef FENNEC_FORMAT_FORMAT_H
32#define FENNEC_FORMAT_FORMAT_H
34#include <fennec/string/string.h>
36#include <fennec/format/detail/_format.h>
42template<
typename...ArgsT>
43string format(
const cstring& str, ArgsT&&...args) {
44 static constexpr size_t argc =
sizeof...(ArgsT);
45 static constexpr format_arg default_fmt = {
58 if constexpr(argc == 0) {
62 detail::_format_argarray<argc> argarray = { fennec::forward<ArgsT>(args)... };
67 while (i <= str.length()) {
68 size_t brace = str.
find(
'{', i);
69 size_t end = str.find(
'}', i);
70 format_arg fmt = default_fmt;
74 if (str[end + 1] ==
'}') {
75 res += string(str.data() + i, end - i);
79 assertf(
false,
"fennec::format syntax error, encountered unexpected '{'")
83 if (brace >= str.length()) {
84 res += string(str.data() + i, str.length() - i);
87 res += string(str.data() + i, brace - i);
90 size_t next_brace = str.find(
'{', brace + 1);
91 if (brace + 1 == next_brace) {
98 size_t colon = str.find(
':', brace);
101 assertf(colon < next_brace or end < next_brace,
"fennec::format syntax error, mismatched '{}'");
104 size_t id =
min(colon, end) - 1;
110 for (
size_t j =
id, k = 1; j > brace; --j, k *= 10) {
111 size_t u = (str[j] -
'0');
112 assertf(u < 10,
"fennec::format syntax error, invalid argument index");
120 assertf(arg < argc,
"fennec::format syntax error, invalid argument index");
124 fmt.sign = fmt.sign ==
'\0' ?
'-' : fmt.sign;
125 res += argarray.format(arg, fmt);
142 size_t parse = colon;
143 while (str[parse + 1] !=
'}') {
144 if (next_brace < end) {
146 nrfd += str[end - 1] ==
'{';
148 end = str.find(
'}', parse);
149 next_brace = str.find(
'{', parse);
156 assertf(nrfd <= 2 and parse < str.length() - 1 and str[parse + 1] ==
'}',
157 "fennec::format syntax error, mismatched '{}'");
160 switch (str[parse]) {
165 fmt.type = str[parse--];
171 fmt.type = str[parse--];
175 fmt.upper =
true; [[fallthrough]];
178 fmt.type = str[parse--];
183 fmt.type = str[parse--];
187 fmt.upper =
true; [[fallthrough]];
190 fmt.type = str[parse--];
195 fmt.upper =
true; [[fallthrough]];
198 fmt.type = str[parse--];
202 fmt.upper =
true; [[fallthrough]];
205 fmt.type = str[parse--];
209 fmt.upper =
true; [[fallthrough]];
212 fmt.type = str[parse--];
216 fmt.upper =
true; [[fallthrough]];
219 fmt.type = str[parse--];
224 if (parse == colon) {
225 fmt.sign = fmt.sign ==
'\0' ?
'-' : fmt.sign;
226 res += argarray.format(arg, fmt);
233 bool found_decimal =
false;
234 size_t num_decimals = 0;
235 bool is_float_t = detail::_isfmt_f(fmt.type);
236 bool is_str_t = fmt.type ==
's';
237 bool is_integer_t = detail::_isfmt_i(fmt.type);
238 bool ded_width_f =
false;
239 bool ded_width =
false;
240 size_t ded_temp_i = 0;
248 while (isdigit(str[parse]) or (found_decimal = (str[parse] ==
'.')) or str[parse] ==
'{' or str[parse] ==
'}') {
251 assertf(is_float_t or is_str_t,
"fennec::format syntax error, encountered precision argument on non-floating point format");
252 assertf(num_decimals == 0,
"fennec::format syntax error, multiple decimals detected in floating point format");
254 found_decimal =
false;
263 if (str[parse] ==
'{') {
264 assertf(str[parse - 1] ==
'0' or str[parse - 1] ==
'.' or not isdigit(str[parse - 1]),
265 "fennec::format syntax error, unexpected digit preceding nested replacement field");
267 bool prec = str[parse - 1] ==
'.';
268 bool ded = str[parse + 1] ==
'}';
272 sub = prec ? ++arg_c + 1 : arg_c++;
273 }
else if (nrfd == 1 and nnrf == 2 and prec and ded) {
278 sub = ded ? ++arg_c : x;
281 assertf(sub < argc,
"fennec::format syntax error, argument index out of range in nested replacement field");
282 assertf(argarray.is_integer(sub),
"fennec::format argument error, nested replacement field argument is not convertible to integral type");
284 (prec ? fmt.precision : fmt.width) = argarray.int_value(sub);
290 swap(ded_temp_i, parse);
305 if (str[parse] ==
'}') {
311 fmt.fill = str[parse] ==
'0' ?
'0' :
' ';
314 x += j * (str[parse] -
'0');
323 if (parse == colon) {
324 fmt.sign = fmt.sign ==
'\0' ?
'-' : fmt.sign;
325 res += argarray.format(arg, fmt);
331 if (str[parse] ==
'#') {
332 assertf(is_float_t or is_integer_t,
"fennec::format syntax error, encountered alt spec ('#') with non-decimal type");
338 if (str[parse] ==
'-' or str[parse] ==
'+' or str[parse] ==
' ') {
339 fmt.sign = str[parse];
340 if (str[parse] ==
' ') {
347 if (str[parse] ==
'<' or str[parse] ==
'>' or str[parse] ==
'^') {
348 fmt.align = str[parse];
353 if (str[parse] !=
':') {
354 fmt.fill = str[parse];
355 if (str[parse] ==
' ') {
356 fmt.sign = fmt.sign ==
'\0' ?
' ' : fmt.sign;
362 fmt.sign = fmt.sign ==
'\0' ?
'-' : fmt.sign;
365 assertf(parse == colon,
"fennec::format syntax error, malformed format string detected, possible double colon");
369 res += argarray.format(arg, fmt);
constexpr genType min(genType x, genType y)
Returns if otherwise it returns .
Definition common.h:688
constexpr size_t find(char c, size_t i=0) const
Finds the index of the first occurrence of c in the string.
Definition string.h:251
constexpr void swap(T &x, T &y) noexcept
Swaps x and y.
Definition utility.h:114