Add RNG functionality to the math/ module.

The new RNG wraps up state for random number generation, so
one can have many rngs and even marshal and unmarshal them.
Adds math/rng, math/rng-uniform, and math/rng-int.

Also introduce `in` and change semantics for
indexing out of range. This commit enforces stricter
invariants on keys when indexing via a function call
on the data structure, or the new `in` function.

The `get` function is now more lax about keys, and will
not throw an error when a bad key is used for a data structure, instead
returning the default value.
This commit is contained in:
Calvin Rose 2019-11-08 17:35:27 -06:00
parent 58e3e63a89
commit aee1687215
12 changed files with 384 additions and 176 deletions

View File

@ -2,6 +2,13 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## Unreleased ## Unreleased
- Add `math/rng`, `math/rng-int`, and `math/rng-uniform`.
- Add `in` function to index in a stricter manner. Opposingly, `get` will
now not throw errors on bad keys.
- Indexed types and byte sequences will now error when indexed out of range or
with bad keys.
- Add rng functions to Janet. This also replaces the RNG behind `math/random`
and `math/seedrandom` with a consistent, platform independent RNG.
- Add `with-vars` macro. - Add `with-vars` macro.
- Add the `quickbin` command to jpm. - Add the `quickbin` command to jpm.
- Create shell.c when making the amlagamated source. This can be compiled with - Create shell.c when making the amlagamated source. This can be compiled with

View File

