diff --git a/examples/3sum.dst b/examples/3sum.dst index 831c95a4..57027f13 100644 --- a/examples/3sum.dst +++ b/examples/3sum.dst @@ -1,16 +1,13 @@ -(import examples.iterators :as "") - (defn sum3 "Solve the 3SUM problem in O(n^2) time." [s] (def tab @{}) (def solutions @{}) (def len (length s)) - (for [k 0 len] + (loop [k :range [0 len]] (put tab (get s k) k)) - (for [i 0 len] - (for [j 0 len] + (loop [i :range [0 len], j :range [0 len]] (def k (get tab (- 0 (get s i) (get s j)))) (when (and k (not= k i) (not= k j) (not= i j)) - (put solutions {i true j true k true} true)))) - (iter2array (map (fn [x] (iter2array (keys x))) (keys solutions)))) + (put solutions {i true j true k true} true))) + (map keys (keys solution))) diff --git a/examples/fizzbuzz.dst b/examples/fizzbuzz.dst index 17686e7d..8187bbaf 100644 --- a/examples/fizzbuzz.dst +++ b/examples/fizzbuzz.dst @@ -3,7 +3,7 @@ (defn fizzbuzz "Prints the fizzbuzz problem." [] - (for [i 1 101] + (loop [i :range [1 101]] (let [fizz (zero? (% i 3)) buzz (zero? (% i 5))] (print (cond diff --git a/examples/frequencies.dst b/examples/frequencies.dst index df66a4d5..b9d35eb6 100644 --- a/examples/frequencies.dst +++ b/examples/frequencies.dst @@ -4,7 +4,8 @@ "Get the number of occurences of each value in a indexed structure." [ind] (def freqs @{}) - (each (fn [x] - (let [n (get freqs x)] - (put freqs x (if n (+ 1 n) 1)))) ind) + (loop + [x :in ind] + (def n (get freqs x)) + (put freqs x (if n (+ 1 n) 1))) freqs) diff --git a/examples/hello.dst b/examples/hello.dst index b27f5127..cad00e8f 100644 --- a/examples/hello.dst +++ b/examples/hello.dst @@ -1,3 +1,5 @@ # Prints hello +(import examples.3sum) + (print "hello, world!") diff --git a/examples/primes.dst b/examples/primes.dst index f47757b3..03e6facd 100644 --- a/examples/primes.dst +++ b/examples/primes.dst @@ -4,10 +4,10 @@ "Returns a list of prime numbers less than n." [n] (def list @[]) - (for [i 2 n] + (loop [i :range [2 n]] (var isprime? true) (def len (length list)) - (for [j 0 len] + (loop [j :range [0 len]] (def trial (get list j)) (if (zero? (% i trial)) (:= isprime? false))) (if isprime? (array.push list i))) diff --git a/src/compiler/boot.dst b/src/compiler/boot.dst index 3d241b37..09a3135f 100644 --- a/src/compiler/boot.dst +++ b/src/compiler/boot.dst @@ -218,18 +218,63 @@ value." (array.concat accum body) (apply1 tuple accum)) +(defmacro loop + "A general purpose loop macro." + [head & body] + (def head1 (ast.unwrap1 head)) + (def len (length head1)) + (defn doone + [i] + (if (>= i len) + (tuple.prepend body 'do) + (do + (def bindings (get head1 i)) + (def verb (ast.unwrap1 (get head1 (+ i 1)))) + (def object (ast.unwrap1 (get head1 (+ i 2)))) + (switch verb + :range (do + (def [start end _inc] (ast.unwrap1 object)) + (def inc (if _inc _inc 1)) + (def endsym (gensym)) + (tuple 'do + (tuple 'var bindings start) + (tuple 'def endsym end) + (tuple 'while (tuple < bindings endsym) + (doone (+ i 3)) + (tuple ':= bindings (tuple + bindings inc))))) + :keys (do + (def $dict (gensym "dict")) + (tuple 'do + (tuple 'def $dict object) + (tuple 'var bindings (tuple next $dict nil)) + (tuple 'while (tuple not= nil bindings) + (doone (+ i 3)) + (tuple ':= bindings (tuple next $dict bindings))))) + :in (do + (def $len (gensym "len")) + (def $i (gensym "i")) + (def $indexed (gensym "indexed")) + (tuple 'do + (tuple 'def $indexed object) + (tuple 'def $len (tuple length $indexed)) + (tuple 'var $i 0) + (tuple 'while (tuple < $i $len) + (tuple 'def bindings (tuple get $indexed $i)) + (doone (+ i 3)) + (tuple ':= $i (tuple + 1 $i))))) + (error ("unexpected loop verb: " verb)))))) + (doone 0)) + (defmacro for - "An imperative for loop over an integer range. Use with caution and discretion." - [head & body] - (def [sym start end _inc] (ast.unwrap1 head)) - (def inc (if _inc _inc 1)) - (def endsym (gensym)) + "Similar to loop, but accumulates the loop body into an array and returns that." + [head & body] + (def $accum (gensym "accum")) (tuple 'do - (tuple 'var sym start) - (tuple 'def endsym end) - (tuple 'while (tuple < sym endsym) - (tuple.prepend body 'do) - (tuple ':= sym (tuple + sym inc))))) + (tuple 'def $accum @[]) + (tuple 'loop head + (tuple array.push $accum + (tuple.prepend body 'do))) + $accum)) (defmacro and "Evaluates to the last argument if all preceding elements are true, otherwise @@ -332,7 +377,7 @@ Returns nil if args is empty." (def len (length args)) (when (pos? len) (var ret (get args 0)) - (for [i 0 len] + (loop [i :range [0 len]] (def v (get args i)) (if (order v ret) (:= ret v))) ret)) @@ -356,7 +401,7 @@ Returns nil if args is empty." [a lo hi by] (def pivot (get a hi)) (var i lo) - (for [j lo hi] + (loop [j :range [lo hi]] (def aj (get a j)) (when (by aj pivot) (def ai (get a i)) @@ -391,8 +436,8 @@ Returns nil if args is empty." an indexed type (array, tuple) with a function to produce a value." [f init ind] (var res init) - (for [i 0 (length ind)] - (:= res (f res (get ind i)))) + (loop [x :in ind] + (:= res (f res x))) res) (defn map @@ -402,19 +447,19 @@ the same type as the input sequence." (def ninds (length inds)) (if (= 0 ninds) (error "expected at least 1 indexed collection.")) (var limit (length (get inds 0))) - (for [i 0 ninds] + (loop [i :range [0 ninds]] (def l (length (get inds i))) (if (< l limit) (:= limit l))) (def [i1 i2 i3 i4] inds) (def res (array.new limit)) (switch ninds - 1 (for [i 0 limit] (array.push res (f (get i1 i)))) - 2 (for [i 0 limit] (array.push res (f (get i1 i) (get i2 i)))) - 3 (for [i 0 limit] (array.push res (f (get i1 i) (get i2 i) (get i3 i)))) - 4 (for [i 0 limit] (array.push res (f (get i1 i) (get i2 i) (get i3 i) (get i4 i)))) - (for [i 0 limit] + 1 (loop [i :range [0 limit]] (array.push res (f (get i1 i)))) + 2 (loop [i :range [0 limit]] (array.push res (f (get i1 i) (get i2 i)))) + 3 (loop [i :range [0 limit]] (array.push res (f (get i1 i) (get i2 i) (get i3 i)))) + 4 (loop [i :range [0 limit]] (array.push res (f (get i1 i) (get i2 i) (get i3 i) (get i4 i)))) + (loop [i :range [0 limit]] (def args (array.new ninds)) - (for [j 0 ninds] (array.push args (get (get inds j) i))) + (loop [j :range [0 ninds]] (array.push args (get (get inds j) i))) (array.push res (apply1 f args)))) res) @@ -425,18 +470,18 @@ return a new indexed type." (def ninds (length inds)) (if (= 0 ninds) (error "expected at least 1 indexed collection.")) (var limit (length (get inds 0))) - (for [i 0 ninds] + (loop [i :range [0 ninds]] (def l (length (get inds i))) (if (< l limit) (:= limit l))) (def [i1 i2 i3 i4] inds) (switch ninds - 1 (for [i 0 limit] (f (get i1 i))) - 2 (for [i 0 limit] (f (get i1 i) (get i2 i))) - 3 (for [i 0 limit] (f (get i1 i) (get i2 i) (get i3 i))) - 4 (for [i 0 limit] (f (get i1 i) (get i2 i) (get i3 i) (get i4 i))) - (for [i 0 limit] + 1 (loop [i :range [0 limit]] (f (get i1 i))) + 2 (loop [i :range [0 limit]] (f (get i1 i) (get i2 i))) + 3 (loop [i :range [0 limit]] (f (get i1 i) (get i2 i) (get i3 i))) + 4 (loop [i :range [0 limit]] (f (get i1 i) (get i2 i) (get i3 i) (get i4 i))) + (loop [i :range [0 limit]] (def args (array.new ninds)) - (for [j 0 ninds] (array.push args (get (get inds j) i))) + (loop [j :range [0 ninds]] (array.push args (get (get inds j) i))) (apply1 f args)))) (defn mapcat @@ -445,8 +490,8 @@ use array to concatenate the results. Returns the same type as the input sequence." [f ind t] (def res @[]) - (for [i 0 (length ind)] - (array.concat res (f (get ind i)))) + (loop [x :in ind] + (array.concat res (f x))) (if (= :tuple (type (or t ind))) (apply1 tuple res) res)) @@ -455,10 +500,8 @@ type as the input sequence." "Given a predicate, take only elements from an array or tuple for which (pred element) is truthy. Returns the same type as the input sequence." [pred ind t] - (def len (length ind)) - (def res (array.new len)) - (for [i 0 len] - (def item (get ind i)) + (def res @[]) + (loop [item :in ind] (if (pred item) (array.push res item))) (if (= :tuple (type (or t ind))) @@ -469,7 +512,7 @@ which (pred element) is truthy. Returns the same type as the input sequence." "Create an array of values [0, n)." [n] (def arr (array.new n)) - (for [i 0 n] (put arr i i)) + (loop [i :range [0 n]] (put arr i i)) arr) (defn find-index @@ -521,19 +564,18 @@ the predicate, and abort on first failure." (defn juxt* [& funs] - (def len (length funs)) (fn [& args] (def ret @[]) - (for [i 0 len] - (array.push ret (apply1 (get funs i) args))) + (loop [f :in funs] + (array.push ret (apply1 f args))) (apply1 tuple ret))) (defmacro juxt [& funs] (def parts @['tuple]) (def $args (gensym)) - (for [i 0 (length funs)] - (array.push parts (tuple apply1 (get funs i) $args))) + (loop [f :in funs] + (array.push parts (tuple apply1 f $args))) (tuple 'fn (tuple '& $args) (apply1 tuple parts))) (defmacro -> @@ -611,7 +653,7 @@ in the same manner, and so on. Useful for expressing pipelines of data." (def lk (length keys)) (def lv (length vals)) (def len (if (< lk lv) lk lv)) - (for [i 0 len] + (loop [i :range [0 len]] (put res (get keys i) (get vals i))) (if (= :struct t) (table.to-struct res) @@ -631,18 +673,15 @@ in the same manner, and so on. Useful for expressing pipelines of data." collection" [& colls] (def container @{}) - (for [i 0 (length colls)] - (def c (get colls i)) - (var key (next c nil)) - (while (not= nil key) - (put container key (get c key)) - (:= key (next c key)))) + (loop [c :in colls + key :keys c] + (put container key (get c key))) (if (table? (get colls 0)) container (table.to-struct container))) (defn keys "Get the keys of an associative data structure." [x] - (def arr @[]) + (def arr (array.new (length x))) (var k (next x nil)) (while (not= nil k) (array.push arr k) @@ -652,7 +691,7 @@ in the same manner, and so on. Useful for expressing pipelines of data." (defn values "Get the values of an associative data structure." [x] - (def arr @[]) + (def arr (array.new (length x))) (var k (next x nil)) (while (not= nil k) (array.push arr (get x k)) @@ -662,7 +701,7 @@ in the same manner, and so on. Useful for expressing pipelines of data." (defn pairs "Get the values of an associative data structure." [x] - (def arr @[]) + (def arr (array.new (length x))) (var k (next x nil)) (while (not= nil k) (array.push arr (tuple k (get x k))) @@ -706,12 +745,12 @@ to call on any table. Does not print table prototype information." (def len (length y)) (if (< len 5) (do - (for [i 0 len] + (loop [i :range [0 len]] (when (not= i 0) (buffer.push-string buf " ")) (recur (get y i)))) (do (buffer.push-string indent " ") - (for [i 0 len] + (loop [i :range [0 len]] (when (not= i len) (buffer.push-string buf indent)) (recur (get y i))) (buffer.popn indent 2) @@ -719,9 +758,7 @@ to call on any table. Does not print table prototype information." (defn pp-dict-nested [y] (buffer.push-string indent " ") - (def ps (sort (pairs y))) - (for [i 0 (length ps)] - (def [k v] (get ps i)) + (loop [[k v] :in (sort (pairs y))] (buffer.push-string buf indent) (recur k) (buffer.push-string buf " ") @@ -730,10 +767,9 @@ to call on any table. Does not print table prototype information." (buffer.push-string buf indent)) (defn pp-dict-simple [y] - (def ps (sort (pairs y))) - (for [i 0 (length ps)] - (def [k v] (get ps i)) - (if (pos? i) (buffer.push-string buf " ")) + (var i -1) + (loop [[k v] :in (sort (pairs y))] + (if (pos? (++ i)) (buffer.push-string buf " ")) (recur k) (buffer.push-string buf " ") (recur v))) @@ -772,11 +808,7 @@ to call on any table. Does not print table prototype information." [x] (defn doarray [a] - (def len (length a)) - (def newa @[]) - (for [i 0 len] - (array.push newa (macroexpand-1 (get a i)))) - newa) + (map macroexpand-1 a)) (defn dotable [t] (def newt @{}) @@ -891,7 +923,7 @@ onvalue." (buffer.clear buf) (chunks buf p) (:= len (length buf)) - (for [i 0 len] + (loop [i :range [0 len]] (fiber.yield (get buf i)))) 0)) @@ -943,15 +975,13 @@ onvalue." (pp x)) (when f (def st (fiber.stack f)) - (def len (length st)) - (for [i 0 len] - (def { + (loop [{ :function func :tail tail :pc pc :c c :name name - } (get st i)) + } :in st] (file.write stdout " in") (when c (file.write stdout " cfunction")) (when name (file.write stdout (string " " name))) @@ -1067,7 +1097,8 @@ returned from compiling and running the file." (while k (def v (get newenv k)) (when (not (get v :private)) - (put env (symbol prefix k) v)) + (def newv (table.setproto @{:private true} v)) + (put env (symbol prefix k) newv)) (:= k (next newenv k)))) (defmacro import [path & args] diff --git a/src/compiler/compile.c b/src/compiler/compile.c index a7f2e5bd..c7f09657 100644 --- a/src/compiler/compile.c +++ b/src/compiler/compile.c @@ -830,7 +830,6 @@ recur: DstSignal status = dst_call(f, dst_tuple_length(tup) - 1, tup + 1, &x); dst_gcunlock(lock); if (status != DST_SIGNAL_OK) { - printf("Status: %d\n", status); const uint8_t *es = dst_formatc("error in macro expansion: %V", x); dstc_error(c, ast, es); } diff --git a/src/core/string.c b/src/core/string.c index de3ff201..043b62a9 100644 --- a/src/core/string.c +++ b/src/core/string.c @@ -885,6 +885,47 @@ static int cfun_checkset(DstArgs args) { DST_RETURN_TRUE(args); } +static int cfun_join(DstArgs args) { + const Dst *parts; + const uint8_t *joiner; + uint8_t *buf, *out; + int32_t joinerlen, partslen, finallen, i; + DST_MINARITY(args, 1); + DST_MAXARITY(args, 2); + DST_ARG_INDEXED(parts, partslen, args, 0); + if (args.n == 2) { + DST_ARG_BYTES(joiner, joinerlen, args, 1); + } else { + joiner = NULL; + joinerlen = 0; + } + /* Check args */ + finallen = 0; + for (i = 0; i < partslen; i++) { + const uint8_t *chunk; + int32_t chunklen = 0; + if (!dst_chararray_view(parts[i], &chunk, &chunklen)) { + DST_THROW(args, "expected string|symbol|buffer"); + } + if (i) finallen += joinerlen; + finallen += chunklen; + } + out = buf = dst_string_begin(finallen); + for (i = 0; i < partslen; i++) { + const uint8_t *chunk = NULL; + int32_t chunklen = 0; + if (i) { + memcpy(out, joiner, joinerlen); + out += joinerlen; + } + dst_chararray_view(parts[i], &chunk, &chunklen); + memcpy(out, chunk, chunklen); + out += chunklen; + } + DST_RETURN_STRING(args, dst_string_end(buf)); + +} + static const DstReg cfuns[] = { {"string.slice", cfun_slice}, {"string.repeat", cfun_repeat}, @@ -899,6 +940,7 @@ static const DstReg cfuns[] = { {"string.replace-all", cfun_replaceall}, {"string.split", cfun_split}, {"string.check-set", cfun_checkset}, + {"string.join", cfun_join}, {NULL, NULL} }; diff --git a/src/include/dst/dst.h b/src/include/dst/dst.h index 626326db..9f79ef55 100644 --- a/src/include/dst/dst.h +++ b/src/include/dst/dst.h @@ -274,6 +274,13 @@ int dst_typeabstract_err(DstArgs args, int32_t n, const DstAbstractType *at); }\ } while (0) +#define DST_ARG_INDEXED(DESTVALS, DESTLEN, A, N) do {\ + if ((A).n <= (N)) return dst_typemany_err(A, N, DST_TFLAG_INDEXED);\ + if (!dst_seq_view((A).v[(N)], &(DESTVALS), &(DESTLEN))) {\ + return dst_typemany_err(A, N, DST_TFLAG_INDEXED);\ + }\ +} while (0) + #define _DST_ARG(TYPE, NAME, DEST, A, N) do { \ DST_CHECK(A, N, TYPE);\ DEST = dst_unwrap_##NAME((A).v[(N)]); \