summaryrefslogtreecommitdiff
path: root/cljlib.fnl
diff options
context:
space:
mode:
authorAndrey Orst <andreyorst@gmail.com>2021-01-19 16:46:00 +0000
committerAndrey Orst <andreyorst@gmail.com>2021-01-19 16:46:00 +0000
commit270beed0505ef47159d94fb162ff4840958f3ce5 (patch)
tree6504ecb114419b0dd3055e509abd8b602586f614 /cljlib.fnl
parenta2b08f721c28b3b56a802031bc35df6a68b219d8 (diff)
fix: Fennel 0.8.0 enhancements
Changelog: - fixed bug in try - reworked pretty printing for sets - handle cycles in sets - use new fennel.view format - reorganized library to make requiring it easier
Diffstat (limited to 'cljlib.fnl')
-rw-r--r--cljlib.fnl1286
1 files changed, 0 insertions, 1286 deletions
diff --git a/cljlib.fnl b/cljlib.fnl
deleted file mode 100644
index 5820d57..0000000
--- a/cljlib.fnl
+++ /dev/null
@@ -1,1286 +0,0 @@
-(local core {:_VERSION "0.3.0"
- :_LICENSE "[MIT](https://gitlab.com/andreyorst/fennel-cljlib/-/raw/master/LICENSE)"
- :_COPYRIGHT "Copyright (C) 2020 Andrey Orst"
- :_DESCRIPTION "Fennel-cljlib - functions from Clojure's core.clj implemented on top
-of Fennel.
-
-This library contains a set of functions providing functions that
-behave similarly to Clojure's equivalents. Library itself has nothing
-Fennel specific so it should work, e.g:
-
-``` lua
-Lua 5.3.5 Copyright (C) 1994-2018 Lua.org, PUC-Rio
-> clj = require\"cljlib\"
-> table.concat(clj.mapv(function (x) return x * x end, {1, 2, 3}), \" \")
--- 1 4 9
-```
-
-This example is mapping an anonymous `function` over a table,
-producing new table and concatenating it with `\" \"`.
-
-However this library also provides Fennel-specific set of
-[macros](./cljlib-macros.md), that provides additional facilities like
-`fn*` or `defmulti` which extend the language allowing writing code
-that looks and works mostly like Clojure.
-
-Each function in this library is created with `fn*`, which is a
-special macros for creating multi-arity functions. So when you see
-function signature like `(foo [x])`, this means that this is function
-`foo`, that accepts exactly one argument `x`. In contrary, functions
-created with `fn` will produce `(foo x)` signature (`x` is not inside
-brackets).
-
-Functions, which signatures look like `(foo ([x]) ([x y]) ([x y &
-zs]))`, it is a multi-arity function, which accepts either one, two,
-or three-or-more arguments. Each `([...])` represents different body
-of a function which is chosen by checking amount of arguments passed
-to the function. See [Clojure's doc section on multi-arity
-functions](https://clojure.org/guides/learn/functions#_multi_arity_functions)."})
-
-(local insert table.insert)
-(local _unpack (or table.unpack _G.unpack))
-(require-macros :cljlib-macros)
-
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Utility functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(fn* core.apply
- "Apply `f` to the argument list formed by prepending intervening
-arguments to `args`, and `f` must support variadic amount of
-arguments.
-
-# Examples
-Applying `print` to different arguments:
-
-``` fennel
-(apply print [1 2 3 4])
-;; prints 1 2 3 4
-(apply print 1 [2 3 4])
-;; => 1 2 3 4
-(apply print 1 2 3 4 5 6 [7 8 9])
-;; => 1 2 3 4 5 6 7 8 9
-```"
- ([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 (empty [])]
- (for [i 1 (- (length args) 1)]
- (insert flat-args (. args i)))
- (each [_ a (ipairs (. args (length args)))]
- (insert flat-args a))
- (f a b c d (_unpack flat-args)))))
-
-(fn* core.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)))
-
-(fn* core.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)))
-
-(fn* core.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)))
-
-(fn* core.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)))
-
-(fn* core.le
- "Returns true if nums are in monotonically non-decreasing order"
- ([x] true)
- ([x y] (<= x y))
- ([x y & more]
- (if (<= x y)
- (if (next more 1)
- (le y (. more 1) (_unpack more 2))
- (<= y (. more 1)))
- false)))
-
-(fn* core.lt
- "Returns true if nums are in monotonically decreasing order"
- ([x] true)
- ([x y] (< x y))
- ([x y & more]
- (if (< x y)
- (if (next more 1)
- (lt y (. more 1) (_unpack more 2))
- (< y (. more 1)))
- false)))
-
-(fn* core.ge
- "Returns true if nums are in monotonically non-increasing order"
- ([x] true)
- ([x y] (>= x y))
- ([x y & more]
- (if (>= x y)
- (if (next more 1)
- (ge y (. more 1) (_unpack more 2))
- (>= y (. more 1)))
- false)))
-
-(fn* core.gt
- "Returns true if nums are in monotonically increasing order"
- ([x] true)
- ([x y] (> x y))
- ([x y & more]
- (if (> x y)
- (if (next more 1)
- (gt y (. more 1) (_unpack more 2))
- (> y (. more 1)))
- false)))
-
-(fn* core.inc "Increase number by one" [x] (+ x 1))
-(fn* core.dec "Decrease number by one" [x] (- x 1))
-
-(local utility-doc-order
- [:apply :add :sub :mul :div :le :lt :ge :gt :inc :dec])
-
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;; Tests and predicates ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(fn fast-table-type [tbl]
- (let [m (getmetatable tbl)]
- (if-let [t (and m (. m :cljlib/type))]
- t)))
-
-(fn* core.map?
- "Check whether `tbl` is an associative table.
-
-Non empty associative tables are tested for two things:
-- `next` returns the key-value pair,
-- key, that is returned by the `next` is not equal to `1`.
-
-Empty tables can't be analyzed with this method, and `map?` will
-return `false`. If you need this test pass for empty table, see
-[`hash-map`](#hash-map) for creating tables that have additional
-metadata attached for this test to work.
-
-# Examples
-Non empty tables:
-
-``` fennel
-(assert (map? {:a 1 :b 2}))
-
-(local some-table {:key :value})
-(assert (map? some-table))
-```
-
-Empty tables:
-
-``` fennel
-(local some-table {})
-(assert (not (map? some-table)))
-```
-
-Empty tables created with [`hash-map`](#hash-map) will pass the test:
-
-``` fennel
-(local some-table (hash-map))
-(assert (map? some-table))
-```"
- [tbl]
- (if (= (type tbl) :table)
- (if-let [t (fast-table-type tbl)]
- (= t :table)
- (let [(k _) (next tbl)]
- (and (not= k nil)
- (not= k 1))))))
-
-(fn* core.vector?
- "Check whether `tbl` is an 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`](#vector) for creating tables that have additional
-metadata attached for this test to work.
-
-# Examples
-Non empty vector:
-
-``` fennel
-(assert (vector? [1 2 3 4]))
-
-(local some-table [1 2 3])
-(assert (vector? some-table))
-```
-
-Empty tables:
-
-``` fennel
-(local some-table [])
-(assert (not (vector? some-table)))
-```
-
-Empty tables created with [`vector`](#vector) will pass the test:
-
-``` fennel
-(local some-table (vector))
-(assert (vector? some-table))
-```"
- [tbl]
- (if (= (type tbl) :table)
- (if-let [t (fast-table-type tbl)]
- (= t :seq)
- (let [(k _) (next tbl)]
- (and (not= k nil) (= k 1))))))
-
-(fn* core.multifn?
- "Test if `mf` is an instance of `multifn`.
-
-`multifn` is a special kind of table, created with `defmulti` macros
-from `cljlib-macros.fnl`."
- [mf]
- (= (. (or (getmetatable mf) {}) :cljlib/type) :multifn))
-
-(fn* core.set?
- ""
- [s]
- (match (. (or (getmetatable s) {}) :cljlib/type)
- :cljlib/ordered-set :cljlib/ordered-set
- :cljlib/hash-set :cljlib/hash-set
- _ false))
-
-(fn* core.nil?
- "Test if value is nil."
- ([] true)
- ([x] (= x nil)))
-
-(fn* core.zero?
- "Test if value is equal to zero."
- [x]
- (= x 0))
-
-(fn* core.pos?
- "Test if `x` is greater than zero."
- [x]
- (> x 0))
-
-(fn* core.neg?
- "Test if `x` is less than zero."
- [x]
- (< x 0))
-
-(fn* core.even?
- "Test if value is even."
- [x]
- (= (% x 2) 0))
-
-(fn* core.odd?
- "Test if value is odd."
- [x]
- (not (even? x)))
-
-(fn* core.string?
- "Test if `x` is a string."
- [x]
- (= (type x) :string))
-
-(fn* core.boolean?
- "Test if `x` is a Boolean"
- [x]
- (= (type x) :boolean))
-
-(fn* core.true?
- "Test if `x` is `true`"
- [x]
- (= x true))
-
-(fn* core.false?
- "Test if `x` is `false`"
- [x]
- (= x false))
-
-(fn* core.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))))
-
-(fn* core.pos-int?
- "Test if `x` is a positive integer."
- [x]
- (and (int? x)
- (pos? x)))
-
-(fn* core.neg-int?
- "Test if `x` is a negetive integer."
- [x]
- (and (int? x)
- (neg? x)))
-
-(fn* core.double?
- "Test if `x` is a number with floating point data."
- [x]
- (and (= (type x) :number)
- (not= x (math.floor x))))
-
-(fn* core.empty?
- "Check if collection is empty."
- [x]
- (match (type x)
- :table (= (next x) nil)
- :string (= x "")
- _ (error "empty?: unsupported collection")))
-
-(fn* core.not-empty
- "If `x` is empty, returns `nil`, otherwise `x`."
- [x]
- (if (not (empty? x))
- x))
-
-(local predicate-doc-order
- [:map? :vector? :multifn? :set? :nil? :zero? :pos?
- :neg? :even? :odd? :string? :boolean? :true? :false?
- :int? :pos-int? :neg-int? :double? :empty? :not-empty])
-
-
-;;;;;;;;;;;;;;;;;;;;;; Sequence manipuletion functions ;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(fn* core.vector
- "Constructs sequential table out of it's arguments.
-
-Sets additional metadata for function [`vector?`](#vector?) to work.
-
-# Examples
-
-``` fennel
-(local v (vector 1 2 3 4))
-(assert (eq v [1 2 3 4]))
-```"
- [& args]
- (setmetatable args {:cljlib/type :seq}))
-
-(fn* core.seq
- "Create sequential table.
-
-Transforms original table to sequential table of key value pairs
-stored as sequential tables in linear time. If `col` is an
-associative table, returns sequential table of vectors with key and
-value. If `col` is sequential table, returns its shallow copy.
-
-# Examples
-Sequential tables remain as is:
-
-``` fennel
-(seq [1 2 3 4])
-;; [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})
-;; [[:b 2] [:a 1] [:c 3]]
-```
-
-See `into` macros for transforming this back to associative table.
-Additionally you can use [`conj`](#conj) and [`apply`](#apply) with
-[`hash-map`](#hash-map):
-
-``` fennel
-(apply conj (hash-map) [:c 3] [[:a 1] [:b 2]])
-;; => {:a 1 :b 2 :c 3}
-```"
- [col]
- (let [res (empty [])]
- (match (type col)
- :table (let [m (or (getmetatable col) {})]
- (when-some [_ ((or m.cljlib/next next) col)]
- (var assoc? false)
- (let [assoc-res (empty [])]
- (each [k v (pairs col)]
- (if (and (not assoc?)
- (map? col))
- (set assoc? true))
- (insert res v)
- (insert assoc-res [k v]))
- (if assoc? assoc-res res))))
- :string (let [char utf8.char]
- (each [_ b (utf8.codes col)]
- (insert res (char b)))
- res)
- :nil nil
- _ (error (.. "expected table, string or nil") 2))))
-
-(fn* core.kvseq
- "Transforms any table kind to key-value sequence."
- [tbl]
- (let [res (empty [])]
- (each [k v (pairs tbl)]
- (insert res [k v]))
- res))
-
-(fn* core.first
- "Return first element of a table. Calls `seq` on its argument."
- [col]
- (when-some [col (seq col)]
- (. col 1)))
-
-(fn* core.rest
- "Returns table of all elements of a table but the first one. Calls
- `seq` on its argument."
- [col]
- (if-some [col (seq col)]
- (vector (_unpack col 2))
- (empty [])))
-
-(fn* core.last
- "Returns the last element of a table. Calls `seq` on its argument."
- [col]
- (when-some [col (seq col)]
- (var (i v) (next col))
- (while i
- (local (_i _v) (next col i))
- (if _i (set v _v))
- (set i _i))
- v))
-
-(fn* core.butlast
- "Returns everything but the last element of a table as a new
- table. Calls `seq` on its argument."
- [col]
- (when-some [col (seq col)]
- (table.remove col (length col))
- (when (not (empty? col))
- col)))
-
-(fn* core.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?`](#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`](#hash-map) for creating empty associative tables."
- ([] (empty []))
- ([tbl] tbl)
- ([tbl x]
- (when-some [x x]
- (let [tbl (or tbl (empty []))]
- (if (map? tbl)
- (tset tbl (. x 1) (. x 2))
- (set? tbl)
- (tset tbl x x)
- (insert tbl x))))
- tbl)
- ([tbl x & xs]
- (apply conj (conj tbl x) xs)))
-
-(fn* core.disj
- "Remove key `k` from set `s`."
- ([s] (if (set? s) s
- (error "expected either hash-set or ordered-set as first argument" 2)))
- ([s k]
- (if (set? s)
- (doto s (tset k nil))
- (error "expected either hash-set or ordered-set as first argument" 2)))
- ([s k & ks]
- (apply disj (disj s k) ks)))
-
-(fn consj [...]
- "Like conj but joins at the front. Modifies `tbl`."
- (let [[tbl x & xs] [...]]
- (if (nil? x) tbl
- (consj (doto tbl (insert 1 x)) (_unpack xs)))))
-
-(fn* core.cons
- "Insert `x` to `tbl` at the front. Calls [`seq`](#seq) on `tbl`."
- [x tbl]
- (if-some [x x]
- (doto (or (seq tbl) (empty []))
- (insert 1 x))
- tbl))
-
-(fn* core.concat
- "Concatenate tables."
- ([] nil)
- ([x] (or (seq x) (empty [])))
- ([x y] (let [to (or (seq x) (empty []))
- from (or (seq y) (empty []))]
- (each [_ v (ipairs from)]
- (insert to v))
- to))
- ([x y & xs]
- (apply concat (concat x y) xs)))
-
-(fn* core.reduce
- "Reduce collection `col` using function `f` and optional initial value `val`.
-
-`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. Calls `seq` on `col`.
-
-Early termination is possible with the use of [`reduced`](#reduced)
-function.
-
-# Examples
-Reduce sequence of numbers with [`add`](#add)
-
-``` fennel
-(reduce add [1 2 3 4])
-;; => 10
-(reduce add 10 [1 2 3 4])
-;; => 20
-```
-
-"
- ([f col]
- (let [col (or (seq col) (empty []))]
- (match (length col)
- 0 (f)
- 1 (. col 1)
- 2 (f (. col 1) (. col 2))
- _ (let [[a b & rest] col]
- (reduce f (f a b) rest)))))
- ([f val col]
- (if-some [reduced (when-some [m (getmetatable val)]
- (and m.cljlib/reduced
- (= m.cljlib/reduced.status :ready)
- m.cljlib/reduced.val))]
- reduced
- (let [col (or (seq col) (empty []))]
- (let [[x & xs] col]
- (if (nil? x)
- val
- (reduce f (f val x) xs)))))))
-
-(fn* core.reduced
- "Wraps `x` in such a way so [`reduce`](#reduce) will terminate early
-with this value.
-
-# Examples
-Stop reduction is result is higher than `10`:
-
-``` fennel
-(reduce (fn [res x]
- (if (>= res 10)
- (reduced res)
- (+ res x)))
- [1 2 3])
-;; => 6
-
-(reduce (fn [res x]
- (if (>= res 10)
- (reduced res)
- (+ res x)))
- [1 2 3 4 :nil])
-;; => 10
-```
-
-Note that in second example we had `:nil` in the array, which is not a
-valid number, but we've terminated right before we've reached it."
- [x]
- (setmetatable
- {} {:cljlib/reduced {:status :ready
- :val x}}))
-
-(fn* core.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`](#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 tbl]
- (var res val)
- (each [_ [k v] (ipairs (or (kvseq tbl) (empty [])))]
- (set res (f res k v))
- (match (getmetatable res)
- m (if (and m.cljlib/reduced
- (= m.cljlib/reduced.status :ready))
- (do (set res m.cljlib/reduced.val)
- (lua :break)))))
- res)
-
-(fn* core.mapv
- "Maps function `f` over one or more collections.
-
-Accepts arbitrary amount of collections, calls `seq` on each of it.
-Function `f` must take the same amount of arguments as the amount of
-tables, passed to `mapv`. Applies `f` over first value of each
-table. Then applies `f` to second value of each table. Continues until
-any of the tables is exhausted. All remaining values are
-ignored. Returns a sequential table of results.
-
-# Examples
-Map `string.upcase` over the string:
-
-``` fennel
-(mapv string.upper \"string\")
-;; => [\"S\" \"T\" \"R\" \"I\" \"N\" \"G\"]
-```
-
-Map [`mul`](#mul) over two tables:
-
-``` fennel
-(mapv mul [1 2 3 4] [1 0 -1])
-;; => [1 0 -3]
-```
-
-Basic `zipmap` implementation:
-
-``` fennel
-(fn zipmap [keys vals]
- (into {} (mapv vector keys vals)))
-
-(zipmap [:a :b :c] [1 2 3 4])
-;; => {:a 1 :b 2 :c 3}
-```"
- ([f col]
- (local res (empty []))
- (each [_ v (ipairs (or (seq col) (empty [])))]
- (when-some [tmp (f v)]
- (insert res tmp)))
- res)
- ([f col1 col2]
- (let [res (empty [])
- col1 (or (seq col1) (empty []))
- col2 (or (seq col2) (empty []))]
- (var (i1 v1) (next col1))
- (var (i2 v2) (next col2))
- (while (and i1 i2)
- (when-some [tmp (f v1 v2)]
- (insert res tmp))
- (set (i1 v1) (next col1 i1))
- (set (i2 v2) (next col2 i2)))
- res))
- ([f col1 col2 col3]
- (let [res (empty [])
- col1 (or (seq col1) (empty []))
- col2 (or (seq col2) (empty []))
- col3 (or (seq col3) (empty []))]
- (var (i1 v1) (next col1))
- (var (i2 v2) (next col2))
- (var (i3 v3) (next col3))
- (while (and i1 i2 i3)
- (when-some [tmp (f v1 v2 v3)]
- (insert res tmp))
- (set (i1 v1) (next col1 i1))
- (set (i2 v2) (next col2 i2))
- (set (i3 v3) (next col3 i3)))
- res))
- ([f col1 col2 col3 & cols]
- (let [step (fn step [cols]
- (if (->> cols
- (mapv #(not= (next $) nil))
- (reduce #(and $1 $2)))
- (cons (mapv #(. (or (seq $) (empty [])) 1) cols) (step (mapv #(do [(_unpack $ 2)]) cols)))
- (empty [])))
- res (empty [])]
- (each [_ v (ipairs (step (consj cols col3 col2 col1)))]
- (when-some [tmp (apply f v)]
- (insert res tmp)))
- res)))
-
-(fn* core.filter
- "Returns a sequential table of the items in `col` for which `pred`
- returns logical true."
- [pred col]
- (if-let [col (seq col)]
- (let [f (. col 1)
- r [(_unpack col 2)]]
- (if (pred f)
- (cons f (filter pred r))
- (filter pred r)))
- (empty [])))
-
-(fn* core.every?
- "Test if every item in `tbl` satisfies the `pred`."
- [pred tbl]
- (if (empty? tbl) true
- (pred (. tbl 1)) (every? pred [(_unpack tbl 2)])
- false))
-
-(fn* core.some
- "Test if any item in `tbl` satisfies the `pred`."
- [pred tbl]
- (when-let [tbl (seq tbl)]
- (or (pred (. tbl 1)) (some pred [(_unpack tbl 2)]))))
-
-(fn* core.not-any?
- "Test if no item in `tbl` satisfy the `pred`."
- [pred tbl]
- (some #(not (pred $)) tbl))
-
-(fn* core.range
- "return range of of numbers from `lower` to `upper` with optional `step`."
- ([upper] (range 0 upper 1))
- ([lower upper] (range lower upper 1))
- ([lower upper step]
- (let [res (empty [])]
- (for [i lower (- upper step) step]
- (insert res i))
- res)))
-
-(fn* core.reverse
- "Returns table with same items as in `tbl` but in reverse order."
- [tbl]
- (when-some [tbl (seq tbl)]
- (reduce consj (empty []) tbl)))
-
-(local sequence-doc--order [:vector :seq :kvseq :first :rest :last :butlast
- :conj :disj :cons :concat :reduce :reduced :reduce-kv
- :mapv :filter :every? :some :not-any? :range :reverse])
-
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Equality ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(var eq nil)
-
-(fn deep-index [tbl key]
- "This function uses the pre-declared `eq`, which we set later on,
-because `eq` requires this function internally. Several other
-functions also reuse this indexing method, such as sets."
- (var res nil)
- (each [k v (pairs tbl)]
- (when (eq k key)
- (set res v)
- (lua :break)))
- res)
-
-(set eq (fn*
- ([x] true)
- ([x y]
- (if (= x y)
- true
- (and (= (type x) :table) (= (type y) :table))
- (let [oldmeta (getmetatable y)]
- ;; In case if we'll get something like
- ;; (eq {[1 2 3] {:a [1 2 3]}} {[1 2 3] {:a [1 2 3]}})
- ;; we have to do even deeper search
- (setmetatable y {:__index deep-index})
- (var [res count-a count-b] [true 0 0])
- (each [k v (pairs x)]
- (set res (eq v (. y k)))
- (set count-a (+ count-a 1))
- (when (not res) (lua :break)))
- (when res
- (each [_ _ (pairs y)]
- (set count-b (+ count-b 1)))
- (set res (= count-a count-b)))
- (setmetatable y oldmeta)
- res)
- false))
- ([x y & xs]
- (reduce #(and $1 $2) (eq x y) (mapv #(eq x $) xs)))))
-
-(set core.eq (with-meta eq {:fnl/docstring "Deep compare values."}))
-
-
-;;;;;;;;;;;;;;;;;;;;;; Function manipulation functions ;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(fn* core.identity "Returns its argument." [x] x)
-
-(fn* core.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 (g x y z (_unpack args))))))
- ([f g & fs]
- (reduce comp (consj fs g f))))
-
-(fn* core.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
-oppisite 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)))))
-
-(fn* core.constantly
- "Returns a function that takes any number of arguments and returns `x`."
- [x]
- (fn [...] x))
-
-(fn* core.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
- (fn [tbl key]
- (each [k v (pairs tbl)]
- (when (eq k key)
- (lua "return v"))))})]
- (fn [...]
- (let [args [...]]
- (if-some [res (. memo args)]
- res
- (let [res (f ...)]
- (tset memo args res)
- res))))))
-
-(local function-manipulation-doc-order
- [:identity :comp :complement :constantly :memoize])
-
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Hash table extras ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(fn* core.assoc
- "Associate key `k` with value `v` in `tbl`."
- ([tbl k v]
- (setmetatable
- (doto tbl (tset k v))
- {:cljlib/type :table}))
- ([tbl k v & kvs]
- (assert (= (% (length kvs) 2) 0)
- (.. "no value supplied for key " (. kvs (length kvs))))
- (tset tbl k v)
- (var [k v] [nil nil])
- (var (i k) (next kvs))
- (while i
- (set (i v) (next kvs i))
- (tset tbl k v)
- (set (i k) (next kvs i)))
- (setmetatable tbl {:cljlib/type :table})))
-
-(fn* core.hash-map
- "Create associative table from keys and values"
- ([] (empty {}))
- ([& kvs] (apply assoc {} kvs)))
-
-(fn* core.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]
- (if-some [res (. tbl key)]
- res
- not-found)))
-
-(fn* core.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]
- (var res tbl)
- (var t tbl)
- (each [_ k (ipairs keys)]
- (if-some [v (. t k)]
- (set [res t] [v v])
- (set res not-found)))
- res))
-
-(fn* core.keys
- "Returns a sequence of the table's keys, in the same order as [`seq`](#seq)."
- [tbl]
- (let [res []]
- (each [k _ (pairs tbl)]
- (insert res k))
- res))
-
-(fn* core.vals
- "Returns a sequence of the table's values, in the same order as [`seq`](#seq)."
- [tbl]
- (let [res []]
- (each [_ v (pairs tbl)]
- (insert res v))
- res))
-
-(fn* core.find
- "Returns the map entry for `key`, or `nil` if key not present."
- [tbl key]
- (when-some [v (. tbl key)]
- [key v]))
-
-(fn* core.dissoc
- "Remove `key` from table `tbl`."
- ([tbl] tbl)
- ([tbl key]
- (doto tbl (tset key nil)))
- ([tbl key & keys]
- (apply dissoc (dissoc tbl key) keys)))
-
-(local hash-table-doc-order
- [:assoc :hash-map :get :get-in :keys :vals :find :dissoc])
-
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Multimethods ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(fn* core.remove-method
- "Remove method from `multifn` for given `dispatch-val`."
- [multifn dispatch-val]
- (if (multifn? multifn)
- (tset multifn dispatch-val nil)
- (error (.. (tostring multifn) " is not a multifn") 2))
- multifn)
-
-(fn* core.remove-all-methods
- "Removes all of the methods of multimethod"
- [multifn]
- (if (multifn? multifn)
- (each [k _ (pairs multifn)]
- (tset multifn k nil))
- (error (.. (tostring multifn) " is not a multifn") 2))
- multifn)
-
-(fn* core.methods
- "Given a multimethod, returns a map of dispatch values -> dispatch fns"
- [multifn]
- (if (multifn? multifn)
- (let [m {}]
- (each [k v (pairs multifn)]
- (tset m k v))
- m)
- (error (.. (tostring multifn) " is not a multifn") 2)))
-
-(fn* core.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."
- [multifn dispatch-val]
- (if (multifn? multifn)
- (or (. multifn dispatch-val)
- (. multifn :default))
- (error (.. (tostring multifn) " is not a multifn") 2)))
-
-(local multimethods-doc-order
- [:remove-method :remove-all-methods :methods :get-method])
-
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Sets ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(fn viewset [Set]
- "Workaround for a bug https://todo.sr.ht/~technomancy/fennel/26"
- (let [items []]
- (each [_ v (pairs Set)]
- (insert items ((require :fennelview) v)))
- (.. "#{" (table.concat items " ") "}")))
-
-(fn ordered-set-newindex [Set]
- "`__newindex` metamethod for ordered-set."
- (fn [t k v]
- (if (= nil v)
- (let [k (. Set k)]
- (each [key index (pairs Set)]
- (if (= index k) (tset Set key nil)
- (> index k) (tset Set key (- index 1)))))
- (if (not (. Set v))
- (tset Set v (+ 1 (length t)))))))
-
-(fn hash-set-newindex [Set]
- "`__newindex` metamethod for hash-set."
- (fn [t k v]
- (if (= nil v)
- (each [key _ (pairs Set)]
- (when (eq key k)
- (tset Set key nil)
- (lua :break)))
- (if (not (. Set v))
- (tset Set v true)))))
-
-(fn set-length [Set]
- "`__len` metamethod for set data structure."
- (fn []
- (var len 0)
- (each [_ _ (pairs Set)]
- (set len (+ 1 len)))
- len))
-
-(fn set-eq [s1 s2]
- "`__eq` metamethod for set data structure."
- (var [size res] [0 true])
- (each [i k (pairs s1)]
- (set size (+ size 1))
- (if res (set res (. s2 k))
- (lua :break)))
- (and res (= size (length s2))))
-
-(fn ordered-set-ipairs [Set]
- "Returns stateless `ipairs` iterator for ordered sets."
- (fn []
- (fn set-next [t i]
- (fn loop [t k]
- (local (k v) (next t k))
- (if v (if (= v (+ 1 i))
- (values v k)
- (loop t k))))
- (loop t))
- (values set-next Set 0)))
-
-(fn hash-set-ipairs [Set]
- "Returns stateful `ipairs` iterator for hashed sets."
- (fn []
- (var i 0)
- (fn iter [t _]
- (var (k v) (next t))
- (for [j 1 i]
- (set (k v) (next t k)))
- (if k (do (set i (+ i 1))
- (values i k))))
- (values iter Set nil)))
-
-;; Sets are bootstrapped upon previous functions.
-
-(fn* core.ordered-set
- "Create ordered set.
-
-Set is a collection of unique elements, which sore purpose is only to
-tell you if something is in the set or not.
-
-`ordered-set` is follows the argument insertion order, unlike sorted
-sets, which apply some sorting algorithm internally. New items added
-at the end of the set. Ordered set supports removal of items via
-`tset` and [`disj`](#disj). To add element to the ordered set use
-`tset` or [`conj`](#conj). Both operations modify the set.
-
-**Note**: Hash set prints as `#{a b c}`, but this construct is not
-supported by the Fennel reader, so you can't create sets with this
-syntax. Use `hash-set` function instead.
-
-Below are some examples of how to create and manipulate sets.
-
-## Create ordered set:
-Ordered sets are created by passing any amount of elements desired to
-be in the set:
-
-``` fennel
->> (ordered-set)
-#{}
->> (ordered-set :a :c :b)
-#{\"a\" \"c\" \"b\"}
-```
-
-Duplicate items are not added:
-
-``` fennel
->> (ordered-set)
-#{}
->> (ordered-set :a :c :a :a :a :a :c :b)
-#{\"a\" \"c\" \"b\"}
-```
-
-## Check if set contains desired value:
-Sets are functions of their keys, so simply calling a set with a
-desired key will either return the key, or `nil`:
-
-``` fennel
->> (local oset (ordered-set [:a :b :c] [:c :d :e] :e :f))
->> (oset [:a :b :c])
-[:a :b :c]
->> (. oset :e)
-:e
->> (oset [:a :b :f])
-nil
-```
-
-## Add items to existing set:
-To add element to the set use [`conj`](#conj) or `tset`
-
-``` fennel
->> (local oset (ordered-set :a :b :c))
->> (conj oset :d :e)
->> oset
-#{\"a\" \"b\" \"c\" \"d\" \"e\"}
-```
-
-### Remove items from the set:
-To add element to the set use [`disj`](#disj) or `tset`
-
-``` fennel
->> (local oset (ordered-set :a :b :c))
->> (disj oset :b)
->> oset
-#{\"a\" \"c\"}
->> (tset oset :a nil)
->> oset
-#{\"c\"}
-```
-
-## Equality semantics
-Both `ordered-set` and [`hash-set`](#hash-set) implement `__eq` metamethod,
-and are compared for having the same keys without particular order and
-same size:
-
-``` fennel
->> (= (ordered-set :a :b) (ordered-set :b :a))
-true
->> (= (ordered-set :a :b) (ordered-set :b :a :c))
-false
->> (= (ordered-set :a :b) (hash-set :a :b))
-true
-```"
- [& xs]
- (let [Set (setmetatable {} {:__index deep-index})
- set-ipairs (ordered-set-ipairs Set)]
- (var i 1)
- (each [_ val (ipairs xs)]
- (when (not (. Set val))
- (tset Set val i)
- (set i (+ 1 i))))
- (setmetatable {}
- {:cljlib/type :cljlib/ordered-set
- :cljlib/next #(next Set $2)
- :__eq set-eq
- :__call #(if (. Set $2) $2)
- :__len (set-length Set)
- :__index #(match $2
- :cljlib/empty #(ordered-set)
- _ (if (. Set $2) $2))
- :__newindex (ordered-set-newindex Set)
- :__ipairs set-ipairs
- :__pairs set-ipairs
- :__name "ordered set"
- :__fennelview viewset})))
-
-(fn* core.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.
-
-Hash set differs from ordered set in that the keys are do not have any
-particular order. New items are added at the arbitrary position by
-using [`conj`](#con) or `tset` functions, and items can be removed
-with [`disj`](#disj) or `tset` functions. Rest semantics are the same
-as for [`ordered-set`](#ordered-set)
-
-**Note**: Hash set prints as `#{a b c}`, but this construct is not
-supported by the Fennel reader, so you can't create sets with this
-syntax. Use `hash-set` function instead."
- [& xs]
- (let [Set (setmetatable {} {:__index deep-index})
- set-ipairs (hash-set-ipairs Set)]
- (each [_ val (ipairs xs)]
- (when (not (. Set val))
- (tset Set val true)))
- (setmetatable {}
- {:cljlib/type :cljlib/hash-set
- :cljlib/next #(next Set $2)
- :__eq set-eq
- :__call #(if (. Set $2) $2)
- :__len (set-length Set)
- :__index #(match $2
- :cljlib/empty #(hash-set)
- _ (if (. Set $2) $2))
- :__newindex (hash-set-newindex Set)
- :__ipairs set-ipairs
- :__pairs set-ipairs
- :__name "hash set"
- :__fennelview viewset})))
-
-(local set-doc-order
- [:ordered-set :hash-set])
-
-
-(doto core
- (tset :_DOC_ORDER (concat utility-doc-order
- [:eq]
- predicate-doc-order
- sequence-doc--order
- function-manipulation-doc-order
- hash-table-doc-order
- multimethods-doc-order
- set-doc-order)))
-
-;; LocalWords: cljlib Clojure's clj lua PUC mapv concat Clojure fn zs
-;; LocalWords: defmulti multi arity eq metadata prepending variadic
-;; LocalWords: args tbl LocalWords memoized referentially