@ -25,14 +25,14 @@
(array/push modifiers ith)) (array/push modifiers ith))
(if (< i len) (recur (+ i 1))))))) (if (< i len) (recur (+ i 1)))))))
(def start (fstart 0)) (def start (fstart 0))
(def args (get more start)) (def args (in more start))
# Add function signature to docstring # Add function signature to docstring
(var index 0) (var index 0)
(def arglen (length args)) (def arglen (length args))
(def buf (buffer "(" name)) (def buf (buffer "(" name))
(while (< index arglen) (while (< index arglen)
(buffer/push-string buf " ") (buffer/push-string buf " ")
(buffer/format buf "%p" (get args index)) (buffer/format buf "%p" (in args index))
(set index (+ index 1))) (set index (+ index 1)))
(array/push modifiers (string buf ")\n\n" docstr)) (array/push modifiers (string buf ")\n\n" docstr))
# Build return value # Build return value
@ -116,7 +116,7 @@
:table true :table true
:buffer true :buffer true
:struct true}) :struct true})
(fn idempotent? [x] (not (get non-atomic-types (type x)))))) (fn idempotent? [x] (not (in non-atomic-types (type x))))))
# C style macros and functions for imperative sugar. No bitwise though. # C style macros and functions for imperative sugar. No bitwise though.
(defn inc "Returns x + 1." [x] (+ x 1)) (defn inc "Returns x + 1." [x] (+ x 1))
@ -163,9 +163,9 @@
(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) (in pairs i)
(tuple 'if (get pairs i) (tuple 'if (in pairs i)
(get pairs (+ i 1)) (in pairs (+ i 1))
(aux (+ i 2)))))) (aux (+ i 2))))))
(aux 0)) (aux 0))
@ -179,9 +179,9 @@
(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) (in pairs i)
(tuple 'if (tuple = sym (get pairs i)) (tuple 'if (tuple = sym (in pairs i))
(get pairs (+ i 1)) (in pairs (+ i 1))
(aux (+ i 2)))))) (aux (+ i 2))))))
(if atm (if atm
(aux 0) (aux 0)
@ -231,8 +231,8 @@
(while (> i 0) (while (> i 0)
(-- i) (-- i)
(set ret (if (= ret true) (set ret (if (= ret true)
(get forms i) (in forms i)
(tuple 'if (get forms i) ret)))) (tuple 'if (in forms i) ret))))
ret) ret)
(defmacro or (defmacro or
@ -244,7 +244,7 @@
(var i len) (var i len)
(while (> i 0) (while (> i 0)
(-- i) (-- i)
(def fi (get forms i)) (def fi (in forms i))
(set ret (if (idempotent? fi) (set ret (if (idempotent? fi)
(tuple 'if fi fi ret) (tuple 'if fi fi ret)
(do (do
@ -260,7 +260,7 @@
(def len (length syms)) (def len (length syms))
(def accum @[]) (def accum @[])
(while (< i len) (while (< i len)
(array/push accum (get syms i) [gensym]) (array/push accum (in syms i) [gensym])
(++ i)) (++ i))
~(let (,;accum) ,;body)) ~(let (,;accum) ,;body))
@ -299,7 +299,7 @@
,(unless (= ds in) ~(def ,ds ,in)) ,(unless (= ds in) ~(def ,ds ,in))
(def ,len (,length ,ds)) (def ,len (,length ,ds))
(while (,< ,i ,len) (while (,< ,i ,len)
(def ,binding (get ,ds ,i)) (def ,binding (in ,ds ,i))
,;body ,;body
(++ ,i))))) (++ ,i)))))
@ -311,7 +311,7 @@
,(unless (= ds in) ~(def ,ds ,in)) ,(unless (= ds in) ~(def ,ds ,in))
(var ,k (,next ,ds nil)) (var ,k (,next ,ds nil))
(while ,k (while ,k
(def ,binding ,(if pair? ~(tuple ,k (get ,ds ,k)) k)) (def ,binding ,(if pair? ~(tuple ,k (in ,ds ,k)) k))
,;body ,;body
(set ,k (,next ,ds ,k)))))) (set ,k (,next ,ds ,k))))))
@ -327,48 +327,47 @@
(defn- loop1 (defn- loop1
[body head i] [body head i]
# Terminate recursion
(when (<= (length head) i)
(break ~(do ,;body)))
(def {i binding (def {i binding
(+ i 1) verb (+ i 1) verb} head)
(+ i 2) object} head)
(cond # 2 term expression
(when (keyword? binding)
(break
(let [rest (loop1 body head (+ i 2))]
(case binding
:until ~(do (if ,verb (break) nil) ,rest)
:while ~(do (if ,verb nil (break)) ,rest)
:let ~(let ,verb (do ,rest))
:after ~(do ,rest ,verb nil)
:before ~(do ,verb ,rest nil)
:repeat (with-syms [iter]
~(do (var ,iter ,verb) (while (> ,iter 0) ,rest (-- ,iter))))
:when ~(when ,verb ,rest)
(error (string "unexpected loop modifier " binding))))))
# Terminate recursion # 3 term expression
(<= (length head) i) (def {(+ i 2) object} head)
~(do ,;body) (let [rest (loop1 body head (+ i 3))]
(case verb
# 2 term expression :range (let [[start stop step] object]
(keyword? binding) (for-template binding start stop (or step 1) < + [rest]))
(let [rest (loop1 body head (+ i 2))] :keys (keys-template binding object false [rest])
(case binding :pairs (keys-template binding object true [rest])
:until ~(do (if ,verb (break) nil) ,rest) :down (let [[start stop step] object]
:while ~(do (if ,verb nil (break)) ,rest) (for-template binding start stop (or step 1) > - [rest]))
:let ~(let ,verb (do ,rest)) :in (each-template binding object [rest])
:after ~(do ,rest ,verb nil) :iterate (iterate-template binding object rest)
:before ~(do ,verb ,rest nil) :generate (with-syms [f s]
:repeat (with-syms [iter] ~(let [,f ,object]
~(do (var ,iter ,verb) (while (> ,iter 0) ,rest (-- ,iter)))) (while true
:when ~(when ,verb ,rest) (def ,binding (,resume ,f))
(error (string "unexpected loop modifier " binding)))) (if (= :dead (,fiber/status ,f)) (break))
,rest)))
# 3 term expression (error (string "unexpected loop verb " verb)))))
(let [rest (loop1 body head (+ i 3))]
(case verb
:range (let [[start stop step] object]
(for-template binding start stop (or step 1) < + [rest]))
:keys (keys-template binding object false [rest])
:pairs (keys-template binding object true [rest])
:down (let [[start stop step] object]
(for-template binding start stop (or step 1) > - [rest]))
:in (each-template binding object [rest])
:iterate (iterate-template binding object rest)
:generate (with-syms [f s]
~(let [,f ,object]
(while true
(def ,binding (,resume ,f))
(if (= :dead (,fiber/status ,f)) (break))
,rest)))
(error (string "unexpected loop verb " verb))))))
(defmacro for (defmacro for
"Do a c style for loop for side effects. Returns nil." "Do a c style for loop for side effects. Returns nil."
@ -466,11 +465,11 @@
(if (zero? len) (error "expected at least 1 binding")) (if (zero? len) (error "expected at least 1 binding"))
(if (odd? len) (error "expected an even number of bindings")) (if (odd? len) (error "expected an even number of bindings"))
(defn aux [i] (defn aux [i]
(def bl (get bindings i))
(def br (get bindings (+ 1 i)))
(if (>= i len) (if (>= i len)
tru tru
(do (do
(def bl (in bindings i))
(def br (in bindings (+ 1 i)))
(def atm (idempotent? bl)) (def atm (idempotent? bl))
(def sym (if atm bl (gensym))) (def sym (if atm bl (gensym)))
(if atm (if atm
@ -499,7 +498,7 @@
[& functions] [& functions]
(case (length functions) (case (length functions)
0 nil 0 nil
1 (get functions 0) 1 (in functions 0)
2 (let [[f g] functions] (fn [& x] (f (g ;x)))) 2 (let [[f g] functions] (fn [& x] (f (g ;x))))
3 (let [[f g h] functions] (fn [& x] (f (g (h ;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)))))) 4 (let [[f g h i] functions] (fn [& x] (f (g (h (i ;x))))))
@ -547,12 +546,12 @@
(defn first (defn first
"Get the first element from an indexed data structure." "Get the first element from an indexed data structure."
[xs] [xs]
(get xs 0)) (in xs 0))
(defn last (defn last
"Get the last element from an indexed data structure." "Get the last element from an indexed data structure."
[xs] [xs]
(get xs (- (length xs) 1))) (in xs (- (length xs) 1)))
### ###
### ###
@ -566,16 +565,16 @@
(defn part (defn part
[a lo hi by] [a lo hi by]
(def pivot (get a hi)) (def pivot (in a hi))
(var i lo) (var i lo)
(for j lo hi (for j lo hi
(def aj (get a j)) (def aj (in a j))
(when (by aj pivot) (when (by aj pivot)
(def ai (get a i)) (def ai (in a i))
(set (a i) aj) (set (a i) aj)
(set (a j) ai) (set (a j) ai)
(++ i))) (++ i)))
(set (a hi) (get a i)) (set (a hi) (in a i))
(set (a i) pivot) (set (a i) pivot)
i) i)
@ -609,20 +608,20 @@
[f & inds] [f & inds]
(def ninds (length inds)) (def ninds (length inds))
(if (= 0 ninds) (error "expected at least 1 indexed collection")) (if (= 0 ninds) (error "expected at least 1 indexed collection"))
(var limit (length (get inds 0))) (var limit (length (in inds 0)))
(for i 0 ninds (for i 0 ninds
(def l (length (get inds i))) (def l (length (in inds i)))
(if (< l limit) (set limit l))) (if (< l limit) (set limit l)))
(def [i1 i2 i3 i4] inds) (def [i1 i2 i3 i4] inds)
(def res (array/new limit)) (def res (array/new limit))
(case ninds (case ninds
1 (for i 0 limit (set (res i) (f (get i1 i)))) 1 (for i 0 limit (set (res i) (f (in i1 i))))
2 (for i 0 limit (set (res i) (f (get i1 i) (get i2 i)))) 2 (for i 0 limit (set (res i) (f (in i1 i) (in i2 i))))
3 (for i 0 limit (set (res i) (f (get i1 i) (get i2 i) (get i3 i)))) 3 (for i 0 limit (set (res i) (f (in i1 i) (in i2 i) (in i3 i))))
4 (for i 0 limit (set (res i) (f (get i1 i) (get i2 i) (get i3 i) (get i4 i)))) 4 (for i 0 limit (set (res i) (f (in i1 i) (in i2 i) (in i3 i) (in i4 i))))
(for i 0 limit (for i 0 limit
(def args (array/new ninds)) (def args (array/new ninds))
(for j 0 ninds (set (args j) (get (get inds j) i))) (for j 0 ninds (set (args j) (in (in inds j) i)))
(set (res i) (f ;args)))) (set (res i) (f ;args))))
res) res)
@ -695,7 +694,7 @@
(var i 0) (var i 0)
(var going true) (var going true)
(while (if (< i len) going) (while (if (< i len) going)
(def item (get ind i)) (def item (in ind i))
(if (pred item) (set going false) (++ i))) (if (pred item) (set going false) (++ i)))
(if going nil i)) (if going nil i))
@ -705,7 +704,7 @@
and a not found. Consider find-index if this is an issue." and a not found. Consider find-index if this is an issue."
[pred ind] [pred ind]
(def i (find-index pred ind)) (def i (find-index pred ind))
(if (= i nil) nil (get ind i))) (if (= i nil) nil (in ind i)))
(defn take (defn take
"Take first n elements in an indexed type. Returns new indexed instance." "Take first n elements in an indexed type. Returns new indexed instance."
@ -783,7 +782,7 @@
[x & forms] [x & forms]
(defn fop [last n] (defn fop [last n]
(def [h t] (if (= :tuple (type n)) (def [h t] (if (= :tuple (type n))
(tuple (get n 0) (array/slice n 1)) (tuple (in n 0) (array/slice n 1))
(tuple n @[]))) (tuple n @[])))
(def parts (array/concat @[h last] t)) (def parts (array/concat @[h last] t))
(tuple/slice parts 0)) (tuple/slice parts 0))
@ -796,7 +795,7 @@
[x & forms] [x & forms]
(defn fop [last n] (defn fop [last n]
(def [h t] (if (= :tuple (type n)) (def [h t] (if (= :tuple (type n))
(tuple (get n 0) (array/slice n 1)) (tuple (in n 0) (array/slice n 1))
(tuple n @[]))) (tuple n @[])))
(def parts (array/concat @[h] t @[last])) (def parts (array/concat @[h] t @[last]))
(tuple/slice parts 0)) (tuple/slice parts 0))
@ -811,7 +810,7 @@
[x & forms] [x & forms]
(defn fop [last n] (defn fop [last n]
(def [h t] (if (= :tuple (type n)) (def [h t] (if (= :tuple (type n))
(tuple (get n 0) (array/slice n 1)) (tuple (in n 0) (array/slice n 1))
(tuple n @[]))) (tuple n @[])))
(def sym (gensym)) (def sym (gensym))
(def parts (array/concat @[h sym] t)) (def parts (array/concat @[h sym] t))
@ -827,7 +826,7 @@
[x & forms] [x & forms]
(defn fop [last n] (defn fop [last n]
(def [h t] (if (= :tuple (type n)) (def [h t] (if (= :tuple (type n))
(tuple (get n 0) (array/slice n 1)) (tuple (in n 0) (array/slice n 1))
(tuple n @[]))) (tuple n @[])))
(def sym (gensym)) (def sym (gensym))
(def parts (array/concat @[h] t @[sym])) (def parts (array/concat @[h] t @[sym]))
@ -843,7 +842,7 @@
(defn walk-dict [f form] (defn walk-dict [f form]
(def ret @{}) (def ret @{})
(loop [k :keys form] (loop [k :keys form]
(put ret (f k) (f (get form k)))) (put ret (f k) (f (in form k))))
ret) ret)
(defn walk (defn walk
@ -950,7 +949,7 @@
(var n (- len 1)) (var n (- len 1))
(def reversed (array/new len)) (def reversed (array/new len))
(while (>= n 0) (while (>= n 0)
(array/push reversed (get t n)) (array/push reversed (in t n))
(-- n)) (-- n))
reversed) reversed)
@ -961,7 +960,7 @@
[ds] [ds]
(def ret @{}) (def ret @{})
(loop [k :keys ds] (loop [k :keys ds]
(put ret (get ds k) k)) (put ret (in ds k) k))
ret) ret)
(defn zipcoll (defn zipcoll
@ -973,7 +972,7 @@
(def lv (length vals)) (def lv (length vals))
(def len (if (< lk lv) lk lv)) (def len (if (< lk lv) lk lv))
(for i 0 len (for i 0 len
(put res (get keys i) (get vals i))) (put res (in keys i) (in vals i)))
res) res)
(defn get-in (defn get-in
@ -1043,7 +1042,7 @@
[tab & colls] [tab & colls]
(loop [c :in colls (loop [c :in colls
key :keys c] key :keys c]
(set (tab key) (get c key))) (set (tab key) (in c key)))
tab) tab)
(defn merge (defn merge
@ -1054,7 +1053,7 @@
(def container @{}) (def container @{})
(loop [c :in colls (loop [c :in colls
key :keys c] key :keys c]
(set (container key) (get c key))) (set (container key) (in c key)))
container) container)
(defn keys (defn keys
@ -1073,7 +1072,7 @@
(def arr (array/new (length x))) (def arr (array/new (length x)))
(var k (next x nil)) (var k (next x nil))
(while (not= nil k) (while (not= nil k)
(array/push arr (get x k)) (array/push arr (in x k))
(set k (next x k))) (set k (next x k)))
arr) arr)
@ -1083,7 +1082,7 @@
(def arr (array/new (length x))) (def arr (array/new (length x)))
(var k (next x nil)) (var k (next x nil))
(while (not= nil k) (while (not= nil k)
(array/push arr (tuple k (get x k))) (array/push arr (tuple k (in x k)))
(set k (next x k))) (set k (next x k)))
arr) arr)
@ -1092,7 +1091,7 @@
[ind] [ind]
(def freqs @{}) (def freqs @{})
(each x ind (each x ind
(def n (get freqs x)) (def n (in freqs x))
(set (freqs x) (if n (+ 1 n) 1))) (set (freqs x) (if n (+ 1 n) 1)))
freqs) freqs)
@ -1106,7 +1105,7 @@
(def len (min ;(map length cols))) (def len (min ;(map length cols)))
(loop [i :range [0 len] (loop [i :range [0 len]
ci :range [0 ncol]] ci :range [0 ncol]]
(array/push res (get (get cols ci) i)))) (array/push res (in (in cols ci) i))))
res) res)
(defn distinct (defn distinct
@ -1114,7 +1113,7 @@
[xs] [xs]
(def ret @[]) (def ret @[])
(def seen @{}) (def seen @{})
(each x xs (if (get seen x) nil (do (put seen x true) (array/push ret x)))) (each x xs (if (in seen x) nil (do (put seen x true) (array/push ret x))))
ret) ret)
(defn flatten-into (defn flatten-into
@ -1138,7 +1137,7 @@
like @[k v k v ...]. Returns a new array." like @[k v k v ...]. Returns a new array."
[dict] [dict]
(def ret (array/new (* 2 (length dict)))) (def ret (array/new (* 2 (length dict))))
(loop [k :keys dict] (array/push ret k (get dict k))) (loop [k :keys dict] (array/push ret k (in dict k)))
ret) ret)
(defn interpose (defn interpose
@ -1147,10 +1146,10 @@
[sep ind] [sep ind]
(def len (length ind)) (def len (length ind))
(def ret (array/new (- (* 2 len) 1))) (def ret (array/new (- (* 2 len) 1)))
(if (> len 0) (put ret 0 (get ind 0))) (if (> len 0) (put ret 0 (in ind 0)))
(var i 1) (var i 1)
(while (< i len) (while (< i len)
(array/push ret sep (get ind i)) (array/push ret sep (in ind i))
(++ i)) (++ i))
ret) ret)
@ -1233,7 +1232,7 @@
(cond (cond
(symbol? pattern) (symbol? pattern)
(if (get seen pattern) (if (in seen pattern)
~(if (= ,pattern ,expr) ,(onmatch) ,sentinel) ~(if (= ,pattern ,expr) ,(onmatch) ,sentinel)
(do (do
(put seen pattern true) (put seen pattern true)
@ -1244,7 +1243,7 @@
# Unification with external values # Unification with external values
~(if (= ,(pattern 1) ,expr) ,(onmatch) ,sentinel) ~(if (= ,(pattern 1) ,expr) ,(onmatch) ,sentinel)
(match-1 (match-1
(get pattern 0) expr (in pattern 0) expr
(fn [] (fn []
~(if (and ,;(tuple/slice pattern 1)) ,(onmatch) ,sentinel)) seen)) ~(if (and ,;(tuple/slice pattern 1)) ,(onmatch) ,sentinel)) seen))
@ -1259,7 +1258,7 @@
(++ i) (++ i)
(if (= i len) (if (= i len)
(onmatch) (onmatch)
(match-1 (get pattern i) (tuple get $arr i) aux seen)))) (match-1 (in pattern i) (tuple in $arr i) aux seen))))
,sentinel))) ,sentinel)))
(dictionary? pattern) (dictionary? pattern)
@ -1272,7 +1271,7 @@
(set key (next pattern key)) (set key (next pattern key))
(if (= key nil) (if (= key nil)
(onmatch) (onmatch)
(match-1 (get pattern key) (tuple get $dict key) aux seen)))) (match-1 (in pattern key) (tuple in $dict key) aux seen))))
,sentinel))) ,sentinel)))
:else ~(if (= ,pattern ,expr) ,(onmatch) ,sentinel))) :else ~(if (= ,pattern ,expr) ,(onmatch) ,sentinel)))
@ -1293,9 +1292,9 @@
(def len-1 (dec len)) (def len-1 (dec len))
((fn aux [i] ((fn aux [i]
(cond (cond
(= i len-1) (get cases i) (= i len-1) (in cases i)
(< i len-1) (with-syms [$res] (< i len-1) (with-syms [$res]
~(if (= ,sentinel (def ,$res ,(match-1 (get cases i) $x (fn [] (get cases (inc i))) @{}))) ~(if (= ,sentinel (def ,$res ,(match-1 (in cases i) $x (fn [] (in cases (inc i))) @{})))
,(aux (+ 2 i)) ,(aux (+ 2 i))
,$res)))) 0))) ,$res)))) 0)))
@ -1357,7 +1356,7 @@
(def bind-type (def bind-type
(string " " (string " "
(cond (cond
(x :ref) (string :var " (" (type (get (x :ref) 0)) ")") (x :ref) (string :var " (" (type (in (x :ref) 0)) ")")
(x :macro) :macro (x :macro) :macro
(type (x :value))) (type (x :value)))
"\n")) "\n"))
@ -1397,7 +1396,7 @@
(def newt @{}) (def newt @{})
(var key (next t nil)) (var key (next t nil))
(while (not= nil key) (while (not= nil key)
(put newt (recur key) (on-value (get t key))) (put newt (recur key) (on-value (in t key)))
(set key (next t key))) (set key (next t key)))
newt) newt)
@ -1410,24 +1409,24 @@
(recur x))) (recur x)))
(defn expanddef [t] (defn expanddef [t]
(def last (get t (- (length t) 1))) (def last (in t (- (length t) 1)))
(def bound (get t 1)) (def bound (in t 1))
(tuple/slice (tuple/slice
(array/concat (array/concat
@[(get t 0) (expand-bindings bound)] @[(in t 0) (expand-bindings bound)]
(tuple/slice t 2 -2) (tuple/slice t 2 -2)
@[(recur last)]))) @[(recur last)])))
(defn expandall [t] (defn expandall [t]
(def args (map recur (tuple/slice t 1))) (def args (map recur (tuple/slice t 1)))
(tuple (get t 0) ;args)) (tuple (in t 0) ;args))
(defn expandfn [t] (defn expandfn [t]
(def t1 (get t 1)) (def t1 (in t 1))
(if (symbol? t1) (if (symbol? t1)
(do (do
(def args (map recur (tuple/slice t 3))) (def args (map recur (tuple/slice t 3)))
(tuple 'fn t1 (get t 2) ;args)) (tuple 'fn t1 (in t 2) ;args))
(do (do
(def args (map recur (tuple/slice t 2))) (def args (map recur (tuple/slice t 2)))
(tuple 'fn t1 ;args)))) (tuple 'fn t1 ;args))))
@ -1436,15 +1435,15 @@
(defn qq [x] (defn qq [x]
(case (type x) (case (type x)
:tuple (do :tuple (do
(def x0 (get x 0)) (def x0 (in x 0))
(if (or (= 'unquote x0) (= 'unquote-splicing x0)) (if (or (= 'unquote x0) (= 'unquote-splicing x0))
(tuple x0 (recur (get x 1))) (tuple x0 (recur (in x 1)))
(tuple/slice (map qq x)))) (tuple/slice (map qq x))))
:array (map qq x) :array (map qq x)
:table (table (map qq (kvs x))) :table (table (map qq (kvs x)))
:struct (struct (map qq (kvs x))) :struct (struct (map qq (kvs x)))
x)) x))
(tuple (get t 0) (qq (get t 1)))) (tuple (in t 0) (qq (in t 1))))
(def specs (def specs
{'set expanddef {'set expanddef
@ -1458,8 +1457,8 @@
'while expandall}) 'while expandall})
(defn dotup [t] (defn dotup [t]
(def h (get t 0)) (def h (in t 0))
(def s (get specs h)) (def s (in specs h))
(def entry (or (dyn h) {})) (def entry (or (dyn h) {}))
(def m (entry :value)) (def m (entry :value))
(def m? (entry :macro)) (def m? (entry :macro))
@ -1956,7 +1955,7 @@
[path & args] [path & args]
(def [fullpath mod-kind] (module/find path)) (def [fullpath mod-kind] (module/find path))
(unless fullpath (error mod-kind)) (unless fullpath (error mod-kind))
(if-let [check (get module/cache fullpath)] (if-let [check (in module/cache fullpath)]
check check
(do (do
(def loader (module/loaders mod-kind)) (def loader (module/loaders mod-kind))
@ -2123,25 +2122,25 @@ _fiber is bound to the suspended fiber
"q" (fn [&] (set *quiet* true) 1) "q" (fn [&] (set *quiet* true) 1)
"k" (fn [&] (set *compile-only* true) (set *exit-on-error* false) 1) "k" (fn [&] (set *compile-only* true) (set *exit-on-error* false) 1)
"n" (fn [&] (set *colorize* false) 1) "n" (fn [&] (set *colorize* false) 1)
"m" (fn [i &] (setdyn :syspath (get args (+ i 1))) 2) "m" (fn [i &] (setdyn :syspath (in args (+ i 1))) 2)
"c" (fn [i &] "c" (fn [i &]
(def e (dofile (get args (+ i 1)))) (def e (dofile (in args (+ i 1))))
(spit (get args (+ i 2)) (make-image e)) (spit (in args (+ i 2)) (make-image e))
(set *no-file* false) (set *no-file* false)
3) 3)
"-" (fn [&] (set *handleopts* false) 1) "-" (fn [&] (set *handleopts* false) 1)
"l" (fn [i &] "l" (fn [i &]
(import* (get args (+ i 1)) (import* (in args (+ i 1))
:prefix "" :exit *exit-on-error*) :prefix "" :exit *exit-on-error*)
2) 2)
"e" (fn [i &] "e" (fn [i &]
(set *no-file* false) (set *no-file* false)
(eval-string (get args (+ i 1))) (eval-string (in args (+ i 1)))
2)}) 2)})
(defn- dohandler [n i &] (defn- dohandler [n i &]
(def h (get handlers n)) (def h (in handlers n))
(if h (h i) (do (print "unknown flag -" n) ((get handlers "h"))))) (if h (h i) (do (print "unknown flag -" n) ((in handlers "h")))))
(def- safe-forms {'defn true 'defn- true 'defmacro true 'defmacro- true}) (def- safe-forms {'defn true 'defn- true 'defmacro true 'defmacro- true})
(def- importers {'import true 'import* true 'use true 'dofile true 'require true}) (def- importers {'import true 'import* true 'use true 'dofile true 'require true})
@ -2162,7 +2161,7 @@ _fiber is bound to the suspended fiber
(var i 0) (var i 0)
(def lenargs (length args)) (def lenargs (length args))
(while (< i lenargs) (while (< i lenargs)
(def arg (get args i)) (def arg (in args i))
(if (and *handleopts* (= "-" (string/slice arg 0 1))) (if (and *handleopts* (= "-" (string/slice arg 0 1)))
(+= i (dohandler (string/slice arg 1 2) i)) (+= i (dohandler (string/slice arg 1 2) i))
(do (do

View File

@ -129,7 +129,7 @@ const char *janet_getcstring(const Janet *argv, int32_t n) {
int32_t janet_getinteger(const Janet *argv, int32_t n) { int32_t janet_getinteger(const Janet *argv, int32_t n) {
Janet x = argv[n]; Janet x = argv[n];
if (!janet_checkint(x)) { if (!janet_checkint(x)) {
janet_panicf("bad slot #%d, expected integer, got %v", n, x); janet_panicf("bad slot #%d, expected 32 bit signed integer, got %v", n, x);
} }
return janet_unwrap_integer(x); return janet_unwrap_integer(x);
} }
@ -137,7 +137,7 @@ int32_t janet_getinteger(const Janet *argv, int32_t n) {
int64_t janet_getinteger64(const Janet *argv, int32_t n) { int64_t janet_getinteger64(const Janet *argv, int32_t n) {
Janet x = argv[n]; Janet x = argv[n];
if (!janet_checkint64(x)) { if (!janet_checkint64(x)) {
janet_panicf("bad slot #%d, expected 64 bit integer, got %v", n, x); janet_panicf("bad slot #%d, expected 64 bit signed integer, got %v", n, x);
} }
return (int64_t) janet_unwrap_number(x); return (int64_t) janet_unwrap_number(x);
} }

View File

@ -104,7 +104,7 @@ static JanetSlot do_debug(JanetFopts opts, JanetSlot *args) {
janetc_emit(opts.compiler, JOP_SIGNAL | (2 << 24)); janetc_emit(opts.compiler, JOP_SIGNAL | (2 << 24));
return janetc_cslot(janet_wrap_nil()); return janetc_cslot(janet_wrap_nil());
} }
static JanetSlot do_get(JanetFopts opts, JanetSlot *args) { static JanetSlot do_in(JanetFopts opts, JanetSlot *args) {
return opreduce(opts, args, JOP_GET, janet_wrap_nil()); return opreduce(opts, args, JOP_GET, janet_wrap_nil());
} }
static JanetSlot do_put(JanetFopts opts, JanetSlot *args) { static JanetSlot do_put(JanetFopts opts, JanetSlot *args) {
@ -275,7 +275,7 @@ static const JanetFunOptimizer optimizers[] = {
{minarity2, do_apply}, {minarity2, do_apply},
{maxarity1, do_yield}, {maxarity1, do_yield},
{fixarity2, do_resume}, {fixarity2, do_resume},
{fixarity2, do_get}, {fixarity2, do_in},
{fixarity3, do_put}, {fixarity3, do_put},
{fixarity1, do_length}, {fixarity1, do_length},
{NULL, do_add}, {NULL, do_add},

View File

@ -34,7 +34,7 @@
#define JANET_FUN_APPLY 3 #define JANET_FUN_APPLY 3
#define JANET_FUN_YIELD 4 #define JANET_FUN_YIELD 4
#define JANET_FUN_RESUME 5 #define JANET_FUN_RESUME 5
#define JANET_FUN_GET 6 #define JANET_FUN_IN 6
#define JANET_FUN_PUT 7 #define JANET_FUN_PUT 7
#define JANET_FUN_LENGTH 8 #define JANET_FUN_LENGTH 8
#define JANET_FUN_ADD 9 #define JANET_FUN_ADD 9

View File

@ -262,6 +262,61 @@ static Janet janet_core_setdyn(int32_t argc, Janet *argv) {
return argv[1]; return argv[1];
} }
static Janet janet_core_get(int32_t argc, Janet *argv) {
janet_arity(argc, 2, 3);
Janet ds = argv[0];
Janet key = argv[1];
Janet dflt = argc == 3 ? argv[2] : janet_wrap_nil();
JanetType t = janet_type(argv[0]);
switch (t) {
default:
return dflt;
case JANET_STRING:
case JANET_SYMBOL:
case JANET_KEYWORD: {
if (!janet_checkint(key)) return dflt;
int32_t index = janet_unwrap_integer(key);
if (index < 0) return dflt;
const uint8_t *str = janet_unwrap_string(ds);
if (index >= janet_string_length(str)) return dflt;
return janet_wrap_integer(str[index]);
}
case JANET_ABSTRACT: {
void *abst = janet_unwrap_abstract(ds);
JanetAbstractType *type = (JanetAbstractType *)janet_abstract_type(abst);
if (!type->get) return dflt;
return (type->get)(abst, key);
}
case JANET_ARRAY:
case JANET_TUPLE: {
if (!janet_checkint(key)) return dflt;
int32_t index = janet_unwrap_integer(key);
if (index < 0) return dflt;
if (t == JANET_ARRAY) {
JanetArray *a = janet_unwrap_array(ds);
if (index >= a->count) return dflt;
return a->data[index];
} else {
const Janet *t = janet_unwrap_tuple(ds);
if (index >= janet_tuple_length(t)) return dflt;
return t[index];
}
}
case JANET_TABLE: {
JanetTable *flag = NULL;
Janet ret = janet_table_get_ex(janet_unwrap_table(ds), key, &flag);
if (flag == NULL) return dflt;
return ret;
}
case JANET_STRUCT: {
const JanetKV *st = janet_unwrap_struct(ds);
Janet ret = janet_struct_get(st, key);
if (janet_checktype(ret, JANET_NIL)) return dflt;
return ret;
}
}
}
static Janet janet_core_native(int32_t argc, Janet *argv) { static Janet janet_core_native(int32_t argc, Janet *argv) {
JanetModule init; JanetModule init;
janet_arity(argc, 1, 2); janet_arity(argc, 1, 2);
@ -685,6 +740,14 @@ static const JanetReg corelib_cfuns[] = {
JDOC("(slice x &opt start end)\n\n" JDOC("(slice x &opt start end)\n\n"
"Extract a sub-range of an indexed data strutrue or byte sequence.") "Extract a sub-range of an indexed data strutrue or byte sequence.")
}, },
{
"get", janet_core_get,
JDOC("(get ds key &opt dflt)\n\n"
"Get the value mapped to key in data structure ds, and return dflt or nil if not found. "
"Similar to get, but will not throw an error if the key is invalid for the data structure "
"unless the data structure is an abstract type. In that case, the abstract type getter may throw "
"an error.")
},
{NULL, NULL, NULL} {NULL, NULL, NULL}
}; };
@ -960,8 +1023,8 @@ JanetTable *janet_core_env(JanetTable *replacements) {
"will be returned to the last yield in the case of a pending fiber, or the argument to " "will be returned to the last yield in the case of a pending fiber, or the argument to "
"the dispatch function in the case of a new fiber. Returns either the return result of " "the dispatch function in the case of a new fiber. Returns either the return result of "
"the fiber's dispatch function, or the value from the next yield call in fiber.")); "the fiber's dispatch function, or the value from the next yield call in fiber."));
janet_quick_asm(env, JANET_FUN_GET, janet_quick_asm(env, JANET_FUN_IN,
"get", 3, 2, 3, 4, get_asm, sizeof(get_asm), "in", 3, 2, 3, 4, get_asm, sizeof(get_asm),
JDOC("(get ds key &opt dflt)\n\n" JDOC("(get ds key &opt dflt)\n\n"
"Get a value from any associative data structure. Arrays, tuples, tables, structs, strings, " "Get a value from any associative data structure. Arrays, tuples, tables, structs, strings, "
"symbols, and buffers are all associative and can be used with get. Order structures, name " "symbols, and buffers are all associative and can be used with get. Order structures, name "

View File

@ -27,19 +27,131 @@
#include "util.h" #include "util.h"
#endif #endif
static Janet janet_rng_get(void *p, Janet key);
static void janet_rng_marshal(void *p, JanetMarshalContext *ctx) {
JanetRNG *rng = (JanetRNG *)p;
janet_marshal_int(ctx, (int32_t) rng->a);
janet_marshal_int(ctx, (int32_t) rng->b);
janet_marshal_int(ctx, (int32_t) rng->c);
janet_marshal_int(ctx, (int32_t) rng->d);
janet_marshal_int(ctx, (int32_t) rng->counter);
}
static void janet_rng_unmarshal(void *p, JanetMarshalContext *ctx) {
JanetRNG *rng = (JanetRNG *)p;
rng->a = (uint32_t) janet_unmarshal_int(ctx);
rng->b = (uint32_t) janet_unmarshal_int(ctx);
rng->c = (uint32_t) janet_unmarshal_int(ctx);
rng->d = (uint32_t) janet_unmarshal_int(ctx);
rng->counter = (uint32_t) janet_unmarshal_int(ctx);
}
static JanetAbstractType JanetRNG_type = {
"core/rng",
NULL,
NULL,
janet_rng_get,
NULL,
janet_rng_marshal,
janet_rng_unmarshal,
NULL
};
static JANET_THREAD_LOCAL JanetRNG janet_vm_rng = {0};
JanetRNG *janet_default_rng(void) {
return &janet_vm_rng;
}
void janet_rng_seed(JanetRNG *rng, uint32_t seed) {
rng->a = seed + 123573u;
rng->b = (seed + 43234283u) % 12391233u;
rng->c = 0x17af0931u;
rng->d = 0xFFFaaFFFu;
rng->counter = 0u;
}
uint32_t janet_rng_u32(JanetRNG *rng) {
/* Algorithm "xorwow" from p. 5 of Marsaglia, "Xorshift RNGs" */
uint32_t t = rng->d;
uint32_t const s = rng->a;
rng->d = rng->c;
rng->c = rng->b;
rng->b = s;
t ^= t >> 2;
t ^= t << 1;
t ^= s ^ (s << 4);
rng->a = t;
rng->counter += 362437;
return t + rng->counter;
}
double janet_rng_double(JanetRNG *rng) {
uint32_t hi = janet_rng_u32(rng);
uint32_t lo = janet_rng_u32(rng);
uint64_t big = (uint64_t)(lo) | (((uint64_t) hi) << 32);
return ldexp((big >> (64 - 52)), -52);
}
static Janet cfun_rng_make(int32_t argc, Janet *argv) {
janet_arity(argc, 0, 1);
uint32_t seed = (uint32_t)(argc == 1 ? janet_getinteger(argv, 0) : 0);
JanetRNG *rng = janet_abstract(&JanetRNG_type, sizeof(JanetRNG));
janet_rng_seed(rng, seed);
return janet_wrap_abstract(rng);
}
static Janet cfun_rng_uniform(int32_t argc, Janet *argv) {
janet_fixarity(argc, 1);
JanetRNG *rng = janet_getabstract(argv, 0, &JanetRNG_type);
return janet_wrap_number(janet_rng_double(rng));
}
static Janet cfun_rng_int(int32_t argc, Janet *argv) {
janet_arity(argc, 1, 2);
JanetRNG *rng = janet_getabstract(argv, 0, &JanetRNG_type);
if (argc == 1) {
uint32_t word = janet_rng_u32(rng) >> 1;
return janet_wrap_integer(word);
} else {
int32_t max = janet_getinteger(argv, 1);
if (max <= 0) return janet_wrap_number(0);
uint32_t modulo = (uint32_t) max;
uint32_t bad = UINT32_MAX % modulo;
uint32_t word;
do {
word = janet_rng_u32(rng);
} while (word > UINT32_MAX - bad);
word >>= 1;
return janet_wrap_integer(word % modulo);
}
}
static const JanetMethod rng_methods[] = {
{"uniform", cfun_rng_uniform},
{"int", cfun_rng_int},
{NULL, NULL}
};
static Janet janet_rng_get(void *p, Janet key) {
(void) p;
if (!janet_checktype(key, JANET_KEYWORD)) janet_panicf("expected keyword method");
return janet_getmethod(janet_unwrap_keyword(key), rng_methods);
}
/* Get a random number */ /* Get a random number */
static Janet janet_rand(int32_t argc, Janet *argv) { static Janet janet_rand(int32_t argc, Janet *argv) {
(void) argv; (void) argv;
janet_fixarity(argc, 0); janet_fixarity(argc, 0);
double r = (rand() % RAND_MAX) / ((double) RAND_MAX); return janet_wrap_number(janet_rng_double(&janet_vm_rng));
return janet_wrap_number(r);
} }
/* Seed the random number generator */ /* Seed the random number generator */
static Janet janet_srand(int32_t argc, Janet *argv) { static Janet janet_srand(int32_t argc, Janet *argv) {
janet_fixarity(argc, 1); janet_fixarity(argc, 1);
int32_t x = janet_getinteger(argv, 0); int32_t x = janet_getinteger(argv, 0);
srand((unsigned) x); janet_rng_seed(&janet_vm_rng, (uint32_t) x);
return janet_wrap_nil(); return janet_wrap_nil();
} }
@ -108,7 +220,7 @@ static const JanetReg math_cfuns[] = {
{ {
"math/seedrandom", janet_srand, "math/seedrandom", janet_srand,
JDOC("(math/seedrandom seed)\n\n" JDOC("(math/seedrandom seed)\n\n"
"Set the seed for the random number generator. 'seed' should be an " "Set the seed for the random number generator. 'seed' should be "
"an integer.") "an integer.")
}, },
{ {
@ -201,6 +313,24 @@ static const JanetReg math_cfuns[] = {
JDOC("(math/atan2 y x)\n\n" JDOC("(math/atan2 y x)\n\n"
"Return the arctangent of y/x. Works even when x is 0.") "Return the arctangent of y/x. Works even when x is 0.")
}, },
{
"math/rng", cfun_rng_make,
JDOC("(math/rng &opt seed)\n\n"
"Creates a Psuedo-Random number generator, with an optional seed. "
"The seed should be an unsigned 32 bit integer. "
"Do not use this for cryptography. Returns a core/rng abstract type.")
},
{
"math/rng-uniform", cfun_rng_uniform,
JDOC("(math/rng-seed rng seed)\n\n"
"Extract a random number in the range [0, 1) from the RNG.")
},
{
"math/rng-int", cfun_rng_int,
JDOC("(math/rng-int rng &opt max)\n\n"
"Extract a random random integer in the range [0, max] from the RNG. If "
"no max is given, the default is 2^31 - 1.")
},
{NULL, NULL, NULL} {NULL, NULL, NULL}
}; };

View File

@ -55,8 +55,13 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
done = 1; done = 1;
} }
} else { } else {
janet_eprintf("compile error in %s: %s\n", sourcePath, if (cres.macrofiber) {
(const char *)cres.error); janet_eprintf("compile error in %s: ", sourcePath);
janet_stacktrace(cres.macrofiber, janet_wrap_string(cres.error));
} else {
janet_eprintf("compile error in %s: %s\n", sourcePath,
(const char *)cres.error);
}
errflags |= 0x02; errflags |= 0x02;
done = 1; done = 1;
} }

View File

@ -145,6 +145,16 @@ int janet_compare(Janet x, Janet y) {
return (janet_type(x) < janet_type(y)) ? -1 : 1; return (janet_type(x) < janet_type(y)) ? -1 : 1;
} }
static int32_t getter_checkint(Janet key, int32_t max) {
if (!janet_checkint(key)) goto bad;
int32_t ret = janet_unwrap_integer(key);
if (ret < 0) goto bad;
if (ret >= max) goto bad;
return ret;
bad:
janet_panicf("expected integer key in range [0, %d), got %v", max, key);
}
/* Gets a value and returns. Can panic. */ /* Gets a value and returns. Can panic. */
Janet janet_get(Janet ds, Janet key) { Janet janet_get(Janet ds, Janet key) {
Janet value; Janet value;
@ -160,56 +170,28 @@ Janet janet_get(Janet ds, Janet key) {
break; break;
case JANET_ARRAY: { case JANET_ARRAY: {
JanetArray *array = janet_unwrap_array(ds); JanetArray *array = janet_unwrap_array(ds);
int32_t index; int32_t index = getter_checkint(key, array->count);
if (!janet_checkint(key)) value = array->data[index];
janet_panic("expected integer key");
index = janet_unwrap_integer(key);
if (index < 0 || index >= array->count) {
value = janet_wrap_nil();
} else {
value = array->data[index];
}
break; break;
} }
case JANET_TUPLE: { case JANET_TUPLE: {
const Janet *tuple = janet_unwrap_tuple(ds); const Janet *tuple = janet_unwrap_tuple(ds);
int32_t index; int32_t len = janet_tuple_length(tuple);
if (!janet_checkint(key)) value = tuple[getter_checkint(key, len)];
janet_panic("expected integer key");
index = janet_unwrap_integer(key);
if (index < 0 || index >= janet_tuple_length(tuple)) {
value = janet_wrap_nil();
} else {
value = tuple[index];
}
break; break;
} }
case JANET_BUFFER: { case JANET_BUFFER: {
JanetBuffer *buffer = janet_unwrap_buffer(ds); JanetBuffer *buffer = janet_unwrap_buffer(ds);
int32_t index; int32_t index = getter_checkint(key, buffer->count);
if (!janet_checkint(key)) value = janet_wrap_integer(buffer->data[index]);
janet_panic("expected integer key");
index = janet_unwrap_integer(key);
if (index < 0 || index >= buffer->count) {
value = janet_wrap_nil();
} else {
value = janet_wrap_integer(buffer->data[index]);
}
break; break;
} }
case JANET_STRING: case JANET_STRING:
case JANET_SYMBOL: case JANET_SYMBOL:
case JANET_KEYWORD: { case JANET_KEYWORD: {
const uint8_t *str = janet_unwrap_string(ds); const uint8_t *str = janet_unwrap_string(ds);
int32_t index; int32_t index = getter_checkint(key, janet_string_length(str));
if (!janet_checkint(key)) value = janet_wrap_integer(str[index]);
janet_panic("expected integer key");
index = janet_unwrap_integer(key);
if (index < 0 || index >= janet_string_length(str)) {
value = janet_wrap_nil();
} else {
value = janet_wrap_integer(str[index]);
}
break; break;
} }
case JANET_ABSTRACT: { case JANET_ABSTRACT: {
@ -356,7 +338,7 @@ void janet_putindex(Janet ds, int32_t index, Janet value) {
janet_buffer_ensure(buffer, index + 1, 2); janet_buffer_ensure(buffer, index + 1, 2);
buffer->count = index + 1; buffer->count = index + 1;
} }
buffer->data[index] = janet_unwrap_integer(value); buffer->data[index] = (uint8_t)(janet_unwrap_integer(value) & 0xFF);
break; break;
} }
case JANET_TABLE: { case JANET_TABLE: {
@ -382,11 +364,8 @@ void janet_put(Janet ds, Janet key, Janet value) {
janet_panicf("expected %T, got %v", janet_panicf("expected %T, got %v",
JANET_TFLAG_ARRAY | JANET_TFLAG_BUFFER | JANET_TFLAG_TABLE, ds); JANET_TFLAG_ARRAY | JANET_TFLAG_BUFFER | JANET_TFLAG_TABLE, ds);
case JANET_ARRAY: { case JANET_ARRAY: {
int32_t index;
JanetArray *array = janet_unwrap_array(ds); JanetArray *array = janet_unwrap_array(ds);
if (!janet_checkint(key)) janet_panicf("expected integer key, got %v", key); int32_t index = getter_checkint(key, INT32_MAX - 1);
index = janet_unwrap_integer(key);
if (index < 0 || index == INT32_MAX) janet_panicf("bad integer key, got %v", key);
if (index >= array->count) { if (index >= array->count) {
janet_array_setcount(array, index + 1); janet_array_setcount(array, index + 1);
} }
@ -394,11 +373,8 @@ void janet_put(Janet ds, Janet key, Janet value) {
break; break;
} }
case JANET_BUFFER: { case JANET_BUFFER: {
int32_t index;
JanetBuffer *buffer = janet_unwrap_buffer(ds); JanetBuffer *buffer = janet_unwrap_buffer(ds);
if (!janet_checkint(key)) janet_panicf("expected integer key, got %v", key); int32_t index = getter_checkint(key, INT32_MAX - 1);
index = janet_unwrap_integer(key);
if (index < 0 || index == INT32_MAX) janet_panicf("bad integer key, got %v", key);
if (!janet_checkint(value)) if (!janet_checkint(value))
janet_panicf("can only put integers in buffers, got %v", value); janet_panicf("can only put integers in buffers, got %v", value);
if (index >= buffer->count) { if (index >= buffer->count) {

View File

@ -1171,6 +1171,8 @@ int janet_init(void) {
/* Initialize registry */ /* Initialize registry */
janet_vm_registry = janet_table(0); janet_vm_registry = janet_table(0);
janet_gcroot(janet_wrap_table(janet_vm_registry)); janet_gcroot(janet_wrap_table(janet_vm_registry));
/* Seed RNG */
janet_rng_seed(janet_default_rng(), 0);
return 0; return 0;
} }

View File

@ -316,6 +316,7 @@ typedef struct JanetView JanetView;
typedef struct JanetByteView JanetByteView; typedef struct JanetByteView JanetByteView;
typedef struct JanetDictView JanetDictView; typedef struct JanetDictView JanetDictView;
typedef struct JanetRange JanetRange; typedef struct JanetRange JanetRange;
typedef struct JanetRNG JanetRNG;
typedef Janet(*JanetCFunction)(int32_t argc, Janet *argv); typedef Janet(*JanetCFunction)(int32_t argc, Janet *argv);
/* Basic types for all Janet Values */ /* Basic types for all Janet Values */
@ -927,6 +928,11 @@ struct JanetRange {
int32_t end; int32_t end;
}; };
struct JanetRNG {
uint32_t a, b, c, d;
uint32_t counter;
};
/***** END SECTION TYPES *****/ /***** END SECTION TYPES *****/
/***** START SECTION OPCODES *****/ /***** START SECTION OPCODES *****/
@ -1103,6 +1109,11 @@ JANET_API void janet_debug_find(
JanetFuncDef **def_out, int32_t *pc_out, JanetFuncDef **def_out, int32_t *pc_out,
const uint8_t *source, int32_t line, int32_t column); const uint8_t *source, int32_t line, int32_t column);
/* RNG */
JANET_API JanetRNG *janet_default_rng(void);
JANET_API void janet_rng_seed(JanetRNG *rng, uint32_t seed);
JANET_API uint32_t janet_rng_u32(JanetRNG *rng);
/* Array functions */ /* Array functions */
JANET_API JanetArray *janet_array(int32_t capacity); JANET_API JanetArray *janet_array(int32_t capacity);
JANET_API JanetArray *janet_array_n(const Janet *elements, int32_t n); JANET_API JanetArray *janet_array_n(const Janet *elements, int32_t n);

View File

@ -193,4 +193,19 @@
# Trim empty string # Trim empty string
(assert (= "" (string/trim " ")) "string/trim regression") (assert (= "" (string/trim " ")) "string/trim regression")
# RNGs
(defn test-rng
[rng]
(assert (all identity (seq [i :range [0 1000]]
(<= (math/rng-int rng i) i))) "math/rng-int test")
(assert (all identity (seq [i :range [0 1000]]
(def x (math/rng-uniform rng))
(and (>= x 0) (< x 1))))
"math/rng-uniform test"))
(def seedrng (math/rng 123))
(for i 0 75
(test-rng (math/rng (:int seedrng))))
(end-suite) (end-suite)