1
0
mirror of https://github.com/janet-lang/janet synced 2024-07-01 17:43:15 +00:00
janet/src/compiler/boot.dst

476 lines
12 KiB
Plaintext
Raw Normal View History

# Bootstrap the dst environment
# Copyright 2018 (C) Calvin Rose
(def defn :macro
"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]))
(apply1 tuple formargs)))
(def defmacro :macro
"Define a macro."
(do
(def defn* (get (get _env 'defn) 'value))
(fn [name & more]
(def args (array-concat [] name :macro more))
(apply1 defn* args))))
(defmacro defmacro-
"Define a private macro that will not be exported."
[name & more]
(apply1 tuple (array-concat
['defmacro name :private] more)))
2018-03-12 04:57:13 +00:00
(defmacro defn-
"Define a private function that will not be exported."
[name & more]
(apply1 tuple (array-concat
['defn name :private] more)))
2018-03-12 04:57:13 +00:00
2018-03-15 01:46:56 +00:00
(defn even? [x] (== 0 (% x 2)))
(defn odd? [x] (== 1 (% x 2)))
(defn nil? [x] (= x nil))
(defn zero? [x] (== x 0))
(defn pos? [x] (> x 0))
(defn neg? [x] (< x 0))
2018-03-15 01:46:56 +00:00
(defn one? [x] (== x 1))
(defn inc [x] (+ x 1))
(defn dec [x] (- x 1))
(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
# C style macros for imperative sugar
(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."
[])
(defmacro when
2018-03-12 06:06:51 +00:00
"Evaluates the body when the condition is true. Otherwise returns nil."
[cond & body]
(tuple 'if cond (tuple-prepend body 'do)))
(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."
[& 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-16 17:40:10 +00:00
(defn doc*
[env 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"))))
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
(def apply
(fn [f & args]
(def last (- (length args) 1))
(apply1 f (array-concat (array-slice args 0 -2) (get args last)))))
(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"
[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-03-15 01:46:56 +00:00
(defmacro and [& 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 [& forms]
(def len (length forms))
(if (= len 0) false ((fn aux [i]
(cond
(>= (inc i) len) (get forms i)
(tuple 'if (get forms i) true (aux (inc i))))) 0)))
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)
(def iter (do
(defn array-iter [x]
(def len (length x))
(var i 0)
{
:more (fn [] (< i len))
:next (fn []
(def ret (get x i))
2018-03-16 22:15:34 +00:00
(:= i (+ i 1))
ret)
})
(def iters {
:array array-iter
:tuple array-iter
2018-03-16 17:40:10 +00:00
:struct identity})
(fn [x]
(def makei (get iters (type x)))
(if makei (makei x) (error "expected sequence")))))
2018-03-16 17:40:10 +00:00
(defn range2 [bottom top]
(var i bottom)
{
:more (fn [] (< i top))
:next (fn []
(def ret i)
2018-03-16 22:15:34 +00:00
(:= i (+ i 1))
ret)
})
2018-03-16 17:40:10 +00:00
(defn range [top] (range2 0 top))
(defn doiter [itr]
(def {:more more :next next} (iter itr))
(while (more) (next)))
(defn foreach [itr f]
(def {:more more :next next} (iter itr))
2018-03-14 03:39:49 +00:00
(while (more) (f (next))))
(defn iter2array [itr]
(def {:more more :next next} (iter itr))
2018-03-15 01:46:56 +00:00
(def a [])
(while (more) (array-push a (next)))
a)
(defn map [f itr]
(def {:more more :next next} (iter itr))
{:more more :next (fn [] (f (next)))})
(defn reduce [f start itr]
(def itr (iter itr))
(def {:more more :next next} itr)
(if (more)
(reduce f (f start (next)) itr)
start))
2018-03-11 20:30:38 +00:00
(defn filter [pred itr]
(def itr (iter itr))
(def {:more more :next next} itr)
(var alive true)
(var temp nil)
(var isnew true)
(defn nextgood []
(if alive
(if (more)
(do
(def n (next))
(if (pred n) n (nextgood)))
2018-03-16 22:15:34 +00:00
(:= alive false))))
(defn nnext [] (def ret temp) (:= temp (nextgood)) ret)
(defn nmore [] (when isnew (:= isnew false) (nnext)) alive)
{:more nmore :next nnext})
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"))
(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)
(get head (+ 1 i))))
2018-03-16 22:15:34 +00:00
(:= i (+ i 2)))
2018-03-11 20:30:38 +00:00
(array-push accum (tuple-prepend body 'do))
(apply1 tuple accum))
2018-02-03 22:22:04 +00:00
(defn pairs [x]
(var lastkey (next x nil))
{
:more (fn [] lastkey)
:next (fn []
(def ret (tuple lastkey (get x lastkey)))
2018-03-16 22:15:34 +00:00
(:= lastkey (next x lastkey))
ret)
})
(defn keys [x]
(var lastkey (next x nil))
{
:more (fn [] lastkey)
:next (fn []
(def ret lastkey)
2018-03-16 22:15:34 +00:00
(:= lastkey (next x lastkey))
ret)
})
(defn values [x]
(var lastkey (next x nil))
{
:more (fn [] lastkey)
:next (fn []
(def ret (get x lastkey))
2018-03-16 22:15:34 +00:00
(:= lastkey (next x lastkey))
ret)
})
(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)
2018-03-16 22:15:34 +00:00
(tuple ':= sym (tuple '+ sym inc)))))
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))
(apply1 tuple parts))
2018-03-12 16:47:05 +00:00
(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 []]))
(def parts (array-concat [h] t [last]))
(apply1 tuple parts))
2018-03-12 16:47:05 +00:00
(reduce fop x forms))
2018-03-14 23:08:00 +00:00
# Start pretty printer
(def pp (do
(defn- pp-seq [pp seen buf a start end]
(if (get seen a)
(buffer-push-string buf "<cycle>")
(do
(put seen a true)
(def len (length a))
(buffer-push-string buf start)
(for [i 0 len]
(when (not= i 0) (buffer-push-string buf " "))
(pp seen buf (get a i)))
(buffer-push-string buf end)))
buf)
(defn- pp-dict [pp seen buf a start end]
(if (get seen a)
(buffer-push-string buf "<cycle>")
(do
(put seen a true)
(var k (next a nil))
(buffer-push-string buf start)
(while k
(def v (get a k))
(pp seen buf k)
(buffer-push-string buf " ")
(pp seen buf v)
2018-03-16 22:15:34 +00:00
(:= k (next a k))
2018-03-14 23:08:00 +00:00
(when k (buffer-push-string buf " ")))
(buffer-push-string buf end)))
buf)
(def printers :private {
:array (fn [pp seen buf x] (pp-seq pp seen buf x "[" "]"))
:tuple (fn [pp seen buf x] (pp-seq pp seen buf x "(" ")"))
:table (fn [pp seen buf x] (pp-dict pp seen buf x "@{" "}"))
:struct (fn [pp seen buf x] (pp-dict pp seen buf x "{" "}"))
})
(defn- default_printer [pp seen buf x]
(buffer-push-string buf (describe x))
buf)
(defn- pp1 [seen buf x]
(def pmaybe (get printers (type x)))
(def p (if pmaybe pmaybe default_printer))
(p pp1 seen buf x))
(fn [x] (print (pp1 @{} @"" x)))))
# End pretty printer
2018-03-15 01:46:56 +00:00
(defn unique [s]
(def tab @{})
(foreach s (fn [x] (put tab x true)))
2018-03-15 01:46:56 +00:00
(keys tab))
2018-03-16 18:34:48 +00:00
(defn make-env [parent safe]
(def parent (if parent parent _env))
(def newenv (setproto @{} parent))
2018-03-16 18:34:48 +00:00
(if (not safe)
(put newenv '_env @{'value newenv}))
newenv)
(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 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."
(do
(defn val-stream [chunks onerr]
(var going true)
# Stream of characters
(def chars (fiber (fn []
(def buf @"")
(var len 1)
(while (< 0 len)
(buffer-clear buf)
(chunks buf)
2018-03-16 22:15:34 +00:00
(:= len (length buf))
(for [i 0 len]
(yield (get buf i))))
0)))
(var temp nil)
(var tempval nil)
# Stream of values
(def f (fiber (fn []
(def p (parser 1))
(while going
(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))
2018-03-16 22:15:34 +00:00
(:= going false))))
(when (not= :root (parser-status p))
(onerr "parse" "unexpected end of source"))
nil)))
(defn more [] (if temp true
(do
2018-03-16 22:15:34 +00:00
(:= temp true)
(:= tempval (resume f))
going)))
(defn next [] (if temp
2018-03-16 22:15:34 +00:00
(do (:= temp nil) tempval)
(resume f)))
{:more more :next next})
(fn [env chunks onvalue onerr]
(defn doone [source]
2018-03-18 13:13:21 +00:00
(var good true)
(def f (fiber (fn []
(def res (compile source env))
(if (= (type res) :function)
(res)
2018-03-18 13:13:21 +00:00
(do
(:= good false)
(onerr "compile" (get res :error)))))))
(def res (resume f))
2018-03-18 13:13:21 +00:00
(if good
(if (= (fiber-status f) :error)
(onerr "runtime" res)
(onvalue res))))
(foreach (val-stream chunks onerr) doone)
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)))))
2018-03-16 17:40:10 +00:00
(defn import* [env path & args]
(def newenv (require path))
(def {
:prefix prefix
} (apply1 table args))
2018-03-16 17:40:10 +00:00
(foreach (pairs newenv) (fn [[k v]]
(when (not (get v :private))
2018-03-16 17:40:10 +00:00
(put env (symbol (if prefix prefix "") k) v)))))
(defmacro import [path & args]
(apply1 tuple (array-concat [import* '_env path] args)))
2018-03-14 03:39:49 +00:00
(defn repl [getchunk]
(def newenv (make-env))
(defn chunks [buf]
2018-03-14 14:54:29 +00:00
(file-write stdout "> ")
(file-flush stdout)
(file-read stdin :line buf))
(defn onvalue [x]
(put newenv '_ @{'value x})
2018-03-14 23:08:00 +00:00
(pp x))
2018-03-14 03:39:49 +00:00
(run-context newenv (if getchunk getchunk chunks) onvalue
(fn [t x] (print (string t " error: " x)))))