2018-09-06 02:18:42 +00:00
|
|
|
# The core janet library
|
2019-01-06 08:23:03 +00:00
|
|
|
# Copyright 2019 (C) Calvin Rose
|
2018-01-31 22:39:18 +00:00
|
|
|
|
2018-03-28 17:50:06 +00:00
|
|
|
###
|
|
|
|
###
|
|
|
|
### Macros and Basic Functions
|
|
|
|
###
|
|
|
|
###
|
|
|
|
|
2019-01-18 17:04:34 +00:00
|
|
|
(var *env* "The current environment." _env)
|
2018-03-18 18:01:58 +00:00
|
|
|
|
2018-03-13 20:40:56 +00:00
|
|
|
(def defn :macro
|
2018-11-15 20:45:41 +00:00
|
|
|
"(def name & more)\n\nDefine a function. Equivalent to (def name (fn name [args] ...))."
|
2018-07-01 15:52:15 +00:00
|
|
|
(fn defn [name & more]
|
2018-07-02 01:12:46 +00:00
|
|
|
(def len (length more))
|
2018-11-15 20:45:41 +00:00
|
|
|
(def modifiers @[])
|
|
|
|
(var docstr "")
|
2018-07-04 03:07:35 +00:00
|
|
|
(def fstart
|
2018-07-02 01:12:46 +00:00
|
|
|
(fn recur [i]
|
|
|
|
(def {i ith} more)
|
|
|
|
(def t (type ith))
|
2018-11-25 19:03:00 +00:00
|
|
|
(if (= t :tuple)
|
2018-11-15 20:45:41 +00:00
|
|
|
i
|
2018-11-16 07:09:38 +00:00
|
|
|
(do
|
2018-11-25 19:03:00 +00:00
|
|
|
(if (= t :string)
|
2018-12-17 02:57:32 +00:00
|
|
|
(set docstr ith)
|
2018-12-01 03:49:21 +00:00
|
|
|
(array/push modifiers ith))
|
2018-11-15 20:45:41 +00:00
|
|
|
(if (< i len) (recur (+ i 1)))))))
|
2018-07-02 01:12:46 +00:00
|
|
|
(def start (fstart 0))
|
2019-01-07 00:33:27 +00:00
|
|
|
(def args (get more start))
|
2018-11-25 19:03:00 +00:00
|
|
|
# Add function signature to docstring
|
2018-11-15 20:45:41 +00:00
|
|
|
(var index 0)
|
|
|
|
(def arglen (length args))
|
|
|
|
(def buf (buffer "(" name))
|
|
|
|
(while (< index arglen)
|
2018-12-01 03:49:21 +00:00
|
|
|
(buffer/push-string buf " ")
|
2019-02-16 20:12:34 +00:00
|
|
|
(buffer/format buf "%p" (get args index))
|
2018-12-17 02:57:32 +00:00
|
|
|
(set index (+ index 1)))
|
2018-12-01 03:49:21 +00:00
|
|
|
(array/push modifiers (string buf ")\n\n" docstr))
|
2018-11-15 20:45:41 +00:00
|
|
|
# Build return value
|
2018-12-05 20:10:04 +00:00
|
|
|
~(def ,name ,;modifiers (fn ,name ,;(tuple/slice more start)))))
|
2018-03-12 04:26:13 +00:00
|
|
|
|
2018-11-15 20:45:41 +00:00
|
|
|
(defn defmacro :macro
|
2018-07-02 01:12:46 +00:00
|
|
|
"Define a macro."
|
2018-11-15 20:45:41 +00:00
|
|
|
[name & more]
|
2018-11-25 19:03:00 +00:00
|
|
|
(apply defn name :macro more))
|
2018-01-31 22:39:18 +00:00
|
|
|
|
2018-03-16 03:27:44 +00:00
|
|
|
(defmacro defmacro-
|
2018-07-02 01:12:46 +00:00
|
|
|
"Define a private macro that will not be exported."
|
|
|
|
[name & more]
|
2018-11-25 19:03:00 +00:00
|
|
|
(apply defn name :macro :private more))
|
2018-03-16 03:27:44 +00:00
|
|
|
|
2018-03-12 04:57:13 +00:00
|
|
|
(defmacro defn-
|
2018-07-02 01:12:46 +00:00
|
|
|
"Define a private function that will not be exported."
|
|
|
|
[name & more]
|
2018-11-25 19:03:00 +00:00
|
|
|
(apply defn name :private more))
|
2018-03-12 04:57:13 +00:00
|
|
|
|
2018-03-28 17:50:06 +00:00
|
|
|
(defmacro def-
|
2018-07-02 01:12:46 +00:00
|
|
|
"Define a private value that will not be exported."
|
|
|
|
[name & more]
|
2018-12-08 04:57:19 +00:00
|
|
|
~(def ,name :private ,;more))
|
2018-03-28 17:50:06 +00:00
|
|
|
|
2018-07-11 20:51:21 +00:00
|
|
|
(defn defglobal
|
|
|
|
"Dynamically create a global def."
|
|
|
|
[name value]
|
2018-07-17 02:55:45 +00:00
|
|
|
(def name* (symbol name))
|
2018-07-11 20:51:21 +00:00
|
|
|
(put *env* name* @{:value value})
|
|
|
|
nil)
|
|
|
|
|
|
|
|
(defn varglobal
|
|
|
|
"Dynamically create a global var."
|
|
|
|
[name init]
|
2018-07-17 02:55:45 +00:00
|
|
|
(def name* (symbol name))
|
2018-07-11 20:51:21 +00:00
|
|
|
(put *env* name* @{:ref @[init]})
|
|
|
|
nil)
|
|
|
|
|
2018-03-22 00:53:39 +00:00
|
|
|
# Basic predicates
|
2018-11-15 20:45:41 +00:00
|
|
|
(defn even? "Check if x is even." [x] (== 0 (% x 2)))
|
|
|
|
(defn odd? "Check if x is odd." [x] (not= 0 (% x 2)))
|
|
|
|
(defn zero? "Check if x is zero." [x] (== x 0))
|
|
|
|
(defn pos? "Check if x is greater than 0." [x] (> x 0))
|
|
|
|
(defn neg? "Check if x is less than 0." [x] (< x 0))
|
|
|
|
(defn one? "Check if x is equal to 1." [x] (== x 1))
|
2018-12-27 18:05:29 +00:00
|
|
|
(defn number? "Check if x is a number." [x] (= (type x) :number))
|
2018-11-15 20:45:41 +00:00
|
|
|
(defn fiber? "Check if x is a fiber." [x] (= (type x) :fiber))
|
|
|
|
(defn string? "Check if x is a string." [x] (= (type x) :string))
|
|
|
|
(defn symbol? "Check if x is a symbol." [x] (= (type x) :symbol))
|
2019-01-03 00:41:07 +00:00
|
|
|
(defn keyword? "Check if x is a keyword." [x] (= (type x) :keyword))
|
2018-11-15 20:45:41 +00:00
|
|
|
(defn buffer? "Check if x is a buffer." [x] (= (type x) :buffer))
|
2018-12-16 04:19:28 +00:00
|
|
|
(defn function? "Check if x is a function (not a cfunction)." [x]
|
|
|
|
(= (type x) :function))
|
2018-11-15 20:45:41 +00:00
|
|
|
(defn cfunction? "Check if x a cfunction." [x] (= (type x) :cfunction))
|
2019-01-17 23:10:04 +00:00
|
|
|
(defn table? "Check if x a table." [x] (= (type x) :table))
|
2018-12-16 04:19:28 +00:00
|
|
|
(defn struct? "Check if x a struct." [x] (= (type x) :struct))
|
|
|
|
(defn array? "Check if x is an array." [x] (= (type x) :array))
|
|
|
|
(defn tuple? "Check if x is a tuple." [x] (= (type x) :tuple))
|
|
|
|
(defn boolean? "Check if x is a boolean." [x] (= (type x) :boolean))
|
2018-11-15 20:45:41 +00:00
|
|
|
(defn bytes? "Check if x is a string, symbol, or buffer." [x]
|
2018-07-02 01:12:46 +00:00
|
|
|
(def t (type x))
|
2019-01-03 00:41:07 +00:00
|
|
|
(if (= t :string) true (if (= t :symbol) true (if (= t :keyword) true (= t :buffer)))))
|
2018-11-15 20:45:41 +00:00
|
|
|
(defn dictionary? "Check if x a table or struct." [x]
|
2018-07-02 01:12:46 +00:00
|
|
|
(def t (type x))
|
|
|
|
(if (= t :table) true (= t :struct)))
|
2018-11-15 20:45:41 +00:00
|
|
|
(defn indexed? "Check if x is an array or tuple." [x]
|
2018-07-02 01:12:46 +00:00
|
|
|
(def t (type x))
|
|
|
|
(if (= t :array) true (= t :tuple)))
|
2018-11-15 20:45:41 +00:00
|
|
|
(defn callable? "Check if x is a function or cfunction." [x]
|
2018-07-02 01:12:46 +00:00
|
|
|
(def t (type x))
|
|
|
|
(if (= t :function) true (= t :cfunction)))
|
2018-11-15 20:45:41 +00:00
|
|
|
(defn true? "Check if x is true." [x] (= x true))
|
|
|
|
(defn false? "Check if x is false." [x] (= x false))
|
|
|
|
(defn nil? "Check if x is nil." [x] (= x nil))
|
2018-11-29 18:30:59 +00:00
|
|
|
(defn empty? "Check if xs is empty." [xs] (= 0 (length xs)))
|
2018-12-08 15:53:22 +00:00
|
|
|
(def idempotent?
|
|
|
|
"(idempotent? x)\n\nCheck if x is a value that evaluates to itself when compiled."
|
2018-07-02 01:12:46 +00:00
|
|
|
(do
|
2018-07-04 03:07:35 +00:00
|
|
|
(def non-atomic-types
|
2018-07-02 01:12:46 +00:00
|
|
|
{:array true
|
|
|
|
:tuple true
|
|
|
|
:table true
|
2018-11-15 20:45:41 +00:00
|
|
|
:buffer true
|
2018-07-02 01:12:46 +00:00
|
|
|
:struct true})
|
2018-12-08 15:53:22 +00:00
|
|
|
(fn idempotent? [x] (not (get non-atomic-types (type x))))))
|
|
|
|
|
|
|
|
(defmacro with-idemp
|
|
|
|
"Return janet code body that has been prepended
|
2018-12-17 17:06:50 +00:00
|
|
|
with a binding of form to atom. If form is a non-idempotent
|
|
|
|
form (a function call, etc.), make sure the resulting
|
|
|
|
code will only evaluate once, even if body contains multiple
|
2018-12-08 15:53:22 +00:00
|
|
|
copies of binding. In body, use binding instead of form."
|
|
|
|
[binding form & body]
|
|
|
|
(def $result (gensym))
|
|
|
|
(def $form (gensym))
|
|
|
|
~(do
|
|
|
|
(def ,$form ,form)
|
|
|
|
(def ,binding (if (idempotent? ,$form) ,$form (gensym)))
|
|
|
|
(def ,$result (do ,;body))
|
|
|
|
(if (= ,$form ,binding)
|
|
|
|
,$result
|
|
|
|
(tuple 'do (tuple 'def ,binding ,$form) ,$result))))
|
2018-05-26 18:21:49 +00:00
|
|
|
|
2018-12-17 03:13:48 +00:00
|
|
|
# C style macros and functions for imperative sugar. No bitwise though.
|
2018-11-15 20:45:41 +00:00
|
|
|
(defn inc "Returns x + 1." [x] (+ x 1))
|
|
|
|
(defn dec "Returns x - 1." [x] (- x 1))
|
2018-12-17 02:57:32 +00:00
|
|
|
(defmacro ++ "Increments the var x by 1." [x] ~(set ,x (,+ ,x ,1)))
|
|
|
|
(defmacro -- "Decrements the var x by 1." [x] ~(set ,x (,- ,x ,1)))
|
|
|
|
(defmacro += "Increments the var x by n." [x n] ~(set ,x (,+ ,x ,n)))
|
2018-12-17 17:06:50 +00:00
|
|
|
(defmacro -= "Decrements the var x by n." [x n] ~(set ,x (,- ,x ,n)))
|
2018-12-17 02:57:32 +00:00
|
|
|
(defmacro *= "Shorthand for (set x (* x n))." [x n] ~(set ,x (,* ,x ,n)))
|
|
|
|
(defmacro /= "Shorthand for (set x (/ x n))." [x n] ~(set ,x (,/ ,x ,n)))
|
|
|
|
(defmacro %= "Shorthand for (set x (% x n))." [x n] ~(set ,x (,% ,x ,n)))
|
2018-03-16 22:31:03 +00:00
|
|
|
|
2018-05-02 03:38:53 +00:00
|
|
|
(defmacro default
|
2018-07-02 01:12:46 +00:00
|
|
|
"Define a default value for an optional argument.
|
|
|
|
Expands to (def sym (if (= nil sym) val sym))"
|
|
|
|
[sym val]
|
2018-12-01 03:49:21 +00:00
|
|
|
~(def ,sym (if (= nil ,sym) ,val ,sym)))
|
2018-05-02 03:38:53 +00:00
|
|
|
|
2018-03-12 06:06:51 +00:00
|
|
|
(defmacro comment
|
2018-07-02 01:12:46 +00:00
|
|
|
"Ignores the body of the comment."
|
|
|
|
[])
|
2018-03-12 06:06:51 +00:00
|
|
|
|
2018-03-28 17:50:06 +00:00
|
|
|
(defmacro if-not
|
2018-07-02 01:12:46 +00:00
|
|
|
"Shorthand for (if (not ... "
|
2018-12-29 22:23:31 +00:00
|
|
|
[condition exp-1 exp-2 &]
|
2018-12-01 03:49:21 +00:00
|
|
|
~(if ,condition ,exp-2 ,exp-1))
|
2018-03-28 17:50:06 +00:00
|
|
|
|
2018-01-31 22:39:18 +00:00
|
|
|
(defmacro when
|
2018-07-02 01:12:46 +00:00
|
|
|
"Evaluates the body when the condition is true. Otherwise returns nil."
|
|
|
|
[condition & body]
|
2018-12-05 20:10:04 +00:00
|
|
|
~(if ,condition (do ,;body)))
|
2018-03-28 17:50:06 +00:00
|
|
|
|
2018-07-02 01:12:46 +00:00
|
|
|
(defmacro unless
|
|
|
|
"Shorthand for (when (not ... "
|
2018-03-28 17:50:06 +00:00
|
|
|
[condition & body]
|
2018-12-05 20:10:04 +00:00
|
|
|
~(if ,condition nil (do ,;body)))
|
2018-01-31 22:39:18 +00:00
|
|
|
|
2018-03-12 04:26:13 +00:00
|
|
|
(defmacro cond
|
2018-07-02 01:12:46 +00:00
|
|
|
"Evaluates conditions sequentially until the first true condition
|
|
|
|
is found, and then executes the corresponding body. If there are an
|
|
|
|
odd number of forms, the last expression is executed if no forms
|
|
|
|
are matched. If there are no matches, return nil."
|
|
|
|
[& pairs]
|
|
|
|
(defn aux [i]
|
|
|
|
(def restlen (- (length pairs) i))
|
|
|
|
(if (= restlen 0) nil
|
2019-01-07 00:33:27 +00:00
|
|
|
(if (= restlen 1) (get pairs i)
|
|
|
|
(tuple 'if (get pairs i)
|
2018-07-02 01:12:46 +00:00
|
|
|
(get pairs (+ i 1))
|
|
|
|
(aux (+ i 2))))))
|
|
|
|
(aux 0))
|
2018-03-12 04:26:13 +00:00
|
|
|
|
2018-07-04 05:28:31 +00:00
|
|
|
(defmacro case
|
2018-07-02 01:12:46 +00:00
|
|
|
"Select the body that equals the dispatch value. When pairs
|
|
|
|
has an odd number of arguments, the last is the default expression.
|
|
|
|
If no match is found, returns nil"
|
|
|
|
[dispatch & pairs]
|
2018-12-08 15:53:22 +00:00
|
|
|
(def atm (idempotent? dispatch))
|
2018-03-28 17:50:06 +00:00
|
|
|
(def sym (if atm dispatch (gensym)))
|
2018-03-12 04:26:13 +00:00
|
|
|
(defn aux [i]
|
|
|
|
(def restlen (- (length pairs) i))
|
|
|
|
(if (= restlen 0) nil
|
2019-01-07 00:33:27 +00:00
|
|
|
(if (= restlen 1) (get pairs i)
|
|
|
|
(tuple 'if (tuple = sym (get pairs i))
|
2018-07-02 01:12:46 +00:00
|
|
|
(get pairs (+ i 1))
|
|
|
|
(aux (+ i 2))))))
|
2018-03-28 17:50:06 +00:00
|
|
|
(if atm
|
2018-07-02 01:12:46 +00:00
|
|
|
(aux 0)
|
|
|
|
(tuple 'do
|
|
|
|
(tuple 'def sym dispatch)
|
|
|
|
(aux 0))))
|
2018-03-28 17:50:06 +00:00
|
|
|
|
|
|
|
(defmacro let
|
2018-07-02 01:12:46 +00:00
|
|
|
"Create a scope and bind values to symbols. Each pair in bindings is
|
|
|
|
assigned as if with def, and the body of the let form returns the last
|
|
|
|
value."
|
|
|
|
[bindings & body]
|
2018-06-29 03:36:31 +00:00
|
|
|
(if (odd? (length bindings)) (error "expected even number of bindings to let"))
|
|
|
|
(def len (length bindings))
|
2018-03-24 16:48:42 +00:00
|
|
|
(var i 0)
|
|
|
|
(var accum @['do])
|
2018-03-12 04:26:13 +00:00
|
|
|
(while (< i len)
|
2018-07-02 01:12:46 +00:00
|
|
|
(def {i k (+ i 1) v} bindings)
|
2018-12-01 03:49:21 +00:00
|
|
|
(array/push accum (tuple 'def k v))
|
2018-07-02 01:12:46 +00:00
|
|
|
(+= i 2))
|
2018-12-01 03:49:21 +00:00
|
|
|
(array/concat accum body)
|
|
|
|
(tuple/slice accum 0))
|
2018-02-03 22:22:04 +00:00
|
|
|
|
2018-12-25 20:32:42 +00:00
|
|
|
(defmacro try
|
|
|
|
"Try something and catch errors. Body is any expression,
|
|
|
|
and catch should be a form with the first element a tuple. This tuple
|
2018-12-25 20:39:24 +00:00
|
|
|
should contain a binding for errors and an optional binding for
|
2018-12-25 20:32:42 +00:00
|
|
|
the fiber wrapping the body. Returns the result of body if no error,
|
|
|
|
or the result of catch if an error."
|
|
|
|
[body catch]
|
|
|
|
(let [[[err fib]] catch
|
|
|
|
f (gensym)
|
|
|
|
r (gensym)]
|
|
|
|
~(let [,f (,fiber/new (fn [] ,body) :e)
|
|
|
|
,r (resume ,f)]
|
|
|
|
(if (= (,fiber/status ,f) :error)
|
|
|
|
(do (def ,err ,r) ,(if fib ~(def ,fib ,f)) ,;(tuple/slice catch 1))
|
|
|
|
,r))))
|
|
|
|
|
2018-07-10 03:29:15 +00:00
|
|
|
(defmacro and
|
|
|
|
"Evaluates to the last argument if all preceding elements are true, otherwise
|
|
|
|
evaluates to false."
|
|
|
|
[& forms]
|
2018-11-30 18:05:28 +00:00
|
|
|
(var ret true)
|
2018-07-10 03:29:15 +00:00
|
|
|
(def len (length forms))
|
2018-11-30 18:05:28 +00:00
|
|
|
(var i len)
|
|
|
|
(while (> i 0)
|
|
|
|
(-- i)
|
2018-12-17 02:57:32 +00:00
|
|
|
(set ret (if (= ret true)
|
2019-01-07 00:33:27 +00:00
|
|
|
(get forms i)
|
|
|
|
(tuple 'if (get forms i) ret))))
|
2018-11-30 18:05:28 +00:00
|
|
|
ret)
|
2018-07-10 03:29:15 +00:00
|
|
|
|
|
|
|
(defmacro or
|
|
|
|
"Evaluates to the last argument if all preceding elements are false, otherwise
|
|
|
|
evaluates to true."
|
|
|
|
[& forms]
|
2018-11-30 18:05:28 +00:00
|
|
|
(var ret nil)
|
2018-07-10 03:29:15 +00:00
|
|
|
(def len (length forms))
|
2018-11-30 18:05:28 +00:00
|
|
|
(var i len)
|
|
|
|
(while (> i 0)
|
|
|
|
(-- i)
|
2019-01-07 00:33:27 +00:00
|
|
|
(def fi (get forms i))
|
2018-12-17 02:57:32 +00:00
|
|
|
(set ret (if (idempotent? fi)
|
2018-11-30 18:05:28 +00:00
|
|
|
(tuple 'if fi fi ret)
|
|
|
|
(do
|
|
|
|
(def $fi (gensym))
|
|
|
|
(tuple 'do (tuple 'def $fi fi)
|
|
|
|
(tuple 'if $fi $fi ret))))))
|
|
|
|
ret)
|
2018-07-10 03:29:15 +00:00
|
|
|
|
2018-05-24 02:08:36 +00:00
|
|
|
(defmacro loop
|
2018-11-19 20:33:19 +00:00
|
|
|
"A general purpose loop macro. This macro is similar to the Common Lisp
|
2018-11-29 18:30:59 +00:00
|
|
|
loop macro, although intentionally much smaller in scope.
|
|
|
|
The head of the loop should be a tuple that contains a sequence of
|
2018-11-18 14:55:31 +00:00
|
|
|
either bindings or conditionals. A binding is a sequence of three values
|
2018-11-29 18:30:59 +00:00
|
|
|
that define something to loop over. They are formatted like:\n\n
|
2018-11-18 14:55:31 +00:00
|
|
|
\tbinding :verb object/expression\n\n
|
|
|
|
Where binding is a binding as passed to def, :verb is one of a set of keywords,
|
|
|
|
and object is any janet expression. The available verbs are:\n\n
|
|
|
|
\t:iterate - repeatedly evaluate and bind to the expression while it is truthy.\n
|
|
|
|
\t:range - loop over a range. The object should be two element tuple with a start
|
|
|
|
and end value. The range is half open, [start, end).\n
|
|
|
|
\t:keys - Iterate over the keys in a data structure.\n
|
2018-12-29 22:23:31 +00:00
|
|
|
\t:pairs - Iterate over the keys value pairs in a data structure.\n
|
2018-11-21 02:48:06 +00:00
|
|
|
\t:in - Iterate over the values in an indexed data structure or byte sequence.\n
|
|
|
|
\t:generate - Iterate over values yielded from a fiber. Can be paired with the generator
|
|
|
|
function for the producer/consumer pattern.\n\n
|
2018-11-18 14:55:31 +00:00
|
|
|
loop also accepts conditionals to refine the looping further. Conditionals are of
|
|
|
|
the form:\n\n
|
|
|
|
\t:modifier argument\n\n
|
|
|
|
where :modifier is one of a set of keywords, and argument is keyword dependent.
|
|
|
|
:modifier can be one of:\n\n
|
|
|
|
\t:while expression - breaks from the loop if expression is falsey.\n
|
|
|
|
\t:let bindings - defines bindings inside the loop as passed to the let macro.\n
|
2018-11-19 07:15:21 +00:00
|
|
|
\t:before form - evaluates a form for a side effect before of the next inner loop.\n
|
2018-11-19 19:49:10 +00:00
|
|
|
\t:after form - same as :before, but the side effect happens after the next inner loop.\n
|
|
|
|
\t:repeat n - repeats the next inner loop n times.\n
|
2018-11-18 14:55:31 +00:00
|
|
|
\t:when condition - only evaluates the loop body when condition is true.\n\n
|
|
|
|
The loop macro always evaluates to nil."
|
2018-05-24 02:08:36 +00:00
|
|
|
[head & body]
|
2018-06-29 03:36:31 +00:00
|
|
|
(def len (length head))
|
2018-11-19 07:15:21 +00:00
|
|
|
(if (not= :tuple (type head))
|
|
|
|
(error "expected tuple for loop head"))
|
2018-05-24 02:08:36 +00:00
|
|
|
(defn doone
|
2018-11-25 19:03:00 +00:00
|
|
|
[i preds &]
|
2018-05-26 17:46:27 +00:00
|
|
|
(default preds @['and])
|
2018-05-24 02:08:36 +00:00
|
|
|
(if (>= i len)
|
2018-12-01 03:49:21 +00:00
|
|
|
(tuple/prepend body 'do)
|
2018-05-24 02:08:36 +00:00
|
|
|
(do
|
2018-11-21 02:48:06 +00:00
|
|
|
(def {i bindings
|
2018-07-02 01:12:46 +00:00
|
|
|
(+ i 1) verb
|
2018-11-21 02:48:06 +00:00
|
|
|
(+ i 2) object} head)
|
2018-06-29 03:36:31 +00:00
|
|
|
(if (keyword? bindings)
|
2018-11-15 23:28:55 +00:00
|
|
|
(case bindings
|
2018-05-26 17:46:27 +00:00
|
|
|
:while (do
|
2018-12-01 03:49:21 +00:00
|
|
|
(array/push preds verb)
|
2018-05-26 17:46:27 +00:00
|
|
|
(doone (+ i 2) preds))
|
2018-11-24 04:17:45 +00:00
|
|
|
:let (tuple 'let verb (doone (+ i 2) preds))
|
|
|
|
:when (tuple 'if verb (doone (+ i 2) preds))
|
|
|
|
:before (tuple 'do verb (doone (+ i 2) preds))
|
|
|
|
:after (tuple 'do (doone (+ i 2) preds) verb)
|
2018-11-19 19:49:10 +00:00
|
|
|
:repeat (do
|
|
|
|
(def $iter (gensym))
|
|
|
|
(def $n (gensym))
|
|
|
|
(def spreds @['and (tuple < $iter $n)])
|
|
|
|
(def sub (doone (+ i 2) spreds))
|
|
|
|
(tuple 'do
|
|
|
|
(tuple 'def $n verb)
|
|
|
|
(tuple 'var $iter 0)
|
|
|
|
(tuple 'while
|
2018-12-01 03:49:21 +00:00
|
|
|
(tuple/slice spreds)
|
2018-12-17 02:57:32 +00:00
|
|
|
(tuple 'set $iter (tuple + 1 $iter))
|
2018-11-19 19:49:10 +00:00
|
|
|
sub)))
|
|
|
|
(error (string "unexpected loop predicate: " bindings)))
|
2018-07-04 05:28:31 +00:00
|
|
|
(case verb
|
2018-06-30 02:52:55 +00:00
|
|
|
:iterate (do
|
2018-07-12 02:18:24 +00:00
|
|
|
(def $iter (gensym))
|
2018-12-17 02:57:32 +00:00
|
|
|
(def preds @['and (tuple 'set $iter object)])
|
2018-07-02 01:12:46 +00:00
|
|
|
(def subloop (doone (+ i 3) preds))
|
|
|
|
(tuple 'do
|
2018-07-12 02:18:24 +00:00
|
|
|
(tuple 'var $iter nil)
|
2018-12-01 03:49:21 +00:00
|
|
|
(tuple 'while (tuple/slice preds)
|
2018-07-12 02:18:24 +00:00
|
|
|
(tuple 'def bindings $iter)
|
2018-07-02 01:12:46 +00:00
|
|
|
subloop)))
|
2018-05-24 03:43:48 +00:00
|
|
|
:range (do
|
2018-06-29 03:36:31 +00:00
|
|
|
(def [start end _inc] object)
|
2018-05-24 03:43:48 +00:00
|
|
|
(def inc (if _inc _inc 1))
|
|
|
|
(def endsym (gensym))
|
2018-07-12 02:18:24 +00:00
|
|
|
(def $iter (gensym))
|
|
|
|
(def preds @['and (tuple < $iter endsym)])
|
2018-05-26 17:46:27 +00:00
|
|
|
(def subloop (doone (+ i 3) preds))
|
2018-05-24 03:43:48 +00:00
|
|
|
(tuple 'do
|
2018-07-12 02:18:24 +00:00
|
|
|
(tuple 'var $iter start)
|
2018-05-24 03:43:48 +00:00
|
|
|
(tuple 'def endsym end)
|
2018-12-01 03:49:21 +00:00
|
|
|
(tuple 'while (tuple/slice preds)
|
2018-07-12 02:18:24 +00:00
|
|
|
(tuple 'def bindings $iter)
|
2018-05-26 17:46:27 +00:00
|
|
|
subloop
|
2018-12-17 02:57:32 +00:00
|
|
|
(tuple 'set $iter (tuple + $iter inc)))))
|
2018-05-24 03:43:48 +00:00
|
|
|
:keys (do
|
2018-07-01 19:49:33 +00:00
|
|
|
(def $dict (gensym))
|
2018-07-12 02:18:24 +00:00
|
|
|
(def $iter (gensym))
|
|
|
|
(def preds @['and (tuple not= nil $iter)])
|
2018-05-26 17:46:27 +00:00
|
|
|
(def subloop (doone (+ i 3) preds))
|
2018-05-24 03:43:48 +00:00
|
|
|
(tuple 'do
|
|
|
|
(tuple 'def $dict object)
|
2018-07-12 02:18:24 +00:00
|
|
|
(tuple 'var $iter (tuple next $dict nil))
|
2018-12-01 03:49:21 +00:00
|
|
|
(tuple 'while (tuple/slice preds)
|
2018-07-12 02:18:24 +00:00
|
|
|
(tuple 'def bindings $iter)
|
2018-05-26 17:46:27 +00:00
|
|
|
subloop
|
2018-12-17 02:57:32 +00:00
|
|
|
(tuple 'set $iter (tuple next $dict $iter)))))
|
2018-12-29 22:23:31 +00:00
|
|
|
:pairs (do
|
|
|
|
(def sym? (symbol? bindings))
|
|
|
|
(def $dict (gensym))
|
|
|
|
(def $iter (gensym))
|
|
|
|
(def preds @['and (tuple not= nil $iter)])
|
|
|
|
(def subloop (doone (+ i 3) preds))
|
|
|
|
(tuple 'do
|
|
|
|
(tuple 'def $dict object)
|
|
|
|
(tuple 'var $iter (tuple next $dict nil))
|
|
|
|
(tuple 'while (tuple/slice preds)
|
|
|
|
(if sym?
|
|
|
|
(tuple 'def bindings (tuple tuple $iter (tuple get $dict $iter))))
|
|
|
|
(if-not sym? (tuple 'def (get bindings 0) $iter))
|
|
|
|
(if-not sym? (tuple 'def (get bindings 1) (tuple get $dict $iter)))
|
|
|
|
subloop
|
|
|
|
(tuple 'set $iter (tuple next $dict $iter)))))
|
2018-05-24 03:43:48 +00:00
|
|
|
:in (do
|
2018-07-01 19:49:33 +00:00
|
|
|
(def $len (gensym))
|
|
|
|
(def $i (gensym))
|
|
|
|
(def $indexed (gensym))
|
2018-05-26 17:46:27 +00:00
|
|
|
(def preds @['and (tuple < $i $len)])
|
|
|
|
(def subloop (doone (+ i 3) preds))
|
2018-05-24 02:08:36 +00:00
|
|
|
(tuple 'do
|
2018-05-24 03:43:48 +00:00
|
|
|
(tuple 'def $indexed object)
|
|
|
|
(tuple 'def $len (tuple length $indexed))
|
|
|
|
(tuple 'var $i 0)
|
2018-12-01 03:49:21 +00:00
|
|
|
(tuple 'while (tuple/slice preds 0)
|
2018-05-24 03:43:48 +00:00
|
|
|
(tuple 'def bindings (tuple get $indexed $i))
|
2018-05-26 17:46:27 +00:00
|
|
|
subloop
|
2018-12-17 02:57:32 +00:00
|
|
|
(tuple 'set $i (tuple + 1 $i)))))
|
2018-11-21 02:48:06 +00:00
|
|
|
:generate (do
|
|
|
|
(def $fiber (gensym))
|
|
|
|
(def $yieldval (gensym))
|
2018-11-26 14:02:07 +00:00
|
|
|
(def preds @['and
|
2018-11-21 02:48:06 +00:00
|
|
|
(do
|
|
|
|
(def s (gensym))
|
|
|
|
(tuple 'do
|
2018-12-01 03:49:21 +00:00
|
|
|
(tuple 'def s (tuple fiber/status $fiber))
|
2018-11-21 02:48:06 +00:00
|
|
|
(tuple 'or (tuple = s :pending) (tuple = s :new))))])
|
|
|
|
(def subloop (doone (+ i 3) preds))
|
|
|
|
(tuple 'do
|
|
|
|
(tuple 'def $fiber object)
|
|
|
|
(tuple 'var $yieldval (tuple resume $fiber))
|
2018-12-01 03:49:21 +00:00
|
|
|
(tuple 'while (tuple/slice preds 0)
|
2018-11-21 02:48:06 +00:00
|
|
|
(tuple 'def bindings $yieldval)
|
|
|
|
subloop
|
2018-12-17 02:57:32 +00:00
|
|
|
(tuple 'set $yieldval (tuple resume $fiber)))))
|
2018-07-12 01:29:39 +00:00
|
|
|
(error (string "unexpected loop verb: " verb)))))))
|
2018-12-05 16:25:36 +00:00
|
|
|
(doone 0 nil))
|
2018-05-24 02:08:36 +00:00
|
|
|
|
2018-11-29 18:30:59 +00:00
|
|
|
(defmacro seq
|
2018-11-18 14:55:31 +00:00
|
|
|
"Similar to loop, but accumulates the loop body into an array and returns that.
|
|
|
|
See loop for details."
|
2018-05-24 02:08:36 +00:00
|
|
|
[head & body]
|
2018-07-01 19:49:33 +00:00
|
|
|
(def $accum (gensym))
|
2018-12-08 15:53:22 +00:00
|
|
|
~(do (def ,$accum @[]) (loop ,head (array/push ,$accum (do ,;body))) ,$accum))
|
2018-02-07 05:44:51 +00:00
|
|
|
|
2018-11-21 02:48:06 +00:00
|
|
|
(defmacro generate
|
|
|
|
"Create a generator expression using the loop syntax. Returns a fiber
|
|
|
|
that yields all values inside the loop in order. See loop for details."
|
|
|
|
[head & body]
|
2018-12-05 20:10:04 +00:00
|
|
|
~(fiber/new (fn [&] (loop ,head (yield (do ,;body))))))
|
2018-11-21 02:48:06 +00:00
|
|
|
|
2018-11-29 18:30:59 +00:00
|
|
|
(defmacro for
|
|
|
|
"Do a c style for loop for side effects. Returns nil."
|
|
|
|
[binding start end & body]
|
2019-02-08 20:49:28 +00:00
|
|
|
(apply loop (tuple binding :range (tuple start end)) body))
|
2018-11-29 18:30:59 +00:00
|
|
|
|
|
|
|
(defmacro each
|
|
|
|
"Loop over each value in ind. Returns nil."
|
|
|
|
[binding ind & body]
|
2019-02-08 20:49:28 +00:00
|
|
|
(apply loop (tuple binding :in ind) body))
|
2018-11-29 18:30:59 +00:00
|
|
|
|
2018-12-05 20:10:04 +00:00
|
|
|
(defmacro coro
|
|
|
|
"A wrapper for making fibers. Same as (fiber/new (fn [&] ...body))."
|
|
|
|
[& body]
|
|
|
|
(tuple fiber/new (tuple 'fn '[&] ;body)))
|
|
|
|
|
|
|
|
(defn sum
|
2018-12-16 04:19:28 +00:00
|
|
|
"Returns the sum of xs. If xs is empty, returns 0."
|
2018-12-05 20:10:04 +00:00
|
|
|
[xs]
|
2018-07-10 10:51:05 +00:00
|
|
|
(var accum 0)
|
2018-07-10 03:29:15 +00:00
|
|
|
(loop [x :in xs] (+= accum x))
|
|
|
|
accum)
|
2018-03-28 17:50:06 +00:00
|
|
|
|
2018-12-05 20:10:04 +00:00
|
|
|
(defn product
|
2018-12-16 04:19:28 +00:00
|
|
|
"Returns the product of xs. If xs is empty, returns 1."
|
2018-12-05 20:10:04 +00:00
|
|
|
[xs]
|
2018-07-10 10:51:05 +00:00
|
|
|
(var accum 1)
|
2018-07-10 03:29:15 +00:00
|
|
|
(loop [x :in xs] (*= accum x))
|
|
|
|
accum)
|
2018-03-28 17:50:06 +00:00
|
|
|
|
|
|
|
(defmacro if-let
|
2019-01-06 08:23:03 +00:00
|
|
|
"Make multiple bindings, and if all are truthy,
|
2018-12-17 17:06:50 +00:00
|
|
|
evaluate the tru form. If any are false or nil, evaluate
|
|
|
|
the fal form. Bindings have the same syntax as the let macro."
|
2018-11-25 19:03:00 +00:00
|
|
|
[bindings tru fal &]
|
2018-03-28 17:50:06 +00:00
|
|
|
(def len (length bindings))
|
|
|
|
(if (zero? len) (error "expected at least 1 binding"))
|
|
|
|
(if (odd? len) (error "expected an even number of bindings"))
|
|
|
|
(defn aux [i]
|
2019-01-07 00:33:27 +00:00
|
|
|
(def bl (get bindings i))
|
2018-03-28 17:50:06 +00:00
|
|
|
(def br (get bindings (+ 1 i)))
|
|
|
|
(if (>= i len)
|
2018-07-02 01:12:46 +00:00
|
|
|
tru
|
|
|
|
(do
|
2018-12-08 15:53:22 +00:00
|
|
|
(def atm (idempotent? bl))
|
2018-07-02 01:12:46 +00:00
|
|
|
(def sym (if atm bl (gensym)))
|
|
|
|
(if atm
|
|
|
|
# Simple binding
|
|
|
|
(tuple 'do
|
|
|
|
(tuple 'def sym br)
|
|
|
|
(tuple 'if sym (aux (+ 2 i)) fal))
|
|
|
|
# Destructured binding
|
|
|
|
(tuple 'do
|
|
|
|
(tuple 'def sym br)
|
|
|
|
(tuple 'if sym
|
|
|
|
(tuple 'do
|
|
|
|
(tuple 'def bl sym)
|
|
|
|
(aux (+ 2 i)))
|
|
|
|
fal))))))
|
2018-03-28 17:50:06 +00:00
|
|
|
(aux 0))
|
|
|
|
|
|
|
|
(defmacro when-let
|
2018-12-17 17:06:50 +00:00
|
|
|
"Same as (if-let bindings (do ;body))."
|
2018-03-28 17:50:06 +00:00
|
|
|
[bindings & body]
|
2018-12-05 20:10:04 +00:00
|
|
|
~(if-let ,bindings (do ,;body)))
|
2018-03-28 17:50:06 +00:00
|
|
|
|
|
|
|
(defn comp
|
2018-07-02 01:12:46 +00:00
|
|
|
"Takes multiple functions and returns a function that is the composition
|
|
|
|
of those functions."
|
|
|
|
[& functions]
|
2018-07-04 05:28:31 +00:00
|
|
|
(case (length functions)
|
2018-07-02 01:12:46 +00:00
|
|
|
0 nil
|
2018-12-29 04:44:39 +00:00
|
|
|
1 (get functions 0)
|
2018-07-02 01:12:46 +00:00
|
|
|
2 (let [[f g] functions] (fn [x] (f (g x))))
|
|
|
|
3 (let [[f g h] functions] (fn [x] (f (g (h x)))))
|
|
|
|
4 (let [[f g h i] functions] (fn [x] (f (g (h (i x))))))
|
2018-03-28 17:50:06 +00:00
|
|
|
(let [[f g h i j] functions]
|
2018-12-05 20:10:04 +00:00
|
|
|
(comp (fn [x] (f (g (h (i (j x))))))
|
|
|
|
;(tuple/slice functions 5 -1)))))
|
2018-03-28 17:50:06 +00:00
|
|
|
|
|
|
|
(defn identity
|
2018-07-02 01:12:46 +00:00
|
|
|
"A function that returns its first argument."
|
|
|
|
[x]
|
|
|
|
x)
|
2018-03-28 17:50:06 +00:00
|
|
|
|
|
|
|
(defn complement
|
2018-07-02 01:12:46 +00:00
|
|
|
"Returns a function that is the complement to the argument."
|
|
|
|
[f]
|
|
|
|
(fn [x] (not (f x))))
|
2018-03-28 17:50:06 +00:00
|
|
|
|
2018-04-01 19:08:51 +00:00
|
|
|
(defn extreme
|
2018-11-29 18:30:59 +00:00
|
|
|
"Returns the most extreme value in args based on the function order.
|
|
|
|
order should take two values and return true or false (a comparison).
|
2018-07-02 01:12:46 +00:00
|
|
|
Returns nil if args is empty."
|
|
|
|
[order args]
|
|
|
|
(def len (length args))
|
|
|
|
(when (pos? len)
|
2018-12-29 04:44:39 +00:00
|
|
|
(var [ret] args)
|
2018-07-02 01:12:46 +00:00
|
|
|
(loop [i :range [0 len]]
|
2019-01-07 00:33:27 +00:00
|
|
|
(def v (get args i))
|
2018-12-17 02:57:32 +00:00
|
|
|
(if (order v ret) (set ret v)))
|
2018-07-02 01:12:46 +00:00
|
|
|
ret))
|
2018-04-01 19:08:51 +00:00
|
|
|
|
2018-12-16 18:17:30 +00:00
|
|
|
(defn max
|
2018-12-16 04:19:28 +00:00
|
|
|
"Returns the numeric maximum of the arguments."
|
|
|
|
[& args] (extreme > args))
|
|
|
|
|
|
|
|
(defn min
|
|
|
|
"Returns the numeric minimum of the arguments."
|
|
|
|
[& args] (extreme < args))
|
|
|
|
|
2018-12-16 18:17:30 +00:00
|
|
|
(defn max-order
|
2018-12-16 04:19:28 +00:00
|
|
|
"Returns the maximum of the arguments according to a total
|
|
|
|
order over all values."
|
|
|
|
[& args] (extreme order> args))
|
|
|
|
|
|
|
|
(defn min-order
|
|
|
|
"Returns the minimum of the arguments according to a total
|
|
|
|
order over all values."
|
|
|
|
[& args] (extreme order< args))
|
2018-04-01 19:08:51 +00:00
|
|
|
|
2018-11-29 18:30:59 +00:00
|
|
|
(defn first
|
|
|
|
"Get the first element from an indexed data structure."
|
|
|
|
[xs]
|
2018-12-29 04:44:39 +00:00
|
|
|
(get xs 0))
|
2018-11-29 18:30:59 +00:00
|
|
|
|
|
|
|
(defn last
|
|
|
|
"Get the last element from an indexed data structure."
|
|
|
|
[xs]
|
|
|
|
(get xs (- (length xs) 1)))
|
|
|
|
|
2018-03-28 17:50:06 +00:00
|
|
|
###
|
|
|
|
###
|
2018-03-28 20:38:05 +00:00
|
|
|
### Indexed Combinators
|
2018-03-28 17:50:06 +00:00
|
|
|
###
|
|
|
|
###
|
|
|
|
|
2018-05-05 18:41:47 +00:00
|
|
|
(def sort
|
2019-01-06 08:23:03 +00:00
|
|
|
"(sort xs [, by])\n\nSort an array in-place. Uses quick-sort and is not a stable sort."
|
2018-07-02 01:12:46 +00:00
|
|
|
(do
|
|
|
|
|
|
|
|
(defn partition
|
|
|
|
[a lo hi by]
|
2019-01-07 00:33:27 +00:00
|
|
|
(def pivot (get a hi))
|
2018-07-02 01:12:46 +00:00
|
|
|
(var i lo)
|
|
|
|
(loop [j :range [lo hi]]
|
2019-01-07 00:33:27 +00:00
|
|
|
(def aj (get a j))
|
2018-07-17 02:55:45 +00:00
|
|
|
(when (by aj pivot)
|
2019-01-07 00:33:27 +00:00
|
|
|
(def ai (get a i))
|
|
|
|
(set (a i) aj)
|
|
|
|
(set (a j) ai)
|
2018-07-17 02:55:45 +00:00
|
|
|
(++ i)))
|
2019-01-07 00:33:27 +00:00
|
|
|
(set (a hi) (get a i))
|
|
|
|
(set (a i) pivot)
|
2018-07-02 01:12:46 +00:00
|
|
|
i)
|
|
|
|
|
|
|
|
(defn sort-help
|
|
|
|
[a lo hi by]
|
|
|
|
(when (> hi lo)
|
|
|
|
(def piv (partition a lo hi by))
|
|
|
|
(sort-help a lo (- piv 1) by)
|
|
|
|
(sort-help a (+ piv 1) hi by))
|
|
|
|
a)
|
|
|
|
|
2018-12-16 04:19:28 +00:00
|
|
|
(fn sort [a by &]
|
2018-07-02 01:12:46 +00:00
|
|
|
(sort-help a 0 (- (length a) 1) (or by order<)))))
|
2018-03-28 20:38:05 +00:00
|
|
|
|
|
|
|
(defn sorted
|
2018-11-29 18:30:59 +00:00
|
|
|
"Returns a new sorted array without modifying the old one."
|
|
|
|
[ind by]
|
2018-12-01 03:49:21 +00:00
|
|
|
(sort (array/slice ind) by))
|
2018-03-28 20:38:05 +00:00
|
|
|
|
2018-03-28 17:50:06 +00:00
|
|
|
(defn reduce
|
2018-07-02 01:12:46 +00:00
|
|
|
"Reduce, also know as fold-left in many languages, transforms
|
|
|
|
an indexed type (array, tuple) with a function to produce a value."
|
2018-11-29 18:30:59 +00:00
|
|
|
[f init ind]
|
2018-07-02 01:12:46 +00:00
|
|
|
(var res init)
|
|
|
|
(loop [x :in ind]
|
2018-12-17 02:57:32 +00:00
|
|
|
(set res (f res x)))
|
2018-07-02 01:12:46 +00:00
|
|
|
res)
|
2018-03-26 17:36:58 +00:00
|
|
|
|
2018-11-29 18:30:59 +00:00
|
|
|
(defn map
|
2018-08-23 15:10:48 +00:00
|
|
|
"Map a function over every element in an indexed data structure and
|
|
|
|
return an array of the results."
|
2018-07-02 01:12:46 +00:00
|
|
|
[f & inds]
|
|
|
|
(def ninds (length inds))
|
|
|
|
(if (= 0 ninds) (error "expected at least 1 indexed collection"))
|
2018-12-29 04:44:39 +00:00
|
|
|
(var limit (length (get inds 0)))
|
2018-07-02 01:12:46 +00:00
|
|
|
(loop [i :range [0 ninds]]
|
2019-01-07 00:33:27 +00:00
|
|
|
(def l (length (get inds i)))
|
2018-12-17 02:57:32 +00:00
|
|
|
(if (< l limit) (set limit l)))
|
2018-07-02 01:12:46 +00:00
|
|
|
(def [i1 i2 i3 i4] inds)
|
2018-12-01 03:49:21 +00:00
|
|
|
(def res (array/new limit))
|
2018-07-04 05:28:31 +00:00
|
|
|
(case ninds
|
2019-01-07 00:33:27 +00:00
|
|
|
1 (loop [i :range [0 limit]] (set (res i) (f (get i1 i))))
|
|
|
|
2 (loop [i :range [0 limit]] (set (res i) (f (get i1 i) (get i2 i))))
|
|
|
|
3 (loop [i :range [0 limit]] (set (res i) (f (get i1 i) (get i2 i) (get i3 i))))
|
|
|
|
4 (loop [i :range [0 limit]] (set (res i) (f (get i1 i) (get i2 i) (get i3 i) (get i4 i))))
|
2018-07-02 01:12:46 +00:00
|
|
|
(loop [i :range [0 limit]]
|
2018-12-01 03:49:21 +00:00
|
|
|
(def args (array/new ninds))
|
2019-01-07 00:33:27 +00:00
|
|
|
(loop [j :range [0 ninds]] (set (args j) (get (get inds j) i)))
|
|
|
|
(set (res i) (f ;args))))
|
2018-07-02 01:12:46 +00:00
|
|
|
res)
|
2018-03-28 17:50:06 +00:00
|
|
|
|
|
|
|
(defn mapcat
|
2018-07-02 01:12:46 +00:00
|
|
|
"Map a function over every element in an array or tuple and
|
2018-11-29 18:30:59 +00:00
|
|
|
use array to concatenate the results."
|
|
|
|
[f ind]
|
2018-07-02 01:12:46 +00:00
|
|
|
(def res @[])
|
|
|
|
(loop [x :in ind]
|
2018-12-01 03:49:21 +00:00
|
|
|
(array/concat res (f x)))
|
2018-11-29 18:30:59 +00:00
|
|
|
res)
|
2018-03-28 17:50:06 +00:00
|
|
|
|
2019-01-02 15:23:11 +00:00
|
|
|
(defmacro with-syms
|
|
|
|
"Evaluates body with each symbol in syms bound to a generated, unique symbol."
|
|
|
|
[syms & body]
|
|
|
|
~(let ,(mapcat (fn [s] @[s (tuple gensym)]) syms) ,;body))
|
|
|
|
|
2018-03-28 17:50:06 +00:00
|
|
|
(defn filter
|
2018-07-02 01:12:46 +00:00
|
|
|
"Given a predicate, take only elements from an array or tuple for
|
2018-11-29 18:30:59 +00:00
|
|
|
which (pred element) is truthy. Returns a new array."
|
2018-11-30 16:42:13 +00:00
|
|
|
[pred ind]
|
2018-07-02 01:12:46 +00:00
|
|
|
(def res @[])
|
|
|
|
(loop [item :in ind]
|
2018-07-17 02:55:45 +00:00
|
|
|
(if (pred item)
|
2018-12-01 03:49:21 +00:00
|
|
|
(array/push res item)))
|
2018-11-29 18:30:59 +00:00
|
|
|
res)
|
2018-03-28 17:50:06 +00:00
|
|
|
|
2018-12-08 04:57:19 +00:00
|
|
|
(defn count
|
|
|
|
"Count the number of items in ind for which (pred item)
|
|
|
|
is true."
|
|
|
|
[pred ind]
|
|
|
|
(var counter 0)
|
|
|
|
(loop [item :in ind]
|
|
|
|
(if (pred item)
|
|
|
|
(++ counter)))
|
|
|
|
counter)
|
|
|
|
|
2018-11-30 16:42:13 +00:00
|
|
|
(defn keep
|
|
|
|
"Given a predicate, take only elements from an array or tuple for
|
|
|
|
which (pred element) is truthy. Returns a new array of truthy predicate results."
|
|
|
|
[pred ind]
|
|
|
|
(def res @[])
|
|
|
|
(loop [item :in ind]
|
|
|
|
(if-let [y (pred item)]
|
2018-12-01 03:49:21 +00:00
|
|
|
(array/push res y)))
|
2018-11-30 16:42:13 +00:00
|
|
|
res)
|
|
|
|
|
2018-05-18 20:24:09 +00:00
|
|
|
(defn range
|
2018-11-29 18:30:59 +00:00
|
|
|
"Create an array of values [start, end) with a given step.
|
|
|
|
With one argument returns a range [0, end). With two arguments, returns
|
|
|
|
a range [start, end). With three, returns a range with optional step size."
|
2018-07-11 15:57:17 +00:00
|
|
|
[& args]
|
|
|
|
(case (length args)
|
|
|
|
1 (do
|
|
|
|
(def [n] args)
|
2018-12-01 03:49:21 +00:00
|
|
|
(def arr (array/new n))
|
2018-07-11 15:57:17 +00:00
|
|
|
(loop [i :range [0 n]] (put arr i i))
|
|
|
|
arr)
|
|
|
|
2 (do
|
|
|
|
(def [n m] args)
|
2018-12-01 03:49:21 +00:00
|
|
|
(def arr (array/new n))
|
2018-07-11 15:57:17 +00:00
|
|
|
(loop [i :range [n m]] (put arr (- i n) i))
|
|
|
|
arr)
|
2018-11-29 18:30:59 +00:00
|
|
|
3 (do
|
|
|
|
(def [n m s] args)
|
2018-12-01 03:49:21 +00:00
|
|
|
(def arr (array/new n))
|
2018-11-29 18:30:59 +00:00
|
|
|
(loop [i :range [n m s]] (put arr (- i n) i))
|
|
|
|
arr)
|
|
|
|
(error "expected 1 to 3 arguments to range")))
|
2018-05-18 20:24:09 +00:00
|
|
|
|
2018-03-28 17:50:06 +00:00
|
|
|
(defn find-index
|
2018-07-02 01:12:46 +00:00
|
|
|
"Find the index of indexed type for which pred is true. Returns nil if not found."
|
|
|
|
[pred ind]
|
|
|
|
(def len (length ind))
|
|
|
|
(var i 0)
|
|
|
|
(var going true)
|
|
|
|
(while (if (< i len) going)
|
2019-01-07 00:33:27 +00:00
|
|
|
(def item (get ind i))
|
2018-12-17 02:57:32 +00:00
|
|
|
(if (pred item) (set going false) (++ i)))
|
2018-07-02 01:12:46 +00:00
|
|
|
(if going nil i))
|
2018-03-28 17:50:06 +00:00
|
|
|
|
|
|
|
(defn find
|
2018-07-02 01:12:46 +00:00
|
|
|
"Find the first value in an indexed collection that satisfies a predicate. Returns
|
|
|
|
nil if not found. Note their is no way to differentiate a nil from the indexed collection
|
|
|
|
and a not found. Consider find-index if this is an issue."
|
|
|
|
[pred ind]
|
2019-01-29 18:59:08 +00:00
|
|
|
(def i (find-index pred ind))
|
|
|
|
(if (= i nil) nil (get ind i)))
|
2018-03-28 17:50:06 +00:00
|
|
|
|
|
|
|
(defn take-until
|
2018-07-02 01:12:46 +00:00
|
|
|
"Given a predicate, take only elements from an indexed type that satisfy
|
2018-11-29 18:30:59 +00:00
|
|
|
the predicate, and abort on first failure. Returns a new array."
|
2018-07-02 01:12:46 +00:00
|
|
|
[pred ind]
|
|
|
|
(def i (find-index pred ind))
|
|
|
|
(if i
|
2018-12-01 03:49:21 +00:00
|
|
|
(array/slice ind 0 i)
|
2018-07-02 01:12:46 +00:00
|
|
|
ind))
|
2018-03-28 17:50:06 +00:00
|
|
|
|
|
|
|
(defn take-while
|
2018-07-02 01:12:46 +00:00
|
|
|
"Same as (take-until (complement pred) ind)."
|
|
|
|
[pred ind]
|
|
|
|
(take-until (complement pred) ind))
|
2018-03-28 17:50:06 +00:00
|
|
|
|
|
|
|
(defn drop-until
|
2018-07-02 01:12:46 +00:00
|
|
|
"Given a predicate, remove elements from an indexed type that satisfy
|
|
|
|
the predicate, and abort on first failure. Returns a new tuple."
|
|
|
|
[pred ind]
|
|
|
|
(def i (find-index pred ind))
|
2018-12-01 03:49:21 +00:00
|
|
|
(array/slice ind i))
|
2018-03-28 17:50:06 +00:00
|
|
|
|
|
|
|
(defn drop-while
|
2018-07-02 01:12:46 +00:00
|
|
|
"Same as (drop-until (complement pred) ind)."
|
|
|
|
[pred ind]
|
|
|
|
(drop-until (complement pred) ind))
|
2018-03-28 17:50:06 +00:00
|
|
|
|
2018-03-22 00:53:39 +00:00
|
|
|
(defn juxt*
|
2018-11-29 18:30:59 +00:00
|
|
|
"Returns the juxtaposition of functions. In other words,
|
|
|
|
((juxt* a b c) x) evaluates to ((a x) (b x) (c x))."
|
2018-03-22 00:53:39 +00:00
|
|
|
[& funs]
|
|
|
|
(fn [& args]
|
2018-07-02 01:12:46 +00:00
|
|
|
(def ret @[])
|
|
|
|
(loop [f :in funs]
|
2018-12-05 20:10:04 +00:00
|
|
|
(array/push ret (f ;args)))
|
2018-12-01 03:49:21 +00:00
|
|
|
(tuple/slice ret 0)))
|
2018-03-22 00:53:39 +00:00
|
|
|
|
|
|
|
(defmacro juxt
|
2018-11-29 18:30:59 +00:00
|
|
|
"Macro form of juxt*. Same behavior but more efficient."
|
2018-07-02 01:12:46 +00:00
|
|
|
[& funs]
|
|
|
|
(def parts @['tuple])
|
|
|
|
(def $args (gensym))
|
|
|
|
(loop [f :in funs]
|
2018-12-01 03:49:21 +00:00
|
|
|
(array/push parts (tuple apply f $args)))
|
|
|
|
(tuple 'fn (tuple '& $args) (tuple/slice parts 0)))
|
2018-03-22 00:53:39 +00:00
|
|
|
|
2018-03-12 16:47:05 +00:00
|
|
|
(defmacro ->
|
2018-07-02 01:12:46 +00:00
|
|
|
"Threading macro. Inserts x as the second value in the first form
|
2018-12-23 19:13:27 +00:00
|
|
|
in forms, and inserts the modified first form into the second form
|
2018-07-02 01:12:46 +00:00
|
|
|
in the same manner, and so on. Useful for expressing pipelines of data."
|
|
|
|
[x & forms]
|
|
|
|
(defn fop [last n]
|
|
|
|
(def [h t] (if (= :tuple (type n))
|
2019-02-08 20:49:28 +00:00
|
|
|
(tuple (get n 0) (array/slice n 1))
|
|
|
|
(tuple n @[])))
|
2018-12-01 03:49:21 +00:00
|
|
|
(def parts (array/concat @[h last] t))
|
|
|
|
(tuple/slice parts 0))
|
2018-07-02 01:12:46 +00:00
|
|
|
(reduce fop x forms))
|
2018-03-12 16:47:05 +00:00
|
|
|
|
|
|
|
(defmacro ->>
|
2018-07-02 01:12:46 +00:00
|
|
|
"Threading macro. Inserts x as the last value in the first form
|
2018-12-23 19:13:27 +00:00
|
|
|
in forms, and inserts the modified first form into the second form
|
2018-07-02 01:12:46 +00:00
|
|
|
in the same manner, and so on. Useful for expressing pipelines of data."
|
|
|
|
[x & forms]
|
|
|
|
(defn fop [last n]
|
|
|
|
(def [h t] (if (= :tuple (type n))
|
2019-02-08 20:49:28 +00:00
|
|
|
(tuple (get n 0) (array/slice n 1))
|
|
|
|
(tuple n @[])))
|
2018-12-01 03:49:21 +00:00
|
|
|
(def parts (array/concat @[h] t @[last]))
|
|
|
|
(tuple/slice parts 0))
|
2018-07-02 01:12:46 +00:00
|
|
|
(reduce fop x forms))
|
2018-03-12 16:47:05 +00:00
|
|
|
|
2018-12-23 19:13:27 +00:00
|
|
|
(defmacro -?>
|
|
|
|
"Short circuit threading macro. Inserts x as the last value in the first form
|
|
|
|
in forms, and inserts the modified first form into the second form
|
|
|
|
in the same manner, and so on. The pipeline will return nil
|
|
|
|
if an intermediate value is nil.
|
|
|
|
Useful for expressing pipelines of data."
|
|
|
|
[x & forms]
|
|
|
|
(defn fop [last n]
|
|
|
|
(def [h t] (if (= :tuple (type n))
|
2019-02-08 20:49:28 +00:00
|
|
|
(tuple (get n 0) (array/slice n 1))
|
|
|
|
(tuple n @[])))
|
2018-12-23 19:13:27 +00:00
|
|
|
(def sym (gensym))
|
|
|
|
(def parts (array/concat @[h sym] t))
|
|
|
|
~(let [,sym ,last] (if ,sym ,(tuple/slice parts 0))))
|
|
|
|
(reduce fop x forms))
|
|
|
|
|
|
|
|
(defmacro -?>>
|
|
|
|
"Threading macro. Inserts x as the last value in the first form
|
|
|
|
in forms, and inserts the modified first form into the second form
|
|
|
|
in the same manner, and so on. The pipeline will return nil
|
|
|
|
if an intermediate value is nil.
|
|
|
|
Useful for expressing pipelines of data."
|
|
|
|
[x & forms]
|
|
|
|
(defn fop [last n]
|
|
|
|
(def [h t] (if (= :tuple (type n))
|
2019-02-08 20:49:28 +00:00
|
|
|
(tuple (get n 0) (array/slice n 1))
|
|
|
|
(tuple n @[])))
|
2018-12-23 19:13:27 +00:00
|
|
|
(def sym (gensym))
|
|
|
|
(def parts (array/concat @[h] t @[sym]))
|
|
|
|
~(let [,sym ,last] (if ,sym ,(tuple/slice parts 0))))
|
|
|
|
(reduce fop x forms))
|
|
|
|
|
2018-12-29 22:23:31 +00:00
|
|
|
(defn walk-ind [f form]
|
|
|
|
(def len (length form))
|
|
|
|
(def ret (array/new len))
|
|
|
|
(each x form (array/push ret (f x)))
|
|
|
|
ret)
|
|
|
|
|
|
|
|
(defn walk-dict [f form]
|
|
|
|
(def ret @{})
|
|
|
|
(loop [k :keys form]
|
2019-01-07 00:33:27 +00:00
|
|
|
(put ret (f k) (f (get form k))))
|
2018-12-29 22:23:31 +00:00
|
|
|
ret)
|
|
|
|
|
2018-12-24 00:00:16 +00:00
|
|
|
(defn walk
|
|
|
|
"Iterate over the values in ast and apply f
|
|
|
|
to them. Collect the results in a data structure . If ast is not a
|
|
|
|
table, struct, array, or tuple,
|
2018-12-29 22:23:31 +00:00
|
|
|
returns form."
|
2018-12-24 00:00:16 +00:00
|
|
|
[f form]
|
|
|
|
(case (type form)
|
2018-12-29 22:23:31 +00:00
|
|
|
:table (walk-dict f form)
|
|
|
|
:struct (table/to-struct (walk-dict f form))
|
|
|
|
:array (walk-ind f form)
|
|
|
|
:tuple (tuple/slice (walk-ind f form))
|
2018-12-24 00:00:16 +00:00
|
|
|
form))
|
|
|
|
|
2018-12-29 22:23:31 +00:00
|
|
|
(put _env 'walk-ind nil)
|
|
|
|
(put _env 'walk-dict nil)
|
|
|
|
|
2018-12-30 20:34:01 +00:00
|
|
|
(defn postwalk
|
2019-01-06 08:23:03 +00:00
|
|
|
"Do a post-order traversal of a data structure and call (f x)
|
2018-12-24 00:00:16 +00:00
|
|
|
on every visitation."
|
|
|
|
[f form]
|
2018-12-30 20:34:01 +00:00
|
|
|
(f (walk (fn [x] (postwalk f x)) form)))
|
2018-12-24 00:00:16 +00:00
|
|
|
|
2018-12-30 20:34:01 +00:00
|
|
|
(defn prewalk
|
2019-01-06 08:23:03 +00:00
|
|
|
"Similar to postwalk, but do pre-order traversal."
|
2018-12-24 00:00:16 +00:00
|
|
|
[f form]
|
2018-12-30 20:34:01 +00:00
|
|
|
(walk (fn [x] (prewalk f x)) (f form)))
|
2018-12-24 00:00:16 +00:00
|
|
|
|
|
|
|
(defmacro as->
|
|
|
|
"Thread forms together, replacing as in forms with the value
|
|
|
|
of the previous form. The first for is the value x. Returns the
|
|
|
|
last value."
|
|
|
|
[x as & forms]
|
|
|
|
(var prev x)
|
|
|
|
(loop [form :in forms]
|
|
|
|
(def sym (gensym))
|
2018-12-30 20:34:01 +00:00
|
|
|
(def next-prev (postwalk (fn [y] (if (= y as) sym y)) form))
|
2018-12-24 00:00:16 +00:00
|
|
|
(set prev ~(let [,sym ,prev] ,next-prev)))
|
|
|
|
prev)
|
|
|
|
|
|
|
|
(defmacro as?->
|
|
|
|
"Thread forms together, replacing as in forms with the value
|
|
|
|
of the previous form. The first for is the value x. If any
|
|
|
|
intermediate values are falsey, return nil; otherwise, returns the
|
|
|
|
last value."
|
|
|
|
[x as & forms]
|
|
|
|
(var prev x)
|
|
|
|
(loop [form :in forms]
|
|
|
|
(def sym (gensym))
|
2018-12-30 20:34:01 +00:00
|
|
|
(def next-prev (postwalk (fn [y] (if (= y as) sym y)) form))
|
2018-12-24 00:00:16 +00:00
|
|
|
(set prev ~(if-let [,sym ,prev] ,next-prev)))
|
|
|
|
prev)
|
|
|
|
|
2018-05-05 18:41:47 +00:00
|
|
|
(defn partial
|
2018-07-02 01:12:46 +00:00
|
|
|
"Partial function application."
|
|
|
|
[f & more]
|
|
|
|
(if (zero? (length more)) f
|
2018-12-05 20:10:04 +00:00
|
|
|
(fn [& r] (f ;more ;r))))
|
2018-03-28 17:50:06 +00:00
|
|
|
|
2018-12-06 22:26:59 +00:00
|
|
|
(defn every?
|
2018-12-01 03:49:21 +00:00
|
|
|
"Returns true if each value in is truthy, otherwise the first
|
|
|
|
falsey value."
|
|
|
|
[ind]
|
2018-07-02 01:12:46 +00:00
|
|
|
(var res true)
|
2018-11-29 18:30:59 +00:00
|
|
|
(loop [x :in ind :while res]
|
2018-12-17 02:57:32 +00:00
|
|
|
(if x nil (set res x)))
|
2018-07-02 01:12:46 +00:00
|
|
|
res)
|
2018-03-28 17:50:06 +00:00
|
|
|
|
2018-11-29 18:30:59 +00:00
|
|
|
(defn reverse
|
2018-07-02 01:12:46 +00:00
|
|
|
"Reverses the order of the elements in a given array or tuple and returns a new array."
|
|
|
|
[t]
|
2018-11-29 18:30:59 +00:00
|
|
|
(def len (length t))
|
|
|
|
(var n (dec len))
|
2018-12-01 03:49:21 +00:00
|
|
|
(def reversed (array/new len))
|
2018-07-02 01:12:46 +00:00
|
|
|
(while (>= n 0)
|
2019-01-07 00:33:27 +00:00
|
|
|
(array/push reversed (get t n))
|
2018-07-02 01:12:46 +00:00
|
|
|
(-- n))
|
2018-03-23 22:36:56 +00:00
|
|
|
reversed)
|
|
|
|
|
2018-10-21 15:46:36 +00:00
|
|
|
(defn invert
|
|
|
|
"Returns a table of where the keys of an associative data structure
|
|
|
|
are the values, and the values of the keys. If multiple keys have the same
|
|
|
|
value, one key will be ignored."
|
|
|
|
[ds]
|
|
|
|
(def ret @{})
|
|
|
|
(loop [k :keys ds]
|
2019-01-07 00:33:27 +00:00
|
|
|
(put ret (get ds k) k))
|
2018-10-21 15:46:36 +00:00
|
|
|
ret)
|
|
|
|
|
2018-03-23 22:36:56 +00:00
|
|
|
(defn zipcoll
|
2019-01-18 17:04:34 +00:00
|
|
|
"Creates a table from two arrays/tuples.
|
2018-12-17 17:06:50 +00:00
|
|
|
Returns a new table."
|
2018-11-29 18:30:59 +00:00
|
|
|
[keys vals]
|
2018-03-28 17:50:06 +00:00
|
|
|
(def res @{})
|
|
|
|
(def lk (length keys))
|
|
|
|
(def lv (length vals))
|
|
|
|
(def len (if (< lk lv) lk lv))
|
2018-05-24 02:08:36 +00:00
|
|
|
(loop [i :range [0 len]]
|
2019-01-07 00:33:27 +00:00
|
|
|
(put res (get keys i) (get vals i)))
|
2018-11-29 18:30:59 +00:00
|
|
|
res)
|
|
|
|
|
2018-03-23 22:36:56 +00:00
|
|
|
(defn update
|
2018-07-02 01:12:46 +00:00
|
|
|
"Accepts a key argument and passes its' associated value to a function.
|
|
|
|
The key then, is associated to the function's return value"
|
2019-01-07 00:33:27 +00:00
|
|
|
[ds key func & args]
|
|
|
|
(def old (get ds key))
|
|
|
|
(set (ds key) (func old ;args)))
|
2018-03-23 22:36:56 +00:00
|
|
|
|
2018-11-29 18:30:59 +00:00
|
|
|
(defn merge-into
|
|
|
|
"Merges multiple tables/structs into a table. If a key appears in more than one
|
|
|
|
collection, then later values replace any previous ones.
|
|
|
|
Returns the original table."
|
|
|
|
[tab & colls]
|
|
|
|
(loop [c :in colls
|
|
|
|
key :keys c]
|
2019-01-07 00:33:27 +00:00
|
|
|
(set (tab key) (get c key)))
|
2018-11-29 18:30:59 +00:00
|
|
|
tab)
|
|
|
|
|
2018-03-23 22:36:56 +00:00
|
|
|
(defn merge
|
2018-04-30 22:11:19 +00:00
|
|
|
"Merges multiple tables/structs to one. If a key appears in more than one
|
2018-07-02 01:12:46 +00:00
|
|
|
collection, then later values replace any previous ones.
|
2018-11-29 18:30:59 +00:00
|
|
|
Returns a new table."
|
2018-03-23 22:36:56 +00:00
|
|
|
[& colls]
|
|
|
|
(def container @{})
|
2018-05-24 02:08:36 +00:00
|
|
|
(loop [c :in colls
|
2018-07-02 01:12:46 +00:00
|
|
|
key :keys c]
|
2019-01-07 00:33:27 +00:00
|
|
|
(set (container key) (get c key)))
|
2018-11-29 18:30:59 +00:00
|
|
|
container)
|
2018-03-23 22:36:56 +00:00
|
|
|
|
2018-03-29 01:16:12 +00:00
|
|
|
(defn keys
|
2018-07-02 01:12:46 +00:00
|
|
|
"Get the keys of an associative data structure."
|
|
|
|
[x]
|
2018-12-01 03:49:21 +00:00
|
|
|
(def arr (array/new (length x)))
|
2018-07-02 01:12:46 +00:00
|
|
|
(var k (next x nil))
|
|
|
|
(while (not= nil k)
|
2018-12-01 03:49:21 +00:00
|
|
|
(array/push arr k)
|
2018-12-17 02:57:32 +00:00
|
|
|
(set k (next x k)))
|
2018-07-02 01:12:46 +00:00
|
|
|
arr)
|
2018-03-29 01:16:12 +00:00
|
|
|
|
|
|
|
(defn values
|
2018-07-02 01:12:46 +00:00
|
|
|
"Get the values of an associative data structure."
|
|
|
|
[x]
|
2018-12-01 03:49:21 +00:00
|
|
|
(def arr (array/new (length x)))
|
2018-07-02 01:12:46 +00:00
|
|
|
(var k (next x nil))
|
|
|
|
(while (not= nil k)
|
2019-01-07 00:33:27 +00:00
|
|
|
(array/push arr (get x k))
|
2018-12-17 02:57:32 +00:00
|
|
|
(set k (next x k)))
|
2018-07-02 01:12:46 +00:00
|
|
|
arr)
|
2018-03-29 01:16:12 +00:00
|
|
|
|
|
|
|
(defn pairs
|
2018-07-02 01:12:46 +00:00
|
|
|
"Get the values of an associative data structure."
|
|
|
|
[x]
|
2018-12-01 03:49:21 +00:00
|
|
|
(def arr (array/new (length x)))
|
2018-07-02 01:12:46 +00:00
|
|
|
(var k (next x nil))
|
|
|
|
(while (not= nil k)
|
2019-01-07 00:33:27 +00:00
|
|
|
(array/push arr (tuple k (get x k)))
|
2018-12-17 02:57:32 +00:00
|
|
|
(set k (next x k)))
|
2018-07-02 01:12:46 +00:00
|
|
|
arr)
|
2018-03-29 01:16:12 +00:00
|
|
|
|
2018-06-03 18:21:24 +00:00
|
|
|
(defn frequencies
|
2018-11-29 18:30:59 +00:00
|
|
|
"Get the number of occurrences of each value in a indexed structure."
|
2018-07-02 01:12:46 +00:00
|
|
|
[ind]
|
|
|
|
(def freqs @{})
|
|
|
|
(loop
|
|
|
|
[x :in ind]
|
2019-01-07 00:33:27 +00:00
|
|
|
(def n (get freqs x))
|
|
|
|
(set (freqs x) (if n (+ 1 n) 1)))
|
2018-07-02 01:12:46 +00:00
|
|
|
freqs)
|
2018-06-03 18:21:24 +00:00
|
|
|
|
2018-11-08 03:52:49 +00:00
|
|
|
(defn interleave
|
|
|
|
"Returns an array of the first elements of each col,
|
|
|
|
then the second, etc."
|
|
|
|
[& cols]
|
|
|
|
(def res @[])
|
2018-11-08 03:56:26 +00:00
|
|
|
(def ncol (length cols))
|
|
|
|
(when (> ncol 0)
|
2018-12-05 20:10:04 +00:00
|
|
|
(def len (min ;(map length cols)))
|
2018-11-29 18:30:59 +00:00
|
|
|
(loop [i :range [0 len]
|
|
|
|
ci :range [0 ncol]]
|
2019-01-07 00:33:27 +00:00
|
|
|
(array/push res (get (get cols ci) i))))
|
2018-11-08 03:52:49 +00:00
|
|
|
res)
|
|
|
|
|
2018-11-30 16:42:13 +00:00
|
|
|
(defn distinct
|
2019-01-06 08:23:03 +00:00
|
|
|
"Returns an array of the deduplicated values in xs."
|
2018-11-30 16:42:13 +00:00
|
|
|
[xs]
|
|
|
|
(def ret @[])
|
|
|
|
(def seen @{})
|
2019-01-07 00:33:27 +00:00
|
|
|
(loop [x :in xs] (if (get seen x) nil (do (put seen x true) (array/push ret x))))
|
2018-11-30 16:42:13 +00:00
|
|
|
ret)
|
|
|
|
|
|
|
|
(defn flatten-into
|
|
|
|
"Takes a nested array (tree), and appends the depth first traversal of
|
|
|
|
that array to an array 'into'. Returns array into."
|
|
|
|
[into xs]
|
|
|
|
(loop [x :in xs]
|
|
|
|
(if (indexed? x)
|
|
|
|
(flatten-into into x)
|
2018-12-01 03:49:21 +00:00
|
|
|
(array/push into x)))
|
2018-11-30 16:42:13 +00:00
|
|
|
into)
|
|
|
|
|
|
|
|
(defn flatten
|
|
|
|
"Takes a nested array (tree), and returns the depth first traversal of
|
|
|
|
that array. Returns a new array."
|
|
|
|
[xs]
|
|
|
|
(flatten-into @[] xs))
|
|
|
|
|
2018-12-06 22:26:59 +00:00
|
|
|
(defn kvs
|
2018-11-30 16:42:13 +00:00
|
|
|
"Takes a table or struct and returns and array of key value pairs
|
|
|
|
like @[k v k v ...]. Returns a new array."
|
|
|
|
[dict]
|
2018-12-01 03:49:21 +00:00
|
|
|
(def ret (array/new (* 2 (length dict))))
|
2019-01-07 00:33:27 +00:00
|
|
|
(loop [k :keys dict] (array/push ret k (get dict k)))
|
2018-11-30 16:42:13 +00:00
|
|
|
ret)
|
|
|
|
|
|
|
|
(defn interpose
|
|
|
|
"Returns a sequence of the elements of ind separated by
|
|
|
|
sep. Returns a new array."
|
|
|
|
[sep ind]
|
|
|
|
(def len (length ind))
|
2018-12-01 03:49:21 +00:00
|
|
|
(def ret (array/new (- (* 2 len) 1)))
|
2018-12-29 04:44:39 +00:00
|
|
|
(if (> len 0) (put ret 0 (get ind 0)))
|
2018-11-30 16:42:13 +00:00
|
|
|
(var i 1)
|
|
|
|
(while (< i len)
|
2019-01-07 00:33:27 +00:00
|
|
|
(array/push ret sep (get ind i))
|
2018-11-30 16:42:13 +00:00
|
|
|
(++ i))
|
|
|
|
ret)
|
|
|
|
|
2019-02-08 05:44:30 +00:00
|
|
|
(defn partition
|
|
|
|
"Partition an indexed data structure into tuples
|
|
|
|
of size n. Returns a new array."
|
|
|
|
[n ind]
|
|
|
|
(var i 0) (var nextn n)
|
|
|
|
(def len (length ind))
|
|
|
|
(def ret (array/new (math/ceil (/ len n))))
|
|
|
|
(while (<= nextn len)
|
|
|
|
(array/push ret (tuple/slice ind i nextn))
|
|
|
|
(set i nextn)
|
|
|
|
(+= nextn n))
|
|
|
|
(if (not= i len) (array/push ret (tuple/slice ind i)))
|
|
|
|
ret)
|
|
|
|
|
2019-02-15 23:56:41 +00:00
|
|
|
###
|
|
|
|
###
|
|
|
|
### IO Helpers
|
|
|
|
###
|
|
|
|
###
|
|
|
|
|
|
|
|
(defn slurp
|
|
|
|
"Read all data from a file with name path
|
|
|
|
and then close the file."
|
|
|
|
[path]
|
|
|
|
(def f (file/open path :r))
|
|
|
|
(if-not f (error (string "could not open file " path)))
|
|
|
|
(def contents (file/read f :all))
|
|
|
|
(file/close f)
|
|
|
|
contents)
|
|
|
|
|
|
|
|
(defn spit
|
|
|
|
"Write contents to a file at path.
|
|
|
|
Can optionally append to the file."
|
|
|
|
[path contents mode &]
|
|
|
|
(default mode :w)
|
|
|
|
(def f (file/open path mode))
|
|
|
|
(if-not f (error (string "could not open file " path " with mode " mode)))
|
|
|
|
(file/write f contents)
|
|
|
|
(file/close f)
|
|
|
|
nil)
|
|
|
|
|
2018-12-08 15:53:22 +00:00
|
|
|
###
|
|
|
|
###
|
|
|
|
### Pattern Matching
|
|
|
|
###
|
|
|
|
###
|
|
|
|
|
|
|
|
# Sentinel value for mismatches
|
|
|
|
(def- sentinel ~',(gensym))
|
|
|
|
|
|
|
|
(defn- match-1
|
|
|
|
[pattern expr onmatch seen]
|
|
|
|
(cond
|
|
|
|
|
2019-01-18 20:24:58 +00:00
|
|
|
(symbol? pattern)
|
2018-12-08 15:53:22 +00:00
|
|
|
(if (get seen pattern)
|
|
|
|
~(if (= ,pattern ,expr) ,(onmatch) ,sentinel)
|
|
|
|
(do
|
|
|
|
(put seen pattern true)
|
|
|
|
~(if (= nil (def ,pattern ,expr)) ,sentinel ,(onmatch))))
|
|
|
|
|
|
|
|
(tuple? pattern)
|
2018-12-29 04:44:39 +00:00
|
|
|
(match-1
|
|
|
|
(get pattern 0) expr
|
|
|
|
(fn []
|
|
|
|
~(if (and ,;(tuple/slice pattern 1)) ,(onmatch) ,sentinel)) seen)
|
2018-12-08 15:53:22 +00:00
|
|
|
|
2018-12-08 22:56:31 +00:00
|
|
|
(array? pattern)
|
2018-12-08 15:53:22 +00:00
|
|
|
(do
|
|
|
|
(def len (length pattern))
|
|
|
|
(var i -1)
|
2018-12-08 16:04:19 +00:00
|
|
|
(with-idemp
|
|
|
|
$arr expr
|
|
|
|
~(if (indexed? ,$arr)
|
|
|
|
,((fn aux []
|
|
|
|
(++ i)
|
|
|
|
(if (= i len)
|
|
|
|
(onmatch)
|
2019-01-07 00:33:27 +00:00
|
|
|
(match-1 (get pattern i) (tuple get $arr i) aux seen))))
|
2018-12-08 16:04:19 +00:00
|
|
|
,sentinel)))
|
2018-12-08 15:53:22 +00:00
|
|
|
|
|
|
|
(dictionary? pattern)
|
|
|
|
(do
|
|
|
|
(var key nil)
|
2018-12-08 16:04:19 +00:00
|
|
|
(with-idemp
|
|
|
|
$dict expr
|
2018-12-08 22:56:31 +00:00
|
|
|
~(if (dictionary? ,$dict)
|
2018-12-08 16:04:19 +00:00
|
|
|
,((fn aux []
|
2018-12-17 02:57:32 +00:00
|
|
|
(set key (next pattern key))
|
2018-12-08 16:04:19 +00:00
|
|
|
(if (= key nil)
|
|
|
|
(onmatch)
|
|
|
|
(match-1 (get pattern key) (tuple get $dict key) aux seen))))
|
|
|
|
,sentinel)))
|
2018-12-08 15:53:22 +00:00
|
|
|
|
|
|
|
:else ~(if (= ,pattern ,expr) ,(onmatch) ,sentinel)))
|
|
|
|
|
|
|
|
(defmacro match
|
|
|
|
"Pattern matching. Match an expression x against
|
|
|
|
any number of cases. Easy case is a pattern to match against, followed
|
|
|
|
by an expression to evaluate to if that case is matched. A pattern that is
|
|
|
|
a symbol will match anything, binding x's value to that symbol. An array
|
|
|
|
will match only if all of it's elements match the corresponding elements in
|
|
|
|
x. A table or struct will match if all values match with the corresponding
|
|
|
|
values in x. A tuple pattern will match if it's first element matches, and the following
|
|
|
|
elements are treated as predicates and are true. Any other value pattern will only
|
|
|
|
match if it is equal to x."
|
|
|
|
[x & cases]
|
|
|
|
(with-idemp $x x
|
|
|
|
(def len (length cases))
|
|
|
|
(def len-1 (dec len))
|
|
|
|
((fn aux [i]
|
|
|
|
(cond
|
|
|
|
(= i len-1) (get cases i)
|
2019-01-18 20:24:58 +00:00
|
|
|
(< i len-1) (with-syms [$res]
|
2019-01-07 00:33:27 +00:00
|
|
|
~(if (= ,sentinel (def ,$res ,(match-1 (get cases i) $x (fn [] (get cases (inc i))) @{})))
|
2018-12-08 15:53:22 +00:00
|
|
|
,(aux (+ 2 i))
|
|
|
|
,$res)))) 0)))
|
|
|
|
|
2019-01-18 17:04:34 +00:00
|
|
|
(put _env 'sentinel nil)
|
|
|
|
(put _env 'match-1 nil)
|
2018-12-08 15:53:22 +00:00
|
|
|
|
2018-11-16 07:09:38 +00:00
|
|
|
###
|
|
|
|
###
|
|
|
|
### Documentation
|
|
|
|
###
|
|
|
|
###
|
|
|
|
|
|
|
|
(var *doc-width*
|
|
|
|
"Width in columns to print documentation."
|
|
|
|
80)
|
|
|
|
|
|
|
|
(defn doc-format
|
|
|
|
"Reformat text to wrap at a given line."
|
|
|
|
[text]
|
|
|
|
|
|
|
|
(def maxcol (- *doc-width* 8))
|
|
|
|
(var buf @" ")
|
|
|
|
(var word @"")
|
|
|
|
(var current 0)
|
|
|
|
|
|
|
|
(defn pushword
|
|
|
|
[]
|
|
|
|
(def oldcur current)
|
|
|
|
(def spacer
|
|
|
|
(if (<= maxcol (+ current (length word) 1))
|
2018-12-17 02:57:32 +00:00
|
|
|
(do (set current 0) "\n ")
|
2018-11-16 07:09:38 +00:00
|
|
|
(do (++ current) " ")))
|
|
|
|
(+= current (length word))
|
|
|
|
(if (> oldcur 0)
|
2018-12-01 03:49:21 +00:00
|
|
|
(buffer/push-string buf spacer))
|
|
|
|
(buffer/push-string buf word)
|
|
|
|
(buffer/clear word))
|
2018-11-16 07:09:38 +00:00
|
|
|
|
|
|
|
(loop [b :in text]
|
|
|
|
(if (and (not= b 10) (not= b 32))
|
|
|
|
(if (= b 9)
|
2018-12-01 03:49:21 +00:00
|
|
|
(buffer/push-string word " ")
|
|
|
|
(buffer/push-byte word b))
|
2018-11-16 07:09:38 +00:00
|
|
|
(do
|
|
|
|
(if (> (length word) 0) (pushword))
|
|
|
|
(when (= b 10)
|
2018-12-01 03:49:21 +00:00
|
|
|
(buffer/push-string buf "\n ")
|
2018-12-17 02:57:32 +00:00
|
|
|
(set current 0)))))
|
2018-11-16 07:09:38 +00:00
|
|
|
|
|
|
|
# Last word
|
|
|
|
(pushword)
|
|
|
|
|
|
|
|
buf)
|
|
|
|
|
|
|
|
(defn doc*
|
|
|
|
"Get the documentation for a symbol in a given environment."
|
|
|
|
[env sym]
|
2019-01-07 00:33:27 +00:00
|
|
|
(def x (get env sym))
|
2018-11-16 07:09:38 +00:00
|
|
|
(if (not x)
|
|
|
|
(print "symbol " sym " not found.")
|
|
|
|
(do
|
2018-12-17 06:41:11 +00:00
|
|
|
(def bind-type
|
2018-12-17 17:06:50 +00:00
|
|
|
(string " "
|
2018-12-17 06:41:11 +00:00
|
|
|
(cond
|
2019-01-07 00:33:27 +00:00
|
|
|
(x :ref) (string :var " (" (type (get (x :ref) 0)) ")")
|
|
|
|
(x :macro) :macro
|
|
|
|
(type (x :value)))
|
2018-12-17 06:41:11 +00:00
|
|
|
"\n"))
|
2019-01-07 00:33:27 +00:00
|
|
|
(def sm (x :source-map))
|
|
|
|
(def d (x :doc))
|
2018-12-17 06:41:11 +00:00
|
|
|
(print "\n\n"
|
|
|
|
(if d bind-type "")
|
2019-01-06 07:05:15 +00:00
|
|
|
(if-let [[path start end] sm] (string " " path " (" start ":" end ")\n") "")
|
|
|
|
(if (or d sm) "\n" "")
|
2018-12-17 06:41:11 +00:00
|
|
|
(if d (doc-format d) "no documentation found.")
|
|
|
|
"\n\n"))))
|
2018-11-16 07:09:38 +00:00
|
|
|
|
|
|
|
(defmacro doc
|
|
|
|
"Shows documentation for the given symbol."
|
|
|
|
[sym]
|
2019-01-18 17:04:34 +00:00
|
|
|
~(,doc* *env* ',sym))
|
2018-11-16 07:09:38 +00:00
|
|
|
|
2018-03-28 17:50:06 +00:00
|
|
|
###
|
|
|
|
###
|
|
|
|
### Macro Expansion
|
|
|
|
###
|
|
|
|
###
|
2018-03-15 01:46:56 +00:00
|
|
|
|
2018-11-30 18:05:28 +00:00
|
|
|
(defn macex1
|
2018-07-02 01:12:46 +00:00
|
|
|
"Expand macros in a form, but do not recursively expand macros."
|
|
|
|
[x]
|
|
|
|
|
2018-11-15 23:28:55 +00:00
|
|
|
(defn dotable [t on-value]
|
2018-07-02 01:12:46 +00:00
|
|
|
(def newt @{})
|
|
|
|
(var key (next t nil))
|
|
|
|
(while (not= nil key)
|
2019-01-07 00:33:27 +00:00
|
|
|
(put newt (macex1 key) (on-value (get t key)))
|
2018-12-17 02:57:32 +00:00
|
|
|
(set key (next t key)))
|
2018-07-02 01:12:46 +00:00
|
|
|
newt)
|
|
|
|
|
|
|
|
(defn expand-bindings [x]
|
2018-07-04 05:28:31 +00:00
|
|
|
(case (type x)
|
2018-11-29 18:30:59 +00:00
|
|
|
:array (map expand-bindings x)
|
2018-12-01 03:49:21 +00:00
|
|
|
:tuple (tuple/slice (map expand-bindings x))
|
2018-07-02 01:12:46 +00:00
|
|
|
:table (dotable x expand-bindings)
|
2018-12-01 03:49:21 +00:00
|
|
|
:struct (table/to-struct (dotable x expand-bindings))
|
2018-11-30 18:05:28 +00:00
|
|
|
(macex1 x)))
|
2018-07-02 01:12:46 +00:00
|
|
|
|
|
|
|
(defn expanddef [t]
|
2018-11-15 23:28:55 +00:00
|
|
|
(def last (get t (- (length t) 1)))
|
2018-12-29 04:44:39 +00:00
|
|
|
(def bound (get t 1))
|
2018-12-01 03:49:21 +00:00
|
|
|
(tuple/slice
|
|
|
|
(array/concat
|
2018-12-29 04:44:39 +00:00
|
|
|
@[(get t 0) (expand-bindings bound)]
|
2018-12-01 03:49:21 +00:00
|
|
|
(tuple/slice t 2 -2)
|
2018-11-30 18:05:28 +00:00
|
|
|
@[(macex1 last)])))
|
2018-07-02 01:12:46 +00:00
|
|
|
|
|
|
|
(defn expandall [t]
|
2018-12-01 03:49:21 +00:00
|
|
|
(def args (map macex1 (tuple/slice t 1)))
|
2018-12-29 04:44:39 +00:00
|
|
|
(tuple (get t 0) ;args))
|
2018-07-02 01:12:46 +00:00
|
|
|
|
|
|
|
(defn expandfn [t]
|
2018-12-29 04:44:39 +00:00
|
|
|
(def t1 (get t 1))
|
|
|
|
(if (symbol? t1)
|
2018-11-15 23:28:55 +00:00
|
|
|
(do
|
2018-12-01 03:49:21 +00:00
|
|
|
(def args (map macex1 (tuple/slice t 3)))
|
2018-12-29 04:44:39 +00:00
|
|
|
(tuple 'fn t1 (get t 2) ;args))
|
2018-11-15 23:28:55 +00:00
|
|
|
(do
|
2018-12-01 03:49:21 +00:00
|
|
|
(def args (map macex1 (tuple/slice t 2)))
|
2018-12-29 04:44:39 +00:00
|
|
|
(tuple 'fn t1 ;args))))
|
2018-12-01 03:49:21 +00:00
|
|
|
|
|
|
|
(defn expandqq [t]
|
|
|
|
(defn qq [x]
|
|
|
|
(case (type x)
|
|
|
|
:tuple (do
|
2018-12-29 04:44:39 +00:00
|
|
|
(def x0 (get x 0))
|
2018-12-01 03:49:21 +00:00
|
|
|
(if (or (= 'unquote x0) (= 'unquote-splicing x0))
|
2018-12-29 04:44:39 +00:00
|
|
|
(tuple x0 (macex1 (get x 1)))
|
2018-12-01 03:49:21 +00:00
|
|
|
(tuple/slice (map qq x))))
|
|
|
|
:array (map qq x)
|
|
|
|
:table (table (map qq (kvs x)))
|
|
|
|
:struct (struct (map qq (kvs x)))
|
|
|
|
x))
|
2018-12-29 04:44:39 +00:00
|
|
|
(tuple (get t 0) (qq (get t 1))))
|
2018-07-02 01:12:46 +00:00
|
|
|
|
2018-07-17 02:55:45 +00:00
|
|
|
(def specs
|
2018-12-17 02:57:32 +00:00
|
|
|
{'set expanddef
|
2018-07-17 02:55:45 +00:00
|
|
|
'def expanddef
|
|
|
|
'do expandall
|
|
|
|
'fn expandfn
|
|
|
|
'if expandall
|
|
|
|
'quote identity
|
2018-12-01 03:49:21 +00:00
|
|
|
'quasiquote expandqq
|
2018-07-17 02:55:45 +00:00
|
|
|
'var expanddef
|
|
|
|
'while expandall})
|
2018-07-02 01:12:46 +00:00
|
|
|
|
|
|
|
(defn dotup [t]
|
2018-12-29 04:44:39 +00:00
|
|
|
(def h (get t 0))
|
2019-01-07 00:33:27 +00:00
|
|
|
(def s (get specs h))
|
|
|
|
(def entry (or (get *env* h) {}))
|
|
|
|
(def m (entry :value))
|
|
|
|
(def m? (entry :macro))
|
2018-07-02 01:12:46 +00:00
|
|
|
(cond
|
|
|
|
s (s t)
|
2018-12-05 20:10:04 +00:00
|
|
|
m? (m ;(tuple/slice t 1))
|
2018-12-01 03:49:21 +00:00
|
|
|
(tuple/slice (map macex1 t))))
|
2018-07-02 01:12:46 +00:00
|
|
|
|
2018-07-17 02:55:45 +00:00
|
|
|
(def ret
|
|
|
|
(case (type x)
|
2019-02-11 23:37:59 +00:00
|
|
|
:tuple (if (= (tuple/type x) :brackets)
|
|
|
|
(tuple/brackets ;(map macex1 x))
|
|
|
|
(dotup x))
|
2018-11-30 18:05:28 +00:00
|
|
|
:array (map macex1 x)
|
2018-12-01 03:49:21 +00:00
|
|
|
:struct (table/to-struct (dotable x macex1))
|
2018-11-30 18:05:28 +00:00
|
|
|
:table (dotable x macex1)
|
2018-07-17 02:55:45 +00:00
|
|
|
x))
|
2018-07-02 01:12:46 +00:00
|
|
|
ret)
|
2018-03-18 18:01:58 +00:00
|
|
|
|
2018-12-16 18:17:30 +00:00
|
|
|
(defn all
|
2018-12-16 04:19:28 +00:00
|
|
|
"Returns true if all xs are truthy, otherwise the first false or nil value."
|
2018-12-17 06:41:11 +00:00
|
|
|
[pred xs]
|
2018-11-30 16:42:13 +00:00
|
|
|
(var ret true)
|
2018-12-17 02:57:32 +00:00
|
|
|
(loop [x :in xs :while ret] (set ret (pred x)))
|
2018-11-30 16:42:13 +00:00
|
|
|
ret)
|
2018-06-29 21:42:00 +00:00
|
|
|
|
2018-12-16 04:19:28 +00:00
|
|
|
(defn some
|
|
|
|
"Returns false if all xs are false or nil, otherwise returns the first true value."
|
2018-12-17 06:41:11 +00:00
|
|
|
[pred xs]
|
2018-11-30 16:42:13 +00:00
|
|
|
(var ret nil)
|
2018-12-17 02:57:32 +00:00
|
|
|
(loop [x :in xs :while (not ret)] (if-let [y (pred x)] (set ret y)))
|
2018-11-30 16:42:13 +00:00
|
|
|
ret)
|
2018-06-29 21:42:00 +00:00
|
|
|
|
2018-12-16 04:19:28 +00:00
|
|
|
(defn deep-not=
|
2018-07-02 01:12:46 +00:00
|
|
|
"Like not=, but mutable types (arrays, tables, buffers) are considered
|
|
|
|
equal if they have identical structure. Much slower than not=."
|
2018-12-16 04:19:28 +00:00
|
|
|
[x y]
|
2018-07-02 01:12:46 +00:00
|
|
|
(def tx (type x))
|
|
|
|
(or
|
|
|
|
(not= tx (type y))
|
2018-07-04 05:28:31 +00:00
|
|
|
(case tx
|
2018-11-30 16:42:13 +00:00
|
|
|
:tuple (or (not= (length x) (length y)) (some identity (map deep-not= x y)))
|
|
|
|
:array (or (not= (length x) (length y)) (some identity (map deep-not= x y)))
|
2018-06-29 23:44:33 +00:00
|
|
|
:struct (deep-not= (pairs x) (pairs y))
|
2018-12-01 03:49:21 +00:00
|
|
|
:table (deep-not= (table/to-struct x) (table/to-struct y))
|
2018-06-29 23:44:33 +00:00
|
|
|
:buffer (not= (string x) (string y))
|
|
|
|
(not= x y))))
|
|
|
|
|
2018-12-16 04:19:28 +00:00
|
|
|
(defn deep=
|
2018-07-02 01:12:46 +00:00
|
|
|
"Like =, but mutable types (arrays, tables, buffers) are considered
|
|
|
|
equal if they have identical structure. Much slower than =."
|
2018-12-16 04:19:28 +00:00
|
|
|
[x y]
|
2018-07-02 01:12:46 +00:00
|
|
|
(not (deep-not= x y)))
|
2018-06-29 23:44:33 +00:00
|
|
|
|
2018-11-30 18:05:28 +00:00
|
|
|
(defn macex
|
2018-07-02 01:12:46 +00:00
|
|
|
"Expand macros completely."
|
|
|
|
[x]
|
|
|
|
(var previous x)
|
2018-11-30 18:05:28 +00:00
|
|
|
(var current (macex1 x))
|
2018-07-02 01:12:46 +00:00
|
|
|
(var counter 0)
|
|
|
|
(while (deep-not= current previous)
|
|
|
|
(if (> (++ counter) 200)
|
|
|
|
(error "macro expansion too nested"))
|
2018-12-17 02:57:32 +00:00
|
|
|
(set previous current)
|
|
|
|
(set current (macex1 current)))
|
2018-07-02 01:12:46 +00:00
|
|
|
current)
|
2018-03-18 18:01:58 +00:00
|
|
|
|
2019-01-12 22:31:15 +00:00
|
|
|
(defn pp
|
|
|
|
"Pretty print to stdout."
|
|
|
|
[x]
|
2019-02-16 20:12:34 +00:00
|
|
|
(print (buffer/format @"" "%p" x)))
|
2019-01-12 22:31:15 +00:00
|
|
|
|
2018-03-29 01:16:12 +00:00
|
|
|
###
|
|
|
|
###
|
|
|
|
### Evaluation and Compilation
|
|
|
|
###
|
|
|
|
###
|
|
|
|
|
2018-11-08 03:27:06 +00:00
|
|
|
(defn make-env
|
2018-12-16 04:22:51 +00:00
|
|
|
"Create a new environment table. The new environment
|
|
|
|
will inherit bindings from the parent environment, but new
|
|
|
|
bindings will not pollute the parent environment."
|
2018-11-25 19:03:00 +00:00
|
|
|
[parent &]
|
2018-07-02 01:12:46 +00:00
|
|
|
(def parent (if parent parent _env))
|
2018-12-01 03:49:21 +00:00
|
|
|
(def newenv (table/setproto @{} parent))
|
2018-07-02 01:12:46 +00:00
|
|
|
newenv)
|
2018-03-15 21:19:31 +00:00
|
|
|
|
2019-01-31 04:07:30 +00:00
|
|
|
(defn bad-parse
|
|
|
|
"Default handler for a parse error."
|
|
|
|
[p where]
|
|
|
|
(file/write stderr
|
|
|
|
"parse error in "
|
|
|
|
where
|
|
|
|
" around byte "
|
|
|
|
(string (parser/where p))
|
2019-01-31 19:48:28 +00:00
|
|
|
": "
|
|
|
|
(or (parser/error p) "unmatched delimiter")
|
|
|
|
"\n"))
|
2019-01-31 04:07:30 +00:00
|
|
|
|
|
|
|
(defn bad-compile
|
|
|
|
"Default handler for a compile error."
|
|
|
|
[msg macrof where]
|
2019-01-31 19:48:28 +00:00
|
|
|
(file/write stderr "compile error: " msg " while compiling " where "\n")
|
2019-01-31 04:07:30 +00:00
|
|
|
(when macrof (debug/stacktrace macrof)))
|
|
|
|
|
|
|
|
(defn getline
|
|
|
|
"Read a line from stdin into a buffer."
|
2019-01-31 17:34:22 +00:00
|
|
|
[buf p &]
|
|
|
|
(default buf @"")
|
|
|
|
(when p (file/write stdout p))
|
|
|
|
(file/read stdin :line buf)
|
|
|
|
buf)
|
2019-01-31 04:07:30 +00:00
|
|
|
|
2018-03-28 17:50:06 +00:00
|
|
|
(defn run-context
|
2018-09-06 02:18:42 +00:00
|
|
|
"Run a context. This evaluates expressions of janet in an environment,
|
2019-01-31 04:07:30 +00:00
|
|
|
and is encapsulates the parsing, compilation, and evaluation.
|
|
|
|
opts is a table or struct of options. The options are as follows:\n\n\t
|
|
|
|
:chunks - callback to read into a buffer - default is getline\n\t
|
|
|
|
:on-parse-error - callback when parsing fails - default is bad-parse\n\t
|
|
|
|
:env - the environment to compile against - default is *env*\n\t
|
|
|
|
:source - string path of source for better errors - default is \"<anonymous>\"\n\t
|
|
|
|
:on-compile-error - callback when compilation fails - default is bad-compile\n\t
|
|
|
|
:on-status - callback when a value is evaluated - default is debug/stacktrace"
|
|
|
|
[opts]
|
|
|
|
|
|
|
|
(def {:env env
|
|
|
|
:chunks chunks
|
|
|
|
:on-status onstatus
|
|
|
|
:on-compile-error on-compile-error
|
|
|
|
:on-parse-error on-parse-error
|
|
|
|
:source where} opts)
|
|
|
|
(default env *env*)
|
|
|
|
(default chunks getline)
|
|
|
|
(default onstatus debug/stacktrace)
|
|
|
|
(default on-compile-error bad-compile)
|
|
|
|
(default on-parse-error bad-parse)
|
|
|
|
(default where "<anonymous>")
|
2018-07-02 01:12:46 +00:00
|
|
|
|
|
|
|
# Are we done yet?
|
|
|
|
(var going true)
|
|
|
|
|
|
|
|
# The parser object
|
2018-12-01 03:49:21 +00:00
|
|
|
(def p (parser/new))
|
2018-07-02 01:12:46 +00:00
|
|
|
|
|
|
|
# Evaluate 1 source form
|
|
|
|
(defn eval1 [source]
|
|
|
|
(var good true)
|
2018-07-04 03:07:35 +00:00
|
|
|
(def f
|
2018-12-01 03:49:21 +00:00
|
|
|
(fiber/new
|
2018-11-29 18:30:59 +00:00
|
|
|
(fn []
|
2018-07-02 01:12:46 +00:00
|
|
|
(def res (compile source env where))
|
|
|
|
(if (= (type res) :function)
|
|
|
|
(res)
|
|
|
|
(do
|
2018-12-17 02:57:32 +00:00
|
|
|
(set good false)
|
2018-12-13 23:46:53 +00:00
|
|
|
(def {:error err :start start :end end :fiber errf} res)
|
2019-01-31 04:07:30 +00:00
|
|
|
(def msg
|
2018-12-13 23:46:53 +00:00
|
|
|
(if (<= 0 start)
|
2019-01-31 04:07:30 +00:00
|
|
|
(string "compile error: " err " at (" start ":" end ")")
|
|
|
|
err))
|
|
|
|
(on-compile-error msg errf where))))
|
2018-07-02 01:12:46 +00:00
|
|
|
:a))
|
2018-10-17 03:08:26 +00:00
|
|
|
(def res (resume f nil))
|
2019-01-31 04:07:30 +00:00
|
|
|
(when good (if going (onstatus f res))))
|
2018-07-02 01:12:46 +00:00
|
|
|
|
|
|
|
(def oldenv *env*)
|
2018-12-17 02:57:32 +00:00
|
|
|
(set *env* env)
|
2018-11-26 14:02:07 +00:00
|
|
|
|
|
|
|
# Run loop
|
|
|
|
(def buf @"")
|
|
|
|
(while going
|
2018-12-01 03:49:21 +00:00
|
|
|
(buffer/clear buf)
|
2018-11-26 14:02:07 +00:00
|
|
|
(chunks buf p)
|
|
|
|
(var pindex 0)
|
2018-12-16 18:17:30 +00:00
|
|
|
(var pstatus nil)
|
2018-11-26 14:02:07 +00:00
|
|
|
(def len (length buf))
|
2018-12-17 02:57:32 +00:00
|
|
|
(if (= len 0) (set going false))
|
2018-11-26 14:02:07 +00:00
|
|
|
(while (> len pindex)
|
2018-12-01 03:49:21 +00:00
|
|
|
(+= pindex (parser/consume p buf pindex))
|
2019-01-04 01:44:58 +00:00
|
|
|
(while (parser/has-more p)
|
2018-12-16 18:17:30 +00:00
|
|
|
(eval1 (parser/produce p)))
|
2019-01-04 01:44:58 +00:00
|
|
|
(when (= (parser/status p) :error)
|
2019-01-31 04:07:30 +00:00
|
|
|
(on-parse-error p where))))
|
2018-11-26 14:02:07 +00:00
|
|
|
|
2019-01-18 04:43:46 +00:00
|
|
|
(if (= (parser/status p) :pending)
|
2019-01-31 04:07:30 +00:00
|
|
|
(on-parse-error p where))
|
2019-01-18 04:43:46 +00:00
|
|
|
|
2018-12-17 02:57:32 +00:00
|
|
|
(set *env* oldenv)
|
2018-07-02 01:12:46 +00:00
|
|
|
|
|
|
|
env)
|
2018-03-12 04:26:13 +00:00
|
|
|
|
2018-12-06 01:48:29 +00:00
|
|
|
(defn eval-string
|
2018-07-02 01:12:46 +00:00
|
|
|
"Evaluates a string in the current environment. If more control over the
|
|
|
|
environment is needed, use run-context."
|
2019-01-20 21:06:30 +00:00
|
|
|
[str env &]
|
2018-07-02 01:12:46 +00:00
|
|
|
(var state (string str))
|
2018-08-03 17:41:44 +00:00
|
|
|
(defn chunks [buf _]
|
2018-07-02 01:12:46 +00:00
|
|
|
(def ret state)
|
2018-12-17 02:57:32 +00:00
|
|
|
(set state nil)
|
2018-10-22 05:28:39 +00:00
|
|
|
(when ret
|
2018-12-01 03:49:21 +00:00
|
|
|
(buffer/push-string buf str)
|
|
|
|
(buffer/push-string buf "\n")))
|
2018-07-02 01:12:46 +00:00
|
|
|
(var returnval nil)
|
2019-01-31 04:07:30 +00:00
|
|
|
(run-context {:env env
|
|
|
|
:chunks chunks
|
|
|
|
:on-compile-error error
|
|
|
|
:on-parse-error error
|
|
|
|
:on-status (fn [f val]
|
|
|
|
(set returnval val)
|
|
|
|
(if-not (= (fiber/status f) :dead)
|
|
|
|
(debug/stacktrace f val)))
|
|
|
|
:source "eval"})
|
2018-07-02 01:12:46 +00:00
|
|
|
returnval)
|
|
|
|
|
2018-12-06 01:48:29 +00:00
|
|
|
(defn eval
|
|
|
|
"Evaluates a form in the current environment. If more control over the
|
|
|
|
environment is needed, use run-context."
|
2019-01-20 21:06:30 +00:00
|
|
|
[form env &]
|
|
|
|
(default env *env*)
|
|
|
|
(def res (compile form env "eval"))
|
2018-12-06 01:48:29 +00:00
|
|
|
(if (= (type res) :function)
|
|
|
|
(res)
|
2019-01-07 00:33:27 +00:00
|
|
|
(error (res :error))))
|
2018-12-06 01:48:29 +00:00
|
|
|
|
2019-02-16 18:21:29 +00:00
|
|
|
(defn make-image
|
|
|
|
"Create an image from an environment returned by require.
|
|
|
|
Returns the image source as a string."
|
|
|
|
[env]
|
|
|
|
(marshal env (invert (env-lookup _env))))
|
|
|
|
|
|
|
|
(defn load-image
|
|
|
|
"The inverse operation to make-image. Returns an environment."
|
|
|
|
[image]
|
|
|
|
(unmarshal image (env-lookup _env)))
|
|
|
|
|
2019-01-18 17:04:34 +00:00
|
|
|
(def module/paths
|
2019-01-31 15:09:34 +00:00
|
|
|
"The list of paths to look for modules. The following
|
2019-01-18 17:04:34 +00:00
|
|
|
substitutions are preformed on each path. :sys: becomes
|
|
|
|
module/*syspath*, :name: becomes the last part of the module
|
2019-01-18 17:24:12 +00:00
|
|
|
name after the last /, and :all: is the module name literally.
|
2019-01-18 17:04:34 +00:00
|
|
|
:native: becomes the dynamic library file extension, usually dll
|
2019-02-16 18:21:29 +00:00
|
|
|
or so. Each element is a two element tuple, containing the path
|
|
|
|
template and a keyword :source, :native, or :image indicating how
|
|
|
|
require should load files found at these paths."
|
|
|
|
@[["./:all:.janet" :source]
|
|
|
|
["./:all:/init.janet" :source]
|
|
|
|
[":sys:/:all:.janet" :source]
|
|
|
|
[":sys:/:all:/init.janet" :source]
|
|
|
|
["./:all:.:native:" :native]
|
|
|
|
["./:all:/:name:.:native:" :native]
|
|
|
|
[":sys:/:all:.:native:" :native]
|
|
|
|
[":sys:/:all:/:name:.:native:" :native]
|
|
|
|
["./:all:.jimage" :image]
|
|
|
|
["./:all:.:name:.jimage" :image]
|
|
|
|
[":sys:/:all:.jimage" :image]
|
|
|
|
[":sys:/:all:/:name:.jimage" :image]
|
|
|
|
[":all:" :source]])
|
2019-01-18 17:04:34 +00:00
|
|
|
|
|
|
|
(var module/*syspath*
|
|
|
|
"The path where globally installed libraries are located.
|
|
|
|
The default value is the environment variable JANET_PATH,
|
|
|
|
and if that is not set /usr/local/lib/janet on linux/posix, and
|
|
|
|
on Windows the default is the empty string."
|
|
|
|
(or (os/getenv "JANET_PATH")
|
2019-02-18 19:31:23 +00:00
|
|
|
(if (= :windows (os/which)) "" JANET_DEFAULT_PATH)))
|
|
|
|
|
|
|
|
(put _env 'JANET_DEFAULT_PATH nil)
|
2018-05-19 00:53:19 +00:00
|
|
|
|
2019-02-16 18:21:29 +00:00
|
|
|
(defn- fexists [path]
|
|
|
|
(def f (file/open path))
|
|
|
|
(if f (do (file/close f) path)))
|
|
|
|
|
2018-12-01 03:49:21 +00:00
|
|
|
(defn module/find
|
2019-02-16 18:21:29 +00:00
|
|
|
"Try to match a module or path name from the patterns in module/paths.
|
|
|
|
Returns a tuple (fullpath kind) where the kind is one of :source, :native,
|
|
|
|
or image if the module is found, otherise a tuple with nil followed by
|
|
|
|
an error message."
|
|
|
|
[path]
|
2018-12-01 03:49:21 +00:00
|
|
|
(def parts (string/split "/" path))
|
2019-01-18 17:04:34 +00:00
|
|
|
(def name (get parts (- (length parts) 1)))
|
|
|
|
(def nati (if (= :windows (os/which)) "dll" "so"))
|
2019-02-16 18:21:29 +00:00
|
|
|
(defn make-full
|
|
|
|
[[p mod-kind]]
|
|
|
|
(def fullpath (->> p
|
|
|
|
(string/replace ":name:" name)
|
|
|
|
(string/replace ":sys:" module/*syspath*)
|
|
|
|
(string/replace ":native:" nati)
|
|
|
|
(string/replace ":all:" path)))
|
|
|
|
[fullpath mod-kind])
|
|
|
|
(defn check-path [x] (if (fexists (x 0)) x))
|
|
|
|
(def paths (map make-full module/paths))
|
|
|
|
(def res (find check-path paths))
|
|
|
|
(if res res [nil (string "could not find module "
|
|
|
|
path
|
|
|
|
":\n "
|
|
|
|
;(interpose "\n " (map 0 paths)))]))
|
|
|
|
|
|
|
|
(put _env 'fexists nil)
|
2019-01-18 17:04:34 +00:00
|
|
|
|
|
|
|
(def module/cache
|
|
|
|
"Table mapping loaded module identifiers to their environments."
|
|
|
|
@{})
|
|
|
|
|
|
|
|
(def module/loading
|
|
|
|
"Table mapping currently loading modules to true. Used to prevent
|
|
|
|
circular dependencies."
|
|
|
|
@{})
|
|
|
|
|
|
|
|
(defn require
|
|
|
|
"Require a module with the given name. Will search all of the paths in
|
2018-12-01 03:49:21 +00:00
|
|
|
module/paths, then the path as a raw file path. Returns the new environment
|
2018-07-02 01:12:46 +00:00
|
|
|
returned from compiling and running the file."
|
2019-01-18 17:04:34 +00:00
|
|
|
[path & args]
|
|
|
|
(def {:exit exit-on-error} (table ;args))
|
|
|
|
(if-let [check (get module/cache path)]
|
|
|
|
check
|
2019-02-16 18:21:29 +00:00
|
|
|
(do
|
|
|
|
(def [fullpath mod-kind] (module/find path))
|
|
|
|
(unless fullpath (error mod-kind))
|
|
|
|
(def env (case mod-kind
|
|
|
|
:source (do
|
|
|
|
# Normal janet module
|
|
|
|
(def f (file/open fullpath))
|
|
|
|
(def newenv (make-env))
|
|
|
|
(put module/loading fullpath true)
|
|
|
|
(defn chunks [buf _] (file/read f 2048 buf))
|
|
|
|
(run-context {:env newenv
|
|
|
|
:chunks chunks
|
|
|
|
:on-status (fn [f x]
|
|
|
|
(when (not= (fiber/status f) :dead)
|
|
|
|
(debug/stacktrace f x)
|
|
|
|
(if exit-on-error (os/exit 1))))
|
|
|
|
:source fullpath})
|
|
|
|
(file/close f)
|
|
|
|
(put module/loading fullpath nil)
|
|
|
|
(table/setproto newenv nil))
|
|
|
|
:native (native fullpath (make-env))
|
|
|
|
:image (load-image (slurp fullpath))))
|
|
|
|
(put module/cache fullpath env)
|
|
|
|
(put module/cache path env)
|
|
|
|
env)))
|
2019-02-15 23:56:41 +00:00
|
|
|
|
2018-12-06 22:26:59 +00:00
|
|
|
(defn import*
|
2018-12-16 18:17:30 +00:00
|
|
|
"Import a module into a given environment table. This is the
|
|
|
|
functional form of (import ...) that expects and explicit environment
|
|
|
|
table."
|
2018-12-01 03:49:21 +00:00
|
|
|
[env path & args]
|
2018-10-17 03:08:26 +00:00
|
|
|
(def {:as as
|
2019-01-06 03:52:28 +00:00
|
|
|
:prefix prefix} (table ;args))
|
|
|
|
(def newenv (require path ;args))
|
2018-12-01 03:49:21 +00:00
|
|
|
(def prefix (or (and as (string as "/")) prefix (string path "/")))
|
2019-01-07 00:33:27 +00:00
|
|
|
(loop [[k v] :pairs newenv :when (not (v :private))]
|
2019-01-06 03:52:28 +00:00
|
|
|
(def newv (table/setproto @{:private true} v))
|
|
|
|
(put env (symbol prefix k) newv)))
|
2018-03-16 17:40:10 +00:00
|
|
|
|
2018-08-03 17:41:44 +00:00
|
|
|
(defmacro import
|
2018-05-19 00:53:19 +00:00
|
|
|
"Import a module. First requires the module, and then merges its
|
2018-07-02 01:12:46 +00:00
|
|
|
symbols into the current environment, prepending a given prefix as needed.
|
|
|
|
(use the :as or :prefix option to set a prefix). If no prefix is provided,
|
|
|
|
use the name of the module as a prefix."
|
2018-08-03 17:41:44 +00:00
|
|
|
[path & args]
|
2018-05-19 02:18:34 +00:00
|
|
|
(def argm (map (fn [x]
|
2018-11-29 19:03:45 +00:00
|
|
|
(if (keyword? x)
|
2018-07-02 01:12:46 +00:00
|
|
|
x
|
|
|
|
(string x)))
|
|
|
|
args))
|
2019-01-18 17:04:34 +00:00
|
|
|
(tuple import* '*env* (string path) ;argm))
|
2018-03-12 04:26:13 +00:00
|
|
|
|
2018-08-03 17:41:44 +00:00
|
|
|
(defn repl
|
2018-05-07 03:25:59 +00:00
|
|
|
"Run a repl. The first parameter is an optional function to call to
|
2018-11-30 19:17:10 +00:00
|
|
|
get a chunk of source code that should return nil for end of file.
|
2018-12-06 22:26:59 +00:00
|
|
|
The second parameter is a function that is called when a signal is
|
2018-11-30 19:17:10 +00:00
|
|
|
caught."
|
|
|
|
[chunks onsignal &]
|
2018-07-02 01:12:46 +00:00
|
|
|
(def newenv (make-env))
|
2019-01-31 04:07:30 +00:00
|
|
|
(default onsignal (fn [f x]
|
|
|
|
(case (fiber/status f)
|
2018-11-30 19:17:10 +00:00
|
|
|
:dead (do
|
|
|
|
(put newenv '_ @{:value x})
|
2019-02-16 20:12:34 +00:00
|
|
|
(print (buffer/format @"" "%.20p" x)))
|
2019-01-31 04:07:30 +00:00
|
|
|
(debug/stacktrace f x))))
|
|
|
|
(run-context {:env newenv
|
|
|
|
:chunks chunks
|
|
|
|
:on-status onsignal
|
|
|
|
:source "repl"}))
|
2018-05-22 02:08:16 +00:00
|
|
|
|
2019-01-09 22:09:16 +00:00
|
|
|
(defmacro meta
|
|
|
|
"Add metadata to the current environment."
|
|
|
|
[& args]
|
|
|
|
(def opts (table ;args))
|
|
|
|
(loop [[k v] :pairs opts]
|
|
|
|
(put *env* k v)))
|
|
|
|
|
2019-01-09 00:59:54 +00:00
|
|
|
(defn all-bindings
|
2018-05-22 02:08:16 +00:00
|
|
|
"Get all symbols available in the current environment."
|
2018-11-25 19:03:00 +00:00
|
|
|
[env &]
|
2018-05-22 02:08:16 +00:00
|
|
|
(default env *env*)
|
|
|
|
(def envs @[])
|
2018-12-17 02:57:32 +00:00
|
|
|
(do (var e env) (while e (array/push envs e) (set e (table/getproto e))))
|
2018-05-22 02:08:16 +00:00
|
|
|
(def symbol-set @{})
|
2018-11-27 06:42:41 +00:00
|
|
|
(loop [envi :in envs
|
2019-01-09 22:09:16 +00:00
|
|
|
k :keys envi
|
|
|
|
:when (symbol? k)]
|
2019-01-07 00:33:27 +00:00
|
|
|
(put symbol-set k true))
|
2018-05-22 02:08:16 +00:00
|
|
|
(sort (keys symbol-set)))
|
2019-01-18 17:04:34 +00:00
|
|
|
|
|
|
|
# Use dynamic *env* from now on
|
|
|
|
(put _env '_env nil)
|