2018-09-06 02:18:42 +00:00
|
|
|
# The core janet library
|
2018-03-16 03:27:44 +00:00
|
|
|
# Copyright 2018 (C) Calvin Rose
|
2018-01-31 22:39:18 +00:00
|
|
|
|
2018-03-28 17:50:06 +00:00
|
|
|
###
|
|
|
|
###
|
|
|
|
### Macros and Basic Functions
|
|
|
|
###
|
|
|
|
###
|
|
|
|
|
2018-03-24 05:44:17 +00:00
|
|
|
(var *env*
|
2018-07-02 01:12:46 +00:00
|
|
|
"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))
|
|
|
|
(def tuple? (= t :tuple))
|
|
|
|
(def array? (= t :array))
|
2018-11-15 20:45:41 +00:00
|
|
|
(if (if tuple? tuple? array?)
|
|
|
|
i
|
2018-11-16 07:09:38 +00:00
|
|
|
(do
|
2018-11-15 20:45:41 +00:00
|
|
|
(if (= (type ith) :string)
|
|
|
|
(:= docstr ith)
|
|
|
|
(array.push modifiers ith))
|
|
|
|
(if (< i len) (recur (+ i 1)))))))
|
2018-07-02 01:12:46 +00:00
|
|
|
(def start (fstart 0))
|
2018-11-15 20:45:41 +00:00
|
|
|
(def args (get more start))
|
|
|
|
# Add arguments to definition
|
|
|
|
(var index 0)
|
|
|
|
(def arglen (length args))
|
|
|
|
(def buf (buffer "(" name))
|
|
|
|
(while (< index arglen)
|
|
|
|
(buffer.push-string buf " " (get args index))
|
|
|
|
(:= index (+ index 1)))
|
|
|
|
(array.push modifiers (string buf ")\n\n" docstr))
|
|
|
|
# Build return value
|
2018-07-02 01:12:46 +00:00
|
|
|
(def fnbody (tuple.prepend (tuple.prepend (tuple.slice more start) name) 'fn))
|
2018-11-15 20:45:41 +00:00
|
|
|
(def formargs (array.concat @['def name] modifiers @[fnbody]))
|
2018-08-23 15:10:48 +00:00
|
|
|
(tuple.slice formargs 0)))
|
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]
|
|
|
|
(apply defn (array.concat @[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-08-23 15:10:48 +00:00
|
|
|
(tuple.slice (array.concat @['defmacro name :private] more) 0))
|
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-08-23 15:10:48 +00:00
|
|
|
(tuple.slice (array.concat @['defn name :private] more) 0))
|
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-08-23 15:10:48 +00:00
|
|
|
(tuple.slice (array.concat @['def name :private] more) 0))
|
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))
|
|
|
|
(defn integer? "Check if x is an integer." [x] (= (type x) :integer))
|
|
|
|
(defn real? [x] "Check if x is a real number." (= (type x) :real))
|
|
|
|
(defn number? "Check if x is a number." [x]
|
2018-05-13 00:31:28 +00:00
|
|
|
(def t (type x))
|
|
|
|
(if (= t :integer) true (= t :real)))
|
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))
|
2018-11-16 07:09:38 +00:00
|
|
|
(defn keyword? "Check if x is a keyword style symbol."
|
2018-11-15 20:45:41 +00:00
|
|
|
[x]
|
|
|
|
(if (not= (type x) :symbol) nil (= 58 (get x 0))))
|
|
|
|
(defn buffer? "Check if x is a buffer." [x] (= (type x) :buffer))
|
|
|
|
(defn function? "Check if x is a function (not a cfunction)."
|
|
|
|
[x] (= (type x) :function))
|
|
|
|
(defn cfunction? "Check if x a cfunction." [x] (= (type x) :cfunction))
|
|
|
|
(defn abstract? "Check if x an abstract type." [x] (= (type x) :abstract))
|
|
|
|
(defn table? [x] "Check if x a table." (= (type x) :table ))
|
|
|
|
(defn struct? [x] "Check if x a struct." (= (type x) :struct))
|
|
|
|
(defn array? [x] "Check if x is an array." (= (type x) :array))
|
|
|
|
(defn tuple? [x] "Check if x is a tuple." (= (type x) :tuple))
|
|
|
|
(defn boolean? [x] "Check if x is a boolean." (= (type x) :boolean))
|
|
|
|
(defn bytes? "Check if x is a string, symbol, or buffer." [x]
|
2018-07-02 01:12:46 +00:00
|
|
|
(def t (type x))
|
|
|
|
(if (= t :string) true (if (= t :symbol) 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-07-04 03:07:35 +00:00
|
|
|
(def atomic?
|
2018-11-15 20:45:41 +00:00
|
|
|
"(atomic? 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-11-15 20:45:41 +00:00
|
|
|
(fn atomic? [x] (not (get non-atomic-types (type x))))))
|
2018-05-26 18:21:49 +00:00
|
|
|
|
2018-03-22 00:53:39 +00:00
|
|
|
# C style macros and functions for imperative sugar
|
2018-11-15 20:45:41 +00:00
|
|
|
(defn inc "Returns x + 1." [x] (+ x 1))
|
|
|
|
(defn dec "Returns x - 1." [x] (- x 1))
|
|
|
|
(defmacro ++ "Increments the var x by 1." [x] (tuple ':= x (tuple + x 1)))
|
|
|
|
(defmacro -- "Decrements the var x by 1." [x] (tuple ':= x (tuple - x 1)))
|
|
|
|
(defmacro += "Increments the var x by n." [x n] (tuple ':= x (tuple + x n)))
|
|
|
|
(defmacro -= "Decrements the vat x by n." [x n] (tuple ':= x (tuple - x n)))
|
|
|
|
(defmacro *= "Shorthand for (:= x (* x n))." [x n] (tuple ':= x (tuple * x n)))
|
|
|
|
(defmacro /= "Shorthand for (:= x (/ x n))." [x n] (tuple ':= x (tuple / x n)))
|
|
|
|
(defmacro %= "Shorthand for (:= x (% x n))." [x n] (tuple ':= x (tuple % x n)))
|
|
|
|
(defmacro &= "Shorthand for (:= x (& x n))." [x n] (tuple ':= x (tuple & x n)))
|
|
|
|
(defmacro |= "Shorthand for (:= x (| x n))." [x n] (tuple ':= x (tuple | x n)))
|
|
|
|
(defmacro ^= "Shorthand for (:= x (^ x n))." [x n] (tuple ':= x (tuple ^ x n)))
|
|
|
|
(defmacro >>= "Shorthand for (:= x (>> x n))." [x n] (tuple ':= x (tuple >> x n)))
|
|
|
|
(defmacro <<= "Shorthand for (:= x (<< x n))." [x n] (tuple ':= x (tuple << x n)))
|
|
|
|
(defmacro >>>= "Shorthand for (:= x (>>> x n))." [x n] (tuple ':= x (tuple >>> 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]
|
|
|
|
(tuple 'def sym (tuple 'if (tuple = 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 ... "
|
|
|
|
[condition exp-1 exp-2]
|
|
|
|
(tuple '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]
|
|
|
|
(tuple 'if condition (tuple.prepend body 'do)))
|
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-05-08 23:40:28 +00:00
|
|
|
(tuple 'if condition nil (tuple.prepend body 'do)))
|
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
|
|
|
|
(if (= restlen 1) (get pairs i)
|
|
|
|
(tuple 'if (get pairs i)
|
|
|
|
(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-06-29 03:36:31 +00:00
|
|
|
(def atm (atomic? 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
|
2018-07-02 01:12:46 +00:00
|
|
|
(if (= restlen 1) (get pairs i)
|
|
|
|
(tuple 'if (tuple = sym (get pairs i))
|
|
|
|
(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)
|
|
|
|
(array.push accum (tuple 'def k v))
|
|
|
|
(+= i 2))
|
2018-05-08 23:40:28 +00:00
|
|
|
(array.concat accum body)
|
2018-08-23 15:10:48 +00:00
|
|
|
(tuple.slice accum 0))
|
2018-02-03 22:22:04 +00:00
|
|
|
|
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]
|
|
|
|
(def len (length forms))
|
|
|
|
(if (= len 0)
|
|
|
|
true
|
|
|
|
((fn aux [i]
|
|
|
|
(cond
|
2018-09-10 18:21:08 +00:00
|
|
|
(>= (+ 1 i) len) (get forms i)
|
|
|
|
(tuple 'if (get forms i) (aux (+ 1 i)) false))) 0)))
|
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]
|
|
|
|
(def len (length forms))
|
|
|
|
(if (= len 0)
|
|
|
|
false
|
|
|
|
((fn aux [i]
|
|
|
|
(def fi (get forms i))
|
|
|
|
(if
|
2018-09-10 18:21:08 +00:00
|
|
|
(>= (+ 1 i) len) fi
|
2018-07-10 03:29:15 +00:00
|
|
|
(do
|
|
|
|
(if (atomic? fi)
|
2018-09-10 18:21:08 +00:00
|
|
|
(tuple 'if fi fi (aux (+ 1 i)))
|
2018-07-10 03:29:15 +00:00
|
|
|
(do
|
|
|
|
(def $fi (gensym))
|
|
|
|
(tuple 'do (tuple 'def $fi fi)
|
2018-09-10 18:21:08 +00:00
|
|
|
(tuple 'if $fi $fi (aux (+ 1 i))))))))) 0)))
|
2018-07-10 03:29:15 +00:00
|
|
|
|
2018-05-24 02:08:36 +00:00
|
|
|
(defmacro loop
|
|
|
|
"A general purpose loop macro."
|
|
|
|
[head & body]
|
2018-06-29 03:36:31 +00:00
|
|
|
(def len (length head))
|
2018-05-24 02:08:36 +00:00
|
|
|
(defn doone
|
2018-08-03 17:41:44 +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)
|
|
|
|
(tuple.prepend body 'do)
|
|
|
|
(do
|
2018-06-29 21:42:00 +00:00
|
|
|
(def {
|
2018-07-02 01:12:46 +00:00
|
|
|
i bindings
|
|
|
|
(+ i 1) verb
|
|
|
|
(+ 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
|
|
|
|
(array.push preds verb)
|
|
|
|
(doone (+ i 2) preds))
|
|
|
|
:let (tuple 'let verb (doone (+ i 2)))
|
|
|
|
:when (tuple 'if verb (doone (+ i 2)))
|
|
|
|
(error ("unexpected loop predicate: " verb)))
|
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))
|
|
|
|
(def preds @['and (tuple ':= $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-08-23 15:10:48 +00:00
|
|
|
(tuple 'while (tuple.slice preds 0)
|
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-08-23 15:10:48 +00:00
|
|
|
(tuple 'while (tuple.slice preds 0)
|
2018-07-12 02:18:24 +00:00
|
|
|
(tuple 'def bindings $iter)
|
2018-05-26 17:46:27 +00:00
|
|
|
subloop
|
2018-07-12 02:18:24 +00:00
|
|
|
(tuple ':= $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-08-23 15:10:48 +00:00
|
|
|
(tuple 'while (tuple.slice preds 0)
|
2018-07-12 02:18:24 +00:00
|
|
|
(tuple 'def bindings $iter)
|
2018-05-26 17:46:27 +00:00
|
|
|
subloop
|
2018-07-12 02:18:24 +00:00
|
|
|
(tuple ':= $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-08-23 15:10:48 +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-05-24 03:43:48 +00:00
|
|
|
(tuple ':= $i (tuple + 1 $i)))))
|
2018-07-12 01:29:39 +00:00
|
|
|
(error (string "unexpected loop verb: " verb)))))))
|
2018-08-03 17:41:44 +00:00
|
|
|
(doone 0 nil))
|
2018-05-24 02:08:36 +00:00
|
|
|
|
2018-08-23 15:10:48 +00:00
|
|
|
(defmacro fora
|
2018-05-24 02:08:36 +00:00
|
|
|
"Similar to loop, but accumulates the loop body into an array and returns that."
|
|
|
|
[head & body]
|
2018-07-01 19:49:33 +00:00
|
|
|
(def $accum (gensym))
|
2018-03-12 04:26:13 +00:00
|
|
|
(tuple 'do
|
2018-05-24 02:08:36 +00:00
|
|
|
(tuple 'def $accum @[])
|
2018-06-10 00:41:02 +00:00
|
|
|
(tuple 'loop head
|
2018-07-02 01:12:46 +00:00
|
|
|
(tuple array.push $accum
|
|
|
|
(tuple.prepend body 'do)))
|
2018-05-24 02:08:36 +00:00
|
|
|
$accum))
|
2018-02-07 05:44:51 +00:00
|
|
|
|
2018-08-23 15:10:48 +00:00
|
|
|
(defmacro for
|
|
|
|
"Similar to loop, but accumulates the loop body into a tuple and returns that."
|
|
|
|
[head & body]
|
|
|
|
(def $accum (gensym))
|
|
|
|
(tuple 'do
|
|
|
|
(tuple 'def $accum @[])
|
|
|
|
(tuple 'loop head
|
|
|
|
(tuple array.push $accum
|
|
|
|
(tuple.prepend body 'do)))
|
|
|
|
(tuple tuple.slice $accum 0)))
|
|
|
|
|
2018-07-10 03:29:15 +00:00
|
|
|
(defn sum [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-07-10 03:29:15 +00:00
|
|
|
(defn product [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 coro
|
2018-08-21 17:09:01 +00:00
|
|
|
"A wrapper for making fibers. Same as (fiber.new (fn @[] ...body))."
|
2018-07-02 01:12:46 +00:00
|
|
|
[& body]
|
2018-08-03 17:41:44 +00:00
|
|
|
(tuple fiber.new (apply tuple 'fn @[] body)))
|
2018-03-28 17:50:06 +00:00
|
|
|
|
|
|
|
(defmacro if-let
|
2018-07-02 01:12:46 +00:00
|
|
|
"Takes the first one or two forms in a vector and if both are true binds
|
|
|
|
all the forms with let and evaluates the first expression else
|
|
|
|
evaluates the second"
|
2018-08-03 17:41:44 +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]
|
|
|
|
(def bl (get bindings i))
|
|
|
|
(def br (get bindings (+ 1 i)))
|
|
|
|
(if (>= i len)
|
2018-07-02 01:12:46 +00:00
|
|
|
tru
|
|
|
|
(do
|
|
|
|
(def atm (atomic? bl))
|
|
|
|
(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-07-02 01:12:46 +00:00
|
|
|
"Takes the first one or two forms in vector and if true binds
|
|
|
|
all the forms with let and evaluates the body"
|
2018-03-28 17:50:06 +00:00
|
|
|
[bindings & body]
|
2018-05-08 23:40:28 +00:00
|
|
|
(tuple 'if-let bindings (tuple.prepend body 'do)))
|
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
|
|
|
|
1 (get functions 0)
|
|
|
|
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-07-02 01:12:46 +00:00
|
|
|
(apply 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-07-02 01:12:46 +00:00
|
|
|
"Returns the most extreme value in args based on the orderer order.
|
|
|
|
Returns nil if args is empty."
|
|
|
|
[order args]
|
|
|
|
(def len (length args))
|
|
|
|
(when (pos? len)
|
|
|
|
(var ret (get args 0))
|
|
|
|
(loop [i :range [0 len]]
|
2018-07-17 02:55:45 +00:00
|
|
|
(def v (get args i))
|
|
|
|
(if (order v ret) (:= ret v)))
|
2018-07-02 01:12:46 +00:00
|
|
|
ret))
|
2018-04-01 19:08:51 +00:00
|
|
|
|
|
|
|
(defn max [& args] (extreme > args))
|
|
|
|
(defn min [& args] (extreme < args))
|
|
|
|
(defn max-order [& args] (extreme order> args))
|
|
|
|
(defn min-order [& args] (extreme order< args))
|
|
|
|
|
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
|
2018-07-02 01:12:46 +00:00
|
|
|
"Sort an array in-place. Uses quicksort and is not a stable sort."
|
|
|
|
(do
|
|
|
|
|
|
|
|
(defn partition
|
|
|
|
[a lo hi by]
|
|
|
|
(def pivot (get a hi))
|
|
|
|
(var i lo)
|
|
|
|
(loop [j :range [lo hi]]
|
2018-07-17 02:55:45 +00:00
|
|
|
(def aj (get a j))
|
|
|
|
(when (by aj pivot)
|
|
|
|
(def ai (get a i))
|
|
|
|
(put a i aj)
|
|
|
|
(put a j ai)
|
|
|
|
(++ i)))
|
2018-07-02 01:12:46 +00:00
|
|
|
(put a hi (get a i))
|
|
|
|
(put a i pivot)
|
|
|
|
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-08-03 17:41:44 +00:00
|
|
|
(fn @[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-07-02 01:12:46 +00:00
|
|
|
"Returns the sorted version of an indexed data structure."
|
2018-08-23 15:10:48 +00:00
|
|
|
@[ind by t]
|
|
|
|
(def sa (sort (array.slice ind 0) by))
|
|
|
|
(if (= :tuple (or t (type ind)))
|
|
|
|
(tuple.slice sa 0)
|
2018-07-02 01:12:46 +00:00
|
|
|
sa))
|
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-08-03 17:41:44 +00:00
|
|
|
@[f init ind]
|
2018-07-02 01:12:46 +00:00
|
|
|
(var res init)
|
|
|
|
(loop [x :in ind]
|
2018-07-17 02:55:45 +00:00
|
|
|
(:= res (f res x)))
|
2018-07-02 01:12:46 +00:00
|
|
|
res)
|
2018-03-26 17:36:58 +00:00
|
|
|
|
2018-08-23 15:10:48 +00:00
|
|
|
(defn mapa
|
|
|
|
"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"))
|
|
|
|
(var limit (length (get inds 0)))
|
|
|
|
(loop [i :range [0 ninds]]
|
2018-07-17 02:55:45 +00:00
|
|
|
(def l (length (get inds i)))
|
|
|
|
(if (< l limit) (:= limit l)))
|
2018-07-02 01:12:46 +00:00
|
|
|
(def [i1 i2 i3 i4] inds)
|
|
|
|
(def res (array.new limit))
|
2018-07-04 05:28:31 +00:00
|
|
|
(case ninds
|
2018-07-11 00:01:39 +00:00
|
|
|
1 (loop [i :range [0 limit]] (put res i (f (get i1 i))))
|
|
|
|
2 (loop [i :range [0 limit]] (put res i (f (get i1 i) (get i2 i))))
|
|
|
|
3 (loop [i :range [0 limit]] (put res i (f (get i1 i) (get i2 i) (get i3 i))))
|
|
|
|
4 (loop [i :range [0 limit]] (put 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-07-17 02:55:45 +00:00
|
|
|
(def args (array.new ninds))
|
|
|
|
(loop [j :range [0 ninds]] (put args j (get (get inds j) i)))
|
2018-08-26 16:53:39 +00:00
|
|
|
(put res i (apply f args))))
|
2018-07-02 01:12:46 +00:00
|
|
|
res)
|
2018-03-28 17:50:06 +00:00
|
|
|
|
2018-08-23 15:10:48 +00:00
|
|
|
(defn map
|
|
|
|
"Map a function over every element in an indexed data structure and
|
|
|
|
return a tuple of the results."
|
|
|
|
[f & inds]
|
|
|
|
(tuple.slice (apply mapa f inds) 0))
|
|
|
|
|
2018-05-05 18:41:47 +00:00
|
|
|
(defn each
|
2018-07-02 01:12:46 +00:00
|
|
|
"Map a function over every element in an array or tuple but do not
|
|
|
|
return a new indexed type."
|
|
|
|
[f & inds]
|
|
|
|
(def ninds (length inds))
|
|
|
|
(if (= 0 ninds) (error "expected at least 1 indexed collection"))
|
|
|
|
(var limit (length (get inds 0)))
|
|
|
|
(loop [i :range [0 ninds]]
|
2018-07-17 02:55:45 +00:00
|
|
|
(def l (length (get inds i)))
|
|
|
|
(if (< l limit) (:= limit l)))
|
2018-07-02 01:12:46 +00:00
|
|
|
(def [i1 i2 i3 i4] inds)
|
2018-07-04 05:28:31 +00:00
|
|
|
(case ninds
|
2018-07-02 01:12:46 +00:00
|
|
|
1 (loop [i :range [0 limit]] (f (get i1 i)))
|
|
|
|
2 (loop [i :range [0 limit]] (f (get i1 i) (get i2 i)))
|
|
|
|
3 (loop [i :range [0 limit]] (f (get i1 i) (get i2 i) (get i3 i)))
|
|
|
|
4 (loop [i :range [0 limit]] (f (get i1 i) (get i2 i) (get i3 i) (get i4 i)))
|
|
|
|
(loop [i :range [0 limit]]
|
2018-07-17 02:55:45 +00:00
|
|
|
(def args (array.new ninds))
|
|
|
|
(loop [j :range [0 ninds]] (array.push args (get (get inds j) i)))
|
2018-08-26 16:53:39 +00:00
|
|
|
(apply f args))))
|
2018-05-05 18:41:47 +00:00
|
|
|
|
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-08-23 15:10:48 +00:00
|
|
|
use array to concatenate the results. Returns the type given
|
|
|
|
as the third argument, or same type as the input indexed structure."
|
2018-08-03 17:41:44 +00:00
|
|
|
@[f ind t]
|
2018-07-02 01:12:46 +00:00
|
|
|
(def res @[])
|
|
|
|
(loop [x :in ind]
|
2018-07-17 02:55:45 +00:00
|
|
|
(array.concat res (f x)))
|
2018-08-23 15:10:48 +00:00
|
|
|
(if (= :tuple (or t (type ind)))
|
|
|
|
(tuple.slice res 0)
|
2018-07-02 01:12:46 +00:00
|
|
|
res))
|
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-08-23 15:10:48 +00:00
|
|
|
which (pred element) is truthy. Returns the type given as the
|
|
|
|
third argument, or the same type as the input indexed structure."
|
2018-08-03 17:41:44 +00:00
|
|
|
@[pred ind t]
|
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)
|
|
|
|
(array.push res item)))
|
2018-08-23 15:10:48 +00:00
|
|
|
(if (= :tuple (or t (type ind)))
|
|
|
|
(tuple.slice res 0)
|
2018-07-02 01:12:46 +00:00
|
|
|
res))
|
2018-03-28 17:50:06 +00:00
|
|
|
|
2018-05-18 20:24:09 +00:00
|
|
|
(defn range
|
|
|
|
"Create an array of values [0, n)."
|
2018-07-11 15:57:17 +00:00
|
|
|
[& args]
|
|
|
|
(case (length args)
|
|
|
|
1 (do
|
|
|
|
(def [n] args)
|
|
|
|
(def arr (array.new n))
|
|
|
|
(loop [i :range [0 n]] (put arr i i))
|
|
|
|
arr)
|
|
|
|
2 (do
|
|
|
|
(def [n m] args)
|
|
|
|
(def arr (array.new n))
|
|
|
|
(loop [i :range [n m]] (put arr (- i n) i))
|
|
|
|
arr)
|
|
|
|
(error "expected 1 to 2 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)
|
|
|
|
(def item (get ind i))
|
|
|
|
(if (pred item) (:= going false) (++ i)))
|
|
|
|
(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]
|
|
|
|
(get ind (find-index pred ind)))
|
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
|
|
|
|
the predicate, and abort on first failure. Returns a new tuple."
|
|
|
|
[pred ind]
|
|
|
|
(def i (find-index pred ind))
|
|
|
|
(if i
|
|
|
|
(tuple.slice ind 0 i)
|
|
|
|
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-08-23 15:10:48 +00:00
|
|
|
(tuple.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*
|
|
|
|
[& funs]
|
|
|
|
(fn [& args]
|
2018-07-02 01:12:46 +00:00
|
|
|
(def ret @[])
|
|
|
|
(loop [f :in funs]
|
2018-08-26 16:53:39 +00:00
|
|
|
(array.push ret (apply f args)))
|
2018-08-23 15:10:48 +00:00
|
|
|
(tuple.slice ret 0)))
|
2018-03-22 00:53:39 +00:00
|
|
|
|
|
|
|
(defmacro juxt
|
2018-07-02 01:12:46 +00:00
|
|
|
[& funs]
|
|
|
|
(def parts @['tuple])
|
|
|
|
(def $args (gensym))
|
|
|
|
(loop [f :in funs]
|
2018-08-26 16:53:39 +00:00
|
|
|
(array.push parts (tuple apply f $args)))
|
2018-08-23 15:10:48 +00:00
|
|
|
(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
|
|
|
|
in form, and inserts the modified firsts form into the second form
|
|
|
|
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))
|
|
|
|
[tuple (get n 0) (array.slice n 1)]
|
|
|
|
[tuple n @[]]))
|
|
|
|
(def parts (array.concat @[h last] t))
|
2018-08-23 15:10:48 +00:00
|
|
|
(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
|
|
|
|
in form, and inserts the modified firsts form into the second form
|
|
|
|
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))
|
|
|
|
[tuple (get n 0) (array.slice n 1)]
|
|
|
|
[tuple n @[]]))
|
|
|
|
(def parts (array.concat @[h] t @[last]))
|
2018-08-23 15:10:48 +00:00
|
|
|
(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-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-08-26 16:53:39 +00:00
|
|
|
(fn [& r] (apply f (array.concat @[] more r)))))
|
2018-03-28 17:50:06 +00:00
|
|
|
|
2018-08-03 17:41:44 +00:00
|
|
|
(defn every? [pred ind]
|
2018-07-02 01:12:46 +00:00
|
|
|
(var res true)
|
|
|
|
(var i 0)
|
2018-08-03 17:41:44 +00:00
|
|
|
(def len (length ind))
|
2018-07-02 01:12:46 +00:00
|
|
|
(while (< i len)
|
2018-08-03 17:41:44 +00:00
|
|
|
(def item (get ind i))
|
2018-07-02 01:12:46 +00:00
|
|
|
(if (pred item)
|
|
|
|
(++ i)
|
|
|
|
(do (:= res false) (:= i len))))
|
|
|
|
res)
|
2018-03-28 17:50:06 +00:00
|
|
|
|
2018-05-08 23:40:28 +00:00
|
|
|
(defn array.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]
|
|
|
|
(var n (dec (length t)))
|
|
|
|
(var reversed @[])
|
|
|
|
(while (>= n 0)
|
|
|
|
(array.push reversed (get t n))
|
|
|
|
(-- n))
|
2018-03-23 22:36:56 +00:00
|
|
|
reversed)
|
|
|
|
|
2018-05-08 23:40:28 +00:00
|
|
|
(defn tuple.reverse
|
2018-07-02 01:12:46 +00:00
|
|
|
"Reverses the order of the elements given an array or tuple and returns a tuple"
|
|
|
|
[t]
|
2018-08-23 15:10:48 +00:00
|
|
|
(tuple.slice (array.reverse t) 0))
|
2018-03-23 22:36:56 +00:00
|
|
|
|
|
|
|
(defn reverse
|
2018-07-02 01:12:46 +00:00
|
|
|
"Reverses order of elements in a given array or tuple"
|
|
|
|
[t]
|
2018-07-04 05:28:31 +00:00
|
|
|
((case (type t)
|
2018-07-02 01:12:46 +00:00
|
|
|
:tuple tuple.reverse
|
|
|
|
:array array.reverse) t))
|
2018-03-23 22:36:56 +00:00
|
|
|
|
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]
|
|
|
|
(put ret (get ds k) k))
|
|
|
|
ret)
|
|
|
|
|
2018-03-23 22:36:56 +00:00
|
|
|
(defn zipcoll
|
2018-07-02 01:12:46 +00:00
|
|
|
"Creates an table or tuple from two arrays/tuples. If a third argument of
|
|
|
|
:struct is given result is struct else is table."
|
2018-08-03 17:41:44 +00:00
|
|
|
@[keys vals t]
|
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]]
|
2018-07-17 02:55:45 +00:00
|
|
|
(put res (get keys i) (get vals i)))
|
2018-03-28 17:50:06 +00:00
|
|
|
(if (= :struct t)
|
2018-05-08 23:40:28 +00:00
|
|
|
(table.to-struct res)
|
2018-03-28 17:50:06 +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"
|
2018-03-23 22:36:56 +00:00
|
|
|
[coll a-key a-function & args]
|
2018-03-24 16:48:42 +00:00
|
|
|
(def old-value (get coll a-key))
|
2018-03-23 22:36:56 +00:00
|
|
|
(put coll a-key (apply a-function old-value args)))
|
|
|
|
|
|
|
|
(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.
|
|
|
|
The type of the first collection determines the type of the resulting
|
|
|
|
collection"
|
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]
|
2018-07-17 02:55:45 +00:00
|
|
|
(put container key (get c key)))
|
2018-05-08 23:40:28 +00:00
|
|
|
(if (table? (get colls 0)) container (table.to-struct 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]
|
|
|
|
(def arr (array.new (length x)))
|
|
|
|
(var k (next x nil))
|
|
|
|
(while (not= nil k)
|
|
|
|
(array.push arr k)
|
|
|
|
(:= k (next x k)))
|
|
|
|
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]
|
|
|
|
(def arr (array.new (length x)))
|
|
|
|
(var k (next x nil))
|
|
|
|
(while (not= nil k)
|
|
|
|
(array.push arr (get x k))
|
|
|
|
(:= k (next x k)))
|
|
|
|
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]
|
|
|
|
(def arr (array.new (length x)))
|
|
|
|
(var k (next x nil))
|
|
|
|
(while (not= nil k)
|
|
|
|
(array.push arr (tuple k (get x k)))
|
|
|
|
(:= k (next x k)))
|
|
|
|
arr)
|
2018-03-29 01:16:12 +00:00
|
|
|
|
2018-06-03 18:21:24 +00:00
|
|
|
(defn frequencies
|
2018-07-02 01:12:46 +00:00
|
|
|
"Get the number of occurences of each value in a indexed structure."
|
|
|
|
[ind]
|
|
|
|
(def freqs @{})
|
|
|
|
(loop
|
|
|
|
[x :in ind]
|
|
|
|
(def n (get freqs x))
|
|
|
|
(put freqs x (if n (+ 1 n) 1)))
|
|
|
|
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)
|
|
|
|
(def len (apply min (mapa length cols)))
|
|
|
|
(loop [i :range [0 len]]
|
|
|
|
(loop [ci :range [0 ncol]]
|
|
|
|
(array.push res (get (get cols ci) i)))))
|
2018-11-08 03:52:49 +00:00
|
|
|
res)
|
|
|
|
|
2018-03-28 17:50:06 +00:00
|
|
|
###
|
|
|
|
###
|
|
|
|
### Pretty Printer
|
|
|
|
###
|
|
|
|
###
|
|
|
|
|
2018-05-05 18:41:47 +00:00
|
|
|
(defn pp
|
2018-07-02 01:12:46 +00:00
|
|
|
"Pretty print a value. Displays values inside collections, and is safe
|
|
|
|
to call on any table. Does not print table prototype information."
|
2018-10-17 03:08:26 +00:00
|
|
|
@[x file]
|
2018-03-26 17:36:58 +00:00
|
|
|
|
2018-10-17 03:08:26 +00:00
|
|
|
(default file stdout)
|
2018-03-26 17:36:58 +00:00
|
|
|
(def buf @"")
|
|
|
|
(def indent @"\n")
|
|
|
|
(def seen @{})
|
|
|
|
(var nextid 0)
|
|
|
|
|
|
|
|
# Forward declaration
|
|
|
|
(var recur nil)
|
|
|
|
|
|
|
|
(defn do-ds
|
2018-07-02 01:12:46 +00:00
|
|
|
[y start end checkcycle dispatch]
|
|
|
|
(def id (get seen y))
|
|
|
|
(if (and checkcycle id)
|
|
|
|
(do
|
|
|
|
(buffer.push-string buf "<cycle ")
|
|
|
|
(buffer.push-string buf (string id))
|
|
|
|
(buffer.push-string buf ">"))
|
|
|
|
(do
|
|
|
|
(put seen y (++ nextid))
|
|
|
|
(buffer.push-string buf start)
|
|
|
|
(dispatch y)
|
|
|
|
(buffer.push-string buf end))))
|
2018-03-26 17:36:58 +00:00
|
|
|
|
|
|
|
(defn pp-seq [y]
|
2018-07-02 01:12:46 +00:00
|
|
|
(def len (length y))
|
|
|
|
(if (< len 5)
|
|
|
|
(do
|
|
|
|
(loop [i :range [0 len]]
|
2018-07-17 02:55:45 +00:00
|
|
|
(when (not= i 0) (buffer.push-string buf " "))
|
|
|
|
(recur (get y i))))
|
2018-07-02 01:12:46 +00:00
|
|
|
(do
|
|
|
|
(buffer.push-string indent " ")
|
|
|
|
(loop [i :range [0 len]]
|
2018-07-17 02:55:45 +00:00
|
|
|
(when (not= i len) (buffer.push-string buf indent))
|
|
|
|
(recur (get y i)))
|
2018-07-02 01:12:46 +00:00
|
|
|
(buffer.popn indent 2)
|
|
|
|
(buffer.push-string buf indent))))
|
2018-03-26 17:36:58 +00:00
|
|
|
|
2018-05-05 18:05:56 +00:00
|
|
|
(defn pp-dict-nested [y]
|
2018-07-02 01:12:46 +00:00
|
|
|
(buffer.push-string indent " ")
|
|
|
|
(loop [[k v] :in (sort (pairs y))]
|
2018-07-17 02:55:45 +00:00
|
|
|
(buffer.push-string buf indent)
|
|
|
|
(recur k)
|
|
|
|
(buffer.push-string buf " ")
|
|
|
|
(recur v))
|
2018-07-02 01:12:46 +00:00
|
|
|
(buffer.popn indent 2)
|
|
|
|
(buffer.push-string buf indent))
|
2018-03-26 17:36:58 +00:00
|
|
|
|
|
|
|
(defn pp-dict-simple [y]
|
2018-07-02 01:12:46 +00:00
|
|
|
(var i -1)
|
|
|
|
(loop [[k v] :in (sort (pairs y))]
|
2018-07-17 02:55:45 +00:00
|
|
|
(if (pos? (++ i)) (buffer.push-string buf " "))
|
|
|
|
(recur k)
|
|
|
|
(buffer.push-string buf " ")
|
|
|
|
(recur v)))
|
2018-03-26 17:36:58 +00:00
|
|
|
|
|
|
|
(defn pp-dict [y]
|
2018-07-02 01:12:46 +00:00
|
|
|
(def complex? (> (length y) 4))
|
|
|
|
((if complex? pp-dict-nested pp-dict-simple) y))
|
2018-03-26 17:36:58 +00:00
|
|
|
|
2018-11-08 03:27:06 +00:00
|
|
|
(def printers
|
2018-10-17 03:08:26 +00:00
|
|
|
{:array (fn [y] (do-ds y "@[" "]" true pp-seq))
|
|
|
|
:tuple (fn [y] (do-ds y "(" ")" false pp-seq))
|
|
|
|
:table (fn [y] (do-ds y "@{" "}" true pp-dict))
|
|
|
|
:struct (fn [y] (do-ds y "{" "}" false pp-dict))})
|
2018-05-05 18:41:47 +00:00
|
|
|
|
2018-03-26 17:36:58 +00:00
|
|
|
(:= recur (fn [y]
|
2018-07-02 01:12:46 +00:00
|
|
|
(def p (get printers (type y)))
|
|
|
|
(if p
|
|
|
|
(p y)
|
|
|
|
(buffer.push-string buf (describe y)))))
|
2018-03-14 23:08:00 +00:00
|
|
|
|
2018-03-26 17:36:58 +00:00
|
|
|
(recur x)
|
2018-05-08 23:40:28 +00:00
|
|
|
(buffer.push-string buf "\n")
|
2018-03-14 23:08:00 +00:00
|
|
|
|
2018-10-17 03:08:26 +00:00
|
|
|
(file.write file buf)
|
2018-05-22 02:08:16 +00:00
|
|
|
nil)
|
2018-03-14 23:08:00 +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))
|
|
|
|
(do (:= current 0) "\n ")
|
|
|
|
(do (++ current) " ")))
|
|
|
|
(+= current (length word))
|
|
|
|
(if (> oldcur 0)
|
|
|
|
(buffer.push-string buf spacer))
|
|
|
|
(buffer.push-string buf word)
|
|
|
|
(buffer.clear word))
|
|
|
|
|
|
|
|
(loop [b :in text]
|
|
|
|
(if (and (not= b 10) (not= b 32))
|
|
|
|
(if (= b 9)
|
|
|
|
(buffer.push-string word " ")
|
|
|
|
(buffer.push-byte word b))
|
|
|
|
(do
|
|
|
|
(if (> (length word) 0) (pushword))
|
|
|
|
(when (= b 10)
|
|
|
|
(buffer.push-string buf "\n ")
|
|
|
|
(:= current 0)))))
|
|
|
|
|
|
|
|
# Last word
|
|
|
|
(pushword)
|
|
|
|
|
|
|
|
buf)
|
|
|
|
|
|
|
|
(defn doc*
|
|
|
|
"Get the documentation for a symbol in a given environment."
|
|
|
|
[env sym]
|
|
|
|
(def x (get env sym))
|
|
|
|
(if (not x)
|
|
|
|
(print "symbol " sym " not found.")
|
|
|
|
(do
|
|
|
|
(def d (get x :doc))
|
|
|
|
(print "\n\n" (if d (doc-format d) "no documentation found.") "\n\n"))))
|
|
|
|
|
|
|
|
(defmacro doc
|
|
|
|
"Shows documentation for the given symbol."
|
|
|
|
[sym]
|
|
|
|
(tuple doc* '_env (tuple 'quote sym)))
|
|
|
|
|
2018-03-28 17:50:06 +00:00
|
|
|
###
|
|
|
|
###
|
|
|
|
### Macro Expansion
|
|
|
|
###
|
|
|
|
###
|
2018-03-15 01:46:56 +00:00
|
|
|
|
2018-05-22 02:08:16 +00:00
|
|
|
(defn macroexpand-1
|
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)
|
2018-11-15 23:28:55 +00:00
|
|
|
(put newt (macroexpand-1 key) (on-value (get t key)))
|
2018-07-02 01:12:46 +00:00
|
|
|
(:= key (next t key)))
|
|
|
|
newt)
|
|
|
|
|
|
|
|
(defn expand-bindings [x]
|
2018-07-04 05:28:31 +00:00
|
|
|
(case (type x)
|
2018-11-08 03:27:06 +00:00
|
|
|
:array (mapa expand-bindings x)
|
|
|
|
:tuple (map expand-bindings x)
|
2018-07-02 01:12:46 +00:00
|
|
|
:table (dotable x expand-bindings)
|
|
|
|
:struct (table.to-struct (dotable x expand-bindings))
|
|
|
|
(macroexpand-1 x)))
|
|
|
|
|
|
|
|
(defn expanddef [t]
|
2018-11-15 23:28:55 +00:00
|
|
|
(def last (get t (- (length t) 1)))
|
|
|
|
(def bound (get t 1))
|
2018-11-16 07:09:38 +00:00
|
|
|
(tuple.slice
|
|
|
|
(array.concat
|
2018-11-15 23:28:55 +00:00
|
|
|
@[(get t 0) (expand-bindings bound)]
|
|
|
|
(tuple.slice t 2 -2)
|
|
|
|
@[(macroexpand-1 last)])
|
|
|
|
0))
|
2018-07-02 01:12:46 +00:00
|
|
|
|
|
|
|
(defn expandall [t]
|
2018-11-08 03:27:06 +00:00
|
|
|
(def args (mapa macroexpand-1 (tuple.slice t 1)))
|
2018-07-02 01:12:46 +00:00
|
|
|
(apply tuple (get t 0) args))
|
|
|
|
|
|
|
|
(defn expandfn [t]
|
2018-11-15 23:28:55 +00:00
|
|
|
(if (symbol? (get t 1))
|
|
|
|
(do
|
|
|
|
(def args (mapa macroexpand-1 (tuple.slice t 3)))
|
|
|
|
(apply tuple 'fn (get t 1) (get t 2) args))
|
|
|
|
(do
|
|
|
|
(def args (mapa macroexpand-1 (tuple.slice t 2)))
|
|
|
|
(apply tuple 'fn (get t 1) args))))
|
2018-07-02 01:12:46 +00:00
|
|
|
|
2018-07-17 02:55:45 +00:00
|
|
|
(def specs
|
|
|
|
{':= expanddef
|
|
|
|
'def expanddef
|
|
|
|
'do expandall
|
|
|
|
'fn expandfn
|
|
|
|
'if expandall
|
|
|
|
'quote identity
|
|
|
|
'var expanddef
|
|
|
|
'while expandall})
|
2018-07-02 01:12:46 +00:00
|
|
|
|
|
|
|
(defn dotup [t]
|
|
|
|
(def h (get t 0))
|
|
|
|
(def s (get specs h))
|
|
|
|
(def entry (or (get *env* h) {}))
|
|
|
|
(def m (get entry :value))
|
|
|
|
(def m? (get entry :macro))
|
|
|
|
(cond
|
|
|
|
s (s t)
|
2018-08-26 16:53:39 +00:00
|
|
|
m? (apply m (tuple.slice t 1))
|
2018-11-15 23:28:55 +00:00
|
|
|
(map macroexpand-1 t)))
|
2018-07-02 01:12:46 +00:00
|
|
|
|
2018-07-17 02:55:45 +00:00
|
|
|
(def ret
|
|
|
|
(case (type x)
|
|
|
|
:tuple (dotup x)
|
2018-11-08 03:27:06 +00:00
|
|
|
:array (mapa macroexpand-1 x)
|
2018-07-17 02:55:45 +00:00
|
|
|
:struct (table.to-struct (dotable x macroexpand-1))
|
|
|
|
:table (dotable x macroexpand-1)
|
|
|
|
x))
|
2018-07-02 01:12:46 +00:00
|
|
|
ret)
|
2018-03-18 18:01:58 +00:00
|
|
|
|
2018-06-29 23:44:33 +00:00
|
|
|
(defn all? [xs]
|
2018-06-29 21:42:00 +00:00
|
|
|
(var good true)
|
|
|
|
(loop [x :in xs :while good] (if x nil (:= good false)))
|
|
|
|
good)
|
|
|
|
|
2018-06-29 23:44:33 +00:00
|
|
|
(defn some? [xs]
|
2018-06-29 21:42:00 +00:00
|
|
|
(var bad true)
|
|
|
|
(loop [x :in xs :while bad] (if x (:= bad false)))
|
|
|
|
(not bad))
|
|
|
|
|
2018-06-29 23:44:33 +00:00
|
|
|
(defn deep-not= [x y]
|
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=."
|
|
|
|
(def tx (type x))
|
|
|
|
(or
|
|
|
|
(not= tx (type y))
|
2018-07-04 05:28:31 +00:00
|
|
|
(case tx
|
2018-06-29 23:44:33 +00:00
|
|
|
:tuple (or (not= (length x) (length y)) (some? (map deep-not= x y)))
|
|
|
|
:array (or (not= (length x) (length y)) (some? (map deep-not= x y)))
|
|
|
|
:struct (deep-not= (pairs x) (pairs y))
|
|
|
|
:table (deep-not= (table.to-struct x) (table.to-struct y))
|
|
|
|
:buffer (not= (string x) (string y))
|
|
|
|
(not= x y))))
|
|
|
|
|
|
|
|
(defn deep= [x y]
|
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 =."
|
|
|
|
(not (deep-not= x y)))
|
2018-06-29 23:44:33 +00:00
|
|
|
|
2018-03-18 18:01:58 +00:00
|
|
|
(defn macroexpand
|
2018-07-02 01:12:46 +00:00
|
|
|
"Expand macros completely."
|
|
|
|
[x]
|
|
|
|
(var previous x)
|
|
|
|
(var current (macroexpand-1 x))
|
|
|
|
(var counter 0)
|
|
|
|
(while (deep-not= current previous)
|
|
|
|
(if (> (++ counter) 200)
|
|
|
|
(error "macro expansion too nested"))
|
|
|
|
(:= previous current)
|
|
|
|
(:= current (macroexpand-1 current)))
|
|
|
|
current)
|
2018-03-18 18:01:58 +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-08-03 17:41:44 +00:00
|
|
|
@[parent]
|
2018-07-02 01:12:46 +00:00
|
|
|
(def parent (if parent parent _env))
|
|
|
|
(def newenv (table.setproto @{} parent))
|
|
|
|
(put newenv '_env @{:value newenv :private true})
|
|
|
|
newenv)
|
2018-03-15 21:19:31 +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,
|
|
|
|
and is encapsulates the parsing, compilation, and evaluation of janet.
|
2018-07-02 01:12:46 +00:00
|
|
|
env is the environment to evaluate the code in, chunks is a function
|
|
|
|
that returns strings or buffers of source code (from a repl, file,
|
|
|
|
network connection, etc. onvalue and onerr are callbacks that are
|
|
|
|
invoked when a result is returned and when an error is produced,
|
|
|
|
respectively.
|
|
|
|
|
|
|
|
This function can be used to implement a repl very easily, simply
|
|
|
|
pass a function that reads line from stdin to chunks, and print to
|
|
|
|
onvalue."
|
2018-08-03 17:41:44 +00:00
|
|
|
@[env chunks onvalue onerr where]
|
2018-07-02 01:12:46 +00:00
|
|
|
|
|
|
|
# Are we done yet?
|
|
|
|
(var going true)
|
|
|
|
|
|
|
|
# The parser object
|
|
|
|
(def p (parser.new))
|
|
|
|
|
|
|
|
# Fiber stream of characters
|
2018-07-04 03:07:35 +00:00
|
|
|
(def chars
|
2018-07-02 01:12:46 +00:00
|
|
|
(coro
|
|
|
|
(def buf @"")
|
|
|
|
(var len 1)
|
|
|
|
(while (< 0 len)
|
|
|
|
(buffer.clear buf)
|
|
|
|
(chunks buf p)
|
|
|
|
(:= len (length buf))
|
|
|
|
(loop [i :range [0 len]]
|
2018-07-17 02:55:45 +00:00
|
|
|
(yield (get buf i))))
|
2018-07-02 01:12:46 +00:00
|
|
|
0))
|
|
|
|
|
|
|
|
# Fiber stream of values
|
2018-07-04 03:07:35 +00:00
|
|
|
(def vals
|
2018-07-02 01:12:46 +00:00
|
|
|
(coro
|
|
|
|
(while going
|
2018-07-04 05:28:31 +00:00
|
|
|
(case (parser.status p)
|
2018-07-02 01:12:46 +00:00
|
|
|
:full (yield (parser.produce p))
|
|
|
|
:error (do
|
|
|
|
(def (line col) (parser.where p))
|
|
|
|
(onerr where "parse" (string (parser.error p) " on line " line ", column " col)))
|
2018-07-04 05:28:31 +00:00
|
|
|
(case (fiber.status chars)
|
2018-10-17 03:08:26 +00:00
|
|
|
:new (parser.byte p (resume chars nil))
|
|
|
|
:pending (parser.byte p (resume chars nil))
|
2018-07-02 01:12:46 +00:00
|
|
|
(:= going false))))
|
|
|
|
(when (not= :root (parser.status p))
|
|
|
|
(onerr where "parse" "unexpected end of source"))))
|
|
|
|
|
|
|
|
# Evaluate 1 source form
|
|
|
|
(defn eval1 [source]
|
|
|
|
(var good true)
|
2018-07-04 03:07:35 +00:00
|
|
|
(def f
|
|
|
|
(fiber.new
|
2018-08-03 17:41:44 +00:00
|
|
|
(fn @[]
|
2018-07-02 01:12:46 +00:00
|
|
|
(def res (compile source env where))
|
|
|
|
(if (= (type res) :function)
|
|
|
|
(res)
|
|
|
|
(do
|
|
|
|
(:= good false)
|
2018-07-04 03:07:35 +00:00
|
|
|
(def {:error err :line errl :column errc :fiber errf} res)
|
|
|
|
(onerr
|
|
|
|
where
|
2018-07-02 01:12:46 +00:00
|
|
|
"compile"
|
|
|
|
(if (< 0 errl)
|
2018-07-04 03:07:35 +00:00
|
|
|
(string err "\n in a form at line " errl ", column " errc)
|
|
|
|
err)
|
|
|
|
errf))))
|
2018-07-02 01:12:46 +00:00
|
|
|
:a))
|
2018-10-17 03:08:26 +00:00
|
|
|
(def res (resume f nil))
|
2018-07-02 01:12:46 +00:00
|
|
|
(when good
|
|
|
|
(def sig (fiber.status f))
|
|
|
|
(if going
|
|
|
|
(if (= sig :dead)
|
|
|
|
(onvalue res)
|
|
|
|
(onerr where "runtime" res f)))))
|
|
|
|
|
|
|
|
# Run loop
|
|
|
|
(def oldenv *env*)
|
|
|
|
(:= *env* env)
|
2018-10-17 03:08:26 +00:00
|
|
|
(while going (eval1 (resume vals nil)))
|
2018-07-02 01:12:46 +00:00
|
|
|
(:= *env* oldenv)
|
|
|
|
|
|
|
|
env)
|
2018-03-12 04:26:13 +00:00
|
|
|
|
2018-03-22 00:53:39 +00:00
|
|
|
(defn default-error-handler
|
2018-08-13 21:40:55 +00:00
|
|
|
@[source t x f]
|
2018-10-17 03:08:26 +00:00
|
|
|
(file.write stderr (string t " error in " source ": "))
|
2018-07-02 01:12:46 +00:00
|
|
|
(if (bytes? x)
|
2018-10-17 03:08:26 +00:00
|
|
|
(do (file.write stderr x)
|
|
|
|
(file.write stderr "\n"))
|
|
|
|
(pp x stderr))
|
2018-07-02 01:12:46 +00:00
|
|
|
(when f
|
|
|
|
(def st (fiber.stack f))
|
2018-07-04 03:07:35 +00:00
|
|
|
(loop
|
2018-07-17 02:55:45 +00:00
|
|
|
[{:function func
|
2018-07-02 01:12:46 +00:00
|
|
|
:tail tail
|
|
|
|
:pc pc
|
|
|
|
:c c
|
|
|
|
:name name
|
|
|
|
:source source
|
2018-07-04 03:07:35 +00:00
|
|
|
:line source-line
|
2018-07-17 02:55:45 +00:00
|
|
|
:column source-col} :in st]
|
2018-10-17 03:08:26 +00:00
|
|
|
(file.write stderr " in")
|
|
|
|
(when c (file.write stderr " cfunction"))
|
2018-07-02 01:12:46 +00:00
|
|
|
(if name
|
2018-10-17 03:08:26 +00:00
|
|
|
(file.write stderr " " name)
|
|
|
|
(when func (file.write stderr " " (string func))))
|
2018-07-04 03:07:35 +00:00
|
|
|
(if source
|
2018-07-02 01:12:46 +00:00
|
|
|
(do
|
2018-10-17 03:08:26 +00:00
|
|
|
(file.write stderr " [" source "]")
|
2018-07-17 02:55:45 +00:00
|
|
|
(if source-line
|
|
|
|
(file.write
|
2018-10-17 03:08:26 +00:00
|
|
|
stderr
|
2018-07-04 03:07:35 +00:00
|
|
|
" on line "
|
2018-07-17 02:55:45 +00:00
|
|
|
(string source-line)
|
2018-07-04 03:07:35 +00:00
|
|
|
", column "
|
|
|
|
(string source-col)))))
|
|
|
|
(if (and (not source-line) pc)
|
2018-10-17 03:08:26 +00:00
|
|
|
(file.write stderr " (pc=" (string pc) ")"))
|
|
|
|
(when tail (file.write stderr " (tailcall)"))
|
|
|
|
(file.write stderr "\n"))))
|
2018-03-22 00:53:39 +00:00
|
|
|
|
2018-05-19 02:18:34 +00:00
|
|
|
(defn eval
|
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."
|
|
|
|
[str]
|
|
|
|
(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)
|
|
|
|
(:= state nil)
|
2018-10-22 05:28:39 +00:00
|
|
|
(when ret
|
|
|
|
(buffer.push-string buf ret)
|
|
|
|
(buffer.push-string buf "\n")))
|
2018-07-02 01:12:46 +00:00
|
|
|
(var returnval nil)
|
|
|
|
(run-context *env* chunks (fn [x] (:= returnval x)) default-error-handler "eval")
|
|
|
|
returnval)
|
|
|
|
|
2018-07-17 02:55:45 +00:00
|
|
|
(do
|
2018-09-06 02:18:42 +00:00
|
|
|
(def syspath (or (os.getenv "JANET_PATH") "/usr/local/lib/janet/"))
|
2018-07-17 02:55:45 +00:00
|
|
|
(defglobal 'module.paths
|
2018-09-06 02:18:42 +00:00
|
|
|
@["./?.janet"
|
|
|
|
"./?/init.janet"
|
|
|
|
"./janet_modules/?.janet"
|
|
|
|
"./janet_modules/?/init.janet"
|
|
|
|
(string syspath janet.version "/?.janet")
|
|
|
|
(string syspath janet.version "/?/init.janet")
|
|
|
|
(string syspath "/?.janet")
|
|
|
|
(string syspath "/?/init.janet")])
|
2018-07-17 02:55:45 +00:00
|
|
|
(defglobal 'module.native-paths
|
|
|
|
@["./?.so"
|
|
|
|
"./?/??.so"
|
2018-09-06 02:18:42 +00:00
|
|
|
"./janet_modules/?.so"
|
|
|
|
"./janet_modules/?/??.so"
|
|
|
|
(string syspath janet.version "/?.so")
|
|
|
|
(string syspath janet.version "/?/??.so")
|
2018-07-17 02:55:45 +00:00
|
|
|
(string syspath "/?.so")
|
|
|
|
(string syspath "/?/??.so")]))
|
2018-11-08 03:27:06 +00:00
|
|
|
|
2018-08-07 04:54:47 +00:00
|
|
|
(if (= :windows (os.which))
|
|
|
|
(loop [i :range [0 (length module.native-paths)]]
|
|
|
|
(def x (get module.native-paths i))
|
|
|
|
(put
|
|
|
|
module.native-paths
|
|
|
|
i
|
|
|
|
(string.replace ".so" ".dll" x))))
|
2018-05-19 00:53:19 +00:00
|
|
|
|
2018-05-20 01:29:22 +00:00
|
|
|
(defn module.find
|
|
|
|
[path paths]
|
|
|
|
(def parts (string.split "." path))
|
|
|
|
(def last (get parts (- (length parts) 1)))
|
|
|
|
(def normname (string.replace-all "." "/" path))
|
|
|
|
(array.push
|
2018-08-23 15:10:48 +00:00
|
|
|
(mapa (fn [x]
|
2018-07-02 01:12:46 +00:00
|
|
|
(def y (string.replace "??" last x))
|
|
|
|
(string.replace "?" normname y))
|
2018-05-20 01:29:22 +00:00
|
|
|
paths)
|
|
|
|
path))
|
|
|
|
|
2018-05-19 02:18:34 +00:00
|
|
|
(def require
|
2018-07-02 01:12:46 +00:00
|
|
|
"Require a module with the given name. Will search all of the paths in
|
|
|
|
module.paths, then the path as a raw file path. Returns the new environment
|
|
|
|
returned from compiling and running the file."
|
|
|
|
(do
|
|
|
|
|
|
|
|
(defn check-mod
|
|
|
|
[f testpath]
|
|
|
|
(if f f (file.open testpath)))
|
|
|
|
|
|
|
|
(defn find-mod [path]
|
|
|
|
(def paths (module.find path module.paths))
|
|
|
|
(reduce check-mod nil paths))
|
|
|
|
|
|
|
|
(defn check-native
|
|
|
|
[p testpath]
|
|
|
|
(if p
|
|
|
|
p
|
|
|
|
(do
|
|
|
|
(def f (file.open testpath))
|
|
|
|
(if f (do (file.close f) testpath)))))
|
|
|
|
|
|
|
|
(defn find-native [path]
|
|
|
|
(def paths (module.find path module.native-paths))
|
|
|
|
(reduce check-native nil paths))
|
|
|
|
|
|
|
|
(def cache @{})
|
|
|
|
(def loading @{})
|
2018-08-03 17:41:44 +00:00
|
|
|
(fn require @[path args]
|
2018-07-02 01:12:46 +00:00
|
|
|
(when (get loading path)
|
|
|
|
(error (string "circular dependency: module " path " is loading")))
|
|
|
|
(def {:exit exit-on-error} (or args {}))
|
|
|
|
(def check (get cache path))
|
2018-07-04 03:07:35 +00:00
|
|
|
(if check
|
|
|
|
check
|
2018-07-02 01:12:46 +00:00
|
|
|
(do
|
|
|
|
(def newenv (make-env))
|
|
|
|
(put cache path newenv)
|
|
|
|
(put loading path true)
|
|
|
|
(def f (find-mod path))
|
|
|
|
(if f
|
|
|
|
(do
|
2018-09-06 02:18:42 +00:00
|
|
|
# Normal janet module
|
2018-08-03 17:41:44 +00:00
|
|
|
(defn chunks [buf _] (file.read f 1024 buf))
|
2018-07-02 01:12:46 +00:00
|
|
|
(run-context newenv chunks identity
|
|
|
|
(if exit-on-error
|
2018-08-03 17:41:44 +00:00
|
|
|
(fn @[a b c d] (default-error-handler a b c d) (os.exit 1))
|
2018-07-02 01:12:46 +00:00
|
|
|
default-error-handler)
|
|
|
|
path)
|
2018-07-17 02:55:45 +00:00
|
|
|
(file.close f))
|
2018-07-02 01:12:46 +00:00
|
|
|
(do
|
|
|
|
# Try native module
|
|
|
|
(def n (find-native path))
|
|
|
|
(if (not n)
|
|
|
|
(error (string "could not open file for module " path)))
|
2018-07-17 02:55:45 +00:00
|
|
|
((native n) newenv)))
|
|
|
|
(put loading path false)
|
|
|
|
newenv)))))
|
2018-03-12 04:26:13 +00:00
|
|
|
|
2018-03-16 17:40:10 +00:00
|
|
|
(defn import* [env path & args]
|
2018-08-26 16:53:39 +00:00
|
|
|
(def targs (apply table args))
|
2018-10-17 03:08:26 +00:00
|
|
|
(def {:as as
|
|
|
|
:prefix prefix} targs)
|
2018-07-02 01:12:46 +00:00
|
|
|
(def newenv (require path targs))
|
|
|
|
(var k (next newenv nil))
|
|
|
|
(def {:meta meta} newenv)
|
|
|
|
(def prefix (or (and as (string as ".")) prefix (string path ".")))
|
|
|
|
(while k
|
|
|
|
(def v (get newenv k))
|
|
|
|
(when (not (get v :private))
|
|
|
|
(def newv (table.setproto @{:private true} v))
|
|
|
|
(put env (symbol prefix k) newv))
|
|
|
|
(:= k (next newenv k))))
|
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-07-02 01:12:46 +00:00
|
|
|
(if (and (symbol? x) (= (get x 0) 58))
|
|
|
|
x
|
|
|
|
(string x)))
|
|
|
|
args))
|
2018-06-29 03:36:31 +00:00
|
|
|
(apply 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-07-02 01:12:46 +00:00
|
|
|
get a chunk of source code. Should return nil for end of file."
|
2018-08-03 17:41:44 +00:00
|
|
|
@[getchunk onvalue onerr]
|
2018-07-02 01:12:46 +00:00
|
|
|
(def newenv (make-env))
|
2018-08-21 17:09:01 +00:00
|
|
|
(default getchunk (fn @[buf]
|
2018-07-02 01:12:46 +00:00
|
|
|
(file.read stdin :line buf)))
|
|
|
|
(default onvalue (fn [x]
|
|
|
|
(put newenv '_ @{:value x})
|
|
|
|
(pp x)))
|
|
|
|
(default onerr default-error-handler)
|
|
|
|
(run-context newenv getchunk onvalue onerr "repl"))
|
2018-05-22 02:08:16 +00:00
|
|
|
|
|
|
|
(defn all-symbols
|
|
|
|
"Get all symbols available in the current environment."
|
2018-08-03 17:41:44 +00:00
|
|
|
@[env]
|
2018-05-22 02:08:16 +00:00
|
|
|
(default env *env*)
|
|
|
|
(def envs @[])
|
|
|
|
(do (var e env) (while e (array.push envs e) (:= e (table.getproto e))))
|
|
|
|
(array.reverse envs)
|
|
|
|
(def symbol-set @{})
|
|
|
|
(defn onenv [envi]
|
|
|
|
(defn onk [k]
|
|
|
|
(put symbol-set k true))
|
|
|
|
(each onk (keys envi)))
|
|
|
|
(each onenv envs)
|
|
|
|
(sort (keys symbol-set)))
|