From 3e402d397e9f94a41cb70098f13d3495c241363f Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Wed, 15 May 2024 18:16:19 -0500 Subject: [PATCH 01/24] Use older openbsd build for CI. --- .builds/openbsd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.builds/openbsd.yml b/.builds/openbsd.yml index 5526b15c..c03faaf1 100644 --- a/.builds/openbsd.yml +++ b/.builds/openbsd.yml @@ -1,4 +1,4 @@ -image: openbsd/latest +image: openbsd/7.4 sources: - https://git.sr.ht/~bakpakin/janet packages: From c747e8d16c743ec2db8bd81cb7be563d80a8038f Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Wed, 15 May 2024 18:20:20 -0500 Subject: [PATCH 02/24] Address some compiler linter messages on openbsd --- src/core/inttypes.c | 4 ++-- src/core/pp.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/inttypes.c b/src/core/inttypes.c index b4c999b8..bed6dff1 100644 --- a/src/core/inttypes.c +++ b/src/core/inttypes.c @@ -73,13 +73,13 @@ static void *int64_unmarshal(JanetMarshalContext *ctx) { static void it_s64_tostring(void *p, JanetBuffer *buffer) { char str[32]; - sprintf(str, "%" PRId64, *((int64_t *)p)); + snprintf(str, sizeof(str), "%" PRId64, *((int64_t *)p)); janet_buffer_push_cstring(buffer, str); } static void it_u64_tostring(void *p, JanetBuffer *buffer) { char str[32]; - sprintf(str, "%" PRIu64, *((uint64_t *)p)); + snprintf(str, sizeof(str), "%" PRIu64, *((uint64_t *)p)); janet_buffer_push_cstring(buffer, str); } diff --git a/src/core/pp.c b/src/core/pp.c index 89ecc141..bba70b6b 100644 --- a/src/core/pp.c +++ b/src/core/pp.c @@ -830,7 +830,7 @@ static const char *scanformat( if (loc != NULL && *loc != '\0') { const char *mapping = get_fmt_mapping(*p2++); size_t len = strlen(mapping); - strcpy(form, mapping); + memcpy(form, mapping, len); form += len; } else { *(form++) = *(p2++); From 9946f3bdf4fc6aa7adfb321f8141490443042aff Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Wed, 15 May 2024 20:16:42 -0500 Subject: [PATCH 03/24] Add buffer/format-at Move changes over from bundle-tools branch and add testing. --- src/core/buffer.c | 22 ++++++++++++++++++++++ test/suite-buffer.janet | 15 +++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/core/buffer.c b/src/core/buffer.c index 5d9e7b3d..a34f29fb 100644 --- a/src/core/buffer.c +++ b/src/core/buffer.c @@ -655,6 +655,27 @@ JANET_CORE_FN(cfun_buffer_format, return argv[0]; } +JANET_CORE_FN(cfun_buffer_format_at, + "(buffer/format-at buffer at format & args)", + "Snprintf like functionality for printing values into a buffer. Returns " + "the modified buffer.") { + janet_arity(argc, 2, -1); + JanetBuffer *buffer = janet_getbuffer(argv, 0); + int32_t at = janet_getinteger(argv, 1); + if (at < 0) { + at += buffer->count + 1; + } + if (at > buffer->count || at < 0) janet_panicf("expected index at to be in range [0, %d), got %d", buffer->count, at); + int32_t oldcount = buffer->count; + buffer->count = at; + const char *strfrmt = (const char *) janet_getstring(argv, 2); + janet_buffer_format(buffer, strfrmt, 2, argc, argv); + if (buffer->count < oldcount) { + buffer->count = oldcount; + } + return argv[0]; +} + void janet_lib_buffer(JanetTable *env) { JanetRegExt buffer_cfuns[] = { JANET_CORE_REG("buffer/new", cfun_buffer_new), @@ -681,6 +702,7 @@ void janet_lib_buffer(JanetTable *env) { JANET_CORE_REG("buffer/bit-toggle", cfun_buffer_bittoggle), JANET_CORE_REG("buffer/blit", cfun_buffer_blit), JANET_CORE_REG("buffer/format", cfun_buffer_format), + JANET_CORE_REG("buffer/format-at", cfun_buffer_format_at), JANET_REG_END }; janet_core_cfuns_ext(env, NULL, buffer_cfuns); diff --git a/test/suite-buffer.janet b/test/suite-buffer.janet index 7d585680..9821e384 100644 --- a/test/suite-buffer.janet +++ b/test/suite-buffer.janet @@ -162,5 +162,20 @@ (assert (deep= @"abc423" (buffer/push-at @"abc123" 3 "4")) "buffer/push-at 3") +# buffer/format-at +(def start-buf (buffer/new-filled 100 (chr "x"))) +(buffer/format-at start-buf 50 "aa%dbb" 32) +(assert (= (string start-buf) (string (string/repeat "x" 50) "aa32bb" (string/repeat "x" 44))) + "buffer/format-at 1") +(assert + (deep= + (buffer/format @"" "%j" [1 2 3 :a :b :c]) + (buffer/format-at @"" 0 "%j" [1 2 3 :a :b :c])) + "buffer/format-at empty buffer") +(def buf @"xxxyyy") +(buffer/format-at buf -4 "xxx") +(assert (= (string buf) "xxxxxx") "buffer/format-at negative index") +(assert-error "expected index at to be in range [0, 0), got 1" (buffer/format-at @"" 1 "abc")) + (end-suite) From fdaf2e1594bb3b68a9e7c179388ac8afa9025204 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Wed, 15 May 2024 20:16:42 -0500 Subject: [PATCH 04/24] Add *module/make-env* --- CHANGELOG.md | 2 ++ src/boot/boot.janet | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 370f7e8c..efd8fc3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ All notable changes to this project will be documented in this file. ## Unreleased - ??? +- Add *module/make-env* dynamic binding +- Add buffer/format-at - Add long form command line options for readable CLI usage - Fix bug with `net/accept-loop` that would sometimes miss connections. diff --git a/src/boot/boot.janet b/src/boot/boot.janet index f3dbfd2a..8af8fb27 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -2771,6 +2771,7 @@ (defdyn *module/paths* "Dynamic binding for overriding `module/cache`") (defdyn *module/loading* "Dynamic binding for overriding `module/cache`") (defdyn *module/loaders* "Dynamic binding for overriding `module/loaders`") +(defdyn *module/make-env* "Dynamic binding for creating new environments for `import`, `require`, and `dofile`. Overrides `make-env`.") (def module/cache "A table, mapping loaded module identifiers to their environments." @@ -2958,7 +2959,7 @@ :core/stream path (file/open path :rb))) (def path-is-file (= f path)) - (default env (make-env (curenv))) + (default env ((dyn *module/make-env* make-env))) (def spath (string path)) (put env :source (or source (if-not path-is-file spath path))) (var exit-error nil) From fe540136794627e5101bbaa10e78475c102fe416 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Thu, 16 May 2024 19:11:25 -0500 Subject: [PATCH 05/24] Update naming *module-make-env* for #1447 --- src/boot/boot.janet | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/boot/boot.janet b/src/boot/boot.janet index 8af8fb27..c465dfcb 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -2767,11 +2767,11 @@ (defn- check-is-dep [x] (unless (or (string/has-prefix? "/" x) (string/has-prefix? "@" x) (string/has-prefix? "." x)) x)) (defn- check-project-relative [x] (if (string/has-prefix? "/" x) x)) -(defdyn *module/cache* "Dynamic binding for overriding `module/cache`") -(defdyn *module/paths* "Dynamic binding for overriding `module/cache`") -(defdyn *module/loading* "Dynamic binding for overriding `module/cache`") -(defdyn *module/loaders* "Dynamic binding for overriding `module/loaders`") -(defdyn *module/make-env* "Dynamic binding for creating new environments for `import`, `require`, and `dofile`. Overrides `make-env`.") +(defdyn *module-cache* "Dynamic binding for overriding `module/cache`") +(defdyn *module-paths* "Dynamic binding for overriding `module/cache`") +(defdyn *module-loading* "Dynamic binding for overriding `module/cache`") +(defdyn *module-loaders* "Dynamic binding for overriding `module/loaders`") +(defdyn *module-make-env* "Dynamic binding for creating new environments for `import`, `require`, and `dofile`. Overrides `make-env`.") (def module/cache "A table, mapping loaded module identifiers to their environments." @@ -2801,7 +2801,7 @@ keyword name of a loader in `module/loaders`. Returns the modified `module/paths`. ``` [ext loader] - (def mp (dyn *module/paths* module/paths)) + (def mp (dyn *module-paths* module/paths)) (defn- find-prefix [pre] (or (find-index |(and (string? ($ 0)) (string/has-prefix? pre ($ 0))) mp) 0)) @@ -2819,7 +2819,7 @@ (module/add-paths "/init.janet" :source) (module/add-paths ".janet" :source) (module/add-paths ".jimage" :image) -(array/insert module/paths 0 [(fn is-cached [path] (if (in (dyn *module/cache* module/cache) path) path)) :preload check-not-relative]) +(array/insert module/paths 0 [(fn is-cached [path] (if (in (dyn *module-cache* module/cache) path) path)) :preload check-not-relative]) # Version of fexists that works even with a reduced OS (defn- fexists @@ -2849,7 +2849,7 @@ ``` [path] (var ret nil) - (def mp (dyn *module/paths* module/paths)) + (def mp (dyn *module-paths* module/paths)) (each [p mod-kind checker] mp (when (mod-filter checker path) (if (function? p) @@ -2959,7 +2959,7 @@ :core/stream path (file/open path :rb))) (def path-is-file (= f path)) - (default env ((dyn *module/make-env* make-env))) + (default env ((dyn *module-make-env* make-env))) (def spath (string path)) (put env :source (or source (if-not path-is-file spath path))) (var exit-error nil) @@ -3021,12 +3021,12 @@ of files as modules.`` @{:native (fn native-loader [path &] (native path (make-env))) :source (fn source-loader [path args] - (def ml (dyn *module/loading* module/loading)) + (def ml (dyn *module-loading* module/loading)) (put ml path true) (defer (put ml path nil) (dofile path ;args))) :preload (fn preload-loader [path & args] - (def mc (dyn *module/cache* module/cache)) + (def mc (dyn *module-cache* module/cache)) (when-let [m (in mc path)] (if (function? m) (set (mc path) (m path ;args)) @@ -3037,9 +3037,9 @@ [path args kargs] (def [fullpath mod-kind] (module/find path)) (unless fullpath (error mod-kind)) - (def mc (dyn *module/cache* module/cache)) - (def ml (dyn *module/loading* module/loading)) - (def mls (dyn *module/loaders* module/loaders)) + (def mc (dyn *module-cache* module/cache)) + (def ml (dyn *module-loading* module/loading)) + (def mls (dyn *module-loaders* module/loaders)) (if-let [check (if-not (kargs :fresh) (in mc fullpath))] check (if (ml fullpath) From e914eaf05542b1050bd9a2fa241af0239770ecf0 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Thu, 16 May 2024 21:37:08 -0500 Subject: [PATCH 06/24] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index efd8fc3c..6613d4c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. ## Unreleased - ??? -- Add *module/make-env* dynamic binding +- Add *module-make-env* dynamic binding - Add buffer/format-at - Add long form command line options for readable CLI usage - Fix bug with `net/accept-loop` that would sometimes miss connections. From 721f280966baec6c56f6ddd1a6a31038b416e99d Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Thu, 16 May 2024 21:42:34 -0500 Subject: [PATCH 07/24] Add `with-env`. --- CHANGELOG.md | 1 + src/boot/boot.janet | 5 +++++ test/suite-boot.janet | 3 +++ 3 files changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6613d4c2..98204705 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ All notable changes to this project will be documented in this file. ## Unreleased - ??? +- Add `with-env` - Add *module-make-env* dynamic binding - Add buffer/format-at - Add long form command line options for readable CLI usage diff --git a/src/boot/boot.janet b/src/boot/boot.janet index c465dfcb..71672fa1 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -1423,6 +1423,11 @@ ~(setdyn ,(bindings i) ,(bindings (+ i 1))))) ~(,resume (,fiber/new (fn [] ,;dyn-forms ,;body) :p))) +(defmacro with-env + `Run a block of code with a given environment table` + [env & body] + ~(,resume (,fiber/new (fn [] ,;body) : ,env))) + (defmacro with-vars ``Evaluates `body` with each var in `vars` temporarily bound. Similar signature to `let`, but each binding must be a var.`` diff --git a/test/suite-boot.janet b/test/suite-boot.janet index 89fb0076..b412c52d 100644 --- a/test/suite-boot.janet +++ b/test/suite-boot.janet @@ -976,4 +976,7 @@ (assert (= () '() (macex '())) "macex ()") (assert (= '[] (macex '[])) "macex []") +(assert (= :a (with-env @{:b :a} (dyn :b))) "with-env dyn") +(assert-error "unknown symbol +" (with-env @{} (eval '(+ 1 2)))) + (end-suite) From c1647a74c5a6055fd901c771a929ad0c8308f370 Mon Sep 17 00:00:00 2001 From: znley Date: Sat, 18 May 2024 07:18:59 +0000 Subject: [PATCH 08/24] Add LoongArch64 support --- src/include/janet.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/include/janet.h b/src/include/janet.h index 195c1c47..49430e55 100644 --- a/src/include/janet.h +++ b/src/include/janet.h @@ -112,7 +112,8 @@ extern "C" { || defined(__s390x__) /* S390 64-bit (BE) */ \ || (defined(__ppc64__) || defined(__PPC64__)) \ || defined(__aarch64__) /* ARM 64-bit */ \ - || (defined(__riscv) && (__riscv_xlen == 64)) /* RISC-V 64-bit */ + || (defined(__riscv) && (__riscv_xlen == 64)) /* RISC-V 64-bit */ \ + || defined(__loongarch64) /* LoongArch64 64-bit */ #define JANET_64 1 #else #define JANET_32 1 From 2e2f8abfc0afe4d442fe187ef76c8b27a9736e14 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sat, 18 May 2024 13:23:33 -0500 Subject: [PATCH 09/24] 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 10/24] 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 11/24] 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 12/24] 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 13/24] 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 14/24] 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 15/24] 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 16/24] 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 From ace60e18989632abd37c054239bd9b507630770b Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sat, 18 May 2024 17:55:47 -0500 Subject: [PATCH 17/24] Add ev/with-*lock macros. --- CHANGELOG.md | 1 + src/boot/boot.janet | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98204705..7e43aee4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ All notable changes to this project will be documented in this file. ## Unreleased - ??? +- Add macros `ev/with-lock`, `ev/with-rlock`, and `ev/with-wlock` for using mutexes and rwlocks. - Add `with-env` - Add *module-make-env* dynamic binding - Add buffer/format-at diff --git a/src/boot/boot.janet b/src/boot/boot.janet index 71672fa1..a8c02ce3 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -3748,6 +3748,30 @@ [& body] ~(,ev/thread (fn _do-thread [&] ,;body))) + (defn- acquire-release + [acq rel lock body] + (def l (gensym)) + ~(do + (def ,l ,lock) + (,acq ,l) + (defer (,rel ,l) + ,;body))) + + (defmacro ev/with-lock + ``Run a body of code after acquiring a lock. Will automatically release the lock when done.`` + [lock & body] + (acquire-release ev/acquire-lock ev/release-lock lock body)) + + (defmacro ev/with-rlock + ``Run a body of code after acquiring read access to an rwlock. Will automatically release the lock when done.`` + [lock & body] + (acquire-release ev/acquire-rlock ev/release-rlock lock body)) + + (defmacro ev/with-wlock + ``Run a body of code after acquiring read access to an rwlock. Will automatically release the lock when done.`` + [lock & body] + (acquire-release ev/acquire-wlock ev/release-wlock lock body)) + (defmacro ev/spawn-thread ``Run some code in a new thread. Like `ev/do-thread`, but returns nil immediately.`` [& body] From 03166a745a0957bb90336648938efb56dc557357 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sun, 19 May 2024 13:25:25 -0500 Subject: [PATCH 18/24] Disallow nan and inf in jdn. --- src/core/compile.c | 2 +- src/core/pp.c | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/compile.c b/src/core/compile.c index 4f45ff1f..93ae2039 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(slotchunks, sizeof(uint32_t)); if (NULL == chunks) { JANET_OUT_OF_MEMORY; } diff --git a/src/core/pp.c b/src/core/pp.c index bba70b6b..4b44c354 100644 --- a/src/core/pp.c +++ b/src/core/pp.c @@ -379,7 +379,10 @@ static int print_jdn_one(struct pretty *S, Janet x, int depth) { break; 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)); + double num = janet_unwrap_number(x); + if (isnan(num)) return 1; + if (isinf(num)) return 1; + int count = snprintf((char *) S->buffer->data + S->buffer->count, BUFSIZE, "%.17g", num); S->buffer->count += count; break; case JANET_SYMBOL: From 58ccb66659d575b0ff002422b6da6efaf8c35871 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sun, 19 May 2024 17:14:21 -0500 Subject: [PATCH 19/24] Move janet_buffer_dtostr --- src/core/strtod.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/strtod.c b/src/core/strtod.c index 89d20b45..258ba9d8 100644 --- a/src/core/strtod.c +++ b/src/core/strtod.c @@ -489,6 +489,8 @@ int janet_scan_uint64(const uint8_t *str, int32_t len, uint64_t *out) { return 0; } +#endif + void janet_buffer_dtostr(JanetBuffer *buffer, double x) { #define BUFSIZE 32 janet_buffer_extra(buffer, BUFSIZE); @@ -503,5 +505,3 @@ void janet_buffer_dtostr(JanetBuffer *buffer, double x) { } buffer->count += count; } - -#endif From 2637b33957ead7efa0bc7bb94fa63af4b2b541e4 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sun, 19 May 2024 17:40:39 -0500 Subject: [PATCH 20/24] Include locale.h and xlocale.h on some platforms. --- src/core/os.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/os.c b/src/core/os.c index ed12cdfb..cc9f9697 100644 --- a/src/core/os.c +++ b/src/core/os.c @@ -46,6 +46,7 @@ #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(JANET_APPLE) /* It seems only some bsds use this header for xlocale */ +#include #include #define JANET_EXTENDED_LOCALE #else From 7d3acc0ed6155a2c2bb1f08d167ae3da3b89a3b3 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sun, 19 May 2024 17:59:56 -0500 Subject: [PATCH 21/24] Get rid of "extended locale" support. While useful on some platforms, behavior seems to be different across the board, making use difficult. --- src/core/os.c | 76 ++++++++------------------------------------------- 1 file changed, 11 insertions(+), 65 deletions(-) diff --git a/src/core/os.c b/src/core/os.c index cc9f9697..9e0fb5dc 100644 --- a/src/core/os.c +++ b/src/core/os.c @@ -38,24 +38,14 @@ #include #include #include - +#include #ifdef JANET_BSD #include #endif -#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(JANET_APPLE) -/* It seems only some bsds use this header for xlocale */ -#include -#include -#define JANET_EXTENDED_LOCALE -#else -#include -#endif - #ifdef JANET_LINUX #include -#define JANET_EXTENDED_LOCALE #endif #ifdef JANET_WINDOWS @@ -1905,55 +1895,17 @@ JANET_CORE_FN(os_mktime, JANET_CORE_FN(os_setlocale, "(os/setlocale &opt locale category)", "Set the system locale, which affects how dates and numbers are formatted. " - "Passing nil to locale will return the current locale.") { + "Passing nil to locale will return the current locale. Category can be one of:\n\n" + " * :all (default)\n" + " * :collate\n" + " * :ctype\n" + " * :monetary\n" + " * :numeric\n" + " * :time\n\n" + "Returns the new locale if set successfully, otherwise nil. Note that this will affect " + "other functions such as `os/strftime` and even `printf`.") { janet_arity(argc, 0, 2); const char *locale_name = janet_optcstring(argv, argc, 0, NULL); -#ifdef JANET_EXTENDED_LOCALE - int category_int = LC_ALL; - int category_mask = LC_ALL_MASK; - if (argc > 1 && !janet_checktype(argv[1], JANET_NIL)) { - if (janet_keyeq(argv[1], "all")) { - category_mask = LC_ALL_MASK; - category_int = LC_ALL; - } else if (janet_keyeq(argv[1], "collate")) { - category_mask = LC_COLLATE_MASK; - category_int = LC_COLLATE; - } else if (janet_keyeq(argv[1], "ctype")) { - category_mask = LC_CTYPE_MASK; - category_int = LC_CTYPE; - } else if (janet_keyeq(argv[1], "monetary")) { - category_mask = LC_MONETARY_MASK; - category_int = LC_MONETARY; - } else if (janet_keyeq(argv[1], "numeric")) { - category_mask = LC_NUMERIC_MASK; - category_int = LC_NUMERIC; - } else if (janet_keyeq(argv[1], "time")) { - category_mask = LC_TIME_MASK; - category_int = LC_TIME; - } else { - janet_panicf("expected one of :all, :collate, :ctype, :monetary, :numeric, or :time, got %v", argv[1]); - } - } - if (locale_name == NULL) { - /* Now return new locale */ - const char *old = setlocale(category_int, NULL); - if (old == NULL) return janet_wrap_nil(); - return janet_cstringv(old); - } - /* Use newlocale instead of setlocale for per-thread behavior */ - locale_t loc = newlocale(category_mask, locale_name, 0); - if (loc == 0) { - janet_panicf("failed to make locale - %s", janet_strerror(errno)); - } - locale_t old_locale = uselocale(loc); - if (old_locale == 0) { - janet_panicf("failed to set locale - %s", janet_strerror(errno)); - } - if (old_locale != LC_GLOBAL_LOCALE) { - freelocale(old_locale); - } - return janet_wrap_nil(); -#else int category_int = LC_ALL; if (argc > 1 && !janet_checktype(argv[1], JANET_NIL)) { if (janet_keyeq(argv[1], "all")) { @@ -1973,11 +1925,8 @@ JANET_CORE_FN(os_setlocale, } } const char *old = setlocale(category_int, locale_name); - if (old == NULL) { - janet_panicf("failed to set locale - %s", janet_strerror(errno)); - } + if (old == NULL) return janet_wrap_nil(); return janet_cstringv(old); -#endif } JANET_CORE_FN(os_link, @@ -2834,8 +2783,5 @@ void janet_lib_os(JanetTable *env) { #endif JANET_REG_END }; -#if defined(JANET_WINDOWS) && !defined(JANET_REDUCED_OS) - _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); -#endif janet_core_cfuns_ext(env, NULL, os_cfuns); } From 60e0c8ea9265f9ba972b87134997f3a34606ef8a Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sat, 25 May 2024 09:25:27 -0500 Subject: [PATCH 22/24] Ignore gcov --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 0f06ee40..caeac6eb 100644 --- a/.gitignore +++ b/.gitignore @@ -126,6 +126,9 @@ vgcore.* *.idb *.pdb +# GGov +*.gcov + # Kernel Module Compile Results *.mod* *.cmd From ae2c5820a1515b7f52ee570b206d1559654aec5d Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sat, 25 May 2024 13:24:01 -0500 Subject: [PATCH 23/24] Fix janet_strerror when _GNU_SOURCE defined. --- src/core/util.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/util.c b/src/core/util.c index 9d7aab3a..09cf36f2 100644 --- a/src/core/util.c +++ b/src/core/util.c @@ -958,6 +958,9 @@ const char *janet_strerror(int e) { #ifdef JANET_WINDOWS /* Microsoft strerror seems sane here and is thread safe by default */ return strerror(e); +#elif defined(_GNU_SOURCE) + /* See https://linux.die.net/man/3/strerror_r */ + return strerror_r(e, janet_vm.strerror_buf, sizeof(janet_vm.strerror_buf)); #else strerror_r(e, janet_vm.strerror_buf, sizeof(janet_vm.strerror_buf)); return janet_vm.strerror_buf; From 7bae7d9efddce8ff0718234f79f3a9200f342690 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sun, 26 May 2024 12:04:35 -0500 Subject: [PATCH 24/24] Merge changes from bundle-tools branch: - Update file IO to explicitly use 64bit ftell/fseek - Add env argument to eval - Allow naming function literals with keywords. --- .github/workflows/test.yml | 4 +- .gitignore | 1 + CHANGELOG.md | 3 + src/boot/boot.janet | 163 ++++++++++++++++--------------------- src/core/compile.c | 2 +- src/core/debug.c | 7 +- src/core/features.h | 2 + src/core/io.c | 11 ++- src/core/os.c | 14 +++- src/core/specials.c | 7 +- src/core/vm.c | 2 +- test/suite-buffer.janet | 2 +- test/suite-value.janet | 2 +- 13 files changed, 112 insertions(+), 108 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 98fa7d02..e26ed7ed 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -73,7 +73,7 @@ jobs: - name: Compile the project run: make clean && make CC=x86_64-w64-mingw32-gcc LD=x86_64-w64-mingw32-gcc UNAME=MINGW RUN=wine - name: Test the project - run: make test UNAME=MINGW RUN=wine + run: make test UNAME=MINGW RUN=wine VERBOSE=1 test-arm-linux: name: Build and test ARM32 cross compilation @@ -88,4 +88,4 @@ jobs: - name: Compile the project run: make RUN="qemu-arm -L /usr/arm-linux-gnueabi/" CC=arm-linux-gnueabi-gcc LD=arm-linux-gnueabi-gcc - name: Test the project - run: make RUN="qemu-arm -L /usr/arm-linux-gnueabi/" SUBRUN="qemu-arm -L /usr/arm-linux-gnueabi/" test + run: make RUN="qemu-arm -L /usr/arm-linux-gnueabi/" SUBRUN="qemu-arm -L /usr/arm-linux-gnueabi/" test VERBOSE=1 diff --git a/.gitignore b/.gitignore index caeac6eb..ae5e64de 100644 --- a/.gitignore +++ b/.gitignore @@ -48,6 +48,7 @@ janet.wasm # Generated files *.gen.h *.gen.c +*.tmp # Generate test files *.out diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e43aee4..067626a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ All notable changes to this project will be documented in this file. ## Unreleased - ??? +- Add extra optional `env` argument to `eval` and `eval-string`. +- Allow naming function literals with a keyword. This allows better stacktraces for macros without + accidentally adding new bindings. - Add macros `ev/with-lock`, `ev/with-rlock`, and `ev/with-wlock` for using mutexes and rwlocks. - Add `with-env` - Add *module-make-env* dynamic binding diff --git a/src/boot/boot.janet b/src/boot/boot.janet index a8c02ce3..55e31d86 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -1,5 +1,5 @@ # The core janet library -# Copyright 2023 © Calvin Rose +# Copyright 2024 © Calvin Rose ### ### @@ -244,7 +244,7 @@ (let [[[err fib]] catch f (gensym) r (gensym)] - ~(let [,f (,fiber/new (fn [] ,body) :ie) + ~(let [,f (,fiber/new (fn :try [] ,body) :ie) ,r (,resume ,f)] (if (,= (,fiber/status ,f) :error) (do (def ,err ,r) ,(if fib ~(def ,fib ,f)) ,;(tuple/slice catch 1)) @@ -256,7 +256,7 @@ error, and the second is the return value or error.` [& body] (let [f (gensym) r (gensym)] - ~(let [,f (,fiber/new (fn [] ,;body) :ie) + ~(let [,f (,fiber/new (fn :protect [] ,;body) :ie) ,r (,resume ,f)] [(,not= :error (,fiber/status ,f)) ,r]))) @@ -313,7 +313,7 @@ [form & body] (with-syms [f r] ~(do - (def ,f (,fiber/new (fn [] ,;body) :ti)) + (def ,f (,fiber/new (fn :defer [] ,;body) :ti)) (def ,r (,resume ,f)) ,form (if (= (,fiber/status ,f) :dead) @@ -326,7 +326,7 @@ [form & body] (with-syms [f r] ~(do - (def ,f (,fiber/new (fn [] ,;body) :ti)) + (def ,f (,fiber/new (fn :edefer [] ,;body) :ti)) (def ,r (,resume ,f)) (if (= (,fiber/status ,f) :dead) ,r @@ -338,7 +338,7 @@ [tag & body] (with-syms [res target payload fib] ~(do - (def ,fib (,fiber/new (fn [] [,tag (do ,;body)]) :i0)) + (def ,fib (,fiber/new (fn :prompt [] [,tag (do ,;body)]) :i0)) (def ,res (,resume ,fib)) (def [,target ,payload] ,res) (if (,= ,tag ,target) @@ -629,17 +629,17 @@ ``Create a generator expression using the `loop` syntax. Returns a fiber that yields all values inside the loop in order. See `loop` for details.`` [head & body] - ~(,fiber/new (fn [] (loop ,head (yield (do ,;body)))) :yi)) + ~(,fiber/new (fn :generate [] (loop ,head (yield (do ,;body)))) :yi)) (defmacro coro "A wrapper for making fibers that may yield multiple values (coroutine). Same as `(fiber/new (fn [] ;body) :yi)`." [& body] - (tuple fiber/new (tuple 'fn '[] ;body) :yi)) + (tuple fiber/new (tuple 'fn :coro '[] ;body) :yi)) (defmacro fiber-fn "A wrapper for making fibers. Same as `(fiber/new (fn [] ;body) flags)`." [flags & body] - (tuple fiber/new (tuple 'fn '[] ;body) flags)) + (tuple fiber/new (tuple 'fn :fiber-fn '[] ;body) flags)) (defn sum "Returns the sum of xs. If xs is empty, returns 0." @@ -688,7 +688,7 @@ ~(if (def ,(def sym (gensym)) ,br) (do (def ,bl ,sym) ,(aux (+ 2 i))) ,fal2))))) - (aux 0)) + (aux 0)) (defmacro when-let "Same as `(if-let bindings (do ;body))`." @@ -702,11 +702,11 @@ (case (length functions) 0 nil 1 (in functions 0) - 2 (let [[f g] functions] (fn [& x] (f (g ;x)))) - 3 (let [[f g h] functions] (fn [& x] (f (g (h ;x))))) - 4 (let [[f g h i] functions] (fn [& x] (f (g (h (i ;x)))))) + 2 (let [[f g] functions] (fn :comp [& x] (f (g ;x)))) + 3 (let [[f g h] functions] (fn :comp [& x] (f (g (h ;x))))) + 4 (let [[f g h i] functions] (fn :comp [& x] (f (g (h (i ;x)))))) (let [[f g h i] functions] - (comp (fn [x] (f (g (h (i x))))) + (comp (fn :comp [x] (f (g (h (i x))))) ;(tuple/slice functions 4 -1))))) (defn identity @@ -717,7 +717,7 @@ (defn complement "Returns a function that is the complement to the argument." [f] - (fn [x] (not (f x)))) + (fn :complement [x] (not (f x)))) (defmacro- do-extreme [order args] @@ -880,7 +880,7 @@ ``Sorts `ind` in-place by calling a function `f` on each element and comparing the result with `<`.`` [f ind] - (sort ind (fn [x y] (< (f x) (f y))))) + (sort ind (fn :sort-by-comp [x y] (< (f x) (f y))))) (defn sorted ``Returns a new sorted array without modifying the old one. @@ -893,7 +893,7 @@ ``Returns a new sorted array that compares elements by invoking a function `f` on each element and comparing the result with `<`.`` [f ind] - (sorted ind (fn [x y] (< (f x) (f y))))) + (sorted ind (fn :sorted-by-comp [x y] (< (f x) (f y))))) (defn reduce ``Reduce, also know as fold-left in many languages, transforms @@ -1192,7 +1192,7 @@ ``Returns the juxtaposition of functions. In other words, `((juxt* a b c) x)` evaluates to `[(a x) (b x) (c x)]`.`` [& funs] - (fn [& args] + (fn :juxt* [& args] (def ret @[]) (each f funs (array/push ret (f ;args))) @@ -1205,7 +1205,7 @@ (def $args (gensym)) (each f funs (array/push parts (tuple apply f $args))) - (tuple 'fn (tuple '& $args) (tuple/slice parts 0))) + (tuple 'fn :juxt (tuple '& $args) (tuple/slice parts 0))) (defmacro defdyn ``Define an alias for a keyword that is used as a dynamic binding. The @@ -1421,12 +1421,12 @@ (def dyn-forms (seq [i :range [0 (length bindings) 2]] ~(setdyn ,(bindings i) ,(bindings (+ i 1))))) - ~(,resume (,fiber/new (fn [] ,;dyn-forms ,;body) :p))) + ~(,resume (,fiber/new (fn :with-dyns [] ,;dyn-forms ,;body) :p))) (defmacro with-env `Run a block of code with a given environment table` [env & body] - ~(,resume (,fiber/new (fn [] ,;body) : ,env))) + ~(,resume (,fiber/new (fn :with-env [] ,;body) : ,env))) (defmacro with-vars ``Evaluates `body` with each var in `vars` temporarily bound. Similar signature to @@ -1441,7 +1441,7 @@ (with-syms [ret f s] ~(do ,;saveold - (def ,f (,fiber/new (fn [] ,;setnew ,;body) :ti)) + (def ,f (,fiber/new (fn :with-vars [] ,;setnew ,;body) :ti)) (def ,ret (,resume ,f)) ,;restoreold (if (= (,fiber/status ,f) :dead) ,ret (,propagate ,ret ,f))))) @@ -1450,7 +1450,7 @@ "Partial function application." [f & more] (if (zero? (length more)) f - (fn [& r] (f ;more ;r)))) + (fn :partial [& r] (f ;more ;r)))) (defn every? ``Evaluates to the last element of `ind` if all preceding elements are truthy, @@ -1807,7 +1807,6 @@ (printf (dyn *pretty-format* "%q") x) (flush)) - (defn file/lines "Return an iterator over the lines of a file." [file] @@ -2148,8 +2147,8 @@ (def ret (case (type x) :tuple (if (= (tuple/type x) :brackets) - (tuple/brackets ;(map recur x)) - (dotup x)) + (tuple/brackets ;(map recur x)) + (dotup x)) :array (map recur x) :struct (table/to-struct (dotable x recur)) :table (dotable x recur) @@ -2330,7 +2329,7 @@ x))) x)) (def expanded (macex arg on-binding)) - (def name-splice (if name [name] [])) + (def name-splice (if name [name] [:short-fn])) (def fn-args (seq [i :range [0 (+ 1 max-param-seen)]] (symbol prefix '$ i))) ~(fn ,;name-splice [,;fn-args ,;(if vararg ['& (symbol prefix '$&)] [])] ,expanded)) @@ -2420,29 +2419,9 @@ col ": parse error: " (:error p) - (if ec "\e[0m" "")) + (if ec "\e[0m")) (eflush)) -(defn- print-line-col - ``Print the source code at a line, column in a source file. If unable to open - the file, prints nothing.`` - [where line col] - (if-not line (break)) - (unless (string? where) (break)) - (when-with [f (file/open where :r)] - (def source-code (file/read f :all)) - (var index 0) - (repeat (dec line) - (if-not index (break)) - (set index (string/find "\n" source-code index)) - (if index (++ index))) - (when index - (def line-end (string/find "\n" source-code index)) - (eprint " " (string/slice source-code index line-end)) - (when col - (+= index col) - (eprint (string/repeat " " (inc col)) "^"))))) - (defn warn-compile "Default handler for a compile warning." [msg level where &opt line col] @@ -2455,10 +2434,7 @@ ":" col ": compile warning (" level "): ") - (eprint msg) - (when ec - (print-line-col where line col) - (eprin "\e[0m")) + (eprint msg (if ec "\e[0m")) (eflush)) (defn bad-compile @@ -2475,10 +2451,7 @@ ": compile error: ") (if macrof (debug/stacktrace macrof msg "") - (eprint msg)) - (when ec - (print-line-col where line col) - (eprin "\e[0m")) + (eprint msg (if ec "\e[0m"))) (eflush)) (defn curenv @@ -2547,7 +2520,7 @@ :read read :expander expand} opts) (default env (or (fiber/getenv (fiber/current)) @{})) - (default chunks (fn [buf p] (getline "" buf env))) + (default chunks (fn chunks [buf p] (getline "" buf env))) (default onstatus debug/stacktrace) (default on-compile-error bad-compile) (default on-compile-warning warn-compile) @@ -2682,8 +2655,8 @@ (defn eval ``Evaluates a form in the current environment. If more control over the environment is needed, use `run-context`.`` - [form] - (def res (compile form nil :eval)) + [form &opt env] + (def res (compile form env :eval)) (if (= (type res) :function) (res) (error (get res :error)))) @@ -2722,9 +2695,9 @@ (defn eval-string ``Evaluates a string in the current environment. If more control over the environment is needed, use `run-context`.`` - [str] + [str &opt env] (var ret nil) - (each x (parse-all str) (set ret (eval x))) + (each x (parse-all str) (set ret (eval x env))) ret) (def load-image-dict @@ -2867,7 +2840,7 @@ (set ret [fullpath mod-kind]) (break)))))) (if ret ret - (let [expander (fn [[t _ chk]] + (let [expander (fn :expander [[t _ chk]] (when (string? t) (when (mod-filter chk path) (module/expand-path path t)))) @@ -2934,7 +2907,7 @@ set to a truthy value." [env &opt level is-repl] (default level 1) - (fn [f x] + (fn :debugger [f x] (def fs (fiber/status f)) (if (= :dead fs) (when is-repl @@ -3024,7 +2997,7 @@ ``A table of loading method names to loading functions. This table lets `require` and `import` load many different kinds of files as modules.`` - @{:native (fn native-loader [path &] (native path (make-env))) + @{:native (fn native-loader [path &] (native path ((dyn *module-make-env* make-env)))) :source (fn source-loader [path args] (def ml (dyn *module-loading* module/loading)) (put ml path true) @@ -3142,6 +3115,7 @@ [&opt env local] (env-walk keyword? env local)) + (defdyn *doc-width* "Width in columns to print documentation printed with `doc-format`.") @@ -3704,7 +3678,7 @@ [&opt chunks onsignal env parser read] (default env (make-env)) (default chunks - (fn [buf p] + (fn :chunks [buf p] (getline (string "repl:" @@ -3735,18 +3709,18 @@ Returns a fiber that is scheduled to run the function. ``` [f & args] - (ev/go (fn _call [&] (f ;args)))) + (ev/go (fn :call [&] (f ;args)))) (defmacro ev/spawn "Run some code in a new fiber. This is shorthand for `(ev/go (fn [] ;body))`." [& body] - ~(,ev/go (fn _spawn [&] ,;body))) + ~(,ev/go (fn :spawn [&] ,;body))) (defmacro ev/do-thread ``Run some code in a new thread. Suspends the current fiber until the thread is complete, and evaluates to nil.`` [& body] - ~(,ev/thread (fn _do-thread [&] ,;body))) + ~(,ev/thread (fn :do-thread [&] ,;body))) (defn- acquire-release [acq rel lock body] @@ -3775,7 +3749,7 @@ (defmacro ev/spawn-thread ``Run some code in a new thread. Like `ev/do-thread`, but returns nil immediately.`` [& body] - ~(,ev/thread (fn _spawn-thread [&] ,;body) nil :n)) + ~(,ev/thread (fn :spawn-thread [&] ,;body) nil :n)) (defmacro ev/with-deadline `` @@ -3824,7 +3798,7 @@ (def ,res @[]) ,;(seq [[i body] :pairs bodies] ~(do - (def ,ftemp (,ev/go (fn [] (put ,res ,i ,body)) nil ,chan)) + (def ,ftemp (,ev/go (fn :ev/gather [] (put ,res ,i ,body)) nil ,chan)) (,put ,fset ,ftemp ,ftemp))) (,wait-for-fibers ,chan ,fset) ,res)))) @@ -3907,12 +3881,12 @@ ~(defn ,alias ,;meta [,;formal-args] (,ffi/call (,(delay (make-ptr))) (,(delay (make-sig))) ,;formal-args)) ~(defn ,alias ,;meta [,;formal-args] - (,ffi/call ,(make-ptr) ,(make-sig) ,;formal-args))))) + (,ffi/call ,(make-ptr) ,(make-sig) ,;formal-args)))) (defmacro ffi/defbind "Generate bindings for native functions in a convenient manner." [name ret-type & body] - ~(ffi/defbind-alias ,name ,name ,ret-type ,;body)) + ~(ffi/defbind-alias ,name ,name ,ret-type ,;body))) ### ### @@ -3989,7 +3963,6 @@ (merge-into module/cache old-modcache) nil) - ### ### ### CLI Tool Main @@ -4026,6 +3999,28 @@ (compwhen (not (dyn 'os/isatty)) (defmacro os/isatty [&] true)) +(def- long-to-short + "map long options to short options" + {"-help" "h" + "-version" "v" + "-stdin" "s" + "-eval" "e" + "-expression" "E" + "-debug" "d" + "-repl" "r" + "-noprofile" "R" + "-persistent" "p" + "-quiet" "q" + "-flycheck" "k" + "-syspath" "m" + "-compile" "c" + "-image" "i" + "-nocolor" "n" + "-color" "N" + "-library" "l" + "-lint-warn" "w" + "-lint-error" "x"}) + (defn cli-main `Entrance for the Janet CLI tool. Call this function with the command line arguments as an array or tuple of strings to invoke the CLI interface.` @@ -4057,28 +4052,6 @@ (def x (in args (+ i 1))) (or (scan-number x) (keyword x))) - (def- long-to-short - "map long options to short options" - {"-help" "h" - "-version" "v" - "-stdin" "s" - "-eval" "e" - "-expression" "E" - "-debug" "d" - "-repl" "r" - "-noprofile" "R" - "-persistent" "p" - "-quiet" "q" - "-flycheck" "k" - "-syspath" "m" - "-compile" "c" - "-image" "i" - "-nocolor" "n" - "-color" "N" - "-library" "l" - "-lint-warn" "w" - "-lint-error" "x"}) - # Flag handlers (def handlers {"h" (fn [&] diff --git a/src/core/compile.c b/src/core/compile.c index 93ae2039..587db3a5 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -1056,7 +1056,7 @@ JanetCompileResult janet_compile_lint(Janet source, if (c.result.status == JANET_COMPILE_OK) { JanetFuncDef *def = janetc_pop_funcdef(&c); - def->name = janet_cstring("_thunk"); + def->name = janet_cstring("thunk"); janet_def_addflags(def); c.result.funcdef = def; } else { diff --git a/src/core/debug.c b/src/core/debug.c index 35b2b331..12c6c2c4 100644 --- a/src/core/debug.c +++ b/src/core/debug.c @@ -164,7 +164,7 @@ void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *prefix) { } } if (frame->flags & JANET_STACKFRAME_TAILCALL) - janet_eprintf(" (tailcall)"); + janet_eprintf(" (tail call)"); if (frame->func && frame->pc) { int32_t off = (int32_t)(frame->pc - def->bytecode); if (def->sourcemap) { @@ -180,6 +180,11 @@ void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *prefix) { } } janet_eprintf("\n"); + /* Print fiber points optionally. Clutters traces but provides info + if (i <= 0 && fi > 0) { + janet_eprintf(" in parent fiber\n"); + } + */ } } diff --git a/src/core/features.h b/src/core/features.h index b3e1f752..c9bc72be 100644 --- a/src/core/features.h +++ b/src/core/features.h @@ -76,4 +76,6 @@ #define __BSD_VISIBLE 1 #endif +#define _FILE_OFFSET_BITS 64 + #endif diff --git a/src/core/io.c b/src/core/io.c index 49dba260..75e2ec5e 100644 --- a/src/core/io.c +++ b/src/core/io.c @@ -41,6 +41,11 @@ static void io_file_marshal(void *p, JanetMarshalContext *ctx); static void *io_file_unmarshal(JanetMarshalContext *ctx); static Janet io_file_next(void *p, Janet key); +#ifdef JANET_WINDOWS +#define ftell _ftelli64 +#define fseek _fseeki64 +#endif + const JanetAbstractType janet_file_type = { "core/file", cfun_io_gc, @@ -337,7 +342,7 @@ JANET_CORE_FN(cfun_io_fseek, JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type); if (iof->flags & JANET_FILE_CLOSED) janet_panic("file is closed"); - long int offset = 0; + int64_t offset = 0; int whence = SEEK_CUR; if (argc >= 2) { const uint8_t *whence_sym = janet_getkeyword(argv, 1); @@ -351,7 +356,7 @@ JANET_CORE_FN(cfun_io_fseek, janet_panicf("expected one of :cur, :set, :end, got %v", argv[1]); } if (argc == 3) { - offset = (long) janet_getinteger64(argv, 2); + offset = (int64_t) janet_getinteger64(argv, 2); } } if (fseek(iof->file, offset, whence)) janet_panic("error seeking file"); @@ -365,7 +370,7 @@ JANET_CORE_FN(cfun_io_ftell, JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type); if (iof->flags & JANET_FILE_CLOSED) janet_panic("file is closed"); - long pos = ftell(iof->file); + int64_t pos = ftell(iof->file); if (pos == -1) janet_panic("error getting position in file"); return janet_wrap_number((double)pos); } diff --git a/src/core/os.c b/src/core/os.c index 9e0fb5dc..2cd079d8 100644 --- a/src/core/os.c +++ b/src/core/os.c @@ -2411,8 +2411,18 @@ JANET_CORE_FN(os_dir, /* Read directory items with opendir / readdir / closedir */ struct dirent *dp; DIR *dfd = opendir(dir); - if (dfd == NULL) janet_panicf("cannot open directory %s", dir); - while ((dp = readdir(dfd)) != NULL) { + if (dfd == NULL) janet_panicf("cannot open directory %s: %s", dir, janet_strerror(errno)); + for (;;) { + errno = 0; + dp = readdir(dfd); + if (dp == NULL) { + if (errno) { + int olderr = errno; + closedir(dfd); + janet_panicf("failed to read directory %s: %s", dir, janet_strerror(olderr)); + } + break; + } if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) { continue; } diff --git a/src/core/specials.c b/src/core/specials.c index bae6e4a2..934f25d3 100644 --- a/src/core/specials.c +++ b/src/core/specials.c @@ -925,6 +925,7 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) { int structarg = 0; int allow_extra = 0; int selfref = 0; + int hasname = 0; int seenamp = 0; int seenopt = 0; int namedargs = 0; @@ -943,6 +944,10 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) { head = argv[0]; if (janet_checktype(head, JANET_SYMBOL)) { selfref = 1; + hasname = 1; + parami = 1; + } else if (janet_checktype(head, JANET_KEYWORD)) { + hasname = 1; parami = 1; } if (parami >= argn || !janet_checktype(argv[parami], JANET_TUPLE)) { @@ -1103,7 +1108,7 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) { if (vararg) def->flags |= JANET_FUNCDEF_FLAG_VARARG; if (structarg) def->flags |= JANET_FUNCDEF_FLAG_STRUCTARG; - if (selfref) def->name = janet_unwrap_symbol(head); + if (hasname) def->name = janet_unwrap_symbol(head); /* Also correctly unwraps keyword */ janet_def_addflags(def); defindex = janetc_addfuncdef(c, def); diff --git a/src/core/vm.c b/src/core/vm.c index acaeba98..91a86318 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -318,7 +318,7 @@ static Janet janet_binop_call(const char *lmethod, const char *rmethod, Janet lh Janet lr = janet_method_lookup(rhs, rmethod); Janet argv[2] = { rhs, lhs }; if (janet_checktype(lr, JANET_NIL)) { - janet_panicf("could not find method :%s for %v, or :%s for %v", + janet_panicf("could not find method :%s for %v or :%s for %v", lmethod, lhs, rmethod, rhs); } diff --git a/test/suite-buffer.janet b/test/suite-buffer.janet index 9821e384..b6ceecec 100644 --- a/test/suite-buffer.janet +++ b/test/suite-buffer.janet @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Calvin Rose +# Copyright (c) 2024 Calvin Rose # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to diff --git a/test/suite-value.janet b/test/suite-value.janet index 650cc99b..bbd443a6 100644 --- a/test/suite-value.janet +++ b/test/suite-value.janet @@ -42,7 +42,7 @@ (defn buffer-factory [] - @"im am a buffer") + @"i am a buffer") (assert (not= (buffer-factory) (buffer-factory)) "buffer instantiation")