Add :range-to and :down-to to loop.

Fully inclusive ranges are generally useful and
do not complicate implementation much.
This commit is contained in:
Calvin Rose 2020-03-07 09:31:52 -06:00
parent e62f12426b
commit e0c9910d85
3 changed files with 29 additions and 16 deletions

View File

@ -2,6 +2,7 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## Unreleased ## 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. - Fix `and` and `or` macros returning nil instead of false in some cases.
- Allow matching successfully against nil values in the `match` macro. - Allow matching successfully against nil values in the `match` macro.
- Improve `janet_formatc` and `janet_panicf` formatters to be more like `string/format`. - Improve `janet_formatc` and `janet_panicf` formatters to be more like `string/format`.

View File

@ -366,6 +366,16 @@
,;body ,;body
(set ,i (,delta ,i ,step)))))) (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 (defn- each-template
[binding inx body] [binding inx body]
(with-syms [k] (with-syms [k]
@ -399,11 +409,6 @@
(def ,binding ,i) (def ,binding ,i)
,body)))) ,body))))
(defn- check-indexed [x]
(if (indexed? x)
x
(error (string "expected tuple for range, got " x))))
(defn- loop1 (defn- loop1
[body head i] [body head i]
@ -433,12 +438,12 @@
(def {(+ i 2) object} head) (def {(+ i 2) object} head)
(let [rest (loop1 body head (+ i 3))] (let [rest (loop1 body head (+ i 3))]
(case verb (case verb
:range (let [[start stop step] (check-indexed object)] :range (range-template binding object rest + <)
(for-template binding start stop (or step 1) < + [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]) :keys (keys-template binding object false [rest])
:pairs (keys-template binding object true [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]) :in (each-template binding object [rest])
:iterate (iterate-template binding object rest) :iterate (iterate-template binding object rest)
:generate (with-syms [f s] :generate (with-syms [f s]
@ -481,12 +486,14 @@
\t:iterate - repeatedly evaluate and bind to the expression while it is truthy.\n \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 \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 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. \t:range-to - same as :range, but the range is inclusive [start, end].\n
Step should still be a positive integer.\n \t:down - loop over a range, stepping downwards. The object should be two element tuple
\t:keys - Iterate over the keys in a data structure.\n with a start and (exclusive) end value, and an optional (positive!) step size.\n
\t:pairs - Iterate over the keys value pairs in a data structure.\n \t:down-to - same :as down, but the range is inclusive [start, end].\n
\t:in - Iterate over the values in an indexed data structure or byte sequence.\n \t:keys - iterate over the keys in a data structure.\n
\t:generate - Iterate over values yielded from a fiber. Can be paired with the generator \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 function for the producer/consumer pattern.\n\n
loop also accepts conditionals to refine the looping further. Conditionals are of loop also accepts conditionals to refine the looping further. Conditionals are of
the form:\n\n the form:\n\n
@ -510,6 +517,7 @@
(put _env 'iterate-template nil) (put _env 'iterate-template nil)
(put _env 'each-template nil) (put _env 'each-template nil)
(put _env 'keys-template nil) (put _env 'keys-template nil)
(put _env 'range-template nil)
(defmacro seq (defmacro seq
"Similar to loop, but accumulates the loop body into an array and returns that. "Similar to loop, but accumulates the loop body into an array and returns that.

View File

@ -212,13 +212,17 @@
(assert (= 7 (case :a :b 5 :c 6 :u 10 7)) "case with default") (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)))) (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") (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)))) (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") (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= # Some testing for not=
(assert (not= 1 1 0) "not= 1") (assert (not= 1 1 0) "not= 1")
(assert (not= 0 1 1) "not= 2") (assert (not= 0 1 1) "not= 2")