2018-01-31 22:39:18 +00:00
|
|
|
|
2018-03-12 04:26:13 +00:00
|
|
|
# Capture the current env
|
|
|
|
(var *env*
|
|
|
|
"A var that points to the current environment."
|
|
|
|
_env)
|
|
|
|
|
2018-03-13 20:40:56 +00:00
|
|
|
(def defn :macro
|
2018-03-12 04:26:13 +00:00
|
|
|
"Define a function"
|
|
|
|
(fn [name & more]
|
|
|
|
(def fstart (fn recur [i]
|
|
|
|
(def ith (ast-unwrap1 (get more i)))
|
|
|
|
(def t (type ith))
|
|
|
|
(def tuple? (= t :tuple))
|
|
|
|
(def array? (= t :array))
|
|
|
|
(if (if tuple? tuple? array?) i (recur (+ i 1)))))
|
|
|
|
(def start (fstart 0))
|
|
|
|
(def fnbody (tuple-prepend (tuple-prepend (tuple-slice more start) name) 'fn))
|
|
|
|
(def formargs (array-concat ['def name] (array-slice more 0 start) [fnbody]))
|
|
|
|
(apply tuple formargs)))
|
|
|
|
|
2018-03-13 20:40:56 +00:00
|
|
|
(def defmacro :macro
|
2018-03-12 04:26:13 +00:00
|
|
|
"Define a macro."
|
|
|
|
(do
|
|
|
|
(def defn* (get (get _env 'defn) 'value))
|
|
|
|
(fn [name & more]
|
2018-03-13 20:40:56 +00:00
|
|
|
(def args (array-concat [] name :macro more))
|
2018-03-12 04:26:13 +00:00
|
|
|
(apply defn* args))))
|
2018-01-31 22:39:18 +00:00
|
|
|
|
2018-03-12 04:57:13 +00:00
|
|
|
(defmacro defn-
|
|
|
|
"Define a private function that will not be exported."
|
|
|
|
[name & more]
|
|
|
|
(apply tuple (array-concat
|
2018-03-13 20:40:56 +00:00
|
|
|
['defn name :private] more)))
|
2018-03-12 04:57:13 +00:00
|
|
|
|
2018-03-12 06:06:51 +00:00
|
|
|
(defmacro comment
|
|
|
|
"Ignores the body of the comment."
|
|
|
|
[])
|
|
|
|
|
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-01-31 22:39:18 +00:00
|
|
|
[cond & body]
|
|
|
|
(tuple 'if cond (tuple-prepend body 'do)))
|
|
|
|
|
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]
|
|
|
|
(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))
|
|
|
|
|
|
|
|
(defn doc
|
2018-03-12 06:06:51 +00:00
|
|
|
"Shows documentation for the given symbol."
|
2018-03-12 04:26:13 +00:00
|
|
|
[sym]
|
|
|
|
(def x (get *env* sym))
|
|
|
|
(if (not x)
|
|
|
|
(print "symbol " x " not found.")
|
|
|
|
(do
|
|
|
|
(def d (get x 'doc))
|
|
|
|
(print "\n" (if d d "no documentation found.") "\n"))))
|
|
|
|
|
|
|
|
(defmacro select
|
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]
|
|
|
|
(def sym (gensym))
|
|
|
|
(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))))))
|
|
|
|
(tuple 'do
|
|
|
|
(tuple 'def sym dispatch)
|
|
|
|
(aux 0)))
|
|
|
|
|
2018-02-07 18:19:34 +00:00
|
|
|
(defmacro or [x y] (tuple 'if x true y))
|
|
|
|
(defmacro and [x y] (tuple 'if x y false))
|
|
|
|
|
2018-03-12 04:57:13 +00:00
|
|
|
(defn identity
|
2018-03-12 06:06:51 +00:00
|
|
|
"A function that returns its first argument."
|
2018-03-12 04:57:13 +00:00
|
|
|
[x] x)
|
2018-02-07 05:44:51 +00:00
|
|
|
|
2018-01-31 22:39:18 +00:00
|
|
|
(def seq (do
|
|
|
|
(defn array-seq [x]
|
|
|
|
(def len (length x))
|
|
|
|
(var i 0)
|
|
|
|
{
|
|
|
|
:more (fn [] (< i len))
|
|
|
|
:next (fn []
|
|
|
|
(def ret (get x i))
|
|
|
|
(varset! i (+ i 1))
|
|
|
|
ret)
|
|
|
|
})
|
2018-03-11 19:35:23 +00:00
|
|
|
(defn fiber-seq [x]
|
|
|
|
{
|
|
|
|
:more (fn [] (or
|
|
|
|
(= (fiber-status x) :pending)
|
|
|
|
(= (fiber-status x) :new)))
|
|
|
|
:next (fn []
|
|
|
|
(resume x))
|
|
|
|
})
|
2018-01-31 22:39:18 +00:00
|
|
|
(def seqs {
|
|
|
|
:array array-seq
|
|
|
|
:tuple array-seq
|
2018-03-11 19:35:23 +00:00
|
|
|
:fiber fiber-seq
|
2018-01-31 22:39:18 +00:00
|
|
|
:struct (fn [x] x)})
|
|
|
|
(fn [x]
|
|
|
|
(def makeseq (get seqs (type x)))
|
|
|
|
(if makeseq (makeseq x) (error "expected sequence")))))
|
|
|
|
|
|
|
|
(defn range [top]
|
|
|
|
(var i 0)
|
|
|
|
{
|
|
|
|
:more (fn [] (< i top))
|
|
|
|
:next (fn []
|
|
|
|
(def ret i)
|
|
|
|
(varset! i (+ i 1))
|
|
|
|
ret)
|
|
|
|
})
|
|
|
|
|
|
|
|
(defn doseq [s]
|
2018-03-12 04:26:13 +00:00
|
|
|
(def {:more more :next next} (seq s))
|
|
|
|
(while (more) (next)))
|
2018-01-31 22:39:18 +00:00
|
|
|
|
2018-03-14 03:39:49 +00:00
|
|
|
(defn domap [f s]
|
|
|
|
(def {:more more :next next} (seq s))
|
|
|
|
(while (more) (f (next))))
|
|
|
|
|
2018-01-31 22:39:18 +00:00
|
|
|
(defn map [f s]
|
2018-03-12 04:26:13 +00:00
|
|
|
(def {:more more :next next} (seq s))
|
|
|
|
{:more more :next (fn [] (f (next)))})
|
2018-01-31 22:39:18 +00:00
|
|
|
|
|
|
|
(defn reduce [f start s]
|
2018-03-12 16:47:05 +00:00
|
|
|
(def s (seq s))
|
|
|
|
(def {:more more :next next} s)
|
2018-03-12 04:26:13 +00:00
|
|
|
(if (more)
|
|
|
|
(reduce f (f start (next)) s)
|
|
|
|
start))
|
2018-03-11 20:30:38 +00:00
|
|
|
|
2018-03-13 20:40:56 +00:00
|
|
|
(defn filter [pred s]
|
|
|
|
(def s (seq s))
|
|
|
|
(def {:more more :next next} s)
|
|
|
|
(var alive true)
|
|
|
|
(var temp nil)
|
|
|
|
(var isnew true)
|
|
|
|
(defn nextgood []
|
|
|
|
(if alive
|
|
|
|
(if (more)
|
|
|
|
(do
|
|
|
|
(def n (next))
|
|
|
|
(if (pred n) n (nextgood)))
|
|
|
|
(varset! alive false))))
|
|
|
|
(defn nnext [] (def ret temp) (varset! temp (nextgood)) ret)
|
|
|
|
(defn nmore [] (when isnew (varset! isnew false) (nnext)) alive)
|
|
|
|
{:more nmore :next nnext})
|
|
|
|
|
2018-03-11 20:30:38 +00:00
|
|
|
(defn even? [x] (== 0 (% x 2)))
|
|
|
|
(defn odd? [x] (== 1 (% x 2)))
|
2018-03-13 20:40:56 +00:00
|
|
|
(defn nil? [x] (= x nil))
|
|
|
|
(defn zero? [x] (== x 0))
|
|
|
|
(defn one? [x] (== x 1))
|
2018-03-13 17:31:25 +00:00
|
|
|
(defn inc [x] (+ x 1))
|
|
|
|
(defn dec [x] (- x 1))
|
2018-03-11 20:30:38 +00:00
|
|
|
|
|
|
|
(defmacro let [bindings & body]
|
|
|
|
(def head (ast-unwrap1 bindings))
|
|
|
|
(when (odd? (length head)) (error "expected even number of bindings to let"))
|
2018-03-12 04:26:13 +00:00
|
|
|
(def len (length head))
|
|
|
|
(var [i accum] [0 ['do]])
|
|
|
|
(while (< i len)
|
2018-03-11 20:30:38 +00:00
|
|
|
(array-push accum (tuple 'def
|
|
|
|
(get head i)
|
2018-03-12 04:26:13 +00:00
|
|
|
(get head (+ 1 i))))
|
|
|
|
(varset! i (+ i 2)))
|
2018-03-11 20:30:38 +00:00
|
|
|
(array-push accum (tuple-prepend body 'do))
|
|
|
|
(apply tuple accum))
|
2018-02-03 22:22:04 +00:00
|
|
|
|
2018-02-06 15:31:42 +00:00
|
|
|
(defn pairs [x]
|
|
|
|
(var lastkey (next x nil))
|
|
|
|
{
|
|
|
|
:more (fn [] lastkey)
|
|
|
|
:next (fn []
|
|
|
|
(def ret (tuple lastkey (get x lastkey)))
|
|
|
|
(varset! lastkey (next x lastkey))
|
|
|
|
ret)
|
|
|
|
})
|
|
|
|
|
|
|
|
(defn keys [x]
|
|
|
|
(var lastkey (next x nil))
|
|
|
|
{
|
|
|
|
:more (fn [] lastkey)
|
|
|
|
:next (fn []
|
|
|
|
(def ret lastkey)
|
|
|
|
(varset! lastkey (next x lastkey))
|
|
|
|
ret)
|
|
|
|
})
|
|
|
|
|
|
|
|
(defn values [x]
|
|
|
|
(var lastkey (next x nil))
|
|
|
|
{
|
|
|
|
:more (fn [] lastkey)
|
|
|
|
:next (fn []
|
|
|
|
(def ret (get x lastkey))
|
|
|
|
(varset! lastkey (next x lastkey))
|
|
|
|
ret)
|
|
|
|
})
|
2018-02-07 05:44:51 +00:00
|
|
|
|
2018-03-12 04:26:13 +00:00
|
|
|
(defmacro for [head & body]
|
|
|
|
(def head (ast-unwrap1 head))
|
|
|
|
(def sym (get head 0))
|
|
|
|
(def start (get head 1))
|
|
|
|
(def end (get head 2))
|
|
|
|
(def _inc (get head 3))
|
|
|
|
(def inc (if _inc _inc 1))
|
|
|
|
(def endsym (gensym))
|
|
|
|
(tuple 'do
|
|
|
|
(tuple 'var sym start)
|
|
|
|
(tuple 'def endsym end)
|
|
|
|
(tuple 'while (tuple '< sym endsym)
|
|
|
|
(tuple-prepend body 'do)
|
|
|
|
(tuple 'varset! sym (tuple '+ sym inc)))))
|
2018-02-07 05:44:51 +00:00
|
|
|
|
2018-03-12 16:47:05 +00:00
|
|
|
(defmacro ->
|
|
|
|
[x & forms]
|
|
|
|
(defn fop [last nextform]
|
|
|
|
(def n (ast-unwrap1 nextform))
|
|
|
|
(def [h t] (if (= :tuple (type n))
|
|
|
|
[(get n 0) (array-slice n 1)]
|
|
|
|
[n []]))
|
|
|
|
(def parts (array-concat [h last] t))
|
|
|
|
(apply tuple parts))
|
|
|
|
(reduce fop x forms))
|
|
|
|
|
|
|
|
(defmacro ->>
|
|
|
|
[x & forms]
|
|
|
|
(defn fop [last nextform]
|
|
|
|
(def n (ast-unwrap1 nextform))
|
|
|
|
(def [h t] (if (= :tuple (type n))
|
|
|
|
[(get n 0) (array-slice n 1)]
|
|
|
|
[n []]))
|
2018-03-13 20:40:56 +00:00
|
|
|
(def parts (array-concat [h] t [last]))
|
2018-03-12 16:47:05 +00:00
|
|
|
(apply tuple parts))
|
|
|
|
(reduce fop x forms))
|
|
|
|
|
2018-03-13 20:40:56 +00:00
|
|
|
(defn make-env [parent]
|
|
|
|
(def parent (if parent parent _env))
|
|
|
|
(def newenv (setproto @{} parent))
|
|
|
|
newenv)
|
|
|
|
(put _env '_env nil)
|
|
|
|
|
2018-03-12 04:26:13 +00:00
|
|
|
(def 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-12 04:26:13 +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-12 04:26:13 +00:00
|
|
|
(do
|
|
|
|
(defn val-stream [chunks onerr]
|
|
|
|
(var going true)
|
2018-03-13 17:31:25 +00:00
|
|
|
# Stream of characters
|
2018-03-12 04:26:13 +00:00
|
|
|
(def chars (fiber (fn []
|
2018-02-07 05:44:51 +00:00
|
|
|
(def buf @"")
|
|
|
|
(var len 1)
|
|
|
|
(while (< 0 len)
|
|
|
|
(buffer-clear buf)
|
2018-03-12 04:26:13 +00:00
|
|
|
(chunks buf)
|
2018-02-07 05:44:51 +00:00
|
|
|
(varset! len (length buf))
|
|
|
|
(for [i 0 len]
|
2018-03-11 19:35:23 +00:00
|
|
|
(yield (get buf i))))
|
|
|
|
0)))
|
2018-03-12 04:26:13 +00:00
|
|
|
(var temp nil)
|
|
|
|
(var tempval nil)
|
2018-03-13 17:31:25 +00:00
|
|
|
# Stream of values
|
2018-03-12 04:26:13 +00:00
|
|
|
(def f (fiber (fn []
|
2018-02-12 16:49:10 +00:00
|
|
|
(def p (parser 1))
|
2018-03-11 19:35:23 +00:00
|
|
|
(while going
|
2018-03-12 04:26:13 +00:00
|
|
|
(select (parser-status p)
|
|
|
|
:full (yield (parser-produce p))
|
|
|
|
:error (onerr "parse" (parser-error p))
|
|
|
|
(select (fiber-status chars)
|
|
|
|
:new (parser-byte p (resume chars))
|
|
|
|
:pending (parser-byte p (resume chars))
|
|
|
|
(varset! going false)))))))
|
|
|
|
(defn more [] (if temp true
|
|
|
|
(do
|
|
|
|
(varset! temp true)
|
|
|
|
(varset! tempval (resume f))
|
|
|
|
going)))
|
|
|
|
(defn next [] (if temp
|
|
|
|
(do (varset! temp nil) tempval)
|
|
|
|
(resume f)))
|
|
|
|
{:more more :next next})
|
|
|
|
(fn [env chunks onvalue onerr]
|
|
|
|
(defn doone [source]
|
|
|
|
(def f (fiber (fn []
|
|
|
|
(def res (compile source env))
|
|
|
|
(if (= (type res) :function)
|
|
|
|
(res)
|
|
|
|
(onerr "compile" (get res :error))))))
|
|
|
|
(def res (resume f))
|
|
|
|
(if (= (fiber-status f) :error)
|
|
|
|
(onerr "runtime" res)
|
|
|
|
(onvalue res)))
|
2018-03-11 19:35:23 +00:00
|
|
|
(def oldenv *env*)
|
2018-03-12 04:26:13 +00:00
|
|
|
(varset! *env* env)
|
|
|
|
(doseq (map doone (val-stream chunks onerr)))
|
|
|
|
(varset! *env* oldenv)
|
|
|
|
env)))
|
|
|
|
|
|
|
|
(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
|
|
|
|
(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
|
|
|
|
(fn [t x] (print (string t " error: " x))))
|
|
|
|
(file-close f)
|
|
|
|
(put loading path nil)
|
|
|
|
newenv)))))
|
|
|
|
|
|
|
|
(defn import [path & args]
|
|
|
|
(def env (require path))
|
|
|
|
(def {
|
|
|
|
:prefix prefix
|
|
|
|
} (apply table args))
|
2018-03-12 06:06:51 +00:00
|
|
|
(defn one [[k v]]
|
2018-03-13 20:40:56 +00:00
|
|
|
(when (not (get v :private))
|
2018-03-12 04:57:13 +00:00
|
|
|
(put *env* (symbol (if prefix prefix "") k) v)))
|
2018-03-12 04:26:13 +00:00
|
|
|
(doseq (map one (pairs env))))
|
|
|
|
|
2018-03-14 03:39:49 +00:00
|
|
|
(defn repl [getchunk]
|
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]
|
|
|
|
(put newenv '_ @{'value x})
|
|
|
|
(describe x))
|
2018-03-14 03:39:49 +00:00
|
|
|
(run-context newenv (if getchunk getchunk chunks) onvalue
|
2018-03-12 04:26:13 +00:00
|
|
|
(fn [t x] (print (string t " error: " x)))))
|