From 2e2f8abfc0afe4d442fe187ef76c8b27a9736e14 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sat, 18 May 2024 13:23:33 -0500 Subject: [PATCH 1/8] Work on add locales. Need to be careful not to mess with %j formatter, or in some other places. --- src/core/math.c | 22 ++++++++++++++++++++++ src/core/os.c | 38 ++++++++++++++++++++++++++++++++++++++ src/core/pp.c | 7 +++++++ 3 files changed, 67 insertions(+) diff --git a/src/core/math.c b/src/core/math.c index da81d770..a95d3ab3 100644 --- a/src/core/math.c +++ b/src/core/math.c @@ -349,6 +349,26 @@ JANET_CORE_FN(janet_cfun_lcm, "(math/lcm x y)", return janet_wrap_number(janet_lcm(x, y)); } +JANET_CORE_FN(janet_cfun_frexp, "(math/frexp x)", + "Returns a tuple of (mantissa, exponent) from number.") { + janet_fixarity(argc, 1); + double x = janet_getnumber(argv, 0); + int exp; + x = frexp(x, &exp); + Janet *result = janet_tuple_begin(2); + result[0] = janet_wrap_number(x); + result[1] = janet_wrap_number((double) exp); + return janet_wrap_tuple(janet_tuple_end(result)); +} + +JANET_CORE_FN(janet_cfun_ldexp, "(math/ldexp m e)", + "Creates a new number from a mantissa and an exponent.") { + janet_fixarity(argc, 2); + double x = janet_getnumber(argv, 0); + int32_t y = janet_getinteger(argv, 1); + return janet_wrap_number(ldexp(x, y)); +} + /* Module entry point */ void janet_lib_math(JanetTable *env) { JanetRegExt math_cfuns[] = { @@ -395,6 +415,8 @@ void janet_lib_math(JanetTable *env) { JANET_CORE_REG("math/next", janet_nextafter), JANET_CORE_REG("math/gcd", janet_cfun_gcd), JANET_CORE_REG("math/lcm", janet_cfun_lcm), + JANET_CORE_REG("math/frexp", janet_cfun_frexp), + JANET_CORE_REG("math/ldexp", janet_cfun_ldexp), JANET_REG_END }; janet_core_cfuns_ext(env, NULL, math_cfuns); diff --git a/src/core/os.c b/src/core/os.c index 08eb7632..a305cb40 100644 --- a/src/core/os.c +++ b/src/core/os.c @@ -38,6 +38,7 @@ #include #include #include +#include #ifdef JANET_BSD #include @@ -1891,6 +1892,42 @@ JANET_CORE_FN(os_mktime, #define j_symlink symlink #endif +JANET_CORE_FN(os_setlocale, + "(os/setlocale category &opt locale)", + "Set the system locale, which affects how dates, currencies, and numbers are formatted. " + "Passing nil to locale will return the current locale.") { + janet_arity(argc, 1, 2); + const char *locale = janet_optcstring(argv, argc, 1, NULL); + int category_int = 0; + if (janet_keyeq(argv[0], "all")) { + category_int |= LC_ALL_MASK; + } else if (janet_keyeq(argv[0], "collate")) { + category_int |= LC_COLLATE_MASK; + } else if (janet_keyeq(argv[0], "ctype")) { + category_int |= LC_CTYPE_MASK; + } else if (janet_keyeq(argv[0], "monetary")) { + category_int |= LC_MONETARY_MASK; + } else if (janet_keyeq(argv[0], "numeric")) { + category_int |= LC_NUMERIC_MASK; + } else if (janet_keyeq(argv[0], "time")) { + category_int |= LC_TIME_MASK; + } else { + janet_panicf("expected one of :all, :collate, :ctype, :monetary, :numeric, or :time, got %v", argv[0]); + } + if (locale == NULL) { + const char *old = setlocale(category_int, NULL); + if (old == NULL) return janet_wrap_nil(); + return janet_cstringv(old); + } + locale_t loc = newlocale(category_int, locale, 0); + if (loc == 0) { + janet_panicf("failed to set locale - %s", strerror(errno)); + } + uselocale(loc); + freelocale(loc); + return janet_wrap_nil(); +} + JANET_CORE_FN(os_link, "(os/link oldpath newpath &opt symlink)", "Create a link at newpath that points to oldpath and returns nil. " @@ -2688,6 +2725,7 @@ void janet_lib_os(JanetTable *env) { JANET_CORE_REG("os/strftime", os_strftime), JANET_CORE_REG("os/sleep", os_sleep), JANET_CORE_REG("os/isatty", os_isatty), + JANET_CORE_REG("os/setlocale", os_setlocale), /* env functions */ JANET_CORE_REG("os/environ", os_environ), diff --git a/src/core/pp.c b/src/core/pp.c index bba70b6b..7718c32a 100644 --- a/src/core/pp.c +++ b/src/core/pp.c @@ -380,6 +380,13 @@ static int print_jdn_one(struct pretty *S, Janet x, int depth) { case JANET_NUMBER: janet_buffer_ensure(S->buffer, S->buffer->count + BUFSIZE, 2); int count = snprintf((char *) S->buffer->data + S->buffer->count, BUFSIZE, "%.17g", janet_unwrap_number(x)); + /* fix locale issues with commas */ + for (int i = 0; i < count; i++) { + char c = S->buffer->data[S->buffer->count + i]; + if (c == ',' || c == '\'') { + S->buffer->data[S->buffer->count + i] = '.'; + } + } S->buffer->count += count; break; case JANET_SYMBOL: From af232ef7291cf3c89f7d159b8ce4218a78c18d65 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sat, 18 May 2024 14:02:20 -0500 Subject: [PATCH 2/8] windows needs a distinct implementation from posix for thread safety. I must say, the windows solution is a lot simpler. --- src/core/compile.c | 2 +- src/core/os.c | 54 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/src/core/compile.c b/src/core/compile.c index 4f45ff1f..b2438f8b 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -934,7 +934,7 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) { int32_t slotchunks = (def->slotcount + 31) >> 5; /* numchunks is min of slotchunks and scope->ua.count */ int32_t numchunks = slotchunks > scope->ua.count ? scope->ua.count : slotchunks; - uint32_t *chunks = janet_calloc(sizeof(uint32_t), slotchunks); + uint32_t *chunks = janet_calloc(1, slotchunks * sizeof(uint32_t)); if (NULL == chunks) { JANET_OUT_OF_MEMORY; } diff --git a/src/core/os.c b/src/core/os.c index a305cb40..7b1b4b33 100644 --- a/src/core/os.c +++ b/src/core/os.c @@ -1897,35 +1897,64 @@ JANET_CORE_FN(os_setlocale, "Set the system locale, which affects how dates, currencies, and numbers are formatted. " "Passing nil to locale will return the current locale.") { janet_arity(argc, 1, 2); - const char *locale = janet_optcstring(argv, argc, 1, NULL); + const char *locale_name = janet_optcstring(argv, argc, 1, NULL); int category_int = 0; +#ifdef JANET_WINDOWS if (janet_keyeq(argv[0], "all")) { - category_int |= LC_ALL_MASK; + category_int = LC_ALL; } else if (janet_keyeq(argv[0], "collate")) { - category_int |= LC_COLLATE_MASK; + category_int = LC_COLLATE; } else if (janet_keyeq(argv[0], "ctype")) { - category_int |= LC_CTYPE_MASK; + category_int = LC_CTYPE; } else if (janet_keyeq(argv[0], "monetary")) { - category_int |= LC_MONETARY_MASK; + category_int = LC_MONETARY; } else if (janet_keyeq(argv[0], "numeric")) { - category_int |= LC_NUMERIC_MASK; + category_int = LC_NUMERIC; } else if (janet_keyeq(argv[0], "time")) { - category_int |= LC_TIME_MASK; + category_int = LC_TIME; } else { janet_panicf("expected one of :all, :collate, :ctype, :monetary, :numeric, or :time, got %v", argv[0]); } - if (locale == NULL) { + const char *old = setlocale(category_int, locale_name); + if (old == NULL) { + janet_panicf("failed to set locale - %s", strerror(errno)); + } + return janet_wrap_nil(); +#else + if (janet_keyeq(argv[0], "all")) { + category_int = LC_ALL_MASK; + } else if (janet_keyeq(argv[0], "collate")) { + category_int = LC_COLLATE_MASK; + } else if (janet_keyeq(argv[0], "ctype")) { + category_int = LC_CTYPE_MASK; + } else if (janet_keyeq(argv[0], "monetary")) { + category_int = LC_MONETARY_MASK; + } else if (janet_keyeq(argv[0], "numeric")) { + category_int = LC_NUMERIC_MASK; + } else if (janet_keyeq(argv[0], "time")) { + category_int = LC_TIME_MASK; + } else { + janet_panicf("expected one of :all, :collate, :ctype, :monetary, :numeric, or :time, got %v", argv[0]); + } + if (locale_name == NULL) { const char *old = setlocale(category_int, NULL); if (old == NULL) return janet_wrap_nil(); return janet_cstringv(old); } - locale_t loc = newlocale(category_int, locale, 0); + /* Use newlocale instead of setlocale for per-thread behavior */ + locale_t loc = newlocale(category_int, locale_name, 0); if (loc == 0) { + janet_panicf("failed to make locale - %s", strerror(errno)); + } + locale_t old_locale = uselocale(loc); + if (old_locale == 0) { janet_panicf("failed to set locale - %s", strerror(errno)); } - uselocale(loc); - freelocale(loc); + if (old_locale != LC_GLOBAL_LOCALE) { + freelocale(old_locale); + } return janet_wrap_nil(); +#endif } JANET_CORE_FN(os_link, @@ -2782,5 +2811,8 @@ void janet_lib_os(JanetTable *env) { #endif JANET_REG_END }; +#ifdef JANET_WINDOWS + _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); +#endif janet_core_cfuns_ext(env, NULL, os_cfuns); } From e6b73f8cd16d8853d7a564be32719a559da37c7e Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sat, 18 May 2024 14:11:05 -0500 Subject: [PATCH 3/8] BSD, use xlocale for thread safe functionality --- src/core/os.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/os.c b/src/core/os.c index 7b1b4b33..c8b0d7ee 100644 --- a/src/core/os.c +++ b/src/core/os.c @@ -38,10 +38,13 @@ #include #include #include -#include + #ifdef JANET_BSD #include +#include +#else +#include #endif #ifdef JANET_LINUX From ea5d4fd3afa91908b635cbde959943e93106fcd1 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sat, 18 May 2024 14:24:51 -0500 Subject: [PATCH 4/8] JANET_BSD not defined on apple. --- src/core/os.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/os.c b/src/core/os.c index c8b0d7ee..e9dab3e1 100644 --- a/src/core/os.c +++ b/src/core/os.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include @@ -42,6 +41,9 @@ #ifdef JANET_BSD #include +#endif + +#if defined(JANET_BSD) || defined(JANET_APPLE) #include #else #include From 0b03ddb21bc968f869896e92762d45b4f1461337 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sat, 18 May 2024 15:20:22 -0500 Subject: [PATCH 5/8] More work on setting locale for extended locale support. --- src/core/os.c | 56 +++++++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/src/core/os.c b/src/core/os.c index e9dab3e1..b642d735 100644 --- a/src/core/os.c +++ b/src/core/os.c @@ -43,14 +43,22 @@ #include #endif -#if defined(JANET_BSD) || defined(JANET_APPLE) +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(JANET_APPLE) +/* It seems only some bsds use this header for xlocale */ #include +#define JANET_EXTENDED_LOCALE #else #include #endif #ifdef JANET_LINUX #include +#define JANET_EXTENDED_LOCALE +#endif + +/* OpenBSD works here with extended locale support, just in the usual headers */ +#if defined(__OpenBSD__) +#define JANET_EXTENDED_LOCALE #endif #ifdef JANET_WINDOWS @@ -1904,28 +1912,7 @@ JANET_CORE_FN(os_setlocale, janet_arity(argc, 1, 2); const char *locale_name = janet_optcstring(argv, argc, 1, NULL); int category_int = 0; -#ifdef JANET_WINDOWS - if (janet_keyeq(argv[0], "all")) { - category_int = LC_ALL; - } else if (janet_keyeq(argv[0], "collate")) { - category_int = LC_COLLATE; - } else if (janet_keyeq(argv[0], "ctype")) { - category_int = LC_CTYPE; - } else if (janet_keyeq(argv[0], "monetary")) { - category_int = LC_MONETARY; - } else if (janet_keyeq(argv[0], "numeric")) { - category_int = LC_NUMERIC; - } else if (janet_keyeq(argv[0], "time")) { - category_int = LC_TIME; - } else { - janet_panicf("expected one of :all, :collate, :ctype, :monetary, :numeric, or :time, got %v", argv[0]); - } - const char *old = setlocale(category_int, locale_name); - if (old == NULL) { - janet_panicf("failed to set locale - %s", strerror(errno)); - } - return janet_wrap_nil(); -#else +#ifdef JANET_EXTENDED_LOCALE if (janet_keyeq(argv[0], "all")) { category_int = LC_ALL_MASK; } else if (janet_keyeq(argv[0], "collate")) { @@ -1959,6 +1946,27 @@ JANET_CORE_FN(os_setlocale, freelocale(old_locale); } return janet_wrap_nil(); +#else + if (janet_keyeq(argv[0], "all")) { + category_int = LC_ALL; + } else if (janet_keyeq(argv[0], "collate")) { + category_int = LC_COLLATE; + } else if (janet_keyeq(argv[0], "ctype")) { + category_int = LC_CTYPE; + } else if (janet_keyeq(argv[0], "monetary")) { + category_int = LC_MONETARY; + } else if (janet_keyeq(argv[0], "numeric")) { + category_int = LC_NUMERIC; + } else if (janet_keyeq(argv[0], "time")) { + category_int = LC_TIME; + } else { + janet_panicf("expected one of :all, :collate, :ctype, :monetary, :numeric, or :time, got %v", argv[0]); + } + const char *old = setlocale(category_int, locale_name); + if (old == NULL) { + janet_panicf("failed to set locale - %s", strerror(errno)); + } + return janet_wrap_nil(); #endif } @@ -2816,7 +2824,7 @@ void janet_lib_os(JanetTable *env) { #endif JANET_REG_END }; -#ifdef JANET_WINDOWS +#if defined(JANET_WINDOWS) && !defined(JANET_REDUCED_OS) _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); #endif janet_core_cfuns_ext(env, NULL, os_cfuns); From 02f53ca0144a637dea2cda9dce51e83296f3a009 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sat, 18 May 2024 15:21:37 -0500 Subject: [PATCH 6/8] Formatting. --- src/core/os.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/os.c b/src/core/os.c index b642d735..d2de39a7 100644 --- a/src/core/os.c +++ b/src/core/os.c @@ -1906,9 +1906,9 @@ JANET_CORE_FN(os_mktime, #endif JANET_CORE_FN(os_setlocale, - "(os/setlocale category &opt locale)", - "Set the system locale, which affects how dates, currencies, and numbers are formatted. " - "Passing nil to locale will return the current locale.") { + "(os/setlocale category &opt locale)", + "Set the system locale, which affects how dates and numbers are formatted. " + "Passing nil to locale will return the current locale.") { janet_arity(argc, 1, 2); const char *locale_name = janet_optcstring(argv, argc, 1, NULL); int category_int = 0; From 809b6589a1567fa0aa39af810bcc3c4c873f82c2 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sat, 18 May 2024 15:31:23 -0500 Subject: [PATCH 7/8] Put limits.h back. --- src/core/os.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/os.c b/src/core/os.c index d2de39a7..7f779fcf 100644 --- a/src/core/os.c +++ b/src/core/os.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include From 876b7f106f35419f8bc3eab80730f6802f3b0a3b Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sat, 18 May 2024 17:22:10 -0500 Subject: [PATCH 8/8] OpenBSD does not work with LC_*_MASK stuff. --- src/core/os.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/core/os.c b/src/core/os.c index 7f779fcf..d013fd75 100644 --- a/src/core/os.c +++ b/src/core/os.c @@ -57,11 +57,6 @@ #define JANET_EXTENDED_LOCALE #endif -/* OpenBSD works here with extended locale support, just in the usual headers */ -#if defined(__OpenBSD__) -#define JANET_EXTENDED_LOCALE -#endif - #ifdef JANET_WINDOWS #include #include