From c4035b2273c53f34514ab78942d51d5b946024db Mon Sep 17 00:00:00 2001 From: Michael Camilleri Date: Sun, 21 Jun 2020 17:54:06 +0900 Subject: [PATCH 01/31] Change string representation of nil to empty string --- src/core/pp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/pp.c b/src/core/pp.c index 59acba1b..3a855536 100644 --- a/src/core/pp.c +++ b/src/core/pp.c @@ -188,7 +188,7 @@ static void janet_escape_buffer_b(JanetBuffer *buffer, JanetBuffer *bx) { void janet_to_string_b(JanetBuffer *buffer, Janet x) { switch (janet_type(x)) { case JANET_NIL: - janet_buffer_push_cstring(buffer, "nil"); + janet_buffer_push_cstring(buffer, ""); break; case JANET_BOOLEAN: janet_buffer_push_cstring(buffer, @@ -277,6 +277,9 @@ void janet_description_b(JanetBuffer *buffer, Janet x) { switch (janet_type(x)) { default: break; + case JANET_NIL: + janet_buffer_push_cstring(buffer, "nil"); + return; case JANET_KEYWORD: janet_buffer_push_u8(buffer, ':'); break; From 6c917f686ac6f9342e5dc2d29dd769dfcbd02dd9 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Wed, 24 Jun 2020 08:40:23 -0500 Subject: [PATCH 02/31] Add :h default peg class, as well as ad \v to whitespace. --- src/boot/boot.janet | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/boot/boot.janet b/src/boot/boot.janet index ac8cbf90..c20b4a9a 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -1953,20 +1953,24 @@ that should make it easier to write more complex patterns." ~@{:d (range "09") :a (range "az" "AZ") - :s (set " \t\r\n\0\f") + :s (set " \t\r\n\0\f\v") :w (range "az" "AZ" "09") + :h (range "09" "af") :S (if-not :s 1) :W (if-not :w 1) :A (if-not :a 1) :D (if-not :d 1) + :H (if-not :h 1) :d+ (some :d) :a+ (some :a) :s+ (some :s) :w+ (some :w) + :h+ (some :h) :d* (any :d) :a* (any :a) :w* (any :w) - :s* (any :s)}) + :s* (any :s) + :h* (any :h)}) ### ### From ac5de1f96eb084ba452433e78d5be407f994c942 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Wed, 24 Jun 2020 16:00:00 -0500 Subject: [PATCH 03/31] Change compare-primitive to cmp. cmp is implemented as a VM instruction rather than a function. --- CHANGELOG.md | 2 ++ src/boot/boot.janet | 40 +++++++++++++++++++++------------------- src/core/cfuns.c | 4 ++++ src/core/compile.h | 1 + src/core/corelib.c | 9 +++++++++ src/core/string.c | 2 +- test/suite0.janet | 10 +++++----- 7 files changed, 43 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 870dfa10..37dd8e80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ All notable changes to this project will be documented in this file. ## Unreleased - ??? +- Change `compare-primitve` to `cmp` and make it more efficient. +- Change `reverse` to `reversed`, reverse now mutates the backing array - `janet_dobytes` and `janet_dostring` return parse errors in \*out - Add `repeat` macro for iterating something n times. - Add `eachy` (each yield) macro for iterating a fiber. diff --git a/src/boot/boot.janet b/src/boot/boot.janet index c20b4a9a..cb84664b 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -688,28 +688,17 @@ ## Polymorphic comparisons -(defn compare-primitive - "Compare x and y using primitive operators. - Returns -1,0,1 for x < y, x = y, x > y respectively. - Present mostly for constructing 'compare' methods in prototypes." - [x y] - (cond - (= x y) 0 - (< x y) -1 - (> x y) 1)) - (defn compare - "Polymorphic compare. Returns -1,0,1 for x < y, x = y, x > y respectively. + "Polymorphic compare. Returns -1, 0, 1 for x < y, x = y, x > y respectively. Differs from the primitive comparators in that it first checks to see whether either x or y implement a 'compare' method which can - compare x and y. If so it uses that compare method. If not, it + compare x and y. If so it uses that compare method. If not, it delegates to the primitive comparators." [x y] (or (when-let [f (get x :compare)] (f x y)) - (when-let [f (get y :compare) - fyx (f y x)] (- fyx)) - (compare-primitive x y))) + (when-let [f (get y :compare)] (- (f y x))) + (cmp x y))) (defn- compare-reduce [op xs] (var r true) @@ -1209,18 +1198,31 @@ res) (defn reverse + "Reverses the order of the elements in a given array or tuple and returns a new array." + [t] + (def len-1 (- (length t) 1)) + (def half (/ len-1 2)) + (for i 0 half + (def j (- len-1 i)) + (def l (in t i)) + (def r (in t j)) + (put t i r) + (put t j l)) + t) + +(defn reversed "Reverses the order of the elements in a given array or tuple and returns a new array." [t] (def len (length t)) (var n (- len 1)) - (def reversed (array/new len)) + (def ret (array/new len)) (while (>= n 0) - (array/push reversed (in t n)) + (array/push ret (in t n)) (-- n)) - reversed) + ret) (defn invert - "Returns a table of where the keys of an associative data structure + "Returns a table where the keys of an associative data structure are the values, and the values of the keys. If multiple keys have the same value, one key will be ignored." [ds] diff --git a/src/core/cfuns.c b/src/core/cfuns.c index 40db0ff1..0520576f 100644 --- a/src/core/cfuns.c +++ b/src/core/cfuns.c @@ -148,6 +148,9 @@ static JanetSlot do_modulo(JanetFopts opts, JanetSlot *args) { static JanetSlot do_remainder(JanetFopts opts, JanetSlot *args) { return opreduce(opts, args, JOP_REMAINDER, janet_wrap_nil()); } +static JanetSlot do_cmp(JanetFopts opts, JanetSlot *args) { + return opreduce(opts, args, JOP_COMPARE, janet_wrap_nil()); +} static JanetSlot do_put(JanetFopts opts, JanetSlot *args) { if (opts.flags & JANET_FOPTS_DROP) { janetc_emit_sss(opts.compiler, JOP_PUT, args[0], args[1], args[2], 0); @@ -323,6 +326,7 @@ static const JanetFunOptimizer optimizers[] = { {arity1or2, do_next}, {fixarity2, do_modulo}, {fixarity2, do_remainder}, + {fixarity2, do_cmp}, }; const JanetFunOptimizer *janetc_funopt(uint32_t flags) { diff --git a/src/core/compile.h b/src/core/compile.h index 3f53da58..5782bbf1 100644 --- a/src/core/compile.h +++ b/src/core/compile.h @@ -60,6 +60,7 @@ #define JANET_FUN_NEXT 28 #define JANET_FUN_MODULO 29 #define JANET_FUN_REMAINDER 30 +#define JANET_FUN_CMP 31 /* Compiler typedefs */ typedef struct JanetCompiler JanetCompiler; diff --git a/src/core/corelib.c b/src/core/corelib.c index 3e17e918..f2b187f8 100644 --- a/src/core/corelib.c +++ b/src/core/corelib.c @@ -968,6 +968,10 @@ static const uint32_t remainder_asm[] = { JOP_REMAINDER | (1 << 24), JOP_RETURN }; +static const uint32_t cmp_asm[] = { + JOP_COMPARE | (1 << 24), + JOP_RETURN +}; #endif /* ifdef JANET_BOOTSTRAP */ /* @@ -1021,6 +1025,11 @@ JanetTable *janet_core_env(JanetTable *replacements) { "%", 2, 2, 2, 2, remainder_asm, sizeof(remainder_asm), JDOC("(% dividend divisor)\n\n" "Returns the remainder of dividend / divisor.")); + janet_quick_asm(env, JANET_FUN_CMP, + "cmp", 2, 2, 2, 2, cmp_asm, sizeof(cmp_asm), + JDOC("(cmp x y)\n\n" + "Returns -1 if x is strictly less than y, 1 if y is strictly greater " + "than x, and 0 otherwise. To return 0, x and y must be the exact same type.")); janet_quick_asm(env, JANET_FUN_NEXT, "next", 2, 1, 2, 2, next_asm, sizeof(next_asm), JDOC("(next ds &opt key)\n\n" diff --git a/src/core/string.c b/src/core/string.c index b397a0f1..7c4ed80c 100644 --- a/src/core/string.c +++ b/src/core/string.c @@ -62,7 +62,7 @@ int janet_string_compare(const uint8_t *lhs, const uint8_t *rhs) { int32_t ylen = janet_string_length(rhs); int32_t len = xlen > ylen ? ylen : xlen; int res = memcmp(lhs, rhs, len); - if (res) return res; + if (res) return res > 0 ? 1 : -1; if (xlen == ylen) return 0; return xlen < ylen ? -1 : 1; } diff --git a/test/suite0.janet b/test/suite0.janet index f5fa2a8b..fa8352cf 100644 --- a/test/suite0.janet +++ b/test/suite0.janet @@ -337,9 +337,9 @@ ## Polymorphic comparison -- Issue #272 # confirm polymorphic comparison delegation to primitive comparators: -(assert (= 0 (compare-primitive 3 3)) "compare-primitive integers (1)") -(assert (= -1 (compare-primitive 3 5)) "compare-primitive integers (2)") -(assert (= 1 (compare-primitive "foo" "bar")) "compare-primitive strings") +(assert (= 0 (cmp 3 3)) "compare-primitive integers (1)") +(assert (= -1 (cmp 3 5)) "compare-primitive integers (2)") +(assert (= 1 (cmp "foo" "bar")) "compare-primitive strings") (assert (= 0 (compare 1 1)) "compare integers (1)") (assert (= -1 (compare 1 2)) "compare integers (2)") (assert (= 1 (compare "foo" "bar")) "compare strings (1)") @@ -372,9 +372,9 @@ @{:type :mynum :v 0 :compare (fn [self other] (case (type other) - :number (compare-primitive (self :v) other) + :number (cmp (self :v) other) :table (when (= (get other :type) :mynum) - (compare-primitive (self :v) (other :v)))))}) + (cmp (self :v) (other :v)))))}) (let [n3 (table/setproto @{:v 3} mynum)] (assert (= 0 (compare 3 n3)) "compare num to object (1)") From 6a187a384bedf12a87e2abadf2cec5615c376783 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Wed, 24 Jun 2020 16:10:57 -0500 Subject: [PATCH 04/31] Make zipcoll more generic. Work with any iterable (next) type. --- src/boot/boot.janet | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/boot/boot.janet b/src/boot/boot.janet index cb84664b..58c46cb5 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -1236,11 +1236,14 @@ Returns a new table." [ks vs] (def res @{}) - (def lk (length ks)) - (def lv (length vs)) - (def len (if (< lk lv) lk lv)) - (for i 0 len - (put res (in ks i) (in vs i))) + (var kk nil) + (var vk nil) + (while true + (set kk (next ks kk)) + (if (= nil kk) (break)) + (set vk (next vs vk)) + (if (= nil vk) (break)) + (put res (in ks kk) (in vs vk))) res) (defn get-in From 1b420f69aa2bd13b39c79e521db715a04d388b5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20Pospi=CC=81s=CC=8Cil?= Date: Thu, 25 Jun 2020 09:35:03 +0200 Subject: [PATCH 05/31] Fix reverse docstring --- src/boot/boot.janet | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/boot/boot.janet b/src/boot/boot.janet index 58c46cb5..30cb234f 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -1198,7 +1198,7 @@ res) (defn reverse - "Reverses the order of the elements in a given array or tuple and returns a new array." + "Reverses the order of the elements in a given array or tuple and returns it mutated." [t] (def len-1 (- (length t) 1)) (def half (/ len-1 2)) From 7b033a48a36f164714d12d8198f8c89ff70d430b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20Pospi=CC=81s=CC=8Cil?= Date: Thu, 25 Jun 2020 09:43:10 +0200 Subject: [PATCH 06/31] Wrap both reverse and reversed docstring to 80 chr --- src/boot/boot.janet | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/boot/boot.janet b/src/boot/boot.janet index 30cb234f..48bb9234 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -1198,7 +1198,8 @@ res) (defn reverse - "Reverses the order of the elements in a given array or tuple and returns it mutated." + "Reverses the order of the elements in a given array or tuple and returns it + mutated." [t] (def len-1 (- (length t) 1)) (def half (/ len-1 2)) @@ -1211,7 +1212,8 @@ t) (defn reversed - "Reverses the order of the elements in a given array or tuple and returns a new array." + "Reverses the order of the elements in a given array or tuple and returns + a new array." [t] (def len (length t)) (var n (- len 1)) From 51bf8a35385f5008c67df4a9a25b92eec6862f9d Mon Sep 17 00:00:00 2001 From: Jason Pepas Date: Fri, 26 Jun 2020 04:11:21 -0500 Subject: [PATCH 07/31] Add ppc to os/arch --- src/core/os.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/os.c b/src/core/os.c index 6f68fafe..837375cb 100644 --- a/src/core/os.c +++ b/src/core/os.c @@ -159,6 +159,8 @@ static Janet os_arch(int32_t argc, Janet *argv) { return janet_ckeywordv("arm"); #elif (defined(__sparc__)) return janet_ckeywordv("sparc"); +#elif (defined(__ppc__)) + return janet_ckeywordv("ppc"); #else return janet_ckeywordv("unknown"); #endif From f9f90ba1d6deccf35096e7b23abda8921f4461e9 Mon Sep 17 00:00:00 2001 From: Jason Pepas Date: Fri, 26 Jun 2020 14:40:49 -0500 Subject: [PATCH 08/31] Support for systems missing O_CLOEXEC --- src/conf/janetconf.h | 1 + src/core/net.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/conf/janetconf.h b/src/conf/janetconf.h index 2343666b..e8d6e8b9 100644 --- a/src/conf/janetconf.h +++ b/src/conf/janetconf.h @@ -58,6 +58,7 @@ /* #define JANET_NO_REALPATH */ /* #define JANET_NO_SYMLINKS */ /* #define JANET_NO_UMASK */ +/* #define JANET_NO_CLOEXEC */ /* #define JANET_OUT_OF_MEMORY do { printf("janet out of memory\n"); exit(1); } while (0) */ /* #define JANET_EXIT(msg) do { printf("C assert failed executing janet: %s\n", msg); exit(1); } while (0) */ /* #define JANET_TOP_LEVEL_SIGNAL(msg) call_my_function((msg), stderr) */ diff --git a/src/core/net.c b/src/core/net.c index 51bad4bf..44fe7052 100644 --- a/src/core/net.c +++ b/src/core/net.c @@ -112,7 +112,7 @@ typedef struct { #endif static JanetStream *make_stream(int fd, int flags) { JanetStream *stream = janet_abstract(&StreamAT, sizeof(JanetStream)); -#ifndef SOCK_CLOEXEC +#if !defined(SOCK_CLOEXEC) && !defined(JANET_NO_CLOEXEC) int extra = O_CLOEXEC; #else int extra = 0; From de27fc15b68a23a0cca1746593be37eb40e9553d Mon Sep 17 00:00:00 2001 From: Steve Phillips Date: Fri, 26 Jun 2020 20:28:18 -0700 Subject: [PATCH 09/31] Add .gitattributes: detect/syntax highlight .janet files as Clojure --- .gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..c0b7fe0e --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.janet linguist-language=Clojure From 51ff43e2f2ccd3a3bcf295409ba62467e3f646a8 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sat, 27 Jun 2020 11:23:47 -0500 Subject: [PATCH 10/31] Update range checks for 64 bit integers. --- jpm | 41 ++++++++++++++++++++++++++--------------- src/core/util.c | 8 ++++++-- src/include/janet.h | 8 +++++++- 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/jpm b/jpm index 31ca46d2..976c8445 100755 --- a/jpm +++ b/jpm @@ -1069,36 +1069,47 @@ usage: jpm [--key=value, --flag] ... [subcommand] [args] ... Run from a directory containing a project.janet file to perform operations on a project, or from anywhere to do operations on the global module cache (modpath). +Commands that need write permission to the modpath are considered privileged commands - in +some environments they may require super user privileges. +Other project-level commands need to have a ./project.janet file in the current directory. -Subcommands are: - build : build all artifacts +Unprivileged global subcommands: help : show this help text + show-paths : prints the paths that will be used to install things. + quickbin entry executable : Create an executable from a janet script with a main function. + +Privileged global subcommands: install (repo or name)... : install artifacts. If a repo is given, install the contents of that git repository, assuming that the repository is a jpm project. If not, build and install the current project. uninstall (module)... : uninstall a module. If no module is given, uninstall the module defined by the current directory. - show-paths : prints the paths that will be used to install things. - clean : remove any generated files or artifacts - test : run tests. Tests should be .janet files in the test/ directory relative to project.janet. - deps : install dependencies for the current project. clear-cache : clear the git cache. Useful for updating dependencies. clear-manifest : clear the manifest. Useful for fixing broken installs. - run rule : run a rule. Can also run custom rules added via (phony "task" [deps...] ...) - or (rule "ouput.file" [deps...] ...). - rules : list rules available with run. - rule-tree (root rule) (depth) : Print a nice tree to see what rules depend on other rules. - Optinally provide a root rule to start printing from, and a - max depth to print. Without these options, all rules will print - their full dependency tree. - update-pkgs : Update the current package listing from the remote git repository selected. - quickbin entry executable : Create an executable from a janet script with a main function. make-lockfile (lockfile) : Create a lockfile based on repositories in the cache. The lockfile will record the exact versions of dependencies used to ensure a reproducible build. Lockfiles are best used with applications, not libraries. The default lockfile name is lockfile.jdn. load-lockfile (lockfile) : Install modules from a lockfile in a reproducible way. The default lockfile name is lockfile.jdn. + update-pkgs : Update the current package listing from the remote git repository selected. + +Privileged project subcommands: + deps : install dependencies for the current project. + install : install artifacts of the current project. + uninstall : uninstall the current project's artifacts. + +Unprivileged project subcommands: + build : build all artifacts + clean : remove any generated files or artifacts + test : run tests. Tests should be .janet files in the test/ directory relative to project.janet. + run rule : run a rule. Can also run custom rules added via (phony "task" [deps...] ...) + or (rule "ouput.file" [deps...] ...). + rules : list rules available with run. + rule-tree (root rule) (depth) : Print a nice tree to see what rules depend on other rules. + Optionally provide a root rule to start printing from, and a + max depth to print. Without these options, all rules will print + their full dependency tree. debug-repl : Run a repl in the context of the current project.janet file. This lets you run rules and otherwise debug the current project.janet file. diff --git a/src/core/util.c b/src/core/util.c index 1f04aa22..cb8f5bb1 100644 --- a/src/core/util.c +++ b/src/core/util.c @@ -574,8 +574,12 @@ int janet_checksize(Janet x) { if (!janet_checktype(x, JANET_NUMBER)) return 0; double dval = janet_unwrap_number(x); - return dval == (double)((size_t) dval) && - dval <= SIZE_MAX; + if (dval != (double)((size_t) dval)) return 0; + if (SIZE_MAX > JANET_INTMAX_INT64) { + return dval <= JANET_INTMAX_INT64; + } else { + return dval <= SIZE_MAX; + } } JanetTable *janet_get_core_table(const char *name) { diff --git a/src/include/janet.h b/src/include/janet.h index 73960b5a..37c467fc 100644 --- a/src/include/janet.h +++ b/src/include/janet.h @@ -542,6 +542,12 @@ JANET_API Janet janet_wrap_integer(int32_t x); #include +/* Limits for converting doubles to 64 bit integers */ +#define JANET_INTMAX_DOUBLE 9007199254740991.0 +#define JANET_INTMIN_DOUBLE (-9007199254740991.0) +#define JANET_INTMAX_INT64 9007199254740991 +#define JANET_INTMIN_INT64 (-9007199254740991) + #define janet_u64(x) ((x).u64) #define JANET_NANBOX_TAGBITS 0xFFFF800000000000llu @@ -706,7 +712,7 @@ JANET_API int janet_checkint64(Janet x); JANET_API int janet_checksize(Janet x); JANET_API JanetAbstract janet_checkabstract(Janet x, const JanetAbstractType *at); #define janet_checkintrange(x) ((x) >= INT32_MIN && (x) <= INT32_MAX && (x) == (int32_t)(x)) -#define janet_checkint64range(x) ((x) >= INT64_MIN && (x) <= INT64_MAX && (x) == (int64_t)(x)) +#define janet_checkint64range(x) ((x) >= JANET_INTMIN_DOUBLE && (x) <= JANET_INTMAX_DOUBLE && (x) == (int64_t)(x)) #define janet_unwrap_integer(x) ((int32_t) janet_unwrap_number(x)) #define janet_wrap_integer(x) janet_wrap_number((int32_t)(x)) From 9c5e97144d85c0f990c4d86e16871c82356e61c5 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sat, 27 Jun 2020 12:39:16 -0500 Subject: [PATCH 11/31] More small changes to help with cross compilation via makefile. Add option to turn off built in getline via janetconf. --- Makefile | 24 ++++++++++++++++-------- src/conf/janetconf.h | 3 +++ src/core/bytecode.c | 2 +- src/core/corelib.c | 4 +++- src/include/janet.h | 12 ++++++------ src/mainclient/shell.c | 2 +- 6 files changed, 30 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index fbbe91d2..64f49047 100644 --- a/Makefile +++ b/Makefile @@ -38,8 +38,15 @@ PKG_CONFIG_PATH?=$(LIBDIR)/pkgconfig DEBUGGER=gdb SONAME_SETTER=-Wl,-soname, -CFLAGS:=$(CFLAGS) -std=c99 -Wall -Wextra -Isrc/include -Isrc/conf -fPIC -O2 -fvisibility=hidden -LDFLAGS:=$(LDFLAGS) -rdynamic +# For cross compilation +HOSTCC?=$(CC) +HOSTAR?=$(AR) +CFLAGS?=-fPIC -O2 +LDFLAGS?=-rdynamic + +COMMON_CFLAGS:=-std=c99 -Wall -Wextra -Isrc/include -Isrc/conf -fvisibility=hidden +BOOT_CFLAGS:=-DJANET_BOOTSTRAP -DJANET_BUILD=$(JANET_BUILD) -O0 -g $(COMMON_CFLAGS) +BUILD_CFLAGS:=$(CFLAGS) $(COMMON_CFLAGS) # For installation LDCONFIG:=ldconfig "$(LIBDIR)" @@ -131,7 +138,6 @@ JANET_BOOT_HEADERS=src/boot/tests.h ########################################################## JANET_BOOT_OBJECTS=$(patsubst src/%.c,build/%.boot.o,$(JANET_CORE_SOURCES) $(JANET_BOOT_SOURCES)) -BOOT_CFLAGS:=-DJANET_BOOTSTRAP -DJANET_BUILD=$(JANET_BUILD) $(CFLAGS) $(JANET_BOOT_OBJECTS): $(JANET_BOOT_HEADERS) @@ -161,24 +167,26 @@ build/janetconf.h: src/conf/janetconf.h cp $< $@ build/janet.o: build/janet.c build/janet.h build/janetconf.h - $(CC) $(CFLAGS) -c $< -o $@ -I build + $(HOSTCC) $(BUILD_CFLAGS) -c $< -o $@ -I build build/shell.o: build/shell.c build/janet.h build/janetconf.h - $(CC) $(CFLAGS) -c $< -o $@ -I build + $(HOSTCC) $(BUILD_CFLAGS) -c $< -o $@ -I build $(JANET_TARGET): build/janet.o build/shell.o - $(CC) $(LDFLAGS) $(CFLAGS) -o $@ $^ $(CLIBS) + $(HOSTCC) $(LDFLAGS) $(BUILD_CFLAGS) -o $@ $^ $(CLIBS) $(JANET_LIBRARY): build/janet.o build/shell.o - $(CC) $(LDFLAGS) $(CFLAGS) $(SONAME_SETTER)$(SONAME) -shared -o $@ $^ $(CLIBS) + $(HOSTCC) $(LDFLAGS) $(BUILD_CFLAGS) $(SONAME_SETTER)$(SONAME) -shared -o $@ $^ $(CLIBS) $(JANET_STATIC_LIBRARY): build/janet.o build/shell.o - $(AR) rcs $@ $^ + $(HOSTAR) rcs $@ $^ ################### ##### Testing ##### ################### +# Testing assumes HOSTCC=CC + TEST_SCRIPTS=$(wildcard test/suite*.janet) repl: $(JANET_TARGET) diff --git a/src/conf/janetconf.h b/src/conf/janetconf.h index 2343666b..f31a8b35 100644 --- a/src/conf/janetconf.h +++ b/src/conf/janetconf.h @@ -68,4 +68,7 @@ /* #define JANET_OS_NAME my-custom-os */ /* #define JANET_ARCH_NAME pdp-8 */ +/* Main client settings, does not affect library code */ +/* #define JANET_SIMPLE_GETLINE */ + #endif /* end of include guard: JANETCONF_H */ diff --git a/src/core/bytecode.c b/src/core/bytecode.c index 9c84a457..1349a10a 100644 --- a/src/core/bytecode.c +++ b/src/core/bytecode.c @@ -104,7 +104,7 @@ enum JanetInstructionType janet_instructions[JOP_INSTRUCTION_COUNT] = { }; /* Verify some bytecode */ -int32_t janet_verify(JanetFuncDef *def) { +int janet_verify(JanetFuncDef *def) { int vargs = !!(def->flags & JANET_FUNCDEF_FLAG_VARARG); int32_t i; int32_t maxslot = def->arity + vargs; diff --git a/src/core/corelib.c b/src/core/corelib.c index f2b187f8..a488365b 100644 --- a/src/core/corelib.c +++ b/src/core/corelib.c @@ -404,9 +404,11 @@ static Janet janet_core_gcsetinterval(int32_t argc, Janet *argv) { janet_fixarity(argc, 1); size_t s = janet_getsize(argv, 0); /* limit interval to 48 bits */ - if (s > 0xFFFFFFFFFFFFUl) { +#ifdef JANET_64 + if (s >> 48) { janet_panic("interval too large"); } +#endif janet_vm_gc_interval = s; return janet_wrap_nil(); } diff --git a/src/include/janet.h b/src/include/janet.h index 37c467fc..6b96ddf4 100644 --- a/src/include/janet.h +++ b/src/include/janet.h @@ -127,6 +127,12 @@ extern "C" { #define JANET_LITTLE_ENDIAN 1 #endif +/* Limits for converting doubles to 64 bit integers */ +#define JANET_INTMAX_DOUBLE 9007199254740991.0 +#define JANET_INTMIN_DOUBLE (-9007199254740991.0) +#define JANET_INTMAX_INT64 9007199254740991 +#define JANET_INTMIN_INT64 (-9007199254740991) + /* Check emscripten */ #ifdef __EMSCRIPTEN__ #define JANET_NO_DYNAMIC_MODULES @@ -542,12 +548,6 @@ JANET_API Janet janet_wrap_integer(int32_t x); #include -/* Limits for converting doubles to 64 bit integers */ -#define JANET_INTMAX_DOUBLE 9007199254740991.0 -#define JANET_INTMIN_DOUBLE (-9007199254740991.0) -#define JANET_INTMAX_INT64 9007199254740991 -#define JANET_INTMIN_INT64 (-9007199254740991) - #define janet_u64(x) ((x).u64) #define JANET_NANBOX_TAGBITS 0xFFFF800000000000llu diff --git a/src/mainclient/shell.c b/src/mainclient/shell.c index 3b240346..3855dce3 100644 --- a/src/mainclient/shell.c +++ b/src/mainclient/shell.c @@ -84,7 +84,7 @@ static void simpleline(JanetBuffer *buffer) { } /* Windows */ -#ifdef JANET_WINDOWS +#if defined(JANET_WINDOWS) || defined(JANET_SIMPLE_GETLINE) void janet_line_init() { ; From d033412b1f91488acbb848d817d2d8c3d9108267 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sat, 27 Jun 2020 15:21:17 -0500 Subject: [PATCH 12/31] Add symbol/slice and keyword/slice --- CHANGELOG.md | 3 +++ src/core/string.c | 22 ++++++++++++++++++++++ src/core/vm.c | 2 +- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37dd8e80..20307351 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 `symbol/slice` +- Add `keyword/slice` +- Allow cross compilation with Makefile. - Change `compare-primitve` to `cmp` and make it more efficient. - Change `reverse` to `reversed`, reverse now mutates the backing array - `janet_dobytes` and `janet_dostring` return parse errors in \*out diff --git a/src/core/string.c b/src/core/string.c index 7c4ed80c..e37d1687 100644 --- a/src/core/string.c +++ b/src/core/string.c @@ -176,6 +176,18 @@ static Janet cfun_string_slice(int32_t argc, Janet *argv) { return janet_stringv(view.bytes + range.start, range.end - range.start); } +static Janet cfun_symbol_slice(int32_t argc, Janet *argv) { + JanetByteView view = janet_getbytes(argv, 0); + JanetRange range = janet_getslice(argc, argv); + return janet_symbolv(view.bytes + range.start, range.end - range.start); +} + +static Janet cfun_keyword_slice(int32_t argc, Janet *argv) { + JanetByteView view = janet_getbytes(argv, 0); + JanetRange range = janet_getslice(argc, argv); + return janet_keywordv(view.bytes + range.start, range.end - range.start); +} + static Janet cfun_string_repeat(int32_t argc, Janet *argv) { janet_fixarity(argc, 2); JanetByteView view = janet_getbytes(argv, 0); @@ -529,6 +541,16 @@ static const JanetReg string_cfuns[] = { "from the end of the string. Note that index -1 is synonymous with " "index (length bytes) to allow a full negative slice range. ") }, + { + "keyword/slice", cfun_keyword_slice, + JDOC("(keyword/slice bytes &opt start end)\n\n" + "Same a string/slice, but returns a keyword.") + }, + { + "symbol/slice", cfun_symbol_slice, + JDOC("(symbol/slice bytes &opt start end)\n\n" + "Same a string/slice, but returns a symbol.") + }, { "string/repeat", cfun_string_repeat, JDOC("(string/repeat bytes n)\n\n" diff --git a/src/core/vm.c b/src/core/vm.c index 1eb372cb..6e7a0c3d 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -1399,7 +1399,7 @@ int janet_init(void) { * a collection pretty much every cycle, which is * incredibly horrible for performance, but can help ensure * there are no memory bugs during development */ - janet_vm_gc_interval = 0x10000; + janet_vm_gc_interval = 0x400000; janet_symcache_init(); /* Initialize gc roots */ janet_vm_roots = NULL; From 95c633914f0381c9800fedbcc0c47bf70d53f62a Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sat, 27 Jun 2020 16:51:20 -0500 Subject: [PATCH 13/31] Add auto-resizing of gc interval. This should prevent over use of GC and O(n^2) behavior. --- src/boot/boot.janet | 17 +++++++++++++++++ src/core/gc.c | 11 +++++++++++ src/core/state.h | 1 + src/core/vm.c | 5 +---- 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/boot/boot.janet b/src/boot/boot.janet index 58c46cb5..cbbf06e4 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -390,6 +390,16 @@ ,;body (set ,i (,delta ,i ,step)))))) +(defn- for-var-template + [i start stop step comparison delta body] + (with-syms [s] + ~(do + (var ,i ,start) + (def ,s ,stop) + (while (,comparison ,i ,s) + ,;body + (set ,i (,delta ,i ,step)))))) + (defn- check-indexed [x] (if (indexed? x) x @@ -484,6 +494,12 @@ :generate (loop-fiber-template binding object [rest]) (error (string "unexpected loop verb " verb))))) +(defmacro forv + "Do a c style for loop for side effects. The iteration variable i + can be mutated in the loop, unlike normal for. Returns nil." + [i start stop & body] + (for-var-template i start stop 1 < + body)) + (defmacro for "Do a c style for loop for side effects. Returns nil." [i start stop & body] @@ -556,6 +572,7 @@ (put _env 'loop1 nil) (put _env 'check-indexed nil) (put _env 'for-template nil) +(put _env 'for-var-template nil) (put _env 'iterate-template nil) (put _env 'each-template nil) (put _env 'keys-template nil) diff --git a/src/core/gc.c b/src/core/gc.c index 36752b06..e534d9ed 100644 --- a/src/core/gc.c +++ b/src/core/gc.c @@ -39,6 +39,7 @@ struct JanetScratch { JANET_THREAD_LOCAL void *janet_vm_blocks; JANET_THREAD_LOCAL size_t janet_vm_gc_interval; JANET_THREAD_LOCAL size_t janet_vm_next_collection; +JANET_THREAD_LOCAL size_t janet_vm_block_count; JANET_THREAD_LOCAL int janet_vm_gc_suspend = 0; /* Roots */ @@ -327,6 +328,7 @@ void janet_sweep() { previous = current; current->flags &= ~JANET_MEM_REACHABLE; } else { + janet_vm_block_count--; janet_deinit_block(current); if (NULL != previous) { previous->next = next; @@ -359,6 +361,7 @@ void *janet_gcalloc(enum JanetMemoryType type, size_t size) { janet_vm_next_collection += size; mem->next = janet_vm_blocks; janet_vm_blocks = mem; + janet_vm_block_count++; return (void *)mem; } @@ -388,6 +391,14 @@ void janet_collect(void) { uint32_t i; if (janet_vm_gc_suspend) return; depth = JANET_RECURSION_GUARD; + /* Try and prevent many major collections back to back. + * A full collection will take O(janet_vm_block_count) time. + * If we have a large heap, make sure our interval is not too + * small so we won't make many collections over it. This is just a + * heuristic for automatically changing the gc interval */ + if (janet_vm_block_count * 8 > janet_vm_gc_interval) { + janet_vm_gc_interval = janet_vm_block_count * sizeof(JanetGCObject); + } orig_rootcount = janet_vm_root_count; #ifdef JANET_NET janet_net_markloop(); diff --git a/src/core/state.h b/src/core/state.h index 7ead6368..c36f1c63 100644 --- a/src/core/state.h +++ b/src/core/state.h @@ -71,6 +71,7 @@ extern JANET_THREAD_LOCAL uint32_t janet_vm_cache_deleted; extern JANET_THREAD_LOCAL void *janet_vm_blocks; extern JANET_THREAD_LOCAL size_t janet_vm_gc_interval; extern JANET_THREAD_LOCAL size_t janet_vm_next_collection; +extern JANET_THREAD_LOCAL size_t janet_vm_block_count; extern JANET_THREAD_LOCAL int janet_vm_gc_suspend; /* GC roots */ diff --git a/src/core/vm.c b/src/core/vm.c index 6e7a0c3d..f65cfa50 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -1395,11 +1395,8 @@ int janet_init(void) { /* Garbage collection */ janet_vm_blocks = NULL; janet_vm_next_collection = 0; - /* Setting memoryInterval to zero forces - * a collection pretty much every cycle, which is - * incredibly horrible for performance, but can help ensure - * there are no memory bugs during development */ janet_vm_gc_interval = 0x400000; + janet_vm_block_count = 0; janet_symcache_init(); /* Initialize gc roots */ janet_vm_roots = NULL; From 289de840fd99b6f0db0bf5de3d9c261f0c3dfbe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20Pospi=CC=81s=CC=8Cil?= Date: Sun, 28 Jun 2020 20:49:44 +0200 Subject: [PATCH 14/31] Specify input types actions --- src/boot/boot.janet | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/boot/boot.janet b/src/boot/boot.janet index 48bb9234..7e284184 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -1198,7 +1198,7 @@ res) (defn reverse - "Reverses the order of the elements in a given array or tuple and returns it + "Reverses the order of the elements in a given array or buffer and returns it mutated." [t] (def len-1 (- (length t) 1)) @@ -1213,7 +1213,7 @@ (defn reversed "Reverses the order of the elements in a given array or tuple and returns - a new array." + a new array. If string or buffer is provided function returns array of chars reversed." [t] (def len (length t)) (var n (- len 1)) From db63d352a265555dc1fa7d8d36a220d8f09ab85f Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sun, 28 Jun 2020 15:03:01 -0500 Subject: [PATCH 15/31] Add specialization for 3 argument get. This can be inlined with jmpnn instruction (jump if not nil) to skip over the default value. (get a b c) can be exanded statically to asm start: (get $0 $1 $2) (jmpnn $0 :label) ... Instructions to load default value to $0 - often a load. :label asm end. --- src/core/cfuns.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/core/cfuns.c b/src/core/cfuns.c index 0520576f..12cf64b1 100644 --- a/src/core/cfuns.c +++ b/src/core/cfuns.c @@ -33,6 +33,11 @@ static int arity1or2(JanetFopts opts, JanetSlot *args) { int32_t arity = janet_v_count(args); return arity == 1 || arity == 2; } +static int arity2or3(JanetFopts opts, JanetSlot *args) { + (void) opts; + int32_t arity = janet_v_count(args); + return arity == 2 || arity == 3; +} static int fixarity1(JanetFopts opts, JanetSlot *args) { (void) opts; return janet_v_count(args) == 1; @@ -137,7 +142,18 @@ static JanetSlot do_in(JanetFopts opts, JanetSlot *args) { return opreduce(opts, args, JOP_IN, janet_wrap_nil()); } static JanetSlot do_get(JanetFopts opts, JanetSlot *args) { - return opreduce(opts, args, JOP_GET, janet_wrap_nil()); + if (janet_v_count(args) == 3) { + JanetCompiler *c = opts.compiler; + JanetSlot t = janetc_gettarget(opts); + janetc_emit_sss(c, JOP_GET, t, args[0], args[1], 1); + int32_t label = janetc_emit_si(c, JOP_JUMP_IF_NOT_NIL, t, 0, 0); + janetc_copy(c, t, args[2]); + int32_t current = janet_v_count(c->buffer); + c->buffer[label] |= (current - label) << 16; + return t; + } else { + return opreduce(opts, args, JOP_GET, janet_wrap_nil()); + } } static JanetSlot do_next(JanetFopts opts, JanetSlot *args) { return opfunction(opts, args, JOP_NEXT, janet_wrap_nil()); @@ -322,7 +338,7 @@ static const JanetFunOptimizer optimizers[] = { {NULL, do_eq}, {NULL, do_neq}, {fixarity2, do_propagate}, - {fixarity2, do_get}, + {arity2or3, do_get}, {arity1or2, do_next}, {fixarity2, do_modulo}, {fixarity2, do_remainder}, From a1ea62a923f26eea19eb5a875b06f988b13e38d5 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sun, 28 Jun 2020 15:52:59 -0500 Subject: [PATCH 16/31] Fix optimization of do_get. When the target slot (register) is the same as the default register, do not clobber it. --- src/core/cfuns.c | 9 ++++++++- src/core/emit.c | 6 +++--- src/core/emit.h | 3 +++ test/suite8.janet | 3 +++ 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/core/cfuns.c b/src/core/cfuns.c index 12cf64b1..6727fa50 100644 --- a/src/core/cfuns.c +++ b/src/core/cfuns.c @@ -145,9 +145,16 @@ static JanetSlot do_get(JanetFopts opts, JanetSlot *args) { if (janet_v_count(args) == 3) { JanetCompiler *c = opts.compiler; JanetSlot t = janetc_gettarget(opts); + int target_is_default = janetc_sequal(t, args[2]); + JanetSlot dflt_slot = args[2]; + if (target_is_default) { + dflt_slot = janetc_farslot(c); + janetc_copy(c, dflt_slot, t); + } janetc_emit_sss(c, JOP_GET, t, args[0], args[1], 1); int32_t label = janetc_emit_si(c, JOP_JUMP_IF_NOT_NIL, t, 0, 0); - janetc_copy(c, t, args[2]); + janetc_copy(c, t, dflt_slot); + if (target_is_default) janetc_freeslot(c, dflt_slot); int32_t current = janet_v_count(c->buffer); c->buffer[label] |= (current - label) << 16; return t; diff --git a/src/core/emit.c b/src/core/emit.c index c20fc836..236ad1e0 100644 --- a/src/core/emit.c +++ b/src/core/emit.c @@ -37,7 +37,7 @@ int32_t janetc_allocfar(JanetCompiler *c) { return reg; } -/* Get a register less than 256 */ +/* Get a register less than 256 for temporary use. */ int32_t janetc_allocnear(JanetCompiler *c, JanetcRegisterTemp tag) { return janetc_regalloc_temp(&c->scope->ra, tag); } @@ -205,7 +205,7 @@ static int32_t janetc_regnear(JanetCompiler *c, JanetSlot s, JanetcRegisterTemp } /* Check if two slots are equal */ -static int janetc_sequal(JanetSlot lhs, JanetSlot rhs) { +int janetc_sequal(JanetSlot lhs, JanetSlot rhs) { if ((lhs.flags & ~JANET_SLOTTYPE_ANY) == (rhs.flags & ~JANET_SLOTTYPE_ANY) && lhs.index == rhs.index && lhs.envindex == rhs.envindex) { @@ -245,8 +245,8 @@ void janetc_copy( janetc_moveback(c, dest, nearreg); /* Cleanup */ janetc_regalloc_freetemp(&c->scope->ra, nearreg, JANETC_REGTEMP_3); - } + /* Instruction templated emitters */ static int32_t emit1s(JanetCompiler *c, uint8_t op, JanetSlot s, int32_t rest, int wr) { diff --git a/src/core/emit.h b/src/core/emit.h index e9608a5c..5b9229b6 100644 --- a/src/core/emit.h +++ b/src/core/emit.h @@ -42,6 +42,9 @@ int32_t janetc_emit_ssi(JanetCompiler *c, uint8_t op, JanetSlot s1, JanetSlot s2 int32_t janetc_emit_ssu(JanetCompiler *c, uint8_t op, JanetSlot s1, JanetSlot s2, uint8_t immediate, int wr); int32_t janetc_emit_sss(JanetCompiler *c, uint8_t op, JanetSlot s1, JanetSlot s2, JanetSlot s3, int wr); +/* Check if two slots are equivalent */ +int janetc_sequal(JanetSlot x, JanetSlot y); + /* Move value from one slot to another. Cannot copy to constant slots. */ void janetc_copy(JanetCompiler *c, JanetSlot dest, JanetSlot src); diff --git a/test/suite8.janet b/test/suite8.janet index 78bc2ed0..fe384033 100644 --- a/test/suite8.janet +++ b/test/suite8.janet @@ -325,4 +325,7 @@ neldb\0\0\0\xD8\x05printG\x01\0\xDE\xDE\xDE'\x03\0marshal_tes/\x02 (assert-no-error "issue 428 1" (loop [{:a x} :generate (fiber/new f)] (set result x))) (assert (= result :ok) "issue 428 2") +# Inline 3 argument get +(assert (= 10 (do (var a 10) (set a (get '{} :a a)))) "inline get 1") + (end-suite) From aefde67aa298a6897607b8d97a83bc6f0889a7d8 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sun, 28 Jun 2020 18:16:57 -0500 Subject: [PATCH 17/31] And lots of optimization functionality. --- src/core/asm.c | 2 + src/core/bytecode.c | 2 + src/core/cfuns.c | 101 +++++++++++++++++++++++++++++--------------- src/core/vm.c | 12 +++++- src/include/janet.h | 2 + 5 files changed, 83 insertions(+), 36 deletions(-) diff --git a/src/core/asm.c b/src/core/asm.c index af8e8cb3..4793dc25 100644 --- a/src/core/asm.c +++ b/src/core/asm.c @@ -112,6 +112,8 @@ static const JanetInstructionDef janet_ops[] = { {"movn", JOP_MOVE_NEAR}, {"mul", JOP_MULTIPLY}, {"mulim", JOP_MULTIPLY_IMMEDIATE}, + {"neq", JOP_NOT_EQUALS}, + {"neqim", JOP_NOT_EQUALS_IMMEDIATE}, {"next", JOP_NEXT}, {"noop", JOP_NOOP}, {"prop", JOP_PROPAGATE}, diff --git a/src/core/bytecode.c b/src/core/bytecode.c index 1349a10a..4d2a74c3 100644 --- a/src/core/bytecode.c +++ b/src/core/bytecode.c @@ -101,6 +101,8 @@ enum JanetInstructionType janet_instructions[JOP_INSTRUCTION_COUNT] = { JINT_SSS, /* JOP_GREATER_THAN_EQUAL */ JINT_SSS, /* JOP_LESS_THAN_EQUAL */ JINT_SSS, /* JOP_NEXT */ + JINT_SSS, /* JOP_NOT_EQUALS, */ + JINT_SSI, /* JOP_NOT_EQUALS_IMMEDIATE, */ }; /* Verify some bytecode */ diff --git a/src/core/cfuns.c b/src/core/cfuns.c index 6727fa50..41fc18a7 100644 --- a/src/core/cfuns.c +++ b/src/core/cfuns.c @@ -95,34 +95,67 @@ static JanetSlot opfunction( return t; } +/* Check if a value can be coerced to an immediate value */ +static int can_be_imm(Janet x, int8_t *out) { + if (!janet_checkint(x)) return 0; + int32_t integer = janet_unwrap_integer(x); + if (integer > 127 || integer < -127) return 0; + *out = (int8_t) integer; + return 1; +} + +/* Check if a slot can be coerced to an immediate value */ +static int can_slot_be_imm(JanetSlot s, int8_t *out) { + if (!(s.flags & JANET_SLOT_CONSTANT)) return 0; + return can_be_imm(s.constant, out); +} + /* Emit a series of instructions instead of a function call to a math op */ static JanetSlot opreduce( JanetFopts opts, JanetSlot *args, int op, + int opim, Janet nullary) { JanetCompiler *c = opts.compiler; int32_t i, len; + int8_t imm = 0; + int neg = opim < 0; + if (opim < 0) opim = -opim; len = janet_v_count(args); JanetSlot t; if (len == 0) { return janetc_cslot(nullary); } else if (len == 1) { t = janetc_gettarget(opts); - janetc_emit_sss(c, op, t, janetc_cslot(nullary), args[0], 1); + /* Special case subtract to be times -1 */ + if (op == JOP_SUBTRACT) { + janetc_emit_ssi(c, JOP_MULTIPLY_IMMEDIATE, t, args[0], -1, 1); + } else { + janetc_emit_sss(c, op, t, janetc_cslot(nullary), args[0], 1); + } return t; } t = janetc_gettarget(opts); - janetc_emit_sss(c, op, t, args[0], args[1], 1); - for (i = 2; i < len; i++) - janetc_emit_sss(c, op, t, t, args[i], 1); + if (opim && can_slot_be_imm(args[1], &imm)) { + janetc_emit_ssi(c, opim, t, args[0], neg ? -imm : imm, 1); + } else { + janetc_emit_sss(c, op, t, args[0], args[1], 1); + } + for (i = 2; i < len; i++) { + if (opim && can_slot_be_imm(args[i], &imm)) { + janetc_emit_ssi(c, opim, t, t, neg ? -imm : imm, 1); + } else { + janetc_emit_sss(c, op, t, t, args[i], 1); + } + } return t; } /* Function optimizers */ static JanetSlot do_propagate(JanetFopts opts, JanetSlot *args) { - return opreduce(opts, args, JOP_PROPAGATE, janet_wrap_nil()); + return opreduce(opts, args, JOP_PROPAGATE, 0, janet_wrap_nil()); } static JanetSlot do_error(JanetFopts opts, JanetSlot *args) { janetc_emit_s(opts.compiler, JOP_ERROR, args[0], 0); @@ -139,7 +172,7 @@ static JanetSlot do_debug(JanetFopts opts, JanetSlot *args) { return t; } static JanetSlot do_in(JanetFopts opts, JanetSlot *args) { - return opreduce(opts, args, JOP_IN, janet_wrap_nil()); + return opreduce(opts, args, JOP_IN, 0, janet_wrap_nil()); } static JanetSlot do_get(JanetFopts opts, JanetSlot *args) { if (janet_v_count(args) == 3) { @@ -159,20 +192,20 @@ static JanetSlot do_get(JanetFopts opts, JanetSlot *args) { c->buffer[label] |= (current - label) << 16; return t; } else { - return opreduce(opts, args, JOP_GET, janet_wrap_nil()); + return opreduce(opts, args, JOP_GET, 0, janet_wrap_nil()); } } static JanetSlot do_next(JanetFopts opts, JanetSlot *args) { return opfunction(opts, args, JOP_NEXT, janet_wrap_nil()); } static JanetSlot do_modulo(JanetFopts opts, JanetSlot *args) { - return opreduce(opts, args, JOP_MODULO, janet_wrap_nil()); + return opreduce(opts, args, JOP_MODULO, 0, janet_wrap_nil()); } static JanetSlot do_remainder(JanetFopts opts, JanetSlot *args) { - return opreduce(opts, args, JOP_REMAINDER, janet_wrap_nil()); + return opreduce(opts, args, JOP_REMAINDER, 0, janet_wrap_nil()); } static JanetSlot do_cmp(JanetFopts opts, JanetSlot *args) { - return opreduce(opts, args, JOP_COMPARE, janet_wrap_nil()); + return opreduce(opts, args, JOP_COMPARE, 0, janet_wrap_nil()); } static JanetSlot do_put(JanetFopts opts, JanetSlot *args) { if (opts.flags & JANET_FOPTS_DROP) { @@ -226,34 +259,34 @@ static JanetSlot do_apply(JanetFopts opts, JanetSlot *args) { /* Variadic operators specialization */ static JanetSlot do_add(JanetFopts opts, JanetSlot *args) { - return opreduce(opts, args, JOP_ADD, janet_wrap_integer(0)); + return opreduce(opts, args, JOP_ADD, JOP_ADD_IMMEDIATE, janet_wrap_integer(0)); } static JanetSlot do_sub(JanetFopts opts, JanetSlot *args) { - return opreduce(opts, args, JOP_SUBTRACT, janet_wrap_integer(0)); + return opreduce(opts, args, JOP_SUBTRACT, -JOP_ADD_IMMEDIATE, janet_wrap_integer(0)); } static JanetSlot do_mul(JanetFopts opts, JanetSlot *args) { - return opreduce(opts, args, JOP_MULTIPLY, janet_wrap_integer(1)); + return opreduce(opts, args, JOP_MULTIPLY, JOP_MULTIPLY_IMMEDIATE, janet_wrap_integer(1)); } static JanetSlot do_div(JanetFopts opts, JanetSlot *args) { - return opreduce(opts, args, JOP_DIVIDE, janet_wrap_integer(1)); + return opreduce(opts, args, JOP_DIVIDE, JOP_DIVIDE_IMMEDIATE, janet_wrap_integer(1)); } static JanetSlot do_band(JanetFopts opts, JanetSlot *args) { - return opreduce(opts, args, JOP_BAND, janet_wrap_integer(-1)); + return opreduce(opts, args, JOP_BAND, 0, janet_wrap_integer(-1)); } static JanetSlot do_bor(JanetFopts opts, JanetSlot *args) { - return opreduce(opts, args, JOP_BOR, janet_wrap_integer(0)); + return opreduce(opts, args, JOP_BOR, 0, janet_wrap_integer(0)); } static JanetSlot do_bxor(JanetFopts opts, JanetSlot *args) { - return opreduce(opts, args, JOP_BXOR, janet_wrap_integer(0)); + return opreduce(opts, args, JOP_BXOR, 0, janet_wrap_integer(0)); } static JanetSlot do_lshift(JanetFopts opts, JanetSlot *args) { - return opreduce(opts, args, JOP_SHIFT_LEFT, janet_wrap_integer(1)); + return opreduce(opts, args, JOP_SHIFT_LEFT, JOP_SHIFT_LEFT_IMMEDIATE, janet_wrap_integer(1)); } static JanetSlot do_rshift(JanetFopts opts, JanetSlot *args) { - return opreduce(opts, args, JOP_SHIFT_RIGHT, janet_wrap_integer(1)); + return opreduce(opts, args, JOP_SHIFT_RIGHT, JOP_SHIFT_RIGHT_IMMEDIATE, janet_wrap_integer(1)); } static JanetSlot do_rshiftu(JanetFopts opts, JanetSlot *args) { - return opreduce(opts, args, JOP_SHIFT_RIGHT, janet_wrap_integer(1)); + return opreduce(opts, args, JOP_SHIFT_RIGHT_UNSIGNED, JOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE, janet_wrap_integer(1)); } static JanetSlot do_bnot(JanetFopts opts, JanetSlot *args) { return genericSS(opts, JOP_BNOT, args[0]); @@ -264,9 +297,11 @@ static JanetSlot compreduce( JanetFopts opts, JanetSlot *args, int op, + int opim, int invert) { JanetCompiler *c = opts.compiler; int32_t i, len; + int8_t imm = 0; len = janet_v_count(args); int32_t *labels = NULL; JanetSlot t; @@ -277,19 +312,17 @@ static JanetSlot compreduce( } t = janetc_gettarget(opts); for (i = 1; i < len; i++) { - janetc_emit_sss(c, op, t, args[i - 1], args[i], 1); + if (opim && can_slot_be_imm(args[i], &imm)) { + janetc_emit_ssi(c, opim, t, args[i - 1], imm, 1); + } else { + janetc_emit_sss(c, op, t, args[i - 1], args[i], 1); + } if (i != (len - 1)) { - int32_t label = janetc_emit_si(c, JOP_JUMP_IF_NOT, t, 0, 1); + int32_t label = janetc_emit_si(c, invert ? JOP_JUMP_IF : JOP_JUMP_IF_NOT, t, 0, 1); janet_v_push(labels, label); } } int32_t end = janet_v_count(c->buffer); - if (invert) { - janetc_emit_si(c, JOP_JUMP_IF, t, 3, 0); - janetc_emit_s(c, JOP_LOAD_TRUE, t, 1); - janetc_emit(c, JOP_JUMP | (2 << 8)); - janetc_emit_s(c, JOP_LOAD_FALSE, t, 1); - } for (i = 0; i < janet_v_count(labels); i++) { int32_t label = labels[i]; c->buffer[label] |= ((end - label) << 16); @@ -299,22 +332,22 @@ static JanetSlot compreduce( } static JanetSlot do_gt(JanetFopts opts, JanetSlot *args) { - return compreduce(opts, args, JOP_GREATER_THAN, 0); + return compreduce(opts, args, JOP_GREATER_THAN, JOP_GREATER_THAN_IMMEDIATE, 0); } static JanetSlot do_lt(JanetFopts opts, JanetSlot *args) { - return compreduce(opts, args, JOP_LESS_THAN, 0); + return compreduce(opts, args, JOP_LESS_THAN, JOP_LESS_THAN_IMMEDIATE, 0); } static JanetSlot do_gte(JanetFopts opts, JanetSlot *args) { - return compreduce(opts, args, JOP_GREATER_THAN_EQUAL, 0); + return compreduce(opts, args, JOP_GREATER_THAN_EQUAL, 0, 0); } static JanetSlot do_lte(JanetFopts opts, JanetSlot *args) { - return compreduce(opts, args, JOP_LESS_THAN_EQUAL, 0); + return compreduce(opts, args, JOP_LESS_THAN_EQUAL, 0, 0); } static JanetSlot do_eq(JanetFopts opts, JanetSlot *args) { - return compreduce(opts, args, JOP_EQUALS, 0); + return compreduce(opts, args, JOP_EQUALS, JOP_EQUALS_IMMEDIATE, 0); } static JanetSlot do_neq(JanetFopts opts, JanetSlot *args) { - return compreduce(opts, args, JOP_EQUALS, 1); + return compreduce(opts, args, JOP_NOT_EQUALS, JOP_NOT_EQUALS_IMMEDIATE, 1); } /* Arranged by tag */ diff --git a/src/core/vm.c b/src/core/vm.c index f65cfa50..a48f2434 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -374,8 +374,8 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) { &&label_JOP_GREATER_THAN_EQUAL, &&label_JOP_LESS_THAN_EQUAL, &&label_JOP_NEXT, - &&label_unknown_op, - &&label_unknown_op, + &&label_JOP_NOT_EQUALS, + &&label_JOP_NOT_EQUALS_IMMEDIATE, &&label_unknown_op, &&label_unknown_op, &&label_unknown_op, @@ -788,6 +788,14 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) { stack[A] = janet_wrap_boolean(janet_unwrap_integer(stack[B]) == CS); vm_pcnext(); + VM_OP(JOP_NOT_EQUALS) + stack[A] = janet_wrap_boolean(!janet_equals(stack[B], stack[C])); + vm_pcnext(); + + VM_OP(JOP_NOT_EQUALS_IMMEDIATE) + stack[A] = janet_wrap_boolean(janet_unwrap_integer(stack[B]) != CS); + vm_pcnext(); + VM_OP(JOP_COMPARE) stack[A] = janet_wrap_integer(janet_compare(stack[B], stack[C])); vm_pcnext(); diff --git a/src/include/janet.h b/src/include/janet.h index 6b96ddf4..38446a3a 100644 --- a/src/include/janet.h +++ b/src/include/janet.h @@ -1124,6 +1124,8 @@ enum JanetOpCode { JOP_GREATER_THAN_EQUAL, JOP_LESS_THAN_EQUAL, JOP_NEXT, + JOP_NOT_EQUALS, + JOP_NOT_EQUALS_IMMEDIATE, JOP_INSTRUCTION_COUNT }; From e9911fee4d5f8691ada9411d4a42885b96d1f895 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20Pospi=CC=81s=CC=8Cil?= Date: Mon, 29 Jun 2020 09:18:26 +0200 Subject: [PATCH 18/31] Add keyword/slice and symbol/slice tests --- test/suite8.janet | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/test/suite8.janet b/test/suite8.janet index fe384033..cd4cf88e 100644 --- a/test/suite8.janet +++ b/test/suite8.janet @@ -36,7 +36,7 @@ :loop (/ (* "[" :main "]") ,(fn [& captures] ~(while (not= (get DATA POS) 0) ,;captures))) - :main (any (+ :s :loop :+ :- :> :< :.)) })) + :main (any (+ :s :loop :+ :- :> :< :.))})) (defn bf "Run brainfuck." @@ -233,8 +233,8 @@ neldb\0\0\0\xD8\x05printG\x01\0\xDE\xDE\xDE'\x03\0marshal_tes/\x02 (gccollect) (def v (unmarshal - @"\xD7\xCD0\xD4000000\0\x03\x01\xCE\00\0\x01\0\0000\x03\0\0\0000000000\xCC0\0000" - load-image-dict)) + @"\xD7\xCD0\xD4000000\0\x03\x01\xCE\00\0\x01\0\0000\x03\0\0\0000000000\xCC0\0000" + load-image-dict)) (gccollect) # in vs get regression @@ -271,7 +271,7 @@ neldb\0\0\0\xD8\x05printG\x01\0\xDE\xDE\xDE'\x03\0marshal_tes/\x02 :packet-body '(lenprefix (-> :header-len) 1) # header, followed by body, and drop the :header-len capture - :packet (/ (* :packet-header :packet-body) ,|$1) + :packet (/ (* :packet-header :packet-body) ,|$1) # any exact seqence of packets (no extra characters) :main (* (any :packet) -1)})) @@ -328,4 +328,8 @@ neldb\0\0\0\xD8\x05printG\x01\0\xDE\xDE\xDE'\x03\0marshal_tes/\x02 # Inline 3 argument get (assert (= 10 (do (var a 10) (set a (get '{} :a a)))) "inline get 1") +# Keyword and Symbol slice +(assert (= :keyword (keyword/slice "some_keyword_slice" 5 12)) "keyword slice") +(assert (= 'symbol (symbol/slice "some_symbol_slice" 5 11)) "symbol slice") + (end-suite) From 17a131ac21adbe9df038bc218d6c0316642c9f62 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Mon, 29 Jun 2020 19:13:06 -0500 Subject: [PATCH 19/31] Add peg/find and peg/find-all. These peg functions should make pegs a bit easier to use and more efficient in some common cases. --- src/boot/boot.janet | 4 +- src/core/peg.c | 110 ++++++++++++++++++++++++++++++++------------ 2 files changed, 83 insertions(+), 31 deletions(-) diff --git a/src/boot/boot.janet b/src/boot/boot.janet index cbbf06e4..a1b176a8 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -99,7 +99,7 @@ (defn array? "Check if x is an array." [x] (= (type x) :array)) (defn tuple? "Check if x is a tuple." [x] (= (type x) :tuple)) (defn boolean? "Check if x is a boolean." [x] (= (type x) :boolean)) -(defn bytes? "Check if x is a string, symbol, or buffer." [x] +(defn bytes? "Check if x is a string, symbol, keyword, or buffer." [x] (def t (type x)) (if (= t :string) true (if (= t :symbol) true (if (= t :keyword) true (= t :buffer))))) (defn dictionary? "Check if x a table or struct." [x] @@ -112,7 +112,7 @@ (defn true? "Check if x is true." [x] (= x true)) (defn false? "Check if x is false." [x] (= x false)) (defn nil? "Check if x is nil." [x] (= x nil)) -(defn empty? "Check if xs is empty." [xs] (= 0 (length xs))) +(defn empty? "Check if xs is empty." [xs] (= (length xs) 0)) (def idempotent? "(idempotent? x)\n\nCheck if x is a value that evaluates to itself when compiled." diff --git a/src/core/peg.c b/src/core/peg.c index 877338b6..847b47e9 100644 --- a/src/core/peg.c +++ b/src/core/peg.c @@ -1308,47 +1308,89 @@ static Janet cfun_peg_compile(int32_t argc, Janet *argv) { return janet_wrap_abstract(peg); } -static Janet cfun_peg_match(int32_t argc, Janet *argv) { - janet_arity(argc, 2, -1); +/* Common data for peg cfunctions */ +typedef struct { JanetPeg *peg; + PegState s; + JanetByteView bytes; + int32_t start; +} PegCall; + +/* Initialize state for peg cfunctions */ +static PegCall peg_cfun_init(int32_t argc, Janet *argv) { + PegCall ret; + janet_arity(argc, 2, -1); if (janet_checktype(argv[0], JANET_ABSTRACT) && janet_abstract_type(janet_unwrap_abstract(argv[0])) == &janet_peg_type) { - peg = janet_unwrap_abstract(argv[0]); + ret.peg = janet_unwrap_abstract(argv[0]); } else { - peg = compile_peg(argv[0]); + ret.peg = compile_peg(argv[0]); } - JanetByteView bytes = janet_getbytes(argv, 1); - int32_t start; - PegState s; + ret.bytes = janet_getbytes(argv, 1); if (argc > 2) { - start = janet_gethalfrange(argv, 2, bytes.len, "offset"); - s.extrac = argc - 3; - s.extrav = janet_tuple_n(argv + 3, argc - 3); + ret.start = janet_gethalfrange(argv, 2, ret.bytes.len, "offset"); + ret.s.extrac = argc - 3; + ret.s.extrav = janet_tuple_n(argv + 3, argc - 3); } else { - start = 0; - s.extrac = 0; - s.extrav = NULL; + ret.start = 0; + ret.s.extrac = 0; + ret.s.extrav = NULL; } - s.mode = PEG_MODE_NORMAL; - s.text_start = bytes.bytes; - s.text_end = bytes.bytes + bytes.len; - s.depth = JANET_RECURSION_GUARD; - s.captures = janet_array(0); - s.scratch = janet_buffer(10); - s.tags = janet_buffer(10); - s.constants = peg->constants; - s.bytecode = peg->bytecode; - const uint8_t *result = peg_rule(&s, s.bytecode, bytes.bytes + start); - return result ? janet_wrap_array(s.captures) : janet_wrap_nil(); + ret.s.mode = PEG_MODE_NORMAL; + ret.s.text_start = ret.bytes.bytes; + ret.s.text_end = ret.bytes.bytes + ret.bytes.len; + ret.s.depth = JANET_RECURSION_GUARD; + ret.s.captures = janet_array(0); + ret.s.scratch = janet_buffer(10); + ret.s.tags = janet_buffer(10); + ret.s.constants = ret.peg->constants; + ret.s.bytecode = ret.peg->bytecode; + return ret; } +static Janet cfun_peg_match(int32_t argc, Janet *argv) { + PegCall c = peg_cfun_init(argc, argv); + const uint8_t *result = peg_rule(&c.s, c.s.bytecode, c.bytes.bytes + c.start); + return result ? janet_wrap_array(c.s.captures) : janet_wrap_nil(); +} + +static Janet cfun_peg_find(int32_t argc, Janet *argv) { + PegCall c = peg_cfun_init(argc, argv); + for (int32_t i = c.start; i < c.bytes.len; i++) { + c.s.captures->count = 0; + c.s.scratch->count = 0; + c.s.tags->count = 0; + if (peg_rule(&c.s, c.s.bytecode, c.bytes.bytes + i)) + return janet_wrap_integer(i); + } + return janet_wrap_nil(); +} + +static Janet cfun_peg_find_all(int32_t argc, Janet *argv) { + PegCall c = peg_cfun_init(argc, argv); + JanetArray *ret = janet_array(0); + for (int32_t i = c.start; i < c.bytes.len; i++) { + c.s.captures->count = 0; + c.s.scratch->count = 0; + c.s.tags->count = 0; + if (peg_rule(&c.s, c.s.bytecode, c.bytes.bytes + i)) + janet_array_push(ret, janet_wrap_integer(i)); + } + return janet_wrap_array(ret); +} + +static JanetMethod peg_methods[] = { + {"match", cfun_peg_match}, + {"find", cfun_peg_find}, + {"find-all", cfun_peg_find_all}, + {NULL, NULL} +}; + static int cfun_peg_getter(JanetAbstract a, Janet key, Janet *out) { (void) a; - if (janet_keyeq(key, "match")) { - *out = janet_wrap_cfunction(cfun_peg_match); - return 1; - } - return 0; + if (!janet_checktype(key, JANET_KEYWORD)) + return 0; + return janet_getmethod(janet_unwrap_keyword(key), peg_methods, out); } static const JanetReg peg_cfuns[] = { @@ -1364,6 +1406,16 @@ static const JanetReg peg_cfuns[] = { "Match a Parsing Expression Grammar to a byte string and return an array of captured values. " "Returns nil if text does not match the language defined by peg. The syntax of PEGs is documented on the Janet website.") }, + { + "peg/find", cfun_peg_find, + JDOC("(peg/find peg text &opt start & args)\n\n" + "Find first index where the peg matches in text. Returns an integer, or nil if not found.") + }, + { + "peg/find-all", cfun_peg_find_all, + JDOC("(peg/find-all peg text &opt start & args)\n\n" + "Find all indexes where the peg matches in text. Returns an array of integers.") + }, {NULL, NULL, NULL} }; From 5565f02dbdb2bb0e28b873ba2c3ec6f3f8b92fff Mon Sep 17 00:00:00 2001 From: Jason Pepas Date: Mon, 29 Jun 2020 19:36:18 -0500 Subject: [PATCH 20/31] Simplifying workaround for missing O_CLOEXEC --- src/conf/janetconf.h | 1 - src/core/net.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/conf/janetconf.h b/src/conf/janetconf.h index e8d6e8b9..2343666b 100644 --- a/src/conf/janetconf.h +++ b/src/conf/janetconf.h @@ -58,7 +58,6 @@ /* #define JANET_NO_REALPATH */ /* #define JANET_NO_SYMLINKS */ /* #define JANET_NO_UMASK */ -/* #define JANET_NO_CLOEXEC */ /* #define JANET_OUT_OF_MEMORY do { printf("janet out of memory\n"); exit(1); } while (0) */ /* #define JANET_EXIT(msg) do { printf("C assert failed executing janet: %s\n", msg); exit(1); } while (0) */ /* #define JANET_TOP_LEVEL_SIGNAL(msg) call_my_function((msg), stderr) */ diff --git a/src/core/net.c b/src/core/net.c index 44fe7052..6d9ab81d 100644 --- a/src/core/net.c +++ b/src/core/net.c @@ -112,7 +112,7 @@ typedef struct { #endif static JanetStream *make_stream(int fd, int flags) { JanetStream *stream = janet_abstract(&StreamAT, sizeof(JanetStream)); -#if !defined(SOCK_CLOEXEC) && !defined(JANET_NO_CLOEXEC) +#if !defined(SOCK_CLOEXEC) && defined(O_CLOEXEC) int extra = O_CLOEXEC; #else int extra = 0; From 2f5bb7774e13be5f22fb054436cbe2acdf022f14 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Mon, 29 Jun 2020 20:51:38 -0500 Subject: [PATCH 21/31] Fix recursive post-deps. --- jpm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jpm b/jpm index 976c8445..e7f42f40 100755 --- a/jpm +++ b/jpm @@ -757,7 +757,7 @@ int main(int argc, const char **argv) { (os/execute [(git-path) "reset" "--hard" tag] :p)) (unless (dyn :offline) (os/execute [(git-path) "submodule" "update" "--init" "--recursive"] :p)) - (import-rules "./project.janet") + (import-rules "./project.janet" true) (unless no-deps (do-rule "install-deps")) (do-rule "build") (do-rule "install")) @@ -1182,7 +1182,7 @@ Flags are: (defn list-rules [&opt ctx] - (import-rules "./project.janet" true) + (import-rules "./project.janet") (def ks (sort (seq [k :keys (dyn :rules)] k))) (each k ks (print k))) From 597d84e2636019c0bfb0372104766dc070be924b Mon Sep 17 00:00:00 2001 From: Jason Pepas Date: Fri, 26 Jun 2020 14:34:13 -0500 Subject: [PATCH 22/31] Add support for systems missing arc4random_buf --- src/conf/janetconf.h | 1 + src/core/os.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/conf/janetconf.h b/src/conf/janetconf.h index f31a8b35..fc9f72d8 100644 --- a/src/conf/janetconf.h +++ b/src/conf/janetconf.h @@ -58,6 +58,7 @@ /* #define JANET_NO_REALPATH */ /* #define JANET_NO_SYMLINKS */ /* #define JANET_NO_UMASK */ +/* #define JANET_NO_ARC4RANDOM_BUF */ /* #define JANET_OUT_OF_MEMORY do { printf("janet out of memory\n"); exit(1); } while (0) */ /* #define JANET_EXIT(msg) do { printf("C assert failed executing janet: %s\n", msg); exit(1); } while (0) */ /* #define JANET_TOP_LEVEL_SIGNAL(msg) call_my_function((msg), stderr) */ diff --git a/src/core/os.c b/src/core/os.c index 837375cb..16863bff 100644 --- a/src/core/os.c +++ b/src/core/os.c @@ -70,6 +70,20 @@ extern char **environ; void arc4random_buf(void *buf, size_t nbytes); #endif +/* arc4random_buf wasn't available in OS X until 10.7. */ +#ifdef JANET_NO_ARC4RANDOM_BUF +/* Based on https://stackoverflow.com/a/12956868/558735 */ +uint32_t arc4random(void); +void arc4random_buf(void *buf, size_t nbytes) { + size_t entropy_len = (nbytes/4)+1; + uint32_t entropy[entropy_len]; + for (size_t i = 0; i < entropy_len; i++) { + entropy[i] = arc4random(); + } + memcpy(buf, entropy, nbytes); +} +#endif + /* Not POSIX, but all Unixes but Solaris have this function. */ #if defined(JANET_POSIX) && !defined(__sun) time_t timegm(struct tm *tm); From 647fc56d47a07847bdd713449920c8ba485bdb74 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Mon, 29 Jun 2020 22:56:16 -0500 Subject: [PATCH 23/31] Replace for with forv in most places in boot.janet Generates slightly better bytecode with current compiler (gets rid of a single extra move instruction per loop iteration). --- src/boot/boot.janet | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/boot/boot.janet b/src/boot/boot.janet index a1b176a8..fe78017c 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -764,7 +764,7 @@ [a lo hi by] (def pivot (in a hi)) (var i lo) - (for j lo hi + (forv j lo hi (def aj (in a j)) (when (by aj pivot) (def ai (in a i)) @@ -862,19 +862,19 @@ (def ninds (length inds)) (if (= 0 ninds) (error "expected at least 1 indexed collection")) (var limit (length (in inds 0))) - (for i 0 ninds + (forv i 0 ninds (def l (length (in inds i))) (if (< l limit) (set limit l))) (def [i1 i2 i3 i4] inds) (def res (array/new limit)) (case ninds - 1 (for i 0 limit (set (res i) (f (in i1 i)))) - 2 (for i 0 limit (set (res i) (f (in i1 i) (in i2 i)))) - 3 (for i 0 limit (set (res i) (f (in i1 i) (in i2 i) (in i3 i)))) - 4 (for i 0 limit (set (res i) (f (in i1 i) (in i2 i) (in i3 i) (in i4 i)))) - (for i 0 limit + 1 (forv i 0 limit (set (res i) (f (in i1 i)))) + 2 (forv i 0 limit (set (res i) (f (in i1 i) (in i2 i)))) + 3 (forv i 0 limit (set (res i) (f (in i1 i) (in i2 i) (in i3 i)))) + 4 (forv i 0 limit (set (res i) (f (in i1 i) (in i2 i) (in i3 i) (in i4 i)))) + (forv i 0 limit (def args (array/new ninds)) - (for j 0 ninds (set (args j) (in (in inds j) i))) + (forv j 0 ninds (set (args j) (in (in inds j) i))) (set (res i) (f ;args)))) res) @@ -926,12 +926,12 @@ 1 (do (def [n] args) (def arr (array/new n)) - (for i 0 n (put arr i i)) + (forv i 0 n (put arr i i)) arr) 2 (do (def [n m] args) (def arr (array/new (- m n))) - (for i n m (put arr (- i n) i)) + (forv i n m (put arr (- i n) i)) arr) 3 (do (def [n m s] args) @@ -1280,7 +1280,7 @@ (var d ds) (def len-1 (- (length ks) 1)) (if (< len-1 0) (error "expected at least 1 key in ks")) - (for i 0 len-1 + (forv i 0 len-1 (def k (get ks i)) (def v (get d k)) (if (= nil v) @@ -1302,7 +1302,7 @@ (var d ds) (def len-1 (- (length ks) 1)) (if (< len-1 0) (error "expected at least 1 key in ks")) - (for i 0 len-1 + (forv i 0 len-1 (def k (get ks i)) (def v (get d k)) (if (= nil v) @@ -2548,7 +2548,7 @@ [&opt n] (def fun (.fn n)) (def bytecode (.bytecode n)) - (for i 0 (length bytecode) + (forv i 0 (length bytecode) (debug/fbreak fun i)) (print "Set " (length bytecode) " breakpoints in " fun)) @@ -2557,7 +2557,7 @@ [&opt n] (def fun (.fn n)) (def bytecode (.bytecode n)) - (for i 0 (length bytecode) + (forv i 0 (length bytecode) (debug/unfbreak fun i)) (print "Cleared " (length bytecode) " breakpoints in " fun)) @@ -2599,7 +2599,7 @@ "Go to the next breakpoint." [&opt n] (var res nil) - (for i 0 (or n 1) + (forv i 0 (or n 1) (set res (resume (.fiber)))) res) @@ -2613,7 +2613,7 @@ "Execute the next n instructions." [&opt n] (var res nil) - (for i 0 (or n 1) + (forv i 0 (or n 1) (set res (debug/step (.fiber)))) res) From f5d208d5d6e967dbfdfc14ef654a3a810d649066 Mon Sep 17 00:00:00 2001 From: Jason Pepas Date: Tue, 30 Jun 2020 04:06:20 -0500 Subject: [PATCH 24/31] eliminate large stack allocation from arc4random_buf bodge --- src/core/os.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/core/os.c b/src/core/os.c index 16863bff..bc509bc3 100644 --- a/src/core/os.c +++ b/src/core/os.c @@ -72,15 +72,20 @@ void arc4random_buf(void *buf, size_t nbytes); /* arc4random_buf wasn't available in OS X until 10.7. */ #ifdef JANET_NO_ARC4RANDOM_BUF -/* Based on https://stackoverflow.com/a/12956868/558735 */ uint32_t arc4random(void); void arc4random_buf(void *buf, size_t nbytes) { - size_t entropy_len = (nbytes/4)+1; - uint32_t entropy[entropy_len]; - for (size_t i = 0; i < entropy_len; i++) { - entropy[i] = arc4random(); + uint32_t *buf_as_words = (uint32_t*)buf; + size_t nwords = nbytes / 4; + for (size_t i=0; i < nwords; i++) { + buf_as_words[i] = arc4random(); + } + + size_t tail_len = nbytes % 4; + if (tail_len) { + uint8_t *tail = buf + nbytes - tail_len; + uint32_t rand = arc4random(); + memcpy(tail, &rand, tail_len); } - memcpy(buf, entropy, nbytes); } #endif From f06e9ae30c64ae5575f1b64868ab2f82e7d9b840 Mon Sep 17 00:00:00 2001 From: Jason Pepas Date: Tue, 30 Jun 2020 04:17:01 -0500 Subject: [PATCH 25/31] Switch to using /dev/urandom for OS X prior to 10.7 --- src/conf/janetconf.h | 1 - src/core/os.c | 32 +++++++++----------------------- 2 files changed, 9 insertions(+), 24 deletions(-) diff --git a/src/conf/janetconf.h b/src/conf/janetconf.h index fc9f72d8..f31a8b35 100644 --- a/src/conf/janetconf.h +++ b/src/conf/janetconf.h @@ -58,7 +58,6 @@ /* #define JANET_NO_REALPATH */ /* #define JANET_NO_SYMLINKS */ /* #define JANET_NO_UMASK */ -/* #define JANET_NO_ARC4RANDOM_BUF */ /* #define JANET_OUT_OF_MEMORY do { printf("janet out of memory\n"); exit(1); } while (0) */ /* #define JANET_EXIT(msg) do { printf("C assert failed executing janet: %s\n", msg); exit(1); } while (0) */ /* #define JANET_TOP_LEVEL_SIGNAL(msg) call_my_function((msg), stderr) */ diff --git a/src/core/os.c b/src/core/os.c index bc509bc3..201d2930 100644 --- a/src/core/os.c +++ b/src/core/os.c @@ -39,6 +39,10 @@ #define RETRY_EINTR(RC, CALL) do { (RC) = CALL; } while((RC) < 0 && errno == EINTR) +#ifdef JANET_APPLE +#include +#endif + #ifdef JANET_WINDOWS #include #include @@ -66,29 +70,10 @@ extern char **environ; /* Setting C99 standard makes this not available, but it should * work/link properly if we detect a BSD */ -#if defined(JANET_BSD) || defined(JANET_APPLE) +#if defined(JANET_BSD) || defined(MAC_OS_X_VERSION_10_7) void arc4random_buf(void *buf, size_t nbytes); #endif -/* arc4random_buf wasn't available in OS X until 10.7. */ -#ifdef JANET_NO_ARC4RANDOM_BUF -uint32_t arc4random(void); -void arc4random_buf(void *buf, size_t nbytes) { - uint32_t *buf_as_words = (uint32_t*)buf; - size_t nwords = nbytes / 4; - for (size_t i=0; i < nwords; i++) { - buf_as_words[i] = arc4random(); - } - - size_t tail_len = nbytes % 4; - if (tail_len) { - uint8_t *tail = buf + nbytes - tail_len; - uint32_t rand = arc4random(); - memcpy(tail, &rand, tail_len); - } -} -#endif - /* Not POSIX, but all Unixes but Solaris have this function. */ #if defined(JANET_POSIX) && !defined(__sun) time_t timegm(struct tm *tm); @@ -625,10 +610,11 @@ static Janet os_cryptorand(int32_t argc, Janet *argv) { v = v >> 8; } } -#elif defined(JANET_LINUX) +#elif defined(JANET_LINUX) || ( defined(JANET_APPLE) && !defined(MAC_OS_X_VERSION_10_7) ) /* We should be able to call getrandom on linux, but it doesn't seem to be uniformly supported on linux distros. - In both cases, use this fallback path for now... */ + On Mac, arc4random_buf wasn't available on until 10.7. + In these cases, use this fallback path for now... */ int rc; int randfd; RETRY_EINTR(randfd, open("/dev/urandom", O_RDONLY | O_CLOEXEC)); @@ -645,7 +631,7 @@ static Janet os_cryptorand(int32_t argc, Janet *argv) { n -= nread; } RETRY_EINTR(rc, close(randfd)); -#elif defined(JANET_BSD) || defined(JANET_APPLE) +#elif defined(JANET_BSD) || defined(MAC_OS_X_VERSION_10_7) (void) genericerr; arc4random_buf(buffer->data + offset, n); #else From c26f57362060702a201d33e89e4731a5c9c31152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20Pospi=CC=81s=CC=8Cil?= Date: Tue, 30 Jun 2020 17:03:13 +0200 Subject: [PATCH 26/31] Add tests for peg/find and peg/find-all --- test/suite8.janet | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/suite8.janet b/test/suite8.janet index cd4cf88e..f6e8c163 100644 --- a/test/suite8.janet +++ b/test/suite8.janet @@ -332,4 +332,10 @@ neldb\0\0\0\xD8\x05printG\x01\0\xDE\xDE\xDE'\x03\0marshal_tes/\x02 (assert (= :keyword (keyword/slice "some_keyword_slice" 5 12)) "keyword slice") (assert (= 'symbol (symbol/slice "some_symbol_slice" 5 11)) "symbol slice") +# Peg find and find-all +(def p "/usr/local/bin/janet") +(assert (= (peg/find '"n/" p) 13) "peg find 1") +(assert (not (peg/find '"t/" p)) "peg find 2") +(assert (deep= (peg/find-all '"/" p) @[0 4 10 14]) "peg find-all") + (end-suite) From a110b103e8ac21190630d4f11b2d781f09682c79 Mon Sep 17 00:00:00 2001 From: Jason Pepas Date: Wed, 1 Jul 2020 15:35:36 -0500 Subject: [PATCH 27/31] math/nan --- src/core/math.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/core/math.c b/src/core/math.c index a99e012a..bc4b88de 100644 --- a/src/core/math.c +++ b/src/core/math.c @@ -499,5 +499,11 @@ void janet_lib_math(JanetTable *env) { JDOC("The number representing positive infinity")); janet_def(env, "math/-inf", janet_wrap_number(-INFINITY), JDOC("The number representing negative infinity")); +#ifdef NAN + janet_def(env, "math/nan", janet_wrap_number(NAN), +#else + janet_def(env, "math/nan", janet_wrap_number(0.0/0.0), +#endif + JDOC("Not a number (IEEE-754 NaN)")); #endif } From e548e1f6e0e2c12a7d8f3cef3bc97292a1ae2418 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Wed, 1 Jul 2020 21:26:11 -0500 Subject: [PATCH 28/31] Add peg/replace and peg/replace-all --- src/core/math.c | 2 +- src/core/peg.c | 90 ++++++++++++++++++++++++++++++++++++++--------- test/suite8.janet | 11 ++++++ 3 files changed, 86 insertions(+), 17 deletions(-) diff --git a/src/core/math.c b/src/core/math.c index bc4b88de..5491c77d 100644 --- a/src/core/math.c +++ b/src/core/math.c @@ -502,7 +502,7 @@ void janet_lib_math(JanetTable *env) { #ifdef NAN janet_def(env, "math/nan", janet_wrap_number(NAN), #else - janet_def(env, "math/nan", janet_wrap_number(0.0/0.0), + janet_def(env, "math/nan", janet_wrap_number(0.0 / 0.0), #endif JDOC("Not a number (IEEE-754 NaN)")); #endif diff --git a/src/core/peg.c b/src/core/peg.c index 847b47e9..4267b7b7 100644 --- a/src/core/peg.c +++ b/src/core/peg.c @@ -1313,24 +1313,31 @@ typedef struct { JanetPeg *peg; PegState s; JanetByteView bytes; + JanetByteView repl; int32_t start; } PegCall; /* Initialize state for peg cfunctions */ -static PegCall peg_cfun_init(int32_t argc, Janet *argv) { +static PegCall peg_cfun_init(int32_t argc, Janet *argv, int get_replace) { PegCall ret; - janet_arity(argc, 2, -1); + int32_t min = get_replace ? 3 : 2; + janet_arity(argc, get_replace, -1); if (janet_checktype(argv[0], JANET_ABSTRACT) && janet_abstract_type(janet_unwrap_abstract(argv[0])) == &janet_peg_type) { ret.peg = janet_unwrap_abstract(argv[0]); } else { ret.peg = compile_peg(argv[0]); } - ret.bytes = janet_getbytes(argv, 1); - if (argc > 2) { - ret.start = janet_gethalfrange(argv, 2, ret.bytes.len, "offset"); - ret.s.extrac = argc - 3; - ret.s.extrav = janet_tuple_n(argv + 3, argc - 3); + if (get_replace) { + ret.repl = janet_getbytes(argv, 1); + ret.bytes = janet_getbytes(argv, 2); + } else { + ret.bytes = janet_getbytes(argv, 1); + } + if (argc > min) { + ret.start = janet_gethalfrange(argv, min, ret.bytes.len, "offset"); + ret.s.extrac = argc - min - 1; + ret.s.extrav = janet_tuple_n(argv + min + 1, argc - min - 1); } else { ret.start = 0; ret.s.extrac = 0; @@ -1348,18 +1355,22 @@ static PegCall peg_cfun_init(int32_t argc, Janet *argv) { return ret; } +static void peg_call_reset(PegCall *c) { + c->s.captures->count = 0; + c->s.scratch->count = 0; + c->s.tags->count = 0; +} + static Janet cfun_peg_match(int32_t argc, Janet *argv) { - PegCall c = peg_cfun_init(argc, argv); + PegCall c = peg_cfun_init(argc, argv, 0); const uint8_t *result = peg_rule(&c.s, c.s.bytecode, c.bytes.bytes + c.start); return result ? janet_wrap_array(c.s.captures) : janet_wrap_nil(); } static Janet cfun_peg_find(int32_t argc, Janet *argv) { - PegCall c = peg_cfun_init(argc, argv); + PegCall c = peg_cfun_init(argc, argv, 0); for (int32_t i = c.start; i < c.bytes.len; i++) { - c.s.captures->count = 0; - c.s.scratch->count = 0; - c.s.tags->count = 0; + peg_call_reset(&c); if (peg_rule(&c.s, c.s.bytecode, c.bytes.bytes + i)) return janet_wrap_integer(i); } @@ -1367,22 +1378,58 @@ static Janet cfun_peg_find(int32_t argc, Janet *argv) { } static Janet cfun_peg_find_all(int32_t argc, Janet *argv) { - PegCall c = peg_cfun_init(argc, argv); + PegCall c = peg_cfun_init(argc, argv, 0); JanetArray *ret = janet_array(0); for (int32_t i = c.start; i < c.bytes.len; i++) { - c.s.captures->count = 0; - c.s.scratch->count = 0; - c.s.tags->count = 0; + peg_call_reset(&c); if (peg_rule(&c.s, c.s.bytecode, c.bytes.bytes + i)) janet_array_push(ret, janet_wrap_integer(i)); } return janet_wrap_array(ret); } +static Janet cfun_peg_replace_generic(int32_t argc, Janet *argv, int only_one) { + PegCall c = peg_cfun_init(argc, argv, 1); + JanetBuffer *ret = janet_buffer(0); + int32_t trail = 0; + for (int32_t i = c.start; i < c.bytes.len;) { + peg_call_reset(&c); + const uint8_t *result = peg_rule(&c.s, c.s.bytecode, c.bytes.bytes + i); + if (NULL != result) { + if (trail < i) { + janet_buffer_push_bytes(ret, c.bytes.bytes + trail, (i - trail)); + trail = i; + } + int32_t nexti = result - c.bytes.bytes; + janet_buffer_push_bytes(ret, c.repl.bytes, c.repl.len); + trail = nexti; + if (nexti == i) nexti++; + i = nexti; + if (only_one) break; + } else { + i++; + } + } + if (trail < c.bytes.len) { + janet_buffer_push_bytes(ret, c.bytes.bytes + trail, (c.bytes.len - trail)); + } + return janet_wrap_buffer(ret); +} + +static Janet cfun_peg_replace_all(int32_t argc, Janet *argv) { + return cfun_peg_replace_generic(argc, argv, 0); +} + +static Janet cfun_peg_replace(int32_t argc, Janet *argv) { + return cfun_peg_replace_generic(argc, argv, 1); +} + static JanetMethod peg_methods[] = { {"match", cfun_peg_match}, {"find", cfun_peg_find}, {"find-all", cfun_peg_find_all}, + {"replace", cfun_peg_replace}, + {"replace-all", cfun_peg_replace_all}, {NULL, NULL} }; @@ -1416,6 +1463,17 @@ static const JanetReg peg_cfuns[] = { JDOC("(peg/find-all peg text &opt start & args)\n\n" "Find all indexes where the peg matches in text. Returns an array of integers.") }, + { + "peg/replace", cfun_peg_replace, + JDOC("(peg/replace peg repl text &opt start & args)\n\n" + "Replace first match of peg in text with repl, returning a new buffer. The peg does not need to make captures to do replacement. " + "If no matches are found, returns the input string in a new buffer.") + }, + { + "peg/replace-all", cfun_peg_replace_all, + JDOC("(peg/replace-all peg repl text &opt start & args)\n\n" + "Replace all matches of peg in text with repl, returning a new buffer. The peg does not need to make captures to do replacement.") + }, {NULL, NULL, NULL} }; diff --git a/test/suite8.janet b/test/suite8.janet index f6e8c163..fec997ee 100644 --- a/test/suite8.janet +++ b/test/suite8.janet @@ -338,4 +338,15 @@ neldb\0\0\0\xD8\x05printG\x01\0\xDE\xDE\xDE'\x03\0marshal_tes/\x02 (assert (not (peg/find '"t/" p)) "peg find 2") (assert (deep= (peg/find-all '"/" p) @[0 4 10 14]) "peg find-all") +# Peg replace and replace-all +(var ti 0) +(defn check-replacer + [x y z] + (assert (= (string/replace x y z) (string (peg/replace x y z))) "replacer test replace") + (assert (= (string/replace-all x y z) (string (peg/replace-all x y z))) "replacer test replace-all")) +(check-replacer "abc" "Z" "abcabcabcabasciabsabc") +(check-replacer "abc" "Z" "") +(check-replacer "aba" "ZZZZZZ" "ababababababa") +(check-replacer "aba" "" "ababababababa") + (end-suite) From 97c64f27ff1e4390223bf4cce2dda10d562bbdd9 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Wed, 1 Jul 2020 22:37:04 -0500 Subject: [PATCH 29/31] Remove duplicate code in loop macro. Also evaluate for loop and range step exactly once. Multiple evaluations can be inefficent and make infinite loop detection impossible. --- src/boot/boot.janet | 54 ++++++++++++++++++--------------------------- 1 file changed, 21 insertions(+), 33 deletions(-) diff --git a/src/boot/boot.janet b/src/boot/boot.janet index e1fba8da..a4ede24c 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -379,26 +379,23 @@ ,(apply defer [(or dtor :close) binding] [truthy]) ,falsey)) -(defn- for-template - [binding start stop step comparison delta body] - (with-syms [i s] - ~(do - (var ,i ,start) - (def ,s ,stop) - (while (,comparison ,i ,s) - (def ,binding ,i) - ,;body - (set ,i (,delta ,i ,step)))))) - (defn- for-var-template [i start stop step comparison delta body] (with-syms [s] + (def st (if (idempotent? step) step (gensym))) ~(do (var ,i ,start) (def ,s ,stop) + ,;(if (= st step) [] [~(def ,st ,step)]) (while (,comparison ,i ,s) ,;body - (set ,i (,delta ,i ,step)))))) + (set ,i (,delta ,i ,st)))))) + +(defn- for-template + [binding start stop step comparison delta body] + (def i (gensym)) + (for-var-template i start stop step comparison delta + [~(def ,binding ,i) ;body])) (defn- check-indexed [x] (if (indexed? x) @@ -411,26 +408,18 @@ (for-template binding start stop (or step 1) comparison op [rest]))) (defn- each-template - [binding inx body] + [binding inx kind body] (with-syms [k] (def ds (if (idempotent? inx) inx (gensym))) ~(do ,(unless (= ds inx) ~(def ,ds ,inx)) (var ,k (,next ,ds nil)) (while (,not= nil ,k) - (def ,binding (,in ,ds ,k)) - ,;body - (set ,k (,next ,ds ,k)))))) - -(defn- keys-template - [binding in pair? body] - (with-syms [k] - (def ds (if (idempotent? in) in (gensym))) - ~(do - ,(unless (= ds in) ~(def ,ds ,in)) - (var ,k (,next ,ds nil)) - (while (,not= nil ,k) - (def ,binding ,(if pair? ~(tuple ,k (in ,ds ,k)) k)) + (def ,binding + ,(case kind + :each ~(,in ,ds ,k) + :keys k + :pairs ~(,tuple ,k (,in ,ds ,k)))) ,;body (set ,k (,next ,ds ,k)))))) @@ -487,9 +476,9 @@ :range-to (range-template binding object rest + <=) :down (range-template binding object rest - >) :down-to (range-template binding object rest - >=) - :keys (keys-template binding object false [rest]) - :pairs (keys-template binding object true [rest]) - :in (each-template binding object [rest]) + :keys (each-template binding object :keys [rest]) + :pairs (each-template binding object :pairs [rest]) + :in (each-template binding object :each [rest]) :iterate (iterate-template binding object rest) :generate (loop-fiber-template binding object [rest]) (error (string "unexpected loop verb " verb))))) @@ -508,12 +497,12 @@ (defmacro eachk "Loop over each key in ds. Returns nil." [x ds & body] - (keys-template x ds false body)) + (each-template x ds :each body)) (defmacro eachp "Loop over each (key, value) pair in ds. Returns nil." [x ds & body] - (keys-template x ds true body)) + (each-template x ds :pairs body)) (defmacro eachy "Resume a fiber in a loop until it has errored or died. Evaluate the body @@ -530,7 +519,7 @@ (defmacro each "Loop over each value in ds. Returns nil." [x ds & body] - (each-template x ds body)) + (each-template x ds :each body)) (defmacro loop "A general purpose loop macro. This macro is similar to the Common Lisp @@ -575,7 +564,6 @@ (put _env 'for-var-template nil) (put _env 'iterate-template nil) (put _env 'each-template nil) -(put _env 'keys-template nil) (put _env 'range-template nil) (put _env 'loop-fiber-template nil) From b89f0fac7b7241663e2631305d4dd387e3a1b5bb Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Fri, 3 Jul 2020 09:54:58 -0500 Subject: [PATCH 30/31] Move clock shims to util (Helps #430). The thread module should also use these clock shims rather than clock_gettime, which is not available on older mac systems. --- src/conf/janetconf.h | 9 +++++---- src/core/os.c | 30 +----------------------------- src/core/thread.c | 2 +- src/core/util.c | 32 ++++++++++++++++++++++++++++++++ src/core/util.h | 7 +++++++ 5 files changed, 46 insertions(+), 34 deletions(-) diff --git a/src/conf/janetconf.h b/src/conf/janetconf.h index f31a8b35..eae3caa7 100644 --- a/src/conf/janetconf.h +++ b/src/conf/janetconf.h @@ -41,7 +41,8 @@ /* #define JANET_API __attribute__((visibility ("default"))) */ /* These settings should be specified before amalgamation is - * built. */ + * built. Any build with these set should be considered non-standard, and + * certain Janet libraries should be expected not to work. */ /* #define JANET_NO_DOCSTRINGS */ /* #define JANET_NO_SOURCEMAPS */ /* #define JANET_REDUCED_OS */ @@ -51,13 +52,13 @@ /* #define JANET_NO_NET */ /* #define JANET_NO_TYPED_ARRAY */ /* #define JANET_NO_INT_TYPES */ +/* #define JANET_NO_REALPATH */ +/* #define JANET_NO_SYMLINKS */ +/* #define JANET_NO_UMASK */ /* Other settings */ /* #define JANET_NO_PRF */ /* #define JANET_NO_UTC_MKTIME */ -/* #define JANET_NO_REALPATH */ -/* #define JANET_NO_SYMLINKS */ -/* #define JANET_NO_UMASK */ /* #define JANET_OUT_OF_MEMORY do { printf("janet out of memory\n"); exit(1); } while (0) */ /* #define JANET_EXIT(msg) do { printf("C assert failed executing janet: %s\n", msg); exit(1); } while (0) */ /* #define JANET_TOP_LEVEL_SIGNAL(msg) call_my_function((msg), stderr) */ diff --git a/src/core/os.c b/src/core/os.c index 201d2930..1b794890 100644 --- a/src/core/os.c +++ b/src/core/os.c @@ -514,39 +514,11 @@ static Janet os_time(int32_t argc, Janet *argv) { return janet_wrap_number(dtime); } -/* Clock shims */ -#ifdef JANET_WINDOWS -static int gettime(struct timespec *spec) { - FILETIME ftime; - GetSystemTimeAsFileTime(&ftime); - int64_t wintime = (int64_t)(ftime.dwLowDateTime) | ((int64_t)(ftime.dwHighDateTime) << 32); - /* Windows epoch is January 1, 1601 apparently */ - wintime -= 116444736000000000LL; - spec->tv_sec = wintime / 10000000LL; - /* Resolution is 100 nanoseconds. */ - spec->tv_nsec = wintime % 10000000LL * 100; - return 0; -} -#elif defined(__MACH__) -static int gettime(struct timespec *spec) { - clock_serv_t cclock; - mach_timespec_t mts; - host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); - clock_get_time(cclock, &mts); - mach_port_deallocate(mach_task_self(), cclock); - spec->tv_sec = mts.tv_sec; - spec->tv_nsec = mts.tv_nsec; - return 0; -} -#else -#define gettime(TV) clock_gettime(CLOCK_MONOTONIC, (TV)) -#endif - static Janet os_clock(int32_t argc, Janet *argv) { janet_fixarity(argc, 0); (void) argv; struct timespec tv; - if (gettime(&tv)) janet_panic("could not get time"); + if (janet_gettime(&tv)) janet_panic("could not get time"); double dtime = tv.tv_sec + (tv.tv_nsec / 1E9); return janet_wrap_number(dtime); } diff --git a/src/core/thread.c b/src/core/thread.c index 54905135..d1d6cbab 100644 --- a/src/core/thread.c +++ b/src/core/thread.c @@ -234,7 +234,7 @@ static void janet_waiter_init(JanetWaiter *waiter, double sec) { if (waiter->timedwait) { /* N seconds -> timespec of (now + sec) */ struct timespec now; - clock_gettime(CLOCK_REALTIME, &now); + janet_gettime(&now); time_t tvsec = (time_t) floor(sec); long tvnsec = (long) floor(1000000000.0 * (sec - ((double) tvsec))); tvsec += now.tv_sec; diff --git a/src/core/util.c b/src/core/util.c index cb8f5bb1..7277f95b 100644 --- a/src/core/util.c +++ b/src/core/util.c @@ -590,3 +590,35 @@ JanetTable *janet_get_core_table(const char *name) { if (!janet_checktype(out, JANET_TABLE)) return NULL; return janet_unwrap_table(out); } + +/* Clock shims for various platforms */ +#ifdef JANET_GETTIME +#ifdef JANET_WINDOWS +int janet_gettime(struct timespec *spec) { + FILETIME ftime; + GetSystemTimeAsFileTime(&ftime); + int64_t wintime = (int64_t)(ftime.dwLowDateTime) | ((int64_t)(ftime.dwHighDateTime) << 32); + /* Windows epoch is January 1, 1601 apparently */ + wintime -= 116444736000000000LL; + spec->tv_sec = wintime / 10000000LL; + /* Resolution is 100 nanoseconds. */ + spec->tv_nsec = wintime % 10000000LL * 100; + return 0; +} +#elif defined(__MACH__) +int janet_gettime(struct timespec *spec) { + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + spec->tv_sec = mts.tv_sec; + spec->tv_nsec = mts.tv_nsec; + return 0; +} +#else +int janet_gettime(struct timespec *spec) { + return clock_gettime(CLOCK_MONOTONIC, spec); +} +#endif +#endif diff --git a/src/core/util.h b/src/core/util.h index c487622e..dd239a4b 100644 --- a/src/core/util.h +++ b/src/core/util.h @@ -97,6 +97,13 @@ void janet_core_def(JanetTable *env, const char *name, Janet x, const void *p); void janet_core_cfuns(JanetTable *env, const char *regprefix, const JanetReg *cfuns); #endif +/* Clock gettime */ +#if !defined(JANET_REDUCED_OS) || !defined(JANET_SINGLE_THREADED) +#include +#define JANET_GETTIME +int janet_gettime(struct timespec *spec); +#endif + /* Initialize builtin libraries */ void janet_lib_io(JanetTable *env); void janet_lib_math(JanetTable *env); From 55cf9f5e1ca147eb872273e6c4555253a20f2bb7 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Fri, 3 Jul 2020 10:13:55 -0500 Subject: [PATCH 31/31] Don't break reverse backwards compat. Breaking backwards compatibiliy here is not worth it. Also update changelog. --- CHANGELOG.md | 7 ++++++- src/boot/boot.janet | 6 +++--- src/core/util.c | 3 +++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20307351..d0ff15a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,16 @@ All notable changes to this project will be documented in this file. ## Unreleased - ??? +- The gc interval is now autotuned, to prevent very bad gc behavior. +- Improvements to the bytecode compiler, Janet will now generate more efficient bytecode. +- Add `peg/find`, `peg/find-all`, `peg/replace`, and `peg/replace-all` +- Add `math/nan` +- Add `forv` macro - Add `symbol/slice` - Add `keyword/slice` - Allow cross compilation with Makefile. - Change `compare-primitve` to `cmp` and make it more efficient. -- Change `reverse` to `reversed`, reverse now mutates the backing array +- Add `reverse!` for reversing an array or buffer in place. - `janet_dobytes` and `janet_dostring` return parse errors in \*out - Add `repeat` macro for iterating something n times. - Add `eachy` (each yield) macro for iterating a fiber. diff --git a/src/boot/boot.janet b/src/boot/boot.janet index a4ede24c..c90fae8d 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -1202,13 +1202,13 @@ (if x nil (set res x))) res) -(defn reverse +(defn reverse! "Reverses the order of the elements in a given array or buffer and returns it mutated." [t] (def len-1 (- (length t) 1)) (def half (/ len-1 2)) - (for i 0 half + (forv i 0 half (def j (- len-1 i)) (def l (in t i)) (def r (in t j)) @@ -1216,7 +1216,7 @@ (put t j l)) t) -(defn reversed +(defn reverse "Reverses the order of the elements in a given array or tuple and returns a new array. If string or buffer is provided function returns array of chars reversed." [t] diff --git a/src/core/util.c b/src/core/util.c index 7277f95b..847e7471 100644 --- a/src/core/util.c +++ b/src/core/util.c @@ -26,6 +26,9 @@ #include "util.h" #include "state.h" #include "gc.h" +#ifdef JANET_WINDOWS +#include +#endif #endif #include