ludus/prelude.ld

1536 lines
39 KiB
Plaintext
Raw Normal View History

2024-06-16 02:18:58 +00:00
& this file, uniquely, gets `base` loaded as context. See src/base.janet for exports
2023-12-12 20:38:16 +00:00
2024-06-05 19:52:03 +00:00
& some forward declarations
& TODO: fix this so that we don't need (as many of) them
2024-06-16 01:51:56 +00:00
fn and
2024-06-05 19:52:03 +00:00
fn append
2024-06-16 01:51:56 +00:00
fn apply_command
2024-06-17 17:06:37 +00:00
fn assoc
2024-06-16 01:51:56 +00:00
fn atan/2
fn deg/rad
2024-06-05 19:52:03 +00:00
fn dict
2024-06-16 01:51:56 +00:00
fn first
fn floor
2024-06-05 19:52:03 +00:00
fn get
2024-06-16 01:51:56 +00:00
fn join
fn mod
fn neg?
2024-06-17 17:06:37 +00:00
fn print!
2024-06-16 01:51:56 +00:00
fn some?
fn state/call
2024-06-05 19:52:03 +00:00
fn store!
2024-06-16 01:51:56 +00:00
fn string
2024-06-05 19:52:03 +00:00
fn turn/rad
2024-06-16 01:51:56 +00:00
fn unbox
fn update!
2024-06-05 19:52:03 +00:00
& the very base: know something's type
fn type {
"Returns a keyword representing the type of the value passed in."
(x) -> base :type (x)
}
2024-06-16 01:51:56 +00:00
& some helper type functions
fn coll? {
"Returns true if a value is a collection: dict, list, tuple, or set."
(coll as :dict) -> true
(coll as :list) -> true
(coll as :tuple) -> true
(coll as :set) -> true
(_) -> false
}
fn ordered? {
"Returns true if a value is an indexed collection: list or tuple."
(coll as :list) -> true
(coll as :tuple) -> true
2024-06-21 19:28:46 +00:00
(coll as :string) -> true
2024-06-16 01:51:56 +00:00
(_) -> false
}
fn assoc? {
"Returns true if a value is an associative collection: a dict or a pkg."
(assoc as :dict) -> true
(assoc as :pkg) -> true
(_) -> false
}
&&& nil: working with nothing
fn nil? {
"Returns true if a value is nil."
(nil) -> true
(_) -> false
}
fn some? {
"Returns true if a value is not nil."
(nil) -> false
(_) -> true
}
fn some {
"Takes a possibly nil value and a default value. Returns the value if it's not nil, returns the default if it's nil."
(nil, default) -> default
(value, _) -> value
}
& ...and if two things are the same
fn eq? {
"Returns true if all arguments have the same value."
(x) -> true
2024-06-14 18:53:23 +00:00
(x, y) -> base :eq? (x, y)
2023-12-13 22:02:39 +00:00
(x, y, ...zs) -> if eq? (x, y)
then loop (y, zs) with {
(a, []) -> eq? (a, x)
(a, [b, ...cs]) -> if eq? (a, x)
then recur (b, cs)
else false
}
else false
}
2024-06-05 19:52:03 +00:00
&&& true & false: boolean logic (part the first)
fn bool? {
"Returns true if a value is of type :boolean."
(false) -> true
(true) -> true
(_) -> false
}
fn true? {
"Returns true if a value is boolean `true`. Useful to distinguish between `true` and anything else."
(true) -> true
(_) -> false
}
fn false? {
"Returns `true` if a value is `false`, otherwise returns `false`. Useful to distinguish between `false` and `nil`."
(false) -> true
(_) -> false
}
fn bool {
"Returns false if a value is nil or false, otherwise returns true."
(nil) -> false
(false) -> false
(_) -> true
}
fn not {
"Returns false if a value is truthy, true if a value is falsy."
(nil) -> true
(false) -> true
(_) -> false
}
2023-12-13 22:02:39 +00:00
fn neq? {
"Returns true if none of the arguments have the same value."
(x) -> false
(x, y) -> not (eq? (x, y))
(x, y, ...zs) -> if eq? (x, y)
then false
else loop (y, zs) with {
(a, []) -> neq? (a, x)
(a, [b, ...cs]) -> if neq? (a, x)
then recur (b, cs)
else false
}
}
2023-12-13 22:02:39 +00:00
& tuples: not a lot you can do with them functionally
fn tuple? {
"Returns true if a value is a tuple."
(tuple as :tuple) -> true
(_) -> false
}
2023-12-07 01:29:21 +00:00
&&& functions: getting things done
fn fn? {
"Returns true if an argument is a function."
(f as :fn) -> true
(_) -> false
}
& what we need for some very basic list manipulation
2023-12-02 00:08:51 +00:00
fn rest {
"Returns all but the first element of a list or tuple, as a list."
([]) -> []
(()) -> ()
(xs as :list) -> base :rest (xs)
(xs as :tuple) -> base :rest (xs)
2024-06-21 19:28:46 +00:00
(xs as :string) -> base :str_slice (xs, 1)
2023-12-02 00:08:51 +00:00
}
fn inc {
"Increments a number."
(x as :number) -> base :inc (x)
}
fn dec {
"Decrements a number."
(x as :number) -> base :dec (x)
}
fn count {
"Returns the number of elements in a collection (including string)."
(xs as :list) -> base :count (xs)
(xs as :tuple) -> base :count (xs)
2023-12-02 00:08:51 +00:00
(xs as :dict) -> base :count (xs)
(xs as :string) -> base :count (xs)
(xs as :set) -> base :count (xs)
}
2023-12-07 01:29:21 +00:00
fn empty? {
"Returns true if something is empty. Otherwise returns false (including for things that can't logically be empty, like numbers)."
([]) -> true
(#{}) -> true
2024-01-22 22:17:29 +00:00
(s as :set) -> eq? (s, ${})
2023-12-07 01:29:21 +00:00
(()) -> true
("") -> true
(_) -> false
}
2024-06-05 19:52:03 +00:00
fn any? {
"Returns true if something is not empty, otherwise returns false (including for things that can't be logically full, like numbers)."
([...]) -> true
(#{...}) -> true
(s as :set) -> not (empty? (s))
((...)) -> true
(s as :string) -> not (empty? (s))
(_) -> false
}
fn list? {
"Returns true if the value is a list."
(l as :list) -> true
(_) -> false
}
2023-12-02 00:08:51 +00:00
fn list {
2024-06-16 01:51:56 +00:00
"Takes a value and returns it as a list. For values, it simply wraps them in a list. For collections, conversions are as follows. A tuple->list conversion preservers order and length. Unordered collections do not preserve order: sets and dicts don't have predictable or stable ordering in output. Dicts return lists of (key, value) tuples."
(x) -> base :to_list (x)
2023-12-02 00:08:51 +00:00
}
fn fold {
"Folds a list."
2024-06-20 20:36:01 +00:00
(f as :fn, []) -> []
2023-12-02 00:08:51 +00:00
(f as :fn, xs as :list) -> fold (f, xs, f ())
2024-06-20 20:36:01 +00:00
(f as :fn, [], root) -> []
2024-06-17 17:06:37 +00:00
(f as :fn, xs as :list, root) -> loop (root, first (xs), rest (xs)) with {
(prev, curr, []) -> f (prev, curr)
(prev, curr, remaining) -> recur (
f (prev, curr)
first (remaining)
rest (remaining)
)
2023-12-02 00:08:51 +00:00
}
}
2024-06-05 19:52:03 +00:00
& TODO: optimize these with base :conj!
2023-12-02 00:08:51 +00:00
fn map {
2024-01-22 22:17:29 +00:00
"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]`."
2023-12-02 00:08:51 +00:00
(f as :fn, xs) -> {
2023-12-04 02:10:22 +00:00
fn mapper (prev, curr) -> append (prev, f (curr))
2023-12-02 00:08:51 +00:00
fold (mapper, xs, [])
}
2023-12-04 04:14:55 +00:00
(kw as :keyword, xs) -> {
fn mapper (prev, curr) -> append (prev, kw (curr))
fold (mapper, xs, [])
}
2023-12-02 00:08:51 +00:00
}
2023-12-13 22:08:15 +00:00
fn filter {
2024-01-22 22:17:29 +00:00
"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]`."
2023-12-13 22:08:15 +00:00
(p? as :fn, xs) -> {
fn filterer (filtered, x) -> if p? (x)
then append (filtered, x)
else filtered
fold (filterer, xs, [])
}
}
fn keep {
"Takes a list and returns a new list with any `nil` values omitted."
(xs) -> filter (some?, xs)
}
2023-12-04 02:10:22 +00:00
fn append {
"Adds an element to a list or set."
2023-12-02 00:08:51 +00:00
() -> []
(xs as :list) -> xs
(xs as :list, x) -> base :conj (xs, x)
(xs as :set) -> xs
(xs as :set, x) -> base :conj (xs, x)
}
fn append! {
"Adds an element to a list, modifying it."
() -> []
(xs as :list) -> xs
(xs as :list, x) -> base :conj! (xs, x)
}
fn concat {
"Combines two lists, strings, or sets."
(x as :string, y as :string) -> base :concat (x, y)
(xs as :list, ys as :list) -> base :concat (xs, ys)
(xs as :set, ys as :set) -> base :concat (xs, ys)
2023-12-08 20:04:44 +00:00
(xs, ys, ...zs) -> fold (concat, zs, concat (xs, ys))
}
2024-06-16 01:51:56 +00:00
fn set {
"Takes an ordered collection--list or tuple--and turns it into a set."
(xs as :list) -> fold (append, xs, ${})
(xs as :tuple) -> do xs > list > set
}
fn set? {
"Returns true if a value is a set."
(xs as :set) -> true
(_) -> false
}
fn contains? {
"Returns true if a set or list contains a value."
(value, set as :set) -> bool (base :get (set, value))
(value, list as :list) -> contains? (value, set (list))
}
fn omit {
"Returns a new set with the value omitted."
(value, s as :set) -> base :disj (s, value)
}
2023-12-04 05:41:57 +00:00
fn print! {
"Sends a text representation of Ludus values to the console."
2023-12-04 05:41:57 +00:00
(...args) -> {
base :print! (args)
:ok
2023-12-04 05:41:57 +00:00
}
2023-12-02 00:08:51 +00:00
}
fn show {
"Returns a text representation of a Ludus value as a string."
(x) -> base :show (x)
}
2023-12-04 02:48:53 +00:00
fn report! {
2023-12-02 00:08:51 +00:00
"Prints a value, then returns it."
(x) -> {
2023-12-04 02:48:53 +00:00
print! (x)
2023-12-02 00:08:51 +00:00
x
}
(msg as :string, x) -> {
2024-06-16 01:51:56 +00:00
print! (concat ("{msg} ", show (x)))
x
}
}
fn doc! {
"Prints the documentation of a function to the console."
(f as :fn) -> do f > base :doc > print!
(_) -> :none
}
&&& strings: harder than they look!
fn string? {
"Returns true if a value is a string."
(x as :string) -> true
(_) -> false
}
fn string {
2024-06-06 20:14:04 +00:00
"Converts a value to a string by using `show`. If it is a string, returns it unharmed. Use this to build up strings of different kinds of values."
(x as :string) -> x
(x) -> show (x)
(x, ...xs) -> loop (x, xs) with {
2024-06-06 20:14:04 +00:00
(out, [x]) -> concat (out, show (x))
(out, [x, ...xs]) -> recur (concat (out, show (x)), xs)
}
}
fn join {
"Takes a list of strings, and joins them into a single string, interposing an optional separator."
([]) -> ""
2023-12-08 20:27:33 +00:00
([str as :string]) -> str
(strs as :list) -> join (strs, "")
([], separator as :string) -> ""
2024-06-16 01:51:56 +00:00
([str as :string], separator as :string) -> str
([str, ...strs], separator as :string) -> fold (
fn (joined, to_join) -> concat (joined, separator, to_join)
strs
str
)
}
2024-06-10 22:26:48 +00:00
fn split {
"Takes a string, and turns it into a list of strings, breaking on the separator."
(str as :string, break as :string) -> base :split (break, str)
}
fn trim {
"Trims whitespace from a string. Takes an optional argument, `:left` or `:right`, to trim only on the left or right."
(str as :string) -> base :trim (str)
(str as :string, :left) -> base :triml (str)
(str as :string, :right) -> base :trimr (str)
}
fn upcase {
"Takes a string and returns it in all uppercase. Works only for ascii characters."
(str as :string) -> base :upcase (str)
}
fn downcase {
"Takes a string and returns it in all lowercase. Works only for ascii characters."
(str as :string) -> base :downcase (str)
}
2024-06-14 18:53:23 +00:00
fn ws? {
"Tells if a string is a whitespace character."
(" ") -> true
("\n") -> true
("\t") -> true
(_) -> false
}
2024-06-15 21:03:49 +00:00
fn strip {
"Removes punctuation from a string, removing all instances of ,.;:?!"
("{x},{y}") -> strip ("{x}{y}")
("{x}.{y}") -> strip ("{x}{y}")
("{x};{y}") -> strip ("{x}{y}")
("{x}:{y}") -> strip ("{x}{y}")
("{x}?{y}") -> strip ("{x}{y}")
("{x}!{y}") -> strip ("{x}{y}")
(x) -> x
}
2024-06-14 18:53:23 +00:00
fn words {
"Takes a string and returns a list of the words in the string. Strips all whitespace."
(str as :string) -> {
2024-06-15 21:03:49 +00:00
let no_punct = strip (str)
let strs = split (no_punct, " ")
fn worder (list, str) -> if empty? (str)
2024-06-17 17:29:08 +00:00
then list
else append (list, str)
fold (worder, strs, [])
2024-06-14 18:53:23 +00:00
}
}
fn sentence {
"Takes a list of words and turns it into a sentence."
(strs as :list) -> join (strs, " ")
}
2024-06-16 01:51:56 +00:00
&&& boxes: mutable state and state changes
2024-06-05 19:52:03 +00:00
fn box? {
"Returns true if a value is a box."
(b as :box) -> true
(_) -> false
2023-12-02 00:08:51 +00:00
}
2024-06-05 19:52:03 +00:00
fn unbox {
"Returns the value that is stored in a box."
(b as :box) -> base :unbox (b)
2023-12-02 00:08:51 +00:00
}
2024-06-05 19:52:03 +00:00
fn store! {
"Stores a value in a box, replacing the value that was previously there. Returns the value."
2024-06-06 00:16:29 +00:00
(b as :box, value) -> {
base :store! (b, value)
value
}
2023-12-02 00:08:51 +00:00
}
fn update! {
2024-06-05 19:52:03 +00:00
"Updates a box by applying a function to its value. Returns the new value."
(b as :box, f as :fn) -> {
let current = unbox (b)
let new = f (current)
2024-06-05 19:52:03 +00:00
store! (b, new)
}
}
&&& numbers, basically: arithmetic and not much else, yet
2023-12-07 01:29:21 +00:00
& TODO: add nan?,
fn number? {
"Returns true if a value is a number."
(x as :number) -> true
(_) -> false
}
fn add {
"Adds numbers or vectors."
() -> 0
(x as :number) -> x
(x as :number, y as :number) -> base :add (x, y)
2024-06-14 21:21:32 +00:00
(x, y, ...zs) -> fold (add, zs, base :add (x, y))
& add vectors
2023-12-04 02:10:22 +00:00
((x1, y1), (x2, y2)) -> (add (x1, x2), add (y1, y2))
}
fn sub {
"Subtracts numbers or vectors."
() -> 0
(x as :number) -> x
(x as :number, y as :number) -> base :sub (x, y)
2024-06-14 21:21:32 +00:00
(x, y, ...zs) -> fold (sub, zs, base :sub (x, y))
2024-01-22 22:37:11 +00:00
((x1, y1), (x2, y2)) -> (base :sub (x1, x2), base :sub (y1, y2))
}
fn mult {
"Multiplies numbers or vectors."
() -> 1
(x as :number) -> x
(x as :number, y as :number) -> base :mult (x, y)
2024-06-14 21:21:32 +00:00
(x, y, ...zs) -> fold (mult, zs, mult (x, y))
(scalar as :number, (x, y)) -> (mult (x, scalar), mult (y, scalar))
((x, y), scalar as :number) -> mult (scalar, (x, y))
}
fn div {
"Divides numbers. Panics on division by zero."
(x as :number) -> x
2023-12-18 04:13:50 +00:00
(_, 0) -> panic! "Division by zero."
(x as :number, y as :number) -> base :div (x, y)
(x, y, ...zs) -> {
let divisor = fold (mult, zs, y)
div (x, divisor)
}
}
fn div/0 {
"Divides numbers. Returns 0 on division by zero."
(x as :number) -> x
(_, 0) -> 0
(x as :number, y as :number) -> base :div (x, y)
(x, y, ...zs) -> {
let divisor = fold (mult, zs, y)
div/0 (x, divisor)
}
}
fn div/safe {
"Divides a number. Returns a result tuple."
(x as :number) -> (:ok, x)
(_, 0) -> (:err, "Division by zero")
(x, y) -> (:ok, div (x, y))
(x, y, ...zs) -> {
let divisor = fold (mult, zs, y)
div/safe (x, divisor)
}
}
2024-01-22 22:17:29 +00:00
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)
}
2024-06-15 21:03:49 +00:00
fn inv/safe {
"Returns the inverse of a number: 1/n or `div/safe (1, n)`. Returns a result tuple."
(x as :number) -> div/safe (1, x)
}
fn abs {
"Returns the absolute value of a number."
(0) -> 0
2024-01-22 22:17:29 +00:00
(n as :number) -> if neg? (n) then mult (-1, n) else n
}
2023-12-04 04:14:55 +00:00
fn neg {
"Multiplies a number by -1, negating it."
(n as :number) -> mult (n, -1)
}
fn angle {
"Calculates the angle between two vectors."
(v1, v2) -> sub (atan/2 (v2), atan/2 (v1))
}
fn zero? {
"Returns true if a number is 0."
(0) -> true
(_) -> false
}
fn gt? {
"Returns true if numbers are in decreasing order."
(x as :number) -> true
(x as :number, y as :number) -> base :gt (x, y)
(x, y, ...zs) -> loop (y, zs) with {
(a, [b]) -> base :gt (a, b)
(a, [b, ...cs]) -> if base :gt (a, b)
then recur (b, cs)
else false
}
}
fn gte? {
"Returns true if numbers are in decreasing or flat order."
(x as :number) -> true
(x as :number, y as :number) -> base :gte (x, y)
(x, y, ...zs) -> loop (y, zs) with {
(a, [b]) -> base :gte (a, b)
(a, [b, ...cs]) -> if base :gte (a, b)
then recur (b, cs)
else false
}
}
fn lt? {
"Returns true if numbers are in increasing order."
(x as :number) -> true
(x as :number, y as :number) -> base :lt (x, y)
(x, y, ...zs) -> loop (y, zs) with {
(a, [b]) -> base :lt (a, b)
(a, [b, ...cs]) -> if base :lt (a, b)
then recur (b, cs)
else false
}
}
fn lte? {
"Returns true if numbers are in increasing or flat order."
(x as :number) -> true
(x as :number, y as :number) -> base :lte (x, y)
(x, y, ...zs) -> loop (y, zs) with {
(a, [b]) -> base :lte (a, b)
(a, [b, ...cs]) -> if base :lte (a, b)
then recur (b, cs)
else false
}
}
2024-01-22 22:17:29 +00:00
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? {
"Returns true if a value is a negative number, otherwise returns false."
(x as :number) if lt? (x, 0) -> true
(_) -> false
}
fn pos? {
"Returns true if a value is a positive number, otherwise returns false."
(x as :number) if gt? (x, 0) -> true
(_) -> false
}
2023-12-07 01:29:21 +00:00
fn even? {
"Returns true if a value is an even number, otherwise returns false."
2024-01-22 22:17:29 +00:00
(x as :number) if eq? (0, mod (x, 2)) -> true
2023-12-07 01:29:21 +00:00
(_) -> false
}
fn odd? {
"Returns true if a value is an odd number, otherwise returns false."
2024-01-22 22:17:29 +00:00
(x as :number) if eq? (1, mod (x, 2)) -> true
2023-12-07 01:29:21 +00:00
(_) -> false
}
2024-01-22 22:17:29 +00:00
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))
}
2024-06-05 19:52:03 +00:00
& additional list operations now that we have comparitors
fn at {
2024-06-15 21:03:49 +00:00
"Returns the element at index n of a list or tuple, or the byte at index n of a string. Zero-indexed: the first element is at index 0. Returns nil if nothing is found in a list or tuple; returns an empty string if nothing is found in a string."
2024-06-17 17:06:37 +00:00
(xs as :list, n as :number) -> base :nth (n, xs)
2024-06-15 21:03:49 +00:00
(xs as :tuple, n as :number) -> base :nth (n, xs)
2024-06-21 19:28:46 +00:00
(str as :string, n as :number) -> {
let raw = base :nth (n, str)
when {
nil? (raw) -> nil
gte? (raw, 128) -> panic! "not an ASCII char"
2024-06-24 16:56:43 +00:00
true -> base :str_slice (str, n, inc (n))
2024-06-21 19:28:46 +00:00
}
2024-06-05 19:52:03 +00:00
}
(_) -> nil
}
fn first {
"Returns the first element of a list or tuple."
2024-06-16 01:51:56 +00:00
(xs) if ordered? (xs) -> at (xs, 0)
2024-06-05 19:52:03 +00:00
}
fn second {
"Returns the second element of a list or tuple."
2024-06-16 01:51:56 +00:00
(xs) if ordered? (xs) -> at (xs, 1)
2024-06-05 19:52:03 +00:00
}
fn last {
"Returns the last element of a list or tuple."
2024-06-16 01:51:56 +00:00
(xs) if ordered? (xs) -> at (xs, dec (count (xs)))
2024-06-05 19:52:03 +00:00
}
fn butlast {
"Returns a list, omitting the last element."
2024-06-19 22:23:54 +00:00
(xs as :list) -> base :slice (xs, 0, dec (count (xs)))
2024-06-05 19:52:03 +00:00
}
fn slice {
2024-06-16 02:18:58 +00:00
"Returns a slice of a list or a string, representing a sub-list or sub-string."
2024-06-05 19:52:03 +00:00
(xs as :list, end as :number) -> slice (xs, 0, end)
(xs as :list, start as :number, end as :number) -> when {
gte? (start, end) -> []
gt? (end, count (xs)) -> slice (xs, start, count (xs))
neg? (start) -> slice (xs, 0, end)
2024-06-19 22:23:54 +00:00
true -> base :slice (xs, start, end)
2024-06-05 19:52:03 +00:00
}
2024-06-10 22:26:48 +00:00
(str as :string, end as :number) -> base :str_slice (str, 0, end)
(str as :string, start as :number, end as :number) -> base :str_slice (str, start, end)
2024-06-05 19:52:03 +00:00
}
2023-12-07 01:29:21 +00:00
&&& keywords: funny names
fn keyword? {
"Returns true if a value is a keyword, otherwise returns false."
(kw as :keyword) -> true
(_) -> false
}
& TODO: determine if Ludus should have a `keyword` function that takes a string and returns a keyword. Too many panics, it has weird memory consequences, etc.
& TODO: make `and` and `or` special forms which lazily evaluate arguments
fn and {
"Returns true if all values passed in are truthy. Note that this does not short-circuit: all arguments are evaulated before they are passed in."
() -> true
(x) -> bool (x)
(x, y) -> base :and (x, y)
2024-06-14 21:21:32 +00:00
(x, y, ...zs) -> fold (and, zs, base :and (x, y))
}
fn or {
"Returns true if any value passed in is truthy. Note that this does not short-circuit: all arguments are evaluated before they are passed in."
() -> true
(x) -> bool (x)
(x, y) -> base :or (x, y)
2024-06-14 21:21:32 +00:00
(x, y, ...zs) -> fold (or, zs, base :or (x, y))
}
2023-12-04 02:10:22 +00:00
fn assoc {
"Takes a dict, key, and value, and returns a new dict with the key set to value."
2023-12-04 02:10:22 +00:00
() -> #{}
(dict as :dict) -> dict
(dict as :dict, key as :keyword, value) -> base :assoc (dict, key, value)
(dict as :dict, (key as :keyword, value)) -> base :assoc (dict, key, value)
}
2023-12-04 02:10:22 +00:00
fn dissoc {
"Takes a dict and a key, and returns a new dict with the key and associated value omitted."
(dict as :dict) -> dict
(dict as :dict, key as :keyword) -> base :dissoc (dict, key)
}
fn update {
"Takes a dict, key, and function, and returns a new dict with the key set to the result of applying the function to original value held at the key."
(dict as :dict) -> dict
(dict as :dict, key as :keyword, updater as :fn) -> base :assoc (dict, key, updater (get (key, dict)))
}
2023-12-04 02:10:22 +00:00
fn keys {
2024-06-15 21:03:49 +00:00
"Takes a dict and returns a list of keys in that dict."
(dict as :dict) -> do dict > list > map (first, _)
2023-12-04 02:10:22 +00:00
}
fn values {
2024-06-15 21:03:49 +00:00
"Takes a dict and returns a list of values in that dict."
(dict) -> do dict > list > map (second, _)
2023-12-04 02:10:22 +00:00
}
fn diff {
2024-06-15 21:03:49 +00:00
"Takes two dicts and returns a dict describing their differences. Does this shallowly, offering diffs only for keys in the original dict."
2023-12-04 02:10:22 +00:00
(d1 as :dict, d2 as :dict) -> {
let key1 = keys (d1)
let key2 = keys (d2)
let all = do concat (d1, d2) > set > list
let diffs = loop (all, []) with {
& TODO: reduce this redundancy?
([k, ...ks], diffs) -> {
let v1 = get (k, d1)
let v2 = get (k, d2)
if eq? (v1, v2)
then recur (ks, diffs)
else recur (ks, append (diffs, (k, (v1, v2))))
}
([k], diffs) -> {
let v1 = get (k, d1)
let v2 = get (k, d2)
if eq? (v1, v2)
then diffs
else append (diffs, (k, (v1, v2)))
}
}
dict (diffs)
2023-12-04 02:10:22 +00:00
}
}
2024-01-22 22:17:29 +00:00
& TODO: consider merging `get` and `at`
fn get {
2024-06-15 21:03:49 +00:00
"Takes a key, dict, and optional default value; returns the value at key. If the value is not found, returns nil or the default value."
2023-12-04 04:14:55 +00:00
(key as :keyword) -> get (key, _)
(key as :keyword, dict as :dict) -> get (key, dict, nil)
2024-06-15 21:03:49 +00:00
(key as :keyword, dict as :dict, default) -> base :get (key, dict, default)
}
2024-01-22 22:17:29 +00:00
& TODO: add sets to this?
fn has? {
"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, dict as :dict) -> do dict > key > nil?
}
fn dict {
"Takes a list or tuple of (key, value) tuples and returns it as a dict. Returns dicts unharmed."
(dict as :dict) -> dict
2023-12-04 02:10:22 +00:00
(list as :list) -> fold (assoc, list)
(tup as :tuple) -> do tup > list > dict
}
fn dict? {
"Returns true if a value is a dict."
(dict as :dict) -> true
(_) -> false
}
fn each! {
"Takes a list and applies a function, presumably with side effects, to each element in the list. Returns nil."
2023-12-04 04:14:55 +00:00
(f! as :fn, []) -> nil
(f! as :fn, [x]) -> { f! (x); nil }
2024-06-16 01:51:56 +00:00
(f! as :fn, [x, ...xs]) -> { f! (x); each! (f!, xs) }
}
2023-12-04 02:10:22 +00:00
&&& Trigonometry functions
& Ludus uses turns as its default unit to measure angles
& However, anything that takes an angle can also take a
& units argument, that's a keyword of :turns, :degrees, or :radians
let pi = base :pi
let tau = mult (2, pi)
fn sin {
"Returns the sine of an angle. Default angle measure is turns. An optional keyword argument specifies the units of the angle passed in."
(a as :number) -> do a > turn/rad > base :sin
(a as :number, :turns) -> do a > turn/rad > base :sin
(a as :number, :degrees) -> do a > deg/rad > base :sin
(a as :number, :radians) -> base :sin (a)
}
fn cos {
"Returns the cosine of an angle. Default angle measure is turns. An optional keyword argument specifies the units of the angle passed in."
(a as :number) -> do a > turn/rad > base :cos
(a as :number, :turns) -> do a > turn/rad > base :cos
(a as :number, :degrees) -> do a > deg/rad > base :cos
(a as :number, :radians) -> base :cos (a)
}
fn tan {
"Returns the sine of an angle. Default angle measure is turns. An optional keyword argument specifies the units of the angle passed in."
(a as :number) -> do a > turn/rad > base :tan
(a as :number, :turns) -> do a > turn/rad > base :tan
(a as :number, :degrees) -> do a > deg/rad > base :tan
(a as :number, :radians) -> base :tan (a)
}
2023-12-04 04:14:55 +00:00
fn rotate {
"Rotates a vector by an angle. Default angle measure is turns. An optional keyword argument specifies the units of the angle passed in."
((x, y), angle) -> rotate ((x, y), angle, :turns)
((x, y), angle, units as :keyword) -> (
sub (mult (x, cos (angle, units)), mult (y, sin (angle, units)))
add (mult (x, sin (angle, units)), mult (y, cos (angle, units)))
)
}
fn turn/deg {
"Converts an angle in turns to an angle in degrees."
(a as :number) -> mult (a, 360)
}
fn deg/turn {
"Converts an angle in degrees to an angle in turns."
(a as :number) -> div (a, 360)
}
fn turn/rad {
"Converts an angle in turns to an angle in radians."
(a as :number) -> mult (a, tau)
}
fn rad/turn {
"Converts an angle in radians to an angle in turns."
(a as :number) -> div (a, tau)
}
fn deg/rad {
"Converts an angle in degrees to an angle in radians."
(a as :number) -> mult (tau, div (a, 360))
}
fn rad/deg {
"Converts an angle in radians to an angle in degrees."
(a as :number) -> mult (360, div (a, tau))
}
fn atan/2 {
"Returns an angle from a slope. Takes an optional keyword argument to specify units. Takes either two numbers or a vector tuple."
(x as :number, y as :number) -> do base :atan_2 (x, y) > rad/turn
(x, y, :turns) -> atan/2 (x, y)
(x, y, :radians) -> base :atan_2 (x, y)
(x, y, :degrees) -> do base :atan_2 (x, y) > rad/deg
((x, y)) -> atan/2 (x, y)
((x, y), units as :keyword) -> atan/2 (x, y, units)
}
fn mod {
2024-06-16 01:51:56 +00:00
"Returns the modulus of num and div. Truncates towards negative infinity. Panics if div is 0."
(num as :number, 0) -> panic! "Division by zero."
(num as :number, div as :number) -> base :mod (num, div)
}
fn mod/0 {
"Returns the modulus of num and div. Truncates towards negative infinity. Returns 0 if div is 0."
(num as :number, 0) -> 0
2024-06-07 21:25:46 +00:00
(num as :number, div as :number) -> base :mod (num, div)
}
2024-06-16 01:51:56 +00:00
fn mod/safe {
"Returns the modulus of num and div in a result tuple, or an error if div is 0. Truncates towards negative infinity."
(num as :number, 0) -> (:err, "Division by zero.")
(num as :number, div as :number) -> (:ok, base :mod (num, div))
}
fn square {
"Squares a number."
(x as :number) -> mult (x, x)
}
fn sqrt {
2024-06-16 01:51:56 +00:00
"Returns the square root of a number. Panics if the number is negative."
(x as :number) if not (neg? (x)) -> base :sqrt (x)
}
fn sqrt/safe {
"Returns a result containing the square root of a number, or an error if the number is negative."
(x as :number) -> if not (neg? (x))
then (:ok, base :sqrt (x))
else (:err, "sqrt of negative number")
}
fn sum_of_squares {
"Returns the sum of squares of numbers."
() -> 0
(x as :number) -> square (x)
(x as :number, y as :number) -> add (square (x), square (y))
2024-06-16 01:51:56 +00:00
(x, y, ...zs) -> fold (
fn (sum, z) -> add (sum, square (z))
zs
sum_of_squares (x, y))
}
fn dist {
2024-06-16 01:51:56 +00:00
"Returns the distance from the origin to a point described by x and y, or by the vector (x, y)."
(x as :number, y as :number) -> sqrt (sum_of_squares (x, y))
((x, y)) -> dist (x, y)
}
2023-12-07 01:29:21 +00:00
&&& more number functions
fn random {
2024-06-16 01:51:56 +00:00
"Returns a random something. 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 collection (list, dict, set), it returns a random member of that collection."
() -> base :random ()
2024-06-07 20:40:37 +00:00
(n as :number) -> mult (n, random ())
(m as :number, n as :number) -> add (m, random (sub (n, m)))
2024-01-22 22:17:29 +00:00
(l as :list) -> {
let i = do l > count > random > floor
at (l, i)
}
(d as :dict) -> {
let key = do d > keys > random
2024-06-07 20:45:31 +00:00
get (key, d)
2024-01-22 22:17:29 +00:00
}
2024-06-16 01:51:56 +00:00
(s as :set) -> do s > list > random
2024-01-22 22:17:29 +00:00
}
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 {
"Truncates a number towards negative infinity. With positive numbers, it returns the integer part. With negative numbers, returns the next more-negative integer."
(n as :number) -> base :floor (n)
}
fn ceil {
"Truncates a number towards positive infinity. With negative numbers, it returns the integer part. With positive numbers, returns the next more-positive integer."
(n as :number) -> base :ceil (n)
}
fn round {
"Rounds a number to the nearest integer."
(n as :number) -> base :round (n)
}
fn range {
2024-01-22 22:17:29 +00:00
"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)
(start as :number, end as :number) -> base :range (start, end)
}
&&& Results, errors and other unhappy values
fn ok {
"Takes a value and wraps it in an :ok result tuple."
(value) -> (:ok, value)
}
fn ok? {
"Takes a value and returns true if it is an :ok result tuple."
((:ok, _)) -> true
(_) -> false
}
fn err {
"Takes a value and wraps it in an :err result tuple, presumably as an error message."
(msg) -> (:err, msg)
}
fn err? {
"Takes a value and returns true if it is an :err result tuple."
((:err, _)) -> true
(_) -> false
}
fn unwrap! {
"Takes a result tuple. If it's :ok, then returns the value. If it's not :ok, then it panics. If it's not a result tuple, it also panics."
((:ok, value)) -> value
2023-12-18 04:13:50 +00:00
((:err, msg)) -> panic! string ("Unwrapped :err! ", msg)
(_) -> panic! "Cannot unwrap something that's not an error tuple."
}
fn unwrap_or {
"Takes a value that is a result tuple and a default value. If it's :ok, then it returns the value. If it's :err, returns the default value."
((:ok, value), _) -> value
((:err, _), default) -> default
}
fn assert! {
"Asserts a condition: returns the value if the value is truthy, panics if the value is falsy. Takes an optional message."
2024-06-16 01:51:56 +00:00
(value) -> if value
then value
else panic! "Assert failed: {value}"
(msg, value) -> if value
then value
2024-06-16 01:51:56 +00:00
else panic! "Assert failed: {msg} with {value}"
}
2023-12-04 02:10:22 +00:00
&&& Turtle & other graphics
& some basic colors
2024-01-22 22:17:29 +00:00
& these are the "basic" css colors
& https://developer.mozilla.org/en-US/docs/Web/CSS/named-color
2023-12-18 00:17:03 +00:00
let colors = #{
2024-01-22 22:17:29 +00:00
:black (0, 0, 0, 255)
:silver (192, 192, 192, 255)
:gray (128, 128, 128, 255)
2023-12-04 17:12:05 +00:00
:white (255, 255, 255, 255)
2024-01-22 22:17:29 +00:00
:maroon (128, 0, 0, 255)
2023-12-04 17:12:05 +00:00
:red (255, 0, 0, 255)
2024-01-22 22:17:29 +00:00
: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)
2023-12-04 17:12:05 +00:00
:blue (0, 0, 255, 255)
2024-01-22 22:17:29 +00:00
:teal (0, 128, 128, 255)
:aqua (0, 255, 25, 255)
2023-12-04 02:10:22 +00:00
}
& the initial turtle state
let turtle_init = #{
:position (0, 0) & let's call this the origin for now
:heading 0 & this is straight up
2023-12-04 04:14:55 +00:00
:pendown? true
:pencolor colors :white
2023-12-04 02:10:22 +00:00
:penwidth 1
2023-12-04 04:14:55 +00:00
:visible? true
2023-12-04 02:10:22 +00:00
}
& turtle states: refs that get modified by calls
& turtle_commands is a list of commands, expressed as tuples
2024-06-05 19:52:03 +00:00
box turtle_commands = []
2023-12-04 02:10:22 +00:00
& and a list of turtle states
2024-06-05 19:52:03 +00:00
box turtle_states = [turtle_init]
2023-12-04 02:10:22 +00:00
2023-12-08 22:30:33 +00:00
fn reset_turtle! {
"Resets the turtle to its original state."
2024-06-05 19:52:03 +00:00
() -> store! (turtle_states, [turtle_init])
2023-12-08 22:30:33 +00:00
}
& and a list of calls to p5--at least for now
2024-06-05 19:52:03 +00:00
box p5_calls = []
2023-12-04 02:48:53 +00:00
& ...and finally, a background color
& we need to store this separately because, while it can be updated later,
& it must be the first call to p5.
2024-06-05 19:52:03 +00:00
box bgcolor = colors :black
2023-12-04 04:14:55 +00:00
fn add_call! (call) -> update! (p5_calls, append! (_, call))
2023-12-04 02:48:53 +00:00
fn add_command! (command) -> {
update! (turtle_commands, append! (_, command))
2024-06-05 19:52:03 +00:00
let prev = do turtle_states > unbox > last
2023-12-04 04:14:55 +00:00
let curr = apply_command (prev, command)
update! (turtle_states, append! (_, curr))
2023-12-04 04:14:55 +00:00
let call = state/call ()
if call then { add_call! (call); :ok } else :ok
}
fn make_line ((x1, y1), (x2, y2)) -> (:line, x1, y1, x2, y2)
let turtle_radius = 20
let turtle_angle = 0.385
2023-12-04 04:14:55 +00:00
let turtle_color = (255, 255, 255, 150)
2023-12-04 04:14:55 +00:00
fn render_turtle! () -> {
2024-06-05 19:52:03 +00:00
let state = do turtle_states > unbox > last
2023-12-04 04:14:55 +00:00
if state :visible?
then {
let (r, g, b, a) = turtle_color
add_call! ((:fill, r, g, b, a))
let #{heading
:pencolor (pen_r, pen_g, pen_b, pen_a)
2024-06-20 16:04:45 +00:00
:position (x, y)
pendown?
...} = state
let first = mult ((0, 1), turtle_radius)
2023-12-04 05:41:57 +00:00
let (x1, y1) = first
let (x2, y2) = rotate (first, turtle_angle)
let (x3, y3) = rotate (first, neg (turtle_angle))
add_call! ((:push))
add_call! ((:translate, x, y))
add_call! ((:rotate, turn/rad (heading)))
add_call! ((:noStroke))
add_call! ((:beginShape))
2023-12-04 05:41:57 +00:00
add_call! ((:vertex, x1, y1))
add_call! ((:vertex, x2, y2))
add_call! ((:vertex, x3, y3))
add_call! ((:endShape))
2024-01-22 22:17:29 +00:00
& 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, pen_r, pen_g, pen_b, pen_a))
2024-06-20 16:04:45 +00:00
if pendown? then add_call! ((:line, 0, 0, x1, y1)) else nil
2023-12-04 04:14:55 +00:00
add_call! ((:pop))
:ok
}
else :ok
}
fn state/call () -> {
2024-06-05 19:52:03 +00:00
let cmd = do turtle_commands > unbox > last > first
let states = unbox (turtle_states)
2023-12-04 04:14:55 +00:00
let curr = last (states)
let prev = at (states, sub (count (states), 2))
2023-12-04 02:48:53 +00:00
match cmd with {
2023-12-04 04:14:55 +00:00
:forward -> if curr :pendown?
2023-12-04 18:58:29 +00:00
then make_line (prev :position, curr :position)
2023-12-04 04:14:55 +00:00
else nil
:back -> if curr :pendown?
2023-12-04 18:58:29 +00:00
then make_line (prev :position, curr :position)
2023-12-04 04:14:55 +00:00
else nil
:home -> if curr :pendown?
2023-12-04 18:58:29 +00:00
then make_line (prev :position, curr :position)
2023-12-04 04:14:55 +00:00
else nil
:goto -> if curr :pendown?
2023-12-04 18:58:29 +00:00
then make_line (prev :position, curr :position)
2023-12-04 04:14:55 +00:00
else nil
:penwidth -> (:strokeWeight, curr :penwidth)
:pencolor -> {
let (r, g, b, a) = curr :pencolor
(:stroke, r, g, b, a)
}
:clear -> (:background, 0, 0, 0, 255)
2024-06-05 19:52:03 +00:00
_ -> nil
2023-12-04 02:48:53 +00:00
}
}
fn forward! {
"Moves the turtle forward by a number of steps. Alias: fd!"
(steps as :number) -> add_command! ((:forward, steps))
}
let fd! = forward!
fn back! {
"Moves the turtle backward by a number of steps. Alias: bk!"
(steps as :number) -> add_command! ((:back, steps))
}
let bk! = back!
fn left! {
"Rotates the turtle left, measured in turns. Alias: lt!"
(turns as :number) -> add_command! ((:left, turns))
}
let lt! = left!
fn right! {
"Rotates the turtle right, measured in turns. Alias: rt!"
(turns as :number) -> add_command! ((:right, turns))
}
let rt! = right!
fn penup! {
"Lifts the turtle's pen, stopping it from drawing. Alias: pu!"
() -> add_command! ((:penup))
}
let pu! = penup!
fn pendown! {
"Lowers the turtle's pen, causing it to draw. Alias: pd!"
() -> add_command! ((:pendown))
}
let pd! = pendown!
fn pencolor! {
"Changes the turtle's pen color. Takes a single grayscale value, an rgb tuple, or an rgba tuple. Alias: pc!"
(gray as :number) -> add_command! ((:pencolor, (gray, gray, gray, 255)))
((r as :number, g as :number, b as :number)) -> add_command! ((:pencolor, (r, g, b, 255)))
((r as :number, g as :number, b as :number, a as :number)) -> add_command! ((:pencolor, (r, g, b, a)))
}
let pc! = pencolor!
fn penwidth! {
"Sets the width of the turtle's pen, measured in pixels. Alias: pw!"
(width as :number) -> add_command! ((:penwidth, width))
}
let pw! = penwidth!
2023-12-04 02:10:22 +00:00
fn background! {
"Sets the background color behind the turtle and path. Alias: bg!"
2024-06-05 19:52:03 +00:00
(gray as :number) -> store! (bgcolor, (gray, gray, gray, 255))
2024-06-14 18:53:23 +00:00
((r as :number, g as :number, b as :number)) -> store! (bgcolor, (r, g, b, 255))
2024-06-05 19:52:03 +00:00
((r as :number, g as :number, b as :number, a as :number)) -> store! (bgcolor, (r, g, b, a))
2023-12-04 02:10:22 +00:00
}
let bg! = background!
fn home! {
"Sends the turtle home: to the centre of the screen, pointing up. If the pen is down, the turtle will draw a path to home."
() -> add_command! ((:home))
}
fn clear! {
"Clears the canvas and sends the turtle home."
() -> add_command! ((:clear))
}
fn goto! {
"Sends the turtle to (x, y) coordinates. If the pen is down, the turtle will draw a path to its new location."
(x as :number, y as :number) -> add_command! ((:goto, (x, y)))
((x, y)) -> goto! (x, y)
}
fn setheading! {
"Sets the turtle's heading. The angle is specified in turns, with 0 pointing up. Increasing values rotate the turtle counter-clockwise."
(heading as :number) -> add_command! ((:setheading, heading))
}
2024-06-20 16:10:07 +00:00
fn showturtle! {
"If the turtle is hidden, shows the turtle. If the turtle is already visible, does nothing."
() -> add_command! ((:show))
}
fn hideturtle! {
"If the turtle is visible, hides it. If the turtle is already hidden, does nothing."
() -> add_command! ((:hide))
}
2023-12-04 02:10:22 +00:00
fn heading/vector {
"Takes a turtle heading, and returns a unit vector of that heading."
(heading) -> {
& 0 is 90º/0.25T, 0.25 is 180º/0.5T, 0.5 is 270º/0.75T, 0.75 is 0º/0T
2023-12-04 02:10:22 +00:00
let angle = add (heading, 0.25)
(cos (angle), sin (angle))
}
}
fn apply_command {
2023-12-04 02:10:22 +00:00
"Takes a turtle state and a command and calculates a new state."
(state, command) -> match command with {
(:goto, (x, y)) -> assoc (state, :position, (x, y))
(:home) -> do state >
assoc (_, :position, (0, 0)) >
assoc (_, :heading, 0)
2024-06-21 19:28:46 +00:00
(:clear) -> do state >
assoc (state, :position, (0, 0)) >
assoc (_, :heading, 0)
(:right, turns) -> update (state, :heading, add (_, turns))
(:left, turns) -> update (state, :heading, sub (_, turns))
(:forward, steps) -> {
let #{heading, position, ...} = state
2023-12-04 02:10:22 +00:00
let unit = heading/vector (heading)
let vect = mult (steps, unit)
update (state, :position, add (vect, _))
}
(:back, steps) -> {
let #{heading, position, ...} = state
2023-12-04 02:10:22 +00:00
let unit = heading/vector (heading)
let vect = mult (steps, unit)
update (state, :position, sub (_, vect))
}
2023-12-04 04:14:55 +00:00
(:penup) -> assoc (state, :pendown?, false)
(:pendown) -> assoc (state, :pendown?, true)
2023-12-04 02:10:22 +00:00
(:penwidth, pixels) -> assoc (state, :penwidth, pixels)
(:pencolor, color) -> assoc (state, :pencolor, color)
(:setheading, heading) -> assoc (state, :heading, heading)
2024-06-20 16:10:07 +00:00
(:show) -> assoc (state, :visible?, true)
(:hide) -> assoc (state, :visible?, false)
}
}
2023-12-04 04:14:55 +00:00
fn turtle_state {
"Returns the turtle's current state."
2024-06-05 19:52:03 +00:00
() -> do turtle_states > unbox > last
2023-12-04 04:14:55 +00:00
}
2024-06-07 21:16:29 +00:00
fn load_turtle_state! {
"Sets the turtle state to a previously saved state. Returns the state."
(state) -> {
update! (turtle_states, append! (_, state))
2024-06-07 21:16:29 +00:00
let call = state/call ()
if call then { add_call! (call); :ok } else :ok
}
}
2023-12-04 04:14:55 +00:00
& position () -> (x, y)
fn position {
"Returns the turtle's current position."
() -> turtle_state () :position
}
2023-12-04 04:14:55 +00:00
fn heading {
"Returns the turtle's current heading."
() -> turtle_state () :heading
}
2023-12-04 04:14:55 +00:00
fn pendown? {
"Returns the turtle's pen state: true if the pen is down."
() -> turtle_state () :pendown?
}
2023-12-04 04:14:55 +00:00
fn pencolor {
"Returns the turtle's pen color as an (r, g, b, a) tuple."
() -> turtle_state () :pencolor
}
fn penwidth {
"Returns the turtle's pen width in pixels."
() -> turtle_state () :penwidth
2023-12-04 04:14:55 +00:00
}
2024-06-14 18:53:23 +00:00
box state = nil
2024-06-05 19:52:03 +00:00
pkg Prelude {
2024-06-15 21:03:49 +00:00
abs & math
add & math
and & bool
angle & math
any? & dicts lists strings sets tuples
append & lists sets
assert! & errors
assoc & dicts
assoc? & dicts
at & lists strings
atan/2 & math
back! & turtles
background! & turtles
between? & math
bg! & turtles
bgcolor & turtles
bk! & turtles
bool & bool
bool? & bool
box? & boxes
butlast & lists strings tuples
ceil & math
clear! & turtles
coll? & dicts lists sets tuples
colors & turtles
concat & string list set
2024-06-16 01:51:56 +00:00
contains? & list set
2024-06-15 21:03:49 +00:00
cos & math
count & string list set tuple dict
dec & math
deg/rad & math
deg/turn & math
dict & dict
dict? & dict
diff & dict
dissoc & dict
dist & math
div & math
div/0 & math
div/safe & math
doc! & env
downcase & string
each! & list
empty? & list dict set string tuple
eq? & values
err & result
err? & result
even? & math
false? & bool
fd! & turtles
filter & list
first & list tuple
floor & math
fn? & functions
fold & lists
forward! & turtles
get & dicts
goto! & turtles
gt? & math
gte? & math
heading & turtles
heading/vector & math
2024-06-20 16:10:07 +00:00
hideturtle! & turtles
2024-06-15 21:03:49 +00:00
home! & turtles
inc & math
inv & math
inv/0 & math
inv/safe & math
join & lists strings
keep & lists
keys & dicts
keyword? & keywords
last & lists tuples
left! & turtles
list & lists
list? & lists
load_turtle_state! & turtles
lt! & turtles
lt? & math
lte? & math
map & lists
max & math
min & math
mod & math
2024-06-16 01:51:56 +00:00
mod/0
mod/safe
2024-06-15 21:03:49 +00:00
mult & math
neg & math
neg? & math
neq? & values
nil? & nil
not & bool
odd? & math
ok & results
ok? & results
2024-06-16 01:51:56 +00:00
omit & set
2024-06-15 21:03:49 +00:00
or & bool
ordered? & lists tuples strings
p5_calls & turtles
pc! & turtles
pd! & turtles
pencolor & turtles
pencolor! & turtles
pendown! & turtles
pendown? & turtles
penup! & turtles
penwidth & turtles
penwidth! & turtles
pi & math
pos? & math
position & turtles
print! & environment
pu! & turtles
pw! & turtles
rad/deg & math
rad/turn & math
random & math dicts lists tuples sets
random_int & math
range & math lists
render_turtle! & turtles
report! & environment
reset_turtle! & turtles
rest & lists tuples
right! & turtles
round & math
rt! & turtles
second & lists tuples
sentence & lists strings
set & sets
set? & sets
setheading! & turtles
2024-06-15 21:03:49 +00:00
show & strings
2024-06-20 16:10:07 +00:00
showturtle! & turtles
2024-06-15 21:03:49 +00:00
sin & math
slice & lists tuples strings
some & values
some? & values
split & strings
2024-06-16 01:51:56 +00:00
sqrt & math
sqrt/safe & math
2024-06-15 21:03:49 +00:00
square & math
state & environment
store! & boxes
string & strings
string? & strings
strip & strings
sub & math
sum_of_squares & math
tan & math
tau & math
trim & strings
tuple? & tuples
turn/deg & math
turn/rad & math
turtle_commands & turtles
turtle_state & turtles
turtle_states & turtles
type & values
unbox & boxes
unwrap! & results
unwrap_or & results
upcase & strings
update & dicts
update! & boxes
values & dicts
words & strings lists
ws? & strings
zero? & math
2023-12-04 05:41:57 +00:00
}