1
0
mirror of https://github.com/janet-lang/janet synced 2025-01-13 09:00:26 +00:00

Update boot.dst for better functionality (require, macros,

documentation, cond, let, etc.)
This commit is contained in:
Calvin Rose 2018-03-12 00:26:13 -04:00
parent 1ba93c5112
commit 3b2658150e
3 changed files with 229 additions and 135 deletions

View File

@ -1,14 +1,82 @@
(def defmacro macro
(fn [name & more] (tuple 'def name 'macro (tuple-prepend (tuple-prepend more name) 'fn))))
(defmacro defn # Capture the current env
[name & more] (var *env*
(tuple 'def name (tuple-prepend (tuple-prepend more name) 'fn))) "A var that points to the current environment."
_env)
(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]))
(apply 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))
(apply defn* args))))
(defmacro when (defmacro when
"(when cond & body)
Evaluates the body when the condition is true. Otherwise returns nil."
[cond & body] [cond & body]
(tuple 'if cond (tuple-prepend body 'do))) (tuple 'if cond (tuple-prepend body 'do)))
(defmacro cond
"(cond & body)
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))
(defn doc
"(doc sym)
Shows documentation for the given symbol."
[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
"(select dispatch & body)
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)))
(defmacro or [x y] (tuple 'if x true y)) (defmacro or [x y] (tuple 'if x true y))
(defmacro and [x y] (tuple 'if x y false)) (defmacro and [x y] (tuple 'if x y false))
@ -53,44 +121,18 @@
}) })
(defn doseq [s] (defn doseq [s]
(def s (seq s)) (def {:more more :next next} (seq s))
(def more? (get s :more)) (while (more) (next)))
(def getnext (get s :next))
(while (more?)
(getnext)))
(defn map [f s] (defn map [f s]
(def s (seq s)) (def {:more more :next next} (seq s))
(def more (get s :more)) {:more more :next (fn [] (f (next)))})
(def getnext (get s :next))
{
:more more
:next (fn [] (f (getnext)))
})
(defn reduce [f start s] (defn reduce [f start s]
(def s (seq s)) (def {:more more :next next} (seq s))
(def more? (get s :more)) (if (more)
(def getnext (get s :next)) (reduce f (f start (next)) s)
(var ret start) start))
(while (more?)
(varset! ret (f ret (getnext))))
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)
(tuple 'varset! sym (tuple '+ sym inc)))))
(defn even? [x] (== 0 (% x 2))) (defn even? [x] (== 0 (% x 2)))
(defn odd? [x] (== 1 (% x 2))) (defn odd? [x] (== 1 (% x 2)))
@ -98,11 +140,13 @@
(defmacro let [bindings & body] (defmacro let [bindings & body]
(def head (ast-unwrap1 bindings)) (def head (ast-unwrap1 bindings))
(when (odd? (length head)) (error "expected even number of bindings to let")) (when (odd? (length head)) (error "expected even number of bindings to let"))
(var accum ['do]) (def len (length head))
(for [i 0 (length head) 2] (var [i accum] [0 ['do]])
(while (< i len)
(array-push accum (tuple 'def (array-push accum (tuple 'def
(get head i) (get head i)
(get head (+ 1 i))))) (get head (+ 1 i))))
(varset! i (+ i 2)))
(array-push accum (tuple-prepend body 'do)) (array-push accum (tuple-prepend body 'do))
(apply tuple accum)) (apply tuple accum))
@ -136,106 +180,130 @@
ret) 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)
(tuple 'varset! sym (tuple '+ sym inc)))))
# Compile time # Compile time
(var *read* nil) (defn make-env [parent]
(var *onvalue* identity) (def parent (if parent parent _env))
(var *env* (setproto @{} _env)) (def newenv (setproto @{} parent))
newenv)
(put _env '_env nil)
(def require-loading @{}) (def run-context
"(run-context env chunks onvalue onerr)
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.
(defn onerr [t e] This function can be used to implemement a repl very easily, simply
(print (string t " error: " e))) pass a function that reads line from stdin to chunks, and print to
onvalue."
(defn char-stream [getchunk] (do
(fiber (fn [] (defn val-stream [chunks onerr]
(var going true)
(def chars (fiber (fn []
(def buf @"") (def buf @"")
(var len 1) (var len 1)
(while (< 0 len) (while (< 0 len)
(buffer-clear buf) (buffer-clear buf)
(getchunk buf) (chunks buf)
(varset! len (length buf)) (varset! len (length buf))
(for [i 0 len] (for [i 0 len]
(yield (get buf i)))) (yield (get buf i))))
0))) 0)))
(var temp nil)
(defn val-stream [chars ondone] (var tempval nil)
(fiber (fn [] (def f (fiber (fn []
(def p (parser 1)) (def p (parser 1))
(var going true)
(while going (while going
(def s (parser-status p)) (select (parser-status p)
(if (= s :full) :full (yield (parser-produce p))
(yield (parser-produce p)) :error (onerr "parse" (parser-error p))
(if (= s :error) (select (fiber-status chars)
(onerr "parse" (parser-error p)) :new (parser-byte p (resume chars))
:pending (parser-byte p (resume chars))
(varset! going false)))))))
(defn more [] (if temp true
(do (do
(def stat (fiber-status chars)) (varset! temp true)
(if (or (= :new stat) (= :pending stat)) (varset! tempval (resume f))
(parser-byte p (resume chars)) going)))
(varset! going false)))))) (defn next [] (if temp
(ondone)))) (do (varset! temp nil) tempval)
(resume f)))
{:more more :next next})
(defn require [path] (fn [env chunks onvalue onerr]
(when (get require-loading path) (defn doone [source]
(error (string "circular dependency: module " path " is already loading"))) (def f (fiber (fn []
(def oldread *read*) (def res (compile source env))
(def oldonvalue *onvalue*) (if (= (type res) :function)
(res)
(onerr "compile" (get res :error))))))
(def res (resume f))
(if (= (fiber-status f) :error)
(onerr "runtime" res)
(onvalue res)))
(def oldenv *env*) (def oldenv *env*)
(def f (file-open path)) (varset! *env* env)
(defn getter [buf] (file-read f 1024 buf) buf) (doseq (map doone (val-stream chunks onerr)))
(defn resetter []
(put require-loading path nil)
(varset! *read* oldread)
(varset! *onvalue* oldonvalue)
(varset! *env* oldenv) (varset! *env* oldenv)
(file-close f) env)))
nil)
(def cs (char-stream getter))
(def vs (val-stream cs resetter))
(varset! *onvalue* identity)
(varset! *read* (fn [] (resume vs)))
(varset! *env* (setproto @{} _env))
*env*)
(defn dorepl [] (def require (do
(def oldread *read*) (def cache @{})
(defn getter [buf] (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))
(defn one [pair]
(def [k v] pair)
(put *env* (symbol (if prefix prefix "") k) v))
(doseq (map one (pairs env))))
(defn repl []
(def newenv (make-env))
(defn chunks [buf]
(file-write stdout ">> ") (file-write stdout ">> ")
(file-read stdin :line buf)) (file-read stdin :line buf))
(defn resetter [] (defn onvalue [x]
(varset! *read* oldread) (put newenv '_ @{'value x})
nil) (describe x))
(def cs (char-stream getter)) (run-context newenv chunks onvalue
(def vs (val-stream cs resetter)) (fn [t x] (print (string t " error: " x)))))
(varset! *onvalue* (fn [ret]
(put *env* '_ @{'value ret})
(describe ret)))
(varset! *read* (fn [] (resume vs))))
(defn dostring [str]
(def oldread *read*)
(defn getter [buf]
(buffer-push-string buf str)
(buffer-push-string buf "\n"))
(defn resetter []
(varset! *read* oldread)
nil)
(def cs (char-stream getter))
(def vs (val-stream cs resetter))
(varset! *onvalue* identity)
(varset! *read* (fn [] (resume vs))))
(defn init-loop []
(while *read*
(def wrapper (fiber (fn []
(while *read*
(def source (*read*))
(def res (compile source *env*))
(if (= (type res) :function)
(*onvalue* (res))
(onerr "compile" (get res :error)))))))
(def eb (resume wrapper))
(if (= (fiber-status wrapper) :error) (onerr "runtime" eb wrapper))))
(defn init-repl [] (dorepl) (init-loop))

