Add a global ref tracker, make some prelude changes

This commit is contained in:
Scott Richmond 2024-01-22 17:04:33 -05:00
parent c2943e0ba7
commit 5c037ed46e
3 changed files with 115 additions and 22 deletions

View File

@ -1,6 +1,33 @@
# start a repl set dotenv-load
repl:
clj -X:repl
default:
@just --list
test:
@echo $test
# build clojurescript release
build: build:
shadow-cljs release module shadow-cljs release module
# open a janet repl in a different os window
repl:
kitten @ launch --type=os-window --allow-remote-control --cwd=current --title=hx_repl:ludus
kitten @ send-text -m "title:hx_repl:ludus" "lein repl"
restart:
kitten @ send-text -m "title:hx_repl:ludus" "\04"
kitten @ send-text -m "title:hx_repl:ludus" "janet -s\n"
# send what's selected to the repl and evaluate it
eval:
sd "$" "\n" | sd "\n\n" "\n" | kitten @ send-text -m "title:hx_repl:ludus" --stdin
# send what's selected to a buffer, and then evaluate what's in the buffer
buffer:
sd "$" "\n" | sd "\n\n" "\n" > .repl-buffer.janet
kitten @ send-text -m "title:hx_repl:ludus" "(import ./.repl-buffer :prefix \"\")"

View File

@ -817,7 +817,7 @@
(resolve-word times-expr ctx) (resolve-word times-expr ctx)
(-> times-expr :data first)) (-> times-expr :data first))
expr (second data)] expr (second data)]
(if (not (number? times)) (throw (ex-info (str "Repeat needs a number, not a " (base/get-type times)) {}))) (when (not (number? times)) (throw (ex-info (str "Repeat needs a number, not a " (base/get-type times)) {})))
(dotimes [_ times] (interpret-ast expr ctx)))) (dotimes [_ times] (interpret-ast expr ctx))))
(defn- interpret-literal [ast] (-> ast :data first)) (defn- interpret-literal [ast] (-> ast :data first))

View File

