2018-03-16 03:27:44 +00:00
|
|
|
# Bootstrap the dst environment
|
|
|
|
# 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-03-22 00:53:39 +00:00
|
|
|
"The current environment."
|
2018-03-18 18:01:58 +00:00
|
|
|
_env)
|
|
|
|
|
2018-03-13 20:40:56 +00:00
|
|
|
(def defn :macro
|
2018-03-12 04:26:13 +00:00
|
|
|
"Define a function"
|
2018-03-24 05:44:17 +00:00
|
|
|
(fn [name & more]
|
2018-04-01 22:24:04 +00:00
|
|
|
(def len (length more))
|
2018-03-12 04:26:13 +00:00
|
|
|
(def fstart (fn recur [i]
|
|
|
|
(def ith (ast-unwrap1 (get more i)))
|
|
|
|
(def t (type ith))
|
|
|
|
(def tuple? (= t :tuple))
|
|
|
|
(def array? (= t :array))
|
2018-04-01 22:24:04 +00:00
|
|
|
(if (if tuple? tuple? array?) i
|
|
|
|
(if (< i len) (recur (+ i 1))))))
|
2018-03-12 04:26:13 +00:00
|
|
|
(def start (fstart 0))
|
|
|
|
(def fnbody (tuple-prepend (tuple-prepend (tuple-slice more start) name) 'fn))
|
2018-03-24 16:48:42 +00:00
|
|
|
(def formargs (array-concat @['def name] (array-slice more 0 start) @[fnbody]))
|
2018-03-16 19:45:24 +00:00
|
|
|
(apply1 tuple formargs)))
|
2018-03-12 04:26:13 +00:00
|
|
|
|
2018-03-13 20:40:56 +00:00
|
|
|
(def defmacro :macro
|
2018-03-12 04:26:13 +00:00
|
|
|
"Define a macro."
|
|
|
|
(do
|
2018-03-18 18:01:58 +00:00
|
|
|
(def defn* (get (get _env 'defn) :value))
|
2018-03-12 04:26:13 +00:00
|
|
|
(fn [name & more]
|
2018-03-26 01:12:43 +00:00
|
|
|
(apply1 defn* (array-concat
|
|
|
|
@[name :macro] more)))))
|
2018-01-31 22:39:18 +00:00
|
|
|
|
2018-03-16 03:27:44 +00:00
|
|
|
(defmacro defmacro-
|
|
|
|
"Define a private macro that will not be exported."
|
|
|
|
[name & more]
|
2018-03-16 19:45:24 +00:00
|
|
|
(apply1 tuple (array-concat
|
2018-03-24 16:48:42 +00:00
|
|
|
@['defmacro name :private] more)))
|
2018-03-16 03:27:44 +00:00
|
|
|
|
2018-03-12 04:57:13 +00:00
|
|
|
(defmacro defn-
|
|
|
|
"Define a private function that will not be exported."
|
|
|
|
[name & more]
|
2018-03-16 19:45:24 +00:00
|
|
|
(apply1 tuple (array-concat
|
2018-03-24 16:48:42 +00:00
|
|
|
@['defn name :private] more)))
|
2018-03-12 04:57:13 +00:00
|
|
|
|
2018-03-28 17:50:06 +00:00
|
|
|
(defmacro def-
|
|
|
|
"Define a private value that will not be exported."
|
|
|
|
[name & more]
|
|
|
|
(apply1 tuple (array-concat
|
|
|
|
@['def name :private] more)))
|
|
|
|
|
2018-03-22 00:53:39 +00:00
|
|
|
# Basic predicates
|
2018-03-15 01:46:56 +00:00
|
|
|
(defn even? [x] (== 0 (% x 2)))
|
|
|
|
(defn odd? [x] (== 1 (% x 2)))
|
|
|
|
(defn zero? [x] (== x 0))
|
2018-03-16 03:27:44 +00:00
|
|
|
(defn pos? [x] (> x 0))
|
|
|
|
(defn neg? [x] (< x 0))
|
2018-03-15 01:46:56 +00:00
|
|
|
(defn one? [x] (== x 1))
|
2018-03-23 22:36:56 +00:00
|
|
|
(defn table? [x] (= (type x) :table ))
|
|
|
|
(defn struct? [x] (= (type x) :struct))
|
|
|
|
(defn array? [x] (= (type x) :array))
|
|
|
|
(defn tuple? [x] (= (type x) :tuple))
|
|
|
|
(defn boolean? [x] (= (type x) :boolean))
|
2018-03-29 01:16:12 +00:00
|
|
|
(defn indexed? [x]
|
|
|
|
(def t (type x))
|
|
|
|
(if (= t :array) true (= t :tuple)))
|
2018-03-28 17:50:06 +00:00
|
|
|
(defn function? [x]
|
|
|
|
(def t (type x))
|
|
|
|
(if (= t :function) true (= t :cfunction)))
|
2018-03-23 22:36:56 +00:00
|
|
|
(defn true? [x] (= (type x) true))
|
|
|
|
(defn false? [x] (= (type x) false))
|
|
|
|
(defn nil? [x] (= x nil))
|
2018-03-15 21:19:31 +00:00
|
|
|
(def atomic? (do
|
|
|
|
(def non-atomic-types {
|
|
|
|
:array true
|
|
|
|
:tuple true
|
|
|
|
:table true
|
|
|
|
:struct true
|
|
|
|
})
|
|
|
|
(fn [x] (not (get non-atomic-types (type x))))))
|
2018-03-15 01:46:56 +00:00
|
|
|
|
2018-03-22 00:53:39 +00:00
|
|
|
# C style macros and functions for imperative sugar
|
|
|
|
(defn inc [x] (+ x 1))
|
|
|
|
(defn dec [x] (- x 1))
|
2018-03-16 22:31:03 +00:00
|
|
|
(defmacro ++ [x] (tuple ':= x (tuple + x 1)))
|
|
|
|
(defmacro -- [x] (tuple ':= x (tuple - x 1)))
|
|
|
|
(defmacro += [x n] (tuple ':= x (tuple + x n)))
|
|
|
|
(defmacro -= [x n] (tuple ':= x (tuple - x n)))
|
|
|
|
(defmacro *= [x n] (tuple ':= x (tuple * x n)))
|
|
|
|
(defmacro /= [x n] (tuple ':= x (tuple / x n)))
|
|
|
|
(defmacro %= [x n] (tuple ':= x (tuple % x n)))
|
|
|
|
(defmacro &= [x n] (tuple ':= x (tuple & x n)))
|
|
|
|
(defmacro |= [x n] (tuple ':= x (tuple | x n)))
|
|
|
|
(defmacro ^= [x n] (tuple ':= x (tuple ^ x n)))
|
|
|
|
(defmacro >>= [x n] (tuple ':= x (tuple >> x n)))
|
|
|
|
(defmacro <<= [x n] (tuple ':= x (tuple << x n)))
|
|
|
|
(defmacro >>>= [x n] (tuple ':= x (tuple >>> x n)))
|
|
|
|
|
2018-03-12 06:06:51 +00:00
|
|
|
(defmacro comment
|
|
|
|
"Ignores the body of the comment."
|
|
|
|
[])
|
|
|
|
|
2018-03-28 17:50:06 +00:00
|
|
|
(defmacro if-not
|
|
|
|
"Sorthand for (if (not ... "
|
|
|
|
[condition exp-1 exp-2]
|
|
|
|
(tuple 'if condition exp-2 exp-1))
|
|
|
|
|
2018-01-31 22:39:18 +00:00
|
|
|
(defmacro when
|
2018-03-12 06:06:51 +00:00
|
|
|
"Evaluates the body when the condition is true. Otherwise returns nil."
|
2018-03-28 17:50:06 +00:00
|
|
|
[condition & body]
|
|
|
|
(tuple 'if condition (tuple-prepend body 'do)))
|
|
|
|
|
|
|
|
(defmacro when-not
|
|
|
|
"Sorthand for (when (not ... "
|
|
|
|
[condition & body]
|
|
|
|
(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-03-12 06:06:51 +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."
|
2018-03-12 04:26:13 +00:00
|
|
|
[& pairs]
|
2018-03-28 17:50:06 +00:00
|
|
|
(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-03-16 17:40:10 +00:00
|
|
|
(defn doc*
|
|
|
|
[env sym]
|
|
|
|
(def x (get env sym))
|
2018-03-12 04:26:13 +00:00
|
|
|
(if (not x)
|
|
|
|
(print "symbol " x " not found.")
|
|
|
|
(do
|
|
|
|
(def d (get x 'doc))
|
|
|
|
(print "\n" (if d d "no documentation found.") "\n"))))
|
|
|
|
|
2018-03-16 17:40:10 +00:00
|
|
|
(defmacro doc
|
|
|
|
"Shows documentation for the given symbol."
|
|
|
|
[sym]
|
2018-03-16 19:58:11 +00:00
|
|
|
(tuple doc* '_env (tuple 'quote sym)))
|
2018-03-16 17:40:10 +00:00
|
|
|
|
2018-03-28 17:50:06 +00:00
|
|
|
(defn apply
|
|
|
|
"Evaluate to (f ...args), where the final value of args must be an array or
|
|
|
|
tuple and will be spliced into the function call. For example, (apply + 1 2 @[3 4])
|
|
|
|
evaluates to 10."
|
|
|
|
[f & args]
|
2018-03-16 19:45:24 +00:00
|
|
|
(def last (- (length args) 1))
|
2018-03-28 17:50:06 +00:00
|
|
|
(apply1 f (array-concat (array-slice args 0 -2) (get args last))))
|
2018-03-16 19:45:24 +00:00
|
|
|
|
2018-03-28 17:50:06 +00:00
|
|
|
(defmacro switch
|
2018-03-12 06:06:51 +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"
|
2018-03-12 04:26:13 +00:00
|
|
|
[dispatch & pairs]
|
2018-03-28 17:50:06 +00:00
|
|
|
(def atm (atomic? (ast-unwrap1 dispatch)))
|
|
|
|
(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
|
|
|
|
(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
|
|
|
|
(aux 0)
|
|
|
|
(tuple 'do
|
|
|
|
(tuple 'def sym dispatch)
|
|
|
|
(aux 0))))
|
|
|
|
|
|
|
|
(defmacro let
|
|
|
|
"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-03-11 20:30:38 +00:00
|
|
|
(def head (ast-unwrap1 bindings))
|
2018-03-28 17:50:06 +00:00
|
|
|
(if (odd? (length head)) (error "expected even number of bindings to let"))
|
2018-03-12 04:26:13 +00:00
|
|
|
(def len (length head))
|
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-03-24 05:44:17 +00:00
|
|
|
(array-push accum (tuple 'def
|
2018-03-11 20:30:38 +00:00
|
|
|
(get head i)
|
2018-03-12 04:26:13 +00:00
|
|
|
(get head (+ 1 i))))
|
2018-03-28 17:50:06 +00:00
|
|
|
(+= i 2))
|
|
|
|
(array-concat accum body)
|
2018-03-16 19:45:24 +00:00
|
|
|
(apply1 tuple accum))
|
2018-02-03 22:22:04 +00:00
|
|
|
|
2018-03-28 17:50:06 +00:00
|
|
|
(defmacro for
|
|
|
|
"An imperative for loop over an integer range. Use with caution and discretion."
|
|
|
|
[head & body]
|
|
|
|
(def [sym start end _inc] (ast-unwrap1 head))
|
2018-03-12 04:26:13 +00:00
|
|
|
(def inc (if _inc _inc 1))
|
|
|
|
(def endsym (gensym))
|
|
|
|
(tuple 'do
|
|
|
|
(tuple 'var sym start)
|
|
|
|
(tuple 'def endsym end)
|
2018-03-28 17:50:06 +00:00
|
|
|
(tuple 'while (tuple < sym endsym)
|
2018-03-12 04:26:13 +00:00
|
|
|
(tuple-prepend body 'do)
|
2018-03-28 17:50:06 +00:00
|
|
|
(tuple ':= sym (tuple + sym inc)))))
|
2018-02-07 05:44:51 +00:00
|
|
|
|
2018-03-28 17:50:06 +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
|
|
|
|
(>= (inc i) len) (get forms i)
|
|
|
|
(tuple 'if (get forms i) (aux (inc i)) false))) 0)))
|
|
|
|
|
|
|
|
(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
|
|
|
|
(>= (inc i) len) fi
|
|
|
|
(do
|
|
|
|
(if (atomic? (ast-unwrap1 fi))
|
|
|
|
(tuple 'if fi fi (aux (inc i)))
|
|
|
|
(do
|
|
|
|
(def $fi (gensym))
|
|
|
|
(tuple 'do (tuple 'def $fi fi)
|
|
|
|
(tuple 'if $fi $fi (aux (inc i))))))))) 0)))
|
|
|
|
|
|
|
|
(defmacro coro
|
|
|
|
"A wrapper for making fibers. Same as (fiber (fn [] ...body))."
|
|
|
|
[& body]
|
|
|
|
(tuple fiber (apply tuple 'fn [] body)))
|
|
|
|
|
|
|
|
(defmacro if-let
|
|
|
|
"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"
|
|
|
|
[bindings tru fal]
|
|
|
|
(def bindings (ast-unwrap1 bindings))
|
|
|
|
(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)
|
|
|
|
tru
|
|
|
|
(do
|
|
|
|
(def atm (atomic? (ast-unwrap1 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))))))
|
|
|
|
(aux 0))
|
|
|
|
|
2018-03-28 20:38:05 +00:00
|
|
|
(defmacro default
|
|
|
|
"Suplies a default argument when a value is nil."
|
|
|
|
[sym default-value]
|
|
|
|
(tuple 'def sym (tuple 'or sym default-value)))
|
|
|
|
|
2018-03-28 17:50:06 +00:00
|
|
|
(defmacro when-let
|
|
|
|
"Takes the first one or two forms in vector and if true binds
|
|
|
|
all the forms with let and evaluates the body"
|
|
|
|
[bindings & body]
|
|
|
|
(tuple 'if-let bindings (tuple-prepend body 'do)))
|
|
|
|
|
|
|
|
(defn comp
|
|
|
|
"Takes multiple functions and returns a function that is the composition
|
|
|
|
of those functions."
|
|
|
|
[& functions]
|
|
|
|
(switch (length functions)
|
|
|
|
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))))))
|
|
|
|
(let [[f g h i j] functions]
|
|
|
|
(apply comp (fn [x] (f (g (h (i (j x))))))
|
|
|
|
(tuple-slice functions 5 -1)))))
|
|
|
|
|
|
|
|
(defn identity
|
|
|
|
"A function that returns its first argument."
|
|
|
|
[x]
|
|
|
|
x)
|
|
|
|
|
|
|
|
(defn complement
|
|
|
|
"Returns a function that is the complement to the argument."
|
|
|
|
[f]
|
|
|
|
(fn [x] (not (f x))))
|
|
|
|
|
2018-04-01 19:08:51 +00:00
|
|
|
(defn extreme
|
|
|
|
"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))
|
|
|
|
(for [i 0 len]
|
|
|
|
(def v (get args i))
|
|
|
|
(if (order v ret) (:= ret v)))
|
|
|
|
ret))
|
|
|
|
|
|
|
|
(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-03-28 20:38:05 +00:00
|
|
|
(def sort
|
|
|
|
"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)
|
|
|
|
(for [j lo hi]
|
|
|
|
(def aj (get a j))
|
|
|
|
(when (by aj pivot)
|
|
|
|
(def ai (get a i))
|
|
|
|
(put a i aj)
|
|
|
|
(put a j ai)
|
|
|
|
(++ i)))
|
|
|
|
(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)
|
|
|
|
|
|
|
|
(fn [a by]
|
|
|
|
(sort-help a 0 (- (length a) 1) (or by order<)))))
|
|
|
|
|
|
|
|
(defn sorted
|
|
|
|
"Returns the sorted version of an indexed data structure."
|
|
|
|
[ind by]
|
|
|
|
(def sa (sort (apply1 array ind) by))
|
|
|
|
(if (= :tuple (type ind))
|
|
|
|
(apply1 tuple sa)
|
|
|
|
sa))
|
|
|
|
|
2018-03-28 17:50:06 +00:00
|
|
|
(defn reduce
|
2018-04-26 20:57:23 +00:00
|
|
|
"Reduce, also know as foldleft in many languages, transforms
|
2018-03-28 17:50:06 +00:00
|
|
|
an indexed type (array, tuple) with a function to produce a value."
|
|
|
|
[f init ind]
|
|
|
|
(var res init)
|
|
|
|
(for [i 0 (length ind)]
|
|
|
|
(:= res (f res (get ind i))))
|
2018-03-26 17:36:58 +00:00
|
|
|
res)
|
|
|
|
|
2018-03-28 17:50:06 +00:00
|
|
|
(defn foreach
|
|
|
|
"Call function f on every value in indexed ind."
|
|
|
|
[f ind]
|
|
|
|
(for [i 0 (length ind)]
|
|
|
|
(f (get ind i))))
|
|
|
|
|
|
|
|
(defn map
|
|
|
|
"Map a function over every element in an array or tuple and return
|
|
|
|
the same type as the input sequence."
|
2018-04-01 19:08:51 +00:00
|
|
|
[f & inds]
|
2018-03-28 17:50:06 +00:00
|
|
|
(def res @[])
|
2018-04-01 19:08:51 +00:00
|
|
|
(def ninds (length inds))
|
|
|
|
(if (= 0 ninds) (error "expected at least 1 indexed collection."))
|
|
|
|
(var limit (length (get inds 0)))
|
|
|
|
(for [i 0 ninds]
|
|
|
|
(def l (length (get inds i)))
|
|
|
|
(if (< l limit) (:= limit l)))
|
|
|
|
(def [i1 i2 i3 i4] inds)
|
|
|
|
(switch ninds
|
|
|
|
1 (for [i 0 limit] (array-push res (f (get i1 i))))
|
|
|
|
2 (for [i 0 limit] (array-push res (f (get i1 i) (get i2 i))))
|
|
|
|
3 (for [i 0 limit] (array-push res (f (get i1 i) (get i2 i) (get i3 i))))
|
|
|
|
4 (for [i 0 limit] (array-push res (f (get i1 i) (get i2 i) (get i3 i) (get i4 i))))
|
|
|
|
(for [i 0 limit]
|
|
|
|
(def args @[])
|
|
|
|
(for [j 0 ninds] (array-push args (get (get inds j) i)))
|
|
|
|
(array-push res (apply1 f args))))
|
|
|
|
res)
|
2018-03-28 17:50:06 +00:00
|
|
|
|
|
|
|
(defn mapcat
|
|
|
|
"Map a function over every element in an array or tuple and
|
|
|
|
use array concat to concatentae the results. Returns the same
|
|
|
|
type as the input sequence."
|
|
|
|
[f ind t]
|
|
|
|
(def res @[])
|
|
|
|
(for [i 0 (length ind)]
|
|
|
|
(array-concat res (f (get ind i))))
|
|
|
|
(if (= :tuple (type (or t ind)))
|
|
|
|
(apply1 tuple res)
|
|
|
|
res))
|
|
|
|
|
|
|
|
(defn filter
|
|
|
|
"Given a predicate, take only elements from an array or tuple for
|
|
|
|
which (pred element) is truthy. Returns the same type as the input sequence."
|
|
|
|
[pred ind t]
|
|
|
|
(def res @[])
|
|
|
|
(for [i 0 (length ind)]
|
|
|
|
(def item (get ind i))
|
|
|
|
(if (pred item)
|
|
|
|
(array-push res item)))
|
|
|
|
(if (= :tuple (type (or t ind)))
|
|
|
|
(apply1 tuple res)
|
|
|
|
res))
|
|
|
|
|
|
|
|
(defn find-index
|
|
|
|
"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))
|
|
|
|
|
|
|
|
(defn find
|
|
|
|
"Find the first value in an indexed collection that satsifies 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)))
|
|
|
|
|
|
|
|
(defn take-until
|
|
|
|
"Given a predicate, take only elements from an indexed type that satsify
|
|
|
|
the predicate, and abort on first failiure. Returns a new indexed type that is
|
|
|
|
the same type as the input."
|
|
|
|
[pred ind t]
|
|
|
|
(def i (find-index pred ind))
|
|
|
|
(if (= :tuple (type (or t ind)))
|
|
|
|
(tuple-slice ind 0 i)
|
|
|
|
(array-slice ind 0 i)))
|
|
|
|
|
|
|
|
(defn take-while
|
|
|
|
"Same as (take-until (complement pred) ind t)."
|
|
|
|
[pred ind t]
|
|
|
|
(take-until (complement pred) ind t))
|
|
|
|
|
|
|
|
(defn drop-until
|
|
|
|
"Given a predicate, remove elements from an indexed type that satsify
|
|
|
|
the predicate, and abort on first failiure."
|
|
|
|
[pred ind t]
|
|
|
|
(def i (find-index pred ind))
|
|
|
|
(if (= :tuple (type (or t ind)))
|
|
|
|
(tuple-slice ind i -1)
|
|
|
|
(array-slice ind i -1)))
|
|
|
|
|
|
|
|
(defn drop-while
|
|
|
|
"Same as (drop-until (complement pred) ind t)."
|
|
|
|
[pred ind t]
|
|
|
|
(drop-until (complement pred) ind t))
|
|
|
|
|
2018-03-22 00:53:39 +00:00
|
|
|
(defn juxt*
|
|
|
|
[& funs]
|
|
|
|
(def len (length funs))
|
|
|
|
(fn [& args]
|
2018-03-24 16:48:42 +00:00
|
|
|
(def ret @[])
|
2018-03-22 00:53:39 +00:00
|
|
|
(for [i 0 len]
|
|
|
|
(array-push ret (apply1 (get funs i) args)))
|
|
|
|
(apply1 tuple ret)))
|
|
|
|
|
|
|
|
(defmacro juxt
|
|
|
|
[& funs]
|
2018-03-24 16:48:42 +00:00
|
|
|
(def parts @['tuple])
|
2018-03-22 00:53:39 +00:00
|
|
|
(def $args (gensym))
|
|
|
|
(for [i 0 (length funs)]
|
|
|
|
(array-push parts (tuple apply1 (get funs i) $args)))
|
2018-03-24 16:48:42 +00:00
|
|
|
(tuple 'fn (tuple '& $args) (apply1 tuple parts)))
|
2018-03-22 00:53:39 +00:00
|
|
|
|
2018-03-12 16:47:05 +00:00
|
|
|
(defmacro ->
|
2018-04-01 22:24:04 +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."
|
2018-03-12 16:47:05 +00:00
|
|
|
[x & forms]
|
|
|
|
(defn fop [last nextform]
|
|
|
|
(def n (ast-unwrap1 nextform))
|
|
|
|
(def [h t] (if (= :tuple (type n))
|
2018-03-24 16:48:42 +00:00
|
|
|
[tuple (get n 0) (array-slice n 1)]
|
|
|
|
[tuple n @[]]))
|
|
|
|
(def parts (array-concat @[h last] t))
|
2018-03-16 19:45:24 +00:00
|
|
|
(apply1 tuple parts))
|
2018-03-12 16:47:05 +00:00
|
|
|
(reduce fop x forms))
|
|
|
|
|
|
|
|
(defmacro ->>
|
2018-04-01 22:24:04 +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."
|
2018-03-12 16:47:05 +00:00
|
|
|
[x & forms]
|
|
|
|
(defn fop [last nextform]
|
|
|
|
(def n (ast-unwrap1 nextform))
|
|
|
|
(def [h t] (if (= :tuple (type n))
|
2018-03-24 16:48:42 +00:00
|
|
|
[tuple (get n 0) (array-slice n 1)]
|
|
|
|
[tuple n @[]]))
|
|
|
|
(def parts (array-concat @[h] t @[last]))
|
2018-03-16 19:45:24 +00:00
|
|
|
(apply1 tuple parts))
|
2018-03-12 16:47:05 +00:00
|
|
|
(reduce fop x forms))
|
|
|
|
|
2018-03-28 17:50:06 +00:00
|
|
|
(defn partial
|
|
|
|
"Partial function application."
|
|
|
|
[f & more]
|
|
|
|
(if (zero? (length more)) f
|
|
|
|
(fn [& r] (apply1 f (array-concat @[] more r)))))
|
|
|
|
|
|
|
|
(defn every? [pred seq]
|
|
|
|
(var res true)
|
|
|
|
(var i 0)
|
|
|
|
(def len (length seq))
|
|
|
|
(while (< i len)
|
|
|
|
(def item (get seq i))
|
|
|
|
(if (pred item)
|
|
|
|
(++ i)
|
|
|
|
(do (:= res false) (:= i len))))
|
|
|
|
res)
|
|
|
|
|
2018-03-23 22:36:56 +00:00
|
|
|
(defn reverse-array
|
|
|
|
"Reverses the order of the elements in a given array or tuple and returns a new array."
|
|
|
|
[t]
|
|
|
|
(var n (dec (length t)))
|
2018-03-24 16:48:42 +00:00
|
|
|
(var reversed @[])
|
2018-03-23 22:36:56 +00:00
|
|
|
(while (>= n 0)
|
|
|
|
(array-push reversed (get t n))
|
|
|
|
(-- n))
|
|
|
|
reversed)
|
|
|
|
|
|
|
|
(defn reverse-tuple
|
|
|
|
"Reverses the order of the elements given an array or tuple and returns a tuple"
|
|
|
|
[t]
|
|
|
|
(apply1 tuple (reverse-array t)))
|
|
|
|
|
|
|
|
(defn reverse
|
|
|
|
"Reverses order of elements in a given array or tuple"
|
|
|
|
[t]
|
2018-03-28 17:50:06 +00:00
|
|
|
(switch (type t)
|
2018-03-23 22:36:56 +00:00
|
|
|
:tuple (reverse-tuple t)
|
|
|
|
:array (reverse-array t)))
|
|
|
|
|
|
|
|
(defn zipcoll
|
2018-03-24 05:44:17 +00:00
|
|
|
"Creates an table or tuple from two arrays/tuples. If a third argument of
|
2018-03-28 17:50:06 +00:00
|
|
|
:struct is given result is struct else is table."
|
|
|
|
[keys vals t]
|
|
|
|
(def res @{})
|
|
|
|
(def lk (length keys))
|
|
|
|
(def lv (length vals))
|
|
|
|
(def len (if (< lk lv) lk lv))
|
|
|
|
(for [i 0 len]
|
|
|
|
(put res (get keys i) (get vals i)))
|
|
|
|
(if (= :struct t)
|
|
|
|
(table-to-struct res)
|
|
|
|
res))
|
2018-03-23 22:36:56 +00:00
|
|
|
|
|
|
|
(defn update
|
2018-03-24 05:44:17 +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
|
|
|
|
"Merges mutliple tables/structs to one. If a key appears in more than one
|
|
|
|
collection, then later values replace any previous ones.
|
|
|
|
The type of the first collection determines the type of the resulting
|
|
|
|
collection"
|
|
|
|
[& colls]
|
|
|
|
(def container @{})
|
|
|
|
(for [i 0 (length colls)]
|
|
|
|
(def c (get colls i))
|
|
|
|
(var key (next c nil))
|
|
|
|
(while (not= nil key)
|
|
|
|
(put container key (get c key))
|
|
|
|
(:= key (next c key))))
|
|
|
|
(if (table? (get colls 0)) container (table-to-struct container)))
|
|
|
|
|
2018-03-29 01:16:12 +00:00
|
|
|
(defn keys
|
|
|
|
"Get the keys of an associative data structure."
|
|
|
|
[x]
|
|
|
|
(def arr @[])
|
|
|
|
(var k (next x nil))
|
2018-04-02 20:10:16 +00:00
|
|
|
(while (not= nil k)
|
2018-03-29 01:16:12 +00:00
|
|
|
(array-push arr k)
|
|
|
|
(:= k (next x k)))
|
|
|
|
arr)
|
|
|
|
|
|
|
|
(defn values
|
|
|
|
"Get the values of an associative data structure."
|
|
|
|
[x]
|
|
|
|
(def arr @[])
|
|
|
|
(var k (next x nil))
|
2018-04-02 20:10:16 +00:00
|
|
|
(while (not= nil k)
|
2018-03-29 01:16:12 +00:00
|
|
|
(array-push arr (get x k))
|
|
|
|
(:= k (next x k)))
|
|
|
|
arr)
|
|
|
|
|
|
|
|
(defn pairs
|
|
|
|
"Get the values of an associative data structure."
|
|
|
|
[x]
|
|
|
|
(def arr @[])
|
|
|
|
(var k (next x nil))
|
2018-04-02 20:10:16 +00:00
|
|
|
(while (not= nil k)
|
2018-03-29 01:16:12 +00:00
|
|
|
(array-push arr (tuple k (get x k)))
|
|
|
|
(:= k (next x k)))
|
|
|
|
arr)
|
|
|
|
|
2018-03-28 17:50:06 +00:00
|
|
|
###
|
|
|
|
###
|
|
|
|
### Pretty Printer
|
|
|
|
###
|
|
|
|
###
|
|
|
|
|
|
|
|
(defn pp
|
|
|
|
"Pretty print a value. Displays values inside collections, and is safe
|
|
|
|
to call on any table. Does not print table prototype information."
|
|
|
|
[x]
|
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
|
|
|
|
[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 ">"))
|
2018-03-14 23:08:00 +00:00
|
|
|
(do
|
2018-03-26 17:36:58 +00:00
|
|
|
(put seen y (++ nextid))
|
2018-03-14 23:08:00 +00:00
|
|
|
(buffer-push-string buf start)
|
2018-03-26 17:36:58 +00:00
|
|
|
(dispatch y)
|
|
|
|
(buffer-push-string buf end))))
|
|
|
|
|
|
|
|
(defn pp-seq [y]
|
|
|
|
(def len (length y))
|
|
|
|
(if (< len 5)
|
|
|
|
(do
|
2018-03-14 23:08:00 +00:00
|
|
|
(for [i 0 len]
|
|
|
|
(when (not= i 0) (buffer-push-string buf " "))
|
2018-03-26 17:36:58 +00:00
|
|
|
(recur (get y i))))
|
2018-03-14 23:08:00 +00:00
|
|
|
(do
|
2018-03-26 17:36:58 +00:00
|
|
|
(buffer-push-string indent " ")
|
|
|
|
(for [i 0 len]
|
|
|
|
(when (not= i len) (buffer-push-string buf indent))
|
|
|
|
(recur (get y i)))
|
|
|
|
(buffer-popn indent 2)
|
|
|
|
(buffer-push-string buf indent))))
|
|
|
|
|
2018-04-30 21:05:42 +00:00
|
|
|
(defn pp-dict-nested [y proto?]
|
2018-03-26 17:36:58 +00:00
|
|
|
(buffer-push-string indent " ")
|
2018-03-29 01:16:12 +00:00
|
|
|
(def ps (sort (pairs y)))
|
|
|
|
(for [i 0 (length ps)]
|
|
|
|
(def [k v] (get ps i))
|
|
|
|
(buffer-push-string buf indent)
|
2018-03-26 17:36:58 +00:00
|
|
|
(recur k)
|
|
|
|
(buffer-push-string buf " ")
|
2018-03-29 01:16:12 +00:00
|
|
|
(recur v))
|
2018-04-30 21:05:42 +00:00
|
|
|
(when proto?
|
|
|
|
(buffer-push-string buf indent)
|
|
|
|
(buffer-push-string buf "{proto} ")
|
|
|
|
(recur proto?))
|
2018-03-26 17:36:58 +00:00
|
|
|
(buffer-popn indent 2)
|
|
|
|
(buffer-push-string buf indent))
|
|
|
|
|
|
|
|
(defn pp-dict-simple [y]
|
2018-03-29 01:16:12 +00:00
|
|
|
(def ps (sort (pairs y)))
|
|
|
|
(for [i 0 (length ps)]
|
|
|
|
(def [k v] (get ps i))
|
|
|
|
(if (pos? i) (buffer-push-string buf " "))
|
2018-03-26 17:36:58 +00:00
|
|
|
(recur k)
|
|
|
|
(buffer-push-string buf " ")
|
2018-03-29 01:16:12 +00:00
|
|
|
(recur v)))
|
2018-03-26 17:36:58 +00:00
|
|
|
|
|
|
|
(defn pp-dict [y]
|
2018-04-30 21:05:42 +00:00
|
|
|
(def proto? (and (table? y) (getproto y)))
|
|
|
|
(def complex? (or proto? (> (length y) 4)))
|
|
|
|
((if complex? pp-dict-nested pp-dict-simple) y proto?))
|
2018-03-26 17:36:58 +00:00
|
|
|
|
|
|
|
(def printers {
|
|
|
|
: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-03-14 23:08:00 +00:00
|
|
|
})
|
2018-03-26 17:36:58 +00:00
|
|
|
|
|
|
|
(:= recur (fn [y]
|
|
|
|
(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)
|
|
|
|
(buffer-push-string buf "\n")
|
2018-03-14 23:08:00 +00:00
|
|
|
|
2018-03-26 17:36:58 +00:00
|
|
|
(file-write stdout buf))
|
2018-03-14 23:08:00 +00:00
|
|
|
|
2018-03-28 17:50:06 +00:00
|
|
|
###
|
|
|
|
###
|
|
|
|
### Macro Expansion
|
|
|
|
###
|
|
|
|
###
|
2018-03-15 01:46:56 +00:00
|
|
|
|
2018-03-18 18:01:58 +00:00
|
|
|
(defn macroexpand1
|
|
|
|
"Expand macros in a form, but do not recursively expand macros."
|
|
|
|
[x]
|
|
|
|
|
2018-03-24 05:44:17 +00:00
|
|
|
(defn doarray [a]
|
2018-03-18 18:01:58 +00:00
|
|
|
(def len (length a))
|
2018-03-24 16:48:42 +00:00
|
|
|
(def newa @[])
|
2018-03-18 18:01:58 +00:00
|
|
|
(for [i 0 len]
|
|
|
|
(array-push newa (macroexpand1 (get a i))))
|
|
|
|
newa)
|
|
|
|
|
|
|
|
(defn dotable [t]
|
|
|
|
(def newt @{})
|
|
|
|
(var key (next t nil))
|
|
|
|
(while (not= nil key)
|
|
|
|
(put newt (macroexpand1 key) (macroexpand1 (get t key)))
|
|
|
|
(:= key (next t key)))
|
|
|
|
newt)
|
|
|
|
|
|
|
|
(defn expandlast [t]
|
|
|
|
(def len (length t))
|
|
|
|
(def last (get t (- len 1)))
|
|
|
|
(tuple-append (tuple-slice t 0 -2) (macroexpand1 last)))
|
|
|
|
|
|
|
|
(defn expandall [t]
|
|
|
|
(def args (doarray (tuple-slice t 1)))
|
|
|
|
(apply tuple (get t 0) args))
|
|
|
|
|
|
|
|
(defn expandfn [t]
|
|
|
|
(def args (doarray (tuple-slice t 2)))
|
|
|
|
(apply tuple 'fn (get t 1) args))
|
|
|
|
|
|
|
|
(def specs {
|
|
|
|
':= expandlast
|
|
|
|
'ast-quote identity
|
|
|
|
'def expandlast
|
|
|
|
'do expandall
|
|
|
|
'fn expandfn
|
|
|
|
'if expandall
|
|
|
|
'quote identity
|
|
|
|
'var expandlast
|
|
|
|
'while expandall
|
2018-03-24 05:44:17 +00:00
|
|
|
})
|
2018-03-18 18:01:58 +00:00
|
|
|
|
|
|
|
(defn dotup [t]
|
|
|
|
(def h (get t 0))
|
|
|
|
(def s (get specs h))
|
|
|
|
(def entry (get *env* h))
|
|
|
|
(def m (get entry :value))
|
|
|
|
(def m? (get entry :macro))
|
|
|
|
(cond
|
|
|
|
s (s t)
|
|
|
|
m? (apply1 m (tuple-slice t 1))
|
|
|
|
(apply1 tuple (doarray t))))
|
|
|
|
|
|
|
|
(defn doarray* [a]
|
|
|
|
(def res (doarray a))
|
|
|
|
(if (= (apply tuple res) (apply tuple a)) a res))
|
|
|
|
|
|
|
|
(defn dotable* [t]
|
|
|
|
(def res (dotable t))
|
|
|
|
(if (= (table-to-struct res) (table-to-struct t)) t res))
|
|
|
|
|
|
|
|
(def ux (ast-unwrap1 x))
|
2018-03-28 17:50:06 +00:00
|
|
|
(switch (type ux)
|
2018-03-18 18:01:58 +00:00
|
|
|
:tuple (dotup ux)
|
|
|
|
:array (doarray* ux)
|
|
|
|
:struct (table-to-struct (dotable ux))
|
|
|
|
:table (dotable* ux)
|
|
|
|
ux))
|
|
|
|
|
|
|
|
(defn macroexpand
|
|
|
|
"Expand macros completely."
|
|
|
|
[x]
|
|
|
|
(var previous x)
|
|
|
|
(var current (macroexpand1 x))
|
|
|
|
(var counter 0)
|
|
|
|
(while (not= current previous)
|
|
|
|
(if (> (++ counter) 200)
|
|
|
|
(error "macro expansion too nested"))
|
|
|
|
(:= previous current)
|
|
|
|
(:= current (macroexpand1 current)))
|
|
|
|
current)
|
|
|
|
|
2018-03-29 01:16:12 +00:00
|
|
|
###
|
|
|
|
###
|
|
|
|
### Evaluation and Compilation
|
|
|
|
###
|
|
|
|
###
|
|
|
|
|
2018-03-24 16:48:42 +00:00
|
|
|
(defn make-env [parent]
|
2018-03-13 20:40:56 +00:00
|
|
|
(def parent (if parent parent _env))
|
|
|
|
(def newenv (setproto @{} parent))
|
2018-03-24 16:48:42 +00:00
|
|
|
(put newenv '_env @{:value newenv :private true})
|
2018-03-13 20:40:56 +00:00
|
|
|
newenv)
|
2018-03-15 21:19:31 +00:00
|
|
|
|
2018-03-28 17:50:06 +00:00
|
|
|
(defn run-context
|
2018-03-12 06:06:51 +00:00
|
|
|
"Run a context. This evaluates expressions of dst in an environment,
|
|
|
|
and is encapsulates the parsing, compilation, and evaluation of dst.
|
|
|
|
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.
|
2018-03-24 05:44:17 +00:00
|
|
|
|
2018-03-12 06:06:51 +00:00
|
|
|
This function can be used to implemement a repl very easily, simply
|
|
|
|
pass a function that reads line from stdin to chunks, and print to
|
|
|
|
onvalue."
|
2018-03-28 17:50:06 +00:00
|
|
|
[env chunks onvalue onerr]
|
|
|
|
|
|
|
|
# Are we done yet?
|
2018-03-12 04:26:13 +00:00
|
|
|
(var going true)
|
2018-03-28 17:50:06 +00:00
|
|
|
|
|
|
|
# Fiber stream of characters
|
|
|
|
(def chars (coro
|
2018-02-07 05:44:51 +00:00
|
|
|
(def buf @"")
|
|
|
|
(var len 1)
|
|
|
|
(while (< 0 len)
|
|
|
|
(buffer-clear buf)
|
2018-03-24 05:44:17 +00:00
|
|
|
(chunks buf)
|
2018-03-16 22:15:34 +00:00
|
|
|
(:= len (length buf))
|
2018-02-07 05:44:51 +00:00
|
|
|
(for [i 0 len]
|
2018-03-11 19:35:23 +00:00
|
|
|
(yield (get buf i))))
|
2018-03-28 17:50:06 +00:00
|
|
|
0))
|
|
|
|
|
|
|
|
# Fiber stream of values
|
|
|
|
(def vals (coro
|
2018-03-26 00:39:38 +00:00
|
|
|
(def p (parser 1))
|
|
|
|
(while going
|
2018-03-28 17:50:06 +00:00
|
|
|
(switch (parser-status p)
|
2018-03-26 00:39:38 +00:00
|
|
|
:full (yield (parser-produce p))
|
|
|
|
:error (onerr "parse" (parser-error p))
|
2018-03-28 17:50:06 +00:00
|
|
|
(switch (fiber-status chars)
|
2018-03-26 00:39:38 +00:00
|
|
|
:new (parser-byte p (resume chars))
|
|
|
|
:pending (parser-byte p (resume chars))
|
|
|
|
(:= going false))))
|
|
|
|
(when (not= :root (parser-status p))
|
2018-03-28 17:50:06 +00:00
|
|
|
(onerr "parse" "unexpected end of source"))))
|
|
|
|
|
|
|
|
# Evaluate 1 source form
|
|
|
|
(defn eval1 [source]
|
2018-03-18 13:13:21 +00:00
|
|
|
(var good true)
|
2018-03-26 00:39:38 +00:00
|
|
|
(def f (coro
|
2018-03-12 04:26:13 +00:00
|
|
|
(def res (compile source env))
|
|
|
|
(if (= (type res) :function)
|
|
|
|
(res)
|
2018-03-18 13:13:21 +00:00
|
|
|
(do
|
|
|
|
(:= good false)
|
2018-03-26 00:39:38 +00:00
|
|
|
(onerr "compile" (get res :error))))))
|
2018-03-12 04:26:13 +00:00
|
|
|
(def res (resume f))
|
2018-03-28 17:50:06 +00:00
|
|
|
(if good
|
|
|
|
(if (= (fiber-status f) :error)
|
|
|
|
(onerr "runtime" res f)
|
|
|
|
(onvalue res))))
|
|
|
|
|
|
|
|
# Run loop
|
2018-03-26 00:39:38 +00:00
|
|
|
(def oldenv *env*)
|
|
|
|
(:= *env* env)
|
2018-03-28 17:50:06 +00:00
|
|
|
(while going (eval1 (resume vals)))
|
2018-03-26 00:39:38 +00:00
|
|
|
(:= *env* oldenv)
|
2018-03-28 17:50:06 +00:00
|
|
|
|
|
|
|
env)
|
2018-03-12 04:26:13 +00:00
|
|
|
|
2018-03-22 00:53:39 +00:00
|
|
|
(defn default-error-handler
|
|
|
|
[t x f]
|
2018-03-22 01:48:19 +00:00
|
|
|
(file-write stdout (string t " error: "))
|
2018-03-22 00:53:39 +00:00
|
|
|
(pp x)
|
|
|
|
(when f
|
|
|
|
(def st (fiber-stack f))
|
|
|
|
(def len (length st))
|
|
|
|
(for [i 0 len]
|
|
|
|
(def {
|
|
|
|
:function func
|
|
|
|
:tail tail
|
|
|
|
:pc pc
|
|
|
|
:c c
|
|
|
|
:name name
|
|
|
|
} (get st i))
|
|
|
|
(file-write stdout " in")
|
|
|
|
(when c (file-write stdout " cfunction"))
|
2018-03-24 05:44:17 +00:00
|
|
|
(when name (file-write stdout (string " " name)))
|
2018-03-22 00:53:39 +00:00
|
|
|
(when func (file-write stdout (string " " func)))
|
|
|
|
(when pc (file-write stdout (string " (pc=" pc ")")))
|
|
|
|
(when tail (file-write stdout " (tailcall)"))
|
2018-03-22 01:48:19 +00:00
|
|
|
(file-write stdout "\n"))))
|
2018-03-22 00:53:39 +00:00
|
|
|
|
2018-03-12 04:26:13 +00:00
|
|
|
(def require (do
|
|
|
|
(def cache @{})
|
|
|
|
(def loading @{})
|
|
|
|
(fn [path]
|
|
|
|
(when (get loading path)
|
|
|
|
(error (string "circular dependency: module " path " is loading")))
|
|
|
|
(def check (get cache path))
|
|
|
|
(if check check (do
|
2018-04-26 17:13:31 +00:00
|
|
|
(if (= ".so" (string-slice path -3 -1))
|
|
|
|
((native path))
|
|
|
|
(do
|
|
|
|
(def newenv (make-env))
|
|
|
|
(put cache path newenv)
|
|
|
|
(put loading path true)
|
|
|
|
(def f (file-open path))
|
|
|
|
(defn chunks [buf] (file-read f 1024 buf))
|
|
|
|
(run-context newenv chunks identity default-error-handler)
|
|
|
|
(file-close f)
|
|
|
|
(put loading path nil)
|
|
|
|
newenv)))))))
|
2018-03-12 04:26:13 +00:00
|
|
|
|
2018-03-16 17:40:10 +00:00
|
|
|
(defn import* [env path & args]
|
|
|
|
(def newenv (require path))
|
2018-03-12 04:26:13 +00:00
|
|
|
(def {
|
|
|
|
:prefix prefix
|
2018-03-16 19:45:24 +00:00
|
|
|
} (apply1 table args))
|
2018-03-28 17:50:06 +00:00
|
|
|
(var k (next newenv nil))
|
|
|
|
(def prefix (if prefix prefix ""))
|
|
|
|
(while k
|
|
|
|
(def v (get newenv k))
|
2018-03-13 20:40:56 +00:00
|
|
|
(when (not (get v :private))
|
2018-03-28 17:50:06 +00:00
|
|
|
(put env (symbol prefix k) v))
|
|
|
|
(:= k (next newenv k)))
|
|
|
|
env)
|
2018-03-16 17:40:10 +00:00
|
|
|
|
|
|
|
(defmacro import [path & args]
|
2018-03-24 05:44:17 +00:00
|
|
|
(apply tuple import* '_env path args))
|
2018-03-12 04:26:13 +00:00
|
|
|
|
2018-03-14 03:39:49 +00:00
|
|
|
(defn repl [getchunk]
|
2018-03-28 17:50:06 +00:00
|
|
|
"Run a repl. The first paramets is an optional function to call to
|
|
|
|
get a chunk of soure code. Should return nil for end of file."
|
2018-03-12 04:26:13 +00:00
|
|
|
(def newenv (make-env))
|
|
|
|
(defn chunks [buf]
|
2018-03-14 14:54:29 +00:00
|
|
|
(file-write stdout "> ")
|
2018-03-13 17:31:25 +00:00
|
|
|
(file-flush stdout)
|
2018-02-07 05:44:51 +00:00
|
|
|
(file-read stdin :line buf))
|
2018-03-12 04:26:13 +00:00
|
|
|
(defn onvalue [x]
|
2018-03-18 18:01:58 +00:00
|
|
|
(put newenv '_ @{:value x})
|
2018-03-14 23:08:00 +00:00
|
|
|
(pp x))
|
2018-03-22 00:53:39 +00:00
|
|
|
(run-context newenv (if getchunk getchunk chunks)
|
|
|
|
onvalue default-error-handler))
|