View File

@ -184,6 +184,31 @@ static int cfun_slice(DstArgs args) {
return dst_return(args, dst_wrap_array(ret)); return dst_return(args, dst_wrap_array(ret));
} }
static int cfun_concat(DstArgs args) {
int32_t i;
DstArray *array;
if (args.n < 1 || !dst_checktype(args.v[0], DST_ARRAY)) return dst_throw(args, "expected array");
array = dst_unwrap_array(args.v[0]);
for (i = 1; i < args.n; i++) {
switch (dst_type(args.v[i])) {
default:
dst_array_push(array, args.v[i]);
break;
case DST_ARRAY:
case DST_TUPLE:
{
int32_t j, len;
const Dst *vals;
dst_seq_view(args.v[i], &vals, &len);
for (j = 0; j < len; j++)
dst_array_push(array, vals[j]);
}
break;
}
}
return dst_return(args, args.v[0]);
}
/* Load the array module */ /* Load the array module */
int dst_lib_array(DstArgs args) { int dst_lib_array(DstArgs args) {
DstTable *env = dst_env_arg(args); DstTable *env = dst_env_arg(args);
@ -193,5 +218,6 @@ int dst_lib_array(DstArgs args) {
dst_env_def(env, "array-setcount", dst_wrap_cfunction(cfun_setcount)); dst_env_def(env, "array-setcount", dst_wrap_cfunction(cfun_setcount));
dst_env_def(env, "array-ensure", dst_wrap_cfunction(cfun_ensure)); dst_env_def(env, "array-ensure", dst_wrap_cfunction(cfun_ensure));
dst_env_def(env, "array-slice", dst_wrap_cfunction(cfun_slice)); dst_env_def(env, "array-slice", dst_wrap_cfunction(cfun_slice));
dst_env_def(env, "array-concat", dst_wrap_cfunction(cfun_concat));
return 0; return 0;
} }

View File

@ -1,7 +1,7 @@
(do (do
(var dorepl false) (var should-repl false)
(var nofile true) (var no-file true)
# Flag handlers # Flag handlers
(def handlers { (def handlers {
@ -13,7 +13,7 @@
(print " -r Enter the repl after running all scripts") (print " -r Enter the repl after running all scripts")
(exit 0)) (exit 0))
"v" (fn [] (print VERSION) (exit 0)) "v" (fn [] (print VERSION) (exit 0))
"r" (fn [] (varset! dorepl true)) "r" (fn [] (varset! should-repl true))
}) })
(defn dohandler [n] (defn dohandler [n]
@ -27,11 +27,11 @@
(if (= "-" (string-slice arg 0 1)) (if (= "-" (string-slice arg 0 1))
(dohandler (string-slice arg 1 2)) (dohandler (string-slice arg 1 2))
(do (do
(varset! nofile false) (varset! no-file false)
(require arg) (require arg))))
(init-loop))))
(when (or dorepl nofile) (when (or should-repl no-file)
(print (string "Dst " VERSION " Copyright (C) 2017-2018 Calvin Rose")) (print (string "Dst " VERSION " Copyright (C) 2017-2018 Calvin Rose"))
(init-repl)) (repl))
) )