@ -136,7 +136,7 @@ fn empty? {
"Returns true if something is empty. Otherwise returns false (including for things that can't logically be empty, like numbers)." "Returns true if something is empty. Otherwise returns false (including for things that can't logically be empty, like numbers)."
([]) -> true ([]) -> true
(#{}) -> true (#{}) -> true
(s as :set) -> eq (s, ${}) (s as :set) -> eq? (s, ${})
(()) -> true (()) -> true
("") -> true ("") -> true
(_) -> false (_) -> false
@ -165,6 +165,8 @@ fn set? {
(_) -> false (_) -> false
} }
& add a contains? or has? function
fn fold { fn fold {
"Folds a list." "Folds a list."
(f as :fn, xs as :list) -> fold (f, xs, f ()) (f as :fn, xs as :list) -> fold (f, xs, f ())
@ -179,7 +181,7 @@ fn fold {
} }
fn map { fn map {
"Maps over a list." "Maps a function over a list: returns a new list with elements that are the result of applying the function to each element in the original list. E.g., `map ([1, 2, 3], inc) &=> [2, 3, 4]`."
(f as :fn, xs) -> { (f as :fn, xs) -> {
fn mapper (prev, curr) -> append (prev, f (curr)) fn mapper (prev, curr) -> append (prev, f (curr))
fold (mapper, xs, []) fold (mapper, xs, [])
@ -191,7 +193,7 @@ fn map {
} }
fn filter { fn filter {
"Takes a list and a predicate function, and returns a new list with only the items that produce truthy values when the function is called on them." "Takes a list and a predicate function, and returns a new list with only the items that produce truthy values when the function is called on them. E.g., `filter ([1, 2, 3, 4], odd?) &=> [1, 3]`."
(p? as :fn, xs) -> { (p? as :fn, xs) -> {
fn filterer (filtered, x) -> if p? (x) fn filterer (filtered, x) -> if p? (x)
then append (filtered, x) then append (filtered, x)
@ -413,10 +415,20 @@ fn div/safe {
} }
} }
fn inv {
"Returns the inverse of a number: 1/n or `div (1, n)`. Panics on division by zero."
(x as :number) -> div (1, x)
}
fn inv/0 {
"Returns the inverse of a number: 1/n or `div/0 (1, n)`. Returns 0 on division by zero."
(x as :number) -> div/0 (1, x)
}
fn abs { fn abs {
"Returns the absolute value of a number." "Returns the absolute value of a number."
(0) -> 0 (0) -> 0
(n) -> if neg? (n) then mult (-1, n) else n (n as :number) -> if neg? (n) then mult (-1, n) else n
} }
fn neg { fn neg {
@ -483,6 +495,14 @@ fn lte? {
} }
} }
fn between? {
"Returns true if a number is in the range [lower, higher): greater than or equal to the lower number, less than the higher."
(lower as :number, higher as :number, x as :number) -> and (
gte? (x, lower)
lt? (x, higher)
)
}
fn neg? { fn neg? {
"Returns true if a value is a negative number, otherwise returns false." "Returns true if a value is a negative number, otherwise returns false."
(x as :number) if lt? (x, 0) -> true (x as :number) if lt? (x, 0) -> true
@ -497,16 +517,30 @@ fn pos? {
fn even? { fn even? {
"Returns true if a value is an even number, otherwise returns false." "Returns true if a value is an even number, otherwise returns false."
(x as :number) if eq (0, mod (x, 2)) -> true (x as :number) if eq? (0, mod (x, 2)) -> true
(_) -> false (_) -> false
} }
fn odd? { fn odd? {
"Returns true if a value is an odd number, otherwise returns false." "Returns true if a value is an odd number, otherwise returns false."
(x as :number) if eq (1, mod (x, 2)) -> true (x as :number) if eq? (1, mod (x, 2)) -> true
(_) -> false (_) -> false
} }
fn min {
"Returns the number in its arguments that is closest to negative infinity."
(x as :number) -> x
(x as :number, y as :number) -> if lt? (x, y) then x else y
(x, y, ...zs) -> fold (min, zs, min (x, y))
}
fn max {
"Returns the number in its arguments that is closest to positive infinity."
(x as :number) -> x
(x as :number, y as :number) -> if gt? (x, y) then x else y
(x, y, ...zs) -> fold (max, zs, max (x, y))
}
&&& keywords: funny names &&& keywords: funny names
fn keyword? { fn keyword? {
"Returns true if a value is a keyword, otherwise returns false." "Returns true if a value is a keyword, otherwise returns false."
@ -590,6 +624,7 @@ fn or {
&&& associative collections: dicts, structs, namespaces &&& associative collections: dicts, structs, namespaces
& TODO?: get_in, update_in, merge & TODO?: get_in, update_in, merge
& TODO?: consider renaming these: put & take, not assoc/dissoc
fn assoc { fn assoc {
"Takes a dict, key, and value, and returns a new dict with the key set to value." "Takes a dict, key, and value, and returns a new dict with the key set to value."
() -> #{} () -> #{}
@ -677,6 +712,7 @@ fn assoc? {
(_) -> false (_) -> false
} }
& TODO: consider merging `get` and `at`
fn get { fn get {
"Takes a dict or struct, key, and optional default value; returns the value at key. If the value is not found, returns nil or the default value. Returns nil or default if the first argument is not a dict or struct." "Takes a dict or struct, key, and optional default value; returns the value at key. If the value is not found, returns nil or the default value. Returns nil or default if the first argument is not a dict or struct."
(key as :keyword) -> get (key, _) (key as :keyword) -> get (key, _)
@ -684,6 +720,7 @@ fn get {
(key as :keyword, coll, default) -> base :get (key, coll, default) (key as :keyword, coll, default) -> base :get (key, coll, default)
} }
& TODO: add sets to this?
fn has? { fn has? {
"Takes a key and a dict, and returns true if there is a non-`nil` value stored at the key." "Takes a key and a dict, and returns true if there is a non-`nil` value stored at the key."
(key as :keyword) -> has? (key, _) (key as :keyword) -> has? (key, _)
@ -691,8 +728,7 @@ fn has? {
} }
fn dict { fn dict {
"Takes a struct or ns, and returns it as a dict. Or, takes a list or tuple of (key, value) tuples and returns it as a dict. Returns dicts unharmed." "Takes an ns, and returns it as a dict. Or, takes a list or tuple of (key, value) tuples and returns it as a dict. Returns dicts unharmed."
(struct as :struct) -> base :to_dict (struct)
(ns_ as :ns) -> base :to_dict (ns_) (ns_ as :ns) -> base :to_dict (ns_)
(dict as :dict) -> dict (dict as :dict) -> dict
(list as :list) -> fold (assoc, list) (list as :list) -> fold (assoc, list)
@ -829,12 +865,25 @@ fn dist {
} }
&&& more number functions &&& more number functions
& TODO: add max, min
fn random { fn random {
"Returns a random number. With zero arguments, returns a random number between 0 (inclusive) and 1 (exclusive). With one argument, returns a random number between 0 and n. With two arguments, returns a random number between m and n." "Returns a random number. With zero arguments, returns a random number between 0 (inclusive) and 1 (exclusive). With one argument, returns a random number between 0 and n. With two arguments, returns a random number between m and n. Alternately, given a list, it returns a random member of that list."
() -> base :random () () -> base :random ()
(n as :number) -> base :random (n) (n as :number) -> base :random (n)
(m as :number, n as :number) -> add (m, random (n)) (m as :number, n as :number) -> add (m, random (n))
(l as :list) -> {
let i = do l > count > random > floor
at (l, i)
}
(d as :dict) -> {
let key = do d > keys > random
get (d, key)
}
}
fn random_int {
"Returns a random integer. With one argument, returns a random integer between 0 and that number. With two arguments, returns a random integer between them."
(n as :number) -> do n > random > floor
(m as :number, n as :number) -> floor (random (m, n))
} }
fn floor { fn floor {
@ -853,7 +902,7 @@ fn round {
} }
fn range { fn range {
"Returns the set of integers between start (inclusive) and end (exclusive) as a list. With one argument, starts at 0. If end is less than start, returns an empty list." "Returns the set of integers between start (inclusive) and end (exclusive) as a list: [start, end). With one argument, starts at 0. If end is less than start, returns an empty list."
(end as :number) -> base :range (0, end) (end as :number) -> base :range (0, end)
(start as :number, end as :number) -> base :range (start, end) (start as :number, end as :number) -> base :range (start, end)
} }
@ -906,15 +955,25 @@ fn assert! {
&&& Turtle & other graphics &&& Turtle & other graphics
& some basic colors & some basic colors
&&& TODO: add colors & these are the "basic" css colors
& https://developer.mozilla.org/en-US/docs/Web/CSS/named-color
let colors = #{ let colors = #{
:white (255, 255, 255, 255)
:light_gray (150, 150, 150, 255)
:dark_gray (50, 50, 50, 255)
:red (255, 0, 0, 255)
:green (0, 255, 0, 255)
:blue (0, 0, 255, 255)
:black (0, 0, 0, 255) :black (0, 0, 0, 255)
:silver (192, 192, 192, 255)
:gray (128, 128, 128, 255)
:white (255, 255, 255, 255)
:maroon (128, 0, 0, 255)
:red (255, 0, 0, 255)
:purple (128, 0, 128, 255)
:fuchsia (255, 0, 255, 255)
:green (0, 128, 0, 255)
:lime (0, 255, 0, 255)
:olive (128, 128, 0, 255)
:yellow (255, 255, 0, 255)
:navy (0, 0, 128, 255)
:blue (0, 0, 255, 255)
:teal (0, 128, 128, 255)
:aqua (0, 255, 25, 255)
} }
& the initial turtle state & the initial turtle state
@ -986,6 +1045,7 @@ fn render_turtle! () -> {
add_call! ((:vertex, x2, y2)) add_call! ((:vertex, x2, y2))
add_call! ((:vertex, x3, y3)) add_call! ((:vertex, x3, y3))
add_call! ((:endShape)) add_call! ((:endShape))
& there's a happy bug here: the stroke will be the same width as the pen width. Keep this for now. Consider also showing the pen colour here?
add_call! ((:stroke, 0)) add_call! ((:stroke, 0))
add_call! ((:line, 0, 0, x1, y1)) add_call! ((:line, 0, 0, x1, y1))
add_call! ((:pop)) add_call! ((:pop))
@ -1219,6 +1279,8 @@ ns prelude {
div div
div/0 div/0
div/safe div/safe
inv
inv/0
angle angle
abs abs
neg neg
@ -1231,6 +1293,9 @@ ns prelude {
gte? gte?
lt? lt?
lte? lte?
min
max
between?
keyword? keyword?
nil? nil?
some? some?
@ -1269,6 +1334,7 @@ ns prelude {
sum_of_squares sum_of_squares
dist dist
random random
random_int
pi pi
tau tau
floor floor