From dc9f4c1448772c89705fbcdf53330eb2025a9d36 Mon Sep 17 00:00:00 2001 From: GrieferAtWork Date: Sat, 9 Nov 2024 19:30:24 +0100 Subject: [PATCH] Re-write `string.format` for more feature/performance Since this function is the core driver of template strings, it stands to reason that it should be as efficient as possible, and be capable of using as many fast-pass runtime features as can be. Additionally, this adds a whole expression stack to format string arguments, pretty much enabling the use of arbitrarily complex expressions within template string arguments. --- .vs/deemon-v141.sln | 1 + .vs/deemon-v141.vcxproj | 2 + .vs/deemon-v141.vcxproj.filters | 6 + .vs/deemon-v142.sln | 1 + .vs/deemon-v142.vcxproj | 2 + .vs/deemon-v142.vcxproj.filters | 6 + include/deemon/api.h | 9 + include/deemon/int.h | 12 + include/deemon/object.h | 42 + include/deemon/string.h | 20 + .../link-deemon-gcc-i386-cygwin.def | 1 + .../link-deemon-msvc-i386-win32.def | 1 + src/deemon/objects/none.c | 8 +- .../objects/unicode/bytes_functions.c.inl | 31 +- src/deemon/objects/unicode/format-expr.c.inl | 1525 +++++++++++++++++ src/deemon/objects/unicode/format-impl.c.inl | 573 +++++++ src/deemon/objects/unicode/format.c | 1032 ++++++++++- src/deemon/objects/unicode/string_functions.c | 20 +- src/dex/_hostasm/generator-vstack-ex.c | 2 +- src/dex/_hostasm/libgen86 | 2 +- 20 files changed, 3267 insertions(+), 29 deletions(-) create mode 100644 src/deemon/objects/unicode/format-expr.c.inl create mode 100644 src/deemon/objects/unicode/format-impl.c.inl diff --git a/.vs/deemon-v141.sln b/.vs/deemon-v141.sln index e6cbb983..f8f9705e 100644 --- a/.vs/deemon-v141.sln +++ b/.vs/deemon-v141.sln @@ -382,6 +382,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{2360741E-F ..\util\test\deemon-int-tostr.dee = ..\util\test\deemon-int-tostr.dee ..\util\test\deemon-java-lambda.dee = ..\util\test\deemon-java-lambda.dee ..\util\test\deemon-kwcall-reference-loophole.dee = ..\util\test\deemon-kwcall-reference-loophole.dee + ..\util\test\deemon-list-setrange.dee = ..\util\test\deemon-list-setrange.dee ..\util\test\deemon-list-sort.dee = ..\util\test\deemon-list-sort.dee ..\util\test\deemon-mapping-byattr.dee = ..\util\test\deemon-mapping-byattr.dee ..\util\test\deemon-mapping-byhash.dee = ..\util\test\deemon-mapping-byhash.dee diff --git a/.vs/deemon-v141.vcxproj b/.vs/deemon-v141.vcxproj index 8bcdd590..e3bb3cdb 100644 --- a/.vs/deemon-v141.vcxproj +++ b/.vs/deemon-v141.vcxproj @@ -281,6 +281,8 @@ + + diff --git a/.vs/deemon-v141.vcxproj.filters b/.vs/deemon-v141.vcxproj.filters index 8de83a08..a19ed829 100644 --- a/.vs/deemon-v141.vcxproj.filters +++ b/.vs/deemon-v141.vcxproj.filters @@ -816,6 +816,12 @@ src\objects\unicode + + src\objects\unicode + + + src\objects\unicode + src\objects\unicode diff --git a/.vs/deemon-v142.sln b/.vs/deemon-v142.sln index 82a3e52a..074413ef 100644 --- a/.vs/deemon-v142.sln +++ b/.vs/deemon-v142.sln @@ -382,6 +382,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{2360741E-F ..\util\test\deemon-int-tostr.dee = ..\util\test\deemon-int-tostr.dee ..\util\test\deemon-java-lambda.dee = ..\util\test\deemon-java-lambda.dee ..\util\test\deemon-kwcall-reference-loophole.dee = ..\util\test\deemon-kwcall-reference-loophole.dee + ..\util\test\deemon-list-setrange.dee = ..\util\test\deemon-list-setrange.dee ..\util\test\deemon-list-sort.dee = ..\util\test\deemon-list-sort.dee ..\util\test\deemon-mapping-byattr.dee = ..\util\test\deemon-mapping-byattr.dee ..\util\test\deemon-mapping-byhash.dee = ..\util\test\deemon-mapping-byhash.dee diff --git a/.vs/deemon-v142.vcxproj b/.vs/deemon-v142.vcxproj index 4f883910..b9950b7f 100644 --- a/.vs/deemon-v142.vcxproj +++ b/.vs/deemon-v142.vcxproj @@ -281,6 +281,8 @@ + + diff --git a/.vs/deemon-v142.vcxproj.filters b/.vs/deemon-v142.vcxproj.filters index 8de83a08..a19ed829 100644 --- a/.vs/deemon-v142.vcxproj.filters +++ b/.vs/deemon-v142.vcxproj.filters @@ -816,6 +816,12 @@ src\objects\unicode + + src\objects\unicode + + + src\objects\unicode + src\objects\unicode diff --git a/include/deemon/api.h b/include/deemon/api.h index f0319cb8..1c38428e 100644 --- a/include/deemon/api.h +++ b/include/deemon/api.h @@ -406,6 +406,15 @@ __pragma_GCC_diagnostic_ignored(Wstringop_overread) #endif #endif /* !CONFIG_[NO_]EXPERIMENTAL_NEW_MAPPING_OPERATORS */ +#if (!defined(CONFIG_EXPERIMENTAL_NEW_STRING_FORMAT) && \ + !defined(CONFIG_NO_EXPERIMENTAL_NEW_STRING_FORMAT)) +#if 1 /* Not ready, yet */ +#define CONFIG_EXPERIMENTAL_NEW_STRING_FORMAT +#else +#define CONFIG_NO_EXPERIMENTAL_NEW_STRING_FORMAT +#endif +#endif /* !CONFIG_[NO_]EXPERIMENTAL_NEW_STRING_FORMAT */ + #ifdef CONFIG_HOST_WINDOWS diff --git a/include/deemon/int.h b/include/deemon/int.h index 4fad25ad..f5c4c8e7 100644 --- a/include/deemon/int.h +++ b/include/deemon/int.h @@ -760,6 +760,11 @@ DFUNDEF WUNUSED NONNULL((1, 4)) int (DCALL Dee_Atou64)(/*utf-8*/ char const *__r sizeof(T) <= 2 ? Dee_Atoi16(str, len, DEE_PRIVATE_ATOI_FLAGS(T, radix_and_flags), (int16_t *)(value)) : \ sizeof(T) <= 4 ? Dee_Atoi32(str, len, DEE_PRIVATE_ATOI_FLAGS(T, radix_and_flags), (int32_t *)(value)) : \ Dee_Atoi64(str, len, DEE_PRIVATE_ATOI_FLAGS(T, radix_and_flags), (int64_t *)(value))) +#define Dee_TAtoiu(str, len, radix_and_flags, value) \ + (sizeof(T) <= 1 ? Dee_Atoi8(str, len, (radix_and_flags), (int8_t *)(value)) : \ + sizeof(T) <= 2 ? Dee_Atoi16(str, len, (radix_and_flags), (int16_t *)(value)) : \ + sizeof(T) <= 4 ? Dee_Atoi32(str, len, (radix_and_flags), (int32_t *)(value)) : \ + Dee_Atoi64(str, len, (radix_and_flags), (int64_t *)(value))) #else /* __NO_builtin_choose_expr */ #define DEE_PRIVATE_ATOI_FLAGS(T, flags) \ __builtin_choose_expr(((T)-1) < (T)0, (flags) | DEEATOI_STRING_FSIGNED, (flags)) @@ -768,7 +773,14 @@ DFUNDEF WUNUSED NONNULL((1, 4)) int (DCALL Dee_Atou64)(/*utf-8*/ char const *__r __builtin_choose_expr(sizeof(T) <= 2, Dee_Atoi16(str, len, DEE_PRIVATE_ATOI_FLAGS(T, radix_and_flags), (int16_t *)(value)), \ __builtin_choose_expr(sizeof(T) <= 4, Dee_Atoi32(str, len, DEE_PRIVATE_ATOI_FLAGS(T, radix_and_flags), (int32_t *)(value)), \ Dee_Atoi64(str, len, DEE_PRIVATE_ATOI_FLAGS(T, radix_and_flags), (int64_t *)(value))))) +#define Dee_TAtoiu(str, len, radix_and_flags, value) \ + __builtin_choose_expr(sizeof(*(value)) <= 1, Dee_Atoi8(str, len, (radix_and_flags), (int8_t *)(value)), \ + __builtin_choose_expr(sizeof(*(value)) <= 2, Dee_Atoi16(str, len, (radix_and_flags), (int16_t *)(value)), \ + __builtin_choose_expr(sizeof(*(value)) <= 4, Dee_Atoi32(str, len, (radix_and_flags), (int32_t *)(value)), \ + Dee_Atoi64(str, len, (radix_and_flags), (int64_t *)(value))))) #endif /* !__NO_builtin_choose_expr */ +#define Dee_TAtois(str, len, radix_and_flags, value) \ + Dee_TAtoiu(str, len, (radix_and_flags) | DEEATOI_STRING_FSIGNED, value) /* Print an integer to a given format-printer. diff --git a/include/deemon/object.h b/include/deemon/object.h index fa63fdba..b7843e34 100644 --- a/include/deemon/object.h +++ b/include/deemon/object.h @@ -2008,6 +2008,48 @@ typedef WUNUSED_T NONNULL_T((2, 3)) Dee_ssize_t (DCALL *Dee_foreach_pair_t)(void typedef WUNUSED_T NONNULL_T((2)) Dee_ssize_t (DCALL *Dee_enumerate_t)(void *arg, DeeObject *index, /*nullable*/ DeeObject *value); typedef WUNUSED_T Dee_ssize_t (DCALL *Dee_enumerate_index_t)(void *arg, size_t index, /*nullable*/ DeeObject *value); +#if 0 +struct my_foreach_data { +}; + +PRIVATE WUNUSED NONNULL((2)) Dee_ssize_t DCALL +my_foreach_cb(void *arg, DeeObject *elem) { + struct my_foreach_data *data; + data = (struct my_foreach_data *)arg; + return DeeError_NOTIMPLEMENTED(); +} + +struct my_foreach_pair_data { +}; + +PRIVATE WUNUSED NONNULL((2, 3)) Dee_ssize_t DCALL +my_foreach_pair_cb(void *arg, DeeObject *key, DeeObject *value) { + struct my_foreach_pair_data *data; + data = (struct my_foreach_pair_data *)arg; + return DeeError_NOTIMPLEMENTED(); +} + +struct my_enumerate_data { +}; + +PRIVATE WUNUSED NONNULL((2)) Dee_ssize_t DCALL +my_enumerate_cb(void *arg, DeeObject *index, /*nullable*/ DeeObject *value) { + struct my_enumerate_data *data; + data = (struct my_enumerate_data *)arg; + return DeeError_NOTIMPLEMENTED(); +} + +struct my_enumerate_index_data { +}; + +PRIVATE WUNUSED Dee_ssize_t DCALL +my_enumerate_index_cb(void *arg, size_t index, /*nullable*/ DeeObject *value) { + struct my_enumerate_index_data *data; + data = (struct my_enumerate_index_data *)arg; + return DeeError_NOTIMPLEMENTED(); +} +#endif + struct Dee_type_nsi; struct Dee_type_seq_cache; struct Dee_type_seq { diff --git a/include/deemon/string.h b/include/deemon/string.h index 10a5afd6..e66db1f8 100644 --- a/include/deemon/string.h +++ b/include/deemon/string.h @@ -2153,6 +2153,26 @@ DeeString_NewWithWidth(void const *__restrict str, +/* Generic format printer function (can be used to implement + * both string (unicode), as well as Bytes formatting) + * @param: pattern_printer: Printer used to emit data from `pattern' (i.e. static data) + * Note that this is a distinct argument so-as to allow `pattern' + * to be an ASCII string, rather than it needing to be utf-8. + * @param: data_printer: Printer used to emit data from `args' (i.e. dynamic data) */ +DFUNDEF WUNUSED NONNULL((1, 3)) Dee_ssize_t DCALL +DeeString_FormatPrinter(char const *pattern, size_t pattern_length, DeeObject *args, + Dee_formatprinter_t pattern_printer, + Dee_formatprinter_t data_printer, void *arg); + +/* API functions for `string.format'. */ +DFUNDEF WUNUSED NONNULL((1, 2)) DREF DeeObject *DFCALL +DeeString_FormatWStr(/*utf-8*/ char const *pattern_wstr, DeeObject *args); +DFUNDEF WUNUSED NONNULL((1, 2)) DREF DeeObject *DFCALL +DeeString_Format(DeeObject *pattern, DeeObject *args); + + + + /* Return a string containing a single character. */ #if defined(__INTELLISENSE__) && defined(__cplusplus) extern "C++" { diff --git a/src/deemon/linker-scripts/link-deemon-gcc-i386-cygwin.def b/src/deemon/linker-scripts/link-deemon-gcc-i386-cygwin.def index a2791a5c..aae9717a 100644 --- a/src/deemon/linker-scripts/link-deemon-gcc-i386-cygwin.def +++ b/src/deemon/linker-scripts/link-deemon-gcc-i386-cygwin.def @@ -1052,6 +1052,7 @@ EXPORTS _DeeString_Chr32@4=DeeString_Chr32@4 _DeeString_Chr8@4=DeeString_Chr8@4 _DeeString_DecodeBackslashEscaped@16=DeeString_DecodeBackslashEscaped@16 + _DeeString_FormatPrinter@24=DeeString_FormatPrinter@24 _DeeString_FreeWidth@4=DeeString_FreeWidth@4 _DeeString_FromBackslashEscaped@12=DeeString_FromBackslashEscaped@12 _DeeString_GetChar@8=DeeString_GetChar@8 diff --git a/src/deemon/linker-scripts/link-deemon-msvc-i386-win32.def b/src/deemon/linker-scripts/link-deemon-msvc-i386-win32.def index 49c1545b..6818b058 100644 --- a/src/deemon/linker-scripts/link-deemon-msvc-i386-win32.def +++ b/src/deemon/linker-scripts/link-deemon-msvc-i386-win32.def @@ -1052,6 +1052,7 @@ EXPORTS DeeString_Chr32@4=_DeeString_Chr32@4 DeeString_Chr8@4=_DeeString_Chr8@4 DeeString_DecodeBackslashEscaped@16=_DeeString_DecodeBackslashEscaped@16 + DeeString_FormatPrinter@24=_DeeString_FormatPrinter@24 DeeString_FreeWidth@4=_DeeString_FreeWidth@4 DeeString_FromBackslashEscaped@12=_DeeString_FromBackslashEscaped@12 DeeString_GetChar@8=_DeeString_GetChar@8 diff --git a/src/deemon/objects/none.c b/src/deemon/objects/none.c index 558dd526..66247185 100644 --- a/src/deemon/objects/none.c +++ b/src/deemon/objects/none.c @@ -360,10 +360,10 @@ PRIVATE struct type_seq none_seq = { /* .tp_delrange = */ (int (DCALL *)(DeeObject *, DeeObject *, DeeObject *))&none_i3, /* .tp_setrange = */ (int (DCALL *)(DeeObject *, DeeObject *, DeeObject *, DeeObject *))&none_i4, /* .tp_nsi = */ &none_nsi, - /* .tp_foreach = */ (Dee_ssize_t (DCALL *)(DeeObject *__restrict, Dee_foreach_t, void *))&none_s3, - /* .tp_foreach_pair = */ (Dee_ssize_t (DCALL *)(DeeObject *__restrict, Dee_foreach_pair_t, void *))&none_s3, - /* .tp_enumerate = */ (Dee_ssize_t (DCALL *)(DeeObject *__restrict, Dee_enumerate_t, void *))&none_s3, - /* .tp_enumerate_index = */ (Dee_ssize_t (DCALL *)(DeeObject *__restrict, Dee_enumerate_index_t, void *, size_t, size_t))&none_s5, + /* .tp_foreach = */ (Dee_ssize_t (DCALL *)(DeeObject *__restrict, Dee_foreach_t, void *))&none_s3, /* TODO: Infinite loop */ + /* .tp_foreach_pair = */ (Dee_ssize_t (DCALL *)(DeeObject *__restrict, Dee_foreach_pair_t, void *))&none_s3, /* TODO: Infinite loop */ + /* .tp_enumerate = */ (Dee_ssize_t (DCALL *)(DeeObject *__restrict, Dee_enumerate_t, void *))&none_s3, /* TODO: Infinite loop */ + /* .tp_enumerate_index = */ (Dee_ssize_t (DCALL *)(DeeObject *__restrict, Dee_enumerate_index_t, void *, size_t, size_t))&none_s5, /* TODO: Infinite loop */ /* .tp_iterkeys = */ (DREF DeeObject *(DCALL *)(DeeObject *__restrict))&none_1, /* .tp_bounditem = */ (int (DCALL *)(DeeObject *, DeeObject *))&none_i2_1, /* .tp_hasitem = */ (int (DCALL *)(DeeObject *, DeeObject *))&none_i2_1, diff --git a/src/deemon/objects/unicode/bytes_functions.c.inl b/src/deemon/objects/unicode/bytes_functions.c.inl index 8ce4d3b7..271ddcd1 100644 --- a/src/deemon/objects/unicode/bytes_functions.c.inl +++ b/src/deemon/objects/unicode/bytes_functions.c.inl @@ -778,11 +778,31 @@ err: return NULL; } + +#ifdef CONFIG_EXPERIMENTAL_NEW_STRING_FORMAT +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DCALL +bytes_format(Bytes *self, size_t argc, DeeObject *const *argv) { + DeeObject *args; + struct bytes_printer printer; + if (DeeArg_Unpack(argc, argv, "o:format", &args)) + goto err; + bytes_printer_init(&printer); + if unlikely(DeeString_FormatPrinter((char const *)DeeBytes_DATA(self), DeeBytes_SIZE(self), args, + (dformatprinter)&bytes_printer_append, &bytes_printer_print, + &printer) < 0) + goto err_printer; + return bytes_printer_pack(&printer); +err_printer: + bytes_printer_fini(&printer); +err: + return NULL; +} +#else /* CONFIG_EXPERIMENTAL_NEW_STRING_FORMAT */ INTDEF dssize_t DCALL -DeeBytes_Format(dformatprinter printer, - dformatprinter format_printer, void *arg, - char const *__restrict format, - size_t format_len, DeeObject *__restrict args); +DeeBytes_Format_old(dformatprinter printer, + dformatprinter format_printer, void *arg, + char const *__restrict format, + size_t format_len, DeeObject *__restrict args); PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DCALL bytes_format(Bytes *self, size_t argc, DeeObject *const *argv) { @@ -791,7 +811,7 @@ bytes_format(Bytes *self, size_t argc, DeeObject *const *argv) { goto err; { struct bytes_printer printer = BYTES_PRINTER_INIT; - if unlikely(DeeBytes_Format(&bytes_printer_print, + if unlikely(DeeBytes_Format_old(&bytes_printer_print, (dformatprinter)&bytes_printer_append, &printer, (char *)DeeBytes_DATA(self), @@ -805,6 +825,7 @@ err_printer: err: return NULL; } +#endif /* !CONFIG_EXPERIMENTAL_NEW_STRING_FORMAT */ diff --git a/src/deemon/objects/unicode/format-expr.c.inl b/src/deemon/objects/unicode/format-expr.c.inl new file mode 100644 index 00000000..f1f75685 --- /dev/null +++ b/src/deemon/objects/unicode/format-expr.c.inl @@ -0,0 +1,1525 @@ +/* Copyright (c) 2018-2024 Griefer@Work * + * * + * This software is provided 'as-is', without any express or implied * + * warranty. In no event will the authors be held liable for any damages * + * arising from the use of this software. * + * * + * Permission is granted to anyone to use this software for any purpose, * + * including commercial applications, and to alter it and redistribute it * + * freely, subject to the following restrictions: * + * * + * 1. The origin of this software must not be misrepresented; you must not * + * claim that you wrote the original software. If you use this software * + * in a product, an acknowledgement (see the following) in the product * + * documentation is required: * + * Portions Copyright (c) 2018-2024 Griefer@Work * + * 2. Altered source versions must be plainly marked as such, and must not be * + * misrepresented as being the original software. * + * 3. This notice may not be removed or altered from any source distribution. * + */ +#ifdef __INTELLISENSE__ +#include "format.c" +//#define DEFINE_sfa_skipexpr +#define DEFINE_sfa_evalexpr +#endif /* __INTELLISENSE__ */ + +#if (defined(DEFINE_sfa_skipexpr) + defined(DEFINE_sfa_evalexpr)) != 1 +#error "Must #define exactly one of these macros" +#endif /* DEFINE_sfa_... */ + +#ifdef DEFINE_sfa_skipexpr +#define LOCAL_ERRVAL (-1) +#define LOCAL_return_type int +#define LOCAL_OK__or__lhs 0 +#define LOCAL_ISOK(x) ((x) == 0) +#define LOCAL__param_lhs /* nothing */ +#define LOCAL__arg_lhs(x) /* nothing */ +#define LOCAL_sfa_evalunary_base sfa_skipunary_base +#define LOCAL_sfa_evalunary sfa_skipunary +#define LOCAL_sfa_evalprod sfa_skipprod +#define LOCAL_sfa_evalsum sfa_skipsum +#define LOCAL_sfa_evalshift sfa_skipshift +#define LOCAL_sfa_evalcmp sfa_skipcmp +#define LOCAL_sfa_evalcmpeq sfa_skipcmpeq +#define LOCAL_sfa_evaland sfa_skipand +#define LOCAL_sfa_evalxor sfa_skipxor +#define LOCAL_sfa_evalor sfa_skipor +#define LOCAL_sfa_evalas sfa_skipas +#define LOCAL_sfa_evalland sfa_skipland +#define LOCAL_sfa_evallor sfa_skiplor +#define LOCAL_sfa_evalcond sfa_skipcond +#define LOCAL_sfa_evalunary_operand sfa_skipunary_operand +#define LOCAL_sfa_evalprod_operand sfa_skipprod_operand +#define LOCAL_sfa_evalsum_operand sfa_skipsum_operand +#define LOCAL_sfa_evalshift_operand sfa_skipshift_operand +#define LOCAL_sfa_evalcmp_operand sfa_skipcmp_operand +#define LOCAL_sfa_evalcmpeq_operand sfa_skipcmpeq_operand +#define LOCAL_sfa_evaland_operand sfa_skipand_operand +#define LOCAL_sfa_evalxor_operand sfa_skipxor_operand +#define LOCAL_sfa_evalor_operand sfa_skipor_operand +#define LOCAL_sfa_evalas_operand sfa_skipas_operand +#define LOCAL_sfa_evalland_operand sfa_skipland_operand +#define LOCAL_sfa_evallor_operand sfa_skiplor_operand +#define LOCAL_sfa_evalcond_operand sfa_skipcond_operand +#else /* DEFINE_sfa_skipexpr */ +#define LOCAL_ERRVAL NULL +#define LOCAL_OK__or__lhs lhs +#define LOCAL_ISOK(x) ((x) != NULL) +#define LOCAL_return_type DREF DeeObject * +#define LOCAL__param_lhs , /*inherit(always)*/ DREF DeeObject *__restrict lhs +#define LOCAL__arg_lhs(x) , x +#define LOCAL_sfa_evalunary_base sfa_evalunary_base +#define LOCAL_sfa_evalunary sfa_evalunary +#define LOCAL_sfa_evalprod sfa_evalprod +#define LOCAL_sfa_evalsum sfa_evalsum +#define LOCAL_sfa_evalshift sfa_evalshift +#define LOCAL_sfa_evalcmp sfa_evalcmp +#define LOCAL_sfa_evalcmpeq sfa_evalcmpeq +#define LOCAL_sfa_evaland sfa_evaland +#define LOCAL_sfa_evalxor sfa_evalxor +#define LOCAL_sfa_evalor sfa_evalor +#define LOCAL_sfa_evalas sfa_evalas +#define LOCAL_sfa_evalland sfa_evalland +#define LOCAL_sfa_evallor sfa_evallor +#define LOCAL_sfa_evalcond sfa_evalcond +#define LOCAL_sfa_evalunary_operand sfa_evalunary_operand +#define LOCAL_sfa_evalprod_operand sfa_evalprod_operand +#define LOCAL_sfa_evalsum_operand sfa_evalsum_operand +#define LOCAL_sfa_evalshift_operand sfa_evalshift_operand +#define LOCAL_sfa_evalcmp_operand sfa_evalcmp_operand +#define LOCAL_sfa_evalcmpeq_operand sfa_evalcmpeq_operand +#define LOCAL_sfa_evaland_operand sfa_evaland_operand +#define LOCAL_sfa_evalxor_operand sfa_evalxor_operand +#define LOCAL_sfa_evalor_operand sfa_evalor_operand +#define LOCAL_sfa_evalas_operand sfa_evalas_operand +#define LOCAL_sfa_evalland_operand sfa_evalland_operand +#define LOCAL_sfa_evallor_operand sfa_evallor_operand +#define LOCAL_sfa_evalcond_operand sfa_evalcond_operand +#endif /* !DEFINE_sfa_skipexpr */ + +DECL_BEGIN + +PRIVATE WUNUSED NONNULL((1)) LOCAL_return_type DFCALL +LOCAL_sfa_evalunary_base(struct string_format_advanced *__restrict self) { +#ifdef DEFINE_sfa_skipexpr + LOCAL_return_type result = 0; +#else /* DEFINE_sfa_skipexpr */ + LOCAL_return_type result; +#endif /* !DEFINE_sfa_skipexpr */ + switch (__builtin_expect(self->sfa_exprtok, SFA_TOK_KEYWORD)) { + + case SFA_TOK_KEYWORD: { + size_t kwdlen = sfa_tok_keyword_getlen(self); +#ifndef DEFINE_sfa_skipexpr + char const *kwd = self->sfa_parser.sfp_iter; + result = DeeObject_GetItemStringLenHash(self->sfa_args, kwd, kwdlen, + Dee_HashUtf8(kwd, kwdlen)); + if unlikely(!result) + goto err; +#endif /* !DEFINE_sfa_skipexpr */ + self->sfa_parser.sfp_iter += kwdlen; + sfa_yield(self); + } break; + + case SFA_TOK_INT: { + size_t intlen = sfa_tok_int_getlen(self); +#ifndef DEFINE_sfa_skipexpr + int decode_status; + size_t index; + char const *intrepr = self->sfa_parser.sfp_iter; + decode_status = Dee_TAtoiu(intrepr, intlen, DEEINT_STRING(0, DEEINT_STRING_FTRY), &index); + if unlikely(decode_status < 0) + goto err; + if (decode_status > 0) { + DREF DeeObject *index_ob; + index_ob = DeeInt_FromString(intrepr, intlen, DEEINT_STRING(0, DEEINT_STRING_FNORMAL)); + if unlikely(!index_ob) + goto err; + result = DeeObject_GetItem(self->sfa_args, index_ob); + Dee_Decref(index_ob); + } else { + result = DeeObject_GetItemIndex(self->sfa_args, index); + } + if unlikely(!result) + goto err; +#endif /* !DEFINE_sfa_skipexpr */ + self->sfa_parser.sfp_iter += intlen; + sfa_yield(self); + } break; + + case SFA_TOK_CHAR: + case SFA_TOK_STRING: + /* TODO */ + DeeError_NOTIMPLEMENTED(); + goto err; + + case SFA_TOK_D_TRUE: +#ifndef DEFINE_sfa_skipexpr + result = Dee_True; + Dee_Incref(result); +#endif /* !DEFINE_sfa_skipexpr */ + sfa_yield(self); + break; + + case SFA_TOK_D_FALSE: +#ifndef DEFINE_sfa_skipexpr + result = Dee_False; + Dee_Incref(result); +#endif /* !DEFINE_sfa_skipexpr */ + sfa_yield(self); + break; + + case SFA_TOK_D_NONE: +#ifndef DEFINE_sfa_skipexpr + result = Dee_None; + Dee_Incref(result); +#endif /* !DEFINE_sfa_skipexpr */ + sfa_yield(self); + break; + + case SFA_TOK_D_STR: + case SFA_TOK_D_REPR: + case SFA_TOK_D_COPY: + case SFA_TOK_D_DEEPCOPY: + case SFA_TOK_D_TYPE: { +#ifndef DEFINE_sfa_skipexpr + unsigned int tok = self->sfa_exprtok; +#endif /* !DEFINE_sfa_skipexpr */ + bool saved_sfa_inparen; + sfa_yield(self); + if unlikely(self->sfa_exprtok != '(') { + sfa_err_bad_token(self, "("); + goto err; + } + sfa_yield(self); + saved_sfa_inparen = self->sfa_inparen; + self->sfa_inparen = true; + result = LOCAL_sfa_evalcond(self); + self->sfa_inparen = saved_sfa_inparen; + if unlikely(!LOCAL_ISOK(result)) + goto err; + if unlikely(self->sfa_exprtok != ')') { +#ifndef DEFINE_sfa_skipexpr + Dee_Decref(result); +#endif /* !DEFINE_sfa_skipexpr */ + sfa_err_bad_token(self, ")"); + goto err; + } + sfa_yield(self); +#ifndef DEFINE_sfa_skipexpr + { + DREF DeeObject *final_result; + switch (tok) { + case SFA_TOK_D_STR: + final_result = DeeObject_Str(result); + break; + case SFA_TOK_D_REPR: + final_result = DeeObject_Repr(result); + break; + case SFA_TOK_D_COPY: + final_result = DeeObject_Copy(result); + break; + case SFA_TOK_D_DEEPCOPY: + final_result = DeeObject_DeepCopy(result); + break; + case SFA_TOK_D_TYPE: + final_result = (DREF DeeObject *)Dee_TYPE(result); + Dee_Incref(final_result); + break; + default: __builtin_unreachable(); + } + Dee_Decref(result); + /*if unlikely(!final_result) + goto err;*/ + result = final_result; + } +#endif /* !DEFINE_sfa_skipexpr */ + } break; + + case SFA_TOK_D_INT: { + size_t intlen = sfa_tok_int_getlen(self); +#ifndef DEFINE_sfa_skipexpr + char const *intrepr = self->sfa_parser.sfp_iter; + result = DeeInt_FromString(intrepr, intlen, DEEINT_STRING(0, DEEINT_STRING_FNORMAL)); + /*if unlikely(!result) + goto err;*/ +#endif /* !DEFINE_sfa_skipexpr */ + self->sfa_parser.sfp_iter += intlen; + sfa_yield(self); + } break; + + case SFA_TOK_D_BOUND: { + bool saved_sfa_inparen; + sfa_yield(self); + if unlikely(self->sfa_exprtok != '(') { + sfa_err_bad_token(self, "("); + goto err; + } + sfa_yield(self); + + saved_sfa_inparen = self->sfa_inparen; + self->sfa_inparen = true; + /* TODO */ + DeeError_NOTIMPLEMENTED(); + self->sfa_inparen = saved_sfa_inparen; + goto err; + } break; + + case '#': + case '+': + case '-': + case '~': { +#ifndef DEFINE_sfa_skipexpr + unsigned int tok = self->sfa_exprtok; +#endif /* !DEFINE_sfa_skipexpr */ + sfa_yield(self); + result = LOCAL_sfa_evalunary(self); + if unlikely(!LOCAL_ISOK(result)) + goto err; +#ifndef DEFINE_sfa_skipexpr + { + DREF DeeObject *final_result; + switch (tok) { + case '#': final_result = DeeObject_SizeOb(result); break; + case '+': final_result = DeeObject_Pos(result); break; + case '-': final_result = DeeObject_Neg(result); break; + case '~': final_result = DeeObject_Inv(result); break; + default: __builtin_unreachable(); + } + Dee_Decref(result); + /*if unlikely(!final_result) + goto err;*/ + result = final_result; + } +#endif /* !DEFINE_sfa_skipexpr */ + } break; + + case '(': { + bool saved_sfa_inparen; + sfa_yield(self); + if (self->sfa_exprtok == ')') { + sfa_yield(self); +#ifndef DEFINE_sfa_skipexpr + result = Dee_EmptyTuple; + Dee_Incref(result); +#endif /* !DEFINE_sfa_skipexpr */ + break; + } + saved_sfa_inparen = self->sfa_inparen; + self->sfa_inparen = true; + result = LOCAL_sfa_evalcond(self); + if unlikely(!LOCAL_ISOK(result)) + goto err; + if (self->sfa_exprtok == ',') { + sfa_yield(self); + if (self->sfa_exprtok == ')') { + sfa_yield(self); +#ifndef DEFINE_sfa_skipexpr + { + DREF DeeObject *final_result; + final_result = DeeTuple_NewVectorSymbolic(1, &result); + if unlikely(!final_result) { + Dee_Decref(result); + goto err; + } + result = final_result; + } +#endif /* !DEFINE_sfa_skipexpr */ + } else { +#ifndef DEFINE_sfa_skipexpr + DREF DeeTupleObject *items; + size_t count = 1; + items = DeeTuple_TryNewUninitialized(4); + if unlikely(!items) { + items = DeeTuple_NewUninitialized(2); + if unlikely(!items) { + Dee_Decref(result); + goto err; + } + } + DeeTuple_SET(items, 0, result); /* Inherit reference */ +#endif /* !DEFINE_sfa_skipexpr */ + do { + LOCAL_return_type elem; + elem = LOCAL_sfa_evalcond(self); + if unlikely(!LOCAL_ISOK(elem)) { +#ifndef DEFINE_sfa_skipexpr +err_tuple_items_count: + Dee_Decrefv(DeeTuple_ELEM(items), count); + DeeTuple_FreeUninitialized(items); +#endif /* !DEFINE_sfa_skipexpr */ + goto err; + } +#ifndef DEFINE_sfa_skipexpr + if unlikely(count >= DeeTuple_SIZE(items)) { + size_t new_size = DeeTuple_SIZE(items) * 2; + DREF DeeTupleObject *new_items; + new_items = DeeTuple_TryResizeUninitialized(items, new_size); + if unlikely(!new_items) { + new_size = count + 1; + new_items = DeeTuple_ResizeUninitialized(items, new_size); + if unlikely(!new_items) { + Dee_Decref(elem); + goto err_tuple_items_count; + } + } + items = new_items; + } + DeeTuple_SET(items, count, elem); /* Inherit reference */ + ++count; +#endif /* !DEFINE_sfa_skipexpr */ + if (self->sfa_exprtok != ',') + break; + sfa_yield(self); + } while (self->sfa_exprtok != ')'); +#ifndef DEFINE_sfa_skipexpr + result = (LOCAL_return_type)DeeTuple_TruncateUninitialized(items, count); +#endif /* !DEFINE_sfa_skipexpr */ + if (self->sfa_exprtok != ')') { +#ifndef DEFINE_sfa_skipexpr + Dee_Decref(result); +#endif /* !DEFINE_sfa_skipexpr */ + sfa_err_bad_token(self, ")"); + goto err; + } + } + } + self->sfa_inparen = saved_sfa_inparen; + } break; + + case '[': { + LOCAL_return_type elem; + bool saved_sfa_inparen; +#ifndef DEFINE_sfa_skipexpr + DREF DeeTupleObject *items; + size_t count; +#endif /* !DEFINE_sfa_skipexpr */ + sfa_yield(self); + if (self->sfa_exprtok == ']') { + sfa_yield(self); +#ifndef DEFINE_sfa_skipexpr + result = Dee_EmptyTuple; + Dee_Incref(result); +#endif /* !DEFINE_sfa_skipexpr */ + break; + } +#ifndef DEFINE_sfa_skipexpr + count = 0; + items = DeeTuple_TryNewUninitialized(2); + if unlikely(!items) { + items = DeeTuple_NewUninitialized(1); + if unlikely(!items) + goto err; + } +#endif /* !DEFINE_sfa_skipexpr */ + saved_sfa_inparen = self->sfa_inparen; + self->sfa_inparen = true; + + do { + elem = LOCAL_sfa_evalcond(self); + if unlikely(!LOCAL_ISOK(elem)) { +#ifndef DEFINE_sfa_skipexpr +err_list_items_count: + Dee_Decrefv(DeeTuple_ELEM(items), count); + DeeTuple_FreeUninitialized(items); +#endif /* !DEFINE_sfa_skipexpr */ + goto err; + } +#ifndef DEFINE_sfa_skipexpr + if unlikely(count >= DeeTuple_SIZE(items)) { + size_t new_size = DeeTuple_SIZE(items) * 2; + DREF DeeTupleObject *new_items; + new_items = DeeTuple_TryResizeUninitialized(items, new_size); + if unlikely(!new_items) { + new_size = count + 1; + new_items = DeeTuple_ResizeUninitialized(items, new_size); + if unlikely(!new_items) { + Dee_Decref(elem); + goto err_list_items_count; + } + } + items = new_items; + } + DeeTuple_SET(items, count, elem); /* Inherit reference */ + ++count; +#endif /* !DEFINE_sfa_skipexpr */ + if (self->sfa_exprtok != ',') + break; + sfa_yield(self); + } while (self->sfa_exprtok != ']'); +#ifndef DEFINE_sfa_skipexpr + result = (LOCAL_return_type)DeeTuple_TruncateUninitialized(items, count); +#endif /* !DEFINE_sfa_skipexpr */ + if (self->sfa_exprtok != ']') { +#ifndef DEFINE_sfa_skipexpr + Dee_Decref(result); +#endif /* !DEFINE_sfa_skipexpr */ + sfa_err_bad_token(self, "]"); + goto err; + } + self->sfa_inparen = saved_sfa_inparen; + } break; + + case '!': + if likely(!self->sfa_inparen) { + sfa_yield(self); + result = LOCAL_sfa_evalunary(self); + if unlikely(!LOCAL_ISOK(result)) + goto err; +#ifndef DEFINE_sfa_skipexpr + { + int asbool = DeeObject_BoolInherited(result); + if unlikely(asbool < 0) + goto err; + result = DeeBool_For(!asbool); + Dee_Incref(result); + } +#endif /* !DEFINE_sfa_skipexpr */ + break; + } + ATTR_FALLTHROUGH + default: + sfa_err_bad_token(self, ""); + goto err; + } + return result; +err: + return LOCAL_ERRVAL; +} + +PRIVATE WUNUSED NONNULL((1)) LOCAL_return_type DFCALL +LOCAL_sfa_evalunary_operand(struct string_format_advanced *__restrict self LOCAL__param_lhs) { + ASSERT(self->sfa_exprtok == '.' || + self->sfa_exprtok == '[' || + self->sfa_exprtok == '('); + do { + switch (self->sfa_exprtok) { + + case '.': { + sfa_yield(self); + if (self->sfa_exprtok == '{') { +#ifndef DEFINE_sfa_skipexpr + DREF DeeObject *attrname; + LOCAL_return_type result; +#endif /* !DEFINE_sfa_skipexpr */ + sfa_yield(self); +#ifdef DEFINE_sfa_skipexpr + if unlikely(string_format_advanced_skip1(self)) + goto err_lhs; +#else /* DEFINE_sfa_skipexpr */ + attrname = string_format_advanced_do1_intostr(self); + if unlikely(!attrname) + goto err_lhs; + result = DeeObject_GetAttr(lhs, attrname); + Dee_Decref(attrname); + Dee_Decref(lhs); + if unlikely(!result) + goto err; + lhs = result; +#endif /* !DEFINE_sfa_skipexpr */ + } else if (self->sfa_exprtok == SFA_TOK_KEYWORD) { + size_t kwdlen = sfa_tok_keyword_getlen(self); +#ifndef DEFINE_sfa_skipexpr + LOCAL_return_type result; + char const *kwd = self->sfa_parser.sfp_iter; + result = DeeObject_GetAttrStringLenHash(lhs, kwd, kwdlen, Dee_HashUtf8(kwd, kwdlen)); + Dee_Decref(lhs); + if unlikely(!result) + goto err; + lhs = result; +#endif /* !DEFINE_sfa_skipexpr */ + self->sfa_parser.sfp_iter += kwdlen; + sfa_yield(self); + } else { + sfa_err_bad_token(self, ""); + } + } break; + + case '[': { +#ifndef DEFINE_sfa_skipexpr + LOCAL_return_type result; +#endif /* !DEFINE_sfa_skipexpr */ + bool saved_sfa_inparen; + saved_sfa_inparen = self->sfa_inparen; + self->sfa_inparen = true; + sfa_yield(self); + if (self->sfa_exprtok == ':') { + sfa_yield(self); + if (self->sfa_exprtok == ']') { + sfa_yield(self); +#ifndef DEFINE_sfa_skipexpr + result = DeeObject_GetRange(lhs, Dee_None, Dee_None); +#endif /* !DEFINE_sfa_skipexpr */ + } else { + LOCAL_return_type end_index; + end_index = LOCAL_sfa_evalcond(self); + if unlikely(!LOCAL_ISOK(end_index)) + goto err_lhs; + if unlikely(self->sfa_exprtok != ']') { +#ifndef DEFINE_sfa_skipexpr + Dee_Decref(end_index); +#endif /* !DEFINE_sfa_skipexpr */ + sfa_err_bad_token(self, "]"); + goto err_lhs; + } + sfa_yield(self); +#ifndef DEFINE_sfa_skipexpr + result = DeeObject_GetRange(lhs, Dee_None, end_index); + Dee_Decref(end_index); +#endif /* !DEFINE_sfa_skipexpr */ + } + } else { + LOCAL_return_type index; + index = LOCAL_sfa_evalcond(self); + if unlikely(!LOCAL_ISOK(index)) + goto err_lhs; + if (self->sfa_exprtok == ':') { + sfa_yield(self); + if (self->sfa_exprtok == ']') { + sfa_yield(self); +#ifndef DEFINE_sfa_skipexpr + result = DeeObject_GetRange(lhs, index, Dee_None); +#endif /* !DEFINE_sfa_skipexpr */ + } else { + LOCAL_return_type end_index; + end_index = LOCAL_sfa_evalcond(self); + if unlikely(!LOCAL_ISOK(end_index)) { +#ifndef DEFINE_sfa_skipexpr + Dee_Decref(index); +#endif /* !DEFINE_sfa_skipexpr */ + goto err_lhs; + } + if unlikely(self->sfa_exprtok != ']') { +#ifndef DEFINE_sfa_skipexpr + Dee_Decref(end_index); + Dee_Decref(index); +#endif /* !DEFINE_sfa_skipexpr */ + sfa_err_bad_token(self, "]"); + goto err_lhs; + } + sfa_yield(self); +#ifndef DEFINE_sfa_skipexpr + result = DeeObject_GetRange(lhs, index, end_index); + Dee_Decref(end_index); +#endif /* !DEFINE_sfa_skipexpr */ + } + } else { + if unlikely(self->sfa_exprtok != ']') { + sfa_err_bad_token(self, "]"); + goto err_lhs; + } + sfa_yield(self); +#ifndef DEFINE_sfa_skipexpr + result = DeeObject_GetItem(lhs, index); +#endif /* !DEFINE_sfa_skipexpr */ + } +#ifndef DEFINE_sfa_skipexpr + Dee_Decref(index); +#endif /* !DEFINE_sfa_skipexpr */ + } + self->sfa_inparen = saved_sfa_inparen; +#ifndef DEFINE_sfa_skipexpr + Dee_Decref(lhs); + if unlikely(!result) + goto err; + lhs = result; +#endif /* !DEFINE_sfa_skipexpr */ + } break; + + case '(': { + bool saved_sfa_inparen; + saved_sfa_inparen = self->sfa_inparen; + self->sfa_inparen = true; + /* TODO */ + DeeError_NOTIMPLEMENTED(); + self->sfa_inparen = saved_sfa_inparen; + goto err_lhs; + } break; + + default: __builtin_unreachable(); + } + } while (self->sfa_exprtok == '.' || + self->sfa_exprtok == '[' || + self->sfa_exprtok == '('); + return LOCAL_OK__or__lhs; +err_lhs: +#ifndef DEFINE_sfa_skipexpr + Dee_Decref(lhs); +err: +#endif /* !DEFINE_sfa_skipexpr */ + return LOCAL_ERRVAL; +} + +PRIVATE WUNUSED NONNULL((1)) LOCAL_return_type DFCALL +LOCAL_sfa_evalprod_operand(struct string_format_advanced *__restrict self LOCAL__param_lhs) { + LOCAL_return_type rhs; + ASSERT(self->sfa_exprtok == '*' || + self->sfa_exprtok == '/' || + self->sfa_exprtok == '%' || + self->sfa_exprtok == SFA_TOK_POW); + do { +#ifndef DEFINE_sfa_skipexpr + unsigned int tok = self->sfa_exprtok; +#endif /* !DEFINE_sfa_skipexpr */ + sfa_yield(self); + rhs = LOCAL_sfa_evalunary(self); + if unlikely(!LOCAL_ISOK(rhs)) + goto err_lhs; +#ifndef DEFINE_sfa_skipexpr + { + LOCAL_return_type result; + switch (tok) { + case '*': + result = DeeObject_Mul(lhs, rhs); + break; + case '/': + result = DeeObject_Div(lhs, rhs); + break; + case '%': + result = DeeObject_Mod(lhs, rhs); + break; + case SFA_TOK_POW: + result = DeeObject_Pow(lhs, rhs); + break; + default: __builtin_unreachable();; + } + Dee_Decref(rhs); + Dee_Decref(lhs); + lhs = result; + if unlikely(!result) + goto err; + } +#endif /* !DEFINE_sfa_skipexpr */ + } while (self->sfa_exprtok == '*' || + self->sfa_exprtok == '/' || + self->sfa_exprtok == '%' || + self->sfa_exprtok == SFA_TOK_POW); + return LOCAL_OK__or__lhs; +err_lhs: +#ifndef DEFINE_sfa_skipexpr + Dee_Decref(lhs); +err: +#endif /* !DEFINE_sfa_skipexpr */ + return LOCAL_ERRVAL; +} + +PRIVATE WUNUSED NONNULL((1)) LOCAL_return_type DFCALL +LOCAL_sfa_evalsum_operand(struct string_format_advanced *__restrict self LOCAL__param_lhs) { + LOCAL_return_type rhs; + ASSERT(self->sfa_exprtok == '+' || + self->sfa_exprtok == '-'); + do { +#ifndef DEFINE_sfa_skipexpr + unsigned int tok = self->sfa_exprtok; +#endif /* !DEFINE_sfa_skipexpr */ + sfa_yield(self); + rhs = LOCAL_sfa_evalprod(self); + if unlikely(!LOCAL_ISOK(rhs)) + goto err_lhs; +#ifndef DEFINE_sfa_skipexpr + { + LOCAL_return_type result; + switch (tok) { + case '+': + result = DeeObject_Add(lhs, rhs); + break; + case '-': + result = DeeObject_Sub(lhs, rhs); + break; + default: __builtin_unreachable();; + } + Dee_Decref(rhs); + Dee_Decref(lhs); + lhs = result; + if unlikely(!result) + goto err; + } +#endif /* !DEFINE_sfa_skipexpr */ + } while (self->sfa_exprtok == '+' || + self->sfa_exprtok == '-'); + return LOCAL_OK__or__lhs; +err_lhs: +#ifndef DEFINE_sfa_skipexpr + Dee_Decref(lhs); +err: +#endif /* !DEFINE_sfa_skipexpr */ + return LOCAL_ERRVAL; +} + +PRIVATE WUNUSED NONNULL((1)) LOCAL_return_type DFCALL +LOCAL_sfa_evalshift_operand(struct string_format_advanced *__restrict self LOCAL__param_lhs) { + LOCAL_return_type rhs; + ASSERT(self->sfa_exprtok == SFA_TOK_SHL || + self->sfa_exprtok == SFA_TOK_SHR); + do { +#ifndef DEFINE_sfa_skipexpr + unsigned int tok = self->sfa_exprtok; +#endif /* !DEFINE_sfa_skipexpr */ + sfa_yield(self); + rhs = LOCAL_sfa_evalsum(self); + if unlikely(!LOCAL_ISOK(rhs)) + goto err_lhs; +#ifndef DEFINE_sfa_skipexpr + { + LOCAL_return_type result; + switch (tok) { + case SFA_TOK_SHL: + result = DeeObject_Shl(lhs, rhs); + break; + case SFA_TOK_SHR: + result = DeeObject_Shr(lhs, rhs); + break; + default: __builtin_unreachable();; + } + Dee_Decref(rhs); + Dee_Decref(lhs); + lhs = result; + if unlikely(!result) + goto err; + } +#endif /* !DEFINE_sfa_skipexpr */ + } while (self->sfa_exprtok == SFA_TOK_SHL || + self->sfa_exprtok == SFA_TOK_SHR); + return LOCAL_OK__or__lhs; +err_lhs: +#ifndef DEFINE_sfa_skipexpr + Dee_Decref(lhs); +err: +#endif /* !DEFINE_sfa_skipexpr */ + return LOCAL_ERRVAL; +} + +PRIVATE WUNUSED NONNULL((1)) LOCAL_return_type DFCALL +LOCAL_sfa_evalcmp_operand(struct string_format_advanced *__restrict self LOCAL__param_lhs) { + LOCAL_return_type rhs; + ASSERT(self->sfa_exprtok == '<' || + self->sfa_exprtok == '>' || + self->sfa_exprtok == SFA_TOK_LOWER_EQUAL || + self->sfa_exprtok == SFA_TOK_GREATER_EQUAL); + do { +#ifndef DEFINE_sfa_skipexpr + unsigned int tok = self->sfa_exprtok; +#endif /* !DEFINE_sfa_skipexpr */ + sfa_yield(self); + rhs = LOCAL_sfa_evalshift(self); + if unlikely(!LOCAL_ISOK(rhs)) + goto err_lhs; +#ifndef DEFINE_sfa_skipexpr + { + LOCAL_return_type result; + switch (tok) { + case '<': + result = DeeObject_CmpLo(lhs, rhs); + break; + case '>': + result = DeeObject_CmpGr(lhs, rhs); + break; + case SFA_TOK_LOWER_EQUAL: + result = DeeObject_CmpLe(lhs, rhs); + break; + case SFA_TOK_GREATER_EQUAL: + result = DeeObject_CmpGe(lhs, rhs); + break; + default: __builtin_unreachable();; + } + Dee_Decref(rhs); + Dee_Decref(lhs); + lhs = result; + if unlikely(!result) + goto err; + } +#endif /* !DEFINE_sfa_skipexpr */ + } while (self->sfa_exprtok == '<' || + self->sfa_exprtok == '>' || + self->sfa_exprtok == SFA_TOK_LOWER_EQUAL || + self->sfa_exprtok == SFA_TOK_GREATER_EQUAL); + return LOCAL_OK__or__lhs; +err_lhs: +#ifndef DEFINE_sfa_skipexpr + Dee_Decref(lhs); +err: +#endif /* !DEFINE_sfa_skipexpr */ + return LOCAL_ERRVAL; +} + +PRIVATE WUNUSED NONNULL((1)) LOCAL_return_type DFCALL +LOCAL_sfa_evalcmpeq_operand(struct string_format_advanced *__restrict self LOCAL__param_lhs) { + unsigned int tok; + LOCAL_return_type rhs; + ASSERT(self->sfa_exprtok == SFA_TOK_EQUAL || self->sfa_exprtok == SFA_TOK_EQUAL3 || + self->sfa_exprtok == SFA_TOK_QMARK_QMARK || + self->sfa_exprtok == SFA_TOK_D_IS || self->sfa_exprtok == SFA_TOK_D_IN || + (self->sfa_inparen && (self->sfa_exprtok == '!' || + self->sfa_exprtok == SFA_TOK_NOT_EQUAL || + self->sfa_exprtok == SFA_TOK_NOT_EQUAL3))); + do { + tok = self->sfa_exprtok; + sfa_yield(self); + if (tok == '!') { + /* Special case: multiple tokens */ +#ifndef DEFINE_sfa_skipexpr + bool invert = true; +#endif /* !DEFINE_sfa_skipexpr */ + char const *orig = self->sfa_parser.sfp_iter; + ASSERT(self->sfa_inparen); + while unlikely(self->sfa_exprtok == '!') { +#ifndef DEFINE_sfa_skipexpr + invert = !invert; +#endif /* !DEFINE_sfa_skipexpr */ + sfa_yield(self); + } + switch (self->sfa_exprtok) { + case SFA_TOK_D_IS: + case SFA_TOK_D_IN: +#ifdef DEFINE_sfa_skipexpr + goto do_sfa_evalcmp; +#else /* DEFINE_sfa_skipexpr */ + if (!invert) + goto do_sfa_evalcmp; + rhs = LOCAL_sfa_evalcmp(self); + if unlikely(!LOCAL_ISOK(rhs)) + goto err_lhs; + { + int result_bool; + switch (tok) { + case SFA_TOK_D_IS: { + if (DeeNone_Check(rhs)) { + result_bool = DeeNone_Check(lhs); + } else if (DeeSuper_Check(lhs)) { + result_bool = DeeType_Extends(DeeSuper_TYPE(lhs), (DeeTypeObject *)rhs); + } else { + result_bool = DeeObject_InstanceOf(lhs, (DeeTypeObject *)rhs); + } + } break; + case SFA_TOK_D_IN: + result_bool = DeeObject_ContainsAsBool(rhs, lhs); + break; + default: __builtin_unreachable(); + } + Dee_Decref(rhs); + Dee_Decref(lhs); + if unlikely(result_bool < 0) + goto err; + lhs = DeeBool_For(!result_bool); /* Invert here! */ + Dee_Incref(lhs); + } + break; +#endif /* !DEFINE_sfa_skipexpr */ + default: + self->sfa_parser.sfp_iter = orig; + goto done; + } + +#ifndef DEFINE_sfa_skipexpr + } else if (tok == SFA_TOK_QMARK_QMARK) { + if (DeeNone_Check(lhs)) { + Dee_Decref(lhs); + lhs = LOCAL_sfa_evalcmp(self); + if unlikely(!lhs) + goto err; + } else { + if unlikely(sfa_skipcmp(self)) + goto err_lhs; + } +#endif /* !DEFINE_sfa_skipexpr */ + } else { +do_sfa_evalcmp: + rhs = LOCAL_sfa_evalcmp(self); + if unlikely(!LOCAL_ISOK(rhs)) + goto err_lhs; +#ifndef DEFINE_sfa_skipexpr + { + LOCAL_return_type result; + switch (tok) { + case SFA_TOK_NOT_EQUAL: + result = DeeObject_CmpNe(lhs, rhs); + break; + case SFA_TOK_EQUAL: + result = DeeObject_CmpEq(lhs, rhs); + break; + case SFA_TOK_NOT_EQUAL3: + case SFA_TOK_EQUAL3: { + bool is_same = lhs == rhs; + if (tok == SFA_TOK_NOT_EQUAL3) + is_same = !is_same; + result = DeeBool_For(is_same); + Dee_Incref(result); + } break; + case SFA_TOK_D_IS: { + unsigned int is_instance; + if (DeeNone_Check(rhs)) { + is_instance = DeeNone_Check(lhs); + } else if (DeeSuper_Check(lhs)) { + is_instance = DeeType_Extends(DeeSuper_TYPE(lhs), (DeeTypeObject *)rhs); + } else { + is_instance = DeeObject_InstanceOf(lhs, (DeeTypeObject *)rhs); + } + result = DeeBool_For(is_instance); + Dee_Incref(result); + } break; + case SFA_TOK_D_IN: + result = DeeObject_Contains(rhs, lhs); + break; + default: __builtin_unreachable(); + } + Dee_Decref(rhs); + Dee_Decref(lhs); + lhs = result; + if unlikely(!result) + goto err; + } +#endif /* !DEFINE_sfa_skipexpr */ + } + } while (self->sfa_exprtok == SFA_TOK_EQUAL || self->sfa_exprtok == SFA_TOK_EQUAL3 || + self->sfa_exprtok == SFA_TOK_QMARK_QMARK || + self->sfa_exprtok == SFA_TOK_D_IS || self->sfa_exprtok == SFA_TOK_D_IN || + (self->sfa_inparen && (self->sfa_exprtok == '!' || + self->sfa_exprtok == SFA_TOK_NOT_EQUAL || + self->sfa_exprtok == SFA_TOK_NOT_EQUAL3))); +done: + return LOCAL_OK__or__lhs; +err_lhs: +#ifndef DEFINE_sfa_skipexpr + Dee_Decref(lhs); +err: +#endif /* !DEFINE_sfa_skipexpr */ + return LOCAL_ERRVAL; +} + +PRIVATE WUNUSED NONNULL((1)) LOCAL_return_type DFCALL +LOCAL_sfa_evalas_operand(struct string_format_advanced *__restrict self LOCAL__param_lhs) { + LOCAL_return_type rhs; + ASSERT(self->sfa_exprtok == SFA_TOK_D_AS); + do { + sfa_yield(self); + rhs = LOCAL_sfa_evalcmpeq(self); + if unlikely(!LOCAL_ISOK(rhs)) + goto err_lhs; +#ifndef DEFINE_sfa_skipexpr + { + LOCAL_return_type result; + result = DeeSuper_New((DeeTypeObject *)rhs, lhs); + Dee_Decref(rhs); + Dee_Decref(lhs); + lhs = result; + if unlikely(!result) + goto err; + } +#endif /* !DEFINE_sfa_skipexpr */ + } while (self->sfa_exprtok == SFA_TOK_D_AS); + return LOCAL_OK__or__lhs; +err_lhs: +#ifndef DEFINE_sfa_skipexpr + Dee_Decref(lhs); +err: +#endif /* !DEFINE_sfa_skipexpr */ + return LOCAL_ERRVAL; +} + +PRIVATE WUNUSED NONNULL((1)) LOCAL_return_type DFCALL +LOCAL_sfa_evaland_operand(struct string_format_advanced *__restrict self LOCAL__param_lhs) { + LOCAL_return_type rhs; + ASSERT(self->sfa_exprtok == '&'); + do { + sfa_yield(self); + rhs = LOCAL_sfa_evalas(self); + if unlikely(!LOCAL_ISOK(rhs)) + goto err_lhs; +#ifndef DEFINE_sfa_skipexpr + { + LOCAL_return_type result; + result = DeeObject_And(lhs, rhs); + Dee_Decref(rhs); + Dee_Decref(lhs); + lhs = result; + if unlikely(!result) + goto err; + } +#endif /* !DEFINE_sfa_skipexpr */ + } while (self->sfa_exprtok == '&'); + return LOCAL_OK__or__lhs; +err_lhs: +#ifndef DEFINE_sfa_skipexpr + Dee_Decref(lhs); +err: +#endif /* !DEFINE_sfa_skipexpr */ + return LOCAL_ERRVAL; +} + +PRIVATE WUNUSED NONNULL((1)) LOCAL_return_type DFCALL +LOCAL_sfa_evalxor_operand(struct string_format_advanced *__restrict self LOCAL__param_lhs) { + LOCAL_return_type rhs; + ASSERT(self->sfa_exprtok == '^'); + do { + sfa_yield(self); + rhs = LOCAL_sfa_evaland(self); + if unlikely(!LOCAL_ISOK(rhs)) + goto err_lhs; +#ifndef DEFINE_sfa_skipexpr + { + LOCAL_return_type result; + result = DeeObject_Xor(lhs, rhs); + Dee_Decref(rhs); + Dee_Decref(lhs); + lhs = result; + if unlikely(!result) + goto err; + } +#endif /* !DEFINE_sfa_skipexpr */ + } while (self->sfa_exprtok == '^'); + return LOCAL_OK__or__lhs; +err_lhs: +#ifndef DEFINE_sfa_skipexpr + Dee_Decref(lhs); +err: +#endif /* !DEFINE_sfa_skipexpr */ + return LOCAL_ERRVAL; +} + +PRIVATE WUNUSED NONNULL((1)) LOCAL_return_type DFCALL +LOCAL_sfa_evalor_operand(struct string_format_advanced *__restrict self LOCAL__param_lhs) { + LOCAL_return_type rhs; + ASSERT(self->sfa_exprtok == '|'); + do { + sfa_yield(self); + rhs = LOCAL_sfa_evalxor(self); + if unlikely(!LOCAL_ISOK(rhs)) + goto err_lhs; +#ifndef DEFINE_sfa_skipexpr + { + LOCAL_return_type result; + result = DeeObject_Or(lhs, rhs); + Dee_Decref(rhs); + Dee_Decref(lhs); + lhs = result; + if unlikely(!result) + goto err; + } +#endif /* !DEFINE_sfa_skipexpr */ + } while (self->sfa_exprtok == '|'); + return LOCAL_OK__or__lhs; +err_lhs: +#ifndef DEFINE_sfa_skipexpr + Dee_Decref(lhs); +err: +#endif /* !DEFINE_sfa_skipexpr */ + return LOCAL_ERRVAL; +} + +PRIVATE WUNUSED NONNULL((1)) LOCAL_return_type DFCALL +LOCAL_sfa_evalland_operand(struct string_format_advanced *__restrict self LOCAL__param_lhs) { + ASSERT(self->sfa_exprtok == SFA_TOK_LAND); + do { +#ifndef DEFINE_sfa_skipexpr + int lhs_bool; +#endif /* !DEFINE_sfa_skipexpr */ + sfa_yield(self); +#ifdef DEFINE_sfa_skipexpr + if unlikely(sfa_skipor(self)) + goto err; +#else /* DEFINE_sfa_skipexpr */ + lhs_bool = DeeObject_BoolInherited(lhs); + if unlikely(lhs_bool < 0) + goto err; + if (!lhs_bool) { + if unlikely(sfa_skipor(self)) + goto err; + lhs = Dee_False; + } else { + lhs = sfa_evalor(self); + if unlikely(!lhs) + goto err; + lhs_bool = DeeObject_BoolInherited(lhs); + if unlikely(lhs_bool < 0) + goto err; + lhs = DeeBool_For(lhs_bool); + } + Dee_Incref(lhs); +#endif /* !DEFINE_sfa_skipexpr */ + } while (self->sfa_exprtok == SFA_TOK_LAND); + return LOCAL_OK__or__lhs; +err: + return LOCAL_ERRVAL; +} + +PRIVATE WUNUSED NONNULL((1)) LOCAL_return_type DFCALL +LOCAL_sfa_evallor_operand(struct string_format_advanced *__restrict self LOCAL__param_lhs) { + ASSERT(self->sfa_exprtok == SFA_TOK_LOR); + do { +#ifndef DEFINE_sfa_skipexpr + int lhs_bool; +#endif /* !DEFINE_sfa_skipexpr */ + sfa_yield(self); +#ifdef DEFINE_sfa_skipexpr + if unlikely(sfa_skiplor(self)) + goto err; +#else /* DEFINE_sfa_skipexpr */ + lhs_bool = DeeObject_BoolInherited(lhs); + if unlikely(lhs_bool < 0) + goto err; + if (lhs_bool) { + if unlikely(sfa_skiplor(self)) + goto err; + lhs = Dee_True; + } else { + lhs = sfa_evallor(self); + if unlikely(!lhs) + goto err; + lhs_bool = DeeObject_BoolInherited(lhs); + if unlikely(lhs_bool < 0) + goto err; + lhs = DeeBool_For(lhs_bool); + } + Dee_Incref(lhs); +#endif /* !DEFINE_sfa_skipexpr */ + } while (self->sfa_exprtok == SFA_TOK_LOR); + return LOCAL_OK__or__lhs; +err: + return LOCAL_ERRVAL; +} + +PRIVATE WUNUSED NONNULL((1)) LOCAL_return_type DFCALL +LOCAL_sfa_evalcond_operand(struct string_format_advanced *__restrict self LOCAL__param_lhs) { +#ifndef DEFINE_sfa_skipexpr + int lhs_bool; +#endif /* !DEFINE_sfa_skipexpr */ + LOCAL_return_type result; + ASSERT(self->sfa_exprtok == '?'); +#ifndef DEFINE_sfa_skipexpr + lhs_bool = DeeObject_BoolInherited(lhs); + if unlikely(lhs_bool < 0) + goto err; +#endif /* !DEFINE_sfa_skipexpr */ + sfa_yield(self); +#ifdef DEFINE_sfa_skipexpr + result = sfa_skiplor(self); + if unlikely(!result) + goto err; + if unlikely(self->sfa_exprtok != ':') + return sfa_err_bad_token(self, ":"); + sfa_yield(self); + return sfa_skipcond(self); +#else /* DEFINE_sfa_skipexpr */ + if (lhs_bool) { + result = sfa_evallor(self); + if unlikely(!result) + goto err; + if unlikely(self->sfa_exprtok != ':') { + sfa_err_bad_token(self, ":"); +err_r: + Dee_Decref(result); + goto err; + } + sfa_yield(self); + if unlikely(sfa_skipcond(self)) + goto err_r; + return result; + } + if unlikely(sfa_skiplor(self)) + goto err; + if unlikely(self->sfa_exprtok != ':') { + sfa_err_bad_token(self, ":"); + goto err; + } + sfa_yield(self); + return sfa_evalcond(self); +#endif /* !DEFINE_sfa_skipexpr */ +err: + return LOCAL_ERRVAL; +} + + + +PRIVATE WUNUSED NONNULL((1)) LOCAL_return_type DFCALL +LOCAL_sfa_evalunary(struct string_format_advanced *__restrict self) { + LOCAL_return_type result = LOCAL_sfa_evalunary_base(self); + if (LOCAL_ISOK(result) && (self->sfa_exprtok == '.' || + self->sfa_exprtok == '[' || + self->sfa_exprtok == '(')) { + result = LOCAL_sfa_evalunary_operand(self LOCAL__arg_lhs(result)); + } + return result; +} + +PRIVATE WUNUSED NONNULL((1)) LOCAL_return_type DFCALL +LOCAL_sfa_evalprod(struct string_format_advanced *__restrict self) { + LOCAL_return_type result = LOCAL_sfa_evalunary(self); + if (LOCAL_ISOK(result) && (self->sfa_exprtok == '*' || + self->sfa_exprtok == '/' || + self->sfa_exprtok == '%' || + self->sfa_exprtok == SFA_TOK_POW)) { + result = LOCAL_sfa_evalprod_operand(self LOCAL__arg_lhs(result)); + } + return result; +} + +PRIVATE WUNUSED NONNULL((1)) LOCAL_return_type DFCALL +LOCAL_sfa_evalsum(struct string_format_advanced *__restrict self) { + LOCAL_return_type result = LOCAL_sfa_evalprod(self); + if (LOCAL_ISOK(result) && (self->sfa_exprtok == '+' || + self->sfa_exprtok == '-')) { + result = LOCAL_sfa_evalsum_operand(self LOCAL__arg_lhs(result)); + } + return result; +} + +PRIVATE WUNUSED NONNULL((1)) LOCAL_return_type DFCALL +LOCAL_sfa_evalshift(struct string_format_advanced *__restrict self) { + LOCAL_return_type result = LOCAL_sfa_evalsum(self); + if (LOCAL_ISOK(result) && (self->sfa_exprtok == SFA_TOK_SHL || + self->sfa_exprtok == SFA_TOK_SHR)) { + result = LOCAL_sfa_evalshift_operand(self LOCAL__arg_lhs(result)); + } + return result; +} + +PRIVATE WUNUSED NONNULL((1)) LOCAL_return_type DFCALL +LOCAL_sfa_evalcmp(struct string_format_advanced *__restrict self) { + LOCAL_return_type result = LOCAL_sfa_evalshift(self); + if (LOCAL_ISOK(result) && (self->sfa_exprtok == '<' || + self->sfa_exprtok == '>' || + self->sfa_exprtok == SFA_TOK_LOWER_EQUAL || + self->sfa_exprtok == SFA_TOK_GREATER_EQUAL)) { + result = LOCAL_sfa_evalcmp_operand(self LOCAL__arg_lhs(result)); + } + return result; +} + +PRIVATE WUNUSED NONNULL((1)) LOCAL_return_type DFCALL +LOCAL_sfa_evalcmpeq(struct string_format_advanced *__restrict self) { + LOCAL_return_type result = LOCAL_sfa_evalcmp(self); + if (LOCAL_ISOK(result)) { + switch (self->sfa_exprtok) { + case '!': + case SFA_TOK_NOT_EQUAL: + case SFA_TOK_NOT_EQUAL3: + if (!self->sfa_inparen) + break; + ATTR_FALLTHROUGH + case SFA_TOK_EQUAL: + case SFA_TOK_EQUAL3: + case SFA_TOK_QMARK_QMARK: + case SFA_TOK_D_IS: + case SFA_TOK_D_IN: + result = LOCAL_sfa_evalcmpeq_operand(self LOCAL__arg_lhs(result)); + break; + default: + break; + } + } + return result; +} + +PRIVATE WUNUSED NONNULL((1)) LOCAL_return_type DFCALL +LOCAL_sfa_evaland(struct string_format_advanced *__restrict self) { + LOCAL_return_type result = LOCAL_sfa_evalcmpeq(self); + if (LOCAL_ISOK(result) && self->sfa_exprtok == '&') + result = LOCAL_sfa_evaland_operand(self LOCAL__arg_lhs(result)); + return result; +} + +PRIVATE WUNUSED NONNULL((1)) LOCAL_return_type DFCALL +LOCAL_sfa_evalxor(struct string_format_advanced *__restrict self) { + LOCAL_return_type result = LOCAL_sfa_evaland(self); + if (LOCAL_ISOK(result) && self->sfa_exprtok == '^') + result = LOCAL_sfa_evalxor_operand(self LOCAL__arg_lhs(result)); + return result; +} + +PRIVATE WUNUSED NONNULL((1)) LOCAL_return_type DFCALL +LOCAL_sfa_evalor(struct string_format_advanced *__restrict self) { + LOCAL_return_type result = LOCAL_sfa_evalxor(self); + if (LOCAL_ISOK(result) && self->sfa_exprtok == '|') + result = LOCAL_sfa_evalor_operand(self LOCAL__arg_lhs(result)); + return result; +} + +PRIVATE WUNUSED NONNULL((1)) LOCAL_return_type DFCALL +LOCAL_sfa_evalas(struct string_format_advanced *__restrict self) { + LOCAL_return_type result = LOCAL_sfa_evalor(self); + if (LOCAL_ISOK(result) && self->sfa_exprtok == SFA_TOK_D_AS) + result = LOCAL_sfa_evalas_operand(self LOCAL__arg_lhs(result)); + return result; +} + +PRIVATE WUNUSED NONNULL((1)) LOCAL_return_type DFCALL +LOCAL_sfa_evalland(struct string_format_advanced *__restrict self) { + LOCAL_return_type result = LOCAL_sfa_evalor(self); + if (LOCAL_ISOK(result) && self->sfa_exprtok == SFA_TOK_LAND) + result = LOCAL_sfa_evalland_operand(self LOCAL__arg_lhs(result)); + return result; +} + +PRIVATE WUNUSED NONNULL((1)) LOCAL_return_type DFCALL +LOCAL_sfa_evallor(struct string_format_advanced *__restrict self) { + LOCAL_return_type result = LOCAL_sfa_evalland(self); + if (LOCAL_ISOK(result) && self->sfa_exprtok == SFA_TOK_LOR) + result = LOCAL_sfa_evallor_operand(self LOCAL__arg_lhs(result)); + return result; +} + +PRIVATE WUNUSED NONNULL((1)) LOCAL_return_type DFCALL +LOCAL_sfa_evalcond(struct string_format_advanced *__restrict self) { +#ifdef __OPTIMIZE_SIZE__ + LOCAL_return_type result = LOCAL_sfa_evallor(self); + if (LOCAL_ISOK(result) && self->sfa_exprtok == '?') + result = LOCAL_sfa_evalcond_operand(self LOCAL__arg_lhs(result)); + return result; +#else /* __OPTIMIZE_SIZE__ */ + LOCAL_return_type result = LOCAL_sfa_evalunary_base(self); + if (LOCAL_ISOK(result)) { + switch (self->sfa_exprtok) { + case '.': + case '[': + case '(': + result = LOCAL_sfa_evalunary_operand(self LOCAL__arg_lhs(result)); + if unlikely(!LOCAL_ISOK(result)) + goto done; + if (self->sfa_exprtok == '*' || self->sfa_exprtok == '/' || + self->sfa_exprtok == '%' || self->sfa_exprtok == SFA_TOK_POW) { + case '*': + case '/': + case '%': + case SFA_TOK_POW: + result = LOCAL_sfa_evalprod_operand(self LOCAL__arg_lhs(result)); + if unlikely(!LOCAL_ISOK(result)) + goto done; + } + if (self->sfa_exprtok == '+' || self->sfa_exprtok == '-') { + case '+': + case '-': + result = LOCAL_sfa_evalsum_operand(self LOCAL__arg_lhs(result)); + if unlikely(!LOCAL_ISOK(result)) + goto done; + } + if (self->sfa_exprtok == SFA_TOK_SHL || self->sfa_exprtok == SFA_TOK_SHR) { + case SFA_TOK_SHL: + case SFA_TOK_SHR: + result = LOCAL_sfa_evalshift_operand(self LOCAL__arg_lhs(result)); + if unlikely(!LOCAL_ISOK(result)) + goto done; + } + if (self->sfa_exprtok == '<' || self->sfa_exprtok == '>' || + self->sfa_exprtok == SFA_TOK_LOWER_EQUAL || + self->sfa_exprtok == SFA_TOK_GREATER_EQUAL) { + case '<': + case '>': + case SFA_TOK_LOWER_EQUAL: + case SFA_TOK_GREATER_EQUAL: + result = LOCAL_sfa_evalcmp_operand(self LOCAL__arg_lhs(result)); + if unlikely(!LOCAL_ISOK(result)) + goto done; + } + if (self->sfa_exprtok == SFA_TOK_EQUAL || self->sfa_exprtok == SFA_TOK_EQUAL3 || + self->sfa_exprtok == SFA_TOK_QMARK_QMARK || + self->sfa_exprtok == SFA_TOK_D_IS || self->sfa_exprtok == SFA_TOK_D_IN || + (self->sfa_inparen && (self->sfa_exprtok == '!' || + self->sfa_exprtok == SFA_TOK_NOT_EQUAL || + self->sfa_exprtok == SFA_TOK_NOT_EQUAL3))) { + __IF0 { + case '!': + case SFA_TOK_NOT_EQUAL: + case SFA_TOK_NOT_EQUAL3: + if (!self->sfa_inparen) + break; + } + case SFA_TOK_EQUAL: + case SFA_TOK_EQUAL3: + case SFA_TOK_QMARK_QMARK: + case SFA_TOK_D_IS: + case SFA_TOK_D_IN: + result = LOCAL_sfa_evalcmpeq_operand(self LOCAL__arg_lhs(result)); + if unlikely(!LOCAL_ISOK(result)) + goto done; + } + if (self->sfa_exprtok == '&') { + case '&': + result = LOCAL_sfa_evaland_operand(self LOCAL__arg_lhs(result)); + if unlikely(!LOCAL_ISOK(result)) + goto done; + } + if (self->sfa_exprtok == '^') { + case '^': + result = LOCAL_sfa_evalxor_operand(self LOCAL__arg_lhs(result)); + if unlikely(!LOCAL_ISOK(result)) + goto done; + } + if (self->sfa_exprtok == '|') { + case '|': + result = LOCAL_sfa_evalor_operand(self LOCAL__arg_lhs(result)); + if unlikely(!LOCAL_ISOK(result)) + goto done; + } + if (self->sfa_exprtok == SFA_TOK_D_AS) { + case SFA_TOK_D_AS: + result = LOCAL_sfa_evalas_operand(self LOCAL__arg_lhs(result)); + if unlikely(!LOCAL_ISOK(result)) + goto done; + } + if (self->sfa_exprtok == SFA_TOK_LAND) { + case SFA_TOK_LAND: + result = LOCAL_sfa_evalland_operand(self LOCAL__arg_lhs(result)); + if unlikely(!LOCAL_ISOK(result)) + goto done; + } + if (self->sfa_exprtok == SFA_TOK_LOR) { + case SFA_TOK_LOR: + result = LOCAL_sfa_evallor_operand(self LOCAL__arg_lhs(result)); + if unlikely(!LOCAL_ISOK(result)) + goto done; + } + if (self->sfa_exprtok == '?') { + case '?': + result = LOCAL_sfa_evalcond_operand(self LOCAL__arg_lhs(result)); + /*if unlikely(!LOCAL_ISOK(result)) + goto done;*/ + } + break; + default: break; + } + } +done: + return result; +#endif /* !__OPTIMIZE_SIZE__ */ +} + + +DECL_END + +#undef DEFINE_sfa_skipexpr +#undef DEFINE_sfa_evalexpr + +#undef LOCAL_ERRVAL +#undef LOCAL_OK__or__lhs +#undef LOCAL_ISOK +#undef LOCAL_return_type +#undef LOCAL__param_lhs +#undef LOCAL__arg_lhs + +#undef LOCAL_sfa_evalunary_base +#undef LOCAL_sfa_evalunary +#undef LOCAL_sfa_evalprod +#undef LOCAL_sfa_evalsum +#undef LOCAL_sfa_evalshift +#undef LOCAL_sfa_evalcmp +#undef LOCAL_sfa_evalcmpeq +#undef LOCAL_sfa_evaland +#undef LOCAL_sfa_evalxor +#undef LOCAL_sfa_evalor +#undef LOCAL_sfa_evalas +#undef LOCAL_sfa_evalland +#undef LOCAL_sfa_evallor +#undef LOCAL_sfa_evalcond +#undef LOCAL_sfa_evalunary_operand +#undef LOCAL_sfa_evalprod_operand +#undef LOCAL_sfa_evalsum_operand +#undef LOCAL_sfa_evalshift_operand +#undef LOCAL_sfa_evalcmp_operand +#undef LOCAL_sfa_evalcmpeq_operand +#undef LOCAL_sfa_evaland_operand +#undef LOCAL_sfa_evalxor_operand +#undef LOCAL_sfa_evalor_operand +#undef LOCAL_sfa_evalas_operand +#undef LOCAL_sfa_evalland_operand +#undef LOCAL_sfa_evallor_operand +#undef LOCAL_sfa_evalcond_operand diff --git a/src/deemon/objects/unicode/format-impl.c.inl b/src/deemon/objects/unicode/format-impl.c.inl new file mode 100644 index 00000000..1acefa10 --- /dev/null +++ b/src/deemon/objects/unicode/format-impl.c.inl @@ -0,0 +1,573 @@ +/* Copyright (c) 2018-2024 Griefer@Work * + * * + * This software is provided 'as-is', without any express or implied * + * warranty. In no event will the authors be held liable for any damages * + * arising from the use of this software. * + * * + * Permission is granted to anyone to use this software for any purpose, * + * including commercial applications, and to alter it and redistribute it * + * freely, subject to the following restrictions: * + * * + * 1. The origin of this software must not be misrepresented; you must not * + * claim that you wrote the original software. If you use this software * + * in a product, an acknowledgement (see the following) in the product * + * documentation is required: * + * Portions Copyright (c) 2018-2024 Griefer@Work * + * 2. Altered source versions must be plainly marked as such, and must not be * + * misrepresented as being the original software. * + * 3. This notice may not be removed or altered from any source distribution. * + */ +#ifdef __INTELLISENSE__ +#include "format.c" +//#define DEFINE_DeeString_FormatPrinter +#define DEFINE_DeeString_FormatWStr +//#define DEFINE_DeeString_Format +#endif /* __INTELLISENSE__ */ + +#if (defined(DEFINE_DeeString_FormatPrinter) + \ + defined(DEFINE_DeeString_FormatWStr) + \ + defined(DEFINE_DeeString_Format)) > 1 +#error "Must #define exactly one of these macros" +#endif /* DEFINE_DeeString_Format... */ + + +#ifndef STRING_FORMAT_WSTR_FOREACH_DONE +#define STRING_FORMAT_WSTR_FOREACH_DONE SSIZE_MIN +#endif /* !STRING_FORMAT_WSTR_FOREACH_DONE */ + +DECL_BEGIN + +#ifdef DEFINE_DeeString_FormatPrinter +#define LOCAL_string_format_data generic_string_format_data +#define LOCAL_string_format_advanced generic_string_format_advanced +#define LOCAL_parse_format_template_for_object_format_ex generic_parse_format_template_for_object_format_ex +#define LOCAL_parse_format_template_for_object_format generic_parse_format_template_for_object_format +#define LOCAL_string_format_wstr_foreach_cb generic_string_format_wstr_foreach_cb +#else /* DEFINE_DeeString_FormatPrinter */ +#define LOCAL_string_format_data unicode_string_format_data +#define LOCAL_string_format_advanced unicode_string_format_advanced +#define LOCAL_parse_format_template_for_object_format_ex unicode_parse_format_template_for_object_format_ex +#define LOCAL_parse_format_template_for_object_format unicode_parse_format_template_for_object_format +#define LOCAL_string_format_wstr_foreach_cb unicode_string_format_wstr_foreach_cb +#endif /* !DEFINE_DeeString_FormatPrinter */ + +#if (defined(DEFINE_DeeString_FormatPrinter) ? !defined(GENERIC_STRING_FORMAT_DATA_DEFINED) \ + : !defined(UNICODE_STRING_FORMAT_DATA_DEFINED)) +#ifdef DEFINE_DeeString_FormatPrinter +#define GENERIC_STRING_FORMAT_DATA_DEFINED +#else /* DEFINE_DeeString_FormatPrinter */ +#define UNICODE_STRING_FORMAT_DATA_DEFINED +#endif /* !DEFINE_DeeString_FormatPrinter */ +struct LOCAL_string_format_data { + struct string_format_parser sfd_parser; /* Underlying parser. */ +#ifdef DEFINE_DeeString_FormatPrinter + Dee_formatprinter_t sfd_sprinter; /* [1..1] Output printer for static data */ + Dee_formatprinter_t sfd_dprinter; /* [1..1] Output printer for dynamic data */ + void *sfd_printarg; /* [?..?] Cookie for `sfd_sprinter' and `sfd_dprinter' */ + Dee_ssize_t sfd_result; /* Result of invocations of `sfd_sprinter' and `sfd_dprinter' (or negative after error) */ +#else /* DEFINE_DeeString_FormatPrinter */ + struct unicode_printer sfd_uprinter; /* Output printer (content leading up to `sfd_parser.sfp_iter' is already printed) */ +#endif /* !DEFINE_DeeString_FormatPrinter */ +}; +#endif /* ... */ + + +#ifdef DEFINE_DeeString_FormatPrinter +#define LOCAL_string_format_data_account(err, sfd, temp) \ + do { \ + if unlikely((temp) < 0) { \ + (sfd)->sfd_result = (temp); \ + goto err; \ + } \ + (sfd)->sfd_result += (temp); \ + } __WHILE0 +#define LOCAL_string_format_data_getsprinter(sfd) ((sfd)->sfd_sprinter) +#define LOCAL_string_format_data_getdprinter(sfd) ((sfd)->sfd_dprinter) +#define LOCAL_string_format_data_getprintarg(sfd) ((sfd)->sfd_printarg) +#else /* DEFINE_DeeString_FormatPrinter */ +#define LOCAL_string_format_data_getsprinter(sfd) ((Dee_formatprinter_t)&unicode_printer_print) +#define LOCAL_string_format_data_getdprinter(sfd) (&unicode_printer_print) +#define LOCAL_string_format_data_getprintarg(sfd) (&(sfd)->sfd_uprinter) +#endif /* !DEFINE_DeeString_FormatPrinter */ + +#ifndef LOCAL_string_format_data_account +#define LOCAL_string_format_data_account(err, sfd, temp) \ + do { \ + if unlikely((temp) < 0) \ + goto err; \ + } __WHILE0 +#endif /* !LOCAL_string_format_data_account */ + +#define LOCAL_string_format_data_sprint(sfd, data, datalen) \ + ((*LOCAL_string_format_data_getsprinter(sfd))(LOCAL_string_format_data_getprintarg(sfd), data, datalen)) +#define LOCAL_string_format_data_dprint(sfd, data, datalen) \ + ((*LOCAL_string_format_data_getdprinter(sfd))(LOCAL_string_format_data_getprintarg(sfd), data, datalen)) + + +#if (defined(DEFINE_DeeString_FormatPrinter) || \ + (defined(DEFINE_DeeString_FormatWStr) && !defined(__OPTIMIZE_SIZE__))) + +/************************************************************************/ +/* ADVANCED STRING FORMATTING */ +/************************************************************************/ + + +/* Implements advanced string format processing. + * - When called, `data->sfd_parser.sfp_iter' points at the first character after the + * first unescaped '{', which is guarantied to not be one of "}!:", and + * `data->sfd_uprinter' contains any static template text leading up to + * (but obviously not including) this first unescaped '{'. + * - Upon success (return >= 0), `data->sfd_uprinter' contains the final + * string, but other fields are left undefined. + * - Upon error (return < 0), `data->sfd_uprinter' must be finalized by + * the caller, and other fields may be undefined. + */ +PRIVATE ATTR_NOINLINE WUNUSED NONNULL((1, 2)) Dee_ssize_t DFCALL +LOCAL_string_format_advanced(struct LOCAL_string_format_data *__restrict data, DeeObject *args) { + Dee_ssize_t temp; + char const *next_brace; + struct string_format_advanced sfa; + sfa.sfa_parser.sfp_iter = data->sfd_parser.sfp_iter; + sfa.sfa_parser.sfp_wend = data->sfd_parser.sfp_wend; + sfa.sfa_args = args; + sfa.sfa_printer = LOCAL_string_format_data_getdprinter(data); + sfa.sfa_arg = LOCAL_string_format_data_getprintarg(data); + +print_advanced_format_arg: + sfa_yield(&sfa); /* Yield first token */ + temp = string_format_advanced_do1(&sfa); + LOCAL_string_format_data_account(err_temp, data, temp); + next_brace = find_next_brace(sfa.sfa_parser.sfp_iter, + sfa.sfa_parser.sfp_wend); +handle_next_brace: + if (!next_brace) { + /* End of template pattern -> append remainder and exit foreach loop */ + temp = LOCAL_string_format_data_sprint(data, sfa.sfa_parser.sfp_iter, + (size_t)(sfa.sfa_parser.sfp_wend - + sfa.sfa_parser.sfp_iter)); + LOCAL_string_format_data_account(err_temp, data, temp); + return 0; + } + +#ifdef DEFINE_DeeString_FormatPrinter + /* Make sure there is at least 1 more char after the '{' */ + if likely((next_brace + 1) >= sfa.sfa_parser.sfp_wend) + return err_invalid_char_after_lbrace_in_simple(""); +#endif /* DEFINE_DeeString_FormatPrinter */ + + /* Print static data until the next '{' or '}' */ + temp = LOCAL_string_format_data_sprint(data, sfa.sfa_parser.sfp_iter, + (size_t)(next_brace - sfa.sfa_parser.sfp_iter)); + LOCAL_string_format_data_account(err_temp, data, temp); + + /* Check for {{ or }}-escape */ + if unlikely(next_brace[0] == next_brace[1]) { + sfa.sfa_parser.sfp_iter = next_brace + 1; + next_brace = find_next_brace(next_brace + 2, sfa.sfa_parser.sfp_wend); + goto handle_next_brace; + } + + /* Make sure that it isn't a '}' */ + if unlikely(*next_brace == '}') + return err_unmatched_rbrace_in_advanced(); + ASSERT(*next_brace == '{'); + sfa.sfa_parser.sfp_iter = next_brace + 1; + goto print_advanced_format_arg; +err_temp: + return temp; +} + + + + + +/************************************************************************/ +/* SIMPLE STRING FORMATTING */ +/************************************************************************/ + +PRIVATE ATTR_NOINLINE WUNUSED NONNULL((1, 2, 3)) Dee_ssize_t DCALL +LOCAL_parse_format_template_for_object_format_ex(struct LOCAL_string_format_data *data, + char const *flush_start, DeeObject *elem) { + Dee_ssize_t result; + char const *next_brace; + struct unicode_printer format_str_printer; + DREF DeeObject *format_str; + unicode_printer_init(&format_str_printer); + if unlikely(unicode_printer_printutf8(&format_str_printer, data->sfd_parser.sfp_iter, + (size_t)(flush_start - data->sfd_parser.sfp_iter) - 1) < 0) + goto err_format_str_printer; + next_brace = find_next_brace(flush_start, data->sfd_parser.sfp_wend); +again_handle_next_brace: + if unlikely(!next_brace) + goto err_format_str_printer_unmatched; + if unlikely(next_brace[0] == next_brace[1]) { + ++next_brace; +/*handle_brace_after_escape_character:*/ + if unlikely(unicode_printer_printutf8(&format_str_printer, flush_start, + (size_t)(next_brace - flush_start) - 1) < 0) + goto err_format_str_printer; + flush_start = next_brace; + next_brace = find_next_brace(flush_start + 1, data->sfd_parser.sfp_wend); + goto again_handle_next_brace; + } + if unlikely(next_brace[0] == '{') + goto err_format_str_printer_err_lbrace_in_format_in_simple; + if unlikely(unicode_printer_printutf8(&format_str_printer, flush_start, + (size_t)(next_brace - flush_start)) < 0) + goto err_format_str_printer; + data->sfd_parser.sfp_iter = next_brace; + format_str = unicode_printer_pack(&format_str_printer); + if unlikely(!format_str) + goto err; + result = DeeObject_PrintFormat(elem, + LOCAL_string_format_data_getdprinter(data), + LOCAL_string_format_data_getprintarg(data), + format_str); + Dee_Decref(format_str); + return result; +err_format_str_printer_err_lbrace_in_format_in_simple: + err_lbrace_in_format_in_simple(); + goto err_format_str_printer; +err_format_str_printer_unmatched: + err_unmatched_lbrace_in_simple(); +err_format_str_printer: + unicode_printer_fini(&format_str_printer); +err: + return -1; +} + + + +/* Handler for "{:...}" patterns in simple template strings. + * Called with `data->sfd_parser.sfp_iter' already pointing after the ':'-character. + * - Upon success (return >= 0), `data->sfd_parser.sfp_iter' points at the closing '}' + * - On error (return < 0), `data->sfd_parser.sfp_iter' is left undefined. */ +PRIVATE ATTR_NOINLINE WUNUSED NONNULL((1, 2)) Dee_ssize_t DCALL +LOCAL_parse_format_template_for_object_format(struct LOCAL_string_format_data *data, DeeObject *elem) { + char const *next_brace, *format_start; + next_brace = find_next_brace(data->sfd_parser.sfp_iter, data->sfd_parser.sfp_wend); + if unlikely(!next_brace) + return err_unmatched_lbrace_in_simple(); + /* Check for {{ or }}-escape */ + if unlikely(next_brace[0] == next_brace[1]) + return LOCAL_parse_format_template_for_object_format_ex(data, next_brace + 1, elem); + if unlikely(next_brace[0] == '{') + return err_lbrace_in_format_in_simple(); + ASSERT(next_brace[0] == '}'); + format_start = data->sfd_parser.sfp_iter; + data->sfd_parser.sfp_iter = next_brace; + return DeeObject_PrintFormatString(elem, + LOCAL_string_format_data_getdprinter(data), + LOCAL_string_format_data_getprintarg(data), + format_start, (size_t)(next_brace - format_start)); +} + +PRIVATE WUNUSED NONNULL((2)) Dee_ssize_t DCALL +LOCAL_string_format_wstr_foreach_cb(void *arg, DeeObject *elem) { +#ifdef DEFINE_DeeString_FormatPrinter +#define LOCAL_err_printerr done +#else /* DEFINE_DeeString_FormatPrinter */ +#define LOCAL_err_printerr err +#endif /* !DEFINE_DeeString_FormatPrinter */ + Dee_ssize_t temp; + char const *next_brace; + struct LOCAL_string_format_data *data; + data = (struct LOCAL_string_format_data *)arg; + switch (*data->sfd_parser.sfp_iter) { + + case '}': +do_append_str: + temp = DeeObject_Print(elem, + LOCAL_string_format_data_getdprinter(data), + LOCAL_string_format_data_getprintarg(data)); + break; + + case '!': + ++data->sfd_parser.sfp_iter; + switch (__builtin_expect(*data->sfd_parser.sfp_iter, 'r')) { + case 'r': + ++data->sfd_parser.sfp_iter; + if unlikely(*data->sfd_parser.sfp_iter != '}') + goto err_missing_rbrace; +/*do_append_repr:*/ + temp = DeeObject_PrintRepr(elem, + LOCAL_string_format_data_getdprinter(data), + LOCAL_string_format_data_getprintarg(data)); + break; + case 's': + case 'a': + ++data->sfd_parser.sfp_iter; + if unlikely(*data->sfd_parser.sfp_iter != '}') { +err_missing_rbrace: + return err_invalid_char_after_lbrace_exclaim_spec_in_simple(data->sfd_parser.sfp_iter[-1], + data->sfd_parser.sfp_iter); + } + goto do_append_str; + default: + return err_unknown_repr_mode_in_simple(data->sfd_parser.sfp_iter); + } + break; + + case ':': + /* Must use `DeeObject_PrintFormatString()' */ + ++data->sfd_parser.sfp_iter; + temp = LOCAL_parse_format_template_for_object_format(data, elem); + break; + + default: + /* When using a non-simple pattern string, *all* + * format elements must use a non-simple format. */ + return err_invalid_char_after_lbrace_in_simple(data->sfd_parser.sfp_iter); + } + LOCAL_string_format_data_account(LOCAL_err_printerr, data, temp); + ASSERT(*data->sfd_parser.sfp_iter == '}'); + ++data->sfd_parser.sfp_iter; + next_brace = find_next_brace(data->sfd_parser.sfp_iter, + data->sfd_parser.sfp_wend); +handle_next_lbrace: + if (!next_brace) { + /* End of template pattern -> append remainder and exit foreach loop */ + temp = LOCAL_string_format_data_sprint(data, data->sfd_parser.sfp_iter, + (size_t)(data->sfd_parser.sfp_wend - + data->sfd_parser.sfp_iter)); + LOCAL_string_format_data_account(LOCAL_err_printerr, data, temp); + goto done; + } + if (next_brace[1] == next_brace[0]) { + /* Special case: double left-or-right brace */ + ++next_brace; +/*handle_brace_after_escape_character:*/ + temp = LOCAL_string_format_data_sprint(data, data->sfd_parser.sfp_iter, + (size_t)(next_brace - data->sfd_parser.sfp_iter) - 1); + LOCAL_string_format_data_account(LOCAL_err_printerr, data, temp); + data->sfd_parser.sfp_iter = next_brace; + ++next_brace; + next_brace = find_next_brace(next_brace, data->sfd_parser.sfp_wend); + goto handle_next_lbrace; + } + if unlikely(*next_brace == '}') + return err_unmatched_rbrace_in_simple(); + ASSERT(*next_brace == '{'); + temp = LOCAL_string_format_data_sprint(data, data->sfd_parser.sfp_iter, + (size_t)(next_brace - data->sfd_parser.sfp_iter)); + LOCAL_string_format_data_account(LOCAL_err_printerr, data, temp); + data->sfd_parser.sfp_iter = next_brace + 1; + return 0; +done: + return STRING_FORMAT_WSTR_FOREACH_DONE; +#ifndef DEFINE_DeeString_FormatPrinter +err: + return -1; +#endif /* !DEFINE_DeeString_FormatPrinter */ +#undef LOCAL_err_printerr +} +#else /* DEFINE_DeeString_FormatPrinter || (DEFINE_DeeString_FormatWStr && !__OPTIMIZE_SIZE__) */ + +PRIVATE ATTR_NOINLINE WUNUSED NONNULL((1, 2)) Dee_ssize_t DFCALL +LOCAL_string_format_advanced(struct LOCAL_string_format_data *__restrict data, + DeeObject *args); + +PRIVATE WUNUSED NONNULL((2)) Dee_ssize_t DCALL +LOCAL_string_format_wstr_foreach_cb(void *arg, DeeObject *elem); + +#endif /* !DEFINE_DeeString_FormatPrinter && (!DEFINE_DeeString_FormatWStr || __OPTIMIZE_SIZE__) */ + + + +#ifdef DEFINE_DeeString_FormatPrinter +DFUNDEF WUNUSED NONNULL((1, 3)) Dee_ssize_t DCALL +DeeString_FormatPrinter(char const *pattern, size_t pattern_length, DeeObject *args, + Dee_formatprinter_t pattern_printer, + Dee_formatprinter_t data_printer, void *arg) +#define LOCAL_pattern pattern +#define LOCAL_pattern_length pattern_length +#elif defined(DEFINE_DeeString_FormatWStr) +DFUNDEF WUNUSED NONNULL((1, 2)) DREF DeeObject *DFCALL +DeeString_FormatWStr(/*utf-8*/ char const *pattern_wstr, DeeObject *args) +#define LOCAL_pattern pattern_wstr +#define LOCAL_pattern_length WSTR_LENGTH(pattern_wstr) +#else /* DEFINE_DeeString_Format */ +DFUNDEF WUNUSED NONNULL((1, 2)) DREF DeeObject *DFCALL +DeeString_Format(DeeObject *pattern, DeeObject *args) +#define LOCAL_pattern pattern_wstr +#define LOCAL_pattern_length WSTR_LENGTH(pattern_wstr) +#endif +{ +#if defined(DEFINE_DeeString_Format) && defined(__OPTIMIZE_SIZE__) + char const *pattern_wstr; + pattern_wstr = DeeString_AsUtf8(pattern); + if unlikely(!pattern_wstr) + return NULL; + return DeeString_FormatWStr(pattern_wstr, args); +#elif defined(DEFINE_DeeString_FormatWStr) && defined(__OPTIMIZE_SIZE__) + Dee_ssize_t status; + struct unicode_printer printer; + unicode_printer_init(&printer); + status = DeeString_FormatPrinter(pattern_wstr, WSTR_LENGTH(pattern_wstr), args, + (Dee_formatprinter_t)&unicode_printer_printutf8, + &unicode_printer_print, &printer); + if unlikely(status < 0) + goto err; + return unicode_printer_pack(&printer); +err: + unicode_printer_fini(&printer); + return NULL; +#else /* ... */ +#ifdef DEFINE_DeeString_Format + char const *pattern_wstr; +#endif /* DEFINE_DeeString_Format */ + Dee_ssize_t temp; + struct LOCAL_string_format_data data; +#ifdef DEFINE_DeeString_Format + pattern_wstr = DeeString_AsUtf8(pattern); + if unlikely(!pattern_wstr) + goto err; +#define WANT_err +#endif /* DEFINE_DeeString_Format */ + + data.sfd_parser.sfp_wend = LOCAL_pattern + LOCAL_pattern_length; + data.sfd_parser.sfp_iter = find_next_brace(LOCAL_pattern, data.sfd_parser.sfp_wend); + if unlikely(!data.sfd_parser.sfp_iter) { +#ifdef DEFINE_DeeString_Format + return_reference_(pattern); /* Can just re-return "pattern" as-is. */ +#elif defined(DEFINE_DeeString_FormatWStr) + return DeeString_NewUtf8(LOCAL_pattern, WSTR_LENGTH(LOCAL_pattern), STRING_ERROR_FIGNORE); +#elif defined(DEFINE_DeeString_FormatPrinter) + return (*pattern_printer)(arg, LOCAL_pattern, LOCAL_pattern_length); +#endif /* ... */ + } + +#ifdef DEFINE_DeeString_FormatPrinter + data.sfd_sprinter = pattern_printer; + data.sfd_dprinter = data_printer; + data.sfd_printarg = arg; + data.sfd_result = 0; +#define LOCAL_err_printerr done +#else /* DEFINE_DeeString_FormatPrinter */ + unicode_printer_init(&data.sfd_uprinter); +#define LOCAL_err_printerr err_printer +#endif /* !DEFINE_DeeString_FormatPrinter */ +again_handle_brace: +#ifdef DEFINE_DeeString_FormatPrinter + /* Make sure there is at least 1 more char after the '{' */ + if likely((data.sfd_parser.sfp_iter + 1) >= data.sfd_parser.sfp_wend) + return err_invalid_char_after_lbrace_in_simple(""); +#endif /* DEFINE_DeeString_FormatPrinter */ + temp = LOCAL_string_format_data_sprint(&data, LOCAL_pattern, + (size_t)(data.sfd_parser.sfp_iter - LOCAL_pattern)); + LOCAL_string_format_data_account(LOCAL_err_printerr, &data, temp); + if unlikely(*data.sfd_parser.sfp_iter == '}') { + if likely(data.sfd_parser.sfp_iter[1] == '}') { + /* Special case: double right brace '}}' */ +/*handle_brace_before_escape_character:*/ + ++data.sfd_parser.sfp_iter; + goto handle_brace_after_escape_character; + } +#ifdef DEFINE_DeeString_FormatPrinter + data.sfd_result = err_unmatched_rbrace_in_simple(); +#else /* DEFINE_DeeString_FormatPrinter */ + err_unmatched_rbrace_in_simple(); +#endif /* !DEFINE_DeeString_FormatPrinter */ + goto LOCAL_err_printerr; + } + + /* At this point, we've got the first unescaped '{' at "data.sfd_parser.sfp_iter". + * Figure out if this is a simple, or advanced pattern and act accordingly. */ + ++data.sfd_parser.sfp_iter; + switch (__builtin_expect(*data.sfd_parser.sfp_iter, '}')) { + case '}': + case '!': + case ':': { + /* Simple pattern */ + + /* Assume that this is a "simple" format string, as generated by template strings. + * >> local x = f"foo = {foo}, bar = {bar}"; + * generated: + * >> local x = "foo = {}, bar = {}".format({ foo, bar }); + * + * In this case, we use DeeObject_Foreach() to enumerate `args' + * and append format string parts, as well as argument elements + * onto the resulting string. */ + temp = DeeObject_Foreach(args, &LOCAL_string_format_wstr_foreach_cb, &data); + if likely(temp == STRING_FORMAT_WSTR_FOREACH_DONE) + goto done; /* Likely case: the pattern was fully printed without a premature end of `args' */ + if unlikely(temp < 0) { +#ifdef DEFINE_DeeString_FormatPrinter + data.sfd_result = temp; +#endif /* DEFINE_DeeString_FormatPrinter */ + goto LOCAL_err_printerr; + } + /* Error: `args' ended too early */ + goto err_not_enough_args; + } break; + + case '{': +handle_brace_after_escape_character: + /* Special case: double left-or-right brace */ + LOCAL_pattern = data.sfd_parser.sfp_iter; + data.sfd_parser.sfp_iter = find_next_brace(LOCAL_pattern + 1, data.sfd_parser.sfp_wend); + if unlikely(!data.sfd_parser.sfp_iter) { +/*done_printer_remainder:*/ + temp = LOCAL_string_format_data_sprint(&data, LOCAL_pattern, + (size_t)(data.sfd_parser.sfp_wend - LOCAL_pattern)); + LOCAL_string_format_data_account(LOCAL_err_printerr, &data, temp); + goto done; + } + goto again_handle_brace; + + default: + /* Advanced pattern */ + temp = LOCAL_string_format_advanced(&data, args); + if unlikely(temp < 0) { +#ifdef DEFINE_DeeString_FormatPrinter + data.sfd_result = temp; +#endif /* DEFINE_DeeString_FormatPrinter */ + goto LOCAL_err_printerr; + } + break; + } +done: +#ifdef DEFINE_DeeString_FormatPrinter + return data.sfd_result; +#else /* DEFINE_DeeString_FormatPrinter */ + return unicode_printer_pack(&data.sfd_uprinter); +#endif /* !DEFINE_DeeString_FormatPrinter */ +err_not_enough_args: + DeeError_Throwf(&DeeError_UnpackError, + "Insufficient number of arguments"); +#ifndef DEFINE_DeeString_FormatPrinter +err_printer: + unicode_printer_fini(&data.sfd_uprinter); +#endif /* !DEFINE_DeeString_FormatPrinter */ +#ifdef WANT_err +#undef WANT_err +err: +#endif /* WANT_err */ +#ifdef DEFINE_DeeString_FormatPrinter + return -1; +#else /* DEFINE_DeeString_FormatPrinter */ + return NULL; +#endif /* !DEFINE_DeeString_FormatPrinter */ +#undef LOCAL_err_printerr +#undef LOCAL_pattern_length +#undef LOCAL_pattern +#endif /* !... */ +} + + +#undef LOCAL_string_format_data +#undef LOCAL_string_format_advanced +#undef LOCAL_parse_format_template_for_object_format_ex +#undef LOCAL_parse_format_template_for_object_format +#undef LOCAL_string_format_wstr_foreach_cb + +#undef LOCAL_string_format_data_getsprinter +#undef LOCAL_string_format_data_getdprinter +#undef LOCAL_string_format_data_getprintarg +#undef LOCAL_string_format_data_account + +DECL_END + +#undef DEFINE_DeeString_FormatPrinter +#undef DEFINE_DeeString_FormatWStr +#undef DEFINE_DeeString_Format diff --git a/src/deemon/objects/unicode/format.c b/src/deemon/objects/unicode/format.c index bab48ccd..38d44d25 100644 --- a/src/deemon/objects/unicode/format.c +++ b/src/deemon/objects/unicode/format.c @@ -17,11 +17,12 @@ * misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source distribution. * */ -#ifndef GUARD_DEEMON_OBJECTS_UNICODE_ISORMAT_C -#define GUARD_DEEMON_OBJECTS_UNICODE_ISORMAT_C 1 +#ifndef GUARD_DEEMON_OBJECTS_UNICODE_FORMAT_C +#define GUARD_DEEMON_OBJECTS_UNICODE_FORMAT_C 1 #include #include +#include #include #include #include @@ -30,6 +31,8 @@ #include #include #include +#include +#include #include #include #include @@ -38,8 +41,997 @@ #include "../../runtime/runtime_error.h" +#undef SSIZE_MIN +#undef SSIZE_MAX +#include +#define SSIZE_MIN __SSIZE_MIN__ +#define SSIZE_MAX __SSIZE_MAX__ + DECL_BEGIN +/* + * =========================== Format strings =========================== + * + * These format patterns are used to drive template strings, as well as + * provide for more advanced patterns. They come in 2 variants: + * - Simple + * - Advanced + * - These patterns are mutually exclusive, allowing detection of which + * one we're dealing with. Additionally, even though you might think + * that you're allowed to mix these 2 variants, you can't. You must + * always use either one, or the other for the *entire* format pattern. + * + * + * Examples: + * - Simple: + * >> print r"\{foo = {}, bar = {}\}".format({ 10, 20 }); // "{foo = 10, bar = 20}" + * >> print r"{{foo = {}, bar = {}}}".format({ 10, 20 }); // "{foo = 10, bar = 20}" + * - Advanced: + * >> // "Hello Mr Simpson\nWe are contacting you regarding your INSURANCE" + * >> print "Hello {salutation} {surname}\nWe are contacting you regarding your {subject.upper()}" + * >> .format({ "salutation": "Mr", "surname": "Simpson", "subject": "Insurance" }); + * >> + * >> // "Hello Mr Simpson\nWe are contacting you regarding your INSURANCE" + * >> print "Hello {0} {1}\nWe are contacting you regarding your {2.upper()}" + * >> .format({ "Mr", "Simpson", "Insurance" }); + * + * + * Definitions: + * >> TEXT ::= | '{{' | '}}'; + * >> + * >> SIMPLE_TEMPLATE ::= [TEXT...] + * >> [ // Start of template argument + * >> '{' + * >> // Template argument spec (when not given, defaults to "!s") + * >> [ + * >> ('!' ( + * >> 'r' | // Insert `DeeObject_Repr()' of next argument + * >> 's' | // Insert `DeeObject_Str()' of next argument + * >> 'a' // Insert `DeeObject_Str()' of next argument + * >> )) | + * >> // Insert `DeeObject_PrintFormatString()' of next argument + * >> // The format string used cannot include other arguments. + * >> (':' [TEXT...]) + * >> ] + * >> '}' + * >> ] + * >> [SIMPLE_TEMPLATE]; + * >> + * >> ADVANCED_TEMPLATE ::= [TEXT...] + * >> [ // Start of template argument + * >> '{' + * >> ARGUMENT_EXPR + * >> [ + * >> ('!' ( + * >> 'r' | // Insert `DeeObject_Repr()' of "ARGUMENT_EXPR" + * >> 's' | // Insert `DeeObject_Str()' of "ARGUMENT_EXPR" + * >> 'a' // Insert `DeeObject_Str()' of "ARGUMENT_EXPR" + * >> )) | + * >> // Insert `DeeObject_PrintFormatString()' of "ARGUMENT_EXPR" + * >> // The format string used is a recursive template string! + * >> (':' [ADVANCED_TEMPLATE...]) + * >> ] + * >> '}' + * >> ] + * >> [ADVANCED_TEMPLATE]; + * >> + * >> ARGUMENT_EXPR_UNARY_BASE ::= ( + * >> NUMBER // Uses "arg = args[int()]" + * >> | SYMBOL // Uses "arg = args[string()]" + * >> | ('"' [...] '"') // Uses "arg = string()" (accepts c-escape) + * >> | ("'" [...] "'") // Uses "arg = string()" (accepts c-escape) + * >> | ('$' NUMBER) // Uses "arg = int()" + * >> | ('$' [NUMBER] '.' NUMBER) // Uses "arg = float()" // XXX: Document that this also accepts that E-suffix + * >> | ('$true') // Uses "arg = true" + * >> | ('$false') // Uses "arg = false" + * >> | ('$none') // Uses "arg = none" + * >> | ('$str' '(' ARGUMENT_EXPR ')') // Uses "arg = str(ARGUMENT_EXPR)" + * >> | ('$repr' '(' ARGUMENT_EXPR ')') // Uses "arg = repr(ARGUMENT_EXPR)" + * >> | ('$copy' '(' ARGUMENT_EXPR ')') // Uses "arg = copy(ARGUMENT_EXPR)" + * >> | ('$deepcopy' '(' ARGUMENT_EXPR ')') // Uses "arg = deepcopy(ARGUMENT_EXPR)" + * >> | ('$type' '(' ARGUMENT_EXPR ')') // Uses "arg = type(ARGUMENT_EXPR)" + * >> | ('!' ARGUMENT_EXPR_UNARY) // Uses "arg = !ARGUMENT_EXPR" (Not allowed at start of expr; "{!foo}" is malformed; use "{(!foo)}" instead!) + * >> | ('#' ARGUMENT_EXPR_UNARY) // Uses "arg = #ARGUMENT_EXPR" + * >> | ('+' ARGUMENT_EXPR_UNARY) // Uses "arg = +ARGUMENT_EXPR" + * >> | ('-' ARGUMENT_EXPR_UNARY) // Uses "arg = -ARGUMENT_EXPR" + * >> | ('~' ARGUMENT_EXPR_UNARY) // Uses "arg = ~ARGUMENT_EXPR" + * >> | ('(' ARGUMENT_EXPR ')') // Uses "arg = ARGUMENT_EXPR" + * >> | ('(' ARGUMENT_EXPR_EXP ',' [',' ~~ (ARGUMENT_EXPR_EXP...)] ')') // Uses "arg = Sequence({ARGUMENT_EXPR,...})" (Yes: sequence type isn't mandatory here; not necessary a Tuple) + * >> | ('[' ARGUMENT_EXPR_EXP [',' [',' ~~ (ARGUMENT_EXPR_EXP...)]] ']') // Uses "arg = Sequence({ARGUMENT_EXPR,...})" (Yes: sequence type isn't mandatory here; not necessary a List) + * >> | ('$bound' '(' ARGUMENT_EXPR_UNARY_BASE '.' SYMBOL ')') + * >> | ('$bound' '(' ARGUMENT_EXPR_UNARY_BASE '.' '{' ARGUMENT_EXPR '}' ')') + * >> ); + * >> + * >> ARGUMENT_EXPR_UNARY ::= ARGUMENT_EXPR_UNARY_BASE [( + * >> ('.' SYMBOL) // Modify `arg = arg.operator . (SYMBOL)' // Getattr operation + * >> ('.' '{' ARGUMENT_EXPR '}') // Modify `arg = arg.operator . (ARGUMENT_EXPR)' // Getattr operation + * >> | ('[' ARGUMENT_EXPR ']') // Modify `arg = arg.operator [] (ARGUMENT_EXPR)' // Getitem operation + * >> | ('[' ARGUMENT_EXPR ':' ARGUMENT_EXPR ']') // Modify `arg = arg.operator [] (ARGUMENT_EXPR, ARGUMENT_EXPR)' // Getrange operation + * >> | ('(' ARGUMENT_EXPR_CALL_ARGS ')') // Modify `arg = arg.operator () (ARGUMENT_EXPR_CALL_ARGS)' // Call operation + * >> )...]; + * >> + * >> ARGUMENT_EXPR_CALL_ARGS ::= [',' ~~ (ARGUMENT_EXPR_EXP...)] [ + * >> [','] // If unlabelled arguments were also used + * >> (',' ~~ ( + * >> SYMBOL ':' ARGUMENT_EXPR + * >> )...) + * >> ]; + * >> + * >> ARGUMENT_EXPR_PROD ::= ARGUMENT_EXPR_UNARY [( + * >> ('*' ARGUMENT_EXPR_UNARY) + * >> | ('/' ARGUMENT_EXPR_UNARY) + * >> | ('%' ARGUMENT_EXPR_UNARY) + * >> | ('**' ARGUMENT_EXPR_UNARY) + * >> )...]; + * >> + * >> ARGUMENT_EXPR_SUM ::= ARGUMENT_EXPR_PROD [( + * >> ('+' ARGUMENT_EXPR_PROD) + * >> | ('-' ARGUMENT_EXPR_PROD) + * >> )...]; + * >> + * >> ARGUMENT_EXPR_SHIFT ::= ARGUMENT_EXPR_SUM [( + * >> ('<<' ARGUMENT_EXPR_SUM) + * >> | ('>>' ARGUMENT_EXPR_SUM) + * >> )...]; + * >> + * >> ARGUMENT_EXPR_CMP ::= ARGUMENT_EXPR_SHIFT [( + * >> ('<' ARGUMENT_EXPR_SHIFT) + * >> | ('<=' ARGUMENT_EXPR_SHIFT) + * >> | ('>' ARGUMENT_EXPR_SHIFT) + * >> | ('>=' ARGUMENT_EXPR_SHIFT) + * >> )...]; + * >> + * >> ARGUMENT_EXPR_CMPEQ ::= ARGUMENT_EXPR_CMP [( + * >> ('==' ARGUMENT_EXPR_CMP) + * >> | ('!=' ARGUMENT_EXPR_CMP) (Not allowed at start of expr; "{foo != bar}" is malformed; use "{(foo != bar)}" instead!) + * >> | ('===' ARGUMENT_EXPR_CMP) + * >> | ('!==' ARGUMENT_EXPR_CMP) (Not allowed at start of expr; "{foo !== bar}" is malformed; use "{(foo !== bar)}" instead!) + * >> | ('??' ARGUMENT_EXPR_CMP) + * >> | ('$is' ARGUMENT_EXPR_CMP) + * >> | ('!' '$is' ARGUMENT_EXPR_CMP) (Not allowed at start of expr; "{foo !$is bar}" is malformed; use "{(foo !$is bar)}" instead!) + * >> | ('$in' ARGUMENT_EXPR_CMP) + * >> | ('!' '$in' ARGUMENT_EXPR_CMP) (Not allowed at start of expr; "{foo !$in bar}" is malformed; use "{(foo !$in bar)}" instead!) + * >> )...]; + * >> + * >> ARGUMENT_EXPR_AND ::= ARGUMENT_EXPR_CMPEQ [('&' ARGUMENT_EXPR_CMPEQ)...]; + * >> ARGUMENT_EXPR_XOR ::= ARGUMENT_EXPR_AND [('^' ARGUMENT_EXPR_AND)...]; + * >> ARGUMENT_EXPR_OR ::= ARGUMENT_EXPR_XOR [('|' ARGUMENT_EXPR_XOR)...]; + * >> ARGUMENT_EXPR_AS ::= ARGUMENT_EXPR_OR [('$as' ARGUMENT_EXPR_OR)...]; + * >> ARGUMENT_EXPR_LAND ::= ARGUMENT_EXPR_AS [('&&' ARGUMENT_EXPR_AS)...]; + * >> ARGUMENT_EXPR_LOR ::= ARGUMENT_EXPR_LAND [('||' ARGUMENT_EXPR_LAND)...]; + * >> ARGUMENT_EXPR_COND ::= ARGUMENT_EXPR_LOR ['?' ARGUMENT_EXPR_LOR : ARGUMENT_EXPR_COND]; // ff-branch is mandatory to prevent ambiguity in case of "{foo?bar:baz}" (is "baz" the argument for "(foo?bar).__format__", or is it the ff-branch?) + * >> + * >> ARGUMENT_EXPR ::= ARGUMENT_EXPR_COND; // Note that in here, whitespace outside of "strings" is ignored + * >> ARGUMENT_EXPR_EXP ::= ARGUMENT_EXPR ['...']; + * >> + * >> NUMBER ::= ['0x'|'0X'|'0b'|'0B'] ('0'...'9')...; + * >> SYMBOL ::= SYMBOL_START [SYMBOL_CONT...]; + * >> SYMBOL_START ::= (('a'...'z')|('A'...'Z')|'_'|'$'); // Anything matching `DeeUni_IsSymStrt()' + * >> SYMBOL_CONT ::= SYMBOL_START|('0'...'9'); // Anything matching `DeeUni_IsSymCont()' + * + */ + + +PRIVATE ATTR_COLD int DCALL err_lbrace_in_format_in_simple(void) { + return DeeError_Throwf(&DeeError_ValueError, + "Unescaped '{' in {:format-string} in simple format " + "pattern (only allowed in advanced patterns)"); +} + +PRIVATE ATTR_COLD int DCALL err_unmatched_lbrace_in_simple(void) { + return DeeError_Throwf(&DeeError_ValueError, + "Unmatched '{' in simple format pattern"); +} + +PRIVATE ATTR_COLD int DCALL err_unmatched_lbrace_in_advanced(void) { + return DeeError_Throwf(&DeeError_ValueError, + "Unmatched '{' in simple advanced pattern"); +} + +PRIVATE ATTR_COLD int DCALL err_unmatched_rbrace_in_simple(void) { + return DeeError_Throwf(&DeeError_ValueError, + "Unmatched '}' in simple format pattern"); +} + +PRIVATE ATTR_COLD int DCALL err_unmatched_rbrace_in_advanced(void) { + return DeeError_Throwf(&DeeError_ValueError, + "Unmatched '}' in advanced format pattern"); +} + +PRIVATE ATTR_COLD int DCALL err_unknown_repr_mode_in_simple(char const *mode) { + return DeeError_Throwf(&DeeError_ValueError, + "Unrecognized object repesentation mode '{!%.1q}' in simple format pattern", + mode); +} + +PRIVATE ATTR_COLD int DCALL err_unknown_repr_mode_in_advanced(char const *mode) { + return DeeError_Throwf(&DeeError_ValueError, + "Unrecognized object repesentation mode '{!%.1q}' in advanced format pattern", + mode); +} + +PRIVATE ATTR_COLD int DCALL err_invalid_char_after_lbrace_in_simple(char const *ptr) { + return DeeError_Throwf(&DeeError_ValueError, + "Invalid character %.1q following `{' in simple format pattern", + ptr); + +} + +PRIVATE ATTR_COLD int DCALL err_invalid_char_after_expr_in_advanced(char const *ptr) { + return DeeError_Throwf(&DeeError_ValueError, + "Invalid character %.1q following `{' in advanced format pattern", + ptr); + +} + +PRIVATE ATTR_COLD int DCALL err_invalid_char_after_lbrace_exclaim_spec_in_simple(char spec, char const *ptr) { + return DeeError_Throwf(&DeeError_ValueError, + "Expected '}' after '{!%c' in simple format pattern, but got %.1q", + spec, ptr); +} + +PRIVATE ATTR_COLD int DCALL err_invalid_char_after_lbrace_exclaim_spec_in_advanced(char spec, char const *ptr) { + return DeeError_Throwf(&DeeError_ValueError, + "Expected '}' after '{!%c' in advanced format pattern, but got %.1q", + spec, ptr); +} + +/* Return a pointer to the next '{' or '}' character. + * If not found, return `NULL' instead. */ +PRIVATE WUNUSED char const *DCALL +find_next_brace(char const *str, char const *end) { + for (; str < end; ++str) { + if (*str == '{' || *str == '}') + return str; + } + return NULL; +} + + + +enum { + /* Special tokens. */ + SFA_TOK_EOF = '\0', /* END-OF-FILE (will always be ZERO) */ + SFA_TOK_CHAR = '\'', /* 'f'. */ + SFA_TOK_STRING = '\"', /* "foobar" */ + SFA_TOK_INT = '0', /* 42 */ + + /* Double (or longer) tokens. */ + SFA_TOK_TWOCHAR_BEGIN = 256, + SFA_TOK_SHL = SFA_TOK_TWOCHAR_BEGIN, /* "<<". */ + SFA_TOK_SHR, /* ">>". */ + SFA_TOK_EQUAL, /* "==". */ + SFA_TOK_NOT_EQUAL, /* "!=". */ + SFA_TOK_GREATER_EQUAL, /* ">=". */ + SFA_TOK_LOWER_EQUAL, /* "<=". */ + SFA_TOK_DOTS, /* "...". */ + SFA_TOK_LAND, /* "&&". */ + SFA_TOK_LOR, /* "||". */ + SFA_TOK_POW, /* "**". */ + SFA_TOK_EQUAL3, /* "===". */ + SFA_TOK_NOT_EQUAL3, /* "!==". */ + SFA_TOK_QMARK_QMARK, /* "??". */ + SFA_TOK_D_IS, /* "$is". */ + SFA_TOK_D_IN, /* "$in". */ + SFA_TOK_D_AS, /* "$as". */ + SFA_TOK_D_TRUE, /* "$true". */ + SFA_TOK_D_FALSE, /* "$false". */ + SFA_TOK_D_NONE, /* "$none". */ + SFA_TOK_D_STR, /* "$str". */ + SFA_TOK_D_REPR, /* "$repr". */ + SFA_TOK_D_COPY, /* "$copy". */ + SFA_TOK_D_DEEPCOPY, /* "$deepcopy". */ + SFA_TOK_D_TYPE, /* "$type". */ + SFA_TOK_D_BOUND, /* "$bound". */ + SFA_TOK_D_INT, /* "$42". */ + SFA_TOK_KEYWORD, /* KEEP THIS THE LAST TOKEN! */ +}; + +struct string_format_parser { + char const *sfp_iter; /* [1..1] Pointer just after the next '{' character. */ + char const *sfp_wend; /* [1..1] Input string end pointer. */ +}; + +struct string_format_advanced { + struct string_format_parser sfa_parser; /* Underlying parser */ + DeeObject *sfa_args; /* [1..1] Format template arguments */ + Dee_formatprinter_t sfa_printer; /* [1..1] Output printer */ + void *sfa_arg; /* [?..?] Cookie for `sfa_printer' */ + unsigned int sfa_exprtok; /* Current expression token (`sfa_parser.sfp_iter' usually points after the + * token, expect for `SFA_TOK_CHAR', `SFA_TOK_STRING', `SFA_TOK_INT' and + * `SFA_TOK_KEYWORD', where it points to the start) */ + bool sfa_inparen; /* Are we within parenthesis? */ +}; + + +PRIVATE NONNULL((1)) unsigned int DFCALL +sfa_yield(struct string_format_advanced *__restrict self) { +#define RETURN0(x) do{ result = (x); goto done0; }__WHILE0 +#define RETURN1(x) do{ result = (x); goto done1; }__WHILE0 +#define RETURN2(x) do{ result = (x); goto done2; }__WHILE0 +#define RETURN3(x) do{ result = (x); goto done3; }__WHILE0 +#define RETURN5(x) do{ result = (x); goto done5; }__WHILE0 +#define RETURN6(x) do{ result = (x); goto done6; }__WHILE0 +#define RETURN9(x) do{ result = (x); goto done9; }__WHILE0 + unsigned int result; + unsigned char ch; + char const *ptr; + ptr = self->sfa_parser.sfp_iter; +again: + if unlikely(ptr >= self->sfa_parser.sfp_wend) + RETURN0(SFA_TOK_EOF); + ch = (unsigned char)*ptr; + switch (ch) { + + case '<': + case '>': + case '=': + case '!': + case '.': + case '&': + case '|': + case '*': + case '?': + if likely((ptr + 1) < self->sfa_parser.sfp_wend) { + unsigned char nextch = (unsigned char)ptr[1]; + switch (ch) { + case '<': + if (nextch == '<') + RETURN2(SFA_TOK_SHL); + if (nextch == '=') + RETURN2(SFA_TOK_LOWER_EQUAL); + break; + case '>': + if (nextch == '>') + RETURN2(SFA_TOK_SHR); + if (nextch == '=') + RETURN2(SFA_TOK_GREATER_EQUAL); + break; + case '=': + if (nextch == '=') { + if likely((ptr + 2) < self->sfa_parser.sfp_wend) { + if (ptr[2] == '=') + RETURN3(SFA_TOK_EQUAL3); + } + RETURN2(SFA_TOK_EQUAL); + } + break; + case '!': + if (nextch == '=') { + if likely((ptr + 2) < self->sfa_parser.sfp_wend) { + if (ptr[2] == '=') + RETURN3(SFA_TOK_NOT_EQUAL3); + } + RETURN2(SFA_TOK_NOT_EQUAL); + } + break; + case '.': + if (nextch == '.') { + if likely((ptr + 2) < self->sfa_parser.sfp_wend) { + if (ptr[2] == '.') + RETURN3(SFA_TOK_DOTS); + } + } + break; + case '&': + if (nextch == '&') + RETURN2(SFA_TOK_LAND); + break; + case '|': + if (nextch == '|') + RETURN2(SFA_TOK_LOR); + break; + case '*': + if (nextch == '*') + RETURN2(SFA_TOK_POW); + break; + case '?': + if (nextch == '*') + RETURN2(SFA_TOK_QMARK_QMARK); + break; + default: __builtin_unreachable(); + } + } + goto onechar; + + case '$': + if likely((ptr + 2) < self->sfa_parser.sfp_wend) { + unsigned char nextch1 = (unsigned char)ptr[1]; + unsigned char nextch2 = (unsigned char)ptr[2]; + size_t kwd_len = 2; + while ((ptr + kwd_len + 1) < self->sfa_parser.sfp_wend && + DeeUni_IsSymCont(ptr[kwd_len + 1])) + ++kwd_len; + switch (nextch1) { + case 'i': + if (kwd_len == 2) { + if (nextch2 == 's') + RETURN3(SFA_TOK_D_IS); + if (nextch2 == 'n') + RETURN3(SFA_TOK_D_IN); + } + break; + + case 'a': + if (kwd_len == 2) { + if (nextch2 == 's') + RETURN3(SFA_TOK_D_AS); + } + break; + + case 't': + if (kwd_len == 4) { + if (nextch2 == 'r' && ptr[3] == 'u' && ptr[4] == 'e') + RETURN5(SFA_TOK_D_TRUE); + if (nextch2 == 'y' && ptr[3] == 'p' && ptr[4] == 'e') + RETURN5(SFA_TOK_D_TYPE); + } + break; + + case 'f': + if (kwd_len == 5) { + if (nextch2 == 'a' && ptr[3] == 'l' && ptr[4] == 's' && ptr[5] == 'e') + RETURN5(SFA_TOK_D_FALSE); + } + break; + + case 'n': + if (kwd_len == 4) { + if (nextch2 == 'o' && ptr[3] == 'n' && ptr[4] == 'e') + RETURN5(SFA_TOK_D_NONE); + } + break; + + case 's': + if (kwd_len == 3) { + if (nextch2 == 't' && ptr[3] == 'r') + RETURN5(SFA_TOK_D_STR); + } + break; + + case 'r': + if (kwd_len == 4) { + if (nextch2 == 'e' && ptr[3] == 'p' && ptr[4] == 'r') + RETURN5(SFA_TOK_D_REPR); + } + break; + + case 'c': + if (kwd_len == 4) { + if (nextch2 == 'o' && ptr[3] == 'p' && ptr[4] == 'y') + RETURN5(SFA_TOK_D_COPY); + } + break; + + case 'd': + if (kwd_len == 8) { + if (nextch2 == 'e' && ptr[3] == 'e' && ptr[4] == 'p' && + ptr[5] == 'c' && ptr[6] == 'o' && ptr[7] == 'p' && ptr[8] == 'y') + RETURN9(SFA_TOK_D_DEEPCOPY); + } + break; + + case 'b': + if (kwd_len == 5) { + if (nextch2 == 'o' && ptr[3] == 'u' && ptr[4] == 'n' && ptr[5] == 'd') + RETURN6(SFA_TOK_D_BOUND); + } + break; + + default: break; + } + } + if likely((ptr + 1) < self->sfa_parser.sfp_wend) { + unsigned char nextch1 = (unsigned char)ptr[1]; + if (DeeUni_IsDigit(nextch1)) + RETURN1(SFA_TOK_D_INT); /* Consume the '$', but leave the integer as-is */ + } + goto onechar; + + default: + if (ch >= 0x80) { + char const *next_ptr = ptr; + uint32_t uni = Dee_unicode_readutf8_n(&next_ptr, self->sfa_parser.sfp_wend); + uniflag_t flags = DeeUni_Flags(uni); + if (flags & Dee_UNICODE_ISSPACE) { + ptr = next_ptr; + goto again; + } else if (flags & Dee_UNICODE_ISDIGIT) { + RETURN0(SFA_TOK_INT); + } else { + RETURN0(SFA_TOK_KEYWORD); + } + } else { + uniflag_t flags; + flags = DeeUni_Flags(ch); + if (flags & Dee_UNICODE_ISSPACE) { + case ' ': case '\t': case '\r': case '\n': + ++ptr; + goto again; + } else if (flags & Dee_UNICODE_ISDIGIT) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + RETURN0(SFA_TOK_INT); + } else if (flags & Dee_UNICODE_ISSYMSTRT) { + case 'a': case 'b': case 'c': case 'd': case 'e': + case 'f': case 'g': case 'h': case 'i': case 'j': + case 'k': case 'l': case 'm': case 'n': case 'o': + case 'p': case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': case 'y': + case 'z': + case 'A': case 'B': case 'C': case 'D': case 'E': + case 'F': case 'G': case 'H': case 'I': case 'J': + case 'K': case 'L': case 'M': case 'N': case 'O': + case 'P': case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': case 'Y': + case 'Z': + case '_': + RETURN0(SFA_TOK_KEYWORD); + } else { +onechar: + RETURN1(ch); + } + } + break; + } +done1: + ++ptr; +done0: + self->sfa_parser.sfp_iter = ptr; + self->sfa_exprtok = result; + return result; +done9: + ptr += 3; +done6: + ++ptr; +done5: + ptr += 2; +done3: + ++ptr; +done2: + ++ptr; + goto done1; +#undef RETURN0 +#undef RETURN1 +#undef RETURN2 +#undef RETURN3 +#undef RETURN5 +#undef RETURN6 +#undef RETURN9 +} + +PRIVATE NONNULL((1)) size_t DFCALL +sfa_tok_int_getlen(struct string_format_advanced *__restrict self) { + char const *int_end = self->sfa_parser.sfp_iter; + while (int_end < self->sfa_parser.sfp_wend && DeeUni_IsAlnum(*int_end)) + ++int_end; + return (size_t)(int_end - self->sfa_parser.sfp_iter); +} + +PRIVATE NONNULL((1)) size_t DFCALL +sfa_tok_keyword_getlen(struct string_format_advanced *__restrict self) { + char const *kwd_end = self->sfa_parser.sfp_iter; + while (kwd_end < self->sfa_parser.sfp_wend) { + char const *nextptr = kwd_end; + uint32_t ch = Dee_unicode_readutf8_n(&nextptr, self->sfa_parser.sfp_wend); + if (!DeeUni_IsSymCont(ch)) + break; + kwd_end = nextptr; + } + return (size_t)(kwd_end - self->sfa_parser.sfp_iter); +} + +PRIVATE ATTR_COLD NONNULL((1)) size_t DFCALL +sfa_tok_getlen_for_rewind(struct string_format_advanced *__restrict self) { + size_t result = 1; + switch (self->sfa_exprtok) { + case SFA_TOK_KEYWORD: + case SFA_TOK_CHAR: + case SFA_TOK_STRING: + case SFA_TOK_INT: + case SFA_TOK_D_INT: + result = 0; + break; + case SFA_TOK_SHR: + case SFA_TOK_EQUAL: + case SFA_TOK_NOT_EQUAL: + case SFA_TOK_GREATER_EQUAL: + case SFA_TOK_LOWER_EQUAL: + case SFA_TOK_LAND: + case SFA_TOK_LOR: + case SFA_TOK_POW: + case SFA_TOK_QMARK_QMARK: + result = 2; + break; + case SFA_TOK_DOTS: + case SFA_TOK_EQUAL3: + case SFA_TOK_NOT_EQUAL3: + case SFA_TOK_D_IS: + case SFA_TOK_D_IN: + case SFA_TOK_D_AS: + result = 3; + break; + case SFA_TOK_D_STR: + result = 4; + break; + case SFA_TOK_D_TRUE: + case SFA_TOK_D_NONE: + case SFA_TOK_D_REPR: + case SFA_TOK_D_COPY: + case SFA_TOK_D_TYPE: + result = 5; + break; + case SFA_TOK_D_FALSE: + case SFA_TOK_D_BOUND: + result = 6; + break; + case SFA_TOK_D_DEEPCOPY: + result = 9; + break; + default: break; + } + return result; +} + +PRIVATE ATTR_COLD NONNULL((1)) int DFCALL +sfa_err_bad_token(struct string_format_advanced *__restrict self, + char const *expected_token) { + char const *actual_token_repr = NULL; + size_t actual_token_length = 0; + switch (self->sfa_exprtok) { + case SFA_TOK_KEYWORD: + actual_token_length = sfa_tok_keyword_getlen(self); + break; + case SFA_TOK_CHAR: + case SFA_TOK_STRING: + actual_token_repr = ""; + break; + case SFA_TOK_INT: + actual_token_repr = ""; + break; + case SFA_TOK_D_INT: + actual_token_repr = "<$int>"; + break; + default: + actual_token_length = sfa_tok_getlen_for_rewind(self); + break; + } + if (actual_token_repr) { + actual_token_length = strlen(actual_token_repr); + } else { + actual_token_repr = self->sfa_parser.sfp_iter; + } + return DeeError_Throwf(&DeeError_ValueError, + *expected_token == '<' + ? "Unexpected token %.?q in advanced format pattern expression when %s was expected" + : "Unexpected token %.?q in advanced format pattern expression when %q was expected", + actual_token_length, actual_token_repr, expected_token); +} + + +PRIVATE WUNUSED NONNULL((1)) int DFCALL sfa_skipunary_base(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) int DFCALL sfa_skipunary(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) int DFCALL sfa_skipprod(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) int DFCALL sfa_skipsum(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) int DFCALL sfa_skipshift(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) int DFCALL sfa_skipcmp(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) int DFCALL sfa_skipcmpeq(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) int DFCALL sfa_skipand(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) int DFCALL sfa_skipxor(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) int DFCALL sfa_skipor(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) int DFCALL sfa_skipas(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) int DFCALL sfa_skipland(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) int DFCALL sfa_skiplor(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) int DFCALL sfa_skipcond(struct string_format_advanced *__restrict self); + +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DFCALL sfa_evalunary_base(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DFCALL sfa_evalunary(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DFCALL sfa_evalprod(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DFCALL sfa_evalsum(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DFCALL sfa_evalshift(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DFCALL sfa_evalcmp(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DFCALL sfa_evalcmpeq(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DFCALL sfa_evaland(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DFCALL sfa_evalxor(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DFCALL sfa_evalor(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DFCALL sfa_evalas(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DFCALL sfa_evalland(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DFCALL sfa_evallor(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DFCALL sfa_evalcond(struct string_format_advanced *__restrict self); + +PRIVATE WUNUSED NONNULL((1)) int DFCALL sfa_skipunary_operand(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) int DFCALL sfa_skipprod_operand(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) int DFCALL sfa_skipsum_operand(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) int DFCALL sfa_skipshift_operand(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) int DFCALL sfa_skipcmp_operand(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) int DFCALL sfa_skipcmpeq_operand(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) int DFCALL sfa_skipand_operand(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) int DFCALL sfa_skipxor_operand(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) int DFCALL sfa_skipor_operand(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) int DFCALL sfa_skipas_operand(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) int DFCALL sfa_skipland_operand(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) int DFCALL sfa_skiplor_operand(struct string_format_advanced *__restrict self); +PRIVATE WUNUSED NONNULL((1)) int DFCALL sfa_skipcond_operand(struct string_format_advanced *__restrict self); + +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DFCALL sfa_evalunary_operand(struct string_format_advanced *__restrict self, /*inherit(always)*/ DREF DeeObject *__restrict lhs); +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DFCALL sfa_evalprod_operand(struct string_format_advanced *__restrict self, /*inherit(always)*/ DREF DeeObject *__restrict lhs); +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DFCALL sfa_evalsum_operand(struct string_format_advanced *__restrict self, /*inherit(always)*/ DREF DeeObject *__restrict lhs); +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DFCALL sfa_evalshift_operand(struct string_format_advanced *__restrict self, /*inherit(always)*/ DREF DeeObject *__restrict lhs); +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DFCALL sfa_evalcmp_operand(struct string_format_advanced *__restrict self, /*inherit(always)*/ DREF DeeObject *__restrict lhs); +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DFCALL sfa_evalcmpeq_operand(struct string_format_advanced *__restrict self, /*inherit(always)*/ DREF DeeObject *__restrict lhs); +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DFCALL sfa_evaland_operand(struct string_format_advanced *__restrict self, /*inherit(always)*/ DREF DeeObject *__restrict lhs); +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DFCALL sfa_evalxor_operand(struct string_format_advanced *__restrict self, /*inherit(always)*/ DREF DeeObject *__restrict lhs); +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DFCALL sfa_evalor_operand(struct string_format_advanced *__restrict self, /*inherit(always)*/ DREF DeeObject *__restrict lhs); +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DFCALL sfa_evalas_operand(struct string_format_advanced *__restrict self, /*inherit(always)*/ DREF DeeObject *__restrict lhs); +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DFCALL sfa_evalland_operand(struct string_format_advanced *__restrict self, /*inherit(always)*/ DREF DeeObject *__restrict lhs); +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DFCALL sfa_evallor_operand(struct string_format_advanced *__restrict self, /*inherit(always)*/ DREF DeeObject *__restrict lhs); +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DFCALL sfa_evalcond_operand(struct string_format_advanced *__restrict self, /*inherit(always)*/ DREF DeeObject *__restrict lhs); + +PRIVATE ATTR_NOINLINE WUNUSED NONNULL((1)) Dee_ssize_t DFCALL +string_format_advanced_do1(struct string_format_advanced *__restrict self); + + +/* Parse and print an advanced, extended format-string text element with escapes. + * PATTERN: "foo = {foo:x<{width}} after" + * IN: -----------------^ ^ ^ + * IN(next_brace): -------+ | + * OUT: --------------------------+ + * @return: * : Dee_formatprinter_t-compatible return value */ +PRIVATE ATTR_NOINLINE WUNUSED NONNULL((1, 2, 3)) Dee_ssize_t DFCALL +string_format_advanced_do_format_ex_with_escapes(struct string_format_advanced *__restrict self, + DeeObject *__restrict value, + char const *next_brace) { + Dee_ssize_t result; + struct unicode_printer format; + DREF DeeObject *format_str; + unicode_printer_init(&format); +again_handle_next_brace: + if unlikely(unicode_printer_printutf8(&format, self->sfa_parser.sfp_iter, + (size_t)(next_brace - self->sfa_parser.sfp_iter)) < 0) + goto err_format_printer; + ASSERT(*next_brace == '{' || *next_brace == '}'); + + /* Check for escaped {{ or }} */ + if unlikely((next_brace + 1) < self->sfa_parser.sfp_wend && + (next_brace[0] == next_brace[1])) { + self->sfa_parser.sfp_iter = next_brace + 1; + next_brace = find_next_brace(self->sfa_parser.sfp_iter + 1, + self->sfa_parser.sfp_wend); +again_handle_next_brace_nullable: + if unlikely(!next_brace) { + err_unmatched_lbrace_in_advanced(); + goto err_format_printer; + } + goto again_handle_next_brace; + } + + if (*next_brace == '{') { + /* Recursively emplaced object format (within the "format"-printer itself) */ + Dee_formatprinter_t saved_sfa_printer; + void *saved_sfa_arg; + saved_sfa_printer = self->sfa_printer; + saved_sfa_arg = self->sfa_arg; + self->sfa_printer = &unicode_printer_print; + self->sfa_arg = &format; + + /* Print object repr into the "format"-printer */ + self->sfa_parser.sfp_iter = next_brace + 1; + sfa_yield(self); /* Yield first token */ + result = string_format_advanced_do1(self); + if unlikely(result < 0) + goto err_format_printer; + + /* Restore context */ + self->sfa_printer = saved_sfa_printer; + self->sfa_arg = saved_sfa_arg; + + /* Find, and handle the next brace */ + next_brace = find_next_brace(self->sfa_parser.sfp_iter, + self->sfa_parser.sfp_wend); + goto again_handle_next_brace_nullable; + } + + ASSERT(*next_brace == '}'); /* Known to be unescaped -> end of argument */ + self->sfa_parser.sfp_iter = next_brace + 1; + format_str = unicode_printer_pack(&format); + if unlikely(!format_str) + goto err; + result = DeeObject_PrintFormat(value, self->sfa_printer, self->sfa_arg, format_str); + Dee_Decref(format_str); + return result; +err_format_printer: + unicode_printer_fini(&format); +err: + return -1; +} + +/* Parse and print an advanced, extended format-string text element. + * PATTERN: "foo = {foo:x<{width}} after" + * IN: -----------------^ ^ + * OUT: --------------------------+ + * @return: * : Dee_formatprinter_t-compatible return value */ +PRIVATE ATTR_NOINLINE WUNUSED NONNULL((1, 2)) Dee_ssize_t DFCALL +string_format_advanced_do_format_ex(struct string_format_advanced *__restrict self, + DeeObject *__restrict value) { + Dee_ssize_t result; + char const *next_brace; + next_brace = find_next_brace(self->sfa_parser.sfp_iter, + self->sfa_parser.sfp_wend); + if unlikely(!next_brace) + return err_unmatched_lbrace_in_advanced(); + + /* Check if the format string contains escapes or recursively emplaced arguments. */ + if unlikely(((next_brace + 1) < self->sfa_parser.sfp_wend && next_brace[0] == next_brace[1]) || + (*next_brace == '{')) + return string_format_advanced_do_format_ex_with_escapes(self, value, next_brace); + + /* Simple case: format string can be used as-is. */ + ASSERT(*next_brace == '}'); + result = DeeObject_PrintFormatString(value, self->sfa_printer, self->sfa_arg, + self->sfa_parser.sfp_iter, + (size_t)(next_brace - self->sfa_parser.sfp_iter)); + self->sfa_parser.sfp_iter = next_brace + 1; + return result; +} + +/* Parse and print an advanced format-string text element. + * PATTERN: "foo = {foo + $1!r} after" + * IN: -------------^ ^ + * OUT: -----------------------+ + * @return: * : Dee_formatprinter_t-compatible return value */ +PRIVATE ATTR_NOINLINE WUNUSED NONNULL((1)) Dee_ssize_t DFCALL +string_format_advanced_do1(struct string_format_advanced *__restrict self) { + Dee_ssize_t result; + DREF DeeObject *value; + + /* Evaluate expression */ + value = sfa_evalcond(self); + if unlikely(!value) + goto err; + if unlikely(self->sfa_parser.sfp_iter >= self->sfa_parser.sfp_wend) { + err_unmatched_lbrace_in_advanced(); + goto err_value; + } + + /* Check what sort of object representation mode is being used. */ + switch (__builtin_expect(self->sfa_exprtok, '}')) { + case '}': + result = DeeObject_Print(value, self->sfa_printer, self->sfa_arg); + break; + + case '!': + if unlikely(self->sfa_parser.sfp_iter >= self->sfa_parser.sfp_wend) { + err_unknown_repr_mode_in_advanced(""); + goto err_value; + } + switch (__builtin_expect(*self->sfa_parser.sfp_iter, 'r')) { + case 'r': + ++self->sfa_parser.sfp_iter; + if unlikely(self->sfa_parser.sfp_iter >= self->sfa_parser.sfp_wend) { + err_invalid_char_after_lbrace_exclaim_spec_in_advanced('r', ""); + goto err_value; + } + if unlikely(*self->sfa_parser.sfp_iter != '}') { + err_invalid_char_after_lbrace_exclaim_spec_in_advanced('r', self->sfa_parser.sfp_iter); + goto err_value; + } + ++self->sfa_parser.sfp_iter; + result = DeeObject_PrintRepr(value, self->sfa_printer, self->sfa_arg); + break; + case 's': + case 'a': + ++self->sfa_parser.sfp_iter; + if unlikely(self->sfa_parser.sfp_iter >= self->sfa_parser.sfp_wend) { + err_invalid_char_after_lbrace_exclaim_spec_in_advanced(self->sfa_parser.sfp_iter[-1], ""); + goto err_value; + } + if unlikely(*self->sfa_parser.sfp_iter != '}') { + err_invalid_char_after_lbrace_exclaim_spec_in_advanced(self->sfa_parser.sfp_iter[-1], + self->sfa_parser.sfp_iter); + goto err_value; + } + ++self->sfa_parser.sfp_iter; + result = DeeObject_Print(value, self->sfa_printer, self->sfa_arg); + break; + default: + return err_unknown_repr_mode_in_advanced(self->sfa_parser.sfp_iter); + } + break; + + case ':': + result = string_format_advanced_do_format_ex(self, value); + break; + + default: + self->sfa_parser.sfp_iter -= sfa_tok_getlen_for_rewind(self); + err_invalid_char_after_expr_in_advanced(self->sfa_parser.sfp_iter); + goto err_value; + } + Dee_Decref(value); + return result; +err_value: + Dee_Decref(value); +err: + return -1; +} + +PRIVATE ATTR_NOINLINE WUNUSED NONNULL((1)) int DFCALL +string_format_advanced_skip1(struct string_format_advanced *__restrict self) { + if unlikely(sfa_skipcond(self)) + goto err; + if unlikely(self->sfa_parser.sfp_iter >= self->sfa_parser.sfp_wend) + return err_unmatched_lbrace_in_advanced(); + switch (__builtin_expect(*self->sfa_parser.sfp_iter, '}')) { + case '}': + ++self->sfa_parser.sfp_iter; + break; + case '!': + ++self->sfa_parser.sfp_iter; + if unlikely(self->sfa_parser.sfp_iter >= self->sfa_parser.sfp_wend) + return err_unknown_repr_mode_in_advanced(""); + switch (__builtin_expect(*self->sfa_parser.sfp_iter, 'r')) { + case 'r': + ++self->sfa_parser.sfp_iter; + if unlikely(self->sfa_parser.sfp_iter >= self->sfa_parser.sfp_wend) + return err_invalid_char_after_lbrace_exclaim_spec_in_advanced('r', ""); + if unlikely(*self->sfa_parser.sfp_iter != '}') + return err_invalid_char_after_lbrace_exclaim_spec_in_advanced('r', self->sfa_parser.sfp_iter); + ++self->sfa_parser.sfp_iter; + break; + case 's': + case 'a': + ++self->sfa_parser.sfp_iter; + if unlikely(self->sfa_parser.sfp_iter >= self->sfa_parser.sfp_wend) + return err_invalid_char_after_lbrace_exclaim_spec_in_advanced(self->sfa_parser.sfp_iter[-1], ""); + if unlikely(*self->sfa_parser.sfp_iter != '}') + return err_invalid_char_after_lbrace_exclaim_spec_in_advanced(self->sfa_parser.sfp_iter[-1], + self->sfa_parser.sfp_iter); + ++self->sfa_parser.sfp_iter; + break; + default: + return err_unknown_repr_mode_in_advanced(self->sfa_parser.sfp_iter); + } + break; + + case ':': + ++self->sfa_parser.sfp_iter; + break; + + default: + return err_invalid_char_after_expr_in_advanced(self->sfa_parser.sfp_iter); + } + return 0; +err: + return -1; +} + +PRIVATE ATTR_NOINLINE WUNUSED NONNULL((1)) DREF DeeObject *DFCALL +string_format_advanced_do1_intostr(struct string_format_advanced *__restrict self) { + Dee_formatprinter_t saved_sfa_printer; + void *saved_sfa_arg; + struct unicode_printer printer; + Dee_ssize_t temp; + unicode_printer_init(&printer); + saved_sfa_printer = self->sfa_printer; + saved_sfa_arg = self->sfa_arg; + self->sfa_printer = &unicode_printer_print; + self->sfa_arg = &printer; + temp = string_format_advanced_do1(self); + self->sfa_printer = saved_sfa_printer; + self->sfa_arg = saved_sfa_arg; + if unlikely(temp < 0) + goto err_printer; + return unicode_printer_pack(&printer); +err_printer: + unicode_printer_fini(&printer); + return NULL; +} + + + + + +#ifndef CONFIG_EXPERIMENTAL_NEW_STRING_FORMAT /* TODO: Re-write this file such that "string.format" uses `DeeObject_Foreach()'. * For this purpose, scan ahead until the first '{'. If it is followed by * '}' or '!', assume that the format string is "simple", meaning that @@ -48,8 +1040,6 @@ DECL_BEGIN * TODO: Get rid of ':' and "DeeObject_PrintFormat()" (or at least re-work them) */ - - struct formatter { /* TODO: Unicode support */ char *f_iter; /* [1..1] Current format string position. */ @@ -797,9 +1787,9 @@ format_impl(struct formatter *__restrict self, * @param: args: A sequence (usually a Dict, or a List) used for * providing input values to the format string. */ INTERN WUNUSED NONNULL((1, 3, 5)) dssize_t DCALL -DeeString_Format(dformatprinter printer, void *arg, - /*utf-8*/ char const *__restrict format, - size_t format_len, DeeObject *__restrict args) { +DeeString_Format_old(dformatprinter printer, void *arg, + /*utf-8*/ char const *__restrict format, + size_t format_len, DeeObject *__restrict args) { struct formatter self; dssize_t result; self.f_flush_start = (char *)format; @@ -963,12 +1953,11 @@ format_bytes_impl(struct formatter *__restrict self, return -1; } - INTERN WUNUSED NONNULL((1, 2, 4, 6)) dssize_t DCALL -DeeBytes_Format(dformatprinter printer, - dformatprinter format_printer, void *arg, - char const *__restrict format, - size_t format_len, DeeObject *__restrict args) { +DeeBytes_Format_old(dformatprinter printer, + dformatprinter format_printer, void *arg, + char const *__restrict format, + size_t format_len, DeeObject *__restrict args) { struct formatter self; dssize_t result; self.f_flush_start = (char *)format; @@ -982,9 +1971,24 @@ DeeBytes_Format(dformatprinter printer, Dee_XDecref(self.f_seqiter); return result; } +#endif /* !CONFIG_EXPERIMENTAL_NEW_STRING_FORMAT */ + +DECL_END +#ifndef __INTELLISENSE__ +#define DEFINE_DeeString_FormatPrinter +#include "format-impl.c.inl" +#define DEFINE_DeeString_FormatWStr +#include "format-impl.c.inl" +#define DEFINE_DeeString_Format +#include "format-impl.c.inl" + +#define DEFINE_sfa_skipexpr +#include "format-expr.c.inl" +#define DEFINE_sfa_evalexpr +#include "format-expr.c.inl" +#endif /* !__INTELLISENSE__ */ -DECL_END -#endif /* !GUARD_DEEMON_OBJECTS_UNICODE_ISORMAT_C */ +#endif /* !GUARD_DEEMON_OBJECTS_UNICODE_FORMAT_C */ diff --git a/src/deemon/objects/unicode/string_functions.c b/src/deemon/objects/unicode/string_functions.c index 88ff54aa..3b1d6ecb 100644 --- a/src/deemon/objects/unicode/string_functions.c +++ b/src/deemon/objects/unicode/string_functions.c @@ -7408,10 +7408,21 @@ string_dedent(String *self, size_t argc, DeeObject *const *argv) { } +#ifdef CONFIG_EXPERIMENTAL_NEW_STRING_FORMAT +INTERN WUNUSED NONNULL((1)) DREF String *DCALL +string_format(String *self, size_t argc, DeeObject *const *argv) { + DeeObject *args; + if (DeeArg_Unpack(argc, argv, "o:format", &args)) + goto err; + return (DREF String *)DeeString_Format((DeeObject *)self, args); +err: + return NULL; +} +#else /* CONFIG_EXPERIMENTAL_NEW_STRING_FORMAT */ INTDEF WUNUSED NONNULL((1, 3, 5)) dssize_t DCALL -DeeString_Format(dformatprinter printer, void *arg, - /*utf-8*/ char const *__restrict format, - size_t format_len, DeeObject *__restrict args); +DeeString_Format_old(dformatprinter printer, void *arg, + /*utf-8*/ char const *__restrict format, + size_t format_len, DeeObject *__restrict args); INTERN WUNUSED NONNULL((1)) DREF String *DCALL string_format(String *self, size_t argc, DeeObject *const *argv) { @@ -7424,7 +7435,7 @@ string_format(String *self, size_t argc, DeeObject *const *argv) { goto err; { struct unicode_printer printer = UNICODE_PRINTER_INIT; - if unlikely(DeeString_Format(&unicode_printer_print, + if unlikely(DeeString_Format_old(&unicode_printer_print, &printer, utf8_repr, WSTR_LENGTH(utf8_repr), @@ -7437,6 +7448,7 @@ string_format(String *self, size_t argc, DeeObject *const *argv) { err: return NULL; } +#endif /* !CONFIG_EXPERIMENTAL_NEW_STRING_FORMAT */ INTDEF WUNUSED NONNULL((1, 2)) DREF DeeObject *DCALL DeeString_Scanf(DeeObject *self, DeeObject *format); diff --git a/src/dex/_hostasm/generator-vstack-ex.c b/src/dex/_hostasm/generator-vstack-ex.c index 118d9cf3..e4af425d 100644 --- a/src/dex/_hostasm/generator-vstack-ex.c +++ b/src/dex/_hostasm/generator-vstack-ex.c @@ -3489,7 +3489,7 @@ vopcallseqmap_impl(struct fungen *__restrict self, * >> push arg @a * >> push arg @b * >> push arg @c - * >> callattr top, @"format", [#2] + * >> callattr top, @"format", [#3] * >> ret pop * * Since we know that "String.format" never incref's its args-sequence diff --git a/src/dex/_hostasm/libgen86 b/src/dex/_hostasm/libgen86 index be19773f..89c2314b 160000 --- a/src/dex/_hostasm/libgen86 +++ b/src/dex/_hostasm/libgen86 @@ -1 +1 @@ -Subproject commit be19773f24f541e0b95cb5456692a727484562d1 +Subproject commit 89c2314bafc65522f0decc420de9eff2034ef992