mirror of
https://github.com/janet-lang/janet
synced 2025-02-09 05:20:03 +00:00
Update core namespace. Clean up some code,
and put more emphasis on indexed data-structure combinators instead of iterators.
This commit is contained in:
parent
d9f6c7b069
commit
73ead5c2de
@ -1,3 +1,6 @@
|
|||||||
|
|
||||||
|
(import "examples/iterators.dst")
|
||||||
|
|
||||||
(defn sum3
|
(defn sum3
|
||||||
"Solve the 3SUM problem O(n^2) time."
|
"Solve the 3SUM problem O(n^2) time."
|
||||||
[s]
|
[s]
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
# Get the number of occurences of elements in a set
|
# Get the number of occurences of elements in a set
|
||||||
|
|
||||||
|
(import "examples/iterators.dst")
|
||||||
|
|
||||||
(defn frequencies
|
(defn frequencies
|
||||||
"Get the number of occurences of each value in a sequence."
|
"Get the number of occurences of each value in a sequence."
|
||||||
[s]
|
[s]
|
||||||
|
109
examples/iterators.dst
Normal file
109
examples/iterators.dst
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
###
|
||||||
|
###
|
||||||
|
### Iterators
|
||||||
|
###
|
||||||
|
###
|
||||||
|
|
||||||
|
(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))
|
||||||
|
(:= i (+ i 1))
|
||||||
|
ret)
|
||||||
|
})
|
||||||
|
(def iters {
|
||||||
|
:array array-iter
|
||||||
|
:tuple array-iter
|
||||||
|
:struct identity})
|
||||||
|
(fn [x]
|
||||||
|
(def makei (get iters (type x)))
|
||||||
|
(if makei (makei x) (error "expected sequence")))))
|
||||||
|
|
||||||
|
(defn range2 [bottom top]
|
||||||
|
(var i bottom)
|
||||||
|
{
|
||||||
|
:more (fn [] (< i top))
|
||||||
|
:next (fn []
|
||||||
|
(def ret i)
|
||||||
|
(:= i (+ i 1))
|
||||||
|
ret)
|
||||||
|
})
|
||||||
|
|
||||||
|
(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))
|
||||||
|
(while (more) (f (next))))
|
||||||
|
|
||||||
|
(defn iter2array [itr]
|
||||||
|
(def {:more more :next next} (iter itr))
|
||||||
|
(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))
|
||||||
|
|
||||||
|
(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)))
|
||||||
|
(:= alive false))))
|
||||||
|
(defn nnext [] (def ret temp) (:= temp (nextgood)) ret)
|
||||||
|
(defn nmore [] (when isnew (:= isnew false) (nnext)) alive)
|
||||||
|
{:more nmore :next nnext})
|
||||||
|
|
||||||
|
(defn pairs [x]
|
||||||
|
(var lastkey (next x nil))
|
||||||
|
{
|
||||||
|
:more (fn [] lastkey)
|
||||||
|
:next (fn []
|
||||||
|
(def ret (tuple lastkey (get x lastkey)))
|
||||||
|
(:= lastkey (next x lastkey))
|
||||||
|
ret)
|
||||||
|
})
|
||||||
|
|
||||||
|
(defn keys [x]
|
||||||
|
(var lastkey (next x nil))
|
||||||
|
{
|
||||||
|
:more (fn [] lastkey)
|
||||||
|
:next (fn []
|
||||||
|
(def ret lastkey)
|
||||||
|
(:= lastkey (next x lastkey))
|
||||||
|
ret)
|
||||||
|
})
|
||||||
|
|
||||||
|
(defn values [x]
|
||||||
|
(var lastkey (next x nil))
|
||||||
|
{
|
||||||
|
:more (fn [] lastkey)
|
||||||
|
:next (fn []
|
||||||
|
(def ret (get x lastkey))
|
||||||
|
(:= lastkey (next x lastkey))
|
||||||
|
ret)
|
||||||
|
})
|
||||||
|
|
@ -1,6 +1,12 @@
|
|||||||
# Bootstrap the dst environment
|
# Bootstrap the dst environment
|
||||||
# Copyright 2018 (C) Calvin Rose
|
# Copyright 2018 (C) Calvin Rose
|
||||||
|
|
||||||
|
###
|
||||||
|
###
|
||||||
|
### Macros and Basic Functions
|
||||||
|
###
|
||||||
|
###
|
||||||
|
|
||||||
(var *env*
|
(var *env*
|
||||||
"The current environment."
|
"The current environment."
|
||||||
_env)
|
_env)
|
||||||
@ -39,6 +45,12 @@
|
|||||||
(apply1 tuple (array-concat
|
(apply1 tuple (array-concat
|
||||||
@['defn name :private] more)))
|
@['defn name :private] more)))
|
||||||
|
|
||||||
|
(defmacro def-
|
||||||
|
"Define a private value that will not be exported."
|
||||||
|
[name & more]
|
||||||
|
(apply1 tuple (array-concat
|
||||||
|
@['def name :private] more)))
|
||||||
|
|
||||||
# Basic predicates
|
# Basic predicates
|
||||||
(defn even? [x] (== 0 (% x 2)))
|
(defn even? [x] (== 0 (% x 2)))
|
||||||
(defn odd? [x] (== 1 (% x 2)))
|
(defn odd? [x] (== 1 (% x 2)))
|
||||||
@ -51,6 +63,9 @@
|
|||||||
(defn array? [x] (= (type x) :array))
|
(defn array? [x] (= (type x) :array))
|
||||||
(defn tuple? [x] (= (type x) :tuple))
|
(defn tuple? [x] (= (type x) :tuple))
|
||||||
(defn boolean? [x] (= (type x) :boolean))
|
(defn boolean? [x] (= (type x) :boolean))
|
||||||
|
(defn function? [x]
|
||||||
|
(def t (type x))
|
||||||
|
(if (= t :function) true (= t :cfunction)))
|
||||||
(defn true? [x] (= (type x) true))
|
(defn true? [x] (= (type x) true))
|
||||||
(defn false? [x] (= (type x) false))
|
(defn false? [x] (= (type x) false))
|
||||||
(defn nil? [x] (= x nil))
|
(defn nil? [x] (= x nil))
|
||||||
@ -84,10 +99,20 @@
|
|||||||
"Ignores the body of the comment."
|
"Ignores the body of the comment."
|
||||||
[])
|
[])
|
||||||
|
|
||||||
|
(defmacro if-not
|
||||||
|
"Sorthand for (if (not ... "
|
||||||
|
[condition exp-1 exp-2]
|
||||||
|
(tuple 'if condition exp-2 exp-1))
|
||||||
|
|
||||||
(defmacro when
|
(defmacro when
|
||||||
"Evaluates the body when the condition is true. Otherwise returns nil."
|
"Evaluates the body when the condition is true. Otherwise returns nil."
|
||||||
[cond & body]
|
[condition & body]
|
||||||
(tuple 'if cond (tuple-prepend body 'do)))
|
(tuple 'if condition (tuple-prepend body 'do)))
|
||||||
|
|
||||||
|
(defmacro when-not
|
||||||
|
"Sorthand for (when (not ... "
|
||||||
|
[condition & body]
|
||||||
|
(tuple 'if condition nil (tuple-prepend body 'do)))
|
||||||
|
|
||||||
(defmacro cond
|
(defmacro cond
|
||||||
"Evaluates conditions sequentially until the first true condition
|
"Evaluates conditions sequentially until the first true condition
|
||||||
@ -95,14 +120,14 @@ is found, and then executes the corresponding body. If there are an
|
|||||||
odd number of forms, the last expression is executed if no forms
|
odd number of forms, the last expression is executed if no forms
|
||||||
are matched. If there are no matches, return nil."
|
are matched. If there are no matches, return nil."
|
||||||
[& pairs]
|
[& pairs]
|
||||||
(defn aux [i]
|
(defn aux [i]
|
||||||
(def restlen (- (length pairs) i))
|
(def restlen (- (length pairs) i))
|
||||||
(if (= restlen 0) nil
|
(if (= restlen 0) nil
|
||||||
(if (= restlen 1) (get pairs i)
|
(if (= restlen 1) (get pairs i)
|
||||||
(tuple 'if (get pairs i)
|
(tuple 'if (get pairs i)
|
||||||
(get pairs (+ i 1))
|
(get pairs (+ i 1))
|
||||||
(aux (+ i 2))))))
|
(aux (+ i 2))))))
|
||||||
(aux 0))
|
(aux 0))
|
||||||
|
|
||||||
(defn doc*
|
(defn doc*
|
||||||
[env sym]
|
[env sym]
|
||||||
@ -118,17 +143,21 @@ are matched. If there are no matches, return nil."
|
|||||||
[sym]
|
[sym]
|
||||||
(tuple doc* '_env (tuple 'quote sym)))
|
(tuple doc* '_env (tuple 'quote sym)))
|
||||||
|
|
||||||
(def apply
|
(defn apply
|
||||||
(fn [f & args]
|
"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]
|
||||||
(def last (- (length args) 1))
|
(def last (- (length args) 1))
|
||||||
(apply1 f (array-concat (array-slice args 0 -2) (get args last)))))
|
(apply1 f (array-concat (array-slice args 0 -2) (get args last))))
|
||||||
|
|
||||||
(defmacro select
|
(defmacro switch
|
||||||
"Select the body that equals the dispatch value. When pairs
|
"Select the body that equals the dispatch value. When pairs
|
||||||
has an odd number of arguments, the last is the default expression.
|
has an odd number of arguments, the last is the default expression.
|
||||||
If no match is found, returns nil"
|
If no match is found, returns nil"
|
||||||
[dispatch & pairs]
|
[dispatch & pairs]
|
||||||
(def sym (gensym))
|
(def atm (atomic? (ast-unwrap1 dispatch)))
|
||||||
|
(def sym (if atm dispatch (gensym)))
|
||||||
(defn aux [i]
|
(defn aux [i]
|
||||||
(def restlen (- (length pairs) i))
|
(def restlen (- (length pairs) i))
|
||||||
(if (= restlen 0) nil
|
(if (= restlen 0) nil
|
||||||
@ -136,104 +165,19 @@ If no match is found, returns nil"
|
|||||||
(tuple 'if (tuple = sym (get pairs i))
|
(tuple 'if (tuple = sym (get pairs i))
|
||||||
(get pairs (+ i 1))
|
(get pairs (+ i 1))
|
||||||
(aux (+ i 2))))))
|
(aux (+ i 2))))))
|
||||||
(tuple 'do
|
(if atm
|
||||||
(tuple 'def sym dispatch)
|
(aux 0)
|
||||||
(aux 0)))
|
(tuple 'do
|
||||||
|
(tuple 'def sym dispatch)
|
||||||
|
(aux 0))))
|
||||||
|
|
||||||
(defmacro and [& forms]
|
(defmacro let
|
||||||
(def len (length forms))
|
"Create a scope and bind values to symbols. Each pair in bindings is
|
||||||
(if (= len 0) true ((fn aux [i]
|
assigned as if with def, and the body of the let form returns the last
|
||||||
(cond
|
value."
|
||||||
(>= (inc i) len) (get forms i)
|
[bindings & body]
|
||||||
(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)))
|
|
||||||
|
|
||||||
(defn identity
|
|
||||||
"A function that returns its first argument."
|
|
||||||
[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))
|
|
||||||
(:= i (+ i 1))
|
|
||||||
ret)
|
|
||||||
})
|
|
||||||
(def iters {
|
|
||||||
:array array-iter
|
|
||||||
:tuple array-iter
|
|
||||||
:struct identity})
|
|
||||||
(fn [x]
|
|
||||||
(def makei (get iters (type x)))
|
|
||||||
(if makei (makei x) (error "expected sequence")))))
|
|
||||||
|
|
||||||
(defn range2 [bottom top]
|
|
||||||
(var i bottom)
|
|
||||||
{
|
|
||||||
:more (fn [] (< i top))
|
|
||||||
:next (fn []
|
|
||||||
(def ret i)
|
|
||||||
(:= i (+ i 1))
|
|
||||||
ret)
|
|
||||||
})
|
|
||||||
|
|
||||||
(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))
|
|
||||||
(while (more) (f (next))))
|
|
||||||
|
|
||||||
(defn iter2array [itr]
|
|
||||||
(def {:more more :next next} (iter itr))
|
|
||||||
(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))
|
|
||||||
|
|
||||||
(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)))
|
|
||||||
(:= alive false))))
|
|
||||||
(defn nnext [] (def ret temp) (:= temp (nextgood)) ret)
|
|
||||||
(defn nmore [] (when isnew (:= isnew false) (nnext)) alive)
|
|
||||||
{:more nmore :next nnext})
|
|
||||||
|
|
||||||
(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"))
|
(if (odd? (length head)) (error "expected even number of bindings to let"))
|
||||||
(def len (length head))
|
(def len (length head))
|
||||||
(var i 0)
|
(var i 0)
|
||||||
(var accum @['do])
|
(var accum @['do])
|
||||||
@ -241,70 +185,237 @@ If no match is found, returns nil"
|
|||||||
(array-push accum (tuple 'def
|
(array-push accum (tuple 'def
|
||||||
(get head i)
|
(get head i)
|
||||||
(get head (+ 1 i))))
|
(get head (+ 1 i))))
|
||||||
(:= i (+ i 2)))
|
(+= i 2))
|
||||||
(array-push accum (tuple-prepend body 'do))
|
(array-concat accum body)
|
||||||
(apply1 tuple accum))
|
(apply1 tuple accum))
|
||||||
|
|
||||||
(defn pairs [x]
|
(defmacro for
|
||||||
(var lastkey (next x nil))
|
"An imperative for loop over an integer range. Use with caution and discretion."
|
||||||
{
|
[head & body]
|
||||||
:more (fn [] lastkey)
|
(def [sym start end _inc] (ast-unwrap1 head))
|
||||||
:next (fn []
|
|
||||||
(def ret (tuple lastkey (get x lastkey)))
|
|
||||||
(:= lastkey (next x lastkey))
|
|
||||||
ret)
|
|
||||||
})
|
|
||||||
|
|
||||||
(defn keys [x]
|
|
||||||
(var lastkey (next x nil))
|
|
||||||
{
|
|
||||||
:more (fn [] lastkey)
|
|
||||||
:next (fn []
|
|
||||||
(def ret lastkey)
|
|
||||||
(:= lastkey (next x lastkey))
|
|
||||||
ret)
|
|
||||||
})
|
|
||||||
|
|
||||||
(defn values [x]
|
|
||||||
(var lastkey (next x nil))
|
|
||||||
{
|
|
||||||
:more (fn [] lastkey)
|
|
||||||
:next (fn []
|
|
||||||
(def ret (get x lastkey))
|
|
||||||
(:= lastkey (next x lastkey))
|
|
||||||
ret)
|
|
||||||
})
|
|
||||||
|
|
||||||
(defn partial [f & more]
|
|
||||||
(if (zero? (length more)) f
|
|
||||||
(fn [& r] (apply1 f (array-concat @[] more r)))))
|
|
||||||
|
|
||||||
(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 inc (if _inc _inc 1))
|
||||||
(def endsym (gensym))
|
(def endsym (gensym))
|
||||||
(tuple 'do
|
(tuple 'do
|
||||||
(tuple 'var sym start)
|
(tuple 'var sym start)
|
||||||
(tuple 'def endsym end)
|
(tuple 'def endsym end)
|
||||||
(tuple 'while (tuple '< sym endsym)
|
(tuple 'while (tuple < sym endsym)
|
||||||
(tuple-prepend body 'do)
|
(tuple-prepend body 'do)
|
||||||
(tuple ':= sym (tuple '+ sym inc)))))
|
(tuple ':= sym (tuple + sym inc)))))
|
||||||
|
|
||||||
(defn every? [pred seq]
|
(defmacro and
|
||||||
(var res true)
|
"Evaluates to the last argument if all preceding elements are true, otherwise
|
||||||
(var i 0)
|
evaluates to false."
|
||||||
(def len (length seq))
|
[& forms]
|
||||||
(while (< i len)
|
(def len (length forms))
|
||||||
(def item (get seq i))
|
(if (= len 0) true ((fn aux [i]
|
||||||
(if (pred item)
|
(cond
|
||||||
(++ i)
|
(>= (inc i) len) (get forms i)
|
||||||
(do (:= res false) (:= i len))))
|
(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))
|
||||||
|
|
||||||
|
(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))))
|
||||||
|
|
||||||
|
###
|
||||||
|
###
|
||||||
|
### Indexed Conbinators
|
||||||
|
###
|
||||||
|
###
|
||||||
|
|
||||||
|
(defn reduce
|
||||||
|
"Reduce, also know as foldleft in many languages, transform
|
||||||
|
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))))
|
||||||
res)
|
res)
|
||||||
|
|
||||||
|
(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."
|
||||||
|
[f ind t]
|
||||||
|
(def res @[])
|
||||||
|
(for [i 0 (length ind)]
|
||||||
|
(array-push res (f (get ind i))))
|
||||||
|
(if (= :tuple (type (or t ind)))
|
||||||
|
(apply1 tuple res)
|
||||||
|
res))
|
||||||
|
|
||||||
|
(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))
|
||||||
|
|
||||||
|
(defn zip
|
||||||
|
[& seqs]
|
||||||
|
(def lens (length seqs))
|
||||||
|
(def ret @[])
|
||||||
|
(if (= 0 lens) (error "expected at least 1 sequence"))
|
||||||
|
(var minlen (length (get seqs 0)))
|
||||||
|
(for [j 1 lens]
|
||||||
|
(def sl (length (get seqs j)))
|
||||||
|
(if (< sl minlen) (:= minlen sl)))
|
||||||
|
(for [i 0 minlen]
|
||||||
|
(def accum @[])
|
||||||
|
(for [j 0 lens]
|
||||||
|
(array-push accum (get seqs j i)))
|
||||||
|
(array-push ret (apply1 tuple accum)))
|
||||||
|
(apply1 tuple ret))
|
||||||
|
|
||||||
(defn juxt*
|
(defn juxt*
|
||||||
[& funs]
|
[& funs]
|
||||||
(def len (length funs))
|
(def len (length funs))
|
||||||
@ -344,6 +455,23 @@ If no match is found, returns nil"
|
|||||||
(apply1 tuple parts))
|
(apply1 tuple parts))
|
||||||
(reduce fop x forms))
|
(reduce fop x forms))
|
||||||
|
|
||||||
|
(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)
|
||||||
|
|
||||||
(defn reverse-array
|
(defn reverse-array
|
||||||
"Reverses the order of the elements in a given array or tuple and returns a new array."
|
"Reverses the order of the elements in a given array or tuple and returns a new array."
|
||||||
[t]
|
[t]
|
||||||
@ -362,74 +490,23 @@ If no match is found, returns nil"
|
|||||||
(defn reverse
|
(defn reverse
|
||||||
"Reverses order of elements in a given array or tuple"
|
"Reverses order of elements in a given array or tuple"
|
||||||
[t]
|
[t]
|
||||||
(select (type t)
|
(switch (type t)
|
||||||
:tuple (reverse-tuple t)
|
:tuple (reverse-tuple t)
|
||||||
:array (reverse-array t)))
|
:array (reverse-array t)))
|
||||||
|
|
||||||
(defmacro if-not
|
|
||||||
"Sorthand for (if (not ... "
|
|
||||||
[condition exp-1 exp-2]
|
|
||||||
(tuple 'if (tuple not condition)
|
|
||||||
exp-1
|
|
||||||
exp-2))
|
|
||||||
|
|
||||||
(defmacro when-not
|
|
||||||
"Sorthand for (when (not ... "
|
|
||||||
[condition exp-1]
|
|
||||||
(tuple 'when (tuple not condition) exp-1))
|
|
||||||
|
|
||||||
(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 then else]
|
|
||||||
(tuple 'let bindings
|
|
||||||
(tuple 'if (tuple 'and (tuple 'get bindings 1)
|
|
||||||
(tuple 'if
|
|
||||||
(tuple '> (tuple 'length bindings) 2)
|
|
||||||
(tuple 'get bindings 3) 'true))
|
|
||||||
then
|
|
||||||
else)))
|
|
||||||
|
|
||||||
(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 'let bindings
|
|
||||||
(tuple
|
|
||||||
'when
|
|
||||||
(tuple 'and (tuple 'get bindings 1)
|
|
||||||
(tuple 'if
|
|
||||||
(tuple '> (tuple 'length bindings) 2)
|
|
||||||
(tuple 'get bindings 3) 'true))
|
|
||||||
(apply1 tuple (array-concat @['do] body)))))
|
|
||||||
|
|
||||||
(defn comp
|
|
||||||
"Takes multiple functions and returns a function that is the composition
|
|
||||||
of those functions."
|
|
||||||
[& functions]
|
|
||||||
(select (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 zipcoll
|
(defn zipcoll
|
||||||
"Creates an table or tuple from two arrays/tuples. If a third argument of
|
"Creates an table or tuple from two arrays/tuples. If a third argument of
|
||||||
:struct is givent resault is struct else is table."
|
:struct is given result is struct else is table."
|
||||||
[coll-1 coll-2 the-type]
|
[keys vals t]
|
||||||
(var zipping-table @{})
|
(def res @{})
|
||||||
(def {:more more1 :next next1} (iter coll-1))
|
(def lk (length keys))
|
||||||
(def {:more more2 :next next2} (iter coll-2))
|
(def lv (length vals))
|
||||||
(while (and (more1) (more2))
|
(def len (if (< lk lv) lk lv))
|
||||||
(put zipping-table (next1) (next2)))
|
(for [i 0 len]
|
||||||
(if (struct? the-type)
|
(put res (get keys i) (get vals i)))
|
||||||
(table-to-struct zipping-table)
|
(if (= :struct t)
|
||||||
zipping-table))
|
(table-to-struct res)
|
||||||
|
res))
|
||||||
|
|
||||||
(defn update
|
(defn update
|
||||||
"Accepts a key argument and passes its' associated value to a function.
|
"Accepts a key argument and passes its' associated value to a function.
|
||||||
@ -453,8 +530,16 @@ If no match is found, returns nil"
|
|||||||
(:= key (next c key))))
|
(:= key (next c key))))
|
||||||
(if (table? (get colls 0)) container (table-to-struct container)))
|
(if (table? (get colls 0)) container (table-to-struct container)))
|
||||||
|
|
||||||
# Start pretty printer
|
###
|
||||||
(defn pp [x]
|
###
|
||||||
|
### 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]
|
||||||
|
|
||||||
(def buf @"")
|
(def buf @"")
|
||||||
(def indent @"\n")
|
(def indent @"\n")
|
||||||
@ -537,12 +622,12 @@ If no match is found, returns nil"
|
|||||||
(buffer-push-string buf "\n")
|
(buffer-push-string buf "\n")
|
||||||
|
|
||||||
(file-write stdout buf))
|
(file-write stdout buf))
|
||||||
# End pretty printer
|
|
||||||
|
|
||||||
(defn unique [s]
|
###
|
||||||
(def tab @{})
|
###
|
||||||
(foreach s (fn [x] (put tab x true)))
|
### Macro Expansion
|
||||||
(keys tab))
|
###
|
||||||
|
###
|
||||||
|
|
||||||
(defn macroexpand1
|
(defn macroexpand1
|
||||||
"Expand macros in a form, but do not recursively expand macros."
|
"Expand macros in a form, but do not recursively expand macros."
|
||||||
@ -608,7 +693,7 @@ If no match is found, returns nil"
|
|||||||
(if (= (table-to-struct res) (table-to-struct t)) t res))
|
(if (= (table-to-struct res) (table-to-struct t)) t res))
|
||||||
|
|
||||||
(def ux (ast-unwrap1 x))
|
(def ux (ast-unwrap1 x))
|
||||||
(select (type ux)
|
(switch (type ux)
|
||||||
:tuple (dotup ux)
|
:tuple (dotup ux)
|
||||||
:array (doarray* ux)
|
:array (doarray* ux)
|
||||||
:struct (table-to-struct (dotable ux))
|
:struct (table-to-struct (dotable ux))
|
||||||
@ -628,18 +713,13 @@ If no match is found, returns nil"
|
|||||||
(:= current (macroexpand1 current)))
|
(:= current (macroexpand1 current)))
|
||||||
current)
|
current)
|
||||||
|
|
||||||
(defmacro coro
|
|
||||||
"A wrapper for making fibers. Same as (fiber (fn [] ...body))."
|
|
||||||
[& body]
|
|
||||||
(tuple fiber (apply tuple 'fn [] body)))
|
|
||||||
|
|
||||||
(defn make-env [parent]
|
(defn make-env [parent]
|
||||||
(def parent (if parent parent _env))
|
(def parent (if parent parent _env))
|
||||||
(def newenv (setproto @{} parent))
|
(def newenv (setproto @{} parent))
|
||||||
(put newenv '_env @{:value newenv :private true})
|
(put newenv '_env @{:value newenv :private true})
|
||||||
newenv)
|
newenv)
|
||||||
|
|
||||||
(def run-context
|
(defn run-context
|
||||||
"Run a context. This evaluates expressions of dst in an environment,
|
"Run a context. This evaluates expressions of dst in an environment,
|
||||||
and is encapsulates the parsing, compilation, and evaluation of dst.
|
and is encapsulates the parsing, compilation, and evaluation of dst.
|
||||||
env is the environment to evaluate the code in, chunks is a function
|
env is the environment to evaluate the code in, chunks is a function
|
||||||
@ -651,11 +731,13 @@ respectively.
|
|||||||
This function can be used to implemement a repl very easily, simply
|
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
|
pass a function that reads line from stdin to chunks, and print to
|
||||||
onvalue."
|
onvalue."
|
||||||
(do
|
[env chunks onvalue onerr]
|
||||||
(defn val-stream [chunks onerr]
|
|
||||||
|
# Are we done yet?
|
||||||
(var going true)
|
(var going true)
|
||||||
# Stream of characters
|
|
||||||
(def chars (fiber (fn []
|
# Fiber stream of characters
|
||||||
|
(def chars (coro
|
||||||
(def buf @"")
|
(def buf @"")
|
||||||
(var len 1)
|
(var len 1)
|
||||||
(while (< 0 len)
|
(while (< 0 len)
|
||||||
@ -664,34 +746,24 @@ onvalue."
|
|||||||
(:= len (length buf))
|
(:= len (length buf))
|
||||||
(for [i 0 len]
|
(for [i 0 len]
|
||||||
(yield (get buf i))))
|
(yield (get buf i))))
|
||||||
0)))
|
0))
|
||||||
(var temp nil)
|
|
||||||
(var tempval nil)
|
# Fiber stream of values
|
||||||
# Stream of values
|
(def vals (coro
|
||||||
(def f (coro
|
|
||||||
(def p (parser 1))
|
(def p (parser 1))
|
||||||
(while going
|
(while going
|
||||||
(select (parser-status p)
|
(switch (parser-status p)
|
||||||
:full (yield (parser-produce p))
|
:full (yield (parser-produce p))
|
||||||
:error (onerr "parse" (parser-error p))
|
:error (onerr "parse" (parser-error p))
|
||||||
(select (fiber-status chars)
|
(switch (fiber-status chars)
|
||||||
:new (parser-byte p (resume chars))
|
:new (parser-byte p (resume chars))
|
||||||
:pending (parser-byte p (resume chars))
|
:pending (parser-byte p (resume chars))
|
||||||
(:= going false))))
|
(:= going false))))
|
||||||
(when (not= :root (parser-status p))
|
(when (not= :root (parser-status p))
|
||||||
(onerr "parse" "unexpected end of source"))
|
(onerr "parse" "unexpected end of source"))))
|
||||||
nil))
|
|
||||||
(defn more [] (if temp true
|
# Evaluate 1 source form
|
||||||
(do
|
(defn eval1 [source]
|
||||||
(:= temp true)
|
|
||||||
(:= tempval (resume f))
|
|
||||||
going)))
|
|
||||||
(defn next [] (if temp
|
|
||||||
(do (:= temp nil) tempval)
|
|
||||||
(resume f)))
|
|
||||||
{:more more :next next})
|
|
||||||
(fn [env chunks onvalue onerr]
|
|
||||||
(defn doone [source]
|
|
||||||
(var good true)
|
(var good true)
|
||||||
(def f (coro
|
(def f (coro
|
||||||
(def res (compile source env))
|
(def res (compile source env))
|
||||||
@ -701,15 +773,18 @@ onvalue."
|
|||||||
(:= good false)
|
(:= good false)
|
||||||
(onerr "compile" (get res :error))))))
|
(onerr "compile" (get res :error))))))
|
||||||
(def res (resume f))
|
(def res (resume f))
|
||||||
(if good
|
(if good
|
||||||
(if (= (fiber-status f) :error)
|
(if (= (fiber-status f) :error)
|
||||||
(onerr "runtime" res f)
|
(onerr "runtime" res f)
|
||||||
(onvalue res))))
|
(onvalue res))))
|
||||||
|
|
||||||
|
# Run loop
|
||||||
(def oldenv *env*)
|
(def oldenv *env*)
|
||||||
(:= *env* env)
|
(:= *env* env)
|
||||||
(foreach (val-stream chunks onerr) doone)
|
(while going (eval1 (resume vals)))
|
||||||
(:= *env* oldenv)
|
(:= *env* oldenv)
|
||||||
env)))
|
|
||||||
|
env)
|
||||||
|
|
||||||
(defn default-error-handler
|
(defn default-error-handler
|
||||||
[t x f]
|
[t x f]
|
||||||
@ -757,14 +832,21 @@ onvalue."
|
|||||||
(def {
|
(def {
|
||||||
:prefix prefix
|
:prefix prefix
|
||||||
} (apply1 table args))
|
} (apply1 table args))
|
||||||
(foreach (pairs newenv) (fn [[k v]]
|
(var k (next newenv nil))
|
||||||
|
(def prefix (if prefix prefix ""))
|
||||||
|
(while k
|
||||||
|
(def v (get newenv k))
|
||||||
(when (not (get v :private))
|
(when (not (get v :private))
|
||||||
(put env (symbol (if prefix prefix "") k) v)))))
|
(put env (symbol prefix k) v))
|
||||||
|
(:= k (next newenv k)))
|
||||||
|
env)
|
||||||
|
|
||||||
(defmacro import [path & args]
|
(defmacro import [path & args]
|
||||||
(apply tuple import* '_env path args))
|
(apply tuple import* '_env path args))
|
||||||
|
|
||||||
(defn repl [getchunk]
|
(defn repl [getchunk]
|
||||||
|
"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."
|
||||||
(def newenv (make-env))
|
(def newenv (make-env))
|
||||||
(defn chunks [buf]
|
(defn chunks [buf]
|
||||||
(file-write stdout "> ")
|
(file-write stdout "> ")
|
||||||
|
@ -830,10 +830,6 @@ recur:
|
|||||||
x = dst_resume(f, dst_tuple_length(tup) - 1, tup + 1);
|
x = dst_resume(f, dst_tuple_length(tup) - 1, tup + 1);
|
||||||
dst_gcunlock(lock);
|
dst_gcunlock(lock);
|
||||||
if (f->status == DST_FIBER_ERROR || f->status == DST_FIBER_DEBUG) {
|
if (f->status == DST_FIBER_ERROR || f->status == DST_FIBER_DEBUG) {
|
||||||
dst_puts(dst_unwrap_string(x));
|
|
||||||
printf("\n");
|
|
||||||
dst_puts(dst_unwrap_string(headval));
|
|
||||||
printf("\n");
|
|
||||||
dstc_cerror(c, ast, "error in macro expansion");
|
dstc_cerror(c, ast, "error in macro expansion");
|
||||||
}
|
}
|
||||||
/* Tail recur on the value */
|
/* Tail recur on the value */
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
(var *should-repl* false)
|
(do
|
||||||
(var *no-file* true)
|
|
||||||
|
(var *should-repl* :private false)
|
||||||
|
(var *no-file* :private true)
|
||||||
|
|
||||||
# Flag handlers
|
# Flag handlers
|
||||||
(def handlers {
|
(def handlers :private {
|
||||||
"h" (fn []
|
"h" (fn []
|
||||||
(print "usage: " (get args 0) " [options] scripts...")
|
(print "usage: " (get args 0) " [options] scripts...")
|
||||||
(print "Options are:")
|
(print "Options are:")
|
||||||
@ -14,13 +16,12 @@
|
|||||||
"r" (fn [] (:= *should-repl* true))
|
"r" (fn [] (:= *should-repl* true))
|
||||||
})
|
})
|
||||||
|
|
||||||
(defn dohandler [n]
|
(defn- dohandler [n]
|
||||||
(def h (get handlers n))
|
(def h (get handlers n))
|
||||||
(if h (h) (print "unknown flag -" n)))
|
(if h (h) (print "unknown flag -" n)))
|
||||||
|
|
||||||
# Process arguments
|
# Process arguments
|
||||||
(def nargs (length args))
|
(for [i 1 (length args)]
|
||||||
(for [i 1 nargs]
|
|
||||||
(def arg (get args i))
|
(def arg (get args i))
|
||||||
(if (= "-" (string-slice arg 0 1))
|
(if (= "-" (string-slice arg 0 1))
|
||||||
(dohandler (string-slice arg 1 2))
|
(dohandler (string-slice arg 1 2))
|
||||||
@ -31,3 +32,5 @@
|
|||||||
(when (or *should-repl* *no-file*)
|
(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"))
|
||||||
(repl getline))
|
(repl getline))
|
||||||
|
|
||||||
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user