diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c9150e8..0dc68a30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ All notable changes to this project will be documented in this file. ## Unreleased +- Add `:range-to` and `:down-to` verbs in the `loop` macro. - Fix `and` and `or` macros returning nil instead of false in some cases. - Allow matching successfully against nil values in the `match` macro. - Improve `janet_formatc` and `janet_panicf` formatters to be more like `string/format`. diff --git a/src/boot/boot.janet b/src/boot/boot.janet index b87dfe6a..317d08ec 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -366,6 +366,16 @@ ,;body (set ,i (,delta ,i ,step)))))) +(defn- check-indexed [x] + (if (indexed? x) + x + (error (string "expected tuple for range, got " x)))) + +(defn- range-template + [binding object rest op comparison] + (let [[start stop step] (check-indexed object)] + (for-template binding start stop (or step 1) comparison op [rest]))) + (defn- each-template [binding inx body] (with-syms [k] @@ -399,11 +409,6 @@ (def ,binding ,i) ,body)))) -(defn- check-indexed [x] - (if (indexed? x) - x - (error (string "expected tuple for range, got " x)))) - (defn- loop1 [body head i] @@ -433,12 +438,12 @@ (def {(+ i 2) object} head) (let [rest (loop1 body head (+ i 3))] (case verb - :range (let [[start stop step] (check-indexed object)] - (for-template binding start stop (or step 1) < + [rest])) + :range (range-template binding object rest + <) + :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]) - :down (let [[start stop step] (check-indexed object)] - (for-template binding start stop (or step 1) > - [rest])) :in (each-template binding object [rest]) :iterate (iterate-template binding object rest) :generate (with-syms [f s] @@ -481,12 +486,14 @@ \t:iterate - repeatedly evaluate and bind to the expression while it is truthy.\n \t:range - loop over a range. The object should be two element tuple with a start and end value, and an optional positive step. The range is half open, [start, end).\n - \t:down - Same as range, but breaks the loop when the binding is less than or equal to end. - Step should still be a positive integer.\n - \t:keys - Iterate over the keys in a data structure.\n - \t:pairs - Iterate over the keys value pairs in a data structure.\n - \t:in - Iterate over the values in an indexed data structure or byte sequence.\n - \t:generate - Iterate over values yielded from a fiber. Can be paired with the generator + \t:range-to - same as :range, but the range is inclusive [start, end].\n + \t:down - loop over a range, stepping downwards. The object should be two element tuple + with a start and (exclusive) end value, and an optional (positive!) step size.\n + \t:down-to - same :as down, but the range is inclusive [start, end].\n + \t:keys - iterate over the keys in a data structure.\n + \t:pairs - iterate over the keys value pairs as tuples in a data structure.\n + \t:in - iterate over the values in a data structure.\n + \t:generate - iterate over values yielded from a fiber. Can be paired with the generator function for the producer/consumer pattern.\n\n loop also accepts conditionals to refine the looping further. Conditionals are of the form:\n\n @@ -510,6 +517,7 @@ (put _env 'iterate-template nil) (put _env 'each-template nil) (put _env 'keys-template nil) +(put _env 'range-template nil) (defmacro seq "Similar to loop, but accumulates the loop body into an array and returns that. diff --git a/test/suite1.janet b/test/suite1.janet index 3541f227..5b9f4f4f 100644 --- a/test/suite1.janet +++ b/test/suite1.janet @@ -212,13 +212,17 @@ (assert (= 7 (case :a :b 5 :c 6 :u 10 7)) "case with default") -# Testing the loop and for macros +# Testing the loop and seq macros (def xs (apply tuple (seq [x :range [0 10] :when (even? x)] (tuple (/ x 2) x)))) (assert (= xs '((0 0) (1 2) (2 4) (3 6) (4 8))) "seq macro 1") (def xs (apply tuple (seq [x :down [8 -2] :when (even? x)] (tuple (/ x 2) x)))) (assert (= xs '((4 8) (3 6) (2 4) (1 2) (0 0))) "seq macro 2") +# :range-to and :down-to +(assert (deep= (seq [x :range-to [0 10]] x) (seq [x :range [0 11]] x)) "loop :range-to") +(assert (deep= (seq [x :down-to [10 0]] x) (seq [x :down [10 -1]] x)) "loop :down-to") + # Some testing for not= (assert (not= 1 1 0) "not= 1") (assert (not= 0 1 1) "not= 2")