summaryrefslogtreecommitdiff
path: root/init.fnl
diff options
context:
space:
mode:
Diffstat (limited to 'init.fnl')
-rw-r--r--init.fnl2251
1 files changed, 0 insertions, 2251 deletions
diff --git a/init.fnl b/init.fnl
deleted file mode 100644
index 0753401..0000000
--- a/init.fnl
+++ /dev/null
@@ -1,2251 +0,0 @@
-(import-macros
- {: defn
- : defn-
- : ns
- : def
- : fn*
- : if-let
- : if-some
- : cond}
- (if ... (if (= ... :init) :init-macros ...) :init-macros))
-
-(ns core
- "MIT License
-
-Copyright (c) 2022 Andrey Listopadov
-
-Permission is hereby granted‚ free of charge‚ to any person obtaining a copy
-of this software and associated documentation files (the “Software”)‚ to deal
-in the Software without restriction‚ including without limitation the rights
-to use‚ copy‚ modify‚ merge‚ publish‚ distribute‚ sublicense‚ and/or sell
-copies of the Software‚ and to permit persons to whom the Software is
-furnished to do so‚ subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED “AS IS”‚ WITHOUT WARRANTY OF ANY KIND‚ EXPRESS OR
-IMPLIED‚ INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY‚
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM‚ DAMAGES OR OTHER
-LIABILITY‚ WHETHER IN AN ACTION OF CONTRACT‚ TORT OR OTHERWISE‚ ARISING FROM‚
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE."
- (:require [lazy-seq :as lazy]
- [itable :as itable]))
-
-;;; Utility functions
-
-(fn unpack* [x ...]
- (if (core.seq? x)
- (lazy.unpack x)
- (itable.unpack x ...)))
-
-(fn pack* [...]
- (doto [...] (tset :n (select "#" ...))))
-
-(fn pairs* [t]
- (match (getmetatable t)
- {:__pairs p} (p t)
- _ (pairs t)))
-
-(fn ipairs* [t]
- (match (getmetatable t)
- {:__ipairs i} (i t)
- _ (ipairs t)))
-
-(fn length* [t]
- (match (getmetatable t)
- {:__len l} (l t)
- _ (length t)))
-
-(defn apply
- "Apply `f` to the argument list formed by prepending intervening
-arguments to `args`, and `f` must support variadic amount of
-arguments.
-
-# Examples
-Applying `add` to different amount of arguments:
-
-``` fennel
-(assert-eq (apply add [1 2 3 4]) 10)
-(assert-eq (apply add 1 [2 3 4]) 10)
-(assert-eq (apply add 1 2 3 4 5 6 [7 8 9]) 45)
-```"
- ([f args] (f (unpack* args)))
- ([f a args] (f a (unpack* args)))
- ([f a b args] (f a b (unpack* args)))
- ([f a b c args] (f a b c (unpack* args)))
- ([f a b c d & args]
- (let [flat-args []
- len (- (length* args) 1)]
- (for [i 1 len]
- (tset flat-args i (. args i)))
- (each [i a (pairs* (. args (+ len 1)))]
- (tset flat-args (+ i len) a))
- (f a b c d (unpack* flat-args)))))
-
-(defn add
- "Sum arbitrary amount of numbers."
- ([] 0)
- ([a] a)
- ([a b] (+ a b))
- ([a b c] (+ a b c))
- ([a b c d] (+ a b c d))
- ([a b c d & rest] (apply add (+ a b c d) rest)))
-
-(defn sub
- "Subtract arbitrary amount of numbers."
- ([] 0)
- ([a] (- a))
- ([a b] (- a b))
- ([a b c] (- a b c))
- ([a b c d] (- a b c d))
- ([a b c d & rest] (apply sub (- a b c d) rest)))
-
-(defn mul
- "Multiply arbitrary amount of numbers."
- ([] 1)
- ([a] a)
- ([a b] (* a b))
- ([a b c] (* a b c))
- ([a b c d] (* a b c d))
- ([a b c d & rest] (apply mul (* a b c d) rest)))
-
-(defn div
- "Divide arbitrary amount of numbers."
- ([a] (/ 1 a))
- ([a b] (/ a b))
- ([a b c] (/ a b c))
- ([a b c d] (/ a b c d))
- ([a b c d & rest] (apply div (/ a b c d) rest)))
-
-(defn le
- "Returns true if nums are in monotonically non-decreasing order"
- ([a] true)
- ([a b] (<= a b))
- ([a b & [c d & more]]
- (if (<= a b)
- (if d (apply le b c d more)
- (<= b c))
- false)))
-
-(defn lt
- "Returns true if nums are in monotonically decreasing order"
- ([a] true)
- ([a b] (< a b))
- ([a b & [c d & more]]
- (if (< a b)
- (if d (apply lt b c d more)
- (< b c))
- false)))
-
-(defn ge
- "Returns true if nums are in monotonically non-increasing order"
- ([a] true)
- ([a b] (>= a b))
- ([a b & [c d & more]]
- (if (>= a b)
- (if d (apply ge b c d more)
- (>= b c))
- false)))
-
-(defn gt
- "Returns true if nums are in monotonically increasing order"
- ([a] true)
- ([a b] (> a b))
- ([a b & [c d & more]]
- (if (> a b)
- (if d (apply gt b c d more)
- (> b c))
- false)))
-
-(defn inc
- "Increase number `x` by one"
- [x]
- (+ x 1))
-
-(defn dec
- "Decrease number `x` by one"
- [x]
- (- x 1))
-
-(defn class
- "Return cljlib type of the `x`, or lua type."
- [x]
- (match (type x)
- :table (match (getmetatable x)
- {:cljlib/type t} t
- _ :table)
- t t))
-
-(defn constantly
- "Returns a function that takes any number of arguments and returns `x`."
- [x]
- (fn [] x))
-
-(defn complement
- "Takes a function `f` and returns the function that takes the same
-amount of arguments as `f`, has the same effect, and returns the
-opposite truth value."
- [f]
- (fn*
- ([] (not (f)))
- ([a] (not (f a)))
- ([a b] (not (f a b)))
- ([a b & cs] (not (apply f a b cs)))))
-
-(defn identity
- "Returns its argument."
- [x]
- x)
-
-(defn comp
- "Compose functions."
- ([] identity)
- ([f] f)
- ([f g]
- (fn*
- ([] (f (g)))
- ([x] (f (g x)))
- ([x y] (f (g x y)))
- ([x y z] (f (g x y z)))
- ([x y z & args] (f (apply g x y z args)))))
- ([f g & fs]
- (core.reduce comp (core.cons f (core.cons g fs)))))
-
-(defn eq
- "Comparison function.
-
-Accepts arbitrary amount of values, and does the deep comparison. If
-values implement `__eq` metamethod, tries to use it, by checking if
-first value is equal to second value, and the second value is equal to
-the first value. If values are not equal and are tables does the deep
-comparison. Tables as keys are supported."
- ([] true)
- ([_] true)
- ([a b]
- (if (and (= a b) (= b a))
- true
- (= :table (type a) (type b))
- (do (var (res count-a) (values true 0))
- (each [k v (pairs* a) :until (not res)]
- (set res (eq v (do (var (res done) (values nil nil))
- (each [k* v (pairs* b) :until done]
- (when (eq k* k)
- (set (res done) (values v true))))
- res)))
- (set count-a (+ count-a 1)))
- (when res
- (let [count-b (accumulate [res 0 _ _ (pairs* b)]
- (+ res 1))]
- (set res (= count-a count-b))))
- res)
- false))
- ([a b & cs]
- (and (eq a b) (apply eq b cs))))
-
-(fn deep-index [tbl key]
- "This function uses the `eq` function to compare keys of the given
-table `tbl` and the given `key`. Several other functions also reuse
-this indexing method, such as sets."
- (accumulate [res nil
- k v (pairs* tbl)
- :until res]
- (when (eq k key)
- v)))
-
-(fn deep-newindex [tbl key val]
- "This function uses the `eq` function to compare keys of the given
-table `tbl` and the given `key`. If the key is found it's being
-set, if not a new key is set."
- (var done false)
- (when (= :table (type key))
- (each [k _ (pairs* tbl) :until done]
- (when (eq k key)
- (rawset tbl k val)
- (set done true))))
- (when (not done)
- (rawset tbl key val)))
-
-(defn memoize
- "Returns a memoized version of a referentially transparent function.
-The memoized version of the function keeps a cache of the mapping from
-arguments to results and, when calls with the same arguments are
-repeated often, has higher performance at the expense of higher memory
-use."
- [f]
- (let [memo (setmetatable {} {:__index deep-index})]
- (fn* [& args]
- (match (. memo args)
- res (unpack* res 1 res.n)
- _ (let [res (pack* (f ...))]
- (tset memo args res)
- (unpack* res 1 res.n))))))
-
-(defn deref
- "Dereference an object."
- [x]
- (match (getmetatable x)
- {:cljlib/deref f} (f x)
- _ (error "object doesn't implement cljlib/deref metamethod" 2)))
-
-(defn empty
- "Get an empty variant of a given collection."
- [x]
- (match (getmetatable x)
- {:cljlib/empty f} (f)
- _ (match (type x)
- :table []
- :string ""
- _ (error (.. "don't know how to create empty variant of type " _)))))
-
-;;;Tests and predicates
-
-(defn nil?
- "Test if `x` is nil."
- ([] true)
- ([x] (= x nil)))
-
-(defn zero?
- "Test if `x` is equal to zero."
- [x]
- (= x 0))
-
-(defn pos?
- "Test if `x` is greater than zero."
- [x]
- (> x 0))
-
-(defn neg?
- "Test if `x` is less than zero."
- [x]
- (< x 0))
-
-(defn even?
- "Test if `x` is even."
- [x]
- (= (% x 2) 0))
-
-(defn odd?
- "Test if `x` is odd."
- [x]
- (not (even? x)))
-
-(defn string?
- "Test if `x` is a string."
- [x]
- (= (type x) :string))
-
-(defn boolean?
- "Test if `x` is a Boolean"
- [x]
- (= (type x) :boolean))
-
-(defn true?
- "Test if `x` is `true`"
- [x]
- (= x true))
-
-(defn false?
- "Test if `x` is `false`"
- [x]
- (= x false))
-
-(defn int?
- "Test if `x` is a number without floating point data.
-
-Number is rounded with `math.floor` and compared with original number."
- [x]
- (and (= (type x) :number)
- (= x (math.floor x))))
-
-(defn pos-int?
- "Test if `x` is a positive integer."
- [x]
- (and (int? x)
- (pos? x)))
-
-(defn neg-int?
- "Test if `x` is a negative integer."
- [x]
- (and (int? x)
- (neg? x)))
-
-(defn double?
- "Test if `x` is a number with floating point data."
- [x]
- (and (= (type x) :number)
- (not= x (math.floor x))))
-
-(defn empty?
- "Check if collection is empty."
- [x]
- (match (type x)
- :table
- (match (getmetatable x)
- {:cljlib/type :seq}
- (nil? (core.seq x))
- (where (or nil {:cljlib/type nil}))
- (let [(next*) (pairs* x)]
- (= (next* x) nil)))
- :string (= x "")
- :nil true
- _ (error "empty?: unsupported collection")))
-
-(defn not-empty
- "If `x` is empty, returns `nil`, otherwise `x`."
- [x]
- (if (not (empty? x))
- x))
-
-(defn map?
- "Check whether `x` is an associative table.
-
-Non-empty tables are tested by calling `next`. If the length of the
-table is greater than zero, the last integer key is passed to the
-`next`, and if `next` returns a key, the table is considered
-associative. If the length is zero, `next` is called with what `paris`
-returns for the table, and if the key is returned, table is considered
-associative.
-
-Empty tables can't be analyzed with this method, and `map?` will
-always return `false`. If you need this test pass for empty table,
-see `hash-map` for creating tables that have additional metadata
-attached for this test to work.
-
-# Examples
-Non-empty map:
-
-``` fennel
-(assert-is (map? {:a 1 :b 2}))
-```
-
-Empty tables don't pass the test:
-
-``` fennel
-(assert-not (map? {}))
-```
-
-Empty tables created with `hash-map` will pass the test:
-
-``` fennel
-(assert-is (map? (hash-map)))
-```"
- [x]
- (if (= :table (type x))
- (match (getmetatable x)
- {:cljlib/type :hash-map} true
- {:cljlib/type :sorted-map} true
- (where (or nil {:cljlib/type nil}))
- (let [len (length* x)
- (nxt t k) (pairs* x)]
- (not= nil (nxt t (if (= len 0) k len))))
- _ false)
- false))
-
-(defn vector?
- "Check whether `tbl` is a sequential table.
-
-Non-empty sequential tables are tested for two things:
-- `next` returns the key-value pair,
-- key, that is returned by the `next` is equal to `1`.
-
-Empty tables can't be analyzed with this method, and `vector?` will
-always return `false`. If you need this test pass for empty table,
-see `vector` for creating tables that have additional
-metadata attached for this test to work.
-
-# Examples
-Non-empty vector:
-
-``` fennel
-(assert-is (vector? [1 2 3 4]))
-```
-
-Empty tables don't pass the test:
-
-``` fennel
-(assert-not (vector? []))
-```
-
-Empty tables created with `vector` will pass the test:
-
-``` fennel
-(assert-is (vector? (vector)))
-```"
- [x]
- (if (= :table (type x))
- (match (getmetatable x)
- {:cljlib/type :vector} true
- (where (or nil {:cljlib/type nil}))
- (let [len (length* x)
- (nxt t k) (pairs* x)]
- (if (not= nil (nxt t (if (= len 0) k len))) false
- (> len 0) true
- false))
- _ false)
- false))
-
-(defn set?
- "Check if object is a set."
- [x]
- (match (getmetatable x)
- {:cljlib/type :hash-set} true
- _ false))
-
-(defn seq?
- "Check if object is a sequence."
- [x]
- (lazy.seq? x))
-
-(defn some?
- "Returns true if x is not nil, false otherwise."
- [x]
- (not= x nil))
-
-;;; Vector
-
-(fn vec->transient [immutable]
- (fn [vec]
- (var len (length vec))
- (->> {:__index (fn [_ i]
- (if (<= i len)
- (. vec i)))
- :__len #len
- :cljlib/type :transient
- :cljlib/conj #(error "can't `conj` onto transient vector, use `conj!`")
- :cljlib/assoc #(error "can't `assoc` onto transient vector, use `assoc!`")
- :cljlib/dissoc #(error "can't `dissoc` onto transient vector, use `dissoc!`")
- :cljlib/conj! (fn [tvec v]
- (set len (+ len 1))
- (doto tvec (tset len v)))
- :cljlib/assoc! (fn [tvec ...]
- (let [len (length tvec)]
- (for [i 1 (select "#" ...) 2]
- (let [(k v) (select i ...)]
- (if (<= 1 i len)
- (tset tvec i v)
- (error (.. "index " i " is out of bounds"))))))
- tvec)
- :cljlib/pop! (fn [tvec]
- (if (= len 0)
- (error "transient vector is empty" 2)
- (let [val (table.remove tvec)]
- (set len (- len 1))
- tvec)))
- :cljlib/dissoc! #(error "can't `dissoc!` with a transient vector")
- :cljlib/persistent! (fn [tvec]
- (let [v (fcollect [i 1 len] (. tvec i))]
- (while (> len 0)
- (table.remove tvec)
- (set len (- len 1)))
- (setmetatable tvec
- {:__index #(error "attempt to use transient after it was persistet")
- :__newindex #(error "attempt to use transient after it was persistet")})
- (immutable (itable v))))}
- (setmetatable {}))))
-
-(fn vec* [v len]
- (match (getmetatable v)
- mt (doto mt
- (tset :__len (constantly (or len (length* v))))
- (tset :cljlib/type :vector)
- (tset :cljlib/editable true)
- (tset :cljlib/conj
- (fn [t v]
- (let [len (length* t)]
- (vec* (itable.assoc t (+ len 1) v) (+ len 1)))))
- (tset :cljlib/pop
- (fn [t]
- (let [len (- (length* t) 1)
- coll []]
- (when (< len 0)
- (error "can't pop empty vector" 2))
- (for [i 1 len]
- (tset coll i (. t i)))
- (vec* (itable coll) len))))
- (tset :cljlib/empty
- (fn [] (vec* (itable []))))
- (tset :cljlib/transient (vec->transient vec*))
- (tset :__fennelview (fn [coll view inspector indent]
- (if (empty? coll)
- "[]"
- (let [lines (fcollect [i 1 (length* coll)]
- (.. " " (view (. coll i) inspector indent)))]
- (tset lines 1 (.. "[" (string.gsub (or (. lines 1) "") "^%s+" "")))
- (tset lines (length lines) (.. (. lines (length lines)) "]"))
- lines)))))
- nil (vec* (setmetatable v {})))
- v)
-
-(defn vec
- "Coerce collection `coll` to a vector."
- [coll]
- (cond (empty? coll) (vec* (itable []) 0)
- (vector? coll) (vec* (itable coll) (length* coll))
- :else (let [packed (-> coll core.seq lazy.pack)
- len packed.n]
- (-> packed
- (doto (tset :n nil))
- (itable {:fast-index? true})
- (vec* len)))))
-
-(defn vector
- "Constructs sequential table out of its arguments.
-
-Sets additional metadata for function `vector?` to work.
-
-# Examples
-
-``` fennel
-(def :private v (vector 1 2 3 4))
-(assert-eq v [1 2 3 4])
-```"
- [& args]
- (vec args))
-
-(defn nth
- "Returns the value at the `index`. `get` returns `nil` if `index` out
-of bounds, `nth` raises an error unless `not-found` is supplied.
-`nth` also works for strings and sequences."
- ([coll i]
- (if (vector? coll)
- (if (or (< i 1) (< (length* coll) i))
- (error (string.format "index %d is out of bounds" i))
- (. coll i))
- (string? coll)
- (nth (vec coll) i)
- (seq? coll)
- (nth (vec coll) i)
- :else
- (error "expected an indexed collection")))
- ([coll i not-found]
- (assert (int? i) "expected an integer key")
- (if (vector? coll)
- (or (. coll i) not-found)
- (string? coll)
- (nth (vec coll) i not-found)
- (seq? coll)
- (nth (vec coll) i not-found)
- :else
- (error "expected an indexed collection"))))
-
-;;; Sequences
-
-(defn- seq*
- "Add cljlib sequence meta-info."
- [x]
- (match (getmetatable x)
- mt (doto mt
- (tset :cljlib/type :seq)
- (tset :cljlib/conj
- (fn [s v] (core.cons v s)))
- (tset :cljlib/empty #(core.list))))
- x)
-
-(defn seq
- "Construct a sequence from the given collection `coll`. If `coll` is
-an associative table, returns sequence of vectors with key and value.
-If `col` is sequential table, returns its shallow copy. If `col` is
-string, return sequential table of its codepoints.
-
-# Examples
-Sequential tables are transformed to sequences:
-
-``` fennel
-(seq [1 2 3 4]) ;; @seq(1 2 3 4)
-```
-
-Associative tables are transformed to format like this `[[key1 value1]
-... [keyN valueN]]` and order is non-deterministic:
-
-``` fennel
-(seq {:a 1 :b 2 :c 3}) ;; @seq([:b 2] [:a 1] [:c 3])
-```"
- [coll]
- (seq* (match (getmetatable coll)
- {:cljlib/seq f} (f coll)
- _ (cond (lazy.seq? coll) (lazy.seq coll)
- (map? coll) (lazy.map vec coll)
- :else (lazy.seq coll)))))
-
-(defn rseq
- "Returns, in possibly-constant time, a seq of the items in `rev` in reverse order.
-Input must be traversable with `ipairs`. Doesn't work in constant
-time if `rev` implements a linear-time `__len` metamethod, or invoking
-Lua `#` operator on `rev` takes linar time. If `t` is empty returns
-`nil`.
-
-# Examples
-
-``` fennel
-(def :private v [1 2 3])
-(def :private r (rseq v))
-
-(assert-eq (reverse v) r)
-```"
- [rev]
- (seq* (lazy.rseq rev)))
-
-(defn lazy-seq
- "Create lazy sequence from the result of calling a function `f`.
-Delays execution of `f` until sequence is consumed. `f` must return a
-sequence or a vector."
- [f]
- (seq* (lazy.lazy-seq f)))
-
-(defn first
- "Return first element of a `coll`. Calls `seq` on its argument."
- [coll]
- (lazy.first (seq coll)))
-
-(defn rest
- "Returns a sequence of all elements of a `coll` but the first one.
-Calls `seq` on its argument."
- [coll]
- (seq* (lazy.rest (seq coll))))
-
-(defn- next*
- "Return the tail of a sequence.
-
-If the sequence is empty, returns nil."
- [s]
- (seq* (lazy.next s)))
-
-(doto core (tset :next next*)) ; luajit doesn't like next redefinition
-
-(defn count
- "Count amount of elements in the sequence."
- [s]
- (match (getmetatable s)
- {:cljlib/type :vector} (length* s)
- _ (lazy.count s)))
-
-(defn cons
- "Construct a cons cell.
-Prepends new `head` to a `tail`, which must be either a table,
-sequence, or nil.
-
-# Examples
-
-``` fennel
-(assert-eq [0 1] (cons 0 [1]))
-(assert-eq (list 0 1 2 3) (cons 0 (cons 1 (list 2 3))))
-```"
- [head tail]
- (seq* (lazy.cons head tail)))
-
-(fn list
- [...]
- "Create eager sequence of provided values.
-
-# Examples
-
-``` fennel
-(local l (list 1 2 3 4 5))
-(assert-eq [1 2 3 4 5] l)
-```"
- (seq* (lazy.list ...)))
-
-(set core.list list)
-
-(defn list*
- "Creates a new sequence containing the items prepended to the rest,
-the last of which will be treated as a sequence.
-
-# Examples
-
-``` fennel
-(local l (list* 1 2 3 [4 5]))
-(assert-eq [1 2 3 4 5] l)
-```"
- [& args]
- (seq* (apply lazy.list* args)))
-
-(defn last
- "Returns the last element of a `coll`. Calls `seq` on its argument."
- [coll]
- (match (next* coll)
- coll* (last coll*)
- _ (first coll)))
-
-(defn butlast
- "Returns everything but the last element of the `coll` as a new
- sequence. Calls `seq` on its argument."
- [coll]
- (seq (lazy.drop-last coll)))
-
-(defn map
- "Returns a lazy sequence consisting of the result of applying `f` to
-the set of first items of each `coll`, followed by applying `f` to the
-set of second items in each `coll`, until any one of the `colls` is
-exhausted. Any remaining items in other `colls` are ignored. Function
-`f` should accept number-of-colls arguments. Returns a transducer when
-no collection is provided.
-
-# Examples
-
-``` fennel
-(map #(+ $ 1) [1 2 3]) ;; => @seq(2 3 4)
-(map #(+ $1 $2) [1 2 3] [4 5 6]) ;; => @seq(5 7 9)
-(def :private res (map #(+ $ 1) [:a :b :c])) ;; will raise an error only when realized
-```"
- ([f]
- (fn* [rf]
- (fn*
- ([] (rf))
- ([result] (rf result))
- ([result input]
- (rf result (f input)))
- ([result input & inputs]
- (rf result (apply f input inputs))))))
- ([f coll]
- (seq* (lazy.map f coll)))
- ([f coll & colls]
- (seq* (apply lazy.map f coll colls))))
-
-(defn mapv
- "Returns a vector consisting of the result of applying `f` to the
-set of first items of each `coll`, followed by applying `f` to the set
-of second items in each coll, until any one of the `colls` is
-exhausted. Any remaining items in other collections are ignored.
-Function `f` should accept number-of-colls arguments."
- ([f coll]
- (->> coll
- (core.transduce (map f)
- core.conj!
- (core.transient (vector)))
- core.persistent!))
- ([f coll & colls] (vec (apply map f coll colls))))
-
-(defn map-indexed
- "Returns a lazy sequence consisting of the result of applying `f` to 1
-and the first item of `coll`, followed by applying `f` to 2 and the
-second item in `coll`, etc., until `coll` is exhausted. Returns a
-transducer when no collection is provided."
- ([f]
- (fn* [rf]
- (var i -1)
- (fn*
- ([] (rf))
- ([result] (rf result))
- ([result input]
- (set i (+ i 1))
- (rf result (f i input))))))
- ([f coll]
- (seq* (lazy.map-indexed f coll))))
-
-(defn mapcat
- "Apply `concat` to the result of calling `map` with `f` and
-collections `colls`. Returns a transducer when no collection is
-provided."
- ([f]
- (comp (map f) core.cat))
- ([f & colls]
- (seq* (apply lazy.mapcat f colls))))
-
-(defn filter
- "Returns a lazy sequence of the items in `coll` for which
-`pred` returns logical true. Returns a transducer when no collection
-is provided."
- ([pred]
- (fn* [rf]
- (fn*
- ([] (rf))
- ([result] (rf result))
- ([result input]
- (if (pred input)
- (rf result input)
- result)))))
- ([pred coll]
- (seq* (lazy.filter pred coll))))
-
-(defn filterv
- "Returns a vector of the items in `coll` for which
-`pred` returns logical true."
- [pred coll]
- (vec (filter pred coll)))
-
-(defn every?
- "Test if every item in `coll` satisfies the `pred`."
- [pred coll]
- (lazy.every? pred coll))
-
-(defn some
- "Test if any item in `coll` satisfies the `pred`."
- [pred coll]
- (lazy.some? pred coll))
-
-(defn not-any?
- "Test if no item in `coll` satisfy the `pred`."
- [pred coll]
- (some #(not (pred $)) coll))
-
-(defn range
- "Returns lazy sequence of numbers from `lower` to `upper` with optional
-`step`."
- ([] (seq* (lazy.range)))
- ([upper] (seq* (lazy.range upper)))
- ([lower upper] (seq* (lazy.range lower upper)))
- ([lower upper step] (seq* (lazy.range lower upper step))))
-
-(defn concat
- "Return a lazy sequence of concatenated `colls`."
- [& colls]
- (seq* (apply lazy.concat colls)))
-
-(defn reverse
- "Returns a lazy sequence with same items as in `coll` but in reverse order."
- [coll]
- (seq* (lazy.reverse coll)))
-
-(defn take
- "Returns a lazy sequence of the first `n` items in `coll`, or all items if
-there are fewer than `n`."
- ([n]
- (fn* [rf]
- (var n n)
- (fn*
- ([] (rf))
- ([result] (rf result))
- ([result input]
- (let [result (if (< 0 n)
- (rf result input)
- result)]
- (set n (- n 1))
- (if (not (< 0 n))
- (core.ensure-reduced result)
- result))))))
- ([n coll]
- (seq* (lazy.take n coll))))
-
-(defn take-while
- "Take the elements from the collection `coll` until `pred` returns logical
-false for any of the elemnts. Returns a lazy sequence. Returns a
-transducer when no collection is provided."
- ([pred]
- (fn* [rf]
- (fn*
- ([] (rf))
- ([result] (rf result))
- ([result input]
- (if (pred input)
- (rf result input)
- (core.reduced result))))))
- ([pred coll]
- (seq* (lazy.take-while pred coll))))
-
-(defn drop
- "Drop `n` elements from collection `coll`, returning a lazy sequence
-of remaining elements. Returns a transducer when no collection is
-provided."
- ([n]
- (fn* [rf]
- (var nv n)
- (fn*
- ([] (rf))
- ([result] (rf result))
- ([result input]
- (let [n nv]
- (set nv (- nv 1))
- (if (pos? n)
- result
- (rf result input)))))))
- ([n coll]
- (seq* (lazy.drop n coll))))
-
-(defn drop-while
- "Drop the elements from the collection `coll` until `pred` returns logical
-false for any of the elemnts. Returns a lazy sequence. Returns a
-transducer when no collection is provided."
- ([pred]
- (fn* [rf]
- (var dv true)
- (fn*
- ([] (rf))
- ([result] (rf result))
- ([result input]
- (let [drop? dv]
- (if (and drop? (pred input))
- result
- (do
- (set dv nil)
- (rf result input))))))))
- ([pred coll]
- (seq* (lazy.drop-while pred coll))))
-
-(defn drop-last
- "Return a lazy sequence from `coll` without last `n` elements."
- ([] (seq* (lazy.drop-last)))
- ([coll] (seq* (lazy.drop-last coll)))
- ([n coll] (seq* (lazy.drop-last n coll))))
-
-(defn take-last
- "Return a sequence of last `n` elements of the `coll`."
- [n coll]
- (seq* (lazy.take-last n coll)))
-
-(defn take-nth
- "Return a lazy sequence of every `n` item in `coll`. Returns a
-transducer when no collection is provided."
- ([n]
- (fn* [rf]
- (var iv -1)
- (fn*
- ([] (rf))
- ([result] (rf result))
- ([result input]
- (set iv (+ iv 1))
- (if (= 0 (% iv n))
- (rf result input)
- result)))))
- ([n coll]
- (seq* (lazy.take-nth n coll))))
-
-(defn split-at
- "Return a table with sequence `coll` being split at `n`"
- [n coll]
- (vec (lazy.split-at n coll)))
-
-(defn split-with
- "Return a table with sequence `coll` being split with `pred`"
- [pred coll]
- (vec (lazy.split-with pred coll)))
-
-(defn nthrest
- "Returns the nth rest of `coll`, `coll` when `n` is 0.
-
-# Examples
-
-``` fennel
-(assert-eq (nthrest [1 2 3 4] 3) [4])
-(assert-eq (nthrest [1 2 3 4] 2) [3 4])
-(assert-eq (nthrest [1 2 3 4] 1) [2 3 4])
-(assert-eq (nthrest [1 2 3 4] 0) [1 2 3 4])
-```
-"
- [coll n]
- (seq* (lazy.nthrest coll n)))
-
-(defn nthnext
- "Returns the nth next of `coll`, (seq coll) when `n` is 0."
- [coll n]
- (lazy.nthnext coll n))
-
-(defn keep
- "Returns a lazy sequence of the non-nil results of calling `f` on the
-items of the `coll`. Returns a transducer when no collection is
-provided."
- ([f]
- (fn* [rf]
- (fn*
- ([] (rf))
- ([result] (rf result))
- ([result input]
- (let [v (f input)]
- (if (nil? v)
- result
- (rf result v)))))))
- ([f coll]
- (seq* (lazy.keep f coll))))
-
-(defn keep-indexed
- "Returns a lazy sequence of the non-nil results of (f index item) in
-the `coll`. Note, this means false return values will be included.
-`f` must be free of side effects. Returns a transducer when no
-collection is provided."
- ([f]
- (fn* [rf]
- (var iv -1)
- (fn*
- ([] (rf))
- ([result] (rf result))
- ([result input]
- (set iv (+ iv 1))
- (let [v (f iv input)]
- (if (nil? v)
- result
- (rf result v)))))))
- ([f coll]
- (seq* (lazy.keep-indexed f coll))))
-
-(defn partition
- "Given a collection `coll`, returns a lazy sequence of lists of `n`
-items each, at offsets `step` apart. If `step` is not supplied,
-defaults to `n`, i.e. the partitions do not overlap. If a `pad`
-collection is supplied, use its elements as necessary to complete last
-partition up to `n` items. In case there are not enough padding
-elements, return a partition with less than `n` items."
- ([n coll] (map seq* (lazy.partition n coll)))
- ([n step coll] (map seq* (lazy.partition n step coll)))
- ([n step pad coll] (map seq* (lazy.partition n step pad coll))))
-
-(fn array []
- (var len 0)
- (->> {:__len #len
- :__index {:clear (fn [self]
- (while (not= 0 len)
- (tset self len nil)
- (set len (- len 1))
- self))
- :add (fn [self val]
- (set len (+ len 1))
- (tset self len val)
- self)}}
- (setmetatable [])))
-
-(defn partition-by
- "Applies `f` to each value in `coll`, splitting it each time `f`
-returns a new value. Returns a lazy seq of partitions. Returns a
-transducer, if collection is not supplied."
- ([f]
- (fn* [rf]
- (let [a (array)
- none {}]
- (var pv none)
- (fn*
- ([] (rf))
- ([result]
- (rf (if (empty? a)
- result
- (let [v (vec a)]
- (a:clear)
- (core.unreduced (rf result v))))))
- ([result input]
- (let [pval pv
- val (f input)]
- (set pv val)
- (if (or (= pval none)
- (= val pval))
- (do
- (a:add input)
- result)
- (let [v (vec a)]
- (a:clear)
- (let [ret (rf result v)]
- (when (not (core.reduced? ret))
- (a:add input))
- ret)))))))))
- ([f coll]
- (map seq* (lazy.partition-by f coll))))
-
-(defn partition-all
- "Given a collection `coll`, returns a lazy sequence of lists like
-`partition`, but may include partitions with fewer than n items at the
-end. Accepts addiitonal `step` argument, similarly to `partition`.
-Returns a transducer, if collection is not supplied."
- ([n]
- (fn* [rf]
- (let [a (array)]
- (fn*
- ([] (rf))
- ([result]
- (rf (if (= 0 (length a))
- result
- (let [v (vec a)]
- (a:clear)
- (core.unreduced (rf result v))))))
- ([result input]
- (a:add input)
- (if (= n (length a))
- (let [v (vec a)]
- (a:clear)
- (rf result v))
- result))))))
- ([n coll]
- (map seq* (lazy.partition-all n coll)))
- ([n step coll]
- (map seq* (lazy.partition-all n step coll))))
-
-(defn reductions
- "Returns a lazy seq of the intermediate values of the reduction (as
-per reduce) of `coll` by `f`, starting with `init`."
- ([f coll] (seq* (lazy.reductions f coll)))
- ([f init coll] (seq* (lazy.reductions f init coll))))
-
-(defn contains?
- "Test if `elt` is in the `coll`. It may be a linear search depending
-on the type of the collection."
- [coll elt]
- (lazy.contains? coll elt))
-
-(defn distinct
- "Returns a lazy sequence of the elements of the `coll` without
-duplicates. Comparison is done by equality. Returns a transducer when
-no collection is provided."
- ([]
- (fn* [rf]
- (let [seen (setmetatable {} {:__index deep-index})]
- (fn*
- ([] (rf))
- ([result] (rf result))
- ([result input]
- (if (. seen input)
- result
- (do
- (tset seen input true)
- (rf result input))))))))
- ([coll]
- (seq* (lazy.distinct coll))))
-
-(defn dedupe
- "Returns a lazy sequence removing consecutive duplicates in coll.
-Returns a transducer when no collection is provided."
- ([]
- (fn* [rf]
- (let [none {}]
- (var pv none)
- (fn*
- ([] (rf))
- ([result] (rf result))
- ([result input]
- (let [prior pv]
- (set pv input)
- (if (= prior input)
- result
- (rf result input))))))))
- ([coll] (core.sequence (dedupe) coll)))
-
-(defn random-sample
- "Returns items from `coll` with random probability of `prob` (0.0 -
-1.0). Returns a transducer when no collection is provided."
- ([prob]
- (filter (fn [] (< (math.random) prob))))
- ([prob coll]
- (filter (fn [] (< (math.random) prob)) coll)))
-
-(defn doall
- "Realize whole lazy sequence `seq`.
-
-Walks whole sequence, realizing each cell. Use at your own risk on
-infinite sequences."
- [seq]
- (seq* (lazy.doall seq)))
-
-(defn dorun
- "Realize whole sequence `seq` for side effects.
-
-Walks whole sequence, realizing each cell. Use at your own risk on
-infinite sequences."
- [seq]
- (lazy.dorun seq))
-
-(defn line-seq
- "Accepts a `file` handle, and creates a lazy sequence of lines using
-`lines` metamethod.
-
-# Examples
-
-Lazy sequence of file lines may seem similar to an iterator over a
-file, but the main difference is that sequence can be shared onve
-realized, and iterator can't. Lazy sequence can be consumed in
-iterator style with the `doseq` macro.
-
-Bear in mind, that since the sequence is lazy it should be realized or
-truncated before the file is closed:
-
-``` fennel
-(let [lines (with-open [f (io.open \"init.fnl\" :r)]
- (line-seq f))]
- ;; this will error because only first line was realized, but the
- ;; file was closed before the rest of lines were cached
- (assert-not (pcall next lines)))
-```
-
-Sequence is realized with `doall` before file was closed and can be shared:
-
-``` fennel
-(let [lines (with-open [f (io.open \"init.fnl\" :r)]
- (doall (line-seq f)))]
- (assert-is (pcall next lines)))
-```
-
-Infinite files can't be fully realized, but can be partially realized
-with `take`:
-
-``` fennel
-(let [lines (with-open [f (io.open \"/dev/urandom\" :r)]
- (doall (take 3 (line-seq f))))]
- (assert-is (pcall next lines)))
-```"
- [file]
- (seq* (lazy.line-seq file)))
-
-(defn iterate
- "Returns an infinete lazy sequence of x, (f x), (f (f x)) etc."
- [f x]
- (seq* (lazy.iterate f x)))
-
-(defn remove
- "Returns a lazy sequence of the items in the `coll` without elements
-for wich `pred` returns logical true. Returns a transducer when no
-collection is provided."
- ([pred]
- (filter (complement pred)))
- ([pred coll]
- (seq* (lazy.remove pred coll))))
-
-(defn cycle
- "Create a lazy infinite sequence of repetitions of the items in the
-`coll`."
- [coll]
- (seq* (lazy.cycle coll)))
-
-(defn repeat
- "Takes a value `x` and returns an infinite lazy sequence of this value.
-
-# Examples
-
-``` fennel
-(assert-eq 20 (reduce add (take 10 (repeat 2))))
-```"
- [x]
- (seq* (lazy.repeat x)))
-
-(defn repeatedly
- "Takes a function `f` and returns an infinite lazy sequence of
-function applications. Rest arguments are passed to the function."
- [f & args]
- (seq* (apply lazy.repeatedly f args)))
-
-(defn tree-seq
- "Returns a lazy sequence of the nodes in a tree, via a depth-first walk.
-
-`branch?` must be a function of one arg that returns true if passed a
-node that can have children (but may not). `children` must be a
-function of one arg that returns a sequence of the children. Will
-only be called on nodes for which `branch?` returns true. `root` is
-the root node of the tree.
-
-# Examples
-
-For the given tree `[\"A\" [\"B\" [\"D\"] [\"E\"]] [\"C\" [\"F\"]]]`:
-
- A
- / \\
- B C
- / \\ \\
- D E F
-
-Calling `tree-seq` with `next` as the `branch?` and `rest` as the
-`children` returns a flat representation of a tree:
-
-``` fennel
-(assert-eq (map first (tree-seq next rest [\"A\" [\"B\" [\"D\"] [\"E\"]] [\"C\" [\"F\"]]]))
- [\"A\" \"B\" \"D\" \"E\" \"C\" \"F\"])
-```"
- [branch? children root]
- (seq* (lazy.tree-seq branch? children root)))
-
-(defn interleave
- "Returns a lazy sequence of the first item in each sequence, then the
-second one, until any sequence exhausts."
- ([] (seq* (lazy.interleave)))
- ([s] (seq* (lazy.interleave s)))
- ([s1 s2] (seq* (lazy.interleave s1 s2)))
- ([s1 s2 & ss] (seq* (apply lazy.interleave s1 s2 ss))))
-
-(defn interpose
- "Returns a lazy sequence of the elements of `coll` separated by
-`separator`. Returns a transducer when no collection is provided."
- ([sep]
- (fn* [rf]
- (var started false)
- (fn*
- ([] (rf))
- ([result] (rf result))
- ([result input]
- (if started
- (let [sepr (rf result sep)]
- (if (core.reduced? sepr)
- sepr
- (rf sepr input)))
- (do
- (set started true)
- (rf result input)))))))
- ([separator coll]
- (seq* (lazy.interpose separator coll))))
-
-(defn halt-when
- "Returns a transducer that ends transduction when `pred` returns `true`
-for an input. When `retf` is supplied it must be a `fn` of 2 arguments
-- it will be passed the (completed) result so far and the input that
-triggered the predicate, and its return value (if it does not throw an
-exception) will be the return value of the transducer. If `retf` is
-not supplied, the input that triggered the predicate will be
-returned. If the predicate never returns `true` the transduction is
-unaffected."
- ([pred]
- (halt-when pred nil))
- ([pred retf]
- (fn* [rf]
- (let [halt (setmetatable {} {:__fennelview #"#<halt>"})]
- (fn*
- ([] (rf))
- ([result]
- (if (and (map? result) (contains? result halt))
- result.value
- (rf result)))
- ([result input]
- (if (pred input)
- (core.reduced {halt true :value (if retf (retf (rf result) input) input)})
- (rf result input))))))))
-
-(defn realized?
- "Check if sequence's first element is realized."
- [s]
- (lazy.realized? s))
-
-(defn keys
- "Returns a sequence of the map's keys, in the same order as `seq`."
- [coll]
- (assert (or (map? coll) (empty? coll)) "expected a map")
- (if (empty? coll)
- (lazy.list)
- (lazy.keys coll)))
-
-(defn vals
- "Returns a sequence of the table's values, in the same order as `seq`."
- [coll]
- (assert (or (map? coll) (empty? coll)) "expected a map")
- (if (empty? coll)
- (lazy.list)
- (lazy.vals coll)))
-
-(defn find
- "Returns the map entry for `key`, or `nil` if key is not present in
-`coll`."
- [coll key]
- (assert (or (map? coll) (empty? coll)) "expected a map")
- (match (. coll key)
- v [key v]))
-
-(defn sort
- "Returns a sorted sequence of the items in `coll`. If no `comparator`
-is supplied, uses `<`."
- ([coll]
- (match (seq coll)
- s (seq (itable.sort (vec s)))
- _ (list)))
- ([comparator coll]
- (match (seq coll)
- s (seq (itable.sort (vec s) comparator))
- _ (list))))
-
-;;; Reduce
-
-(defn reduce
- "`f` should be a function of 2 arguments. If `val` is not supplied,
-returns the result of applying `f` to the first 2 items in `coll`,
-then applying `f` to that result and the 3rd item, etc. If `coll`
-contains no items, f must accept no arguments as well, and reduce
-returns the result of calling `f` with no arguments. If `coll` has
-only 1 item, it is returned and `f` is not called. If `val` is
-supplied, returns the result of applying `f` to `val` and the first
-item in `coll`, then applying `f` to that result and the 2nd item,
-etc. If `coll` contains no items, returns `val` and `f` is not
-called. Early termination is supported via `reduced`.
-
-# Examples
-
-``` fennel
-(defn- add
- ([] 0)
- ([a] a)
- ([a b] (+ a b))
- ([a b & cs] (apply add (+ a b) cs)))
-;; no initial value
-(assert-eq 10 (reduce add [1 2 3 4]))
-;; initial value
-(assert-eq 10 (reduce add 1 [2 3 4]))
-;; empty collection - function is called with 0 args
-(assert-eq 0 (reduce add []))
-(assert-eq 10.3 (reduce math.floor 10.3 []))
-;; collection with a single element doesn't call a function unless the
-;; initial value is supplied
-(assert-eq 10.3 (reduce math.floor [10.3]))
-(assert-eq 7 (reduce add 3 [4]))
-```"
- ([f coll] (lazy.reduce f (seq coll)))
- ([f val coll] (lazy.reduce f val (seq coll))))
-
-(defn reduced
- "Terminates the `reduce` early with a given `value`.
-
-# Examples
-
-``` fennel
-(assert-eq :NaN
- (reduce (fn [acc x]
- (if (not= :number (type x))
- (reduced :NaN)
- (+ acc x)))
- [1 2 :3 4 5]))
-```"
- [value]
- (doto (lazy.reduced value)
- (-> getmetatable (tset :cljlib/deref #($:unbox)))))
-
-(defn reduced?
- "Returns true if `x` is the result of a call to reduced"
- [x]
- (lazy.reduced? x))
-
-(defn unreduced
- "If `x` is `reduced?`, returns `(deref x)`, else returns `x`."
- [x]
- (if (reduced? x) (deref x) x))
-
-(defn ensure-reduced
- "If x is already reduced?, returns it, else returns (reduced x)"
- [x]
- (if (reduced? x)
- x
- (reduced x)))
-
-(defn- preserving-reduced [rf]
- (fn* [a b]
- (let [ret (rf a b)]
- (if (reduced? ret)
- (reduced ret)
- ret))))
-
-(defn cat
- "A transducer which concatenates the contents of each input, which must be a
- collection, into the reduction. Accepts the reducing function `rf`."
- [rf]
- (let [rrf (preserving-reduced rf)]
- (fn*
- ([] (rf))
- ([result] (rf result))
- ([result input]
- (reduce rrf result input)))))
-
-(defn reduce-kv
- "Reduces an associative table using function `f` and initial value `val`.
-
-`f` should be a function of 3 arguments. Returns the result of
-applying `f` to `val`, the first key and the first value in `tbl`,
-then applying `f` to that result and the 2nd key and value, etc. If
-`tbl` contains no entries, returns `val` and `f` is not called. Note
-that `reduce-kv` is supported on sequential tables and strings, where
-the keys will be the ordinals.
-
-Early termination is possible with the use of `reduced`
-function.
-
-# Examples
-Reduce associative table by adding values from all keys:
-
-``` fennel
-(local t {:a1 1
- :b1 2
- :a2 2
- :b2 3})
-
-(reduce-kv #(+ $1 $3) 0 t)
-;; => 8
-```
-
-Reduce table by adding values from keys that start with letter `a`:
-
-``` fennel
-(local t {:a1 1
- :b1 2
- :a2 2
- :b2 3})
-
-(reduce-kv (fn [res k v] (if (= (string.sub k 1 1) :a) (+ res v) res))
- 0 t)
-;; => 3
-```"
- [f val s]
- (if (map? s)
- (reduce (fn [res [k v]] (f res k v)) val (seq s))
- (reduce (fn [res [k v]] (f res k v)) val (map vector (drop 1 (range)) (seq s)))))
-
-(defn completing
- "Takes a reducing function `f` of 2 args and returns a function
-suitable for transduce by adding an arity-1 signature that calls
-`cf` (default - `identity`) on the result argument."
- ([f] (completing f identity))
- ([f cf]
- (fn*
- ([] (f))
- ([x] (cf x))
- ([x y] (f x y)))))
-
-(defn transduce
- "`reduce` with a transformation of `f` (`xform`). If `init` is not
-supplied, `f` will be called to produce it. `f` should be a reducing
-step function that accepts both 1 and 2 arguments, if it accepts only
-2 you can add the arity-1 with `completing`. Returns the result of
-applying (the transformed) `xform` to `init` and the first item in
-`coll`, then applying `xform` to that result and the 2nd item, etc. If
-`coll` contains no items, returns `init` and `f` is not called. Note
-that certain transforms may inject or skip items."
- ([xform f coll] (transduce xform f (f) coll))
- ([xform f init coll]
- (let [f (xform f)]
- (f (reduce f init (seq coll))))))
-
-(defn sequence
- "Coerces coll to a (possibly empty) sequence, if it is not already
-one. Will not force a lazy seq. `(sequence nil)` yields an empty list,
-When a transducer `xform` is supplied, returns a lazy sequence of
-applications of the transform to the items in `coll`, i.e. to the set
-of first items of each `coll`, followed by the set of second items in
-each `coll`, until any one of the `colls` is exhausted. Any remaining
-items in other `colls` are ignored. The transform should accept
-number-of-colls arguments"
- ([coll]
- (if (seq? coll) coll
- (or (seq coll) (list))))
- ([xform coll]
- (let [f (xform (completing #(cons $2 $1)))]
- (or ((fn step [coll]
- (if-some [s (seq coll)]
- (let [res (f nil (first s))]
- (cond (reduced? res) (f (deref res))
- (seq? res) (concat res (lazy-seq #(step (rest s))))
- :else (step (rest s))))
- (f nil)))
- coll)
- (list))))
- ([xform coll & colls]
- (let [f (xform (completing #(cons $2 $1)))]
- (or ((fn step [colls]
- (if (every? seq colls)
- (let [res (apply f nil (map first colls))]
- (cond (reduced? res) (f (deref res))
- (seq? res) (concat res (lazy-seq #(step (map rest colls))))
- :else (step (map rest colls))))
- (f nil)))
- (cons coll colls))
- (list)))))
-
-;;; Hash map
-
-(fn map->transient [immutable]
- (fn [map]
- (let [removed (setmetatable {} {:__index deep-index})]
- (->> {:__index (fn [_ k]
- (if (not (. removed k))
- (. map k)))
- :cljlib/type :transient
- :cljlib/conj #(error "can't `conj` onto transient map, use `conj!`")
- :cljlib/assoc #(error "can't `assoc` onto transient map, use `assoc!`")
- :cljlib/dissoc #(error "can't `dissoc` onto transient map, use `dissoc!`")
- :cljlib/conj! (fn [tmap [k v]]
- (if (= nil v)
- (tset removed k true)
- (tset removed k nil))
- (doto tmap (tset k v)))
- :cljlib/assoc! (fn [tmap ...]
- (for [i 1 (select "#" ...) 2]
- (let [(k v) (select i ...)]
- (tset tmap k v)
- (if (= nil v)
- (tset removed k true)
- (tset removed k nil))))
- tmap)
- :cljlib/dissoc! (fn [tmap ...]
- (for [i 1 (select "#" ...)]
- (let [k (select i ...)]
- (tset tmap k nil)
- (tset removed k true)))
- tmap)
- :cljlib/persistent! (fn [tmap]
- (let [t (collect [k v (pairs tmap)
- :into (collect [k v (pairs map)]
- (values k v))]
- (values k v))]
- (each [k (pairs removed)]
- (tset t k nil))
- (each [_ k (ipairs (icollect [k (pairs* tmap)] k))]
- (tset tmap k nil))
- (setmetatable tmap
- {:__index #(error "attempt to use transient after it was persistet")
- :__newindex #(error "attempt to use transient after it was persistet")})
- (immutable (itable t))))}
- (setmetatable {})))))
-
-(fn hash-map* [x]
- "Add cljlib hash-map meta-info."
- (match (getmetatable x)
- mt (doto mt
- (tset :cljlib/type :hash-map)
- (tset :cljlib/editable true)
- (tset :cljlib/conj
- (fn [t [k v] ...]
- (apply core.assoc
- t k v
- (accumulate [kvs [] _ [k v] (ipairs* [...])]
- (doto kvs
- (table.insert k)
- (table.insert v))))))
- (tset :cljlib/transient (map->transient hash-map*))
- (tset :cljlib/empty #(hash-map* (itable {}))))
- _ (hash-map* (setmetatable x {})))
- x)
-
-(defn assoc
- "Associate `val` under a `key`.
-Accepts extra keys and values.
-
-# Examples
-
-``` fennel
-(assert-eq {:a 1 :b 2} (assoc {:a 1} :b 2))
-(assert-eq {:a 1 :b 2} (assoc {:a 1 :b 1} :b 2))
-(assert-eq {:a 1 :b 2 :c 3} (assoc {:a 1 :b 1} :b 2 :c 3))
-```"
- ([tbl]
- (hash-map* (itable {})))
- ([tbl k v]
- (assert (or (nil? tbl) (map? tbl) (empty? tbl)) "expected a map")
- (assert (not (nil? k)) "attempt to use nil as key")
- (hash-map* (itable.assoc (or tbl {}) k v)))
- ([tbl k v & kvs]
- (assert (or (nil? tbl) (map? tbl) (empty? tbl)) "expected a map")
- (assert (not (nil? k)) "attempt to use nil as key")
- (hash-map* (apply itable.assoc (or tbl {}) k v kvs))))
-
-(defn assoc-in
- "Associate `val` into set of immutable nested tables `t`, via given `key-seq`.
-Returns a new immutable table. Returns a new immutable table.
-
-# Examples
-
-Replace value under nested keys:
-
-``` fennel
-(assert-eq
- {:a {:b {:c 1}}}
- (assoc-in {:a {:b {:c 0}}} [:a :b :c] 1))
-```
-
-Create new entries as you go:
-
-``` fennel
-(assert-eq
- {:a {:b {:c 1}} :e 2}
- (assoc-in {:e 2} [:a :b :c] 1))
-```"
- [tbl key-seq val]
- (assert (or (nil? tbl) (map? tbl) (empty? tbl)) "expected a map or nil")
- (hash-map* (itable.assoc-in tbl key-seq val)))
-
-(defn update
- "Update table value stored under `key` by calling a function `f` on
-that value. `f` must take one argument, which will be a value stored
-under the key in the table.
-
-# Examples
-
-Same as `assoc` but accepts function to produce new value based on key value.
-
-``` fennel
-(assert-eq
- {:data \"THIS SHOULD BE UPPERCASE\"}
- (update {:data \"this should be uppercase\"} :data string.upper))
-```"
- [tbl key f]
- (assert (or (nil? tbl) (map? tbl) (empty? tbl)) "expected a map")
- (hash-map* (itable.update tbl key f)))
-
-
-(defn update-in
- "Update table value stored under set of immutable nested tables, via
-given `key-seq` by calling a function `f` on the value stored under the
-last key. `f` must take one argument, which will be a value stored
-under the key in the table. Returns a new immutable table.
-
-# Examples
-
-Same as `assoc-in` but accepts function to produce new value based on key value.
-
-``` fennel
-(fn capitalize-words [s]
- (pick-values 1
- (s:gsub \"(%a)([%w_`]*)\" #(.. ($1:upper) ($2:lower)))))
-
-(assert-eq
- {:user {:name \"John Doe\"}}
- (update-in {:user {:name \"john doe\"}} [:user :name] capitalize-words))
-```"
- [tbl key-seq f]
- (assert (or (nil? tbl) (map? tbl) (empty? tbl)) "expected a map or nil")
- (hash-map* (itable.update-in tbl key-seq f)))
-
-(defn hash-map
- "Create associative table from `kvs` represented as sequence of keys
-and values"
- [& kvs]
- (apply assoc {} kvs))
-
-(defn get
- "Get value from the table by accessing it with a `key`.
-Accepts additional `not-found` as a marker to return if value wasn't
-found in the table."
- ([tbl key] (get tbl key nil))
- ([tbl key not-found]
- (assert (or (map? tbl) (empty? tbl)) "expected a map")
- (or (. tbl key) not-found)))
-
-(defn get-in
- "Get value from nested set of tables by providing key sequence.
-Accepts additional `not-found` as a marker to return if value wasn't
-found in the table."
- ([tbl keys] (get-in tbl keys nil))
- ([tbl keys not-found]
- (assert (or (map? tbl) (empty? tbl)) "expected a map")
- (var (res t done) (values tbl tbl nil))
- (each [_ k (ipairs* keys) :until done]
- (match (. t k)
- v (set (res t) (values v v))
- _ (set (res done) (values not-found true))))
- res))
-
-(defn dissoc
- "Remove `key` from table `tbl`. Optionally takes more `keys`."
- ([tbl] tbl)
- ([tbl key]
- (assert (or (map? tbl) (empty? tbl)) "expected a map")
- (hash-map* (doto tbl (tset key nil))))
- ([tbl key & keys]
- (apply dissoc (dissoc tbl key) keys)))
-
-(defn merge
- "Merge `maps` rght to left into a single hash-map."
- [& maps]
- (when (some identity maps)
- (->> maps
- (reduce (fn [a b] (collect [k v (pairs* b) :into a]
- (values k v)))
- {})
- itable
- hash-map*)))
-
-(defn frequencies
- "Return a table of unique entries from table `t` associated to amount
-of their appearances.
-
-# Examples
-
-Count each entry of a random letter:
-
-``` fennel
-(let [fruits [:banana :banana :apple :strawberry :apple :banana]]
- (assert-eq (frequencies fruits)
- {:banana 3
- :apple 2
- :strawberry 1}))
-```"
- [t]
- (hash-map* (itable.frequencies t)))
-
-(defn group-by
- "Group table items in an associative table under the keys that are
-results of calling `f` on each element of sequential table `t`.
-Elements that the function call resulted in `nil` returned in a
-separate table.
-
-# Examples
-
-Group rows by their date:
-
-``` fennel
-(local rows
- [{:date \"2007-03-03\" :product \"pineapple\"}
- {:date \"2007-03-04\" :product \"pizza\"}
- {:date \"2007-03-04\" :product \"pineapple pizza\"}
- {:date \"2007-03-05\" :product \"bananas\"}])
-
-(assert-eq (group-by #(. $ :date) rows)
- {\"2007-03-03\"
- [{:date \"2007-03-03\" :product \"pineapple\"}]
- \"2007-03-04\"
- [{:date \"2007-03-04\" :product \"pizza\"}
- {:date \"2007-03-04\" :product \"pineapple pizza\"}]
- \"2007-03-05\"
- [{:date \"2007-03-05\" :product \"bananas\"}]})
-```"
- [f t]
- (hash-map* (pick-values 1 (itable.group-by f t))))
-
-(defn zipmap
- "Return an associative table with the `keys` mapped to the
-corresponding `vals`."
- [keys vals]
- (hash-map* (itable (lazy.zipmap keys vals))))
-
-(defn replace
- "Given a map of replacement pairs and a vector/collection `coll`,
-returns a vector/seq with any elements `=` a key in `smap` replaced
-with the corresponding `val` in `smap`. Returns a transducer when no
-collection is provided."
- ([smap]
- (map #(if-let [e (find smap $)] (. e 2) $)))
- ([smap coll]
- (if (vector? coll)
- (->> coll
- (reduce (fn [res v]
- (if-let [e (find smap v)]
- (doto res (table.insert (. e 2)))
- (doto res (table.insert v))))
- [])
- itable
- vec*)
- (map #(if-let [e (find smap $)] (. e 2) $) coll))))
-
-;;; Conj
-
-(defn conj
- "Insert `x` as a last element of a table `tbl`.
-
-If `tbl` is a sequential table or empty table, inserts `x` and
-optional `xs` as final element in the table.
-
-If `tbl` is an associative table, that satisfies `map?` test,
-insert `[key value]` pair into the table.
-
-Mutates `tbl`.
-
-# Examples
-Adding to sequential tables:
-
-``` fennel
-(conj [] 1 2 3 4)
-;; => [1 2 3 4]
-(conj [1 2 3] 4 5)
-;; => [1 2 3 4 5]
-```
-
-Adding to associative tables:
-
-``` fennel
-(conj {:a 1} [:b 2] [:c 3])
-;; => {:a 1 :b 2 :c 3}
-```
-
-Note, that passing literal empty associative table `{}` will not work:
-
-``` fennel
-(conj {} [:a 1] [:b 2])
-;; => [[:a 1] [:b 2]]
-(conj (hash-map) [:a 1] [:b 2])
-;; => {:a 1 :b 2}
-```
-
-See `hash-map` for creating empty associative tables."
- ([] (vector))
- ([s] s)
- ([s x]
- (match (getmetatable s)
- {:cljlib/conj f} (f s x)
- _ (if (vector? s) (vec* (itable.insert s x))
- (map? s) (apply assoc s x)
- (nil? s) (cons x s)
- (empty? s) (vector x)
- (error "expected collection, got" (type s)))))
- ([s x & xs]
- (apply conj (conj s x) xs)))
-
-(defn disj
- "Returns a new set type, that does not contain the
-specified `key` or `keys`."
- ([Set] Set)
- ([Set key]
- (match (getmetatable Set)
- {:cljlib/type :hash-set :cljlib/disj f} (f Set key)
- _ (error (.. "disj is not supported on " (class Set)) 2)))
- ([Set key & keys]
- (match (getmetatable Set)
- {:cljlib/type :hash-set :cljlib/disj f} (apply f Set key keys)
- _ (error (.. "disj is not supported on " (class Set)) 2))))
-
-(defn pop
- "If `coll` is a list returns a new list without the first
-item. If `coll` is a vector, returns a new vector without the last
-item. If the collection is empty, raises an error. Not the same as
-`next` or `butlast`."
- [coll]
- (match (getmetatable coll)
- {:cljlib/type :seq} (match (seq coll)
- s (drop 1 s)
- _ (error "can't pop empty list" 2))
- {:cljlib/pop f} (f coll)
- _ (error (.. "pop is not supported on " (class coll)) 2)))
-
-;;; Transients
-
-(defn transient
- "Returns a new, transient version of the collection."
- [coll]
- (match (getmetatable coll)
- {:cljlib/editable true :cljlib/transient f} (f coll)
- _ (error "expected editable collection" 2)))
-
-(defn conj!
- "Adds `x` to the transient collection, and return `coll`."
- ([] (transient (vec* [])))
- ([coll] coll)
- ([coll x]
- (match (getmetatable coll)
- {:cljlib/type :transient :cljlib/conj! f} (f coll x)
- {:cljlib/type :transient} (error "unsupported transient operation" 2)
- _ (error "expected transient collection" 2))
- coll))
-
-(defn assoc!
- "Remove `k`from transient map, and return `map`."
- [map k & ks]
- (match (getmetatable map)
- {:cljlib/type :transient :cljlib/dissoc! f} (apply f map k ks)
- {:cljlib/type :transient} (error "unsupported transient operation" 2)
- _ (error "expected transient collection" 2))
- map)
-
-(defn dissoc!
- "Remove `k`from transient map, and return `map`."
- [map k & ks]
- (match (getmetatable map)
- {:cljlib/type :transient :cljlib/dissoc! f} (apply f map k ks)
- {:cljlib/type :transient} (error "unsupported transient operation" 2)
- _ (error "expected transient collection" 2))
- map)
-
-(defn disj!
- "disj[oin]. Returns a transient set of the same type, that does not
-contain `key`."
- ([Set] Set)
- ([Set key & ks]
- (match (getmetatable Set)
- {:cljlib/type :transient :cljlib/disj! f} (apply f Set key ks)
- {:cljlib/type :transient} (error "unsupported transient operation" 2)
- _ (error "expected transient collection" 2))))
-
-(defn pop!
- "Removes the last item from a transient vector. If the collection is
-empty, raises an error Returns coll"
- [coll]
- (match (getmetatable coll)
- {:cljlib/type :transient :cljlib/pop! f} (f coll)
- {:cljlib/type :transient} (error "unsupported transient operation" 2)
- _ (error "expected transient collection" 2)))
-
-(defn persistent!
- "Returns a new, persistent version of the transient collection. The
-transient collection cannot be used after this call, any such use will
-raise an error."
- [coll]
- (match (getmetatable coll)
- {:cljlib/type :transient :cljlib/persistent! f} (f coll)
- _ (error "expected transient collection" 2)))
-
-;;; Into
-
-(defn into
- "Returns a new coll consisting of `to` with all of the items of `from`
-conjoined. A transducer `xform` may be supplied.
-
-# Examples
-
-Insert items of one collection into another collection:
-
-```fennel
-(assert-eq [1 2 3 :a :b :c] (into [1 2 3] \"abc\"))
-(assert-eq {:a 2 :b 3} (into {:a 1} {:a 2 :b 3}))
-```
-
-Transform a hash-map into a sequence of key-value pairs:
-
-``` fennel
-(assert-eq [[:a 1]] (into (vector) {:a 1}))
-```
-
-You can also construct a hash-map from a sequence of key-value pairs:
-
-``` fennel
-(assert-eq {:a 1 :b 2 :c 3}
- (into (hash-map) [[:a 1] [:b 2] [:c 3]]))
-```"
- ([] (vector))
- ([to] to)
- ([to from]
- (match (getmetatable to)
- {:cljlib/editable true}
- (persistent! (reduce conj! (transient to) from))
- _ (reduce conj to from)))
- ([to xform from]
- (match (getmetatable to)
- {:cljlib/editable true}
- (persistent! (transduce xform conj! (transient to) from))
- _ (transduce xform conj to from))))
-
-;;; Hash Set
-
-(fn viewset [Set view inspector indent]
- (if (. inspector.seen Set)
- (.. "@set" (. inspector.seen Set) "{...}")
- (let [prefix (.. "@set"
- (if (inspector.visible-cycle? Set)
- (. inspector.seen Set) "")
- "{")
- set-indent (length prefix)
- indent-str (string.rep " " set-indent)
- lines (icollect [v (pairs* Set)]
- (.. indent-str
- (view v inspector (+ indent set-indent) true)))]
- (tset lines 1 (.. prefix (string.gsub (or (. lines 1) "") "^%s+" "")))
- (tset lines (length lines) (.. (. lines (length lines)) "}"))
- lines)))
-
-(fn hash-set->transient [immutable]
- (fn [hset]
- (let [removed (setmetatable {} {:__index deep-index})]
- (->> {:__index (fn [_ k]
- (if (not (. removed k)) (. hset k)))
- :cljlib/type :transient
- :cljlib/conj #(error "can't `conj` onto transient set, use `conj!`")
- :cljlib/disj #(error "can't `disj` a transient set, use `disj!`")
- :cljlib/assoc #(error "can't `assoc` onto transient set, use `assoc!`")
- :cljlib/dissoc #(error "can't `dissoc` onto transient set, use `dissoc!`")
- :cljlib/conj! (fn [thset v]
- (if (= nil v)
- (tset removed v true)
- (tset removed v nil))
- (doto thset (tset v v)))
- :cljlib/assoc! #(error "can't `assoc!` onto transient set")
- :cljlib/assoc! #(error "can't `dissoc!` a transient set")
- :cljlib/disj! (fn [thset ...]
- (for [i 1 (select "#" ...)]
- (let [k (select i ...)]
- (tset thset k nil)
- (tset removed k true)))
- thset)
- :cljlib/persistent! (fn [thset]
- (let [t (collect [k v (pairs thset)
- :into (collect [k v (pairs hset)]
- (values k v))]
- (values k v))]
- (each [k (pairs removed)]
- (tset t k nil))
- (each [_ k (ipairs (icollect [k (pairs* thset)] k))]
- (tset thset k nil))
- (setmetatable thset
- {:__index #(error "attempt to use transient after it was persistet")
- :__newindex #(error "attempt to use transient after it was persistet")})
- (immutable (itable t))))}
- (setmetatable {})))))
-
-(fn hash-set* [x]
- (match (getmetatable x)
- mt (doto mt
- (tset :cljlib/type :hash-set)
- (tset :cljlib/conj
- (fn [s v ...]
- (hash-set*
- (itable.assoc
- s v v
- (unpack* (let [res []]
- (each [ _ v (ipairs [...])]
- (table.insert res v)
- (table.insert res v))
- res))))))
- (tset :cljlib/disj
- (fn [s k ...]
- (let [to-remove
- (collect [_ k (ipairs [...])
- :into (->> {:__index deep-index}
- (setmetatable {k true}))]
- k true)]
- (hash-set*
- (itable.assoc {}
- (unpack*
- (let [res []]
- (each [_ v (pairs s)]
- (when (not (. to-remove v))
- (table.insert res v)
- (table.insert res v)))
- res)))))))
- (tset :cljlib/empty #(hash-set* (itable {})))
- (tset :cljlib/editable true)
- (tset :cljlib/transient (hash-set->transient hash-set*))
- (tset :cljlib/seq (fn [s] (map #(if (vector? $) (. $ 1) $) s)))
- (tset :__fennelview viewset)
- (tset :__fennelrest (fn [s i]
- (var j 1)
- (let [vals []]
- (each [v (pairs* s)]
- (if (>= j i)
- (table.insert vals v)
- (set j (+ j 1))))
- (core.hash-set (unpack* vals))))))
- _ (hash-set* (setmetatable x {})))
- x)
-
-(defn hash-set
- "Create hash set.
-
-Set is a collection of unique elements, which sore purpose is only to
-tell you if something is in the set or not."
- [& xs]
- (let [Set (collect [_ val (pairs* xs)
- :into (->> {:__newindex deep-newindex}
- (setmetatable {}))]
- (values val val))]
- (hash-set* (itable Set))))
-
-;;; Multimethods
-
-(defn multifn?
- "Test if `mf' is an instance of `multifn'.
-
-`multifn' is a special kind of table, created with `defmulti' macros
-from `macros.fnl'."
- [mf]
- (match (getmetatable mf)
- {:cljlib/type :multifn} true
- _ false))
-
-(defn remove-method
- "Remove method from `multimethod' for given `dispatch-value'."
- [multimethod dispatch-value]
- (if (multifn? multimethod)
- (tset multimethod dispatch-value nil)
- (error (.. (tostring multimethod) " is not a multifn") 2))
- multimethod)
-
-(defn remove-all-methods
- "Removes all methods of `multimethod'"
- [multimethod]
- (if (multifn? multimethod)
- (each [k _ (pairs multimethod)]
- (tset multimethod k nil))
- (error (.. (tostring multimethod) " is not a multifn") 2))
- multimethod)
-
-(defn methods
- "Given a `multimethod', returns a map of dispatch values -> dispatch fns"
- [multimethod]
- (if (multifn? multimethod)
- (let [m {}]
- (each [k v (pairs multimethod)]
- (tset m k v))
- m)
- (error (.. (tostring multimethod) " is not a multifn") 2)))
-
-(defn get-method
- "Given a `multimethod' and a `dispatch-value', returns the dispatch
-`fn' that would apply to that value, or `nil' if none apply and no
-default."
- [multimethod dispatch-value]
- (if (multifn? multimethod)
- (or (. multimethod dispatch-value)
- (. multimethod :default))
- (error (.. (tostring multimethod) " is not a multifn") 2)))
-
-core